fonttools 4.55.4__cp313-cp313-musllinux_1_2_aarch64.whl → 4.61.1__cp313-cp313-musllinux_1_2_aarch64.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.
Files changed (140) hide show
  1. fontTools/__init__.py +1 -1
  2. fontTools/annotations.py +30 -0
  3. fontTools/cffLib/CFF2ToCFF.py +65 -10
  4. fontTools/cffLib/__init__.py +61 -26
  5. fontTools/cffLib/specializer.py +4 -1
  6. fontTools/cffLib/transforms.py +11 -6
  7. fontTools/config/__init__.py +15 -0
  8. fontTools/cu2qu/cu2qu.c +6567 -5579
  9. fontTools/cu2qu/cu2qu.cpython-313-aarch64-linux-musl.so +0 -0
  10. fontTools/cu2qu/cu2qu.py +36 -4
  11. fontTools/cu2qu/ufo.py +14 -0
  12. fontTools/designspaceLib/__init__.py +8 -3
  13. fontTools/designspaceLib/statNames.py +14 -7
  14. fontTools/feaLib/ast.py +24 -15
  15. fontTools/feaLib/builder.py +139 -66
  16. fontTools/feaLib/error.py +1 -1
  17. fontTools/feaLib/lexer.c +7038 -7995
  18. fontTools/feaLib/lexer.cpython-313-aarch64-linux-musl.so +0 -0
  19. fontTools/feaLib/parser.py +75 -40
  20. fontTools/feaLib/variableScalar.py +6 -1
  21. fontTools/fontBuilder.py +50 -44
  22. fontTools/merge/__init__.py +1 -1
  23. fontTools/merge/cmap.py +33 -1
  24. fontTools/merge/tables.py +12 -1
  25. fontTools/misc/bezierTools.c +14913 -17013
  26. fontTools/misc/bezierTools.cpython-313-aarch64-linux-musl.so +0 -0
  27. fontTools/misc/bezierTools.py +4 -1
  28. fontTools/misc/configTools.py +3 -1
  29. fontTools/misc/enumTools.py +23 -0
  30. fontTools/misc/etree.py +4 -27
  31. fontTools/misc/filesystem/__init__.py +68 -0
  32. fontTools/misc/filesystem/_base.py +134 -0
  33. fontTools/misc/filesystem/_copy.py +45 -0
  34. fontTools/misc/filesystem/_errors.py +54 -0
  35. fontTools/misc/filesystem/_info.py +75 -0
  36. fontTools/misc/filesystem/_osfs.py +164 -0
  37. fontTools/misc/filesystem/_path.py +67 -0
  38. fontTools/misc/filesystem/_subfs.py +92 -0
  39. fontTools/misc/filesystem/_tempfs.py +34 -0
  40. fontTools/misc/filesystem/_tools.py +34 -0
  41. fontTools/misc/filesystem/_walk.py +55 -0
  42. fontTools/misc/filesystem/_zipfs.py +204 -0
  43. fontTools/misc/fixedTools.py +1 -1
  44. fontTools/misc/loggingTools.py +1 -1
  45. fontTools/misc/psCharStrings.py +17 -2
  46. fontTools/misc/sstruct.py +2 -6
  47. fontTools/misc/symfont.py +6 -8
  48. fontTools/misc/testTools.py +5 -1
  49. fontTools/misc/textTools.py +4 -2
  50. fontTools/misc/visitor.py +32 -16
  51. fontTools/misc/xmlWriter.py +44 -8
  52. fontTools/mtiLib/__init__.py +1 -3
  53. fontTools/otlLib/builder.py +402 -155
  54. fontTools/otlLib/optimize/gpos.py +49 -63
  55. fontTools/pens/filterPen.py +218 -26
  56. fontTools/pens/momentsPen.c +5514 -5584
  57. fontTools/pens/momentsPen.cpython-313-aarch64-linux-musl.so +0 -0
  58. fontTools/pens/pointPen.py +61 -18
  59. fontTools/pens/roundingPen.py +2 -2
  60. fontTools/pens/t2CharStringPen.py +31 -11
  61. fontTools/qu2cu/qu2cu.c +6581 -6168
  62. fontTools/qu2cu/qu2cu.cpython-313-aarch64-linux-musl.so +0 -0
  63. fontTools/subset/__init__.py +283 -25
  64. fontTools/subset/svg.py +2 -3
  65. fontTools/ttLib/__init__.py +4 -0
  66. fontTools/ttLib/__main__.py +47 -8
  67. fontTools/ttLib/removeOverlaps.py +7 -5
  68. fontTools/ttLib/reorderGlyphs.py +8 -7
  69. fontTools/ttLib/sfnt.py +11 -9
  70. fontTools/ttLib/tables/D__e_b_g.py +20 -2
  71. fontTools/ttLib/tables/G_V_A_R_.py +5 -0
  72. fontTools/ttLib/tables/S__i_l_f.py +2 -2
  73. fontTools/ttLib/tables/T_S_I__0.py +14 -3
  74. fontTools/ttLib/tables/T_S_I__1.py +2 -5
  75. fontTools/ttLib/tables/T_S_I__5.py +18 -7
  76. fontTools/ttLib/tables/__init__.py +1 -0
  77. fontTools/ttLib/tables/_a_v_a_r.py +12 -3
  78. fontTools/ttLib/tables/_c_m_a_p.py +20 -7
  79. fontTools/ttLib/tables/_c_v_t.py +3 -2
  80. fontTools/ttLib/tables/_f_p_g_m.py +3 -1
  81. fontTools/ttLib/tables/_g_l_y_f.py +45 -21
  82. fontTools/ttLib/tables/_g_v_a_r.py +67 -19
  83. fontTools/ttLib/tables/_h_d_m_x.py +4 -4
  84. fontTools/ttLib/tables/_h_m_t_x.py +7 -3
  85. fontTools/ttLib/tables/_l_o_c_a.py +2 -2
  86. fontTools/ttLib/tables/_n_a_m_e.py +11 -6
  87. fontTools/ttLib/tables/_p_o_s_t.py +9 -7
  88. fontTools/ttLib/tables/otBase.py +5 -12
  89. fontTools/ttLib/tables/otConverters.py +5 -2
  90. fontTools/ttLib/tables/otData.py +1 -1
  91. fontTools/ttLib/tables/otTables.py +33 -30
  92. fontTools/ttLib/tables/otTraverse.py +2 -1
  93. fontTools/ttLib/tables/sbixStrike.py +3 -3
  94. fontTools/ttLib/ttFont.py +666 -120
  95. fontTools/ttLib/ttGlyphSet.py +0 -10
  96. fontTools/ttLib/woff2.py +10 -13
  97. fontTools/ttx.py +13 -1
  98. fontTools/ufoLib/__init__.py +300 -202
  99. fontTools/ufoLib/converters.py +103 -30
  100. fontTools/ufoLib/errors.py +8 -0
  101. fontTools/ufoLib/etree.py +1 -1
  102. fontTools/ufoLib/filenames.py +171 -106
  103. fontTools/ufoLib/glifLib.py +303 -205
  104. fontTools/ufoLib/kerning.py +98 -48
  105. fontTools/ufoLib/utils.py +46 -15
  106. fontTools/ufoLib/validators.py +121 -99
  107. fontTools/unicodedata/Blocks.py +35 -20
  108. fontTools/unicodedata/Mirrored.py +446 -0
  109. fontTools/unicodedata/ScriptExtensions.py +63 -37
  110. fontTools/unicodedata/Scripts.py +173 -152
  111. fontTools/unicodedata/__init__.py +10 -2
  112. fontTools/varLib/__init__.py +198 -109
  113. fontTools/varLib/avar/__init__.py +0 -0
  114. fontTools/varLib/avar/__main__.py +72 -0
  115. fontTools/varLib/avar/build.py +79 -0
  116. fontTools/varLib/avar/map.py +108 -0
  117. fontTools/varLib/avar/plan.py +1004 -0
  118. fontTools/varLib/{avar.py → avar/unbuild.py} +70 -59
  119. fontTools/varLib/avarPlanner.py +3 -999
  120. fontTools/varLib/featureVars.py +21 -7
  121. fontTools/varLib/hvar.py +113 -0
  122. fontTools/varLib/instancer/__init__.py +180 -65
  123. fontTools/varLib/interpolatableHelpers.py +3 -0
  124. fontTools/varLib/iup.c +7564 -6903
  125. fontTools/varLib/iup.cpython-313-aarch64-linux-musl.so +0 -0
  126. fontTools/varLib/models.py +17 -2
  127. fontTools/varLib/mutator.py +11 -0
  128. fontTools/varLib/varStore.py +10 -38
  129. fontTools/voltLib/__main__.py +206 -0
  130. fontTools/voltLib/ast.py +4 -0
  131. fontTools/voltLib/parser.py +16 -8
  132. fontTools/voltLib/voltToFea.py +347 -166
  133. {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/METADATA +269 -1410
  134. {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/RECORD +318 -294
  135. {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/WHEEL +1 -1
  136. fonttools-4.61.1.dist-info/licenses/LICENSE.external +388 -0
  137. {fonttools-4.55.4.data → fonttools-4.61.1.data}/data/share/man/man1/ttx.1 +0 -0
  138. {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/entry_points.txt +0 -0
  139. {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info/licenses}/LICENSE +0 -0
  140. {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/top_level.txt +0 -0
fontTools/ttLib/ttFont.py CHANGED
@@ -1,24 +1,130 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ import os
5
+ import traceback
6
+ from io import BytesIO, StringIO, UnsupportedOperation
7
+ from typing import TYPE_CHECKING, TypedDict, TypeVar, overload
8
+
1
9
  from fontTools.config import Config
2
10
  from fontTools.misc import xmlWriter
3
11
  from fontTools.misc.configTools import AbstractConfig
4
- from fontTools.misc.textTools import Tag, byteord, tostr
5
12
  from fontTools.misc.loggingTools import deprecateArgument
13
+ from fontTools.misc.textTools import Tag, byteord, tostr
6
14
  from fontTools.ttLib import TTLibError
15
+ from fontTools.ttLib.sfnt import SFNTReader, SFNTWriter
7
16
  from fontTools.ttLib.ttGlyphSet import (
8
- _TTGlyph,
17
+ _TTGlyph, # noqa: F401
18
+ _TTGlyphSet,
9
19
  _TTGlyphSetCFF,
10
20
  _TTGlyphSetGlyf,
11
21
  _TTGlyphSetVARC,
12
22
  )
13
- from fontTools.ttLib.sfnt import SFNTReader, SFNTWriter
14
- from io import BytesIO, StringIO, UnsupportedOperation
15
- import os
16
- import logging
17
- import traceback
23
+
24
+ if TYPE_CHECKING:
25
+ from collections.abc import Mapping, MutableMapping
26
+ from types import ModuleType, TracebackType
27
+ from typing import Any, BinaryIO, Literal, Sequence, TextIO
28
+
29
+ from typing_extensions import Self, Unpack
30
+
31
+ from fontTools.ttLib.tables import (
32
+ B_A_S_E_,
33
+ C_B_D_T_,
34
+ C_B_L_C_,
35
+ C_F_F_,
36
+ C_F_F__2,
37
+ C_O_L_R_,
38
+ C_P_A_L_,
39
+ D_S_I_G_,
40
+ E_B_D_T_,
41
+ E_B_L_C_,
42
+ F_F_T_M_,
43
+ G_D_E_F_,
44
+ G_M_A_P_,
45
+ G_P_K_G_,
46
+ G_P_O_S_,
47
+ G_S_U_B_,
48
+ G_V_A_R_,
49
+ H_V_A_R_,
50
+ J_S_T_F_,
51
+ L_T_S_H_,
52
+ M_A_T_H_,
53
+ M_E_T_A_,
54
+ M_V_A_R_,
55
+ S_I_N_G_,
56
+ S_T_A_T_,
57
+ S_V_G_,
58
+ T_S_I__0,
59
+ T_S_I__1,
60
+ T_S_I__2,
61
+ T_S_I__3,
62
+ T_S_I__5,
63
+ T_S_I_B_,
64
+ T_S_I_C_,
65
+ T_S_I_D_,
66
+ T_S_I_J_,
67
+ T_S_I_P_,
68
+ T_S_I_S_,
69
+ T_S_I_V_,
70
+ T_T_F_A_,
71
+ V_A_R_C_,
72
+ V_D_M_X_,
73
+ V_O_R_G_,
74
+ V_V_A_R_,
75
+ D__e_b_g,
76
+ F__e_a_t,
77
+ G__l_a_t,
78
+ G__l_o_c,
79
+ O_S_2f_2,
80
+ S__i_l_f,
81
+ S__i_l_l,
82
+ _a_n_k_r,
83
+ _a_v_a_r,
84
+ _b_s_l_n,
85
+ _c_i_d_g,
86
+ _c_m_a_p,
87
+ _c_v_a_r,
88
+ _c_v_t,
89
+ _f_e_a_t,
90
+ _f_p_g_m,
91
+ _f_v_a_r,
92
+ _g_a_s_p,
93
+ _g_c_i_d,
94
+ _g_l_y_f,
95
+ _g_v_a_r,
96
+ _h_d_m_x,
97
+ _h_e_a_d,
98
+ _h_h_e_a,
99
+ _h_m_t_x,
100
+ _k_e_r_n,
101
+ _l_c_a_r,
102
+ _l_o_c_a,
103
+ _l_t_a_g,
104
+ _m_a_x_p,
105
+ _m_e_t_a,
106
+ _m_o_r_t,
107
+ _m_o_r_x,
108
+ _n_a_m_e,
109
+ _o_p_b_d,
110
+ _p_o_s_t,
111
+ _p_r_e_p,
112
+ _p_r_o_p,
113
+ _s_b_i_x,
114
+ _t_r_a_k,
115
+ _v_h_e_a,
116
+ _v_m_t_x,
117
+ )
118
+ from fontTools.ttLib.tables.DefaultTable import DefaultTable
119
+
120
+ _VT_co = TypeVar("_VT_co", covariant=True) # Value type covariant containers.
18
121
 
19
122
  log = logging.getLogger(__name__)
20
123
 
21
124
 
125
+ _NumberT = TypeVar("_NumberT", bound=float)
126
+
127
+
22
128
  class TTFont(object):
23
129
  """Represents a TrueType font.
24
130
 
@@ -27,6 +133,7 @@ class TTFont(object):
27
133
  they're actually accessed. This means that simple operations can be extremely fast.
28
134
 
29
135
  Example usage:
136
+
30
137
  .. code-block:: pycon
31
138
 
32
139
  >>>
@@ -39,8 +146,10 @@ class TTFont(object):
39
146
  >> tt['head'].unitsPerEm
40
147
  2048
41
148
 
42
- For details of the objects returned when accessing each table, see :ref:`tables`.
149
+ For details of the objects returned when accessing each table, see the
150
+ :doc:`tables </ttLib/tables>` documentation.
43
151
  To add a table to the font, use the :py:func:`newTable` function:
152
+
44
153
  .. code-block:: pycon
45
154
 
46
155
  >>>
@@ -50,7 +159,8 @@ class TTFont(object):
50
159
  >> font["OS/2"] = os2
51
160
 
52
161
  TrueType fonts can also be serialized to and from XML format (see also the
53
- :ref:`ttx` binary):
162
+ :doc:`ttx </ttx>` binary):
163
+
54
164
  .. code-block:: pycon
55
165
 
56
166
  >>
@@ -99,24 +209,44 @@ class TTFont(object):
99
209
  The default is ``lazy=None`` which is somewhere in between.
100
210
  """
101
211
 
212
+ tables: dict[Tag, DefaultTable | GlyphOrder]
213
+ reader: SFNTReader | None
214
+ sfntVersion: str
215
+ flavor: str | None
216
+ flavorData: Any | None
217
+ lazy: bool | None
218
+ recalcBBoxes: bool
219
+ recalcTimestamp: bool
220
+ ignoreDecompileErrors: bool
221
+ cfg: AbstractConfig
222
+ glyphOrder: list[str]
223
+ _reverseGlyphOrderDict: dict[str, int]
224
+ _tableCache: MutableMapping[tuple[Tag, bytes], DefaultTable] | None
225
+ disassembleInstructions: bool
226
+ bitmapGlyphDataFormat: str
227
+ # Deprecated attributes
228
+ verbose: bool | None
229
+ quiet: bool | None
230
+
102
231
  def __init__(
103
232
  self,
104
- file=None,
105
- res_name_or_index=None,
106
- sfntVersion="\000\001\000\000",
107
- flavor=None,
108
- checkChecksums=0,
109
- verbose=None,
110
- recalcBBoxes=True,
111
- allowVID=NotImplemented,
112
- ignoreDecompileErrors=False,
113
- recalcTimestamp=True,
114
- fontNumber=-1,
115
- lazy=None,
116
- quiet=None,
117
- _tableCache=None,
118
- cfg={},
119
- ):
233
+ file: str | os.PathLike[str] | BinaryIO | None = None,
234
+ res_name_or_index: str | int | None = None,
235
+ sfntVersion: str = "\000\001\000\000",
236
+ flavor: str | None = None,
237
+ checkChecksums: int = 0,
238
+ verbose: bool | None = None, # Deprecated
239
+ recalcBBoxes: bool = True,
240
+ allowVID: Any = NotImplemented, # Deprecated/Unused
241
+ ignoreDecompileErrors: bool = False,
242
+ recalcTimestamp: bool = True,
243
+ fontNumber: int = -1,
244
+ lazy: bool | None = None,
245
+ quiet: bool | None = None, # Deprecated
246
+ _tableCache: MutableMapping[tuple[Tag, bytes], DefaultTable] | None = None,
247
+ cfg: Mapping[str, Any] | AbstractConfig = {},
248
+ ) -> None:
249
+ # Set deprecated attributes
120
250
  for name in ("verbose", "quiet"):
121
251
  val = locals().get(name)
122
252
  if val is not None:
@@ -138,6 +268,10 @@ class TTFont(object):
138
268
  return
139
269
  seekable = True
140
270
  if not hasattr(file, "read"):
271
+ if not isinstance(file, (str, os.PathLike)):
272
+ raise TypeError(
273
+ "fileOrPath must be a file path (str or PathLike) if it isn't an object with a `read` method."
274
+ )
141
275
  closeStream = True
142
276
  # assume file is a string
143
277
  if res_name_or_index is not None:
@@ -156,6 +290,7 @@ class TTFont(object):
156
290
  file = open(file, "rb")
157
291
  else:
158
292
  # assume "file" is a readable file object
293
+ assert not isinstance(file, (str, os.PathLike))
159
294
  closeStream = False
160
295
  # SFNTReader wants the input file to be seekable.
161
296
  # SpooledTemporaryFile has no seekable() on < 3.11, but still can seek:
@@ -187,23 +322,31 @@ class TTFont(object):
187
322
  self.flavor = self.reader.flavor
188
323
  self.flavorData = self.reader.flavorData
189
324
 
190
- def __enter__(self):
325
+ def __enter__(self) -> Self:
191
326
  return self
192
327
 
193
- def __exit__(self, type, value, traceback):
328
+ def __exit__(
329
+ self,
330
+ exc_type: type[BaseException] | None,
331
+ exc_value: BaseException | None,
332
+ traceback: TracebackType | None,
333
+ ) -> None:
194
334
  self.close()
195
335
 
196
- def close(self):
336
+ def close(self) -> None:
197
337
  """If we still have a reader object, close it."""
198
338
  if self.reader is not None:
199
339
  self.reader.close()
340
+ self.reader = None
200
341
 
201
- def save(self, file, reorderTables=True):
342
+ def save(
343
+ self, file: str | os.PathLike[str] | BinaryIO, reorderTables: bool | None = True
344
+ ) -> None:
202
345
  """Save the font to disk.
203
346
 
204
347
  Args:
205
348
  file: Similarly to the constructor, can be either a pathname or a writable
206
- file object.
349
+ binary file object.
207
350
  reorderTables (Option[bool]): If true (the default), reorder the tables,
208
351
  sorting them by tag (recommended by the OpenType specification). If
209
352
  false, retain the original font order. If None, reorder by table
@@ -228,6 +371,10 @@ class TTFont(object):
228
371
  ):
229
372
  if reorderTables is False:
230
373
  # sort tables using the original font's order
374
+ if self.reader is None:
375
+ raise TTLibError(
376
+ "The original table order is unavailable because there isn't a font to read it from."
377
+ )
231
378
  tableOrder = list(self.reader.keys())
232
379
  else:
233
380
  # use the recommended order from the OpenType specification
@@ -240,24 +387,28 @@ class TTFont(object):
240
387
 
241
388
  if createStream:
242
389
  # "file" is a path
390
+ assert isinstance(file, (str, os.PathLike))
243
391
  with open(file, "wb") as file:
244
392
  file.write(tmp.getvalue())
245
393
  else:
394
+ assert not isinstance(file, (str, os.PathLike))
246
395
  file.write(tmp.getvalue())
247
396
 
248
397
  tmp.close()
249
398
 
250
- def _save(self, file, tableCache=None):
399
+ def _save(
400
+ self,
401
+ file: BinaryIO,
402
+ tableCache: MutableMapping[tuple[Tag, bytes], Any] | None = None,
403
+ ) -> bool:
251
404
  """Internal function, to be shared by save() and TTCollection.save()"""
252
405
 
253
406
  if self.recalcTimestamp and "head" in self:
254
- self[
255
- "head"
256
- ] # make sure 'head' is loaded so the recalculation is actually done
407
+ # make sure 'head' is loaded so the recalculation is actually done
408
+ self["head"]
257
409
 
258
- tags = list(self.keys())
259
- if "GlyphOrder" in tags:
260
- tags.remove("GlyphOrder")
410
+ tags = self.keys()
411
+ tags.pop(0) # skip GlyphOrder tag
261
412
  numTables = len(tags)
262
413
  # write to a temporary stream to allow saving to unseekable streams
263
414
  writer = SFNTWriter(
@@ -272,7 +423,22 @@ class TTFont(object):
272
423
 
273
424
  return writer.reordersTables()
274
425
 
275
- def saveXML(self, fileOrPath, newlinestr="\n", **kwargs):
426
+ class XMLSavingOptions(TypedDict):
427
+ writeVersion: bool
428
+ quiet: bool | None
429
+ tables: Sequence[str | bytes] | None
430
+ skipTables: Sequence[str] | None
431
+ splitTables: bool
432
+ splitGlyphs: bool
433
+ disassembleInstructions: bool
434
+ bitmapGlyphDataFormat: str
435
+
436
+ def saveXML(
437
+ self,
438
+ fileOrPath: str | os.PathLike[str] | BinaryIO | TextIO,
439
+ newlinestr: str = "\n",
440
+ **kwargs: Unpack[XMLSavingOptions],
441
+ ) -> None:
276
442
  """Export the font as TTX (an XML-based text file), or as a series of text
277
443
  files when splitTables is true. In the latter case, the 'fileOrPath'
278
444
  argument should be a path to a directory.
@@ -287,30 +453,25 @@ class TTFont(object):
287
453
 
288
454
  def _saveXML(
289
455
  self,
290
- writer,
291
- writeVersion=True,
292
- quiet=None,
293
- tables=None,
294
- skipTables=None,
295
- splitTables=False,
296
- splitGlyphs=False,
297
- disassembleInstructions=True,
298
- bitmapGlyphDataFormat="raw",
299
- ):
456
+ writer: xmlWriter.XMLWriter,
457
+ writeVersion: bool = True,
458
+ quiet: bool | None = None, # Deprecated
459
+ tables: Sequence[str | bytes] | None = None,
460
+ skipTables: Sequence[str] | None = None,
461
+ splitTables: bool = False,
462
+ splitGlyphs: bool = False,
463
+ disassembleInstructions: bool = True,
464
+ bitmapGlyphDataFormat: str = "raw",
465
+ ) -> None:
300
466
  if quiet is not None:
301
467
  deprecateArgument("quiet", "configure logging instead")
302
468
 
303
469
  self.disassembleInstructions = disassembleInstructions
304
470
  self.bitmapGlyphDataFormat = bitmapGlyphDataFormat
305
471
  if not tables:
306
- tables = list(self.keys())
307
- if "GlyphOrder" not in tables:
308
- tables = ["GlyphOrder"] + tables
472
+ tables = self.keys()
309
473
  if skipTables:
310
- for tag in skipTables:
311
- if tag in tables:
312
- tables.remove(tag)
313
- numTables = len(tables)
474
+ tables = [tag for tag in tables if tag not in skipTables]
314
475
 
315
476
  if writeVersion:
316
477
  from fontTools import version
@@ -331,10 +492,13 @@ class TTFont(object):
331
492
  if not splitTables:
332
493
  writer.newline()
333
494
  else:
495
+ if writer.filename is None:
496
+ raise TTLibError(
497
+ "splitTables requires the file name to be a file system path, not a stream."
498
+ )
334
499
  path, ext = os.path.splitext(writer.filename)
335
500
 
336
- for i in range(numTables):
337
- tag = tables[i]
501
+ for tag in tables:
338
502
  if splitTables:
339
503
  tablePath = path + "." + tagToIdentifier(tag) + ext
340
504
  tableWriter = xmlWriter.XMLWriter(
@@ -355,7 +519,13 @@ class TTFont(object):
355
519
  writer.endtag("ttFont")
356
520
  writer.newline()
357
521
 
358
- def _tableToXML(self, writer, tag, quiet=None, splitGlyphs=False):
522
+ def _tableToXML(
523
+ self,
524
+ writer: xmlWriter.XMLWriter,
525
+ tag: str | bytes,
526
+ quiet: bool | None = None,
527
+ splitGlyphs: bool = False,
528
+ ) -> None:
359
529
  if quiet is not None:
360
530
  deprecateArgument("quiet", "configure logging instead")
361
531
  if tag in self:
@@ -367,7 +537,7 @@ class TTFont(object):
367
537
  if tag not in self:
368
538
  return
369
539
  xmlTag = tagToXML(tag)
370
- attrs = dict()
540
+ attrs: dict[str, Any] = {}
371
541
  if hasattr(table, "ERROR"):
372
542
  attrs["ERROR"] = "decompilation error"
373
543
  from .tables.DefaultTable import DefaultTable
@@ -384,7 +554,9 @@ class TTFont(object):
384
554
  writer.newline()
385
555
  writer.newline()
386
556
 
387
- def importXML(self, fileOrPath, quiet=None):
557
+ def importXML(
558
+ self, fileOrPath: str | os.PathLike[str] | BinaryIO, quiet: bool | None = None
559
+ ) -> None:
388
560
  """Import a TTX file (an XML-based text format), so as to recreate
389
561
  a font object.
390
562
  """
@@ -403,12 +575,12 @@ class TTFont(object):
403
575
  reader = xmlReader.XMLReader(fileOrPath, self)
404
576
  reader.read()
405
577
 
406
- def isLoaded(self, tag):
578
+ def isLoaded(self, tag: str | bytes) -> bool:
407
579
  """Return true if the table identified by ``tag`` has been
408
580
  decompiled and loaded into memory."""
409
581
  return tag in self.tables
410
582
 
411
- def has_key(self, tag):
583
+ def has_key(self, tag: str | bytes) -> bool:
412
584
  """Test if the table identified by ``tag`` is present in the font.
413
585
 
414
586
  As well as this method, ``tag in font`` can also be used to determine the
@@ -424,7 +596,7 @@ class TTFont(object):
424
596
 
425
597
  __contains__ = has_key
426
598
 
427
- def keys(self):
599
+ def keys(self) -> list[str]:
428
600
  """Returns the list of tables in the font, along with the ``GlyphOrder`` pseudo-table."""
429
601
  keys = list(self.tables.keys())
430
602
  if self.reader:
@@ -437,7 +609,7 @@ class TTFont(object):
437
609
  keys = sortedTagList(keys)
438
610
  return ["GlyphOrder"] + keys
439
611
 
440
- def ensureDecompiled(self, recurse=None):
612
+ def ensureDecompiled(self, recurse: bool | None = None) -> None:
441
613
  """Decompile all the tables, even if a TTFont was opened in 'lazy' mode."""
442
614
  for tag in self.keys():
443
615
  table = self[tag]
@@ -447,10 +619,185 @@ class TTFont(object):
447
619
  table.ensureDecompiled(recurse=recurse)
448
620
  self.lazy = False
449
621
 
450
- def __len__(self):
622
+ def __len__(self) -> int:
451
623
  return len(list(self.keys()))
452
624
 
453
- def __getitem__(self, tag):
625
+ @overload
626
+ def __getitem__(self, tag: Literal["BASE"]) -> B_A_S_E_.table_B_A_S_E_: ...
627
+ @overload
628
+ def __getitem__(self, tag: Literal["CBDT"]) -> C_B_D_T_.table_C_B_D_T_: ...
629
+ @overload
630
+ def __getitem__(self, tag: Literal["CBLC"]) -> C_B_L_C_.table_C_B_L_C_: ...
631
+ @overload
632
+ def __getitem__(self, tag: Literal["CFF "]) -> C_F_F_.table_C_F_F_: ...
633
+ @overload
634
+ def __getitem__(self, tag: Literal["CFF2"]) -> C_F_F__2.table_C_F_F__2: ...
635
+ @overload
636
+ def __getitem__(self, tag: Literal["COLR"]) -> C_O_L_R_.table_C_O_L_R_: ...
637
+ @overload
638
+ def __getitem__(self, tag: Literal["CPAL"]) -> C_P_A_L_.table_C_P_A_L_: ...
639
+ @overload
640
+ def __getitem__(self, tag: Literal["DSIG"]) -> D_S_I_G_.table_D_S_I_G_: ...
641
+ @overload
642
+ def __getitem__(self, tag: Literal["EBDT"]) -> E_B_D_T_.table_E_B_D_T_: ...
643
+ @overload
644
+ def __getitem__(self, tag: Literal["EBLC"]) -> E_B_L_C_.table_E_B_L_C_: ...
645
+ @overload
646
+ def __getitem__(self, tag: Literal["FFTM"]) -> F_F_T_M_.table_F_F_T_M_: ...
647
+ @overload
648
+ def __getitem__(self, tag: Literal["GDEF"]) -> G_D_E_F_.table_G_D_E_F_: ...
649
+ @overload
650
+ def __getitem__(self, tag: Literal["GMAP"]) -> G_M_A_P_.table_G_M_A_P_: ...
651
+ @overload
652
+ def __getitem__(self, tag: Literal["GPKG"]) -> G_P_K_G_.table_G_P_K_G_: ...
653
+ @overload
654
+ def __getitem__(self, tag: Literal["GPOS"]) -> G_P_O_S_.table_G_P_O_S_: ...
655
+ @overload
656
+ def __getitem__(self, tag: Literal["GSUB"]) -> G_S_U_B_.table_G_S_U_B_: ...
657
+ @overload
658
+ def __getitem__(self, tag: Literal["GVAR"]) -> G_V_A_R_.table_G_V_A_R_: ...
659
+ @overload
660
+ def __getitem__(self, tag: Literal["HVAR"]) -> H_V_A_R_.table_H_V_A_R_: ...
661
+ @overload
662
+ def __getitem__(self, tag: Literal["JSTF"]) -> J_S_T_F_.table_J_S_T_F_: ...
663
+ @overload
664
+ def __getitem__(self, tag: Literal["LTSH"]) -> L_T_S_H_.table_L_T_S_H_: ...
665
+ @overload
666
+ def __getitem__(self, tag: Literal["MATH"]) -> M_A_T_H_.table_M_A_T_H_: ...
667
+ @overload
668
+ def __getitem__(self, tag: Literal["META"]) -> M_E_T_A_.table_M_E_T_A_: ...
669
+ @overload
670
+ def __getitem__(self, tag: Literal["MVAR"]) -> M_V_A_R_.table_M_V_A_R_: ...
671
+ @overload
672
+ def __getitem__(self, tag: Literal["SING"]) -> S_I_N_G_.table_S_I_N_G_: ...
673
+ @overload
674
+ def __getitem__(self, tag: Literal["STAT"]) -> S_T_A_T_.table_S_T_A_T_: ...
675
+ @overload
676
+ def __getitem__(self, tag: Literal["SVG "]) -> S_V_G_.table_S_V_G_: ...
677
+ @overload
678
+ def __getitem__(self, tag: Literal["TSI0"]) -> T_S_I__0.table_T_S_I__0: ...
679
+ @overload
680
+ def __getitem__(self, tag: Literal["TSI1"]) -> T_S_I__1.table_T_S_I__1: ...
681
+ @overload
682
+ def __getitem__(self, tag: Literal["TSI2"]) -> T_S_I__2.table_T_S_I__2: ...
683
+ @overload
684
+ def __getitem__(self, tag: Literal["TSI3"]) -> T_S_I__3.table_T_S_I__3: ...
685
+ @overload
686
+ def __getitem__(self, tag: Literal["TSI5"]) -> T_S_I__5.table_T_S_I__5: ...
687
+ @overload
688
+ def __getitem__(self, tag: Literal["TSIB"]) -> T_S_I_B_.table_T_S_I_B_: ...
689
+ @overload
690
+ def __getitem__(self, tag: Literal["TSIC"]) -> T_S_I_C_.table_T_S_I_C_: ...
691
+ @overload
692
+ def __getitem__(self, tag: Literal["TSID"]) -> T_S_I_D_.table_T_S_I_D_: ...
693
+ @overload
694
+ def __getitem__(self, tag: Literal["TSIJ"]) -> T_S_I_J_.table_T_S_I_J_: ...
695
+ @overload
696
+ def __getitem__(self, tag: Literal["TSIP"]) -> T_S_I_P_.table_T_S_I_P_: ...
697
+ @overload
698
+ def __getitem__(self, tag: Literal["TSIS"]) -> T_S_I_S_.table_T_S_I_S_: ...
699
+ @overload
700
+ def __getitem__(self, tag: Literal["TSIV"]) -> T_S_I_V_.table_T_S_I_V_: ...
701
+ @overload
702
+ def __getitem__(self, tag: Literal["TTFA"]) -> T_T_F_A_.table_T_T_F_A_: ...
703
+ @overload
704
+ def __getitem__(self, tag: Literal["VARC"]) -> V_A_R_C_.table_V_A_R_C_: ...
705
+ @overload
706
+ def __getitem__(self, tag: Literal["VDMX"]) -> V_D_M_X_.table_V_D_M_X_: ...
707
+ @overload
708
+ def __getitem__(self, tag: Literal["VORG"]) -> V_O_R_G_.table_V_O_R_G_: ...
709
+ @overload
710
+ def __getitem__(self, tag: Literal["VVAR"]) -> V_V_A_R_.table_V_V_A_R_: ...
711
+ @overload
712
+ def __getitem__(self, tag: Literal["Debg"]) -> D__e_b_g.table_D__e_b_g: ...
713
+ @overload
714
+ def __getitem__(self, tag: Literal["Feat"]) -> F__e_a_t.table_F__e_a_t: ...
715
+ @overload
716
+ def __getitem__(self, tag: Literal["Glat"]) -> G__l_a_t.table_G__l_a_t: ...
717
+ @overload
718
+ def __getitem__(self, tag: Literal["Gloc"]) -> G__l_o_c.table_G__l_o_c: ...
719
+ @overload
720
+ def __getitem__(self, tag: Literal["OS/2"]) -> O_S_2f_2.table_O_S_2f_2: ...
721
+ @overload
722
+ def __getitem__(self, tag: Literal["Silf"]) -> S__i_l_f.table_S__i_l_f: ...
723
+ @overload
724
+ def __getitem__(self, tag: Literal["Sill"]) -> S__i_l_l.table_S__i_l_l: ...
725
+ @overload
726
+ def __getitem__(self, tag: Literal["ankr"]) -> _a_n_k_r.table__a_n_k_r: ...
727
+ @overload
728
+ def __getitem__(self, tag: Literal["avar"]) -> _a_v_a_r.table__a_v_a_r: ...
729
+ @overload
730
+ def __getitem__(self, tag: Literal["bsln"]) -> _b_s_l_n.table__b_s_l_n: ...
731
+ @overload
732
+ def __getitem__(self, tag: Literal["cidg"]) -> _c_i_d_g.table__c_i_d_g: ...
733
+ @overload
734
+ def __getitem__(self, tag: Literal["cmap"]) -> _c_m_a_p.table__c_m_a_p: ...
735
+ @overload
736
+ def __getitem__(self, tag: Literal["cvar"]) -> _c_v_a_r.table__c_v_a_r: ...
737
+ @overload
738
+ def __getitem__(self, tag: Literal["cvt "]) -> _c_v_t.table__c_v_t: ...
739
+ @overload
740
+ def __getitem__(self, tag: Literal["feat"]) -> _f_e_a_t.table__f_e_a_t: ...
741
+ @overload
742
+ def __getitem__(self, tag: Literal["fpgm"]) -> _f_p_g_m.table__f_p_g_m: ...
743
+ @overload
744
+ def __getitem__(self, tag: Literal["fvar"]) -> _f_v_a_r.table__f_v_a_r: ...
745
+ @overload
746
+ def __getitem__(self, tag: Literal["gasp"]) -> _g_a_s_p.table__g_a_s_p: ...
747
+ @overload
748
+ def __getitem__(self, tag: Literal["gcid"]) -> _g_c_i_d.table__g_c_i_d: ...
749
+ @overload
750
+ def __getitem__(self, tag: Literal["glyf"]) -> _g_l_y_f.table__g_l_y_f: ...
751
+ @overload
752
+ def __getitem__(self, tag: Literal["gvar"]) -> _g_v_a_r.table__g_v_a_r: ...
753
+ @overload
754
+ def __getitem__(self, tag: Literal["hdmx"]) -> _h_d_m_x.table__h_d_m_x: ...
755
+ @overload
756
+ def __getitem__(self, tag: Literal["head"]) -> _h_e_a_d.table__h_e_a_d: ...
757
+ @overload
758
+ def __getitem__(self, tag: Literal["hhea"]) -> _h_h_e_a.table__h_h_e_a: ...
759
+ @overload
760
+ def __getitem__(self, tag: Literal["hmtx"]) -> _h_m_t_x.table__h_m_t_x: ...
761
+ @overload
762
+ def __getitem__(self, tag: Literal["kern"]) -> _k_e_r_n.table__k_e_r_n: ...
763
+ @overload
764
+ def __getitem__(self, tag: Literal["lcar"]) -> _l_c_a_r.table__l_c_a_r: ...
765
+ @overload
766
+ def __getitem__(self, tag: Literal["loca"]) -> _l_o_c_a.table__l_o_c_a: ...
767
+ @overload
768
+ def __getitem__(self, tag: Literal["ltag"]) -> _l_t_a_g.table__l_t_a_g: ...
769
+ @overload
770
+ def __getitem__(self, tag: Literal["maxp"]) -> _m_a_x_p.table__m_a_x_p: ...
771
+ @overload
772
+ def __getitem__(self, tag: Literal["meta"]) -> _m_e_t_a.table__m_e_t_a: ...
773
+ @overload
774
+ def __getitem__(self, tag: Literal["mort"]) -> _m_o_r_t.table__m_o_r_t: ...
775
+ @overload
776
+ def __getitem__(self, tag: Literal["morx"]) -> _m_o_r_x.table__m_o_r_x: ...
777
+ @overload
778
+ def __getitem__(self, tag: Literal["name"]) -> _n_a_m_e.table__n_a_m_e: ...
779
+ @overload
780
+ def __getitem__(self, tag: Literal["opbd"]) -> _o_p_b_d.table__o_p_b_d: ...
781
+ @overload
782
+ def __getitem__(self, tag: Literal["post"]) -> _p_o_s_t.table__p_o_s_t: ...
783
+ @overload
784
+ def __getitem__(self, tag: Literal["prep"]) -> _p_r_e_p.table__p_r_e_p: ...
785
+ @overload
786
+ def __getitem__(self, tag: Literal["prop"]) -> _p_r_o_p.table__p_r_o_p: ...
787
+ @overload
788
+ def __getitem__(self, tag: Literal["sbix"]) -> _s_b_i_x.table__s_b_i_x: ...
789
+ @overload
790
+ def __getitem__(self, tag: Literal["trak"]) -> _t_r_a_k.table__t_r_a_k: ...
791
+ @overload
792
+ def __getitem__(self, tag: Literal["vhea"]) -> _v_h_e_a.table__v_h_e_a: ...
793
+ @overload
794
+ def __getitem__(self, tag: Literal["vmtx"]) -> _v_m_t_x.table__v_m_t_x: ...
795
+ @overload
796
+ def __getitem__(self, tag: Literal["GlyphOrder"]) -> GlyphOrder: ...
797
+ @overload
798
+ def __getitem__(self, tag: str | bytes) -> DefaultTable | GlyphOrder: ...
799
+
800
+ def __getitem__(self, tag: str | bytes) -> DefaultTable | GlyphOrder:
454
801
  tag = Tag(tag)
455
802
  table = self.tables.get(tag)
456
803
  if table is None:
@@ -463,8 +810,9 @@ class TTFont(object):
463
810
  raise KeyError("'%s' table not found" % tag)
464
811
  return table
465
812
 
466
- def _readTable(self, tag):
813
+ def _readTable(self, tag: Tag) -> DefaultTable:
467
814
  log.debug("Reading '%s' table from disk", tag)
815
+ assert self.reader is not None
468
816
  data = self.reader[tag]
469
817
  if self._tableCache is not None:
470
818
  table = self._tableCache.get((tag, data))
@@ -495,10 +843,10 @@ class TTFont(object):
495
843
  self._tableCache[(tag, data)] = table
496
844
  return table
497
845
 
498
- def __setitem__(self, tag, table):
846
+ def __setitem__(self, tag: str | bytes, table: DefaultTable) -> None:
499
847
  self.tables[Tag(tag)] = table
500
848
 
501
- def __delitem__(self, tag):
849
+ def __delitem__(self, tag: str | bytes) -> None:
502
850
  if tag not in self:
503
851
  raise KeyError("'%s' table not found" % tag)
504
852
  if tag in self.tables:
@@ -506,14 +854,195 @@ class TTFont(object):
506
854
  if self.reader and tag in self.reader:
507
855
  del self.reader[tag]
508
856
 
509
- def get(self, tag, default=None):
857
+ @overload
858
+ def get(self, tag: Literal["BASE"]) -> B_A_S_E_.table_B_A_S_E_ | None: ...
859
+ @overload
860
+ def get(self, tag: Literal["CBDT"]) -> C_B_D_T_.table_C_B_D_T_ | None: ...
861
+ @overload
862
+ def get(self, tag: Literal["CBLC"]) -> C_B_L_C_.table_C_B_L_C_ | None: ...
863
+ @overload
864
+ def get(self, tag: Literal["CFF "]) -> C_F_F_.table_C_F_F_ | None: ...
865
+ @overload
866
+ def get(self, tag: Literal["CFF2"]) -> C_F_F__2.table_C_F_F__2 | None: ...
867
+ @overload
868
+ def get(self, tag: Literal["COLR"]) -> C_O_L_R_.table_C_O_L_R_ | None: ...
869
+ @overload
870
+ def get(self, tag: Literal["CPAL"]) -> C_P_A_L_.table_C_P_A_L_ | None: ...
871
+ @overload
872
+ def get(self, tag: Literal["DSIG"]) -> D_S_I_G_.table_D_S_I_G_ | None: ...
873
+ @overload
874
+ def get(self, tag: Literal["EBDT"]) -> E_B_D_T_.table_E_B_D_T_ | None: ...
875
+ @overload
876
+ def get(self, tag: Literal["EBLC"]) -> E_B_L_C_.table_E_B_L_C_ | None: ...
877
+ @overload
878
+ def get(self, tag: Literal["FFTM"]) -> F_F_T_M_.table_F_F_T_M_ | None: ...
879
+ @overload
880
+ def get(self, tag: Literal["GDEF"]) -> G_D_E_F_.table_G_D_E_F_ | None: ...
881
+ @overload
882
+ def get(self, tag: Literal["GMAP"]) -> G_M_A_P_.table_G_M_A_P_ | None: ...
883
+ @overload
884
+ def get(self, tag: Literal["GPKG"]) -> G_P_K_G_.table_G_P_K_G_ | None: ...
885
+ @overload
886
+ def get(self, tag: Literal["GPOS"]) -> G_P_O_S_.table_G_P_O_S_ | None: ...
887
+ @overload
888
+ def get(self, tag: Literal["GSUB"]) -> G_S_U_B_.table_G_S_U_B_ | None: ...
889
+ @overload
890
+ def get(self, tag: Literal["GVAR"]) -> G_V_A_R_.table_G_V_A_R_ | None: ...
891
+ @overload
892
+ def get(self, tag: Literal["HVAR"]) -> H_V_A_R_.table_H_V_A_R_ | None: ...
893
+ @overload
894
+ def get(self, tag: Literal["JSTF"]) -> J_S_T_F_.table_J_S_T_F_ | None: ...
895
+ @overload
896
+ def get(self, tag: Literal["LTSH"]) -> L_T_S_H_.table_L_T_S_H_ | None: ...
897
+ @overload
898
+ def get(self, tag: Literal["MATH"]) -> M_A_T_H_.table_M_A_T_H_ | None: ...
899
+ @overload
900
+ def get(self, tag: Literal["META"]) -> M_E_T_A_.table_M_E_T_A_ | None: ...
901
+ @overload
902
+ def get(self, tag: Literal["MVAR"]) -> M_V_A_R_.table_M_V_A_R_ | None: ...
903
+ @overload
904
+ def get(self, tag: Literal["SING"]) -> S_I_N_G_.table_S_I_N_G_ | None: ...
905
+ @overload
906
+ def get(self, tag: Literal["STAT"]) -> S_T_A_T_.table_S_T_A_T_ | None: ...
907
+ @overload
908
+ def get(self, tag: Literal["SVG "]) -> S_V_G_.table_S_V_G_ | None: ...
909
+ @overload
910
+ def get(self, tag: Literal["TSI0"]) -> T_S_I__0.table_T_S_I__0 | None: ...
911
+ @overload
912
+ def get(self, tag: Literal["TSI1"]) -> T_S_I__1.table_T_S_I__1 | None: ...
913
+ @overload
914
+ def get(self, tag: Literal["TSI2"]) -> T_S_I__2.table_T_S_I__2 | None: ...
915
+ @overload
916
+ def get(self, tag: Literal["TSI3"]) -> T_S_I__3.table_T_S_I__3 | None: ...
917
+ @overload
918
+ def get(self, tag: Literal["TSI5"]) -> T_S_I__5.table_T_S_I__5 | None: ...
919
+ @overload
920
+ def get(self, tag: Literal["TSIB"]) -> T_S_I_B_.table_T_S_I_B_ | None: ...
921
+ @overload
922
+ def get(self, tag: Literal["TSIC"]) -> T_S_I_C_.table_T_S_I_C_ | None: ...
923
+ @overload
924
+ def get(self, tag: Literal["TSID"]) -> T_S_I_D_.table_T_S_I_D_ | None: ...
925
+ @overload
926
+ def get(self, tag: Literal["TSIJ"]) -> T_S_I_J_.table_T_S_I_J_ | None: ...
927
+ @overload
928
+ def get(self, tag: Literal["TSIP"]) -> T_S_I_P_.table_T_S_I_P_ | None: ...
929
+ @overload
930
+ def get(self, tag: Literal["TSIS"]) -> T_S_I_S_.table_T_S_I_S_ | None: ...
931
+ @overload
932
+ def get(self, tag: Literal["TSIV"]) -> T_S_I_V_.table_T_S_I_V_ | None: ...
933
+ @overload
934
+ def get(self, tag: Literal["TTFA"]) -> T_T_F_A_.table_T_T_F_A_ | None: ...
935
+ @overload
936
+ def get(self, tag: Literal["VARC"]) -> V_A_R_C_.table_V_A_R_C_ | None: ...
937
+ @overload
938
+ def get(self, tag: Literal["VDMX"]) -> V_D_M_X_.table_V_D_M_X_ | None: ...
939
+ @overload
940
+ def get(self, tag: Literal["VORG"]) -> V_O_R_G_.table_V_O_R_G_ | None: ...
941
+ @overload
942
+ def get(self, tag: Literal["VVAR"]) -> V_V_A_R_.table_V_V_A_R_ | None: ...
943
+ @overload
944
+ def get(self, tag: Literal["Debg"]) -> D__e_b_g.table_D__e_b_g | None: ...
945
+ @overload
946
+ def get(self, tag: Literal["Feat"]) -> F__e_a_t.table_F__e_a_t | None: ...
947
+ @overload
948
+ def get(self, tag: Literal["Glat"]) -> G__l_a_t.table_G__l_a_t | None: ...
949
+ @overload
950
+ def get(self, tag: Literal["Gloc"]) -> G__l_o_c.table_G__l_o_c | None: ...
951
+ @overload
952
+ def get(self, tag: Literal["OS/2"]) -> O_S_2f_2.table_O_S_2f_2 | None: ...
953
+ @overload
954
+ def get(self, tag: Literal["Silf"]) -> S__i_l_f.table_S__i_l_f | None: ...
955
+ @overload
956
+ def get(self, tag: Literal["Sill"]) -> S__i_l_l.table_S__i_l_l | None: ...
957
+ @overload
958
+ def get(self, tag: Literal["ankr"]) -> _a_n_k_r.table__a_n_k_r | None: ...
959
+ @overload
960
+ def get(self, tag: Literal["avar"]) -> _a_v_a_r.table__a_v_a_r | None: ...
961
+ @overload
962
+ def get(self, tag: Literal["bsln"]) -> _b_s_l_n.table__b_s_l_n | None: ...
963
+ @overload
964
+ def get(self, tag: Literal["cidg"]) -> _c_i_d_g.table__c_i_d_g | None: ...
965
+ @overload
966
+ def get(self, tag: Literal["cmap"]) -> _c_m_a_p.table__c_m_a_p | None: ...
967
+ @overload
968
+ def get(self, tag: Literal["cvar"]) -> _c_v_a_r.table__c_v_a_r | None: ...
969
+ @overload
970
+ def get(self, tag: Literal["cvt "]) -> _c_v_t.table__c_v_t | None: ...
971
+ @overload
972
+ def get(self, tag: Literal["feat"]) -> _f_e_a_t.table__f_e_a_t | None: ...
973
+ @overload
974
+ def get(self, tag: Literal["fpgm"]) -> _f_p_g_m.table__f_p_g_m | None: ...
975
+ @overload
976
+ def get(self, tag: Literal["fvar"]) -> _f_v_a_r.table__f_v_a_r | None: ...
977
+ @overload
978
+ def get(self, tag: Literal["gasp"]) -> _g_a_s_p.table__g_a_s_p | None: ...
979
+ @overload
980
+ def get(self, tag: Literal["gcid"]) -> _g_c_i_d.table__g_c_i_d | None: ...
981
+ @overload
982
+ def get(self, tag: Literal["glyf"]) -> _g_l_y_f.table__g_l_y_f | None: ...
983
+ @overload
984
+ def get(self, tag: Literal["gvar"]) -> _g_v_a_r.table__g_v_a_r | None: ...
985
+ @overload
986
+ def get(self, tag: Literal["hdmx"]) -> _h_d_m_x.table__h_d_m_x | None: ...
987
+ @overload
988
+ def get(self, tag: Literal["head"]) -> _h_e_a_d.table__h_e_a_d | None: ...
989
+ @overload
990
+ def get(self, tag: Literal["hhea"]) -> _h_h_e_a.table__h_h_e_a | None: ...
991
+ @overload
992
+ def get(self, tag: Literal["hmtx"]) -> _h_m_t_x.table__h_m_t_x | None: ...
993
+ @overload
994
+ def get(self, tag: Literal["kern"]) -> _k_e_r_n.table__k_e_r_n | None: ...
995
+ @overload
996
+ def get(self, tag: Literal["lcar"]) -> _l_c_a_r.table__l_c_a_r | None: ...
997
+ @overload
998
+ def get(self, tag: Literal["loca"]) -> _l_o_c_a.table__l_o_c_a | None: ...
999
+ @overload
1000
+ def get(self, tag: Literal["ltag"]) -> _l_t_a_g.table__l_t_a_g | None: ...
1001
+ @overload
1002
+ def get(self, tag: Literal["maxp"]) -> _m_a_x_p.table__m_a_x_p | None: ...
1003
+ @overload
1004
+ def get(self, tag: Literal["meta"]) -> _m_e_t_a.table__m_e_t_a | None: ...
1005
+ @overload
1006
+ def get(self, tag: Literal["mort"]) -> _m_o_r_t.table__m_o_r_t | None: ...
1007
+ @overload
1008
+ def get(self, tag: Literal["morx"]) -> _m_o_r_x.table__m_o_r_x | None: ...
1009
+ @overload
1010
+ def get(self, tag: Literal["name"]) -> _n_a_m_e.table__n_a_m_e | None: ...
1011
+ @overload
1012
+ def get(self, tag: Literal["opbd"]) -> _o_p_b_d.table__o_p_b_d | None: ...
1013
+ @overload
1014
+ def get(self, tag: Literal["post"]) -> _p_o_s_t.table__p_o_s_t | None: ...
1015
+ @overload
1016
+ def get(self, tag: Literal["prep"]) -> _p_r_e_p.table__p_r_e_p | None: ...
1017
+ @overload
1018
+ def get(self, tag: Literal["prop"]) -> _p_r_o_p.table__p_r_o_p | None: ...
1019
+ @overload
1020
+ def get(self, tag: Literal["sbix"]) -> _s_b_i_x.table__s_b_i_x | None: ...
1021
+ @overload
1022
+ def get(self, tag: Literal["trak"]) -> _t_r_a_k.table__t_r_a_k | None: ...
1023
+ @overload
1024
+ def get(self, tag: Literal["vhea"]) -> _v_h_e_a.table__v_h_e_a | None: ...
1025
+ @overload
1026
+ def get(self, tag: Literal["vmtx"]) -> _v_m_t_x.table__v_m_t_x | None: ...
1027
+ @overload
1028
+ def get(self, tag: Literal["GlyphOrder"]) -> GlyphOrder: ...
1029
+ @overload
1030
+ def get(self, tag: str | bytes) -> DefaultTable | GlyphOrder | Any | None: ...
1031
+ @overload
1032
+ def get(
1033
+ self, tag: str | bytes, default: _VT_co
1034
+ ) -> DefaultTable | GlyphOrder | Any | _VT_co: ...
1035
+
1036
+ def get(
1037
+ self, tag: str | bytes, default: Any | None = None
1038
+ ) -> DefaultTable | GlyphOrder | Any | None:
510
1039
  """Returns the table if it exists or (optionally) a default if it doesn't."""
511
1040
  try:
512
1041
  return self[tag]
513
1042
  except KeyError:
514
1043
  return default
515
1044
 
516
- def setGlyphOrder(self, glyphOrder):
1045
+ def setGlyphOrder(self, glyphOrder: list[str]) -> None:
517
1046
  """Set the glyph order
518
1047
 
519
1048
  Args:
@@ -525,7 +1054,7 @@ class TTFont(object):
525
1054
  if self.isLoaded("glyf"):
526
1055
  self["glyf"].setGlyphOrder(glyphOrder)
527
1056
 
528
- def getGlyphOrder(self):
1057
+ def getGlyphOrder(self) -> list[str]:
529
1058
  """Returns a list of glyph names ordered by their position in the font."""
530
1059
  try:
531
1060
  return self.glyphOrder
@@ -560,7 +1089,7 @@ class TTFont(object):
560
1089
  self._getGlyphNamesFromCmap()
561
1090
  return self.glyphOrder
562
1091
 
563
- def _getGlyphNamesFromCmap(self):
1092
+ def _getGlyphNamesFromCmap(self) -> None:
564
1093
  #
565
1094
  # This is rather convoluted, but then again, it's an interesting problem:
566
1095
  # - we need to use the unicode values found in the cmap table to
@@ -589,10 +1118,8 @@ class TTFont(object):
589
1118
  # temporary cmap and by the real cmap in case we don't find a unicode
590
1119
  # cmap.
591
1120
  numGlyphs = int(self["maxp"].numGlyphs)
592
- glyphOrder = [None] * numGlyphs
1121
+ glyphOrder = ["glyph%.5d" % i for i in range(numGlyphs)]
593
1122
  glyphOrder[0] = ".notdef"
594
- for i in range(1, numGlyphs):
595
- glyphOrder[i] = "glyph%.5d" % i
596
1123
  # Set the glyph order, so the cmap parser has something
597
1124
  # to work with (so we don't get called recursively).
598
1125
  self.glyphOrder = glyphOrder
@@ -602,17 +1129,16 @@ class TTFont(object):
602
1129
  # this naming table will usually not cover all glyphs in the font.
603
1130
  # If the font has no Unicode cmap table, reversecmap will be empty.
604
1131
  if "cmap" in self:
605
- reversecmap = self["cmap"].buildReversed()
1132
+ reversecmap = self["cmap"].buildReversedMin()
606
1133
  else:
607
1134
  reversecmap = {}
608
1135
  useCount = {}
609
- for i in range(numGlyphs):
610
- tempName = glyphOrder[i]
1136
+ for i, tempName in enumerate(glyphOrder):
611
1137
  if tempName in reversecmap:
612
1138
  # If a font maps both U+0041 LATIN CAPITAL LETTER A and
613
1139
  # U+0391 GREEK CAPITAL LETTER ALPHA to the same glyph,
614
1140
  # we prefer naming the glyph as "A".
615
- glyphName = self._makeGlyphName(min(reversecmap[tempName]))
1141
+ glyphName = self._makeGlyphName(reversecmap[tempName])
616
1142
  numUses = useCount[glyphName] = useCount.get(glyphName, 0) + 1
617
1143
  if numUses > 1:
618
1144
  glyphName = "%s.alt%d" % (glyphName, numUses - 1)
@@ -629,7 +1155,7 @@ class TTFont(object):
629
1155
  self.tables["cmap"] = cmapLoading
630
1156
 
631
1157
  @staticmethod
632
- def _makeGlyphName(codepoint):
1158
+ def _makeGlyphName(codepoint: int) -> str:
633
1159
  from fontTools import agl # Adobe Glyph List
634
1160
 
635
1161
  if codepoint in agl.UV2AGL:
@@ -639,12 +1165,12 @@ class TTFont(object):
639
1165
  else:
640
1166
  return "u%X" % codepoint
641
1167
 
642
- def getGlyphNames(self):
1168
+ def getGlyphNames(self) -> list[str]:
643
1169
  """Get a list of glyph names, sorted alphabetically."""
644
1170
  glyphNames = sorted(self.getGlyphOrder())
645
1171
  return glyphNames
646
1172
 
647
- def getGlyphNames2(self):
1173
+ def getGlyphNames2(self) -> list[str]:
648
1174
  """Get a list of glyph names, sorted alphabetically,
649
1175
  but not case sensitive.
650
1176
  """
@@ -652,7 +1178,7 @@ class TTFont(object):
652
1178
 
653
1179
  return textTools.caselessSort(self.getGlyphOrder())
654
1180
 
655
- def getGlyphName(self, glyphID):
1181
+ def getGlyphName(self, glyphID: int) -> str:
656
1182
  """Returns the name for the glyph with the given ID.
657
1183
 
658
1184
  If no name is available, synthesises one with the form ``glyphXXXXX``` where
@@ -663,13 +1189,13 @@ class TTFont(object):
663
1189
  except IndexError:
664
1190
  return "glyph%.5d" % glyphID
665
1191
 
666
- def getGlyphNameMany(self, lst):
1192
+ def getGlyphNameMany(self, lst: Sequence[int]) -> list[str]:
667
1193
  """Converts a list of glyph IDs into a list of glyph names."""
668
1194
  glyphOrder = self.getGlyphOrder()
669
1195
  cnt = len(glyphOrder)
670
1196
  return [glyphOrder[gid] if gid < cnt else "glyph%.5d" % gid for gid in lst]
671
1197
 
672
- def getGlyphID(self, glyphName):
1198
+ def getGlyphID(self, glyphName: str) -> int:
673
1199
  """Returns the ID of the glyph with the given name."""
674
1200
  try:
675
1201
  return self.getReverseGlyphMap()[glyphName]
@@ -681,7 +1207,7 @@ class TTFont(object):
681
1207
  raise KeyError(glyphName)
682
1208
  raise
683
1209
 
684
- def getGlyphIDMany(self, lst):
1210
+ def getGlyphIDMany(self, lst: Sequence[str]) -> list[int]:
685
1211
  """Converts a list of glyph names into a list of glyph IDs."""
686
1212
  d = self.getReverseGlyphMap()
687
1213
  try:
@@ -690,19 +1216,25 @@ class TTFont(object):
690
1216
  getGlyphID = self.getGlyphID
691
1217
  return [getGlyphID(glyphName) for glyphName in lst]
692
1218
 
693
- def getReverseGlyphMap(self, rebuild=False):
1219
+ def getReverseGlyphMap(self, rebuild: bool = False) -> dict[str, int]:
694
1220
  """Returns a mapping of glyph names to glyph IDs."""
695
1221
  if rebuild or not hasattr(self, "_reverseGlyphOrderDict"):
696
1222
  self._buildReverseGlyphOrderDict()
697
1223
  return self._reverseGlyphOrderDict
698
1224
 
699
- def _buildReverseGlyphOrderDict(self):
1225
+ def _buildReverseGlyphOrderDict(self) -> dict[str, int]:
700
1226
  self._reverseGlyphOrderDict = d = {}
701
1227
  for glyphID, glyphName in enumerate(self.getGlyphOrder()):
702
1228
  d[glyphName] = glyphID
703
1229
  return d
704
1230
 
705
- def _writeTable(self, tag, writer, done, tableCache=None):
1231
+ def _writeTable(
1232
+ self,
1233
+ tag: str | bytes,
1234
+ writer: SFNTWriter,
1235
+ done: list[str | bytes], # Use list as original
1236
+ tableCache: MutableMapping[tuple[Tag, bytes], DefaultTable] | None = None,
1237
+ ) -> None:
706
1238
  """Internal helper function for self.save(). Keeps track of
707
1239
  inter-table dependencies.
708
1240
  """
@@ -728,7 +1260,7 @@ class TTFont(object):
728
1260
  if tableCache is not None:
729
1261
  tableCache[(Tag(tag), tabledata)] = writer[tag]
730
1262
 
731
- def getTableData(self, tag):
1263
+ def getTableData(self, tag: str | bytes) -> bytes:
732
1264
  """Returns the binary representation of a table.
733
1265
 
734
1266
  If the table is currently loaded and in memory, the data is compiled to
@@ -746,8 +1278,12 @@ class TTFont(object):
746
1278
  raise KeyError(tag)
747
1279
 
748
1280
  def getGlyphSet(
749
- self, preferCFF=True, location=None, normalized=False, recalcBounds=True
750
- ):
1281
+ self,
1282
+ preferCFF: bool = True,
1283
+ location: Mapping[str, _NumberT] | None = None,
1284
+ normalized: bool = False,
1285
+ recalcBounds: bool = True,
1286
+ ) -> _TTGlyphSet:
751
1287
  """Return a generic GlyphSet, which is a dict-like object
752
1288
  mapping glyph names to glyph objects. The returned glyph objects
753
1289
  have a ``.draw()`` method that supports the Pen protocol, and will
@@ -786,7 +1322,7 @@ class TTFont(object):
786
1322
  glyphSet = _TTGlyphSetVARC(self, location, glyphSet)
787
1323
  return glyphSet
788
1324
 
789
- def normalizeLocation(self, location):
1325
+ def normalizeLocation(self, location: Mapping[str, float]) -> dict[str, float]:
790
1326
  """Normalize a ``location`` from the font's defined axes space (also
791
1327
  known as user space) into the normalized (-1..+1) space. It applies
792
1328
  ``avar`` mapping if the font contains an ``avar`` table.
@@ -809,7 +1345,7 @@ class TTFont(object):
809
1345
 
810
1346
  def getBestCmap(
811
1347
  self,
812
- cmapPreferences=(
1348
+ cmapPreferences: Sequence[tuple[int, int]] = (
813
1349
  (3, 10),
814
1350
  (0, 6),
815
1351
  (0, 4),
@@ -819,7 +1355,7 @@ class TTFont(object):
819
1355
  (0, 1),
820
1356
  (0, 0),
821
1357
  ),
822
- ):
1358
+ ) -> dict[int, str] | None:
823
1359
  """Returns the 'best' Unicode cmap dictionary available in the font
824
1360
  or ``None``, if no Unicode cmap subtable is available.
825
1361
 
@@ -844,7 +1380,7 @@ class TTFont(object):
844
1380
  """
845
1381
  return self["cmap"].getBestCmap(cmapPreferences=cmapPreferences)
846
1382
 
847
- def reorderGlyphs(self, new_glyph_order):
1383
+ def reorderGlyphs(self, new_glyph_order: list[str]) -> None:
848
1384
  from .reorderGlyphs import reorderGlyphs
849
1385
 
850
1386
  reorderGlyphs(self, new_glyph_order)
@@ -855,21 +1391,22 @@ class GlyphOrder(object):
855
1391
  table, but it's nice to present it as such in the TTX format.
856
1392
  """
857
1393
 
858
- def __init__(self, tag=None):
1394
+ def __init__(self, tag: str | None = None) -> None:
859
1395
  pass
860
1396
 
861
- def toXML(self, writer, ttFont):
1397
+ def toXML(self, writer: xmlWriter.XMLWriter, ttFont: TTFont) -> None:
862
1398
  glyphOrder = ttFont.getGlyphOrder()
863
1399
  writer.comment(
864
- "The 'id' attribute is only for humans; " "it is ignored when parsed."
1400
+ "The 'id' attribute is only for humans; it is ignored when parsed."
865
1401
  )
866
1402
  writer.newline()
867
- for i in range(len(glyphOrder)):
868
- glyphName = glyphOrder[i]
1403
+ for i, glyphName in enumerate(glyphOrder):
869
1404
  writer.simpletag("GlyphID", id=i, name=glyphName)
870
1405
  writer.newline()
871
1406
 
872
- def fromXML(self, name, attrs, content, ttFont):
1407
+ def fromXML(
1408
+ self, name: str, attrs: dict[str, str], content: list[Any], ttFont: TTFont
1409
+ ) -> None:
873
1410
  if not hasattr(self, "glyphOrder"):
874
1411
  self.glyphOrder = []
875
1412
  if name == "GlyphID":
@@ -877,7 +1414,7 @@ class GlyphOrder(object):
877
1414
  ttFont.setGlyphOrder(self.glyphOrder)
878
1415
 
879
1416
 
880
- def getTableModule(tag):
1417
+ def getTableModule(tag: str | bytes) -> ModuleType | None:
881
1418
  """Fetch the packer/unpacker module for a table.
882
1419
  Return None when no module is found.
883
1420
  """
@@ -902,10 +1439,12 @@ def getTableModule(tag):
902
1439
  # Registry for custom table packer/unpacker classes. Keys are table
903
1440
  # tags, values are (moduleName, className) tuples.
904
1441
  # See registerCustomTableClass() and getCustomTableClass()
905
- _customTableRegistry = {}
1442
+ _customTableRegistry: dict[str | bytes, tuple[str, str]] = {}
906
1443
 
907
1444
 
908
- def registerCustomTableClass(tag, moduleName, className=None):
1445
+ def registerCustomTableClass(
1446
+ tag: str | bytes, moduleName: str, className: str | None = None
1447
+ ) -> None:
909
1448
  """Register a custom packer/unpacker class for a table.
910
1449
 
911
1450
  The 'moduleName' must be an importable module. If no 'className'
@@ -920,12 +1459,12 @@ def registerCustomTableClass(tag, moduleName, className=None):
920
1459
  _customTableRegistry[tag] = (moduleName, className)
921
1460
 
922
1461
 
923
- def unregisterCustomTableClass(tag):
1462
+ def unregisterCustomTableClass(tag: str | bytes) -> None:
924
1463
  """Unregister the custom packer/unpacker class for a table."""
925
1464
  del _customTableRegistry[tag]
926
1465
 
927
1466
 
928
- def getCustomTableClass(tag):
1467
+ def getCustomTableClass(tag: str | bytes) -> type[DefaultTable] | None:
929
1468
  """Return the custom table class for tag, if one has been registered
930
1469
  with 'registerCustomTableClass()'. Else return None.
931
1470
  """
@@ -938,7 +1477,7 @@ def getCustomTableClass(tag):
938
1477
  return getattr(module, className)
939
1478
 
940
1479
 
941
- def getTableClass(tag):
1480
+ def getTableClass(tag: str | bytes) -> type[DefaultTable]:
942
1481
  """Fetch the packer/unpacker class for a table."""
943
1482
  tableClass = getCustomTableClass(tag)
944
1483
  if tableClass is not None:
@@ -953,7 +1492,7 @@ def getTableClass(tag):
953
1492
  return tableClass
954
1493
 
955
1494
 
956
- def getClassTag(klass):
1495
+ def getClassTag(klass: type[DefaultTable]) -> str | bytes:
957
1496
  """Fetch the table tag for a class object."""
958
1497
  name = klass.__name__
959
1498
  assert name[:6] == "table_"
@@ -961,13 +1500,13 @@ def getClassTag(klass):
961
1500
  return identifierToTag(name)
962
1501
 
963
1502
 
964
- def newTable(tag):
1503
+ def newTable(tag: str | bytes) -> DefaultTable:
965
1504
  """Return a new instance of a table."""
966
1505
  tableClass = getTableClass(tag)
967
1506
  return tableClass(tag)
968
1507
 
969
1508
 
970
- def _escapechar(c):
1509
+ def _escapechar(c: str) -> str:
971
1510
  """Helper function for tagToIdentifier()"""
972
1511
  import re
973
1512
 
@@ -979,7 +1518,7 @@ def _escapechar(c):
979
1518
  return hex(byteord(c))[2:]
980
1519
 
981
1520
 
982
- def tagToIdentifier(tag):
1521
+ def tagToIdentifier(tag: str | bytes) -> str:
983
1522
  """Convert a table tag to a valid (but UGLY) python identifier,
984
1523
  as well as a filename that's guaranteed to be unique even on a
985
1524
  caseless file system. Each character is mapped to two characters.
@@ -1014,7 +1553,7 @@ def tagToIdentifier(tag):
1014
1553
  return ident
1015
1554
 
1016
1555
 
1017
- def identifierToTag(ident):
1556
+ def identifierToTag(ident: str) -> str:
1018
1557
  """the opposite of tagToIdentifier()"""
1019
1558
  if ident == "GlyphOrder":
1020
1559
  return ident
@@ -1035,7 +1574,7 @@ def identifierToTag(ident):
1035
1574
  return Tag(tag)
1036
1575
 
1037
1576
 
1038
- def tagToXML(tag):
1577
+ def tagToXML(tag: str | bytes) -> str:
1039
1578
  """Similarly to tagToIdentifier(), this converts a TT tag
1040
1579
  to a valid XML element name. Since XML element names are
1041
1580
  case sensitive, this is a fairly simple/readable translation.
@@ -1053,7 +1592,7 @@ def tagToXML(tag):
1053
1592
  return tagToIdentifier(tag)
1054
1593
 
1055
1594
 
1056
- def xmlToTag(tag):
1595
+ def xmlToTag(tag: str) -> str:
1057
1596
  """The opposite of tagToXML()"""
1058
1597
  if tag == "OS_2":
1059
1598
  return Tag("OS/2")
@@ -1089,7 +1628,9 @@ TTFTableOrder = [
1089
1628
  OTFTableOrder = ["head", "hhea", "maxp", "OS/2", "name", "cmap", "post", "CFF "]
1090
1629
 
1091
1630
 
1092
- def sortedTagList(tagList, tableOrder=None):
1631
+ def sortedTagList(
1632
+ tagList: Sequence[str], tableOrder: Sequence[str] | None = None
1633
+ ) -> list[str]:
1093
1634
  """Return a sorted copy of tagList, sorted according to the OpenType
1094
1635
  specification, or according to a custom tableOrder. If given and not
1095
1636
  None, tableOrder needs to be a list of tag names.
@@ -1113,7 +1654,12 @@ def sortedTagList(tagList, tableOrder=None):
1113
1654
  return orderedTables
1114
1655
 
1115
1656
 
1116
- def reorderFontTables(inFile, outFile, tableOrder=None, checkChecksums=False):
1657
+ def reorderFontTables(
1658
+ inFile: BinaryIO, # Takes file-like object as per original
1659
+ outFile: BinaryIO, # Takes file-like object
1660
+ tableOrder: Sequence[str] | None = None,
1661
+ checkChecksums: bool = False, # Keep param even if reader handles it
1662
+ ) -> None:
1117
1663
  """Rewrite a font file, ordering the tables as recommended by the
1118
1664
  OpenType specification 1.4.
1119
1665
  """
@@ -1133,7 +1679,7 @@ def reorderFontTables(inFile, outFile, tableOrder=None, checkChecksums=False):
1133
1679
  writer.close()
1134
1680
 
1135
1681
 
1136
- def maxPowerOfTwo(x):
1682
+ def maxPowerOfTwo(x: int) -> int:
1137
1683
  """Return the highest exponent of two, so that
1138
1684
  (2 ** exponent) <= x. Return 0 if x is 0.
1139
1685
  """
@@ -1144,7 +1690,7 @@ def maxPowerOfTwo(x):
1144
1690
  return max(exponent - 1, 0)
1145
1691
 
1146
1692
 
1147
- def getSearchRange(n, itemSize=16):
1693
+ def getSearchRange(n: int, itemSize: int = 16) -> tuple[int, int, int]:
1148
1694
  """Calculate searchRange, entrySelector, rangeShift."""
1149
1695
  # itemSize defaults to 16, for backward compatibility
1150
1696
  # with upstream fonttools.