fonttools 4.56.0__cp310-cp310-win32.whl → 4.58.0__cp310-cp310-win32.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.

Files changed (71) hide show
  1. fontTools/__init__.py +1 -1
  2. fontTools/cffLib/__init__.py +61 -26
  3. fontTools/config/__init__.py +15 -0
  4. fontTools/cu2qu/cu2qu.c +15345 -14829
  5. fontTools/cu2qu/cu2qu.cp310-win32.pyd +0 -0
  6. fontTools/designspaceLib/statNames.py +14 -7
  7. fontTools/feaLib/ast.py +92 -13
  8. fontTools/feaLib/builder.py +52 -13
  9. fontTools/feaLib/lexer.c +17143 -17986
  10. fontTools/feaLib/lexer.cp310-win32.pyd +0 -0
  11. fontTools/feaLib/parser.py +59 -39
  12. fontTools/fontBuilder.py +6 -0
  13. fontTools/misc/bezierTools.c +39936 -41831
  14. fontTools/misc/bezierTools.cp310-win32.pyd +0 -0
  15. fontTools/misc/etree.py +4 -27
  16. fontTools/misc/testTools.py +2 -1
  17. fontTools/mtiLib/__init__.py +0 -2
  18. fontTools/otlLib/builder.py +195 -145
  19. fontTools/otlLib/optimize/gpos.py +49 -63
  20. fontTools/pens/momentsPen.c +13266 -13448
  21. fontTools/pens/momentsPen.cp310-win32.pyd +0 -0
  22. fontTools/pens/pointPen.py +21 -12
  23. fontTools/qu2cu/qu2cu.c +16538 -16269
  24. fontTools/qu2cu/qu2cu.cp310-win32.pyd +0 -0
  25. fontTools/subset/__init__.py +11 -0
  26. fontTools/ttLib/__init__.py +4 -0
  27. fontTools/ttLib/__main__.py +47 -8
  28. fontTools/ttLib/tables/D__e_b_g.py +20 -2
  29. fontTools/ttLib/tables/G_V_A_R_.py +5 -0
  30. fontTools/ttLib/tables/T_S_I__0.py +14 -3
  31. fontTools/ttLib/tables/T_S_I__5.py +16 -5
  32. fontTools/ttLib/tables/__init__.py +1 -0
  33. fontTools/ttLib/tables/_c_m_a_p.py +19 -6
  34. fontTools/ttLib/tables/_c_v_t.py +2 -0
  35. fontTools/ttLib/tables/_f_p_g_m.py +3 -1
  36. fontTools/ttLib/tables/_g_l_y_f.py +11 -10
  37. fontTools/ttLib/tables/_g_v_a_r.py +62 -17
  38. fontTools/ttLib/tables/_p_o_s_t.py +5 -2
  39. fontTools/ttLib/tables/otBase.py +1 -0
  40. fontTools/ttLib/tables/otConverters.py +5 -2
  41. fontTools/ttLib/tables/otTables.py +5 -1
  42. fontTools/ttLib/ttFont.py +3 -5
  43. fontTools/ttLib/ttGlyphSet.py +0 -10
  44. fontTools/ttx.py +13 -1
  45. fontTools/ufoLib/__init__.py +2 -2
  46. fontTools/ufoLib/converters.py +89 -25
  47. fontTools/ufoLib/errors.py +8 -0
  48. fontTools/ufoLib/etree.py +1 -1
  49. fontTools/ufoLib/filenames.py +155 -100
  50. fontTools/ufoLib/glifLib.py +9 -2
  51. fontTools/ufoLib/kerning.py +66 -36
  52. fontTools/ufoLib/utils.py +5 -2
  53. fontTools/unicodedata/Mirrored.py +446 -0
  54. fontTools/unicodedata/__init__.py +6 -2
  55. fontTools/varLib/__init__.py +94 -89
  56. fontTools/varLib/hvar.py +113 -0
  57. fontTools/varLib/iup.c +19630 -19154
  58. fontTools/varLib/iup.cp310-win32.pyd +0 -0
  59. fontTools/varLib/varStore.py +1 -1
  60. fontTools/voltLib/__main__.py +206 -0
  61. fontTools/voltLib/ast.py +4 -0
  62. fontTools/voltLib/parser.py +16 -8
  63. fontTools/voltLib/voltToFea.py +347 -166
  64. {fonttools-4.56.0.dist-info → fonttools-4.58.0.dist-info}/METADATA +60 -12
  65. {fonttools-4.56.0.dist-info → fonttools-4.58.0.dist-info}/RECORD +71 -66
  66. {fonttools-4.56.0.dist-info → fonttools-4.58.0.dist-info}/WHEEL +1 -1
  67. fonttools-4.58.0.dist-info/licenses/LICENSE.external +359 -0
  68. {fonttools-4.56.0.data → fonttools-4.58.0.data}/data/share/man/man1/ttx.1 +0 -0
  69. {fonttools-4.56.0.dist-info → fonttools-4.58.0.dist-info}/entry_points.txt +0 -0
  70. {fonttools-4.56.0.dist-info → fonttools-4.58.0.dist-info/licenses}/LICENSE +0 -0
  71. {fonttools-4.56.0.dist-info → fonttools-4.58.0.dist-info}/top_level.txt +0 -0
