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/ufoLib/glifLib.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Generic module for reading and writing the .glif format.
|
|
3
3
|
|
|
4
4
|
More info about the .glif format (GLyphInterchangeFormat) can be found here:
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
http://unifiedfontobject.org
|
|
7
7
|
|
|
8
|
-
The main class in this module is GlyphSet
|
|
8
|
+
The main class in this module is :class:`GlyphSet`. It manages a set of .glif files
|
|
9
9
|
in a folder. It offers two ways to read glyph data, and one way to write
|
|
10
10
|
glyph data. See the class doc string for details.
|
|
11
11
|
"""
|
|
@@ -13,34 +13,50 @@ glyph data. See the class doc string for details.
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
15
|
import logging
|
|
16
|
-
import enum
|
|
17
|
-
from warnings import warn
|
|
18
16
|
from collections import OrderedDict
|
|
19
|
-
import
|
|
20
|
-
import
|
|
21
|
-
|
|
22
|
-
import fs
|
|
23
|
-
import
|
|
17
|
+
from typing import TYPE_CHECKING, Any, Optional, Union, cast
|
|
18
|
+
from warnings import warn
|
|
19
|
+
|
|
20
|
+
import fontTools.misc.filesystem as fs
|
|
21
|
+
from fontTools.misc import etree, plistlib
|
|
24
22
|
from fontTools.misc.textTools import tobytes
|
|
25
|
-
from fontTools.misc import plistlib
|
|
26
23
|
from fontTools.pens.pointPen import AbstractPointPen, PointToSegmentPen
|
|
24
|
+
from fontTools.ufoLib import UFOFormatVersion, _UFOBaseIO
|
|
27
25
|
from fontTools.ufoLib.errors import GlifLibError
|
|
28
26
|
from fontTools.ufoLib.filenames import userNameToFileName
|
|
27
|
+
from fontTools.ufoLib.utils import (
|
|
28
|
+
BaseFormatVersion,
|
|
29
|
+
normalizeFormatVersion,
|
|
30
|
+
numberTypes,
|
|
31
|
+
)
|
|
29
32
|
from fontTools.ufoLib.validators import (
|
|
30
|
-
|
|
33
|
+
anchorsValidator,
|
|
31
34
|
colorValidator,
|
|
35
|
+
genericTypeValidator,
|
|
36
|
+
glyphLibValidator,
|
|
32
37
|
guidelinesValidator,
|
|
33
|
-
anchorsValidator,
|
|
34
38
|
identifierValidator,
|
|
35
39
|
imageValidator,
|
|
36
|
-
glyphLibValidator,
|
|
37
40
|
)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
from
|
|
41
|
+
|
|
42
|
+
if TYPE_CHECKING:
|
|
43
|
+
from collections.abc import Callable, Iterable, Set
|
|
44
|
+
from logging import Logger
|
|
45
|
+
|
|
46
|
+
from fontTools.annotations import (
|
|
47
|
+
ElementType,
|
|
48
|
+
FormatVersion,
|
|
49
|
+
FormatVersions,
|
|
50
|
+
GLIFFormatVersionInput,
|
|
51
|
+
GlyphNameToFileNameFunc,
|
|
52
|
+
IntFloat,
|
|
53
|
+
PathOrFS,
|
|
54
|
+
UFOFormatVersionInput,
|
|
55
|
+
)
|
|
56
|
+
from fontTools.misc.filesystem._base import FS
|
|
41
57
|
|
|
42
58
|
|
|
43
|
-
__all__ = [
|
|
59
|
+
__all__: list[str] = [
|
|
44
60
|
"GlyphSet",
|
|
45
61
|
"GlifLibError",
|
|
46
62
|
"readGlyphFromString",
|
|
@@ -48,7 +64,7 @@ __all__ = [
|
|
|
48
64
|
"glyphNameToFileName",
|
|
49
65
|
]
|
|
50
66
|
|
|
51
|
-
logger = logging.getLogger(__name__)
|
|
67
|
+
logger: Logger = logging.getLogger(__name__)
|
|
52
68
|
|
|
53
69
|
|
|
54
70
|
# ---------
|
|
@@ -59,18 +75,29 @@ CONTENTS_FILENAME = "contents.plist"
|
|
|
59
75
|
LAYERINFO_FILENAME = "layerinfo.plist"
|
|
60
76
|
|
|
61
77
|
|
|
62
|
-
class GLIFFormatVersion(
|
|
78
|
+
class GLIFFormatVersion(BaseFormatVersion):
|
|
79
|
+
"""Class representing the versions of the .glif format supported by the UFO version in use.
|
|
80
|
+
|
|
81
|
+
For a given :mod:`fontTools.ufoLib.UFOFormatVersion`, the :func:`supported_versions` method will
|
|
82
|
+
return the supported versions of the GLIF file format. If the UFO version is unspecified, the
|
|
83
|
+
:func:`supported_versions` method will return all available GLIF format versions.
|
|
84
|
+
"""
|
|
85
|
+
|
|
63
86
|
FORMAT_1_0 = (1, 0)
|
|
64
87
|
FORMAT_2_0 = (2, 0)
|
|
65
88
|
|
|
66
89
|
@classmethod
|
|
67
|
-
def default(
|
|
90
|
+
def default(
|
|
91
|
+
cls, ufoFormatVersion: Optional[UFOFormatVersion] = None
|
|
92
|
+
) -> GLIFFormatVersion:
|
|
68
93
|
if ufoFormatVersion is not None:
|
|
69
94
|
return max(cls.supported_versions(ufoFormatVersion))
|
|
70
95
|
return super().default()
|
|
71
96
|
|
|
72
97
|
@classmethod
|
|
73
|
-
def supported_versions(
|
|
98
|
+
def supported_versions(
|
|
99
|
+
cls, ufoFormatVersion: Optional[UFOFormatVersion] = None
|
|
100
|
+
) -> frozenset[GLIFFormatVersion]:
|
|
74
101
|
if ufoFormatVersion is None:
|
|
75
102
|
# if ufo format unspecified, return all the supported GLIF formats
|
|
76
103
|
return super().supported_versions()
|
|
@@ -81,10 +108,6 @@ class GLIFFormatVersion(tuple, _VersionTupleEnumMixin, enum.Enum):
|
|
|
81
108
|
return frozenset(versions)
|
|
82
109
|
|
|
83
110
|
|
|
84
|
-
# workaround for py3.11, see https://github.com/fonttools/fonttools/pull/2655
|
|
85
|
-
GLIFFormatVersion.__str__ = _VersionTupleEnumMixin.__str__
|
|
86
|
-
|
|
87
|
-
|
|
88
111
|
# ------------
|
|
89
112
|
# Simple Glyph
|
|
90
113
|
# ------------
|
|
@@ -96,11 +119,11 @@ class Glyph:
|
|
|
96
119
|
the draw() or the drawPoints() method has been called.
|
|
97
120
|
"""
|
|
98
121
|
|
|
99
|
-
def __init__(self, glyphName, glyphSet):
|
|
100
|
-
self.glyphName = glyphName
|
|
101
|
-
self.glyphSet = glyphSet
|
|
122
|
+
def __init__(self, glyphName: str, glyphSet: GlyphSet) -> None:
|
|
123
|
+
self.glyphName: str = glyphName
|
|
124
|
+
self.glyphSet: GlyphSet = glyphSet
|
|
102
125
|
|
|
103
|
-
def draw(self, pen, outputImpliedClosingLine=False):
|
|
126
|
+
def draw(self, pen: Any, outputImpliedClosingLine: bool = False) -> None:
|
|
104
127
|
"""
|
|
105
128
|
Draw this glyph onto a *FontTools* Pen.
|
|
106
129
|
"""
|
|
@@ -109,7 +132,7 @@ class Glyph:
|
|
|
109
132
|
)
|
|
110
133
|
self.drawPoints(pointPen)
|
|
111
134
|
|
|
112
|
-
def drawPoints(self, pointPen):
|
|
135
|
+
def drawPoints(self, pointPen: AbstractPointPen) -> None:
|
|
113
136
|
"""
|
|
114
137
|
Draw this glyph onto a PointPen.
|
|
115
138
|
"""
|
|
@@ -139,13 +162,13 @@ class GlyphSet(_UFOBaseIO):
|
|
|
139
162
|
|
|
140
163
|
def __init__(
|
|
141
164
|
self,
|
|
142
|
-
path,
|
|
143
|
-
glyphNameToFileNameFunc=None,
|
|
144
|
-
ufoFormatVersion=None,
|
|
145
|
-
validateRead=True,
|
|
146
|
-
validateWrite=True,
|
|
147
|
-
expectContentsFile=False,
|
|
148
|
-
):
|
|
165
|
+
path: PathOrFS,
|
|
166
|
+
glyphNameToFileNameFunc: GlyphNameToFileNameFunc = None,
|
|
167
|
+
ufoFormatVersion: UFOFormatVersionInput = None,
|
|
168
|
+
validateRead: bool = True,
|
|
169
|
+
validateWrite: bool = True,
|
|
170
|
+
expectContentsFile: bool = False,
|
|
171
|
+
) -> None:
|
|
149
172
|
"""
|
|
150
173
|
'path' should be a path (string) to an existing local directory, or
|
|
151
174
|
an instance of fs.base.FS class.
|
|
@@ -163,7 +186,9 @@ class GlyphSet(_UFOBaseIO):
|
|
|
163
186
|
are reading an existing UFO and ``False`` if you create a fresh glyph set.
|
|
164
187
|
"""
|
|
165
188
|
try:
|
|
166
|
-
ufoFormatVersion =
|
|
189
|
+
ufoFormatVersion = normalizeFormatVersion(
|
|
190
|
+
ufoFormatVersion, UFOFormatVersion
|
|
191
|
+
)
|
|
167
192
|
except ValueError as e:
|
|
168
193
|
from fontTools.ufoLib.errors import UnsupportedUFOFormat
|
|
169
194
|
|
|
@@ -176,10 +201,10 @@ class GlyphSet(_UFOBaseIO):
|
|
|
176
201
|
|
|
177
202
|
if isinstance(path, str):
|
|
178
203
|
try:
|
|
179
|
-
filesystem = fs.osfs.OSFS(path)
|
|
204
|
+
filesystem: FS = fs.osfs.OSFS(path)
|
|
180
205
|
except fs.errors.CreateFailed:
|
|
181
206
|
raise GlifLibError("No glyphs directory '%s'" % path)
|
|
182
|
-
self._shouldClose = True
|
|
207
|
+
self._shouldClose: bool = True
|
|
183
208
|
elif isinstance(path, fs.base.FS):
|
|
184
209
|
filesystem = path
|
|
185
210
|
try:
|
|
@@ -199,26 +224,28 @@ class GlyphSet(_UFOBaseIO):
|
|
|
199
224
|
# 'dirName' is kept for backward compatibility only, but it's DEPRECATED
|
|
200
225
|
# as it's not guaranteed that it maps to an existing OSFS directory.
|
|
201
226
|
# Client could use the FS api via the `self.fs` attribute instead.
|
|
202
|
-
self.dirName = fs.path.
|
|
203
|
-
self.fs = filesystem
|
|
227
|
+
self.dirName: str = fs.path.basename(path)
|
|
228
|
+
self.fs: FS = filesystem
|
|
204
229
|
# if glyphSet contains no 'contents.plist', we consider it empty
|
|
205
|
-
self._havePreviousFile = filesystem.exists(CONTENTS_FILENAME)
|
|
230
|
+
self._havePreviousFile: bool = filesystem.exists(CONTENTS_FILENAME)
|
|
206
231
|
if expectContentsFile and not self._havePreviousFile:
|
|
207
232
|
raise GlifLibError(f"{CONTENTS_FILENAME} is missing.")
|
|
208
233
|
# attribute kept for backward compatibility
|
|
209
|
-
self.ufoFormatVersion = ufoFormatVersion.major
|
|
210
|
-
self.ufoFormatVersionTuple = ufoFormatVersion
|
|
234
|
+
self.ufoFormatVersion: int = ufoFormatVersion.major
|
|
235
|
+
self.ufoFormatVersionTuple: UFOFormatVersion = ufoFormatVersion
|
|
211
236
|
if glyphNameToFileNameFunc is None:
|
|
212
237
|
glyphNameToFileNameFunc = glyphNameToFileName
|
|
213
|
-
self.glyphNameToFileName =
|
|
214
|
-
|
|
215
|
-
|
|
238
|
+
self.glyphNameToFileName: Callable[[str, set[str]], str] = (
|
|
239
|
+
glyphNameToFileNameFunc
|
|
240
|
+
)
|
|
241
|
+
self._validateRead: bool = validateRead
|
|
242
|
+
self._validateWrite: bool = validateWrite
|
|
216
243
|
self._existingFileNames: set[str] | None = None
|
|
217
|
-
self._reverseContents = None
|
|
244
|
+
self._reverseContents: Optional[dict[str, str]] = None
|
|
218
245
|
|
|
219
246
|
self.rebuildContents()
|
|
220
247
|
|
|
221
|
-
def rebuildContents(self, validateRead=None
|
|
248
|
+
def rebuildContents(self, validateRead: bool = False) -> None:
|
|
222
249
|
"""
|
|
223
250
|
Rebuild the contents dict by loading contents.plist.
|
|
224
251
|
|
|
@@ -246,11 +273,11 @@ class GlyphSet(_UFOBaseIO):
|
|
|
246
273
|
)
|
|
247
274
|
if invalidFormat:
|
|
248
275
|
raise GlifLibError("%s is not properly formatted" % CONTENTS_FILENAME)
|
|
249
|
-
self.contents = contents
|
|
276
|
+
self.contents: dict[str, str] = contents
|
|
250
277
|
self._existingFileNames = None
|
|
251
278
|
self._reverseContents = None
|
|
252
279
|
|
|
253
|
-
def getReverseContents(self):
|
|
280
|
+
def getReverseContents(self) -> dict[str, str]:
|
|
254
281
|
"""
|
|
255
282
|
Return a reversed dict of self.contents, mapping file names to
|
|
256
283
|
glyph names. This is primarily an aid for custom glyph name to file
|
|
@@ -266,7 +293,7 @@ class GlyphSet(_UFOBaseIO):
|
|
|
266
293
|
self._reverseContents = d
|
|
267
294
|
return self._reverseContents
|
|
268
295
|
|
|
269
|
-
def writeContents(self):
|
|
296
|
+
def writeContents(self) -> None:
|
|
270
297
|
"""
|
|
271
298
|
Write the contents.plist file out to disk. Call this method when
|
|
272
299
|
you're done writing glyphs.
|
|
@@ -275,7 +302,7 @@ class GlyphSet(_UFOBaseIO):
|
|
|
275
302
|
|
|
276
303
|
# layer info
|
|
277
304
|
|
|
278
|
-
def readLayerInfo(self, info, validateRead=None):
|
|
305
|
+
def readLayerInfo(self, info: Any, validateRead: Optional[bool] = None) -> None:
|
|
279
306
|
"""
|
|
280
307
|
``validateRead`` will validate the data, by default it is set to the
|
|
281
308
|
class's ``validateRead`` value, can be overridden.
|
|
@@ -297,7 +324,7 @@ class GlyphSet(_UFOBaseIO):
|
|
|
297
324
|
% attr
|
|
298
325
|
)
|
|
299
326
|
|
|
300
|
-
def writeLayerInfo(self, info, validateWrite=None):
|
|
327
|
+
def writeLayerInfo(self, info: Any, validateWrite: Optional[bool] = None) -> None:
|
|
301
328
|
"""
|
|
302
329
|
``validateWrite`` will validate the data, by default it is set to the
|
|
303
330
|
class's ``validateWrite`` value, can be overridden.
|
|
@@ -333,7 +360,7 @@ class GlyphSet(_UFOBaseIO):
|
|
|
333
360
|
# data empty, remove existing file
|
|
334
361
|
self.fs.remove(LAYERINFO_FILENAME)
|
|
335
362
|
|
|
336
|
-
def getGLIF(self, glyphName):
|
|
363
|
+
def getGLIF(self, glyphName: str) -> bytes:
|
|
337
364
|
"""
|
|
338
365
|
Get the raw GLIF text for a given glyph name. This only works
|
|
339
366
|
for GLIF files that are already on disk.
|
|
@@ -354,7 +381,7 @@ class GlyphSet(_UFOBaseIO):
|
|
|
354
381
|
"does not exist on %s" % (fileName, glyphName, self.fs)
|
|
355
382
|
)
|
|
356
383
|
|
|
357
|
-
def getGLIFModificationTime(self, glyphName):
|
|
384
|
+
def getGLIFModificationTime(self, glyphName: str) -> Optional[float]:
|
|
358
385
|
"""
|
|
359
386
|
Returns the modification time for the GLIF file with 'glyphName', as
|
|
360
387
|
a floating point number giving the number of seconds since the epoch.
|
|
@@ -367,7 +394,13 @@ class GlyphSet(_UFOBaseIO):
|
|
|
367
394
|
|
|
368
395
|
# reading/writing API
|
|
369
396
|
|
|
370
|
-
def readGlyph(
|
|
397
|
+
def readGlyph(
|
|
398
|
+
self,
|
|
399
|
+
glyphName: str,
|
|
400
|
+
glyphObject: Optional[Any] = None,
|
|
401
|
+
pointPen: Optional[AbstractPointPen] = None,
|
|
402
|
+
validate: Optional[bool] = None,
|
|
403
|
+
) -> None:
|
|
371
404
|
"""
|
|
372
405
|
Read a .glif file for 'glyphName' from the glyph set. The
|
|
373
406
|
'glyphObject' argument can be any kind of object (even None);
|
|
@@ -444,12 +477,12 @@ class GlyphSet(_UFOBaseIO):
|
|
|
444
477
|
|
|
445
478
|
def writeGlyph(
|
|
446
479
|
self,
|
|
447
|
-
glyphName,
|
|
448
|
-
glyphObject=None,
|
|
449
|
-
drawPointsFunc=None,
|
|
450
|
-
formatVersion=None,
|
|
451
|
-
validate=None,
|
|
452
|
-
):
|
|
480
|
+
glyphName: str,
|
|
481
|
+
glyphObject: Optional[Any] = None,
|
|
482
|
+
drawPointsFunc: Optional[Callable[[AbstractPointPen], None]] = None,
|
|
483
|
+
formatVersion: GLIFFormatVersionInput = None,
|
|
484
|
+
validate: Optional[bool] = None,
|
|
485
|
+
) -> None:
|
|
453
486
|
"""
|
|
454
487
|
Write a .glif file for 'glyphName' to the glyph set. The
|
|
455
488
|
'glyphObject' argument can be any kind of object (even None);
|
|
@@ -499,7 +532,7 @@ class GlyphSet(_UFOBaseIO):
|
|
|
499
532
|
formatVersion = GLIFFormatVersion.default(self.ufoFormatVersionTuple)
|
|
500
533
|
else:
|
|
501
534
|
try:
|
|
502
|
-
formatVersion =
|
|
535
|
+
formatVersion = normalizeFormatVersion(formatVersion, GLIFFormatVersion)
|
|
503
536
|
except ValueError as e:
|
|
504
537
|
from fontTools.ufoLib.errors import UnsupportedGLIFFormat
|
|
505
538
|
|
|
@@ -543,7 +576,7 @@ class GlyphSet(_UFOBaseIO):
|
|
|
543
576
|
return
|
|
544
577
|
self.fs.writebytes(fileName, data)
|
|
545
578
|
|
|
546
|
-
def deleteGlyph(self, glyphName):
|
|
579
|
+
def deleteGlyph(self, glyphName: str) -> None:
|
|
547
580
|
"""Permanently delete the glyph from the glyph set on disk. Will
|
|
548
581
|
raise KeyError if the glyph is not present in the glyph set.
|
|
549
582
|
"""
|
|
@@ -557,25 +590,27 @@ class GlyphSet(_UFOBaseIO):
|
|
|
557
590
|
|
|
558
591
|
# dict-like support
|
|
559
592
|
|
|
560
|
-
def keys(self):
|
|
593
|
+
def keys(self) -> list[str]:
|
|
561
594
|
return list(self.contents.keys())
|
|
562
595
|
|
|
563
|
-
def has_key(self, glyphName):
|
|
596
|
+
def has_key(self, glyphName: str) -> bool:
|
|
564
597
|
return glyphName in self.contents
|
|
565
598
|
|
|
566
599
|
__contains__ = has_key
|
|
567
600
|
|
|
568
|
-
def __len__(self):
|
|
601
|
+
def __len__(self) -> int:
|
|
569
602
|
return len(self.contents)
|
|
570
603
|
|
|
571
|
-
def __getitem__(self, glyphName):
|
|
604
|
+
def __getitem__(self, glyphName: str) -> Any:
|
|
572
605
|
if glyphName not in self.contents:
|
|
573
606
|
raise KeyError(glyphName)
|
|
574
607
|
return self.glyphClass(glyphName, self)
|
|
575
608
|
|
|
576
609
|
# quickly fetch unicode values
|
|
577
610
|
|
|
578
|
-
def getUnicodes(
|
|
611
|
+
def getUnicodes(
|
|
612
|
+
self, glyphNames: Optional[Iterable[str]] = None
|
|
613
|
+
) -> dict[str, list[int]]:
|
|
579
614
|
"""
|
|
580
615
|
Return a dictionary that maps glyph names to lists containing
|
|
581
616
|
the unicode value[s] for that glyph, if any. This parses the .glif
|
|
@@ -590,7 +625,9 @@ class GlyphSet(_UFOBaseIO):
|
|
|
590
625
|
unicodes[glyphName] = _fetchUnicodes(text)
|
|
591
626
|
return unicodes
|
|
592
627
|
|
|
593
|
-
def getComponentReferences(
|
|
628
|
+
def getComponentReferences(
|
|
629
|
+
self, glyphNames: Optional[Iterable[str]] = None
|
|
630
|
+
) -> dict[str, list[str]]:
|
|
594
631
|
"""
|
|
595
632
|
Return a dictionary that maps glyph names to lists containing the
|
|
596
633
|
base glyph name of components in the glyph. This parses the .glif
|
|
@@ -605,7 +642,9 @@ class GlyphSet(_UFOBaseIO):
|
|
|
605
642
|
components[glyphName] = _fetchComponentBases(text)
|
|
606
643
|
return components
|
|
607
644
|
|
|
608
|
-
def getImageReferences(
|
|
645
|
+
def getImageReferences(
|
|
646
|
+
self, glyphNames: Optional[Iterable[str]] = None
|
|
647
|
+
) -> dict[str, Optional[str]]:
|
|
609
648
|
"""
|
|
610
649
|
Return a dictionary that maps glyph names to the file name of the image
|
|
611
650
|
referenced by the glyph. This parses the .glif files partially, so it is a
|
|
@@ -620,14 +659,14 @@ class GlyphSet(_UFOBaseIO):
|
|
|
620
659
|
images[glyphName] = _fetchImageFileName(text)
|
|
621
660
|
return images
|
|
622
661
|
|
|
623
|
-
def close(self):
|
|
662
|
+
def close(self) -> None:
|
|
624
663
|
if self._shouldClose:
|
|
625
664
|
self.fs.close()
|
|
626
665
|
|
|
627
|
-
def __enter__(self):
|
|
666
|
+
def __enter__(self) -> GlyphSet:
|
|
628
667
|
return self
|
|
629
668
|
|
|
630
|
-
def __exit__(self, exc_type, exc_value, exc_tb):
|
|
669
|
+
def __exit__(self, exc_type: Any, exc_value: Any, exc_tb: Any) -> None:
|
|
631
670
|
self.close()
|
|
632
671
|
|
|
633
672
|
|
|
@@ -636,7 +675,7 @@ class GlyphSet(_UFOBaseIO):
|
|
|
636
675
|
# -----------------------
|
|
637
676
|
|
|
638
677
|
|
|
639
|
-
def glyphNameToFileName(glyphName, existingFileNames):
|
|
678
|
+
def glyphNameToFileName(glyphName: str, existingFileNames: Optional[set[str]]) -> str:
|
|
640
679
|
"""
|
|
641
680
|
Wrapper around the userNameToFileName function in filenames.py
|
|
642
681
|
|
|
@@ -654,12 +693,12 @@ def glyphNameToFileName(glyphName, existingFileNames):
|
|
|
654
693
|
|
|
655
694
|
|
|
656
695
|
def readGlyphFromString(
|
|
657
|
-
aString,
|
|
658
|
-
glyphObject=None,
|
|
659
|
-
pointPen=None,
|
|
660
|
-
formatVersions=None,
|
|
661
|
-
validate=True,
|
|
662
|
-
):
|
|
696
|
+
aString: Union[str, bytes],
|
|
697
|
+
glyphObject: Optional[Any] = None,
|
|
698
|
+
pointPen: Optional[Any] = None,
|
|
699
|
+
formatVersions: FormatVersions = None,
|
|
700
|
+
validate: bool = True,
|
|
701
|
+
) -> None:
|
|
663
702
|
"""
|
|
664
703
|
Read .glif data from a string into a glyph object.
|
|
665
704
|
|
|
@@ -700,7 +739,7 @@ def readGlyphFromString(
|
|
|
700
739
|
|
|
701
740
|
The formatVersions optional argument define the GLIF format versions
|
|
702
741
|
that are allowed to be read.
|
|
703
|
-
The type is Optional[Iterable[
|
|
742
|
+
The type is Optional[Iterable[tuple[int, int], int]]. It can contain
|
|
704
743
|
either integers (for the major versions to be allowed, with minor
|
|
705
744
|
digits defaulting to 0), or tuples of integers to specify both
|
|
706
745
|
(major, minor) versions.
|
|
@@ -712,12 +751,14 @@ def readGlyphFromString(
|
|
|
712
751
|
tree = _glifTreeFromString(aString)
|
|
713
752
|
|
|
714
753
|
if formatVersions is None:
|
|
715
|
-
validFormatVersions =
|
|
754
|
+
validFormatVersions: Set[GLIFFormatVersion] = (
|
|
755
|
+
GLIFFormatVersion.supported_versions()
|
|
756
|
+
)
|
|
716
757
|
else:
|
|
717
758
|
validFormatVersions, invalidFormatVersions = set(), set()
|
|
718
759
|
for v in formatVersions:
|
|
719
760
|
try:
|
|
720
|
-
formatVersion =
|
|
761
|
+
formatVersion = normalizeFormatVersion(v, GLIFFormatVersion)
|
|
721
762
|
except ValueError:
|
|
722
763
|
invalidFormatVersions.add(v)
|
|
723
764
|
else:
|
|
@@ -738,16 +779,16 @@ def readGlyphFromString(
|
|
|
738
779
|
|
|
739
780
|
|
|
740
781
|
def _writeGlyphToBytes(
|
|
741
|
-
glyphName,
|
|
742
|
-
glyphObject=None,
|
|
743
|
-
drawPointsFunc=None,
|
|
744
|
-
writer=None,
|
|
745
|
-
formatVersion=None,
|
|
746
|
-
validate=True,
|
|
747
|
-
):
|
|
782
|
+
glyphName: str,
|
|
783
|
+
glyphObject: Optional[Any] = None,
|
|
784
|
+
drawPointsFunc: Optional[Callable[[Any], None]] = None,
|
|
785
|
+
writer: Optional[Any] = None,
|
|
786
|
+
formatVersion: Optional[FormatVersion] = None,
|
|
787
|
+
validate: bool = True,
|
|
788
|
+
) -> bytes:
|
|
748
789
|
"""Return .glif data for a glyph as a UTF-8 encoded bytes string."""
|
|
749
790
|
try:
|
|
750
|
-
formatVersion =
|
|
791
|
+
formatVersion = normalizeFormatVersion(formatVersion, GLIFFormatVersion)
|
|
751
792
|
except ValueError:
|
|
752
793
|
from fontTools.ufoLib.errors import UnsupportedGLIFFormat
|
|
753
794
|
|
|
@@ -765,7 +806,7 @@ def _writeGlyphToBytes(
|
|
|
765
806
|
if formatVersion.minor != 0:
|
|
766
807
|
glyphAttrs["formatMinor"] = repr(formatVersion.minor)
|
|
767
808
|
root = etree.Element("glyph", glyphAttrs)
|
|
768
|
-
identifiers = set()
|
|
809
|
+
identifiers: set[str] = set()
|
|
769
810
|
# advance
|
|
770
811
|
_writeAdvance(glyphObject, root, validate)
|
|
771
812
|
# unicodes
|
|
@@ -805,12 +846,12 @@ def _writeGlyphToBytes(
|
|
|
805
846
|
|
|
806
847
|
|
|
807
848
|
def writeGlyphToString(
|
|
808
|
-
glyphName,
|
|
809
|
-
glyphObject=None,
|
|
810
|
-
drawPointsFunc=None,
|
|
811
|
-
formatVersion=None,
|
|
812
|
-
validate=True,
|
|
813
|
-
):
|
|
849
|
+
glyphName: str,
|
|
850
|
+
glyphObject: Optional[Any] = None,
|
|
851
|
+
drawPointsFunc: Optional[Callable[[Any], None]] = None,
|
|
852
|
+
formatVersion: Optional[FormatVersion] = None,
|
|
853
|
+
validate: bool = True,
|
|
854
|
+
) -> str:
|
|
814
855
|
"""
|
|
815
856
|
Return .glif data for a glyph as a string. The XML declaration's
|
|
816
857
|
encoding is always set to "UTF-8".
|
|
@@ -865,7 +906,7 @@ def writeGlyphToString(
|
|
|
865
906
|
return data.decode("utf-8")
|
|
866
907
|
|
|
867
908
|
|
|
868
|
-
def _writeAdvance(glyphObject, element, validate):
|
|
909
|
+
def _writeAdvance(glyphObject: Any, element: ElementType, validate: bool) -> None:
|
|
869
910
|
width = getattr(glyphObject, "width", None)
|
|
870
911
|
if width is not None:
|
|
871
912
|
if validate and not isinstance(width, numberTypes):
|
|
@@ -890,8 +931,8 @@ def _writeAdvance(glyphObject, element, validate):
|
|
|
890
931
|
etree.SubElement(element, "advance", dict(height=repr(height)))
|
|
891
932
|
|
|
892
933
|
|
|
893
|
-
def _writeUnicodes(glyphObject, element, validate):
|
|
894
|
-
unicodes = getattr(glyphObject, "unicodes",
|
|
934
|
+
def _writeUnicodes(glyphObject: Any, element: ElementType, validate: bool) -> None:
|
|
935
|
+
unicodes = getattr(glyphObject, "unicodes", [])
|
|
895
936
|
if validate and isinstance(unicodes, int):
|
|
896
937
|
unicodes = [unicodes]
|
|
897
938
|
seen = set()
|
|
@@ -905,17 +946,21 @@ def _writeUnicodes(glyphObject, element, validate):
|
|
|
905
946
|
etree.SubElement(element, "unicode", dict(hex=hexCode))
|
|
906
947
|
|
|
907
948
|
|
|
908
|
-
def _writeNote(glyphObject, element, validate):
|
|
949
|
+
def _writeNote(glyphObject: Any, element: ElementType, validate: bool) -> None:
|
|
909
950
|
note = getattr(glyphObject, "note", None)
|
|
910
951
|
if validate and not isinstance(note, str):
|
|
911
952
|
raise GlifLibError("note attribute must be str")
|
|
912
|
-
note
|
|
913
|
-
|
|
914
|
-
|
|
953
|
+
if isinstance(note, str):
|
|
954
|
+
note = note.strip()
|
|
955
|
+
note = "\n" + note + "\n"
|
|
956
|
+
etree.SubElement(element, "note").text = note
|
|
915
957
|
|
|
916
958
|
|
|
917
|
-
def _writeImage(glyphObject, element, validate):
|
|
959
|
+
def _writeImage(glyphObject: Any, element: ElementType, validate: bool) -> None:
|
|
918
960
|
image = getattr(glyphObject, "image", None)
|
|
961
|
+
if image is None:
|
|
962
|
+
return
|
|
963
|
+
|
|
919
964
|
if validate and not imageValidator(image):
|
|
920
965
|
raise GlifLibError(
|
|
921
966
|
"image attribute must be a dict or dict-like object with the proper structure."
|
|
@@ -931,7 +976,9 @@ def _writeImage(glyphObject, element, validate):
|
|
|
931
976
|
etree.SubElement(element, "image", attrs)
|
|
932
977
|
|
|
933
978
|
|
|
934
|
-
def _writeGuidelines(
|
|
979
|
+
def _writeGuidelines(
|
|
980
|
+
glyphObject: Any, element: ElementType, identifiers: set[str], validate: bool
|
|
981
|
+
) -> None:
|
|
935
982
|
guidelines = getattr(glyphObject, "guidelines", [])
|
|
936
983
|
if validate and not guidelinesValidator(guidelines):
|
|
937
984
|
raise GlifLibError("guidelines attribute does not have the proper structure.")
|
|
@@ -961,7 +1008,7 @@ def _writeGuidelines(glyphObject, element, identifiers, validate):
|
|
|
961
1008
|
etree.SubElement(element, "guideline", attrs)
|
|
962
1009
|
|
|
963
1010
|
|
|
964
|
-
def _writeAnchorsFormat1(pen, anchors, validate):
|
|
1011
|
+
def _writeAnchorsFormat1(pen: Any, anchors: Any, validate: bool) -> None:
|
|
965
1012
|
if validate and not anchorsValidator(anchors):
|
|
966
1013
|
raise GlifLibError("anchors attribute does not have the proper structure.")
|
|
967
1014
|
for anchor in anchors:
|
|
@@ -978,7 +1025,12 @@ def _writeAnchorsFormat1(pen, anchors, validate):
|
|
|
978
1025
|
pen.endPath()
|
|
979
1026
|
|
|
980
1027
|
|
|
981
|
-
def _writeAnchors(
|
|
1028
|
+
def _writeAnchors(
|
|
1029
|
+
glyphObject: Any,
|
|
1030
|
+
element: ElementType,
|
|
1031
|
+
identifiers: set[str],
|
|
1032
|
+
validate: bool,
|
|
1033
|
+
) -> None:
|
|
982
1034
|
anchors = getattr(glyphObject, "anchors", [])
|
|
983
1035
|
if validate and not anchorsValidator(anchors):
|
|
984
1036
|
raise GlifLibError("anchors attribute does not have the proper structure.")
|
|
@@ -1003,7 +1055,7 @@ def _writeAnchors(glyphObject, element, identifiers, validate):
|
|
|
1003
1055
|
etree.SubElement(element, "anchor", attrs)
|
|
1004
1056
|
|
|
1005
1057
|
|
|
1006
|
-
def _writeLib(glyphObject, element, validate):
|
|
1058
|
+
def _writeLib(glyphObject: Any, element: ElementType, validate: bool) -> None:
|
|
1007
1059
|
lib = getattr(glyphObject, "lib", None)
|
|
1008
1060
|
if not lib:
|
|
1009
1061
|
# don't write empty lib
|
|
@@ -1029,7 +1081,7 @@ layerInfoVersion3ValueData = {
|
|
|
1029
1081
|
}
|
|
1030
1082
|
|
|
1031
1083
|
|
|
1032
|
-
def validateLayerInfoVersion3ValueForAttribute(attr, value):
|
|
1084
|
+
def validateLayerInfoVersion3ValueForAttribute(attr: str, value: Any) -> bool:
|
|
1033
1085
|
"""
|
|
1034
1086
|
This performs very basic validation of the value for attribute
|
|
1035
1087
|
following the UFO 3 fontinfo.plist specification. The results
|
|
@@ -1046,6 +1098,7 @@ def validateLayerInfoVersion3ValueForAttribute(attr, value):
|
|
|
1046
1098
|
validator = dataValidationDict.get("valueValidator")
|
|
1047
1099
|
valueOptions = dataValidationDict.get("valueOptions")
|
|
1048
1100
|
# have specific options for the validator
|
|
1101
|
+
assert callable(validator)
|
|
1049
1102
|
if valueOptions is not None:
|
|
1050
1103
|
isValidValue = validator(value, valueOptions)
|
|
1051
1104
|
# no specific options
|
|
@@ -1057,7 +1110,7 @@ def validateLayerInfoVersion3ValueForAttribute(attr, value):
|
|
|
1057
1110
|
return isValidValue
|
|
1058
1111
|
|
|
1059
1112
|
|
|
1060
|
-
def validateLayerInfoVersion3Data(infoData):
|
|
1113
|
+
def validateLayerInfoVersion3Data(infoData: dict[str, Any]) -> dict[str, Any]:
|
|
1061
1114
|
"""
|
|
1062
1115
|
This performs very basic validation of the value for infoData
|
|
1063
1116
|
following the UFO 3 layerinfo.plist specification. The results
|
|
@@ -1081,7 +1134,7 @@ def validateLayerInfoVersion3Data(infoData):
|
|
|
1081
1134
|
# -----------------
|
|
1082
1135
|
|
|
1083
1136
|
|
|
1084
|
-
def _glifTreeFromFile(aFile):
|
|
1137
|
+
def _glifTreeFromFile(aFile: Union[str, bytes, int]) -> ElementType:
|
|
1085
1138
|
if etree._have_lxml:
|
|
1086
1139
|
tree = etree.parse(aFile, parser=etree.XMLParser(remove_comments=True))
|
|
1087
1140
|
else:
|
|
@@ -1094,7 +1147,7 @@ def _glifTreeFromFile(aFile):
|
|
|
1094
1147
|
return root
|
|
1095
1148
|
|
|
1096
1149
|
|
|
1097
|
-
def _glifTreeFromString(aString):
|
|
1150
|
+
def _glifTreeFromString(aString: Union[str, bytes]) -> ElementType:
|
|
1098
1151
|
data = tobytes(aString, encoding="utf-8")
|
|
1099
1152
|
try:
|
|
1100
1153
|
if etree._have_lxml:
|
|
@@ -1112,16 +1165,18 @@ def _glifTreeFromString(aString):
|
|
|
1112
1165
|
|
|
1113
1166
|
|
|
1114
1167
|
def _readGlyphFromTree(
|
|
1115
|
-
tree,
|
|
1116
|
-
glyphObject=None,
|
|
1117
|
-
pointPen=None,
|
|
1118
|
-
formatVersions=GLIFFormatVersion.supported_versions(),
|
|
1119
|
-
validate=True,
|
|
1120
|
-
):
|
|
1168
|
+
tree: ElementType,
|
|
1169
|
+
glyphObject: Optional[Any] = None,
|
|
1170
|
+
pointPen: Optional[AbstractPointPen] = None,
|
|
1171
|
+
formatVersions: Set[GLIFFormatVersion] = GLIFFormatVersion.supported_versions(),
|
|
1172
|
+
validate: bool = True,
|
|
1173
|
+
) -> None:
|
|
1121
1174
|
# check the format version
|
|
1122
1175
|
formatVersionMajor = tree.get("format")
|
|
1123
|
-
if
|
|
1124
|
-
|
|
1176
|
+
if formatVersionMajor is None:
|
|
1177
|
+
if validate:
|
|
1178
|
+
raise GlifLibError("Unspecified format version in GLIF.")
|
|
1179
|
+
formatVersionMajor = 0
|
|
1125
1180
|
formatVersionMinor = tree.get("formatMinor", 0)
|
|
1126
1181
|
try:
|
|
1127
1182
|
formatVersion = GLIFFormatVersion(
|
|
@@ -1163,14 +1218,21 @@ def _readGlyphFromTree(
|
|
|
1163
1218
|
|
|
1164
1219
|
|
|
1165
1220
|
def _readGlyphFromTreeFormat1(
|
|
1166
|
-
tree
|
|
1167
|
-
|
|
1221
|
+
tree: ElementType,
|
|
1222
|
+
glyphObject: Optional[Any] = None,
|
|
1223
|
+
pointPen: Optional[AbstractPointPen] = None,
|
|
1224
|
+
validate: bool = False,
|
|
1225
|
+
**kwargs: Any,
|
|
1226
|
+
) -> None:
|
|
1168
1227
|
# get the name
|
|
1169
1228
|
_readName(glyphObject, tree, validate)
|
|
1170
1229
|
# populate the sub elements
|
|
1171
1230
|
unicodes = []
|
|
1172
1231
|
haveSeenAdvance = haveSeenOutline = haveSeenLib = haveSeenNote = False
|
|
1173
1232
|
for element in tree:
|
|
1233
|
+
if glyphObject is None:
|
|
1234
|
+
continue
|
|
1235
|
+
|
|
1174
1236
|
if element.tag == "outline":
|
|
1175
1237
|
if validate:
|
|
1176
1238
|
if haveSeenOutline:
|
|
@@ -1183,8 +1245,6 @@ def _readGlyphFromTreeFormat1(
|
|
|
1183
1245
|
raise GlifLibError("Invalid outline structure.")
|
|
1184
1246
|
haveSeenOutline = True
|
|
1185
1247
|
buildOutlineFormat1(glyphObject, pointPen, element, validate)
|
|
1186
|
-
elif glyphObject is None:
|
|
1187
|
-
continue
|
|
1188
1248
|
elif element.tag == "advance":
|
|
1189
1249
|
if validate and haveSeenAdvance:
|
|
1190
1250
|
raise GlifLibError("The advance element occurs more than once.")
|
|
@@ -1222,8 +1282,12 @@ def _readGlyphFromTreeFormat1(
|
|
|
1222
1282
|
|
|
1223
1283
|
|
|
1224
1284
|
def _readGlyphFromTreeFormat2(
|
|
1225
|
-
tree
|
|
1226
|
-
|
|
1285
|
+
tree: ElementType,
|
|
1286
|
+
glyphObject: Optional[Any] = None,
|
|
1287
|
+
pointPen: Optional[AbstractPointPen] = None,
|
|
1288
|
+
validate: bool = False,
|
|
1289
|
+
formatMinor: int = 0,
|
|
1290
|
+
) -> None:
|
|
1227
1291
|
# get the name
|
|
1228
1292
|
_readName(glyphObject, tree, validate)
|
|
1229
1293
|
# populate the sub elements
|
|
@@ -1233,8 +1297,10 @@ def _readGlyphFromTreeFormat2(
|
|
|
1233
1297
|
haveSeenAdvance = haveSeenImage = haveSeenOutline = haveSeenLib = haveSeenNote = (
|
|
1234
1298
|
False
|
|
1235
1299
|
)
|
|
1236
|
-
identifiers = set()
|
|
1300
|
+
identifiers: set[str] = set()
|
|
1237
1301
|
for element in tree:
|
|
1302
|
+
if glyphObject is None:
|
|
1303
|
+
continue
|
|
1238
1304
|
if element.tag == "outline":
|
|
1239
1305
|
if validate:
|
|
1240
1306
|
if haveSeenOutline:
|
|
@@ -1250,8 +1316,6 @@ def _readGlyphFromTreeFormat2(
|
|
|
1250
1316
|
buildOutlineFormat2(
|
|
1251
1317
|
glyphObject, pointPen, element, identifiers, validate
|
|
1252
1318
|
)
|
|
1253
|
-
elif glyphObject is None:
|
|
1254
|
-
continue
|
|
1255
1319
|
elif element.tag == "advance":
|
|
1256
1320
|
if validate and haveSeenAdvance:
|
|
1257
1321
|
raise GlifLibError("The advance element occurs more than once.")
|
|
@@ -1322,13 +1386,13 @@ def _readGlyphFromTreeFormat2(
|
|
|
1322
1386
|
_relaxedSetattr(glyphObject, "anchors", anchors)
|
|
1323
1387
|
|
|
1324
1388
|
|
|
1325
|
-
_READ_GLYPH_FROM_TREE_FUNCS = {
|
|
1389
|
+
_READ_GLYPH_FROM_TREE_FUNCS: dict[GLIFFormatVersion, Callable[..., Any]] = {
|
|
1326
1390
|
GLIFFormatVersion.FORMAT_1_0: _readGlyphFromTreeFormat1,
|
|
1327
1391
|
GLIFFormatVersion.FORMAT_2_0: _readGlyphFromTreeFormat2,
|
|
1328
1392
|
}
|
|
1329
1393
|
|
|
1330
1394
|
|
|
1331
|
-
def _readName(glyphObject, root, validate):
|
|
1395
|
+
def _readName(glyphObject: Optional[Any], root: ElementType, validate: bool) -> None:
|
|
1332
1396
|
glyphName = root.get("name")
|
|
1333
1397
|
if validate and not glyphName:
|
|
1334
1398
|
raise GlifLibError("Empty glyph name in GLIF.")
|
|
@@ -1336,20 +1400,22 @@ def _readName(glyphObject, root, validate):
|
|
|
1336
1400
|
_relaxedSetattr(glyphObject, "name", glyphName)
|
|
1337
1401
|
|
|
1338
1402
|
|
|
1339
|
-
def _readAdvance(glyphObject, advance):
|
|
1403
|
+
def _readAdvance(glyphObject: Optional[Any], advance: ElementType) -> None:
|
|
1340
1404
|
width = _number(advance.get("width", 0))
|
|
1341
1405
|
_relaxedSetattr(glyphObject, "width", width)
|
|
1342
1406
|
height = _number(advance.get("height", 0))
|
|
1343
1407
|
_relaxedSetattr(glyphObject, "height", height)
|
|
1344
1408
|
|
|
1345
1409
|
|
|
1346
|
-
def _readNote(glyphObject, note):
|
|
1410
|
+
def _readNote(glyphObject: Optional[Any], note: ElementType) -> None:
|
|
1411
|
+
if note.text is None:
|
|
1412
|
+
return
|
|
1347
1413
|
lines = note.text.split("\n")
|
|
1348
1414
|
note = "\n".join(line.strip() for line in lines if line.strip())
|
|
1349
1415
|
_relaxedSetattr(glyphObject, "note", note)
|
|
1350
1416
|
|
|
1351
1417
|
|
|
1352
|
-
def _readLib(glyphObject, lib, validate):
|
|
1418
|
+
def _readLib(glyphObject: Optional[Any], lib: ElementType, validate: bool) -> None:
|
|
1353
1419
|
assert len(lib) == 1
|
|
1354
1420
|
child = lib[0]
|
|
1355
1421
|
plist = plistlib.fromtree(child)
|
|
@@ -1360,7 +1426,7 @@ def _readLib(glyphObject, lib, validate):
|
|
|
1360
1426
|
_relaxedSetattr(glyphObject, "lib", plist)
|
|
1361
1427
|
|
|
1362
1428
|
|
|
1363
|
-
def _readImage(glyphObject, image, validate):
|
|
1429
|
+
def _readImage(glyphObject: Optional[Any], image: ElementType, validate: bool) -> None:
|
|
1364
1430
|
imageData = dict(image.attrib)
|
|
1365
1431
|
for attr, default in _transformationInfo:
|
|
1366
1432
|
value = imageData.get(attr, default)
|
|
@@ -1374,8 +1440,8 @@ def _readImage(glyphObject, image, validate):
|
|
|
1374
1440
|
# GLIF to PointPen
|
|
1375
1441
|
# ----------------
|
|
1376
1442
|
|
|
1377
|
-
contourAttributesFormat2 = {"identifier"}
|
|
1378
|
-
componentAttributesFormat1 = {
|
|
1443
|
+
contourAttributesFormat2: set[str] = {"identifier"}
|
|
1444
|
+
componentAttributesFormat1: set[str] = {
|
|
1379
1445
|
"base",
|
|
1380
1446
|
"xScale",
|
|
1381
1447
|
"xyScale",
|
|
@@ -1384,16 +1450,21 @@ componentAttributesFormat1 = {
|
|
|
1384
1450
|
"xOffset",
|
|
1385
1451
|
"yOffset",
|
|
1386
1452
|
}
|
|
1387
|
-
componentAttributesFormat2 = componentAttributesFormat1 | {"identifier"}
|
|
1388
|
-
pointAttributesFormat1 = {"x", "y", "type", "smooth", "name"}
|
|
1389
|
-
pointAttributesFormat2 = pointAttributesFormat1 | {"identifier"}
|
|
1390
|
-
pointSmoothOptions = {"no", "yes"}
|
|
1391
|
-
pointTypeOptions = {"move", "line", "offcurve", "curve", "qcurve"}
|
|
1453
|
+
componentAttributesFormat2: set[str] = componentAttributesFormat1 | {"identifier"}
|
|
1454
|
+
pointAttributesFormat1: set[str] = {"x", "y", "type", "smooth", "name"}
|
|
1455
|
+
pointAttributesFormat2: set[str] = pointAttributesFormat1 | {"identifier"}
|
|
1456
|
+
pointSmoothOptions: set[str] = {"no", "yes"}
|
|
1457
|
+
pointTypeOptions: set[str] = {"move", "line", "offcurve", "curve", "qcurve"}
|
|
1392
1458
|
|
|
1393
1459
|
# format 1
|
|
1394
1460
|
|
|
1395
1461
|
|
|
1396
|
-
def buildOutlineFormat1(
|
|
1462
|
+
def buildOutlineFormat1(
|
|
1463
|
+
glyphObject: Any,
|
|
1464
|
+
pen: Optional[AbstractPointPen],
|
|
1465
|
+
outline: Iterable[ElementType],
|
|
1466
|
+
validate: bool,
|
|
1467
|
+
) -> None:
|
|
1397
1468
|
anchors = []
|
|
1398
1469
|
for element in outline:
|
|
1399
1470
|
if element.tag == "contour":
|
|
@@ -1417,7 +1488,7 @@ def buildOutlineFormat1(glyphObject, pen, outline, validate):
|
|
|
1417
1488
|
_relaxedSetattr(glyphObject, "anchors", anchors)
|
|
1418
1489
|
|
|
1419
1490
|
|
|
1420
|
-
def _buildAnchorFormat1(point, validate):
|
|
1491
|
+
def _buildAnchorFormat1(point: ElementType, validate: bool) -> Optional[dict[str, Any]]:
|
|
1421
1492
|
if point.get("type") != "move":
|
|
1422
1493
|
return None
|
|
1423
1494
|
name = point.get("name")
|
|
@@ -1427,15 +1498,19 @@ def _buildAnchorFormat1(point, validate):
|
|
|
1427
1498
|
y = point.get("y")
|
|
1428
1499
|
if validate and x is None:
|
|
1429
1500
|
raise GlifLibError("Required x attribute is missing in point element.")
|
|
1501
|
+
assert x is not None
|
|
1430
1502
|
if validate and y is None:
|
|
1431
1503
|
raise GlifLibError("Required y attribute is missing in point element.")
|
|
1504
|
+
assert y is not None
|
|
1432
1505
|
x = _number(x)
|
|
1433
1506
|
y = _number(y)
|
|
1434
1507
|
anchor = dict(x=x, y=y, name=name)
|
|
1435
1508
|
return anchor
|
|
1436
1509
|
|
|
1437
1510
|
|
|
1438
|
-
def _buildOutlineContourFormat1(
|
|
1511
|
+
def _buildOutlineContourFormat1(
|
|
1512
|
+
pen: AbstractPointPen, contour: ElementType, validate: bool
|
|
1513
|
+
) -> None:
|
|
1439
1514
|
if validate and contour.attrib:
|
|
1440
1515
|
raise GlifLibError("Unknown attributes in contour element.")
|
|
1441
1516
|
pen.beginPath()
|
|
@@ -1450,7 +1525,9 @@ def _buildOutlineContourFormat1(pen, contour, validate):
|
|
|
1450
1525
|
pen.endPath()
|
|
1451
1526
|
|
|
1452
1527
|
|
|
1453
|
-
def _buildOutlinePointsFormat1(
|
|
1528
|
+
def _buildOutlinePointsFormat1(
|
|
1529
|
+
pen: AbstractPointPen, contour: list[dict[str, Any]]
|
|
1530
|
+
) -> None:
|
|
1454
1531
|
for point in contour:
|
|
1455
1532
|
x = point["x"]
|
|
1456
1533
|
y = point["y"]
|
|
@@ -1460,7 +1537,9 @@ def _buildOutlinePointsFormat1(pen, contour):
|
|
|
1460
1537
|
pen.addPoint((x, y), segmentType=segmentType, smooth=smooth, name=name)
|
|
1461
1538
|
|
|
1462
1539
|
|
|
1463
|
-
def _buildOutlineComponentFormat1(
|
|
1540
|
+
def _buildOutlineComponentFormat1(
|
|
1541
|
+
pen: AbstractPointPen, component: ElementType, validate: bool
|
|
1542
|
+
) -> None:
|
|
1464
1543
|
if validate:
|
|
1465
1544
|
if len(component):
|
|
1466
1545
|
raise GlifLibError("Unknown child elements of component element.")
|
|
@@ -1470,21 +1549,26 @@ def _buildOutlineComponentFormat1(pen, component, validate):
|
|
|
1470
1549
|
baseGlyphName = component.get("base")
|
|
1471
1550
|
if validate and baseGlyphName is None:
|
|
1472
1551
|
raise GlifLibError("The base attribute is not defined in the component.")
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
pen.addComponent(baseGlyphName, tuple(transformation))
|
|
1552
|
+
assert baseGlyphName is not None
|
|
1553
|
+
transformation = tuple(
|
|
1554
|
+
_number(component.get(attr) or default) for attr, default in _transformationInfo
|
|
1555
|
+
)
|
|
1556
|
+
transformation = cast(
|
|
1557
|
+
tuple[float, float, float, float, float, float], transformation
|
|
1558
|
+
)
|
|
1559
|
+
pen.addComponent(baseGlyphName, transformation)
|
|
1482
1560
|
|
|
1483
1561
|
|
|
1484
1562
|
# format 2
|
|
1485
1563
|
|
|
1486
1564
|
|
|
1487
|
-
def buildOutlineFormat2(
|
|
1565
|
+
def buildOutlineFormat2(
|
|
1566
|
+
glyphObject: Any,
|
|
1567
|
+
pen: AbstractPointPen,
|
|
1568
|
+
outline: Iterable[ElementType],
|
|
1569
|
+
identifiers: set[str],
|
|
1570
|
+
validate: bool,
|
|
1571
|
+
) -> None:
|
|
1488
1572
|
for element in outline:
|
|
1489
1573
|
if element.tag == "contour":
|
|
1490
1574
|
_buildOutlineContourFormat2(pen, element, identifiers, validate)
|
|
@@ -1494,7 +1578,9 @@ def buildOutlineFormat2(glyphObject, pen, outline, identifiers, validate):
|
|
|
1494
1578
|
raise GlifLibError("Unknown element in outline element: %s" % element.tag)
|
|
1495
1579
|
|
|
1496
1580
|
|
|
1497
|
-
def _buildOutlineContourFormat2(
|
|
1581
|
+
def _buildOutlineContourFormat2(
|
|
1582
|
+
pen: AbstractPointPen, contour: ElementType, identifiers: set[str], validate: bool
|
|
1583
|
+
) -> None:
|
|
1498
1584
|
if validate:
|
|
1499
1585
|
for attr in contour.attrib.keys():
|
|
1500
1586
|
if attr not in contourAttributesFormat2:
|
|
@@ -1527,7 +1613,12 @@ def _buildOutlineContourFormat2(pen, contour, identifiers, validate):
|
|
|
1527
1613
|
pen.endPath()
|
|
1528
1614
|
|
|
1529
1615
|
|
|
1530
|
-
def _buildOutlinePointsFormat2(
|
|
1616
|
+
def _buildOutlinePointsFormat2(
|
|
1617
|
+
pen: AbstractPointPen,
|
|
1618
|
+
contour: list[dict[str, Any]],
|
|
1619
|
+
identifiers: set[str],
|
|
1620
|
+
validate: bool,
|
|
1621
|
+
) -> None:
|
|
1531
1622
|
for point in contour:
|
|
1532
1623
|
x = point["x"]
|
|
1533
1624
|
y = point["y"]
|
|
@@ -1560,7 +1651,9 @@ def _buildOutlinePointsFormat2(pen, contour, identifiers, validate):
|
|
|
1560
1651
|
)
|
|
1561
1652
|
|
|
1562
1653
|
|
|
1563
|
-
def _buildOutlineComponentFormat2(
|
|
1654
|
+
def _buildOutlineComponentFormat2(
|
|
1655
|
+
pen: AbstractPointPen, component: ElementType, identifiers: set[str], validate: bool
|
|
1656
|
+
) -> None:
|
|
1564
1657
|
if validate:
|
|
1565
1658
|
if len(component):
|
|
1566
1659
|
raise GlifLibError("Unknown child elements of component element.")
|
|
@@ -1570,14 +1663,13 @@ def _buildOutlineComponentFormat2(pen, component, identifiers, validate):
|
|
|
1570
1663
|
baseGlyphName = component.get("base")
|
|
1571
1664
|
if validate and baseGlyphName is None:
|
|
1572
1665
|
raise GlifLibError("The base attribute is not defined in the component.")
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
transformation.append(value)
|
|
1666
|
+
assert baseGlyphName is not None
|
|
1667
|
+
transformation = tuple(
|
|
1668
|
+
_number(component.get(attr) or default) for attr, default in _transformationInfo
|
|
1669
|
+
)
|
|
1670
|
+
transformation = cast(
|
|
1671
|
+
tuple[float, float, float, float, float, float], transformation
|
|
1672
|
+
)
|
|
1581
1673
|
identifier = component.get("identifier")
|
|
1582
1674
|
if identifier is not None:
|
|
1583
1675
|
if validate:
|
|
@@ -1589,9 +1681,9 @@ def _buildOutlineComponentFormat2(pen, component, identifiers, validate):
|
|
|
1589
1681
|
raise GlifLibError("The identifier %s is not valid." % identifier)
|
|
1590
1682
|
identifiers.add(identifier)
|
|
1591
1683
|
try:
|
|
1592
|
-
pen.addComponent(baseGlyphName,
|
|
1684
|
+
pen.addComponent(baseGlyphName, transformation, identifier=identifier)
|
|
1593
1685
|
except TypeError:
|
|
1594
|
-
pen.addComponent(baseGlyphName,
|
|
1686
|
+
pen.addComponent(baseGlyphName, transformation)
|
|
1595
1687
|
warn(
|
|
1596
1688
|
"The addComponent method needs an identifier kwarg. The component's identifier value has been discarded.",
|
|
1597
1689
|
DeprecationWarning,
|
|
@@ -1710,14 +1802,14 @@ def _validateAndMassagePointStructures(
|
|
|
1710
1802
|
# ---------------------
|
|
1711
1803
|
|
|
1712
1804
|
|
|
1713
|
-
def _relaxedSetattr(object, attr, value):
|
|
1805
|
+
def _relaxedSetattr(object: Any, attr: str, value: Any) -> None:
|
|
1714
1806
|
try:
|
|
1715
1807
|
setattr(object, attr, value)
|
|
1716
1808
|
except AttributeError:
|
|
1717
1809
|
pass
|
|
1718
1810
|
|
|
1719
1811
|
|
|
1720
|
-
def _number(s):
|
|
1812
|
+
def _number(s: Union[str, int, float]) -> IntFloat:
|
|
1721
1813
|
"""
|
|
1722
1814
|
Given a numeric string, return an integer or a float, whichever
|
|
1723
1815
|
the string indicates. _number("1") will return the integer 1,
|
|
@@ -1733,7 +1825,7 @@ def _number(s):
|
|
|
1733
1825
|
GlifLibError: Could not convert a to an int or float.
|
|
1734
1826
|
"""
|
|
1735
1827
|
try:
|
|
1736
|
-
n = int(s)
|
|
1828
|
+
n: IntFloat = int(s)
|
|
1737
1829
|
return n
|
|
1738
1830
|
except ValueError:
|
|
1739
1831
|
pass
|
|
@@ -1756,21 +1848,21 @@ class _DoneParsing(Exception):
|
|
|
1756
1848
|
|
|
1757
1849
|
|
|
1758
1850
|
class _BaseParser:
|
|
1759
|
-
def __init__(self):
|
|
1760
|
-
self._elementStack = []
|
|
1851
|
+
def __init__(self) -> None:
|
|
1852
|
+
self._elementStack: list[str] = []
|
|
1761
1853
|
|
|
1762
|
-
def parse(self, text):
|
|
1854
|
+
def parse(self, text: bytes):
|
|
1763
1855
|
from xml.parsers.expat import ParserCreate
|
|
1764
1856
|
|
|
1765
1857
|
parser = ParserCreate()
|
|
1766
1858
|
parser.StartElementHandler = self.startElementHandler
|
|
1767
1859
|
parser.EndElementHandler = self.endElementHandler
|
|
1768
|
-
parser.Parse(text,
|
|
1860
|
+
parser.Parse(text, True)
|
|
1769
1861
|
|
|
1770
|
-
def startElementHandler(self, name, attrs):
|
|
1862
|
+
def startElementHandler(self, name: str, attrs: Any) -> None:
|
|
1771
1863
|
self._elementStack.append(name)
|
|
1772
1864
|
|
|
1773
|
-
def endElementHandler(self, name):
|
|
1865
|
+
def endElementHandler(self, name: str) -> None:
|
|
1774
1866
|
other = self._elementStack.pop(-1)
|
|
1775
1867
|
assert other == name
|
|
1776
1868
|
|
|
@@ -1778,7 +1870,7 @@ class _BaseParser:
|
|
|
1778
1870
|
# unicodes
|
|
1779
1871
|
|
|
1780
1872
|
|
|
1781
|
-
def _fetchUnicodes(glif):
|
|
1873
|
+
def _fetchUnicodes(glif: bytes) -> list[int]:
|
|
1782
1874
|
"""
|
|
1783
1875
|
Get a list of unicodes listed in glif.
|
|
1784
1876
|
"""
|
|
@@ -1788,11 +1880,11 @@ def _fetchUnicodes(glif):
|
|
|
1788
1880
|
|
|
1789
1881
|
|
|
1790
1882
|
class _FetchUnicodesParser(_BaseParser):
|
|
1791
|
-
def __init__(self):
|
|
1792
|
-
self.unicodes = []
|
|
1883
|
+
def __init__(self) -> None:
|
|
1884
|
+
self.unicodes: list[int] = []
|
|
1793
1885
|
super().__init__()
|
|
1794
1886
|
|
|
1795
|
-
def startElementHandler(self, name, attrs):
|
|
1887
|
+
def startElementHandler(self, name: str, attrs: dict[str, str]) -> None:
|
|
1796
1888
|
if (
|
|
1797
1889
|
name == "unicode"
|
|
1798
1890
|
and self._elementStack
|
|
@@ -1801,9 +1893,9 @@ class _FetchUnicodesParser(_BaseParser):
|
|
|
1801
1893
|
value = attrs.get("hex")
|
|
1802
1894
|
if value is not None:
|
|
1803
1895
|
try:
|
|
1804
|
-
|
|
1805
|
-
if
|
|
1806
|
-
self.unicodes.append(
|
|
1896
|
+
intValue = int(value, 16)
|
|
1897
|
+
if intValue not in self.unicodes:
|
|
1898
|
+
self.unicodes.append(intValue)
|
|
1807
1899
|
except ValueError:
|
|
1808
1900
|
pass
|
|
1809
1901
|
super().startElementHandler(name, attrs)
|
|
@@ -1812,7 +1904,7 @@ class _FetchUnicodesParser(_BaseParser):
|
|
|
1812
1904
|
# image
|
|
1813
1905
|
|
|
1814
1906
|
|
|
1815
|
-
def _fetchImageFileName(glif):
|
|
1907
|
+
def _fetchImageFileName(glif: bytes) -> Optional[str]:
|
|
1816
1908
|
"""
|
|
1817
1909
|
The image file name (if any) from glif.
|
|
1818
1910
|
"""
|
|
@@ -1825,11 +1917,11 @@ def _fetchImageFileName(glif):
|
|
|
1825
1917
|
|
|
1826
1918
|
|
|
1827
1919
|
class _FetchImageFileNameParser(_BaseParser):
|
|
1828
|
-
def __init__(self):
|
|
1829
|
-
self.fileName = None
|
|
1920
|
+
def __init__(self) -> None:
|
|
1921
|
+
self.fileName: Optional[str] = None
|
|
1830
1922
|
super().__init__()
|
|
1831
1923
|
|
|
1832
|
-
def startElementHandler(self, name, attrs):
|
|
1924
|
+
def startElementHandler(self, name: str, attrs: dict[str, str]) -> None:
|
|
1833
1925
|
if name == "image" and self._elementStack and self._elementStack[-1] == "glyph":
|
|
1834
1926
|
self.fileName = attrs.get("fileName")
|
|
1835
1927
|
raise _DoneParsing
|
|
@@ -1839,7 +1931,7 @@ class _FetchImageFileNameParser(_BaseParser):
|
|
|
1839
1931
|
# component references
|
|
1840
1932
|
|
|
1841
1933
|
|
|
1842
|
-
def _fetchComponentBases(glif):
|
|
1934
|
+
def _fetchComponentBases(glif: bytes) -> list[str]:
|
|
1843
1935
|
"""
|
|
1844
1936
|
Get a list of component base glyphs listed in glif.
|
|
1845
1937
|
"""
|
|
@@ -1852,11 +1944,11 @@ def _fetchComponentBases(glif):
|
|
|
1852
1944
|
|
|
1853
1945
|
|
|
1854
1946
|
class _FetchComponentBasesParser(_BaseParser):
|
|
1855
|
-
def __init__(self):
|
|
1856
|
-
self.bases = []
|
|
1947
|
+
def __init__(self) -> None:
|
|
1948
|
+
self.bases: list[str] = []
|
|
1857
1949
|
super().__init__()
|
|
1858
1950
|
|
|
1859
|
-
def startElementHandler(self, name, attrs):
|
|
1951
|
+
def startElementHandler(self, name: str, attrs: dict[str, str]) -> None:
|
|
1860
1952
|
if (
|
|
1861
1953
|
name == "component"
|
|
1862
1954
|
and self._elementStack
|
|
@@ -1867,7 +1959,7 @@ class _FetchComponentBasesParser(_BaseParser):
|
|
|
1867
1959
|
self.bases.append(base)
|
|
1868
1960
|
super().startElementHandler(name, attrs)
|
|
1869
1961
|
|
|
1870
|
-
def endElementHandler(self, name):
|
|
1962
|
+
def endElementHandler(self, name: str) -> None:
|
|
1871
1963
|
if name == "outline":
|
|
1872
1964
|
raise _DoneParsing
|
|
1873
1965
|
super().endElementHandler(name)
|
|
@@ -1877,7 +1969,7 @@ class _FetchComponentBasesParser(_BaseParser):
|
|
|
1877
1969
|
# GLIF Point Pen
|
|
1878
1970
|
# --------------
|
|
1879
1971
|
|
|
1880
|
-
_transformationInfo = [
|
|
1972
|
+
_transformationInfo: list[tuple[str, int]] = [
|
|
1881
1973
|
# field name, default value
|
|
1882
1974
|
("xScale", 1),
|
|
1883
1975
|
("xyScale", 0),
|
|
@@ -1894,15 +1986,21 @@ class GLIFPointPen(AbstractPointPen):
|
|
|
1894
1986
|
part of .glif files.
|
|
1895
1987
|
"""
|
|
1896
1988
|
|
|
1897
|
-
def __init__(
|
|
1989
|
+
def __init__(
|
|
1990
|
+
self,
|
|
1991
|
+
element: ElementType,
|
|
1992
|
+
formatVersion: Optional[FormatVersion] = None,
|
|
1993
|
+
identifiers: Optional[set[str]] = None,
|
|
1994
|
+
validate: bool = True,
|
|
1995
|
+
) -> None:
|
|
1898
1996
|
if identifiers is None:
|
|
1899
1997
|
identifiers = set()
|
|
1900
|
-
self.formatVersion =
|
|
1998
|
+
self.formatVersion = normalizeFormatVersion(formatVersion, GLIFFormatVersion)
|
|
1901
1999
|
self.identifiers = identifiers
|
|
1902
2000
|
self.outline = element
|
|
1903
2001
|
self.contour = None
|
|
1904
2002
|
self.prevOffCurveCount = 0
|
|
1905
|
-
self.prevPointTypes = []
|
|
2003
|
+
self.prevPointTypes: list[str] = []
|
|
1906
2004
|
self.validate = validate
|
|
1907
2005
|
|
|
1908
2006
|
def beginPath(self, identifier=None, **kwargs):
|