kopia lustrzana https://github.com/inkstitch/inkstitch
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
rodzic
e761d8b42c
commit
43a31ba80e
|
@ -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 |
|
@ -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])
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Ładowanie…
Reference in New Issue