fonttools 4.58.5__cp311-cp311-musllinux_1_2_aarch64.whl → 4.59.1__cp311-cp311-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.

Potentially problematic release.


This version of fonttools might be problematic. Click here for more details.

Files changed (67) hide show
  1. fontTools/__init__.py +1 -1
  2. fontTools/cffLib/CFF2ToCFF.py +40 -10
  3. fontTools/cffLib/transforms.py +11 -6
  4. fontTools/cu2qu/cu2qu.c +21 -6
  5. fontTools/cu2qu/cu2qu.cpython-311-aarch64-linux-musl.so +0 -0
  6. fontTools/feaLib/builder.py +6 -1
  7. fontTools/feaLib/lexer.c +21 -6
  8. fontTools/feaLib/lexer.cpython-311-aarch64-linux-musl.so +0 -0
  9. fontTools/merge/__init__.py +1 -1
  10. fontTools/misc/bezierTools.c +24 -9
  11. fontTools/misc/bezierTools.cpython-311-aarch64-linux-musl.so +0 -0
  12. fontTools/misc/filesystem/__init__.py +68 -0
  13. fontTools/misc/filesystem/_base.py +134 -0
  14. fontTools/misc/filesystem/_copy.py +45 -0
  15. fontTools/misc/filesystem/_errors.py +54 -0
  16. fontTools/misc/filesystem/_info.py +75 -0
  17. fontTools/misc/filesystem/_osfs.py +164 -0
  18. fontTools/misc/filesystem/_path.py +67 -0
  19. fontTools/misc/filesystem/_subfs.py +92 -0
  20. fontTools/misc/filesystem/_tempfs.py +34 -0
  21. fontTools/misc/filesystem/_tools.py +34 -0
  22. fontTools/misc/filesystem/_walk.py +55 -0
  23. fontTools/misc/filesystem/_zipfs.py +204 -0
  24. fontTools/misc/psCharStrings.py +17 -2
  25. fontTools/misc/sstruct.py +2 -6
  26. fontTools/misc/xmlWriter.py +28 -1
  27. fontTools/pens/momentsPen.c +11 -4
  28. fontTools/pens/momentsPen.cpython-311-aarch64-linux-musl.so +0 -0
  29. fontTools/pens/roundingPen.py +2 -2
  30. fontTools/qu2cu/qu2cu.c +23 -8
  31. fontTools/qu2cu/qu2cu.cpython-311-aarch64-linux-musl.so +0 -0
  32. fontTools/subset/__init__.py +11 -11
  33. fontTools/ttLib/sfnt.py +2 -3
  34. fontTools/ttLib/tables/S__i_l_f.py +2 -2
  35. fontTools/ttLib/tables/T_S_I__1.py +2 -5
  36. fontTools/ttLib/tables/T_S_I__5.py +2 -2
  37. fontTools/ttLib/tables/_c_m_a_p.py +1 -1
  38. fontTools/ttLib/tables/_c_v_t.py +1 -2
  39. fontTools/ttLib/tables/_g_l_y_f.py +9 -10
  40. fontTools/ttLib/tables/_g_v_a_r.py +6 -3
  41. fontTools/ttLib/tables/_h_d_m_x.py +4 -4
  42. fontTools/ttLib/tables/_h_m_t_x.py +7 -3
  43. fontTools/ttLib/tables/_l_o_c_a.py +2 -2
  44. fontTools/ttLib/tables/_p_o_s_t.py +3 -4
  45. fontTools/ttLib/tables/otBase.py +2 -4
  46. fontTools/ttLib/tables/otTables.py +7 -12
  47. fontTools/ttLib/tables/sbixStrike.py +3 -3
  48. fontTools/ttLib/ttFont.py +7 -16
  49. fontTools/ttLib/woff2.py +10 -13
  50. fontTools/ufoLib/__init__.py +20 -25
  51. fontTools/ufoLib/glifLib.py +12 -17
  52. fontTools/ufoLib/validators.py +2 -4
  53. fontTools/unicodedata/__init__.py +2 -0
  54. fontTools/varLib/featureVars.py +8 -0
  55. fontTools/varLib/hvar.py +1 -1
  56. fontTools/varLib/instancer/__init__.py +65 -5
  57. fontTools/varLib/iup.c +23 -8
  58. fontTools/varLib/iup.cpython-311-aarch64-linux-musl.so +0 -0
  59. fontTools/varLib/mutator.py +11 -0
  60. {fonttools-4.58.5.dist-info → fonttools-4.59.1.dist-info}/METADATA +42 -12
  61. {fonttools-4.58.5.dist-info → fonttools-4.59.1.dist-info}/RECORD +67 -55
  62. {fonttools-4.58.5.dist-info → fonttools-4.59.1.dist-info}/licenses/LICENSE.external +29 -0
  63. {fonttools-4.58.5.data → fonttools-4.59.1.data}/data/share/man/man1/ttx.1 +0 -0
  64. {fonttools-4.58.5.dist-info → fonttools-4.59.1.dist-info}/WHEEL +0 -0
  65. {fonttools-4.58.5.dist-info → fonttools-4.59.1.dist-info}/entry_points.txt +0 -0
  66. {fonttools-4.58.5.dist-info → fonttools-4.59.1.dist-info}/licenses/LICENSE +0 -0
  67. {fonttools-4.58.5.dist-info → fonttools-4.59.1.dist-info}/top_level.txt +0 -0
