fonttools 4.55.4__cp313-cp313-musllinux_1_2_aarch64.whl → 4.61.1__cp313-cp313-musllinux_1_2_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fontTools/__init__.py +1 -1
- fontTools/annotations.py +30 -0
- fontTools/cffLib/CFF2ToCFF.py +65 -10
- fontTools/cffLib/__init__.py +61 -26
- fontTools/cffLib/specializer.py +4 -1
- fontTools/cffLib/transforms.py +11 -6
- fontTools/config/__init__.py +15 -0
- fontTools/cu2qu/cu2qu.c +6567 -5579
- fontTools/cu2qu/cu2qu.cpython-313-aarch64-linux-musl.so +0 -0
- fontTools/cu2qu/cu2qu.py +36 -4
- fontTools/cu2qu/ufo.py +14 -0
- fontTools/designspaceLib/__init__.py +8 -3
- fontTools/designspaceLib/statNames.py +14 -7
- fontTools/feaLib/ast.py +24 -15
- fontTools/feaLib/builder.py +139 -66
- fontTools/feaLib/error.py +1 -1
- fontTools/feaLib/lexer.c +7038 -7995
- fontTools/feaLib/lexer.cpython-313-aarch64-linux-musl.so +0 -0
- fontTools/feaLib/parser.py +75 -40
- fontTools/feaLib/variableScalar.py +6 -1
- fontTools/fontBuilder.py +50 -44
- fontTools/merge/__init__.py +1 -1
- fontTools/merge/cmap.py +33 -1
- fontTools/merge/tables.py +12 -1
- fontTools/misc/bezierTools.c +14913 -17013
- fontTools/misc/bezierTools.cpython-313-aarch64-linux-musl.so +0 -0
- fontTools/misc/bezierTools.py +4 -1
- fontTools/misc/configTools.py +3 -1
- fontTools/misc/enumTools.py +23 -0
- fontTools/misc/etree.py +4 -27
- fontTools/misc/filesystem/__init__.py +68 -0
- fontTools/misc/filesystem/_base.py +134 -0
- fontTools/misc/filesystem/_copy.py +45 -0
- fontTools/misc/filesystem/_errors.py +54 -0
- fontTools/misc/filesystem/_info.py +75 -0
- fontTools/misc/filesystem/_osfs.py +164 -0
- fontTools/misc/filesystem/_path.py +67 -0
- fontTools/misc/filesystem/_subfs.py +92 -0
- fontTools/misc/filesystem/_tempfs.py +34 -0
- fontTools/misc/filesystem/_tools.py +34 -0
- fontTools/misc/filesystem/_walk.py +55 -0
- fontTools/misc/filesystem/_zipfs.py +204 -0
- fontTools/misc/fixedTools.py +1 -1
- fontTools/misc/loggingTools.py +1 -1
- fontTools/misc/psCharStrings.py +17 -2
- fontTools/misc/sstruct.py +2 -6
- fontTools/misc/symfont.py +6 -8
- fontTools/misc/testTools.py +5 -1
- fontTools/misc/textTools.py +4 -2
- fontTools/misc/visitor.py +32 -16
- fontTools/misc/xmlWriter.py +44 -8
- fontTools/mtiLib/__init__.py +1 -3
- fontTools/otlLib/builder.py +402 -155
- fontTools/otlLib/optimize/gpos.py +49 -63
- fontTools/pens/filterPen.py +218 -26
- fontTools/pens/momentsPen.c +5514 -5584
- fontTools/pens/momentsPen.cpython-313-aarch64-linux-musl.so +0 -0
- fontTools/pens/pointPen.py +61 -18
- fontTools/pens/roundingPen.py +2 -2
- fontTools/pens/t2CharStringPen.py +31 -11
- fontTools/qu2cu/qu2cu.c +6581 -6168
- fontTools/qu2cu/qu2cu.cpython-313-aarch64-linux-musl.so +0 -0
- fontTools/subset/__init__.py +283 -25
- fontTools/subset/svg.py +2 -3
- fontTools/ttLib/__init__.py +4 -0
- fontTools/ttLib/__main__.py +47 -8
- fontTools/ttLib/removeOverlaps.py +7 -5
- fontTools/ttLib/reorderGlyphs.py +8 -7
- fontTools/ttLib/sfnt.py +11 -9
- fontTools/ttLib/tables/D__e_b_g.py +20 -2
- fontTools/ttLib/tables/G_V_A_R_.py +5 -0
- fontTools/ttLib/tables/S__i_l_f.py +2 -2
- fontTools/ttLib/tables/T_S_I__0.py +14 -3
- fontTools/ttLib/tables/T_S_I__1.py +2 -5
- fontTools/ttLib/tables/T_S_I__5.py +18 -7
- fontTools/ttLib/tables/__init__.py +1 -0
- fontTools/ttLib/tables/_a_v_a_r.py +12 -3
- fontTools/ttLib/tables/_c_m_a_p.py +20 -7
- fontTools/ttLib/tables/_c_v_t.py +3 -2
- fontTools/ttLib/tables/_f_p_g_m.py +3 -1
- fontTools/ttLib/tables/_g_l_y_f.py +45 -21
- fontTools/ttLib/tables/_g_v_a_r.py +67 -19
- fontTools/ttLib/tables/_h_d_m_x.py +4 -4
- fontTools/ttLib/tables/_h_m_t_x.py +7 -3
- fontTools/ttLib/tables/_l_o_c_a.py +2 -2
- fontTools/ttLib/tables/_n_a_m_e.py +11 -6
- fontTools/ttLib/tables/_p_o_s_t.py +9 -7
- fontTools/ttLib/tables/otBase.py +5 -12
- fontTools/ttLib/tables/otConverters.py +5 -2
- fontTools/ttLib/tables/otData.py +1 -1
- fontTools/ttLib/tables/otTables.py +33 -30
- fontTools/ttLib/tables/otTraverse.py +2 -1
- fontTools/ttLib/tables/sbixStrike.py +3 -3
- fontTools/ttLib/ttFont.py +666 -120
- fontTools/ttLib/ttGlyphSet.py +0 -10
- fontTools/ttLib/woff2.py +10 -13
- fontTools/ttx.py +13 -1
- fontTools/ufoLib/__init__.py +300 -202
- fontTools/ufoLib/converters.py +103 -30
- fontTools/ufoLib/errors.py +8 -0
- fontTools/ufoLib/etree.py +1 -1
- fontTools/ufoLib/filenames.py +171 -106
- fontTools/ufoLib/glifLib.py +303 -205
- fontTools/ufoLib/kerning.py +98 -48
- fontTools/ufoLib/utils.py +46 -15
- fontTools/ufoLib/validators.py +121 -99
- fontTools/unicodedata/Blocks.py +35 -20
- fontTools/unicodedata/Mirrored.py +446 -0
- fontTools/unicodedata/ScriptExtensions.py +63 -37
- fontTools/unicodedata/Scripts.py +173 -152
- fontTools/unicodedata/__init__.py +10 -2
- fontTools/varLib/__init__.py +198 -109
- fontTools/varLib/avar/__init__.py +0 -0
- fontTools/varLib/avar/__main__.py +72 -0
- fontTools/varLib/avar/build.py +79 -0
- fontTools/varLib/avar/map.py +108 -0
- fontTools/varLib/avar/plan.py +1004 -0
- fontTools/varLib/{avar.py → avar/unbuild.py} +70 -59
- fontTools/varLib/avarPlanner.py +3 -999
- fontTools/varLib/featureVars.py +21 -7
- fontTools/varLib/hvar.py +113 -0
- fontTools/varLib/instancer/__init__.py +180 -65
- fontTools/varLib/interpolatableHelpers.py +3 -0
- fontTools/varLib/iup.c +7564 -6903
- fontTools/varLib/iup.cpython-313-aarch64-linux-musl.so +0 -0
- fontTools/varLib/models.py +17 -2
- fontTools/varLib/mutator.py +11 -0
- fontTools/varLib/varStore.py +10 -38
- fontTools/voltLib/__main__.py +206 -0
- fontTools/voltLib/ast.py +4 -0
- fontTools/voltLib/parser.py +16 -8
- fontTools/voltLib/voltToFea.py +347 -166
- {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/METADATA +269 -1410
- {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/RECORD +318 -294
- {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/WHEEL +1 -1
- fonttools-4.61.1.dist-info/licenses/LICENSE.external +388 -0
- {fonttools-4.55.4.data → fonttools-4.61.1.data}/data/share/man/man1/ttx.1 +0 -0
- {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/entry_points.txt +0 -0
- {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info/licenses}/LICENSE +0 -0
- {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/top_level.txt +0 -0
|
Binary file
|
fontTools/feaLib/parser.py
CHANGED
|
@@ -887,6 +887,11 @@ class Parser(object):
|
|
|
887
887
|
|
|
888
888
|
is_deletion = False
|
|
889
889
|
if len(new) == 1 and isinstance(new[0], ast.NullGlyph):
|
|
890
|
+
if reverse:
|
|
891
|
+
raise FeatureLibError(
|
|
892
|
+
"Reverse chaining substitutions do not support glyph deletion",
|
|
893
|
+
location,
|
|
894
|
+
)
|
|
890
895
|
new = [] # Deletion
|
|
891
896
|
is_deletion = True
|
|
892
897
|
|
|
@@ -1286,6 +1291,19 @@ class Parser(object):
|
|
|
1286
1291
|
n = match.group(0)[1:]
|
|
1287
1292
|
return bytechr(int(n, 16)).decode(encoding)
|
|
1288
1293
|
|
|
1294
|
+
def find_previous(self, statements, class_):
|
|
1295
|
+
for previous in reversed(statements):
|
|
1296
|
+
if isinstance(previous, self.ast.Comment):
|
|
1297
|
+
continue
|
|
1298
|
+
elif isinstance(previous, class_):
|
|
1299
|
+
return previous
|
|
1300
|
+
else:
|
|
1301
|
+
# If we find something that doesn't match what we're looking
|
|
1302
|
+
# for, and isn't a comment, fail
|
|
1303
|
+
return None
|
|
1304
|
+
# Out of statements to look at
|
|
1305
|
+
return None
|
|
1306
|
+
|
|
1289
1307
|
def parse_table_BASE_(self, table):
|
|
1290
1308
|
statements = table.statements
|
|
1291
1309
|
while self.next_token_ != "}" or self.cur_comments_:
|
|
@@ -1306,6 +1324,19 @@ class Parser(object):
|
|
|
1306
1324
|
location=self.cur_token_location_,
|
|
1307
1325
|
)
|
|
1308
1326
|
)
|
|
1327
|
+
elif self.is_cur_keyword_("HorizAxis.MinMax"):
|
|
1328
|
+
base_script_list = self.find_previous(statements, ast.BaseAxis)
|
|
1329
|
+
if base_script_list is None:
|
|
1330
|
+
raise FeatureLibError(
|
|
1331
|
+
"MinMax must be preceded by BaseScriptList",
|
|
1332
|
+
self.cur_token_location_,
|
|
1333
|
+
)
|
|
1334
|
+
if base_script_list.vertical:
|
|
1335
|
+
raise FeatureLibError(
|
|
1336
|
+
"HorizAxis.MinMax must be preceded by HorizAxis statements",
|
|
1337
|
+
self.cur_token_location_,
|
|
1338
|
+
)
|
|
1339
|
+
base_script_list.minmax.append(self.parse_base_minmax_())
|
|
1309
1340
|
elif self.is_cur_keyword_("VertAxis.BaseTagList"):
|
|
1310
1341
|
vert_bases = self.parse_base_tag_list_()
|
|
1311
1342
|
elif self.is_cur_keyword_("VertAxis.BaseScriptList"):
|
|
@@ -1318,6 +1349,19 @@ class Parser(object):
|
|
|
1318
1349
|
location=self.cur_token_location_,
|
|
1319
1350
|
)
|
|
1320
1351
|
)
|
|
1352
|
+
elif self.is_cur_keyword_("VertAxis.MinMax"):
|
|
1353
|
+
base_script_list = self.find_previous(statements, ast.BaseAxis)
|
|
1354
|
+
if base_script_list is None:
|
|
1355
|
+
raise FeatureLibError(
|
|
1356
|
+
"MinMax must be preceded by BaseScriptList",
|
|
1357
|
+
self.cur_token_location_,
|
|
1358
|
+
)
|
|
1359
|
+
if not base_script_list.vertical:
|
|
1360
|
+
raise FeatureLibError(
|
|
1361
|
+
"VertAxis.MinMax must be preceded by VertAxis statements",
|
|
1362
|
+
self.cur_token_location_,
|
|
1363
|
+
)
|
|
1364
|
+
base_script_list.minmax.append(self.parse_base_minmax_())
|
|
1321
1365
|
elif self.cur_token_ == ";":
|
|
1322
1366
|
continue
|
|
1323
1367
|
|
|
@@ -1574,7 +1618,7 @@ class Parser(object):
|
|
|
1574
1618
|
"HorizAxis.BaseScriptList",
|
|
1575
1619
|
"VertAxis.BaseScriptList",
|
|
1576
1620
|
), self.cur_token_
|
|
1577
|
-
scripts = [
|
|
1621
|
+
scripts = [self.parse_base_script_record_(count)]
|
|
1578
1622
|
while self.next_token_ == ",":
|
|
1579
1623
|
self.expect_symbol_(",")
|
|
1580
1624
|
scripts.append(self.parse_base_script_record_(count))
|
|
@@ -1587,6 +1631,25 @@ class Parser(object):
|
|
|
1587
1631
|
coords = [self.expect_number_() for i in range(count)]
|
|
1588
1632
|
return script_tag, base_tag, coords
|
|
1589
1633
|
|
|
1634
|
+
def parse_base_minmax_(self):
|
|
1635
|
+
script_tag = self.expect_script_tag_()
|
|
1636
|
+
language = self.expect_language_tag_()
|
|
1637
|
+
min_coord = self.expect_number_()
|
|
1638
|
+
self.advance_lexer_()
|
|
1639
|
+
if not (self.cur_token_type_ is Lexer.SYMBOL and self.cur_token_ == ","):
|
|
1640
|
+
raise FeatureLibError(
|
|
1641
|
+
"Expected a comma between min and max coordinates",
|
|
1642
|
+
self.cur_token_location_,
|
|
1643
|
+
)
|
|
1644
|
+
max_coord = self.expect_number_()
|
|
1645
|
+
if self.next_token_ == ",": # feature tag...
|
|
1646
|
+
raise FeatureLibError(
|
|
1647
|
+
"Feature tags are not yet supported in BASE table",
|
|
1648
|
+
self.cur_token_location_,
|
|
1649
|
+
)
|
|
1650
|
+
|
|
1651
|
+
return script_tag, language, min_coord, max_coord
|
|
1652
|
+
|
|
1590
1653
|
def parse_device_(self):
|
|
1591
1654
|
result = None
|
|
1592
1655
|
self.expect_symbol_("<")
|
|
@@ -2004,44 +2067,6 @@ class Parser(object):
|
|
|
2004
2067
|
)
|
|
2005
2068
|
self.expect_symbol_(";")
|
|
2006
2069
|
|
|
2007
|
-
# A multiple substitution may have a single destination, in which case
|
|
2008
|
-
# it will look just like a single substitution. So if there are both
|
|
2009
|
-
# multiple and single substitutions, upgrade all the single ones to
|
|
2010
|
-
# multiple substitutions.
|
|
2011
|
-
|
|
2012
|
-
# Check if we have a mix of non-contextual singles and multiples.
|
|
2013
|
-
has_single = False
|
|
2014
|
-
has_multiple = False
|
|
2015
|
-
for s in statements:
|
|
2016
|
-
if isinstance(s, self.ast.SingleSubstStatement):
|
|
2017
|
-
has_single = not any([s.prefix, s.suffix, s.forceChain])
|
|
2018
|
-
elif isinstance(s, self.ast.MultipleSubstStatement):
|
|
2019
|
-
has_multiple = not any([s.prefix, s.suffix, s.forceChain])
|
|
2020
|
-
|
|
2021
|
-
# Upgrade all single substitutions to multiple substitutions.
|
|
2022
|
-
if has_single and has_multiple:
|
|
2023
|
-
statements = []
|
|
2024
|
-
for s in block.statements:
|
|
2025
|
-
if isinstance(s, self.ast.SingleSubstStatement):
|
|
2026
|
-
glyphs = s.glyphs[0].glyphSet()
|
|
2027
|
-
replacements = s.replacements[0].glyphSet()
|
|
2028
|
-
if len(replacements) == 1:
|
|
2029
|
-
replacements *= len(glyphs)
|
|
2030
|
-
for i, glyph in enumerate(glyphs):
|
|
2031
|
-
statements.append(
|
|
2032
|
-
self.ast.MultipleSubstStatement(
|
|
2033
|
-
s.prefix,
|
|
2034
|
-
glyph,
|
|
2035
|
-
s.suffix,
|
|
2036
|
-
[replacements[i]],
|
|
2037
|
-
s.forceChain,
|
|
2038
|
-
location=s.location,
|
|
2039
|
-
)
|
|
2040
|
-
)
|
|
2041
|
-
else:
|
|
2042
|
-
statements.append(s)
|
|
2043
|
-
block.statements = statements
|
|
2044
|
-
|
|
2045
2070
|
def is_cur_keyword_(self, k):
|
|
2046
2071
|
if self.cur_token_type_ is Lexer.NAME:
|
|
2047
2072
|
if isinstance(k, type("")): # basestring is gone in Python3
|
|
@@ -2178,7 +2203,7 @@ class Parser(object):
|
|
|
2178
2203
|
raise FeatureLibError(
|
|
2179
2204
|
"Expected an equals sign", self.cur_token_location_
|
|
2180
2205
|
)
|
|
2181
|
-
value = self.
|
|
2206
|
+
value = self.expect_integer_or_float_()
|
|
2182
2207
|
location[axis] = value
|
|
2183
2208
|
if self.next_token_type_ is Lexer.NAME and self.next_token_[0] == ":":
|
|
2184
2209
|
# Lexer has just read the value as a glyph name. We'll correct it later
|
|
@@ -2210,6 +2235,16 @@ class Parser(object):
|
|
|
2210
2235
|
"Expected a floating-point number", self.cur_token_location_
|
|
2211
2236
|
)
|
|
2212
2237
|
|
|
2238
|
+
def expect_integer_or_float_(self):
|
|
2239
|
+
if self.next_token_type_ == Lexer.FLOAT:
|
|
2240
|
+
return self.expect_float_()
|
|
2241
|
+
elif self.next_token_type_ is Lexer.NUMBER:
|
|
2242
|
+
return self.expect_number_()
|
|
2243
|
+
else:
|
|
2244
|
+
raise FeatureLibError(
|
|
2245
|
+
"Expected an integer or floating-point number", self.cur_token_location_
|
|
2246
|
+
)
|
|
2247
|
+
|
|
2213
2248
|
def expect_decipoint_(self):
|
|
2214
2249
|
if self.next_token_type_ == Lexer.FLOAT:
|
|
2215
2250
|
return self.expect_float_()
|
|
@@ -17,7 +17,12 @@ class VariableScalar:
|
|
|
17
17
|
def __repr__(self):
|
|
18
18
|
items = []
|
|
19
19
|
for location, value in self.values.items():
|
|
20
|
-
loc = ",".join(
|
|
20
|
+
loc = ",".join(
|
|
21
|
+
[
|
|
22
|
+
f"{ax}={int(coord) if float(coord).is_integer() else coord}"
|
|
23
|
+
for ax, coord in location
|
|
24
|
+
]
|
|
25
|
+
)
|
|
21
26
|
items.append("%s:%i" % (loc, value))
|
|
22
27
|
return "(" + (" ".join(items)) + ")"
|
|
23
28
|
|
fontTools/fontBuilder.py
CHANGED
|
@@ -264,49 +264,49 @@ _nameIDs = dict(
|
|
|
264
264
|
# to insert in setupNameTable doc string:
|
|
265
265
|
# print("\n".join(("%s (nameID %s)" % (k, v)) for k, v in sorted(_nameIDs.items(), key=lambda x: x[1])))
|
|
266
266
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
)
|
|
267
|
+
|
|
268
|
+
def _getOS2Defaults():
|
|
269
|
+
return dict(
|
|
270
|
+
version=3,
|
|
271
|
+
xAvgCharWidth=0,
|
|
272
|
+
usWeightClass=400,
|
|
273
|
+
usWidthClass=5,
|
|
274
|
+
fsType=0x0004, # default: Preview & Print embedding
|
|
275
|
+
ySubscriptXSize=0,
|
|
276
|
+
ySubscriptYSize=0,
|
|
277
|
+
ySubscriptXOffset=0,
|
|
278
|
+
ySubscriptYOffset=0,
|
|
279
|
+
ySuperscriptXSize=0,
|
|
280
|
+
ySuperscriptYSize=0,
|
|
281
|
+
ySuperscriptXOffset=0,
|
|
282
|
+
ySuperscriptYOffset=0,
|
|
283
|
+
yStrikeoutSize=0,
|
|
284
|
+
yStrikeoutPosition=0,
|
|
285
|
+
sFamilyClass=0,
|
|
286
|
+
panose=Panose(),
|
|
287
|
+
ulUnicodeRange1=0,
|
|
288
|
+
ulUnicodeRange2=0,
|
|
289
|
+
ulUnicodeRange3=0,
|
|
290
|
+
ulUnicodeRange4=0,
|
|
291
|
+
achVendID="????",
|
|
292
|
+
fsSelection=0,
|
|
293
|
+
usFirstCharIndex=0,
|
|
294
|
+
usLastCharIndex=0,
|
|
295
|
+
sTypoAscender=0,
|
|
296
|
+
sTypoDescender=0,
|
|
297
|
+
sTypoLineGap=0,
|
|
298
|
+
usWinAscent=0,
|
|
299
|
+
usWinDescent=0,
|
|
300
|
+
ulCodePageRange1=0,
|
|
301
|
+
ulCodePageRange2=0,
|
|
302
|
+
sxHeight=0,
|
|
303
|
+
sCapHeight=0,
|
|
304
|
+
usDefaultChar=0, # .notdef
|
|
305
|
+
usBreakChar=32, # space
|
|
306
|
+
usMaxContext=0,
|
|
307
|
+
usLowerOpticalPointSize=0,
|
|
308
|
+
usUpperOpticalPointSize=0,
|
|
309
|
+
)
|
|
310
310
|
|
|
311
311
|
|
|
312
312
|
class FontBuilder(object):
|
|
@@ -493,7 +493,7 @@ class FontBuilder(object):
|
|
|
493
493
|
"""Create a new `OS/2` table and initialize it with default values,
|
|
494
494
|
which can be overridden by keyword arguments.
|
|
495
495
|
"""
|
|
496
|
-
self._initTableWithValues("OS/2",
|
|
496
|
+
self._initTableWithValues("OS/2", _getOS2Defaults(), values)
|
|
497
497
|
if "xAvgCharWidth" not in values:
|
|
498
498
|
assert (
|
|
499
499
|
"hmtx" in self.font
|
|
@@ -714,6 +714,12 @@ class FontBuilder(object):
|
|
|
714
714
|
gvar.reserved = 0
|
|
715
715
|
gvar.variations = variations
|
|
716
716
|
|
|
717
|
+
def setupGVAR(self, variations):
|
|
718
|
+
gvar = self.font["GVAR"] = newTable("GVAR")
|
|
719
|
+
gvar.version = 1
|
|
720
|
+
gvar.reserved = 0
|
|
721
|
+
gvar.variations = variations
|
|
722
|
+
|
|
717
723
|
def calcGlyphBounds(self):
|
|
718
724
|
"""Calculate the bounding boxes of all glyphs in the `glyf` table.
|
|
719
725
|
This is usually not called explicitly by client code.
|
fontTools/merge/__init__.py
CHANGED
|
@@ -196,7 +196,7 @@ def main(args=None):
|
|
|
196
196
|
|
|
197
197
|
if len(fontfiles) < 1:
|
|
198
198
|
print(
|
|
199
|
-
"usage:
|
|
199
|
+
"usage: fonttools merge [font1 ... fontN] [--input-file=filelist.txt] [--output-file=merged.ttf] [--import-file=tables.ttx]",
|
|
200
200
|
file=sys.stderr,
|
|
201
201
|
)
|
|
202
202
|
print(
|
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
|