1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
|
#!/usr/bin/env python3
# coding=utf-8
#
# Copyright (C) 2011 Felipe Correa da Silva Sanches <juca@members.fsf.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
# Adapted for the inkstitch lettering module to allow glyph annotations for characters
# 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):
"""
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=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 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(scale_by, -scale_by, inplace=True)
path.translate(0, float(emsize) - float(baseline), inplace=True)
return path
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) - float(baseline)
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)
fontface.set("cap-height", str(caps))
fontface.set("x-height", str(xheight))
fontface.set("ascent", str(ascender))
fontface.set("descent", str(descender))
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")
return baseline
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('.')
if unicode_char is None:
if len(glyph_name) == 2:
unicode_char = glyph_name[0]
else:
return
typographic_feature = ''
if len(glyph_name) == 2:
typographic_feature = glyph_name[1]
else:
arabic_form = glyph.get('arabic-form', None)
if arabic_form is not None and len(arabic_form) > 4:
typographic_feature = arabic_form[:4]
if typographic_feature:
typographic_feature = f".{typographic_feature}"
layer = self.svg.add(Layer.new(f"GlyphLayer-{unicode_char}{typographic_feature}"))
# 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, 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__":
LetteringSvgFontToLayers().run()
|