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>
pull/1915/head
Claudine Peyrat 2022-11-22 10:46:44 +01:00 zatwierdzone przez GitHub
rodzic e761d8b42c
commit 43a31ba80e
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
4 zmienionych plików z 95 dodań i 26 usunięć

Wyświetl plik

@ -24,7 +24,7 @@
showgrid="true"
inkscape:zoom="2.8284271"
inkscape:cx="122.8598"
inkscape:cy="90.1561
inkscape:cy="90.1561"
inkscape:window-width="1440"
inkscape:window-height="796"
inkscape:window-x="0"

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 79 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 79 KiB

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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