kopia lustrzana https://github.com/inkstitch/inkstitch
Add inkstitch svg version tag (#2199)
... to make it easier to update legacy default valuespull/2127/head
rodzic
067ef9095a
commit
6504c72fb7
|
@ -58,60 +58,6 @@ def param(*args, **kwargs):
|
||||||
class EmbroideryElement(object):
|
class EmbroideryElement(object):
|
||||||
def __init__(self, node):
|
def __init__(self, node):
|
||||||
self.node = 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
|
@property
|
||||||
def id(self):
|
def id(self):
|
||||||
|
@ -128,14 +74,6 @@ class EmbroideryElement(object):
|
||||||
params.append(prop.fget.param)
|
params.append(prop.fget.param)
|
||||||
return params
|
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
|
@cache
|
||||||
def get_param(self, param, default):
|
def get_param(self, param, default):
|
||||||
value = self.node.get(INKSTITCH_ATTRIBS[param], "").strip()
|
value = self.node.get(INKSTITCH_ATTRIBS[param], "").strip()
|
||||||
|
|
|
@ -3,112 +3,33 @@
|
||||||
# Copyright (c) 2010 Authors
|
# Copyright (c) 2010 Authors
|
||||||
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
|
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
|
||||||
|
|
||||||
import json
|
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
from collections.abc import MutableMapping
|
|
||||||
|
|
||||||
from lxml import etree
|
|
||||||
from lxml.etree import Comment
|
|
||||||
from stringcase import snakecase
|
|
||||||
|
|
||||||
import inkex
|
import inkex
|
||||||
|
from lxml.etree import Comment
|
||||||
|
from stringcase import snakecase
|
||||||
|
|
||||||
from ..commands import is_command, layer_commands
|
from ..commands import is_command, layer_commands
|
||||||
from ..elements import EmbroideryElement, nodes_to_elements
|
from ..elements import EmbroideryElement, nodes_to_elements
|
||||||
from ..elements.clone import is_clone
|
from ..elements.clone import is_clone
|
||||||
from ..i18n import _
|
from ..i18n import _
|
||||||
from ..marker import has_marker
|
from ..marker import has_marker
|
||||||
|
from ..metadata import InkStitchMetadata
|
||||||
from ..svg import generate_unique_id
|
from ..svg import generate_unique_id
|
||||||
from ..svg.tags import (CONNECTOR_TYPE, EMBROIDERABLE_TAGS, INKSCAPE_GROUPMODE,
|
from ..svg.tags import (CONNECTOR_TYPE, EMBROIDERABLE_TAGS, INKSCAPE_GROUPMODE,
|
||||||
NOT_EMBROIDERABLE_TAGS, SVG_CLIPPATH_TAG, SVG_DEFS_TAG,
|
NOT_EMBROIDERABLE_TAGS, SVG_CLIPPATH_TAG, SVG_DEFS_TAG,
|
||||||
SVG_GROUP_TAG, SVG_MASK_TAG)
|
SVG_GROUP_TAG, SVG_MASK_TAG)
|
||||||
from ..utils.settings import DEFAULT_METADATA, global_settings
|
from ..update import update_inkstitch_document
|
||||||
|
|
||||||
SVG_METADATA_TAG = inkex.addNS("metadata", "svg")
|
|
||||||
|
|
||||||
|
|
||||||
def strip_namespace(tag):
|
class InkstitchExtension(inkex.EffectExtension):
|
||||||
"""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):
|
|
||||||
"""Base class for Inkstitch extensions. Not intended for direct use."""
|
"""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
|
@classmethod
|
||||||
def name(cls):
|
def name(cls):
|
||||||
return snakecase(cls.__name__)
|
return snakecase(cls.__name__)
|
||||||
|
|
|
@ -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)
|
|
@ -27,6 +27,7 @@ SVG_IMAGE_TAG = inkex.addNS('image', 'svg')
|
||||||
SVG_CLIPPATH_TAG = inkex.addNS('clipPath', 'svg')
|
SVG_CLIPPATH_TAG = inkex.addNS('clipPath', 'svg')
|
||||||
SVG_MASK_TAG = inkex.addNS('mask', 'svg')
|
SVG_MASK_TAG = inkex.addNS('mask', 'svg')
|
||||||
|
|
||||||
|
SVG_METADATA_TAG = inkex.addNS("metadata", "svg")
|
||||||
INKSCAPE_LABEL = inkex.addNS('label', 'inkscape')
|
INKSCAPE_LABEL = inkex.addNS('label', 'inkscape')
|
||||||
INKSCAPE_GROUPMODE = inkex.addNS('groupmode', 'inkscape')
|
INKSCAPE_GROUPMODE = inkex.addNS('groupmode', 'inkscape')
|
||||||
CONNECTION_START = inkex.addNS('connection-start', 'inkscape')
|
CONNECTION_START = inkex.addNS('connection-start', 'inkscape')
|
||||||
|
|
|
@ -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]
|
Ładowanie…
Reference in New Issue