fonttools 4.55.8__cp39-cp39-macosx_10_9_universal2.whl → 4.57.0__cp39-cp39-macosx_10_9_universal2.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fonttools might be problematic. Click here for more details.
- fontTools/__init__.py +1 -1
- fontTools/config/__init__.py +15 -0
- fontTools/cu2qu/cu2qu.c +6 -6
- fontTools/cu2qu/cu2qu.cpython-39-darwin.so +0 -0
- fontTools/feaLib/ast.py +8 -3
- fontTools/feaLib/builder.py +32 -9
- fontTools/feaLib/error.py +1 -1
- fontTools/feaLib/lexer.c +5 -5
- fontTools/feaLib/lexer.cpython-39-darwin.so +0 -0
- fontTools/feaLib/parser.py +58 -0
- fontTools/misc/bezierTools.c +6 -6
- fontTools/misc/bezierTools.cpython-39-darwin.so +0 -0
- fontTools/misc/testTools.py +5 -1
- fontTools/otlLib/optimize/gpos.py +7 -1
- fontTools/pens/momentsPen.c +5 -5
- fontTools/pens/momentsPen.cpython-39-darwin.so +0 -0
- fontTools/qu2cu/qu2cu.c +6 -6
- fontTools/qu2cu/qu2cu.cpython-39-darwin.so +0 -0
- fontTools/ttLib/__init__.py +4 -0
- fontTools/ttLib/__main__.py +47 -8
- fontTools/ttLib/tables/D__e_b_g.py +20 -2
- fontTools/ttLib/tables/_c_m_a_p.py +19 -6
- fontTools/ttLib/tables/_g_l_y_f.py +9 -4
- fontTools/ttLib/tables/_g_v_a_r.py +4 -2
- fontTools/ttLib/tables/otConverters.py +5 -2
- fontTools/ttLib/tables/otData.py +1 -1
- fontTools/ttLib/tables/otTables.py +26 -18
- fontTools/ttLib/tables/otTraverse.py +2 -1
- fontTools/ttLib/ttFont.py +9 -7
- fontTools/ttLib/ttGlyphSet.py +0 -10
- fontTools/ttx.py +13 -1
- fontTools/varLib/__init__.py +93 -101
- fontTools/varLib/hvar.py +113 -0
- fontTools/varLib/iup.c +6 -6
- fontTools/varLib/iup.cpython-39-darwin.so +0 -0
- fontTools/varLib/varStore.py +10 -38
- {fonttools-4.55.8.dist-info → fonttools-4.57.0.dist-info}/METADATA +25 -2
- {fonttools-4.55.8.dist-info → fonttools-4.57.0.dist-info}/RECORD +43 -42
- {fonttools-4.55.8.dist-info → fonttools-4.57.0.dist-info}/WHEEL +2 -1
- {fonttools-4.55.8.data → fonttools-4.57.0.data}/data/share/man/man1/ttx.1 +0 -0
- {fonttools-4.55.8.dist-info → fonttools-4.57.0.dist-info}/entry_points.txt +0 -0
- {fonttools-4.55.8.dist-info → fonttools-4.57.0.dist-info/licenses}/LICENSE +0 -0
- {fonttools-4.55.8.dist-info → fonttools-4.57.0.dist-info}/top_level.txt +0 -0
|
@@ -141,6 +141,17 @@ class table__c_m_a_p(DefaultTable.DefaultTable):
|
|
|
141
141
|
result.setdefault(name, set()).add(codepoint)
|
|
142
142
|
return result
|
|
143
143
|
|
|
144
|
+
def buildReversedMin(self):
|
|
145
|
+
result = {}
|
|
146
|
+
for subtable in self.tables:
|
|
147
|
+
if subtable.isUnicode():
|
|
148
|
+
for codepoint, name in subtable.cmap.items():
|
|
149
|
+
if name in result:
|
|
150
|
+
result[name] = min(result[name], codepoint)
|
|
151
|
+
else:
|
|
152
|
+
result[name] = codepoint
|
|
153
|
+
return result
|
|
154
|
+
|
|
144
155
|
def decompile(self, data, ttFont):
|
|
145
156
|
tableVersion, numSubTables = struct.unpack(">HH", data[:4])
|
|
146
157
|
self.tableVersion = int(tableVersion)
|
|
@@ -1162,13 +1173,15 @@ class cmap_format_12_or_13(CmapSubtable):
|
|
|
1162
1173
|
charCodes = []
|
|
1163
1174
|
gids = []
|
|
1164
1175
|
pos = 0
|
|
1176
|
+
groups = array.array("I", data[: self.nGroups * 12])
|
|
1177
|
+
if sys.byteorder != "big":
|
|
1178
|
+
groups.byteswap()
|
|
1165
1179
|
for i in range(self.nGroups):
|
|
1166
|
-
startCharCode
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
pos += 12
|
|
1180
|
+
startCharCode = groups[i * 3]
|
|
1181
|
+
endCharCode = groups[i * 3 + 1]
|
|
1182
|
+
glyphID = groups[i * 3 + 2]
|
|
1170
1183
|
lenGroup = 1 + endCharCode - startCharCode
|
|
1171
|
-
charCodes.extend(
|
|
1184
|
+
charCodes.extend(range(startCharCode, endCharCode + 1))
|
|
1172
1185
|
gids.extend(self._computeGIDs(glyphID, lenGroup))
|
|
1173
1186
|
self.data = data = None
|
|
1174
1187
|
self.cmap = _make_map(self.ttFont, charCodes, gids)
|
|
@@ -1299,7 +1312,7 @@ class cmap_format_12(cmap_format_12_or_13):
|
|
|
1299
1312
|
cmap_format_12_or_13.__init__(self, format)
|
|
1300
1313
|
|
|
1301
1314
|
def _computeGIDs(self, startingGlyph, numberOfGlyphs):
|
|
1302
|
-
return
|
|
1315
|
+
return range(startingGlyph, startingGlyph + numberOfGlyphs)
|
|
1303
1316
|
|
|
1304
1317
|
def _IsInSameRun(self, glyphID, lastGlyphID, charCode, lastCharCode):
|
|
1305
1318
|
return (glyphID == 1 + lastGlyphID) and (charCode == 1 + lastCharCode)
|
|
@@ -134,6 +134,8 @@ class table__g_l_y_f(DefaultTable.DefaultTable):
|
|
|
134
134
|
glyph.expand(self)
|
|
135
135
|
|
|
136
136
|
def compile(self, ttFont):
|
|
137
|
+
optimizeSpeed = ttFont.cfg[ttLib.OPTIMIZE_FONT_SPEED]
|
|
138
|
+
|
|
137
139
|
self.axisTags = (
|
|
138
140
|
[axis.axisTag for axis in ttFont["fvar"].axes] if "fvar" in ttFont else []
|
|
139
141
|
)
|
|
@@ -148,7 +150,12 @@ class table__g_l_y_f(DefaultTable.DefaultTable):
|
|
|
148
150
|
boundsDone = set()
|
|
149
151
|
for glyphName in self.glyphOrder:
|
|
150
152
|
glyph = self.glyphs[glyphName]
|
|
151
|
-
glyphData = glyph.compile(
|
|
153
|
+
glyphData = glyph.compile(
|
|
154
|
+
self,
|
|
155
|
+
recalcBBoxes,
|
|
156
|
+
boundsDone=boundsDone,
|
|
157
|
+
optimizeSize=not optimizeSpeed,
|
|
158
|
+
)
|
|
152
159
|
if padding > 1:
|
|
153
160
|
glyphData = pad(glyphData, size=padding)
|
|
154
161
|
locations.append(currentLocation)
|
|
@@ -714,7 +721,7 @@ class Glyph(object):
|
|
|
714
721
|
self.decompileCoordinates(data)
|
|
715
722
|
|
|
716
723
|
def compile(
|
|
717
|
-
self, glyfTable, recalcBBoxes=True, *, boundsDone=None, optimizeSize=
|
|
724
|
+
self, glyfTable, recalcBBoxes=True, *, boundsDone=None, optimizeSize=True
|
|
718
725
|
):
|
|
719
726
|
if hasattr(self, "data"):
|
|
720
727
|
if recalcBBoxes:
|
|
@@ -732,8 +739,6 @@ class Glyph(object):
|
|
|
732
739
|
if self.isComposite():
|
|
733
740
|
data = data + self.compileComponents(glyfTable)
|
|
734
741
|
else:
|
|
735
|
-
if optimizeSize is None:
|
|
736
|
-
optimizeSize = getattr(glyfTable, "optimizeSize", True)
|
|
737
742
|
data = data + self.compileCoordinates(optimizeSize=optimizeSize)
|
|
738
743
|
return data
|
|
739
744
|
|
|
@@ -3,6 +3,7 @@ from functools import partial
|
|
|
3
3
|
from fontTools.misc import sstruct
|
|
4
4
|
from fontTools.misc.textTools import safeEval
|
|
5
5
|
from fontTools.misc.lazyTools import LazyDict
|
|
6
|
+
from fontTools.ttLib import OPTIMIZE_FONT_SPEED
|
|
6
7
|
from fontTools.ttLib.tables.TupleVariation import TupleVariation
|
|
7
8
|
from . import DefaultTable
|
|
8
9
|
import array
|
|
@@ -57,6 +58,7 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
|
57
58
|
self.variations = {}
|
|
58
59
|
|
|
59
60
|
def compile(self, ttFont):
|
|
61
|
+
|
|
60
62
|
axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
|
|
61
63
|
sharedTuples = tv.compileSharedTuples(
|
|
62
64
|
axisTags, itertools.chain(*self.variations.values())
|
|
@@ -91,9 +93,9 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
|
91
93
|
return b"".join(result)
|
|
92
94
|
|
|
93
95
|
def compileGlyphs_(self, ttFont, axisTags, sharedCoordIndices):
|
|
96
|
+
optimizeSpeed = ttFont.cfg[OPTIMIZE_FONT_SPEED]
|
|
94
97
|
result = []
|
|
95
98
|
glyf = ttFont["glyf"]
|
|
96
|
-
optimizeSize = getattr(self, "optimizeSize", True)
|
|
97
99
|
for glyphName in ttFont.getGlyphOrder():
|
|
98
100
|
variations = self.variations.get(glyphName, [])
|
|
99
101
|
if not variations:
|
|
@@ -106,7 +108,7 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
|
106
108
|
pointCountUnused,
|
|
107
109
|
axisTags,
|
|
108
110
|
sharedCoordIndices,
|
|
109
|
-
optimizeSize=
|
|
111
|
+
optimizeSize=not optimizeSpeed,
|
|
110
112
|
)
|
|
111
113
|
)
|
|
112
114
|
return result
|
|
@@ -10,7 +10,7 @@ from fontTools.ttLib.tables.TupleVariation import TupleVariation
|
|
|
10
10
|
from fontTools.misc.roundTools import nearestMultipleShortestRepr, otRound
|
|
11
11
|
from fontTools.misc.textTools import bytesjoin, tobytes, tostr, pad, safeEval
|
|
12
12
|
from fontTools.misc.lazyTools import LazyList
|
|
13
|
-
from fontTools.ttLib import getSearchRange
|
|
13
|
+
from fontTools.ttLib import OPTIMIZE_FONT_SPEED, getSearchRange
|
|
14
14
|
from .otBase import (
|
|
15
15
|
CountReference,
|
|
16
16
|
FormatSwitchingBaseTable,
|
|
@@ -1810,7 +1810,10 @@ class TupleValues:
|
|
|
1810
1810
|
return TupleVariation.decompileDeltas_(None, data)[0]
|
|
1811
1811
|
|
|
1812
1812
|
def write(self, writer, font, tableDict, values, repeatIndex=None):
|
|
1813
|
-
|
|
1813
|
+
optimizeSpeed = font.cfg[OPTIMIZE_FONT_SPEED]
|
|
1814
|
+
return bytes(
|
|
1815
|
+
TupleVariation.compileDeltaValues_(values, optimizeSize=not optimizeSpeed)
|
|
1816
|
+
)
|
|
1814
1817
|
|
|
1815
1818
|
def xmlRead(self, attrs, content, font):
|
|
1816
1819
|
return safeEval(attrs["value"])
|
fontTools/ttLib/tables/otData.py
CHANGED
|
@@ -11,6 +11,7 @@ from functools import reduce
|
|
|
11
11
|
from math import radians
|
|
12
12
|
import itertools
|
|
13
13
|
from collections import defaultdict, namedtuple
|
|
14
|
+
from fontTools.ttLib import OPTIMIZE_FONT_SPEED
|
|
14
15
|
from fontTools.ttLib.tables.TupleVariation import TupleVariation
|
|
15
16
|
from fontTools.ttLib.tables.otTraverse import dfs_base_table
|
|
16
17
|
from fontTools.misc.arrayTools import quantizeRect
|
|
@@ -236,6 +237,8 @@ class VarComponent:
|
|
|
236
237
|
return data[i:]
|
|
237
238
|
|
|
238
239
|
def compile(self, font):
|
|
240
|
+
optimizeSpeed = font.cfg[OPTIMIZE_FONT_SPEED]
|
|
241
|
+
|
|
239
242
|
data = []
|
|
240
243
|
|
|
241
244
|
flags = self.flags
|
|
@@ -259,7 +262,8 @@ class VarComponent:
|
|
|
259
262
|
data.append(_write_uint32var(self.axisIndicesIndex))
|
|
260
263
|
data.append(
|
|
261
264
|
TupleVariation.compileDeltaValues_(
|
|
262
|
-
[fl2fi(v, 14) for v in self.axisValues]
|
|
265
|
+
[fl2fi(v, 14) for v in self.axisValues],
|
|
266
|
+
optimizeSize=not optimizeSpeed,
|
|
263
267
|
)
|
|
264
268
|
)
|
|
265
269
|
else:
|
|
@@ -2226,24 +2230,28 @@ _equivalents = {
|
|
|
2226
2230
|
def fixLookupOverFlows(ttf, overflowRecord):
|
|
2227
2231
|
"""Either the offset from the LookupList to a lookup overflowed, or
|
|
2228
2232
|
an offset from a lookup to a subtable overflowed.
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2233
|
+
|
|
2234
|
+
The table layout is::
|
|
2235
|
+
|
|
2236
|
+
GPSO/GUSB
|
|
2237
|
+
Script List
|
|
2238
|
+
Feature List
|
|
2239
|
+
LookUpList
|
|
2240
|
+
Lookup[0] and contents
|
|
2241
|
+
SubTable offset list
|
|
2242
|
+
SubTable[0] and contents
|
|
2243
|
+
...
|
|
2244
|
+
SubTable[n] and contents
|
|
2245
|
+
...
|
|
2246
|
+
Lookup[n] and contents
|
|
2247
|
+
SubTable offset list
|
|
2248
|
+
SubTable[0] and contents
|
|
2249
|
+
...
|
|
2250
|
+
SubTable[n] and contents
|
|
2251
|
+
|
|
2245
2252
|
If the offset to a lookup overflowed (SubTableIndex is None)
|
|
2246
|
-
we must promote the *previous*
|
|
2253
|
+
we must promote the *previous* lookup to an Extension type.
|
|
2254
|
+
|
|
2247
2255
|
If the offset from a lookup to subtable overflowed, then we must promote it
|
|
2248
2256
|
to an Extension Lookup type.
|
|
2249
2257
|
"""
|
|
@@ -79,7 +79,8 @@ def bfs_base_table(
|
|
|
79
79
|
"""Breadth-first search tree of BaseTables.
|
|
80
80
|
|
|
81
81
|
Args:
|
|
82
|
-
|
|
82
|
+
root
|
|
83
|
+
the root of the tree.
|
|
83
84
|
root_accessor (Optional[str]): attribute name for the root table, if any (mostly
|
|
84
85
|
useful for debugging).
|
|
85
86
|
skip_root (Optional[bool]): if True, the root itself is not visited, only its
|
fontTools/ttLib/ttFont.py
CHANGED
|
@@ -27,6 +27,7 @@ class TTFont(object):
|
|
|
27
27
|
they're actually accessed. This means that simple operations can be extremely fast.
|
|
28
28
|
|
|
29
29
|
Example usage:
|
|
30
|
+
|
|
30
31
|
.. code-block:: pycon
|
|
31
32
|
|
|
32
33
|
>>>
|
|
@@ -39,8 +40,10 @@ class TTFont(object):
|
|
|
39
40
|
>> tt['head'].unitsPerEm
|
|
40
41
|
2048
|
|
41
42
|
|
|
42
|
-
For details of the objects returned when accessing each table, see
|
|
43
|
+
For details of the objects returned when accessing each table, see the
|
|
44
|
+
:doc:`tables </ttLib/tables>` documentation.
|
|
43
45
|
To add a table to the font, use the :py:func:`newTable` function:
|
|
46
|
+
|
|
44
47
|
.. code-block:: pycon
|
|
45
48
|
|
|
46
49
|
>>>
|
|
@@ -50,7 +53,8 @@ class TTFont(object):
|
|
|
50
53
|
>> font["OS/2"] = os2
|
|
51
54
|
|
|
52
55
|
TrueType fonts can also be serialized to and from XML format (see also the
|
|
53
|
-
:
|
|
56
|
+
:doc:`ttx </ttx>` binary):
|
|
57
|
+
|
|
54
58
|
.. code-block:: pycon
|
|
55
59
|
|
|
56
60
|
>>
|
|
@@ -589,10 +593,8 @@ class TTFont(object):
|
|
|
589
593
|
# temporary cmap and by the real cmap in case we don't find a unicode
|
|
590
594
|
# cmap.
|
|
591
595
|
numGlyphs = int(self["maxp"].numGlyphs)
|
|
592
|
-
glyphOrder = [
|
|
596
|
+
glyphOrder = ["glyph%.5d" % i for i in range(numGlyphs)]
|
|
593
597
|
glyphOrder[0] = ".notdef"
|
|
594
|
-
for i in range(1, numGlyphs):
|
|
595
|
-
glyphOrder[i] = "glyph%.5d" % i
|
|
596
598
|
# Set the glyph order, so the cmap parser has something
|
|
597
599
|
# to work with (so we don't get called recursively).
|
|
598
600
|
self.glyphOrder = glyphOrder
|
|
@@ -602,7 +604,7 @@ class TTFont(object):
|
|
|
602
604
|
# this naming table will usually not cover all glyphs in the font.
|
|
603
605
|
# If the font has no Unicode cmap table, reversecmap will be empty.
|
|
604
606
|
if "cmap" in self:
|
|
605
|
-
reversecmap = self["cmap"].
|
|
607
|
+
reversecmap = self["cmap"].buildReversedMin()
|
|
606
608
|
else:
|
|
607
609
|
reversecmap = {}
|
|
608
610
|
useCount = {}
|
|
@@ -612,7 +614,7 @@ class TTFont(object):
|
|
|
612
614
|
# If a font maps both U+0041 LATIN CAPITAL LETTER A and
|
|
613
615
|
# U+0391 GREEK CAPITAL LETTER ALPHA to the same glyph,
|
|
614
616
|
# we prefer naming the glyph as "A".
|
|
615
|
-
glyphName = self._makeGlyphName(
|
|
617
|
+
glyphName = self._makeGlyphName(reversecmap[tempName])
|
|
616
618
|
numUses = useCount[glyphName] = useCount.get(glyphName, 0) + 1
|
|
617
619
|
if numUses > 1:
|
|
618
620
|
glyphName = "%s.alt%d" % (glyphName, numUses - 1)
|
fontTools/ttLib/ttGlyphSet.py
CHANGED
|
@@ -104,16 +104,6 @@ class _TTGlyphSetGlyf(_TTGlyphSet):
|
|
|
104
104
|
return _TTGlyphGlyf(self, glyphName, recalcBounds=self.recalcBounds)
|
|
105
105
|
|
|
106
106
|
|
|
107
|
-
class _TTGlyphSetGlyf(_TTGlyphSet):
|
|
108
|
-
def __init__(self, font, location, recalcBounds=True):
|
|
109
|
-
self.glyfTable = font["glyf"]
|
|
110
|
-
super().__init__(font, location, self.glyfTable, recalcBounds=recalcBounds)
|
|
111
|
-
self.gvarTable = font.get("gvar")
|
|
112
|
-
|
|
113
|
-
def __getitem__(self, glyphName):
|
|
114
|
-
return _TTGlyphGlyf(self, glyphName, recalcBounds=self.recalcBounds)
|
|
115
|
-
|
|
116
|
-
|
|
117
107
|
class _TTGlyphSetCFF(_TTGlyphSet):
|
|
118
108
|
def __init__(self, font, location):
|
|
119
109
|
tableTag = "CFF2" if "CFF2" in font else "CFF "
|
fontTools/ttx.py
CHANGED
|
@@ -101,9 +101,15 @@ Compile options
|
|
|
101
101
|
--with-zopfli
|
|
102
102
|
Use Zopfli instead of Zlib to compress WOFF. The Python
|
|
103
103
|
extension is available at https://pypi.python.org/pypi/zopfli
|
|
104
|
+
--optimize-font-speed
|
|
105
|
+
Enable optimizations that prioritize speed over file size.
|
|
106
|
+
This mainly affects how glyf t able and gvar / VARC tables are
|
|
107
|
+
compiled. The produced fonts will be larger, but rendering
|
|
108
|
+
performance will be improved with HarfBuzz and other text
|
|
109
|
+
layout engines.
|
|
104
110
|
"""
|
|
105
111
|
|
|
106
|
-
from fontTools.ttLib import TTFont, TTLibError
|
|
112
|
+
from fontTools.ttLib import OPTIMIZE_FONT_SPEED, TTFont, TTLibError
|
|
107
113
|
from fontTools.misc.macCreatorType import getMacCreatorAndType
|
|
108
114
|
from fontTools.unicode import setUnicodeData
|
|
109
115
|
from fontTools.misc.textTools import Tag, tostr
|
|
@@ -141,6 +147,7 @@ class Options(object):
|
|
|
141
147
|
recalcTimestamp = None
|
|
142
148
|
flavor = None
|
|
143
149
|
useZopfli = False
|
|
150
|
+
optimizeFontSpeed = False
|
|
144
151
|
|
|
145
152
|
def __init__(self, rawOptions, numFiles):
|
|
146
153
|
self.onlyTables = []
|
|
@@ -229,6 +236,8 @@ class Options(object):
|
|
|
229
236
|
self.flavor = value
|
|
230
237
|
elif option == "--with-zopfli":
|
|
231
238
|
self.useZopfli = True
|
|
239
|
+
elif option == "--optimize-font-speed":
|
|
240
|
+
self.optimizeFontSpeed = True
|
|
232
241
|
if self.verbose and self.quiet:
|
|
233
242
|
raise getopt.GetoptError("-q and -v options are mutually exclusive")
|
|
234
243
|
if self.verbose:
|
|
@@ -324,6 +333,8 @@ def ttCompile(input, output, options):
|
|
|
324
333
|
recalcBBoxes=options.recalcBBoxes,
|
|
325
334
|
recalcTimestamp=options.recalcTimestamp,
|
|
326
335
|
)
|
|
336
|
+
if options.optimizeFontSpeed:
|
|
337
|
+
ttf.cfg[OPTIMIZE_FONT_SPEED] = options.optimizeFontSpeed
|
|
327
338
|
ttf.importXML(input)
|
|
328
339
|
|
|
329
340
|
if options.recalcTimestamp is None and "head" in ttf and input is not sys.stdin:
|
|
@@ -386,6 +397,7 @@ def parseOptions(args):
|
|
|
386
397
|
"version",
|
|
387
398
|
"with-zopfli",
|
|
388
399
|
"newline=",
|
|
400
|
+
"optimize-font-speed",
|
|
389
401
|
],
|
|
390
402
|
)
|
|
391
403
|
|
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
|
"""
|
|
@@ -325,7 +325,6 @@ def _add_gvar(font, masterModel, master_ttfs, tolerance=0.5, optimize=True):
|
|
|
325
325
|
|
|
326
326
|
for glyph in font.getGlyphOrder():
|
|
327
327
|
log.debug("building gvar for glyph '%s'", glyph)
|
|
328
|
-
isComposite = glyf[glyph].isComposite()
|
|
329
328
|
|
|
330
329
|
allData = [
|
|
331
330
|
m.glyf._getCoordinatesAndControls(glyph, m.hMetrics, m.vMetrics)
|
|
@@ -363,7 +362,7 @@ def _add_gvar(font, masterModel, master_ttfs, tolerance=0.5, optimize=True):
|
|
|
363
362
|
endPts = control.endPts
|
|
364
363
|
|
|
365
364
|
for i, (delta, support) in enumerate(zip(deltas[1:], supports[1:])):
|
|
366
|
-
if all(v == 0 for v in delta.array)
|
|
365
|
+
if all(v == 0 for v in delta.array):
|
|
367
366
|
continue
|
|
368
367
|
var = TupleVariation(support, delta)
|
|
369
368
|
if optimize:
|
|
@@ -372,16 +371,6 @@ def _add_gvar(font, masterModel, master_ttfs, tolerance=0.5, optimize=True):
|
|
|
372
371
|
)
|
|
373
372
|
|
|
374
373
|
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
374
|
# Use "optimized" version only if smaller...
|
|
386
375
|
var_opt = TupleVariation(support, delta_opt)
|
|
387
376
|
|
|
@@ -490,7 +479,15 @@ def _merge_TTHinting(font, masterModel, master_ttfs):
|
|
|
490
479
|
|
|
491
480
|
_MetricsFields = namedtuple(
|
|
492
481
|
"_MetricsFields",
|
|
493
|
-
[
|
|
482
|
+
[
|
|
483
|
+
"tableTag",
|
|
484
|
+
"metricsTag",
|
|
485
|
+
"sb1",
|
|
486
|
+
"sb2",
|
|
487
|
+
"advMapping",
|
|
488
|
+
"vOrigMapping",
|
|
489
|
+
"phantomIndex",
|
|
490
|
+
],
|
|
494
491
|
)
|
|
495
492
|
|
|
496
493
|
HVAR_FIELDS = _MetricsFields(
|
|
@@ -500,6 +497,7 @@ HVAR_FIELDS = _MetricsFields(
|
|
|
500
497
|
sb2="RsbMap",
|
|
501
498
|
advMapping="AdvWidthMap",
|
|
502
499
|
vOrigMapping=None,
|
|
500
|
+
phantomIndex=0,
|
|
503
501
|
)
|
|
504
502
|
|
|
505
503
|
VVAR_FIELDS = _MetricsFields(
|
|
@@ -509,109 +507,43 @@ VVAR_FIELDS = _MetricsFields(
|
|
|
509
507
|
sb2="BsbMap",
|
|
510
508
|
advMapping="AdvHeightMap",
|
|
511
509
|
vOrigMapping="VOrgMap",
|
|
510
|
+
phantomIndex=1,
|
|
512
511
|
)
|
|
513
512
|
|
|
514
513
|
|
|
515
514
|
def _add_HVAR(font, masterModel, master_ttfs, axisTags):
|
|
516
|
-
|
|
515
|
+
getAdvanceMetrics = partial(
|
|
516
|
+
_get_advance_metrics, font, masterModel, master_ttfs, axisTags, HVAR_FIELDS
|
|
517
|
+
)
|
|
518
|
+
_add_VHVAR(font, axisTags, HVAR_FIELDS, getAdvanceMetrics)
|
|
517
519
|
|
|
518
520
|
|
|
519
521
|
def _add_VVAR(font, masterModel, master_ttfs, axisTags):
|
|
520
|
-
|
|
522
|
+
getAdvanceMetrics = partial(
|
|
523
|
+
_get_advance_metrics, font, masterModel, master_ttfs, axisTags, VVAR_FIELDS
|
|
524
|
+
)
|
|
525
|
+
_add_VHVAR(font, axisTags, VVAR_FIELDS, getAdvanceMetrics)
|
|
521
526
|
|
|
522
527
|
|
|
523
|
-
def _add_VHVAR(font,
|
|
528
|
+
def _add_VHVAR(font, axisTags, tableFields, getAdvanceMetrics):
|
|
524
529
|
tableTag = tableFields.tableTag
|
|
525
530
|
assert tableTag not in font
|
|
531
|
+
glyphOrder = font.getGlyphOrder()
|
|
526
532
|
log.info("Generating " + tableTag)
|
|
527
533
|
VHVAR = newTable(tableTag)
|
|
528
534
|
tableClass = getattr(ot, tableTag)
|
|
529
535
|
vhvar = VHVAR.table = tableClass()
|
|
530
536
|
vhvar.Version = 0x00010000
|
|
531
537
|
|
|
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
|
-
)
|
|
538
|
+
vhAdvanceDeltasAndSupports, vOrigDeltasAndSupports = getAdvanceMetrics()
|
|
555
539
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
setattr(vhvar, tableFields.advMapping, None)
|
|
540
|
+
if vOrigDeltasAndSupports:
|
|
541
|
+
singleModel = False
|
|
559
542
|
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
|
|
543
|
+
singleModel = models.allEqual(
|
|
544
|
+
id(v[1]) for v in vhAdvanceDeltasAndSupports.values()
|
|
597
545
|
)
|
|
598
546
|
|
|
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
547
|
directStore = None
|
|
616
548
|
if singleModel:
|
|
617
549
|
# Build direct mapping
|
|
@@ -632,7 +564,7 @@ def _get_advance_metrics(
|
|
|
632
564
|
storeBuilder.setSupports(supports)
|
|
633
565
|
advMapping[glyphName] = storeBuilder.storeDeltas(deltas, round=noRound)
|
|
634
566
|
|
|
635
|
-
if
|
|
567
|
+
if vOrigDeltasAndSupports:
|
|
636
568
|
vOrigMap = {}
|
|
637
569
|
for glyphName in glyphOrder:
|
|
638
570
|
deltas, supports = vOrigDeltasAndSupports[glyphName]
|
|
@@ -644,7 +576,7 @@ def _get_advance_metrics(
|
|
|
644
576
|
advMapping = [mapping2[advMapping[g]] for g in glyphOrder]
|
|
645
577
|
advanceMapping = builder.buildVarIdxMap(advMapping, glyphOrder)
|
|
646
578
|
|
|
647
|
-
if
|
|
579
|
+
if vOrigDeltasAndSupports:
|
|
648
580
|
vOrigMap = [mapping2[vOrigMap[g]] for g in glyphOrder]
|
|
649
581
|
|
|
650
582
|
useDirect = False
|
|
@@ -668,10 +600,70 @@ def _get_advance_metrics(
|
|
|
668
600
|
advanceMapping = None
|
|
669
601
|
else:
|
|
670
602
|
metricsStore = indirectStore
|
|
671
|
-
if
|
|
603
|
+
if vOrigDeltasAndSupports:
|
|
672
604
|
vOrigMapping = builder.buildVarIdxMap(vOrigMap, glyphOrder)
|
|
673
605
|
|
|
674
|
-
|
|
606
|
+
vhvar.VarStore = metricsStore
|
|
607
|
+
setattr(vhvar, tableFields.advMapping, advanceMapping)
|
|
608
|
+
if vOrigMapping is not None:
|
|
609
|
+
setattr(vhvar, tableFields.vOrigMapping, vOrigMapping)
|
|
610
|
+
setattr(vhvar, tableFields.sb1, None)
|
|
611
|
+
setattr(vhvar, tableFields.sb2, None)
|
|
612
|
+
|
|
613
|
+
font[tableTag] = VHVAR
|
|
614
|
+
return
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
def _get_advance_metrics(font, masterModel, master_ttfs, axisTags, tableFields):
|
|
618
|
+
tableTag = tableFields.tableTag
|
|
619
|
+
glyphOrder = font.getGlyphOrder()
|
|
620
|
+
|
|
621
|
+
# Build list of source font advance widths for each glyph
|
|
622
|
+
metricsTag = tableFields.metricsTag
|
|
623
|
+
advMetricses = [m[metricsTag].metrics for m in master_ttfs]
|
|
624
|
+
|
|
625
|
+
# Build list of source font vertical origin coords for each glyph
|
|
626
|
+
if tableTag == "VVAR" and "VORG" in master_ttfs[0]:
|
|
627
|
+
vOrigMetricses = [m["VORG"].VOriginRecords for m in master_ttfs]
|
|
628
|
+
defaultYOrigs = [m["VORG"].defaultVertOriginY for m in master_ttfs]
|
|
629
|
+
vOrigMetricses = list(zip(vOrigMetricses, defaultYOrigs))
|
|
630
|
+
else:
|
|
631
|
+
vOrigMetricses = None
|
|
632
|
+
|
|
633
|
+
vhAdvanceDeltasAndSupports = {}
|
|
634
|
+
vOrigDeltasAndSupports = {}
|
|
635
|
+
# HACK: we treat width 65535 as a sentinel value to signal that a glyph
|
|
636
|
+
# from a non-default master should not participate in computing {H,V}VAR,
|
|
637
|
+
# as if it were missing. Allows to variate other glyph-related data independently
|
|
638
|
+
# from glyph metrics
|
|
639
|
+
sparse_advance = 0xFFFF
|
|
640
|
+
for glyph in glyphOrder:
|
|
641
|
+
vhAdvances = [
|
|
642
|
+
(
|
|
643
|
+
metrics[glyph][0]
|
|
644
|
+
if glyph in metrics and metrics[glyph][0] != sparse_advance
|
|
645
|
+
else None
|
|
646
|
+
)
|
|
647
|
+
for metrics in advMetricses
|
|
648
|
+
]
|
|
649
|
+
vhAdvanceDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(
|
|
650
|
+
vhAdvances, round=round
|
|
651
|
+
)
|
|
652
|
+
|
|
653
|
+
if vOrigMetricses:
|
|
654
|
+
for glyph in glyphOrder:
|
|
655
|
+
# We need to supply a vOrigs tuple with non-None default values
|
|
656
|
+
# for each glyph. vOrigMetricses contains values only for those
|
|
657
|
+
# glyphs which have a non-default vOrig.
|
|
658
|
+
vOrigs = [
|
|
659
|
+
metrics[glyph] if glyph in metrics else defaultVOrig
|
|
660
|
+
for metrics, defaultVOrig in vOrigMetricses
|
|
661
|
+
]
|
|
662
|
+
vOrigDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(
|
|
663
|
+
vOrigs, round=round
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
return vhAdvanceDeltasAndSupports, vOrigDeltasAndSupports
|
|
675
667
|
|
|
676
668
|
|
|
677
669
|
def _add_MVAR(font, masterModel, master_ttfs, axisTags):
|