Преглед на файлове

- working on adding DPI setting for PNG export in the Film Tool

Marius Stanciu преди 5 години
родител
ревизия
f99a5a8073
променени са 7 файла, в които са добавени 114 реда и са изтрити 41 реда
  1. 4 0
      CHANGELOG.md
  2. 1 0
      appGUI/preferences/PreferencesUIManager.py
  3. 29 18
      appGUI/preferences/tools/ToolsFilmPrefGroupUI.py
  4. 14 9
      appObjects/FlatCAMGeometry.py
  5. 55 10
      appTools/ToolFilm.py
  6. 10 4
      camlib.py
  7. 1 0
      defaults.py

+ 4 - 0
CHANGELOG.md

@@ -7,6 +7,10 @@ CHANGELOG for FlatCAM beta
 
 
 =================================================
 =================================================
 
 
+5.10.2020
+
+- working on adding DPI setting for PNG export in the Film Tool
+
 26.09.2020
 26.09.2020
 
 
 - the Selected Tab is now Properties Tab for FlatCAM objects
 - the Selected Tab is now Properties Tab for FlatCAM objects

+ 1 - 0
appGUI/preferences/PreferencesUIManager.py

@@ -461,6 +461,7 @@ class PreferencesUIManager:
             "tools_film_file_type_radio": self.ui.tools_defaults_form.tools_film_group.file_type_radio,
             "tools_film_file_type_radio": self.ui.tools_defaults_form.tools_film_group.file_type_radio,
             "tools_film_orientation": self.ui.tools_defaults_form.tools_film_group.orientation_radio,
             "tools_film_orientation": self.ui.tools_defaults_form.tools_film_group.orientation_radio,
             "tools_film_pagesize": self.ui.tools_defaults_form.tools_film_group.pagesize_combo,
             "tools_film_pagesize": self.ui.tools_defaults_form.tools_film_group.pagesize_combo,
+            "tools_film_png_dpi": self.ui.tools_defaults_form.tools_film_group.png_dpi_spinner,
 
 
             # Panelize Tool
             # Panelize Tool
             "tools_panelize_spacing_columns": self.ui.tools_defaults_form.tools_panelize_group.pspacing_columns,
             "tools_panelize_spacing_columns": self.ui.tools_defaults_form.tools_panelize_group.pspacing_columns,

+ 29 - 18
appGUI/preferences/tools/ToolsFilmPrefGroupUI.py

@@ -1,7 +1,7 @@
-from PyQt5 import QtWidgets, QtCore, QtGui
-from PyQt5.QtCore import Qt, QSettings
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QSettings
 
 
-from appGUI.GUIElements import RadioSet, FCEntry, FCDoubleSpinner, FCCheckBox, FCComboBox, FCColorEntry
+from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCComboBox, FCColorEntry, FCLabel, FCSpinner
 from appGUI.preferences.OptionsGroupUI import OptionsGroupUI
 from appGUI.preferences.OptionsGroupUI import OptionsGroupUI
 
 
 import gettext
 import gettext
@@ -28,7 +28,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
         self.decimals = decimals
         self.decimals = decimals
 
 
         # ## Parameters
         # ## Parameters
