Patched Styles To Layers extension
This commit is contained in:
parent
2807b7b688
commit
b9f7cc314c
@ -20,8 +20,11 @@
|
|||||||
<param name="decimals" type="int" min="0" max="10" gui-text="Decimal tolerance" gui-description="The more decimals the more distinct layers you will get. This only applies for the sub layers (threshold > 1)">1</param>
|
<param name="decimals" type="int" min="0" max="10" gui-text="Decimal tolerance" gui-description="The more decimals the more distinct layers you will get. This only applies for the sub layers (threshold > 1)">1</param>
|
||||||
<param name="apply_transformations" type="bool" gui-text="Apply transformations (requires separate extension)" gui-description="This will call the extension 'Apply Transformations'. Helps avoiding geometry shifting">false</param>
|
<param name="apply_transformations" type="bool" gui-text="Apply transformations (requires separate extension)" gui-description="This will call the extension 'Apply Transformations'. Helps avoiding geometry shifting">false</param>
|
||||||
<param name="cleanup" type="bool" gui-text="Cleanup all unused groups/layers (requires separate extension)" gui-description="This will call the extension 'Remove Empty Groups' if available">true</param>
|
<param name="cleanup" type="bool" gui-text="Cleanup all unused groups/layers (requires separate extension)" gui-description="This will call the extension 'Remove Empty Groups' if available">true</param>
|
||||||
|
<param name="put_unfiltered" type="bool" gui-text="Put unfiltered elements to a separate layer">false</param>
|
||||||
<label>This extension will re-layer your selected items or the whole document according to their style (stroke or fill)</label>
|
<param name="show_info" type="bool" gui-text="Show elements which have no style attributes to filter">false</param>
|
||||||
|
<spacer/>
|
||||||
|
<label>This extension will re-layer your selected items or the whole document according to their style values (stroke or fill).</label>
|
||||||
|
<label>The filtering applies only to style attribute of the elements. It does not filter for stroke or fill if they are set separately. You can use the separate 'Cleanup Styles' extension to migrate these separated attributes into style attribute.</label>
|
||||||
<label>Tinkered by Mario Voigt / Stadtfabrikanten e.V. (2020)</label>
|
<label>Tinkered by Mario Voigt / Stadtfabrikanten e.V. (2020)</label>
|
||||||
<label appearance="url">https://fablabchemnitz.de</label>
|
<label appearance="url">https://fablabchemnitz.de</label>
|
||||||
<effect>
|
<effect>
|
||||||
|
@ -8,7 +8,7 @@ Features
|
|||||||
Author: Mario Voigt / FabLab Chemnitz
|
Author: Mario Voigt / FabLab Chemnitz
|
||||||
Mail: mario.voigt@stadtfabrikanten.org
|
Mail: mario.voigt@stadtfabrikanten.org
|
||||||
Date: 19.08.2020
|
Date: 19.08.2020
|
||||||
Last patch: 05.04.2021
|
Last patch: 11.04.2021
|
||||||
License: GNU GPL v3
|
License: GNU GPL v3
|
||||||
"""
|
"""
|
||||||
import inkex
|
import inkex
|
||||||
@ -40,23 +40,17 @@ class StylesToLayers(inkex.EffectExtension):
|
|||||||
return layer
|
return layer
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
inkex.Effect.__init__(self)
|
inkex.EffectExtension.__init__(self)
|
||||||
self.arg_parser.add_argument("--apply_transformations", type=inkex.Boolean, default=False, help="Run 'Apply Transformations' extension before running vpype. Helps avoiding geometry shifting")
|
self.arg_parser.add_argument("--apply_transformations", type=inkex.Boolean, default=False, help="Run 'Apply Transformations' extension before running vpype. Helps avoiding geometry shifting")
|
||||||
self.arg_parser.add_argument("--separateby", default = "stroke", help = "Separate by")
|
self.arg_parser.add_argument("--separateby", default = "stroke", help = "Separate by")
|
||||||
self.arg_parser.add_argument("--parsecolors",default = "hexval", help = "Sort colors by")
|
self.arg_parser.add_argument("--parsecolors",default = "hexval", help = "Sort colors by")
|
||||||
self.arg_parser.add_argument("--subdividethreshold", type=int, default = 1, help = "Threshold for splitting into sub layers")
|
self.arg_parser.add_argument("--subdividethreshold", type=int, default = 1, help = "Threshold for splitting into sub layers")
|
||||||
self.arg_parser.add_argument("--decimals", type=int, default = 1, help = "Decimal tolerance")
|
self.arg_parser.add_argument("--decimals", type=int, default = 1, help = "Decimal tolerance")
|
||||||
self.arg_parser.add_argument("--cleanup", type=inkex.Boolean, default = True, help = "Decimal tolerance")
|
self.arg_parser.add_argument("--cleanup", type=inkex.Boolean, default = True, help = "Decimal tolerance")
|
||||||
|
self.arg_parser.add_argument("--put_unfiltered", type=inkex.Boolean, default = False, help = "Put unfiltered elements to a separate layer")
|
||||||
|
self.arg_parser.add_argument("--show_info", type=inkex.Boolean, default = False, help = "Show elements which have no style attributes to filter")
|
||||||
|
|
||||||
def effect(self):
|
def effect(self):
|
||||||
applyTransformAvailable = False # at first we apply external extension
|
|
||||||
try:
|
|
||||||
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 ...")
|
|
||||||
|
|
||||||
|
|
||||||
def colorsort(stroke_value): #this function applies to stroke or fill (hex colors)
|
def colorsort(stroke_value): #this function applies to stroke or fill (hex colors)
|
||||||
if self.options.parsecolors == "hexval":
|
if self.options.parsecolors == "hexval":
|
||||||
@ -69,6 +63,14 @@ class StylesToLayers(inkex.EffectExtension):
|
|||||||
return float(Color(stroke_value).to_hsl()[2])
|
return float(Color(stroke_value).to_hsl()[2])
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
applyTransformAvailable = False # at first we apply external extension
|
||||||
|
try:
|
||||||
|
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 ...")
|
||||||
|
|
||||||
layer_name = None
|
layer_name = None
|
||||||
layerNodeList = [] #list with layer, neutral_value, element and self.options.separateby type
|
layerNodeList = [] #list with layer, neutral_value, element and self.options.separateby type
|
||||||
selected = [] #list of items to parse
|
selected = [] #list of items to parse
|
||||||
@ -80,107 +82,123 @@ class StylesToLayers(inkex.EffectExtension):
|
|||||||
selected = self.svg.selected.values()
|
selected = self.svg.selected.values()
|
||||||
|
|
||||||
for element in selected:
|
for element in selected:
|
||||||
|
|
||||||
|
# additional option to apply transformations. As we clear up some groups to form new layers, we might lose translations, rotations, etc.
|
||||||
if self.options.apply_transformations is True and applyTransformAvailable is True:
|
if self.options.apply_transformations is True and applyTransformAvailable is True:
|
||||||
applytransform.ApplyTransform().recursiveFuseTransform(element)
|
applytransform.ApplyTransform().recursiveFuseTransform(element)
|
||||||
style = element.get('style')
|
|
||||||
if style is not None:
|
|
||||||
#if no style attributes or stroke/fill are set as extra attribute
|
|
||||||
stroke = element.get('stroke')
|
|
||||||
stroke_width = element.get('stroke-width')
|
|
||||||
stroke_opacity = element.get('stroke-opacity')
|
|
||||||
fill = element.get('fill')
|
|
||||||
fill_opacity = element.get('fill-opacity')
|
|
||||||
|
|
||||||
# possible values for fill are #HEXCOLOR (like #000000), color name (like purple, black, red) or gradients (URLs)
|
if isinstance(element, inkex.ShapeElement): # Elements which have a visible representation on the canvas (even without a style attribute but by their type); if we do not use that ifInstance Filter we provokate unkown InkScape fatal crashes
|
||||||
|
|
||||||
neutral_value = None #we will use this value to slice the filter result into sub layers (threshold)
|
style = element.get('style')
|
||||||
|
if style is not None:
|
||||||
|
#if no style attributes or stroke/fill are set as extra attribute
|
||||||
|
stroke = element.get('stroke')
|
||||||
|
stroke_width = element.get('stroke-width')
|
||||||
|
stroke_opacity = element.get('stroke-opacity')
|
||||||
|
fill = element.get('fill')
|
||||||
|
fill_opacity = element.get('fill-opacity')
|
||||||
|
|
||||||
if fill is not None:
|
# possible values for fill are #HEXCOLOR (like #000000), color name (like purple, black, red) or gradients (URLs)
|
||||||
style = 'fill:'+ fill + ";"
|
|
||||||
if stroke is not None:
|
|
||||||
style = style + 'stroke:' + stroke + ";"
|
|
||||||
|
|
||||||
#we don't want to destroy elements with gradients (they contain svg:stop elements which have a style too) and we don't want to mess with tspans (text)
|
neutral_value = None #we will use this value to slice the filter result into sub layers (threshold)
|
||||||
#the Styles to Layers extension still might brick the gradients (some tests failed)
|
|
||||||
if style and element.tag != inkex.addNS('stop','svg') and element.tag != inkex.addNS('tspan','svg'):
|
if fill is not None:
|
||||||
|
style = 'fill:'+ fill + ";"
|
||||||
|
if stroke is not None:
|
||||||
|
style = style + 'stroke:' + stroke + ";"
|
||||||
|
|
||||||
|
#we don't want to destroy elements with gradients (they contain svg:stop elements which have a style too) and we don't want to mess with tspans (text)
|
||||||
|
#the Styles to Layers extension still might brick the gradients (some tests failed)
|
||||||
|
if style and element.tag != inkex.addNS('stop','svg') and element.tag != inkex.addNS('tspan','svg'):
|
||||||
|
|
||||||
|
if self.options.separateby == "stroke":
|
||||||
|
stroke = re.search('(;|^)stroke:(.*?)(;|$)', style)
|
||||||
|
if stroke is not None:
|
||||||
|
stroke = stroke[0]
|
||||||
|
stroke_value = stroke.split("stroke:")[1].split(";")[0]
|
||||||
|
if stroke_value != "none":
|
||||||
|
stroke_converted = str(Color(stroke_value).to_rgb()) #the color can be hex code or clear name. we handle both the same
|
||||||
|
neutral_value = colorsort(stroke_converted)
|
||||||
|
layer_name = "stroke-" + self.options.parsecolors + "-" + stroke_converted
|
||||||
|
else:
|
||||||
|
layer_name = "stroke-" + self.options.parsecolors + "-none"
|
||||||
|
|
||||||
|
elif self.options.separateby == "stroke_width":
|
||||||
|
stroke_width = re.search('stroke-width:(.*?)(;|$)', style)
|
||||||
|
if stroke_width is not None:
|
||||||
|
stroke_width = stroke_width[0]
|
||||||
|
neutral_value = self.svg.unittouu(stroke_width.split("stroke-width:")[1].split(";")[0])
|
||||||
|
layer_name = stroke_width
|
||||||
|
else:
|
||||||
|
layer_name = "stroke-width-none"
|
||||||
|
|
||||||
|
elif self.options.separateby == "stroke_hairline":
|
||||||
|
stroke_hairline = re.search('-inkscape-stroke:hairline(;|$)', style)
|
||||||
|
if stroke_hairline is not None:
|
||||||
|
neutral_value = 1
|
||||||
|
layer_name = "stroke-hairline-yes"
|
||||||
|
else:
|
||||||
|
neutral_value = 0
|
||||||
|
layer_name = "stroke-hairline-no"
|
||||||
|
|
||||||
|
elif self.options.separateby == "stroke_opacity":
|
||||||
|
stroke_opacity = re.search('stroke-opacity:(.*?)(;|$)', style)
|
||||||
|
if stroke_opacity is not None:
|
||||||
|
stroke_opacity = stroke_opacity[0]
|
||||||
|
neutral_value = float(stroke_opacity.split("stroke-opacity:")[1].split(";")[0])
|
||||||
|
layer_name = stroke_opacity
|
||||||
|
else:
|
||||||
|
layer_name = "stroke-opacity-none"
|
||||||
|
|
||||||
|
elif self.options.separateby == "fill":
|
||||||
|
fill = re.search('fill:(.*?)(;|$)', style)
|
||||||
|
if fill is not None:
|
||||||
|
fill = fill[0]
|
||||||
|
fill_value = fill.split("fill:")[1].split(";")[0]
|
||||||
|
#check if the fill color is a real color or a gradient. if it's a gradient we skip the element
|
||||||
|
if fill_value != "none" and "url" not in fill_value:
|
||||||
|
fill_converted = str(Color(fill_value).to_rgb()) #the color can be hex code or clear name. we handle both the same
|
||||||
|
neutral_value = colorsort(fill_converted)
|
||||||
|
layer_name = "fill-" + self.options.parsecolors + "-" + fill_converted
|
||||||
|
elif "url" in fill_value: #okay we found a gradient. we put it to some group
|
||||||
|
layer_name = "fill-" + self.options.parsecolors + "-gradient"
|
||||||
|
else:
|
||||||
|
layer_name = "fill-" + self.options.parsecolors + "-none"
|
||||||
|
|
||||||
|
elif self.options.separateby == "fill_opacity":
|
||||||
|
fill_opacity = re.search('fill-opacity:(.*?)(;|$)', style)
|
||||||
|
if fill_opacity is not None:
|
||||||
|
fill_opacity = fill_opacity[0]
|
||||||
|
neutral_value = float(fill_opacity.split("fill-opacity:")[1].split(";")[0])
|
||||||
|
layer_name = fill_opacity
|
||||||
|
else:
|
||||||
|
layer_name = "fill-opacity-none"
|
||||||
|
|
||||||
if self.options.separateby == "stroke":
|
|
||||||
stroke = re.search('(;|^)stroke:(.*?)(;|$)', style)
|
|
||||||
if stroke is not None:
|
|
||||||
stroke = stroke[0]
|
|
||||||
stroke_value = stroke.split("stroke:")[1].split(";")[0]
|
|
||||||
if stroke_value != "none":
|
|
||||||
stroke_converted = str(Color(stroke_value).to_rgb()) #the color can be hex code or clear name. we handle both the same
|
|
||||||
neutral_value = colorsort(stroke_converted)
|
|
||||||
layer_name = "stroke-" + self.options.parsecolors + "-" + stroke_converted
|
|
||||||
else:
|
else:
|
||||||
layer_name = "stroke-" + self.options.parsecolors + "-none"
|
inkex.utils.debug("No proper option selected.")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
elif self.options.separateby == "stroke_width":
|
if neutral_value is not None: #apply decimals filter
|
||||||
stroke_width = re.search('stroke-width:(.*?)(;|$)', style)
|
neutral_value = float(round(neutral_value, self.options.decimals))
|
||||||
if stroke_width is not None:
|
|
||||||
stroke_width = stroke_width[0]
|
|
||||||
neutral_value = self.svg.unittouu(stroke_width.split("stroke-width:")[1].split(";")[0])
|
|
||||||
layer_name = stroke_width
|
|
||||||
else:
|
|
||||||
layer_name = "stroke-width-none"
|
|
||||||
|
|
||||||
elif self.options.separateby == "stroke_hairline":
|
if layer_name is not None:
|
||||||
stroke_hairline = re.search('-inkscape-stroke:hairline(;|$)', style)
|
layer_name = layer_name.split(";")[0] #cut off existing semicolons to avoid duplicated layers with/without semicolon
|
||||||
if stroke_hairline is not None:
|
currentLayer = self.findLayer(layer_name)
|
||||||
neutral_value = 1
|
if currentLayer is None: #layer does not exist, so create a new one
|
||||||
layer_name = "stroke-hairline-yes"
|
layerNodeList.append([self.createLayer(layerNodeList, layer_name), neutral_value, element, self.options.separateby])
|
||||||
else:
|
else:
|
||||||
neutral_value = 0
|
layerNodeList.append([currentLayer, neutral_value, element, self.options.separateby]) #layer is existent. append items to this later
|
||||||
layer_name = "stroke-hairline-no"
|
else: #if no style attribute in element and not a group
|
||||||
|
if isinstance(element, inkex.Group) is False:
|
||||||
elif self.options.separateby == "stroke_opacity":
|
if self.options.show_info:
|
||||||
stroke_opacity = re.search('stroke-opacity:(.*?)(;|$)', style)
|
inkex.utils.debug(element.get('id') + ' has no style attribute')
|
||||||
if stroke_opacity is not None:
|
if self.options.put_unfiltered:
|
||||||
stroke_opacity = stroke_opacity[0]
|
layer_name = 'without-style-attribute'
|
||||||
neutral_value = float(stroke_opacity.split("stroke-opacity:")[1].split(";")[0])
|
currentLayer = self.findLayer(layer_name)
|
||||||
layer_name = stroke_opacity
|
if currentLayer is None: #layer does not exist, so create a new one
|
||||||
else:
|
layerNodeList.append([self.createLayer(layerNodeList, layer_name), None, element, None])
|
||||||
layer_name = "stroke-opacity-none"
|
else:
|
||||||
|
layerNodeList.append([currentLayer, None, element, None]) #layer is existent. append items to this later
|
||||||
elif self.options.separateby == "fill":
|
|
||||||
fill = re.search('fill:(.*?)(;|$)', style)
|
|
||||||
if fill is not None:
|
|
||||||
fill = fill[0]
|
|
||||||
fill_value = fill.split("fill:")[1].split(";")[0]
|
|
||||||
#check if the fill color is a real color or a gradient. if it's a gradient we skip the element
|
|
||||||
if fill_value != "none" and "url" not in fill_value:
|
|
||||||
fill_converted = str(Color(fill_value).to_rgb()) #the color can be hex code or clear name. we handle both the same
|
|
||||||
neutral_value = colorsort(fill_converted)
|
|
||||||
layer_name = "fill-" + self.options.parsecolors + "-" + fill_converted
|
|
||||||
elif "url" in fill_value: #okay we found a gradient. we put it to some group
|
|
||||||
layer_name = "fill-" + self.options.parsecolors + "-gradient"
|
|
||||||
else:
|
|
||||||
layer_name = "fill-" + self.options.parsecolors + "-none"
|
|
||||||
|
|
||||||
elif self.options.separateby == "fill_opacity":
|
|
||||||
fill_opacity = re.search('fill-opacity:(.*?)(;|$)', style)
|
|
||||||
if fill_opacity is not None:
|
|
||||||
fill_opacity = fill_opacity[0]
|
|
||||||
neutral_value = float(fill_opacity.split("fill-opacity:")[1].split(";")[0])
|
|
||||||
layer_name = fill_opacity
|
|
||||||
else:
|
|
||||||
layer_name = "fill-opacity-none"
|
|
||||||
|
|
||||||
else:
|
|
||||||
inkex.utils.debug("No proper option selected.")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
if neutral_value is not None: #apply decimals filter
|
|
||||||
neutral_value = float(round(neutral_value, self.options.decimals))
|
|
||||||
|
|
||||||
if layer_name is not None:
|
|
||||||
layer_name = layer_name.split(";")[0] #cut off existing semicolons to avoid duplicated layers with/without semicolon
|
|
||||||
currentLayer = self.findLayer(layer_name)
|
|
||||||
if currentLayer is None: #layer does not exist, so create a new one
|
|
||||||
layerNodeList.append([self.createLayer(layerNodeList, layer_name), neutral_value, element, self.options.separateby])
|
|
||||||
else:
|
|
||||||
layerNodeList.append([currentLayer, neutral_value, element, self.options.separateby]) #layer is existent. append items to this later
|
|
||||||
|
|
||||||
contentlength = 0 #some counter to track if there are layers inside or if it is just a list with empty children
|
contentlength = 0 #some counter to track if there are layers inside or if it is just a list with empty children
|
||||||
for layerNode in layerNodeList:
|
for layerNode in layerNodeList:
|
||||||
|
Reference in New Issue
Block a user