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">
<name>Cleanup Styles</name>
<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="stroke_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 name="main_tabs" type="notebook">
<page name="tab_active" gui-text="Cleanup Styles">
<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="prefer_composed">Catch dedicated, but prefer composed (master) style</option>
<option value="prefer_dedicated">Catch dedicated and prefer over composed (master) style</option>
<option value="ignore">Ignore dedicated</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 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">
<object-type>all</object-type>
<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
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 re
class Cleanup(inkex.EffectExtension):
groups = []
def __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("--stroke_units", default="mm", help="Stroke unit")
self.arg_parser.add_argument("--opacity", type=float, default="100.0", help="Opacity")
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("--reset_fill_attributes", type=inkex.Boolean, help="Sets 'fill:none;' to style attribute")
self.arg_parser.add_argument("--main_tabs")
self.arg_parser.add_argument("--dedicated_style_attributes", default="ignore", help="Handling of dedicated style attributes")
self.arg_parser.add_argument("--stroke_width_override", type=inkex.Boolean, default=False, help="Override stroke width")
self.arg_parser.add_argument("--stroke_width", type=float, default=0.100, help="Stroke width")
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_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):
if len(self.svg.selected) == 0:
self.getAttribs(self.document.getroot())
else:
for id, node in self.svg.selected.items():
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):
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
def changeStyle(self, node):
nodeDict = []
nodeDict.append(inkex.addNS('line','svg'))
nodeDict.append(inkex.addNS('polyline','svg'))
nodeDict.append(inkex.addNS('polygon','svg'))
nodeDict.append(inkex.addNS('circle','svg'))
nodeDict.append(inkex.addNS('ellipse','svg'))
nodeDict.append(inkex.addNS('rect','svg'))
nodeDict.append(inkex.addNS('path','svg'))
nodeDict.append(inkex.addNS('g','svg'))
if node.tag in nodeDict:
if node.attrib.has_key('style'):
style = node.get('style')
if style:
#add missing style attributes if required
if style.endswith(';') is False:
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 ;
style += 'stroke:none;'
if "stroke-width:" not in style:
style += 'stroke-width:{:1.4f};'.format(self.svg.unittouu(str(self.options.stroke_width) + self.options.stroke_units))
if "stroke-opacity:" not in style:
style += 'stroke-opacity:{:1.1f};'.format(self.options.opacity / 100)
#we check/modify the style of all shapes (not groups)
if isinstance(node, inkex.ShapeElement) and not isinstance(node, inkex.Group):
# the final styles applied to this element (with influence from top level elements like groups)
composed_style = node.composed_style()
composedStyleAttributes = str(composed_style).split(';') #array
composedStyleAttributesDict = {}
if len(composed_style) > 0: #Style "composed_style" might contain just empty '' string which will lead to failing dict update
for composedStyleAttribute in composedStyleAttributes:
composedStyleAttributesDict.update({'{}'.format(composedStyleAttribute.split(':')[0]): composedStyleAttribute.split(':')[1]})
#three options to handle dedicated attributes (attributes not in the "style" attribute, but separate):
# - just delete all dedicated properties
# - merge dedicated properties, and prefer them over those from composed style
# - merge dedicated properties, but prefer properties from composed style
dedicatedStyleAttributesDict = {}
popDict = []
popDict.extend(['stroke', 'stroke-opacity', 'stroke-width', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'fill', 'fill-opacity'])
for popItem in popDict:
if node.attrib.has_key(str(popItem)):
dedicatedStyleAttributesDict.update({'{}'.format(popItem): node.get(popItem)})
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:
if "vector-effect:non-scaling-stroke" not in style:
style += 'vector-effect:non-scaling-stroke;'
if "-inkscape-stroke:hairline" not in style:
style += '-inkscape-stroke:hairline;'
if re.search('fill:(.*?)(;|$)', style) is None: #if "fill" is None, add one.
style += 'fill:none;'
#then parse the content and check what we need to adjust
declarations = style.split(';')
for i, decl in enumerate(declarations):
parts = decl.split(':', 2)
if len(parts) == 2:
(prop, val) = parts
prop = prop.strip().lower()
if prop == 'stroke-width':
new_val = self.svg.unittouu(str(self.options.stroke_width) + self.options.stroke_units)
declarations[i] = prop + ':{:1.4f}'.format(new_val)
if prop == 'stroke-opacity':
new_val = self.options.opacity / 100
declarations[i] = prop + ':{:1.1f}'.format(new_val)
if self.options.reset_style_attributes is True:
if prop == 'stroke-dasharray':
declarations[i] = ''
if prop == 'stroke-dashoffset':
declarations[i] = ''
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
node.set('style', ';'.join(declarations))
# now parse the style with possibly merged dedicated attributes modded style attribute (dedicatedStyleAttributes)
if node.attrib.has_key('style') is False:
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
style = node.get('style')
#add missing style attributes if required
if style.endswith(';') is False:
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 ;
style += 'stroke:none;'
if self.options.stroke_width_override is True and "stroke-width:" not in style:
style += 'stroke-width:{:1.4f};'.format(self.svg.unittouu(str(self.options.stroke_width) + self.options.stroke_width_units))
if self.options.stroke_opacity_override is True and "stroke-opacity:" not in style:
style += 'stroke-opacity:{:1.1f};'.format(self.options.stroke_opacity / 100)
if self.options.apply_hairlines is True:
if "vector-effect:non-scaling-stroke" not in style:
style += 'vector-effect:non-scaling-stroke;'
if "-inkscape-stroke:hairline" not in style:
style += '-inkscape-stroke:hairline;'
if re.search('fill:(.*?)(;|$)', style) is None: #if "fill" is None, add one.
style += 'fill:none;'
#then parse the content and check what we need to adjust
declarations = style.split(';')
for i, decl in enumerate(declarations):
parts = decl.split(':', 2)
if len(parts) == 2:
(prop, val) = parts
prop = prop.strip().lower()
if prop == 'stroke-width' and self.options.stroke_width_override is True:
new_val = self.svg.unittouu(str(self.options.stroke_width) + self.options.stroke_width_units)
declarations[i] = prop + ':{:1.4f}'.format(new_val)
if prop == 'stroke-opacity' and self.options.stroke_opacity_override is True:
new_val = self.options.stroke_opacity / 100
declarations[i] = prop + ':{:1.1f}'.format(new_val)
if self.options.reset_stroke_attributes is True:
if prop == 'stroke-dasharray':
declarations[i] = ''
if prop == 'stroke-dashoffset':
declarations[i] = ''
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__':
Cleanup().run()

View File

@ -25,7 +25,7 @@
<label appearance="header">About</label>
<separator />
<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 appearance="url">https://fablabchemnitz.de</label>
<label>License: GNU GPL v3</label>