-        self.film_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
+        self.film_label = FCLabel("<b>%s:</b>" % _("Parameters"))
         self.film_label.setToolTip(
         self.film_label.setToolTip(
             _("Create a PCB film from a Gerber or Geometry object.\n"
             _("Create a PCB film from a Gerber or Geometry object.\n"
               "The file is saved in SVG format.")
               "The file is saved in SVG format.")
@@ -40,7 +40,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
 
 
         self.film_type_radio = RadioSet([{'label': 'Pos', 'value': 'pos'},
         self.film_type_radio = RadioSet([{'label': 'Pos', 'value': 'pos'},
                                          {'label': 'Neg', 'value': 'neg'}])
                                          {'label': 'Neg', 'value': 'neg'}])
-        ftypelbl = QtWidgets.QLabel('%s:' % _('Film Type'))
+        ftypelbl = FCLabel('%s:' % _('Film Type'))
         ftypelbl.setToolTip(
         ftypelbl.setToolTip(
             _("Generate a Positive black film or a Negative film.\n"
             _("Generate a Positive black film or a Negative film.\n"
               "Positive means that it will print the features\n"
               "Positive means that it will print the features\n"
@@ -53,7 +53,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.film_type_radio, 0, 1)
         grid0.addWidget(self.film_type_radio, 0, 1)
 
 
         # Film Color
         # Film Color
-        self.film_color_label = QtWidgets.QLabel('%s:' % _('Film Color'))
+        self.film_color_label = FCLabel('%s:' % _('Film Color'))
         self.film_color_label.setToolTip(
         self.film_color_label.setToolTip(
             _("Set the film color when positive film is selected.")
             _("Set the film color when positive film is selected.")
         )
         )
@@ -68,7 +68,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
         self.film_boundary_entry.set_range(0, 9999.9999)
         self.film_boundary_entry.set_range(0, 9999.9999)
         self.film_boundary_entry.setSingleStep(0.1)
         self.film_boundary_entry.setSingleStep(0.1)
 
 
-        self.film_boundary_label = QtWidgets.QLabel('%s:' % _("Border"))
+        self.film_boundary_label = FCLabel('%s:' % _("Border"))
         self.film_boundary_label.setToolTip(
         self.film_boundary_label.setToolTip(
             _("Specify a border around the object.\n"
             _("Specify a border around the object.\n"
               "Only for negative film.\n"
               "Only for negative film.\n"
@@ -87,7 +87,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
         self.film_scale_stroke_entry.set_range(0, 9999.9999)
         self.film_scale_stroke_entry.set_range(0, 9999.9999)
         self.film_scale_stroke_entry.setSingleStep(0.1)
         self.film_scale_stroke_entry.setSingleStep(0.1)
 
 
-        self.film_scale_stroke_label = QtWidgets.QLabel('%s:' % _("Scale Stroke"))
+        self.film_scale_stroke_label = FCLabel('%s:' % _("Scale Stroke"))
         self.film_scale_stroke_label.setToolTip(
         self.film_scale_stroke_label.setToolTip(
             _("Scale the line stroke thickness of each feature in the SVG file.\n"
             _("Scale the line stroke thickness of each feature in the SVG file.\n"
               "It means that the line that envelope each SVG feature will be thicker or thinner,\n"
               "It means that the line that envelope each SVG feature will be thicker or thinner,\n"
@@ -96,7 +96,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.film_scale_stroke_label, 3, 0)
         grid0.addWidget(self.film_scale_stroke_label, 3, 0)
         grid0.addWidget(self.film_scale_stroke_entry, 3, 1)
         grid0.addWidget(self.film_scale_stroke_entry, 3, 1)
 
 
-        self.film_adj_label = QtWidgets.QLabel('<b>%s</b>' % _("Film Adjustments"))
+        self.film_adj_label = FCLabel('<b>%s</b>' % _("Film Adjustments"))
         self.film_adj_label.setToolTip(
         self.film_adj_label.setToolTip(
             _("Sometime the printers will distort the print shape, especially the Laser types.\n"
             _("Sometime the printers will distort the print shape, especially the Laser types.\n"
               "This section provide the tools to compensate for the print distortions.")
               "This section provide the tools to compensate for the print distortions.")
@@ -117,7 +117,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
         )
         )
         grid0.addWidget(self.film_scale_cb, 5, 0, 1, 2)
         grid0.addWidget(self.film_scale_cb, 5, 0, 1, 2)
 
 
-        self.film_scalex_label = QtWidgets.QLabel('%s:' % _("X factor"))
+        self.film_scalex_label = FCLabel('%s:' % _("X factor"))
         self.film_scalex_entry = FCDoubleSpinner()
         self.film_scalex_entry = FCDoubleSpinner()
         self.film_scalex_entry.set_range(-999.9999, 999.9999)
         self.film_scalex_entry.set_range(-999.9999, 999.9999)
         self.film_scalex_entry.set_precision(self.decimals)
         self.film_scalex_entry.set_precision(self.decimals)
@@ -126,7 +126,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.film_scalex_label, 6, 0)
         grid0.addWidget(self.film_scalex_label, 6, 0)
         grid0.addWidget(self.film_scalex_entry, 6, 1)
         grid0.addWidget(self.film_scalex_entry, 6, 1)
 
 
-        self.film_scaley_label = QtWidgets.QLabel('%s:' % _("Y factor"))
+        self.film_scaley_label = FCLabel('%s:' % _("Y factor"))
         self.film_scaley_entry = FCDoubleSpinner()
         self.film_scaley_entry = FCDoubleSpinner()
         self.film_scaley_entry.set_range(-999.9999, 999.9999)
         self.film_scaley_entry.set_range(-999.9999, 999.9999)
         self.film_scaley_entry.set_precision(self.decimals)
         self.film_scaley_entry.set_precision(self.decimals)
@@ -148,7 +148,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
         )
         )
         grid0.addWidget(self.film_skew_cb, 8, 0, 1, 2)
         grid0.addWidget(self.film_skew_cb, 8, 0, 1, 2)
 
 
-        self.film_skewx_label = QtWidgets.QLabel('%s:' % _("X angle"))
+        self.film_skewx_label = FCLabel('%s:' % _("X angle"))
         self.film_skewx_entry = FCDoubleSpinner()
         self.film_skewx_entry = FCDoubleSpinner()
         self.film_skewx_entry.set_range(-999.9999, 999.9999)
         self.film_skewx_entry.set_range(-999.9999, 999.9999)
         self.film_skewx_entry.set_precision(self.decimals)
         self.film_skewx_entry.set_precision(self.decimals)
@@ -157,7 +157,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.film_skewx_label, 9, 0)
         grid0.addWidget(self.film_skewx_label, 9, 0)
         grid0.addWidget(self.film_skewx_entry, 9, 1)
         grid0.addWidget(self.film_skewx_entry, 9, 1)
 
 
-        self.film_skewy_label = QtWidgets.QLabel('%s:' % _("Y angle"))
+        self.film_skewy_label = FCLabel('%s:' % _("Y angle"))
         self.film_skewy_entry = FCDoubleSpinner()
         self.film_skewy_entry = FCDoubleSpinner()
         self.film_skewy_entry.set_range(-999.9999, 999.9999)
         self.film_skewy_entry.set_range(-999.9999, 999.9999)
         self.film_skewy_entry.set_precision(self.decimals)
         self.film_skewy_entry.set_precision(self.decimals)
@@ -166,7 +166,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.film_skewy_label, 10, 0)
         grid0.addWidget(self.film_skewy_label, 10, 0)
         grid0.addWidget(self.film_skewy_entry, 10, 1)
         grid0.addWidget(self.film_skewy_entry, 10, 1)
 
 
-        self.film_skew_ref_label = QtWidgets.QLabel('%s:' % _("Reference"))
+        self.film_skew_ref_label = FCLabel('%s:' % _("Reference"))
         self.film_skew_ref_label.setToolTip(
         self.film_skew_ref_label.setToolTip(
             _("The reference point to be used as origin for the skew.\n"
             _("The reference point to be used as origin for the skew.\n"
               "It can be one of the four points of the geometry bounding box.")
               "It can be one of the four points of the geometry bounding box.")
@@ -198,7 +198,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
                                           {'label': _('Y'), 'value': 'y'},
                                           {'label': _('Y'), 'value': 'y'},
                                           {'label': _('Both'), 'value': 'both'}],
                                           {'label': _('Both'), 'value': 'both'}],
                                          stretch=False)
                                          stretch=False)
-        self.film_mirror_axis_label = QtWidgets.QLabel('%s:' % _("Mirror axis"))
+        self.film_mirror_axis_label = FCLabel('%s:' % _("Mirror axis"))
 
 
         grid0.addWidget(self.film_mirror_axis_label, 13, 0)
         grid0.addWidget(self.film_mirror_axis_label, 13, 0)
         grid0.addWidget(self.film_mirror_axis, 13, 1)
         grid0.addWidget(self.film_mirror_axis, 13, 1)
@@ -213,7 +213,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
                                          {'label': _('PDF'), 'value': 'pdf'}
                                          {'label': _('PDF'), 'value': 'pdf'}
                                          ], stretch=False)
                                          ], stretch=False)
 
 
-        self.file_type_label = QtWidgets.QLabel(_("Film Type:"))
+        self.file_type_label = FCLabel(_("Film Type:"))
         self.file_type_label.setToolTip(
         self.file_type_label.setToolTip(
             _("The file type of the saved film. Can be:\n"
             _("The file type of the saved film. Can be:\n"
               "- 'SVG' -> open-source vectorial format\n"
               "- 'SVG' -> open-source vectorial format\n"
@@ -224,7 +224,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.file_type_radio, 15, 1)
         grid0.addWidget(self.file_type_radio, 15, 1)
 
 
         # Page orientation
         # Page orientation
-        self.orientation_label = QtWidgets.QLabel('%s:' % _("Page Orientation"))
+        self.orientation_label = FCLabel('%s:' % _("Page Orientation"))
         self.orientation_label.setToolTip(_("Can be:\n"
         self.orientation_label.setToolTip(_("Can be:\n"
                                             "- Portrait\n"
                                             "- Portrait\n"
                                             "- Landscape"))
                                             "- Landscape"))
@@ -237,7 +237,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.orientation_radio, 16, 1)
         grid0.addWidget(self.orientation_radio, 16, 1)
 
 
         # Page Size
         # Page Size
-        self.pagesize_label = QtWidgets.QLabel('%s:' % _("Page Size"))
+        self.pagesize_label = FCLabel('%s:' % _("Page Size"))
         self.pagesize_label.setToolTip(_("A selection of standard ISO 216 page sizes."))
         self.pagesize_label.setToolTip(_("A selection of standard ISO 216 page sizes."))
 
 
         self.pagesize_combo = FCComboBox()
         self.pagesize_combo = FCComboBox()
@@ -302,6 +302,17 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.pagesize_label, 17, 0)
         grid0.addWidget(self.pagesize_label, 17, 0)
         grid0.addWidget(self.pagesize_combo, 17, 1)
         grid0.addWidget(self.pagesize_combo, 17, 1)
 
 
+        # PNG DPI
+        self.png_dpi_label = FCLabel('%s:' % "PNG DPI")
+        self.png_dpi_label.setToolTip(
+            _("Default value is 96 DPI. Change this value to scale the PNG file.")
+        )
+        self.png_dpi_spinner = FCSpinner()
+        self.png_dpi_spinner.set_range(0, 100000)
+
+        grid0.addWidget(self.png_dpi_label, 19, 0)
+        grid0.addWidget(self.png_dpi_spinner, 19, 1)
+
         self.layout.addStretch()
         self.layout.addStretch()
 
 
         # Film Tool
         # Film Tool

+ 14 - 9
appObjects/FlatCAMGeometry.py

@@ -1854,6 +1854,7 @@ class GeometryObject(FlatCAMObj, Geometry):
             return
             return
 
 
         self.multigeo = True
         self.multigeo = True
+
         # Object initialization function for app.app_obj.new_object()
         # Object initialization function for app.app_obj.new_object()
         # RUNNING ON SEPARATE THREAD!
         # RUNNING ON SEPARATE THREAD!
         def job_init_single_geometry(job_obj, app_obj):
         def job_init_single_geometry(job_obj, app_obj):
@@ -2115,9 +2116,9 @@ class GeometryObject(FlatCAMObj, Geometry):
                 is_first = True if tooluid_key == tool_lst[0] else False
                 is_first = True if tooluid_key == tool_lst[0] else False
                 is_last = True if tooluid_key == tool_lst[-1] else False
                 is_last = True if tooluid_key == tool_lst[-1] else False
                 res, start_gcode = job_obj.geometry_tool_gcode_gen(tooluid_key, tools_dict, first_pt=(0, 0),
                 res, start_gcode = job_obj.geometry_tool_gcode_gen(tooluid_key, tools_dict, first_pt=(0, 0),
-                                                                   tolerance = tol,
+                                                                   tolerance=tol,
                                                                    is_first=is_first, is_last=is_last,
                                                                    is_first=is_first, is_last=is_last,
-                                                                   toolchange = True)
+                                                                   toolchange=True)
                 if res == 'fail':
                 if res == 'fail':
                     log.debug("GeometryObject.mtool_gen_cncjob() --> generate_from_geometry2() failed")
                     log.debug("GeometryObject.mtool_gen_cncjob() --> generate_from_geometry2() failed")
                     return 'fail'
                     return 'fail'
@@ -2303,8 +2304,8 @@ class GeometryObject(FlatCAMObj, Geometry):
                                                    toolchangexy=toolchangexy,
                                                    toolchangexy=toolchangexy,
                                                    extracut=extracut, extracut_length=extracut_length,
                                                    extracut=extracut, extracut_length=extracut_length,
                                                    startz=startz, endz=endz, endxy=endxy,
                                                    startz=startz, endz=endz, endxy=endxy,
-                                                   pp_geometry_name=ppname_g
-            )
+                                                   pp_geometry_name=ppname_g)
+
             job_obj.source_file = res
             job_obj.source_file = res
             # tell gcode_parse from which point to start drawing the lines depending on what kind of object is the
             # tell gcode_parse from which point to start drawing the lines depending on what kind of object is the
             # source of gcode
             # source of gcode
