fonttools 4.58.0__py3-none-any.whl → 4.58.2__py3-none-any.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/specializer.py +4 -1
- fontTools/feaLib/ast.py +1 -72
- fontTools/feaLib/builder.py +55 -45
- fontTools/merge/cmap.py +33 -1
- fontTools/merge/tables.py +12 -1
- fontTools/misc/loggingTools.py +1 -1
- fontTools/misc/symfont.py +6 -8
- fontTools/mtiLib/__init__.py +1 -1
- fontTools/otlLib/builder.py +164 -0
- fontTools/pens/t2CharStringPen.py +31 -11
- fontTools/subset/__init__.py +82 -2
- fontTools/ttLib/reorderGlyphs.py +8 -7
- fontTools/ufoLib/__init__.py +1 -1
- fontTools/varLib/__init__.py +18 -6
- fontTools/varLib/featureVars.py +13 -7
- fontTools/varLib/hvar.py +1 -1
- fontTools/varLib/instancer/__init__.py +14 -5
- {fonttools-4.58.0.dist-info → fonttools-4.58.2.dist-info}/METADATA +26 -1
- {fonttools-4.58.0.dist-info → fonttools-4.58.2.dist-info}/RECORD +26 -26
- {fonttools-4.58.0.dist-info → fonttools-4.58.2.dist-info}/WHEEL +1 -1
- {fonttools-4.58.0.data → fonttools-4.58.2.data}/data/share/man/man1/ttx.1 +0 -0
- {fonttools-4.58.0.dist-info → fonttools-4.58.2.dist-info}/entry_points.txt +0 -0
- {fonttools-4.58.0.dist-info → fonttools-4.58.2.dist-info}/licenses/LICENSE +0 -0
- {fonttools-4.58.0.dist-info → fonttools-4.58.2.dist-info}/licenses/LICENSE.external +0 -0
- {fonttools-4.58.0.dist-info → fonttools-4.58.2.dist-info}/top_level.txt +0 -0
fontTools/__init__.py
CHANGED
fontTools/cffLib/specializer.py
CHANGED
|
@@ -580,7 +580,10 @@ def specializeCommands(
|
|
|
580
580
|
for i in range(len(commands) - 1, 0, -1):
|
|
581
581
|
if "rmoveto" == commands[i][0] == commands[i - 1][0]:
|
|
582
582
|
v1, v2 = commands[i - 1][1], commands[i][1]
|
|
583
|
-
commands[i - 1] = (
|
|
583
|
+
commands[i - 1] = (
|
|
584
|
+
"rmoveto",
|
|
585
|
+
[_addArgs(v1[0], v2[0]), _addArgs(v1[1], v2[1])],
|
|
586
|
+
)
|
|
584
587
|
del commands[i]
|
|
585
588
|
|
|
586
589
|
# 2. Specialize rmoveto/rlineto/rrcurveto operators into horizontal/vertical variants.
|
fontTools/feaLib/ast.py
CHANGED
|
@@ -337,76 +337,6 @@ class AnonymousBlock(Statement):
|
|
|
337
337
|
return res
|
|
338
338
|
|
|
339
339
|
|
|
340
|
-
def _upgrade_mixed_subst_statements(statements):
|
|
341
|
-
# https://github.com/fonttools/fonttools/issues/612
|
|
342
|
-
# A multiple substitution may have a single destination, in which case
|
|
343
|
-
# it will look just like a single substitution. So if there are both
|
|
344
|
-
# multiple and single substitutions, upgrade all the single ones to
|
|
345
|
-
# multiple substitutions. Similarly, a ligature substitution may have a
|
|
346
|
-
# single source glyph, so if there are both ligature and single
|
|
347
|
-
# substitutions, upgrade all the single ones to ligature substitutions.
|
|
348
|
-
|
|
349
|
-
has_single = False
|
|
350
|
-
has_multiple = False
|
|
351
|
-
has_ligature = False
|
|
352
|
-
for s in statements:
|
|
353
|
-
if isinstance(s, SingleSubstStatement):
|
|
354
|
-
has_single = not any([s.prefix, s.suffix, s.forceChain])
|
|
355
|
-
elif isinstance(s, MultipleSubstStatement):
|
|
356
|
-
has_multiple = not any([s.prefix, s.suffix, s.forceChain])
|
|
357
|
-
elif isinstance(s, LigatureSubstStatement):
|
|
358
|
-
has_ligature = not any([s.prefix, s.suffix, s.forceChain])
|
|
359
|
-
|
|
360
|
-
to_multiple = False
|
|
361
|
-
to_ligature = False
|
|
362
|
-
|
|
363
|
-
# If we have mixed single and multiple substitutions,
|
|
364
|
-
# upgrade all single substitutions to multiple substitutions.
|
|
365
|
-
if has_single and has_multiple and not has_ligature:
|
|
366
|
-
to_multiple = True
|
|
367
|
-
|
|
368
|
-
# If we have mixed single and ligature substitutions,
|
|
369
|
-
# upgrade all single substitutions to ligature substitutions.
|
|
370
|
-
elif has_single and has_ligature and not has_multiple:
|
|
371
|
-
to_ligature = True
|
|
372
|
-
|
|
373
|
-
if to_multiple or to_ligature:
|
|
374
|
-
ret = []
|
|
375
|
-
for s in statements:
|
|
376
|
-
if isinstance(s, SingleSubstStatement):
|
|
377
|
-
glyphs = s.glyphs[0].glyphSet()
|
|
378
|
-
replacements = s.replacements[0].glyphSet()
|
|
379
|
-
if len(replacements) == 1:
|
|
380
|
-
replacements *= len(glyphs)
|
|
381
|
-
for glyph, replacement in zip(glyphs, replacements):
|
|
382
|
-
if to_multiple:
|
|
383
|
-
ret.append(
|
|
384
|
-
MultipleSubstStatement(
|
|
385
|
-
s.prefix,
|
|
386
|
-
glyph,
|
|
387
|
-
s.suffix,
|
|
388
|
-
[replacement],
|
|
389
|
-
s.forceChain,
|
|
390
|
-
location=s.location,
|
|
391
|
-
)
|
|
392
|
-
)
|
|
393
|
-
elif to_ligature:
|
|
394
|
-
ret.append(
|
|
395
|
-
LigatureSubstStatement(
|
|
396
|
-
s.prefix,
|
|
397
|
-
[GlyphName(glyph)],
|
|
398
|
-
s.suffix,
|
|
399
|
-
replacement,
|
|
400
|
-
s.forceChain,
|
|
401
|
-
location=s.location,
|
|
402
|
-
)
|
|
403
|
-
)
|
|
404
|
-
else:
|
|
405
|
-
ret.append(s)
|
|
406
|
-
return ret
|
|
407
|
-
return statements
|
|
408
|
-
|
|
409
|
-
|
|
410
340
|
class Block(Statement):
|
|
411
341
|
"""A block of statements: feature, lookup, etc."""
|
|
412
342
|
|
|
@@ -418,8 +348,7 @@ class Block(Statement):
|
|
|
418
348
|
"""When handed a 'builder' object of comparable interface to
|
|
419
349
|
:class:`fontTools.feaLib.builder`, walks the statements in this
|
|
420
350
|
block, calling the builder callbacks."""
|
|
421
|
-
|
|
422
|
-
for s in statements:
|
|
351
|
+
for s in self.statements:
|
|
423
352
|
s.build(builder)
|
|
424
353
|
|
|
425
354
|
def asFea(self, indent=""):
|
fontTools/feaLib/builder.py
CHANGED
|
@@ -29,6 +29,7 @@ from fontTools.otlLib.builder import (
|
|
|
29
29
|
PairPosBuilder,
|
|
30
30
|
SinglePosBuilder,
|
|
31
31
|
ChainContextualRule,
|
|
32
|
+
AnySubstBuilder,
|
|
32
33
|
)
|
|
33
34
|
from fontTools.otlLib.error import OpenTypeLibError
|
|
34
35
|
from fontTools.varLib.varStore import OnlineVarStoreBuilder
|
|
@@ -866,13 +867,22 @@ class Builder(object):
|
|
|
866
867
|
for lookup in self.lookups_:
|
|
867
868
|
if lookup.table != tag:
|
|
868
869
|
continue
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
870
|
+
name = self.get_lookup_name_(lookup)
|
|
871
|
+
resolved = lookup.promote_lookup_type(is_named_lookup=name is not None)
|
|
872
|
+
if resolved is None:
|
|
873
|
+
raise FeatureLibError(
|
|
874
|
+
"Within a named lookup block, all rules must be of "
|
|
875
|
+
"the same lookup type and flag",
|
|
876
|
+
lookup.location,
|
|
877
|
+
)
|
|
878
|
+
for l in resolved:
|
|
879
|
+
lookup.lookup_index = len(lookups)
|
|
880
|
+
self.lookup_locations[tag][str(lookup.lookup_index)] = LookupDebugInfo(
|
|
881
|
+
location=str(lookup.location),
|
|
882
|
+
name=name,
|
|
883
|
+
feature=None,
|
|
884
|
+
)
|
|
885
|
+
lookups.append(l)
|
|
876
886
|
otLookups = []
|
|
877
887
|
for l in lookups:
|
|
878
888
|
try:
|
|
@@ -1294,6 +1304,24 @@ class Builder(object):
|
|
|
1294
1304
|
|
|
1295
1305
|
# GSUB rules
|
|
1296
1306
|
|
|
1307
|
+
def add_any_subst_(self, location, mapping):
|
|
1308
|
+
lookup = self.get_lookup_(location, AnySubstBuilder)
|
|
1309
|
+
for key, value in mapping.items():
|
|
1310
|
+
if key in lookup.mapping:
|
|
1311
|
+
if value == lookup.mapping[key]:
|
|
1312
|
+
log.info(
|
|
1313
|
+
'Removing duplicate substitution from "%s" to "%s" at %s',
|
|
1314
|
+
", ".join(key),
|
|
1315
|
+
", ".join(value),
|
|
1316
|
+
location,
|
|
1317
|
+
)
|
|
1318
|
+
else:
|
|
1319
|
+
raise FeatureLibError(
|
|
1320
|
+
'Already defined substitution for "%s"' % ", ".join(key),
|
|
1321
|
+
location,
|
|
1322
|
+
)
|
|
1323
|
+
lookup.mapping[key] = value
|
|
1324
|
+
|
|
1297
1325
|
# GSUB 1
|
|
1298
1326
|
def add_single_subst(self, location, prefix, suffix, mapping, forceChain):
|
|
1299
1327
|
if self.cur_feature_name_ == "aalt":
|
|
@@ -1305,24 +1333,11 @@ class Builder(object):
|
|
|
1305
1333
|
if prefix or suffix or forceChain:
|
|
1306
1334
|
self.add_single_subst_chained_(location, prefix, suffix, mapping)
|
|
1307
1335
|
return
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
"Removing duplicate single substitution from glyph"
|
|
1314
|
-
' "%s" to "%s" at %s',
|
|
1315
|
-
from_glyph,
|
|
1316
|
-
to_glyph,
|
|
1317
|
-
location,
|
|
1318
|
-
)
|
|
1319
|
-
else:
|
|
1320
|
-
raise FeatureLibError(
|
|
1321
|
-
'Already defined rule for replacing glyph "%s" by "%s"'
|
|
1322
|
-
% (from_glyph, lookup.mapping[from_glyph]),
|
|
1323
|
-
location,
|
|
1324
|
-
)
|
|
1325
|
-
lookup.mapping[from_glyph] = to_glyph
|
|
1336
|
+
|
|
1337
|
+
self.add_any_subst_(
|
|
1338
|
+
location,
|
|
1339
|
+
{(key,): (value,) for key, value in mapping.items()},
|
|
1340
|
+
)
|
|
1326
1341
|
|
|
1327
1342
|
# GSUB 2
|
|
1328
1343
|
def add_multiple_subst(
|
|
@@ -1331,21 +1346,10 @@ class Builder(object):
|
|
|
1331
1346
|
if prefix or suffix or forceChain:
|
|
1332
1347
|
self.add_multi_subst_chained_(location, prefix, glyph, suffix, replacements)
|
|
1333
1348
|
return
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
"Removing duplicate multiple substitution from glyph"
|
|
1339
|
-
' "%s" to %s%s',
|
|
1340
|
-
glyph,
|
|
1341
|
-
replacements,
|
|
1342
|
-
f" at {location}" if location else "",
|
|
1343
|
-
)
|
|
1344
|
-
else:
|
|
1345
|
-
raise FeatureLibError(
|
|
1346
|
-
'Already defined substitution for glyph "%s"' % glyph, location
|
|
1347
|
-
)
|
|
1348
|
-
lookup.mapping[glyph] = replacements
|
|
1349
|
+
self.add_any_subst_(
|
|
1350
|
+
location,
|
|
1351
|
+
{(glyph,): tuple(replacements)},
|
|
1352
|
+
)
|
|
1349
1353
|
|
|
1350
1354
|
# GSUB 3
|
|
1351
1355
|
def add_alternate_subst(self, location, prefix, glyph, suffix, replacement):
|
|
@@ -1375,9 +1379,6 @@ class Builder(object):
|
|
|
1375
1379
|
location, prefix, glyphs, suffix, replacement
|
|
1376
1380
|
)
|
|
1377
1381
|
return
|
|
1378
|
-
else:
|
|
1379
|
-
lookup = self.get_lookup_(location, LigatureSubstBuilder)
|
|
1380
|
-
|
|
1381
1382
|
if not all(glyphs):
|
|
1382
1383
|
raise FeatureLibError("Empty glyph class in substitution", location)
|
|
1383
1384
|
|
|
@@ -1386,8 +1387,10 @@ class Builder(object):
|
|
|
1386
1387
|
# substitutions to be specified on target sequences that contain
|
|
1387
1388
|
# glyph classes, the implementation software will enumerate
|
|
1388
1389
|
# all specific glyph sequences if glyph classes are detected"
|
|
1389
|
-
|
|
1390
|
-
|
|
1390
|
+
self.add_any_subst_(
|
|
1391
|
+
location,
|
|
1392
|
+
{g: (replacement,) for g in itertools.product(*glyphs)},
|
|
1393
|
+
)
|
|
1391
1394
|
|
|
1392
1395
|
# GSUB 5/6
|
|
1393
1396
|
def add_chain_context_subst(self, location, prefix, glyphs, suffix, lookups):
|
|
@@ -1445,6 +1448,13 @@ class Builder(object):
|
|
|
1445
1448
|
sub = self.get_chained_lookup_(location, LigatureSubstBuilder)
|
|
1446
1449
|
|
|
1447
1450
|
for g in itertools.product(*glyphs):
|
|
1451
|
+
existing = sub.ligatures.get(g, replacement)
|
|
1452
|
+
if existing != replacement:
|
|
1453
|
+
raise FeatureLibError(
|
|
1454
|
+
f"Conflicting ligature sub rules: '{g}' maps to '{existing}' and '{replacement}'",
|
|
1455
|
+
location,
|
|
1456
|
+
)
|
|
1457
|
+
|
|
1448
1458
|
sub.ligatures[g] = replacement
|
|
1449
1459
|
|
|
1450
1460
|
chain.rules.append(ChainContextualRule(prefix, glyphs, suffix, [sub]))
|
fontTools/merge/cmap.py
CHANGED
|
@@ -54,6 +54,28 @@ def _glyphsAreSame(
|
|
|
54
54
|
return True
|
|
55
55
|
|
|
56
56
|
|
|
57
|
+
def computeMegaUvs(merger, uvsTables):
|
|
58
|
+
"""Returns merged UVS subtable (cmap format=14)."""
|
|
59
|
+
uvsDict = {}
|
|
60
|
+
cmap = merger.cmap
|
|
61
|
+
for table in uvsTables:
|
|
62
|
+
for variationSelector, uvsMapping in table.uvsDict.items():
|
|
63
|
+
if variationSelector not in uvsDict:
|
|
64
|
+
uvsDict[variationSelector] = {}
|
|
65
|
+
for unicodeValue, glyphName in uvsMapping:
|
|
66
|
+
if cmap.get(unicodeValue) == glyphName:
|
|
67
|
+
# this is a default variation
|
|
68
|
+
glyphName = None
|
|
69
|
+
# prefer previous glyph id if both fonts defined UVS
|
|
70
|
+
if unicodeValue not in uvsDict[variationSelector]:
|
|
71
|
+
uvsDict[variationSelector][unicodeValue] = glyphName
|
|
72
|
+
|
|
73
|
+
for variationSelector in uvsDict:
|
|
74
|
+
uvsDict[variationSelector] = [*uvsDict[variationSelector].items()]
|
|
75
|
+
|
|
76
|
+
return uvsDict
|
|
77
|
+
|
|
78
|
+
|
|
57
79
|
# Valid (format, platformID, platEncID) triplets for cmap subtables containing
|
|
58
80
|
# Unicode BMP-only and Unicode Full Repertoire semantics.
|
|
59
81
|
# Cf. OpenType spec for "Platform specific encodings":
|
|
@@ -61,24 +83,29 @@ def _glyphsAreSame(
|
|
|
61
83
|
class _CmapUnicodePlatEncodings:
|
|
62
84
|
BMP = {(4, 3, 1), (4, 0, 3), (4, 0, 4), (4, 0, 6)}
|
|
63
85
|
FullRepertoire = {(12, 3, 10), (12, 0, 4), (12, 0, 6)}
|
|
86
|
+
UVS = {(14, 0, 5)}
|
|
64
87
|
|
|
65
88
|
|
|
66
89
|
def computeMegaCmap(merger, cmapTables):
|
|
67
|
-
"""Sets merger.cmap and merger.
|
|
90
|
+
"""Sets merger.cmap and merger.uvsDict."""
|
|
68
91
|
|
|
69
92
|
# TODO Handle format=14.
|
|
70
93
|
# Only merge format 4 and 12 Unicode subtables, ignores all other subtables
|
|
71
94
|
# If there is a format 12 table for a font, ignore the format 4 table of it
|
|
72
95
|
chosenCmapTables = []
|
|
96
|
+
chosenUvsTables = []
|
|
73
97
|
for fontIdx, table in enumerate(cmapTables):
|
|
74
98
|
format4 = None
|
|
75
99
|
format12 = None
|
|
100
|
+
format14 = None
|
|
76
101
|
for subtable in table.tables:
|
|
77
102
|
properties = (subtable.format, subtable.platformID, subtable.platEncID)
|
|
78
103
|
if properties in _CmapUnicodePlatEncodings.BMP:
|
|
79
104
|
format4 = subtable
|
|
80
105
|
elif properties in _CmapUnicodePlatEncodings.FullRepertoire:
|
|
81
106
|
format12 = subtable
|
|
107
|
+
elif properties in _CmapUnicodePlatEncodings.UVS:
|
|
108
|
+
format14 = subtable
|
|
82
109
|
else:
|
|
83
110
|
log.warning(
|
|
84
111
|
"Dropped cmap subtable from font '%s':\t"
|
|
@@ -93,6 +120,9 @@ def computeMegaCmap(merger, cmapTables):
|
|
|
93
120
|
elif format4 is not None:
|
|
94
121
|
chosenCmapTables.append((format4, fontIdx))
|
|
95
122
|
|
|
123
|
+
if format14 is not None:
|
|
124
|
+
chosenUvsTables.append(format14)
|
|
125
|
+
|
|
96
126
|
# Build the unicode mapping
|
|
97
127
|
merger.cmap = cmap = {}
|
|
98
128
|
fontIndexForGlyph = {}
|
|
@@ -127,6 +157,8 @@ def computeMegaCmap(merger, cmapTables):
|
|
|
127
157
|
"Dropped mapping from codepoint %#06X to glyphId '%s'", uni, gid
|
|
128
158
|
)
|
|
129
159
|
|
|
160
|
+
merger.uvsDict = computeMegaUvs(merger, chosenUvsTables)
|
|
161
|
+
|
|
130
162
|
|
|
131
163
|
def renameCFFCharStrings(merger, glyphOrder, cffTable):
|
|
132
164
|
"""Rename topDictIndex charStrings based on glyphOrder."""
|
fontTools/merge/tables.py
CHANGED
|
@@ -312,7 +312,6 @@ def merge(self, m, tables):
|
|
|
312
312
|
|
|
313
313
|
@add_method(ttLib.getTableClass("cmap"))
|
|
314
314
|
def merge(self, m, tables):
|
|
315
|
-
# TODO Handle format=14.
|
|
316
315
|
if not hasattr(m, "cmap"):
|
|
317
316
|
computeMegaCmap(m, tables)
|
|
318
317
|
cmap = m.cmap
|
|
@@ -336,6 +335,18 @@ def merge(self, m, tables):
|
|
|
336
335
|
cmapTable.cmap = cmapBmpOnly
|
|
337
336
|
# ordered by platform then encoding
|
|
338
337
|
self.tables.insert(0, cmapTable)
|
|
338
|
+
|
|
339
|
+
uvsDict = m.uvsDict
|
|
340
|
+
if uvsDict:
|
|
341
|
+
# format-14
|
|
342
|
+
uvsTable = module.cmap_classes[14](14)
|
|
343
|
+
uvsTable.platformID = 0
|
|
344
|
+
uvsTable.platEncID = 5
|
|
345
|
+
uvsTable.language = 0
|
|
346
|
+
uvsTable.cmap = {}
|
|
347
|
+
uvsTable.uvsDict = uvsDict
|
|
348
|
+
# ordered by platform then encoding
|
|
349
|
+
self.tables.insert(0, uvsTable)
|
|
339
350
|
self.tableVersion = 0
|
|
340
351
|
self.numSubTables = len(self.tables)
|
|
341
352
|
return self
|
fontTools/misc/loggingTools.py
CHANGED
|
@@ -284,7 +284,7 @@ class Timer(object):
|
|
|
284
284
|
"""
|
|
285
285
|
|
|
286
286
|
# timeit.default_timer choses the most accurate clock for each platform
|
|
287
|
-
_time = timeit.default_timer
|
|
287
|
+
_time: Callable[[], float] = staticmethod(timeit.default_timer)
|
|
288
288
|
default_msg = "elapsed time: %(time).3fs"
|
|
289
289
|
default_format = "Took %(time).3fs to %(msg)s"
|
|
290
290
|
|
fontTools/misc/symfont.py
CHANGED
|
@@ -234,11 +234,9 @@ if __name__ == '__main__':
|
|
|
234
234
|
|
|
235
235
|
|
|
236
236
|
if __name__ == "__main__":
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
pen.closePath()
|
|
244
|
-
print(pen.value)
|
|
237
|
+
import sys
|
|
238
|
+
|
|
239
|
+
if sys.argv[1:]:
|
|
240
|
+
penName = sys.argv[1]
|
|
241
|
+
funcs = [(name, eval(f)) for name, f in zip(sys.argv[2::2], sys.argv[3::2])]
|
|
242
|
+
printGreenPen(penName, funcs, file=sys.stdout)
|
fontTools/mtiLib/__init__.py
CHANGED
|
@@ -1375,7 +1375,7 @@ def main(args=None, font=None):
|
|
|
1375
1375
|
|
|
1376
1376
|
for f in args.inputs:
|
|
1377
1377
|
log.debug("Processing %s", f)
|
|
1378
|
-
with open(f, "rt", encoding="utf-8") as f:
|
|
1378
|
+
with open(f, "rt", encoding="utf-8-sig") as f:
|
|
1379
1379
|
table = build(f, font, tableTag=args.tableTag)
|
|
1380
1380
|
blob = table.compile(font) # Make sure it compiles
|
|
1381
1381
|
decompiled = table.__class__()
|
fontTools/otlLib/builder.py
CHANGED
|
@@ -170,6 +170,9 @@ class LookupBuilder(object):
|
|
|
170
170
|
and self.extension == other.extension
|
|
171
171
|
)
|
|
172
172
|
|
|
173
|
+
def promote_lookup_type(self, is_named_lookup):
|
|
174
|
+
return [self]
|
|
175
|
+
|
|
173
176
|
def inferGlyphClasses(self):
|
|
174
177
|
"""Infers glyph glasses for the GDEF table, such as {"cedilla":3}."""
|
|
175
178
|
return {}
|
|
@@ -883,6 +886,14 @@ class LigatureSubstBuilder(LookupBuilder):
|
|
|
883
886
|
)
|
|
884
887
|
return self.buildLookup_(subtables)
|
|
885
888
|
|
|
889
|
+
def getAlternateGlyphs(self):
|
|
890
|
+
# https://github.com/fonttools/fonttools/issues/3845
|
|
891
|
+
return {
|
|
892
|
+
components[0]: [ligature]
|
|
893
|
+
for components, ligature in self.ligatures.items()
|
|
894
|
+
if len(components) == 1
|
|
895
|
+
}
|
|
896
|
+
|
|
886
897
|
def add_subtable_break(self, location):
|
|
887
898
|
self.ligatures[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
|
|
888
899
|
|
|
@@ -921,6 +932,14 @@ class MultipleSubstBuilder(LookupBuilder):
|
|
|
921
932
|
subtables = self.build_subst_subtables(self.mapping, buildMultipleSubstSubtable)
|
|
922
933
|
return self.buildLookup_(subtables)
|
|
923
934
|
|
|
935
|
+
def getAlternateGlyphs(self):
|
|
936
|
+
# https://github.com/fonttools/fonttools/issues/3845
|
|
937
|
+
return {
|
|
938
|
+
glyph: replacements
|
|
939
|
+
for glyph, replacements in self.mapping.items()
|
|
940
|
+
if len(replacements) == 1
|
|
941
|
+
}
|
|
942
|
+
|
|
924
943
|
def add_subtable_break(self, location):
|
|
925
944
|
self.mapping[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
|
|
926
945
|
|
|
@@ -1308,6 +1327,151 @@ class ReverseChainSingleSubstBuilder(LookupBuilder):
|
|
|
1308
1327
|
pass
|
|
1309
1328
|
|
|
1310
1329
|
|
|
1330
|
+
class AnySubstBuilder(LookupBuilder):
|
|
1331
|
+
"""A temporary builder for Single, Multiple, or Ligature substitution lookup.
|
|
1332
|
+
|
|
1333
|
+
Users are expected to manually add substitutions to the ``mapping``
|
|
1334
|
+
attribute after the object has been initialized, e.g.::
|
|
1335
|
+
|
|
1336
|
+
# sub x by y;
|
|
1337
|
+
builder.mapping[("x",)] = ("y",)
|
|
1338
|
+
# sub a by b c;
|
|
1339
|
+
builder.mapping[("a",)] = ("b", "c")
|
|
1340
|
+
# sub f i by f_i;
|
|
1341
|
+
builder.mapping[("f", "i")] = ("f_i",)
|
|
1342
|
+
|
|
1343
|
+
Then call `promote_lookup_type()` to convert this builder into the
|
|
1344
|
+
appropriate type of substitution lookup builder. This would promote single
|
|
1345
|
+
substitutions to either multiple or ligature substitutions, depending on the
|
|
1346
|
+
rest of the rules in the mapping.
|
|
1347
|
+
|
|
1348
|
+
Attributes:
|
|
1349
|
+
font (``fontTools.TTLib.TTFont``): A font object.
|
|
1350
|
+
location: A string or tuple representing the location in the original
|
|
1351
|
+
source which produced this lookup.
|
|
1352
|
+
mapping: An ordered dictionary mapping a tuple of glyph names to another
|
|
1353
|
+
tuple of glyph names.
|
|
1354
|
+
lookupflag (int): The lookup's flag
|
|
1355
|
+
markFilterSet: Either ``None`` if no mark filtering set is used, or
|
|
1356
|
+
an integer representing the filtering set to be used for this
|
|
1357
|
+
lookup. If a mark filtering set is provided,
|
|
1358
|
+
`LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
|
|
1359
|
+
flags.
|
|
1360
|
+
"""
|
|
1361
|
+
|
|
1362
|
+
def __init__(self, font, location):
|
|
1363
|
+
LookupBuilder.__init__(self, font, location, "GSUB", 0)
|
|
1364
|
+
self.mapping = OrderedDict()
|
|
1365
|
+
|
|
1366
|
+
def _add_to_single_subst(self, builder, key, value):
|
|
1367
|
+
if key[0] != self.SUBTABLE_BREAK_:
|
|
1368
|
+
key = key[0]
|
|
1369
|
+
builder.mapping[key] = value[0]
|
|
1370
|
+
|
|
1371
|
+
def _add_to_multiple_subst(self, builder, key, value):
|
|
1372
|
+
if key[0] != self.SUBTABLE_BREAK_:
|
|
1373
|
+
key = key[0]
|
|
1374
|
+
builder.mapping[key] = value
|
|
1375
|
+
|
|
1376
|
+
def _add_to_ligature_subst(self, builder, key, value):
|
|
1377
|
+
builder.ligatures[key] = value[0]
|
|
1378
|
+
|
|
1379
|
+
def promote_lookup_type(self, is_named_lookup):
|
|
1380
|
+
# https://github.com/fonttools/fonttools/issues/612
|
|
1381
|
+
# A multiple substitution may have a single destination, in which case
|
|
1382
|
+
# it will look just like a single substitution. So if there are both
|
|
1383
|
+
# multiple and single substitutions, upgrade all the single ones to
|
|
1384
|
+
# multiple substitutions. Similarly, a ligature substitution may have a
|
|
1385
|
+
# single source glyph, so if there are both ligature and single
|
|
1386
|
+
# substitutions, upgrade all the single ones to ligature substitutions.
|
|
1387
|
+
builder_classes = []
|
|
1388
|
+
for key, value in self.mapping.items():
|
|
1389
|
+
if key[0] == self.SUBTABLE_BREAK_:
|
|
1390
|
+
builder_classes.append(None)
|
|
1391
|
+
elif len(key) == 1 and len(value) == 1:
|
|
1392
|
+
builder_classes.append(SingleSubstBuilder)
|
|
1393
|
+
elif len(key) == 1 and len(value) != 1:
|
|
1394
|
+
builder_classes.append(MultipleSubstBuilder)
|
|
1395
|
+
elif len(key) > 1 and len(value) == 1:
|
|
1396
|
+
builder_classes.append(LigatureSubstBuilder)
|
|
1397
|
+
else:
|
|
1398
|
+
assert False, "Should not happen"
|
|
1399
|
+
|
|
1400
|
+
has_multiple = any(b is MultipleSubstBuilder for b in builder_classes)
|
|
1401
|
+
has_ligature = any(b is LigatureSubstBuilder for b in builder_classes)
|
|
1402
|
+
|
|
1403
|
+
# If we have mixed single and multiple substitutions,
|
|
1404
|
+
# upgrade all single substitutions to multiple substitutions.
|
|
1405
|
+
to_multiple = has_multiple and not has_ligature
|
|
1406
|
+
|
|
1407
|
+
# If we have mixed single and ligature substitutions,
|
|
1408
|
+
# upgrade all single substitutions to ligature substitutions.
|
|
1409
|
+
to_ligature = has_ligature and not has_multiple
|
|
1410
|
+
|
|
1411
|
+
# If we have only single substitutions, we can keep them as is.
|
|
1412
|
+
to_single = not has_ligature and not has_multiple
|
|
1413
|
+
|
|
1414
|
+
ret = []
|
|
1415
|
+
if to_single:
|
|
1416
|
+
builder = SingleSubstBuilder(self.font, self.location)
|
|
1417
|
+
for key, value in self.mapping.items():
|
|
1418
|
+
self._add_to_single_subst(builder, key, value)
|
|
1419
|
+
ret = [builder]
|
|
1420
|
+
elif to_multiple:
|
|
1421
|
+
builder = MultipleSubstBuilder(self.font, self.location)
|
|
1422
|
+
for key, value in self.mapping.items():
|
|
1423
|
+
self._add_to_multiple_subst(builder, key, value)
|
|
1424
|
+
ret = [builder]
|
|
1425
|
+
elif to_ligature:
|
|
1426
|
+
builder = LigatureSubstBuilder(self.font, self.location)
|
|
1427
|
+
for key, value in self.mapping.items():
|
|
1428
|
+
self._add_to_ligature_subst(builder, key, value)
|
|
1429
|
+
ret = [builder]
|
|
1430
|
+
elif is_named_lookup:
|
|
1431
|
+
# This is a named lookup with mixed substitutions that can’t be promoted,
|
|
1432
|
+
# since we can’t split it into multiple lookups, we return None here to
|
|
1433
|
+
# signal that to the caller
|
|
1434
|
+
return None
|
|
1435
|
+
else:
|
|
1436
|
+
curr_builder = None
|
|
1437
|
+
for builder_class, (key, value) in zip(
|
|
1438
|
+
builder_classes, self.mapping.items()
|
|
1439
|
+
):
|
|
1440
|
+
if curr_builder is None or type(curr_builder) is not builder_class:
|
|
1441
|
+
curr_builder = builder_class(self.font, self.location)
|
|
1442
|
+
ret.append(curr_builder)
|
|
1443
|
+
if builder_class is SingleSubstBuilder:
|
|
1444
|
+
self._add_to_single_subst(curr_builder, key, value)
|
|
1445
|
+
elif builder_class is MultipleSubstBuilder:
|
|
1446
|
+
self._add_to_multiple_subst(curr_builder, key, value)
|
|
1447
|
+
elif builder_class is LigatureSubstBuilder:
|
|
1448
|
+
self._add_to_ligature_subst(curr_builder, key, value)
|
|
1449
|
+
else:
|
|
1450
|
+
assert False, "Should not happen"
|
|
1451
|
+
|
|
1452
|
+
for builder in ret:
|
|
1453
|
+
builder.extension = self.extension
|
|
1454
|
+
builder.lookupflag = self.lookupflag
|
|
1455
|
+
builder.markFilterSet = self.markFilterSet
|
|
1456
|
+
return ret
|
|
1457
|
+
|
|
1458
|
+
def equals(self, other):
|
|
1459
|
+
return LookupBuilder.equals(self, other) and self.mapping == other.mapping
|
|
1460
|
+
|
|
1461
|
+
def build(self):
|
|
1462
|
+
assert False
|
|
1463
|
+
|
|
1464
|
+
def getAlternateGlyphs(self):
|
|
1465
|
+
return {
|
|
1466
|
+
key[0]: value
|
|
1467
|
+
for key, value in self.mapping.items()
|
|
1468
|
+
if len(key) == 1 and len(value) == 1
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
def add_subtable_break(self, location):
|
|
1472
|
+
self.mapping[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
|
|
1473
|
+
|
|
1474
|
+
|
|
1311
1475
|
class SingleSubstBuilder(LookupBuilder):
|
|
1312
1476
|
"""Builds a Single Substitution (GSUB1) lookup.
|
|
1313
1477
|
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
# Copyright (c) 2009 Type Supply LLC
|
|
2
2
|
# Author: Tal Leming
|
|
3
3
|
|
|
4
|
-
from
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from typing import Any, Dict, List, Tuple
|
|
7
|
+
|
|
8
|
+
from fontTools.cffLib.specializer import commandsToProgram, specializeCommands
|
|
5
9
|
from fontTools.misc.psCharStrings import T2CharString
|
|
10
|
+
from fontTools.misc.roundTools import otRound, roundFunc
|
|
6
11
|
from fontTools.pens.basePen import BasePen
|
|
7
|
-
from fontTools.cffLib.specializer import specializeCommands, commandsToProgram
|
|
8
12
|
|
|
9
13
|
|
|
10
14
|
class T2CharStringPen(BasePen):
|
|
@@ -18,36 +22,52 @@ class T2CharStringPen(BasePen):
|
|
|
18
22
|
which are close to their integral part within the tolerated range.
|
|
19
23
|
"""
|
|
20
24
|
|
|
21
|
-
def __init__(
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
width: float | None,
|
|
28
|
+
glyphSet: Dict[str, Any] | None,
|
|
29
|
+
roundTolerance: float = 0.5,
|
|
30
|
+
CFF2: bool = False,
|
|
31
|
+
) -> None:
|
|
22
32
|
super(T2CharStringPen, self).__init__(glyphSet)
|
|
23
33
|
self.round = roundFunc(roundTolerance)
|
|
24
34
|
self._CFF2 = CFF2
|
|
25
35
|
self._width = width
|
|
26
|
-
self._commands = []
|
|
36
|
+
self._commands: List[Tuple[str | bytes, List[float]]] = []
|
|
27
37
|
self._p0 = (0, 0)
|
|
28
38
|
|
|
29
|
-
def _p(self, pt):
|
|
39
|
+
def _p(self, pt: Tuple[float, float]) -> List[float]:
|
|
30
40
|
p0 = self._p0
|
|
31
41
|
pt = self._p0 = (self.round(pt[0]), self.round(pt[1]))
|
|
32
42
|
return [pt[0] - p0[0], pt[1] - p0[1]]
|
|
33
43
|
|
|
34
|
-
def _moveTo(self, pt):
|
|
44
|
+
def _moveTo(self, pt: Tuple[float, float]) -> None:
|
|
35
45
|
self._commands.append(("rmoveto", self._p(pt)))
|
|
36
46
|
|
|
37
|
-
def _lineTo(self, pt):
|
|
47
|
+
def _lineTo(self, pt: Tuple[float, float]) -> None:
|
|
38
48
|
self._commands.append(("rlineto", self._p(pt)))
|
|
39
49
|
|
|
40
|
-
def _curveToOne(
|
|
50
|
+
def _curveToOne(
|
|
51
|
+
self,
|
|
52
|
+
pt1: Tuple[float, float],
|
|
53
|
+
pt2: Tuple[float, float],
|
|
54
|
+
pt3: Tuple[float, float],
|
|
55
|
+
) -> None:
|
|
41
56
|
_p = self._p
|
|
42
57
|
self._commands.append(("rrcurveto", _p(pt1) + _p(pt2) + _p(pt3)))
|
|
43
58
|
|
|
44
|
-
def _closePath(self):
|
|
59
|
+
def _closePath(self) -> None:
|
|
45
60
|
pass
|
|
46
61
|
|
|
47
|
-
def _endPath(self):
|
|
62
|
+
def _endPath(self) -> None:
|
|
48
63
|
pass
|
|
49
64
|
|
|
50
|
-
def getCharString(
|
|
65
|
+
def getCharString(
|
|
66
|
+
self,
|
|
67
|
+
private: Dict | None = None,
|
|
68
|
+
globalSubrs: List | None = None,
|
|
69
|
+
optimize: bool = True,
|
|
70
|
+
) -> T2CharString:
|
|
51
71
|
commands = self._commands
|
|
52
72
|
if optimize:
|
|
53
73
|
maxstack = 48 if not self._CFF2 else 513
|
fontTools/subset/__init__.py
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
#
|
|
3
3
|
# Google Author(s): Behdad Esfahbod
|
|
4
4
|
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
5
7
|
from fontTools import config
|
|
6
8
|
from fontTools.misc.roundTools import otRound
|
|
7
9
|
from fontTools import ttLib
|
|
@@ -15,7 +17,7 @@ from fontTools.subset.util import _add_method, _uniq_sort
|
|
|
15
17
|
from fontTools.subset.cff import *
|
|
16
18
|
from fontTools.subset.svg import *
|
|
17
19
|
from fontTools.varLib import varStore, multiVarStore # For monkey-patching
|
|
18
|
-
from fontTools.ttLib.tables._n_a_m_e import NameRecordVisitor
|
|
20
|
+
from fontTools.ttLib.tables._n_a_m_e import NameRecordVisitor, makeName
|
|
19
21
|
from fontTools.unicodedata import mirrored
|
|
20
22
|
import sys
|
|
21
23
|
import struct
|
|
@@ -3004,6 +3006,9 @@ def prune_pre_subset(self, font, options):
|
|
|
3004
3006
|
return True
|
|
3005
3007
|
|
|
3006
3008
|
|
|
3009
|
+
NAME_IDS_TO_OBFUSCATE = {1, 2, 3, 4, 6, 16, 17, 18}
|
|
3010
|
+
|
|
3011
|
+
|
|
3007
3012
|
@_add_method(ttLib.getTableClass("name"))
|
|
3008
3013
|
def prune_post_subset(self, font, options):
|
|
3009
3014
|
visitor = NameRecordVisitor()
|
|
@@ -3022,6 +3027,11 @@ def prune_post_subset(self, font, options):
|
|
|
3022
3027
|
self.names = [n for n in self.names if n.langID in options.name_languages]
|
|
3023
3028
|
if options.obfuscate_names:
|
|
3024
3029
|
namerecs = []
|
|
3030
|
+
# Preserve names to be scrambled or dropped elsewhere so that other
|
|
3031
|
+
# parts of the font don't break.
|
|
3032
|
+
needRemapping = visitor.seen.intersection(NAME_IDS_TO_OBFUSCATE)
|
|
3033
|
+
if needRemapping:
|
|
3034
|
+
_remap_select_name_ids(font, needRemapping)
|
|
3025
3035
|
for n in self.names:
|
|
3026
3036
|
if n.nameID in [1, 4]:
|
|
3027
3037
|
n.string = ".\x7f".encode("utf_16_be") if n.isUnicode() else ".\x7f"
|
|
@@ -3036,6 +3046,76 @@ def prune_post_subset(self, font, options):
|
|
|
3036
3046
|
return True # Required table
|
|
3037
3047
|
|
|
3038
3048
|
|
|
3049
|
+
def _remap_select_name_ids(font: ttLib.TTFont, needRemapping: set[int]) -> None:
|
|
3050
|
+
"""Remap a set of IDs so that the originals can be safely scrambled or
|
|
3051
|
+
dropped.
|
|
3052
|
+
|
|
3053
|
+
For each name record whose name id is in the `needRemapping` set, make a copy
|
|
3054
|
+
and allocate a new unused name id in the font-specific range (> 255).
|
|
3055
|
+
|
|
3056
|
+
Finally update references to these in the `fvar` and `STAT` tables.
|
|
3057
|
+
"""
|
|
3058
|
+
|
|
3059
|
+
if "fvar" not in font and "STAT" not in font:
|
|
3060
|
+
return
|
|
3061
|
+
|
|
3062
|
+
name = font["name"]
|
|
3063
|
+
|
|
3064
|
+
# 1. Assign new IDs for names to be preserved.
|
|
3065
|
+
existingIds = {record.nameID for record in name.names}
|
|
3066
|
+
remapping = {}
|
|
3067
|
+
nextId = name._findUnusedNameID() - 1 # Should skip gaps in name IDs.
|
|
3068
|
+
for nameId in needRemapping:
|
|
3069
|
+
nextId += 1 # We should have complete freedom until 32767.
|
|
3070
|
+
assert nextId not in existingIds, "_findUnusedNameID did not skip gaps"
|
|
3071
|
+
if nextId > 32767:
|
|
3072
|
+
raise ValueError("Ran out of name IDs while trying to remap existing ones.")
|
|
3073
|
+
remapping[nameId] = nextId
|
|
3074
|
+
|
|
3075
|
+
# 2. Copy records to use the new ID. We can't rewrite them in place, because
|
|
3076
|
+
# that could make IDs 1 to 6 "disappear" from code that follows. Some
|
|
3077
|
+
# tools that produce EOT fonts expect them to exist, even when they're
|
|
3078
|
+
# scrambled. See https://github.com/fonttools/fonttools/issues/165.
|
|
3079
|
+
copiedRecords = []
|
|
3080
|
+
for record in name.names:
|
|
3081
|
+
if record.nameID not in needRemapping:
|
|
3082
|
+
continue
|
|
3083
|
+
recordCopy = makeName(
|
|
3084
|
+
record.string,
|
|
3085
|
+
remapping[record.nameID],
|
|
3086
|
+
record.platformID,
|
|
3087
|
+
record.platEncID,
|
|
3088
|
+
record.langID,
|
|
3089
|
+
)
|
|
3090
|
+
copiedRecords.append(recordCopy)
|
|
3091
|
+
name.names.extend(copiedRecords)
|
|
3092
|
+
|
|
3093
|
+
# 3. Rewrite the corresponding IDs in other tables. For now, care only about
|
|
3094
|
+
# STAT and fvar. If more tables need to be changed, consider adapting
|
|
3095
|
+
# NameRecordVisitor to rewrite IDs wherever it finds them.
|
|
3096
|
+
fvar = font.get("fvar")
|
|
3097
|
+
if fvar is not None:
|
|
3098
|
+
for axis in fvar.axes:
|
|
3099
|
+
axis.axisNameID = remapping.get(axis.axisNameID, axis.axisNameID)
|
|
3100
|
+
for instance in fvar.instances:
|
|
3101
|
+
nameID = instance.subfamilyNameID
|
|
3102
|
+
instance.subfamilyNameID = remapping.get(nameID, nameID)
|
|
3103
|
+
nameID = instance.postscriptNameID
|
|
3104
|
+
instance.postscriptNameID = remapping.get(nameID, nameID)
|
|
3105
|
+
|
|
3106
|
+
stat = font.get("STAT")
|
|
3107
|
+
if stat is None:
|
|
3108
|
+
return
|
|
3109
|
+
elidedNameID = stat.table.ElidedFallbackNameID
|
|
3110
|
+
stat.table.ElidedFallbackNameID = remapping.get(elidedNameID, elidedNameID)
|
|
3111
|
+
if stat.table.DesignAxisRecord:
|
|
3112
|
+
for axis in stat.table.DesignAxisRecord.Axis:
|
|
3113
|
+
axis.AxisNameID = remapping.get(axis.AxisNameID, axis.AxisNameID)
|
|
3114
|
+
if stat.table.AxisValueArray:
|
|
3115
|
+
for value in stat.table.AxisValueArray.AxisValue:
|
|
3116
|
+
value.ValueNameID = remapping.get(value.ValueNameID, value.ValueNameID)
|
|
3117
|
+
|
|
3118
|
+
|
|
3039
3119
|
@_add_method(ttLib.getTableClass("head"))
|
|
3040
3120
|
def prune_post_subset(self, font, options):
|
|
3041
3121
|
# Force re-compiling head table, to update any recalculated values.
|
|
@@ -3757,7 +3837,7 @@ def main(args=None):
|
|
|
3757
3837
|
text += g[7:]
|
|
3758
3838
|
continue
|
|
3759
3839
|
if g.startswith("--text-file="):
|
|
3760
|
-
with open(g[12:], encoding="utf-8") as f:
|
|
3840
|
+
with open(g[12:], encoding="utf-8-sig") as f:
|
|
3761
3841
|
text += f.read().replace("\n", "")
|
|
3762
3842
|
continue
|
|
3763
3843
|
if g.startswith("--unicodes="):
|
fontTools/ttLib/reorderGlyphs.py
CHANGED
|
@@ -275,10 +275,11 @@ def reorderGlyphs(font: ttLib.TTFont, new_glyph_order: List[str]):
|
|
|
275
275
|
for reorder in _REORDER_RULES.get(reorder_key, []):
|
|
276
276
|
reorder.apply(font, value)
|
|
277
277
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
278
|
+
for tag in ["CFF ", "CFF2"]:
|
|
279
|
+
if tag in font:
|
|
280
|
+
cff_table = font[tag]
|
|
281
|
+
charstrings = cff_table.cff.topDictIndex[0].CharStrings.charStrings
|
|
282
|
+
cff_table.cff.topDictIndex[0].charset = new_glyph_order
|
|
283
|
+
cff_table.cff.topDictIndex[0].CharStrings.charStrings = {
|
|
284
|
+
k: charstrings.get(k) for k in new_glyph_order
|
|
285
|
+
}
|
fontTools/ufoLib/__init__.py
CHANGED
|
@@ -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 ""
|
fontTools/varLib/__init__.py
CHANGED
|
@@ -109,6 +109,8 @@ def _add_fvar(font, axes, instances: List[InstanceDescriptor]):
|
|
|
109
109
|
axis.flags = int(a.hidden)
|
|
110
110
|
fvar.axes.append(axis)
|
|
111
111
|
|
|
112
|
+
default_coordinates = {axis.axisTag: axis.defaultValue for axis in fvar.axes}
|
|
113
|
+
|
|
112
114
|
for instance in instances:
|
|
113
115
|
# Filter out discrete axis locations
|
|
114
116
|
coordinates = {
|
|
@@ -130,16 +132,26 @@ def _add_fvar(font, axes, instances: List[InstanceDescriptor]):
|
|
|
130
132
|
psname = instance.postScriptFontName
|
|
131
133
|
|
|
132
134
|
inst = NamedInstance()
|
|
133
|
-
inst.
|
|
134
|
-
|
|
135
|
+
inst.coordinates = {
|
|
136
|
+
axes[k].tag: axes[k].map_backward(v) for k, v in coordinates.items()
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
subfamilyNameID = nameTable.findMultilingualName(
|
|
140
|
+
localisedStyleName, windows=True, mac=macNames
|
|
135
141
|
)
|
|
142
|
+
if subfamilyNameID in {2, 17} and inst.coordinates == default_coordinates:
|
|
143
|
+
# Instances can only reuse an existing name ID 2 or 17 if they are at the
|
|
144
|
+
# default location across all axes, see:
|
|
145
|
+
# https://github.com/fonttools/fonttools/issues/3825.
|
|
146
|
+
inst.subfamilyNameID = subfamilyNameID
|
|
147
|
+
else:
|
|
148
|
+
inst.subfamilyNameID = nameTable.addMultilingualName(
|
|
149
|
+
localisedStyleName, windows=True, mac=macNames, minNameID=256
|
|
150
|
+
)
|
|
151
|
+
|
|
136
152
|
if psname is not None:
|
|
137
153
|
psname = tostr(psname)
|
|
138
154
|
inst.postscriptNameID = nameTable.addName(psname, platforms=platforms)
|
|
139
|
-
inst.coordinates = {
|
|
140
|
-
axes[k].tag: axes[k].map_backward(v) for k, v in coordinates.items()
|
|
141
|
-
}
|
|
142
|
-
# inst.coordinates = {axes[k].tag:v for k,v in coordinates.items()}
|
|
143
155
|
fvar.instances.append(inst)
|
|
144
156
|
|
|
145
157
|
assert "fvar" not in font
|
fontTools/varLib/featureVars.py
CHANGED
|
@@ -392,10 +392,13 @@ def addFeatureVariationsRaw(font, table, conditionalSubstitutions, featureTag="r
|
|
|
392
392
|
|
|
393
393
|
for scriptRecord in table.ScriptList.ScriptRecord:
|
|
394
394
|
if scriptRecord.Script.DefaultLangSys is None:
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
395
|
+
# We need to have a default LangSys to attach variations to.
|
|
396
|
+
langSys = ot.LangSys()
|
|
397
|
+
langSys.LookupOrder = None
|
|
398
|
+
langSys.ReqFeatureIndex = 0xFFFF
|
|
399
|
+
langSys.FeatureIndex = []
|
|
400
|
+
langSys.FeatureCount = 0
|
|
401
|
+
scriptRecord.Script.DefaultLangSys = langSys
|
|
399
402
|
langSystems = [lsr.LangSys for lsr in scriptRecord.Script.LangSysRecord]
|
|
400
403
|
for langSys in [scriptRecord.Script.DefaultLangSys] + langSystems:
|
|
401
404
|
langSys.FeatureIndex.append(varFeatureIndex)
|
|
@@ -597,9 +600,12 @@ def buildFeatureRecord(featureTag, lookupListIndices):
|
|
|
597
600
|
def buildFeatureVariationRecord(conditionTable, substitutionRecords):
|
|
598
601
|
"""Build a FeatureVariationRecord."""
|
|
599
602
|
fvr = ot.FeatureVariationRecord()
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
+
if len(conditionTable) != 0:
|
|
604
|
+
fvr.ConditionSet = ot.ConditionSet()
|
|
605
|
+
fvr.ConditionSet.ConditionTable = conditionTable
|
|
606
|
+
fvr.ConditionSet.ConditionCount = len(conditionTable)
|
|
607
|
+
else:
|
|
608
|
+
fvr.ConditionSet = None
|
|
603
609
|
fvr.FeatureTableSubstitution = ot.FeatureTableSubstitution()
|
|
604
610
|
fvr.FeatureTableSubstitution.Version = 0x00010000
|
|
605
611
|
fvr.FeatureTableSubstitution.SubstitutionRecord = substitutionRecords
|
fontTools/varLib/hvar.py
CHANGED
|
@@ -18,7 +18,7 @@ def _get_advance_metrics(font, axisTags, tableFields):
|
|
|
18
18
|
# advance width at that peak. Then pass these all to a VariationModel
|
|
19
19
|
# builder to compute back the deltas.
|
|
20
20
|
# 2. For each master peak, pull out the deltas of the advance width directly,
|
|
21
|
-
# and feed these to the VarStoreBuilder, forgoing the
|
|
21
|
+
# and feed these to the VarStoreBuilder, forgoing the remodeling step.
|
|
22
22
|
# We'll go with the second option, as it's simpler, faster, and more direct.
|
|
23
23
|
gvar = font["gvar"]
|
|
24
24
|
vhAdvanceDeltasAndSupports = {}
|
|
@@ -675,6 +675,7 @@ def instantiateCFF2(
|
|
|
675
675
|
privateDicts.append(fd.Private)
|
|
676
676
|
|
|
677
677
|
allCommands = []
|
|
678
|
+
allCommandPrivates = []
|
|
678
679
|
for cs in charStrings:
|
|
679
680
|
assert cs.private.vstore.otVarStore is varStore # Or in many places!!
|
|
680
681
|
commands = programToCommands(cs.program, getNumRegions=getNumRegions)
|
|
@@ -683,6 +684,7 @@ def instantiateCFF2(
|
|
|
683
684
|
if specialize:
|
|
684
685
|
commands = specializeCommands(commands, generalizeFirst=not generalize)
|
|
685
686
|
allCommands.append(commands)
|
|
687
|
+
allCommandPrivates.append(cs.private)
|
|
686
688
|
|
|
687
689
|
def storeBlendsToVarStore(arg):
|
|
688
690
|
if not isinstance(arg, list):
|
|
@@ -742,8 +744,8 @@ def instantiateCFF2(
|
|
|
742
744
|
assert varData.ItemCount == 0
|
|
743
745
|
|
|
744
746
|
# Add charstring blend lists to VarStore so we can instantiate them
|
|
745
|
-
for commands in allCommands:
|
|
746
|
-
vsindex = 0
|
|
747
|
+
for commands, private in zip(allCommands, allCommandPrivates):
|
|
748
|
+
vsindex = getattr(private, "vsindex", 0)
|
|
747
749
|
for command in commands:
|
|
748
750
|
if command[0] == "vsindex":
|
|
749
751
|
vsindex = command[1][0]
|
|
@@ -752,7 +754,6 @@ def instantiateCFF2(
|
|
|
752
754
|
storeBlendsToVarStore(arg)
|
|
753
755
|
|
|
754
756
|
# Add private blend lists to VarStore so we can instantiate values
|
|
755
|
-
vsindex = 0
|
|
756
757
|
for opcode, name, arg_type, default, converter in privateDictOperators2:
|
|
757
758
|
if arg_type not in ("number", "delta", "array"):
|
|
758
759
|
continue
|
|
@@ -763,6 +764,7 @@ def instantiateCFF2(
|
|
|
763
764
|
continue
|
|
764
765
|
values = getattr(private, name)
|
|
765
766
|
|
|
767
|
+
# This is safe here since "vsindex" is the first in the privateDictOperators2
|
|
766
768
|
if name == "vsindex":
|
|
767
769
|
vsindex = values[0]
|
|
768
770
|
continue
|
|
@@ -783,8 +785,8 @@ def instantiateCFF2(
|
|
|
783
785
|
|
|
784
786
|
# Read back new charstring blends from the instantiated VarStore
|
|
785
787
|
varDataCursor = [0] * len(varStore.VarData)
|
|
786
|
-
for commands in allCommands:
|
|
787
|
-
vsindex = 0
|
|
788
|
+
for commands, private in zip(allCommands, allCommandPrivates):
|
|
789
|
+
vsindex = getattr(private, "vsindex", 0)
|
|
788
790
|
for command in commands:
|
|
789
791
|
if command[0] == "vsindex":
|
|
790
792
|
vsindex = command[1][0]
|
|
@@ -799,9 +801,16 @@ def instantiateCFF2(
|
|
|
799
801
|
if arg_type not in ("number", "delta", "array"):
|
|
800
802
|
continue
|
|
801
803
|
|
|
804
|
+
vsindex = 0
|
|
802
805
|
for private in privateDicts:
|
|
803
806
|
if not hasattr(private, name):
|
|
804
807
|
continue
|
|
808
|
+
|
|
809
|
+
# This is safe here since "vsindex" is the first in the privateDictOperators2
|
|
810
|
+
if name == "vsindex":
|
|
811
|
+
vsindex = values[0]
|
|
812
|
+
continue
|
|
813
|
+
|
|
805
814
|
values = getattr(private, name)
|
|
806
815
|
if arg_type == "number":
|
|
807
816
|
values = [values]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fonttools
|
|
3
|
-
Version: 4.58.
|
|
3
|
+
Version: 4.58.2
|
|
4
4
|
Summary: Tools to manipulate font files
|
|
5
5
|
Home-page: http://github.com/fonttools/fonttools
|
|
6
6
|
Author: Just van Rossum
|
|
@@ -388,6 +388,31 @@ Have fun!
|
|
|
388
388
|
Changelog
|
|
389
389
|
~~~~~~~~~
|
|
390
390
|
|
|
391
|
+
4.58.2 (released 2025-06-06)
|
|
392
|
+
----------------------------
|
|
393
|
+
|
|
394
|
+
- [ttLib.reorderGlyphs] Handle CFF2 when reordering glyphs (#3852)
|
|
395
|
+
- [subset] Copy name IDs in use before scrapping or scrambling them for webfonts (#3853)
|
|
396
|
+
|
|
397
|
+
4.58.1 (released 2025-05-28)
|
|
398
|
+
----------------------------
|
|
399
|
+
|
|
400
|
+
- [varLib] Make sure that fvar named instances only reuse name ID 2 or 17 if they are at the default location across all axes, to match OT spec requirement (#3831).
|
|
401
|
+
- [feaLib] Improve single substitution promotion to multiple/ligature substitutions, fixing a few bugs as well (#3849).
|
|
402
|
+
- [loggingTools] Make ``Timer._time`` a static method that doesn't take self, makes it easier to override (#3836).
|
|
403
|
+
- [featureVars] Use ``None`` for empty ConditionSet, which translates to a null offset in the compiled table (#3850).
|
|
404
|
+
- [feaLib] Raise an error on conflicting ligature substitution rules instead of silently taking the last one (#3835).
|
|
405
|
+
- Add typing annotations to T2CharStringPen (#3837).
|
|
406
|
+
- [feaLib] Add single substitutions that were promoted to multiple or ligature substitutions to ``aalt`` feature (#3847).
|
|
407
|
+
- [featureVars] Create a default ``LangSys`` in a ``ScriptRecord`` if missing when adding feature variations to existing GSUB later in the build (#3838).
|
|
408
|
+
- [symfont] Added a ``main()``.
|
|
409
|
+
- [cffLib.specializer] Fix rmoveto merging when blends used (#3839, #3840).
|
|
410
|
+
- [pyftmerge] Add support for cmap format 14 in the merge tool (#3830).
|
|
411
|
+
- [varLib.instancer/cff2] Fix vsindex of Private dicts when instantiating (#3828, #3232).
|
|
412
|
+
- Update text file read to use UTF-8 with optional BOM so it works with e.g. Windows Notepad.exe (#3824).
|
|
413
|
+
- [varLib] Ensure that instances only reuse name ID 2 or 17 if they are at the default location across all axes (#3831).
|
|
414
|
+
- [varLib] Create a dflt LangSys in a ScriptRecord when adding variations later, to fix an avoidable crash in an edge case (#3838).
|
|
415
|
+
|
|
391
416
|
4.58.0 (released 2025-05-10)
|
|
392
417
|
----------------------------
|
|
393
418
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
fontTools/__init__.py,sha256=
|
|
1
|
+
fontTools/__init__.py,sha256=XeIRdnWncwRzBzhwui9It6L8mVeq0VAJRsvcJorF_AY,183
|
|
2
2
|
fontTools/__main__.py,sha256=VjkGh1UD-i1zTDA1dXo1uecSs6PxHdGQ5vlCk_mCCYs,925
|
|
3
3
|
fontTools/afmLib.py,sha256=1MagIItOzRV4vV5kKPxeDZbPJsfxLB3wdHLFkQvl0uk,13164
|
|
4
4
|
fontTools/agl.py,sha256=05bm8Uq45uVWW8nPbP6xbNgmFyxQr8sWhYAiP0VSjnI,112975
|
|
@@ -10,7 +10,7 @@ fontTools/unicode.py,sha256=ZZ7OMmWvIyV1IL1k6ioTzaRAh3tUvm6gvK7QgFbOIHY,1237
|
|
|
10
10
|
fontTools/cffLib/CFF2ToCFF.py,sha256=pvwh6qxJ0D7c4xgXBcyAdmZGzpTiywMy45-jjp7dKck,6088
|
|
11
11
|
fontTools/cffLib/CFFToCFF2.py,sha256=Qnk7lYlsTRHnlZQ6NXNdr_f4MJwZQ21kcS08KFbsyY8,10119
|
|
12
12
|
fontTools/cffLib/__init__.py,sha256=62vpcR7u8cE407kXduAwnFttHnsoCpDQ7IBK-qOYFQ8,107886
|
|
13
|
-
fontTools/cffLib/specializer.py,sha256=
|
|
13
|
+
fontTools/cffLib/specializer.py,sha256=vsOPkR_jHNe6tESQEjmm0i76y7sWI5MKo3bsTmI3sNM,32609
|
|
14
14
|
fontTools/cffLib/transforms.py,sha256=kHBnYQmcJBLIMUC6Uws4eor2mJiNNHiR_eRePXHDPC8,17371
|
|
15
15
|
fontTools/cffLib/width.py,sha256=IqGL0CLyCZqi_hvsHySG08qpYxS3kaqW-tsAT-bjHV4,6074
|
|
16
16
|
fontTools/colorLib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -38,8 +38,8 @@ fontTools/encodings/__init__.py,sha256=DJBWmoX_Haau7qlgmvWyfbhSzrX2qL636Rns7CG01
|
|
|
38
38
|
fontTools/encodings/codecs.py,sha256=u50ruwz9fcRsrUrRGpR17Cr55Ovn1fvCHCKrElVumDE,4721
|
|
39
39
|
fontTools/feaLib/__init__.py,sha256=jlIru2ghxvb1HhC5Je2BCXjFJmFQlYKpruorPoz3BvQ,213
|
|
40
40
|
fontTools/feaLib/__main__.py,sha256=Df2PA6LXwna98lSXiL7R4as_ZEdWCIk3egSM5w7GpvM,2240
|
|
41
|
-
fontTools/feaLib/ast.py,sha256=
|
|
42
|
-
fontTools/feaLib/builder.py,sha256=
|
|
41
|
+
fontTools/feaLib/ast.py,sha256=ElVBjb_Ut5kVgdu5_XyWLYiWIc66ZnXEj_lhynPn5Mg,74142
|
|
42
|
+
fontTools/feaLib/builder.py,sha256=cJGF2d4ueHO5vvzcxnKRufFsIbaJsYvTlBsrDyxDMKI,73052
|
|
43
43
|
fontTools/feaLib/error.py,sha256=Bz_5tNcNVcY7_nrAmFlQNhQldtqZWd8WUGQ2E3PWhZo,648
|
|
44
44
|
fontTools/feaLib/lexer.py,sha256=emyMPmRoqNZkzxnJyI6JRCCtXrbCOFofwa9O6ABGLiw,11121
|
|
45
45
|
fontTools/feaLib/location.py,sha256=JXzHqGV56EHdcq823AwA5oaK05hf_1ySWpScbo3zGC0,234
|
|
@@ -49,10 +49,10 @@ fontTools/feaLib/variableScalar.py,sha256=Xu8tpDlQbfIfjnKnYDEf43EqVdyIJUy8_1ROVP
|
|
|
49
49
|
fontTools/merge/__init__.py,sha256=-l65-mbTwSh0gjarnojIfsAX-ZkMtdz3vGTjtYHQ2ws,8250
|
|
50
50
|
fontTools/merge/__main__.py,sha256=hDx3gfbUBO83AJKumSEhiV-xqNTJNNgK2uFjazOGTmw,94
|
|
51
51
|
fontTools/merge/base.py,sha256=l0G1Px98E9ZdVuFLMUBKWdtr7Jb8JX8vxcjeaDUUnzY,2389
|
|
52
|
-
fontTools/merge/cmap.py,sha256=
|
|
52
|
+
fontTools/merge/cmap.py,sha256=HpthxVH5lA7VegJ8yHoBjd9vrFBV7UB5OknKGYpxWY8,6728
|
|
53
53
|
fontTools/merge/layout.py,sha256=fkMPGPLxEdxohS3scVM4W7LmNthSz-UPyocsffe2KqE,16075
|
|
54
54
|
fontTools/merge/options.py,sha256=xko_1-WErcNQkirECzIOOYxSJR_bRtdQYQYOtmgccYI,2501
|
|
55
|
-
fontTools/merge/tables.py,sha256=
|
|
55
|
+
fontTools/merge/tables.py,sha256=7SzXYL04awDEDhvU2-9T_8A2gAjvgGyYAHUICUJOpZg,10958
|
|
56
56
|
fontTools/merge/unicode.py,sha256=kb1Jrfuoq1KUcVhhSKnflAED_wMZxXDjVwB-CI9k05Y,4273
|
|
57
57
|
fontTools/merge/util.py,sha256=BH3bZWNFy-Tsj1cth7aSpGVJ18YXKXqDakPn6Wzku6U,3378
|
|
58
58
|
fontTools/misc/__init__.py,sha256=DJBWmoX_Haau7qlgmvWyfbhSzrX2qL636Rns7CG01pk,75
|
|
@@ -71,7 +71,7 @@ fontTools/misc/fixedTools.py,sha256=gsotTCOJLyMis13M4_jQJ8-QPob2Gl2TtNJhW6FER1I,
|
|
|
71
71
|
fontTools/misc/intTools.py,sha256=l6pjk4UYlXcyLtfC0DdOC5RL6UJ8ihRR0zRiYow5xA8,586
|
|
72
72
|
fontTools/misc/iterTools.py,sha256=17H6LPZszp32bTKoNorp6uZF1PKj47BAbe5QG8irUjo,390
|
|
73
73
|
fontTools/misc/lazyTools.py,sha256=BC6MmF-OzJ3GrBD8TYDZ-VCSN4UOx0pN0r3oF4GSoiw,1020
|
|
74
|
-
fontTools/misc/loggingTools.py,sha256=
|
|
74
|
+
fontTools/misc/loggingTools.py,sha256=NOYROsLK5TzONK5967OGdVonNyXC6kP_CmPr7M2PW_c,19933
|
|
75
75
|
fontTools/misc/macCreatorType.py,sha256=Je9jtqUr7EPbpH3QxlVl3pizoQ-1AOPMBIctHIMTM3k,1593
|
|
76
76
|
fontTools/misc/macRes.py,sha256=GT_pnfPw2NCvvOF86nHLAnOtZ6SMHqEuLntaplXzvHM,8579
|
|
77
77
|
fontTools/misc/psCharStrings.py,sha256=Tb5-k_5krP0eu7qD054iGxE4Zybk9oB4jdiKzcsV0rw,43036
|
|
@@ -80,7 +80,7 @@ fontTools/misc/psOperators.py,sha256=9SLl5PPBulLo0Xxg_dqlJMitNIBdiGKdkXhOWsNSYZE
|
|
|
80
80
|
fontTools/misc/py23.py,sha256=aPVCEUz_deggwLBCeTSsccX6QgJavZqvdVtuhpzrPvA,2238
|
|
81
81
|
fontTools/misc/roundTools.py,sha256=1RSXZ0gyi1qW42tz6WSBMJD1FlPdtgqKfWixVN9bd78,3173
|
|
82
82
|
fontTools/misc/sstruct.py,sha256=HuXwoRr9-mAbBxI3gJ3n34ML7NAGSHsAAazaaloWQB4,7158
|
|
83
|
-
fontTools/misc/symfont.py,sha256=
|
|
83
|
+
fontTools/misc/symfont.py,sha256=x5ZwqK9Ik9orG6qSftgVGygBFE1wTSngrMK2We1Z5AM,6977
|
|
84
84
|
fontTools/misc/testTools.py,sha256=3vj_KllUQVEiVFbS0SzTmeuKv44-L-disI1dZ4XhOfw,7052
|
|
85
85
|
fontTools/misc/textTools.py,sha256=pbhr6LVhm3J-0Z4saYnJfxBDzyoiw4BR9pAgwypiOw8,3377
|
|
86
86
|
fontTools/misc/timeTools.py,sha256=e9h5pgzL04tBDXmCv_8eRGB4boFV8GKXlS6dq3ggEpw,2234
|
|
@@ -92,10 +92,10 @@ fontTools/misc/xmlReader.py,sha256=igut4_d13RT4WarliqVvuuPybO1uSXVeoBOeW4j0_e4,6
|
|
|
92
92
|
fontTools/misc/xmlWriter.py,sha256=CA1c-Ov5vFTF9tT4bGk-f3yBvaX7lVmSdLPYygUqlAE,6046
|
|
93
93
|
fontTools/misc/plistlib/__init__.py,sha256=1HfhHPt3As6u2eRSlFfl6XdnXv_ypQImeQdWIw6wK7Y,21113
|
|
94
94
|
fontTools/misc/plistlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
95
|
-
fontTools/mtiLib/__init__.py,sha256=
|
|
95
|
+
fontTools/mtiLib/__init__.py,sha256=EzYwNaENLf906h1THBeq6nSRHUKpOAYxuzO9x9PHzh8,46602
|
|
96
96
|
fontTools/mtiLib/__main__.py,sha256=gd8X89jnZOe-752k7uaR1lWoiju-2zIT5Yx35Kl0Xek,94
|
|
97
97
|
fontTools/otlLib/__init__.py,sha256=D2leUW-3gsUTOFcJYGC18edBYjIJ804ut4qitJYWsaQ,45
|
|
98
|
-
fontTools/otlLib/builder.py,sha256=
|
|
98
|
+
fontTools/otlLib/builder.py,sha256=nv1bEbN-Q3OhDZJyz3t1CK4M-EoFZ1iPHIPHIQUBNB8,127669
|
|
99
99
|
fontTools/otlLib/error.py,sha256=cthuhBuOwZYpkTLi5gFPupUxkXkCHe-L_YgkE7N1wCI,335
|
|
100
100
|
fontTools/otlLib/maxContextCalc.py,sha256=3es4Kt84TaZ49sA2ev1zrlwPJikJCAECx5KavwhyB-I,3175
|
|
101
101
|
fontTools/otlLib/optimize/__init__.py,sha256=UUQRpNkHU2RczCRt-Gz7sEiYE9AQq9BHLXZEOyvsnX4,1530
|
|
@@ -125,7 +125,7 @@ fontTools/pens/reverseContourPen.py,sha256=oz64ZRhLAvT7DYMAwGKoLzZXQK8l81jRiYnTZ
|
|
|
125
125
|
fontTools/pens/roundingPen.py,sha256=Q4vvG0Esq_sLNODU0TITU4F3wcXcKWo4BA7DWdDaVcM,4649
|
|
126
126
|
fontTools/pens/statisticsPen.py,sha256=piWK6NjjWqk9MLROjeE2-4EsxVYMyNU7UQFGD_trE9g,9808
|
|
127
127
|
fontTools/pens/svgPathPen.py,sha256=T3b6SZS9B9sVWMK9mSFDtjHeviQs_yOJOZKq5Sg5Zdg,8572
|
|
128
|
-
fontTools/pens/t2CharStringPen.py,sha256=
|
|
128
|
+
fontTools/pens/t2CharStringPen.py,sha256=GgGklb5XsCer0w37ujgRLRXx-EuzdFsyCYuzCx4n-Qs,2931
|
|
129
129
|
fontTools/pens/teePen.py,sha256=P1ARJOCMJ6MxK-PB1yZ-ips3CUfnadWYnQ_do6VIasQ,1290
|
|
130
130
|
fontTools/pens/transformPen.py,sha256=s0kUyQdnemUwHvYr2SFboFmh4WY1S9OHBL8L4PJKRwE,4056
|
|
131
131
|
fontTools/pens/ttGlyphPen.py,sha256=yLtB-E5pTQR59OKVYySttWBu1xC2vR8ezSaRhIMtVwg,11870
|
|
@@ -135,7 +135,7 @@ fontTools/qu2cu/__main__.py,sha256=9FWf6SIZaRaC8SiL0LhjAWC2yIdY9N_9wlRko8m1l2Q,9
|
|
|
135
135
|
fontTools/qu2cu/benchmark.py,sha256=GMcr_4r7L6K9SmJ13itt-_XKhnKqSVUDPlXUG6IZmmM,1400
|
|
136
136
|
fontTools/qu2cu/cli.py,sha256=U2rooYnVVEalGRAWGFHk-Kp6Okys8wtzdaWLjw1bngY,3714
|
|
137
137
|
fontTools/qu2cu/qu2cu.py,sha256=IYtpkwHdfKOXJr65Y_pJ9Lrt_MgJaISAKGMAs5ilFSM,12288
|
|
138
|
-
fontTools/subset/__init__.py,sha256=
|
|
138
|
+
fontTools/subset/__init__.py,sha256=c3EulDBf-RYUaUFTM_JoIuVHvmLK7Fwf4_IlgxS46BY,137659
|
|
139
139
|
fontTools/subset/__main__.py,sha256=bhtfP2SqP4k799pxtksFgnC-XGNQDr3LcO4lc8T5e5g,95
|
|
140
140
|
fontTools/subset/cff.py,sha256=rqMRJOlX5FacV1LW8aDlVOglgEM87TkMA9bdsYenask,6145
|
|
141
141
|
fontTools/subset/svg.py,sha256=8dLBzQlnIt4_fOKEFDAVlKTucdHvcbCcyG9-a6UBZZ0,9384
|
|
@@ -150,7 +150,7 @@ fontTools/ttLib/__init__.py,sha256=1k7qp9z04gA3m6GvxDaINjqrKbzOkdTA_4RnqW_-LrA,6
|
|
|
150
150
|
fontTools/ttLib/__main__.py,sha256=lHMPWsnzjKPuMFavf6i1gpk9KexiAk4qzgDd50Mbby0,4733
|
|
151
151
|
fontTools/ttLib/macUtils.py,sha256=lj3oeFpyjV7ko_JqnluneITmAtlc119J-vwTTg2s73A,1737
|
|
152
152
|
fontTools/ttLib/removeOverlaps.py,sha256=YBtj1PX-d2jMgCiWGuI6ibghWApUWqH2trJGXNxrbjQ,12612
|
|
153
|
-
fontTools/ttLib/reorderGlyphs.py,sha256=
|
|
153
|
+
fontTools/ttLib/reorderGlyphs.py,sha256=TbxLxqPTUGiKRX3ulGFCwVm2lEisFYlX6caONJr_4oY,10371
|
|
154
154
|
fontTools/ttLib/scaleUpem.py,sha256=U_-NGkwfS9GRIackdEXjGYZ-wSomcUPXQahDneLeArI,14618
|
|
155
155
|
fontTools/ttLib/sfnt.py,sha256=rkznKfteU_Rn9P65WSjFaiwQgpEAoh-TrQpvkQhdIlo,22832
|
|
156
156
|
fontTools/ttLib/standardGlyphOrder.py,sha256=7AY_fVWdtwZ4iv5uWdyKAUcbEQiSDt1lN4sqx9xXwE0,5785
|
|
@@ -259,7 +259,7 @@ fontTools/ttLib/tables/sbixGlyph.py,sha256=tjEUPVRfx6gr5yme8UytGTtVrimKN5qmbzT1G
|
|
|
259
259
|
fontTools/ttLib/tables/sbixStrike.py,sha256=gFyOlhRIGnd59y0SrhtsT2Ce4L3yaBrLoFJ_dK9u9mQ,6663
|
|
260
260
|
fontTools/ttLib/tables/table_API_readme.txt,sha256=eZlRTLUkLzc_9Ot3pdfhyMb3ahU0_Iipx0vSbzOVGy8,2748
|
|
261
261
|
fontTools/ttLib/tables/ttProgram.py,sha256=tgtxgd-EnOq-2PUlYEihp-6NHu_7HnE5rxeSAtmXOtU,35888
|
|
262
|
-
fontTools/ufoLib/__init__.py,sha256=
|
|
262
|
+
fontTools/ufoLib/__init__.py,sha256=_Um-de5MGbr7lxarlsoHSdxLpzAD88LfLEYMrnup2Sc,94431
|
|
263
263
|
fontTools/ufoLib/converters.py,sha256=9j4BQ7EXVfWiVoB7OZCzRGsRJWi9G9w_27iFuiJNphw,13044
|
|
264
264
|
fontTools/ufoLib/errors.py,sha256=9f8l5NaFAj3BZPa6Bbqt06FL4afffLuMzy4nPf-eOlE,845
|
|
265
265
|
fontTools/ufoLib/etree.py,sha256=T3sjLTgjMAq6VyYRicWPaMIVBJ2YSuwZxV6Vc5yZtQI,231
|
|
@@ -276,15 +276,15 @@ fontTools/unicodedata/OTTags.py,sha256=wOPpbMsNcp_gdvPFeITtgVMnTN8TJSNAsVEdu_nuP
|
|
|
276
276
|
fontTools/unicodedata/ScriptExtensions.py,sha256=YTZr2bOteHiz_7I4108PRy0Is4kFof-32yFuoKjFHjc,28207
|
|
277
277
|
fontTools/unicodedata/Scripts.py,sha256=I0nY08ovsZ4pHU5wYchjat9DjnxcpoTzaXAn5oFaKNI,130271
|
|
278
278
|
fontTools/unicodedata/__init__.py,sha256=Ega5OM4ksWZPCeJv99NLo2pksMpzMqlhkXCScGE6byM,8983
|
|
279
|
-
fontTools/varLib/__init__.py,sha256=
|
|
279
|
+
fontTools/varLib/__init__.py,sha256=dc1_8itv2pS3q6mO8vMuEe-XdWOiJLIWVsJ8-bhPflI,54236
|
|
280
280
|
fontTools/varLib/__main__.py,sha256=wbdYC5bPjWCxA0I4SKcLO88gl-UMtsYS8MxdW9ySTkY,95
|
|
281
281
|
fontTools/varLib/avar.py,sha256=Ye_u0HHznaPQaTzufNFKDj_v9o_LxOKJoa_eTK1D1F0,9647
|
|
282
282
|
fontTools/varLib/avarPlanner.py,sha256=uLMGsL6cBbEMq5YItwABG_vXlXV3bxquM93WGDJ1brA,27358
|
|
283
283
|
fontTools/varLib/builder.py,sha256=mSKOCcnnw-WzmZs15FayoqCDh77Ts7o9Tre9psh8CUc,6609
|
|
284
284
|
fontTools/varLib/cff.py,sha256=EVgaQcoROIrYQsRuftnxFuGGldEPYbrIh5yBckylJC4,22901
|
|
285
285
|
fontTools/varLib/errors.py,sha256=dMo8eGj76I7H4hrBEiNbYrGs2J1K1SwdsUyTHpkVOrQ,6934
|
|
286
|
-
fontTools/varLib/featureVars.py,sha256=
|
|
287
|
-
fontTools/varLib/hvar.py,sha256=
|
|
286
|
+
fontTools/varLib/featureVars.py,sha256=ZmHPyy4KuamR4bI1PfH-Umk4EN_CfwvNfchu7BOmECg,25695
|
|
287
|
+
fontTools/varLib/hvar.py,sha256=u7ppYCyWRBbJhIYyBcOxuVvwbZHnlOmXlUJH74a3LHs,3695
|
|
288
288
|
fontTools/varLib/interpolatable.py,sha256=Bhlq_LhEZ-sXfLNY8aFEChFrsKuT2kzmnuMfG5qi0v4,45221
|
|
289
289
|
fontTools/varLib/interpolatableHelpers.py,sha256=lXd7kwfIVl-4opd-vxCDhf48RnJ7IQKv_uuFQM_6vaU,11496
|
|
290
290
|
fontTools/varLib/interpolatablePlot.py,sha256=w393P6mGLRhYkIjSxMww3qyoYxAUZzCXlmPBbI_84C0,44375
|
|
@@ -300,7 +300,7 @@ fontTools/varLib/mvar.py,sha256=LTV77vH_3Ecg_qKBO5xQzjLOlJir_ppEr7mPVZRgad8,2449
|
|
|
300
300
|
fontTools/varLib/plot.py,sha256=NoSZkJ5ndxNcDvJIvd5pQ9_jX6X1oM1K2G_tR4sdPVs,7494
|
|
301
301
|
fontTools/varLib/stat.py,sha256=XuNKKZxGlBrl4OGFDAwVXhpBwJi23U3BdHmNTKoJnvE,4811
|
|
302
302
|
fontTools/varLib/varStore.py,sha256=2QA9SDI6jQyQ_zq82OOwa3FBkfl-ksaSo1KGmVFpa9Q,24069
|
|
303
|
-
fontTools/varLib/instancer/__init__.py,sha256=
|
|
303
|
+
fontTools/varLib/instancer/__init__.py,sha256=4I3M5PL3rLcch_yw_rflb0f09SG3zA3eADws-purFXg,71827
|
|
304
304
|
fontTools/varLib/instancer/__main__.py,sha256=zfULwcP01FhplS1IlcMgNQnLxk5RVfmOuinWjqeid-g,104
|
|
305
305
|
fontTools/varLib/instancer/featureVars.py,sha256=oPqSlnHLMDTtOsmQMi6gkzLox7ymCrqlRAkvC_EJ4bc,7110
|
|
306
306
|
fontTools/varLib/instancer/names.py,sha256=IPRqel_M8zVU0jl30WsfgufxUm9PBBQDQCY3VHapeHc,14950
|
|
@@ -312,11 +312,11 @@ fontTools/voltLib/error.py,sha256=phcQOQj-xOspCXu9hBJQRhSOBDzxHRgZd3fWQOFNJzw,39
|
|
|
312
312
|
fontTools/voltLib/lexer.py,sha256=OvuETOSvlS6v7iCVeJ3IdH2Cg71n3OJoEyiB3-h6vhE,3368
|
|
313
313
|
fontTools/voltLib/parser.py,sha256=rkw2IHBZPsrhGVC7Kw7V501m0u52kh1JSM5HXp-xchM,25396
|
|
314
314
|
fontTools/voltLib/voltToFea.py,sha256=Z2yvnaZLQXzPLT86Uta0zRsXIYgj6NnvZtSWt5xmw2s,36549
|
|
315
|
-
fonttools-4.58.
|
|
316
|
-
fonttools-4.58.
|
|
317
|
-
fonttools-4.58.
|
|
318
|
-
fonttools-4.58.
|
|
319
|
-
fonttools-4.58.
|
|
320
|
-
fonttools-4.58.
|
|
321
|
-
fonttools-4.58.
|
|
322
|
-
fonttools-4.58.
|
|
315
|
+
fonttools-4.58.2.data/data/share/man/man1/ttx.1,sha256=cLbm_pOOj1C76T2QXvDxzwDj9gk-GTd5RztvTMsouFw,5377
|
|
316
|
+
fonttools-4.58.2.dist-info/licenses/LICENSE,sha256=Z4cgj4P2Wcy8IiOy_elS_6b36KymLxqKK_W8UbsbI4M,1072
|
|
317
|
+
fonttools-4.58.2.dist-info/licenses/LICENSE.external,sha256=1IdixYQuQZseNUNQ1bR3SJQhW1d5SGRUR23TNBJjzwo,18687
|
|
318
|
+
fonttools-4.58.2.dist-info/METADATA,sha256=rzM2sNi609hxqlGZhiaNgS6kxP83lj4AqumcSBLDxO8,106341
|
|
319
|
+
fonttools-4.58.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
320
|
+
fonttools-4.58.2.dist-info/entry_points.txt,sha256=8kVHddxfFWA44FSD4mBpmC-4uCynQnkoz_9aNJb227Y,147
|
|
321
|
+
fonttools-4.58.2.dist-info/top_level.txt,sha256=rRgRylrXzekqWOsrhygzib12pQ7WILf7UGjqEwkIFDM,10
|
|
322
|
+
fonttools-4.58.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|