@@ -91,12 +91,11 @@ class table_T_S_I__1(LogMixin, DefaultTable.DefaultTable):
91
91
  glyphNames = ttFont.getGlyphOrder()
92
92
 
93
93
  indices = []
94
- for i in range(len(glyphNames)):
94
+ for i, name in enumerate(glyphNames):
95
95
  if len(data) % 2:
96
96
  data = (
97
97
  data + b"\015"
98
98
  ) # align on 2-byte boundaries, fill with return chars. Yum.
99
- name = glyphNames[i]
100
99
  if name in self.glyphPrograms:
101
100
  text = tobytes(self.glyphPrograms[name], encoding="utf-8")
102
101
  else:
@@ -108,13 +107,11 @@ class table_T_S_I__1(LogMixin, DefaultTable.DefaultTable):
108
107
  data = data + text
109
108
 
110
109
  extra_indices = []
111
- codes = sorted(self.extras.items())
112
- for i in range(len(codes)):
110
+ for code, name in sorted(self.extras.items()):
113
111
  if len(data) % 2:
114
112
  data = (
115
113
  data + b"\015"
116
114
  ) # align on 2-byte boundaries, fill with return chars.
117
- code, name = codes[i]
118
115
  if name in self.extraPrograms:
119
116
  text = tobytes(self.extraPrograms[name], encoding="utf-8")
120
117
  else:
@@ -38,8 +38,8 @@ class table_T_S_I__5(DefaultTable.DefaultTable):
38
38
  def compile(self, ttFont):
39
39
  glyphNames = ttFont.getGlyphOrder()
40
40
  a = array.array("H")
41
- for i in range(len(glyphNames)):
42
- a.append(self.glyphGrouping.get(glyphNames[i], 0))
41
+ for glyphName in glyphNames:
42
+ a.append(self.glyphGrouping.get(glyphName, 0))
43
43
  if sys.byteorder != "big":
44
44
  a.byteswap()
45
45
  return a.tobytes()
@@ -398,7 +398,7 @@ class cmap_format_0(CmapSubtable):
398
398
  assert 262 == self.length, "Format 0 cmap subtable not 262 bytes"
399
399
  gids = array.array("B")
400
400
  gids.frombytes(self.data)
401
- charCodes = list(range(len(gids)))
401
+ charCodes = range(len(gids))
402
402
  self.cmap = _make_map(self.ttFont, charCodes, gids)
403
403
 
404
404
  def compile(self, ttFont):
@@ -29,8 +29,7 @@ class table__c_v_t(DefaultTable.DefaultTable):
29
29
  return values.tobytes()
