Massive patching in cleanup styles extension

This commit is contained in:
leyghisbb 2021-04-12 19:14:57 +02:00
parent 89f19a339e
commit 7960396034
3 changed files with 166 additions and 87 deletions

View File

@ -2,20 +2,43 @@
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension"> <inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Cleanup Styles</name> <name>Cleanup Styles</name>
<id>fablabchemnitz.de.cleanup</id> <id>fablabchemnitz.de.cleanup</id>
<param name="stroke_width" type="float" precision="4" min="0.0000" max="5.0000" gui-text="Stroke width">0.1000</param> <param name="main_tabs" type="notebook">
<param name="stroke_units" gui-text="Units" type="optiongroup" appearance="combo"> <page name="tab_active" gui-text="Cleanup Styles">
<option value="px">px</option> <param name="dedicated_style_attributes" gui-text="Handling of dedicated style attributes" gui-description="We delete dedicated attributes like 'fill' or 'stroke'. Please choose an option what should happen with those properties." type="optiongroup" appearance="combo">
<option value="pt">pt</option> <option value="prefer_composed">Catch dedicated, but prefer composed (master) style</option>
<option value="in">in</option> <option value="prefer_dedicated">Catch dedicated and prefer over composed (master) style</option>
<option value="cm">cm</option> <option value="ignore">Ignore dedicated</option>
<option value="mm">mm</option> </param>
<param name="stroke_width_override" type="bool" gui-text="Override stroke width">false</param>
<param name="stroke_width" type="float" precision="3" min="0.0000" max="5.000" gui-text="Stroke width">0.100</param>
<param name="stroke_width_units" gui-text="Units" type="optiongroup" appearance="combo">
<option value="px">px</option>
<option value="pt">pt</option>
<option value="in">in</option>
<option value="cm">cm</option>
<option value="mm">mm</option>
</param>
<param name="stroke_opacity_override" type="bool" gui-text="Override stroke opacity">false</param>
<param name="stroke_opacity" type="float" precision="1" min="0.0" max="100.0" gui-text="Stroke opacity (%)">100.0</param>
<param name="reset_stroke_attributes" type="bool" gui-text="Reset stroke style attributes" gui-description="Remove stroke style attributes 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linejoin', 'stroke-linecap', 'stroke-miterlimit'">true</param>
<param name="reset_fill_attributes" type="bool" gui-text="Reset fill style attributes" gui-description="Sets 'fill:none;fill-opacity:1;' to style attribute">true</param>
<param name="apply_hairlines" type="bool" gui-text="Add additional hairline style" gui-description="Adds 'vector-effect:non-scaling-stroke;' and '-inkscape-stroke:hairline;' Hint: stroke-width is kept in background. All hairlines still have a valued width.">true</param>
<param name="apply_black_strokes" type="bool" gui-text="Apply black strokes where strokes missing" gui-description="Adds 'stroke:#000000;' to style attribute">true</param>
<param name="remove_group_styles" type="bool" gui-text="Remove styles from groups" gui-description="Remove style attributes from parent groups. So we have styles directly at the level of visivle nodes!">false</param>
<label>This extension works on current selection or for complete document</label>
</page>
<page name="tab_about" gui-text="About">
<label appearance="header">About</label>
<separator />
<label>Cleanup Styles by Mario Voigt / Stadtfabrikanten e.V. (2021)</label>
<label>This piece of software is part of the MightyScape for InkScape 1.0/1.1dev Extension Collection.</label>
<label>You found a bug or got some fresh code? Just report to mario.voigt@stadtfabrikanten.org. Thanks!</label>
<label appearance="url">https://fablabchemnitz.de</label>
<label>License: GNU GPL v3</label>
<separator />
<label>This extension generates inventory stickers for thermo printers (we use Brother QL-720NW) from our Teedy instance. Teedy is an open source software document management system (DMS). You can find the complete documentation at the Wiki space of https://fablabchemnitz.de</label>
</page>
</param> </param>
<param name="opacity" type="float" precision="1" min="0" max="100" gui-text="Opacity (%)">100.0</param>
<param name="reset_style_attributes" type="bool" gui-text="Reset stroke style attributes" gui-description="Remove stroke style attributes like stroke-dasharray, stroke-dashoffset, stroke-linejoin, linecap, stroke-miterlimit">true</param>
<param name="reset_fill_attributes" type="bool" gui-text="Reset fill style attributes" gui-description="Sets 'fill:none;' to style attribute">true</param>
<param name="apply_hairlines" type="bool" gui-text="Add additional hairline style" gui-description="Adds 'vector-effect:non-scaling-stroke;' and '-inkscape-stroke:hairline;' Hint: stroke-width is kept in background. All hairlines still have a valued width.">true</param>
<param name="apply_black_strokes" type="bool" gui-text="Apply black strokes where strokes missing" gui-description="Adds 'stroke:#000000;' to style attribute">true</param>
<label>This extension works on current selection or for complete document</label>
<effect needs-live-preview="true"> <effect needs-live-preview="true">
<object-type>all</object-type> <object-type>all</object-type>
<effects-menu> <effects-menu>

