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
|
@@ -15,8 +15,7 @@ except ImportError: # pragma: no cover
|
|
|
15
15
|
# fall back to built-in unicodedata (possibly outdated)
|
|
16
16
|
from unicodedata import *
|
|
17
17
|
|
|
18
|
-
from . import Blocks, Scripts, ScriptExtensions, OTTags
|
|
19
|
-
|
|
18
|
+
from . import Blocks, Mirrored, Scripts, ScriptExtensions, OTTags
|
|
20
19
|
|
|
21
20
|
__all__ = [
|
|
22
21
|
# names from built-in unicodedata module
|
|
@@ -46,6 +45,11 @@ __all__ = [
|
|
|
46
45
|
]
|
|
47
46
|
|
|
48
47
|
|
|
48
|
+
def mirrored(code):
|
|
49
|
+
"""If code (unicode codepoint) has a mirrored version returns it, otherwise None."""
|
|
50
|
+
return Mirrored.MIRRORED.get(code)
|
|
51
|
+
|
|
52
|
+
|
|
49
53
|
def script(char):
|
|
50
54
|
"""Return the four-letter script code assigned to the Unicode character
|
|
51
55
|
'char' as string.
|
|
@@ -193,6 +197,10 @@ RTL_SCRIPTS = {
|
|
|
193
197
|
"Yezi", # Yezidi
|
|
194
198
|
# Unicode-14.0 additions
|
|
195
199
|
"Ougr", # Old Uyghur
|
|
200
|
+
# Unicode-16.0 additions
|
|
201
|
+
"Gara", # Garay
|
|
202
|
+
# Unicode-17.0 additions
|
|
203
|
+
"Sidt", # Sidetic
|
|
196
204
|
}
|
|
197
205
|
|
|
198
206
|
|
fontTools/varLib/__init__.py
CHANGED
|
@@ -12,13 +12,13 @@ a Glyphs source, eg., using noto-source as an example:
|
|
|
12
12
|
|
|
13
13
|
.. code-block:: sh
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
$ fontmake -o ttf-interpolatable -g NotoSansArabic-MM.glyphs
|
|
16
16
|
|
|
17
17
|
Then you can make a variable-font this way:
|
|
18
18
|
|
|
19
19
|
.. code-block:: sh
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
$ fonttools varLib master_ufo/NotoSansArabic.designspace
|
|
22
22
|
|
|
23
23
|
API *will* change in near future.
|
|
24
24
|
"""
|
|
@@ -30,7 +30,11 @@ from fontTools.misc.fixedTools import floatToFixed as fl2fi
|
|
|
30
30
|
from fontTools.misc.textTools import Tag, tostr
|
|
31
31
|
from fontTools.ttLib import TTFont, newTable
|
|
32
32
|
from fontTools.ttLib.tables._f_v_a_r import Axis, NamedInstance
|
|
33
|
-
from fontTools.ttLib.tables._g_l_y_f import
|
|
33
|
+
from fontTools.ttLib.tables._g_l_y_f import (
|
|
34
|
+
GlyphCoordinates,
|
|
35
|
+
dropImpliedOnCurvePoints,
|
|
36
|
+
USE_MY_METRICS,
|
|
37
|
+
)
|
|
34
38
|
from fontTools.ttLib.tables.ttProgram import Program
|
|
35
39
|
from fontTools.ttLib.tables.TupleVariation import TupleVariation
|
|
36
40
|
from fontTools.ttLib.tables import otTables as ot
|
|
@@ -109,6 +113,8 @@ def _add_fvar(font, axes, instances: List[InstanceDescriptor]):
|
|
|
109
113
|
axis.flags = int(a.hidden)
|
|
110
114
|
fvar.axes.append(axis)
|
|
111
115
|
|
|
116
|
+
default_coordinates = {axis.axisTag: axis.defaultValue for axis in fvar.axes}
|
|
117
|
+
|
|
112
118
|
for instance in instances:
|
|
113
119
|
# Filter out discrete axis locations
|
|
114
120
|
coordinates = {
|
|
@@ -130,16 +136,26 @@ def _add_fvar(font, axes, instances: List[InstanceDescriptor]):
|
|
|
130
136
|
psname = instance.postScriptFontName
|
|
131
137
|
|
|
132
138
|
inst = NamedInstance()
|
|
133
|
-
inst.
|
|
134
|
-
|
|
139
|
+
inst.coordinates = {
|
|
140
|
+
axes[k].tag: axes[k].map_backward(v) for k, v in coordinates.items()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
subfamilyNameID = nameTable.findMultilingualName(
|
|
144
|
+
localisedStyleName, windows=True, mac=macNames
|
|
135
145
|
)
|
|
146
|
+
if subfamilyNameID in {2, 17} and inst.coordinates == default_coordinates:
|
|
147
|
+
# Instances can only reuse an existing name ID 2 or 17 if they are at the
|
|
148
|
+
# default location across all axes, see:
|
|
149
|
+
# https://github.com/fonttools/fonttools/issues/3825.
|
|
150
|
+
inst.subfamilyNameID = subfamilyNameID
|
|
151
|
+
else:
|
|
152
|
+
inst.subfamilyNameID = nameTable.addMultilingualName(
|
|
153
|
+
localisedStyleName, windows=True, mac=macNames, minNameID=256
|
|
154
|
+
)
|
|
155
|
+
|
|
136
156
|
if psname is not None:
|
|
137
157
|
psname = tostr(psname)
|
|
138
158
|
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
159
|
fvar.instances.append(inst)
|
|
144
160
|
|
|
145
161
|
assert "fvar" not in font
|
|
@@ -325,7 +341,6 @@ def _add_gvar(font, masterModel, master_ttfs, tolerance=0.5, optimize=True):
|
|
|
325
341
|
|
|
326
342
|
for glyph in font.getGlyphOrder():
|
|
327
343
|
log.debug("building gvar for glyph '%s'", glyph)
|
|
328
|
-
isComposite = glyf[glyph].isComposite()
|
|
329
344
|
|
|
330
345
|
allData = [
|
|
331
346
|
m.glyf._getCoordinatesAndControls(glyph, m.hMetrics, m.vMetrics)
|
|
@@ -363,7 +378,7 @@ def _add_gvar(font, masterModel, master_ttfs, tolerance=0.5, optimize=True):
|
|
|
363
378
|
endPts = control.endPts
|
|
364
379
|
|
|
365
380
|
for i, (delta, support) in enumerate(zip(deltas[1:], supports[1:])):
|
|
366
|
-
if all(v == 0 for v in delta.array)
|
|
381
|
+
if all(v == 0 for v in delta.array):
|
|
367
382
|
continue
|
|
368
383
|
var = TupleVariation(support, delta)
|
|
369
384
|
if optimize:
|
|
@@ -372,16 +387,6 @@ def _add_gvar(font, masterModel, master_ttfs, tolerance=0.5, optimize=True):
|
|
|
372
387
|
)
|
|
373
388
|
|
|
374
389
|
if None in delta_opt:
|
|
375
|
-
"""In composite glyphs, there should be one 0 entry
|
|
376
|
-
to make sure the gvar entry is written to the font.
|
|
377
|
-
|
|
378
|
-
This is to work around an issue with macOS 10.14 and can be
|
|
379
|
-
removed once the behaviour of macOS is changed.
|
|
380
|
-
|
|
381
|
-
https://github.com/fonttools/fonttools/issues/1381
|
|
382
|
-
"""
|
|
383
|
-
if all(d is None for d in delta_opt):
|
|
384
|
-
delta_opt = [(0, 0)] + [None] * (len(delta_opt) - 1)
|
|
385
390
|
# Use "optimized" version only if smaller...
|
|
386
391
|
var_opt = TupleVariation(support, delta_opt)
|
|
387
392
|
|
|
@@ -488,9 +493,88 @@ def _merge_TTHinting(font, masterModel, master_ttfs):
|
|
|
488
493
|
cvar.variations = variations
|
|
489
494
|
|
|
490
495
|
|
|
496
|
+
def _has_inconsistent_use_my_metrics_flag(
|
|
497
|
+
master_glyf, glyph_name, flagged_components, expected_num_components
|
|
498
|
+
) -> bool:
|
|
499
|
+
master_glyph = master_glyf.get(glyph_name)
|
|
500
|
+
# 'sparse' glyph master doesn't contribute. Besides when components don't match
|
|
501
|
+
# the VF build is going to fail anyway, so be lenient here.
|
|
502
|
+
if (
|
|
503
|
+
master_glyph is not None
|
|
504
|
+
and master_glyph.isComposite()
|
|
505
|
+
and len(master_glyph.components) == expected_num_components
|
|
506
|
+
):
|
|
507
|
+
for i, base_glyph in flagged_components:
|
|
508
|
+
comp = master_glyph.components[i]
|
|
509
|
+
if comp.glyphName != base_glyph:
|
|
510
|
+
break
|
|
511
|
+
if not (comp.flags & USE_MY_METRICS):
|
|
512
|
+
return True
|
|
513
|
+
return False
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
def _unset_inconsistent_use_my_metrics_flags(vf, master_fonts):
|
|
517
|
+
"""Clear USE_MY_METRICS on composite components if inconsistent across masters.
|
|
518
|
+
|
|
519
|
+
If a composite glyph's component has USE_MY_METRICS set differently among
|
|
520
|
+
the masters, the flag is removed from the variable font's glyf table so that
|
|
521
|
+
advance widths are not determined by that single component's phantom points.
|
|
522
|
+
"""
|
|
523
|
+
glyf = vf["glyf"]
|
|
524
|
+
master_glyfs = [m["glyf"] for m in master_fonts if "glyf" in m]
|
|
525
|
+
if not master_glyfs:
|
|
526
|
+
# Should not happen: at least the base master (as copied into vf) has glyf
|
|
527
|
+
return
|
|
528
|
+
|
|
529
|
+
for glyph_name in glyf.keys():
|
|
530
|
+
glyph = glyf[glyph_name]
|
|
531
|
+
if not glyph.isComposite():
|
|
532
|
+
continue
|
|
533
|
+
|
|
534
|
+
# collect indices of component(s) that carry the USE_MY_METRICS flag.
|
|
535
|
+
# This is supposed to be 1 component per composite, but you never know.
|
|
536
|
+
flagged_components = [
|
|
537
|
+
(i, comp.glyphName)
|
|
538
|
+
for i, comp in enumerate(glyph.components)
|
|
539
|
+
if (comp.flags & USE_MY_METRICS)
|
|
540
|
+
]
|
|
541
|
+
if not flagged_components:
|
|
542
|
+
# Nothing to fix
|
|
543
|
+
continue
|
|
544
|
+
|
|
545
|
+
# Verify that for all master glyf tables that contribute this glyph, the
|
|
546
|
+
# corresponding component (same glyphName and index) also carries USE_MY_METRICS
|
|
547
|
+
# and unset the flag if not.
|
|
548
|
+
expected_num_components = len(glyph.components)
|
|
549
|
+
if any(
|
|
550
|
+
_has_inconsistent_use_my_metrics_flag(
|
|
551
|
+
master_glyf, glyph_name, flagged_components, expected_num_components
|
|
552
|
+
)
|
|
553
|
+
for master_glyf in master_glyfs
|
|
554
|
+
):
|
|
555
|
+
comp_names = [name for _, name in flagged_components]
|
|
556
|
+
log.info(
|
|
557
|
+
"Composite glyph '%s' has inconsistent USE_MY_METRICS flags across "
|
|
558
|
+
"masters; clearing the flag on component%s %s",
|
|
559
|
+
glyph_name,
|
|
560
|
+
"s" if len(comp_names) > 1 else "",
|
|
561
|
+
comp_names if len(comp_names) > 1 else comp_names[0],
|
|
562
|
+
)
|
|
563
|
+
for i, _ in flagged_components:
|
|
564
|
+
glyph.components[i].flags &= ~USE_MY_METRICS
|
|
565
|
+
|
|
566
|
+
|
|
491
567
|
_MetricsFields = namedtuple(
|
|
492
568
|
"_MetricsFields",
|
|
493
|
-
[
|
|
569
|
+
[
|
|
570
|
+
"tableTag",
|
|
571
|
+
"metricsTag",
|
|
572
|
+
"sb1",
|
|
573
|
+
"sb2",
|
|
574
|
+
"advMapping",
|
|
575
|
+
"vOrigMapping",
|
|
576
|
+
"phantomIndex",
|
|
577
|
+
],
|
|
494
578
|
)
|
|
495
579
|
|
|
496
580
|
HVAR_FIELDS = _MetricsFields(
|
|
@@ -500,6 +584,7 @@ HVAR_FIELDS = _MetricsFields(
|
|
|
500
584
|
sb2="RsbMap",
|
|
501
585
|
advMapping="AdvWidthMap",
|
|
502
586
|
vOrigMapping=None,
|
|
587
|
+
phantomIndex=0,
|
|
503
588
|
)
|
|
504
589
|
|
|
505
590
|
VVAR_FIELDS = _MetricsFields(
|
|
@@ -509,109 +594,43 @@ VVAR_FIELDS = _MetricsFields(
|
|
|
509
594
|
sb2="BsbMap",
|
|
510
595
|
advMapping="AdvHeightMap",
|
|
511
596
|
vOrigMapping="VOrgMap",
|
|
597
|
+
phantomIndex=1,
|
|
512
598
|
)
|
|
513
599
|
|
|
514
600
|
|
|
515
601
|
def _add_HVAR(font, masterModel, master_ttfs, axisTags):
|
|
516
|
-
|
|
602
|
+
getAdvanceMetrics = partial(
|
|
603
|
+
_get_advance_metrics, font, masterModel, master_ttfs, axisTags, HVAR_FIELDS
|
|
604
|
+
)
|
|
605
|
+
_add_VHVAR(font, axisTags, HVAR_FIELDS, getAdvanceMetrics)
|
|
517
606
|
|
|
518
607
|
|
|
519
608
|
def _add_VVAR(font, masterModel, master_ttfs, axisTags):
|
|
520
|
-
|
|
609
|
+
getAdvanceMetrics = partial(
|
|
610
|
+
_get_advance_metrics, font, masterModel, master_ttfs, axisTags, VVAR_FIELDS
|
|
611
|
+
)
|
|
612
|
+
_add_VHVAR(font, axisTags, VVAR_FIELDS, getAdvanceMetrics)
|
|
521
613
|
|
|
522
614
|
|
|
523
|
-
def _add_VHVAR(font,
|
|
615
|
+
def _add_VHVAR(font, axisTags, tableFields, getAdvanceMetrics):
|
|
524
616
|
tableTag = tableFields.tableTag
|
|
525
617
|
assert tableTag not in font
|
|
618
|
+
glyphOrder = font.getGlyphOrder()
|
|
526
619
|
log.info("Generating " + tableTag)
|
|
527
620
|
VHVAR = newTable(tableTag)
|
|
528
621
|
tableClass = getattr(ot, tableTag)
|
|
529
622
|
vhvar = VHVAR.table = tableClass()
|
|
530
623
|
vhvar.Version = 0x00010000
|
|
531
624
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
# Build list of source font advance widths for each glyph
|
|
535
|
-
metricsTag = tableFields.metricsTag
|
|
536
|
-
advMetricses = [m[metricsTag].metrics for m in master_ttfs]
|
|
537
|
-
|
|
538
|
-
# Build list of source font vertical origin coords for each glyph
|
|
539
|
-
if tableTag == "VVAR" and "VORG" in master_ttfs[0]:
|
|
540
|
-
vOrigMetricses = [m["VORG"].VOriginRecords for m in master_ttfs]
|
|
541
|
-
defaultYOrigs = [m["VORG"].defaultVertOriginY for m in master_ttfs]
|
|
542
|
-
vOrigMetricses = list(zip(vOrigMetricses, defaultYOrigs))
|
|
543
|
-
else:
|
|
544
|
-
vOrigMetricses = None
|
|
545
|
-
|
|
546
|
-
metricsStore, advanceMapping, vOrigMapping = _get_advance_metrics(
|
|
547
|
-
font,
|
|
548
|
-
masterModel,
|
|
549
|
-
master_ttfs,
|
|
550
|
-
axisTags,
|
|
551
|
-
glyphOrder,
|
|
552
|
-
advMetricses,
|
|
553
|
-
vOrigMetricses,
|
|
554
|
-
)
|
|
625
|
+
vhAdvanceDeltasAndSupports, vOrigDeltasAndSupports = getAdvanceMetrics()
|
|
555
626
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
setattr(vhvar, tableFields.advMapping, None)
|
|
627
|
+
if vOrigDeltasAndSupports:
|
|
628
|
+
singleModel = False
|
|
559
629
|
else:
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
setattr(vhvar, tableFields.vOrigMapping, vOrigMapping)
|
|
563
|
-
setattr(vhvar, tableFields.sb1, None)
|
|
564
|
-
setattr(vhvar, tableFields.sb2, None)
|
|
565
|
-
|
|
566
|
-
font[tableTag] = VHVAR
|
|
567
|
-
return
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
def _get_advance_metrics(
|
|
571
|
-
font,
|
|
572
|
-
masterModel,
|
|
573
|
-
master_ttfs,
|
|
574
|
-
axisTags,
|
|
575
|
-
glyphOrder,
|
|
576
|
-
advMetricses,
|
|
577
|
-
vOrigMetricses=None,
|
|
578
|
-
):
|
|
579
|
-
vhAdvanceDeltasAndSupports = {}
|
|
580
|
-
vOrigDeltasAndSupports = {}
|
|
581
|
-
# HACK: we treat width 65535 as a sentinel value to signal that a glyph
|
|
582
|
-
# from a non-default master should not participate in computing {H,V}VAR,
|
|
583
|
-
# as if it were missing. Allows to variate other glyph-related data independently
|
|
584
|
-
# from glyph metrics
|
|
585
|
-
sparse_advance = 0xFFFF
|
|
586
|
-
for glyph in glyphOrder:
|
|
587
|
-
vhAdvances = [
|
|
588
|
-
(
|
|
589
|
-
metrics[glyph][0]
|
|
590
|
-
if glyph in metrics and metrics[glyph][0] != sparse_advance
|
|
591
|
-
else None
|
|
592
|
-
)
|
|
593
|
-
for metrics in advMetricses
|
|
594
|
-
]
|
|
595
|
-
vhAdvanceDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(
|
|
596
|
-
vhAdvances, round=round
|
|
630
|
+
singleModel = models.allEqual(
|
|
631
|
+
id(v[1]) for v in vhAdvanceDeltasAndSupports.values()
|
|
597
632
|
)
|
|
598
633
|
|
|
599
|
-
singleModel = models.allEqual(id(v[1]) for v in vhAdvanceDeltasAndSupports.values())
|
|
600
|
-
|
|
601
|
-
if vOrigMetricses:
|
|
602
|
-
singleModel = False
|
|
603
|
-
for glyph in glyphOrder:
|
|
604
|
-
# We need to supply a vOrigs tuple with non-None default values
|
|
605
|
-
# for each glyph. vOrigMetricses contains values only for those
|
|
606
|
-
# glyphs which have a non-default vOrig.
|
|
607
|
-
vOrigs = [
|
|
608
|
-
metrics[glyph] if glyph in metrics else defaultVOrig
|
|
609
|
-
for metrics, defaultVOrig in vOrigMetricses
|
|
610
|
-
]
|
|
611
|
-
vOrigDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(
|
|
612
|
-
vOrigs, round=round
|
|
613
|
-
)
|
|
614
|
-
|
|
615
634
|
directStore = None
|
|
616
635
|
if singleModel:
|
|
617
636
|
# Build direct mapping
|
|
@@ -623,6 +642,8 @@ def _get_advance_metrics(
|
|
|
623
642
|
varData.addItem(vhAdvanceDeltasAndSupports[glyphName][0], round=noRound)
|
|
624
643
|
varData.optimize()
|
|
625
644
|
directStore = builder.buildVarStore(varTupleList, [varData])
|
|
645
|
+
# remove unused regions from VarRegionList
|
|
646
|
+
directStore.prune_regions()
|
|
626
647
|
|
|
627
648
|
# Build optimized indirect mapping
|
|
628
649
|
storeBuilder = varStore.OnlineVarStoreBuilder(axisTags)
|
|
@@ -632,7 +653,7 @@ def _get_advance_metrics(
|
|
|
632
653
|
storeBuilder.setSupports(supports)
|
|
633
654
|
advMapping[glyphName] = storeBuilder.storeDeltas(deltas, round=noRound)
|
|
634
655
|
|
|
635
|
-
if
|
|
656
|
+
if vOrigDeltasAndSupports:
|
|
636
657
|
vOrigMap = {}
|
|
637
658
|
for glyphName in glyphOrder:
|
|
638
659
|
deltas, supports = vOrigDeltasAndSupports[glyphName]
|
|
@@ -644,7 +665,7 @@ def _get_advance_metrics(
|
|
|
644
665
|
advMapping = [mapping2[advMapping[g]] for g in glyphOrder]
|
|
645
666
|
advanceMapping = builder.buildVarIdxMap(advMapping, glyphOrder)
|
|
646
667
|
|
|
647
|
-
if
|
|
668
|
+
if vOrigDeltasAndSupports:
|
|
648
669
|
vOrigMap = [mapping2[vOrigMap[g]] for g in glyphOrder]
|
|
649
670
|
|
|
650
671
|
useDirect = False
|
|
@@ -668,10 +689,70 @@ def _get_advance_metrics(
|
|
|
668
689
|
advanceMapping = None
|
|
669
690
|
else:
|
|
670
691
|
metricsStore = indirectStore
|
|
671
|
-
if
|
|
692
|
+
if vOrigDeltasAndSupports:
|
|
672
693
|
vOrigMapping = builder.buildVarIdxMap(vOrigMap, glyphOrder)
|
|
673
694
|
|
|
674
|
-
|
|
695
|
+
vhvar.VarStore = metricsStore
|
|
696
|
+
setattr(vhvar, tableFields.advMapping, advanceMapping)
|
|
697
|
+
if vOrigMapping is not None:
|
|
698
|
+
setattr(vhvar, tableFields.vOrigMapping, vOrigMapping)
|
|
699
|
+
setattr(vhvar, tableFields.sb1, None)
|
|
700
|
+
setattr(vhvar, tableFields.sb2, None)
|
|
701
|
+
|
|
702
|
+
font[tableTag] = VHVAR
|
|
703
|
+
return
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
def _get_advance_metrics(font, masterModel, master_ttfs, axisTags, tableFields):
|
|
707
|
+
tableTag = tableFields.tableTag
|
|
708
|
+
glyphOrder = font.getGlyphOrder()
|
|
709
|
+
|
|
710
|
+
# Build list of source font advance widths for each glyph
|
|
711
|
+
metricsTag = tableFields.metricsTag
|
|
712
|
+
advMetricses = [m[metricsTag].metrics for m in master_ttfs]
|
|
713
|
+
|
|
714
|
+
# Build list of source font vertical origin coords for each glyph
|
|
715
|
+
if tableTag == "VVAR" and "VORG" in master_ttfs[0]:
|
|
716
|
+
vOrigMetricses = [m["VORG"].VOriginRecords for m in master_ttfs]
|
|
717
|
+
defaultYOrigs = [m["VORG"].defaultVertOriginY for m in master_ttfs]
|
|
718
|
+
vOrigMetricses = list(zip(vOrigMetricses, defaultYOrigs))
|
|
719
|
+
else:
|
|
720
|
+
vOrigMetricses = None
|
|
721
|
+
|
|
722
|
+
vhAdvanceDeltasAndSupports = {}
|
|
723
|
+
vOrigDeltasAndSupports = {}
|
|
724
|
+
# HACK: we treat width 65535 as a sentinel value to signal that a glyph
|
|
725
|
+
# from a non-default master should not participate in computing {H,V}VAR,
|
|
726
|
+
# as if it were missing. Allows to variate other glyph-related data independently
|
|
727
|
+
# from glyph metrics
|
|
728
|
+
sparse_advance = 0xFFFF
|
|
729
|
+
for glyph in glyphOrder:
|
|
730
|
+
vhAdvances = [
|
|
731
|
+
(
|
|
732
|
+
metrics[glyph][0]
|
|
733
|
+
if glyph in metrics and metrics[glyph][0] != sparse_advance
|
|
734
|
+
else None
|
|
735
|
+
)
|
|
736
|
+
for metrics in advMetricses
|
|
737
|
+
]
|
|
738
|
+
vhAdvanceDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(
|
|
739
|
+
vhAdvances, round=round
|
|
740
|
+
)
|
|
741
|
+
|
|
742
|
+
if vOrigMetricses:
|
|
743
|
+
for glyph in glyphOrder:
|
|
744
|
+
# We need to supply a vOrigs tuple with non-None default values
|
|
745
|
+
# for each glyph. vOrigMetricses contains values only for those
|
|
746
|
+
# glyphs which have a non-default vOrig.
|
|
747
|
+
vOrigs = [
|
|
748
|
+
metrics[glyph] if glyph in metrics else defaultVOrig
|
|
749
|
+
for metrics, defaultVOrig in vOrigMetricses
|
|
750
|
+
]
|
|
751
|
+
vOrigDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(
|
|
752
|
+
vOrigs, round=round
|
|
753
|
+
)
|
|
754
|
+
|
|
755
|
+
return vhAdvanceDeltasAndSupports, vOrigDeltasAndSupports
|
|
675
756
|
|
|
676
757
|
|
|
677
758
|
def _add_MVAR(font, masterModel, master_ttfs, axisTags):
|
|
@@ -1199,6 +1280,10 @@ def build(
|
|
|
1199
1280
|
if "DSIG" in vf:
|
|
1200
1281
|
del vf["DSIG"]
|
|
1201
1282
|
|
|
1283
|
+
# Clear USE_MY_METRICS composite flags if set inconsistently across masters.
|
|
1284
|
+
if "glyf" in vf:
|
|
1285
|
+
_unset_inconsistent_use_my_metrics_flags(vf, master_fonts)
|
|
1286
|
+
|
|
1202
1287
|
# TODO append masters as named-instances as well; needs .designspace change.
|
|
1203
1288
|
fvar = _add_fvar(vf, ds.axes, ds.instances)
|
|
1204
1289
|
if "STAT" not in exclude:
|
|
@@ -1477,7 +1562,11 @@ def main(args=None):
|
|
|
1477
1562
|
vf_name_to_output_path[vfs_to_build[0].name] = options.outfile
|
|
1478
1563
|
else:
|
|
1479
1564
|
for vf in vfs_to_build:
|
|
1480
|
-
|
|
1565
|
+
if vf.filename is not None:
|
|
1566
|
+
# Only use basename to prevent path traversal attacks
|
|
1567
|
+
filename = os.path.basename(vf.filename)
|
|
1568
|
+
else:
|
|
1569
|
+
filename = vf.name + ".{ext}"
|
|
1481
1570
|
vf_name_to_output_path[vf.name] = os.path.join(output_dir, filename)
|
|
1482
1571
|
|
|
1483
1572
|
finder = MasterFinder(options.master_finder)
|
|
File without changes
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
log = logging.getLogger("fontTools.varLib.avar")
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def main(args=None):
|
|
7
|
+
from fontTools.ttLib import TTFont
|
|
8
|
+
from fontTools.misc.cliTools import makeOutputFileName
|
|
9
|
+
from fontTools import configLogger
|
|
10
|
+
import argparse
|
|
11
|
+
import sys
|
|
12
|
+
|
|
13
|
+
print(
|
|
14
|
+
"WARNING: This script is deprecated. Use `fonttools varLib.avar.build` "
|
|
15
|
+
"or `fonttools varLib.avar.unbuild` instead.\n",
|
|
16
|
+
file=sys.stderr,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
if args is None:
|
|
20
|
+
args = sys.argv[1:]
|
|
21
|
+
|
|
22
|
+
parser = argparse.ArgumentParser(
|
|
23
|
+
"fonttools varLib.avar",
|
|
24
|
+
description="Add `avar` table from designspace file to variable font.",
|
|
25
|
+
)
|
|
26
|
+
parser.add_argument("font", metavar="varfont.ttf", help="Variable-font file.")
|
|
27
|
+
parser.add_argument(
|
|
28
|
+
"designspace",
|
|
29
|
+
metavar="family.designspace",
|
|
30
|
+
help="Designspace file.",
|
|
31
|
+
nargs="?",
|
|
32
|
+
default=None,
|
|
33
|
+
)
|
|
34
|
+
parser.add_argument(
|
|
35
|
+
"-o",
|
|
36
|
+
"--output-file",
|
|
37
|
+
type=str,
|
|
38
|
+
help="Output font file name.",
|
|
39
|
+
)
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
"-v", "--verbose", action="store_true", help="Run more verbosely."
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
options = parser.parse_args(args)
|
|
45
|
+
|
|
46
|
+
configLogger(level=("INFO" if options.verbose else "WARNING"))
|
|
47
|
+
|
|
48
|
+
font = TTFont(options.font)
|
|
49
|
+
|
|
50
|
+
if options.designspace is None:
|
|
51
|
+
from .unbuild import unbuild
|
|
52
|
+
|
|
53
|
+
unbuild(font)
|
|
54
|
+
return 0
|
|
55
|
+
|
|
56
|
+
from .build import build
|
|
57
|
+
|
|
58
|
+
build(font, options.designspace)
|
|
59
|
+
|
|
60
|
+
if options.output_file is None:
|
|
61
|
+
outfile = makeOutputFileName(options.font, overWrite=True, suffix=".avar")
|
|
62
|
+
else:
|
|
63
|
+
outfile = options.output_file
|
|
64
|
+
if outfile:
|
|
65
|
+
log.info("Saving %s", outfile)
|
|
66
|
+
font.save(outfile)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
import sys
|
|
71
|
+
|
|
72
|
+
sys.exit(main())
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from fontTools.varLib import _add_fvar, _add_avar, load_designspace
|
|
2
|
+
from fontTools.ttLib import newTable
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
log = logging.getLogger("fontTools.varLib.avar")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def build(font, designspace_file):
|
|
9
|
+
ds = load_designspace(designspace_file, require_sources=False)
|
|
10
|
+
|
|
11
|
+
if not "fvar" in font:
|
|
12
|
+
# if "name" not in font:
|
|
13
|
+
font["name"] = newTable("name")
|
|
14
|
+
_add_fvar(font, ds.axes, ds.instances)
|
|
15
|
+
|
|
16
|
+
axisTags = [a.axisTag for a in font["fvar"].axes]
|
|
17
|
+
|
|
18
|
+
if "avar" in font:
|
|
19
|
+
log.warning("avar table already present, overwriting.")
|
|
20
|
+
del font["avar"]
|
|
21
|
+
|
|
22
|
+
_add_avar(font, ds.axes, ds.axisMappings, axisTags)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def main(args=None):
|
|
26
|
+
"""Add `avar` table from designspace file to variable font."""
|
|
27
|
+
|
|
28
|
+
from fontTools.ttLib import TTFont
|
|
29
|
+
from fontTools.misc.cliTools import makeOutputFileName
|
|
30
|
+
from fontTools import configLogger
|
|
31
|
+
import argparse
|
|
32
|
+
|
|
33
|
+
if args is None:
|
|
34
|
+
import sys
|
|
35
|
+
|
|
36
|
+
args = sys.argv[1:]
|
|
37
|
+
|
|
38
|
+
parser = argparse.ArgumentParser(
|
|
39
|
+
"fonttools varLib.avar.build",
|
|
40
|
+
description="Add `avar` table from designspace file to variable font.",
|
|
41
|
+
)
|
|
42
|
+
parser.add_argument("font", metavar="varfont.ttf", help="Variable-font file.")
|
|
43
|
+
parser.add_argument(
|
|
44
|
+
"designspace",
|
|
45
|
+
metavar="family.designspace",
|
|
46
|
+
help="Designspace file.",
|
|
47
|
+
default=None,
|
|
48
|
+
)
|
|
49
|
+
parser.add_argument(
|
|
50
|
+
"-o",
|
|
51
|
+
"--output-file",
|
|
52
|
+
type=str,
|
|
53
|
+
help="Output font file name.",
|
|
54
|
+
)
|
|
55
|
+
parser.add_argument(
|
|
56
|
+
"-v", "--verbose", action="store_true", help="Run more verbosely."
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
options = parser.parse_args(args)
|
|
60
|
+
|
|
61
|
+
configLogger(level=("INFO" if options.verbose else "WARNING"))
|
|
62
|
+
|
|
63
|
+
font = TTFont(options.font)
|
|
64
|
+
|
|
65
|
+
build(font, options.designspace)
|
|
66
|
+
|
|
67
|
+
if options.output_file is None:
|
|
68
|
+
outfile = makeOutputFileName(options.font, overWrite=True, suffix=".avar")
|
|
69
|
+
else:
|
|
70
|
+
outfile = options.output_file
|
|
71
|
+
if outfile:
|
|
72
|
+
log.info("Saving %s", outfile)
|
|
73
|
+
font.save(outfile)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
if __name__ == "__main__":
|
|
77
|
+
import sys
|
|
78
|
+
|
|
79
|
+
sys.exit(main())
|