30
30
 
31
31
  def toXML(self, writer, ttFont):
32
- for i in range(len(self.values)):
33
- value = self.values[i]
32
+ for i, value in enumerate(self.values):
34
33
  writer.simpletag("cv", value=value, index=i)
35
34
  writer.newline()
36
35
 
@@ -974,11 +974,10 @@ class Glyph(object):
974
974
  lastcomponent = len(self.components) - 1
975
975
  more = 1
976
976
  haveInstructions = 0
977
- for i in range(len(self.components)):
977
+ for i, compo in enumerate(self.components):
978
978
  if i == lastcomponent:
979
979
  haveInstructions = hasattr(self, "program")
980
980
  more = 0
981
- compo = self.components[i]
982
981
  data = data + compo.compile(more, haveInstructions, glyfTable)
983
982
  if haveInstructions:
984
983
  instructions = self.program.getBytecode()
@@ -2037,8 +2036,8 @@ class GlyphCoordinates(object):
2037
2036
  if round is noRound:
2038
2037
  return
2039
2038
  a = self._a
2040
- for i in range(len(a)):
2041
- a[i] = round(a[i])
2039
+ for i, value in enumerate(a):
2040
+ a[i] = round(value)
2042
2041
 
2043
2042
  def calcBounds(self):
2044
2043
  a = self._a
