summaryrefslogtreecommitdiff
path: root/lib/extensions
diff options
context:
space:
mode:
Diffstat (limited to 'lib/extensions')
-rw-r--r--lib/extensions/base.py97
-rw-r--r--lib/extensions/lettering.py7
-rw-r--r--lib/extensions/params.py12
-rw-r--r--lib/extensions/select_elements.py8
-rw-r--r--lib/extensions/stroke_to_lpe_satin.py93
-rw-r--r--lib/extensions/troubleshoot.py24
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
})