Binary file
fontTools/misc/etree.py CHANGED
@@ -56,21 +56,7 @@ except ImportError:
56
56
  from xml.etree.ElementTree import *
57
57
  _have_lxml = False
58
58
 
59
- import sys
60
-
61
- # dict is always ordered in python >= 3.6 and on pypy
62
- PY36 = sys.version_info >= (3, 6)
63
- try:
64
- import __pypy__
65
- except ImportError:
66
- __pypy__ = None
67
- _dict_is_ordered = bool(PY36 or __pypy__)
68
- del PY36, __pypy__
69
-
70
- if _dict_is_ordered:
71
- _Attrib = dict
72
- else:
73
- from collections import OrderedDict as _Attrib
59
+ _Attrib = dict
74
60
 
75
61
  if isinstance(Element, type):
76
62
  _Element = Element
@@ -221,18 +207,9 @@ except ImportError:
221
207
  # characters, the surrogate blocks, FFFE, and FFFF:
222
208
  # Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
223
209
  # Here we reversed the pattern to match only the invalid characters.
224
- # For the 'narrow' python builds supporting only UCS-2, which represent
225
- # characters beyond BMP as UTF-16 surrogate pairs, we need to pass through
226
- # the surrogate block. I haven't found a more elegant solution...
227
- UCS2 = sys.maxunicode < 0x10FFFF
228
- if UCS2:
229
- _invalid_xml_string = re.compile(
230
- "[\u0000-\u0008\u000B-\u000C\u000E-\u001F\uFFFE-\uFFFF]"
231
- )
232
- else:
233
- _invalid_xml_string = re.compile(
234
- "[\u0000-\u0008\u000B-\u000C\u000E-\u001F\uD800-\uDFFF\uFFFE-\uFFFF]"
235
- )
210
+ _invalid_xml_string = re.compile(
211
+ "[\u0000-\u0008\u000B-\u000C\u000E-\u001F\uD800-\uDFFF\uFFFE-\uFFFF]"
212
+ )
236
213
 
237
214
  def _tounicode(s):
238
215
  """Test if a string is valid user input and decode it to unicode string
@@ -46,7 +46,8 @@ def parseXmlInto(font, parseInto, xmlSnippet):
46
46
  parsed_xml = [e for e in parseXML(xmlSnippet.strip()) if not isinstance(e, str)]
47
47
  for name, attrs, content in parsed_xml:
48
48
  parseInto.fromXML(name, attrs, content, font)
49
- parseInto.populateDefaults()
49
+ if hasattr(parseInto, "populateDefaults"):
50
+ parseInto.populateDefaults()
50
51
  return parseInto
51
52
 
52
53
 
@@ -1,5 +1,3 @@
1
- #!/usr/bin/python
2
-
3
1
  # FontDame-to-FontTools for OpenType Layout tables
4
2
  #
5
3
  # Source language spec is available at:
@@ -1,6 +1,8 @@
1
+ from __future__ import annotations
2
+
1
3
  from collections import namedtuple, OrderedDict
2
4
  import itertools
3
- import os
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.tables import otBase
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
@@ -73,7 +75,7 @@ LOOKUP_FLAG_IGNORE_MARKS = 0x0008
73
75
  LOOKUP_FLAG_USE_MARK_FILTERING_SET = 0x0010
74
76
 
75
77
 
76
- def buildLookup(subtables, flags=0, markFilterSet=None):
78
+ def buildLookup(subtables, flags=0, markFilterSet=None, table=None, extension=False):
77
79
  """Turns a collection of rules into a lookup.
