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.
- fontTools/__init__.py +1 -1
- fontTools/annotations.py +30 -0
- fontTools/cffLib/CFF2ToCFF.py +65 -10
- fontTools/cffLib/__init__.py +61 -26
- fontTools/cffLib/specializer.py +4 -1
- fontTools/cffLib/transforms.py +11 -6
- fontTools/config/__init__.py +15 -0
- fontTools/cu2qu/cu2qu.c +6567 -5579
- fontTools/cu2qu/cu2qu.cpython-313-aarch64-linux-musl.so +0 -0
- fontTools/cu2qu/cu2qu.py +36 -4
- fontTools/cu2qu/ufo.py +14 -0
- fontTools/designspaceLib/__init__.py +8 -3
- fontTools/designspaceLib/statNames.py +14 -7
- fontTools/feaLib/ast.py +24 -15
- fontTools/feaLib/builder.py +139 -66
- fontTools/feaLib/error.py +1 -1
- fontTools/feaLib/lexer.c +7038 -7995
- fontTools/feaLib/lexer.cpython-313-aarch64-linux-musl.so +0 -0
- fontTools/feaLib/parser.py +75 -40
- fontTools/feaLib/variableScalar.py +6 -1
- fontTools/fontBuilder.py +50 -44
- fontTools/merge/__init__.py +1 -1
- fontTools/merge/cmap.py +33 -1
- fontTools/merge/tables.py +12 -1
- fontTools/misc/bezierTools.c +14913 -17013
- fontTools/misc/bezierTools.cpython-313-aarch64-linux-musl.so +0 -0
- fontTools/misc/bezierTools.py +4 -1
- fontTools/misc/configTools.py +3 -1
- fontTools/misc/enumTools.py +23 -0
- fontTools/misc/etree.py +4 -27
- fontTools/misc/filesystem/__init__.py +68 -0
- fontTools/misc/filesystem/_base.py +134 -0
- fontTools/misc/filesystem/_copy.py +45 -0
- fontTools/misc/filesystem/_errors.py +54 -0
- fontTools/misc/filesystem/_info.py +75 -0
- fontTools/misc/filesystem/_osfs.py +164 -0
- fontTools/misc/filesystem/_path.py +67 -0
- fontTools/misc/filesystem/_subfs.py +92 -0
- fontTools/misc/filesystem/_tempfs.py +34 -0
- fontTools/misc/filesystem/_tools.py +34 -0
- fontTools/misc/filesystem/_walk.py +55 -0
- fontTools/misc/filesystem/_zipfs.py +204 -0
- fontTools/misc/fixedTools.py +1 -1
- fontTools/misc/loggingTools.py +1 -1
- fontTools/misc/psCharStrings.py +17 -2
- fontTools/misc/sstruct.py +2 -6
- fontTools/misc/symfont.py +6 -8
- fontTools/misc/testTools.py +5 -1
- fontTools/misc/textTools.py +4 -2
- fontTools/misc/visitor.py +32 -16
- fontTools/misc/xmlWriter.py +44 -8
- fontTools/mtiLib/__init__.py +1 -3
- fontTools/otlLib/builder.py +402 -155
- fontTools/otlLib/optimize/gpos.py +49 -63
- fontTools/pens/filterPen.py +218 -26
- fontTools/pens/momentsPen.c +5514 -5584
- fontTools/pens/momentsPen.cpython-313-aarch64-linux-musl.so +0 -0
- fontTools/pens/pointPen.py +61 -18
- fontTools/pens/roundingPen.py +2 -2
- fontTools/pens/t2CharStringPen.py +31 -11
- fontTools/qu2cu/qu2cu.c +6581 -6168
- fontTools/qu2cu/qu2cu.cpython-313-aarch64-linux-musl.so +0 -0
- fontTools/subset/__init__.py +283 -25
- fontTools/subset/svg.py +2 -3
- fontTools/ttLib/__init__.py +4 -0
- fontTools/ttLib/__main__.py +47 -8
- fontTools/ttLib/removeOverlaps.py +7 -5
- fontTools/ttLib/reorderGlyphs.py +8 -7
- fontTools/ttLib/sfnt.py +11 -9
- fontTools/ttLib/tables/D__e_b_g.py +20 -2
- fontTools/ttLib/tables/G_V_A_R_.py +5 -0
- fontTools/ttLib/tables/S__i_l_f.py +2 -2
- fontTools/ttLib/tables/T_S_I__0.py +14 -3
- fontTools/ttLib/tables/T_S_I__1.py +2 -5
- fontTools/ttLib/tables/T_S_I__5.py +18 -7
- fontTools/ttLib/tables/__init__.py +1 -0
- fontTools/ttLib/tables/_a_v_a_r.py +12 -3
- fontTools/ttLib/tables/_c_m_a_p.py +20 -7
- fontTools/ttLib/tables/_c_v_t.py +3 -2
- fontTools/ttLib/tables/_f_p_g_m.py +3 -1
- fontTools/ttLib/tables/_g_l_y_f.py +45 -21
- fontTools/ttLib/tables/_g_v_a_r.py +67 -19
- fontTools/ttLib/tables/_h_d_m_x.py +4 -4
- fontTools/ttLib/tables/_h_m_t_x.py +7 -3
- fontTools/ttLib/tables/_l_o_c_a.py +2 -2
- fontTools/ttLib/tables/_n_a_m_e.py +11 -6
- fontTools/ttLib/tables/_p_o_s_t.py +9 -7
- fontTools/ttLib/tables/otBase.py +5 -12
- fontTools/ttLib/tables/otConverters.py +5 -2
- fontTools/ttLib/tables/otData.py +1 -1
- fontTools/ttLib/tables/otTables.py +33 -30
- fontTools/ttLib/tables/otTraverse.py +2 -1
- fontTools/ttLib/tables/sbixStrike.py +3 -3
- fontTools/ttLib/ttFont.py +666 -120
- fontTools/ttLib/ttGlyphSet.py +0 -10
- fontTools/ttLib/woff2.py +10 -13
- fontTools/ttx.py +13 -1
- fontTools/ufoLib/__init__.py +300 -202
- fontTools/ufoLib/converters.py +103 -30
- fontTools/ufoLib/errors.py +8 -0
- fontTools/ufoLib/etree.py +1 -1
- fontTools/ufoLib/filenames.py +171 -106
- fontTools/ufoLib/glifLib.py +303 -205
- fontTools/ufoLib/kerning.py +98 -48
- fontTools/ufoLib/utils.py +46 -15
- fontTools/ufoLib/validators.py +121 -99
- fontTools/unicodedata/Blocks.py +35 -20
- fontTools/unicodedata/Mirrored.py +446 -0
- fontTools/unicodedata/ScriptExtensions.py +63 -37
- fontTools/unicodedata/Scripts.py +173 -152
- fontTools/unicodedata/__init__.py +10 -2
- fontTools/varLib/__init__.py +198 -109
- fontTools/varLib/avar/__init__.py +0 -0
- fontTools/varLib/avar/__main__.py +72 -0
- fontTools/varLib/avar/build.py +79 -0
- fontTools/varLib/avar/map.py +108 -0
- fontTools/varLib/avar/plan.py +1004 -0
- fontTools/varLib/{avar.py → avar/unbuild.py} +70 -59
- fontTools/varLib/avarPlanner.py +3 -999
- fontTools/varLib/featureVars.py +21 -7
- fontTools/varLib/hvar.py +113 -0
- fontTools/varLib/instancer/__init__.py +180 -65
- fontTools/varLib/interpolatableHelpers.py +3 -0
- fontTools/varLib/iup.c +7564 -6903
- fontTools/varLib/iup.cpython-313-aarch64-linux-musl.so +0 -0
- fontTools/varLib/models.py +17 -2
- fontTools/varLib/mutator.py +11 -0
- fontTools/varLib/varStore.py +10 -38
- fontTools/voltLib/__main__.py +206 -0
- fontTools/voltLib/ast.py +4 -0
- fontTools/voltLib/parser.py +16 -8
- fontTools/voltLib/voltToFea.py +347 -166
- {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/METADATA +269 -1410
- {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/RECORD +318 -294
- {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/WHEEL +1 -1
- fonttools-4.61.1.dist-info/licenses/LICENSE.external +388 -0
- {fonttools-4.55.4.data → fonttools-4.61.1.data}/data/share/man/man1/ttx.1 +0 -0
- {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/entry_points.txt +0 -0
- {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info/licenses}/LICENSE +0 -0
- {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
|
-
|
|
14
|
-
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import
|
|
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
|
|
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
|
-
:
|
|
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
|
|
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__(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
255
|
-
|
|
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 =
|
|
259
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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(
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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 = [
|
|
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"].
|
|
1132
|
+
reversecmap = self["cmap"].buildReversedMin()
|
|
606
1133
|
else:
|
|
607
1134
|
reversecmap = {}
|
|
608
1135
|
useCount = {}
|
|
609
|
-
for i in
|
|
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(
|
|
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(
|
|
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,
|
|
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;
|
|
1400
|
+
"The 'id' attribute is only for humans; it is ignored when parsed."
|
|
865
1401
|
)
|
|
866
1402
|
writer.newline()
|
|
867
|
-
for i in
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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.
|