Fix lettering scale, etc. (#1620)

* fix lettering scale
* adapt to updated inkex:  transform operator, selections
* fix #1597
* no traceback error message on broken satin columns
* highlight troubleshoot "steps to solve" through additional headline
* set a minimum value for running stitch repeats
* rename "import" thread list to "apply" thread list
pull/1644/head
Kaalleen 2022-04-24 08:27:42 +02:00 zatwierdzone przez GitHub
rodzic 4058712139
commit f9d57f6ea5
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
30 zmienionych plików z 131 dodań i 108 usunięć

Wyświetl plik

@ -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):

Wyświetl plik

@ -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]

Wyświetl plik

@ -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):

Wyświetl plik

@ -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,

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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:

Wyświetl plik

@ -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

Wyświetl plik

@ -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()

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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}

Wyświetl plik

@ -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

Wyświetl plik

@ -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)

Wyświetl plik

@ -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

Wyświetl plik

@ -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)

Wyświetl plik

@ -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

Wyświetl plik

@ -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)

Wyświetl plik

@ -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
for obj in objects[1:]:
obj.getparent().remove(obj)
if not objects:
errormsg(_("Please select at least to elements to reorder."))
return
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])

Wyświetl plik

@ -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

Wyświetl plik

@ -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(["", ""])

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension translationdomain="inkstitch" xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<name>Import Threadlist</name>
<id>org.inkstitch.import_threadlist</id>
<param name="extension" type="string" gui-hidden="true">import_threadlist</param>
<name>Apply Threadlist</name>
<id>org.inkstitch.apply_threadlist</id>
<param name="extension" type="string" gui-hidden="true">apply_threadlist</param>
<param name="filepath" type="path" gui-text="Choose file" mode="file" filetypes="txt"/>
<param name="method" type="optiongroup" gui-text="Choose method">
<option value="1">Import Ink/Stitch threadlist</option>
<option value="2">Import other threadlist*</option>
<option value="1">Apply Ink/Stitch threadlist</option>
<option value="2">Apply other threadlist*</option>
</param>
<param name="palette" type="enum" gui-text="*Choose color palette">
{%- for item in threadcatalog %}

Wyświetl plik

@ -8,7 +8,9 @@
<object-type>all</object-type>
<effects-menu>
<submenu name="Ink/Stitch" translatable="no">
<submenu name="Commands" />
<submenu name="Commands">
<submenu name="View" />
</submenu>
</submenu>
</effects-menu>
</effect>

Wyświetl plik

@ -7,7 +7,9 @@
<object-type>all</object-type>
<effects-menu>
<submenu name="Ink/Stitch" translatable="no">
<submenu name="Commands" />
<submenu name="Commands">
<submenu name="View" />
</submenu>
</submenu>
</effects-menu>
</effect>