78
80
 
79
81
  A Lookup (as defined in the `OpenType Spec <https://docs.microsoft.com/en-gb/typography/opentype/spec/chapter2#lookupTbl>`__)
@@ -98,6 +100,8 @@ def buildLookup(subtables, flags=0, markFilterSet=None):
98
100
  lookup. If a mark filtering set is provided,
99
101
  `LOOKUP_FLAG_USE_MARK_FILTERING_SET` will be set on the lookup's
100
102
  flags.
103
+ table (str): The name of the table this lookup belongs to, e.g. "GPOS" or "GSUB".
104
+ extension (bool): ``True`` if this is an extension lookup, ``False`` otherwise.
101
105
 
102
106
  Returns:
103
107
  An ``otTables.Lookup`` object or ``None`` if there are no subtables
@@ -113,8 +117,21 @@ def buildLookup(subtables, flags=0, markFilterSet=None):
113
117
  ), "all subtables must have the same LookupType; got %s" % repr(
114
118
  [t.LookupType for t in subtables]
115
119
  )
120
+
121
+ if extension:
122
+ assert table in ("GPOS", "GSUB")
123
+ lookupType = 7 if table == "GSUB" else 9
124
+ extSubTableClass = ot.lookupTypes[table][lookupType]
125
+ for i, st in enumerate(subtables):
126
+ subtables[i] = extSubTableClass()
127
+ subtables[i].Format = 1
128
+ subtables[i].ExtSubTable = st
129
+ subtables[i].ExtensionLookupType = st.LookupType
130
+ else:
131
+ lookupType = subtables[0].LookupType
132
+
116
133
  self = ot.Lookup()
117
- self.LookupType = subtables[0].LookupType
134
+ self.LookupType = lookupType
118
135
  self.LookupFlag = flags
119
136
  self.SubTable = subtables
120
137
  self.SubTableCount = len(self.SubTable)
@@ -133,7 +150,7 @@ def buildLookup(subtables, flags=0, markFilterSet=None):
133
150
  class LookupBuilder(object):
134
151
  SUBTABLE_BREAK_ = "SUBTABLE_BREAK"
135
152
 
136
- def __init__(self, font, location, table, lookup_type):
153
+ def __init__(self, font, location, table, lookup_type, extension=False):
137
154
  self.font = font
138
155
  self.glyphMap = font.getReverseGlyphMap()
139
156
  self.location = location
@@ -141,6 +158,7 @@ class LookupBuilder(object):
141
158
  self.lookupflag = 0
142
159
  self.markFilterSet = None
143
160
  self.lookup_index = None # assigned when making final tables
161
+ self.extension = extension
144
162
  assert table in ("GPOS", "GSUB")
145
163
 
146
164
  def equals(self, other):
@@ -149,6 +167,7 @@ class LookupBuilder(object):
149
167
  and self.table == other.table
150
168
  and self.lookupflag == other.lookupflag
151
169
  and self.markFilterSet == other.markFilterSet
170
+ and self.extension == other.extension
152
171
  )
153
172
 
154
173
  def inferGlyphClasses(self):
@@ -160,7 +179,13 @@ class LookupBuilder(object):
160
179
  return {}
161
180
 
162
181
  def buildLookup_(self, subtables):
163
- return buildLookup(subtables, self.lookupflag, self.markFilterSet)
182
+ return buildLookup(
183
+ subtables,
184
+ self.lookupflag,
185
+ self.markFilterSet,
186
+ self.table,
187
+ self.extension,
188
+ )
164
189
 
165
190
  def buildMarkClasses_(self, marks):
166
191
  """{"cedilla": ("BOTTOM", ast.Anchor), ...} --> {"BOTTOM":0, "TOP":1}
@@ -949,8 +974,20 @@ class CursivePosBuilder(LookupBuilder):
949
974
  An ``otTables.Lookup`` object representing the cursive
950
975
  positioning lookup.
951
976
  """
952
- st = buildCursivePosSubtable(self.attachments, self.glyphMap)
953
- return self.buildLookup_([st])
977
+ attachments = [{}]
978
+ for key in self.attachments:
979
+ if key[0] == self.SUBTABLE_BREAK_:
980
+ attachments.append({})
981
+ else:
982
+ attachments[-1][key] = self.attachments[key]
983
+ subtables = [buildCursivePosSubtable(s, self.glyphMap) for s in attachments]
984
+ return self.buildLookup_(subtables)
985
+
986
+ def add_subtable_break(self, location):
987
+ self.attachments[(self.SUBTABLE_BREAK_, location)] = (
988
+ self.SUBTABLE_BREAK_,
989
+ self.SUBTABLE_BREAK_,
990
+ )
954
991
 