@@ -2168,8 +2167,8 @@ class GlyphCoordinates(object):
2168
2167
  """
2169
2168
  r = self.copy()
2170
2169
  a = r._a
2171
- for i in range(len(a)):
2172
- a[i] = -a[i]
2170
+ for i, value in enumerate(a):
2171
+ a[i] = -value
2173
2172
  return r
2174
2173
 
2175
2174
  def __round__(self, *, round=otRound):
@@ -2214,8 +2213,8 @@ class GlyphCoordinates(object):
2214
2213
  other = other._a
2215
2214
  a = self._a
2216
2215
  assert len(a) == len(other)
2217
- for i in range(len(a)):
2218
- a[i] += other[i]
2216
+ for i, value in enumerate(other):
2217
+ a[i] += value
2219
2218
  return self
2220
2219
  return NotImplemented
2221
2220
 
@@ -2238,8 +2237,8 @@ class GlyphCoordinates(object):
2238
2237
  other = other._a
2239
2238
  a = self._a
2240
2239
  assert len(a) == len(other)
2241
- for i in range(len(a)):
2242
- a[i] -= other[i]
2240
+ for i, value in enumerate(other):
2241
+ a[i] -= value
2243
2242
  return self
2244
2243
  return NotImplemented
2245
2244
 
@@ -64,7 +64,6 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
64
64
  self.variations = {}
65
65
 
66
66
  def compile(self, ttFont):
67
-
68
67
  axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
69
68
  sharedTuples = tv.compileSharedTuples(
70
69
  axisTags, itertools.chain(*self.variations.values())
@@ -141,8 +140,12 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
141
140
  self,
142
141
  )
143
142
 
144
- assert len(glyphs) == self.glyphCount
145
- assert len(axisTags) == self.axisCount
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
+ )
146
149
  sharedCoords = tv.decompileSharedTuples(
147
150
  axisTags, self.sharedTupleCount, data, self.offsetToSharedTuples
148
151
  )
@@ -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)
@@ -174,10 +174,9 @@ class table__p_o_s_t(DefaultTable.DefaultTable):
174
174
  extraNames = self.extraNames = [
175
175
  n for n in self.extraNames if n not in standardGlyphOrder
176
176
  ]
177
- for i in range(len(extraNames)):
178
- extraDict[extraNames[i]] = i
179
- for glyphID in range(numGlyphs):
180
- glyphName = glyphOrder[glyphID]
177
+ for i, name in enumerate(extraNames):
178
+ extraDict[name] = i
179
+ for glyphName in glyphOrder:
181
180
  if glyphName in self.mapping:
182
181
  psName = self.mapping[glyphName]
183
182
  else:
@@ -500,8 +500,7 @@ class OTTableWriter(object):
500
500
  internedTables = {}
501
501
 
502
502
  items = self.items
503
- for i in range(len(items)):
504
- item = items[i]
503
+ for i, item in enumerate(items):
505
504
  if hasattr(item, "getCountData"):
506
505
  items[i] = item.getCountData()
507
506
  elif hasattr(item, "subWriter"):
@@ -1130,8 +1129,7 @@ class BaseTable(object):
1130
1129
  for conv in self.getConverters():
1131
1130
  if conv.repeat:
1132
1131
  value = getattr(self, conv.name, [])
1133
- for i in range(len(value)):
1134
- item = value[i]
1132
+ for i, item in enumerate(value):
1135
1133
  conv.xmlWrite(xmlWriter, font, item, conv.name, [("index", i)])
1136
1134
  else:
1137
1135
  if conv.aux and not eval(conv.aux, None, vars(self)):
@@ -990,8 +990,7 @@ class Coverage(FormatSwitchingBaseTable):
990
990
  if brokenOrder or len(ranges) * 3 < len(glyphs): # 3 words vs. 1 word
991
991
  # Format 2 is more compact
992
992
  index = 0
993
- for i in range(len(ranges)):
994
- start, end = ranges[i]
993
+ for i, (start, end) in enumerate(ranges):
995
994
  r = RangeRecord()
996
995
  r.StartID = start
997
996
  r.Start = font.getGlyphName(start)
@@ -1404,8 +1403,7 @@ class ClassDef(FormatSwitchingBaseTable):
1404
1403
  glyphCount = endGlyph - startGlyph + 1
1405
1404
  if len(ranges) * 3 < glyphCount + 1:
1406
1405
  # Format 2 is more compact
1407
- for i in range(len(ranges)):
1408
- cls, start, startName, end, endName = ranges[i]
1406
+ for i, (cls, start, startName, end, endName) in enumerate(ranges):
1409
1407
  rec = ClassRangeRecord()
1410
1408
  rec.Start = startName
1411
1409
  rec.End = endName
@@ -1463,8 +1461,7 @@ class AlternateSubst(FormatSwitchingBaseTable):
1463
1461
  if alternates is None:
1464
1462
  alternates = self.alternates = {}
1465
1463
  items = list(alternates.items())
1466
- for i in range(len(items)):
1467
- glyphName, set = items[i]
1464
+ for i, (glyphName, set) in enumerate(items):
1468
1465
  items[i] = font.getGlyphID(glyphName), glyphName, set
1469
1466
  items.sort()
1470
1467
  cov = Coverage()
@@ -1520,8 +1517,8 @@ class LigatureSubst(FormatSwitchingBaseTable):
1520
1517
  input = _getGlyphsFromCoverageTable(rawTable["Coverage"])
1521
1518
  ligSets = rawTable["LigatureSet"]
1522
1519
  assert len(input) == len(ligSets)
1523
- for i in range(len(input)):
1524
- ligatures[input[i]] = ligSets[i].Ligature
1520
+ for i, inp in enumerate(input):
1521
+ ligatures[inp] = ligSets[i].Ligature
1525
1522
  else:
1526
1523
  assert 0, "unknown format: %s" % self.Format
1527
1524
  self.ligatures = ligatures
@@ -1577,8 +1574,7 @@ class LigatureSubst(FormatSwitchingBaseTable):
1577
1574
  ligatures = newLigatures
1578
1575
 
1579
1576
  items = list(ligatures.items())
1580
- for i in range(len(items)):
1581
- glyphName, set = items[i]
1577
+ for i, (glyphName, set) in enumerate(items):
1582
1578
  items[i] = font.getGlyphID(glyphName), glyphName, set
1583
1579
  items.sort()
1584
1580
  cov = Coverage()
@@ -2279,8 +2275,7 @@ def fixLookupOverFlows(ttf, overflowRecord):
2279
2275
  lookup = lookups[lookupIndex]
2280
2276
  if lookup.LookupType != extType:
2281
2277
  lookup.LookupType = extType
2282
- for si in range(len(lookup.SubTable)):
2283
- subTable = lookup.SubTable[si]
2278
+ for si, subTable in enumerate(lookup.SubTable):
2284
2279
  extSubTableClass = lookupTypes[overflowRecord.tableType][extType]
2285
2280
  extSubTable = extSubTableClass()
2286
2281
  extSubTable.Format = 1
@@ -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()
fontTools/ttLib/ttFont.py CHANGED
@@ -259,9 +259,8 @@ class TTFont(object):
259
259
  "head"
260
260
  ] # make sure 'head' is loaded so the recalculation is actually done
261
261
 
262
- tags = list(self.keys())
263
- if "GlyphOrder" in tags:
264
- tags.remove("GlyphOrder")
262
+ tags = self.keys()
263
+ tags.pop(0) # skip GlyphOrder tag
265
264
  numTables = len(tags)
266
265
  # write to a temporary stream to allow saving to unseekable streams
267
266
  writer = SFNTWriter(
@@ -307,14 +306,9 @@ class TTFont(object):
307
306
  self.disassembleInstructions = disassembleInstructions
308
307
  self.bitmapGlyphDataFormat = bitmapGlyphDataFormat
309
308
  if not tables:
310
- tables = list(self.keys())
311
- if "GlyphOrder" not in tables:
312
- tables = ["GlyphOrder"] + tables
309
+ tables = self.keys()
313
310
  if skipTables:
314
- for tag in skipTables:
315
- if tag in tables:
316
- tables.remove(tag)
317
- numTables = len(tables)
311
+ tables = [tag for tag in tables if tag not in skipTables]
318
312
 
319
313
  if writeVersion:
320
314
  from fontTools import version
@@ -337,8 +331,7 @@ class TTFont(object):
337
331
  else:
338
332
  path, ext = os.path.splitext(writer.filename)
339
333
 
340
- for i in range(numTables):
341
- tag = tables[i]
334
+ for tag in tables:
342
335
  if splitTables:
343
336
  tablePath = path + "." + tagToIdentifier(tag) + ext
344
337
  tableWriter = xmlWriter.XMLWriter(
@@ -608,8 +601,7 @@ class TTFont(object):
608
601
  else:
609
602
  reversecmap = {}
610
603
  useCount = {}
611
- for i in range(numGlyphs):
612
- tempName = glyphOrder[i]
604
+ for i, tempName in enumerate(glyphOrder):
613
605
  if tempName in reversecmap:
614
606
  # If a font maps both U+0041 LATIN CAPITAL LETTER A and
615
607
  # U+0391 GREEK CAPITAL LETTER ALPHA to the same glyph,
@@ -866,8 +858,7 @@ class GlyphOrder(object):
866
858
  "The 'id' attribute is only for humans; " "it is ignored when parsed."
867
859
  )
868
860
  writer.newline()
869
- for i in range(len(glyphOrder)):
870
- glyphName = glyphOrder[i]
861
+ for i, glyphName in enumerate(glyphOrder):
871
862
  writer.simpletag("GlyphID", id=i, name=glyphName)
872
863
  writer.newline()
873
864
 
fontTools/ttLib/woff2.py CHANGED
@@ -394,10 +394,9 @@ class WOFF2Writer(SFNTWriter):
394
394
 
395
395
  def _calcMasterChecksum(self):
396
396
  """Calculate checkSumAdjustment."""
397
- tags = list(self.tables.keys())
398
397
  checksums = []
399
- for i in range(len(tags)):
400
- checksums.append(self.tables[tags[i]].checkSum)
398
+ for tag in self.tables.keys():
399
+ checksums.append(self.tables[tag].checkSum)
401
400
 
402
401
  # Create a SFNT directory for checksum calculation purposes
403
402
  self.searchRange, self.entrySelector, self.rangeShift = getSearchRange(
@@ -642,10 +641,10 @@ woff2OverlapSimpleBitmapFlag = 0x0001
642
641
 
643
642
  def getKnownTagIndex(tag):
644
643
  """Return index of 'tag' in woff2KnownTags list. Return 63 if not found."""
645
- for i in range(len(woff2KnownTags)):
646
- if tag == woff2KnownTags[i]:
647
- return i
648
- return woff2UnknownTagIndex
644
+ try:
645
+ return woff2KnownTags.index(tag)
646
+ except ValueError:
647
+ return woff2UnknownTagIndex
649
648
 
650
649
 
651
650
  class WOFF2DirectoryEntry(DirectoryEntry):
@@ -747,8 +746,8 @@ class WOFF2LocaTable(getTableClass("loca")):
747
746
  "indexFormat is 0 but local offsets not multiples of 2"
748
747
  )
749
748
  locations = array.array("H")
750
- for i in range(len(self.locations)):
751
- locations.append(self.locations[i] // 2)
749
+ for location in self.locations:
750
+ locations.append(location // 2)
752
751
  else:
753
752
  locations = array.array("I", self.locations)
754
753
  if sys.byteorder != "big":
@@ -1026,11 +1025,10 @@ class WOFF2GlyfTable(getTableClass("glyf")):
1026
1025
  lastcomponent = len(glyph.components) - 1
1027
1026
  more = 1
1028
1027
  haveInstructions = 0
1029
- for i in range(len(glyph.components)):
1028
+ for i, component in enumerate(glyph.components):
1030
1029
  if i == lastcomponent:
1031
1030
  haveInstructions = hasattr(glyph, "program")
1032
1031
  more = 0
1033
- component = glyph.components[i]
1034
1032
  self.compositeStream += component.compile(more, haveInstructions, self)
1035
1033
  if haveInstructions:
1036
1034
  self._encodeInstructions(glyph)
@@ -1078,9 +1076,8 @@ class WOFF2GlyfTable(getTableClass("glyf")):
1078
1076
 
1079
1077
  flags = array.array("B")
1080
1078
  triplets = array.array("B")
1081
- for i in range(len(coordinates)):
1079
+ for i, (x, y) in enumerate(coordinates):
1082
1080
  onCurve = glyph.flags[i] & _g_l_y_f.flagOnCurve
1083
- x, y = coordinates[i]
1084
1081
  absX = abs(x)
1085
1082
  absY = abs(y)
1086
1083
  onCurveBit = 0 if onCurve else 128
@@ -32,30 +32,27 @@ Value conversion functions are available for converting
32
32
  - :func:`.convertFontInfoValueForAttributeFromVersion3ToVersion2`
33
33
  """
