summaryrefslogtreecommitdiff
path: root/lib/extensions/lettering_svg_font_to_layers.py
diff options
context:
space:
mode:
authorClaudine Peyrat <88194877+claudinepeyrat06@users.noreply.github.com>2025-06-22 06:30:31 +0200
committerGitHub <noreply@github.com>2025-06-22 06:30:31 +0200
commitf5dda6fc4505faa3c8f69abcfe1315a150e06604 (patch)
tree353ee8efa4f205743920b2de17367abf1f0e6ee8 /lib/extensions/lettering_svg_font_to_layers.py
parentc1e01e04f3a9a5a6a512c65e69c44d666fb8670a (diff)
Claudine/chose glyphs size (#3799)
* first steps * Update lettering_svg_font_to_layers.py * add more user parameters * Update lettering_svg_font_to_layers.py * round the horiz_adv_x * remove scoriers * better handle the case when the reference glyph is not in the font
Diffstat (limited to 'lib/extensions/lettering_svg_font_to_layers.py')
-rw-r--r--lib/extensions/lettering_svg_font_to_layers.py183
1 files changed, 146 insertions, 37 deletions
diff --git a/lib/extensions/lettering_svg_font_to_layers.py b/lib/extensions/lettering_svg_font_to_layers.py
index be8a7f16..81586195 100644
--- a/lib/extensions/lettering_svg_font_to_layers.py
+++ b/lib/extensions/lettering_svg_font_to_layers.py
@@ -19,70 +19,151 @@
#
#
# Adapted for the inkstitch lettering module to allow glyph annotations for characters
-# in specific positions or settings. Changes: see git history
+# in specific positions or settings, also allows scaling.Changes: see git history
"""Extension for converting svg fonts to layers"""
from inkex import Layer, PathElement, errormsg
from .base import InkstitchExtension
+from ..svg import PIXELS_PER_MM
-class LetteringSvgFontToLayers(InkstitchExtension):
- """Convert an svg font to layers"""
- def add_arguments(self, pars):
+class LetteringSvgFontToLayers(InkstitchExtension):
+ """
+ An Ink/Stitch extension for converting SVG font glyphs into separate layers.
+ This extension processes an SVG font embedded in an SVG document, scaling and converting each glyph into its own layer for
+ further manipulation or embroidery digitization. It provides options to control the number of glyphs processed,
+ the reference character for scaling, and the desired reference height in millimeters.
+ Attributes:
+ None explicitly defined.
+ Methods:
+ add_arguments(pars):
+ Adds command-line arguments for glyph count, reference character, and reference height.
+ scale_hkerning(scale_by):
+ flip_cordinate_system(elem, emsize, baseline, scale_by):
+ Scales and translates the element's path to match the desired coordinate system.
+ reference_size(font, reference):
+ set_view_port(font, scale_by):
+ Sets the SVG viewport size and viewBox based on the scaled em size, return the width (= height) of the viewport
+ set_guide_lines(font, scale_by):
+ Adds guide lines for baseline, ascender, caps, x-height, and descender to the SVG. Return the baseline position
+ convert_glyph_to_layer(glyph, emsize, baseline, scale_by, hide_layer):
+ Converts a single glyph into a new SVG layer, applying scaling and coordinate transformation.
+ effect():
+ Main entry point for the extension. Processes the SVG font, scales glyphs, creates layers, and applies kerning scaling.
+
+ Usage:
+ This extension is intended to be used within the Ink/Stitch environment as a command-line or GUI extension for converting
+ SVG font glyphs into layers suitable for embroidery design.
+ """
+
+ def add_arguments(self, pars) -> None:
+ """ Adds command-line arguments for glyph count, reference character, and reference height.
+ """
pars.add_argument(
"--count",
type=int,
- default=30,
- help="Stop making layers after this number of glyphs.",
+ default=150,
+ help="Stop making layers after this number of glyphs."
+ )
+ pars.add_argument(
+ "--reference",
+ type=str,
+ default="M",
+ help="Reference character for size"
+ )
+ pars.add_argument(
+ "--height",
+ type=float,
+ default=20.0,
+ help="Reference character height in mm"
)
- def flip_cordinate_system(self, elem, emsize, baseline):
- """Scale and translate the element's path, returns the path object"""
+ def scale_hkerning(self, scale_by) -> None:
+ """
+ Scales all horizontal kerning (hkern) values in the font by the given factor.
+ """
+ font = self.svg.defs.findone("svg:font")
+ for hkern in font.findall("svg:hkern"):
+ k = hkern.get("k", None)
+ if k is not None:
+ k = round(float(k) * scale_by, 2)
+ hkern.set(("k"), str(k))
+
+ def flip_cordinate_system(self, elem, emsize, baseline, scale_by):
+ """
+ Scale and translate the element's path, returns the path object
+ """
path = elem.path
- path.scale(1, -1, inplace=True)
- path.translate(0, int(emsize) - int(baseline), inplace=True)
+ path.scale(scale_by, -scale_by, inplace=True)
+ path.translate(0, float(emsize) - float(baseline), inplace=True)
return path
- def effect(self):
- # Current code only reads the first svgfont instance
- font = self.svg.defs.findone("svg:font")
- if font is None:
- return errormsg("There are no svg fonts")
- # setwidth = font.get("horiz-adv-x")
+ def reference_size(self, font, reference):
+ """
+ Returns the height of the reference glyph used for scaling.
+ """
+ for glyph in font.findall("svg:glyph"):
+ if glyph.get("glyph-name") == str(reference):
+ path = glyph.path
+ return path.bounding_box().height
+ break
+
+ def set_view_port(self, font, scale_by) -> float:
+ """
+ Sets the SVG viewport size and viewBox based on the scaled em size, return the width (= height) of the viewport
+ """
+ fontface = font.findone("svg:font-face")
+
+ emsize = fontface.get("units-per-em")
+ emsize = round(float(emsize) * scale_by, 2)
+ fontface.set("units-per-em", str(emsize))
+
+ self.svg.set("width", str(emsize))
+ self.svg.set("height", str(emsize))
+ self.svg.set("viewBox", "0 0 " + str(emsize) + " " + str(emsize))
+ return emsize
+
+ def set_guide_lines(self, font, scale_by) -> float:
+ """
+ Adds guide lines for baseline, ascender, caps, x-height, and descender to the SVG. Return the baseline position_
+ """
+
baseline = font.get("horiz-origin-y")
+ horiz_adv_x = round(float(font.get("horiz-adv-x", 0)) * scale_by, 2)
+ font.set("horiz-adv-x", str(horiz_adv_x))
+
+ fontface = font.findone("svg:font-face")
if baseline is None:
baseline = 0
+ else:
+ baseline = float(baseline) * scale_by
- guidebase = self.svg.viewbox_height - baseline
+ guidebase = (self.svg.viewbox_height) - float(baseline)
- fontface = font.findone("svg:font-face")
+ caps = round(float(fontface.get("cap-height", 0)) * scale_by, 2)
+ xheight = round(float(fontface.get("x-height", 0)) * scale_by, 2)
+ ascender = round(float(fontface.get("ascent", 0)) * scale_by, 2)
+ descender = round(float(fontface.get("descent", 0)) * scale_by, 2)
- emsize = fontface.get("units-per-em")
-
- # TODO: should we guarantee that <svg:font horiz-adv-x> equals <svg:font-face units-per-em> ?
- caps = float(fontface.get("cap-height", 0))
- xheight = float(fontface.get("x-height", 0))
- ascender = float(fontface.get("ascent", 0))
- descender = float(fontface.get("descent", 0))
+ fontface.set("cap-height", str(caps))
+ fontface.set("x-height", str(xheight))
+ fontface.set("ascent", str(ascender))
+ fontface.set("descent", str(descender))
- self.svg.set("width", emsize)
self.svg.namedview.add_guide(guidebase, True, "baseline")
self.svg.namedview.add_guide(guidebase - ascender, True, "ascender")
self.svg.namedview.add_guide(guidebase - caps, True, "caps")
self.svg.namedview.add_guide(guidebase - xheight, True, "xheight")
- self.svg.namedview.add_guide(guidebase + descender, True, "decender")
+ self.svg.namedview.add_guide(guidebase - descender, True, "decender")
- count = 0
- for glyph in font.findall("svg:glyph"):
- hide_layer = count != 0
- self.convert_glyph_to_layer(glyph, emsize, baseline, hide_layer=hide_layer)
- count += 1
- if count >= self.options.count:
- break
+ return baseline
- def convert_glyph_to_layer(self, glyph, emsize, baseline, hide_layer):
+ def convert_glyph_to_layer(self, glyph, emsize, baseline, scale_by, hide_layer):
+ """
+ Converts a single glyph into a new SVG layer, applying scaling and coordinate transformation.
+ """
unicode_char = glyph.get("unicode")
glyph_name = glyph.get("glyph-name").split('.')
@@ -104,13 +185,41 @@ class LetteringSvgFontToLayers(InkstitchExtension):
layer = self.svg.add(Layer.new(f"GlyphLayer-{unicode_char}{typographic_feature}"))
- # glyph layers (except the first one) are innitially hidden
+ # glyph layers (except the first one) are initially hidden
if hide_layer:
layer.style["display"] = "none"
# Using curve description in d attribute of svg:glyph
path = layer.add(PathElement())
- path.path = self.flip_cordinate_system(glyph, emsize, baseline)
+ path.path = self.flip_cordinate_system(glyph, emsize, baseline, scale_by)
+
+ def effect(self) -> None:
+ # Current code only reads the first svgfont instance
+ font = self.svg.defs.findone("svg:font")
+ if font is None:
+ return errormsg("There are no svg fonts")
+
+ reference_size = self.reference_size(font, self.options.reference)
+ if reference_size is None:
+ return errormsg("Reference glyph not found in the font")
+
+ scale_by = self.options.height * PIXELS_PER_MM / reference_size
+ emsize = self.set_view_port(font, scale_by)
+ baseline = self.set_guide_lines(font, scale_by)
+
+ count = 0
+ for glyph in font.findall("svg:glyph"):
+ hide_layer = count != 0
+ hax = glyph.get("horiz-adv-x", None)
+ if hax is not None:
+ hax = round(float(hax) * scale_by, 2)
+ glyph.set(("horiz-adv-x"), str(hax))
+ self.convert_glyph_to_layer(glyph, emsize, baseline, scale_by, hide_layer=hide_layer)
+ count += 1
+ if count >= self.options.count:
+ break
+
+ self.scale_hkerning(scale_by)
if __name__ == "__main__":