summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorClaudine Peyrat <88194877+claudinepeyrat06@users.noreply.github.com>2022-11-22 10:46:44 +0100
committerGitHub <noreply@github.com>2022-11-22 10:46:44 +0100
commit43a31ba80ee844699a52341ff7d6ef6b881e6276 (patch)
tree7735063c054e1cdc2a6d6da386a516f675177dec /lib
parente761d8b42c178d3e9a940963234d4cd62c777f59 (diff)
Claudine/lettering with trims (#1866)
* add trim after each letter allow to add a trim after each letter * add trim after letter, word or line rewriting everythng * style correction correcting all that is signaled by make style * corrections i don't understand why i add to modify get_command_pos in commands. If I don't i can not add a command at the end of a glyph that ends with a polyline (eg B from Fold) * replace checkbox with dropdown * rename variables in English * use same trim methods for auto_satin and non auto_satin * check if trim option is never strip lines of text Co-authored-by: Claudine <claudine@MacBook-Pro-2.local> Co-authored-by: Kaalleen <reni@allenka.de> Co-authored-by: Kaalleen <36401965+kaalleen@users.noreply.github.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/commands.py8
-rw-r--r--lib/extensions/lettering.py31
-rw-r--r--lib/lettering/font.py80
3 files changed, 94 insertions, 25 deletions
diff --git a/lib/commands.py b/lib/commands.py
index a7affb6d..6280fc0c 100644
--- a/lib/commands.py
+++ b/lib/commands.py
@@ -347,7 +347,13 @@ def get_command_pos(element, index, total):
# Put command symbols 30 pixels out from the shape, spaced evenly around it.
# get a line running 30 pixels out from the shape
- outline = element.shape.buffer(30).exterior
+
+ if not isinstance(element.shape.buffer(30), shgeo.MultiPolygon):
+ outline = element.shape.buffer(30).exterior
+ else:
+ polygons = element.shape.buffer(30).geoms
+ polygon = polygons[len(polygons)-1]
+ outline = polygon.exterior
# find the top center point on the outline and start there
top_center = shgeo.Point(outline.centroid.x, outline.bounds[1])
diff --git a/lib/extensions/lettering.py b/lib/extensions/lettering.py
index c870a764..40fd48af 100644
--- a/lib/extensions/lettering.py
+++ b/lib/extensions/lettering.py
@@ -71,8 +71,9 @@ class LetteringFrame(wx.Frame):
self.back_and_forth_checkbox = wx.CheckBox(self, label=_("Stitch lines of text back and forth"))
self.back_and_forth_checkbox.Bind(wx.EVT_CHECKBOX, lambda event: self.on_change("back_and_forth", event))
- self.trim_checkbox = wx.CheckBox(self, label=_("Add trims"))
- self.trim_checkbox.Bind(wx.EVT_CHECKBOX, lambda event: self.on_change("trim", event))
+ self.trim_option_choice = wx.Choice(self, choices=["Never", "after each line", "after each word", "after each letter"],
+ name=_("Add trim after"))
+ self.trim_option_choice.Bind(wx.EVT_CHOICE, lambda event: self.on_trim_option_change(event))
# text editor
self.text_input_box = wx.StaticBox(self, wx.ID_ANY, label=_("Text"))
@@ -105,7 +106,8 @@ class LetteringFrame(wx.Frame):
"text": "",
"back_and_forth": False,
"font": None,
- "scale": 100
+ "scale": 100,
+ "trim_option": 0
})
if INKSTITCH_LETTERING in self.group.attrib:
@@ -123,7 +125,7 @@ class LetteringFrame(wx.Frame):
def apply_settings(self):
"""Make the settings in self.settings visible in the UI."""
self.back_and_forth_checkbox.SetValue(bool(self.settings.back_and_forth))
- self.trim_checkbox.SetValue(bool(self.settings.trim))
+ self.trim_option_choice.SetSelection(self.settings.trim_option)
self.set_initial_font(self.settings.font)
self.text_editor.SetValue(self.settings.text)
self.scale_spinner.SetValue(self.settings.scale)
@@ -219,6 +221,10 @@ class LetteringFrame(wx.Frame):
self.settings[attribute] = event.GetEventObject().GetValue()
self.preview.update()
+ def on_trim_option_change(self, event=None):
+ self.settings.trim_option = self.trim_option_choice.GetCurrentSelection()
+ self.preview.update()
+
def on_font_changed(self, event=None):
font = self.fonts.get(self.font_chooser.GetValue(), self.default_font)
self.settings.font = font.marked_custom_font_id
@@ -253,13 +259,6 @@ class LetteringFrame(wx.Frame):
self.back_and_forth_checkbox.Disable()
self.back_and_forth_checkbox.SetValue(False)
- if font.auto_satin:
- self.trim_checkbox.Enable()
- self.trim_checkbox.SetValue(bool(self.settings.trim))
- else:
- self.trim_checkbox.Disable()
- self.trim_checkbox.SetValue(False)
-
self.update_preview()
self.Layout()
@@ -314,7 +313,9 @@ class LetteringFrame(wx.Frame):
font = self.fonts.get(self.font_chooser.GetValue(), self.default_font)
try:
- font.render_text(self.settings.text, destination_group, back_and_forth=self.settings.back_and_forth, trim=self.settings.trim)
+ font.render_text(self.settings.text, destination_group, back_and_forth=self.settings.back_and_forth,
+ trim_option=self.settings.trim_option)
+
except FontError as e:
if raise_error:
inkex.errormsg(_("Error: Text cannot be applied to the document.\n%s") % e)
@@ -400,7 +401,11 @@ class LetteringFrame(wx.Frame):
# options
left_option_sizer = wx.BoxSizer(wx.VERTICAL)
left_option_sizer.Add(self.back_and_forth_checkbox, 1, wx.EXPAND | wx.LEFT | wx.TOP | wx.RIGHT, 5)
- left_option_sizer.Add(self.trim_checkbox, 1, wx.EXPAND | wx.LEFT | wx.TOP | wx.RIGHT | wx.BOTTOM, 5)
+
+ trim_option_sizer = wx.BoxSizer(wx.HORIZONTAL)
+ trim_option_sizer.Add(wx.StaticText(self, wx.ID_ANY, "Add trims"), 0, wx.LEFT | wx.ALIGN_CENTRE_VERTICAL, 5)
+ trim_option_sizer.Add(self.trim_option_choice, 1, wx.EXPAND | wx.LEFT | wx.TOP | wx.RIGHT | wx.BOTTOM, 5)
+ left_option_sizer.Add(trim_option_sizer, 0, wx.ALIGN_LEFT, 5)
font_scale_sizer = wx.BoxSizer(wx.HORIZONTAL)
font_scale_sizer.Add(wx.StaticText(self, wx.ID_ANY, "Scale"), 0, wx.LEFT | wx.ALIGN_CENTRE_VERTICAL, 0)
diff --git a/lib/lettering/font.py b/lib/lettering/font.py
index 328d2ba4..5a617da1 100644
--- a/lib/lettering/font.py
+++ b/lib/lettering/font.py
@@ -10,15 +10,16 @@ from random import randint
import inkex
-from ..commands import ensure_symbol
-from ..elements import nodes_to_elements
+from ..commands import add_commands, ensure_symbol
+from ..elements import FillStitch, Stroke, nodes_to_elements
from ..exceptions import InkstitchException
from ..extensions.lettering_custom_font_dir import get_custom_font_dir
from ..i18n import _, get_languages
-from ..marker import MARKER, ensure_marker
+from ..marker import MARKER, ensure_marker, has_marker
from ..stitches.auto_satin import auto_satin
-from ..svg.tags import (CONNECTION_END, CONNECTION_START, INKSCAPE_LABEL,
- SVG_PATH_TAG, SVG_USE_TAG, XLINK_HREF)
+from ..svg.tags import (CONNECTION_END, CONNECTION_START, EMBROIDERABLE_TAGS,
+ INKSCAPE_LABEL, SVG_GROUP_TAG, SVG_PATH_TAG,
+ SVG_USE_TAG, XLINK_HREF)
from ..utils import Point
from .font_variant import FontVariant
@@ -180,7 +181,8 @@ class Font(object):
def is_custom_font(self):
return get_custom_font_dir() in self.path
- def render_text(self, text, destination_group, variant=None, back_and_forth=True, trim=False):
+ def render_text(self, text, destination_group, variant=None, back_and_forth=True, trim_option=0):
+
"""Render text into an SVG group element."""
self._load_variants()
@@ -206,7 +208,7 @@ class Font(object):
position.y += self.leading
if self.auto_satin and len(destination_group) > 0:
- self._apply_auto_satin(destination_group, trim)
+ self._apply_auto_satin(destination_group)
# make sure font stroke styles have always a similar look
for element in destination_group.iterdescendants(SVG_PATH_TAG):
@@ -220,6 +222,8 @@ class Font(object):
style += inkex.Style("stroke-width:0.5px")
element.set('style', '%s' % style.to_str())
+ # add trims
+ self._add_trims(destination_group, text, trim_option, back_and_forth)
# make sure necessary marker and command symbols are in the defs section
self._ensure_command_symbols(destination_group)
self._ensure_marker_symbols(destination_group)
@@ -309,6 +313,10 @@ class Font(object):
self._update_commands(node, glyph)
+ # this is used to recognize a glyph layer later in the process
+ # because this is not unique it will be overwritten by inkscape when inserted into the document
+ node.set("id", "glyph")
+
return node
def _update_commands(self, node, glyph):
@@ -329,6 +337,58 @@ class Font(object):
c.set(CONNECTION_END, "#%s" % new_element_id)
c.set(CONNECTION_START, "#%s" % new_symbol_id)
+ def _add_trims(self, destination_group, text, trim_option, back_and_forth):
+ """
+ trim_option == 0 --> no trims
+ trim_option == 1 --> trim at the end of each line
+ trim_option == 2 --> trim after each word
+ trim_option == 3 --> trim after each letter
+ """
+ if trim_option == 0:
+ return
+
+ # reverse every second line of text if back and forth is true and strip spaces
+ text = text.splitlines()
+ text = [t[::-1].strip() if i % 2 != 0 and back_and_forth else t.strip() for i, t in enumerate(text)]
+ text = "\n".join(text)
+
+ i = -1
+ space_indices = [i for i, t in enumerate(text) if t == " "]
+ line_break_indices = [i for i, t in enumerate(text) if t == "\n"]
+ for group in destination_group.iterdescendants(SVG_GROUP_TAG):
+ # make sure we are only looking at glyph groups
+ if group.get("id") != "glyph":
+ continue
+
+ i += 1
+ while i in space_indices + line_break_indices:
+ i += 1
+
+ # letter
+ if trim_option == 3:
+ self._process_trim(group)
+ # word
+ elif trim_option == 2 and i+1 in space_indices + line_break_indices:
+ self._process_trim(group)
+ # line
+ elif trim_option == 1 and i+1 in line_break_indices:
+ self._process_trim(group)
+
+ def _process_trim(self, group):
+ # find the last path that does not carry a marker and add a trim there
+ for path_child in group.iterdescendants(EMBROIDERABLE_TAGS):
+ if not has_marker(path_child):
+ path = path_child
+ if path.get('style') and "fill" in path.get('style'):
+ element = FillStitch(path)
+ else:
+ element = Stroke(path)
+
+ if element.shape:
+ element_id = "%s_%s" % (element.node.get('id'), randint(0, 9999))
+ element.node.set("id", element_id)
+ add_commands(element, ['trim'])
+
def _ensure_command_symbols(self, group):
# collect commands
commands = set()
@@ -349,16 +409,14 @@ class Font(object):
for element in marked_elements:
element.style['marker-start'] = "url(#inkstitch-%s-marker)" % marker
- def _apply_auto_satin(self, group, trim):
+ def _apply_auto_satin(self, group):
"""Apply Auto-Satin to an SVG XML node tree with an svg:g at its root.
The group's contents will be replaced with the results of the auto-
satin operation. Any nested svg:g elements will be removed.
"""
- # TODO: trim option for non-auto-route
-
elements = nodes_to_elements(group.iterdescendants(SVG_PATH_TAG))
if elements:
- auto_satin(elements, preserve_order=True, trim=trim)
+ auto_satin(elements, preserve_order=True, trim=False)