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/otlLib/builder.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from collections import namedtuple, OrderedDict
|
|
2
4
|
import itertools
|
|
3
|
-
import
|
|
5
|
+
from typing import Dict, Union
|
|
4
6
|
from fontTools.misc.fixedTools import fixedToFloat
|
|
5
7
|
from fontTools.misc.roundTools import otRound
|
|
6
8
|
from fontTools import ttLib
|
|
@@ -10,15 +12,15 @@ from fontTools.ttLib.tables.otBase import (
|
|
|
10
12
|
valueRecordFormatDict,
|
|
11
13
|
OTLOffsetOverflowError,
|
|
12
14
|
OTTableWriter,
|
|
13
|
-
CountReference,
|
|
14
15
|
)
|
|
15
|
-
from fontTools.ttLib.
|
|
16
|
+
from fontTools.ttLib.ttFont import TTFont
|
|
16
17
|
from fontTools.feaLib.ast import STATNameStatement
|
|
17
18
|
from fontTools.otlLib.optimize.gpos import (
|
|
18
19
|
_compression_level_from_env,
|
|
19
20
|
compact_lookup,
|
|
20
21
|
)
|
|
21
22
|
from fontTools.otlLib.error import OpenTypeLibError
|
|
23
|
+
from fontTools.misc.loggingTools import deprecateFunction
|
|
22
24
|
from functools import reduce
|
|
23
25
|
import logging
|
|
24
26
|
import copy
|
|
@@ -51,18 +53,21 @@ def buildCoverage(glyphs, glyphMap):
|
|
|
51
53
|
``font.getReverseGlyphMap()``.
|
|
52
54
|
|
|
53
55
|
Returns:
|
|
54
|
-
An ``otTables.Coverage`` object
|
|
55
|
-
supplied.
|
|
56
|
+
An ``otTables.Coverage`` object (empty if no glyphs supplied).
|
|
56
57
|
"""
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
# Per the OpenType spec: "For cases in which subtable offset fields are not
|
|
59
|
+
# documented as permitting NULL values, font compilers must include a subtable
|
|
60
|
+
# of the indicated format, even if it is a header stub without further data
|
|
61
|
+
# (for example, a coverage table with no glyph IDs)."
|
|
62
|
+
# https://github.com/fonttools/fonttools/issues/4003
|
|
60
63
|
self = ot.Coverage()
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
if glyphs:
|
|
65
|
+
try:
|
|
66
|
+
self.glyphs = sorted(set(glyphs), key=glyphMap.__getitem__)
|
|
67
|
+
except KeyError as e:
|
|
68
|
+
raise ValueError(f"Could not find glyph {e} in font") from e
|
|
69
|
+
else:
|
|
70
|
+
self.glyphs = []
|
|
66
71
|
return self
|
|
67
72
|
|
|
68
73
|
|
|
@@ -73,7 +78,7 @@ LOOKUP_FLAG_IGNORE_MARKS = 0x0008
|
|
|
73
78
|
LOOKUP_FLAG_USE_MARK_FILTERING_SET = 0x0010
|
|
74
79
|
|
|
75
80
|
|
|
76
|
-
def buildLookup(subtables, flags=0, markFilterSet=None):
|
|
81
|
+
def buildLookup(subtables, flags=0, markFilterSet=None, table=None, extension=False):
|
|
77
82
|
"""Turns a collection of rules into a lookup.
|
|
78
83
|
|
|
79
84
|
A Lookup (as defined in the `OpenType Spec <https://docs.microsoft.com/en-gb/typography/opentype/spec/chapter2#lookupTbl>`__)
|
|
@@ -98,6 +103,8 @@ def buildLookup(subtables, flags=0, markFilterSet=None):
|
|
|
98
103
|
lookup. If a mark filtering set is provided,
|
|
99
104
|
`LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
|
|
100
105
|
flags.
|
|
106
|
+
table (str): The name of the table this lookup belongs to, e.g. "GPOS" or "GSUB".
|
|
107
|
+
extension (bool): ``True`` if this is an extension lookup, ``False`` otherwise.
|
|
101
108
|
|
|
102
109
|
Returns:
|
|
103
110
|
An ``otTables.Lookup`` object or ``None`` if there are no subtables
|
|
@@ -113,8 +120,21 @@ def buildLookup(subtables, flags=0, markFilterSet=None):
|
|
|
113
120
|
), "all subtables must have the same LookupType; got %s" % repr(
|
|
114
121
|
[t.LookupType for t in subtables]
|
|
115
122
|
)
|
|
123
|
+
|
|
124
|
+
if extension:
|
|
125
|
+
assert table in ("GPOS", "GSUB")
|
|
126
|
+
lookupType = 7 if table == "GSUB" else 9
|
|
127
|
+
extSubTableClass = ot.lookupTypes[table][lookupType]
|
|
128
|
+
for i, st in enumerate(subtables):
|
|
129
|
+
subtables[i] = extSubTableClass()
|
|
130
|
+
subtables[i].Format = 1
|
|
131
|
+
subtables[i].ExtSubTable = st
|
|
132
|
+
subtables[i].ExtensionLookupType = st.LookupType
|
|
133
|
+
else:
|
|
134
|
+
lookupType = subtables[0].LookupType
|
|
135
|
+
|
|
116
136
|
self = ot.Lookup()
|
|
117
|
-
self.LookupType =
|
|
137
|
+
self.LookupType = lookupType
|
|
118
138
|
self.LookupFlag = flags
|
|
119
139
|
self.SubTable = subtables
|
|
120
140
|
self.SubTableCount = len(self.SubTable)
|
|
@@ -133,7 +153,7 @@ def buildLookup(subtables, flags=0, markFilterSet=None):
|
|
|
133
153
|
class LookupBuilder(object):
|
|
134
154
|
SUBTABLE_BREAK_ = "SUBTABLE_BREAK"
|
|
135
155
|
|
|
136
|
-
def __init__(self, font, location, table, lookup_type):
|
|
156
|
+
def __init__(self, font, location, table, lookup_type, extension=False):
|
|
137
157
|
self.font = font
|
|
138
158
|
self.glyphMap = font.getReverseGlyphMap()
|
|
139
159
|
self.location = location
|
|
@@ -141,6 +161,7 @@ class LookupBuilder(object):
|
|
|
141
161
|
self.lookupflag = 0
|
|
142
162
|
self.markFilterSet = None
|
|
143
163
|
self.lookup_index = None # assigned when making final tables
|
|
164
|
+
self.extension = extension
|
|
144
165
|
assert table in ("GPOS", "GSUB")
|
|
145
166
|
|
|
146
167
|
def equals(self, other):
|
|
@@ -149,8 +170,12 @@ class LookupBuilder(object):
|
|
|
149
170
|
and self.table == other.table
|
|
150
171
|
and self.lookupflag == other.lookupflag
|
|
151
172
|
and self.markFilterSet == other.markFilterSet
|
|
173
|
+
and self.extension == other.extension
|
|
152
174
|
)
|
|
153
175
|
|
|
176
|
+
def promote_lookup_type(self, is_named_lookup):
|
|
177
|
+
return [self]
|
|
178
|
+
|
|
154
179
|
def inferGlyphClasses(self):
|
|
155
180
|
"""Infers glyph glasses for the GDEF table, such as {"cedilla":3}."""
|
|
156
181
|
return {}
|
|
@@ -160,7 +185,13 @@ class LookupBuilder(object):
|
|
|
160
185
|
return {}
|
|
161
186
|
|
|
162
187
|
def buildLookup_(self, subtables):
|
|
163
|
-
return buildLookup(
|
|
188
|
+
return buildLookup(
|
|
189
|
+
subtables,
|
|
190
|
+
self.lookupflag,
|
|
191
|
+
self.markFilterSet,
|
|
192
|
+
self.table,
|
|
193
|
+
self.extension,
|
|
194
|
+
)
|
|
164
195
|
|
|
165
196
|
def buildMarkClasses_(self, marks):
|
|
166
197
|
"""{"cedilla": ("BOTTOM", ast.Anchor), ...} --> {"BOTTOM":0, "TOP":1}
|
|
@@ -228,6 +259,10 @@ class LookupBuilder(object):
|
|
|
228
259
|
)
|
|
229
260
|
)
|
|
230
261
|
|
|
262
|
+
def can_add_mapping(self, _mapping) -> bool:
|
|
263
|
+
# used by AnySubstBuilder, below
|
|
264
|
+
return True
|
|
265
|
+
|
|
231
266
|
|
|
232
267
|
class AlternateSubstBuilder(LookupBuilder):
|
|
233
268
|
"""Builds an Alternate Substitution (GSUB3) lookup.
|
|
@@ -858,6 +893,14 @@ class LigatureSubstBuilder(LookupBuilder):
|
|
|
858
893
|
)
|
|
859
894
|
return self.buildLookup_(subtables)
|
|
860
895
|
|
|
896
|
+
def getAlternateGlyphs(self):
|
|
897
|
+
# https://github.com/fonttools/fonttools/issues/3845
|
|
898
|
+
return {
|
|
899
|
+
components[0]: [ligature]
|
|
900
|
+
for components, ligature in self.ligatures.items()
|
|
901
|
+
if len(components) == 1
|
|
902
|
+
}
|
|
903
|
+
|
|
861
904
|
def add_subtable_break(self, location):
|
|
862
905
|
self.ligatures[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
|
|
863
906
|
|
|
@@ -896,6 +939,14 @@ class MultipleSubstBuilder(LookupBuilder):
|
|
|
896
939
|
subtables = self.build_subst_subtables(self.mapping, buildMultipleSubstSubtable)
|
|
897
940
|
return self.buildLookup_(subtables)
|
|
898
941
|
|
|
942
|
+
def getAlternateGlyphs(self):
|
|
943
|
+
# https://github.com/fonttools/fonttools/issues/3845
|
|
944
|
+
return {
|
|
945
|
+
glyph: replacements
|
|
946
|
+
for glyph, replacements in self.mapping.items()
|
|
947
|
+
if len(replacements) == 1
|
|
948
|
+
}
|
|
949
|
+
|
|
899
950
|
def add_subtable_break(self, location):
|
|
900
951
|
self.mapping[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
|
|
901
952
|
|
|
@@ -949,8 +1000,20 @@ class CursivePosBuilder(LookupBuilder):
|
|
|
949
1000
|
An ``otTables.Lookup`` object representing the cursive
|
|
950
1001
|
positioning lookup.
|
|
951
1002
|
"""
|
|
952
|
-
|
|
953
|
-
|
|
1003
|
+
attachments = [{}]
|
|
1004
|
+
for key in self.attachments:
|
|
1005
|
+
if key[0] == self.SUBTABLE_BREAK_:
|
|
1006
|
+
attachments.append({})
|
|
1007
|
+
else:
|
|
1008
|
+
attachments[-1][key] = self.attachments[key]
|
|
1009
|
+
subtables = [buildCursivePosSubtable(s, self.glyphMap) for s in attachments]
|
|
1010
|
+
return self.buildLookup_(subtables)
|
|
1011
|
+
|
|
1012
|
+
def add_subtable_break(self, location):
|
|
1013
|
+
self.attachments[(self.SUBTABLE_BREAK_, location)] = (
|
|
1014
|
+
self.SUBTABLE_BREAK_,
|
|
1015
|
+
self.SUBTABLE_BREAK_,
|
|
1016
|
+
)
|
|
954
1017
|
|
|
955
1018
|
|
|
956
1019
|
class MarkBasePosBuilder(LookupBuilder):
|
|
@@ -985,17 +1048,25 @@ class MarkBasePosBuilder(LookupBuilder):
|
|
|
985
1048
|
LookupBuilder.__init__(self, font, location, "GPOS", 4)
|
|
986
1049
|
self.marks = {} # glyphName -> (markClassName, anchor)
|
|
987
1050
|
self.bases = {} # glyphName -> {markClassName: anchor}
|
|
1051
|
+
self.subtables_ = []
|
|
1052
|
+
|
|
1053
|
+
def get_subtables_(self):
|
|
1054
|
+
subtables_ = self.subtables_
|
|
1055
|
+
if self.bases or self.marks:
|
|
1056
|
+
subtables_.append((self.marks, self.bases))
|
|
1057
|
+
return subtables_
|
|
988
1058
|
|
|
989
1059
|
def equals(self, other):
|
|
990
1060
|
return (
|
|
991
1061
|
LookupBuilder.equals(self, other)
|
|
992
|
-
and self.
|
|
993
|
-
and self.bases == other.bases
|
|
1062
|
+
and self.get_subtables_() == other.get_subtables_()
|
|
994
1063
|
)
|
|
995
1064
|
|
|
996
1065
|
def inferGlyphClasses(self):
|
|
997
|
-
result = {
|
|
998
|
-
|
|
1066
|
+
result = {}
|
|
1067
|
+
for marks, bases in self.get_subtables_():
|
|
1068
|
+
result.update({glyph: 1 for glyph in bases})
|
|
1069
|
+
result.update({glyph: 3 for glyph in marks})
|
|
999
1070
|
return result
|
|
1000
1071
|
|
|
1001
1072
|
def build(self):
|
|
@@ -1005,26 +1076,33 @@ class MarkBasePosBuilder(LookupBuilder):
|
|
|
1005
1076
|
An ``otTables.Lookup`` object representing the mark-to-base
|
|
1006
1077
|
positioning lookup.
|
|
1007
1078
|
"""
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
"Mark class %s not found for mark glyph %s" % (mc, mark)
|
|
1014
|
-
)
|
|
1015
|
-
marks[mark] = (markClasses[mc], anchor)
|
|
1016
|
-
bases = {}
|
|
1017
|
-
for glyph, anchors in self.bases.items():
|
|
1018
|
-
bases[glyph] = {}
|
|
1019
|
-
for mc, anchor in anchors.items():
|
|
1079
|
+
subtables = []
|
|
1080
|
+
for subtable in self.get_subtables_():
|
|
1081
|
+
markClasses = self.buildMarkClasses_(subtable[0])
|
|
1082
|
+
marks = {}
|
|
1083
|
+
for mark, (mc, anchor) in subtable[0].items():
|
|
1020
1084
|
if mc not in markClasses:
|
|
1021
1085
|
raise ValueError(
|
|
1022
|
-
"Mark class %s not found for
|
|
1086
|
+
"Mark class %s not found for mark glyph %s" % (mc, mark)
|
|
1023
1087
|
)
|
|
1024
|
-
|
|
1025
|
-
|
|
1088
|
+
marks[mark] = (markClasses[mc], anchor)
|
|
1089
|
+
bases = {}
|
|
1090
|
+
for glyph, anchors in subtable[1].items():
|
|
1091
|
+
bases[glyph] = {}
|
|
1092
|
+
for mc, anchor in anchors.items():
|
|
1093
|
+
if mc not in markClasses:
|
|
1094
|
+
raise ValueError(
|
|
1095
|
+
"Mark class %s not found for base glyph %s" % (mc, glyph)
|
|
1096
|
+
)
|
|
1097
|
+
bases[glyph][markClasses[mc]] = anchor
|
|
1098
|
+
subtables.append(buildMarkBasePosSubtable(marks, bases, self.glyphMap))
|
|
1026
1099
|
return self.buildLookup_(subtables)
|
|
1027
1100
|
|
|
1101
|
+
def add_subtable_break(self, location):
|
|
1102
|
+
self.subtables_.append((self.marks, self.bases))
|
|
1103
|
+
self.marks = {}
|
|
1104
|
+
self.bases = {}
|
|
1105
|
+
|
|
1028
1106
|
|
|
1029
1107
|
class MarkLigPosBuilder(LookupBuilder):
|
|
1030
1108
|
"""Builds a Mark-To-Ligature Positioning (GPOS5) lookup.
|
|
@@ -1061,17 +1139,25 @@ class MarkLigPosBuilder(LookupBuilder):
|
|
|
1061
1139
|
LookupBuilder.__init__(self, font, location, "GPOS", 5)
|
|
1062
1140
|
self.marks = {} # glyphName -> (markClassName, anchor)
|
|
1063
1141
|
self.ligatures = {} # glyphName -> [{markClassName: anchor}, ...]
|
|
1142
|
+
self.subtables_ = []
|
|
1143
|
+
|
|
1144
|
+
def get_subtables_(self):
|
|
1145
|
+
subtables_ = self.subtables_
|
|
1146
|
+
if self.ligatures or self.marks:
|
|
1147
|
+
subtables_.append((self.marks, self.ligatures))
|
|
1148
|
+
return subtables_
|
|
1064
1149
|
|
|
1065
1150
|
def equals(self, other):
|
|
1066
1151
|
return (
|
|
1067
1152
|
LookupBuilder.equals(self, other)
|
|
1068
|
-
and self.
|
|
1069
|
-
and self.ligatures == other.ligatures
|
|
1153
|
+
and self.get_subtables_() == other.get_subtables_()
|
|
1070
1154
|
)
|
|
1071
1155
|
|
|
1072
1156
|
def inferGlyphClasses(self):
|
|
1073
|
-
result = {
|
|
1074
|
-
|
|
1157
|
+
result = {}
|
|
1158
|
+
for marks, ligatures in self.get_subtables_():
|
|
1159
|
+
result.update({glyph: 2 for glyph in ligatures})
|
|
1160
|
+
result.update({glyph: 3 for glyph in marks})
|
|
1075
1161
|
return result
|
|
1076
1162
|
|
|
1077
1163
|
def build(self):
|
|
@@ -1081,18 +1167,26 @@ class MarkLigPosBuilder(LookupBuilder):
|
|
|
1081
1167
|
An ``otTables.Lookup`` object representing the mark-to-ligature
|
|
1082
1168
|
positioning lookup.
|
|
1083
1169
|
"""
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1170
|
+
subtables = []
|
|
1171
|
+
for subtable in self.get_subtables_():
|
|
1172
|
+
markClasses = self.buildMarkClasses_(subtable[0])
|
|
1173
|
+
marks = {
|
|
1174
|
+
mark: (markClasses[mc], anchor)
|
|
1175
|
+
for mark, (mc, anchor) in subtable[0].items()
|
|
1176
|
+
}
|
|
1177
|
+
ligs = {}
|
|
1178
|
+
for lig, components in subtable[1].items():
|
|
1179
|
+
ligs[lig] = []
|
|
1180
|
+
for c in components:
|
|
1181
|
+
ligs[lig].append({markClasses[mc]: a for mc, a in c.items()})
|
|
1182
|
+
subtables.append(buildMarkLigPosSubtable(marks, ligs, self.glyphMap))
|
|
1094
1183
|
return self.buildLookup_(subtables)
|
|
1095
1184
|
|
|
1185
|
+
def add_subtable_break(self, location):
|
|
1186
|
+
self.subtables_.append((self.marks, self.ligatures))
|
|
1187
|
+
self.marks = {}
|
|
1188
|
+
self.ligatures = {}
|
|
1189
|
+
|
|
1096
1190
|
|
|
1097
1191
|
class MarkMarkPosBuilder(LookupBuilder):
|
|
1098
1192
|
"""Builds a Mark-To-Mark Positioning (GPOS6) lookup.
|
|
@@ -1125,17 +1219,25 @@ class MarkMarkPosBuilder(LookupBuilder):
|
|
|
1125
1219
|
LookupBuilder.__init__(self, font, location, "GPOS", 6)
|
|
1126
1220
|
self.marks = {} # glyphName -> (markClassName, anchor)
|
|
1127
1221
|
self.baseMarks = {} # glyphName -> {markClassName: anchor}
|
|
1222
|
+
self.subtables_ = []
|
|
1223
|
+
|
|
1224
|
+
def get_subtables_(self):
|
|
1225
|
+
subtables_ = self.subtables_
|
|
1226
|
+
if self.baseMarks or self.marks:
|
|
1227
|
+
subtables_.append((self.marks, self.baseMarks))
|
|
1228
|
+
return subtables_
|
|
1128
1229
|
|
|
1129
1230
|
def equals(self, other):
|
|
1130
1231
|
return (
|
|
1131
1232
|
LookupBuilder.equals(self, other)
|
|
1132
|
-
and self.
|
|
1133
|
-
and self.baseMarks == other.baseMarks
|
|
1233
|
+
and self.get_subtables_() == other.get_subtables_()
|
|
1134
1234
|
)
|
|
1135
1235
|
|
|
1136
1236
|
def inferGlyphClasses(self):
|
|
1137
|
-
result = {
|
|
1138
|
-
|
|
1237
|
+
result = {}
|
|
1238
|
+
for marks, baseMarks in self.get_subtables_():
|
|
1239
|
+
result.update({glyph: 3 for glyph in baseMarks})
|
|
1240
|
+
result.update({glyph: 3 for glyph in marks})
|
|
1139
1241
|
return result
|
|
1140
1242
|
|
|
1141
1243
|
def build(self):
|
|
@@ -1145,25 +1247,34 @@ class MarkMarkPosBuilder(LookupBuilder):
|
|
|
1145
1247
|
An ``otTables.Lookup`` object representing the mark-to-mark
|
|
1146
1248
|
positioning lookup.
|
|
1147
1249
|
"""
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1250
|
+
subtables = []
|
|
1251
|
+
for subtable in self.get_subtables_():
|
|
1252
|
+
markClasses = self.buildMarkClasses_(subtable[0])
|
|
1253
|
+
markClassList = sorted(markClasses.keys(), key=markClasses.get)
|
|
1254
|
+
marks = {
|
|
1255
|
+
mark: (markClasses[mc], anchor)
|
|
1256
|
+
for mark, (mc, anchor) in subtable[0].items()
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
st = ot.MarkMarkPos()
|
|
1260
|
+
st.Format = 1
|
|
1261
|
+
st.ClassCount = len(markClasses)
|
|
1262
|
+
st.Mark1Coverage = buildCoverage(marks, self.glyphMap)
|
|
1263
|
+
st.Mark2Coverage = buildCoverage(subtable[1], self.glyphMap)
|
|
1264
|
+
st.Mark1Array = buildMarkArray(marks, self.glyphMap)
|
|
1265
|
+
st.Mark2Array = ot.Mark2Array()
|
|
1266
|
+
st.Mark2Array.Mark2Count = len(st.Mark2Coverage.glyphs)
|
|
1267
|
+
st.Mark2Array.Mark2Record = []
|
|
1268
|
+
for base in st.Mark2Coverage.glyphs:
|
|
1269
|
+
anchors = [subtable[1][base].get(mc) for mc in markClassList]
|
|
1270
|
+
st.Mark2Array.Mark2Record.append(buildMark2Record(anchors))
|
|
1271
|
+
subtables.append(st)
|
|
1272
|
+
return self.buildLookup_(subtables)
|
|
1153
1273
|
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
st.Mark2Coverage = buildCoverage(self.baseMarks, self.glyphMap)
|
|
1159
|
-
st.Mark1Array = buildMarkArray(marks, self.glyphMap)
|
|
1160
|
-
st.Mark2Array = ot.Mark2Array()
|
|
1161
|
-
st.Mark2Array.Mark2Count = len(st.Mark2Coverage.glyphs)
|
|
1162
|
-
st.Mark2Array.Mark2Record = []
|
|
1163
|
-
for base in st.Mark2Coverage.glyphs:
|
|
1164
|
-
anchors = [self.baseMarks[base].get(mc) for mc in markClassList]
|
|
1165
|
-
st.Mark2Array.Mark2Record.append(buildMark2Record(anchors))
|
|
1166
|
-
return self.buildLookup_([st])
|
|
1274
|
+
def add_subtable_break(self, location):
|
|
1275
|
+
self.subtables_.append((self.marks, self.baseMarks))
|
|
1276
|
+
self.marks = {}
|
|
1277
|
+
self.baseMarks = {}
|
|
1167
1278
|
|
|
1168
1279
|
|
|
1169
1280
|
class ReverseChainSingleSubstBuilder(LookupBuilder):
|
|
@@ -1223,6 +1334,177 @@ class ReverseChainSingleSubstBuilder(LookupBuilder):
|
|
|
1223
1334
|
pass
|
|
1224
1335
|
|
|
1225
1336
|
|
|
1337
|
+
class AnySubstBuilder(LookupBuilder):
|
|
1338
|
+
"""A temporary builder for Single, Multiple, or Ligature substitution lookup.
|
|
1339
|
+
|
|
1340
|
+
Users are expected to manually add substitutions to the ``mapping``
|
|
1341
|
+
attribute after the object has been initialized, e.g.::
|
|
1342
|
+
|
|
1343
|
+
# sub x by y;
|
|
1344
|
+
builder.mapping[("x",)] = ("y",)
|
|
1345
|
+
# sub a by b c;
|
|
1346
|
+
builder.mapping[("a",)] = ("b", "c")
|
|
1347
|
+
# sub f i by f_i;
|
|
1348
|
+
builder.mapping[("f", "i")] = ("f_i",)
|
|
1349
|
+
|
|
1350
|
+
Then call `promote_lookup_type()` to convert this builder into the
|
|
1351
|
+
appropriate type of substitution lookup builder. This would promote single
|
|
1352
|
+
substitutions to either multiple or ligature substitutions, depending on the
|
|
1353
|
+
rest of the rules in the mapping.
|
|
1354
|
+
|
|
1355
|
+
Attributes:
|
|
1356
|
+
font (``fontTools.TTLib.TTFont``): A font object.
|
|
1357
|
+
location: A string or tuple representing the location in the original
|
|
1358
|
+
source which produced this lookup.
|
|
1359
|
+
mapping: An ordered dictionary mapping a tuple of glyph names to another
|
|
1360
|
+
tuple of glyph names.
|
|
1361
|
+
lookupflag (int): The lookup's flag
|
|
1362
|
+
markFilterSet: Either ``None`` if no mark filtering set is used, or
|
|
1363
|
+
an integer representing the filtering set to be used for this
|
|
1364
|
+
lookup. If a mark filtering set is provided,
|
|
1365
|
+
`LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
|
|
1366
|
+
flags.
|
|
1367
|
+
"""
|
|
1368
|
+
|
|
1369
|
+
def __init__(self, font, location):
|
|
1370
|
+
LookupBuilder.__init__(self, font, location, "GSUB", 0)
|
|
1371
|
+
self.mapping = OrderedDict()
|
|
1372
|
+
|
|
1373
|
+
def _add_to_single_subst(self, builder, key, value):
|
|
1374
|
+
if key[0] != self.SUBTABLE_BREAK_:
|
|
1375
|
+
key = key[0]
|
|
1376
|
+
builder.mapping[key] = value[0]
|
|
1377
|
+
|
|
1378
|
+
def _add_to_multiple_subst(self, builder, key, value):
|
|
1379
|
+
if key[0] != self.SUBTABLE_BREAK_:
|
|
1380
|
+
key = key[0]
|
|
1381
|
+
builder.mapping[key] = value
|
|
1382
|
+
|
|
1383
|
+
def _add_to_ligature_subst(self, builder, key, value):
|
|
1384
|
+
builder.ligatures[key] = value[0]
|
|
1385
|
+
|
|
1386
|
+
def can_add_mapping(self, mapping) -> bool:
|
|
1387
|
+
if mapping is None:
|
|
1388
|
+
return True
|
|
1389
|
+
# single sub rules can be treated as (degenerate) liga-or-multi sub
|
|
1390
|
+
# rules, but multi and liga sub rules themselves have incompatible
|
|
1391
|
+
# representations. It is uncommon that these are in the same set of
|
|
1392
|
+
# rules, but it happens.
|
|
1393
|
+
is_multi = any(len(v) > 1 for v in mapping.values())
|
|
1394
|
+
is_liga = any(len(k) > 1 for k in mapping.keys())
|
|
1395
|
+
|
|
1396
|
+
has_existing_multi = False
|
|
1397
|
+
has_existing_liga = False
|
|
1398
|
+
|
|
1399
|
+
for k, v in self.mapping.items():
|
|
1400
|
+
if k[0] == self.SUBTABLE_BREAK_:
|
|
1401
|
+
continue
|
|
1402
|
+
if len(k) > 1:
|
|
1403
|
+
has_existing_liga = True
|
|
1404
|
+
if len(v) > 1:
|
|
1405
|
+
has_existing_multi = True
|
|
1406
|
+
|
|
1407
|
+
can_reuse = not (has_existing_multi and is_liga) and not (
|
|
1408
|
+
has_existing_liga and is_multi
|
|
1409
|
+
)
|
|
1410
|
+
return can_reuse
|
|
1411
|
+
|
|
1412
|
+
def promote_lookup_type(self, is_named_lookup):
|
|
1413
|
+
# https://github.com/fonttools/fonttools/issues/612
|
|
1414
|
+
# A multiple substitution may have a single destination, in which case
|
|
1415
|
+
# it will look just like a single substitution. So if there are both
|
|
1416
|
+
# multiple and single substitutions, upgrade all the single ones to
|
|
1417
|
+
# multiple substitutions. Similarly, a ligature substitution may have a
|
|
1418
|
+
# single source glyph, so if there are both ligature and single
|
|
1419
|
+
# substitutions, upgrade all the single ones to ligature substitutions.
|
|
1420
|
+
builder_classes = []
|
|
1421
|
+
for key, value in self.mapping.items():
|
|
1422
|
+
if key[0] == self.SUBTABLE_BREAK_:
|
|
1423
|
+
builder_classes.append(None)
|
|
1424
|
+
elif len(key) == 1 and len(value) == 1:
|
|
1425
|
+
builder_classes.append(SingleSubstBuilder)
|
|
1426
|
+
elif len(key) == 1 and len(value) != 1:
|
|
1427
|
+
builder_classes.append(MultipleSubstBuilder)
|
|
1428
|
+
elif len(key) > 1 and len(value) == 1:
|
|
1429
|
+
builder_classes.append(LigatureSubstBuilder)
|
|
1430
|
+
else:
|
|
1431
|
+
assert False, "Should not happen"
|
|
1432
|
+
|
|
1433
|
+
has_multiple = any(b is MultipleSubstBuilder for b in builder_classes)
|
|
1434
|
+
has_ligature = any(b is LigatureSubstBuilder for b in builder_classes)
|
|
1435
|
+
|
|
1436
|
+
# If we have mixed single and multiple substitutions,
|
|
1437
|
+
# upgrade all single substitutions to multiple substitutions.
|
|
1438
|
+
to_multiple = has_multiple and not has_ligature
|
|
1439
|
+
|
|
1440
|
+
# If we have mixed single and ligature substitutions,
|
|
1441
|
+
# upgrade all single substitutions to ligature substitutions.
|
|
1442
|
+
to_ligature = has_ligature and not has_multiple
|
|
1443
|
+
|
|
1444
|
+
# If we have only single substitutions, we can keep them as is.
|
|
1445
|
+
to_single = not has_ligature and not has_multiple
|
|
1446
|
+
|
|
1447
|
+
ret = []
|
|
1448
|
+
if to_single:
|
|
1449
|
+
builder = SingleSubstBuilder(self.font, self.location)
|
|
1450
|
+
for key, value in self.mapping.items():
|
|
1451
|
+
self._add_to_single_subst(builder, key, value)
|
|
1452
|
+
ret = [builder]
|
|
1453
|
+
elif to_multiple:
|
|
1454
|
+
builder = MultipleSubstBuilder(self.font, self.location)
|
|
1455
|
+
for key, value in self.mapping.items():
|
|
1456
|
+
self._add_to_multiple_subst(builder, key, value)
|
|
1457
|
+
ret = [builder]
|
|
1458
|
+
elif to_ligature:
|
|
1459
|
+
builder = LigatureSubstBuilder(self.font, self.location)
|
|
1460
|
+
for key, value in self.mapping.items():
|
|
1461
|
+
self._add_to_ligature_subst(builder, key, value)
|
|
1462
|
+
ret = [builder]
|
|
1463
|
+
elif is_named_lookup:
|
|
1464
|
+
# This is a named lookup with mixed substitutions that can’t be promoted,
|
|
1465
|
+
# since we can’t split it into multiple lookups, we return None here to
|
|
1466
|
+
# signal that to the caller
|
|
1467
|
+
return None
|
|
1468
|
+
else:
|
|
1469
|
+
curr_builder = None
|
|
1470
|
+
for builder_class, (key, value) in zip(
|
|
1471
|
+
builder_classes, self.mapping.items()
|
|
1472
|
+
):
|
|
1473
|
+
if curr_builder is None or type(curr_builder) is not builder_class:
|
|
1474
|
+
curr_builder = builder_class(self.font, self.location)
|
|
1475
|
+
ret.append(curr_builder)
|
|
1476
|
+
if builder_class is SingleSubstBuilder:
|
|
1477
|
+
self._add_to_single_subst(curr_builder, key, value)
|
|
1478
|
+
elif builder_class is MultipleSubstBuilder:
|
|
1479
|
+
self._add_to_multiple_subst(curr_builder, key, value)
|
|
1480
|
+
elif builder_class is LigatureSubstBuilder:
|
|
1481
|
+
self._add_to_ligature_subst(curr_builder, key, value)
|
|
1482
|
+
else:
|
|
1483
|
+
assert False, "Should not happen"
|
|
1484
|
+
|
|
1485
|
+
for builder in ret:
|
|
1486
|
+
builder.extension = self.extension
|
|
1487
|
+
builder.lookupflag = self.lookupflag
|
|
1488
|
+
builder.markFilterSet = self.markFilterSet
|
|
1489
|
+
return ret
|
|
1490
|
+
|
|
1491
|
+
def equals(self, other):
|
|
1492
|
+
return LookupBuilder.equals(self, other) and self.mapping == other.mapping
|
|
1493
|
+
|
|
1494
|
+
def build(self):
|
|
1495
|
+
assert False
|
|
1496
|
+
|
|
1497
|
+
def getAlternateGlyphs(self):
|
|
1498
|
+
return {
|
|
1499
|
+
key[0]: value
|
|
1500
|
+
for key, value in self.mapping.items()
|
|
1501
|
+
if len(key) == 1 and len(value) == 1
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
def add_subtable_break(self, location):
|
|
1505
|
+
self.mapping[(self.SUBTABLE_BREAK_, location)] = self.SUBTABLE_BREAK_
|
|
1506
|
+
|
|
1507
|
+
|
|
1226
1508
|
class SingleSubstBuilder(LookupBuilder):
|
|
1227
1509
|
"""Builds a Single Substitution (GSUB1) lookup.
|
|
1228
1510
|
|
|
@@ -1484,6 +1766,8 @@ class SinglePosBuilder(LookupBuilder):
|
|
|
1484
1766
|
otValueRection: A ``otTables.ValueRecord`` used to position the
|
|
1485
1767
|
glyph.
|
|
1486
1768
|
"""
|
|
1769
|
+
if otValueRecord is None:
|
|
1770
|
+
otValueRecord = ValueRecord()
|
|
1487
1771
|
if not self.can_add(glyph, otValueRecord):
|
|
1488
1772
|
otherLoc = self.locations[glyph]
|
|
1489
1773
|
raise OpenTypeLibError(
|
|
@@ -1900,53 +2184,15 @@ def buildMarkArray(marks, glyphMap):
|
|
|
1900
2184
|
return self
|
|
1901
2185
|
|
|
1902
2186
|
|
|
2187
|
+
@deprecateFunction(
|
|
2188
|
+
"use buildMarkBasePosSubtable() instead", category=DeprecationWarning
|
|
2189
|
+
)
|
|
1903
2190
|
def buildMarkBasePos(marks, bases, glyphMap):
|
|
1904
2191
|
"""Build a list of MarkBasePos (GPOS4) subtables.
|
|
1905
2192
|
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
containing all marks and bases, although at a later date it may return the
|
|
1909
|
-
optimal list of subtables subsetting the marks and bases into groups which
|
|
1910
|
-
save space. See :func:`buildMarkBasePosSubtable` below.
|
|
1911
|
-
|
|
1912
|
-
Note that if you are implementing a layout compiler, you may find it more
|
|
1913
|
-
flexible to use
|
|
1914
|
-
:py:class:`fontTools.otlLib.lookupBuilders.MarkBasePosBuilder` instead.
|
|
1915
|
-
|
|
1916
|
-
Example::
|
|
1917
|
-
|
|
1918
|
-
# a1, a2, a3, a4, a5 = buildAnchor(500, 100), ...
|
|
1919
|
-
|
|
1920
|
-
marks = {"acute": (0, a1), "grave": (0, a1), "cedilla": (1, a2)}
|
|
1921
|
-
bases = {"a": {0: a3, 1: a5}, "b": {0: a4, 1: a5}}
|
|
1922
|
-
markbaseposes = buildMarkBasePos(marks, bases, font.getReverseGlyphMap())
|
|
1923
|
-
|
|
1924
|
-
Args:
|
|
1925
|
-
marks (dict): A dictionary mapping anchors to glyphs; the keys being
|
|
1926
|
-
glyph names, and the values being a tuple of mark class number and
|
|
1927
|
-
an ``otTables.Anchor`` object representing the mark's attachment
|
|
1928
|
-
point. (See :func:`buildMarkArray`.)
|
|
1929
|
-
bases (dict): A dictionary mapping anchors to glyphs; the keys being
|
|
1930
|
-
glyph names, and the values being dictionaries mapping mark class ID
|
|
1931
|
-
to the appropriate ``otTables.Anchor`` object used for attaching marks
|
|
1932
|
-
of that class. (See :func:`buildBaseArray`.)
|
|
1933
|
-
glyphMap: a glyph name to ID map, typically returned from
|
|
1934
|
-
``font.getReverseGlyphMap()``.
|
|
1935
|
-
|
|
1936
|
-
Returns:
|
|
1937
|
-
A list of ``otTables.MarkBasePos`` objects.
|
|
2193
|
+
.. deprecated:: 4.58.0
|
|
2194
|
+
Use :func:`buildMarkBasePosSubtable` instead.
|
|
1938
2195
|
"""
|
|
1939
|
-
# TODO: Consider emitting multiple subtables to save space.
|
|
1940
|
-
# Partition the marks and bases into disjoint subsets, so that
|
|
1941
|
-
# MarkBasePos rules would only access glyphs from a single
|
|
1942
|
-
# subset. This would likely lead to smaller mark/base
|
|
1943
|
-
# matrices, so we might be able to omit many of the empty
|
|
1944
|
-
# anchor tables that we currently produce. Of course, this
|
|
1945
|
-
# would only work if the MarkBasePos rules of real-world fonts
|
|
1946
|
-
# allow partitioning into multiple subsets. We should find out
|
|
1947
|
-
# whether this is the case; if so, implement the optimization.
|
|
1948
|
-
# On the other hand, a very large number of subtables could
|
|
1949
|
-
# slow down layout engines; so this would need profiling.
|
|
1950
2196
|
return [buildMarkBasePosSubtable(marks, bases, glyphMap)]
|
|
1951
2197
|
|
|
1952
2198
|
|
|
@@ -1954,7 +2200,15 @@ def buildMarkBasePosSubtable(marks, bases, glyphMap):
|
|
|
1954
2200
|
"""Build a single MarkBasePos (GPOS4) subtable.
|
|
1955
2201
|
|
|
1956
2202
|
This builds a mark-to-base lookup subtable containing all of the referenced
|
|
1957
|
-
marks and bases.
|
|
2203
|
+
marks and bases.
|
|
2204
|
+
|
|
2205
|
+
Example::
|
|
2206
|
+
|
|
2207
|
+
# a1, a2, a3, a4, a5 = buildAnchor(500, 100), ...
|
|
2208
|
+
|
|
2209
|
+
marks = {"acute": (0, a1), "grave": (0, a1), "cedilla": (1, a2)}
|
|
2210
|
+
bases = {"a": {0: a3, 1: a5}, "b": {0: a4, 1: a5}}
|
|
2211
|
+
markbaseposes = [buildMarkBasePosSubtable(marks, bases, font.getReverseGlyphMap())]
|
|
1958
2212
|
|
|
1959
2213
|
Args:
|
|
1960
2214
|
marks (dict): A dictionary mapping anchors to glyphs; the keys being
|
|
@@ -1981,14 +2235,21 @@ def buildMarkBasePosSubtable(marks, bases, glyphMap):
|
|
|
1981
2235
|
return self
|
|
1982
2236
|
|
|
1983
2237
|
|
|
2238
|
+
@deprecateFunction("use buildMarkLigPosSubtable() instead", category=DeprecationWarning)
|
|
1984
2239
|
def buildMarkLigPos(marks, ligs, glyphMap):
|
|
1985
2240
|
"""Build a list of MarkLigPos (GPOS5) subtables.
|
|
1986
2241
|
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
2242
|
+
.. deprecated:: 4.58.0
|
|
2243
|
+
Use :func:`buildMarkLigPosSubtable` instead.
|
|
2244
|
+
"""
|
|
2245
|
+
return [buildMarkLigPosSubtable(marks, ligs, glyphMap)]
|
|
2246
|
+
|
|
2247
|
+
|
|
2248
|
+
def buildMarkLigPosSubtable(marks, ligs, glyphMap):
|
|
2249
|
+
"""Build a single MarkLigPos (GPOS5) subtable.
|
|
2250
|
+
|
|
2251
|
+
This builds a mark-to-base lookup subtable containing all of the referenced
|
|
2252
|
+
marks and bases.
|
|
1992
2253
|
|
|
1993
2254
|
Note that if you are implementing a layout compiler, you may find it more
|
|
1994
2255
|
flexible to use
|
|
@@ -2009,37 +2270,9 @@ def buildMarkLigPos(marks, ligs, glyphMap):
|
|
|
2009
2270
|
],
|
|
2010
2271
|
# "c_t": [{...}, {...}]
|
|
2011
2272
|
}
|
|
2012
|
-
|
|
2273
|
+
markligpose = buildMarkLigPosSubtable(marks, ligs,
|
|
2013
2274
|
font.getReverseGlyphMap())
|
|
2014
2275
|
|
|
2015
|
-
Args:
|
|
2016
|
-
marks (dict): A dictionary mapping anchors to glyphs; the keys being
|
|
2017
|
-
glyph names, and the values being a tuple of mark class number and
|
|
2018
|
-
an ``otTables.Anchor`` object representing the mark's attachment
|
|
2019
|
-
point. (See :func:`buildMarkArray`.)
|
|
2020
|
-
ligs (dict): A mapping of ligature names to an array of dictionaries:
|
|
2021
|
-
for each component glyph in the ligature, an dictionary mapping
|
|
2022
|
-
mark class IDs to anchors. (See :func:`buildLigatureArray`.)
|
|
2023
|
-
glyphMap: a glyph name to ID map, typically returned from
|
|
2024
|
-
``font.getReverseGlyphMap()``.
|
|
2025
|
-
|
|
2026
|
-
Returns:
|
|
2027
|
-
A list of ``otTables.MarkLigPos`` objects.
|
|
2028
|
-
|
|
2029
|
-
"""
|
|
2030
|
-
# TODO: Consider splitting into multiple subtables to save space,
|
|
2031
|
-
# as with MarkBasePos, this would be a trade-off that would need
|
|
2032
|
-
# profiling. And, depending on how typical fonts are structured,
|
|
2033
|
-
# it might not be worth doing at all.
|
|
2034
|
-
return [buildMarkLigPosSubtable(marks, ligs, glyphMap)]
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
def buildMarkLigPosSubtable(marks, ligs, glyphMap):
|
|
2038
|
-
"""Build a single MarkLigPos (GPOS5) subtable.
|
|
2039
|
-
|
|
2040
|
-
This builds a mark-to-base lookup subtable containing all of the referenced
|
|
2041
|
-
marks and bases. See :func:`buildMarkLigPos`.
|
|
2042
|
-
|
|
2043
2276
|
Args:
|
|
2044
2277
|
marks (dict): A dictionary mapping anchors to glyphs; the keys being
|
|
2045
2278
|
glyph names, and the values being a tuple of mark class number and
|
|
@@ -2706,10 +2939,18 @@ class ClassDefBuilder(object):
|
|
|
2706
2939
|
AXIS_VALUE_NEGATIVE_INFINITY = fixedToFloat(-0x80000000, 16)
|
|
2707
2940
|
AXIS_VALUE_POSITIVE_INFINITY = fixedToFloat(0x7FFFFFFF, 16)
|
|
2708
2941
|
|
|
2942
|
+
STATName = Union[int, str, Dict[str, str]]
|
|
2943
|
+
"""A raw name ID, English name, or multilingual name."""
|
|
2944
|
+
|
|
2709
2945
|
|
|
2710
2946
|
def buildStatTable(
|
|
2711
|
-
ttFont
|
|
2712
|
-
|
|
2947
|
+
ttFont: TTFont,
|
|
2948
|
+
axes,
|
|
2949
|
+
locations=None,
|
|
2950
|
+
elidedFallbackName: Union[STATName, STATNameStatement] = 2,
|
|
2951
|
+
windowsNames: bool = True,
|
|
2952
|
+
macNames: bool = True,
|
|
2953
|
+
) -> None:
|
|
2713
2954
|
"""Add a 'STAT' table to 'ttFont'.
|
|
2714
2955
|
|
|
2715
2956
|
'axes' is a list of dictionaries describing axes and their
|
|
@@ -2900,7 +3141,13 @@ def _buildAxisValuesFormat4(locations, axes, ttFont, windowsNames=True, macNames
|
|
|
2900
3141
|
return axisValues
|
|
2901
3142
|
|
|
2902
3143
|
|
|
2903
|
-
def _addName(
|
|
3144
|
+
def _addName(
|
|
3145
|
+
ttFont: TTFont,
|
|
3146
|
+
value: Union[STATName, STATNameStatement],
|
|
3147
|
+
minNameID: int = 0,
|
|
3148
|
+
windows: bool = True,
|
|
3149
|
+
mac: bool = True,
|
|
3150
|
+
) -> int:
|
|
2904
3151
|
nameTable = ttFont["name"]
|
|
2905
3152
|
if isinstance(value, int):
|
|
2906
3153
|
# Already a nameID
|