955
992
 
956
993
  class MarkBasePosBuilder(LookupBuilder):
@@ -985,17 +1022,25 @@ class MarkBasePosBuilder(LookupBuilder):
985
1022
  LookupBuilder.__init__(self, font, location, "GPOS", 4)
986
1023
  self.marks = {} # glyphName -> (markClassName, anchor)
987
1024
  self.bases = {} # glyphName -> {markClassName: anchor}
1025
+ self.subtables_ = []
1026
+
1027
+ def get_subtables_(self):
1028
+ subtables_ = self.subtables_
1029
+ if self.bases or self.marks:
1030
+ subtables_.append((self.marks, self.bases))
1031
+ return subtables_
988
1032
 
989
1033
  def equals(self, other):
990
1034
  return (
991
1035
  LookupBuilder.equals(self, other)
992
- and self.marks == other.marks
993
- and self.bases == other.bases
1036
+ and self.get_subtables_() == other.get_subtables_()
994
1037
  )
995
1038
 
996
1039
  def inferGlyphClasses(self):
997
- result = {glyph: 1 for glyph in self.bases}
998
- result.update({glyph: 3 for glyph in self.marks})
1040
+ result = {}
1041
+ for marks, bases in self.get_subtables_():
1042
+ result.update({glyph: 1 for glyph in bases})
1043
+ result.update({glyph: 3 for glyph in marks})
999
1044
  return result
1000
1045
 
1001
1046
  def build(self):
@@ -1005,26 +1050,33 @@ class MarkBasePosBuilder(LookupBuilder):
1005
1050
  An ``otTables.Lookup`` object representing the mark-to-base
1006
1051
  positioning lookup.
1007
1052
  """
1008
- markClasses = self.buildMarkClasses_(self.marks)
1009
- marks = {}
1010
- for mark, (mc, anchor) in self.marks.items():
1011
- if mc not in markClasses:
1012
- raise ValueError(
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():
1053
+ subtables = []
1054
+ for subtable in self.get_subtables_():
1055
+ markClasses = self.buildMarkClasses_(subtable[0])
1056
+ marks = {}
1057
+ for mark, (mc, anchor) in subtable[0].items():
1020
1058
  if mc not in markClasses:
1021
1059
  raise ValueError(
1022
- "Mark class %s not found for base glyph %s" % (mc, glyph)
1060
+ "Mark class %s not found for mark glyph %s" % (mc, mark)
1023
1061
  )
1024
- bases[glyph][markClasses[mc]] = anchor
1025
- subtables = buildMarkBasePos(marks, bases, self.glyphMap)
1062
+ marks[mark] = (markClasses[mc], anchor)
1063
+ bases = {}
1064
+ for glyph, anchors in subtable[1].items():
1065
+ bases[glyph] = {}
1066
+ for mc, anchor in anchors.items():
1067
+ if mc not in markClasses:
1068
+ raise ValueError(
1069
+ "Mark class %s not found for base glyph %s" % (mc, glyph)
1070
+ )
1071
+ bases[glyph][markClasses[mc]] = anchor
1072
+ subtables.append(buildMarkBasePosSubtable(marks, bases, self.glyphMap))
1026
1073
  return self.buildLookup_(subtables)
1027
1074
 
1075
+ def add_subtable_break(self, location):
1076
+ self.subtables_.append((self.marks, self.bases))
1077
+ self.marks = {}
1078
+ self.bases = {}
1079
+
1028
1080
 
1029
1081
  class MarkLigPosBuilder(LookupBuilder):
1030
1082
  """Builds a Mark-To-Ligature Positioning (GPOS5) lookup.
@@ -1061,17 +1113,25 @@ class MarkLigPosBuilder(LookupBuilder):
1061
1113
  LookupBuilder.__init__(self, font, location, "GPOS", 5)
1062
1114
  self.marks = {} # glyphName -> (markClassName, anchor)
1063
1115
  self.ligatures = {} # glyphName -> [{markClassName: anchor}, ...]