@@ -2872,13 +2873,13 @@ class GeometryObject(FlatCAMObj, Geometry):
         self.plot()
         self.plot()
 
 
     @staticmethod
     @staticmethod
-    def merge(geo_list, geo_final, multigeo=None, fuse_tools=None):
+    def merge(geo_list, geo_final, multi_geo=None, fuse_tools=None):
         """
         """
         Merges the geometry of objects in grb_list into the geometry of geo_final.
         Merges the geometry of objects in grb_list into the geometry of geo_final.
 
 
         :param geo_list:    List of GerberObject Objects to join.
         :param geo_list:    List of GerberObject Objects to join.
         :param geo_final:   Destination GerberObject object.
         :param geo_final:   Destination GerberObject object.
-        :param multigeo:    if the merged geometry objects are of type MultiGeo
+        :param multi_geo:   if the merged geometry objects are of type MultiGeo
         :param fuse_tools:  If True will try to fuse tools of the same type for the Geometry objects
         :param fuse_tools:  If True will try to fuse tools of the same type for the Geometry objects
         :return: None
         :return: None
         """
         """
@@ -2908,7 +2909,7 @@ class GeometryObject(FlatCAMObj, Geometry):
                 GeometryObject.merge(geo_list=geo_obj, geo_final=geo_final)
                 GeometryObject.merge(geo_list=geo_obj, geo_final=geo_final)
             # If not list, just append
             # If not list, just append
             else:
             else:
