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
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,33 @@ 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
61
+
62
+
63
+ @cython.cfunc
64
+ @cython.locals(z=cython.complex, den=cython.double)
65
+ @cython.locals(zr=cython.double, zi=cython.double)
66
+ def _complex_div_by_real(z, den):
67
+ """Divide complex by real using Python's method (two separate divisions).
68
+
69
+ This ensures bit-exact compatibility with Python's complex division,
70
+ avoiding C's multiply-by-reciprocal optimization that can cause 1 ULP differences
71
+ on some platforms/compilers (e.g. clang on macOS arm64).
72
+
73
+ https://github.com/fonttools/fonttools/issues/3928
74
+ """
75
+ zr = z.real
76
+ zi = z.imag
77
+ return complex(zr / den, zi / den)
52
78
 
53
79
 
54
80
  @cython.cfunc
@@ -59,8 +85,8 @@ def dot(v1, v2):
59
85
  )
60
86
  def calc_cubic_points(a, b, c, d):
61
87
  _1 = d
62
- _2 = (c / 3.0) + d
63
- _3 = (b + c) / 3.0 + _2
88
+ _2 = _complex_div_by_real(c, 3.0) + d
89
+ _3 = _complex_div_by_real(b + c, 3.0) + _2
64
90
  _4 = a + d + c + b
65
91
  return _1, _2, _3, _4
66
92
 
@@ -273,6 +299,12 @@ def calc_intersect(a, b, c, d):
273
299
  try:
274
300
  h = dot(p, a - c) / dot(p, cd)
275
301
  except ZeroDivisionError:
302
+ # if 3 or 4 points are equal, we do have an intersection despite the zero-div:
303
+ # return one of the off-curves so that the algorithm can attempt a one-curve
304
+ # solution if it's within tolerance:
305
+ # https://github.com/linebender/kurbo/pull/484
306
+ if b == c and (a == b or c == d):
307
+ return b
276
308
  return complex(NAN, NAN)
277
309
  return c + cd * h
278
310
 