1116
+ self.subtables_ = []
1117
+
1118
+ def get_subtables_(self):
1119
+ subtables_ = self.subtables_
1120
+ if self.ligatures or self.marks:
1121
+ subtables_.append((self.marks, self.ligatures))
1122
+ return subtables_
1064
1123
 
1065
1124
  def equals(self, other):
1066
1125
  return (
1067
1126
  LookupBuilder.equals(self, other)
1068
- and self.marks == other.marks
1069
- and self.ligatures == other.ligatures
1127
+ and self.get_subtables_() == other.get_subtables_()
1070
1128
  )
1071
1129
 
1072
1130
  def inferGlyphClasses(self):
1073
- result = {glyph: 2 for glyph in self.ligatures}
1074
- result.update({glyph: 3 for glyph in self.marks})
1131
+ result = {}
1132
+ for marks, ligatures in self.get_subtables_():
1133
+ result.update({glyph: 2 for glyph in ligatures})
1134
+ result.update({glyph: 3 for glyph in marks})
1075
1135
  return result
1076
1136
 
1077
1137
  def build(self):
@@ -1081,18 +1141,26 @@ class MarkLigPosBuilder(LookupBuilder):
1081
1141
  An ``otTables.Lookup`` object representing the mark-to-ligature
1082
1142
  positioning lookup.
1083
1143
  """
1084
- markClasses = self.buildMarkClasses_(self.marks)
1085
- marks = {
1086
- mark: (markClasses[mc], anchor) for mark, (mc, anchor) in self.marks.items()
1087
- }
1088
- ligs = {}
1089
- for lig, components in self.ligatures.items():
1090
- ligs[lig] = []
1091
- for c in components:
1092
- ligs[lig].append({markClasses[mc]: a for mc, a in c.items()})
1093
- subtables = buildMarkLigPos(marks, ligs, self.glyphMap)
1144
+ subtables = []
1145
+ for subtable in self.get_subtables_():
1146
+ markClasses = self.buildMarkClasses_(subtable[0])
1147
+ marks = {
1148
+ mark: (markClasses[mc], anchor)
1149
+ for mark, (mc, anchor) in subtable[0].items()
1150
+ }
1151
+ ligs = {}
1152
+ for lig, components in subtable[1].items():
1153
+ ligs[lig] = []
1154
+ for c in components:
1155
+ ligs[lig].append({markClasses[mc]: a for mc, a in c.items()})
1156
+ subtables.append(buildMarkLigPosSubtable(marks, ligs, self.glyphMap))
1094
1157
  return self.buildLookup_(subtables)
1095
1158
 
1159
+ def add_subtable_break(self, location):
1160
+ self.subtables_.append((self.marks, self.ligatures))
1161
+ self.marks = {}
1162
+ self.ligatures = {}
1163
+
1096
1164
 
1097
1165
  class MarkMarkPosBuilder(LookupBuilder):
1098
1166
  """Builds a Mark-To-Mark Positioning (GPOS6) lookup.
@@ -1125,17 +1193,25 @@ class MarkMarkPosBuilder(LookupBuilder):
1125
1193
  LookupBuilder.__init__(self, font, location, "GPOS", 6)
1126
1194
  self.marks = {} # glyphName -> (markClassName, anchor)
1127
1195
  self.baseMarks = {} # glyphName -> {markClassName: anchor}
1196
+ self.subtables_ = []
1197
+
1198
+ def get_subtables_(self):
1199
+ subtables_ = self.subtables_
1200
+ if self.baseMarks or self.marks:
1201
+ subtables_.append((self.marks, self.baseMarks))
1202
+ return subtables_
1128
1203
 
1129
1204
  def equals(self, other):
1130
1205
  return (
1131
1206
  LookupBuilder.equals(self, other)
1132
- and self.marks == other.marks
1133
- and self.baseMarks == other.baseMarks
1207
+ and self.get_subtables_() == other.get_subtables_()
1134
1208
  )
1135
1209
 
1136
1210
  def inferGlyphClasses(self):
1137
- result = {glyph: 3 for glyph in self.baseMarks}
1138
- result.update({glyph: 3 for glyph in self.marks})
1211
+ result = {}
1212
+ for marks, baseMarks in self.get_subtables_():
1213
+ result.update({glyph: 3 for glyph in baseMarks})
1214
+ result.update({glyph: 3 for glyph in marks})
1139
1215
  return result
1140
1216
 
1141
1217
  def build(self):