34
34
 
35
- import os
36
- from copy import deepcopy
37
- from os import fsdecode
35
+ import enum
38
36
  import logging
37
+ import os
39
38
  import zipfile
40
- import enum
41
39
  from collections import OrderedDict
42
- import fs
43
- import fs.base
44
- import fs.subfs
45
- import fs.errors
46
- import fs.copy
47
- import fs.osfs
48
- import fs.zipfs
49
- import fs.tempfs
50
- import fs.tools
40
+ from copy import deepcopy
41
+ from os import fsdecode
42
+
43
+ from fontTools.misc import filesystem as fs
51
44
  from fontTools.misc import plistlib
52
- from fontTools.ufoLib.validators import *
53
- from fontTools.ufoLib.filenames import userNameToFileName
54
45
  from fontTools.ufoLib.converters import convertUFO1OrUFO2KerningToUFO3Kerning
55
46
  from fontTools.ufoLib.errors import UFOLibError
56
- from fontTools.ufoLib.utils import numberTypes, _VersionTupleEnumMixin
47
+ from fontTools.ufoLib.filenames import userNameToFileName
48
+ from fontTools.ufoLib.utils import _VersionTupleEnumMixin, numberTypes
49
+ from fontTools.ufoLib.validators import *
50
+
51
+ # client code can check this to see if the upstream `fs` package is being used
52
+ haveFS = fs._haveFS
57
53
 
