fonttools 4.56.0__cp39-cp39-macosx_10_9_universal2.whl → 4.58.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/cffLib/__init__.py +61 -26
- fontTools/config/__init__.py +15 -0
- fontTools/cu2qu/cu2qu.c +4563 -4047
- fontTools/cu2qu/cu2qu.cpython-39-darwin.so +0 -0
- fontTools/designspaceLib/statNames.py +14 -7
- fontTools/feaLib/ast.py +92 -13
- fontTools/feaLib/builder.py +52 -13
- fontTools/feaLib/lexer.c +6268 -7111
- fontTools/feaLib/lexer.cpython-39-darwin.so +0 -0
- fontTools/feaLib/parser.py +59 -39
- fontTools/fontBuilder.py +6 -0
- fontTools/misc/bezierTools.c +13479 -15374
- fontTools/misc/bezierTools.cpython-39-darwin.so +0 -0
- fontTools/misc/etree.py +4 -27
- fontTools/misc/testTools.py +2 -1
- fontTools/mtiLib/__init__.py +0 -2
- fontTools/otlLib/builder.py +195 -145
- fontTools/otlLib/optimize/gpos.py +49 -63
- fontTools/pens/momentsPen.c +4492 -4674
- fontTools/pens/momentsPen.cpython-39-darwin.so +0 -0
- fontTools/pens/pointPen.py +21 -12
- fontTools/qu2cu/qu2cu.c +5727 -5458
- fontTools/qu2cu/qu2cu.cpython-39-darwin.so +0 -0
- fontTools/subset/__init__.py +11 -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/G_V_A_R_.py +5 -0
- fontTools/ttLib/tables/T_S_I__0.py +14 -3
- fontTools/ttLib/tables/T_S_I__5.py +16 -5
- fontTools/ttLib/tables/__init__.py +1 -0
- fontTools/ttLib/tables/_c_m_a_p.py +19 -6
- fontTools/ttLib/tables/_c_v_t.py +2 -0
- fontTools/ttLib/tables/_f_p_g_m.py +3 -1
- fontTools/ttLib/tables/_g_l_y_f.py +11 -10
- fontTools/ttLib/tables/_g_v_a_r.py +62 -17
- fontTools/ttLib/tables/_p_o_s_t.py +5 -2
- fontTools/ttLib/tables/otBase.py +1 -0
- fontTools/ttLib/tables/otConverters.py +5 -2
- fontTools/ttLib/tables/otTables.py +5 -1
- fontTools/ttLib/ttFont.py +3 -5
- fontTools/ttLib/ttGlyphSet.py +0 -10
- fontTools/ttx.py +13 -1
- fontTools/ufoLib/__init__.py +2 -2
- fontTools/ufoLib/converters.py +89 -25
- fontTools/ufoLib/errors.py +8 -0
- fontTools/ufoLib/etree.py +1 -1
- fontTools/ufoLib/filenames.py +155 -100
- fontTools/ufoLib/glifLib.py +9 -2
- fontTools/ufoLib/kerning.py +66 -36
- fontTools/ufoLib/utils.py +5 -2
- fontTools/unicodedata/Mirrored.py +446 -0
- fontTools/unicodedata/__init__.py +6 -2
- fontTools/varLib/__init__.py +94 -89
- fontTools/varLib/hvar.py +113 -0
- fontTools/varLib/iup.c +6700 -6224
- fontTools/varLib/iup.cpython-39-darwin.so +0 -0
- fontTools/varLib/varStore.py +1 -1
- 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.56.0.dist-info → fonttools-4.58.0.dist-info}/METADATA +60 -12
- {fonttools-4.56.0.dist-info → fonttools-4.58.0.dist-info}/RECORD +71 -66
- {fonttools-4.56.0.dist-info → fonttools-4.58.0.dist-info}/WHEEL +2 -1
- fonttools-4.58.0.dist-info/licenses/LICENSE.external +359 -0
- {fonttools-4.56.0.data → fonttools-4.58.0.data}/data/share/man/man1/ttx.1 +0 -0
- {fonttools-4.56.0.dist-info → fonttools-4.58.0.dist-info}/entry_points.txt +0 -0
- {fonttools-4.56.0.dist-info → fonttools-4.58.0.dist-info/licenses}/LICENSE +0 -0
- {fonttools-4.56.0.dist-info → fonttools-4.58.0.dist-info}/top_level.txt +0 -0
|
Binary file
|
fontTools/subset/__init__.py
CHANGED
|
@@ -16,6 +16,7 @@ from fontTools.subset.cff import *
|
|
|
16
16
|
from fontTools.subset.svg import *
|
|
17
17
|
from fontTools.varLib import varStore, multiVarStore # For monkey-patching
|
|
18
18
|
from fontTools.ttLib.tables._n_a_m_e import NameRecordVisitor
|
|
19
|
+
from fontTools.unicodedata import mirrored
|
|
19
20
|
import sys
|
|
20
21
|
import struct
|
|
21
22
|
import array
|
|
@@ -2870,6 +2871,15 @@ def prune_post_subset(self, font, options):
|
|
|
2870
2871
|
def closure_glyphs(self, s):
|
|
2871
2872
|
tables = [t for t in self.tables if t.isUnicode()]
|
|
2872
2873
|
|
|
2874
|
+
# Closure unicodes, which for now is pulling in bidi mirrored variants
|
|
2875
|
+
if s.options.bidi_closure:
|
|
2876
|
+
additional_unicodes = set()
|
|
2877
|
+
for u in s.unicodes_requested:
|
|
2878
|
+
mirror_u = mirrored(u)
|
|
2879
|
+
if mirror_u is not None:
|
|
2880
|
+
additional_unicodes.add(mirror_u)
|
|
2881
|
+
s.unicodes_requested.update(additional_unicodes)
|
|
2882
|
+
|
|
2873
2883
|
# Close glyphs
|
|
2874
2884
|
for table in tables:
|
|
2875
2885
|
if table.format == 14:
|
|
@@ -3191,6 +3201,7 @@ class Options(object):
|
|
|
3191
3201
|
self.font_number = -1
|
|
3192
3202
|
self.pretty_svg = False
|
|
3193
3203
|
self.lazy = True
|
|
3204
|
+
self.bidi_closure = True
|
|
3194
3205
|
|
|
3195
3206
|
self.set(**kwargs)
|
|
3196
3207
|
|
fontTools/ttLib/__init__.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""fontTools.ttLib -- a package for dealing with TrueType fonts."""
|
|
2
2
|
|
|
3
|
+
from fontTools.config import OPTIONS
|
|
3
4
|
from fontTools.misc.loggingTools import deprecateFunction
|
|
4
5
|
import logging
|
|
5
6
|
|
|
@@ -7,6 +8,9 @@ import logging
|
|
|
7
8
|
log = logging.getLogger(__name__)
|
|
8
9
|
|
|
9
10
|
|
|
11
|
+
OPTIMIZE_FONT_SPEED = OPTIONS["fontTools.ttLib:OPTIMIZE_FONT_SPEED"]
|
|
12
|
+
|
|
13
|
+
|
|
10
14
|
class TTLibError(Exception):
|
|
11
15
|
pass
|
|
12
16
|
|
fontTools/ttLib/__main__.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import sys
|
|
2
|
-
from fontTools.ttLib import TTLibError, TTLibFileIsCollectionError
|
|
2
|
+
from fontTools.ttLib import OPTIMIZE_FONT_SPEED, TTLibError, TTLibFileIsCollectionError
|
|
3
3
|
from fontTools.ttLib.ttFont import *
|
|
4
4
|
from fontTools.ttLib.ttCollection import TTCollection
|
|
5
5
|
|
|
@@ -51,7 +51,7 @@ def main(args=None):
|
|
|
51
51
|
)
|
|
52
52
|
parser.add_argument("font", metavar="font", nargs="*", help="Font file.")
|
|
53
53
|
parser.add_argument(
|
|
54
|
-
"-t", "--table", metavar="table",
|
|
54
|
+
"-t", "--table", metavar="table", action="append", help="Tables to decompile."
|
|
55
55
|
)
|
|
56
56
|
parser.add_argument(
|
|
57
57
|
"-o", "--output", metavar="FILE", default=None, help="Output file."
|
|
@@ -71,27 +71,66 @@ def main(args=None):
|
|
|
71
71
|
default=None,
|
|
72
72
|
help="Flavor of output font. 'woff' or 'woff2'.",
|
|
73
73
|
)
|
|
74
|
+
parser.add_argument(
|
|
75
|
+
"--no-recalc-timestamp",
|
|
76
|
+
dest="recalcTimestamp",
|
|
77
|
+
action="store_false",
|
|
78
|
+
help="Keep the original font 'modified' timestamp.",
|
|
79
|
+
)
|
|
80
|
+
parser.add_argument(
|
|
81
|
+
"-b",
|
|
82
|
+
dest="recalcBBoxes",
|
|
83
|
+
action="store_false",
|
|
84
|
+
help="Don't recalc glyph bounding boxes: use the values in the original font.",
|
|
85
|
+
)
|
|
86
|
+
parser.add_argument(
|
|
87
|
+
"--optimize-font-speed",
|
|
88
|
+
action="store_true",
|
|
89
|
+
help=(
|
|
90
|
+
"Enable optimizations that prioritize speed over file size. This "
|
|
91
|
+
"mainly affects how glyf table and gvar / VARC tables are compiled."
|
|
92
|
+
),
|
|
93
|
+
)
|
|
74
94
|
options = parser.parse_args(args)
|
|
75
95
|
|
|
76
96
|
fontNumber = int(options.y) if options.y is not None else None
|
|
77
97
|
outFile = options.output
|
|
78
98
|
lazy = options.lazy
|
|
79
99
|
flavor = options.flavor
|
|
80
|
-
tables = options.table
|
|
100
|
+
tables = options.table
|
|
101
|
+
recalcBBoxes = options.recalcBBoxes
|
|
102
|
+
recalcTimestamp = options.recalcTimestamp
|
|
103
|
+
optimizeFontSpeed = options.optimize_font_speed
|
|
81
104
|
|
|
82
105
|
fonts = []
|
|
83
106
|
for f in options.font:
|
|
84
107
|
try:
|
|
85
|
-
font = TTFont(
|
|
108
|
+
font = TTFont(
|
|
109
|
+
f,
|
|
110
|
+
recalcBBoxes=recalcBBoxes,
|
|
111
|
+
recalcTimestamp=recalcTimestamp,
|
|
112
|
+
fontNumber=fontNumber,
|
|
113
|
+
lazy=lazy,
|
|
114
|
+
)
|
|
115
|
+
if optimizeFontSpeed:
|
|
116
|
+
font.cfg[OPTIMIZE_FONT_SPEED] = optimizeFontSpeed
|
|
86
117
|
fonts.append(font)
|
|
87
118
|
except TTLibFileIsCollectionError:
|
|
88
119
|
collection = TTCollection(f, lazy=lazy)
|
|
89
120
|
fonts.extend(collection.fonts)
|
|
90
121
|
|
|
91
|
-
if
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
122
|
+
if tables is None:
|
|
123
|
+
if lazy is False:
|
|
124
|
+
tables = ["*"]
|
|
125
|
+
elif optimizeFontSpeed:
|
|
126
|
+
tables = {"glyf", "gvar", "VARC"}.intersection(font.keys())
|
|
127
|
+
else:
|
|
128
|
+
tables = []
|
|
129
|
+
for font in fonts:
|
|
130
|
+
if "GlyphOrder" in tables:
|
|
131
|
+
font.getGlyphOrder()
|
|
132
|
+
for table in tables if "*" not in tables else font.keys():
|
|
133
|
+
font[table] # Decompiles
|
|
95
134
|
|
|
96
135
|
if outFile is not None:
|
|
97
136
|
if len(fonts) == 1:
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import json
|
|
2
|
+
from textwrap import indent
|
|
2
3
|
|
|
3
4
|
from . import DefaultTable
|
|
5
|
+
from fontTools.misc.textTools import tostr
|
|
4
6
|
|
|
5
7
|
|
|
6
8
|
class table_D__e_b_g(DefaultTable.DefaultTable):
|
|
9
|
+
def __init__(self, tag=None):
|
|
10
|
+
DefaultTable.DefaultTable.__init__(self, tag)
|
|
11
|
+
self.data = {}
|
|
12
|
+
|
|
7
13
|
def decompile(self, data, ttFont):
|
|
8
14
|
self.data = json.loads(data)
|
|
9
15
|
|
|
@@ -11,7 +17,19 @@ class table_D__e_b_g(DefaultTable.DefaultTable):
|
|
|
11
17
|
return json.dumps(self.data).encode("utf-8")
|
|
12
18
|
|
|
13
19
|
def toXML(self, writer, ttFont):
|
|
14
|
-
|
|
20
|
+
# make sure json indentation inside CDATA block matches XMLWriter's
|
|
21
|
+
data = json.dumps(self.data, indent=len(writer.indentwhite))
|
|
22
|
+
prefix = tostr(writer.indentwhite) * (writer.indentlevel + 1)
|
|
23
|
+
# but don't indent the first json line so it's adjacent to `<![CDATA[{`
|
|
24
|
+
cdata = indent(data, prefix, lambda ln: ln != "{\n")
|
|
25
|
+
|
|
26
|
+
writer.begintag("json")
|
|
27
|
+
writer.newline()
|
|
28
|
+
writer.writecdata(cdata)
|
|
29
|
+
writer.newline()
|
|
30
|
+
writer.endtag("json")
|
|
31
|
+
writer.newline()
|
|
15
32
|
|
|
16
33
|
def fromXML(self, name, attrs, content, ttFont):
|
|
17
|
-
|
|
34
|
+
if name == "json":
|
|
35
|
+
self.data = json.loads("".join(content))
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT)
|
|
2
2
|
tool to store its hinting source data.
|
|
3
3
|
|
|
4
4
|
TSI0 is the index table containing the lengths and offsets for the glyph
|
|
@@ -8,9 +8,13 @@ in the TSI1 table.
|
|
|
8
8
|
See also https://learn.microsoft.com/en-us/typography/tools/vtt/tsi-tables
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
import logging
|
|
12
12
|
import struct
|
|
13
13
|
|
|
14
|
+
from . import DefaultTable
|
|
15
|
+
|
|
16
|
+
log = logging.getLogger(__name__)
|
|
17
|
+
|
|
14
18
|
tsi0Format = ">HHL"
|
|
15
19
|
|
|
16
20
|
|
|
@@ -25,7 +29,14 @@ class table_T_S_I__0(DefaultTable.DefaultTable):
|
|
|
25
29
|
numGlyphs = ttFont["maxp"].numGlyphs
|
|
26
30
|
indices = []
|
|
27
31
|
size = struct.calcsize(tsi0Format)
|
|
28
|
-
|
|
32
|
+
numEntries = len(data) // size
|
|
33
|
+
if numEntries != numGlyphs + 5:
|
|
34
|
+
diff = numEntries - numGlyphs - 5
|
|
35
|
+
log.warning(
|
|
36
|
+
"Number of glyphPrograms differs from the number of glyphs in the font "
|
|
37
|
+
f"by {abs(diff)} ({numEntries - 5} programs vs. {numGlyphs} glyphs)."
|
|
38
|
+
)
|
|
39
|
+
for _ in range(numEntries):
|
|
29
40
|
glyphID, textLength, textOffset = fixlongs(
|
|
30
41
|
*struct.unpack(tsi0Format, data[:size])
|
|
31
42
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""TSI{0,1,2,3,5} are private tables used by Microsoft Visual TrueType (VTT)
|
|
2
2
|
tool to store its hinting source data.
|
|
3
3
|
|
|
4
4
|
TSI5 contains the VTT character groups.
|
|
@@ -6,22 +6,33 @@ TSI5 contains the VTT character groups.
|
|
|
6
6
|
See also https://learn.microsoft.com/en-us/typography/tools/vtt/tsi-tables
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
+
import array
|
|
10
|
+
import logging
|
|
11
|
+
import sys
|
|
12
|
+
|
|
9
13
|
from fontTools.misc.textTools import safeEval
|
|
14
|
+
|
|
10
15
|
from . import DefaultTable
|
|
11
|
-
|
|
12
|
-
|
|
16
|
+
|
|
17
|
+
log = logging.getLogger(__name__)
|
|
13
18
|
|
|
14
19
|
|
|
15
20
|
class table_T_S_I__5(DefaultTable.DefaultTable):
|
|
16
21
|
def decompile(self, data, ttFont):
|
|
17
22
|
numGlyphs = ttFont["maxp"].numGlyphs
|
|
18
|
-
assert len(data) == 2 * numGlyphs
|
|
19
23
|
a = array.array("H")
|
|
20
24
|
a.frombytes(data)
|
|
21
25
|
if sys.byteorder != "big":
|
|
22
26
|
a.byteswap()
|
|
23
27
|
self.glyphGrouping = {}
|
|
24
|
-
|
|
28
|
+
numEntries = len(data) // 2
|
|
29
|
+
if numEntries != numGlyphs:
|
|
30
|
+
diff = numEntries - numGlyphs
|
|
31
|
+
log.warning(
|
|
32
|
+
"Number of entries differs from the number of glyphs in the font "
|
|
33
|
+
f"by {abs(diff)} ({numEntries} entries vs. {numGlyphs} glyphs)."
|
|
34
|
+
)
|
|
35
|
+
for i in range(numEntries):
|
|
25
36
|
self.glyphGrouping[ttFont.getGlyphName(i)] = a[i]
|
|
26
37
|
|
|
27
38
|
def compile(self, ttFont):
|
|
@@ -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)
|
fontTools/ttLib/tables/_c_v_t.py
CHANGED
|
@@ -20,7 +20,9 @@ class table__f_p_g_m(DefaultTable.DefaultTable):
|
|
|
20
20
|
self.program = program
|
|
21
21
|
|
|
22
22
|
def compile(self, ttFont):
|
|
23
|
-
|
|
23
|
+
if hasattr(self, "program"):
|
|
24
|
+
return self.program.getBytecode()
|
|
25
|
+
return b""
|
|
24
26
|
|
|
25
27
|
def toXML(self, writer, ttFont):
|
|
26
28
|
self.program.toXML(writer, ttFont)
|
|
@@ -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
|
|
|
@@ -1220,7 +1225,7 @@ class Glyph(object):
|
|
|
1220
1225
|
if boundsDone is not None:
|
|
1221
1226
|
boundsDone.add(glyphName)
|
|
1222
1227
|
# empty components shouldn't update the bounds of the parent glyph
|
|
1223
|
-
if g.
|
|
1228
|
+
if g.yMin == g.yMax and g.xMin == g.xMax:
|
|
1224
1229
|
continue
|
|
1225
1230
|
|
|
1226
1231
|
x, y = compo.x, compo.y
|
|
@@ -1280,11 +1285,7 @@ class Glyph(object):
|
|
|
1280
1285
|
# however, if the referenced component glyph is another composite, we
|
|
1281
1286
|
# must not round here but only at the end, after all the nested
|
|
1282
1287
|
# transforms have been applied, or else rounding errors will compound.
|
|
1283
|
-
if
|
|
1284
|
-
round is not noRound
|
|
1285
|
-
and g.numberOfContours > 0
|
|
1286
|
-
and not compo._hasOnlyIntegerTranslate()
|
|
1287
|
-
):
|
|
1288
|
+
if round is not noRound and g.numberOfContours > 0:
|
|
1288
1289
|
coordinates.toInt(round=round)
|
|
1289
1290
|
if hasattr(compo, "firstPt"):
|
|
1290
1291
|
# component uses two reference points: we apply the transform _before_
|
|
@@ -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
|
|
@@ -23,19 +24,24 @@ log = logging.getLogger(__name__)
|
|
|
23
24
|
# FreeType2 source code for parsing 'gvar':
|
|
24
25
|
# http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/src/truetype/ttgxvar.c
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
GVAR_HEADER_FORMAT_HEAD = """
|
|
27
28
|
> # big endian
|
|
28
29
|
version: H
|
|
29
30
|
reserved: H
|
|
30
31
|
axisCount: H
|
|
31
32
|
sharedTupleCount: H
|
|
32
33
|
offsetToSharedTuples: I
|
|
33
|
-
|
|
34
|
+
"""
|
|
35
|
+
# In between the HEAD and TAIL lies the glyphCount, which is
|
|
36
|
+
# of different size: 2 bytes for gvar, and 3 bytes for GVAR.
|
|
37
|
+
GVAR_HEADER_FORMAT_TAIL = """
|
|
38
|
+
> # big endian
|
|
34
39
|
flags: H
|
|
35
40
|
offsetToGlyphVariationData: I
|
|
36
41
|
"""
|
|
37
42
|
|
|
38
|
-
|
|
43
|
+
GVAR_HEADER_SIZE_HEAD = sstruct.calcsize(GVAR_HEADER_FORMAT_HEAD)
|
|
44
|
+
GVAR_HEADER_SIZE_TAIL = sstruct.calcsize(GVAR_HEADER_FORMAT_TAIL)
|
|
39
45
|
|
|
40
46
|
|
|
41
47
|
class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
@@ -50,6 +56,7 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
|
50
56
|
"""
|
|
51
57
|
|
|
52
58
|
dependencies = ["fvar", "glyf"]
|
|
59
|
+
gid_size = 2
|
|
53
60
|
|
|
54
61
|
def __init__(self, tag=None):
|
|
55
62
|
DefaultTable.DefaultTable.__init__(self, tag)
|
|
@@ -57,6 +64,7 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
|
57
64
|
self.variations = {}
|
|
58
65
|
|
|
59
66
|
def compile(self, ttFont):
|
|
67
|
+
|
|
60
68
|
axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
|
|
61
69
|
sharedTuples = tv.compileSharedTuples(
|
|
62
70
|
axisTags, itertools.chain(*self.variations.values())
|
|
@@ -72,28 +80,33 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
|
72
80
|
offsets.append(offset)
|
|
73
81
|
compiledOffsets, tableFormat = self.compileOffsets_(offsets)
|
|
74
82
|
|
|
83
|
+
GVAR_HEADER_SIZE = GVAR_HEADER_SIZE_HEAD + self.gid_size + GVAR_HEADER_SIZE_TAIL
|
|
75
84
|
header = {}
|
|
76
85
|
header["version"] = self.version
|
|
77
86
|
header["reserved"] = self.reserved
|
|
78
87
|
header["axisCount"] = len(axisTags)
|
|
79
88
|
header["sharedTupleCount"] = len(sharedTuples)
|
|
80
89
|
header["offsetToSharedTuples"] = GVAR_HEADER_SIZE + len(compiledOffsets)
|
|
81
|
-
header["glyphCount"] = len(compiledGlyphs)
|
|
82
90
|
header["flags"] = tableFormat
|
|
83
91
|
header["offsetToGlyphVariationData"] = (
|
|
84
92
|
header["offsetToSharedTuples"] + sharedTupleSize
|
|
85
93
|
)
|
|
86
|
-
compiledHeader = sstruct.pack(GVAR_HEADER_FORMAT, header)
|
|
87
94
|
|
|
88
|
-
result = [
|
|
95
|
+
result = [
|
|
96
|
+
sstruct.pack(GVAR_HEADER_FORMAT_HEAD, header),
|
|
97
|
+
len(compiledGlyphs).to_bytes(self.gid_size, "big"),
|
|
98
|
+
sstruct.pack(GVAR_HEADER_FORMAT_TAIL, header),
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
result.append(compiledOffsets)
|
|
89
102
|
result.extend(sharedTuples)
|
|
90
103
|
result.extend(compiledGlyphs)
|
|
91
104
|
return b"".join(result)
|
|
92
105
|
|
|
93
106
|
def compileGlyphs_(self, ttFont, axisTags, sharedCoordIndices):
|
|
107
|
+
optimizeSpeed = ttFont.cfg[OPTIMIZE_FONT_SPEED]
|
|
94
108
|
result = []
|
|
95
109
|
glyf = ttFont["glyf"]
|
|
96
|
-
optimizeSize = getattr(self, "optimizeSize", True)
|
|
97
110
|
for glyphName in ttFont.getGlyphOrder():
|
|
98
111
|
variations = self.variations.get(glyphName, [])
|
|
99
112
|
if not variations:
|
|
@@ -102,11 +115,12 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
|
102
115
|
pointCountUnused = 0 # pointCount is actually unused by compileGlyph
|
|
103
116
|
result.append(
|
|
104
117
|
compileGlyph_(
|
|
118
|
+
self.gid_size,
|
|
105
119
|
variations,
|
|
106
120
|
pointCountUnused,
|
|
107
121
|
axisTags,
|
|
108
122
|
sharedCoordIndices,
|
|
109
|
-
optimizeSize=
|
|
123
|
+
optimizeSize=not optimizeSpeed,
|
|
110
124
|
)
|
|
111
125
|
)
|
|
112
126
|
return result
|
|
@@ -114,7 +128,19 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
|
114
128
|
def decompile(self, data, ttFont):
|
|
115
129
|
axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
|
|
116
130
|
glyphs = ttFont.getGlyphOrder()
|
|
117
|
-
|
|
131
|
+
|
|
132
|
+
# Parse the header
|
|
133
|
+
GVAR_HEADER_SIZE = GVAR_HEADER_SIZE_HEAD + self.gid_size + GVAR_HEADER_SIZE_TAIL
|
|
134
|
+
sstruct.unpack(GVAR_HEADER_FORMAT_HEAD, data[:GVAR_HEADER_SIZE_HEAD], self)
|
|
135
|
+
self.glyphCount = int.from_bytes(
|
|
136
|
+
data[GVAR_HEADER_SIZE_HEAD : GVAR_HEADER_SIZE_HEAD + self.gid_size], "big"
|
|
137
|
+
)
|
|
138
|
+
sstruct.unpack(
|
|
139
|
+
GVAR_HEADER_FORMAT_TAIL,
|
|
140
|
+
data[GVAR_HEADER_SIZE_HEAD + self.gid_size : GVAR_HEADER_SIZE],
|
|
141
|
+
self,
|
|
142
|
+
)
|
|
143
|
+
|
|
118
144
|
assert len(glyphs) == self.glyphCount
|
|
119
145
|
assert len(axisTags) == self.axisCount
|
|
120
146
|
sharedCoords = tv.decompileSharedTuples(
|
|
@@ -144,7 +170,7 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
|
144
170
|
glyph = glyf[glyphName]
|
|
145
171
|
numPointsInGlyph = self.getNumPoints_(glyph)
|
|
146
172
|
return decompileGlyph_(
|
|
147
|
-
numPointsInGlyph, sharedCoords, axisTags, gvarData
|
|
173
|
+
self.gid_size, numPointsInGlyph, sharedCoords, axisTags, gvarData
|
|
148
174
|
)
|
|
149
175
|
|
|
150
176
|
return read_item
|
|
@@ -262,23 +288,42 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
|
262
288
|
|
|
263
289
|
|
|
264
290
|
def compileGlyph_(
|
|
265
|
-
|
|
291
|
+
dataOffsetSize,
|
|
292
|
+
variations,
|
|
293
|
+
pointCount,
|
|
294
|
+
axisTags,
|
|
295
|
+
sharedCoordIndices,
|
|
296
|
+
*,
|
|
297
|
+
optimizeSize=True,
|
|
266
298
|
):
|
|
299
|
+
assert dataOffsetSize in (2, 3)
|
|
267
300
|
tupleVariationCount, tuples, data = tv.compileTupleVariationStore(
|
|
268
301
|
variations, pointCount, axisTags, sharedCoordIndices, optimizeSize=optimizeSize
|
|
269
302
|
)
|
|
270
303
|
if tupleVariationCount == 0:
|
|
271
304
|
return b""
|
|
272
|
-
|
|
273
|
-
|
|
305
|
+
|
|
306
|
+
offsetToData = 2 + dataOffsetSize + len(tuples)
|
|
307
|
+
|
|
308
|
+
result = [
|
|
309
|
+
tupleVariationCount.to_bytes(2, "big"),
|
|
310
|
+
offsetToData.to_bytes(dataOffsetSize, "big"),
|
|
311
|
+
tuples,
|
|
312
|
+
data,
|
|
313
|
+
]
|
|
314
|
+
if (offsetToData + len(data)) % 2 != 0:
|
|
274
315
|
result.append(b"\0") # padding
|
|
275
316
|
return b"".join(result)
|
|
276
317
|
|
|
277
318
|
|
|
278
|
-
def decompileGlyph_(pointCount, sharedTuples, axisTags, data):
|
|
279
|
-
|
|
319
|
+
def decompileGlyph_(dataOffsetSize, pointCount, sharedTuples, axisTags, data):
|
|
320
|
+
assert dataOffsetSize in (2, 3)
|
|
321
|
+
if len(data) < 2 + dataOffsetSize:
|
|
280
322
|
return []
|
|
281
|
-
|
|
323
|
+
|
|
324
|
+
tupleVariationCount = int.from_bytes(data[:2], "big")
|
|
325
|
+
offsetToData = int.from_bytes(data[2 : 2 + dataOffsetSize], "big")
|
|
326
|
+
|
|
282
327
|
dataPos = offsetToData
|
|
283
328
|
return tv.decompileTupleVariationStore(
|
|
284
329
|
"gvar",
|
|
@@ -287,6 +332,6 @@ def decompileGlyph_(pointCount, sharedTuples, axisTags, data):
|
|
|
287
332
|
pointCount,
|
|
288
333
|
sharedTuples,
|
|
289
334
|
data,
|
|
290
|
-
|
|
335
|
+
2 + dataOffsetSize,
|
|
291
336
|
offsetToData,
|
|
292
337
|
)
|
|
@@ -122,13 +122,16 @@ class table__p_o_s_t(DefaultTable.DefaultTable):
|
|
|
122
122
|
glyphName = psName = self.glyphOrder[i]
|
|
123
123
|
if glyphName == "":
|
|
124
124
|
glyphName = "glyph%.5d" % i
|
|
125
|
+
|
|
125
126
|
if glyphName in allNames:
|
|
126
127
|
# make up a new glyphName that's unique
|
|
127
128
|
n = allNames[glyphName]
|
|
128
|
-
|
|
129
|
+
# check if the exists in any of the seen names or later ones
|
|
130
|
+
names = set(allNames.keys()) | set(self.glyphOrder)
|
|
131
|
+
while (glyphName + "." + str(n)) in names:
|
|
129
132
|
n += 1
|
|
130
133
|
allNames[glyphName] = n + 1
|
|
131
|
-
glyphName = glyphName + "
|
|
134
|
+
glyphName = glyphName + "." + str(n)
|
|
132
135
|
|
|
133
136
|
self.glyphOrder[i] = glyphName
|
|
134
137
|
allNames[glyphName] = 1
|
fontTools/ttLib/tables/otBase.py
CHANGED
|
@@ -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"])
|
|
@@ -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:
|