@@ -1145,25 +1221,34 @@ class MarkMarkPosBuilder(LookupBuilder):
1145
1221
  An ``otTables.Lookup`` object representing the mark-to-mark
1146
1222
  positioning lookup.
1147
1223
  """
1148
- markClasses = self.buildMarkClasses_(self.marks)
1149
- markClassList = sorted(markClasses.keys(), key=markClasses.get)
1150
- marks = {
1151
- mark: (markClasses[mc], anchor) for mark, (mc, anchor) in self.marks.items()
1152
- }
1224
+ subtables = []
1225
+ for subtable in self.get_subtables_():
1226
+ markClasses = self.buildMarkClasses_(subtable[0])
1227
+ markClassList = sorted(markClasses.keys(), key=markClasses.get)
1228
+ marks = {
1229
+ mark: (markClasses[mc], anchor)
1230
+ for mark, (mc, anchor) in subtable[0].items()
1231
+ }
1232
+
1233
+ st = ot.MarkMarkPos()
1234
+ st.Format = 1
1235
+ st.ClassCount = len(markClasses)
1236
+ st.Mark1Coverage = buildCoverage(marks, self.glyphMap)
1237
+ st.Mark2Coverage = buildCoverage(subtable[1], self.glyphMap)
1238
+ st.Mark1Array = buildMarkArray(marks, self.glyphMap)
1239
+ st.Mark2Array = ot.Mark2Array()
1240
+ st.Mark2Array.Mark2Count = len(st.Mark2Coverage.glyphs)
1241
+ st.Mark2Array.Mark2Record = []
1242
+ for base in st.Mark2Coverage.glyphs:
1243
+ anchors = [subtable[1][base].get(mc) for mc in markClassList]
1244
+ st.Mark2Array.Mark2Record.append(buildMark2Record(anchors))
1245
+ subtables.append(st)
1246
+ return self.buildLookup_(subtables)
1153
1247
 
1154
- st = ot.MarkMarkPos()
1155
- st.Format = 1
1156
- st.ClassCount = len(markClasses)
1157
- st.Mark1Coverage = buildCoverage(marks, self.glyphMap)
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])
1248
+ def add_subtable_break(self, location):
1249
+ self.subtables_.append((self.marks, self.baseMarks))
1250
+ self.marks = {}
1251
+ self.baseMarks = {}
1167
1252
 
1168
1253
 
1169
1254
  class ReverseChainSingleSubstBuilder(LookupBuilder):
@@ -1484,6 +1569,8 @@ class SinglePosBuilder(LookupBuilder):
1484
1569
  otValueRection: A ``otTables.ValueRecord`` used to position the
1485
1570
  glyph.
1486
1571
  """
1572
+ if otValueRecord is None:
1573
+ otValueRecord = ValueRecord()
1487
1574
  if not self.can_add(glyph, otValueRecord):
1488
1575
  otherLoc = self.locations[glyph]