58
54
  __all__ = [
55
+ "haveFS",
59
56
  "makeUFOPath",
60
57
  "UFOLibError",
61
58
  "UFOReader",
@@ -184,7 +181,7 @@ class _UFOBaseIO:
184
181
  return
185
182
  self.fs.writebytes(fileName, data)
186
183
  else:
187
- with self.fs.openbin(fileName, mode="w") as fp:
184
+ with self.fs.open(fileName, mode="wb") as fp:
188
185
  try:
189
186
  plistlib.dump(obj, fp)
190
187
  except Exception as e:
@@ -412,7 +409,7 @@ class UFOReader(_UFOBaseIO):
412
409
  path = fsdecode(path)
413
410
  try:
414
411
  if encoding is None:
415
- return self.fs.openbin(path)
412
+ return self.fs.open(path, mode="rb")
416
413
  else:
417
414
  return self.fs.open(path, mode="r", encoding=encoding)
418
415
  except fs.errors.ResourceNotFound:
@@ -818,7 +815,7 @@ class UFOReader(_UFOBaseIO):
818
815
  # systems often have hidden directories
819
816
  continue
820
817
  if validate:
821
- with imagesFS.openbin(path.name) as fp:
818
+ with imagesFS.open(path.name, "rb") as fp:
822
819
  valid, error = pngValidator(fileObj=fp)
823
820
  if valid:
824
821
  result.append(path.name)
@@ -984,18 +981,16 @@ class UFOWriter(UFOReader):
984
981
  % len(rootDirs)
985
982
  )
