- updated the Film Tool to allow exporting PDF and PNG file (besides the SVG file)

This commit is contained in:
Marius Stanciu
2019-11-26 16:37:21 +02:00
parent c025d6ad79
commit f1af9d7999
6 changed files with 490 additions and 343 deletions

View File

@@ -557,7 +557,7 @@ class App(QtCore.QObject):
"gerber_editor_newdim": "0.5, 0.5",
"gerber_editor_array_size": 5,
"gerber_editor_lin_axis": 'X',
"gerber_editor_lin_pitch": 1,
"gerber_editor_lin_pitch": 0.1,
"gerber_editor_lin_angle": 0.0,
"gerber_editor_circ_dir": 'CW',
"gerber_editor_circ_angle": 0.0,
@@ -765,6 +765,7 @@ class App(QtCore.QObject):
"tools_film_skew_ref_radio": 'bottomleft',
"tools_film_mirror_cb": False,
"tools_film_mirror_axis_radio": 'none',
"tools_film_file_type_radio": 'svg',
# Panel Tool
"tools_panelize_spacing_columns": 0,
@@ -1322,6 +1323,7 @@ class App(QtCore.QObject):
"tools_film_skew_ref_radio": self.ui.tools_defaults_form.tools_film_group.film_skew_reference,
"tools_film_mirror_cb": self.ui.tools_defaults_form.tools_film_group.film_mirror_cb,
"tools_film_mirror_axis_radio": self.ui.tools_defaults_form.tools_film_group.film_mirror_axis,
"tools_film_file_type_radio": self.ui.tools_defaults_form.tools_film_group.file_type_radio,
# Panelize Tool
"tools_panelize_spacing_columns": self.ui.tools_defaults_form.tools_panelize_group.pspacing_columns,
@@ -5560,7 +5562,7 @@ class App(QtCore.QObject):
if self.toggle_units_ignore:
return
new_units = self.defaults['units'].upper()
new_units = self.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
# If option is the same, then ignore
if new_units == self.defaults["units"].upper():
@@ -10059,291 +10061,6 @@ class App(QtCore.QObject):
self.inform.emit('[success] %s: %s' %
(_("SVG file exported to"), filename))
def export_svg_negative(self, obj_name, box_name, filename, boundary,
scale_stroke_factor=0.00,
scale_factor_x=None, scale_factor_y=None,
skew_factor_x=None, skew_factor_y=None, skew_reference='center',
mirror=None,
use_thread=True):
"""
Exports a Geometry Object to an SVG file in negative.
:param obj_name: the name of the FlatCAM object to be saved as SVG
:param box_name: the name of the FlatCAM object to be used as delimitation of the content to be saved
:param filename: Path to the SVG file to save to.
:param boundary: thickness of a black border to surround all the features
:param scale_stroke_factor: factor by which to change/scale the thickness of the features
:param scale_factor_x: factor to scale the svg geometry on the X axis
:param scale_factor_y: factor to scale the svg geometry on the Y axis
:param skew_factor_x: factor to skew the svg geometry on the X axis
:param skew_factor_y: factor to skew the svg geometry on the Y axis
:param skew_reference: reference to use for skew. Can be 'bottomleft', 'bottomright', 'topleft', 'topright' and
those are the 4 points of the bounding box of the geometry to be skewed.
:param mirror: can be 'x' or 'y' or 'both'. Axis on which to mirror the svg geometry
:param use_thread: if to be run in a separate thread; boolean
:return:
"""
self.report_usage("export_negative()")
if filename is None:
filename = self.defaults["global_last_save_folder"]
self.log.debug("export_svg() negative")
try:
obj = self.collection.get_by_name(str(obj_name))
except Exception:
# TODO: The return behavior has not been established... should raise exception?
return "Could not retrieve object: %s" % obj_name
try:
box = self.collection.get_by_name(str(box_name))
except Exception:
# TODO: The return behavior has not been established... should raise exception?
return "Could not retrieve object: %s" % box_name
if box is None:
self.inform.emit('[WARNING_NOTCL] %s: %s' %
(_("No object Box. Using instead"), obj))
box = obj
def make_negative_film():
exported_svg = obj.export_svg(scale_stroke_factor=scale_stroke_factor,
scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y,
skew_factor_x=skew_factor_x, skew_factor_y=skew_factor_y,
mirror=mirror
)
# Determine bounding area for svg export
bounds = box.bounds()
size = box.size()
uom = obj.units.lower()
# Convert everything to strings for use in the xml doc
svgwidth = str(size[0] + (2 * boundary))
svgheight = str(size[1] + (2 * boundary))
minx = str(bounds[0] - boundary)
miny = str(bounds[1] + boundary + size[1])
miny_rect = str(bounds[1] - boundary)
# Add a SVG Header and footer to the svg output from shapely
# The transform flips the Y Axis so that everything renders
# properly within svg apps such as inkscape
svg_header = '<svg xmlns="http://www.w3.org/2000/svg" ' \
'version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" '
svg_header += 'width="' + svgwidth + uom + '" '
svg_header += 'height="' + svgheight + uom + '" '
svg_header += 'viewBox="' + minx + ' -' + miny + ' ' + svgwidth + ' ' + svgheight + '" '
svg_header += '>'
svg_header += '<g transform="scale(1,-1)">'
svg_footer = '</g> </svg>'
# Change the attributes of the exported SVG
# We don't need stroke-width - wrong, we do when we have lines with certain width
# We set opacity to maximum
# We set the color to WHITE
root = ET.fromstring(exported_svg)
for child in root:
child.set('fill', '#FFFFFF')
child.set('opacity', '1.0')
child.set('stroke', '#FFFFFF')
# first_svg_elem = 'rect x="' + minx + '" ' + 'y="' + miny_rect + '" '
# first_svg_elem += 'width="' + svgwidth + '" ' + 'height="' + svgheight + '" '
# first_svg_elem += 'fill="#000000" opacity="1.0" stroke-width="0.0"'
first_svg_elem_tag = 'rect'
first_svg_elem_attribs = {
'x': minx,
'y': miny_rect,
'width': svgwidth,
'height': svgheight,
'id': 'neg_rect',
'style': 'fill:#000000;opacity:1.0;stroke-width:0.0'
}
root.insert(0, ET.Element(first_svg_elem_tag, first_svg_elem_attribs))
exported_svg = ET.tostring(root)
svg_elem = svg_header + str(exported_svg) + svg_footer
# Parse the xml through a xml parser just to add line feeds
# and to make it look more pretty for the output
doc = parse_xml_string(svg_elem)
try:
with open(filename, 'w') as fp:
fp.write(doc.toprettyxml())
except PermissionError:
self.inform.emit('[WARNING] %s' %
_("Permission denied, saving not possible.\n"
"Most likely another app is holding the file open and not accessible."))
return 'fail'
if self.defaults["global_open_style"] is False:
self.file_opened.emit("SVG", filename)
self.file_saved.emit("SVG", filename)
self.inform.emit('[success] %s: %s' %
(_("SVG file exported to"), filename))
if use_thread is True:
proc = self.proc_container.new(_("Generating Film ... Please wait."))
def job_thread_film(app_obj):
try:
make_negative_film()
except Exception:
proc.done()
return
proc.done()
self.worker_task.emit({'fcn': job_thread_film, 'params': [self]})
else:
make_negative_film()
def export_svg_positive(self, obj_name, box_name, filename,
scale_stroke_factor=0.00,
scale_factor_x=None, scale_factor_y=None,
skew_factor_x=None, skew_factor_y=None, skew_reference='center',
mirror=None,
use_thread=True):
"""
Exports a Geometry Object to an SVG file in positive black.
:param obj_name: the name of the FlatCAM object to be saved as SVG
:param box_name: the name of the FlatCAM object to be used as delimitation of the content to be saved
:param filename: Path to the SVG file to save to.
:param scale_stroke_factor: factor by which to change/scale the thickness of the features
:param scale_factor_x: factor to scale the svg geometry on the X axis
:param scale_factor_y: factor to scale the svg geometry on the Y axis
:param skew_factor_x: factor to skew the svg geometry on the X axis
:param skew_factor_y: factor to skew the svg geometry on the Y axis
:param skew_reference: reference to use for skew. Can be 'bottomleft', 'bottomright', 'topleft', 'topright' and
those are the 4 points of the bounding box of the geometry to be skewed.
:param mirror: can be 'x' or 'y' or 'both'. Axis on which to mirror the svg geometry
:param use_thread: if to be run in a separate thread; boolean
:return:
"""
self.report_usage("export_svg_positive()")
if filename is None:
filename = self.defaults["global_last_save_folder"]
self.log.debug("export_svg() black")
try:
obj = self.collection.get_by_name(str(obj_name))
except Exception:
# TODO: The return behavior has not been established... should raise exception?
return "Could not retrieve object: %s" % obj_name
try:
box = self.collection.get_by_name(str(box_name))
except Exception:
# TODO: The return behavior has not been established... should raise exception?
return "Could not retrieve object: %s" % box_name
if box is None:
self.inform.emit('[WARNING_NOTCL] %s: %s' %
(_("No object Box. Using instead"), obj))
box = obj
def make_positive_film():
exported_svg = obj.export_svg(scale_stroke_factor=scale_stroke_factor,
scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y,
skew_factor_x=skew_factor_x, skew_factor_y=skew_factor_y,
mirror=mirror
)
self.progress.emit(40)
# Change the attributes of the exported SVG
# We don't need stroke-width
# We set opacity to maximum
# We set the colour to WHITE
root = ET.fromstring(exported_svg)
for child in root:
child.set('fill', str(self.defaults['tools_film_color']))
child.set('opacity', '1.0')
child.set('stroke', str(self.defaults['tools_film_color']))
exported_svg = ET.tostring(root)
# Determine bounding area for svg export
bounds = box.bounds()
size = box.size()
# This contain the measure units
uom = obj.units.lower()
# Define a boundary around SVG of about 1.0mm (~39mils)
if uom in "mm":
boundary = 1.0
else:
boundary = 0.0393701
self.progress.emit(80)
# Convert everything to strings for use in the xml doc
svgwidth = str(size[0] + (2 * boundary))
svgheight = str(size[1] + (2 * boundary))
minx = str(bounds[0] - boundary)
miny = str(bounds[1] + boundary + size[1])
self.log.debug(minx)
self.log.debug(miny)
# Add a SVG Header and footer to the svg output from shapely
# The transform flips the Y Axis so that everything renders
# properly within svg apps such as inkscape
svg_header = '<svg xmlns="http://www.w3.org/2000/svg" ' \
'version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" '
svg_header += 'width="' + svgwidth + uom + '" '
svg_header += 'height="' + svgheight + uom + '" '
svg_header += 'viewBox="' + minx + ' -' + miny + ' ' + svgwidth + ' ' + svgheight + '" '
svg_header += '>'
svg_header += '<g transform="scale(1,-1)">'
svg_footer = '</g> </svg>'
svg_elem = str(svg_header) + str(exported_svg) + str(svg_footer)
self.progress.emit(90)
# Parse the xml through a xml parser just to add line feeds
# and to make it look more pretty for the output
doc = parse_xml_string(svg_elem)
try:
with open(filename, 'w') as fp:
fp.write(doc.toprettyxml())
except PermissionError:
self.inform.emit('[WARNING] %s' %
_("Permission denied, saving not possible.\n"
"Most likely another app is holding the file open and not accessible."))
return 'fail'
self.progress.emit(100)
if self.defaults["global_open_style"] is False:
self.file_opened.emit("SVG", filename)
self.file_saved.emit("SVG", filename)
self.inform.emit('[success] %s: %s' %
(_("SVG file exported to"), filename))
if use_thread is True:
proc = self.proc_container.new(_("Generating Film ... Please wait."))
def job_thread_film(app_obj):
try:
make_positive_film()
except Exception:
proc.done()
return
proc.done()
self.worker_task.emit({'fcn': job_thread_film, 'params': [self]})
else:
make_positive_film()
def save_source_file(self, obj_name, filename, use_thread=True):
"""
Exports a FlatCAM Object to an Gerber/Excellon file.