This repository has been archived on 2023-03-25. You can view files and clone it, but cannot push or open issues or pull requests.
mightyscape-1.1-deprecated/extensions/fablabchemnitz/vpypetools/vpypetools.py

452 lines
28 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
import logging
logger = logging.getLogger()
logger.setLevel(level=logging.ERROR) #we set this to error before importing vpype to ignore the nasty output "WARNING:root:!!! `vpype.Length` is deprecated, use `vpype.LengthType` instead."
import sys
import os
from lxml import etree
import inkex
2021-04-02 23:07:28 +02:00
from inkex import transforms, bezier
from inkex.paths import CubicSuperPath
from inkex.command import inkscape
import vpype
import vpype_viewer
from vpype_viewer import ViewMode
from vpype_cli import execute
logger = logging.getLogger()
logger.setLevel(level=logging.WARNING) #after importing vpype we enabled logging again
import warnings # we import this to suppress moderngl warnings from vpype_viewer
2021-04-06 12:48:54 +02:00
from shapely.geometry import LineString, Point
"""
Extension for InkScape 1.X
Author: Mario Voigt / FabLab Chemnitz
Mail: mario.voigt@stadtfabrikanten.org
2021-04-02 23:07:28 +02:00
Date: 02.04.2021
Last patch: 08.04.2021
License: GNU GPL v3
2021-04-04 15:43:09 +02:00
This piece of spaghetti-code, called "vpypetools", is a wrapper to pass (pipe) line elements from InkScape selection (or complete canvas) to vpype.
It allows to run basic commands on the geometry. The converted lines are getting pushed back into InkScape.
vpypetools allows to enable some important adjusters and debugging settings to get the best out of it.
vpypetools is based on
- Aaron Spike's "Flatten Bezier" extension, licensed by GPL v2
- Mark Riedesel's "Apply Transform" extension (https://github.com/Klowner/inkscape-applytransforms), licensed by GPL v2
- a lot of other extensions to rip off the required code pieces ;-)
- used (tested) version of vpype: commit id https://github.com/abey79/vpype/commit/0b0dc8dd7e32998dbef639f9db578c3bff02690b (29.03.2021)
- used (tested) version of vpype occult: commit id https://github.com/LoicGoulefert/occult/commit/2d04ca57d69078755c340066c226fd6cd927d41e (04.02.2021)
CLI / API docs:
- https://vpype.readthedocs.io/en/stable/api/vpype_cli.html#module-vpype_cli
- https://vpype.readthedocs.io/en/stable/api/vpype.html#module-vpype
Todo's
2021-04-21 11:57:39 +02:00
- find some python code to auto-convert strokes and objects to paths (for input and for output again)
- remove fill property of converted lines (because there is no fill anymore) without crashing Inkscape ...
"""
class vpypetools (inkex.EffectExtension):
2021-04-16 14:41:12 +02:00
def add_arguments(self, pars):
2021-04-02 23:07:28 +02:00
# Line Sorting
2021-04-16 14:41:12 +02:00
pars.add_argument("--linesort", type=inkex.Boolean, default=False)
pars.add_argument("--linesort_no_flip", type=inkex.Boolean, default=False, help="Disable reversing stroke direction for optimization")
2021-04-02 23:07:28 +02:00
# Line Merging
2021-04-16 14:41:12 +02:00
pars.add_argument("--linemerge", type=inkex.Boolean, default=False)
pars.add_argument("--linemerge_tolerance", type=float, default=0.500, help="Maximum distance between two line endings that should be merged (default 0.500 mm)")
pars.add_argument("--linemerge_no_flip", type=inkex.Boolean, default=False, help="Disable reversing stroke direction for merging")
2021-04-03 18:34:01 +02:00
# Trimming
2021-04-16 14:41:12 +02:00
pars.add_argument("--trim", type=inkex.Boolean, default=False)
pars.add_argument("--trim_x_margin", type=float, default=0.000, help="trim margin - x direction (mm)") # keep default at 0.000 to keep clean bbox
pars.add_argument("--trim_y_margin", type=float, default=0.000, help="trim margin - y direction (mm)") # keep default at 0.000 to keep clean bbox
2021-04-03 18:34:01 +02:00
# Relooping
2021-04-16 14:41:12 +02:00
pars.add_argument("--reloop", type=inkex.Boolean, default=False)
pars.add_argument("--reloop_tolerance", type=float, default=0.500, help="Controls how close the path beginning and end must be to consider it closed (default 0.500 mm)")
2021-04-03 18:34:01 +02:00
# Multipass
2021-04-16 14:41:12 +02:00
pars.add_argument("--multipass", type=inkex.Boolean, default=False)
pars.add_argument("--multipass_count", type=int, default=2, help="How many passes for each line (default 2)")
2021-04-03 18:34:01 +02:00
# Filter
2021-04-16 14:41:12 +02:00
pars.add_argument("--filter", type=inkex.Boolean, default=False)
pars.add_argument("--filter_tolerance", type=float, default=0.050, help="Tolerance used to determined if a line is closed or not (default 0.050 mm)")
pars.add_argument("--filter_closed", type=inkex.Boolean, default=False, help="Keep closed lines")
pars.add_argument("--filter_not_closed", type=inkex.Boolean, default=False, help="Keep open lines")
pars.add_argument("--filter_min_length_enabled", type=inkex.Boolean, default=False, help="filter by min length")
pars.add_argument("--filter_min_length", type=float, default=0.000, help="Keep lines whose length isn't shorter than value")
pars.add_argument("--filter_max_length_enabled", type=inkex.Boolean, default=False, help="filter by max length")
pars.add_argument("--filter_max_length", type=float, default=0.000, help="Keep lines whose length isn't greater than value")
2021-04-03 18:34:01 +02:00
# Split All
2021-04-16 14:41:12 +02:00
pars.add_argument("--splitall", type=inkex.Boolean, default=False)
2021-04-03 18:34:01 +02:00
# Plugin Occult
2021-04-16 14:41:12 +02:00
pars.add_argument("--plugin_occult", type=inkex.Boolean, default=False)
pars.add_argument("--plugin_occult_tolerance", type=float, default=0.01, help="Max distance between start and end point to consider a path closed (default 0.01 mm)")
pars.add_argument("--plugin_occult_keepseparatelayer", type=inkex.Boolean, default=False, help="Put occulted lines to separate layer")
2021-04-04 16:31:48 +02:00
# Free Mode
2021-04-16 14:41:12 +02:00
pars.add_argument("--tab")
pars.add_argument("--freemode", type=inkex.Boolean, default=False)
pars.add_argument("--freemode_cmd1", default="")
pars.add_argument("--freemode_cmd1_enabled", type=inkex.Boolean, default=True)
pars.add_argument("--freemode_cmd2", default="")
pars.add_argument("--freemode_cmd2_enabled", type=inkex.Boolean, default=False)
pars.add_argument("--freemode_cmd3", default="")
pars.add_argument("--freemode_cmd3_enabled", type=inkex.Boolean, default=False)
pars.add_argument("--freemode_cmd4", default="")
pars.add_argument("--freemode_cmd4_enabled", type=inkex.Boolean, default=False)
pars.add_argument("--freemode_cmd5", default="")
pars.add_argument("--freemode_cmd5_enabled", type=inkex.Boolean, default=False)
pars.add_argument("--freemode_show_cmd", type=inkex.Boolean, default=False)
2021-04-03 18:34:01 +02:00
2021-04-02 23:07:28 +02:00
# General Settings
2021-04-16 14:41:12 +02:00
pars.add_argument("--input_handling", default="paths", help="Input handling")
pars.add_argument("--flattenbezier", type=inkex.Boolean, default=False, help="Flatten bezier curves to polylines")
pars.add_argument("--flatness", type=float, default=0.1, help="Minimum flatness = 0.1. The smaller the value the more fine segments you will get (quantization).")
pars.add_argument("--decimals", type=int, default=3, help="Accuracy for imported lines' coordinates into vpype. Does not work for 'Multilayer/document'")
pars.add_argument("--simplify", type=inkex.Boolean, default=False, help="Reduces significantly the number of segments used to approximate the curve while still guaranteeing an accurate conversion, but may increase the execution time. Does not work for 'Singlelayer/paths'")
pars.add_argument("--parallel", type=inkex.Boolean, default=False, help="Enables multiprocessing for the SVG conversion. This is recommended ONLY when using 'Simplify geometry' on large SVG files with many curved elements. Does not work for 'Singlelayer/paths'")
pars.add_argument("--apply_transformations", type=inkex.Boolean, default=False, help="Run 'Apply Transformations' extension before running vpype. Helps avoiding geometry shifting")
pars.add_argument("--output_show", type=inkex.Boolean, default=False, help="This will open a separate window showing the finished SVG data. If enabled, output is not applied to InkScape canvas (only for preview)!")
pars.add_argument("--output_show_points", type=inkex.Boolean, default=False, help="Enable point display in viewer")
pars.add_argument("--output_stats", type=inkex.Boolean, default=False, help="Show output statistics before/after conversion")
pars.add_argument("--output_trajectories", type=inkex.Boolean, default=False, help="Add paths for the travel trajectories")
pars.add_argument("--keep_objects", type=inkex.Boolean, default=False, help="If false, selected paths will be removed")
pars.add_argument("--strokes_to_paths", type=inkex.Boolean, default=True, help="Recommended option. Performs 'Path' > 'Stroke to Path' (CTRL + ALT + C) to convert vpype converted lines back to regular path objects")
pars.add_argument("--use_style_of_first_element", type=inkex.Boolean, default=True, help="If enabled the first element in selection is scanned and we apply it's style to all imported vpype lines (but not for trajectories). Does not work for 'Multilayer/document'")
pars.add_argument("--lines_stroke_width", type=float, default=1.0, help="Stroke width of tooling lines (px). Gets overwritten if 'Use style of first selected element' is enabled")
pars.add_argument("--trajectories_stroke_width", type=float, default=1.0, help="Stroke width of trajectory lines (px). Gets overwritten if 'Use style of first selected element' is enabled")
2021-04-15 17:04:18 +02:00
def effect(self):
lc = vpype.LineCollection() # create a new array of LineStrings consisting of Points. We convert selected paths to polylines and grab their points
2021-04-21 11:57:39 +02:00
elementsToWork = [] # we make an array of all collected nodes to get the boundingbox of that array. We need it to place the vpype converted stuff to the correct XY coordinates
applyTransformAvailable = False
# at first we apply external extension
try:
sys.path.append("..") # add parent directory to path to allow importing applytransform (vpype extension is encapsulated in sub directory)
import applytransform
applyTransformAvailable = True
except Exception as e:
# inkex.utils.debug(e)
inkex.utils.debug("Calling 'Apply Transformations' extension failed. Maybe the extension is not installed. You can download it from official InkScape Gallery. Skipping this step")
2021-04-02 23:07:28 +02:00
def flatten(node):
path = node.path.to_superpath()
bezier.cspsubdiv(path, self.options.flatness)
newpath = []
for subpath in path:
first = True
for csp in subpath:
cmd = 'L'
if first:
cmd = 'M'
first = False
newpath.append([cmd, [csp[1][0], csp[1][1]]])
node.path = newpath
# flatten the node's path to linearize, split up the path to it's subpaths (break apart) and add all points to the vpype lines collection
def convertPath(node, nodes = None):
if nodes is None:
nodes = []
if node.tag == inkex.addNS('path','svg'):
nodes.append(node)
2021-04-02 23:07:28 +02:00
if self.options.flattenbezier is True:
flatten(node)
raw = node.path.to_arrays()
subPaths, prev = [], 0
for i in range(len(raw)): # Breaks compound paths into simple paths
if raw[i][0] == 'M' and i != 0:
subPaths.append(raw[prev:i])
prev = i
subPaths.append(raw[prev:])
for subPath in subPaths:
points = []
for csp in subPath:
2021-04-21 11:57:39 +02:00
if len(csp[1]) > 0: #we need exactly two points per straight line segment
points.append(Point(round(csp[1][0], self.options.decimals), round(csp[1][1], self.options.decimals)))
if subPath[-1][0] == 'Z' or subPath[0][1] == subPath[-1][1]: #check if path is closed by Z or first pont == last point
points.append(Point(round(subPath[0][1][0], self.options.decimals), round(subPath[0][1][1], self.options.decimals))) #if closed, we add the first point again
lc.append(LineString(points))
children = node.getchildren()
if children is not None:
for child in children:
convertPath(child, nodes)
return nodes
doc = None #create a vpype document
2021-04-06 12:48:54 +02:00
'''
if 'paths' we process paths only. Objects like rectangles or strokes like polygon have to be converted before accessing them
if 'layers' we can process all layers in the complete document
'''
if self.options.input_handling == "paths":
# getting the bounding box of the current selection. We use to calculate the offset XY from top-left corner of the canvas. This helps us placing back the elements
input_bbox = None
2021-04-06 12:48:54 +02:00
if self.options.apply_transformations is True and applyTransformAvailable is True:
'''
we need to apply transfoms to the complete document even if there are only some single paths selected.
If we apply it to selected nodes only the parent groups still might contain transforms.
This messes with the coordinates and creates hardly controllable behaviour
'''
applytransform.ApplyTransform().recursiveFuseTransform(self.document.getroot())
if len(self.svg.selected) == 0:
2021-04-21 11:57:39 +02:00
elementsToWork = convertPath(self.document.getroot())
for element in elementsToWork:
input_bbox += element.bounding_box()
else:
2021-04-21 11:57:39 +02:00
elementsToWork = None
for element in self.svg.selected.values():
elementsToWork = convertPath(element, elementsToWork)
2021-04-06 12:48:54 +02:00
#input_bbox = inkex.elements._selected.ElementList.bounding_box(self.svg.selected) # get BoundingBox for selection
input_bbox = self.svg.selection.bounding_box() # get BoundingBox for selection
if len(lc) == 0:
inkex.errormsg('Selection appears to be empty or does not contain any valid svg:path nodes. Try to cast your objects to paths using CTRL + SHIFT + C or strokes to paths using CTRL + ALT+ C')
return
# find the first object in selection which has a style attribute (skips groups and other things which have no style)
firstElementStyle = None
2021-04-21 11:57:39 +02:00
for element in elementsToWork:
if element.attrib.has_key('style'):
firstElementStyle = element.get('style')
doc = vpype.Document(page_size=(input_bbox.width + input_bbox.left, input_bbox.height + input_bbox.top)) #create new vpype document
doc.add(lc, layer_id=None) # we add the lineCollection (converted selection) to the vpype document
elif self.options.input_handling == "layers":
doc = vpype.read_multilayer_svg(self.options.input_file, quantization = self.options.flatness, crop = False, simplify = self.options.simplify, parallel = self.options.parallel, default_width = self.document.getroot().get('width'), default_height = self.document.getroot().get('height'))
2021-04-06 12:48:54 +02:00
2021-04-21 11:57:39 +02:00
for element in self.document.getroot().xpath("//svg:g", namespaces=inkex.NSS): #all groups/layers
elementsToWork.append(element)
2021-04-03 18:34:01 +02:00
tooling_length_before = doc.length()
traveling_length_before = doc.pen_up_length()
# build and execute the conversion command
2021-04-03 18:34:01 +02:00
# the following code block is not intended to sum up the commands to build a series (pipe) of commands!
2021-04-02 23:07:28 +02:00
##########################################
# Line Sorting
2021-04-02 23:07:28 +02:00
if self.options.linesort is True:
command = "linesort "
if self.options.linesort_no_flip is True:
command += " --no-flip"
# Line Merging
if self.options.linemerge is True:
command = "linemerge --tolerance " + str(self.options.linemerge_tolerance)
if self.options.linemerge_no_flip is True:
2021-04-03 18:34:01 +02:00
command += " --no-flip"
# Trimming
if self.options.trim is True:
command = "trim " + str(self.options.trim_x_margin) + " " + str(self.options.trim_y_margin)
# Relooping
if self.options.reloop is True:
command = "reloop --tolerance " + str(self.options.reloop_tolerance)
# Multipass
if self.options.multipass is True:
command = "multipass --count " + str(self.options.multipass_count)
# Filter
if self.options.filter is True:
command = "filter --tolerance " + str(self.options.filter_tolerance)
if self.options.filter_min_length_enabled is True:
command += " --min-length " + str(self.options.filter_min_length)
if self.options.filter_max_length_enabled is True:
command += " --max-length " + str(self.options.filter_max_length)
if self.options.filter_closed is True and self.options.filter_not_closed is False:
2021-04-03 18:34:01 +02:00
command += " --closed"
if self.options.filter_not_closed is True and self.options.filter_closed is False:
2021-04-03 18:34:01 +02:00
command += " --not-closed"
2021-04-04 16:31:48 +02:00
if self.options.filter_closed is False and \
self.options.filter_not_closed is False and \
self.options.filter_min_length_enabled is False and \
self.options.filter_max_length_enabled is False:
2021-04-03 18:34:01 +02:00
inkex.errormsg('No filters to apply. Please select at least one filter.')
return
# Plugin Occult
if self.options.plugin_occult is True:
command = "occult --tolerance " + str(self.options.plugin_occult_tolerance)
if self.options.plugin_occult_keepseparatelayer is True:
command += " --keep-occulted"
2021-04-03 18:34:01 +02:00
# Split All
if self.options.splitall is True:
command = " splitall"
2021-04-04 16:31:48 +02:00
# Free Mode
if self.options.freemode is True:
command = ""
if self.options.freemode_cmd1_enabled is True:
command += " " + self.options.freemode_cmd1.strip()
if self.options.freemode_cmd2_enabled is True:
command += " " + self.options.freemode_cmd2.strip()
if self.options.freemode_cmd3_enabled is True:
command += " " + self.options.freemode_cmd3.strip()
if self.options.freemode_cmd4_enabled is True:
command += " " + self.options.freemode_cmd4.strip()
if self.options.freemode_cmd5_enabled is True:
command += " " + self.options.freemode_cmd5.strip()
if self.options.freemode_cmd1_enabled is False and \
self.options.freemode_cmd2_enabled is False and \
self.options.freemode_cmd3_enabled is False and \
self.options.freemode_cmd4_enabled is False and \
self.options.freemode_cmd5_enabled is False:
2021-04-06 12:48:54 +02:00
inkex.utils.debug("Warning: empty vpype pipeline. With this you are just getting read-write layerset/lineset.")
2021-04-04 16:31:48 +02:00
else:
if self.options.freemode_show_cmd is True:
inkex.utils.debug("Your command pipe will be the following:")
inkex.utils.debug(command)
2021-04-04 16:31:48 +02:00
2021-04-03 18:34:01 +02:00
# inkex.utils.debug(command)
2021-04-04 16:31:48 +02:00
try:
doc = execute(command, doc)
except Exception as e:
inkex.utils.debug("Error in vpype:" + str(e))
return
2021-04-02 23:07:28 +02:00
##########################################
2021-04-03 18:34:01 +02:00
tooling_length_after = doc.length()
traveling_length_after = doc.pen_up_length()
if tooling_length_before > 0:
tooling_length_saving = (1.0 - tooling_length_after / tooling_length_before) * 100.0
else:
tooling_length_saving = 0.0
if traveling_length_before > 0:
traveling_length_saving = (1.0 - traveling_length_after / traveling_length_before) * 100.0
else:
traveling_length_saving = 0.0
if self.options.output_stats is True:
inkex.utils.debug('Total tooling length before vpype conversion: ' + str('{:0.2f}'.format(tooling_length_before)) + ' mm')
inkex.utils.debug('Total traveling length before vpype conversion: ' + str('{:0.2f}'.format(traveling_length_before)) + ' mm')
inkex.utils.debug('Total tooling length after vpype conversion: ' + str('{:0.2f}'.format(tooling_length_after)) + ' mm')
inkex.utils.debug('Total traveling length after vpype conversion: ' + str('{:0.2f}'.format(traveling_length_after)) + ' mm')
inkex.utils.debug('Total tooling length optimized: ' + str('{:0.2f}'.format(tooling_length_saving)) + ' %')
inkex.utils.debug('Total traveling length optimized: ' + str('{:0.2f}'.format(traveling_length_saving)) + ' %')
2021-04-03 18:34:01 +02:00
if tooling_length_after == 0:
inkex.errormsg('No lines left after vpype conversion. Conversion result is empty. Cannot continue')
return
# show the vpype document visually
if self.options.output_show:
warnings.filterwarnings("ignore") # workaround to suppress annoying DeprecationWarning
2021-04-09 10:22:11 +02:00
# vpype_viewer.show(doc, view_mode=ViewMode.PREVIEW, show_pen_up=self.options.output_trajectories, show_points=self.options.output_show_points, pen_width=0.1, pen_opacity=1.0, argv=None)
vpype_viewer.show(doc, view_mode=ViewMode.PREVIEW, show_pen_up=self.options.output_trajectories, show_points=self.options.output_show_points, argv=None) # https://vpype.readthedocs.io/en/stable/api/vpype_viewer.ViewMode.html
warnings.filterwarnings("default") # reset warning filter
2021-04-06 12:48:54 +02:00
exit(0) #we leave the code loop because we only want to preview. We don't want to import the geometry
# save the vpype document to new svg file and close it afterwards
output_file = self.options.input_file + ".vpype.svg"
output_fileIO = open(output_file, "w", encoding="utf-8")
#vpype.write_svg(output_fileIO, doc, page_size=None, center=False, source_string='', layer_label_format='%d', show_pen_up=self.options.output_trajectories, color_mode='layer', single_path = True)
vpype.write_svg(output_fileIO, doc, page_size=None, center=False, source_string='', layer_label_format='%d', show_pen_up=self.options.output_trajectories, color_mode='layer')
#vpype.write_svg(output_fileIO, doc, page_size=(self.svg.unittouu(self.document.getroot().get('width')), self.svg.unittouu(self.document.getroot().get('height'))), center=False, source_string='', layer_label_format='%d', show_pen_up=self.options.output_trajectories, color_mode='layer')
output_fileIO.close()
# convert vpype polylines/lines/polygons to regular paths again. We need to use "--with-gui" to respond to "WARNING: ignoring verb FileSave - GUI required for this verb."
2021-04-02 23:07:28 +02:00
if self.options.strokes_to_paths is True:
cli_output = inkscape(output_file, "--with-gui", actions="EditSelectAllInAllLayers;EditUnlinkClone;ObjectToPath;FileSave;FileQuit")
if len(cli_output) > 0:
self.debug(_("Inkscape returned the following output when trying to run the vpype object to path back-conversion:"))
self.debug(cli_output)
2021-04-21 11:57:39 +02:00
# this does not work because line, polyline and polygon have no base class to execute replace_with
#if self.options.strokes_to_paths is True:
# for lineLayer in lineLayers:
# for element in lineLayer:
# element.replace_with(element.to_path_element())
2021-04-06 12:48:54 +02:00
# parse the SVG file
try:
stream = open(output_file, 'r')
except FileNotFoundError as e:
inkex.utils.debug("There was no SVG output generated by vpype. Cannot continue")
exit(1)
p = etree.XMLParser(huge_tree=True)
import_doc = etree.parse(stream, parser=etree.XMLParser(huge_tree=True))
stream.close()
# handle pen_up trajectories (travel lines)
trajectoriesLayer = import_doc.getroot().xpath("//svg:g[@id='pen_up_trajectories']", namespaces=inkex.NSS)
if self.options.output_trajectories is True:
if len(trajectoriesLayer) > 0:
2021-04-07 00:44:26 +02:00
trajectoriesLayer[0].set('style', 'stroke:#0000ff;stroke-width:{:0.2f}px;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:none'.format(self.options.trajectories_stroke_width))
2021-04-06 12:48:54 +02:00
trajectoriesLayer[0].attrib.pop('stroke') # remove unneccesary stroke attribute
trajectoriesLayer[0].attrib.pop('fill') # remove unneccesary fill attribute
else:
if len(trajectoriesLayer) > 0:
2021-04-18 18:21:23 +02:00
trajectoriesLayer[0].delete()
lineLayers = import_doc.getroot().xpath("//svg:g[not(@id='pen_up_trajectories')]", namespaces=inkex.NSS) #all layer except the pen_up trajectories layer
2021-04-06 12:48:54 +02:00
if self.options.use_style_of_first_element is True and self.options.input_handling == "paths" and firstElementStyle is not None:
2021-04-21 11:57:39 +02:00
# if we remove the fill property and use "Use style of first element in layer" the conversion will just crash with an unknown reason
#declarations = firstElementStyle.split(';')
#for i, decl in enumerate(declarations):
# parts = decl.split(':', 2)
# if len(parts) == 2:
# (prop, val) = parts
# prop = prop.strip().lower()
# #if prop == 'fill':
# # declarations[i] = prop + ':none'
for lineLayer in lineLayers:
2021-04-21 11:57:39 +02:00
#lineLayer.set('style', ';'.join(declarations))
lineLayer.set('style', firstElementStyle)
2021-04-06 12:48:54 +02:00
lineLayer.attrib.pop('stroke') # remove unneccesary stroke attribute
lineLayer.attrib.pop('fill') # remove unneccesary fill attribute
2021-04-04 15:43:09 +02:00
else:
2021-04-06 12:48:54 +02:00
for lineLayer in lineLayers:
if lineLayer.attrib.has_key('stroke'):
color = lineLayer.get('stroke')
2021-04-07 00:44:26 +02:00
lineLayer.set('style', 'stroke:' + color + ';stroke-width:{:0.2f}px;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:none'.format(self.options.lines_stroke_width))
2021-04-06 12:48:54 +02:00
lineLayer.attrib.pop('stroke') # remove unneccesary stroke attribute
lineLayer.attrib.pop('fill') # remove unneccesary fill attribute
import_viewBox = import_doc.getroot().get('viewBox').split(" ")
2021-04-22 21:25:07 +02:00
self_viewBox = self.document.getroot().get('viewBox')
if self_viewBox is not None: #some SVG files do not have this attribute
self_viewBoxValues = self_viewBox.split(" ")
scaleX = self.svg.unittouu(self_viewBoxValues[2]) / self.svg.unittouu(import_viewBox[2])
scaleY = self.svg.unittouu(self_viewBoxValues[3]) / self.svg.unittouu(import_viewBox[3])
for element in import_doc.getroot().iter("{http://www.w3.org/2000/svg}g"):
self.document.getroot().append(element)
2021-04-06 12:48:54 +02:00
if self.options.input_handling == "layers":
2021-04-22 21:25:07 +02:00
if self_viewBox is not None:
element.set('transform', 'scale(' + str(scaleX) + ',' + str(scaleY) + ')') #imported groups need to be transformed. Or they have wrong size. Reason: different viewBox sizes/units in namedview definitions
2021-04-06 12:48:54 +02:00
if self.options.apply_transformations is True and applyTransformAvailable is True: #we apply the transforms directly after adding them
applytransform.ApplyTransform().recursiveFuseTransform(element)
# Delete the temporary file again because we do not need it anymore
if os.path.exists(output_file):
os.remove(output_file)
# Remove selection objects to do a real replace with new objects from vpype document
if self.options.keep_objects is False:
2021-04-21 11:57:39 +02:00
for element in elementsToWork:
element.delete()
if __name__ == '__main__':
2021-04-02 23:07:28 +02:00
vpypetools().run()