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.
Files changed (140) hide show
  1. fontTools/__init__.py +1 -1
  2. fontTools/annotations.py +30 -0
  3. fontTools/cffLib/CFF2ToCFF.py +65 -10
  4. fontTools/cffLib/__init__.py +61 -26
  5. fontTools/cffLib/specializer.py +4 -1
  6. fontTools/cffLib/transforms.py +11 -6
  7. fontTools/config/__init__.py +15 -0
  8. fontTools/cu2qu/cu2qu.c +6567 -5579
  9. fontTools/cu2qu/cu2qu.cpython-313-aarch64-linux-musl.so +0 -0
  10. fontTools/cu2qu/cu2qu.py +36 -4
  11. fontTools/cu2qu/ufo.py +14 -0
  12. fontTools/designspaceLib/__init__.py +8 -3
  13. fontTools/designspaceLib/statNames.py +14 -7
  14. fontTools/feaLib/ast.py +24 -15
  15. fontTools/feaLib/builder.py +139 -66
  16. fontTools/feaLib/error.py +1 -1
  17. fontTools/feaLib/lexer.c +7038 -7995
  18. fontTools/feaLib/lexer.cpython-313-aarch64-linux-musl.so +0 -0
  19. fontTools/feaLib/parser.py +75 -40
  20. fontTools/feaLib/variableScalar.py +6 -1
  21. fontTools/fontBuilder.py +50 -44
  22. fontTools/merge/__init__.py +1 -1
  23. fontTools/merge/cmap.py +33 -1
  24. fontTools/merge/tables.py +12 -1
  25. fontTools/misc/bezierTools.c +14913 -17013
  26. fontTools/misc/bezierTools.cpython-313-aarch64-linux-musl.so +0 -0
  27. fontTools/misc/bezierTools.py +4 -1
  28. fontTools/misc/configTools.py +3 -1
  29. fontTools/misc/enumTools.py +23 -0
  30. fontTools/misc/etree.py +4 -27
  31. fontTools/misc/filesystem/__init__.py +68 -0
  32. fontTools/misc/filesystem/_base.py +134 -0
  33. fontTools/misc/filesystem/_copy.py +45 -0
  34. fontTools/misc/filesystem/_errors.py +54 -0
  35. fontTools/misc/filesystem/_info.py +75 -0
  36. fontTools/misc/filesystem/_osfs.py +164 -0
  37. fontTools/misc/filesystem/_path.py +67 -0
  38. fontTools/misc/filesystem/_subfs.py +92 -0
  39. fontTools/misc/filesystem/_tempfs.py +34 -0
  40. fontTools/misc/filesystem/_tools.py +34 -0
  41. fontTools/misc/filesystem/_walk.py +55 -0
  42. fontTools/misc/filesystem/_zipfs.py +204 -0
  43. fontTools/misc/fixedTools.py +1 -1
  44. fontTools/misc/loggingTools.py +1 -1
  45. fontTools/misc/psCharStrings.py +17 -2
  46. fontTools/misc/sstruct.py +2 -6
  47. fontTools/misc/symfont.py +6 -8
  48. fontTools/misc/testTools.py +5 -1
  49. fontTools/misc/textTools.py +4 -2
  50. fontTools/misc/visitor.py +32 -16
  51. fontTools/misc/xmlWriter.py +44 -8
  52. fontTools/mtiLib/__init__.py +1 -3
  53. fontTools/otlLib/builder.py +402 -155
  54. fontTools/otlLib/optimize/gpos.py +49 -63
  55. fontTools/pens/filterPen.py +218 -26
  56. fontTools/pens/momentsPen.c +5514 -5584
  57. fontTools/pens/momentsPen.cpython-313-aarch64-linux-musl.so +0 -0
  58. fontTools/pens/pointPen.py +61 -18
  59. fontTools/pens/roundingPen.py +2 -2
  60. fontTools/pens/t2CharStringPen.py +31 -11
  61. fontTools/qu2cu/qu2cu.c +6581 -6168
  62. fontTools/qu2cu/qu2cu.cpython-313-aarch64-linux-musl.so +0 -0
  63. fontTools/subset/__init__.py +283 -25
  64. fontTools/subset/svg.py +2 -3
  65. fontTools/ttLib/__init__.py +4 -0
  66. fontTools/ttLib/__main__.py +47 -8
  67. fontTools/ttLib/removeOverlaps.py +7 -5
  68. fontTools/ttLib/reorderGlyphs.py +8 -7
  69. fontTools/ttLib/sfnt.py +11 -9
  70. fontTools/ttLib/tables/D__e_b_g.py +20 -2
  71. fontTools/ttLib/tables/G_V_A_R_.py +5 -0
  72. fontTools/ttLib/tables/S__i_l_f.py +2 -2
  73. fontTools/ttLib/tables/T_S_I__0.py +14 -3
  74. fontTools/ttLib/tables/T_S_I__1.py +2 -5
  75. fontTools/ttLib/tables/T_S_I__5.py +18 -7
  76. fontTools/ttLib/tables/__init__.py +1 -0
  77. fontTools/ttLib/tables/_a_v_a_r.py +12 -3
  78. fontTools/ttLib/tables/_c_m_a_p.py +20 -7
  79. fontTools/ttLib/tables/_c_v_t.py +3 -2
  80. fontTools/ttLib/tables/_f_p_g_m.py +3 -1
  81. fontTools/ttLib/tables/_g_l_y_f.py +45 -21
  82. fontTools/ttLib/tables/_g_v_a_r.py +67 -19
  83. fontTools/ttLib/tables/_h_d_m_x.py +4 -4
  84. fontTools/ttLib/tables/_h_m_t_x.py +7 -3
  85. fontTools/ttLib/tables/_l_o_c_a.py +2 -2
  86. fontTools/ttLib/tables/_n_a_m_e.py +11 -6
  87. fontTools/ttLib/tables/_p_o_s_t.py +9 -7
  88. fontTools/ttLib/tables/otBase.py +5 -12
  89. fontTools/ttLib/tables/otConverters.py +5 -2
  90. fontTools/ttLib/tables/otData.py +1 -1
  91. fontTools/ttLib/tables/otTables.py +33 -30
  92. fontTools/ttLib/tables/otTraverse.py +2 -1
  93. fontTools/ttLib/tables/sbixStrike.py +3 -3
  94. fontTools/ttLib/ttFont.py +666 -120
  95. fontTools/ttLib/ttGlyphSet.py +0 -10
  96. fontTools/ttLib/woff2.py +10 -13
  97. fontTools/ttx.py +13 -1
  98. fontTools/ufoLib/__init__.py +300 -202
  99. fontTools/ufoLib/converters.py +103 -30
  100. fontTools/ufoLib/errors.py +8 -0
  101. fontTools/ufoLib/etree.py +1 -1
  102. fontTools/ufoLib/filenames.py +171 -106
  103. fontTools/ufoLib/glifLib.py +303 -205
  104. fontTools/ufoLib/kerning.py +98 -48
  105. fontTools/ufoLib/utils.py +46 -15
  106. fontTools/ufoLib/validators.py +121 -99
  107. fontTools/unicodedata/Blocks.py +35 -20
  108. fontTools/unicodedata/Mirrored.py +446 -0
  109. fontTools/unicodedata/ScriptExtensions.py +63 -37
  110. fontTools/unicodedata/Scripts.py +173 -152
  111. fontTools/unicodedata/__init__.py +10 -2
  112. fontTools/varLib/__init__.py +198 -109
  113. fontTools/varLib/avar/__init__.py +0 -0
  114. fontTools/varLib/avar/__main__.py +72 -0
  115. fontTools/varLib/avar/build.py +79 -0
  116. fontTools/varLib/avar/map.py +108 -0
  117. fontTools/varLib/avar/plan.py +1004 -0
  118. fontTools/varLib/{avar.py → avar/unbuild.py} +70 -59
  119. fontTools/varLib/avarPlanner.py +3 -999
  120. fontTools/varLib/featureVars.py +21 -7
  121. fontTools/varLib/hvar.py +113 -0
  122. fontTools/varLib/instancer/__init__.py +180 -65
  123. fontTools/varLib/interpolatableHelpers.py +3 -0
  124. fontTools/varLib/iup.c +7564 -6903
  125. fontTools/varLib/iup.cpython-313-aarch64-linux-musl.so +0 -0
  126. fontTools/varLib/models.py +17 -2
  127. fontTools/varLib/mutator.py +11 -0
  128. fontTools/varLib/varStore.py +10 -38
  129. fontTools/voltLib/__main__.py +206 -0
  130. fontTools/voltLib/ast.py +4 -0
  131. fontTools/voltLib/parser.py +16 -8
  132. fontTools/voltLib/voltToFea.py +347 -166
  133. {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/METADATA +269 -1410
  134. {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/RECORD +318 -294
  135. {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/WHEEL +1 -1
  136. fonttools-4.61.1.dist-info/licenses/LICENSE.external +388 -0
  137. {fonttools-4.55.4.data → fonttools-4.61.1.data}/data/share/man/man1/ttx.1 +0 -0
  138. {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/entry_points.txt +0 -0
  139. {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info/licenses}/LICENSE +0 -0
  140. {fonttools-4.55.4.dist-info → fonttools-4.61.1.dist-info}/top_level.txt +0 -0
@@ -3,6 +3,7 @@ from functools import partial
3
3
  from fontTools.misc import sstruct
4
4
  from fontTools.misc.textTools import safeEval
5
5
  from fontTools.misc.lazyTools import LazyDict
6
+ from fontTools.ttLib import OPTIMIZE_FONT_SPEED
6
7
  from fontTools.ttLib.tables.TupleVariation import TupleVariation
7
8
  from . import DefaultTable
8
9
  import array
@@ -23,19 +24,24 @@ log = logging.getLogger(__name__)
23
24
  # FreeType2 source code for parsing 'gvar':
24
25
  # http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/src/truetype/ttgxvar.c
25
26
 
26
- GVAR_HEADER_FORMAT = """
27
+ GVAR_HEADER_FORMAT_HEAD = """
27
28
  > # big endian
28
29
  version: H
29
30
  reserved: H
30
31
  axisCount: H
31
32
  sharedTupleCount: H
32
33
  offsetToSharedTuples: I
33
- glyphCount: H
34
+ """
35
+ # In between the HEAD and TAIL lies the glyphCount, which is
36
+ # of different size: 2 bytes for gvar, and 3 bytes for GVAR.
37
+ GVAR_HEADER_FORMAT_TAIL = """
38
+ > # big endian
34
39
  flags: H
35
40
  offsetToGlyphVariationData: I
36
41
  """
37
42
 
38
- GVAR_HEADER_SIZE = sstruct.calcsize(GVAR_HEADER_FORMAT)
43
+ GVAR_HEADER_SIZE_HEAD = sstruct.calcsize(GVAR_HEADER_FORMAT_HEAD)
44
+ GVAR_HEADER_SIZE_TAIL = sstruct.calcsize(GVAR_HEADER_FORMAT_TAIL)
39
45
 
40
46
 
41
47
  class table__g_v_a_r(DefaultTable.DefaultTable):
@@ -50,6 +56,7 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
50
56
  """
51
57
 
52
58
  dependencies = ["fvar", "glyf"]
59
+ gid_size = 2
53
60
 
54
61
  def __init__(self, tag=None):
55
62
  DefaultTable.DefaultTable.__init__(self, tag)
@@ -72,28 +79,33 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
72
79
  offsets.append(offset)
73
80
  compiledOffsets, tableFormat = self.compileOffsets_(offsets)
74
81
 
82
+ GVAR_HEADER_SIZE = GVAR_HEADER_SIZE_HEAD + self.gid_size + GVAR_HEADER_SIZE_TAIL
75
83
  header = {}
76
84
  header["version"] = self.version
77
85
  header["reserved"] = self.reserved
78
86
  header["axisCount"] = len(axisTags)
79
87
  header["sharedTupleCount"] = len(sharedTuples)
80
88
  header["offsetToSharedTuples"] = GVAR_HEADER_SIZE + len(compiledOffsets)
81
- header["glyphCount"] = len(compiledGlyphs)
82
89
  header["flags"] = tableFormat
83
90
  header["offsetToGlyphVariationData"] = (
84
91
  header["offsetToSharedTuples"] + sharedTupleSize
85
92
  )
86
- compiledHeader = sstruct.pack(GVAR_HEADER_FORMAT, header)
87
93
 
88
- result = [compiledHeader, compiledOffsets]
94
+ result = [
95
+ sstruct.pack(GVAR_HEADER_FORMAT_HEAD, header),
96
+ len(compiledGlyphs).to_bytes(self.gid_size, "big"),
97
+ sstruct.pack(GVAR_HEADER_FORMAT_TAIL, header),
98
+ ]
99
+
100
+ result.append(compiledOffsets)
89
101
  result.extend(sharedTuples)
90
102
  result.extend(compiledGlyphs)
91
103
  return b"".join(result)
92
104
 
93
105
  def compileGlyphs_(self, ttFont, axisTags, sharedCoordIndices):
106
+ optimizeSpeed = ttFont.cfg[OPTIMIZE_FONT_SPEED]
94
107
  result = []
95
108
  glyf = ttFont["glyf"]
96
- optimizeSize = getattr(self, "optimizeSize", True)
97
109
  for glyphName in ttFont.getGlyphOrder():
98
110
  variations = self.variations.get(glyphName, [])
99
111
  if not variations:
@@ -102,11 +114,12 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
102
114
  pointCountUnused = 0 # pointCount is actually unused by compileGlyph
103
115
  result.append(
104
116
  compileGlyph_(
117
+ self.gid_size,
105
118
  variations,
106
119
  pointCountUnused,
107
120
  axisTags,
108
121
  sharedCoordIndices,
109
- optimizeSize=optimizeSize,
122
+ optimizeSize=not optimizeSpeed,
110
123
  )
111
124
  )
112
125
  return result
@@ -114,9 +127,25 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
114
127
  def decompile(self, data, ttFont):
115
128
  axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
116
129
  glyphs = ttFont.getGlyphOrder()
117
- sstruct.unpack(GVAR_HEADER_FORMAT, data[0:GVAR_HEADER_SIZE], self)
118
- assert len(glyphs) == self.glyphCount
119
- assert len(axisTags) == self.axisCount
130
+
131
+ # Parse the header
132
+ GVAR_HEADER_SIZE = GVAR_HEADER_SIZE_HEAD + self.gid_size + GVAR_HEADER_SIZE_TAIL
133
+ sstruct.unpack(GVAR_HEADER_FORMAT_HEAD, data[:GVAR_HEADER_SIZE_HEAD], self)
134
+ self.glyphCount = int.from_bytes(
135
+ data[GVAR_HEADER_SIZE_HEAD : GVAR_HEADER_SIZE_HEAD + self.gid_size], "big"
136
+ )
137
+ sstruct.unpack(
138
+ GVAR_HEADER_FORMAT_TAIL,
139
+ data[GVAR_HEADER_SIZE_HEAD + self.gid_size : GVAR_HEADER_SIZE],
140
+ self,
141
+ )
142
+
143
+ assert len(glyphs) == self.glyphCount, (len(glyphs), self.glyphCount)
144
+ assert len(axisTags) == self.axisCount, (
145
+ len(axisTags),
146
+ self.axisCount,
147
+ axisTags,
148
+ )
120
149
  sharedCoords = tv.decompileSharedTuples(
121
150
  axisTags, self.sharedTupleCount, data, self.offsetToSharedTuples
122
151
  )
@@ -144,7 +173,7 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
144
173
  glyph = glyf[glyphName]
145
174
  numPointsInGlyph = self.getNumPoints_(glyph)
146
175
  return decompileGlyph_(
147
- numPointsInGlyph, sharedCoords, axisTags, gvarData
176
+ self.gid_size, numPointsInGlyph, sharedCoords, axisTags, gvarData
148
177
  )
149
178
 
150
179
  return read_item
@@ -262,23 +291,42 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
262
291
 
263
292
 
264
293
  def compileGlyph_(
265
- variations, pointCount, axisTags, sharedCoordIndices, *, optimizeSize=True
294
+ dataOffsetSize,
295
+ variations,
296
+ pointCount,
297
+ axisTags,
298
+ sharedCoordIndices,
299
+ *,
300
+ optimizeSize=True,
266
301
  ):
302
+ assert dataOffsetSize in (2, 3)
267
303
  tupleVariationCount, tuples, data = tv.compileTupleVariationStore(
268
304
  variations, pointCount, axisTags, sharedCoordIndices, optimizeSize=optimizeSize
269
305
  )
270
306
  if tupleVariationCount == 0:
271
307
  return b""
272
- result = [struct.pack(">HH", tupleVariationCount, 4 + len(tuples)), tuples, data]
273
- if (len(tuples) + len(data)) % 2 != 0:
308
+
309
+ offsetToData = 2 + dataOffsetSize + len(tuples)
310
+
311
+ result = [
312
+ tupleVariationCount.to_bytes(2, "big"),
313
+ offsetToData.to_bytes(dataOffsetSize, "big"),
314
+ tuples,
315
+ data,
316
+ ]
317
+ if (offsetToData + len(data)) % 2 != 0:
274
318
  result.append(b"\0") # padding
275
319
  return b"".join(result)
276
320
 
277
321
 
278
- def decompileGlyph_(pointCount, sharedTuples, axisTags, data):
279
- if len(data) < 4:
322
+ def decompileGlyph_(dataOffsetSize, pointCount, sharedTuples, axisTags, data):
323
+ assert dataOffsetSize in (2, 3)
324
+ if len(data) < 2 + dataOffsetSize:
280
325
  return []
281
- tupleVariationCount, offsetToData = struct.unpack(">HH", data[:4])
326
+
327
+ tupleVariationCount = int.from_bytes(data[:2], "big")
328
+ offsetToData = int.from_bytes(data[2 : 2 + dataOffsetSize], "big")
329
+
282
330
  dataPos = offsetToData
283
331
  return tv.decompileTupleVariationStore(
284
332
  "gvar",
@@ -287,6 +335,6 @@ def decompileGlyph_(pointCount, sharedTuples, axisTags, data):
287
335
  pointCount,
288
336
  sharedTuples,
289
337
  data,
290
- 4,
338
+ 2 + dataOffsetSize,
291
339
  offsetToData,
292
340
  )
@@ -65,8 +65,8 @@ class table__h_d_m_x(DefaultTable.DefaultTable):
65
65
  items = sorted(self.hdmx.items())
66
66
  for ppem, widths in items:
67
67
  data = data + bytechr(ppem) + bytechr(max(widths.values()))
68
- for glyphID in range(len(glyphOrder)):
69
- width = widths[glyphOrder[glyphID]]
68
+ for glyphName in glyphOrder:
69
+ width = widths[glyphName]
70
70
  data = data + bytechr(width)
71
71
  data = data + pad
72
72
  return data
@@ -123,5 +123,5 @@ class table__h_d_m_x(DefaultTable.DefaultTable):
123
123
  glyphName = safeEval('"""' + glyphName + '"""')
124
124
  line = list(map(int, line[1:]))
125
125
  assert len(line) == len(ppems), "illegal hdmx format"
126
- for i in range(len(ppems)):
127
- hdmx[ppems[i]][glyphName] = line[i]
126
+ for i, ppem in enumerate(ppems):
127
+ hdmx[ppem][glyphName] = line[i]
@@ -40,15 +40,19 @@ class table__h_m_t_x(DefaultTable.DefaultTable):
40
40
  % (self.headerTag, self.numberOfMetricsName)
41
41
  )
42
42
  numberOfMetrics = numGlyphs
43
- if len(data) < 4 * numberOfMetrics:
44
- raise ttLib.TTLibError("not enough '%s' table data" % self.tableTag)
43
+ numberOfSideBearings = numGlyphs - numberOfMetrics
44
+ tableSize = 4 * numberOfMetrics + 2 * numberOfSideBearings
45
+ if len(data) < tableSize:
46
+ raise ttLib.TTLibError(
47
+ f"not enough '{self.tableTag}' table data: "
48
+ f"expected {tableSize} bytes, got {len(data)}"
49
+ )
45
50
  # Note: advanceWidth is unsigned, but some font editors might
46
51
  # read/write as signed. We can't be sure whether it was a mistake
47
52
  # or not, so we read as unsigned but also issue a warning...
48
53
  metricsFmt = ">" + self.longMetricFormat * numberOfMetrics
49
54
  metrics = struct.unpack(metricsFmt, data[: 4 * numberOfMetrics])
50
55
  data = data[4 * numberOfMetrics :]
51
- numberOfSideBearings = numGlyphs - numberOfMetrics
52
56
  sideBearings = array.array("h", data[: 2 * numberOfSideBearings])
53
57
  data = data[2 * numberOfSideBearings :]
54
58
 
@@ -46,8 +46,8 @@ class table__l_o_c_a(DefaultTable.DefaultTable):
46
46
  max_location = 0
47
47
  if max_location < 0x20000 and all(l % 2 == 0 for l in self.locations):
48
48
  locations = array.array("H")
49
- for i in range(len(self.locations)):
50
- locations.append(self.locations[i] // 2)
49
+ for location in self.locations:
50
+ locations.append(location // 2)
51
51
  ttFont["head"].indexToLocFormat = 0
52
52
  else:
53
53
  locations = array.array("I", self.locations)
@@ -1,4 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
+ from __future__ import annotations
3
+
2
4
  from fontTools.misc import sstruct
3
5
  from fontTools.misc.textTools import (
4
6
  bytechr,
@@ -63,7 +65,7 @@ class table__n_a_m_e(DefaultTable.DefaultTable):
63
65
  )
64
66
  stringData = data[stringOffset:]
65
67
  data = data[6:]
66
- self.names = []
68
+ self.names: list[NameRecord] = []
67
69
  for i in range(n):
68
70
  if len(data) < 12:
69
71
  log.error("skipping malformed name record #%d", i)
@@ -112,7 +114,9 @@ class table__n_a_m_e(DefaultTable.DefaultTable):
112
114
  self.names.append(name)
113
115
  name.fromXML(name, attrs, content, ttFont)
114
116
 
115
- def getName(self, nameID, platformID, platEncID, langID=None):
117
+ def getName(
118
+ self, nameID: int, platformID: int, platEncID: int, langID: int | None = None
119
+ ) -> "NameRecord | None":
116
120
  for namerecord in self.names:
117
121
  if (
118
122
  namerecord.nameID == nameID
@@ -123,8 +127,9 @@ class table__n_a_m_e(DefaultTable.DefaultTable):
123
127
  return namerecord
124
128
  return None # not found
125
129
 
126
- def getDebugName(self, nameID):
127
- englishName = someName = None
130
+ def getDebugName(self, nameID: int) -> str | None:
131
+ englishName: str | None = None
132
+ someName: str | None = None
128
133
  for name in self.names:
129
134
  if name.nameID != nameID:
130
135
  continue
@@ -513,7 +518,7 @@ class NameRecord(object):
513
518
  self.platformID == 3 and self.platEncID in [0, 1, 10]
514
519
  )
515
520
 
516
- def toUnicode(self, errors="strict"):
521
+ def toUnicode(self, errors: str = "strict") -> str:
517
522
  """
518
523
  If self.string is a Unicode string, return it; otherwise try decoding the
519
524
  bytes in self.string to a Unicode string using the encoding of this
@@ -533,7 +538,7 @@ class NameRecord(object):
533
538
  and saving it back will not change them.
534
539
  """
535
540
 
536
- def isascii(b):
541
+ def isascii(b: int) -> bool:
537
542
  return (b >= 0x20 and b <= 0x7E) or b in [0x09, 0x0A, 0x0D]
538
543
 
539
544
  encoding = self.getEncoding()
@@ -118,21 +118,24 @@ class table__p_o_s_t(DefaultTable.DefaultTable):
118
118
  def build_psNameMapping(self, ttFont):
119
119
  mapping = {}
120
120
  allNames = {}
121
+ glyphOrderNames = set(self.glyphOrder)
121
122
  for i in range(ttFont["maxp"].numGlyphs):
122
123
  glyphName = psName = self.glyphOrder[i]
123
124
  if glyphName == "":
124
125
  glyphName = "glyph%.5d" % i
126
+
125
127
  if glyphName in allNames:
126
128
  # make up a new glyphName that's unique
127
129
  n = allNames[glyphName]
128
- while (glyphName + "#" + str(n)) in allNames:
130
+ # check if the glyph name exists in the glyph order
131
+ while f"{glyphName}.{n}" in glyphOrderNames:
129
132
  n += 1
130
133
  allNames[glyphName] = n + 1
131
- glyphName = glyphName + "#" + str(n)
134
+ glyphName = f"{glyphName}.{n}"
132
135
 
133
- self.glyphOrder[i] = glyphName
134
136
  allNames[glyphName] = 1
135
137
  if glyphName != psName:
138
+ self.glyphOrder[i] = glyphName
136
139
  mapping[glyphName] = psName
137
140
 
138
141
  self.mapping = mapping
@@ -171,10 +174,9 @@ class table__p_o_s_t(DefaultTable.DefaultTable):
171
174
  extraNames = self.extraNames = [
172
175
  n for n in self.extraNames if n not in standardGlyphOrder
173
176
  ]
174
- for i in range(len(extraNames)):
175
- extraDict[extraNames[i]] = i
176
- for glyphID in range(numGlyphs):
177
- glyphName = glyphOrder[glyphID]
177
+ for i, name in enumerate(extraNames):
178
+ extraDict[name] = i
179
+ for glyphName in glyphOrder:
178
180
  if glyphName in self.mapping:
179
181
  psName = self.mapping[glyphName]
180
182
  else:
@@ -15,10 +15,7 @@ have_uharfbuzz = False
15
15
  try:
16
16
  import uharfbuzz as hb
17
17
 
18
- # repack method added in uharfbuzz >= 0.23; if uharfbuzz *can* be
19
- # imported but repack method is missing, behave as if uharfbuzz
20
- # is not available (fallback to the slower Python implementation)
21
- have_uharfbuzz = callable(getattr(hb, "repack", None))
18
+ have_uharfbuzz = True
22
19
  except ImportError:
23
20
  pass
24
21
 
@@ -398,6 +395,7 @@ class OTTableWriter(object):
398
395
  self.localState = localState
399
396
  self.tableTag = tableTag
400
397
  self.parent = None
398
+ self.name = "<none>"
401
399
 
402
400
  def __setitem__(self, name, value):
403
401
  state = self.localState.copy() if self.localState else dict()
@@ -499,8 +497,7 @@ class OTTableWriter(object):
499
497
  internedTables = {}
500
498
 
501
499
  items = self.items
502
- for i in range(len(items)):
503
- item = items[i]
500
+ for i, item in enumerate(items):
504
501
  if hasattr(item, "getCountData"):
505
502
  items[i] = item.getCountData()
506
503
  elif hasattr(item, "subWriter"):
@@ -668,10 +665,7 @@ class OTTableWriter(object):
668
665
  tableData = table.getDataForHarfbuzz()
669
666
  data.append(tableData)
670
667
 
671
- if hasattr(hb, "repack_with_tag"):
672
- return hb.repack_with_tag(str(tableTag), data, obj_list)
673
- else:
674
- return hb.repack(data, obj_list)
668
+ return hb.serialize_with_tag(str(tableTag), data, obj_list)
675
669
 
676
670
  def getAllData(self, remove_duplicate=True):
677
671
  """Assemble all data, including all subtables."""
@@ -1129,8 +1123,7 @@ class BaseTable(object):
1129
1123
  for conv in self.getConverters():
1130
1124
  if conv.repeat:
1131
1125
  value = getattr(self, conv.name, [])
1132
- for i in range(len(value)):
1133
- item = value[i]
1126
+ for i, item in enumerate(value):
1134
1127
  conv.xmlWrite(xmlWriter, font, item, conv.name, [("index", i)])
1135
1128
  else:
1136
1129
  if conv.aux and not eval(conv.aux, None, vars(self)):
@@ -10,7 +10,7 @@ from fontTools.ttLib.tables.TupleVariation import TupleVariation
10
10
  from fontTools.misc.roundTools import nearestMultipleShortestRepr, otRound
11
11
  from fontTools.misc.textTools import bytesjoin, tobytes, tostr, pad, safeEval
12
12
  from fontTools.misc.lazyTools import LazyList
13
- from fontTools.ttLib import getSearchRange
13
+ from fontTools.ttLib import OPTIMIZE_FONT_SPEED, getSearchRange
14
14
  from .otBase import (
15
15
  CountReference,
16
16
  FormatSwitchingBaseTable,
@@ -1810,7 +1810,10 @@ class TupleValues:
1810
1810
  return TupleVariation.decompileDeltas_(None, data)[0]
1811
1811
 
1812
1812
  def write(self, writer, font, tableDict, values, repeatIndex=None):
1813
- return bytes(TupleVariation.compileDeltaValues_(values))
1813
+ optimizeSpeed = font.cfg[OPTIMIZE_FONT_SPEED]
1814
+ return bytes(
1815
+ TupleVariation.compileDeltaValues_(values, optimizeSize=not optimizeSpeed)
1816
+ )
1814
1817
 
1815
1818
  def xmlRead(self, attrs, content, font):
1816
1819
  return safeEval(attrs["value"])
@@ -385,7 +385,7 @@ otData = [
385
385
  (
386
386
  "DeltaValue",
387
387
  "DeltaValue",
388
- "",
388
+ None,
389
389
  "DeltaFormat in (1,2,3)",
390
390
  "Array of compressed data",
391
391
  ),
@@ -11,6 +11,7 @@ from functools import reduce
11
11
  from math import radians
12
12
  import itertools
13
13
  from collections import defaultdict, namedtuple
14
+ from fontTools.ttLib import OPTIMIZE_FONT_SPEED
14
15
  from fontTools.ttLib.tables.TupleVariation import TupleVariation
15
16
  from fontTools.ttLib.tables.otTraverse import dfs_base_table
16
17
  from fontTools.misc.arrayTools import quantizeRect
@@ -236,6 +237,8 @@ class VarComponent:
236
237
  return data[i:]
237
238
 
238
239
  def compile(self, font):
240
+ optimizeSpeed = font.cfg[OPTIMIZE_FONT_SPEED]
241
+
239
242
  data = []
240
243
 
241
244
  flags = self.flags
@@ -259,7 +262,8 @@ class VarComponent:
259
262
  data.append(_write_uint32var(self.axisIndicesIndex))
260
263
  data.append(
261
264
  TupleVariation.compileDeltaValues_(
262
- [fl2fi(v, 14) for v in self.axisValues]
265
+ [fl2fi(v, 14) for v in self.axisValues],
266
+ optimizeSize=not optimizeSpeed,
263
267
  )
264
268
  )
265
269
  else:
@@ -986,8 +990,7 @@ class Coverage(FormatSwitchingBaseTable):
986
990
  if brokenOrder or len(ranges) * 3 < len(glyphs): # 3 words vs. 1 word
987
991
  # Format 2 is more compact
988
992
  index = 0
989
- for i in range(len(ranges)):
990
- start, end = ranges[i]
993
+ for i, (start, end) in enumerate(ranges):
991
994
  r = RangeRecord()
992
995
  r.StartID = start
993
996
  r.Start = font.getGlyphName(start)
@@ -1400,8 +1403,7 @@ class ClassDef(FormatSwitchingBaseTable):
1400
1403
  glyphCount = endGlyph - startGlyph + 1
1401
1404
  if len(ranges) * 3 < glyphCount + 1:
1402
1405
  # Format 2 is more compact
1403
- for i in range(len(ranges)):
1404
- cls, start, startName, end, endName = ranges[i]
1406
+ for i, (cls, start, startName, end, endName) in enumerate(ranges):
1405
1407
  rec = ClassRangeRecord()
1406
1408
  rec.Start = startName
1407
1409
  rec.End = endName
@@ -1459,8 +1461,7 @@ class AlternateSubst(FormatSwitchingBaseTable):
1459
1461
  if alternates is None:
1460
1462
  alternates = self.alternates = {}
1461
1463
  items = list(alternates.items())
1462
- for i in range(len(items)):
1463
- glyphName, set = items[i]
1464
+ for i, (glyphName, set) in enumerate(items):
1464
1465
  items[i] = font.getGlyphID(glyphName), glyphName, set
1465
1466
  items.sort()
1466
1467
  cov = Coverage()
@@ -1516,8 +1517,8 @@ class LigatureSubst(FormatSwitchingBaseTable):
1516
1517
  input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
1517
1518
  ligSets = rawTable["LigatureSet"]
1518
1519
  assert len(input) == len(ligSets)
1519
- for i in range(len(input)):
1520
- ligatures[input[i]] = ligSets[i].Ligature
1520
+ for i, inp in enumerate(input):
1521
+ ligatures[inp] = ligSets[i].Ligature
1521
1522
  else:
1522
1523
  assert 0, "unknown format: %s" % self.Format
1523
1524
  self.ligatures = ligatures
@@ -1573,8 +1574,7 @@ class LigatureSubst(FormatSwitchingBaseTable):
1573
1574
  ligatures = newLigatures
1574
1575
 
1575
1576
  items = list(ligatures.items())
1576
- for i in range(len(items)):
1577
- glyphName, set = items[i]
1577
+ for i, (glyphName, set) in enumerate(items):
1578
1578
  items[i] = font.getGlyphID(glyphName), glyphName, set
1579
1579
  items.sort()
1580
1580
  cov = Coverage()
@@ -2226,24 +2226,28 @@ _equivalents = {
2226
2226
  def fixLookupOverFlows(ttf, overflowRecord):
2227
2227
  """Either the offset from the LookupList to a lookup overflowed, or
2228
2228
  an offset from a lookup to a subtable overflowed.
2229
- The table layout is:
2230
- GPSO/GUSB
2231
- Script List
2232
- Feature List
2233
- LookUpList
2234
- Lookup[0] and contents
2235
- SubTable offset list
2236
- SubTable[0] and contents
2237
- ...
2238
- SubTable[n] and contents
2239
- ...
2240
- Lookup[n] and contents
2241
- SubTable offset list
2242
- SubTable[0] and contents
2243
- ...
2244
- SubTable[n] and contents
2229
+
2230
+ The table layout is::
2231
+
2232
+ GPSO/GUSB
2233
+ Script List
2234
+ Feature List
2235
+ LookUpList
2236
+ Lookup[0] and contents
2237
+ SubTable offset list
2238
+ SubTable[0] and contents
2239
+ ...
2240
+ SubTable[n] and contents
2241
+ ...
2242
+ Lookup[n] and contents
2243
+ SubTable offset list
2244
+ SubTable[0] and contents
2245
+ ...
2246
+ SubTable[n] and contents
2247
+
2245
2248
  If the offset to a lookup overflowed (SubTableIndex is None)
2246
- we must promote the *previous* lookup to an Extension type.
2249
+ we must promote the *previous* lookup to an Extension type.
2250
+
2247
2251
  If the offset from a lookup to subtable overflowed, then we must promote it
2248
2252
  to an Extension Lookup type.
2249
2253
  """
@@ -2271,8 +2275,7 @@ def fixLookupOverFlows(ttf, overflowRecord):
2271
2275
  lookup = lookups[lookupIndex]
2272
2276
  if lookup.LookupType != extType:
2273
2277
  lookup.LookupType = extType
2274
- for si in range(len(lookup.SubTable)):
2275
- subTable = lookup.SubTable[si]
2278
+ for si, subTable in enumerate(lookup.SubTable):
2276
2279
  extSubTableClass = lookupTypes[overflowRecord.tableType][extType]
2277
2280
  extSubTable = extSubTableClass()
2278
2281
  extSubTable.Format = 1
@@ -79,7 +79,8 @@ def bfs_base_table(
79
79
  """Breadth-first search tree of BaseTables.
80
80
 
81
81
  Args:
82
- the root of the tree.
82
+ root
83
+ the root of the tree.
83
84
  root_accessor (Optional[str]): attribute name for the root table, if any (mostly
84
85
  useful for debugging).
85
86
  skip_root (Optional[bool]): if True, the root itself is not visited, only its
@@ -128,9 +128,9 @@ class Strike(object):
128
128
  xmlWriter.simpletag("resolution", value=self.resolution)
129
129
  xmlWriter.newline()
130
130
  glyphOrder = ttFont.getGlyphOrder()
131
- for i in range(len(glyphOrder)):
132
- if glyphOrder[i] in self.glyphs:
133
- self.glyphs[glyphOrder[i]].toXML(xmlWriter, ttFont)
131
+ for glyphName in glyphOrder:
132
+ if glyphName in self.glyphs:
133
+ self.glyphs[glyphName].toXML(xmlWriter, ttFont)
134
134
  # TODO: what if there are more glyph data records than (glyf table) glyphs?
135
135
  xmlWriter.endtag("strike")
136
136
  xmlWriter.newline()