fonttools 4.59.0__cp39-cp39-win_amd64.whl → 4.59.2__cp39-cp39-win_amd64.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 (32) 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.cp39-win_amd64.pyd +0 -0
  5. fontTools/cu2qu/cu2qu.py +17 -2
  6. fontTools/feaLib/builder.py +15 -4
  7. fontTools/feaLib/lexer.cp39-win_amd64.pyd +0 -0
  8. fontTools/feaLib/parser.py +11 -1
  9. fontTools/feaLib/variableScalar.py +6 -1
  10. fontTools/misc/bezierTools.cp39-win_amd64.pyd +0 -0
  11. fontTools/misc/psCharStrings.py +17 -2
  12. fontTools/misc/textTools.py +4 -2
  13. fontTools/pens/momentsPen.cp39-win_amd64.pyd +0 -0
  14. fontTools/qu2cu/qu2cu.cp39-win_amd64.pyd +0 -0
  15. fontTools/subset/__init__.py +1 -0
  16. fontTools/ttLib/tables/_a_v_a_r.py +4 -2
  17. fontTools/ttLib/tables/_g_v_a_r.py +6 -3
  18. fontTools/ttLib/tables/_h_m_t_x.py +7 -3
  19. fontTools/ttLib/tables/_n_a_m_e.py +11 -6
  20. fontTools/varLib/__init__.py +80 -1
  21. fontTools/varLib/featureVars.py +8 -0
  22. fontTools/varLib/instancer/__init__.py +120 -22
  23. fontTools/varLib/iup.cp39-win_amd64.pyd +0 -0
  24. fontTools/varLib/mutator.py +11 -0
  25. {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/METADATA +38 -10
  26. {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/RECORD +32 -32
  27. {fonttools-4.59.0.data → fonttools-4.59.2.data}/data/share/man/man1/ttx.1 +0 -0
  28. {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/WHEEL +0 -0
  29. {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/entry_points.txt +0 -0
  30. {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/licenses/LICENSE +0 -0
  31. {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/licenses/LICENSE.external +0 -0
  32. {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/top_level.txt +0 -0
fontTools/__init__.py CHANGED
@@ -3,6 +3,6 @@ from fontTools.misc.loggingTools import configLogger
3
3
 
4
4
  log = logging.getLogger(__name__)
5
5
 
6
- version = __version__ = "4.59.0"
6
+ version = __version__ = "4.59.2"
7
7
 
8
8
  __all__ = ["version", "log", "configLogger"]
@@ -2,13 +2,17 @@
2
2
 
3
3
  from fontTools.ttLib import TTFont, newTable
4
4
  from fontTools.misc.cliTools import makeOutputFileName
5
+ from fontTools.misc.psCharStrings import T2StackUseExtractor
5
6
  from fontTools.cffLib import (
6
7
  TopDictIndex,
7
8
  buildOrder,
8
9
  buildDefaults,
9
10
  topDictOperators,
10
11
  privateDictOperators,
12
+ FDSelect,
11
13
  )
14
+ from .transforms import desubroutinizeCharString
15
+ from .specializer import specializeProgram
12
16
  from .width import optimizeWidths
13
17
  from collections import defaultdict
14
18
  import logging
@@ -27,7 +31,7 @@ def _convertCFF2ToCFF(cff, otFont):
27
31
  The CFF2 font cannot be variable. (TODO Accept those and convert to the
28
32
  default instance?)
29
33
 
30
- This assumes a decompiled CFF table. (i.e. that the object has been
34
+ This assumes a decompiled CFF2 table. (i.e. that the object has been
31
35
  filled via :meth:`decompile` and e.g. not loaded from XML.)"""
32
36
 
33
37
  cff.major = 1
@@ -51,9 +55,14 @@ def _convertCFF2ToCFF(cff, otFont):
51
55
  if hasattr(topDict, key):
52
56
  delattr(topDict, key)
53
57
 
54
- fdArray = topDict.FDArray
55
58
  charStrings = topDict.CharStrings
56
59
 
60
+ fdArray = topDict.FDArray
61
+ if not hasattr(topDict, "FDSelect"):
62
+ # FDSelect is optional in CFF2, but required in CFF.
63
+ fdSelect = topDict.FDSelect = FDSelect()
64
+ fdSelect.gidArray = [0] * len(charStrings.charStrings)
65
+
57
66
  defaults = buildDefaults(privateDictOperators)
58
67
  order = buildOrder(privateDictOperators)
59
68
  for fd in fdArray:
@@ -69,6 +78,7 @@ def _convertCFF2ToCFF(cff, otFont):
69
78
  if hasattr(privateDict, key):
70
79
  delattr(privateDict, key)
71
80
 
81
+ # Add ending operators
72
82
  for cs in charStrings.values():
73
83
  cs.decompile()
74
84
  cs.program.append("endchar")
@@ -100,23 +110,43 @@ def _convertCFF2ToCFF(cff, otFont):
100
110
  if width != private.defaultWidthX:
101
111
  cs.program.insert(0, width - private.nominalWidthX)
102
112
 
113
+ # Handle stack use since stack-depth is lower in CFF than in CFF2.
114
+ for glyphName in charStrings.keys():
115
+ cs, fdIndex = charStrings.getItemAndSelector(glyphName)
116
+ if fdIndex is None:
117
+ fdIndex = 0
118
+ private = fdArray[fdIndex].Private
119
+ extractor = T2StackUseExtractor(
120
+ getattr(private, "Subrs", []), cff.GlobalSubrs, private=private
121
+ )
122
+ stackUse = extractor.execute(cs)
123
+ if stackUse > 48: # CFF stack depth is 48
124
+ desubroutinizeCharString(cs)
125
+ cs.program = specializeProgram(cs.program)
126
+
127
+ # Unused subroutines are still in CFF2 (ie. lacking 'return' operator)
128
+ # because they were not decompiled when we added the 'return'.
129
+ # Moreover, some used subroutines may have become unused after the
130
+ # stack-use fixup. So we remove all unused subroutines now.
131
+ cff.remove_unused_subroutines()
132
+
103
133
  mapping = {
104
- name: ("cid" + str(n) if n else ".notdef")
134
+ name: ("cid" + str(n).zfill(5) if n else ".notdef")
105
135
  for n, name in enumerate(topDict.charset)
106
136
  }
107
137
  topDict.charset = [
108
- "cid" + str(n) if n else ".notdef" for n in range(len(topDict.charset))
138
+ "cid" + str(n).zfill(5) if n else ".notdef" for n in range(len(topDict.charset))
109
139
  ]
110
140
  charStrings.charStrings = {
111
141
  mapping[name]: v for name, v in charStrings.charStrings.items()
112
142
  }
113
143
 
114
- # I'm not sure why the following is *not* necessary. And it breaks
115
- # the output if I add it.
116
- # topDict.ROS = ("Adobe", "Identity", 0)
144
+ topDict.ROS = ("Adobe", "Identity", 0)
117
145
 
118
146
 
119
147
  def convertCFF2ToCFF(font, *, updatePostTable=True):
148
+ if "CFF2" not in font:
149
+ raise ValueError("Input font does not contain a CFF2 table.")
120
150
  cff = font["CFF2"].cff
121
151
  _convertCFF2ToCFF(cff, font)
122
152
  del font["CFF2"]
@@ -131,7 +161,7 @@ def convertCFF2ToCFF(font, *, updatePostTable=True):
131
161
 
132
162
 
133
163
  def main(args=None):
134
- """Convert CFF OTF font to CFF2 OTF font"""
164
+ """Convert CFF2 OTF font to CFF OTF font"""
135
165
  if args is None:
136
166
  import sys
137
167
 
@@ -140,8 +170,8 @@ def main(args=None):
140
170
  import argparse
141
171
 
142
172
  parser = argparse.ArgumentParser(
143
- "fonttools cffLib.CFFToCFF2",
144
- description="Upgrade a CFF font to CFF2.",
173
+ "fonttools cffLib.CFF2ToCFF",
174
+ description="Convert a non-variable CFF2 font to CFF.",
145
175
  )
146
176
  parser.add_argument(
147
177
  "input", metavar="INPUT.ttf", help="Input OTF file with CFF table."
@@ -94,17 +94,22 @@ class _DesubroutinizingT2Decompiler(SimpleT2Decompiler):
94
94
  cs._patches.append((index, subr._desubroutinized))
95
95
 
96
96
 
97
+ def desubroutinizeCharString(cs):
98
+ """Desubroutinize a charstring in-place."""
99
+ cs.decompile()
100
+ subrs = getattr(cs.private, "Subrs", [])
101
+ decompiler = _DesubroutinizingT2Decompiler(subrs, cs.globalSubrs, cs.private)
102
+ decompiler.execute(cs)
103
+ cs.program = cs._desubroutinized
104
+ del cs._desubroutinized
105
+
106
+
97
107
  def desubroutinize(cff):
98
108
  for fontName in cff.fontNames:
99
109
  font = cff[fontName]
100
110
  cs = font.CharStrings
101
111
  for c in cs.values():
102
- c.decompile()
103
- subrs = getattr(c.private, "Subrs", [])
104
- decompiler = _DesubroutinizingT2Decompiler(subrs, c.globalSubrs, c.private)
105
- decompiler.execute(c)
106
- c.program = c._desubroutinized
107
- del c._desubroutinized
112
+ desubroutinizeCharString(c)
108
113
  # Delete all the local subrs
109
114
  if hasattr(font, "FDArray"):
110
115
  for fd in font.FDArray:
Binary file
fontTools/cu2qu/cu2qu.py CHANGED
@@ -37,7 +37,7 @@ NAN = float("NaN")
37
37
  @cython.cfunc
38
38
  @cython.inline
39
39
  @cython.returns(cython.double)
40
- @cython.locals(v1=cython.complex, v2=cython.complex)
40
+ @cython.locals(v1=cython.complex, v2=cython.complex, result=cython.double)
41
41
  def dot(v1, v2):
42
42
  """Return the dot product of two vectors.
43
43
 
@@ -48,7 +48,16 @@ def dot(v1, v2):
48
48
  Returns:
49
49
  double: Dot product.
50
50
  """
51
- return (v1 * v2.conjugate()).real
51
+ result = (v1 * v2.conjugate()).real
52
+ # When vectors are perpendicular (i.e. dot product is 0), the above expression may
53
+ # yield slightly different results when running in pure Python vs C/Cython,
54
+ # both of which are correct within IEEE-754 floating-point precision.
55
+ # It's probably due to the different order of operations and roundings in each
56
+ # implementation. Because we are using the result in a denominator and catching
57
+ # ZeroDivisionError (see `calc_intersect`), it's best to normalize the result here.
58
+ if abs(result) < 1e-15:
59
+ result = 0.0
60
+ return result
52
61
 
53
62
 
54
63
  @cython.cfunc
@@ -273,6 +282,12 @@ def calc_intersect(a, b, c, d):
273
282
  try:
274
283
  h = dot(p, a - c) / dot(p, cd)
275
284
  except ZeroDivisionError:
285
+ # if 3 or 4 points are equal, we do have an intersection despite the zero-div:
286
+ # return one of the off-curves so that the algorithm can attempt a one-curve
287
+ # solution if it's within tolerance:
288
+ # https://github.com/linebender/kurbo/pull/484
289
+ if b == c and (a == b or c == d):
290
+ return b
276
291
  return complex(NAN, NAN)
277
292
  return c + cd * h
278
293
 
@@ -32,6 +32,7 @@ from fontTools.otlLib.builder import (
32
32
  AnySubstBuilder,
33
33
  )
34
34
  from fontTools.otlLib.error import OpenTypeLibError
35
+ from fontTools.varLib.errors import VarLibError
35
36
  from fontTools.varLib.varStore import OnlineVarStoreBuilder
36
37
  from fontTools.varLib.builder import buildVarDevTable
37
38
  from fontTools.varLib.featureVars import addFeatureVariationsRaw
@@ -926,6 +927,11 @@ class Builder(object):
926
927
  l.lookup_index for l in lookups if l.lookup_index is not None
927
928
  )
928
929
  )
930
+ # order doesn't matter, but lookup_indices preserves it.
931
+ # We want to combine identical sets of lookups (order doesn't matter)
932
+ # but also respect the order provided by the user (although there's
933
+ # a reasonable argument to just sort and dedupe, which fontc does)
934
+ lookup_key = frozenset(lookup_indices)
929
935
 
930
936
  size_feature = tag == "GPOS" and feature_tag == "size"
931
937
  force_feature = self.any_feature_variations(feature_tag, tag)
@@ -943,7 +949,7 @@ class Builder(object):
943
949
  "stash debug information. See fonttools#2065."
944
950
  )
945
951
 
946
- feature_key = (feature_tag, lookup_indices)
952
+ feature_key = (feature_tag, lookup_key)
947
953
  feature_index = feature_indices.get(feature_key)
948
954
  if feature_index is None:
949
955
  feature_index = len(table.FeatureList.FeatureRecord)
@@ -1723,9 +1729,14 @@ class Builder(object):
1723
1729
  if not varscalar.does_vary:
1724
1730
  return varscalar.default, None
1725
1731
 
1726
- default, index = varscalar.add_to_variation_store(
1727
- self.varstorebuilder, self.model_cache, self.font.get("avar")
1728
- )
1732
+ try:
1733
+ default, index = varscalar.add_to_variation_store(
1734
+ self.varstorebuilder, self.model_cache, self.font.get("avar")
1735
+ )
1736
+ except VarLibError as e:
1737
+ raise FeatureLibError(
1738
+ "Failed to compute deltas for variable scalar", location
1739
+ ) from e
1729
1740
 
1730
1741
  device = None
1731
1742
  if index is not None and index != 0xFFFFFFFF:
Binary file
@@ -2198,7 +2198,7 @@ class Parser(object):
2198
2198
  raise FeatureLibError(
2199
2199
  "Expected an equals sign", self.cur_token_location_
2200
2200
  )
2201
- value = self.expect_number_()
2201
+ value = self.expect_integer_or_float_()
2202
2202
  location[axis] = value
2203
2203
  if self.next_token_type_ is Lexer.NAME and self.next_token_[0] == ":":
2204
2204
  # Lexer has just read the value as a glyph name. We'll correct it later
@@ -2230,6 +2230,16 @@ class Parser(object):
2230
2230
  "Expected a floating-point number", self.cur_token_location_
2231
2231
  )
2232
2232
 
2233
+ def expect_integer_or_float_(self):
2234
+ if self.next_token_type_ == Lexer.FLOAT:
2235
+ return self.expect_float_()
2236
+ elif self.next_token_type_ is Lexer.NUMBER:
2237
+ return self.expect_number_()
2238
+ else:
2239
+ raise FeatureLibError(
2240
+ "Expected an integer or floating-point number", self.cur_token_location_
2241
+ )
2242
+
2233
2243
  def expect_decipoint_(self):
2234
2244
  if self.next_token_type_ == Lexer.FLOAT:
2235
2245
  return self.expect_float_()
@@ -17,7 +17,12 @@ class VariableScalar:
17
17
  def __repr__(self):
18
18
  items = []
19
19
  for location, value in self.values.items():
20
- loc = ",".join(["%s=%i" % (ax, loc) for ax, loc in location])
20
+ loc = ",".join(
21
+ [
22
+ f"{ax}={int(coord) if float(coord).is_integer() else coord}"
23
+ for ax, coord in location
24
+ ]
25
+ )
21
26
  items.append("%s:%i" % (loc, value))
22
27
  return "(" + (" ".join(items)) + ")"
23
28
 
@@ -338,7 +338,7 @@ class SimpleT2Decompiler(object):
338
338
  self.numRegions = 0
339
339
  self.vsIndex = 0
340
340
 
341
- def execute(self, charString):
341
+ def execute(self, charString, *, pushToStack=None):
342
342
  self.callingStack.append(charString)
343
343
  needsDecompilation = charString.needsDecompilation()
344
344
  if needsDecompilation:
@@ -346,7 +346,8 @@ class SimpleT2Decompiler(object):
346
346
  pushToProgram = program.append
347
347
  else:
348
348
  pushToProgram = lambda x: None
349
- pushToStack = self.operandStack.append
349
+ if pushToStack is None:
350
+ pushToStack = self.operandStack.append
350
351
  index = 0
351
352
  while True:
352
353
  token, isOperator, index = charString.getToken(index)
@@ -551,6 +552,20 @@ t1Operators = [
551
552
  ]
552
553
 
553
554
 
555
+ class T2StackUseExtractor(SimpleT2Decompiler):
556
+
557
+ def execute(self, charString):
558
+ maxStackUse = 0
559
+
560
+ def pushToStack(value):
561
+ nonlocal maxStackUse
562
+ self.operandStack.append(value)
563
+ maxStackUse = max(maxStackUse, len(self.operandStack))
564
+
565
+ super().execute(charString, pushToStack=pushToStack)
566
+ return maxStackUse
567
+
568
+
554
569
  class T2WidthExtractor(SimpleT2Decompiler):
555
570
  def __init__(
556
571
  self,
@@ -1,5 +1,7 @@
1
1
  """fontTools.misc.textTools.py -- miscellaneous routines."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import ast
4
6
  import string
5
7
 
@@ -118,14 +120,14 @@ def pad(data, size):
118
120
  return data
119
121
 
120
122
 
121
- def tostr(s, encoding="ascii", errors="strict"):
123
+ def tostr(s: str | bytes, encoding: str = "ascii", errors: str = "strict") -> str:
122
124
  if not isinstance(s, str):
123
125
  return s.decode(encoding, errors)
124
126
  else:
125
127
  return s
126
128
 
127
129
 
128
- def tobytes(s, encoding="ascii", errors="strict"):
130
+ def tobytes(s: str | bytes, encoding: str = "ascii", errors: str = "strict") -> bytes:
129
131
  if isinstance(s, str):
130
132
  return s.encode(encoding, errors)
131
133
  else:
Binary file
@@ -1530,6 +1530,7 @@ def subset_glyphs(self, s):
1530
1530
  if self.MarkFilteringSet not in s.used_mark_sets:
1531
1531
  self.MarkFilteringSet = None
1532
1532
  self.LookupFlag &= ~0x10
1533
+ self.LookupFlag |= 0x8
1533
1534
  else:
1534
1535
  self.MarkFilteringSet = s.used_mark_sets.index(self.MarkFilteringSet)
1535
1536
  return bool(self.SubTableCount)
@@ -143,7 +143,7 @@ class table__a_v_a_r(BaseTTXConverter):
143
143
  else:
144
144
  super().fromXML(name, attrs, content, ttFont)
145
145
 
146
- def renormalizeLocation(self, location, font):
146
+ def renormalizeLocation(self, location, font, dropZeroes=True):
147
147
 
148
148
  majorVersion = getattr(self, "majorVersion", 1)
149
149
 
@@ -185,7 +185,9 @@ class table__a_v_a_r(BaseTTXConverter):
185
185
  out.append(v)
186
186
 
187
187
  mappedLocation = {
188
- axis.axisTag: fi2fl(v, 14) for v, axis in zip(out, axes) if v != 0
188
+ axis.axisTag: fi2fl(v, 14)
189
+ for v, axis in zip(out, axes)
190
+ if v != 0 or not dropZeroes
189
191
  }
190
192
 
191
193
  return mappedLocation
@@ -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
  )
@@ -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
 
@@ -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()
@@ -30,7 +30,11 @@ from fontTools.misc.fixedTools import floatToFixed as fl2fi
30
30
  from fontTools.misc.textTools import Tag, tostr
31
31
  from fontTools.ttLib import TTFont, newTable
32
32
  from fontTools.ttLib.tables._f_v_a_r import Axis, NamedInstance
33
- from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates, dropImpliedOnCurvePoints
33
+ from fontTools.ttLib.tables._g_l_y_f import (
34
+ GlyphCoordinates,
35
+ dropImpliedOnCurvePoints,
36
+ USE_MY_METRICS,
37
+ )
34
38
  from fontTools.ttLib.tables.ttProgram import Program
35
39
  from fontTools.ttLib.tables.TupleVariation import TupleVariation
36
40
  from fontTools.ttLib.tables import otTables as ot
@@ -489,6 +493,77 @@ def _merge_TTHinting(font, masterModel, master_ttfs):
489
493
  cvar.variations = variations
490
494
 
491
495
 
496
+ def _has_inconsistent_use_my_metrics_flag(
497
+ master_glyf, glyph_name, flagged_components, expected_num_components
498
+ ) -> bool:
499
+ master_glyph = master_glyf.get(glyph_name)
500
+ # 'sparse' glyph master doesn't contribute. Besides when components don't match
501
+ # the VF build is going to fail anyway, so be lenient here.
502
+ if (
503
+ master_glyph is not None
504
+ and master_glyph.isComposite()
505
+ and len(master_glyph.components) == expected_num_components
506
+ ):
507
+ for i, base_glyph in flagged_components:
508
+ comp = master_glyph.components[i]
509
+ if comp.glyphName != base_glyph:
510
+ break
511
+ if not (comp.flags & USE_MY_METRICS):
512
+ return True
513
+ return False
514
+
515
+
516
+ def _unset_inconsistent_use_my_metrics_flags(vf, master_fonts):
517
+ """Clear USE_MY_METRICS on composite components if inconsistent across masters.
518
+
519
+ If a composite glyph's component has USE_MY_METRICS set differently among
520
+ the masters, the flag is removed from the variable font's glyf table so that
521
+ advance widths are not determined by that single component's phantom points.
522
+ """
523
+ glyf = vf["glyf"]
524
+ master_glyfs = [m["glyf"] for m in master_fonts if "glyf" in m]
525
+ if not master_glyfs:
526
+ # Should not happen: at least the base master (as copied into vf) has glyf
527
+ return
528
+
529
+ for glyph_name in glyf.keys():
530
+ glyph = glyf[glyph_name]
531
+ if not glyph.isComposite():
532
+ continue
533
+
534
+ # collect indices of component(s) that carry the USE_MY_METRICS flag.
535
+ # This is supposed to be 1 component per composite, but you never know.
536
+ flagged_components = [
537
+ (i, comp.glyphName)
538
+ for i, comp in enumerate(glyph.components)
539
+ if (comp.flags & USE_MY_METRICS)
540
+ ]
541
+ if not flagged_components:
542
+ # Nothing to fix
543
+ continue
544
+
545
+ # Verify that for all master glyf tables that contribute this glyph, the
546
+ # corresponding component (same glyphName and index) also carries USE_MY_METRICS
547
+ # and unset the flag if not.
548
+ expected_num_components = len(glyph.components)
549
+ if any(
550
+ _has_inconsistent_use_my_metrics_flag(
551
+ master_glyf, glyph_name, flagged_components, expected_num_components
552
+ )
553
+ for master_glyf in master_glyfs
554
+ ):
555
+ comp_names = [name for _, name in flagged_components]
556
+ log.info(
557
+ "Composite glyph '%s' has inconsistent USE_MY_METRICS flags across "
558
+ "masters; clearing the flag on component%s %s",
559
+ glyph_name,
560
+ "s" if len(comp_names) > 1 else "",
561
+ comp_names if len(comp_names) > 1 else comp_names[0],
562
+ )
563
+ for i, _ in flagged_components:
564
+ glyph.components[i].flags &= ~USE_MY_METRICS
565
+
566
+
492
567
  _MetricsFields = namedtuple(
493
568
  "_MetricsFields",
494
569
  [
@@ -1205,6 +1280,10 @@ def build(
1205
1280
  if "DSIG" in vf:
1206
1281
  del vf["DSIG"]
1207
1282
 
1283
+ # Clear USE_MY_METRICS composite flags if set inconsistently across masters.
1284
+ if "glyf" in vf:
1285
+ _unset_inconsistent_use_my_metrics_flags(vf, master_fonts)
1286
+
1208
1287
  # TODO append masters as named-instances as well; needs .designspace change.
1209
1288
  fvar = _add_fvar(vf, ds.axes, ds.instances)
1210
1289
  if "STAT" not in exclude:
@@ -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()
@@ -1,4 +1,4 @@
1
- """ Partially instantiate a variable font.
1
+ """Partially instantiate a variable font.
2
2
 
3
3
  The module exports an `instantiateVariableFont` function and CLI that allow to
4
4
  create full instances (i.e. static fonts) from variable fonts, as well as "partial"
@@ -36,7 +36,7 @@ If the input location specifies all the axes, the resulting instance is no longe
36
36
  'variable' (same as using fontools varLib.mutator):
37
37
  .. code-block:: pycon
38
38
 
39
- >>>
39
+ >>>
40
40
  >> instance = instancer.instantiateVariableFont(
41
41
  ... varfont, {"wght": 700, "wdth": 67.5}
42
42
  ... )
@@ -56,8 +56,10 @@ From the console script, this is equivalent to passing `wght=drop` as input.
56
56
 
57
57
  This module is similar to fontTools.varLib.mutator, which it's intended to supersede.
58
58
  Note that, unlike varLib.mutator, when an axis is not mentioned in the input
59
- location, the varLib.instancer will keep the axis and the corresponding deltas,
60
- whereas mutator implicitly drops the axis at its default coordinate.
59
+ location, by default the varLib.instancer will keep the axis and the corresponding
60
+ deltas, whereas mutator implicitly drops the axis at its default coordinate.
61
+ To obtain the same behavior as mutator, pass the `static=True` parameter or
62
+ the `--static` CLI option.
61
63
 
62
64
  The module supports all the following "levels" of instancing, which can of
63
65
  course be combined:
@@ -72,7 +74,7 @@ L1
72
74
  L2
73
75
  dropping one or more axes while pinning them at non-default locations;
74
76
  .. code-block:: pycon
75
-
77
+
76
78
  >>>
77
79
  >> font = instancer.instantiateVariableFont(varfont, {"wght": 700})
78
80
 
@@ -81,22 +83,18 @@ L3
81
83
  a new minimum or maximum, potentially -- though not necessarily -- dropping
82
84
  entire regions of variations that fall completely outside this new range.
83
85
  .. code-block:: pycon
84
-
86
+
85
87
  >>>
86
88
  >> font = instancer.instantiateVariableFont(varfont, {"wght": (100, 300)})
87
89
 
88
90
  L4
89
91
  moving the default location of an axis, by specifying (min,defalt,max) values:
90
92
  .. code-block:: pycon
91
-
93
+
92
94
  >>>
93
95
  >> font = instancer.instantiateVariableFont(varfont, {"wght": (100, 300, 700)})
94
96
 
95
- Currently only TrueType-flavored variable fonts (i.e. containing 'glyf' table)
96
- are supported, but support for CFF2 variable fonts will be added soon.
97
-
98
- The discussion and implementation of these features are tracked at
99
- https://github.com/fonttools/fonttools/issues/1537
97
+ Both TrueType-flavored (glyf+gvar) variable and CFF2 variable fonts are supported.
100
98
  """
101
99
 
102
100
  from fontTools.misc.fixedTools import (
@@ -120,6 +118,7 @@ from fontTools.cffLib.specializer import (
120
118
  specializeCommands,
121
119
  generalizeCommands,
122
120
  )
121
+ from fontTools.cffLib.CFF2ToCFF import convertCFF2ToCFF
123
122
  from fontTools.varLib import builder
124
123
  from fontTools.varLib.mvar import MVAR_ENTRIES
125
124
  from fontTools.varLib.merger import MutatorMerger
@@ -136,6 +135,7 @@ from enum import IntEnum
136
135
  import logging
137
136
  import os
138
137
  import re
138
+ import io
139
139
  from typing import Dict, Iterable, Mapping, Optional, Sequence, Tuple, Union
140
140
  import warnings
141
141
 
@@ -433,7 +433,27 @@ class AxisLimits(_BaseAxisLimits):
433
433
 
434
434
  avarSegments = {}
435
435
  if usingAvar and "avar" in varfont:
436
- avarSegments = varfont["avar"].segments
436
+ avar = varfont["avar"]
437
+ avarSegments = avar.segments
438
+
439
+ if getattr(avar, "majorVersion", 1) >= 2 and avar.table.VarStore:
440
+ pinnedAxes = set(self.pinnedLocation())
441
+ if not pinnedAxes.issuperset(avarSegments):
442
+ raise NotImplementedError(
443
+ "Partial-instancing avar2 table is not supported"
444
+ )
445
+
446
+ # TODO: Merge this with the main codepath.
447
+
448
+ # Full instancing of avar2 font. Use avar table to normalize location and return.
449
+ location = self.pinnedLocation()
450
+ location = {
451
+ tag: normalize(value, axes[tag], avarSegments.get(tag, None))
452
+ for tag, value in location.items()
453
+ }
454
+ return NormalizedAxisLimits(
455
+ **avar.renormalizeLocation(location, varfont, dropZeroes=False)
456
+ )
437
457
 
438
458
  normalizedLimits = {}
439
459
 
@@ -643,7 +663,11 @@ def instantiateCFF2(
643
663
  # the Private dicts.
644
664
  #
645
665
  # Then prune unused things and possibly drop the VarStore if it's empty.
646
- # In which case, downgrade to CFF table if requested.
666
+ #
667
+ # If the downgrade parameter is True, no actual downgrading is done, but
668
+ # the function returns True if the VarStore was empty after instantiation,
669
+ # and hence a downgrade to CFF is possible. In all other cases it returns
670
+ # False.
647
671
 
648
672
  log.info("Instantiating CFF2 table")
649
673
 
@@ -882,9 +906,9 @@ def instantiateCFF2(
882
906
  del private.vstore
883
907
 
884
908
  if downgrade:
885
- from fontTools.cffLib.CFF2ToCFF import convertCFF2ToCFF
909
+ return True
886
910
 
887
- convertCFF2ToCFF(varfont)
911
+ return False
888
912
 
889
913
 
890
914
  def _instantiateGvarGlyph(
@@ -1116,7 +1140,8 @@ def _instantiateVHVAR(varfont, axisLimits, tableFields, *, round=round):
1116
1140
  varIdx = advMapping.mapping[glyphName]
1117
1141
  else:
1118
1142
  varIdx = varfont.getGlyphID(glyphName)
1119
- metrics[glyphName] = (advanceWidth + round(defaultDeltas[varIdx]), sb)
1143
+ delta = round(defaultDeltas[varIdx])
1144
+ metrics[glyphName] = (max(0, advanceWidth + delta), sb)
1120
1145
 
1121
1146
  if (
1122
1147
  tableTag == "VVAR"
@@ -1377,12 +1402,55 @@ def _isValidAvarSegmentMap(axisTag, segmentMap):
1377
1402
  return True
1378
1403
 
1379
1404
 
1405
+ def downgradeCFF2ToCFF(varfont):
1406
+ # Save these properties
1407
+ recalcTimestamp = varfont.recalcTimestamp
1408
+ recalcBBoxes = varfont.recalcBBoxes
1409
+
1410
+ # Disable them
1411
+ varfont.recalcTimestamp = False
1412
+ varfont.recalcBBoxes = False
1413
+
1414
+ # Save to memory, reload, downgrade and save again, reload.
1415
+ # We do this dance because the convertCFF2ToCFF changes glyph
1416
+ # names, so following save would fail if any other table was
1417
+ # loaded and referencing glyph names.
1418
+ #
1419
+ # The second save+load is unfortunate but also necessary.
1420
+
1421
+ stream = io.BytesIO()
1422
+ log.info("Saving CFF2 font to memory for downgrade")
1423
+ varfont.save(stream)
1424
+ stream.seek(0)
1425
+ varfont = TTFont(stream, recalcTimestamp=False, recalcBBoxes=False)
1426
+
1427
+ convertCFF2ToCFF(varfont)
1428
+
1429
+ stream = io.BytesIO()
1430
+ log.info("Saving downgraded CFF font to memory")
1431
+ varfont.save(stream)
1432
+ stream.seek(0)
1433
+ varfont = TTFont(stream, recalcTimestamp=False, recalcBBoxes=False)
1434
+
1435
+ # Uncomment, to see test all tables can be loaded. This fails without
1436
+ # the extra save+load above.
1437
+ """
1438
+ for tag in varfont.keys():
1439
+ print("Loading", tag)
1440
+ varfont[tag]
1441
+ """
1442
+
1443
+ # Restore them
1444
+ varfont.recalcTimestamp = recalcTimestamp
1445
+ varfont.recalcBBoxes = recalcBBoxes
1446
+
1447
+ return varfont
1448
+
1449
+
1380
1450
  def instantiateAvar(varfont, axisLimits):
1381
1451
  # 'axisLimits' dict must contain user-space (non-normalized) coordinates.
1382
1452
 
1383
1453
  avar = varfont["avar"]
1384
- if getattr(avar, "majorVersion", 1) >= 2 and avar.table.VarStore:
1385
- raise NotImplementedError("avar table with VarStore is not supported")
1386
1454
 
1387
1455
  segments = avar.segments
1388
1456
 
@@ -1393,6 +1461,9 @@ def instantiateAvar(varfont, axisLimits):
1393
1461
  del varfont["avar"]
1394
1462
  return
1395
1463
 
1464
+ if getattr(avar, "majorVersion", 1) >= 2 and avar.table.VarStore:
1465
+ raise NotImplementedError("avar table with VarStore is not supported")
1466
+
1396
1467
  log.info("Instantiating avar table")
1397
1468
  for axis in pinnedAxes:
1398
1469
  if axis in segments:
@@ -1594,6 +1665,7 @@ def instantiateVariableFont(
1594
1665
  updateFontNames=False,
1595
1666
  *,
1596
1667
  downgradeCFF2=False,
1668
+ static=False,
1597
1669
  ):
1598
1670
  """Instantiate variable font, either fully or partially.
1599
1671
 
@@ -1637,12 +1709,23 @@ def instantiateVariableFont(
1637
1709
  software that does not support CFF2. Defaults to False. Note that this
1638
1710
  operation also removes overlaps within glyph shapes, as CFF does not support
1639
1711
  overlaps but CFF2 does.
1712
+ static (bool): if True, generate a full instance (static font) instead of a partial
1713
+ instance (variable font).
1640
1714
  """
1641
1715
  # 'overlap' used to be bool and is now enum; for backward compat keep accepting bool
1642
1716
  overlap = OverlapMode(int(overlap))
1643
1717
 
1644
1718
  sanityCheckVariableTables(varfont)
1645
1719
 
1720
+ if static:
1721
+ unspecified = []
1722
+ for axis in varfont["fvar"].axes:
1723
+ if axis.axisTag not in axisLimits:
1724
+ axisLimits[axis.axisTag] = None
1725
+ unspecified.append(axis.axisTag)
1726
+ if unspecified:
1727
+ log.info("Pinning unspecified axes to default: %s", unspecified)
1728
+
1646
1729
  axisLimits = AxisLimits(axisLimits).limitAxesAndPopulateDefaults(varfont)
1647
1730
 
1648
1731
  log.info("Restricted limits: %s", axisLimits)
@@ -1665,7 +1748,9 @@ def instantiateVariableFont(
1665
1748
  instantiateVARC(varfont, normalizedLimits)
1666
1749
 
1667
1750
  if "CFF2" in varfont:
1668
- instantiateCFF2(varfont, normalizedLimits, downgrade=downgradeCFF2)
1751
+ downgradeCFF2 = instantiateCFF2(
1752
+ varfont, normalizedLimits, downgrade=downgradeCFF2
1753
+ )
1669
1754
 
1670
1755
  if "gvar" in varfont:
1671
1756
  instantiateGvar(varfont, normalizedLimits, optimize=optimize)
@@ -1720,6 +1805,12 @@ def instantiateVariableFont(
1720
1805
  # name table has been updated.
1721
1806
  setRibbiBits(varfont)
1722
1807
 
1808
+ if downgradeCFF2:
1809
+ origVarfont = varfont
1810
+ varfont = downgradeCFF2ToCFF(varfont)
1811
+ if inplace:
1812
+ origVarfont.__dict__ = varfont.__dict__.copy()
1813
+
1723
1814
  return varfont
1724
1815
 
1725
1816
 
@@ -1826,6 +1917,12 @@ def parseArgs(args):
1826
1917
  default=None,
1827
1918
  help="Output instance TTF file (default: INPUT-instance.ttf).",
1828
1919
  )
1920
+ parser.add_argument(
1921
+ "--static",
1922
+ dest="static",
1923
+ action="store_true",
1924
+ help="Make a static font: pin unspecified axes to their default location.",
1925
+ )
1829
1926
  parser.add_argument(
1830
1927
  "--no-optimize",
1831
1928
  dest="optimize",
@@ -1923,13 +2020,13 @@ def main(args=None):
1923
2020
  recalcBBoxes=options.recalc_bounds,
1924
2021
  )
1925
2022
 
1926
- isFullInstance = {
2023
+ isFullInstance = options.static or {
1927
2024
  axisTag
1928
2025
  for axisTag, limit in axisLimits.items()
1929
2026
  if limit is None or limit[0] == limit[2]
1930
2027
  }.issuperset(axis.axisTag for axis in varfont["fvar"].axes)
1931
2028
 
1932
- instantiateVariableFont(
2029
+ varfont = instantiateVariableFont(
1933
2030
  varfont,
1934
2031
  axisLimits,
1935
2032
  inplace=True,
@@ -1937,6 +2034,7 @@ def main(args=None):
1937
2034
  overlap=options.overlap,
1938
2035
  updateFontNames=options.update_name_table,
1939
2036
  downgradeCFF2=options.downgrade_cff2,
2037
+ static=options.static,
1940
2038
  )
1941
2039
 
1942
2040
  suffix = "-instance" if isFullInstance else "-partial"
Binary file
@@ -4,9 +4,16 @@ Instantiate a variation font. Run, eg:
4
4
  .. code-block:: sh
5
5
 
6
6
  $ fonttools varLib.mutator ./NotoSansArabic-VF.ttf wght=140 wdth=85
7
+
8
+ .. warning::
9
+ ``fontTools.varLib.mutator`` is deprecated in favor of :mod:`fontTools.varLib.instancer`
10
+ which provides equivalent full instancing and also supports partial instancing.
11
+ Please migrate CLI usage to ``fonttools varLib.instancer`` and API usage to
12
+ :func:`fontTools.varLib.instancer.instantiateVariableFont`.
7
13
  """
8
14
 
9
15
  from fontTools.misc.fixedTools import floatToFixedToFloat, floatToFixed
16
+ from fontTools.misc.loggingTools import deprecateFunction
10
17
  from fontTools.misc.roundTools import otRound
11
18
  from fontTools.pens.boundsPen import BoundsPen
12
19
  from fontTools.ttLib import TTFont, newTable
@@ -159,6 +166,10 @@ def interpolate_cff2_metrics(varfont, topDict, glyphOrder, loc):
159
166
  hmtx[gname] = tuple(entry)
160
167
 
161
168
 
169
+ @deprecateFunction(
170
+ "use fontTools.varLib.instancer.instantiateVariableFont instead "
171
+ "for either full or partial instancing",
172
+ )
162
173
  def instantiateVariableFont(varfont, location, inplace=False, overlap=True):
163
174
  """Generate a static instance from a variable TTFont and a dictionary
164
175
  defining the desired location along the variable font's axes.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fonttools
3
- Version: 4.59.0
3
+ Version: 4.59.2
4
4
  Summary: Tools to manipulate font files
5
5
  Home-page: http://github.com/fonttools/fonttools
6
6
  Author: Just van Rossum
@@ -168,15 +168,6 @@ are required to unlock the extra features named "ufo", etc.
168
168
 
169
169
  *Extra:* ``lxml``
170
170
 
171
- - ``Lib/fontTools/ufoLib``
172
-
173
- Package for reading and writing UFO source files; it requires:
174
-
175
- * `fs <https://pypi.org/pypi/fs>`__: (aka ``pyfilesystem2``) filesystem
176
- abstraction layer.
177
-
178
- *Extra:* ``ufo``
179
-
180
171
  - ``Lib/fontTools/ttLib/woff2.py``
181
172
 
182
173
  Module to compress/decompress WOFF 2.0 web fonts; it requires:
@@ -269,6 +260,17 @@ are required to unlock the extra features named "ufo", etc.
269
260
 
270
261
  *Extra:* ``pathops``
271
262
 
263
+ - ``Lib/fontTools/ufoLib``
264
+
265
+ Package for reading and writing UFO source files; if available, it will use:
266
+
267
+ * `fs <https://pypi.org/pypi/fs>`__: (aka ``pyfilesystem2``) filesystem abstraction layer
268
+
269
+ for reading and writing UFOs to the local filesystem or zip files (.ufoz), instead of
270
+ the built-in ``fontTools.misc.filesystem`` package.
271
+ The reader and writer classes can in theory also accept any object compatible the
272
+ ``fs.base.FS`` interface, although not all have been tested.
273
+
272
274
  - ``Lib/fontTools/pens/cocoaPen.py`` and ``Lib/fontTools/pens/quartzPen.py``
273
275
 
274
276
  Pens for drawing glyphs with Cocoa ``NSBezierPath`` or ``CGPath`` require:
@@ -386,6 +388,32 @@ Have fun!
386
388
  Changelog
387
389
  ~~~~~~~~~
388
390
 
391
+ 4.59.2 (released 2025-08-27)
392
+ ----------------------------
393
+
394
+ - [varLib] Clear ``USE_MY_METRICS`` component flags when inconsistent across masters (#3912).
395
+ - [varLib.instancer] Avoid negative advance width/height values when instatiating HVAR/VVAR,
396
+ (unlikely in well-behaved fonts) (#3918).
397
+ - [subset] Fix shaping behaviour when pruning empty mark sets (#3915, harfbuzz/harfbuzz#5499).
398
+ - [cu2qu] Fixed ``dot()`` product of perpendicular vectors not always returning exactly 0.0
399
+ in all Python implementations (#3911)
400
+ - [varLib.instancer] Implemented fully-instantiating ``avar2`` fonts (#3909).
401
+ - [feaLib] Allow float values in ``VariableScalar``'s axis locations (#3906, #3907).
402
+ - [cu2qu] Handle special case in ``calc_intersect`` for degenerate cubic curves where 3 to 4
403
+ control points are equal (#3904).
404
+
405
+ 4.59.1 (released 2025-08-14)
406
+ ----------------------------
407
+
408
+ - [featureVars] Update OS/2.usMaxContext if possible after addFeatureVariationsRaw (#3894).
409
+ - [vhmtx] raise TTLibError('not enough data...') when hmtx/vmtx are truncated (#3843, #3901).
410
+ - [feaLib] Combine duplicate features that have the same set of lookups regardless of the order in which those lookups are added to the feature (#3895).
411
+ - [varLib] Deprecate ``varLib.mutator`` in favor of ``varLib.instancer``. The latter
412
+ provides equivalent full (static font) instancing in addition to partial VF instancing.
413
+ CLI users should replace ``fonttools varLib.mutator`` with ``fonttools varLib.instancer``.
414
+ API users should migrate to ``fontTools.varLib.instancer.instantiateVariableFont`` (#2680).
415
+
416
+
389
417
  4.59.0 (released 2025-07-16)
390
418
  ----------------------------
391
419
 
@@ -1,4 +1,4 @@
1
- fontTools/__init__.py,sha256=5U6x_uZd11VYk1ucwvyYEj3wYSfJMdnXPt0twVOXaPQ,191
1
+ fontTools/__init__.py,sha256=tDtr9dKOrmxz5xXK-4JYaRrVOBx6WHpvfrXg_aWIzYU,191
2
2
  fontTools/__main__.py,sha256=T8Tg8xPKHOCVoYVG82p_zpQXfW7_ERRAphBkZVvhWN8,960
3
3
  fontTools/afmLib.py,sha256=YbmmjT8Du6qFUhFHwnAhOdvsyfXszODVjSJtd18CCjY,13603
4
4
  fontTools/agl.py,sha256=4aKwnbvSVUa39eV5Ka8e5ULwV-IEp4pcfwlMwEH_z3k,118208
@@ -7,11 +7,11 @@ fontTools/help.py,sha256=8yn5iAonGPsijFSHmU6aLuuZtaLMhR5CIkSp9hVYL2c,1161
7
7
  fontTools/tfmLib.py,sha256=-bv4iv2VhUSse5pA0oXdudf7o7ZuFWdWNsiHElO06dk,14730
8
8
  fontTools/ttx.py,sha256=CpfOtEVTXAv79XM2jiWKrOFXHFtWSyAniIgC7b9tWf8,17756
9
9
  fontTools/unicode.py,sha256=a7460sU25TnVYGzrVl0uv0lI_pDbANZp8Jfmqx9tAag,1287
10
- fontTools/cffLib/CFF2ToCFF.py,sha256=5uPKDFwoJvH0KVDrCjpf3MdOpqbyvdZMe0jZ3emjdsQ,6291
10
+ fontTools/cffLib/CFF2ToCFF.py,sha256=y-RJL8bbg6F1DPklI5xLUqfrjvjmByyx_l1XG_L1WRA,7657
11
11
  fontTools/cffLib/CFFToCFF2.py,sha256=0dCYSSozptUC9BVUre49e6LgjSxJRtVyMl8vDB6i3r4,10424
12
12
  fontTools/cffLib/__init__.py,sha256=E4wzLsJ1LxWO7CIR7fjZMHaYQJSVdqCO08fOVFowwpM,111580
13
13
  fontTools/cffLib/specializer.py,sha256=dznFa-7VrKZkx6D8klaixTaqEAnrnT6YLX9jzA6S0Cc,33536
14
- fontTools/cffLib/transforms.py,sha256=8hffhsWRhBhVukNSL-7ieuygTVV5Ta3Cz9s4s8Awvgg,17861
14
+ fontTools/cffLib/transforms.py,sha256=YvLBqogfYx_UsPKsU_WsOcS_ASeN3rwFzlow-lL2C6I,17950
15
15
  fontTools/cffLib/width.py,sha256=3L9NWI0uQrJHvHF_IvC_tbW1cq94zgDEPSjubdug8qM,6284
16
16
  fontTools/colorLib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  fontTools/colorLib/builder.py,sha256=S8z4Qzw2FAE-d1Zm1eHyqDBYh6FW4W_hQJWjVeVicOk,23672
@@ -24,8 +24,8 @@ fontTools/cu2qu/__init__.py,sha256=OoM_nBJAleZal6kxeNJn1ESy1pNm5c3DG417yVIE0-Q,6
24
24
  fontTools/cu2qu/__main__.py,sha256=6Vb8Ler3yqJ5w84UwlMJV6cS01uhV4PN10OlXQ6jlqo,98
25
25
  fontTools/cu2qu/benchmark.py,sha256=FwdvNjKfWHo18_CX0CO8AY5c68XSBE4M4TJo_EkB4q8,1350
26
26
  fontTools/cu2qu/cli.py,sha256=CvWzC5a6XF_v5o0yrS4vGI1JXiVVLzSJahTIqpJmiPk,6274
27
- fontTools/cu2qu/cu2qu.cp39-win_amd64.pyd,sha256=rLC7g6awf9EokKwiR6_zG9lZXnI4WQgsV43YhQdaDy0,99328
28
- fontTools/cu2qu/cu2qu.py,sha256=XH2bnQ5aG9ic921ZWzQzU1-q3MQU6INCjLk4XjRj5_Y,16970
27
+ fontTools/cu2qu/cu2qu.cp39-win_amd64.pyd,sha256=f5I3rBx6SBZAYwMhlCbon-QtHQZwC-XyGXGNTIE-4hE,99328
28
+ fontTools/cu2qu/cu2qu.py,sha256=ZciNHndtwWFx5leQO7Gxuk5ybyYTd8ZMsVawBOprNMc,17907
29
29
  fontTools/cu2qu/errors.py,sha256=uYyPSs_x-EMJKO2S3cLGWyk_KlHoOoh_XEtdB_oKBp0,2518
30
30
  fontTools/cu2qu/ufo.py,sha256=Mpd_7Be9jxNcOKFqkyRp8Oem3CS3R-ZYMMSD03LJL6o,12143
31
31
  fontTools/designspaceLib/__init__.py,sha256=80fzbsWaoTMaXsPGMnevXAxR4eqvZeYCwV_GYpBvlkM,132601
@@ -40,14 +40,14 @@ fontTools/encodings/codecs.py,sha256=bSpO6kuPbEIDsXSVHhzftqsm_FFUiXpLVfPSk410SqE
40
40
  fontTools/feaLib/__init__.py,sha256=RprjP6BKswq4pt0J-9L1XGuZfjIFAGD6HDly_haMAN4,217
41
41
  fontTools/feaLib/__main__.py,sha256=niUAPkiYxeRAJMlJuvVJZism2VFufZrNaQtieA7sNLk,2318
42
42
  fontTools/feaLib/ast.py,sha256=q-UvEPZ97AAHpggVOzVHdgfTcE072kuOK08rdAYpCXU,76301
43
- fontTools/feaLib/builder.py,sha256=9f7v9Vfo0HkC6Pqj-hP4B6xj5GDIoV_XLCeDajmbI2g,74962
43
+ fontTools/feaLib/builder.py,sha256=_ofuSJ9Jtz1Ybzk4ReCWSa431PXfIq-UcVkYZ2B0Ruo,75565
44
44
  fontTools/feaLib/error.py,sha256=pqi8F2tnH2h7pXVffxwzuBuWaSHMzZsXs5VckdQKQAI,670
45
- fontTools/feaLib/lexer.cp39-win_amd64.pyd,sha256=jN6yYAVJ2E-k8CiF1GYbi269z5rN0Iy3ulTwQo-REZc,120832
45
+ fontTools/feaLib/lexer.cp39-win_amd64.pyd,sha256=vLJB0fK-w9UW256SjhMP7EX6zSB0M-EDEL6KVFMJeeM,120320
46
46
  fontTools/feaLib/lexer.py,sha256=7VZ3NPFH7V1mvRbym111BNKvbB4hLfGLTMS0VV_3Ipw,11408
47
47
  fontTools/feaLib/location.py,sha256=teHrhjT8zzImcGBEJS1J43oaX9onCPu_pynxS8d-tUg,246
48
48
  fontTools/feaLib/lookupDebugInfo.py,sha256=h4Ig8kmEk5WlGf1C9JJAbbOKQK5OwkFLdj8CT7fOkmU,316
49
- fontTools/feaLib/parser.py,sha256=fdycJS5E1RtBvFifNx4rub360J6LmUyX5XpvfoEGxNI,101710
50
- fontTools/feaLib/variableScalar.py,sha256=RiLHKQh2-wa-BZ015H2e7XkbshssTj2PjlapaMNJfAs,4182
49
+ fontTools/feaLib/parser.py,sha256=T8IVOqzU92A3_V2-R0-W-HzNBjziJJHSg1ZGcmde-Rc,102105
50
+ fontTools/feaLib/variableScalar.py,sha256=KWFE5YBW7WWgLC4eqLfcGmiA7SbYO3VSdsF_5ZwJa0k,4318
51
51
  fontTools/merge/__init__.py,sha256=A6aQxwtbzcLUrE9UOdk3yCrMwqPOboTEr5ZyEu9RxRg,8504
52
52
  fontTools/merge/__main__.py,sha256=3_u3dnyEOyh0O-SrLMLlkXxOfCFT-0SlwJpimosVJ-c,100
53
53
  fontTools/merge/base.py,sha256=LPJKOwMiDwayLGzA1xH325CtYHPvahAA17lihvKjiPw,2470
@@ -59,7 +59,7 @@ fontTools/merge/unicode.py,sha256=mgqRFhRugda62Xt0r28SduaN7YBzRfHxrpNprjLqoX8,43
59
59
  fontTools/merge/util.py,sha256=3alo4b7mhFNC6h8PjeqNU99dS7EuO8sdZkZpvRsEE6E,3521
60
60
  fontTools/misc/__init__.py,sha256=QoK6HlOoqtVqX5gOyv0bJiTXsVBbBRreUifdccWNp2k,76
61
61
  fontTools/misc/arrayTools.py,sha256=baENNALPvYRUhS4rdx_F3ltOmVIf1PV9G2EaMt7gAHM,11907
62
- fontTools/misc/bezierTools.cp39-win_amd64.pyd,sha256=Ukr4Ad6yAh5QgBnT0VxDcC2GkqbkahnGHrpIb54_eRA,344064
62
+ fontTools/misc/bezierTools.cp39-win_amd64.pyd,sha256=sblWaxI1JmmiiWahBJY-OL1txe-SNSkPVNFPkAXMc14,343552
63
63
  fontTools/misc/bezierTools.py,sha256=m4j14ckKYtrKy8NhFFFY_Uv3kuL8g-SWNdEKUzqGjRQ,46535
64
64
  fontTools/misc/classifyTools.py,sha256=wLTjOhLiZaLiwwUTj2Ad5eZ5T_38W0Eo_uzRGWHWYvE,5783
65
65
  fontTools/misc/cliTools.py,sha256=7zKOXczaCKRMW6Yv5jdCZYHco8y0-lfimhIWzQ2IL8A,1915
@@ -77,7 +77,7 @@ fontTools/misc/lazyTools.py,sha256=LJ7QvDG65xOBw2AI43qGCLxVmfdbsf-PUECfrenbkAU,1
77
77
  fontTools/misc/loggingTools.py,sha256=27VatVrX8Yu-w5rFYSUjOnPLJIJ9Hx2R6hJ5YpP_djA,20476
78
78
  fontTools/misc/macCreatorType.py,sha256=5JZKTsnkI_VBhC52lwMSrdmzqgUOhwC42jPvbGahsPo,1649
79
79
  fontTools/misc/macRes.py,sha256=ewiYDKioxxBKW6JQcRmxpNYw5JgtJZIJyqWBG_KplUo,8840
80
- fontTools/misc/psCharStrings.py,sha256=e5kR55Gm3orJsDLo3eu6CxpoZ1pMNZh5Wm-Zj4m7wJs,44532
80
+ fontTools/misc/psCharStrings.py,sha256=T91YzpraGjZ3ChrM56t-KCoEuE79OvPq1tJA3qYywDU,44979
81
81
  fontTools/misc/psLib.py,sha256=cqxG8yMZ7_5VTxgTUl2ARNhIhNu_iTzxLTEd1Egwugo,12497
82
82
  fontTools/misc/psOperators.py,sha256=9nZ4ymbiiCApY9V8OARpYqvO73OEcJgGyTtCuGzD-rw,16272
83
83
  fontTools/misc/py23.py,sha256=BhByQabxZis6fDvK3ZVeI-YRj_1rMQeBZCFzGWIac0U,2334
@@ -85,7 +85,7 @@ fontTools/misc/roundTools.py,sha256=2rmbuk73NYGPmJqP58FQCFioSLilvNffd0WbL5znKUg,
85
85
  fontTools/misc/sstruct.py,sha256=7Lc9x2RV_e2JvSI4A_Rs0tM1mZpjYg_oAJKhP_F9zts,7236
86
86
  fontTools/misc/symfont.py,sha256=KYAtw-ZnG5YReS8XkSDIvxc1bl0xzZl-Wx4J7k7u7LA,7219
87
87
  fontTools/misc/testTools.py,sha256=SG48M4TJIQ4_cPpitUzGEITPnwL-o0yNZKXzWSQdwVE,7285
88
- fontTools/misc/textTools.py,sha256=NIBmM6k9PXIs8DMpio-9ckHS35QxL2EMFwBXP6zG-8w,3531
88
+ fontTools/misc/textTools.py,sha256=ws89gG0KB67ldR8kdGOdYWwx3ht644nHhNrmUt-WYzc,3639
89
89
  fontTools/misc/timeTools.py,sha256=lmncKUKvxQKO4Kqx2k7UNFkYYpj2n5CwR1lPiLZv3tA,2322
90
90
  fontTools/misc/transform.py,sha256=pCR0tbKzmhH6crB_rDT5hnAWySztW_XqL0efmKOVsCU,16314
91
91
  fontTools/misc/treeTools.py,sha256=IMopMUcuhelvz8gNra50Zc1w8DSlWywnL6DFaz1ijQs,1314
@@ -127,7 +127,7 @@ fontTools/pens/explicitClosingLinePen.py,sha256=knCXcjSl2iPy6mLCDnsdDYx6J5rV7FH4
127
127
  fontTools/pens/filterPen.py,sha256=tWhgklyaCTUt7oQRTBbFUcOlc702V0NfadCH3X93CYg,8031
128
128
  fontTools/pens/freetypePen.py,sha256=NqNzXrOTDckoH4N6WLnj-KuxGcg6z7DlqSCfmpq8qAE,20370
129
129
  fontTools/pens/hashPointPen.py,sha256=ZAU87uw5ge3Kb4i9kRV28a5VFeZ_TWSsJabyAzwAHrU,3662
130
- fontTools/pens/momentsPen.cp39-win_amd64.pyd,sha256=j-7TGfrhbh6RmkB1xyibTst9OphWbIHoRcuF_m2JM7g,89088
130
+ fontTools/pens/momentsPen.cp39-win_amd64.pyd,sha256=iKkW0Ejmdx-DPE-Usww0XFl7D6e7li-xrl8kq7EU25o,89088
131
131
  fontTools/pens/momentsPen.py,sha256=Z-V5CjQBSj3qPxg3C_DBFKExqno89nOe3jWwHT9_xsM,26537
132
132
  fontTools/pens/perimeterPen.py,sha256=Zy5F8QzaNJAkkQQSb2QJCp-wZTvDAjBn-B099t2ABds,2222
133
133
  fontTools/pens/pointInsidePen.py,sha256=Hy48iR5NWV3x_wWoos-UC7GMtwvvUhd_q_ykiwaWdzQ,6547
@@ -150,9 +150,9 @@ fontTools/qu2cu/__init__.py,sha256=MpdE0XsHSDo9M3hyHLkPPLxB3FKr3aiT0dPW5qHCuSo,6
150
150
  fontTools/qu2cu/__main__.py,sha256=leKpToUNNyHf0nobr1I19vus2ziA1pO7rRKkreat-Xw,100
151
151
  fontTools/qu2cu/benchmark.py,sha256=PFxx2Bfu7-KuNrzdOIBXHPZvyNphqqcTVy4CneaCo3M,1456
152
152
  fontTools/qu2cu/cli.py,sha256=1QLBTSZW7e_VATJN9vjszRxIk_-Xjxu1KP53yX4T7q8,3839
153
- fontTools/qu2cu/qu2cu.cp39-win_amd64.pyd,sha256=a2l_QMnoA2UMtXdAKHm9YZZE6kf8-H7QnpU9lSuqb9c,107520
153
+ fontTools/qu2cu/qu2cu.cp39-win_amd64.pyd,sha256=AJh2Ioi1e3YzVJ7bOZr33AmlPqEbSjza17rUIjRoccc,107008
154
154
  fontTools/qu2cu/qu2cu.py,sha256=dtp5Zqhcs_NePwA2U5fgG2LtWleRwmBilTurau8sLL0,12693
155
- fontTools/subset/__init__.py,sha256=cMUbkLDSaj0f576WF5zhbZ9mYT4U9b_PPzFFBpg8VEI,141636
155
+ fontTools/subset/__init__.py,sha256=BIx4NBKTps8oFRnB_3uN6MDsQh10IPkrId5VyR9AI9M,141672
156
156
  fontTools/subset/__main__.py,sha256=cEIC52EtGOJvFDfHXzi0M2EAYmyHAcI-ZZ0lb2y4r7s,101
157
157
  fontTools/subset/cff.py,sha256=GSmxdsokxuFKvJJQVcAIOhd5hYQq8KkzxnXE_dgm8yo,6329
158
158
  fontTools/subset/svg.py,sha256=y_yTZuAm3bjcoEOFu5likXoHuG5u1oNiv0mOni2Z9fQ,9637
@@ -231,7 +231,7 @@ fontTools/ttLib/tables/V_O_R_G_.py,sha256=s9g03_qeTV3qoJAWpXxpRCmao0l1wj4WagR_Ys
231
231
  fontTools/ttLib/tables/V_V_A_R_.py,sha256=PiwzLv95tfXH25hYQFAxL11mwgbLjeg4R1LvVH5m7lU,332
232
232
  fontTools/ttLib/tables/__init__.py,sha256=pYmftKvp7RCNIaEJuUNjcZFaiIfOC0zzcApViNKUxkU,2749
233
233
  fontTools/ttLib/tables/_a_n_k_r.py,sha256=eiy6DKxPGw-H9QCLWIQBFveuTFQSKgcPItwgyBOghk8,498
234
- fontTools/ttLib/tables/_a_v_a_r.py,sha256=cIqfWyyU5hNrJ-SIfVCKQcpyi2DUjobAgrmyE2In9FI,7307
234
+ fontTools/ttLib/tables/_a_v_a_r.py,sha256=521RCooxpE8T9sOdm0BMY2E--iWnrhO-cTr7O4dLhC4,7368
235
235
  fontTools/ttLib/tables/_b_s_l_n.py,sha256=iHLFy3sjFFoGa-pDGdcLCCudv4QFMt2VBL06gigGA_k,480
236
236
  fontTools/ttLib/tables/_c_i_d_g.py,sha256=BPa6b0yrmT8OXPW3USRpn_H8DOLKFlDe9subtRDJBrc,937
237
237
  fontTools/ttLib/tables/_c_m_a_p.py,sha256=dvtnrDf1LjXVf_DXW0tz67BqOlf7dINyG0_HF3aoDmw,63793
@@ -243,11 +243,11 @@ fontTools/ttLib/tables/_f_v_a_r.py,sha256=hpxU0-u7_pZYDmQdKpmd_7c6BjVpXHdoQ97jKk
243
243
  fontTools/ttLib/tables/_g_a_s_p.py,sha256=Pr4X2CEg3a_nYAZrKSWT0auQ5HU2WutP1Shxxg7ALPw,2266
244
244
  fontTools/ttLib/tables/_g_c_i_d.py,sha256=diZlew4U8nFK5vimh-GMOjwHw8ccZtIEl9cPq0PrNdA,375
245
245
  fontTools/ttLib/tables/_g_l_y_f.py,sha256=gnZ1femTeEaFeWn4inLiWL0h8U9_iJ0CzU_6PDtFryk,87895
246
- fontTools/ttLib/tables/_g_v_a_r.py,sha256=bXLh4wXqtbL_hEmds5PXWJ5FTx58_1VojaqVvgesYH4,12412
246
+ fontTools/ttLib/tables/_g_v_a_r.py,sha256=3_uN_OGGLpEjLJKfEOBIMyt92NOXpKBoLymRkiMwzCQ,12536
247
247
  fontTools/ttLib/tables/_h_d_m_x.py,sha256=1dRbNSzHQlznrXqG0GtFYbGp_SzzgDcYfJqhSoS1FL8,4379
248
248
  fontTools/ttLib/tables/_h_e_a_d.py,sha256=ft9ghTA1NZsGBvB0yElFFCqVHecuCKGjT2m2GfYB3Yc,5056
249
249
  fontTools/ttLib/tables/_h_h_e_a.py,sha256=pY92ZLt3o0jZ3KQVd_qtxYtk_tbP2DLzSWm_wVP8FNM,4914
250
- fontTools/ttLib/tables/_h_m_t_x.py,sha256=p-9K-E3LcdJByagZ-0F0OA11pCVfNS9HtKRjbpvMM6I,6202
250
+ fontTools/ttLib/tables/_h_m_t_x.py,sha256=39O10q6zJNPGgoDbd5Ivli4-BpESP81ajtDOZy-xCHg,6356
251
251
  fontTools/ttLib/tables/_k_e_r_n.py,sha256=AjG5Fd6XaPAdXi5puDtLuMrfCsHUi9X7uFh76QGCMrc,11083
252
252
  fontTools/ttLib/tables/_l_c_a_r.py,sha256=N-1I6OJHvnF_YfGktyfFTRAG5lrExV7q6HX-0ffSRyQ,403
253
253
  fontTools/ttLib/tables/_l_o_c_a.py,sha256=MHRhxCcHbyK71lD3L91qNfYXD_rflSXsRa-YHDptCik,2250
@@ -256,7 +256,7 @@ fontTools/ttLib/tables/_m_a_x_p.py,sha256=9B6lvWo4y42dyLPIvG6CsVOlWCk7bs4DoVJDB8
256
256
  fontTools/ttLib/tables/_m_e_t_a.py,sha256=I8HaZgcIPQZcCxBiSX0rGrfrs-zXRGUfEbJ8eGvZ07A,4025
257
257
  fontTools/ttLib/tables/_m_o_r_t.py,sha256=LU3D9PmV_nFs6hoccGmr1pfUzjJaeB_WRW2OIS0RwPc,501
258
258
  fontTools/ttLib/tables/_m_o_r_x.py,sha256=vLyrtx_O__BwnPi7Qo3oT8WHaANRARtHcqHSdZ5ct0E,563
259
- fontTools/ttLib/tables/_n_a_m_e.py,sha256=bvahvBMX21pC6G83D35WZap6F7BDd7Xi2AmhVMBZz00,42300
259
+ fontTools/ttLib/tables/_n_a_m_e.py,sha256=Z3BZtWPZfL-tpRjypnxjsvJzdwZWMbJ50BuFngtnkCs,42508
260
260
  fontTools/ttLib/tables/_o_p_b_d.py,sha256=lfJi6kblt_nGmGmRSupwEaud3Ri_y6ftWNuyrCPpzQ0,462
261
261
  fontTools/ttLib/tables/_p_o_s_t.py,sha256=HIsvathEMOAm8QFG7vcRfPQvBqXh-QmsVyOoDWsaRNc,12020
262
262
  fontTools/ttLib/tables/_p_r_e_p.py,sha256=qWDjHiHvHaJCx2hYFmjJeMwpgwvD-cG5zkibMh9TWuk,443
@@ -293,14 +293,14 @@ fontTools/unicodedata/OTTags.py,sha256=IAt8NXaZOhu5cuuks46DDX3E7Ovoqp-PMUQC-WJUP
293
293
  fontTools/unicodedata/ScriptExtensions.py,sha256=eIAXBnM9BbI5V_MWeA9I9Iv2rvgWi8mt8dCWN3cN1gY,29033
294
294
  fontTools/unicodedata/Scripts.py,sha256=jCKY8wlKrSFmsFndzLegVS6vrhVGZ-S3T0dw2vO9Drg,133888
295
295
  fontTools/unicodedata/__init__.py,sha256=dUWWA4Ga1Wud8XkCIWZ02NCHBLtX2XYYUJo4ZLHTd0U,9337
296
- fontTools/varLib/__init__.py,sha256=E38iOYGLna0PhK-t7G33KNl36B11w_Lq7rd6KV5Pt8s,55753
296
+ fontTools/varLib/__init__.py,sha256=HTGmRXieDmkNGeav4uSmoHuhV6IasgiLbeT5TAfu-tQ,58918
297
297
  fontTools/varLib/__main__.py,sha256=ykyZY5GG9IPDsPrUWiHgXEnsgKrQudZkneCTes6GUpU,101
298
298
  fontTools/varLib/avar.py,sha256=tRgKAUn_K5MTCSkB2MgPYYZ2U6Qo_Cg3jFQV0TDKFgc,9907
299
299
  fontTools/varLib/avarPlanner.py,sha256=orjyFvg3YkC-slt7fgSEU1AGjLCkGgMEJ7hTRV6CqUA,28362
300
300
  fontTools/varLib/builder.py,sha256=1k-N-rTwnZqQpzhNLBx2tqu2oYGG44sJSXKTCjAvIVM,6824
301
301
  fontTools/varLib/cff.py,sha256=bl8rrPHHpwzUdZBY80_5JJLWYcXQOolhKKvTJiiU-Bs,23532
302
302
  fontTools/varLib/errors.py,sha256=mXl-quT2Z75_t7Uwb6ug3VMhmbQjO841YNLeghwuY_s,7153
303
- fontTools/varLib/featureVars.py,sha256=fBt7iJtohfsfqO7AULmYMD56hb3apCDXRgpR18pDoG8,26390
303
+ fontTools/varLib/featureVars.py,sha256=cPiijbzRMZ0AiudBNPhF8XE1LoBr5VGkjiwpw5N0oKQ,26883
304
304
  fontTools/varLib/hvar.py,sha256=Tm0ibxOtSVrBQeHiA5-idUQaJEjzOdUWxAQyKTGsOdQ,3808
305
305
  fontTools/varLib/interpolatable.py,sha256=8AXrhsnYY1z0hR6gskqYRYx8qcFsvUKmIIHZRpIOlAU,46430
306
306
  fontTools/varLib/interpolatableHelpers.py,sha256=JnabttZY7sY9-QzdiqkgzQ_S5nG8k_O1TzLEmfNUvNo,11892
@@ -308,17 +308,17 @@ fontTools/varLib/interpolatablePlot.py,sha256=tUKFd8H9B2eD_GE6jV13J-dZkkIeLmk3oj
308
308
  fontTools/varLib/interpolatableTestContourOrder.py,sha256=Pbt0jW0LoVggIwrtADZ7HWK6Ftdoo1bjuWz0ost0HD0,3103
309
309
  fontTools/varLib/interpolatableTestStartingPoint.py,sha256=f5MJ3mj8MctJCvDJwqmW1fIVOgovUMYAOela9HweaRU,4403
310
310
  fontTools/varLib/interpolate_layout.py,sha256=tTPUes_K7MwooUO_wac9AeFEVgL1uGSz4ITYiOizaME,3813
311
- fontTools/varLib/iup.cp39-win_amd64.pyd,sha256=UNFPOp_nWrRyoyypKBQ_2ieZnrPXi8ljEMNcSVyupdY,130048
311
+ fontTools/varLib/iup.cp39-win_amd64.pyd,sha256=VrHg4tNuMTEP4erM77LdqrcPf4YNRDjX9nzihkAUf-0,129536
312
312
  fontTools/varLib/iup.py,sha256=O_xPJOBECrNDbQqCC3e5xf9KsWXUd1i3BAp9Fl6Hv2Y,15474
313
313
  fontTools/varLib/merger.py,sha256=V-B17poOYbbrRsfUYJbdqt46GtRfG833MKwtv9NOB3Q,62519
314
314
  fontTools/varLib/models.py,sha256=ZqQb1Lapj5dCO8dwa3UTx1LsIpF0-GiDte32t_TMJJQ,23040
315
315
  fontTools/varLib/multiVarStore.py,sha256=OvrrTaKrCZCXP40Rrv-2w416P-dNz3xE6gPOEyS3PrY,8558
316
- fontTools/varLib/mutator.py,sha256=bUkUP27sxhEVkdljzbHNylHkj6Ob3FfQ9AoDYTRIwdo,19796
316
+ fontTools/varLib/mutator.py,sha256=0Mxy2ZSfFPEQrZM1fIHxZ17f0U_axLXArQ7apkjm0UI,20333
317
317
  fontTools/varLib/mvar.py,sha256=Gf3q54ICH-E9oAwKYeIKUPLZabfjY0bUT4t220zLzYI,2489
318
318
  fontTools/varLib/plot.py,sha256=BtozrcnKoEyCs0rGy7PZmrUvUNTmZT-5_sylW5PuJ28,7732
319
319
  fontTools/varLib/stat.py,sha256=ScaVFIVpXTqA-F07umv_66GoxtcjaZ54MPLFvFK4s68,4960
320
320
  fontTools/varLib/varStore.py,sha256=GWz-B1YcR-JnIh2aDmeQg621GDEBj9M4pKYcbZraA3w,24808
321
- fontTools/varLib/instancer/__init__.py,sha256=Dp4bxw_aOCdpECbQ4QPj8PujUzzpZAxQso4huWUx2J4,74220
321
+ fontTools/varLib/instancer/__init__.py,sha256=Enbv3I0j25PhYymbZNTboqnx5dnQXmUSv3ORT9-qib8,77606
322
322
  fontTools/varLib/instancer/__main__.py,sha256=YN_tyJDdmLlH3umiLDS2ue0Zc3fSFexa9wCuk3Wuod0,109
323
323
  fontTools/varLib/instancer/featureVars.py,sha256=b3qtGCYVZ9fqkqcgFQUikYQBX_3_x0YgdrvvxIALbuU,7300
324
324
  fontTools/varLib/instancer/names.py,sha256=vmHi7JZlh-N4amxKdaTJ-5DN9mDJ8Wnh_s9W1gJAQ4Y,15338
@@ -330,11 +330,11 @@ fontTools/voltLib/error.py,sha256=3TsaZBA82acFd2j5Beq3WUQTURTKM0zxOnUFGZovSNA,40
330
330
  fontTools/voltLib/lexer.py,sha256=v9V4zdBO2VqVJG__IWrL8fv_CRURmh2eD_1UpbIJn9g,3467
331
331
  fontTools/voltLib/parser.py,sha256=HS72gxtFzvcPSwEbUYj3E41CPK7ZqK9mSe0nLRxn-IY,26060
332
332
  fontTools/voltLib/voltToFea.py,sha256=nS-OSlx_a-TngGICFNKyFxMhjqkV3OQLcvyzw4sQFyk,37460
333
- fonttools-4.59.0.data/data/share/man/man1/ttx.1,sha256=E71F9mRNWlttVpzlnP7w_fqkQygPkph5s-AtVa0Js50,5601
334
- fonttools-4.59.0.dist-info/licenses/LICENSE,sha256=Ir74Bpfs-qF_l-YrmibfoSggvgVYPo3RKtFpskEnTJk,1093
335
- fonttools-4.59.0.dist-info/licenses/LICENSE.external,sha256=sIKl-Gd1smQfAbzLi5yCkISB3l9QK7JUseE7_CqfMD0,20410
336
- fonttools-4.59.0.dist-info/METADATA,sha256=d4GOxfoHGPMLqZHqeziF_YonUyYjeJuRcCWQgCZg81s,110052
337
- fonttools-4.59.0.dist-info/WHEEL,sha256=XkFE14KmFh7mutkkb-qn_ueuH2lwfT8rLdfc5xpQ7wE,99
338
- fonttools-4.59.0.dist-info/entry_points.txt,sha256=8kVHddxfFWA44FSD4mBpmC-4uCynQnkoz_9aNJb227Y,147
339
- fonttools-4.59.0.dist-info/top_level.txt,sha256=rRgRylrXzekqWOsrhygzib12pQ7WILf7UGjqEwkIFDM,10
340
- fonttools-4.59.0.dist-info/RECORD,,
333
+ fonttools-4.59.2.data/data/share/man/man1/ttx.1,sha256=E71F9mRNWlttVpzlnP7w_fqkQygPkph5s-AtVa0Js50,5601
334
+ fonttools-4.59.2.dist-info/licenses/LICENSE,sha256=Ir74Bpfs-qF_l-YrmibfoSggvgVYPo3RKtFpskEnTJk,1093
335
+ fonttools-4.59.2.dist-info/licenses/LICENSE.external,sha256=sIKl-Gd1smQfAbzLi5yCkISB3l9QK7JUseE7_CqfMD0,20410
336
+ fonttools-4.59.2.dist-info/METADATA,sha256=44wZQ6IJ78akWmrHMwFY8-yQxxnNyoD8fXsBLqOulnw,111931
337
+ fonttools-4.59.2.dist-info/WHEEL,sha256=XkFE14KmFh7mutkkb-qn_ueuH2lwfT8rLdfc5xpQ7wE,99
338
+ fonttools-4.59.2.dist-info/entry_points.txt,sha256=8kVHddxfFWA44FSD4mBpmC-4uCynQnkoz_9aNJb227Y,147
339
+ fonttools-4.59.2.dist-info/top_level.txt,sha256=rRgRylrXzekqWOsrhygzib12pQ7WILf7UGjqEwkIFDM,10
340
+ fonttools-4.59.2.dist-info/RECORD,,