1489
1576
  raise OpenTypeLibError(
@@ -1900,53 +1987,15 @@ def buildMarkArray(marks, glyphMap):
1900
1987
  return self
1901
1988
 
1902
1989
 
1990
+ @deprecateFunction(
1991
+ "use buildMarkBasePosSubtable() instead", category=DeprecationWarning
1992
+ )
1903
1993
  def buildMarkBasePos(marks, bases, glyphMap):
1904
1994
  """Build a list of MarkBasePos (GPOS4) subtables.
1905
1995
 
1906
- This routine turns a set of marks and bases into a list of mark-to-base
1907
- positioning subtables. Currently the list will contain a single subtable
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.
1996
+ .. deprecated:: 4.58.0
1997
+ Use :func:`buildMarkBasePosSubtable` instead.
1938
1998
  """
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
1999
  return [buildMarkBasePosSubtable(marks, bases, glyphMap)]
1951
2000
 
1952
2001
 
@@ -1954,7 +2003,15 @@ def buildMarkBasePosSubtable(marks, bases, glyphMap):
1954
2003
  """Build a single MarkBasePos (GPOS4) subtable.
1955
2004
 
1956
2005
  This builds a mark-to-base lookup subtable containing all of the referenced
1957
- marks and bases. See :func:`buildMarkBasePos`.
2006
+ marks and bases.
2007
+
2008
+ Example::
2009
+
2010
+ # a1, a2, a3, a4, a5 = buildAnchor(500, 100), ...
2011
+
2012
+ marks = {"acute": (0, a1), "grave": (0, a1), "cedilla": (1, a2)}
2013
+ bases = {"a": {0: a3, 1: a5}, "b": {0: a4, 1: a5}}
2014
+ markbaseposes = [buildMarkBasePosSubtable(marks, bases, font.getReverseGlyphMap())]
1958
2015
 
1959
2016
  Args:
1960
2017
  marks (dict): A dictionary mapping anchors to glyphs; the keys being
@@ -1981,14 +2038,21 @@ def buildMarkBasePosSubtable(marks, bases, glyphMap):
1981
2038
  return self
1982
2039
 
1983
2040
 
2041
+ @deprecateFunction("use buildMarkLigPosSubtable() instead", category=DeprecationWarning)
1984
2042
  def buildMarkLigPos(marks, ligs, glyphMap):
1985
2043
  """Build a list of MarkLigPos (GPOS5) subtables.
1986
2044
 
1987
- This routine turns a set of marks and ligatures into a list of mark-to-ligature
1988
- positioning subtables. Currently the list will contain a single subtable
1989
- containing all marks and ligatures, although at a later date it may return
1990
- the optimal list of subtables subsetting the marks and ligatures into groups
1991
- which save space. See :func:`buildMarkLigPosSubtable` below.
2045
+ .. deprecated:: 4.58.0
2046
+ Use :func:`buildMarkLigPosSubtable` instead.
2047
+ """
2048
+ return [buildMarkLigPosSubtable(marks, ligs, glyphMap)]
2049
+
2050
+
2051
+ def buildMarkLigPosSubtable(marks, ligs, glyphMap):
2052
+ """Build a single MarkLigPos (GPOS5) subtable.
2053
+
2054
+ This builds a mark-to-base lookup subtable containing all of the referenced
2055
+ marks and bases.
1992
2056
 
1993
2057
  Note that if you are implementing a layout compiler, you may find it more
1994
2058
  flexible to use
@@ -2009,37 +2073,9 @@ def buildMarkLigPos(marks, ligs, glyphMap):
2009
2073
  ],
2010
2074
  # "c_t": [{...}, {...}]
2011
2075
  }
2012
- markligposes = buildMarkLigPos(marks, ligs,
2076
+ markligpose = buildMarkLigPosSubtable(marks, ligs,
2013
2077
  font.getReverseGlyphMap())
2014
2078
 
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
2079
  Args:
2044
2080
  marks (dict): A dictionary mapping anchors to glyphs; the keys being
2045
2081
  glyph names, and the values being a tuple of mark class number and
@@ -2706,10 +2742,18 @@ class ClassDefBuilder(object):
2706
2742
  AXIS_VALUE_NEGATIVE_INFINITY = fixedToFloat(-0x80000000, 16)
2707
2743
  AXIS_VALUE_POSITIVE_INFINITY = fixedToFloat(0x7FFFFFFF, 16)
2708
2744
 
2745
+ STATName = Union[int, str, Dict[str, str]]
2746
+ """A raw name ID, English name, or multilingual name."""
2747
+
2709
2748
 
2710
2749
  def buildStatTable(
2711
- ttFont, axes, locations=None, elidedFallbackName=2, windowsNames=True, macNames=True
2712
- ):
2750
+ ttFont: TTFont,
2751
+ axes,
2752
+ locations=None,
2753
+ elidedFallbackName: Union[STATName, STATNameStatement] = 2,
2754
+ windowsNames: bool = True,
2755
+ macNames: bool = True,
2756
+ ) -> None:
2713
2757
  """Add a 'STAT' table to 'ttFont'.
2714
2758
 
2715
2759
  'axes' is a list of dictionaries describing axes and their
@@ -2900,7 +2944,13 @@ def _buildAxisValuesFormat4(locations, axes, ttFont, windowsNames=True, macNames
2900
2944
  return axisValues
2901
2945
 
2902
2946
 
2903
- def _addName(ttFont, value, minNameID=0, windows=True, mac=True):
2947
+ def _addName(
2948
+ ttFont: TTFont,
2949
+ value: Union[STATName, STATNameStatement],
2950
+ minNameID: int = 0,
2951
+ windows: bool = True,
2952
+ mac: bool = True,
2953
+ ) -> int:
2904
2954
  nameTable = ttFont["name"]
2905
2955
  if isinstance(value, int):
2906
2956
  # Already a nameID