-                if multigeo is None or multigeo is False:
+                if multi_geo is None or multi_geo is False:
                     geo_final.multigeo = False
                     geo_final.multigeo = False
                 else:
                 else:
                     geo_final.multigeo = True
                     geo_final.multigeo = True
@@ -2966,19 +2967,23 @@ class GeometryObject(FlatCAMObj, Geometry):
             new_tool_nr = 1
             new_tool_nr = 1
             for i_lst in intersect_list:
             for i_lst in intersect_list:
                 new_solid_geo = []
                 new_solid_geo = []
+                last_tool = None
                 for old_tool in i_lst:
                 for old_tool in i_lst:
                     new_solid_geo += new_tools[old_tool]['solid_geometry']
                     new_solid_geo += new_tools[old_tool]['solid_geometry']
+                    last_tool = old_tool
 
 
-                if new_solid_geo:
+                if new_solid_geo and last_tool:
                     final_tools[new_tool_nr] = \
                     final_tools[new_tool_nr] = \
                         {
                         {
-                            k: deepcopy(new_tools[old_tool][k]) for k in new_tools[old_tool] if k != 'solid_geometry'
+                            k: deepcopy(new_tools[last_tool][k]) for k in new_tools[last_tool] if k != 'solid_geometry'
                         }
                         }
                     final_tools[new_tool_nr]['solid_geometry'] = deepcopy(new_solid_geo)
                     final_tools[new_tool_nr]['solid_geometry'] = deepcopy(new_solid_geo)
                     new_tool_nr += 1
                     new_tool_nr += 1
         else:
         else:
             final_tools = new_tools
             final_tools = new_tools
 
 
