Add inkstitch svg version tag (#2199)

... to make it easier to update legacy default values
pull/2127/head
Kaalleen 2023-04-15 08:48:27 +02:00 zatwierdzone przez GitHub
rodzic 067ef9095a
commit 6504c72fb7
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
5 zmienionych plików z 222 dodań i 151 usunięć

Wyświetl plik

@ -58,60 +58,6 @@ def param(*args, **kwargs):
class EmbroideryElement(object):
def __init__(self, node):
self.node = node
self._update_legacy_params()
def _update_legacy_params(self): # noqa: C901
# update legacy embroider_ attributes to namespaced attributes
legacy_attribs = False
for attrib in self.node.attrib:
if attrib.startswith('embroider_'):
self.replace_legacy_param(attrib)
legacy_attribs = True
# convert legacy tie setting
legacy_tie = self.get_param('ties', None)
if legacy_tie == "True":
self.set_param('ties', 0)
elif legacy_tie == "False":
self.set_param('ties', 3)
# convert legacy fill_method
legacy_fill_method = self.get_int_param('fill_method', None)
if legacy_fill_method == 0:
self.set_param('fill_method', 'auto_fill')
elif legacy_fill_method == 1:
self.set_param('fill_method', 'contour_fill')
elif legacy_fill_method == 2:
self.set_param('fill_method', 'guided_fill')
elif legacy_fill_method == 3:
self.set_param('fill_method', 'legacy_fill')
# legacy satin method
if self.get_boolean_param('e_stitch', False) is True:
self.remove_param('e_stitch')
self.set_param('satin_method', 'e_stitch')
# default setting for fill_underlay has changed
if legacy_attribs and not self.get_param('fill_underlay', ""):
self.set_param('fill_underlay', False)
# convert legacy stroke_method
if self.get_style("stroke"):
# manual stitch
legacy_manual_stitch = self.get_boolean_param('manual_stitch', False)
if legacy_manual_stitch is True:
self.remove_param('manual_stitch')
self.set_param('stroke_method', 'manual_stitch')
# stroke_method
legacy_stroke_method = self.get_int_param('stroke_method', None)
if legacy_stroke_method == 0:
self.set_param('stroke_method', 'running_stitch')
elif legacy_stroke_method == 1:
self.set_param('stroke_method', 'ripple_stitch')
if (not self.get_param('stroke_method', None) and
self.get_param('satin_column', False) is False and
not self.node.style('stroke-dasharray')):
self.set_param('stroke_method', 'zigzag_stitch')
@property
def id(self):
@ -128,14 +74,6 @@ class EmbroideryElement(object):
params.append(prop.fget.param)
return params
def replace_legacy_param(self, param):
# remove "embroider_" prefix
new_param = param[10:]
if new_param in INKSTITCH_ATTRIBS:
value = self.node.get(param, "").strip()
self.set_param(param[10:], value)
del self.node.attrib[param]
@cache
def get_param(self, param, default):
value = self.node.get(INKSTITCH_ATTRIBS[param], "").strip()

Wyświetl plik

@ -3,112 +3,33 @@
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
import json
import os
import re
from collections.abc import MutableMapping
from lxml import etree
from lxml.etree import Comment
from stringcase import snakecase
import inkex
from lxml.etree import Comment
from stringcase import snakecase
from ..commands import is_command, layer_commands
from ..elements import EmbroideryElement, nodes_to_elements
from ..elements.clone import is_clone
from ..i18n import _
from ..marker import has_marker
from ..metadata import InkStitchMetadata
from ..svg import generate_unique_id
from ..svg.tags import (CONNECTOR_TYPE, EMBROIDERABLE_TAGS, INKSCAPE_GROUPMODE,
NOT_EMBROIDERABLE_TAGS, SVG_CLIPPATH_TAG, SVG_DEFS_TAG,
SVG_GROUP_TAG, SVG_MASK_TAG)
from ..utils.settings import DEFAULT_METADATA, global_settings
SVG_METADATA_TAG = inkex.addNS("metadata", "svg")
from ..update import update_inkstitch_document
def strip_namespace(tag):
"""Remove xml namespace from a tag name.
>>> {http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd}namedview
<<< namedview
"""
match = re.match(r'^\{[^}]+\}(.+)$', tag)
if match:
return match.group(1)
else:
return tag
class InkStitchMetadata(MutableMapping):
"""Helper class to get and set inkstitch-specific metadata attributes.
Operates on a document and acts like a dict. Setting an item adds or
updates a metadata element in the document. Getting an item retrieves
a metadata element's text contents or None if an element by that name
doesn't exist.
"""
def __init__(self, document):
super().__init__()
self.document = document
self.metadata = document.metadata
for setting in DEFAULT_METADATA:
if self[setting] is None:
self[setting] = global_settings[f'default_{setting}']
# Because this class inherints from MutableMapping, all we have to do is
# implement these five methods and we get a full dict-like interface.
def __setitem__(self, name, value):
item = self._find_item(name)
item.text = json.dumps(value)
def _find_item(self, name, create=True):
tag = inkex.addNS(name, "inkstitch")
item = self.metadata.find(tag)
if item is None and create:
item = etree.SubElement(self.metadata, tag)
return item
def __getitem__(self, name):
item = self._find_item(name)
try:
return json.loads(item.text)
except (ValueError, TypeError):
return None
def __delitem__(self, name):
item = self._find_item(name, create=False)
if item is not None:
self.metadata.remove(item)
def __iter__(self):
for child in self.metadata:
if child.prefix == "inkstitch":
yield strip_namespace(child.tag)
def __len__(self):
i = 0
for i, item in enumerate(self):
pass
return i + 1
def __json__(self):
return dict(self)
class InkstitchExtension(inkex.Effect):
class InkstitchExtension(inkex.EffectExtension):
"""Base class for Inkstitch extensions. Not intended for direct use."""
def load(self, *args, **kwargs):
document = super().load(*args, **kwargs)
update_inkstitch_document(document)
return document
@classmethod
def name(cls):
return snakecase(cls.__name__)

85
lib/metadata.py 100644
Wyświetl plik

@ -0,0 +1,85 @@
import json
import re
from collections.abc import MutableMapping
import inkex
from lxml import etree
from .utils.settings import DEFAULT_METADATA, global_settings
def strip_namespace(tag):
"""Remove xml namespace from a tag name.
>>> {http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd}namedview
<<< namedview
"""
match = re.match(r'^\{[^}]+\}(.+)$', tag)
if match:
return match.group(1)
else:
return tag
class InkStitchMetadata(MutableMapping):
"""Helper class to get and set inkstitch-specific metadata attributes.
Operates on a document and acts like a dict. Setting an item adds or
updates a metadata element in the document. Getting an item retrieves
a metadata element's text contents or None if an element by that name
doesn't exist.
"""
def __init__(self, document):
super().__init__()
self.document = document
self.metadata = document.metadata
for setting in DEFAULT_METADATA:
if self[setting] is None:
self[setting] = global_settings[f'default_{setting}']
# Because this class inherints from MutableMapping, all we have to do is
# implement these five methods and we get a full dict-like interface.
def __setitem__(self, name, value):
item = self._find_item(name)
item.text = json.dumps(value)
def _find_item(self, name, create=True):
tag = inkex.addNS(name, "inkstitch")
item = self.metadata.find(tag)
if item is None and create:
item = etree.SubElement(self.metadata, tag)
return item
def __getitem__(self, name):
item = self._find_item(name)
try:
return json.loads(item.text)
except (ValueError, TypeError):
return None
def __delitem__(self, name):
item = self._find_item(name, create=False)
if item is not None:
self.metadata.remove(item)
def __iter__(self):
for child in self.metadata:
if child.prefix == "inkstitch":
yield strip_namespace(child.tag)
def __len__(self):
i = 0
for i, item in enumerate(self):
pass
return i + 1
def __json__(self):
return dict(self)

Wyświetl plik

@ -27,6 +27,7 @@ SVG_IMAGE_TAG = inkex.addNS('image', 'svg')
SVG_CLIPPATH_TAG = inkex.addNS('clipPath', 'svg')
SVG_MASK_TAG = inkex.addNS('mask', 'svg')
SVG_METADATA_TAG = inkex.addNS("metadata", "svg")
INKSCAPE_LABEL = inkex.addNS('label', 'inkscape')
INKSCAPE_GROUPMODE = inkex.addNS('groupmode', 'inkscape')
CONNECTION_START = inkex.addNS('connection-start', 'inkscape')

126
lib/update.py 100644
Wyświetl plik

@ -0,0 +1,126 @@
from inkex import errormsg
from .i18n import _
from .elements import EmbroideryElement
from .metadata import InkStitchMetadata
from .svg.tags import INKSTITCH_ATTRIBS
INKSTITCH_SVG_VERSION = 1
def update_inkstitch_document(svg):
document = svg.getroot()
# get the inkstitch svg version from the document
search_string = "//*[local-name()='inkstitch_svg_version']//text()"
file_version = document.findone(search_string)
try:
file_version = int(file_version)
except (TypeError, ValueError):
file_version = 0
if file_version == INKSTITCH_SVG_VERSION:
return
if file_version > INKSTITCH_SVG_VERSION:
errormsg(_("This document was created with a newer Version of Ink/Stitch. "
"It is possible that not everything works as expected.\n\n"
"Please update your Ink/Stitch version: https://inkstitch.org/docs/install/"))
# they may not want to be bothered with this info everytime they call an inkstitch extension
# let's udowngrade the file version number
_update_inkstitch_svg_version(svg)
else:
# this document is either a new document or it is outdated
# if we cannot find any inkstitch attribute in the document, we assume that this is a new document which doesn't need to be updated
search_string = "//*[namespace-uri()='http://inkstitch.org/namespace' or " \
"@*[namespace-uri()='http://inkstitch.org/namespace'] or " \
"@*[starts-with(name(), 'embroider_')]]"
inkstitch_element = document.findone(search_string)
if inkstitch_element is None:
_update_inkstitch_svg_version(svg)
return
# update elements
for element in document.iterdescendants():
# We are just checking for params and update them.
# No need to check for specific stitch types at this point
update_legacy_params(EmbroideryElement(element), file_version, INKSTITCH_SVG_VERSION)
_update_inkstitch_svg_version(svg)
def _update_inkstitch_svg_version(svg):
# set inkstitch svg version
metadata = InkStitchMetadata(svg.getroot())
metadata['inkstitch_svg_version'] = INKSTITCH_SVG_VERSION
def update_legacy_params(element, file_version, inkstitch_svg_version):
for version in range(file_version + 1, inkstitch_svg_version + 1):
_update_to(version, element)
def _update_to(version, element):
if version == 1:
_update_to_one(element)
def _update_to_one(element): # noqa: C901
# update legacy embroider_ attributes to namespaced attributes
legacy_attribs = False
for attrib in element.node.attrib:
if attrib.startswith('embroider_'):
_replace_legacy_embroider_param(element, attrib)
legacy_attribs = True
# convert legacy tie setting
legacy_tie = element.get_param('ties', None)
if legacy_tie == "True":
element.set_param('ties', 0)
elif legacy_tie == "False":
element.set_param('ties', 3)
# convert legacy fill_method
legacy_fill_method = element.get_int_param('fill_method', None)
if legacy_fill_method == 0:
element.set_param('fill_method', 'auto_fill')
elif legacy_fill_method == 1:
element.set_param('fill_method', 'contour_fill')
elif legacy_fill_method == 2:
element.set_param('fill_method', 'guided_fill')
elif legacy_fill_method == 3:
element.set_param('fill_method', 'legacy_fill')
# legacy satin method
if element.get_boolean_param('e_stitch', False) is True:
element.remove_param('e_stitch')
element.set_param('satin_method', 'e_stitch')
# default setting for fill_underlay has changed
if legacy_attribs and not element.get_param('fill_underlay', ""):
element.set_param('fill_underlay', False)
# convert legacy stroke_method
if element.get_style("stroke"):
# manual stitch
legacy_manual_stitch = element.get_boolean_param('manual_stitch', False)
if legacy_manual_stitch is True:
element.remove_param('manual_stitch')
element.set_param('stroke_method', 'manual_stitch')
# stroke_method
legacy_stroke_method = element.get_int_param('stroke_method', None)
if legacy_stroke_method == 0:
element.set_param('stroke_method', 'running_stitch')
elif legacy_stroke_method == 1:
element.set_param('stroke_method', 'ripple_stitch')
if (not element.get_param('stroke_method', None) and
element.get_param('satin_column', False) is False and
not element.node.style('stroke-dasharray')):
element.set_param('stroke_method', 'zigzag_stitch')
def _replace_legacy_embroider_param(element, param):
# remove "embroider_" prefix
new_param = param[10:]
if new_param in INKSTITCH_ATTRIBS:
value = element.node.get(param, "").strip()
element.set_param(param[10:], value)
del element.node.attrib[param]