svg-ultralight 0.43.1__py3-none-any.whl → 0.44.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of svg-ultralight might be problematic. Click here for more details.
- svg_ultralight/font_tools/comp_results.py +22 -12
- svg_ultralight/font_tools/font_info.py +76 -9
- {svg_ultralight-0.43.1.dist-info → svg_ultralight-0.44.0.dist-info}/METADATA +1 -1
- {svg_ultralight-0.43.1.dist-info → svg_ultralight-0.44.0.dist-info}/RECORD +6 -6
- {svg_ultralight-0.43.1.dist-info → svg_ultralight-0.44.0.dist-info}/WHEEL +0 -0
- {svg_ultralight-0.43.1.dist-info → svg_ultralight-0.44.0.dist-info}/top_level.txt +0 -0
|
@@ -31,8 +31,8 @@ from svg_ultralight.bounding_boxes.padded_text_initializers import (
|
|
|
31
31
|
pad_text_ft,
|
|
32
32
|
)
|
|
33
33
|
from svg_ultralight.constructors import new_element
|
|
34
|
-
from svg_ultralight.font_tools.font_info import get_svg_font_attributes
|
|
35
|
-
from svg_ultralight.
|
|
34
|
+
from svg_ultralight.font_tools.font_info import FTFontInfo, get_svg_font_attributes
|
|
35
|
+
from svg_ultralight.inkscape import write_root
|
|
36
36
|
from svg_ultralight.root_elements import new_svg_root_around_bounds
|
|
37
37
|
|
|
38
38
|
if TYPE_CHECKING:
|
|
@@ -201,7 +201,15 @@ def draw_comparison(
|
|
|
201
201
|
text = Path(font).stem
|
|
202
202
|
font_size = 12
|
|
203
203
|
font_attributes = get_svg_font_attributes(font)
|
|
204
|
-
text_elem = new_element(
|
|
204
|
+
text_elem = new_element(
|
|
205
|
+
"text",
|
|
206
|
+
text=text,
|
|
207
|
+
**font_attributes,
|
|
208
|
+
font_size=font_size,
|
|
209
|
+
fill="none",
|
|
210
|
+
stroke="green",
|
|
211
|
+
stroke_width=0.1,
|
|
212
|
+
)
|
|
205
213
|
padded_pt = pad_text(inkscape, text_elem)
|
|
206
214
|
padded_ft = pad_text_ft(
|
|
207
215
|
font,
|
|
@@ -210,10 +218,10 @@ def draw_comparison(
|
|
|
210
218
|
y_bounds_reference=DEFAULT_Y_BOUNDS_REFERENCE,
|
|
211
219
|
fill="none",
|
|
212
220
|
stroke="orange",
|
|
213
|
-
stroke_width=0.
|
|
221
|
+
stroke_width=0.1,
|
|
214
222
|
)
|
|
215
223
|
|
|
216
|
-
root = new_svg_root_around_bounds(pad_bbox(padded_pt.bbox,
|
|
224
|
+
root = new_svg_root_around_bounds(pad_bbox(padded_pt.bbox, 1))
|
|
217
225
|
root.append(
|
|
218
226
|
new_bbox_rect(
|
|
219
227
|
padded_pt.unpadded_bbox, fill="none", stroke_width=0.07, stroke="red"
|
|
@@ -227,7 +235,7 @@ def draw_comparison(
|
|
|
227
235
|
root.append(padded_pt.elem)
|
|
228
236
|
root.append(padded_ft.elem)
|
|
229
237
|
_ = sys.stdout.write(f"{Path(font).stem} comparison drawn at {output}.\n")
|
|
230
|
-
_ =
|
|
238
|
+
_ = write_root(inkscape, Path(output), root)
|
|
231
239
|
|
|
232
240
|
|
|
233
241
|
def _iter_fonts(*fonts_dirs: Path) -> Iterator[Path]:
|
|
@@ -273,11 +281,13 @@ def _test_every_font_on_my_system(
|
|
|
273
281
|
|
|
274
282
|
if __name__ == "__main__":
|
|
275
283
|
_INKSCAPE = Path(r"C:\Program Files\Inkscape\bin\inkscape")
|
|
276
|
-
_FONT_DIRS = [
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
]
|
|
280
|
-
_test_every_font_on_my_system(_INKSCAPE, _FONT_DIRS)
|
|
284
|
+
# _FONT_DIRS = [
|
|
285
|
+
# Path(r"C:\Windows\Fonts"),
|
|
286
|
+
# Path(r"C:\Users\shaya\AppData\Local\Microsoft\Windows\Fonts"),
|
|
287
|
+
# ]
|
|
288
|
+
# _test_every_font_on_my_system(_INKSCAPE, _FONT_DIRS)
|
|
281
289
|
|
|
282
290
|
font = Path(r"C:\Windows\Fonts\arial.ttf")
|
|
283
|
-
|
|
291
|
+
font = Path("C:/Windows/Fonts/Aptos-Display-Bold.ttf")
|
|
292
|
+
info = FTFontInfo(font)
|
|
293
|
+
draw_comparison(_INKSCAPE, "temp.svg", font, "AApple")
|
|
@@ -105,6 +105,7 @@ from typing import TYPE_CHECKING, Any, cast
|
|
|
105
105
|
from fontTools.pens.basePen import BasePen
|
|
106
106
|
from fontTools.pens.boundsPen import BoundsPen
|
|
107
107
|
from fontTools.ttLib import TTFont
|
|
108
|
+
from paragraphs import par
|
|
108
109
|
from svg_path_data import format_svgd_shortest, get_cpts_from_svgd, get_svgd_from_cpts
|
|
109
110
|
|
|
110
111
|
from svg_ultralight.bounding_boxes.type_bounding_box import BoundingBox
|
|
@@ -121,9 +122,63 @@ if TYPE_CHECKING:
|
|
|
121
122
|
logging.getLogger("fontTools").setLevel(logging.ERROR)
|
|
122
123
|
|
|
123
124
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
) ->
|
|
125
|
+
# extract_gpos_kerning is an unfinished attempt to extract kerning from the GPOS
|
|
126
|
+
# table.
|
|
127
|
+
def get_gpos_kerning(font: TTFont) -> dict[tuple[str, str], int]:
|
|
128
|
+
"""Extract kerning pairs from the GPOS table of a font.
|
|
129
|
+
|
|
130
|
+
:param font: A fontTools TTFont object.
|
|
131
|
+
:return: A dictionary mapping glyph pairs to their kerning values.
|
|
132
|
+
:raises ValueError: If the font does not have a GPOS table.
|
|
133
|
+
|
|
134
|
+
This is the more elaborate kerning that is used in OTF fonts and some TTF fonts.
|
|
135
|
+
It has several flavors, I'm only implementing glyph-pair kerning (Format 1),
|
|
136
|
+
because I don't have fonts to test anything else.
|
|
137
|
+
"""
|
|
138
|
+
if "GPOS" not in font:
|
|
139
|
+
msg = "Font does not have a GPOS table."
|
|
140
|
+
raise ValueError(msg)
|
|
141
|
+
|
|
142
|
+
gpos = font["GPOS"].table
|
|
143
|
+
kern_table: dict[tuple[str, str], int] = {}
|
|
144
|
+
|
|
145
|
+
type2_lookups = (x for x in gpos.LookupList.Lookup if x.LookupType == 2)
|
|
146
|
+
subtables = list(it.chain(*(x.SubTable for x in type2_lookups)))
|
|
147
|
+
for subtable in (x for x in subtables if x.Format == 1): # glyph-pair kerning
|
|
148
|
+
for pair_set, glyph1 in zip(subtable.PairSet, subtable.Coverage.glyphs):
|
|
149
|
+
for pair_value in pair_set.PairValueRecord:
|
|
150
|
+
glyph2 = pair_value.SecondGlyph
|
|
151
|
+
value1 = pair_value.Value1
|
|
152
|
+
xadv = getattr(value1, "XAdvance", None)
|
|
153
|
+
xpla = getattr(value1, "XPlacement", None)
|
|
154
|
+
value = xadv or xpla or 0
|
|
155
|
+
if value != 0: # only record non-zero kerning values
|
|
156
|
+
kern_table[(glyph1, glyph2)] = value
|
|
157
|
+
|
|
158
|
+
for subtable in (x for x in subtables if x.Format == 2): # class-based kerning
|
|
159
|
+
defs1 = subtable.ClassDef1.classDefs
|
|
160
|
+
defs2 = subtable.ClassDef2.classDefs
|
|
161
|
+
record1 = subtable.Class1Record
|
|
162
|
+
defs1 = {k: v for k, v in defs1.items() if v < len(record1)}
|
|
163
|
+
for (glyph1, class1), (glyph2, class2) in it.product(
|
|
164
|
+
defs1.items(), defs2.items()
|
|
165
|
+
):
|
|
166
|
+
class1_record = record1[class1]
|
|
167
|
+
if class2 < len(class1_record.Class2Record):
|
|
168
|
+
value1 = class1_record.Class2Record[class2].Value1
|
|
169
|
+
xadv = getattr(value1, "XAdvance", None)
|
|
170
|
+
xpla = getattr(value1, "XPlacement", None)
|
|
171
|
+
value = xadv or xpla or 0
|
|
172
|
+
if value != 0:
|
|
173
|
+
kern_table[(glyph1, glyph2)] = value
|
|
174
|
+
|
|
175
|
+
return kern_table
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
_XYTuple = tuple[float, float]
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _split_into_quadratic(*pts: _XYTuple) -> Iterator[tuple[_XYTuple, _XYTuple]]:
|
|
127
182
|
"""Connect a series of points with quadratic bezier segments.
|
|
128
183
|
|
|
129
184
|
:param points: a series of at least two (x, y) coordinates.
|
|
@@ -183,9 +238,16 @@ class PathPen(BasePen):
|
|
|
183
238
|
|
|
184
239
|
def curveTo(self, *pts: tuple[float, float]) -> None:
|
|
185
240
|
"""Add a series of cubic bezier segments to the path."""
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
241
|
+
if len(pts) > 3:
|
|
242
|
+
msg = par(
|
|
243
|
+
"""I'm uncertain how to decompose these points into cubics (if the
|
|
244
|
+
goal is to match font rendering in Inkscape and elsewhere. There is
|
|
245
|
+
function, decomposeSuperBezierSegment, in fontTools, but I cannot
|
|
246
|
+
find a reference for the algorithm. I'm hoping to run into one in a
|
|
247
|
+
font file so I have a test case."""
|
|
248
|
+
)
|
|
249
|
+
raise NotImplementedError(msg)
|
|
250
|
+
self._cmds.extend(("C", *map(str, it.chain(*pts))))
|
|
189
251
|
|
|
190
252
|
def qCurveTo(self, *pts: tuple[float, float]) -> None:
|
|
191
253
|
"""Add a series of quadratic bezier segments to the path."""
|
|
@@ -252,13 +314,18 @@ class FTFontInfo:
|
|
|
252
314
|
method would give precedence to the first occurrence. That behavior is copied
|
|
253
315
|
from examples found online.
|
|
254
316
|
"""
|
|
255
|
-
|
|
317
|
+
try:
|
|
256
318
|
kern_tables = cast(
|
|
257
319
|
"list[dict[tuple[str, str], int]]",
|
|
258
320
|
[x.kernTable for x in self.font["kern"].kernTables],
|
|
259
321
|
)
|
|
260
|
-
|
|
261
|
-
|
|
322
|
+
kern = dict(x for d in reversed(kern_tables) for x in d.items())
|
|
323
|
+
except (KeyError, AttributeError):
|
|
324
|
+
kern = {}
|
|
325
|
+
with suppress(Exception):
|
|
326
|
+
kern.update(get_gpos_kerning(self.font))
|
|
327
|
+
|
|
328
|
+
return kern
|
|
262
329
|
|
|
263
330
|
@ft.cached_property
|
|
264
331
|
def hhea(self) -> Any:
|
|
@@ -23,12 +23,12 @@ svg_ultralight/bounding_boxes/type_padded_text.py,sha256=WNIoqC9pfB8g_00P6RHcBdN
|
|
|
23
23
|
svg_ultralight/constructors/__init__.py,sha256=XLOInLhzMERWNnFAs-itMs-OZrBOpvQthZJ2T5duqBE,327
|
|
24
24
|
svg_ultralight/constructors/new_element.py,sha256=hRUW2hR_BTkthEqPClYV7-IeFe9iv2zwb6ehp1k1xDk,3475
|
|
25
25
|
svg_ultralight/font_tools/__init__.py,sha256=NX3C0vvoB-G4S-h1f0NLWePjYAMMR37D1cl_G4WBjHc,83
|
|
26
|
-
svg_ultralight/font_tools/comp_results.py,sha256=
|
|
27
|
-
svg_ultralight/font_tools/font_info.py,sha256=
|
|
26
|
+
svg_ultralight/font_tools/comp_results.py,sha256=ypqIqt7pe4tCCRGMJl7hVJtmQyUAYlQj1HZHmTcVThE,10697
|
|
27
|
+
svg_ultralight/font_tools/font_info.py,sha256=QztVHygo-qkp0IOe5bRnvjiR1rehEVxUyvsMZDJm5A0,29343
|
|
28
28
|
svg_ultralight/font_tools/globs.py,sha256=JdrrGMqDtD4WcY7YGUWV43DUW63RVev-x9vWqsQUhxU,119
|
|
29
29
|
svg_ultralight/strings/__init__.py,sha256=BMGhF1pulscIgkiYvZLr6kPRR0L4lW0jUNFxkul4_EM,295
|
|
30
30
|
svg_ultralight/strings/svg_strings.py,sha256=FQNxNmMkR2M-gCFo_woQKXLgCHi3ncUlRMiaRR_a9nQ,1978
|
|
31
|
-
svg_ultralight-0.
|
|
32
|
-
svg_ultralight-0.
|
|
33
|
-
svg_ultralight-0.
|
|
34
|
-
svg_ultralight-0.
|
|
31
|
+
svg_ultralight-0.44.0.dist-info/METADATA,sha256=mHPgja-zNjke16M_4iZCj5gAS3orif2qnJTgkogwHvc,9052
|
|
32
|
+
svg_ultralight-0.44.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
33
|
+
svg_ultralight-0.44.0.dist-info/top_level.txt,sha256=se-6yqM_0Yg5orJKvKWdjQZ4iR4G_EjhL7oRgju-fdY,15
|
|
34
|
+
svg_ultralight-0.44.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|