+        # if not final_tools:
+        #     return 'fail'
         geo_final.tools = final_tools
         geo_final.tools = final_tools
 
 
     @staticmethod
     @staticmethod

+ 55 - 10
appTools/ToolFilm.py

@@ -9,7 +9,7 @@ from PyQt5 import QtCore, QtWidgets, QtGui
 
 
 from appTool import AppTool
 from appTool import AppTool
 from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, \
 from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, \
-    OptionalHideInputSection, FCComboBox, FCFileSaveDialog, FCButton, FCLabel
+    OptionalHideInputSection, FCComboBox, FCFileSaveDialog, FCButton, FCLabel, FCSpinner
 
 
 from copy import deepcopy
 from copy import deepcopy
 import logging
 import logging
@@ -138,6 +138,8 @@ class Film(AppTool):
         self.ui.orientation_radio.set_value(self.app.defaults["tools_film_orientation"])
         self.ui.orientation_radio.set_value(self.app.defaults["tools_film_orientation"])
         self.ui.pagesize_combo.set_value(self.app.defaults["tools_film_pagesize"])
         self.ui.pagesize_combo.set_value(self.app.defaults["tools_film_pagesize"])
 
 
+        self.ui.png_dpi_spinner.set_value(self.app.defaults["tools_film_png_dpi"])
+
         self.ui.tf_type_obj_combo.set_value('grb')
         self.ui.tf_type_obj_combo.set_value('grb')
         self.ui.tf_type_box_combo.set_value('grb')
         self.ui.tf_type_box_combo.set_value('grb')
         # run once to update the obj_type attribute in the FCCombobox so the last object is showed in cb
         # run once to update the obj_type attribute in the FCCombobox so the last object is showed in cb
