summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/elements/clone.py4
-rw-r--r--lib/elements/satin_column.py24
-rw-r--r--lib/elements/stroke.py3
-rw-r--r--lib/extensions/__init__.py4
-rw-r--r--lib/extensions/apply_threadlist.py (renamed from lib/extensions/import_threadlist.py)9
-rw-r--r--lib/extensions/auto_satin.py2
-rw-r--r--lib/extensions/base.py14
-rw-r--r--lib/extensions/break_apart.py2
-rw-r--r--lib/extensions/cleanup.py2
-rw-r--r--lib/extensions/convert_to_satin.py2
-rw-r--r--lib/extensions/convert_to_stroke.py2
-rw-r--r--lib/extensions/cut_satin.py2
-rw-r--r--lib/extensions/cutwork_segmentation.py2
-rw-r--r--lib/extensions/duplicate_params.py30
-rw-r--r--lib/extensions/flip.py2
-rw-r--r--lib/extensions/lettering.py14
-rw-r--r--lib/extensions/object_commands.py2
-rw-r--r--lib/extensions/palette_split_text.py37
-rw-r--r--lib/extensions/print_pdf.py2
-rw-r--r--lib/extensions/remove_embroidery_settings.py18
-rw-r--r--lib/extensions/reorder.py14
-rw-r--r--lib/extensions/selection_to_pattern.py2
-rw-r--r--lib/extensions/troubleshoot.py4
-rw-r--r--lib/lettering/font.py4
-rw-r--r--lib/stitch_plan/stitch_plan.py14
-rw-r--r--lib/svg/path.py4
-rw-r--r--lib/svg/units.py2
27 files changed, 120 insertions, 101 deletions
diff --git a/lib/elements/clone.py b/lib/elements/clone.py
index f430f9b3..f408917d 100644
--- a/lib/elements/clone.py
+++ b/lib/elements/clone.py
@@ -5,8 +5,6 @@
from math import atan, degrees
-import inkex
-
from ..commands import is_command, is_command_symbol
from ..i18n import _
from ..svg.path import get_node_transform
@@ -128,7 +126,7 @@ class Clone(EmbroideryElement):
return patches
def get_clone_style(self, style_name, node, default=None):
- style = inkex.styles.AttrFallbackStyle(node).get(style_name) or default
+ style = node.style[style_name] or default
return style
def center(self, source_node):
diff --git a/lib/elements/satin_column.py b/lib/elements/satin_column.py
index 497ee471..a30f16d4 100644
--- a/lib/elements/satin_column.py
+++ b/lib/elements/satin_column.py
@@ -6,17 +6,18 @@
from copy import deepcopy
from itertools import chain
-from inkex import paths
from shapely import affinity as shaffinity
from shapely import geometry as shgeo
from shapely.ops import nearest_points
-from .element import EmbroideryElement, param
-from .validation import ValidationError, ValidationWarning
+from inkex import paths
+
from ..i18n import _
from ..stitch_plan import StitchGroup
from ..svg import line_strings_to_csp, point_lists_to_csp
from ..utils import Point, cache, collapse_duplicate_point, cut
+from .element import EmbroideryElement, param
+from .validation import ValidationError, ValidationWarning
class SatinHasFillError(ValidationError):
@@ -51,6 +52,15 @@ class UnequalPointsError(ValidationError):
]
+class NotStitchableError(ValidationError):
+ name = _("Not stitchable satin column")
+ description = _("A satin column consists out of two rails and one or more rungs. This satin column may have a different setup.")
+ steps_to_solve = [
+ _('Make sure your satin column is not a combination of multiple satin columns.'),
+ _('Go to our website and read how a satin column should look like https://inkstitch.org/docs/stitches/satin-column/'),
+ ]
+
+
rung_message = _("Each rung should intersect both rails once.")
@@ -422,6 +432,9 @@ class SatinColumn(EmbroideryElement):
if not intersection.is_empty and not isinstance(intersection, shgeo.Point):
yield TooManyIntersectionsError(rung.interpolate(0.5, normalized=True))
+ if not self.to_stitch_groups():
+ yield NotStitchableError(self.shape.centroid)
+
def reverse(self):
"""Return a new SatinColumn like this one but in the opposite direction.
@@ -859,7 +872,7 @@ class SatinColumn(EmbroideryElement):
points.append(Point(split_point.x, split_point.y))
return [points, split_count]
- def to_stitch_groups(self, last_patch):
+ def to_stitch_groups(self, last_patch=None):
# Stitch a variable-width satin column, zig-zagging between two paths.
# The algorithm will draw zigzags between each consecutive pair of
@@ -884,4 +897,7 @@ class SatinColumn(EmbroideryElement):
else:
patch += self.do_satin()
+ if not patch.stitches:
+ return []
+
return [patch]
diff --git a/lib/elements/stroke.py b/lib/elements/stroke.py
index 763167ad..307c78b8 100644
--- a/lib/elements/stroke.py
+++ b/lib/elements/stroke.py
@@ -77,7 +77,8 @@ class Stroke(EmbroideryElement):
default="1",
sort_index=1)
def repeats(self):
- return self.get_int_param("repeats", 1)
+ repeats = self.get_int_param("repeats", 1)
+ return max(1, repeats)
@property
def paths(self):
diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py
index 4c5e1a71..b6e0d1d1 100644
--- a/lib/extensions/__init__.py
+++ b/lib/extensions/__init__.py
@@ -5,6 +5,7 @@
from lib.extensions.troubleshoot import Troubleshoot
+from .apply_threadlist import ApplyThreadlist
from .auto_satin import AutoSatin
from .break_apart import BreakApart
from .cleanup import Cleanup
@@ -18,7 +19,6 @@ from .embroider_settings import EmbroiderSettings
from .flip import Flip
from .generate_palette import GeneratePalette
from .global_commands import GlobalCommands
-from .import_threadlist import ImportThreadlist
from .input import Input
from .install import Install
from .install_custom_palette import InstallCustomPalette
@@ -71,7 +71,7 @@ __all__ = extensions = [StitchPlanPreview,
RemoveEmbroiderySettings,
Cleanup,
BreakApart,
- ImportThreadlist,
+ ApplyThreadlist,
InstallCustomPalette,
GeneratePalette,
PaletteSplitText,
diff --git a/lib/extensions/import_threadlist.py b/lib/extensions/apply_threadlist.py
index f7fe0bcc..31861513 100644
--- a/lib/extensions/import_threadlist.py
+++ b/lib/extensions/apply_threadlist.py
@@ -14,7 +14,12 @@ from ..threads import ThreadCatalog
from .base import InkstitchExtension
-class ImportThreadlist(InkstitchExtension):
+class ApplyThreadlist(InkstitchExtension):
+ '''
+ Applies colors of a thread list to elements
+ Count of colors and elements should fit together
+ Use case: reapply colors to e.g. a dst file
+ '''
def __init__(self, *args, **kwargs):
InkstitchExtension.__init__(self, *args, **kwargs)
self.arg_parser.add_argument("-f", "--filepath", type=str, default="", dest="filepath")
@@ -23,7 +28,7 @@ class ImportThreadlist(InkstitchExtension):
def effect(self):
# Remove selection, we want all the elements in the document
- self.svg.selected.clear()
+ self.svg.selection.clear()
if not self.get_elements():
return
diff --git a/lib/extensions/auto_satin.py b/lib/extensions/auto_satin.py
index 62fb15af..dfb1a87e 100644
--- a/lib/extensions/auto_satin.py
+++ b/lib/extensions/auto_satin.py
@@ -44,7 +44,7 @@ class AutoSatin(CommandsExtension):
if not self.get_elements():
return
- if not self.svg.selected:
+ if not self.svg.selection:
# L10N auto-route satin columns extension
inkex.errormsg(_("Please select one or more satin columns."))
return False
diff --git a/lib/extensions/base.py b/lib/extensions/base.py
index a065b5cc..75a07c5a 100644
--- a/lib/extensions/base.py
+++ b/lib/extensions/base.py
@@ -121,7 +121,7 @@ class InkstitchExtension(inkex.Effect):
return current_layer
def no_elements_error(self):
- if self.svg.selected:
+ 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")
else:
@@ -156,8 +156,8 @@ class InkstitchExtension(inkex.Effect):
if is_command(node) or node.get(CONNECTOR_TYPE):
return []
- if self.svg.selected:
- if node.get("id") in self.svg.selected:
+ if self.svg.selection:
+ if node.get("id") in self.svg.selection:
selected = True
else:
# if the user didn't select anything that means we process everything
@@ -188,14 +188,6 @@ class InkstitchExtension(inkex.Effect):
self.no_elements_error()
return False
- def get_selected_in_order(self):
- selected = []
- for i in self.options.ids:
- path = '//*[@id="%s"]' % i
- for node in self.document.xpath(path, namespaces=inkex.NSS):
- selected.append(node)
- return selected
-
def elements_to_stitch_groups(self, elements):
patches = []
for element in elements:
diff --git a/lib/extensions/break_apart.py b/lib/extensions/break_apart.py
index b16c901d..5bfd88a4 100644
--- a/lib/extensions/break_apart.py
+++ b/lib/extensions/break_apart.py
@@ -27,7 +27,7 @@ class BreakApart(InkstitchExtension):
self.minimum_size = 5
def effect(self): # noqa: C901
- if not self.svg.selected:
+ if not self.svg.selection:
inkex.errormsg(_("Please select one or more fill areas to break apart."))
return
diff --git a/lib/extensions/cleanup.py b/lib/extensions/cleanup.py
index 99b72a81..a38818b8 100644
--- a/lib/extensions/cleanup.py
+++ b/lib/extensions/cleanup.py
@@ -24,7 +24,7 @@ class Cleanup(InkstitchExtension):
self.fill_threshold = self.options.fill_threshold
self.stroke_threshold = self.options.stroke_threshold
- self.svg.selected.clear()
+ self.svg.selection.clear()
count = 0
svg = self.document.getroot()
diff --git a/lib/extensions/convert_to_satin.py b/lib/extensions/convert_to_satin.py
index 577cd7c0..9950b2e8 100644
--- a/lib/extensions/convert_to_satin.py
+++ b/lib/extensions/convert_to_satin.py
@@ -31,7 +31,7 @@ class ConvertToSatin(InkstitchExtension):
if not self.get_elements():
return
- if not self.svg.selected:
+ if not self.svg.selection:
inkex.errormsg(_("Please select at least one line to convert to a satin column."))
return
diff --git a/lib/extensions/convert_to_stroke.py b/lib/extensions/convert_to_stroke.py
index dfaef615..5a2ab23c 100644
--- a/lib/extensions/convert_to_stroke.py
+++ b/lib/extensions/convert_to_stroke.py
@@ -21,7 +21,7 @@ class ConvertToStroke(InkstitchExtension):
self.arg_parser.add_argument("-k", "--keep_satin", type=inkex.Boolean, default=False, dest="keep_satin")
def effect(self):
- if not self.svg.selected or not self.get_elements():
+ if not self.svg.selection or not self.get_elements():
inkex.errormsg(_("Please select at least one satin column to convert to a running stitch."))
return
diff --git a/lib/extensions/cut_satin.py b/lib/extensions/cut_satin.py
index fcd1ca06..3d38c7d8 100644
--- a/lib/extensions/cut_satin.py
+++ b/lib/extensions/cut_satin.py
@@ -16,7 +16,7 @@ class CutSatin(InkstitchExtension):
if not self.get_elements():
return
- if not self.svg.selected:
+ if not self.svg.selection:
inkex.errormsg(_("Please select one or more satin columns to cut."))
return
diff --git a/lib/extensions/cutwork_segmentation.py b/lib/extensions/cutwork_segmentation.py
index 1b0c568e..672aeade 100644
--- a/lib/extensions/cutwork_segmentation.py
+++ b/lib/extensions/cutwork_segmentation.py
@@ -43,7 +43,7 @@ class CutworkSegmentation(InkstitchExtension):
self.arg_parser.add_argument("-k", "--keep_original", type=inkex.Boolean, default=False, dest="keep_original")
def effect(self):
- if not self.svg.selected:
+ if not self.svg.selection:
inkex.errormsg(_("Please select one or more stroke elements."))
return
diff --git a/lib/extensions/duplicate_params.py b/lib/extensions/duplicate_params.py
index 9fcdbf1c..46110691 100644
--- a/lib/extensions/duplicate_params.py
+++ b/lib/extensions/duplicate_params.py
@@ -3,10 +3,9 @@
# Copyright (c) 2021 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
-import inkex
+from inkex import NSS, ShapeElement, errormsg
from ..i18n import _
-from ..svg.tags import EMBROIDERABLE_TAGS, SVG_GROUP_TAG
from .base import InkstitchExtension
@@ -14,36 +13,29 @@ class DuplicateParams(InkstitchExtension):
# Transfer inkstitch namespaced attributes from the first selected element to the rest of selection
def effect(self):
- objects = self.get_selected_in_order()
+ objects = self.svg.selection.get(ShapeElement)
if len(objects) < 2:
- inkex.errormsg(_("This function copies Ink/Stitch parameters from the first selected element to the rest of the selection. "
- "Please select at least two elements."))
+ errormsg(_("This function copies Ink/Stitch parameters from the first selected element to the rest of the selection. "
+ "Please select at least two elements."))
return
- copy_from = objects[0]
+ copy_from = objects.first()
copy_from_attribs = self.get_inkstitch_attributes(copy_from)
- copy_to_selection = objects[1:]
- self.copy_to = []
-
- # extract copy_to group elements
- for element in copy_to_selection:
- if element.tag == SVG_GROUP_TAG:
- for descendant in element.iterdescendants(EMBROIDERABLE_TAGS):
- self.copy_to.append(descendant)
- elif element.tag in EMBROIDERABLE_TAGS:
- self.copy_to.append(element)
+ copy_to = objects
# remove inkstitch params from copy_to elements
- for element in self.copy_to:
+ for element in copy_to:
+ if element == copy_to.first():
+ continue
copy_to_attribs = self.get_inkstitch_attributes(element)
for attrib in copy_to_attribs:
element.pop(attrib)
# paste inkstitch params from copy_from element to copy_to elements
for attrib in copy_from_attribs:
- for element in self.copy_to:
+ for element in copy_to:
element.attrib[attrib] = copy_from_attribs[attrib]
def get_inkstitch_attributes(self, node):
- return {k: v for k, v in node.attrib.iteritems() if inkex.NSS['inkstitch'] in k}
+ return {k: v for k, v in node.attrib.iteritems() if NSS['inkstitch'] in k}
diff --git a/lib/extensions/flip.py b/lib/extensions/flip.py
index 743f1701..893dc038 100644
--- a/lib/extensions/flip.py
+++ b/lib/extensions/flip.py
@@ -24,7 +24,7 @@ class Flip(InkstitchExtension):
if not self.get_elements():
return
- if not self.svg.selected:
+ if not self.svg.selection:
inkex.errormsg(_("Please select one or more satin columns to flip."))
return
diff --git a/lib/extensions/lettering.py b/lib/extensions/lettering.py
index 35509eb6..658f2bc7 100644
--- a/lib/extensions/lettering.py
+++ b/lib/extensions/lettering.py
@@ -9,10 +9,11 @@ import sys
from base64 import b64decode
import appdirs
-import inkex
import wx
import wx.adv
+import inkex
+
from ..elements import nodes_to_elements
from ..gui import PresetsPanel, SimulatorPreview, info_dialog
from ..i18n import _
@@ -272,12 +273,15 @@ class LetteringFrame(wx.Frame):
font.render_text(self.settings.text, destination_group, back_and_forth=self.settings.back_and_forth, trim=self.settings.trim)
except FontError as e:
if raise_error:
- inkex.errormsg("Error: Text cannot be applied to the document.\n%s" % e)
+ inkex.errormsg(_("Error: Text cannot be applied to the document.\n%s") % e)
return
else:
pass
- if self.settings.scale != 100:
+ # destination_group isn't always the text scaling group (but also the parent group)
+ # the text scaling group label is dependend on the user language, so it would break in international file exchange if we used it
+ # scaling (correction transform) on the parent group is already applied, so let's use that for recognition
+ if self.settings.scale != 100 and not destination_group.get('transform', None):
destination_group.attrib['transform'] = 'scale(%s)' % (self.settings.scale / 100.0)
def generate_patches(self, abort_early=None):
@@ -395,10 +399,10 @@ class Lettering(CommandsExtension):
self.cancelled = True
def get_or_create_group(self):
- if self.svg.selected:
+ if self.svg.selection:
groups = set()
- for node in self.svg.selected.values():
+ for node in self.svg.selection:
if node.tag == SVG_GROUP_TAG and INKSTITCH_LETTERING in node.attrib:
groups.add(node)
diff --git a/lib/extensions/object_commands.py b/lib/extensions/object_commands.py
index 851d4a34..a3ad6128 100644
--- a/lib/extensions/object_commands.py
+++ b/lib/extensions/object_commands.py
@@ -17,7 +17,7 @@ class ObjectCommands(CommandsExtension):
if not self.get_elements():
return
- if not self.svg.selected:
+ if not self.svg.selection:
inkex.errormsg(_("Please select one or more objects to which to attach commands."))
return
diff --git a/lib/extensions/palette_split_text.py b/lib/extensions/palette_split_text.py
index 6c024ced..3257d694 100644
--- a/lib/extensions/palette_split_text.py
+++ b/lib/extensions/palette_split_text.py
@@ -22,22 +22,21 @@ class PaletteSplitText(InkstitchExtension):
line_height = self.options.line_height
- for text in self.svg.selected:
- if type(text) == inkex.elements.TextElement:
- parent = text.getparent()
- content = text.get_text()
- lines = content.split('\n')
- lines.reverse()
- style = text.get('style')
- x = text.get('x')
- y = text.get('y')
- y = float(y) + (len(lines) - 1) * line_height
- for line in lines:
- element = inkex.TextElement()
- element.text = line
- element.set('style', style)
- element.set('x', x)
- element.set('y', str(y))
- y = float(y) - line_height
- parent.insert(0, element)
- parent.remove(text)
+ for text in self.svg.selection.get(inkex.elements.TextElement):
+ parent = text.getparent()
+ content = text.get_text()
+ lines = content.split('\n')
+ lines.reverse()
+ style = text.get('style')
+ x = text.get('x')
+ y = text.get('y')
+ y = float(y) + (len(lines) - 1) * line_height
+ for line in lines:
+ element = inkex.TextElement()
+ element.text = line
+ element.set('style', style)
+ element.set('x', x)
+ element.set('y', str(y))
+ y = float(y) - line_height
+ parent.insert(0, element)
+ parent.remove(text)
diff --git a/lib/extensions/print_pdf.py b/lib/extensions/print_pdf.py
index 16ca3a08..63c3c699 100644
--- a/lib/extensions/print_pdf.py
+++ b/lib/extensions/print_pdf.py
@@ -301,7 +301,7 @@ class Print(InkstitchExtension):
# objects. It's almost certain they meant to print the whole design.
# If they really wanted to print just a few objects, they could set
# the rest invisible temporarily.
- self.svg.selected.clear()
+ self.svg.selection.clear()
if not self.get_elements():
return
diff --git a/lib/extensions/remove_embroidery_settings.py b/lib/extensions/remove_embroidery_settings.py
index 31c15cd9..d8e6cb0e 100644
--- a/lib/extensions/remove_embroidery_settings.py
+++ b/lib/extensions/remove_embroidery_settings.py
@@ -3,11 +3,10 @@
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
-from inkex import NSS, Boolean
+from inkex import NSS, Boolean, ShapeElement
from ..commands import find_commands
from ..svg.svg import find_elements
-from ..svg.tags import EMBROIDERABLE_TAGS, SVG_GROUP_TAG
from .base import InkstitchExtension
@@ -36,7 +35,7 @@ class RemoveEmbroiderySettings(InkstitchExtension):
self.remove_element(print_setting)
def remove_params(self):
- if not self.svg.selected:
+ if not self.svg.selection:
xpath = ".//svg:path|.//svg:circle|.//svg:rect|.//svg:ellipse"
elements = find_elements(self.svg, xpath)
self.remove_inkstitch_attributes(elements)
@@ -45,7 +44,7 @@ class RemoveEmbroiderySettings(InkstitchExtension):
self.remove_inkstitch_attributes(elements)
def remove_commands(self):
- if not self.svg.selected:
+ if not self.svg.selection:
# remove intact command groups
xpath = ".//svg:g[starts-with(@id,'command_group')]"
groups = find_elements(self.svg, xpath)
@@ -58,7 +57,7 @@ class RemoveEmbroiderySettings(InkstitchExtension):
group = command.connector.getparent()
group.getparent().remove(group)
- if not self.svg.selected:
+ if not self.svg.selection:
# remove standalone commands and ungrouped object commands
standalone_commands = ".//svg:use[starts-with(@xlink:href, '#inkstitch_')]|.//svg:path[starts-with(@id, 'command_connector')]"
self.remove_elements(standalone_commands)
@@ -68,14 +67,7 @@ class RemoveEmbroiderySettings(InkstitchExtension):
self.remove_elements(symbols)
def get_selected_elements(self):
- elements = []
- for node in self.svg.selected.values():
- if node.tag == SVG_GROUP_TAG:
- for child in node.iterdescendants(EMBROIDERABLE_TAGS):
- elements.append(child)
- else:
- elements.append(node)
- return elements
+ return self.svg.selection.get(ShapeElement)
def remove_elements(self, xpath):
elements = find_elements(self.svg, xpath)
diff --git a/lib/extensions/reorder.py b/lib/extensions/reorder.py
index be478e39..83ecfe26 100644
--- a/lib/extensions/reorder.py
+++ b/lib/extensions/reorder.py
@@ -3,6 +3,9 @@
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
+from inkex import errormsg
+
+from ..i18n import _
from .base import InkstitchExtension
@@ -11,10 +14,15 @@ class Reorder(InkstitchExtension):
# were selected.
def effect(self):
- objects = self.get_selected_in_order()
+ objects = self.svg.selection
+
+ if not objects:
+ errormsg(_("Please select at least to elements to reorder."))
+ return
- for obj in objects[1:]:
- obj.getparent().remove(obj)
+ for obj in objects:
+ if not obj == objects.first():
+ obj.getparent().remove(obj)
insert_parent = objects[0].getparent()
insert_pos = insert_parent.index(objects[0])
diff --git a/lib/extensions/selection_to_pattern.py b/lib/extensions/selection_to_pattern.py
index b426e5db..8b41ff86 100644
--- a/lib/extensions/selection_to_pattern.py
+++ b/lib/extensions/selection_to_pattern.py
@@ -17,7 +17,7 @@ class SelectionToPattern(InkstitchExtension):
if not self.get_elements():
return
- if not self.svg.selected:
+ if not self.svg.selection:
inkex.errormsg(_("Please select at least one object to be marked as a pattern."))
return
diff --git a/lib/extensions/troubleshoot.py b/lib/extensions/troubleshoot.py
index c4b386d9..f7d979e7 100644
--- a/lib/extensions/troubleshoot.py
+++ b/lib/extensions/troubleshoot.py
@@ -128,7 +128,7 @@ class Troubleshoot(InkstitchExtension):
self.warning_group = warning_group
self.type_warning_group = type_warning_group
- def add_descriptions(self, problem_types):
+ def add_descriptions(self, problem_types): # noqa: C901
svg = self.document.getroot()
# We could use svg.viewport_width, but then we would need to do unit conversions,
@@ -174,6 +174,8 @@ class Troubleshoot(InkstitchExtension):
text.append([problem.name, "font-weight: bold; fill: %s;" % text_color])
text.append([problem.description, "font-size: 3px;"])
text.append(["", ""])
+ if problem.steps_to_solve:
+ text.append([_("Possible solutions"), "font-weight: bold; text-decoration: underline; font-size: 4px;"])
for step in problem.steps_to_solve:
text.append([step, "font-size: 4px;"])
text.append(["", ""])
diff --git a/lib/lettering/font.py b/lib/lettering/font.py
index 104e4b29..24ca86bf 100644
--- a/lib/lettering/font.py
+++ b/lib/lettering/font.py
@@ -87,14 +87,14 @@ class Font(object):
def _load_metadata(self):
try:
- with open(os.path.join(self.path, "font.json"), encoding="utf-8") as metadata_file:
+ with open(os.path.join(self.path, "font.json"), encoding="utf-8-sig") as metadata_file:
self.metadata = json.load(metadata_file)
except IOError:
pass
def _load_license(self):
try:
- with open(os.path.join(self.path, "LICENSE"), encoding="utf-8") as license_file:
+ with open(os.path.join(self.path, "LICENSE"), encoding="utf-8-sig") as license_file:
self.license = license_file.read()
except IOError:
pass
diff --git a/lib/stitch_plan/stitch_plan.py b/lib/stitch_plan/stitch_plan.py
index 74caa54a..4593781a 100644
--- a/lib/stitch_plan/stitch_plan.py
+++ b/lib/stitch_plan/stitch_plan.py
@@ -3,9 +3,14 @@
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
-from .ties import add_ties
-from .color_block import ColorBlock
+from sys import exit
+
+from inkex import errormsg
+
+from ..i18n import _
from ..svg import PIXELS_PER_MM
+from .color_block import ColorBlock
+from .ties import add_ties
def stitch_groups_to_stitch_plan(stitch_groups, collapse_len=None, disable_ties=False): # noqa: C901
@@ -17,6 +22,11 @@ def stitch_groups_to_stitch_plan(stitch_groups, collapse_len=None, disable_ties=
* adds jump-stitches between stitch_group if necessary
"""
+ if not stitch_groups:
+ errormsg(_("There is no selected stitchable element. Please run "
+ "Extensions > Ink/Stitch > Troubleshoot > Troubleshoot objects in case you have expected a stitchout."))
+ exit(1)
+
if collapse_len is None:
collapse_len = 3.0
collapse_len = collapse_len * PIXELS_PER_MM
diff --git a/lib/svg/path.py b/lib/svg/path.py
index 53cf80f2..c33a7a8f 100644
--- a/lib/svg/path.py
+++ b/lib/svg/path.py
@@ -24,7 +24,7 @@ def compose_parent_transforms(node, mat):
trans = node.get('transform')
if trans:
- mat = inkex.transforms.Transform(trans) * mat
+ mat = inkex.transforms.Transform(trans) @ mat
if node.getparent() is not None:
if node.getparent().tag in [SVG_GROUP_TAG, SVG_LINK_TAG]:
mat = compose_parent_transforms(node.getparent(), mat)
@@ -47,7 +47,7 @@ def get_node_transform(node):
# add in the transform implied by the viewBox
viewbox_transform = get_viewbox_transform(node.getroottree().getroot())
- transform = viewbox_transform * transform
+ transform = viewbox_transform @ transform
return transform
diff --git a/lib/svg/units.py b/lib/svg/units.py
index f229c2c3..e8a21c18 100644
--- a/lib/svg/units.py
+++ b/lib/svg/units.py
@@ -149,7 +149,7 @@ def get_viewbox_transform(node):
sx = sy = max(sx, sy) if 'slice' in aspect_ratio else min(sx, sy)
scale_transform = inkex.transforms.Transform("scale(%f, %f)" % (sx, sy))
- transform = transform * scale_transform
+ transform = transform @ scale_transform
except ZeroDivisionError:
pass