fonttools 4.57.0__cp313-cp313-macosx_10_13_universal2.whl → 4.58.1__cp313-cp313-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/cffLib/__init__.py +61 -26
- fontTools/cffLib/specializer.py +4 -1
- fontTools/cu2qu/cu2qu.c +4578 -4050
- fontTools/cu2qu/cu2qu.cpython-313-darwin.so +0 -0
- fontTools/designspaceLib/statNames.py +14 -7
- fontTools/feaLib/ast.py +12 -9
- fontTools/feaLib/builder.py +75 -49
- fontTools/feaLib/lexer.c +6290 -7116
- fontTools/feaLib/lexer.cpython-313-darwin.so +0 -0
- fontTools/feaLib/parser.py +1 -39
- fontTools/fontBuilder.py +6 -0
- fontTools/merge/cmap.py +33 -1
- fontTools/merge/tables.py +12 -1
- fontTools/misc/bezierTools.c +13668 -15551
- fontTools/misc/bezierTools.cpython-313-darwin.so +0 -0
- fontTools/misc/etree.py +4 -27
- fontTools/misc/loggingTools.py +1 -1
- fontTools/misc/symfont.py +6 -8
- fontTools/mtiLib/__init__.py +1 -3
- fontTools/otlLib/builder.py +359 -145
- fontTools/otlLib/optimize/gpos.py +42 -62
- fontTools/pens/momentsPen.c +4509 -4674
- fontTools/pens/momentsPen.cpython-313-darwin.so +0 -0
- fontTools/pens/pointPen.py +21 -12
- fontTools/pens/t2CharStringPen.py +31 -11
- fontTools/qu2cu/qu2cu.c +5746 -5465
- fontTools/qu2cu/qu2cu.cpython-313-darwin.so +0 -0
- fontTools/subset/__init__.py +12 -1
- fontTools/ttLib/tables/G_V_A_R_.py +5 -0
- fontTools/ttLib/tables/T_S_I__0.py +14 -3
- fontTools/ttLib/tables/T_S_I__5.py +16 -5
- fontTools/ttLib/tables/__init__.py +1 -0
- fontTools/ttLib/tables/_c_v_t.py +2 -0
- fontTools/ttLib/tables/_f_p_g_m.py +3 -1
- fontTools/ttLib/tables/_g_l_y_f.py +2 -6
- fontTools/ttLib/tables/_g_v_a_r.py +58 -15
- fontTools/ttLib/tables/_p_o_s_t.py +5 -2
- fontTools/ttLib/tables/otBase.py +1 -0
- fontTools/ufoLib/__init__.py +3 -3
- fontTools/ufoLib/converters.py +89 -25
- fontTools/ufoLib/errors.py +8 -0
- fontTools/ufoLib/etree.py +1 -1
- fontTools/ufoLib/filenames.py +155 -100
- fontTools/ufoLib/glifLib.py +9 -2
- fontTools/ufoLib/kerning.py +66 -36
- fontTools/ufoLib/utils.py +5 -2
- fontTools/unicodedata/Mirrored.py +446 -0
- fontTools/unicodedata/__init__.py +6 -2
- fontTools/varLib/__init__.py +20 -6
- fontTools/varLib/featureVars.py +13 -7
- fontTools/varLib/hvar.py +1 -1
- fontTools/varLib/instancer/__init__.py +14 -5
- fontTools/varLib/iup.c +6851 -6363
- fontTools/varLib/iup.cpython-313-darwin.so +0 -0
- 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.57.0.dist-info → fonttools-4.58.1.dist-info}/METADATA +64 -11
- {fonttools-4.57.0.dist-info → fonttools-4.58.1.dist-info}/RECORD +67 -63
- {fonttools-4.57.0.dist-info → fonttools-4.58.1.dist-info}/WHEEL +1 -1
- fonttools-4.58.1.dist-info/licenses/LICENSE.external +359 -0
- {fonttools-4.57.0.data → fonttools-4.58.1.data}/data/share/man/man1/ttx.1 +0 -0
- {fonttools-4.57.0.dist-info → fonttools-4.58.1.dist-info}/entry_points.txt +0 -0
- {fonttools-4.57.0.dist-info → fonttools-4.58.1.dist-info}/licenses/LICENSE +0 -0
- {fonttools-4.57.0.dist-info → fonttools-4.58.1.dist-info}/top_level.txt +0 -0
|
Binary file
|
fontTools/subset/__init__.py
CHANGED
|
@@ -16,6 +16,7 @@ from fontTools.subset.cff import *
|
|
|
16
16
|
from fontTools.subset.svg import *
|
|
17
17
|
from fontTools.varLib import varStore, multiVarStore # For monkey-patching
|
|
18
18
|
from fontTools.ttLib.tables._n_a_m_e import NameRecordVisitor
|
|
19
|
+
from fontTools.unicodedata import mirrored
|
|
19
20
|
import sys
|
|
20
21
|
import struct
|
|
21
22
|
import array
|
|
@@ -2870,6 +2871,15 @@ def prune_post_subset(self, font, options):
|
|
|
2870
2871
|
def closure_glyphs(self, s):
|
|
2871
2872
|
tables = [t for t in self.tables if t.isUnicode()]
|
|
2872
2873
|
|
|
2874
|
+
# Closure unicodes, which for now is pulling in bidi mirrored variants
|
|
2875
|
+
if s.options.bidi_closure:
|
|
2876
|
+
additional_unicodes = set()
|
|
2877
|
+
for u in s.unicodes_requested:
|
|
2878
|
+
mirror_u = mirrored(u)
|
|
2879
|
+
if mirror_u is not None:
|
|
2880
|
+
additional_unicodes.add(mirror_u)
|
|
2881
|
+
s.unicodes_requested.update(additional_unicodes)
|
|
2882
|
+
|
|
2873
2883
|
# Close glyphs
|
|
2874
2884
|
for table in tables:
|
|
2875
2885
|
if table.format == 14:
|
|
@@ -3191,6 +3201,7 @@ class Options(object):
|
|
|
3191
3201
|
self.font_number = -1
|
|
3192
3202
|
self.pretty_svg = False
|
|
3193
3203
|
self.lazy = True
|
|
3204
|
+
self.bidi_closure = True
|
|
3194
3205
|
|
|
3195
3206
|
self.set(**kwargs)
|
|
3196
3207
|
|
|
@@ -3746,7 +3757,7 @@ def main(args=None):
|
|
|
3746
3757
|
text += g[7:]
|
|
3747
3758
|
continue
|
|
3748
3759
|
if g.startswith("--text-file="):
|
|
3749
|
-
with open(g[12:], encoding="utf-8") as f:
|
|
3760
|
+
with open(g[12:], encoding="utf-8-sig") as f:
|
|
3750
3761
|
text += f.read().replace("\n", "")
|
|
3751
3762
|
continue
|
|
3752
3763
|
if g.startswith("--unicodes="):
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT)
|
|
2
2
|
tool to store its hinting source data.
|
|
3
3
|
|
|
4
4
|
TSI0 is the index table containing the lengths and offsets for the glyph
|
|
@@ -8,9 +8,13 @@ in the TSI1 table.
|
|
|
8
8
|
See also https://learn.microsoft.com/en-us/typography/tools/vtt/tsi-tables
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
import logging
|
|
12
12
|
import struct
|
|
13
13
|
|
|
14
|
+
from . import DefaultTable
|
|
15
|
+
|
|
16
|
+
log = logging.getLogger(__name__)
|
|
17
|
+
|
|
14
18
|
tsi0Format = ">HHL"
|
|
15
19
|
|
|
16
20
|
|
|
@@ -25,7 +29,14 @@ class table_T_S_I__0(DefaultTable.DefaultTable):
|
|
|
25
29
|
numGlyphs = ttFont["maxp"].numGlyphs
|
|
26
30
|
indices = []
|
|
27
31
|
size = struct.calcsize(tsi0Format)
|
|
28
|
-
|
|
32
|
+
numEntries = len(data) // size
|
|
33
|
+
if numEntries != numGlyphs + 5:
|
|
34
|
+
diff = numEntries - numGlyphs - 5
|
|
35
|
+
log.warning(
|
|
36
|
+
"Number of glyphPrograms differs from the number of glyphs in the font "
|
|
37
|
+
f"by {abs(diff)} ({numEntries - 5} programs vs. {numGlyphs} glyphs)."
|
|
38
|
+
)
|
|
39
|
+
for _ in range(numEntries):
|
|
29
40
|
glyphID, textLength, textOffset = fixlongs(
|
|
30
41
|
*struct.unpack(tsi0Format, data[:size])
|
|
31
42
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT)
|
|
2
2
|
tool to store its hinting source data.
|
|
3
3
|
|
|
4
4
|
TSI5 contains the VTT character groups.
|
|
@@ -6,22 +6,33 @@ TSI5 contains the VTT character groups.
|
|
|
6
6
|
See also https://learn.microsoft.com/en-us/typography/tools/vtt/tsi-tables
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
+
import array
|
|
10
|
+
import logging
|
|
11
|
+
import sys
|
|
12
|
+
|
|
9
13
|
from fontTools.misc.textTools import safeEval
|
|
14
|
+
|
|
10
15
|
from . import DefaultTable
|
|
11
|
-
|
|
12
|
-
|
|
16
|
+
|
|
17
|
+
log = logging.getLogger(__name__)
|
|
13
18
|
|
|
14
19
|
|
|
15
20
|
class table_T_S_I__5(DefaultTable.DefaultTable):
|
|
16
21
|
def decompile(self, data, ttFont):
|
|
17
22
|
numGlyphs = ttFont["maxp"].numGlyphs
|
|
18
|
-
assert len(data) == 2 * numGlyphs
|
|
19
23
|
a = array.array("H")
|
|
20
24
|
a.frombytes(data)
|
|
21
25
|
if sys.byteorder != "big":
|
|
22
26
|
a.byteswap()
|
|
23
27
|
self.glyphGrouping = {}
|
|
24
|
-
|
|
28
|
+
numEntries = len(data) // 2
|
|
29
|
+
if numEntries != numGlyphs:
|
|
30
|
+
diff = numEntries - numGlyphs
|
|
31
|
+
log.warning(
|
|
32
|
+
"Number of entries differs from the number of glyphs in the font "
|
|
33
|
+
f"by {abs(diff)} ({numEntries} entries vs. {numGlyphs} glyphs)."
|
|
34
|
+
)
|
|
35
|
+
for i in range(numEntries):
|
|
25
36
|
self.glyphGrouping[ttFont.getGlyphName(i)] = a[i]
|
|
26
37
|
|
|
27
38
|
def compile(self, ttFont):
|
fontTools/ttLib/tables/_c_v_t.py
CHANGED
|
@@ -20,7 +20,9 @@ class table__f_p_g_m(DefaultTable.DefaultTable):
|
|
|
20
20
|
self.program = program
|
|
21
21
|
|
|
22
22
|
def compile(self, ttFont):
|
|
23
|
-
|
|
23
|
+
if hasattr(self, "program"):
|
|
24
|
+
return self.program.getBytecode()
|
|
25
|
+
return b""
|
|
24
26
|
|
|
25
27
|
def toXML(self, writer, ttFont):
|
|
26
28
|
self.program.toXML(writer, ttFont)
|
|
@@ -1225,7 +1225,7 @@ class Glyph(object):
|
|
|
1225
1225
|
if boundsDone is not None:
|
|
1226
1226
|
boundsDone.add(glyphName)
|
|
1227
1227
|
# empty components shouldn't update the bounds of the parent glyph
|
|
1228
|
-
if g.
|
|
1228
|
+
if g.yMin == g.yMax and g.xMin == g.xMax:
|
|
1229
1229
|
continue
|
|
1230
1230
|
|
|
1231
1231
|
x, y = compo.x, compo.y
|
|
@@ -1285,11 +1285,7 @@ class Glyph(object):
|
|
|
1285
1285
|
# however, if the referenced component glyph is another composite, we
|
|
1286
1286
|
# must not round here but only at the end, after all the nested
|
|
1287
1287
|
# transforms have been applied, or else rounding errors will compound.
|
|
1288
|
-
if
|
|
1289
|
-
round is not noRound
|
|
1290
|
-
and g.numberOfContours > 0
|
|
1291
|
-
and not compo._hasOnlyIntegerTranslate()
|
|
1292
|
-
):
|
|
1288
|
+
if round is not noRound and g.numberOfContours > 0:
|
|
1293
1289
|
coordinates.toInt(round=round)
|
|
1294
1290
|
if hasattr(compo, "firstPt"):
|
|
1295
1291
|
# component uses two reference points: we apply the transform _before_
|
|
@@ -24,19 +24,24 @@ log = logging.getLogger(__name__)
|
|
|
24
24
|
# FreeType2 source code for parsing 'gvar':
|
|
25
25
|
# http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/src/truetype/ttgxvar.c
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
GVAR_HEADER_FORMAT_HEAD = """
|
|
28
28
|
> # big endian
|
|
29
29
|
version: H
|
|
30
30
|
reserved: H
|
|
31
31
|
axisCount: H
|
|
32
32
|
sharedTupleCount: H
|
|
33
33
|
offsetToSharedTuples: I
|
|
34
|
-
|
|
34
|
+
"""
|
|
35
|
+
# In between the HEAD and TAIL lies the glyphCount, which is
|
|
36
|
+
# of different size: 2 bytes for gvar, and 3 bytes for GVAR.
|
|
37
|
+
GVAR_HEADER_FORMAT_TAIL = """
|
|
38
|
+
> # big endian
|
|
35
39
|
flags: H
|
|
36
40
|
offsetToGlyphVariationData: I
|
|
37
41
|
"""
|
|
38
42
|
|
|
39
|
-
|
|
43
|
+
GVAR_HEADER_SIZE_HEAD = sstruct.calcsize(GVAR_HEADER_FORMAT_HEAD)
|
|
44
|
+
GVAR_HEADER_SIZE_TAIL = sstruct.calcsize(GVAR_HEADER_FORMAT_TAIL)
|
|
40
45
|
|
|
41
46
|
|
|
42
47
|
class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
@@ -51,6 +56,7 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
|
51
56
|
"""
|
|
52
57
|
|
|
53
58
|
dependencies = ["fvar", "glyf"]
|
|
59
|
+
gid_size = 2
|
|
54
60
|
|
|
55
61
|
def __init__(self, tag=None):
|
|
56
62
|
DefaultTable.DefaultTable.__init__(self, tag)
|
|
@@ -74,20 +80,25 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
|
74
80
|
offsets.append(offset)
|
|
75
81
|
compiledOffsets, tableFormat = self.compileOffsets_(offsets)
|
|
76
82
|
|
|
83
|
+
GVAR_HEADER_SIZE = GVAR_HEADER_SIZE_HEAD + self.gid_size + GVAR_HEADER_SIZE_TAIL
|
|
77
84
|
header = {}
|
|
78
85
|
header["version"] = self.version
|
|
79
86
|
header["reserved"] = self.reserved
|
|
80
87
|
header["axisCount"] = len(axisTags)
|
|
81
88
|
header["sharedTupleCount"] = len(sharedTuples)
|
|
82
89
|
header["offsetToSharedTuples"] = GVAR_HEADER_SIZE + len(compiledOffsets)
|
|
83
|
-
header["glyphCount"] = len(compiledGlyphs)
|
|
84
90
|
header["flags"] = tableFormat
|
|
85
91
|
header["offsetToGlyphVariationData"] = (
|
|
86
92
|
header["offsetToSharedTuples"] + sharedTupleSize
|
|
87
93
|
)
|
|
88
|
-
compiledHeader = sstruct.pack(GVAR_HEADER_FORMAT, header)
|
|
89
94
|
|
|
90
|
-
result = [
|
|
95
|
+
result = [
|
|
96
|
+
sstruct.pack(GVAR_HEADER_FORMAT_HEAD, header),
|
|
97
|
+
len(compiledGlyphs).to_bytes(self.gid_size, "big"),
|
|
98
|
+
sstruct.pack(GVAR_HEADER_FORMAT_TAIL, header),
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
result.append(compiledOffsets)
|
|
91
102
|
result.extend(sharedTuples)
|
|
92
103
|
result.extend(compiledGlyphs)
|
|
93
104
|
return b"".join(result)
|
|
@@ -104,6 +115,7 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
|
104
115
|
pointCountUnused = 0 # pointCount is actually unused by compileGlyph
|
|
105
116
|
result.append(
|
|
106
117
|
compileGlyph_(
|
|
118
|
+
self.gid_size,
|
|
107
119
|
variations,
|
|
108
120
|
pointCountUnused,
|
|
109
121
|
axisTags,
|
|
@@ -116,7 +128,19 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
|
116
128
|
def decompile(self, data, ttFont):
|
|
117
129
|
axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
|
|
118
130
|
glyphs = ttFont.getGlyphOrder()
|
|
119
|
-
|
|
131
|
+
|
|
132
|
+
# Parse the header
|
|
133
|
+
GVAR_HEADER_SIZE = GVAR_HEADER_SIZE_HEAD + self.gid_size + GVAR_HEADER_SIZE_TAIL
|
|
134
|
+
sstruct.unpack(GVAR_HEADER_FORMAT_HEAD, data[:GVAR_HEADER_SIZE_HEAD], self)
|
|
135
|
+
self.glyphCount = int.from_bytes(
|
|
136
|
+
data[GVAR_HEADER_SIZE_HEAD : GVAR_HEADER_SIZE_HEAD + self.gid_size], "big"
|
|
137
|
+
)
|
|
138
|
+
sstruct.unpack(
|
|
139
|
+
GVAR_HEADER_FORMAT_TAIL,
|
|
140
|
+
data[GVAR_HEADER_SIZE_HEAD + self.gid_size : GVAR_HEADER_SIZE],
|
|
141
|
+
self,
|
|
142
|
+
)
|
|
143
|
+
|
|
120
144
|
assert len(glyphs) == self.glyphCount
|
|
121
145
|
assert len(axisTags) == self.axisCount
|
|
122
146
|
sharedCoords = tv.decompileSharedTuples(
|
|
@@ -146,7 +170,7 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
|
146
170
|
glyph = glyf[glyphName]
|
|
147
171
|
numPointsInGlyph = self.getNumPoints_(glyph)
|
|
148
172
|
return decompileGlyph_(
|
|
149
|
-
numPointsInGlyph, sharedCoords, axisTags, gvarData
|
|
173
|
+
self.gid_size, numPointsInGlyph, sharedCoords, axisTags, gvarData
|
|
150
174
|
)
|
|
151
175
|
|
|
152
176
|
return read_item
|
|
@@ -264,23 +288,42 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
|
264
288
|
|
|
265
289
|
|
|
266
290
|
def compileGlyph_(
|
|
267
|
-
|
|
291
|
+
dataOffsetSize,
|
|
292
|
+
variations,
|
|
293
|
+
pointCount,
|
|
294
|
+
axisTags,
|
|
295
|
+
sharedCoordIndices,
|
|
296
|
+
*,
|
|
297
|
+
optimizeSize=True,
|
|
268
298
|
):
|
|
299
|
+
assert dataOffsetSize in (2, 3)
|
|
269
300
|
tupleVariationCount, tuples, data = tv.compileTupleVariationStore(
|
|
270
301
|
variations, pointCount, axisTags, sharedCoordIndices, optimizeSize=optimizeSize
|
|
271
302
|
)
|
|
272
303
|
if tupleVariationCount == 0:
|
|
273
304
|
return b""
|
|
274
|
-
|
|
275
|
-
|
|
305
|
+
|
|
306
|
+
offsetToData = 2 + dataOffsetSize + len(tuples)
|
|
307
|
+
|
|
308
|
+
result = [
|
|
309
|
+
tupleVariationCount.to_bytes(2, "big"),
|
|
310
|
+
offsetToData.to_bytes(dataOffsetSize, "big"),
|
|
311
|
+
tuples,
|
|
312
|
+
data,
|
|
313
|
+
]
|
|
314
|
+
if (offsetToData + len(data)) % 2 != 0:
|
|
276
315
|
result.append(b"\0") # padding
|
|
277
316
|
return b"".join(result)
|
|
278
317
|
|
|
279
318
|
|
|
280
|
-
def decompileGlyph_(pointCount, sharedTuples, axisTags, data):
|
|
281
|
-
|
|
319
|
+
def decompileGlyph_(dataOffsetSize, pointCount, sharedTuples, axisTags, data):
|
|
320
|
+
assert dataOffsetSize in (2, 3)
|
|
321
|
+
if len(data) < 2 + dataOffsetSize:
|
|
282
322
|
return []
|
|
283
|
-
|
|
323
|
+
|
|
324
|
+
tupleVariationCount = int.from_bytes(data[:2], "big")
|
|
325
|
+
offsetToData = int.from_bytes(data[2 : 2 + dataOffsetSize], "big")
|
|
326
|
+
|
|
284
327
|
dataPos = offsetToData
|
|
285
328
|
return tv.decompileTupleVariationStore(
|
|
286
329
|
"gvar",
|
|
@@ -289,6 +332,6 @@ def decompileGlyph_(pointCount, sharedTuples, axisTags, data):
|
|
|
289
332
|
pointCount,
|
|
290
333
|
sharedTuples,
|
|
291
334
|
data,
|
|
292
|
-
|
|
335
|
+
2 + dataOffsetSize,
|
|
293
336
|
offsetToData,
|
|
294
337
|
)
|
|
@@ -122,13 +122,16 @@ class table__p_o_s_t(DefaultTable.DefaultTable):
|
|
|
122
122
|
glyphName = psName = self.glyphOrder[i]
|
|
123
123
|
if glyphName == "":
|
|
124
124
|
glyphName = "glyph%.5d" % i
|
|
125
|
+
|
|
125
126
|
if glyphName in allNames:
|
|
126
127
|
# make up a new glyphName that's unique
|
|
127
128
|
n = allNames[glyphName]
|
|
128
|
-
|
|
129
|
+
# check if the exists in any of the seen names or later ones
|
|
130
|
+
names = set(allNames.keys()) | set(self.glyphOrder)
|
|
131
|
+
while (glyphName + "." + str(n)) in names:
|
|
129
132
|
n += 1
|
|
130
133
|
allNames[glyphName] = n + 1
|
|
131
|
-
glyphName = glyphName + "
|
|
134
|
+
glyphName = glyphName + "." + str(n)
|
|
132
135
|
|
|
133
136
|
self.glyphOrder[i] = glyphName
|
|
134
137
|
allNames[glyphName] = 1
|
fontTools/ttLib/tables/otBase.py
CHANGED
fontTools/ufoLib/__init__.py
CHANGED
|
@@ -204,7 +204,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
204
204
|
"""Read the various components of a .ufo.
|
|
205
205
|
|
|
206
206
|
Attributes:
|
|
207
|
-
path: An
|
|
207
|
+
path: An :class:`os.PathLike` object pointing to the .ufo.
|
|
208
208
|
validate: A boolean indicating if the data read should be
|
|
209
209
|
validated. Defaults to `True`.
|
|
210
210
|
|
|
@@ -654,7 +654,7 @@ class UFOReader(_UFOBaseIO):
|
|
|
654
654
|
The returned string is empty if the file is missing.
|
|
655
655
|
"""
|
|
656
656
|
try:
|
|
657
|
-
with self.fs.open(FEATURES_FILENAME, "r", encoding="utf-8") as f:
|
|
657
|
+
with self.fs.open(FEATURES_FILENAME, "r", encoding="utf-8-sig") as f:
|
|
658
658
|
return f.read()
|
|
659
659
|
except fs.errors.ResourceNotFound:
|
|
660
660
|
return ""
|
|
@@ -891,7 +891,7 @@ class UFOWriter(UFOReader):
|
|
|
891
891
|
"""Write the various components of a .ufo.
|
|
892
892
|
|
|
893
893
|
Attributes:
|
|
894
|
-
path: An
|
|
894
|
+
path: An :class:`os.PathLike` object pointing to the .ufo.
|
|
895
895
|
formatVersion: the UFO format version as a tuple of integers (major, minor),
|
|
896
896
|
or as a single integer for the major digit only (minor is implied to be 0).
|
|
897
897
|
By default, the latest formatVersion will be used; currently it is 3.0,
|
fontTools/ufoLib/converters.py
CHANGED
|
@@ -1,11 +1,33 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Functions for converting UFO1 or UFO2 files into UFO3 format.
|
|
3
|
+
|
|
4
|
+
Currently provides functionality for converting kerning rules
|
|
5
|
+
and kerning groups. Conversion is only supported _from_ UFO1
|
|
6
|
+
or UFO2, and _to_ UFO3.
|
|
3
7
|
"""
|
|
4
8
|
|
|
5
9
|
# adapted from the UFO spec
|
|
6
10
|
|
|
7
11
|
|
|
8
12
|
def convertUFO1OrUFO2KerningToUFO3Kerning(kerning, groups, glyphSet=()):
|
|
13
|
+
"""Convert kerning data in UFO1 or UFO2 syntax into UFO3 syntax.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
kerning:
|
|
17
|
+
A dictionary containing the kerning rules defined in
|
|
18
|
+
the UFO font, as used in :class:`.UFOReader` objects.
|
|
19
|
+
groups:
|
|
20
|
+
A dictionary containing the groups defined in the UFO
|
|
21
|
+
font, as used in :class:`.UFOReader` objects.
|
|
22
|
+
glyphSet:
|
|
23
|
+
Optional; a set of glyph objects to skip (default: None).
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
1. A dictionary representing the converted kerning data.
|
|
27
|
+
2. A copy of the groups dictionary, with all groups renamed to UFO3 syntax.
|
|
28
|
+
3. A dictionary containing the mapping of old group names to new group names.
|
|
29
|
+
|
|
30
|
+
"""
|
|
9
31
|
# gather known kerning groups based on the prefixes
|
|
10
32
|
firstReferencedGroups, secondReferencedGroups = findKnownKerningGroups(groups)
|
|
11
33
|
# Make lists of groups referenced in kerning pairs.
|
|
@@ -63,35 +85,54 @@ def convertUFO1OrUFO2KerningToUFO3Kerning(kerning, groups, glyphSet=()):
|
|
|
63
85
|
|
|
64
86
|
|
|
65
87
|
def findKnownKerningGroups(groups):
|
|
66
|
-
"""
|
|
67
|
-
|
|
68
|
-
In some cases not all kerning groups will be referenced
|
|
69
|
-
by the kerning pairs. The algorithm for locating
|
|
70
|
-
in convertUFO1OrUFO2KerningToUFO3Kerning will
|
|
71
|
-
unreferenced groups. By scanning for known prefixes
|
|
88
|
+
"""Find all kerning groups in a UFO1 or UFO2 font that use known prefixes.
|
|
89
|
+
|
|
90
|
+
In some cases, not all kerning groups will be referenced
|
|
91
|
+
by the kerning pairs in a UFO. The algorithm for locating
|
|
92
|
+
groups in :func:`convertUFO1OrUFO2KerningToUFO3Kerning` will
|
|
93
|
+
miss these unreferenced groups. By scanning for known prefixes,
|
|
72
94
|
this function will catch all of the prefixed groups.
|
|
73
95
|
|
|
74
|
-
|
|
96
|
+
The prefixes and sides by this function are:
|
|
97
|
+
|
|
75
98
|
@MMK_L_ - side 1
|
|
76
99
|
@MMK_R_ - side 2
|
|
77
100
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
101
|
+
as defined in the UFO1 specification.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
groups:
|
|
105
|
+
A dictionary containing the groups defined in the UFO
|
|
106
|
+
font, as read by :class:`.UFOReader`.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
Two sets; the first containing the names of all
|
|
110
|
+
first-side kerning groups identified in the ``groups``
|
|
111
|
+
dictionary, and the second containing the names of all
|
|
112
|
+
second-side kerning groups identified.
|
|
113
|
+
|
|
114
|
+
"First-side" and "second-side" are with respect to the
|
|
115
|
+
writing direction of the script.
|
|
116
|
+
|
|
117
|
+
Example::
|
|
118
|
+
|
|
119
|
+
>>> testGroups = {
|
|
120
|
+
... "@MMK_L_1" : None,
|
|
121
|
+
... "@MMK_L_2" : None,
|
|
122
|
+
... "@MMK_L_3" : None,
|
|
123
|
+
... "@MMK_R_1" : None,
|
|
124
|
+
... "@MMK_R_2" : None,
|
|
125
|
+
... "@MMK_R_3" : None,
|
|
126
|
+
... "@MMK_l_1" : None,
|
|
127
|
+
... "@MMK_r_1" : None,
|
|
128
|
+
... "@MMK_X_1" : None,
|
|
129
|
+
... "foo" : None,
|
|
130
|
+
... }
|
|
131
|
+
>>> first, second = findKnownKerningGroups(testGroups)
|
|
132
|
+
>>> sorted(first) == ['@MMK_L_1', '@MMK_L_2', '@MMK_L_3']
|
|
133
|
+
True
|
|
134
|
+
>>> sorted(second) == ['@MMK_R_1', '@MMK_R_2', '@MMK_R_3']
|
|
135
|
+
True
|
|
95
136
|
"""
|
|
96
137
|
knownFirstGroupPrefixes = ["@MMK_L_"]
|
|
97
138
|
knownSecondGroupPrefixes = ["@MMK_R_"]
|
|
@@ -110,6 +151,27 @@ def findKnownKerningGroups(groups):
|
|
|
110
151
|
|
|
111
152
|
|
|
112
153
|
def makeUniqueGroupName(name, groupNames, counter=0):
|
|
154
|
+
"""Make a kerning group name that will be unique within the set of group names.
|
|
155
|
+
|
|
156
|
+
If the requested kerning group name already exists within the set, this
|
|
157
|
+
will return a new name by adding an incremented counter to the end
|
|
158
|
+
of the requested name.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
name:
|
|
162
|
+
The requested kerning group name.
|
|
163
|
+
groupNames:
|
|
164
|
+
A list of the existing kerning group names.
|
|
165
|
+
counter:
|
|
166
|
+
Optional; a counter of group names already seen (default: 0). If
|
|
167
|
+
:attr:`.counter` is not provided, the function will recurse,
|
|
168
|
+
incrementing the value of :attr:`.counter` until it finds the
|
|
169
|
+
first unused ``name+counter`` combination, and return that result.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
A unique kerning group name composed of the requested name suffixed
|
|
173
|
+
by the smallest available integer counter.
|
|
174
|
+
"""
|
|
113
175
|
# Add a number to the name if the counter is higher than zero.
|
|
114
176
|
newName = name
|
|
115
177
|
if counter > 0:
|
|
@@ -123,6 +185,8 @@ def makeUniqueGroupName(name, groupNames, counter=0):
|
|
|
123
185
|
|
|
124
186
|
def test():
|
|
125
187
|
"""
|
|
188
|
+
Tests for :func:`.convertUFO1OrUFO2KerningToUFO3Kerning`.
|
|
189
|
+
|
|
126
190
|
No known prefixes.
|
|
127
191
|
|
|
128
192
|
>>> testKerning = {
|
fontTools/ufoLib/errors.py
CHANGED
|
@@ -10,6 +10,14 @@ class UnsupportedUFOFormat(UFOLibError):
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class GlifLibError(UFOLibError):
|
|
13
|
+
"""An error raised by glifLib.
|
|
14
|
+
|
|
15
|
+
This class is a loose backport of PEP 678, adding a :attr:`.note`
|
|
16
|
+
attribute that can hold additional context for errors encountered.
|
|
17
|
+
|
|
18
|
+
It will be maintained until only Python 3.11-and-later are supported.
|
|
19
|
+
"""
|
|
20
|
+
|
|
13
21
|
def _add_note(self, note: str) -> None:
|
|
14
22
|
# Loose backport of PEP 678 until we only support Python 3.11+, used for
|
|
15
23
|
# adding additional context to errors.
|
fontTools/ufoLib/etree.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""DEPRECATED - This module is kept here only as a backward compatibility shim
|
|
2
|
-
for the old ufoLib.etree module, which was moved to fontTools.misc.etree
|
|
2
|
+
for the old ufoLib.etree module, which was moved to :mod:`fontTools.misc.etree`.
|
|
3
3
|
Please use the latter instead.
|
|
4
4
|
"""
|
|
5
5
|
|