@@ -187,8 +189,8 @@ class Film(AppTool):
     def generate_positive_normal_film(self, name, boxname, factor, ftype='svg'):
     def generate_positive_normal_film(self, name, boxname, factor, ftype='svg'):
         log.debug("ToolFilm.Film.generate_positive_normal_film() started ...")
         log.debug("ToolFilm.Film.generate_positive_normal_film() started ...")
 
 
-        scale_factor_x = None
-        scale_factor_y = None
+        scale_factor_x = 1
+        scale_factor_y = 1
         skew_factor_x = None
         skew_factor_x = None
         skew_factor_y = None
         skew_factor_y = None
         mirror = None
         mirror = None
@@ -328,8 +330,8 @@ class Film(AppTool):
     def generate_negative_film(self, name, boxname, factor, ftype='svg'):
     def generate_negative_film(self, name, boxname, factor, ftype='svg'):
         log.debug("ToolFilm.Film.generate_negative_film() started ...")
         log.debug("ToolFilm.Film.generate_negative_film() started ...")
 
 
-        scale_factor_x = None
-        scale_factor_y = None
+        scale_factor_x = 1
+        scale_factor_y = 1
         skew_factor_x = None
         skew_factor_x = None
         skew_factor_y = None
         skew_factor_y = None
         mirror = None
         mirror = None
@@ -390,7 +392,7 @@ class Film(AppTool):
 
 
     def export_negative(self, obj_name, box_name, filename, boundary,
     def export_negative(self, obj_name, box_name, filename, boundary,
                         scale_stroke_factor=0.00,
                         scale_stroke_factor=0.00,
-                        scale_factor_x=None, scale_factor_y=None,
+                        scale_factor_x=1, scale_factor_y=1,
                         skew_factor_x=None, skew_factor_y=None, skew_reference='center',
                         skew_factor_x=None, skew_factor_y=None, skew_reference='center',
                         mirror=None,
                         mirror=None,
                         use_thread=True, ftype='svg'):
                         use_thread=True, ftype='svg'):
@@ -434,6 +436,12 @@ class Film(AppTool):
             self.app.inform.emit('[WARNING_NOTCL] %s: %s' % (_("No object Box. Using instead"), obj))
             self.app.inform.emit('[WARNING_NOTCL] %s: %s' % (_("No object Box. Using instead"), obj))
             box = obj
             box = obj
 
 