986
983
  else:
987
- # 'ClosingSubFS' ensures that the parent filesystem is closed
988
- # when its root subdirectory is closed
989
- self.fs = parentFS.opendir(
990
- rootDirs[0], factory=fs.subfs.ClosingSubFS
991
- )
984
+ rootDir = rootDirs[0]
992
985
  else:
993
986
  # if the output zip file didn't exist, we create the root folder;
994
987
  # we name it the same as input 'path', but with '.ufo' extension
995
988
  rootDir = os.path.splitext(os.path.basename(path))[0] + ".ufo"
996
989
  parentFS = fs.zipfs.ZipFS(path, write=True, encoding="utf-8")
997
990
  parentFS.makedir(rootDir)
998
- self.fs = parentFS.opendir(rootDir, factory=fs.subfs.ClosingSubFS)
991
+ # 'ClosingSubFS' ensures that the parent filesystem is closed
992
+ # when its root subdirectory is closed
993
+ self.fs = parentFS.opendir(rootDir, factory=fs.subfs.ClosingSubFS)
999
994
  else:
1000
995
  self.fs = fs.osfs.OSFS(path, create=True)
1001
996
  self._fileStructure = structure
@@ -3,7 +3,7 @@ Generic module for reading and writing the .glif format.
3
3
 
4
4
  More info about the .glif format (GLyphInterchangeFormat) can be found here:
5
5
 
6
- http://unifiedfontobject.org
6
+ http://unifiedfontobject.org
7
7
 
8
8
  The main class in this module is :class:`GlyphSet`. It manages a set of .glif files
9
9
  in a folder. It offers two ways to read glyph data, and one way to write
@@ -12,33 +12,28 @@ glyph data. See the class doc string for details.
12
12
 
13
13
  from __future__ import annotations
14
14
 
15
- import logging
16
15
  import enum
17
- from warnings import warn
16
+ import logging
18
17
  from collections import OrderedDict
19
- import fs
20
- import fs.base
21
- import fs.errors
22
- import fs.osfs
23
- import fs.path
18
+ from warnings import warn
19
+
20
+ import fontTools.misc.filesystem as fs
21
+ from fontTools.misc import etree, plistlib
24
22
  from fontTools.misc.textTools import tobytes
25
- from fontTools.misc import plistlib
26
23
  from fontTools.pens.pointPen import AbstractPointPen, PointToSegmentPen
24
+ from fontTools.ufoLib import UFOFormatVersion, _UFOBaseIO
27
25
  from fontTools.ufoLib.errors import GlifLibError
