diff options
Diffstat (limited to 'lib/extensions')
| -rw-r--r-- | lib/extensions/base.py | 97 | ||||
| -rw-r--r-- | lib/extensions/lettering.py | 7 | ||||
| -rw-r--r-- | lib/extensions/params.py | 12 | ||||
| -rw-r--r-- | lib/extensions/select_elements.py | 8 | ||||
| -rw-r--r-- | lib/extensions/stroke_to_lpe_satin.py | 93 | ||||
| -rw-r--r-- | lib/extensions/troubleshoot.py | 24 |
6 files changed, 109 insertions, 132 deletions
diff --git a/lib/extensions/base.py b/lib/extensions/base.py index 7b3c6f1c..e381e2c1 100644 --- a/lib/extensions/base.py +++ b/lib/extensions/base.py @@ -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 +import inkex from lxml.etree import Comment from stringcase import snakecase -import inkex - 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") - - -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}'] +from ..update import update_inkstitch_document - # 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__) diff --git a/lib/extensions/lettering.py b/lib/extensions/lettering.py index a396765b..b304a6f9 100644 --- a/lib/extensions/lettering.py +++ b/lib/extensions/lettering.py @@ -31,9 +31,11 @@ class LetteringFrame(wx.Frame): DEFAULT_FONT = "small_font" def __init__(self, *args, **kwargs): - # begin wxGlade: MyFrame.__init__ self.group = kwargs.pop('group') self.cancel_hook = kwargs.pop('on_cancel', None) + self.metadata = kwargs.pop('metadata', []) + + # begin wxGlade: MyFrame.__init__ wx.Frame.__init__(self, None, wx.ID_ANY, _("Ink/Stitch Lettering") ) @@ -492,8 +494,9 @@ class Lettering(CommandsExtension): return group def effect(self): + metadata = self.get_inkstitch_metadata() app = wx.App() - frame = LetteringFrame(group=self.get_or_create_group(), on_cancel=self.cancel) + frame = LetteringFrame(group=self.get_or_create_group(), on_cancel=self.cancel, metadata=metadata) # position left, center current_screen = wx.Display.GetFromPoint(wx.GetMousePosition()) diff --git a/lib/extensions/params.py b/lib/extensions/params.py index bf01153b..fb13c223 100644 --- a/lib/extensions/params.py +++ b/lib/extensions/params.py @@ -478,9 +478,11 @@ class SettingsFrame(wx.Frame): lc = wx.Locale() lc.Init(wx.LANGUAGE_DEFAULT) - # begin wxGlade: MyFrame.__init__ self.tabs_factory = kwargs.pop('tabs_factory', []) self.cancel_hook = kwargs.pop('on_cancel', None) + self.metadata = kwargs.pop('metadata', []) + + # begin wxGlade: MyFrame.__init__ wx.Frame.__init__(self, None, wx.ID_ANY, _("Embroidery Params") ) @@ -672,7 +674,8 @@ class Params(InkstitchExtension): classes.append(FillStitch) if element.get_style("stroke") is not None: classes.append(Stroke) - classes.append(SatinColumn) + if len(element.path) > 1: + classes.append(SatinColumn) return classes def get_nodes_by_class(self): @@ -785,8 +788,11 @@ class Params(InkstitchExtension): def effect(self): try: app = wx.App() + metadata = self.get_inkstitch_metadata() frame = SettingsFrame( - tabs_factory=self.create_tabs, on_cancel=self.cancel) + tabs_factory=self.create_tabs, + on_cancel=self.cancel, + metadata=metadata) # position left, center current_screen = wx.Display.GetFromPoint(wx.GetMousePosition()) diff --git a/lib/extensions/select_elements.py b/lib/extensions/select_elements.py index 8fa9ca9d..896e04b0 100644 --- a/lib/extensions/select_elements.py +++ b/lib/extensions/select_elements.py @@ -21,6 +21,7 @@ class SelectElements(InkstitchExtension): pars.add_argument("--info", type=str, dest="info") pars.add_argument("--select-running-stitch", type=Boolean, dest="running", default=False) + pars.add_argument("--running-stitch-condition", type=str, dest="running_stitch_condition", default="all") pars.add_argument("--select-ripples", type=Boolean, dest="ripples", default=False) pars.add_argument("--select-zigzag", type=Boolean, dest="zigzag", default=False) pars.add_argument("--select-manual", type=Boolean, dest="manual", default=False) @@ -101,7 +102,7 @@ class SelectElements(InkstitchExtension): def _select_stroke(self, element): select = False method = element.stroke_method - if self.options.running and method == 'running_stitch': + if self.options.running and method == 'running_stitch' and self._running_condition(element): select = True if self.options.ripples and method == 'ripple_stitch': select = True @@ -130,6 +131,11 @@ class SelectElements(InkstitchExtension): select = True return select + def _running_condition(self, element): + element_id = element.node.get_id() or '' + conditions = {'all': True, 'autorun-top': element_id.startswith('autorun'), 'autorun-underpath': element_id.startswith('underpath')} + return conditions[self.options.running_stitch_condition] + def _select_fill_underlay(self, element): underlay = {'all': True, 'no': not element.fill_underlay, 'yes': element.fill_underlay} return underlay[self.options.fill_underlay] diff --git a/lib/extensions/stroke_to_lpe_satin.py b/lib/extensions/stroke_to_lpe_satin.py index 4052207a..d162539b 100644 --- a/lib/extensions/stroke_to_lpe_satin.py +++ b/lib/extensions/stroke_to_lpe_satin.py @@ -26,6 +26,7 @@ class StrokeToLpeSatin(InkstitchExtension): self.arg_parser.add_argument("-l", "--length", type=float, default=15, dest="length") self.arg_parser.add_argument("-t", "--stretched", type=inkex.Boolean, default=False, dest="stretched") self.arg_parser.add_argument("-r", "--rungs", type=inkex.Boolean, default=False, dest="add_rungs") + self.arg_parser.add_argument("-s", "--path-specific", type=inkex.Boolean, default=True, dest="path_specific") def effect(self): if not self.svg.selection or not self.get_elements(): @@ -52,48 +53,79 @@ class StrokeToLpeSatin(InkstitchExtension): pattern_path = pattern_obj.get_path(self.options.add_rungs, min_width, max_width, length, self.svg.unit) pattern_node_type = pattern_obj.node_types + if not self.options.path_specific: + lpe = self._create_lpe_element(pattern, pattern_path, pattern_node_type) + + for element in self.elements: + if self.options.path_specific: + lpe = self._create_lpe_element(pattern, pattern_path, pattern_node_type, element) + if isinstance(element, SatinColumn): + self._process_satin_column(element, lpe) + elif isinstance(element, Stroke): + self._process_stroke(element, lpe) + + def _create_lpe_element(self, pattern, pattern_path, pattern_node_type, element=None): + # define id for the lpe path + if not element: + lpe_id = f'inkstitch-effect-{pattern}' + else: + lpe_id = f'inkstitch-effect-{pattern}-{element.id}' + + # it is possible, that there is already a path effect with this id, if so, use it + previous_lpe = self.svg.getElementById(lpe_id) + if previous_lpe is not None: + return previous_lpe + # the lpe 'pattern along path' has two options to repeat the pattern, get user input copy_type = 'repeated' if self.options.stretched is False else 'repeated_stretched' + lpe = inkex.PathEffect(attrib={'id': lpe_id, + 'effect': "skeletal", + 'is_visible': "true", + 'lpeversion': "1", + 'pattern': pattern_path, + 'copytype': copy_type, + 'prop_scale': "1", + 'scale_y_rel': "false", + 'spacing': "0", + 'normal_offset': "0", + 'tang_offset': "0", + 'prop_units': "false", + 'vertical_pattern': "false", + 'hide_knot': "false", + 'fuse_tolerance': "0.02", + 'pattern-nodetypes': pattern_node_type}) # add the path effect element to the defs section - self.lpe = inkex.PathEffect(attrib={'id': f'inkstitch-effect-{pattern}', - 'effect': "skeletal", - 'is_visible': "true", - 'lpeversion': "1", - 'pattern': pattern_path, - 'copytype': copy_type, - 'prop_scale': "1", - 'scale_y_rel': "false", - 'spacing': "0", - 'normal_offset': "0", - 'tang_offset': "0", - 'prop_units': "false", - 'vertical_pattern': "false", - 'hide_knot': "false", - 'fuse_tolerance': "0.02", - 'pattern-nodetypes': pattern_node_type}) - self.svg.defs.add(self.lpe) + self.svg.defs.add(lpe) + return lpe - for element in self.elements: - if isinstance(element, SatinColumn): - self._process_satin_column(element) - elif isinstance(element, Stroke): - self._process_stroke(element) + def _process_stroke(self, element, lpe): + element = self._ensure_path_element(element, lpe) - def _process_stroke(self, element): previous_effects = element.node.get(PATH_EFFECT, None) if not previous_effects: - element.node.set(PATH_EFFECT, self.lpe.get_id(as_url=1)) + element.node.set(PATH_EFFECT, lpe.get_id(as_url=1)) element.node.set(ORIGINAL_D, element.node.get('d', '')) else: - url = previous_effects + ';' + self.lpe.get_id(as_url=1) + url = previous_effects + ';' + lpe.get_id(as_url=1) element.node.set(PATH_EFFECT, url) element.node.pop('d') element.set_param('satin_column', 'true') element.node.style['stroke-width'] = self.svg.viewport_to_unit('0.756') - def _process_satin_column(self, element): + def _ensure_path_element(self, element, lpe): + # elements other than paths (rectangle, circles, etc.) can be handled by inkscape for lpe + # but they are way easier to handle for us if we turn them into paths + if element.node.TAG == 'path': + return element + + path_element = element.node.to_path_element() + parent = element.node.getparent() + parent.replace(element.node, path_element) + return Stroke(path_element) + + def _process_satin_column(self, element, lpe): current_effects = element.node.get(PATH_EFFECT, None) # there are possibly multiple path effects, let's check if inkstitch-effect is among them if not current_effects or 'inkstitch-effect' not in current_effects: @@ -106,10 +138,11 @@ class StrokeToLpeSatin(InkstitchExtension): inkstitch_effect = current_effects[inkstitch_effect_position][1:] # get the path effect element old_effect_element = self.svg.getElementById(inkstitch_effect) - # remove the old inkstitch-effect - old_effect_element.getparent().remove(old_effect_element) - # update the path effect link - current_effects[inkstitch_effect_position] = self.lpe.get_id(as_url=1) + # remove the old inkstitch-effect if it is path specific + if inkstitch_effect.endswith(element.id): + old_effect_element.getparent().remove(old_effect_element) + # update path effect link + current_effects[inkstitch_effect_position] = lpe.get_id(as_url=1) element.node.set(PATH_EFFECT, ';'.join(current_effects)) element.node.pop('d') diff --git a/lib/extensions/troubleshoot.py b/lib/extensions/troubleshoot.py index f7d979e7..fdc7fa9e 100644 --- a/lib/extensions/troubleshoot.py +++ b/lib/extensions/troubleshoot.py @@ -7,21 +7,25 @@ import textwrap import inkex -from .base import InkstitchExtension from ..commands import add_layer_commands from ..elements.validation import (ObjectTypeWarning, ValidationError, ValidationWarning) from ..i18n import _ +from ..svg import PIXELS_PER_MM from ..svg.path import get_correction_transform -from ..svg.tags import (INKSCAPE_GROUPMODE, INKSCAPE_LABEL, SODIPODI_ROLE) +from ..svg.tags import INKSCAPE_GROUPMODE, INKSCAPE_LABEL, SODIPODI_ROLE +from .base import InkstitchExtension class Troubleshoot(InkstitchExtension): - def effect(self): + def __init__(self, *args, **kwargs): + InkstitchExtension.__init__(self, *args, **kwargs) + self.arg_parser.add_argument("-p", "--pointer-size", type=float, default=5, dest="pointer_size_mm") + self.arg_parser.add_argument("-f", "--font-size", type=float, default=2, dest="font_size_mm") + def effect(self): self.create_troubleshoot_layer() - problem_types = {'error': set(), 'warning': set(), 'type_warning': set()} if self.get_elements(True): @@ -50,6 +54,8 @@ class Troubleshoot(InkstitchExtension): def insert_pointer(self, problem): correction_transform = get_correction_transform(self.troubleshoot_layer) + pointer_size = self.options.pointer_size_mm * PIXELS_PER_MM + font_size = self.options.font_size_mm * PIXELS_PER_MM if isinstance(problem, ValidationWarning): fill_color = "#ffdd00" @@ -61,12 +67,14 @@ class Troubleshoot(InkstitchExtension): fill_color = "#ff9900" layer = self.type_warning_group - pointer_style = "stroke:#000000;stroke-width:0.2;fill:%s;" % (fill_color) - text_style = "fill:%s;stroke:#000000;stroke-width:0.2;font-size:8px;text-align:center;text-anchor:middle" % (fill_color) + pointer_style = f'stroke:#000000;stroke-width:0.1;fill:{ fill_color }' + text_style = f'fill:{ fill_color };stroke:#000000;stroke-width:0.1;font-size:{ font_size }px;text-align:center;text-anchor:middle' + pointer_path = f'm {problem.position.x},{problem.position.y} {pointer_size / 5},{pointer_size} ' \ + f'h -{pointer_size / 2.5} l {pointer_size / 5},-{pointer_size}' path = inkex.PathElement(attrib={ "id": self.uniqueId("inkstitch__invalid_pointer__"), - "d": "m %s,%s 4,20 h -8 l 4,-20" % (problem.position.x, problem.position.y), + "d": pointer_path, "style": pointer_style, INKSCAPE_LABEL: _('Invalid Pointer'), "transform": correction_transform @@ -76,7 +84,7 @@ class Troubleshoot(InkstitchExtension): text = inkex.TextElement(attrib={ INKSCAPE_LABEL: _('Description'), "x": str(problem.position.x), - "y": str(float(problem.position.y) + 30), + "y": str(float(problem.position.y) + pointer_size + font_size), "transform": correction_transform, "style": text_style }) |