+        new_png_dpi = self.ui.png_dpi_spinner.get_value()
+        dpi_rate = new_png_dpi / 96
+        if dpi_rate != 1:
+            scale_factor_x += dpi_rate
+            scale_factor_y += dpi_rate
+
         def make_negative_film():
         def make_negative_film():
             exported_svg = obj.export_svg(scale_stroke_factor=scale_stroke_factor,
             exported_svg = obj.export_svg(scale_stroke_factor=scale_stroke_factor,
                                           scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y,
                                           scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y,
@@ -513,7 +521,10 @@ class Film(AppTool):
                 try:
                 try:
                     doc_final = StringIO(doc_final)
                     doc_final = StringIO(doc_final)
                     drawing = svg2rlg(doc_final)
                     drawing = svg2rlg(doc_final)
-                    renderPM.drawToFile(drawing, filename, 'PNG')
+                    if new_png_dpi == 96:
+                        renderPM.drawToFile(drawing, filename, 'PNG')
+                    else:
+                        renderPM.drawToFile(drawing, filename, 'PNG', dpi=new_png_dpi)
                 except Exception as e:
                 except Exception as e:
                     log.debug("FilmTool.export_negative() --> PNG output --> %s" % str(e))
                     log.debug("FilmTool.export_negative() --> PNG output --> %s" % str(e))
                     return 'fail'
                     return 'fail'
@@ -566,7 +577,7 @@ class Film(AppTool):
 
 
     def export_positive(self, obj_name, box_name, filename,
     def export_positive(self, obj_name, box_name, filename,
                         scale_stroke_factor=0.00,
                         scale_stroke_factor=0.00,
-                        scale_factor_x=None, scale_factor_y=None,
+                        scale_factor_x=1, scale_factor_y=1,
                         skew_factor_x=None, skew_factor_y=None, skew_reference='center',
                         skew_factor_x=None, skew_factor_y=None, skew_reference='center',
                         mirror=None,  orientation_val='p', pagesize_val='A4', color_val='black', opacity_val=1.0,
                         mirror=None,  orientation_val='p', pagesize_val='A4', color_val='black', opacity_val=1.0,
                         use_thread=True, ftype='svg'):
                         use_thread=True, ftype='svg'):
@@ -620,6 +631,12 @@ class Film(AppTool):
         color = color_val
         color = color_val
         transparency_level = opacity_val
         transparency_level = opacity_val
 
 
+        new_png_dpi = self.ui.png_dpi_spinner.get_value()
+        dpi_rate = new_png_dpi / 96
+        if dpi_rate != 1:
+            scale_factor_x += dpi_rate
+            scale_factor_y += dpi_rate
+
         def make_positive_film(p_size, orientation, color, transparency_level):
         def make_positive_film(p_size, orientation, color, transparency_level):
             log.debug("FilmTool.export_positive().make_positive_film()")
             log.debug("FilmTool.export_positive().make_positive_film()")
 
 
@@ -692,7 +709,10 @@ class Film(AppTool):
                 try:
                 try:
                     doc_final = StringIO(doc_final)
                     doc_final = StringIO(doc_final)
                     drawing = svg2rlg(doc_final)
                     drawing = svg2rlg(doc_final)
-                    renderPM.drawToFile(drawing, filename, 'PNG')
+                    if new_png_dpi == 96:
+                        renderPM.drawToFile(drawing, filename, 'PNG')
+                    else:
+                        renderPM.drawToFile(drawing, filename, 'PNG', dpi=new_png_dpi)
                 except Exception as e:
                 except Exception as e:
                     log.debug("FilmTool.export_positive() --> PNG output --> %s" % str(e))
                     log.debug("FilmTool.export_positive() --> PNG output --> %s" % str(e))
                     return 'fail'
                     return 'fail'
@@ -1199,6 +1219,20 @@ class FilmUI:
 
 
         self.on_film_type(val='hide')
         self.on_film_type(val='hide')
 
 
+        # PNG DPI
+        self.png_dpi_label = FCLabel('%s:' % "PNG DPI")
+        self.png_dpi_label.setToolTip(
+            _("Default value is 96 DPI. Change this value to scale the PNG file.")
+        )
+        self.png_dpi_spinner = FCSpinner(callback=self.confirmation_message_int)
+        self.png_dpi_spinner.set_range(0, 100000)
+
+        grid1.addWidget(self.png_dpi_label, 4, 0)
+        grid1.addWidget(self.png_dpi_spinner, 4, 1)
+
+        self.png_dpi_label.hide()
+        self.png_dpi_spinner.hide()
+
         # Buttons
         # Buttons
         self.film_object_button = FCButton(_("Save Film"))
         self.film_object_button = FCButton(_("Save Film"))
         self.film_object_button.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as.png'))
         self.film_object_button.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as.png'))
@@ -1214,7 +1248,7 @@ class FilmUI:
                                    font-weight: bold;
                                    font-weight: bold;
                                }
                                }
                                """)
                                """)
