fonttools 4.59.2__cp314-cp314-macosx_10_13_universal2.whl → 4.60.1__cp314-cp314-macosx_10_13_universal2.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fonttools might be problematic. Click here for more details.
- fontTools/__init__.py +1 -1
- fontTools/annotations.py +30 -0
- fontTools/cu2qu/cu2qu.c +1057 -937
- fontTools/cu2qu/cu2qu.cpython-314-darwin.so +0 -0
- fontTools/cu2qu/cu2qu.py +19 -2
- fontTools/feaLib/lexer.c +8 -3
- fontTools/feaLib/lexer.cpython-314-darwin.so +0 -0
- fontTools/misc/bezierTools.c +8 -3
- fontTools/misc/bezierTools.cpython-314-darwin.so +0 -0
- fontTools/misc/enumTools.py +23 -0
- fontTools/misc/visitor.py +24 -16
- fontTools/pens/filterPen.py +218 -26
- fontTools/pens/momentsPen.c +8 -3
- fontTools/pens/momentsPen.cpython-314-darwin.so +0 -0
- fontTools/pens/pointPen.py +40 -6
- fontTools/qu2cu/qu2cu.c +20 -7
- fontTools/qu2cu/qu2cu.cpython-314-darwin.so +0 -0
- fontTools/subset/__init__.py +178 -12
- fontTools/ttLib/tables/_p_o_s_t.py +5 -5
- fontTools/ufoLib/__init__.py +278 -175
- fontTools/ufoLib/converters.py +14 -5
- fontTools/ufoLib/filenames.py +16 -6
- fontTools/ufoLib/glifLib.py +286 -190
- fontTools/ufoLib/kerning.py +32 -12
- fontTools/ufoLib/utils.py +41 -13
- fontTools/ufoLib/validators.py +121 -97
- 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/interpolatableHelpers.py +3 -0
- fontTools/varLib/iup.c +14 -5
- fontTools/varLib/iup.cpython-314-darwin.so +0 -0
- {fonttools-4.59.2.dist-info → fonttools-4.60.1.dist-info}/METADATA +41 -2
- {fonttools-4.59.2.dist-info → fonttools-4.60.1.dist-info}/RECORD +44 -37
- {fonttools-4.59.2.data → fonttools-4.60.1.data}/data/share/man/man1/ttx.1 +0 -0
- {fonttools-4.59.2.dist-info → fonttools-4.60.1.dist-info}/WHEEL +0 -0
- {fonttools-4.59.2.dist-info → fonttools-4.60.1.dist-info}/entry_points.txt +0 -0
- {fonttools-4.59.2.dist-info → fonttools-4.60.1.dist-info}/licenses/LICENSE +0 -0
- {fonttools-4.59.2.dist-info → fonttools-4.60.1.dist-info}/licenses/LICENSE.external +0 -0
- {fonttools-4.59.2.dist-info → fonttools-4.60.1.dist-info}/top_level.txt +0 -0
fontTools/ufoLib/__init__.py
CHANGED
|
@@ -32,6 +32,8 @@ Value conversion functions are available for converting
|
|
|
32
32
|
- :func:`.convertFontInfoValueForAttributeFromVersion3ToVersion2`
|
|
33
33
|
"""
|
|
34
34
|
|
|
35
|
+
from __future__ import annotations
|
|
36
|
+
|
|
35
37
|
import enum
|
|
36
38
|
import logging
|
|
37
39
|
import os
|
|
@@ -39,19 +41,47 @@ import zipfile
|
|
|
39
41
|
from collections import OrderedDict
|
|
40
42
|
from copy import deepcopy
|
|
41
43
|
from os import fsdecode
|
|
44
|
+
from typing import IO, TYPE_CHECKING, Any, Optional, Union, cast
|
|
42
45
|
|
|
43
46
|
from fontTools.misc import filesystem as fs
|
|
44
47
|
from fontTools.misc import plistlib
|
|
45
48
|
from fontTools.ufoLib.converters import convertUFO1OrUFO2KerningToUFO3Kerning
|
|
46
49
|
from fontTools.ufoLib.errors import UFOLibError
|
|
47
50
|
from fontTools.ufoLib.filenames import userNameToFileName
|
|
48
|
-
from fontTools.ufoLib.utils import
|
|
51
|
+
from fontTools.ufoLib.utils import (
|
|
52
|
+
BaseFormatVersion,
|
|
53
|
+
normalizeFormatVersion,
|
|
54
|
+
numberTypes,
|
|
55
|
+
)
|
|
49
56
|
from fontTools.ufoLib.validators import *
|
|
50
57
|
|
|
58
|
+
if TYPE_CHECKING:
|
|
59
|
+
from logging import Logger
|
|
60
|
+
|
|
61
|
+
from fontTools.annotations import (
|
|
62
|
+
GlyphNameToFileNameFunc,
|
|
63
|
+
K,
|
|
64
|
+
KerningDict,
|
|
65
|
+
KerningGroups,
|
|
66
|
+
KerningNested,
|
|
67
|
+
PathOrFS,
|
|
68
|
+
PathStr,
|
|
69
|
+
UFOFormatVersionInput,
|
|
70
|
+
V,
|
|
71
|
+
)
|
|
72
|
+
from fontTools.misc.filesystem._base import FS
|
|
73
|
+
from fontTools.ufoLib.glifLib import GlyphSet
|
|
74
|
+
|
|
75
|
+
KerningGroupRenameMaps = dict[str, dict[str, str]]
|
|
76
|
+
LibDict = dict[str, Any]
|
|
77
|
+
LayerOrderList = Optional[list[Optional[str]]]
|
|
78
|
+
AttributeDataDict = dict[str, Any]
|
|
79
|
+
FontInfoAttributes = dict[str, AttributeDataDict]
|
|
80
|
+
|
|
51
81
|
# client code can check this to see if the upstream `fs` package is being used
|
|
52
82
|
haveFS = fs._haveFS
|
|
53
83
|
|
|
54
|
-
__all__ = [
|
|
84
|
+
__all__: list[str] = [
|
|
55
85
|
"haveFS",
|
|
56
86
|
"makeUFOPath",
|
|
57
87
|
"UFOLibError",
|
|
@@ -69,43 +99,37 @@ __all__ = [
|
|
|
69
99
|
"convertFontInfoValueForAttributeFromVersion2ToVersion1",
|
|
70
100
|
]
|
|
71
101
|
|
|
72
|
-
__version__ = "3.0.0"
|
|
102
|
+
__version__: str = "3.0.0"
|
|
73
103
|
|
|
74
104
|
|
|
75
|
-
logger = logging.getLogger(__name__)
|
|
105
|
+
logger: Logger = logging.getLogger(__name__)
|
|
76
106
|
|
|
77
107
|
|
|
78
108
|
# ---------
|
|
79
109
|
# Constants
|
|
80
110
|
# ---------
|
|
81
111
|
|
|
82
|
-
DEFAULT_GLYPHS_DIRNAME = "glyphs"
|
|
83
|
-
DATA_DIRNAME = "data"
|
|
84
|
-
IMAGES_DIRNAME = "images"
|
|
85
|
-
METAINFO_FILENAME = "metainfo.plist"
|
|
86
|
-
FONTINFO_FILENAME = "fontinfo.plist"
|
|
87
|
-
LIB_FILENAME = "lib.plist"
|
|
88
|
-
GROUPS_FILENAME = "groups.plist"
|
|
89
|
-
KERNING_FILENAME = "kerning.plist"
|
|
90
|
-
FEATURES_FILENAME = "features.fea"
|
|
91
|
-
LAYERCONTENTS_FILENAME = "layercontents.plist"
|
|
92
|
-
LAYERINFO_FILENAME = "layerinfo.plist"
|
|
112
|
+
DEFAULT_GLYPHS_DIRNAME: str = "glyphs"
|
|
113
|
+
DATA_DIRNAME: str = "data"
|
|
114
|
+
IMAGES_DIRNAME: str = "images"
|
|
115
|
+
METAINFO_FILENAME: str = "metainfo.plist"
|
|
116
|
+
FONTINFO_FILENAME: str = "fontinfo.plist"
|
|
117
|
+
LIB_FILENAME: str = "lib.plist"
|
|
118
|
+
GROUPS_FILENAME: str = "groups.plist"
|
|
119
|
+
KERNING_FILENAME: str = "kerning.plist"
|
|
120
|
+
FEATURES_FILENAME: str = "features.fea"
|
|
121
|
+
LAYERCONTENTS_FILENAME: str = "layercontents.plist"
|
|
122
|
+
LAYERINFO_FILENAME: str = "layerinfo.plist"
|
|
93
123
|
|
|
94
|
-
DEFAULT_LAYER_NAME = "public.default"
|
|
124
|
+
DEFAULT_LAYER_NAME: str = "public.default"
|
|
95
125
|
|
|
96
126
|
|
|
97
|
-
class UFOFormatVersion(
|
|
127
|
+
class UFOFormatVersion(BaseFormatVersion):
|
|
98
128
|
FORMAT_1_0 = (1, 0)
|
|
99
129
|
FORMAT_2_0 = (2, 0)
|
|
100
130
|
FORMAT_3_0 = (3, 0)
|
|
101
131
|
|
|
102
132
|
|
|
103
|
-
# python 3.11 doesn't like when a mixin overrides a dunder method like __str__
|
|
104
|
-
# for some reasons it keep using Enum.__str__, see
|
|
105
|
-
# https://github.com/fonttools/fonttools/pull/2655
|
|
106
|
-
UFOFormatVersion.__str__ = _VersionTupleEnumMixin.__str__
|
|
107
|
-
|
|
108
|
-
|
|
109
133
|
class UFOFileStructure(enum.Enum):
|
|
110
134
|
ZIP = "zip"
|
|
111
135
|
PACKAGE = "package"
|
|
@@ -117,7 +141,11 @@ class UFOFileStructure(enum.Enum):
|
|
|
117
141
|
|
|
118
142
|
|
|
119
143
|
class _UFOBaseIO:
|
|
120
|
-
|
|
144
|
+
if TYPE_CHECKING:
|
|
145
|
+
fs: FS
|
|
146
|
+
_havePreviousFile: bool
|
|
147
|
+
|
|
148
|
+
def getFileModificationTime(self, path: PathStr) -> Optional[float]:
|
|
121
149
|
"""
|
|
122
150
|
Returns the modification time for the file at the given path, as a
|
|
123
151
|
floating point number giving the number of seconds since the epoch.
|
|
@@ -129,9 +157,11 @@ class _UFOBaseIO:
|
|
|
129
157
|
except (fs.errors.MissingInfoNamespace, fs.errors.ResourceNotFound):
|
|
130
158
|
return None
|
|
131
159
|
else:
|
|
132
|
-
|
|
160
|
+
if dt is not None:
|
|
161
|
+
return dt.timestamp()
|
|
162
|
+
return None
|
|
133
163
|
|
|
134
|
-
def _getPlist(self, fileName, default=None):
|
|
164
|
+
def _getPlist(self, fileName: str, default: Optional[Any] = None) -> Any:
|
|
135
165
|
"""
|
|
136
166
|
Read a property list relative to the UFO filesystem's root.
|
|
137
167
|
Raises UFOLibError if the file is missing and default is None,
|
|
@@ -155,7 +185,7 @@ class _UFOBaseIO:
|
|
|
155
185
|
# TODO(anthrotype): try to narrow this down a little
|
|
156
186
|
raise UFOLibError(f"'{fileName}' could not be read on {self.fs}: {e}")
|
|
157
187
|
|
|
158
|
-
def _writePlist(self, fileName, obj):
|
|
188
|
+
def _writePlist(self, fileName: str, obj: Any) -> None:
|
|
159
189
|
"""
|
|
160
190
|
Write a property list to a file relative to the UFO filesystem's root.
|
|
161
191
|
|
|
@@ -209,15 +239,17 @@ class UFOReader(_UFOBaseIO):
|
|
|
209
239
|
``False`` to not validate the data.
|
|
210
240
|
"""
|
|
211
241
|
|
|
212
|
-
def __init__(self, path, validate=True):
|
|
213
|
-
if
|
|
242
|
+
def __init__(self, path: PathOrFS, validate: bool = True) -> None:
|
|
243
|
+
# Only call __fspath__ if path is not already a str or FS object
|
|
244
|
+
if not isinstance(path, (str, fs.base.FS)) and hasattr(path, "__fspath__"):
|
|
214
245
|
path = path.__fspath__()
|
|
215
246
|
|
|
216
247
|
if isinstance(path, str):
|
|
217
248
|
structure = _sniffFileStructure(path)
|
|
249
|
+
parentFS: FS
|
|
218
250
|
try:
|
|
219
251
|
if structure is UFOFileStructure.ZIP:
|
|
220
|
-
parentFS = fs.zipfs.ZipFS(path, write=False, encoding="utf-8")
|
|
252
|
+
parentFS = fs.zipfs.ZipFS(path, write=False, encoding="utf-8") # type: ignore[abstract]
|
|
221
253
|
else:
|
|
222
254
|
parentFS = fs.osfs.OSFS(path)
|
|
223
255
|
except fs.errors.CreateFailed as e:
|
|
@@ -235,7 +267,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
235
267
|
if len(rootDirs) == 1:
|
|
236
268
|
# 'ClosingSubFS' ensures that the parent zip file is closed when
|
|
237
269
|
# its root subdirectory is closed
|
|
238
|
-
self.fs = parentFS.opendir(
|
|
270
|
+
self.fs: FS = parentFS.opendir(
|
|
239
271
|
rootDirs[0], factory=fs.subfs.ClosingSubFS
|
|
240
272
|
)
|
|
241
273
|
else:
|
|
@@ -247,10 +279,10 @@ class UFOReader(_UFOBaseIO):
|
|
|
247
279
|
self.fs = parentFS
|
|
248
280
|
# when passed a path string, we make sure we close the newly opened fs
|
|
249
281
|
# upon calling UFOReader.close method or context manager's __exit__
|
|
250
|
-
self._shouldClose = True
|
|
282
|
+
self._shouldClose: bool = True
|
|
251
283
|
self._fileStructure = structure
|
|
252
284
|
elif isinstance(path, fs.base.FS):
|
|
253
|
-
filesystem = path
|
|
285
|
+
filesystem: FS = path
|
|
254
286
|
try:
|
|
255
287
|
filesystem.check()
|
|
256
288
|
except fs.errors.FilesystemClosed:
|
|
@@ -272,9 +304,9 @@ class UFOReader(_UFOBaseIO):
|
|
|
272
304
|
"Expected a path string or fs.base.FS object, found '%s'"
|
|
273
305
|
% type(path).__name__
|
|
274
306
|
)
|
|
275
|
-
self._path = fsdecode(path)
|
|
276
|
-
self._validate = validate
|
|
277
|
-
self._upConvertedKerningData = None
|
|
307
|
+
self._path: str = fsdecode(path)
|
|
308
|
+
self._validate: bool = validate
|
|
309
|
+
self._upConvertedKerningData: Optional[dict[str, Any]] = None
|
|
278
310
|
|
|
279
311
|
try:
|
|
280
312
|
self.readMetaInfo(validate=validate)
|
|
@@ -284,7 +316,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
284
316
|
|
|
285
317
|
# properties
|
|
286
318
|
|
|
287
|
-
def _get_path(self):
|
|
319
|
+
def _get_path(self) -> str:
|
|
288
320
|
import warnings
|
|
289
321
|
|
|
290
322
|
warnings.warn(
|
|
@@ -294,9 +326,9 @@ class UFOReader(_UFOBaseIO):
|
|
|
294
326
|
)
|
|
295
327
|
return self._path
|
|
296
328
|
|
|
297
|
-
path = property(_get_path, doc="The path of the UFO (DEPRECATED).")
|
|
329
|
+
path: property = property(_get_path, doc="The path of the UFO (DEPRECATED).")
|
|
298
330
|
|
|
299
|
-
def _get_formatVersion(self):
|
|
331
|
+
def _get_formatVersion(self) -> int:
|
|
300
332
|
import warnings
|
|
301
333
|
|
|
302
334
|
warnings.warn(
|
|
@@ -312,16 +344,16 @@ class UFOReader(_UFOBaseIO):
|
|
|
312
344
|
)
|
|
313
345
|
|
|
314
346
|
@property
|
|
315
|
-
def formatVersionTuple(self):
|
|
347
|
+
def formatVersionTuple(self) -> tuple[int, int]:
|
|
316
348
|
"""The (major, minor) format version of the UFO.
|
|
317
349
|
This is determined by reading metainfo.plist during __init__.
|
|
318
350
|
"""
|
|
319
351
|
return self._formatVersion
|
|
320
352
|
|
|
321
|
-
def _get_fileStructure(self):
|
|
353
|
+
def _get_fileStructure(self) -> Any:
|
|
322
354
|
return self._fileStructure
|
|
323
355
|
|
|
324
|
-
fileStructure = property(
|
|
356
|
+
fileStructure: property = property(
|
|
325
357
|
_get_fileStructure,
|
|
326
358
|
doc=(
|
|
327
359
|
"The file structure of the UFO: "
|
|
@@ -331,7 +363,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
331
363
|
|
|
332
364
|
# up conversion
|
|
333
365
|
|
|
334
|
-
def _upConvertKerning(self, validate):
|
|
366
|
+
def _upConvertKerning(self, validate: bool) -> None:
|
|
335
367
|
"""
|
|
336
368
|
Up convert kerning and groups in UFO 1 and 2.
|
|
337
369
|
The data will be held internally until each bit of data
|
|
@@ -385,7 +417,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
385
417
|
|
|
386
418
|
# support methods
|
|
387
419
|
|
|
388
|
-
def readBytesFromPath(self, path):
|
|
420
|
+
def readBytesFromPath(self, path: PathStr) -> Optional[bytes]:
|
|
389
421
|
"""
|
|
390
422
|
Returns the bytes in the file at the given path.
|
|
391
423
|
The path must be relative to the UFO's filesystem root.
|
|
@@ -396,7 +428,9 @@ class UFOReader(_UFOBaseIO):
|
|
|
396
428
|
except fs.errors.ResourceNotFound:
|
|
397
429
|
return None
|
|
398
430
|
|
|
399
|
-
def getReadFileForPath(
|
|
431
|
+
def getReadFileForPath(
|
|
432
|
+
self, path: PathStr, encoding: Optional[str] = None
|
|
433
|
+
) -> Optional[Union[IO[bytes], IO[str]]]:
|
|
400
434
|
"""
|
|
401
435
|
Returns a file (or file-like) object for the file at the given path.
|
|
402
436
|
The path must be relative to the UFO path.
|
|
@@ -417,7 +451,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
417
451
|
|
|
418
452
|
# metainfo.plist
|
|
419
453
|
|
|
420
|
-
def _readMetaInfo(self, validate=None):
|
|
454
|
+
def _readMetaInfo(self, validate: Optional[bool] = None) -> dict[str, Any]:
|
|
421
455
|
"""
|
|
422
456
|
Read metainfo.plist and return raw data. Only used for internal operations.
|
|
423
457
|
|
|
@@ -459,7 +493,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
459
493
|
data["formatVersionTuple"] = formatVersion
|
|
460
494
|
return data
|
|
461
495
|
|
|
462
|
-
def readMetaInfo(self, validate=None):
|
|
496
|
+
def readMetaInfo(self, validate: Optional[bool] = None) -> None:
|
|
463
497
|
"""
|
|
464
498
|
Read metainfo.plist and set formatVersion. Only used for internal operations.
|
|
465
499
|
|
|
@@ -471,7 +505,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
471
505
|
|
|
472
506
|
# groups.plist
|
|
473
507
|
|
|
474
|
-
def _readGroups(self):
|
|
508
|
+
def _readGroups(self) -> dict[str, list[str]]:
|
|
475
509
|
groups = self._getPlist(GROUPS_FILENAME, {})
|
|
476
510
|
# remove any duplicate glyphs in a kerning group
|
|
477
511
|
for groupName, glyphList in groups.items():
|
|
@@ -479,7 +513,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
479
513
|
groups[groupName] = list(OrderedDict.fromkeys(glyphList))
|
|
480
514
|
return groups
|
|
481
515
|
|
|
482
|
-
def readGroups(self, validate=None):
|
|
516
|
+
def readGroups(self, validate: Optional[bool] = None) -> dict[str, list[str]]:
|
|
483
517
|
"""
|
|
484
518
|
Read groups.plist. Returns a dict.
|
|
485
519
|
``validate`` will validate the read data, by default it is set to the
|
|
@@ -490,7 +524,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
490
524
|
# handle up conversion
|
|
491
525
|
if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
|
|
492
526
|
self._upConvertKerning(validate)
|
|
493
|
-
groups = self._upConvertedKerningData["groups"]
|
|
527
|
+
groups = cast(dict, self._upConvertedKerningData)["groups"]
|
|
494
528
|
# normal
|
|
495
529
|
else:
|
|
496
530
|
groups = self._readGroups()
|
|
@@ -500,7 +534,9 @@ class UFOReader(_UFOBaseIO):
|
|
|
500
534
|
raise UFOLibError(message)
|
|
501
535
|
return groups
|
|
502
536
|
|
|
503
|
-
def getKerningGroupConversionRenameMaps(
|
|
537
|
+
def getKerningGroupConversionRenameMaps(
|
|
538
|
+
self, validate: Optional[bool] = None
|
|
539
|
+
) -> KerningGroupRenameMaps:
|
|
504
540
|
"""
|
|
505
541
|
Get maps defining the renaming that was done during any
|
|
506
542
|
needed kerning group conversion. This method returns a
|
|
@@ -524,17 +560,17 @@ class UFOReader(_UFOBaseIO):
|
|
|
524
560
|
# use the public group reader to force the load and
|
|
525
561
|
# conversion of the data if it hasn't happened yet.
|
|
526
562
|
self.readGroups(validate=validate)
|
|
527
|
-
return self._upConvertedKerningData["groupRenameMaps"]
|
|
563
|
+
return cast(dict, self._upConvertedKerningData)["groupRenameMaps"]
|
|
528
564
|
|
|
529
565
|
# fontinfo.plist
|
|
530
566
|
|
|
531
|
-
def _readInfo(self, validate):
|
|
567
|
+
def _readInfo(self, validate: bool) -> dict[str, Any]:
|
|
532
568
|
data = self._getPlist(FONTINFO_FILENAME, {})
|
|
533
569
|
if validate and not isinstance(data, dict):
|
|
534
570
|
raise UFOLibError("fontinfo.plist is not properly formatted.")
|
|
535
571
|
return data
|
|
536
572
|
|
|
537
|
-
def readInfo(self, info, validate=None):
|
|
573
|
+
def readInfo(self, info: Any, validate: Optional[bool] = None) -> None:
|
|
538
574
|
"""
|
|
539
575
|
Read fontinfo.plist. It requires an object that allows
|
|
540
576
|
setting attributes with names that follow the fontinfo.plist
|
|
@@ -593,11 +629,11 @@ class UFOReader(_UFOBaseIO):
|
|
|
593
629
|
|
|
594
630
|
# kerning.plist
|
|
595
631
|
|
|
596
|
-
def _readKerning(self):
|
|
632
|
+
def _readKerning(self) -> KerningNested:
|
|
597
633
|
data = self._getPlist(KERNING_FILENAME, {})
|
|
598
634
|
return data
|
|
599
635
|
|
|
600
|
-
def readKerning(self, validate=None):
|
|
636
|
+
def readKerning(self, validate: Optional[bool] = None) -> KerningDict:
|
|
601
637
|
"""
|
|
602
638
|
Read kerning.plist. Returns a dict.
|
|
603
639
|
|
|
@@ -609,7 +645,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
609
645
|
# handle up conversion
|
|
610
646
|
if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
|
|
611
647
|
self._upConvertKerning(validate)
|
|
612
|
-
kerningNested = self._upConvertedKerningData["kerning"]
|
|
648
|
+
kerningNested = cast(dict, self._upConvertedKerningData)["kerning"]
|
|
613
649
|
# normal
|
|
614
650
|
else:
|
|
615
651
|
kerningNested = self._readKerning()
|
|
@@ -627,7 +663,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
627
663
|
|
|
628
664
|
# lib.plist
|
|
629
665
|
|
|
630
|
-
def readLib(self, validate=None):
|
|
666
|
+
def readLib(self, validate: Optional[bool] = None) -> dict[str, Any]:
|
|
631
667
|
"""
|
|
632
668
|
Read lib.plist. Returns a dict.
|
|
633
669
|
|
|
@@ -645,7 +681,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
645
681
|
|
|
646
682
|
# features.fea
|
|
647
683
|
|
|
648
|
-
def readFeatures(self):
|
|
684
|
+
def readFeatures(self) -> str:
|
|
649
685
|
"""
|
|
650
686
|
Read features.fea. Return a string.
|
|
651
687
|
The returned string is empty if the file is missing.
|
|
@@ -658,7 +694,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
658
694
|
|
|
659
695
|
# glyph sets & layers
|
|
660
696
|
|
|
661
|
-
def _readLayerContents(self, validate):
|
|
697
|
+
def _readLayerContents(self, validate: bool) -> list[tuple[str, str]]:
|
|
662
698
|
"""
|
|
663
699
|
Rebuild the layer contents list by checking what glyphsets
|
|
664
700
|
are available on disk.
|
|
@@ -674,7 +710,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
674
710
|
raise UFOLibError(error)
|
|
675
711
|
return contents
|
|
676
712
|
|
|
677
|
-
def getLayerNames(self, validate=None):
|
|
713
|
+
def getLayerNames(self, validate: Optional[bool] = None) -> list[str]:
|
|
678
714
|
"""
|
|
679
715
|
Get the ordered layer names from layercontents.plist.
|
|
680
716
|
|
|
@@ -687,7 +723,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
687
723
|
layerNames = [layerName for layerName, directoryName in layerContents]
|
|
688
724
|
return layerNames
|
|
689
725
|
|
|
690
|
-
def getDefaultLayerName(self, validate=None):
|
|
726
|
+
def getDefaultLayerName(self, validate: Optional[bool] = None) -> str:
|
|
691
727
|
"""
|
|
692
728
|
Get the default layer name from layercontents.plist.
|
|
693
729
|
|
|
@@ -703,7 +739,12 @@ class UFOReader(_UFOBaseIO):
|
|
|
703
739
|
# this will already have been raised during __init__
|
|
704
740
|
raise UFOLibError("The default layer is not defined in layercontents.plist.")
|
|
705
741
|
|
|
706
|
-
def getGlyphSet(
|
|
742
|
+
def getGlyphSet(
|
|
743
|
+
self,
|
|
744
|
+
layerName: Optional[str] = None,
|
|
745
|
+
validateRead: Optional[bool] = None,
|
|
746
|
+
validateWrite: Optional[bool] = None,
|
|
747
|
+
) -> GlyphSet:
|
|
707
748
|
"""
|
|
708
749
|
Return the GlyphSet associated with the
|
|
709
750
|
glyphs directory mapped to layerName
|
|
@@ -744,7 +785,9 @@ class UFOReader(_UFOBaseIO):
|
|
|
744
785
|
expectContentsFile=True,
|
|
745
786
|
)
|
|
746
787
|
|
|
747
|
-
def getCharacterMapping(
|
|
788
|
+
def getCharacterMapping(
|
|
789
|
+
self, layerName: Optional[str] = None, validate: Optional[bool] = None
|
|
790
|
+
) -> dict[int, list[str]]:
|
|
748
791
|
"""
|
|
749
792
|
Return a dictionary that maps unicode values (ints) to
|
|
750
793
|
lists of glyph names.
|
|
@@ -755,7 +798,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
755
798
|
layerName, validateRead=validate, validateWrite=True
|
|
756
799
|
)
|
|
757
800
|
allUnicodes = glyphSet.getUnicodes()
|
|
758
|
-
cmap = {}
|
|
801
|
+
cmap: dict[int, list[str]] = {}
|
|
759
802
|
for glyphName, unicodes in allUnicodes.items():
|
|
760
803
|
for code in unicodes:
|
|
761
804
|
if code in cmap:
|
|
@@ -766,7 +809,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
766
809
|
|
|
767
810
|
# /data
|
|
768
811
|
|
|
769
|
-
def getDataDirectoryListing(self):
|
|
812
|
+
def getDataDirectoryListing(self) -> list[str]:
|
|
770
813
|
"""
|
|
771
814
|
Returns a list of all files in the data directory.
|
|
772
815
|
The returned paths will be relative to the UFO.
|
|
@@ -787,7 +830,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
787
830
|
except fs.errors.ResourceError:
|
|
788
831
|
return []
|
|
789
832
|
|
|
790
|
-
def getImageDirectoryListing(self, validate=None):
|
|
833
|
+
def getImageDirectoryListing(self, validate: Optional[bool] = None) -> list[str]:
|
|
791
834
|
"""
|
|
792
835
|
Returns a list of all image file names in
|
|
793
836
|
the images directory. Each of the images will
|
|
@@ -823,7 +866,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
823
866
|
result.append(path.name)
|
|
824
867
|
return result
|
|
825
868
|
|
|
826
|
-
def readData(self, fileName):
|
|
869
|
+
def readData(self, fileName: PathStr) -> bytes:
|
|
827
870
|
"""
|
|
828
871
|
Return bytes for the file named 'fileName' inside the 'data/' directory.
|
|
829
872
|
"""
|
|
@@ -839,7 +882,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
839
882
|
raise UFOLibError(f"No data file named '{fileName}' on {self.fs}")
|
|
840
883
|
return data
|
|
841
884
|
|
|
842
|
-
def readImage(self, fileName, validate=None):
|
|
885
|
+
def readImage(self, fileName: PathStr, validate: Optional[bool] = None) -> bytes:
|
|
843
886
|
"""
|
|
844
887
|
Return image data for the file named fileName.
|
|
845
888
|
|
|
@@ -868,14 +911,14 @@ class UFOReader(_UFOBaseIO):
|
|
|
868
911
|
raise UFOLibError(error)
|
|
869
912
|
return data
|
|
870
913
|
|
|
871
|
-
def close(self):
|
|
914
|
+
def close(self) -> None:
|
|
872
915
|
if self._shouldClose:
|
|
873
916
|
self.fs.close()
|
|
874
917
|
|
|
875
|
-
def __enter__(self):
|
|
918
|
+
def __enter__(self) -> UFOReader:
|
|
876
919
|
return self
|
|
877
920
|
|
|
878
|
-
def __exit__(self, exc_type, exc_value, exc_tb):
|
|
921
|
+
def __exit__(self, exc_type: Any, exc_value: Any, exc_tb: Any) -> None:
|
|
879
922
|
self.close()
|
|
880
923
|
|
|
881
924
|
|
|
@@ -910,14 +953,14 @@ class UFOWriter(UFOReader):
|
|
|
910
953
|
|
|
911
954
|
def __init__(
|
|
912
955
|
self,
|
|
913
|
-
path,
|
|
914
|
-
formatVersion=None,
|
|
915
|
-
fileCreator="com.github.fonttools.ufoLib",
|
|
916
|
-
structure=None,
|
|
917
|
-
validate=True,
|
|
918
|
-
):
|
|
956
|
+
path: PathOrFS,
|
|
957
|
+
formatVersion: UFOFormatVersionInput = None,
|
|
958
|
+
fileCreator: str = "com.github.fonttools.ufoLib",
|
|
959
|
+
structure: Optional[UFOFileStructure] = None,
|
|
960
|
+
validate: bool = True,
|
|
961
|
+
) -> None:
|
|
919
962
|
try:
|
|
920
|
-
formatVersion =
|
|
963
|
+
formatVersion = normalizeFormatVersion(formatVersion, UFOFormatVersion)
|
|
921
964
|
except ValueError as e:
|
|
922
965
|
from fontTools.ufoLib.errors import UnsupportedUFOFormat
|
|
923
966
|
|
|
@@ -963,8 +1006,8 @@ class UFOWriter(UFOReader):
|
|
|
963
1006
|
# we can't write a zip in-place, so we have to copy its
|
|
964
1007
|
# contents to a temporary location and work from there, then
|
|
965
1008
|
# upon closing UFOWriter we create the final zip file
|
|
966
|
-
parentFS = fs.tempfs.TempFS()
|
|
967
|
-
with fs.zipfs.ZipFS(path, encoding="utf-8") as origFS:
|
|
1009
|
+
parentFS: FS = fs.tempfs.TempFS()
|
|
1010
|
+
with fs.zipfs.ZipFS(path, encoding="utf-8") as origFS: # type: ignore[abstract]
|
|
968
1011
|
fs.copy.copy_fs(origFS, parentFS)
|
|
969
1012
|
# if output path is an existing zip, we require that it contains
|
|
970
1013
|
# one, and only one, root directory (with arbitrary name), in turn
|
|
@@ -986,7 +1029,7 @@ class UFOWriter(UFOReader):
|
|
|
986
1029
|
# if the output zip file didn't exist, we create the root folder;
|
|
987
1030
|
# we name it the same as input 'path', but with '.ufo' extension
|
|
988
1031
|
rootDir = os.path.splitext(os.path.basename(path))[0] + ".ufo"
|
|
989
|
-
parentFS = fs.zipfs.ZipFS(path, write=True, encoding="utf-8")
|
|
1032
|
+
parentFS = fs.zipfs.ZipFS(path, write=True, encoding="utf-8") # type: ignore[abstract]
|
|
990
1033
|
parentFS.makedir(rootDir)
|
|
991
1034
|
# 'ClosingSubFS' ensures that the parent filesystem is closed
|
|
992
1035
|
# when its root subdirectory is closed
|
|
@@ -997,7 +1040,7 @@ class UFOWriter(UFOReader):
|
|
|
997
1040
|
self._havePreviousFile = havePreviousFile
|
|
998
1041
|
self._shouldClose = True
|
|
999
1042
|
elif isinstance(path, fs.base.FS):
|
|
1000
|
-
filesystem = path
|
|
1043
|
+
filesystem: FS = path
|
|
1001
1044
|
try:
|
|
1002
1045
|
filesystem.check()
|
|
1003
1046
|
except fs.errors.FilesystemClosed:
|
|
@@ -1032,7 +1075,7 @@ class UFOWriter(UFOReader):
|
|
|
1032
1075
|
self._path = fsdecode(path)
|
|
1033
1076
|
self._formatVersion = formatVersion
|
|
1034
1077
|
self._fileCreator = fileCreator
|
|
1035
|
-
self._downConversionKerningData = None
|
|
1078
|
+
self._downConversionKerningData: Optional[KerningGroupRenameMaps] = None
|
|
1036
1079
|
self._validate = validate
|
|
1037
1080
|
# if the file already exists, get the format version.
|
|
1038
1081
|
# this will be needed for up and down conversion.
|
|
@@ -1050,7 +1093,7 @@ class UFOWriter(UFOReader):
|
|
|
1050
1093
|
"that is trying to be written. This is not supported."
|
|
1051
1094
|
)
|
|
1052
1095
|
# handle the layer contents
|
|
1053
|
-
self.layerContents = {}
|
|
1096
|
+
self.layerContents: Union[dict[str, str], OrderedDict[str, str]] = {}
|
|
1054
1097
|
if previousFormatVersion is not None and previousFormatVersion.major >= 3:
|
|
1055
1098
|
# already exists
|
|
1056
1099
|
self.layerContents = OrderedDict(self._readLayerContents(validate))
|
|
@@ -1064,17 +1107,19 @@ class UFOWriter(UFOReader):
|
|
|
1064
1107
|
|
|
1065
1108
|
# properties
|
|
1066
1109
|
|
|
1067
|
-
def _get_fileCreator(self):
|
|
1110
|
+
def _get_fileCreator(self) -> str:
|
|
1068
1111
|
return self._fileCreator
|
|
1069
1112
|
|
|
1070
|
-
fileCreator = property(
|
|
1113
|
+
fileCreator: property = property(
|
|
1071
1114
|
_get_fileCreator,
|
|
1072
1115
|
doc="The file creator of the UFO. This is set into metainfo.plist during __init__.",
|
|
1073
1116
|
)
|
|
1074
1117
|
|
|
1075
1118
|
# support methods for file system interaction
|
|
1076
1119
|
|
|
1077
|
-
def copyFromReader(
|
|
1120
|
+
def copyFromReader(
|
|
1121
|
+
self, reader: UFOReader, sourcePath: PathStr, destPath: PathStr
|
|
1122
|
+
) -> None:
|
|
1078
1123
|
"""
|
|
1079
1124
|
Copy the sourcePath in the provided UFOReader to destPath
|
|
1080
1125
|
in this writer. The paths must be relative. This works with
|
|
@@ -1097,7 +1142,7 @@ class UFOWriter(UFOReader):
|
|
|
1097
1142
|
else:
|
|
1098
1143
|
fs.copy.copy_file(reader.fs, sourcePath, self.fs, destPath)
|
|
1099
1144
|
|
|
1100
|
-
def writeBytesToPath(self, path, data):
|
|
1145
|
+
def writeBytesToPath(self, path: PathStr, data: bytes) -> None:
|
|
1101
1146
|
"""
|
|
1102
1147
|
Write bytes to a path relative to the UFO filesystem's root.
|
|
1103
1148
|
If writing to an existing UFO, check to see if data matches the data
|
|
@@ -1117,7 +1162,12 @@ class UFOWriter(UFOReader):
|
|
|
1117
1162
|
self.fs.makedirs(fs.path.dirname(path), recreate=True)
|
|
1118
1163
|
self.fs.writebytes(path, data)
|
|
1119
1164
|
|
|
1120
|
-
def getFileObjectForPath(
|
|
1165
|
+
def getFileObjectForPath(
|
|
1166
|
+
self,
|
|
1167
|
+
path: PathStr,
|
|
1168
|
+
mode: str = "w",
|
|
1169
|
+
encoding: Optional[str] = None,
|
|
1170
|
+
) -> Optional[IO[Any]]:
|
|
1121
1171
|
"""
|
|
1122
1172
|
Returns a file (or file-like) object for the
|
|
1123
1173
|
file at the given path. The path must be relative
|
|
@@ -1140,9 +1190,12 @@ class UFOWriter(UFOReader):
|
|
|
1140
1190
|
self.fs.makedirs(fs.path.dirname(path), recreate=True)
|
|
1141
1191
|
return self.fs.open(path, mode=mode, encoding=encoding)
|
|
1142
1192
|
except fs.errors.ResourceError as e:
|
|
1143
|
-
|
|
1193
|
+
raise UFOLibError(f"unable to open '{path}' on {self.fs}: {e}")
|
|
1194
|
+
return None
|
|
1144
1195
|
|
|
1145
|
-
def removePath(
|
|
1196
|
+
def removePath(
|
|
1197
|
+
self, path: PathStr, force: bool = False, removeEmptyParents: bool = True
|
|
1198
|
+
) -> None:
|
|
1146
1199
|
"""
|
|
1147
1200
|
Remove the file (or directory) at path. The path
|
|
1148
1201
|
must be relative to the UFO.
|
|
@@ -1169,7 +1222,7 @@ class UFOWriter(UFOReader):
|
|
|
1169
1222
|
|
|
1170
1223
|
# UFO mod time
|
|
1171
1224
|
|
|
1172
|
-
def setModificationTime(self):
|
|
1225
|
+
def setModificationTime(self) -> None:
|
|
1173
1226
|
"""
|
|
1174
1227
|
Set the UFO modification time to the current time.
|
|
1175
1228
|
This is never called automatically. It is up to the
|
|
@@ -1185,7 +1238,7 @@ class UFOWriter(UFOReader):
|
|
|
1185
1238
|
|
|
1186
1239
|
# metainfo.plist
|
|
1187
1240
|
|
|
1188
|
-
def _writeMetaInfo(self):
|
|
1241
|
+
def _writeMetaInfo(self) -> None:
|
|
1189
1242
|
metaInfo = dict(
|
|
1190
1243
|
creator=self._fileCreator,
|
|
1191
1244
|
formatVersion=self._formatVersion.major,
|
|
@@ -1196,7 +1249,7 @@ class UFOWriter(UFOReader):
|
|
|
1196
1249
|
|
|
1197
1250
|
# groups.plist
|
|
1198
1251
|
|
|
1199
|
-
def setKerningGroupConversionRenameMaps(self, maps):
|
|
1252
|
+
def setKerningGroupConversionRenameMaps(self, maps: KerningGroupRenameMaps) -> None:
|
|
1200
1253
|
"""
|
|
1201
1254
|
Set maps defining the renaming that should be done
|
|
1202
1255
|
when writing groups and kerning in UFO 1 and UFO 2.
|
|
@@ -1221,7 +1274,9 @@ class UFOWriter(UFOReader):
|
|
|
1221
1274
|
remap[dataName] = writeName
|
|
1222
1275
|
self._downConversionKerningData = dict(groupRenameMap=remap)
|
|
1223
1276
|
|
|
1224
|
-
def writeGroups(
|
|
1277
|
+
def writeGroups(
|
|
1278
|
+
self, groups: KerningGroups, validate: Optional[bool] = None
|
|
1279
|
+
) -> None:
|
|
1225
1280
|
"""
|
|
1226
1281
|
Write groups.plist. This method requires a
|
|
1227
1282
|
dict of glyph groups as an argument.
|
|
@@ -1276,7 +1331,7 @@ class UFOWriter(UFOReader):
|
|
|
1276
1331
|
|
|
1277
1332
|
# fontinfo.plist
|
|
1278
1333
|
|
|
1279
|
-
def writeInfo(self, info, validate=None):
|
|
1334
|
+
def writeInfo(self, info: Any, validate: Optional[bool] = None) -> None:
|
|
1280
1335
|
"""
|
|
1281
1336
|
Write info.plist. This method requires an object
|
|
1282
1337
|
that supports getting attributes that follow the
|
|
@@ -1322,7 +1377,9 @@ class UFOWriter(UFOReader):
|
|
|
1322
1377
|
|
|
1323
1378
|
# kerning.plist
|
|
1324
1379
|
|
|
1325
|
-
def writeKerning(
|
|
1380
|
+
def writeKerning(
|
|
1381
|
+
self, kerning: KerningDict, validate: Optional[bool] = None
|
|
1382
|
+
) -> None:
|
|
1326
1383
|
"""
|
|
1327
1384
|
Write kerning.plist. This method requires a
|
|
1328
1385
|
dict of kerning pairs as an argument.
|
|
@@ -1366,7 +1423,7 @@ class UFOWriter(UFOReader):
|
|
|
1366
1423
|
remappedKerning[side1, side2] = value
|
|
1367
1424
|
kerning = remappedKerning
|
|
1368
1425
|
# pack and write
|
|
1369
|
-
kerningDict = {}
|
|
1426
|
+
kerningDict: KerningNested = {}
|
|
1370
1427
|
for left, right in kerning.keys():
|
|
1371
1428
|
value = kerning[left, right]
|
|
1372
1429
|
if left not in kerningDict:
|
|
@@ -1379,7 +1436,7 @@ class UFOWriter(UFOReader):
|
|
|
1379
1436
|
|
|
1380
1437
|
# lib.plist
|
|
1381
1438
|
|
|
1382
|
-
def writeLib(self, libDict, validate=None):
|
|
1439
|
+
def writeLib(self, libDict: LibDict, validate: Optional[bool] = None) -> None:
|
|
1383
1440
|
"""
|
|
1384
1441
|
Write lib.plist. This method requires a
|
|
1385
1442
|
lib dict as an argument.
|
|
@@ -1400,7 +1457,7 @@ class UFOWriter(UFOReader):
|
|
|
1400
1457
|
|
|
1401
1458
|
# features.fea
|
|
1402
1459
|
|
|
1403
|
-
def writeFeatures(self, features, validate=None):
|
|
1460
|
+
def writeFeatures(self, features: str, validate: Optional[bool] = None) -> None:
|
|
1404
1461
|
"""
|
|
1405
1462
|
Write features.fea. This method requires a
|
|
1406
1463
|
features string as an argument.
|
|
@@ -1419,7 +1476,9 @@ class UFOWriter(UFOReader):
|
|
|
1419
1476
|
|
|
1420
1477
|
# glyph sets & layers
|
|
1421
1478
|
|
|
1422
|
-
def writeLayerContents(
|
|
1479
|
+
def writeLayerContents(
|
|
1480
|
+
self, layerOrder: LayerOrderList = None, validate: Optional[bool] = None
|
|
1481
|
+
) -> None:
|
|
1423
1482
|
"""
|
|
1424
1483
|
Write the layercontents.plist file. This method *must* be called
|
|
1425
1484
|
after all glyph sets have been written.
|
|
@@ -1429,7 +1488,7 @@ class UFOWriter(UFOReader):
|
|
|
1429
1488
|
if self._formatVersion < UFOFormatVersion.FORMAT_3_0:
|
|
1430
1489
|
return
|
|
1431
1490
|
if layerOrder is not None:
|
|
1432
|
-
newOrder = []
|
|
1491
|
+
newOrder: list[Optional[str]] = []
|
|
1433
1492
|
for layerName in layerOrder:
|
|
1434
1493
|
if layerName is None:
|
|
1435
1494
|
layerName = DEFAULT_LAYER_NAME
|
|
@@ -1442,11 +1501,13 @@ class UFOWriter(UFOReader):
|
|
|
1442
1501
|
"The layer order content does not match the glyph sets that have been created."
|
|
1443
1502
|
)
|
|
1444
1503
|
layerContents = [
|
|
1445
|
-
(layerName, self.layerContents[layerName])
|
|
1504
|
+
(layerName, self.layerContents[layerName])
|
|
1505
|
+
for layerName in layerOrder
|
|
1506
|
+
if layerName is not None
|
|
1446
1507
|
]
|
|
1447
1508
|
self._writePlist(LAYERCONTENTS_FILENAME, layerContents)
|
|
1448
1509
|
|
|
1449
|
-
def _findDirectoryForLayerName(self, layerName):
|
|
1510
|
+
def _findDirectoryForLayerName(self, layerName: Optional[str]) -> str:
|
|
1450
1511
|
foundDirectory = None
|
|
1451
1512
|
for existingLayerName, directoryName in list(self.layerContents.items()):
|
|
1452
1513
|
if layerName is None and directoryName == DEFAULT_GLYPHS_DIRNAME:
|
|
@@ -1462,15 +1523,15 @@ class UFOWriter(UFOReader):
|
|
|
1462
1523
|
)
|
|
1463
1524
|
return foundDirectory
|
|
1464
1525
|
|
|
1465
|
-
def getGlyphSet(
|
|
1526
|
+
def getGlyphSet( # type: ignore[override]
|
|
1466
1527
|
self,
|
|
1467
|
-
layerName=None,
|
|
1468
|
-
defaultLayer=True,
|
|
1469
|
-
glyphNameToFileNameFunc=None,
|
|
1470
|
-
validateRead=None,
|
|
1471
|
-
validateWrite=None,
|
|
1472
|
-
expectContentsFile=False,
|
|
1473
|
-
):
|
|
1528
|
+
layerName: Optional[str] = None,
|
|
1529
|
+
defaultLayer: bool = True,
|
|
1530
|
+
glyphNameToFileNameFunc: GlyphNameToFileNameFunc = None,
|
|
1531
|
+
validateRead: Optional[bool] = None,
|
|
1532
|
+
validateWrite: Optional[bool] = None,
|
|
1533
|
+
expectContentsFile: bool = False,
|
|
1534
|
+
) -> GlyphSet:
|
|
1474
1535
|
"""
|
|
1475
1536
|
Return the GlyphSet object associated with the
|
|
1476
1537
|
appropriate glyph directory in the .ufo.
|
|
@@ -1530,11 +1591,11 @@ class UFOWriter(UFOReader):
|
|
|
1530
1591
|
|
|
1531
1592
|
def _getDefaultGlyphSet(
|
|
1532
1593
|
self,
|
|
1533
|
-
validateRead,
|
|
1534
|
-
validateWrite,
|
|
1535
|
-
glyphNameToFileNameFunc=None,
|
|
1536
|
-
expectContentsFile=False,
|
|
1537
|
-
):
|
|
1594
|
+
validateRead: bool,
|
|
1595
|
+
validateWrite: bool,
|
|
1596
|
+
glyphNameToFileNameFunc: GlyphNameToFileNameFunc = None,
|
|
1597
|
+
expectContentsFile: bool = False,
|
|
1598
|
+
) -> GlyphSet:
|
|
1538
1599
|
from fontTools.ufoLib.glifLib import GlyphSet
|
|
1539
1600
|
|
|
1540
1601
|
glyphSubFS = self.fs.makedir(DEFAULT_GLYPHS_DIRNAME, recreate=True)
|
|
@@ -1549,13 +1610,13 @@ class UFOWriter(UFOReader):
|
|
|
1549
1610
|
|
|
1550
1611
|
def _getGlyphSetFormatVersion3(
|
|
1551
1612
|
self,
|
|
1552
|
-
validateRead,
|
|
1553
|
-
validateWrite,
|
|
1554
|
-
layerName=None,
|
|
1555
|
-
defaultLayer=True,
|
|
1556
|
-
glyphNameToFileNameFunc=None,
|
|
1557
|
-
expectContentsFile=False,
|
|
1558
|
-
):
|
|
1613
|
+
validateRead: bool,
|
|
1614
|
+
validateWrite: bool,
|
|
1615
|
+
layerName: Optional[str] = None,
|
|
1616
|
+
defaultLayer: bool = True,
|
|
1617
|
+
glyphNameToFileNameFunc: GlyphNameToFileNameFunc = None,
|
|
1618
|
+
expectContentsFile: bool = False,
|
|
1619
|
+
) -> GlyphSet:
|
|
1559
1620
|
from fontTools.ufoLib.glifLib import GlyphSet
|
|
1560
1621
|
|
|
1561
1622
|
# if the default flag is on, make sure that the default in the file
|
|
@@ -1573,6 +1634,11 @@ class UFOWriter(UFOReader):
|
|
|
1573
1634
|
raise UFOLibError(
|
|
1574
1635
|
"The layer name is already mapped to a non-default layer."
|
|
1575
1636
|
)
|
|
1637
|
+
|
|
1638
|
+
# handle layerName is None to avoid MyPy errors
|
|
1639
|
+
if layerName is None:
|
|
1640
|
+
raise TypeError("'leyerName' cannot be None.")
|
|
1641
|
+
|
|
1576
1642
|
# get an existing directory name
|
|
1577
1643
|
if layerName in self.layerContents:
|
|
1578
1644
|
directory = self.layerContents[layerName]
|
|
@@ -1601,7 +1667,12 @@ class UFOWriter(UFOReader):
|
|
|
1601
1667
|
expectContentsFile=expectContentsFile,
|
|
1602
1668
|
)
|
|
1603
1669
|
|
|
1604
|
-
def renameGlyphSet(
|
|
1670
|
+
def renameGlyphSet(
|
|
1671
|
+
self,
|
|
1672
|
+
layerName: Optional[str],
|
|
1673
|
+
newLayerName: Optional[str],
|
|
1674
|
+
defaultLayer: bool = False,
|
|
1675
|
+
) -> None:
|
|
1605
1676
|
"""
|
|
1606
1677
|
Rename a glyph set.
|
|
1607
1678
|
|
|
@@ -1615,7 +1686,7 @@ class UFOWriter(UFOReader):
|
|
|
1615
1686
|
return
|
|
1616
1687
|
# the new and old names can be the same
|
|
1617
1688
|
# as long as the default is being switched
|
|
1618
|
-
if layerName == newLayerName:
|
|
1689
|
+
if layerName is not None and layerName == newLayerName:
|
|
1619
1690
|
# if the default is off and the layer is already not the default, skip
|
|
1620
1691
|
if (
|
|
1621
1692
|
self.layerContents[layerName] != DEFAULT_GLYPHS_DIRNAME
|
|
@@ -1644,12 +1715,13 @@ class UFOWriter(UFOReader):
|
|
|
1644
1715
|
newLayerName, existing=existing, prefix="glyphs."
|
|
1645
1716
|
)
|
|
1646
1717
|
# update the internal mapping
|
|
1647
|
-
|
|
1718
|
+
if layerName is not None:
|
|
1719
|
+
del self.layerContents[layerName]
|
|
1648
1720
|
self.layerContents[newLayerName] = newDirectory
|
|
1649
1721
|
# do the file system copy
|
|
1650
1722
|
self.fs.movedir(oldDirectory, newDirectory, create=True)
|
|
1651
1723
|
|
|
1652
|
-
def deleteGlyphSet(self, layerName):
|
|
1724
|
+
def deleteGlyphSet(self, layerName: Optional[str]) -> None:
|
|
1653
1725
|
"""
|
|
1654
1726
|
Remove the glyph set matching layerName.
|
|
1655
1727
|
"""
|
|
@@ -1659,16 +1731,17 @@ class UFOWriter(UFOReader):
|
|
|
1659
1731
|
return
|
|
1660
1732
|
foundDirectory = self._findDirectoryForLayerName(layerName)
|
|
1661
1733
|
self.removePath(foundDirectory, removeEmptyParents=False)
|
|
1662
|
-
|
|
1734
|
+
if layerName is not None:
|
|
1735
|
+
del self.layerContents[layerName]
|
|
1663
1736
|
|
|
1664
|
-
def writeData(self, fileName, data):
|
|
1737
|
+
def writeData(self, fileName: PathStr, data: bytes) -> None:
|
|
1665
1738
|
"""
|
|
1666
1739
|
Write data to fileName in the 'data' directory.
|
|
1667
1740
|
The data must be a bytes string.
|
|
1668
1741
|
"""
|
|
1669
1742
|
self.writeBytesToPath(f"{DATA_DIRNAME}/{fsdecode(fileName)}", data)
|
|
1670
1743
|
|
|
1671
|
-
def removeData(self, fileName):
|
|
1744
|
+
def removeData(self, fileName: PathStr) -> None:
|
|
1672
1745
|
"""
|
|
1673
1746
|
Remove the file named fileName from the data directory.
|
|
1674
1747
|
"""
|
|
@@ -1676,7 +1749,12 @@ class UFOWriter(UFOReader):
|
|
|
1676
1749
|
|
|
1677
1750
|
# /images
|
|
1678
1751
|
|
|
1679
|
-
def writeImage(
|
|
1752
|
+
def writeImage(
|
|
1753
|
+
self,
|
|
1754
|
+
fileName: PathStr,
|
|
1755
|
+
data: bytes,
|
|
1756
|
+
validate: Optional[bool] = None,
|
|
1757
|
+
) -> None:
|
|
1680
1758
|
"""
|
|
1681
1759
|
Write data to fileName in the images directory.
|
|
1682
1760
|
The data must be a valid PNG.
|
|
@@ -1694,7 +1772,11 @@ class UFOWriter(UFOReader):
|
|
|
1694
1772
|
raise UFOLibError(error)
|
|
1695
1773
|
self.writeBytesToPath(f"{IMAGES_DIRNAME}/{fileName}", data)
|
|
1696
1774
|
|
|
1697
|
-
def removeImage(
|
|
1775
|
+
def removeImage(
|
|
1776
|
+
self,
|
|
1777
|
+
fileName: PathStr,
|
|
1778
|
+
validate: Optional[bool] = None,
|
|
1779
|
+
) -> None: # XXX remove unused 'validate'?
|
|
1698
1780
|
"""
|
|
1699
1781
|
Remove the file named fileName from the
|
|
1700
1782
|
images directory.
|
|
@@ -1705,7 +1787,13 @@ class UFOWriter(UFOReader):
|
|
|
1705
1787
|
)
|
|
1706
1788
|
self.removePath(f"{IMAGES_DIRNAME}/{fsdecode(fileName)}")
|
|
1707
1789
|
|
|
1708
|
-
def copyImageFromReader(
|
|
1790
|
+
def copyImageFromReader(
|
|
1791
|
+
self,
|
|
1792
|
+
reader: UFOReader,
|
|
1793
|
+
sourceFileName: PathStr,
|
|
1794
|
+
destFileName: PathStr,
|
|
1795
|
+
validate: Optional[bool] = None,
|
|
1796
|
+
) -> None:
|
|
1709
1797
|
"""
|
|
1710
1798
|
Copy the sourceFileName in the provided UFOReader to destFileName
|
|
1711
1799
|
in this writer. This uses the most memory efficient method possible
|
|
@@ -1721,12 +1809,12 @@ class UFOWriter(UFOReader):
|
|
|
1721
1809
|
destPath = f"{IMAGES_DIRNAME}/{fsdecode(destFileName)}"
|
|
1722
1810
|
self.copyFromReader(reader, sourcePath, destPath)
|
|
1723
1811
|
|
|
1724
|
-
def close(self):
|
|
1812
|
+
def close(self) -> None:
|
|
1725
1813
|
if self._havePreviousFile and self._fileStructure is UFOFileStructure.ZIP:
|
|
1726
1814
|
# if we are updating an existing zip file, we can now compress the
|
|
1727
1815
|
# contents of the temporary filesystem in the destination path
|
|
1728
1816
|
rootDir = os.path.splitext(os.path.basename(self._path))[0] + ".ufo"
|
|
1729
|
-
with fs.zipfs.ZipFS(self._path, write=True, encoding="utf-8") as destFS:
|
|
1817
|
+
with fs.zipfs.ZipFS(self._path, write=True, encoding="utf-8") as destFS: # type: ignore[abstract]
|
|
1730
1818
|
fs.copy.copy_fs(self.fs, destFS.makedir(rootDir))
|
|
1731
1819
|
super().close()
|
|
1732
1820
|
|
|
@@ -1740,7 +1828,7 @@ UFOReaderWriter = UFOWriter
|
|
|
1740
1828
|
# ----------------
|
|
1741
1829
|
|
|
1742
1830
|
|
|
1743
|
-
def _sniffFileStructure(ufo_path):
|
|
1831
|
+
def _sniffFileStructure(ufo_path: PathStr) -> UFOFileStructure:
|
|
1744
1832
|
"""Return UFOFileStructure.ZIP if the UFO at path 'ufo_path' (str)
|
|
1745
1833
|
is a zip file, else return UFOFileStructure.PACKAGE if 'ufo_path' is a
|
|
1746
1834
|
directory.
|
|
@@ -1759,7 +1847,7 @@ def _sniffFileStructure(ufo_path):
|
|
|
1759
1847
|
raise UFOLibError("No such file or directory: '%s'" % ufo_path)
|
|
1760
1848
|
|
|
1761
1849
|
|
|
1762
|
-
def makeUFOPath(path):
|
|
1850
|
+
def makeUFOPath(path: PathStr) -> str:
|
|
1763
1851
|
"""
|
|
1764
1852
|
Return a .ufo pathname.
|
|
1765
1853
|
|
|
@@ -1786,7 +1874,7 @@ def makeUFOPath(path):
|
|
|
1786
1874
|
# cases of invalid values.
|
|
1787
1875
|
|
|
1788
1876
|
|
|
1789
|
-
def validateFontInfoVersion2ValueForAttribute(attr, value):
|
|
1877
|
+
def validateFontInfoVersion2ValueForAttribute(attr: str, value: Any) -> bool:
|
|
1790
1878
|
"""
|
|
1791
1879
|
This performs very basic validation of the value for attribute
|
|
1792
1880
|
following the UFO 2 fontinfo.plist specification. The results
|
|
@@ -1798,7 +1886,7 @@ def validateFontInfoVersion2ValueForAttribute(attr, value):
|
|
|
1798
1886
|
"""
|
|
1799
1887
|
dataValidationDict = fontInfoAttributesVersion2ValueData[attr]
|
|
1800
1888
|
valueType = dataValidationDict.get("type")
|
|
1801
|
-
validator = dataValidationDict.get("valueValidator")
|
|
1889
|
+
validator = dataValidationDict.get("valueValidator", genericTypeValidator)
|
|
1802
1890
|
valueOptions = dataValidationDict.get("valueOptions")
|
|
1803
1891
|
# have specific options for the validator
|
|
1804
1892
|
if valueOptions is not None:
|
|
@@ -1812,7 +1900,7 @@ def validateFontInfoVersion2ValueForAttribute(attr, value):
|
|
|
1812
1900
|
return isValidValue
|
|
1813
1901
|
|
|
1814
1902
|
|
|
1815
|
-
def validateInfoVersion2Data(infoData):
|
|
1903
|
+
def validateInfoVersion2Data(infoData: dict[str, Any]) -> dict[str, Any]:
|
|
1816
1904
|
"""
|
|
1817
1905
|
This performs very basic validation of the value for infoData
|
|
1818
1906
|
following the UFO 2 fontinfo.plist specification. The results
|
|
@@ -1832,7 +1920,7 @@ def validateInfoVersion2Data(infoData):
|
|
|
1832
1920
|
return validInfoData
|
|
1833
1921
|
|
|
1834
1922
|
|
|
1835
|
-
def validateFontInfoVersion3ValueForAttribute(attr, value):
|
|
1923
|
+
def validateFontInfoVersion3ValueForAttribute(attr: str, value: Any) -> bool:
|
|
1836
1924
|
"""
|
|
1837
1925
|
This performs very basic validation of the value for attribute
|
|
1838
1926
|
following the UFO 3 fontinfo.plist specification. The results
|
|
@@ -1844,7 +1932,7 @@ def validateFontInfoVersion3ValueForAttribute(attr, value):
|
|
|
1844
1932
|
"""
|
|
1845
1933
|
dataValidationDict = fontInfoAttributesVersion3ValueData[attr]
|
|
1846
1934
|
valueType = dataValidationDict.get("type")
|
|
1847
|
-
validator = dataValidationDict.get("valueValidator")
|
|
1935
|
+
validator = dataValidationDict.get("valueValidator", genericTypeValidator)
|
|
1848
1936
|
valueOptions = dataValidationDict.get("valueOptions")
|
|
1849
1937
|
# have specific options for the validator
|
|
1850
1938
|
if valueOptions is not None:
|
|
@@ -1858,7 +1946,7 @@ def validateFontInfoVersion3ValueForAttribute(attr, value):
|
|
|
1858
1946
|
return isValidValue
|
|
1859
1947
|
|
|
1860
1948
|
|
|
1861
|
-
def validateInfoVersion3Data(infoData):
|
|
1949
|
+
def validateInfoVersion3Data(infoData: dict[str, Any]) -> dict[str, Any]:
|
|
1862
1950
|
"""
|
|
1863
1951
|
This performs very basic validation of the value for infoData
|
|
1864
1952
|
following the UFO 3 fontinfo.plist specification. The results
|
|
@@ -1880,18 +1968,18 @@ def validateInfoVersion3Data(infoData):
|
|
|
1880
1968
|
|
|
1881
1969
|
# Value Options
|
|
1882
1970
|
|
|
1883
|
-
fontInfoOpenTypeHeadFlagsOptions = list(range(0, 15))
|
|
1884
|
-
fontInfoOpenTypeOS2SelectionOptions = [1, 2, 3, 4, 7, 8, 9]
|
|
1885
|
-
fontInfoOpenTypeOS2UnicodeRangesOptions = list(range(0, 128))
|
|
1886
|
-
fontInfoOpenTypeOS2CodePageRangesOptions = list(range(0, 64))
|
|
1887
|
-
fontInfoOpenTypeOS2TypeOptions = [0, 1, 2, 3, 8, 9]
|
|
1971
|
+
fontInfoOpenTypeHeadFlagsOptions: list[int] = list(range(0, 15))
|
|
1972
|
+
fontInfoOpenTypeOS2SelectionOptions: list[int] = [1, 2, 3, 4, 7, 8, 9]
|
|
1973
|
+
fontInfoOpenTypeOS2UnicodeRangesOptions: list[int] = list(range(0, 128))
|
|
1974
|
+
fontInfoOpenTypeOS2CodePageRangesOptions: list[int] = list(range(0, 64))
|
|
1975
|
+
fontInfoOpenTypeOS2TypeOptions: list[int] = [0, 1, 2, 3, 8, 9]
|
|
1888
1976
|
|
|
1889
1977
|
# Version Attribute Definitions
|
|
1890
1978
|
# This defines the attributes, types and, in some
|
|
1891
1979
|
# cases the possible values, that can exist is
|
|
1892
1980
|
# fontinfo.plist.
|
|
1893
1981
|
|
|
1894
|
-
fontInfoAttributesVersion1 = {
|
|
1982
|
+
fontInfoAttributesVersion1: set[str] = {
|
|
1895
1983
|
"familyName",
|
|
1896
1984
|
"styleName",
|
|
1897
1985
|
"fullName",
|
|
@@ -1934,7 +2022,7 @@ fontInfoAttributesVersion1 = {
|
|
|
1934
2022
|
"ttVersion",
|
|
1935
2023
|
}
|
|
1936
2024
|
|
|
1937
|
-
fontInfoAttributesVersion2ValueData = {
|
|
2025
|
+
fontInfoAttributesVersion2ValueData: FontInfoAttributes = {
|
|
1938
2026
|
"familyName": dict(type=str),
|
|
1939
2027
|
"styleName": dict(type=str),
|
|
1940
2028
|
"styleMapFamilyName": dict(type=str),
|
|
@@ -2076,9 +2164,11 @@ fontInfoAttributesVersion2ValueData = {
|
|
|
2076
2164
|
"macintoshFONDFamilyID": dict(type=int),
|
|
2077
2165
|
"macintoshFONDName": dict(type=str),
|
|
2078
2166
|
}
|
|
2079
|
-
fontInfoAttributesVersion2 = set(fontInfoAttributesVersion2ValueData.keys())
|
|
2167
|
+
fontInfoAttributesVersion2: set[str] = set(fontInfoAttributesVersion2ValueData.keys())
|
|
2080
2168
|
|
|
2081
|
-
fontInfoAttributesVersion3ValueData = deepcopy(
|
|
2169
|
+
fontInfoAttributesVersion3ValueData: FontInfoAttributes = deepcopy(
|
|
2170
|
+
fontInfoAttributesVersion2ValueData
|
|
2171
|
+
)
|
|
2082
2172
|
fontInfoAttributesVersion3ValueData.update(
|
|
2083
2173
|
{
|
|
2084
2174
|
"versionMinor": dict(type=int, valueValidator=genericNonNegativeIntValidator),
|
|
@@ -2161,7 +2251,7 @@ fontInfoAttributesVersion3ValueData.update(
|
|
|
2161
2251
|
"guidelines": dict(type=list, valueValidator=guidelinesValidator),
|
|
2162
2252
|
}
|
|
2163
2253
|
)
|
|
2164
|
-
fontInfoAttributesVersion3 = set(fontInfoAttributesVersion3ValueData.keys())
|
|
2254
|
+
fontInfoAttributesVersion3: set[str] = set(fontInfoAttributesVersion3ValueData.keys())
|
|
2165
2255
|
|
|
2166
2256
|
# insert the type validator for all attrs that
|
|
2167
2257
|
# have no defined validator.
|
|
@@ -2178,14 +2268,14 @@ for attr, dataDict in list(fontInfoAttributesVersion3ValueData.items()):
|
|
|
2178
2268
|
# to version 2 or vice-versa.
|
|
2179
2269
|
|
|
2180
2270
|
|
|
2181
|
-
def _flipDict(d):
|
|
2271
|
+
def _flipDict(d: dict[K, V]) -> dict[V, K]:
|
|
2182
2272
|
flipped = {}
|
|
2183
2273
|
for key, value in list(d.items()):
|
|
2184
2274
|
flipped[value] = key
|
|
2185
2275
|
return flipped
|
|
2186
2276
|
|
|
2187
2277
|
|
|
2188
|
-
fontInfoAttributesVersion1To2 = {
|
|
2278
|
+
fontInfoAttributesVersion1To2: dict[str, str] = {
|
|
2189
2279
|
"menuName": "styleMapFamilyName",
|
|
2190
2280
|
"designer": "openTypeNameDesigner",
|
|
2191
2281
|
"designerURL": "openTypeNameDesignerURL",
|
|
@@ -2217,12 +2307,17 @@ fontInfoAttributesVersion1To2 = {
|
|
|
2217
2307
|
fontInfoAttributesVersion2To1 = _flipDict(fontInfoAttributesVersion1To2)
|
|
2218
2308
|
deprecatedFontInfoAttributesVersion2 = set(fontInfoAttributesVersion1To2.keys())
|
|
2219
2309
|
|
|
2220
|
-
_fontStyle1To2
|
|
2221
|
-
|
|
2310
|
+
_fontStyle1To2: dict[int, str] = {
|
|
2311
|
+
64: "regular",
|
|
2312
|
+
1: "italic",
|
|
2313
|
+
32: "bold",
|
|
2314
|
+
33: "bold italic",
|
|
2315
|
+
}
|
|
2316
|
+
_fontStyle2To1: dict[str, int] = _flipDict(_fontStyle1To2)
|
|
2222
2317
|
# Some UFO 1 files have 0
|
|
2223
2318
|
_fontStyle1To2[0] = "regular"
|
|
2224
2319
|
|
|
2225
|
-
_widthName1To2 = {
|
|
2320
|
+
_widthName1To2: dict[str, int] = {
|
|
2226
2321
|
"Ultra-condensed": 1,
|
|
2227
2322
|
"Extra-condensed": 2,
|
|
2228
2323
|
"Condensed": 3,
|
|
@@ -2233,7 +2328,7 @@ _widthName1To2 = {
|
|
|
2233
2328
|
"Extra-expanded": 8,
|
|
2234
2329
|
"Ultra-expanded": 9,
|
|
2235
2330
|
}
|
|
2236
|
-
_widthName2To1 = _flipDict(_widthName1To2)
|
|
2331
|
+
_widthName2To1: dict[int, str] = _flipDict(_widthName1To2)
|
|
2237
2332
|
# FontLab's default width value is "Normal".
|
|
2238
2333
|
# Many format version 1 UFOs will have this.
|
|
2239
2334
|
_widthName1To2["Normal"] = 5
|
|
@@ -2245,7 +2340,7 @@ _widthName1To2["medium"] = 5
|
|
|
2245
2340
|
# "Medium" appears in a lot of UFO 1 files.
|
|
2246
2341
|
_widthName1To2["Medium"] = 5
|
|
2247
2342
|
|
|
2248
|
-
_msCharSet1To2 = {
|
|
2343
|
+
_msCharSet1To2: dict[int, int] = {
|
|
2249
2344
|
0: 1,
|
|
2250
2345
|
1: 2,
|
|
2251
2346
|
2: 3,
|
|
@@ -2267,12 +2362,14 @@ _msCharSet1To2 = {
|
|
|
2267
2362
|
238: 19,
|
|
2268
2363
|
255: 20,
|
|
2269
2364
|
}
|
|
2270
|
-
_msCharSet2To1 = _flipDict(_msCharSet1To2)
|
|
2365
|
+
_msCharSet2To1: dict[int, int] = _flipDict(_msCharSet1To2)
|
|
2271
2366
|
|
|
2272
2367
|
# 1 <-> 2
|
|
2273
2368
|
|
|
2274
2369
|
|
|
2275
|
-
def convertFontInfoValueForAttributeFromVersion1ToVersion2(
|
|
2370
|
+
def convertFontInfoValueForAttributeFromVersion1ToVersion2(
|
|
2371
|
+
attr: str, value: Any
|
|
2372
|
+
) -> tuple[str, Any]:
|
|
2276
2373
|
"""
|
|
2277
2374
|
Convert value from version 1 to version 2 format.
|
|
2278
2375
|
Returns the new attribute name and the converted value.
|
|
@@ -2284,7 +2381,7 @@ def convertFontInfoValueForAttributeFromVersion1ToVersion2(attr, value):
|
|
|
2284
2381
|
value = int(value)
|
|
2285
2382
|
if value is not None:
|
|
2286
2383
|
if attr == "fontStyle":
|
|
2287
|
-
v = _fontStyle1To2.get(value)
|
|
2384
|
+
v: Optional[Union[str, int]] = _fontStyle1To2.get(value)
|
|
2288
2385
|
if v is None:
|
|
2289
2386
|
raise UFOLibError(
|
|
2290
2387
|
f"Cannot convert value ({value!r}) for attribute {attr}."
|
|
@@ -2308,7 +2405,9 @@ def convertFontInfoValueForAttributeFromVersion1ToVersion2(attr, value):
|
|
|
2308
2405
|
return attr, value
|
|
2309
2406
|
|
|
2310
2407
|
|
|
2311
|
-
def convertFontInfoValueForAttributeFromVersion2ToVersion1(
|
|
2408
|
+
def convertFontInfoValueForAttributeFromVersion2ToVersion1(
|
|
2409
|
+
attr: str, value: Any
|
|
2410
|
+
) -> tuple[str, Any]:
|
|
2312
2411
|
"""
|
|
2313
2412
|
Convert value from version 2 to version 1 format.
|
|
2314
2413
|
Returns the new attribute name and the converted value.
|
|
@@ -2325,7 +2424,7 @@ def convertFontInfoValueForAttributeFromVersion2ToVersion1(attr, value):
|
|
|
2325
2424
|
return attr, value
|
|
2326
2425
|
|
|
2327
2426
|
|
|
2328
|
-
def _convertFontInfoDataVersion1ToVersion2(data):
|
|
2427
|
+
def _convertFontInfoDataVersion1ToVersion2(data: dict[str, Any]) -> dict[str, Any]:
|
|
2329
2428
|
converted = {}
|
|
2330
2429
|
for attr, value in list(data.items()):
|
|
2331
2430
|
# FontLab gives -1 for the weightValue
|
|
@@ -2349,7 +2448,7 @@ def _convertFontInfoDataVersion1ToVersion2(data):
|
|
|
2349
2448
|
return converted
|
|
2350
2449
|
|
|
2351
2450
|
|
|
2352
|
-
def _convertFontInfoDataVersion2ToVersion1(data):
|
|
2451
|
+
def _convertFontInfoDataVersion2ToVersion1(data: dict[str, Any]) -> dict[str, Any]:
|
|
2353
2452
|
converted = {}
|
|
2354
2453
|
for attr, value in list(data.items()):
|
|
2355
2454
|
newAttr, newValue = convertFontInfoValueForAttributeFromVersion2ToVersion1(
|
|
@@ -2370,16 +2469,16 @@ def _convertFontInfoDataVersion2ToVersion1(data):
|
|
|
2370
2469
|
|
|
2371
2470
|
# 2 <-> 3
|
|
2372
2471
|
|
|
2373
|
-
_ufo2To3NonNegativeInt = {
|
|
2472
|
+
_ufo2To3NonNegativeInt: set[str] = {
|
|
2374
2473
|
"versionMinor",
|
|
2375
2474
|
"openTypeHeadLowestRecPPEM",
|
|
2376
2475
|
"openTypeOS2WinAscent",
|
|
2377
2476
|
"openTypeOS2WinDescent",
|
|
2378
2477
|
}
|
|
2379
|
-
_ufo2To3NonNegativeIntOrFloat = {
|
|
2478
|
+
_ufo2To3NonNegativeIntOrFloat: set[str] = {
|
|
2380
2479
|
"unitsPerEm",
|
|
2381
2480
|
}
|
|
2382
|
-
_ufo2To3FloatToInt = {
|
|
2481
|
+
_ufo2To3FloatToInt: set[str] = {
|
|
2383
2482
|
"openTypeHeadLowestRecPPEM",
|
|
2384
2483
|
"openTypeHheaAscender",
|
|
2385
2484
|
"openTypeHheaDescender",
|
|
@@ -2407,7 +2506,9 @@ _ufo2To3FloatToInt = {
|
|
|
2407
2506
|
}
|
|
2408
2507
|
|
|
2409
2508
|
|
|
2410
|
-
def convertFontInfoValueForAttributeFromVersion2ToVersion3(
|
|
2509
|
+
def convertFontInfoValueForAttributeFromVersion2ToVersion3(
|
|
2510
|
+
attr: str, value: Any
|
|
2511
|
+
) -> tuple[str, Any]:
|
|
2411
2512
|
"""
|
|
2412
2513
|
Convert value from version 2 to version 3 format.
|
|
2413
2514
|
Returns the new attribute name and the converted value.
|
|
@@ -2435,7 +2536,9 @@ def convertFontInfoValueForAttributeFromVersion2ToVersion3(attr, value):
|
|
|
2435
2536
|
return attr, value
|
|
2436
2537
|
|
|
2437
2538
|
|
|
2438
|
-
def convertFontInfoValueForAttributeFromVersion3ToVersion2(
|
|
2539
|
+
def convertFontInfoValueForAttributeFromVersion3ToVersion2(
|
|
2540
|
+
attr: str, value: Any
|
|
2541
|
+
) -> tuple[str, Any]:
|
|
2439
2542
|
"""
|
|
2440
2543
|
Convert value from version 3 to version 2 format.
|
|
2441
2544
|
Returns the new attribute name and the converted value.
|
|
@@ -2444,7 +2547,7 @@ def convertFontInfoValueForAttributeFromVersion3ToVersion2(attr, value):
|
|
|
2444
2547
|
return attr, value
|
|
2445
2548
|
|
|
2446
2549
|
|
|
2447
|
-
def _convertFontInfoDataVersion3ToVersion2(data):
|
|
2550
|
+
def _convertFontInfoDataVersion3ToVersion2(data: dict[str, Any]) -> dict[str, Any]:
|
|
2448
2551
|
converted = {}
|
|
2449
2552
|
for attr, value in list(data.items()):
|
|
2450
2553
|
newAttr, newValue = convertFontInfoValueForAttributeFromVersion3ToVersion2(
|
|
@@ -2456,7 +2559,7 @@ def _convertFontInfoDataVersion3ToVersion2(data):
|
|
|
2456
2559
|
return converted
|
|
2457
2560
|
|
|
2458
2561
|
|
|
2459
|
-
def _convertFontInfoDataVersion2ToVersion3(data):
|
|
2562
|
+
def _convertFontInfoDataVersion2ToVersion3(data: dict[str, Any]) -> dict[str, Any]:
|
|
2460
2563
|
converted = {}
|
|
2461
2564
|
for attr, value in list(data.items()):
|
|
2462
2565
|
attr, value = convertFontInfoValueForAttributeFromVersion2ToVersion3(
|