inkstitch/lib/extensions/base.py

206 wiersze
6.6 KiB
Python
Czysty Zwykły widok Historia

2021-03-12 04:17:19 +00:00
# Authors: see git history
#
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
import json
2019-04-10 15:42:49 +00:00
import os
import re
from collections.abc import MutableMapping
2020-05-16 21:01:00 +00:00
import inkex
from lxml import etree
from stringcase import snakecase
2020-05-16 21:01:00 +00:00
2021-06-28 18:05:50 +00:00
from ..commands import is_command, layer_commands
from ..elements import EmbroideryElement, nodes_to_elements
from ..elements.clone import is_clone
2018-08-22 01:43:09 +00:00
from ..i18n import _
2021-06-30 12:05:13 +00:00
from ..patterns import is_pattern
from ..svg import generate_unique_id
2020-05-16 21:01:00 +00:00
from ..svg.tags import (CONNECTOR_TYPE, EMBROIDERABLE_TAGS, INKSCAPE_GROUPMODE,
2021-10-21 14:24:40 +00:00
NOT_EMBROIDERABLE_TAGS, SVG_DEFS_TAG, SVG_GROUP_TAG)
2018-04-14 01:23:00 +00:00
SVG_METADATA_TAG = inkex.addNS("metadata", "svg")
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)
2018-04-14 01:23:00 +00:00
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):
self.document = document
self.metadata = document.metadata
2018-04-14 01:23:00 +00:00
# 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):
2018-04-21 20:23:13 +00:00
item = self._find_item(name)
item.text = json.dumps(value)
2018-04-14 01:23:00 +00:00
2018-06-10 01:23:21 +00:00
def _find_item(self, name, create=True):
2018-04-14 01:23:00 +00:00
tag = inkex.addNS(name, "inkstitch")
item = self.metadata.find(tag)
2018-06-10 01:23:21 +00:00
if item is None and create:
item = etree.SubElement(self.metadata, tag)
2018-04-14 01:23:00 +00:00
return item
def __getitem__(self, name):
item = self._find_item(name)
try:
return json.loads(item.text)
except (ValueError, TypeError):
return None
2018-04-14 01:23:00 +00:00
def __delitem__(self, name):
2018-06-10 01:23:21 +00:00
item = self._find_item(name, create=False)
2018-04-14 01:23:00 +00:00
2018-06-10 01:23:21 +00:00
if item is not None:
2018-04-14 01:23:00 +00:00
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
2018-04-14 01:23:00 +00:00
for i, item in enumerate(self):
pass
return i + 1
class InkstitchExtension(inkex.Effect):
"""Base class for Inkstitch extensions. Not intended for direct use."""
@classmethod
def name(cls):
return snakecase(cls.__name__)
def hide_all_layers(self):
for g in self.document.getroot().findall(SVG_GROUP_TAG):
if g.get(INKSCAPE_GROUPMODE) == "layer":
g.set("style", "display:none")
2021-07-21 15:15:46 +00:00
def get_current_layer(self):
# if no layer is selected, inkex defaults to the root, which isn't
# particularly useful
2021-07-21 15:15:46 +00:00
current_layer = self.svg.get_current_layer()
if current_layer is self.document.getroot():
try:
2021-07-21 15:15:46 +00:00
current_layer = self.document.xpath(".//svg:g[@inkscape:groupmode='layer']", namespaces=inkex.NSS)[0]
except IndexError:
# No layers at all?? Fine, we'll stick with the default.
pass
2021-07-21 15:15:46 +00:00
return current_layer
def no_elements_error(self):
if self.svg.selection:
# l10n This was previously: "No embroiderable paths selected."
inkex.errormsg(_("Ink/Stitch doesn't know how to work with any of the objects you've selected.") + "\n")
2018-08-22 00:32:50 +00:00
else:
inkex.errormsg(_("There are no objects in the entire document that Ink/Stitch knows how to work with.") + "\n")
2021-07-21 15:15:46 +00:00
inkex.errormsg(_("Tip: Run Extensions > Ink/Stitch > Troubleshoot > Troubleshoot Objects") + "\n")
2020-05-16 21:01:00 +00:00
def descendants(self, node, selected=False, troubleshoot=False): # noqa: C901
nodes = []
element = EmbroideryElement(node)
2018-08-01 01:00:30 +00:00
if element.has_command('ignore_object'):
2018-07-25 02:15:28 +00:00
return []
2018-08-17 02:50:34 +00:00
if node.tag == SVG_GROUP_TAG and node.get(INKSCAPE_GROUPMODE) == "layer":
if len(list(layer_commands(node, "ignore_layer"))):
2018-08-17 02:50:34 +00:00
return []
2021-08-05 19:36:44 +00:00
if (node.tag in EMBROIDERABLE_TAGS or node.tag == SVG_GROUP_TAG) and element.get_style('display', 'inline') is None:
return []
2021-10-21 14:24:40 +00:00
if node.tag == SVG_DEFS_TAG:
return []
2020-05-16 21:01:00 +00:00
# command connectors with a fill color set, will glitch into the elements list
if is_command(node) or node.get(CONNECTOR_TYPE):
return []
2020-05-16 21:01:00 +00:00
if self.svg.selection:
if node.get("id") in self.svg.selection:
2018-08-17 02:50:34 +00:00
selected = True
else:
# if the user didn't select anything that means we process everything
selected = True
for child in node:
2020-05-16 21:01:00 +00:00
nodes.extend(self.descendants(child, selected, troubleshoot))
2020-05-16 21:01:00 +00:00
if selected:
if node.tag == SVG_GROUP_TAG:
pass
2021-06-28 18:05:50 +00:00
elif (node.tag in EMBROIDERABLE_TAGS or is_clone(node)) and not is_pattern(node):
2020-05-16 21:01:00 +00:00
nodes.append(node)
2021-06-27 20:29:57 +00:00
# add images, text and patterns for the troubleshoot extension
2021-06-28 18:05:50 +00:00
elif troubleshoot and (node.tag in NOT_EMBROIDERABLE_TAGS or is_pattern(node)):
2020-05-16 21:01:00 +00:00
nodes.append(node)
return nodes
2020-05-16 21:01:00 +00:00
def get_nodes(self, troubleshoot=False):
return self.descendants(self.document.getroot(), troubleshoot=troubleshoot)
2020-05-16 21:01:00 +00:00
def get_elements(self, troubleshoot=False):
self.elements = nodes_to_elements(self.get_nodes(troubleshoot))
if self.elements:
return True
2020-05-16 21:01:00 +00:00
if not troubleshoot:
self.no_elements_error()
2020-05-16 21:01:00 +00:00
return False
2021-08-07 16:37:17 +00:00
def elements_to_stitch_groups(self, elements):
patches = []
for element in elements:
if patches:
last_patch = patches[-1]
else:
last_patch = None
patches.extend(element.embroider(last_patch))
return patches
2018-04-13 00:05:01 +00:00
2018-04-14 01:23:00 +00:00
def get_inkstitch_metadata(self):
return InkStitchMetadata(self.svg)
2018-04-14 01:23:00 +00:00
2018-06-13 02:15:32 +00:00
def get_base_file_name(self):
svg_filename = self.document.getroot().get(inkex.addNS('docname', 'sodipodi'), "embroidery.svg")
2019-04-10 15:42:49 +00:00
return os.path.splitext(svg_filename)[0]
2018-06-13 02:15:32 +00:00
def uniqueId(self, prefix, make_new_id=True):
"""Override inkex.Effect.uniqueId with a nicer naming scheme."""
return generate_unique_id(self.document, prefix)