-        grid1.addWidget(self.film_object_button, 4, 0, 1, 2)
+        grid1.addWidget(self.film_object_button, 6, 0, 1, 2)
 
 
         self.layout.addStretch()
         self.layout.addStretch()
 
 
@@ -1254,11 +1288,22 @@ class FilmUI:
             self.orientation_radio.show()
             self.orientation_radio.show()
             self.pagesize_label.show()
             self.pagesize_label.show()
             self.pagesize_combo.show()
             self.pagesize_combo.show()
+            self.png_dpi_label.hide()
+            self.png_dpi_spinner.hide()
+        elif val == 'png':
+            self.png_dpi_label.show()
+            self.png_dpi_spinner.show()
+            self.orientation_label.hide()
+            self.orientation_radio.hide()
+            self.pagesize_label.hide()
+            self.pagesize_combo.hide()
         else:
         else:
             self.orientation_label.hide()
             self.orientation_label.hide()
             self.orientation_radio.hide()
             self.orientation_radio.hide()
             self.pagesize_label.hide()
             self.pagesize_label.hide()
             self.pagesize_combo.hide()
             self.pagesize_combo.hide()
+            self.png_dpi_label.hide()
+            self.png_dpi_spinner.hide()
 
 
     def on_punch_source(self, val):
     def on_punch_source(self, val):
         if val == 'pad' and self.punch_cb.get_value():
         if val == 'pad' and self.punch_cb.get_value():

+ 10 - 4
camlib.py

@@ -2255,14 +2255,20 @@ class Geometry(object):
 
 
         geom = geom_svg
         geom = geom_svg
 
 
-        if scale_factor_x:
+        if scale_factor_x and not scale_factor_y:
             geom = affinity.scale(geom_svg, scale_factor_x, 1.0)
             geom = affinity.scale(geom_svg, scale_factor_x, 1.0)
-        if scale_factor_y:
+        elif not scale_factor_x and scale_factor_y:
             geom = affinity.scale(geom_svg, 1.0, scale_factor_y)
             geom = affinity.scale(geom_svg, 1.0, scale_factor_y)
-        if skew_factor_x:
+        elif scale_factor_x and scale_factor_y:
+            geom = affinity.scale(geom_svg, scale_factor_x, scale_factor_y)
+
+        if skew_factor_x and not skew_factor_y:
             geom = affinity.skew(geom_svg, skew_factor_x, 0.0, origin=skew_ref)
             geom = affinity.skew(geom_svg, skew_factor_x, 0.0, origin=skew_ref)
-        if skew_factor_y:
+        elif not skew_factor_x and skew_factor_y:
             geom = affinity.skew(geom_svg, 0.0, skew_factor_y, origin=skew_ref)
             geom = affinity.skew(geom_svg, 0.0, skew_factor_y, origin=skew_ref)
+        elif skew_factor_x and skew_factor_y:
+            geom = affinity.skew(geom_svg, skew_factor_x, skew_factor_y, origin=skew_ref)
+
         if mirror:
         if mirror:
             if mirror == 'x':
             if mirror == 'x':
                 geom = affinity.scale(geom_svg, 1.0, -1.0)
                 geom = affinity.scale(geom_svg, 1.0, -1.0)

+ 1 - 0
defaults.py

@@ -533,6 +533,7 @@ class FlatCAMDefaults:
         "tools_film_file_type_radio": 'svg',
         "tools_film_file_type_radio": 'svg',
         "tools_film_orientation": 'p',
         "tools_film_orientation": 'p',
         "tools_film_pagesize": 'A4',
         "tools_film_pagesize": 'A4',
+        "tools_film_png_dpi": 96,
 
 
         # Panel Tool
         # Panel Tool
         "tools_panelize_spacing_columns": 0.0,
         "tools_panelize_spacing_columns": 0.0,