fontTools/cu2qu/ufo.py CHANGED
@@ -165,9 +165,19 @@ def _glyphs_to_quadratic(glyphs, max_err, reverse_direction, stats, all_quadrati
165
165
  """Do the actual conversion of a set of compatible glyphs, after arguments
166
166
  have been set up.
167
167
 
168
+ Empty glyphs (without contours) are ignored and passed through unchanged.
169
+
168
170
  Return True if the glyphs were modified, else return False.
169
171
  """
170
172
 
173
+ # Skip empty glyphs (with zero contours)
174
+ non_empty_indices = [i for i, g in enumerate(glyphs) if len(g) > 0]
175
+ if not non_empty_indices:
176
+ return False
177
+
178
+ glyphs = [glyphs[i] for i in non_empty_indices]
179
+ max_err = [max_err[i] for i in non_empty_indices]
180
+
171
181
  try:
172
182
  segments_by_location = zip(*[_get_segments(g) for g in glyphs])
173
183
  except UnequalZipLengthsError:
@@ -212,6 +222,8 @@ def glyphs_to_quadratic(
212
222
  compatibility. If this is not required, calling glyphs_to_quadratic with one
213
223
  glyph at a time may yield slightly more optimized results.
214
224
 
225
+ Empty glyphs (without contours) are ignored and passed through unchanged.
226
+
215
227
  Return True if glyphs were modified, else return False.
216
228
 
217
229
  Raises IncompatibleGlyphsError if glyphs have non-interpolatable outlines.
@@ -250,6 +262,8 @@ def fonts_to_quadratic(
250
262
  compatibility. If this is not required, calling fonts_to_quadratic with one
251
263
  font at a time may yield slightly more optimized results.
252
264
 
265
+ Empty glyphs (without contours) are ignored and passed through unchanged.
266
+
253
267
  Return the set of modified glyph names if any, else return an empty set.
254
268
 
255
269
  By default, cu2qu stores the curve type in the fonts' lib, under a private
@@ -1323,6 +1323,11 @@ class VariableFontDescriptor(SimpleDescriptor):
1323
1323
  in the document**. The file may or may not exist.
1324
1324
 
1325
1325
  If not specified, the :attr:`name` will be used as a basename for the file.
1326
+
1327
+ .. note::
1328
+ This is intended to be a simple filename (basename or stem) only.
1329
+ Build tools will only use the basename component and ignore any
1330
+ directory separators for security reasons.
1326
1331
  """
1327
1332
  self.axisSubsets: List[
1328
1333
  Union[RangeAxisSubsetDescriptor, ValueAxisSubsetDescriptor]
@@ -1554,7 +1559,6 @@ class BaseDocWriter(object):
1554
1559
  return ("%f" % num).rstrip("0").rstrip(".")
1555
1560
 
1556
1561
  def _addRule(self, ruleObject):
1557
- # if none of the conditions have minimum or maximum values, do not add the rule.
1558
1562
  ruleElement = ET.Element("rule")
1559
1563
  if ruleObject.name is not None:
1560
1564
  ruleElement.attrib["name"] = ruleObject.name
@@ -1575,8 +1579,9 @@ class BaseDocWriter(object):
1575
1579
  cond.get("maximum")
1576
1580
  )
1577
1581
  conditionsetElement.append(conditionElement)
1578
- if len(conditionsetElement):
1579
- ruleElement.append(conditionsetElement)
1582
+ # Serialize the conditionset even if it is empty, as this is the
1583
+ # canonical way of defining a rule that is always true.
1584
+ ruleElement.append(conditionsetElement)
1580
1585
  for sub in ruleObject.subs:
1581
1586
  subElement = ET.Element("sub")
1582
1587
  subElement.attrib["name"] = sub[0]
@@ -12,14 +12,13 @@ instance:
12
12
  from __future__ import annotations
13
13
 
14
14
  from dataclasses import dataclass
15
- from typing import Dict, Optional, Tuple, Union
15
+ from typing import Dict, Literal, Optional, Tuple, Union
16
16
  import logging
17
17
 
18
18
  from fontTools.designspaceLib import (
19
19
  AxisDescriptor,
20
20
  AxisLabelDescriptor,
21
21
  DesignSpaceDocument,
22
- DesignSpaceDocumentError,
23
22
  DiscreteAxisDescriptor,
24
23
  SimpleLocationDict,
25
24
  SourceDescriptor,
@@ -27,9 +26,13 @@ from fontTools.designspaceLib import (
27
26
 
28
27
  LOGGER = logging.getLogger(__name__)
29
28
 
30
- # TODO(Python 3.8): use Literal
31
- # RibbiStyleName = Union[Literal["regular"], Literal["bold"], Literal["italic"], Literal["bold italic"]]
32
- RibbiStyle = str
29
+ RibbiStyleName = Union[
30
+ Literal["regular"],
31
+ Literal["bold"],
32
+ Literal["italic"],
33
+ Literal["bold italic"],
34
+ ]
35
+
33
36
  BOLD_ITALIC_TO_RIBBI_STYLE = {
34
37
  (False, False): "regular",
35
38
  (False, True): "italic",
@@ -46,7 +49,7 @@ class StatNames:
46
49
  styleNames: Dict[str, str]
47
50
  postScriptFontName: Optional[str]
48
51
  styleMapFamilyNames: Dict[str, str]
49
- styleMapStyleName: Optional[RibbiStyle]
52
+ styleMapStyleName: Optional[RibbiStyleName]
50
53
 
51
54
 
52
55
  def getStatNames(
@@ -61,6 +64,10 @@ def getStatNames(
61
64
  localized names will be empty (family and style names), or the name will be
62
65
  None (PostScript name).
63
66
 
67
+ Note: this method does not consider info attached to the instance, like
68
+ family name. The user needs to override all names on an instance that STAT
69
+ information would compute differently than desired.
70
+
64
71
  .. versionadded:: 5.0
65
72
  """
66
73
  familyNames: Dict[str, str] = {}
@@ -201,7 +208,7 @@ def _getAxisLabelsForUserLocation(
201
208
 
202
209
  def _getRibbiStyle(
203
210
  self: DesignSpaceDocument, userLocation: SimpleLocationDict
204
- ) -> Tuple[RibbiStyle, SimpleLocationDict]:
211
+ ) -> Tuple[RibbiStyleName, SimpleLocationDict]:
205
212
  """Compute the RIBBI style name of the given user location,
206
213
  return the location of the matching Regular in the RIBBI group.
207
214
 
fontTools/feaLib/ast.py CHANGED
@@ -1,3 +1,4 @@
1
+ import weakref
1
2
  from fontTools.feaLib.error import FeatureLibError
2
3
  from fontTools.feaLib.location import FeatureLibLocation
3
4
  from fontTools.misc.encodingTools import getEncoding
@@ -382,8 +383,7 @@ class FeatureBlock(Block):
382
383
  def build(self, builder):
383
384
  """Call the ``start_feature`` callback on the builder object, visit
384
385
  all the statements in this feature, and then call ``end_feature``."""
385
- # TODO(sascha): Handle use_extension.
386
- builder.start_feature(self.location, self.name)
386
+ builder.start_feature(self.location, self.name, self.use_extension)
387
387
  # language exclude_dflt statements modify builder.features_
388
388
  # limit them to this block with temporary builder.features_
389
389
  features = builder.features_
@@ -433,8 +433,7 @@ class LookupBlock(Block):
433
433
  self.name, self.use_extension = name, use_extension
434
434
 
435
435
  def build(self, builder):
436
- # TODO(sascha): Handle use_extension.
437
- builder.start_lookup_block(self.location, self.name)
436
+ builder.start_lookup_block(self.location, self.name, self.use_extension)
438
437
  Block.build(self, builder)
439
438
  builder.end_lookup_block()
440
439
 
@@ -531,7 +530,7 @@ class MarkClass(object):
531
530
  def addDefinition(self, definition):
532
531
  """Add a :class:`MarkClassDefinition` statement to this mark class."""
533
532
  assert isinstance(definition, MarkClassDefinition)
534
- self.definitions.append(definition)
533
+ self.definitions.append(weakref.proxy(definition))
535
534
  for glyph in definition.glyphSet():
536
535
  if glyph in self.glyphs:
537
536
  otherLoc = self.glyphs[glyph].location
@@ -721,7 +720,7 @@ class ChainContextPosStatement(Statement):
721
720
  for i, lookup in enumerate(lookups):
722
721
  if lookup:
723
722
  try:
724
- (_ for _ in lookup)
723
+ iter(lookup)
725
724
  except TypeError:
726
725
  self.lookups[i] = [lookup]
727
726
 
@@ -753,7 +752,7 @@ class ChainContextPosStatement(Statement):
753
752
  if len(self.suffix):
754
753
  res += " " + " ".join(map(asFea, self.suffix))
755
754
  else:
756
- res += " ".join(map(asFea, self.glyph))
755
+ res += " ".join(map(asFea, self.glyphs))
757
756
  res += ";"
758
757
  return res
759
758
 
@@ -779,7 +778,7 @@ class ChainContextSubstStatement(Statement):
779
778
  for i, lookup in enumerate(lookups):
780
779
  if lookup:
781
780
  try:
782
- (_ for _ in lookup)
781
+ iter(lookup)
783
782
  except TypeError:
784
783
  self.lookups[i] = [lookup]
785
784
 
@@ -811,7 +810,7 @@ class ChainContextSubstStatement(Statement):
811
810
  if len(self.suffix):
812
811
  res += " " + " ".join(map(asFea, self.suffix))
813
812
  else:
814
- res += " ".join(map(asFea, self.glyph))
813
+ res += " ".join(map(asFea, self.glyphs))
815
814
  res += ";"
816
815
  return res
817
816
 
@@ -1512,7 +1511,9 @@ class SinglePosStatement(Statement):
1512
1511
  res += " ".join(map(asFea, self.prefix)) + " "
1513
1512
  res += " ".join(
1514
1513
  [
1515
- asFea(x[0]) + "'" + ((" " + x[1].asFea()) if x[1] else "")
1514
+ asFea(x[0])
1515
+ + "'"
1516
+ + ((" " + x[1].asFea()) if x[1] is not None else "")
1516
1517
  for x in self.pos
1517
1518
  ]
1518
1519
  )
@@ -1520,7 +1521,10 @@ class SinglePosStatement(Statement):
1520
1521
  res += " " + " ".join(map(asFea, self.suffix))
1521
1522
  else:
1522
1523
  res += " ".join(
1523
- [asFea(x[0]) + " " + (x[1].asFea() if x[1] else "") for x in self.pos]
1524
+ [
1525
+ asFea(x[0]) + " " + (x[1].asFea() if x[1] is not None else "")
1526
+ for x in self.pos
1527
+ ]
1524
1528
  )
1525
1529
  res += ";"
1526
1530
  return res
@@ -1828,15 +1832,16 @@ class BaseAxis(Statement):
1828
1832
  """An axis definition, being either a ``VertAxis.BaseTagList/BaseScriptList``
1829
1833
  pair or a ``HorizAxis.BaseTagList/BaseScriptList`` pair."""
1830
1834
 
1831
- def __init__(self, bases, scripts, vertical, location=None):
1835
+ def __init__(self, bases, scripts, vertical, minmax=None, location=None):
1832
1836
  Statement.__init__(self, location)
1833
1837
  self.bases = bases #: A list of baseline tag names as strings
1834
1838
  self.scripts = scripts #: A list of script record tuplets (script tag, default baseline tag, base coordinate)
1835
1839
  self.vertical = vertical #: Boolean; VertAxis if True, HorizAxis if False
1840
+ self.minmax = [] #: A set of minmax record
1836
1841
 
1837
1842
  def build(self, builder):
1838
1843
  """Calls the builder object's ``set_base_axis`` callback."""
1839
- builder.set_base_axis(self.bases, self.scripts, self.vertical)
1844
+ builder.set_base_axis(self.bases, self.scripts, self.vertical, self.minmax)
1840
1845
 
1841
1846
  def asFea(self, indent=""):
1842
1847
  direction = "Vert" if self.vertical else "Horiz"
@@ -1844,9 +1849,13 @@ class BaseAxis(Statement):
1844
1849
  "{} {} {}".format(a[0], a[1], " ".join(map(str, a[2])))
1845
1850
  for a in self.scripts
1846
1851
  ]
1852
+ minmaxes = [
1853
+ "\n{}Axis.MinMax {} {} {}, {};".format(direction, a[0], a[1], a[2], a[3])
1854
+ for a in self.minmax
1855
+ ]
1847
1856
  return "{}Axis.BaseTagList {};\n{}{}Axis.BaseScriptList {};".format(
1848
1857
  direction, " ".join(self.bases), indent, direction, ", ".join(scripts)
1849
- )
1858
+ ) + "\n".join(minmaxes)
1850
1859
 
1851
1860
 
1852
1861
  class OS2Field(Statement):
@@ -2098,7 +2107,7 @@ class VariationBlock(Block):
2098
2107
  def build(self, builder):
2099
2108
  """Call the ``start_feature`` callback on the builder object, visit
2100
2109
  all the statements in this feature, and then call ``end_feature``."""
2101
- builder.start_feature(self.location, self.name)
2110
+ builder.start_feature(self.location, self.name, self.use_extension)
2102
2111
  if (
2103
2112
  self.conditionset != "NULL"
2104
2113
  and self.conditionset not in builder.conditionsets_