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.

@@ -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.main import write_svg
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("text", text=text, **font_attributes, font_size=font_size)
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.05,
221
+ stroke_width=0.1,
214
222
  )
215
223
 
216
- root = new_svg_root_around_bounds(pad_bbox(padded_pt.bbox, 10))
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
- _ = write_svg(Path(output), root)
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
- Path(r"C:\Windows\Fonts"),
278
- Path(r"C:\Users\shaya\AppData\Local\Microsoft\Windows\Fonts"),
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
- draw_comparison(_INKSCAPE, "temp.svg", font)
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
- def _split_into_quadratic(
125
- *pts: tuple[float, float],
126
- ) -> Iterator[tuple[tuple[float, float], tuple[float, float]]]:
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
- msg = "Cubic Bezier curves not implemented for getting svg path data."
187
- raise NotImplementedError(msg)
188
- self._cmds.extend(("Q", *map(str, it.chain(*pts))))
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
- with suppress(KeyError, AttributeError):
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
- return dict(x for d in reversed(kern_tables) for x in d.items())
261
- return {}
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:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: svg-ultralight
3
- Version: 0.43.1
3
+ Version: 0.44.0
4
4
  Summary: a sensible way to create svg files with Python
5
5
  Author-email: Shay Hill <shay_public@hotmail.com>
6
6
  License: MIT
@@ -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=iEDbExO3D7ffo0NZxrqf-WjtzUCJZbdHmqwjjac4laY,10444
27
- svg_ultralight/font_tools/font_info.py,sha256=CDAA5hJOkVS8yyRzAk0uVb0WMTL3fY6Jhxk9I6SEFi8,26375
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.43.1.dist-info/METADATA,sha256=0lV1rScmuaMl3tHmBxIBwD2I-shfrGe__RAX7Sx2bmc,9052
32
- svg_ultralight-0.43.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
33
- svg_ultralight-0.43.1.dist-info/top_level.txt,sha256=se-6yqM_0Yg5orJKvKWdjQZ4iR4G_EjhL7oRgju-fdY,15
34
- svg_ultralight-0.43.1.dist-info/RECORD,,
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,,