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
fontTools/ttLib/reorderGlyphs.py
CHANGED
|
@@ -275,10 +275,11 @@ def reorderGlyphs(font: ttLib.TTFont, new_glyph_order: List[str]):
|
|
|
275
275
|
for reorder in _REORDER_RULES.get(reorder_key, []):
|
|
276
276
|
reorder.apply(font, value)
|
|
277
277
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
278
|
+
for tag in ["CFF ", "CFF2"]:
|
|
279
|
+
if tag in font:
|
|
280
|
+
cff_table = font[tag]
|
|
281
|
+
charstrings = cff_table.cff.topDictIndex[0].CharStrings.charStrings
|
|
282
|
+
cff_table.cff.topDictIndex[0].charset = new_glyph_order
|
|
283
|
+
cff_table.cff.topDictIndex[0].CharStrings.charStrings = {
|
|
284
|
+
k: charstrings.get(k) for k in new_glyph_order
|
|
285
|
+
}
|
fontTools/ttLib/sfnt.py
CHANGED
|
@@ -13,6 +13,9 @@ classes, since whenever the number of tables changes or whenever
|
|
|
13
13
|
a table's length changes you need to rewrite the whole file anyway.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from collections.abc import KeysView
|
|
16
19
|
from io import BytesIO
|
|
17
20
|
from types import SimpleNamespace
|
|
18
21
|
from fontTools.misc.textTools import Tag
|
|
@@ -84,7 +87,7 @@ class SFNTReader(object):
|
|
|
84
87
|
|
|
85
88
|
if self.sfntVersion not in ("\x00\x01\x00\x00", "OTTO", "true"):
|
|
86
89
|
raise TTLibError("Not a TrueType or OpenType font (bad sfntVersion)")
|
|
87
|
-
tables = {}
|
|
90
|
+
tables: dict[Tag, DirectoryEntry] = {}
|
|
88
91
|
for i in range(self.numTables):
|
|
89
92
|
entry = self.DirectoryEntry()
|
|
90
93
|
entry.fromFile(self.file)
|
|
@@ -96,15 +99,15 @@ class SFNTReader(object):
|
|
|
96
99
|
if self.flavor == "woff":
|
|
97
100
|
self.flavorData = WOFFFlavorData(self)
|
|
98
101
|
|
|
99
|
-
def has_key(self, tag):
|
|
102
|
+
def has_key(self, tag: str | bytes) -> bool:
|
|
100
103
|
return tag in self.tables
|
|
101
104
|
|
|
102
105
|
__contains__ = has_key
|
|
103
106
|
|
|
104
|
-
def keys(self):
|
|
107
|
+
def keys(self) -> KeysView[Tag]:
|
|
105
108
|
return self.tables.keys()
|
|
106
109
|
|
|
107
|
-
def __getitem__(self, tag):
|
|
110
|
+
def __getitem__(self, tag: str | bytes) -> bytes:
|
|
108
111
|
"""Fetch the raw table data."""
|
|
109
112
|
entry = self.tables[Tag(tag)]
|
|
110
113
|
data = entry.loadData(self.file)
|
|
@@ -122,10 +125,10 @@ class SFNTReader(object):
|
|
|
122
125
|
log.warning("bad checksum for '%s' table", tag)
|
|
123
126
|
return data
|
|
124
127
|
|
|
125
|
-
def __delitem__(self, tag):
|
|
128
|
+
def __delitem__(self, tag: str | bytes) -> None:
|
|
126
129
|
del self.tables[Tag(tag)]
|
|
127
130
|
|
|
128
|
-
def close(self):
|
|
131
|
+
def close(self) -> None:
|
|
129
132
|
self.file.close()
|
|
130
133
|
|
|
131
134
|
# We define custom __getstate__ and __setstate__ to make SFNTReader pickle-able
|
|
@@ -375,10 +378,9 @@ class SFNTWriter(object):
|
|
|
375
378
|
|
|
376
379
|
def _calcMasterChecksum(self, directory):
|
|
377
380
|
# calculate checkSumAdjustment
|
|
378
|
-
tags = list(self.tables.keys())
|
|
379
381
|
checksums = []
|
|
380
|
-
for
|
|
381
|
-
checksums.append(self.tables[
|
|
382
|
+
for tag in self.tables.keys():
|
|
383
|
+
checksums.append(self.tables[tag].checkSum)
|
|
382
384
|
|
|
383
385
|
if self.DirectoryEntry != SFNTDirectoryEntry:
|
|
384
386
|
# Create a SFNT directory for checksum calculation purposes
|
|
@@ -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))
|
|
@@ -948,7 +948,7 @@ class Pass(object):
|
|
|
948
948
|
writer.newline()
|
|
949
949
|
writer.begintag("rules")
|
|
950
950
|
writer.newline()
|
|
951
|
-
for i in
|
|
951
|
+
for i, action in enumerate(self.actions):
|
|
952
952
|
writer.begintag(
|
|
953
953
|
"rule",
|
|
954
954
|
index=i,
|
|
@@ -958,7 +958,7 @@ class Pass(object):
|
|
|
958
958
|
writer.newline()
|
|
959
959
|
if len(self.ruleConstraints[i]):
|
|
960
960
|
writecode("constraint", writer, self.ruleConstraints[i])
|
|
961
|
-
writecode("action", writer,
|
|
961
|
+
writecode("action", writer, action)
|
|
962
962
|
writer.endtag("rule")
|
|
963
963
|
writer.newline()
|
|
964
964
|
writer.endtag("rules")
|
|
@@ -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
|
)
|
|
@@ -91,12 +91,11 @@ class table_T_S_I__1(LogMixin, DefaultTable.DefaultTable):
|
|
|
91
91
|
glyphNames = ttFont.getGlyphOrder()
|
|
92
92
|
|
|
93
93
|
indices = []
|
|
94
|
-
for i in
|
|
94
|
+
for i, name in enumerate(glyphNames):
|
|
95
95
|
if len(data) % 2:
|
|
96
96
|
data = (
|
|
97
97
|
data + b"\015"
|
|
98
98
|
) # align on 2-byte boundaries, fill with return chars. Yum.
|
|
99
|
-
name = glyphNames[i]
|
|
100
99
|
if name in self.glyphPrograms:
|
|
101
100
|
text = tobytes(self.glyphPrograms[name], encoding="utf-8")
|
|
102
101
|
else:
|
|
@@ -108,13 +107,11 @@ class table_T_S_I__1(LogMixin, DefaultTable.DefaultTable):
|
|
|
108
107
|
data = data + text
|
|
109
108
|
|
|
110
109
|
extra_indices = []
|
|
111
|
-
|
|
112
|
-
for i in range(len(codes)):
|
|
110
|
+
for code, name in sorted(self.extras.items()):
|
|
113
111
|
if len(data) % 2:
|
|
114
112
|
data = (
|
|
115
113
|
data + b"\015"
|
|
116
114
|
) # align on 2-byte boundaries, fill with return chars.
|
|
117
|
-
code, name = codes[i]
|
|
118
115
|
if name in self.extraPrograms:
|
|
119
116
|
text = tobytes(self.extraPrograms[name], encoding="utf-8")
|
|
120
117
|
else:
|
|
@@ -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,29 +6,40 @@ 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):
|
|
28
39
|
glyphNames = ttFont.getGlyphOrder()
|
|
29
40
|
a = array.array("H")
|
|
30
|
-
for
|
|
31
|
-
a.append(self.glyphGrouping.get(
|
|
41
|
+
for glyphName in glyphNames:
|
|
42
|
+
a.append(self.glyphGrouping.get(glyphName, 0))
|
|
32
43
|
if sys.byteorder != "big":
|
|
33
44
|
a.byteswap()
|
|
34
45
|
return a.tobytes()
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Mapping, TYPE_CHECKING
|
|
1
4
|
from fontTools.misc import sstruct
|
|
2
5
|
from fontTools.misc.fixedTools import (
|
|
3
6
|
fixedToFloat as fi2fl,
|
|
@@ -20,6 +23,9 @@ log = logging.getLogger(__name__)
|
|
|
20
23
|
|
|
21
24
|
from .otBase import BaseTTXConverter
|
|
22
25
|
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from ..ttFont import TTFont
|
|
28
|
+
|
|
23
29
|
|
|
24
30
|
class table__a_v_a_r(BaseTTXConverter):
|
|
25
31
|
"""Axis Variations table
|
|
@@ -143,8 +149,9 @@ class table__a_v_a_r(BaseTTXConverter):
|
|
|
143
149
|
else:
|
|
144
150
|
super().fromXML(name, attrs, content, ttFont)
|
|
145
151
|
|
|
146
|
-
def renormalizeLocation(
|
|
147
|
-
|
|
152
|
+
def renormalizeLocation(
|
|
153
|
+
self, location: Mapping[str, float], font: TTFont, dropZeroes: bool = True
|
|
154
|
+
) -> dict[str, float]:
|
|
148
155
|
majorVersion = getattr(self, "majorVersion", 1)
|
|
149
156
|
|
|
150
157
|
if majorVersion not in (1, 2):
|
|
@@ -185,7 +192,9 @@ class table__a_v_a_r(BaseTTXConverter):
|
|
|
185
192
|
out.append(v)
|
|
186
193
|
|
|
187
194
|
mappedLocation = {
|
|
188
|
-
axis.axisTag: fi2fl(v, 14)
|
|
195
|
+
axis.axisTag: fi2fl(v, 14)
|
|
196
|
+
for v, axis in zip(out, axes)
|
|
197
|
+
if v != 0 or not dropZeroes
|
|
189
198
|
}
|
|
190
199
|
|
|
191
200
|
return mappedLocation
|
|
@@ -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)
|
|
@@ -387,7 +398,7 @@ class cmap_format_0(CmapSubtable):
|
|
|
387
398
|
assert 262 == self.length, "Format 0 cmap subtable not 262 bytes"
|
|
388
399
|
gids = array.array("B")
|
|
389
400
|
gids.frombytes(self.data)
|
|
390
|
-
charCodes =
|
|
401
|
+
charCodes = range(len(gids))
|
|
391
402
|
self.cmap = _make_map(self.ttFont, charCodes, gids)
|
|
392
403
|
|
|
393
404
|
def compile(self, ttFont):
|
|
@@ -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
|
@@ -21,14 +21,15 @@ class table__c_v_t(DefaultTable.DefaultTable):
|
|
|
21
21
|
self.values = values
|
|
22
22
|
|
|
23
23
|
def compile(self, ttFont):
|
|
24
|
+
if not hasattr(self, "values"):
|
|
25
|
+
return b""
|
|
24
26
|
values = self.values[:]
|
|
25
27
|
if sys.byteorder != "big":
|
|
26
28
|
values.byteswap()
|
|
27
29
|
return values.tobytes()
|
|
28
30
|
|
|
29
31
|
def toXML(self, writer, ttFont):
|
|
30
|
-
for i in
|
|
31
|
-
value = self.values[i]
|
|
32
|
+
for i, value in enumerate(self.values):
|
|
32
33
|
writer.simpletag("cv", value=value, index=i)
|
|
33
34
|
writer.newline()
|
|
34
35
|
|
|
@@ -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
|
|
|
@@ -969,11 +974,10 @@ class Glyph(object):
|
|
|
969
974
|
lastcomponent = len(self.components) - 1
|
|
970
975
|
more = 1
|
|
971
976
|
haveInstructions = 0
|
|
972
|
-
for i in
|
|
977
|
+
for i, compo in enumerate(self.components):
|
|
973
978
|
if i == lastcomponent:
|
|
974
979
|
haveInstructions = hasattr(self, "program")
|
|
975
980
|
more = 0
|
|
976
|
-
compo = self.components[i]
|
|
977
981
|
data = data + compo.compile(more, haveInstructions, glyfTable)
|
|
978
982
|
if haveInstructions:
|
|
979
983
|
instructions = self.program.getBytecode()
|
|
@@ -1187,7 +1191,7 @@ class Glyph(object):
|
|
|
1187
1191
|
):
|
|
1188
1192
|
return
|
|
1189
1193
|
try:
|
|
1190
|
-
coords, endPts, flags = self.getCoordinates(glyfTable)
|
|
1194
|
+
coords, endPts, flags = self.getCoordinates(glyfTable, round=otRound)
|
|
1191
1195
|
self.xMin, self.yMin, self.xMax, self.yMax = coords.calcIntBounds()
|
|
1192
1196
|
except NotImplementedError:
|
|
1193
1197
|
pass
|
|
@@ -1206,9 +1210,7 @@ class Glyph(object):
|
|
|
1206
1210
|
Return True if bounds were calculated, False otherwise.
|
|
1207
1211
|
"""
|
|
1208
1212
|
for compo in self.components:
|
|
1209
|
-
if
|
|
1210
|
-
return False
|
|
1211
|
-
if not float(compo.x).is_integer() or not float(compo.y).is_integer():
|
|
1213
|
+
if not compo._hasOnlyIntegerTranslate():
|
|
1212
1214
|
return False
|
|
1213
1215
|
|
|
1214
1216
|
# All components are untransformed and have an integer x/y translate
|
|
@@ -1222,7 +1224,7 @@ class Glyph(object):
|
|
|
1222
1224
|
if boundsDone is not None:
|
|
1223
1225
|
boundsDone.add(glyphName)
|
|
1224
1226
|
# empty components shouldn't update the bounds of the parent glyph
|
|
1225
|
-
if g.
|
|
1227
|
+
if g.yMin == g.yMax and g.xMin == g.xMax:
|
|
1226
1228
|
continue
|
|
1227
1229
|
|
|
1228
1230
|
x, y = compo.x, compo.y
|
|
@@ -1241,7 +1243,7 @@ class Glyph(object):
|
|
|
1241
1243
|
else:
|
|
1242
1244
|
return self.numberOfContours == -1
|
|
1243
1245
|
|
|
1244
|
-
def getCoordinates(self, glyfTable):
|
|
1246
|
+
def getCoordinates(self, glyfTable, *, round=noRound):
|
|
1245
1247
|
"""Return the coordinates, end points and flags
|
|
1246
1248
|
|
|
1247
1249
|
This method returns three values: A :py:class:`GlyphCoordinates` object,
|
|
@@ -1267,13 +1269,23 @@ class Glyph(object):
|
|
|
1267
1269
|
for compo in self.components:
|
|
1268
1270
|
g = glyfTable[compo.glyphName]
|
|
1269
1271
|
try:
|
|
1270
|
-
coordinates, endPts, flags = g.getCoordinates(
|
|
1272
|
+
coordinates, endPts, flags = g.getCoordinates(
|
|
1273
|
+
glyfTable, round=round
|
|
1274
|
+
)
|
|
1271
1275
|
except RecursionError:
|
|
1272
1276
|
raise ttLib.TTLibError(
|
|
1273
1277
|
"glyph '%s' contains a recursive component reference"
|
|
1274
1278
|
% compo.glyphName
|
|
1275
1279
|
)
|
|
1276
1280
|
coordinates = GlyphCoordinates(coordinates)
|
|
1281
|
+
# if asked to round e.g. while computing bboxes, it's important we
|
|
1282
|
+
# do it immediately before a component transform is applied to a
|
|
1283
|
+
# simple glyph's coordinates in case these might still contain floats;
|
|
1284
|
+
# however, if the referenced component glyph is another composite, we
|
|
1285
|
+
# must not round here but only at the end, after all the nested
|
|
1286
|
+
# transforms have been applied, or else rounding errors will compound.
|
|
1287
|
+
if round is not noRound and g.numberOfContours > 0:
|
|
1288
|
+
coordinates.toInt(round=round)
|
|
1277
1289
|
if hasattr(compo, "firstPt"):
|
|
1278
1290
|
# component uses two reference points: we apply the transform _before_
|
|
1279
1291
|
# computing the offset between the points
|
|
@@ -1930,6 +1942,18 @@ class GlyphComponent(object):
|
|
|
1930
1942
|
result = self.__eq__(other)
|
|
1931
1943
|
return result if result is NotImplemented else not result
|
|
1932
1944
|
|
|
1945
|
+
def _hasOnlyIntegerTranslate(self):
|
|
1946
|
+
"""Return True if it's a 'simple' component.
|
|
1947
|
+
|
|
1948
|
+
That is, it has no anchor points and no transform other than integer translate.
|
|
1949
|
+
"""
|
|
1950
|
+
return (
|
|
1951
|
+
not hasattr(self, "firstPt")
|
|
1952
|
+
and not hasattr(self, "transform")
|
|
1953
|
+
and float(self.x).is_integer()
|
|
1954
|
+
and float(self.y).is_integer()
|
|
1955
|
+
)
|
|
1956
|
+
|
|
1933
1957
|
|
|
1934
1958
|
class GlyphCoordinates(object):
|
|
1935
1959
|
"""A list of glyph coordinates.
|
|
@@ -2012,8 +2036,8 @@ class GlyphCoordinates(object):
|
|
|
2012
2036
|
if round is noRound:
|
|
2013
2037
|
return
|
|
2014
2038
|
a = self._a
|
|
2015
|
-
for i in
|
|
2016
|
-
a[i] = round(
|
|
2039
|
+
for i, value in enumerate(a):
|
|
2040
|
+
a[i] = round(value)
|
|
2017
2041
|
|
|
2018
2042
|
def calcBounds(self):
|
|
2019
2043
|
a = self._a
|
|
@@ -2143,8 +2167,8 @@ class GlyphCoordinates(object):
|
|
|
2143
2167
|
"""
|
|
2144
2168
|
r = self.copy()
|
|
2145
2169
|
a = r._a
|
|
2146
|
-
for i in
|
|
2147
|
-
a[i] = -
|
|
2170
|
+
for i, value in enumerate(a):
|
|
2171
|
+
a[i] = -value
|
|
2148
2172
|
return r
|
|
2149
2173
|
|
|
2150
2174
|
def __round__(self, *, round=otRound):
|
|
@@ -2189,8 +2213,8 @@ class GlyphCoordinates(object):
|
|
|
2189
2213
|
other = other._a
|
|
2190
2214
|
a = self._a
|
|
2191
2215
|
assert len(a) == len(other)
|
|
2192
|
-
for i in
|
|
2193
|
-
a[i] +=
|
|
2216
|
+
for i, value in enumerate(other):
|
|
2217
|
+
a[i] += value
|
|
2194
2218
|
return self
|
|
2195
2219
|
return NotImplemented
|
|
2196
2220
|
|
|
@@ -2213,8 +2237,8 @@ class GlyphCoordinates(object):
|
|
|
2213
2237
|
other = other._a
|
|
2214
2238
|
a = self._a
|
|
2215
2239
|
assert len(a) == len(other)
|
|
2216
|
-
for i in
|
|
2217
|
-
a[i] -=
|
|
2240
|
+
for i, value in enumerate(other):
|
|
2241
|
+
a[i] -= value
|
|
2218
2242
|
return self
|
|
2219
2243
|
return NotImplemented
|
|
2220
2244
|
|