View File

@ -16,30 +16,52 @@ You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Based on coloreffect.py by Jos Hirth and Aaron C. Spike Based on
- coloreffect.py by Jos Hirth and Aaron C. Spike
- cleanup.py https://github.com/attoparsec/inkscape-extensions by attoparsec
Author: Mario Voigt / FabLab Chemnitz
Mail: mario.voigt@stadtfabrikanten.org
Last Patch: 12.04.2021
License: GNU GPL v3
Notes:
- This extension does not check if attributes contain duplicates properties like "opacity:1;fill:#393834;fill-opacity:1;opacity:1;fill:#393834;fill-opacity:1". We assume the SVG syntax is correct
''' '''
import inkex import inkex
import re import re
class Cleanup(inkex.EffectExtension): class Cleanup(inkex.EffectExtension):
groups = []
def __init__(self): def __init__(self):
inkex.Effect.__init__(self) inkex.Effect.__init__(self)
self.arg_parser.add_argument("--stroke_width", type=float, default=0.1, help="Stroke width") self.arg_parser.add_argument("--main_tabs")
self.arg_parser.add_argument("--stroke_units", default="mm", help="Stroke unit") self.arg_parser.add_argument("--dedicated_style_attributes", default="ignore", help="Handling of dedicated style attributes")
self.arg_parser.add_argument("--opacity", type=float, default="100.0", help="Opacity") self.arg_parser.add_argument("--stroke_width_override", type=inkex.Boolean, default=False, help="Override stroke width")
self.arg_parser.add_argument("--reset_style_attributes", type=inkex.Boolean, help="Remove stroke style attributes like stroke-dasharray, stroke-dashoffset, stroke-linejoin, linecap, stroke-miterlimit") self.arg_parser.add_argument("--stroke_width", type=float, default=0.100, help="Stroke width")
self.arg_parser.add_argument("--reset_fill_attributes", type=inkex.Boolean, help="Sets 'fill:none;' to style attribute") self.arg_parser.add_argument("--stroke_width_units", default="mm", help="Stroke width unit")
self.arg_parser.add_argument("--stroke_opacity_override", type=inkex.Boolean, default=False, help="Override stroke opacity")
self.arg_parser.add_argument("--stroke_opacity", type=float, default="100.0", help="Stroke opacity (%)")
self.arg_parser.add_argument("--reset_stroke_attributes", type=inkex.Boolean, help="Remove stroke style attributes 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linejoin', 'stroke-linecap', 'stroke-miterlimit'")
self.arg_parser.add_argument("--reset_fill_attributes", type=inkex.Boolean, help="Sets 'fill:none;fill-opacity:1;' to style attribute")
self.arg_parser.add_argument("--apply_hairlines", type=inkex.Boolean, help="Adds 'vector-effect:non-scaling-stroke;' and '-inkscape-stroke:hairline;' Hint: stroke-width is kept in background. All hairlines still have a valued width.") self.arg_parser.add_argument("--apply_hairlines", type=inkex.Boolean, help="Adds 'vector-effect:non-scaling-stroke;' and '-inkscape-stroke:hairline;' Hint: stroke-width is kept in background. All hairlines still have a valued width.")
self.arg_parser.add_argument("--apply_black_strokes", type=inkex.Boolean, help="Adds 'stroke:#000000;' to style attribute") self.arg_parser.add_argument("--apply_black_strokes", type=inkex.Boolean, help="Adds 'stroke:#000000;' to style attribute")
self.arg_parser.add_argument("--remove_group_styles", type=inkex.Boolean, help="Remove styles from groups")
def effect(self): def effect(self):
if len(self.svg.selected) == 0: if len(self.svg.selected) == 0:
self.getAttribs(self.document.getroot()) self.getAttribs(self.document.getroot())
else: else:
for id, node in self.svg.selected.items(): for id, node in self.svg.selected.items():
self.getAttribs(node) self.getAttribs(node)
#finally remove the styles from collected groups (if enabled)
if self.options.remove_group_styles is True:
for group in self.groups:
if group.attrib.has_key('style') is True:
group.attrib.pop('style')
def getAttribs(self, node): def getAttribs(self, node):
self.changeStyle(node) self.changeStyle(node)
@ -48,72 +70,106 @@ class Cleanup(inkex.EffectExtension):
#stroke and fill styles can be included in style attribute or they can exist separately (can occure in older SVG files). We do not parse other attributes than style #stroke and fill styles can be included in style attribute or they can exist separately (can occure in older SVG files). We do not parse other attributes than style
def changeStyle(self, node): def changeStyle(self, node):
nodeDict = []
nodeDict.append(inkex.addNS('line','svg')) #we check/modify the style of all shapes (not groups)
nodeDict.append(inkex.addNS('polyline','svg')) if isinstance(node, inkex.ShapeElement) and not isinstance(node, inkex.Group):
nodeDict.append(inkex.addNS('polygon','svg')) # the final styles applied to this element (with influence from top level elements like groups)
nodeDict.append(inkex.addNS('circle','svg')) composed_style = node.composed_style()
nodeDict.append(inkex.addNS('ellipse','svg')) composedStyleAttributes = str(composed_style).split(';') #array
nodeDict.append(inkex.addNS('rect','svg')) composedStyleAttributesDict = {}
nodeDict.append(inkex.addNS('path','svg')) if len(composed_style) > 0: #Style "composed_style" might contain just empty '' string which will lead to failing dict update
nodeDict.append(inkex.addNS('g','svg')) for composedStyleAttribute in composedStyleAttributes:
if node.tag in nodeDict: composedStyleAttributesDict.update({'{}'.format(composedStyleAttribute.split(':')[0]): composedStyleAttribute.split(':')[1]})
if node.attrib.has_key('style'):
style = node.get('style') #three options to handle dedicated attributes (attributes not in the "style" attribute, but separate):
if style: # - just delete all dedicated properties
#add missing style attributes if required # - merge dedicated properties, and prefer them over those from composed style
if style.endswith(';') is False: # - merge dedicated properties, but prefer properties from composed style
style += ';' dedicatedStyleAttributesDict = {}
if re.search('(;|^)stroke:(.*?)(;|$)', style) is None: #if "stroke" is None, add one. We need to distinguish because there's also attribute "-inkscape-stroke" that's why we check starting with ^ or ; popDict = []
style += 'stroke:none;' popDict.extend(['stroke', 'stroke-opacity', 'stroke-width', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'fill', 'fill-opacity'])
if "stroke-width:" not in style: for popItem in popDict:
style += 'stroke-width:{:1.4f};'.format(self.svg.unittouu(str(self.options.stroke_width) + self.options.stroke_units)) if node.attrib.has_key(str(popItem)):
if "stroke-opacity:" not in style: dedicatedStyleAttributesDict.update({'{}'.format(popItem): node.get(popItem)})
style += 'stroke-opacity:{:1.1f};'.format(self.options.opacity / 100) node.attrib.pop(popItem)
#inkex.utils.debug("composedStyleAttributesDict = " + str(composedStyleAttributesDict))
#inkex.utils.debug("dedicatedStyleAttributesDict = " + str(dedicatedStyleAttributesDict))
if self.options.dedicated_style_attributes == 'prefer_dedicated':
composedStyleAttributesDict.update(dedicatedStyleAttributesDict)
node.set('style', composedStyleAttributesDict)
elif self.options.dedicated_style_attributes == 'prefer_composed':
dedicatedStyleAttributesDict.update(composedStyleAttributesDict)
node.set('style', dedicatedStyleAttributesDict)
elif self.options.dedicated_style_attributes == 'ignore':
pass
if self.options.apply_hairlines is True: # now parse the style with possibly merged dedicated attributes modded style attribute (dedicatedStyleAttributes)
if "vector-effect:non-scaling-stroke" not in style: if node.attrib.has_key('style') is False:
style += 'vector-effect:non-scaling-stroke;' node.set('style', 'stroke:#000000;') #we add basic stroke color black. We cannot set to empty value or just ";" because it will not update properly
if "-inkscape-stroke:hairline" not in style: style = node.get('style')
style += '-inkscape-stroke:hairline;'
#add missing style attributes if required
if re.search('fill:(.*?)(;|$)', style) is None: #if "fill" is None, add one. if style.endswith(';') is False:
style += 'fill:none;' style += ';'
if re.search('(;|^)stroke:(.*?)(;|$)', style) is None: #if "stroke" is None, add one. We need to distinguish because there's also attribute "-inkscape-stroke" that's why we check starting with ^ or ;
#then parse the content and check what we need to adjust style += 'stroke:none;'
declarations = style.split(';') if self.options.stroke_width_override is True and "stroke-width:" not in style:
for i, decl in enumerate(declarations): style += 'stroke-width:{:1.4f};'.format(self.svg.unittouu(str(self.options.stroke_width) + self.options.stroke_width_units))
parts = decl.split(':', 2) if self.options.stroke_opacity_override is True and "stroke-opacity:" not in style:
if len(parts) == 2: style += 'stroke-opacity:{:1.1f};'.format(self.options.stroke_opacity / 100)
(prop, val) = parts
prop = prop.strip().lower() if self.options.apply_hairlines is True:
if prop == 'stroke-width': if "vector-effect:non-scaling-stroke" not in style:
new_val = self.svg.unittouu(str(self.options.stroke_width) + self.options.stroke_units) style += 'vector-effect:non-scaling-stroke;'
declarations[i] = prop + ':{:1.4f}'.format(new_val) if "-inkscape-stroke:hairline" not in style:
if prop == 'stroke-opacity': style += '-inkscape-stroke:hairline;'
new_val = self.options.opacity / 100
declarations[i] = prop + ':{:1.1f}'.format(new_val) if re.search('fill:(.*?)(;|$)', style) is None: #if "fill" is None, add one.
if self.options.reset_style_attributes is True: style += 'fill:none;'
if prop == 'stroke-dasharray':
declarations[i] = '' #then parse the content and check what we need to adjust
if prop == 'stroke-dashoffset': declarations = style.split(';')
declarations[i] = '' for i, decl in enumerate(declarations):
if prop == 'stroke-linejoin': parts = decl.split(':', 2)
declarations[i] = '' if len(parts) == 2:
if prop == 'stroke-linecap': (prop, val) = parts
declarations[i] = '' prop = prop.strip().lower()
if prop == 'stroke-miterlimit': if prop == 'stroke-width' and self.options.stroke_width_override is True:
declarations[i] = '' new_val = self.svg.unittouu(str(self.options.stroke_width) + self.options.stroke_width_units)
if self.options.apply_black_strokes is True: declarations[i] = prop + ':{:1.4f}'.format(new_val)
if prop == 'stroke': if prop == 'stroke-opacity' and self.options.stroke_opacity_override is True:
if val == 'none': new_val = self.options.stroke_opacity / 100
new_val = '#000000' declarations[i] = prop + ':{:1.1f}'.format(new_val)
declarations[i] = prop + ':' + new_val if self.options.reset_stroke_attributes is True:
if self.options.reset_fill_attributes is True: if prop == 'stroke-dasharray':
if prop == 'fill': declarations[i] = ''
new_val = 'none' if prop == 'stroke-dashoffset':
declarations[i] = prop + ':' + new_val declarations[i] = ''
node.set('style', ';'.join(declarations)) if prop == 'stroke-linejoin':
declarations[i] = ''
if prop == 'stroke-linecap':
declarations[i] = ''
if prop == 'stroke-miterlimit':
declarations[i] = ''
if self.options.apply_black_strokes is True:
if prop == 'stroke':
if val == 'none':
new_val = '#000000'
declarations[i] = prop + ':' + new_val
if self.options.reset_fill_attributes is True:
if prop == 'fill':
new_val = 'none'
declarations[i] = prop + ':' + new_val
if prop == 'fill-opacity':
new_val = '1'
declarations[i] = prop + ':' + new_val
node.set('style', ';'.join(declarations))
# if element is group we add it to collection to remove it's style after parsing all selected items
elif isinstance(node, inkex.ShapeElement) and isinstance(node, inkex.Group):
self.groups.append(node)
if __name__ == '__main__': if __name__ == '__main__':
Cleanup().run() Cleanup().run()

View File

@ -25,7 +25,7 @@
<label appearance="header">About</label> <label appearance="header">About</label>
<separator /> <separator />
<label>Inventory Stickers by Mario Voigt / Stadtfabrikanten e.V. (2021)</label> <label>Inventory Stickers by Mario Voigt / Stadtfabrikanten e.V. (2021)</label>
<label>This piece of software is part of the MightyScape for InkScape 1.0/1.1dev Extension Collection</label> <label>This piece of software is part of the MightyScape for InkScape 1.0/1.1dev Extension Collection.</label>
<label>you found a bug or got some fresh code? Just report to mario.voigt@stadtfabrikanten.org. Thanks!</label> <label>you found a bug or got some fresh code? Just report to mario.voigt@stadtfabrikanten.org. Thanks!</label>
<label appearance="url">https://fablabchemnitz.de</label> <label appearance="url">https://fablabchemnitz.de</label>
<label>License: GNU GPL v3</label> <label>License: GNU GPL v3</label>