28
26
  from fontTools.ufoLib.filenames import userNameToFileName
27
+ from fontTools.ufoLib.utils import _VersionTupleEnumMixin, numberTypes
29
28
  from fontTools.ufoLib.validators import (
30
- genericTypeValidator,
29
+ anchorsValidator,
31
30
  colorValidator,
31
+ genericTypeValidator,
32
+ glyphLibValidator,
32
33
  guidelinesValidator,
33
- anchorsValidator,
34
34
  identifierValidator,
35
35
  imageValidator,
36
- glyphLibValidator,
37
36
  )
38
- from fontTools.misc import etree
39
- from fontTools.ufoLib import _UFOBaseIO, UFOFormatVersion
40
- from fontTools.ufoLib.utils import numberTypes, _VersionTupleEnumMixin
41
-
42
37
 
43
38
  __all__ = [
44
39
  "GlyphSet",
@@ -206,7 +201,7 @@ class GlyphSet(_UFOBaseIO):
206
201
  # 'dirName' is kept for backward compatibility only, but it's DEPRECATED
207
202
  # as it's not guaranteed that it maps to an existing OSFS directory.
208
203
  # Client could use the FS api via the `self.fs` attribute instead.
209
- self.dirName = fs.path.parts(path)[-1]
204
+ self.dirName = fs.path.basename(path)
210
205
  self.fs = filesystem
211
206
  # if glyphSet contains no 'contents.plist', we consider it empty
212
207
  self._havePreviousFile = filesystem.exists(CONTENTS_FILENAME)
@@ -1,14 +1,12 @@
1
1
  """Various low level data validators."""
2
2
 
3
3
  import calendar
4
+ from collections.abc import Mapping
4
5
  from io import open
5
- import fs.base
6
- import fs.osfs
7
6
 
8
- from collections.abc import Mapping
7
+ import fontTools.misc.filesystem as fs
9
8
  from fontTools.ufoLib.utils import numberTypes
10
9
 
11
-
12
10
  # -------
13
11
  # Generic
14
12
  # -------
@@ -197,6 +197,8 @@ RTL_SCRIPTS = {
197
197
  "Yezi", # Yezidi
198
198
  # Unicode-14.0 additions
199
199
  "Ougr", # Old Uyghur
200
+ # Unicode-16.0 additions
201
+ "Gara", # Garay
200
202
  }
201
203
 
202
204
 
@@ -95,6 +95,14 @@ def addFeatureVariations(font, conditionalSubstitutions, featureTag="rvrn"):
95
95
 
96
96
  addFeatureVariationsRaw(font, font["GSUB"].table, conditionsAndLookups, featureTags)
97
97
 
98
+ # Update OS/2.usMaxContext in case the font didn't have features before, but
99
+ # does now, if the OS/2 table exists. The table may be required, but
100
+ # fontTools needs to be able to deal with non-standard fonts. Since feature
101
+ # variations are always 1:1 mappings, we can set the value to at least 1
102
+ # instead of recomputing it with `otlLib.maxContextCalc.maxCtxFont()`.
103
+ if (os2 := font.get("OS/2")) is not None:
104
+ os2.usMaxContext = max(1, os2.usMaxContext)
105
+
98
106
 
99
107
  def _existingVariableFeatures(table):
100
108
  existingFeatureVarsTags = set()
fontTools/varLib/hvar.py CHANGED
@@ -56,7 +56,7 @@ def add_HVAR(font):
56
56
  def add_VVAR(font):
57
57
  if "VVAR" in font:
58
58
  del font["VVAR"]
59
- getAdvanceMetrics = partial(_get_advance_metrics, font, axisTags, HVAR_FIELDS)
59
+ getAdvanceMetrics = partial(_get_advance_metrics, font, axisTags, VVAR_FIELDS)
60
60
  axisTags = [axis.axisTag for axis in font["fvar"].axes]
61
61
  _add_VHVAR(font, axisTags, VVAR_FIELDS, getAdvanceMetrics)
62
62