From 79603960349f261973dce1e1ea9f3a01d0ea551a Mon Sep 17 00:00:00 2001 From: leyghisbb Date: Mon, 12 Apr 2021 19:14:57 +0200 Subject: [PATCH] Massive patching in cleanup styles extension --- extensions/fablabchemnitz/cleanup.inx | 49 +++-- extensions/fablabchemnitz/cleanup.py | 202 +++++++++++------- .../fablabchemnitz/inventory_sticker.inx | 2 +- 3 files changed, 166 insertions(+), 87 deletions(-) diff --git a/extensions/fablabchemnitz/cleanup.inx b/extensions/fablabchemnitz/cleanup.inx index fc0c9065..89ea485f 100644 --- a/extensions/fablabchemnitz/cleanup.inx +++ b/extensions/fablabchemnitz/cleanup.inx @@ -2,20 +2,43 @@ Cleanup Styles fablabchemnitz.de.cleanup - 0.1000 - - - - - - + + + + + + + + false + 0.100 + + + + + + + + false + 100.0 + true + true + true + true + false + + + + + + + + + + + + + - 100.0 - true - true - true - true - all diff --git a/extensions/fablabchemnitz/cleanup.py b/extensions/fablabchemnitz/cleanup.py index b1b56375..e3590eb0 100644 --- a/extensions/fablabchemnitz/cleanup.py +++ b/extensions/fablabchemnitz/cleanup.py @@ -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() \ No newline at end of file diff --git a/extensions/fablabchemnitz/inventory_sticker.inx b/extensions/fablabchemnitz/inventory_sticker.inx index d87fc822..701d74de 100644 --- a/extensions/fablabchemnitz/inventory_sticker.inx +++ b/extensions/fablabchemnitz/inventory_sticker.inx @@ -25,7 +25,7 @@ - +