fonttools 4.59.0__cp313-cp313-win32.whl → 4.59.2__cp313-cp313-win32.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (38) 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 +760 -653
  5. fontTools/cu2qu/cu2qu.cp313-win32.pyd +0 -0
  6. fontTools/cu2qu/cu2qu.py +17 -2
  7. fontTools/feaLib/builder.py +15 -4
  8. fontTools/feaLib/lexer.c +30 -16
  9. fontTools/feaLib/lexer.cp313-win32.pyd +0 -0
  10. fontTools/feaLib/parser.py +11 -1
  11. fontTools/feaLib/variableScalar.py +6 -1
  12. fontTools/misc/bezierTools.c +33 -19
  13. fontTools/misc/bezierTools.cp313-win32.pyd +0 -0
  14. fontTools/misc/psCharStrings.py +17 -2
  15. fontTools/misc/textTools.py +4 -2
  16. fontTools/pens/momentsPen.c +20 -14
  17. fontTools/pens/momentsPen.cp313-win32.pyd +0 -0
  18. fontTools/qu2cu/qu2cu.c +32 -18
  19. fontTools/qu2cu/qu2cu.cp313-win32.pyd +0 -0
  20. fontTools/subset/__init__.py +1 -0
  21. fontTools/ttLib/tables/_a_v_a_r.py +4 -2
  22. fontTools/ttLib/tables/_g_v_a_r.py +6 -3
  23. fontTools/ttLib/tables/_h_m_t_x.py +7 -3
  24. fontTools/ttLib/tables/_n_a_m_e.py +11 -6
  25. fontTools/varLib/__init__.py +80 -1
  26. fontTools/varLib/featureVars.py +8 -0
  27. fontTools/varLib/instancer/__init__.py +120 -22
  28. fontTools/varLib/iup.c +32 -18
  29. fontTools/varLib/iup.cp313-win32.pyd +0 -0
  30. fontTools/varLib/mutator.py +11 -0
  31. {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/METADATA +38 -10
  32. {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/RECORD +38 -38
  33. {fonttools-4.59.0.data → fonttools-4.59.2.data}/data/share/man/man1/ttx.1 +0 -0
  34. {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/WHEEL +0 -0
  35. {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/entry_points.txt +0 -0
  36. {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/licenses/LICENSE +0 -0
  37. {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/licenses/LICENSE.external +0 -0
  38. {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/top_level.txt +0 -0
@@ -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"
fontTools/varLib/iup.c CHANGED
@@ -1,4 +1,4 @@
1
- /* Generated by Cython 3.1.2 */
1
+ /* Generated by Cython 3.1.3 */
2
2
 
3
3
  /* BEGIN: Cython Metadata
4
4
  {
@@ -26,8 +26,8 @@ END: Cython Metadata */
26
26
  #elif PY_VERSION_HEX < 0x03080000
27
27
  #error Cython requires Python 3.8+.
28
28
  #else
29
- #define __PYX_ABI_VERSION "3_1_2"
30
- #define CYTHON_HEX_VERSION 0x030102F0
29
+ #define __PYX_ABI_VERSION "3_1_3"
30
+ #define CYTHON_HEX_VERSION 0x030103F0
31
31
  #define CYTHON_FUTURE_DIVISION 1
32
32
  /* CModulePreamble */
33
33
  #include <stddef.h>
@@ -390,6 +390,9 @@ END: Cython Metadata */
390
390
  enum { __pyx_check_sizeof_voidp = 1 / (int)(SIZEOF_VOID_P == sizeof(void*)) };
391
391
  #endif
392
392
  #endif
393
+ #ifndef CYTHON_LOCK_AND_GIL_DEADLOCK_AVOIDANCE_TIME
394
+ #define CYTHON_LOCK_AND_GIL_DEADLOCK_AVOIDANCE_TIME 100
395
+ #endif
393
396
  #ifndef __has_attribute
394
397
  #define __has_attribute(x) 0
395
398
  #endif
@@ -2141,7 +2144,7 @@ static int __Pyx_IternextUnpackEndCheck(PyObject *retval, Py_ssize_t expected);
2141
2144
  static PyObject *__Pyx_PyLong_AbsNeg(PyObject *num);
2142
2145
  #define __Pyx_PyNumber_Absolute(x)\
2143
2146
  ((likely(PyLong_CheckExact(x))) ?\
2144
- (likely(__Pyx_PyLong_IsNonNeg(x)) ? (Py_INCREF(x), (x)) : __Pyx_PyLong_AbsNeg(x)) :\
2147
+ (likely(__Pyx_PyLong_IsNonNeg(x)) ? __Pyx_NewRef(x) : __Pyx_PyLong_AbsNeg(x)) :\
2145
2148
  PyNumber_Absolute(x))
2146
2149
  #else
2147
2150
  #define __Pyx_PyNumber_Absolute(x) PyNumber_Absolute(x)
@@ -12093,16 +12096,15 @@ static int __Pyx_InitConstants(__pyx_mstatetype *__pyx_mstate) {
12093
12096
  return -1;
12094
12097
  }
12095
12098
  /* #### Code section: init_codeobjects ### */
12096
- \
12097
- typedef struct {
12098
- unsigned int argcount : 3;
12099
- unsigned int num_posonly_args : 1;
12100
- unsigned int num_kwonly_args : 1;
12101
- unsigned int nlocals : 5;
12102
- unsigned int flags : 10;
12103
- unsigned int first_line : 9;
12104
- unsigned int line_table_length : 14;
12105
- } __Pyx_PyCode_New_function_description;
12099
+ typedef struct {
12100
+ unsigned int argcount : 3;
12101
+ unsigned int num_posonly_args : 1;
12102
+ unsigned int num_kwonly_args : 1;
12103
+ unsigned int nlocals : 5;
12104
+ unsigned int flags : 10;
12105
+ unsigned int first_line : 9;
12106
+ unsigned int line_table_length : 14;
12107
+ } __Pyx_PyCode_New_function_description;
12106
12108
  /* NewCodeObj.proto */
12107
12109
  static PyObject* __Pyx_PyCode_New(
12108
12110
  const __Pyx_PyCode_New_function_description descr,
@@ -14329,7 +14331,7 @@ static PyObject *__Pyx_PyLong_AbsNeg(PyObject *n) {
14329
14331
  PyObject *copy = _PyLong_Copy((PyLongObject*)n);
14330
14332
  if (likely(copy)) {
14331
14333
  #if PY_VERSION_HEX >= 0x030C00A7
14332
- ((PyLongObject*)copy)->long_value.lv_tag = ((PyLongObject*)copy)->long_value.lv_tag & ~_PyLong_SIGN_MASK;
14334
+ ((PyLongObject*)copy)->long_value.lv_tag ^= ((PyLongObject*)copy)->long_value.lv_tag & _PyLong_SIGN_MASK;
14333
14335
  #else
14334
14336
  __Pyx_SET_SIZE(copy, -Py_SIZE(copy));
14335
14337
  #endif
@@ -14942,6 +14944,7 @@ static int __Pyx_fix_up_extension_type_from_spec(PyType_Spec *spec, PyTypeObject
14942
14944
  changed = 1;
14943
14945
  }
14944
14946
  #endif // CYTHON_METH_FASTCALL
14947
+ #if !CYTHON_COMPILING_IN_PYPY
14945
14948
  else if (strcmp(memb->name, "__module__") == 0) {
14946
14949
  PyObject *descr;
14947
14950
  assert(memb->type == T_OBJECT);
@@ -14956,11 +14959,13 @@ static int __Pyx_fix_up_extension_type_from_spec(PyType_Spec *spec, PyTypeObject
14956
14959
  }
14957
14960
  changed = 1;
14958
14961
  }
14962
+ #endif // !CYTHON_COMPILING_IN_PYPY
14959
14963
  }
14960
14964
  memb++;
14961
14965
  }
14962
14966
  }
14963
14967
  #endif // !CYTHON_COMPILING_IN_LIMITED_API
14968
+ #if !CYTHON_COMPILING_IN_PYPY
14964
14969
  slot = spec->slots;
14965
14970
  while (slot && slot->slot && slot->slot != Py_tp_getset)
14966
14971
  slot++;
@@ -14992,6 +14997,7 @@ static int __Pyx_fix_up_extension_type_from_spec(PyType_Spec *spec, PyTypeObject
14992
14997
  ++getset;
14993
14998
  }
14994
14999
  }
15000
+ #endif // !CYTHON_COMPILING_IN_PYPY
14995
15001
  if (changed)
14996
15002
  PyType_Modified(type);
14997
15003
  #endif // PY_VERSION_HEX > 0x030900B1
@@ -15090,6 +15096,13 @@ try_unpack:
15090
15096
 
15091
15097
  /* PyObjectCallMethod0 */
15092
15098
  static PyObject* __Pyx_PyObject_CallMethod0(PyObject* obj, PyObject* method_name) {
15099
+ #if CYTHON_VECTORCALL && (__PYX_LIMITED_VERSION_HEX >= 0x030C0000 || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x03090000))
15100
+ PyObject *args[1] = {obj};
15101
+ (void) __Pyx_PyObject_GetMethod;
15102
+ (void) __Pyx_PyObject_CallOneArg;
15103
+ (void) __Pyx_PyObject_CallNoArg;
15104
+ return PyObject_VectorcallMethod(method_name, args, 1 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
15105
+ #else
15093
15106
  PyObject *method = NULL, *result = NULL;
15094
15107
  int is_method = __Pyx_PyObject_GetMethod(obj, method_name, &method);
15095
15108
  if (likely(is_method)) {
@@ -15102,6 +15115,7 @@ static PyObject* __Pyx_PyObject_CallMethod0(PyObject* obj, PyObject* method_name
15102
15115
  Py_DECREF(method);
15103
15116
  bad:
15104
15117
  return result;
15118
+ #endif
15105
15119
  }
15106
15120
 
15107
15121
  /* ValidateBasesTuple */
@@ -15489,7 +15503,7 @@ bad:
15489
15503
  }
15490
15504
 
15491
15505
  /* CommonTypesMetaclass */
15492
- PyObject* __pyx_CommonTypesMetaclass_get_module(CYTHON_UNUSED PyObject *self, CYTHON_UNUSED void* context) {
15506
+ static PyObject* __pyx_CommonTypesMetaclass_get_module(CYTHON_UNUSED PyObject *self, CYTHON_UNUSED void* context) {
15493
15507
  return PyUnicode_FromString(__PYX_ABI_MODULE_NAME);
15494
15508
  }
15495
15509
  static PyGetSetDef __pyx_CommonTypesMetaclass_getset[] = {
@@ -17939,7 +17953,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_Call2Args(PyObject* function, PyOb
17939
17953
  }
17940
17954
 
17941
17955
  /* PyObjectCallMethod1 */
17942
- #if !(CYTHON_VECTORCALL && __PYX_LIMITED_VERSION_HEX >= 0x030C0000)
17956
+ #if !(CYTHON_VECTORCALL && (__PYX_LIMITED_VERSION_HEX >= 0x030C0000 || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x03090000)))
17943
17957
  static PyObject* __Pyx__PyObject_CallMethod1(PyObject* method, PyObject* arg) {
17944
17958
  PyObject *result = __Pyx_PyObject_CallOneArg(method, arg);
17945
17959
  Py_DECREF(method);
@@ -17947,7 +17961,7 @@ static PyObject* __Pyx__PyObject_CallMethod1(PyObject* method, PyObject* arg) {
17947
17961
  }
17948
17962
  #endif
17949
17963
  static PyObject* __Pyx_PyObject_CallMethod1(PyObject* obj, PyObject* method_name, PyObject* arg) {
17950
- #if CYTHON_VECTORCALL && __PYX_LIMITED_VERSION_HEX >= 0x030C0000
17964
+ #if CYTHON_VECTORCALL && (__PYX_LIMITED_VERSION_HEX >= 0x030C0000 || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x03090000))
17951
17965
  PyObject *args[2] = {obj, arg};
17952
17966
  (void) __Pyx_PyObject_GetMethod;
17953
17967
  (void) __Pyx_PyObject_CallOneArg;
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