fonttools 4.55.8__cp38-cp38-win_amd64.whl → 4.57.0__cp38-cp38-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 (37) hide show
  1. fontTools/__init__.py +1 -1
  2. fontTools/config/__init__.py +15 -0
  3. fontTools/cu2qu/cu2qu.cp38-win_amd64.pyd +0 -0
  4. fontTools/feaLib/ast.py +8 -3
  5. fontTools/feaLib/builder.py +32 -9
  6. fontTools/feaLib/error.py +1 -1
  7. fontTools/feaLib/lexer.cp38-win_amd64.pyd +0 -0
  8. fontTools/feaLib/parser.py +58 -0
  9. fontTools/misc/bezierTools.cp38-win_amd64.pyd +0 -0
  10. fontTools/misc/testTools.py +5 -1
  11. fontTools/otlLib/optimize/gpos.py +7 -1
  12. fontTools/pens/momentsPen.cp38-win_amd64.pyd +0 -0
  13. fontTools/qu2cu/qu2cu.cp38-win_amd64.pyd +0 -0
  14. fontTools/ttLib/__init__.py +4 -0
  15. fontTools/ttLib/__main__.py +47 -8
  16. fontTools/ttLib/tables/D__e_b_g.py +20 -2
  17. fontTools/ttLib/tables/_c_m_a_p.py +19 -6
  18. fontTools/ttLib/tables/_g_l_y_f.py +9 -4
  19. fontTools/ttLib/tables/_g_v_a_r.py +4 -2
  20. fontTools/ttLib/tables/otConverters.py +5 -2
  21. fontTools/ttLib/tables/otData.py +1 -1
  22. fontTools/ttLib/tables/otTables.py +26 -18
  23. fontTools/ttLib/tables/otTraverse.py +2 -1
  24. fontTools/ttLib/ttFont.py +9 -7
  25. fontTools/ttLib/ttGlyphSet.py +0 -10
  26. fontTools/ttx.py +13 -1
  27. fontTools/varLib/__init__.py +93 -101
  28. fontTools/varLib/hvar.py +113 -0
  29. fontTools/varLib/iup.cp38-win_amd64.pyd +0 -0
  30. fontTools/varLib/varStore.py +10 -38
  31. {fonttools-4.55.8.dist-info → fonttools-4.57.0.dist-info}/METADATA +23 -1
  32. {fonttools-4.55.8.dist-info → fonttools-4.57.0.dist-info}/RECORD +37 -36
  33. {fonttools-4.55.8.dist-info → fonttools-4.57.0.dist-info}/WHEEL +1 -1
  34. {fonttools-4.55.8.data → fonttools-4.57.0.data}/data/share/man/man1/ttx.1 +0 -0
  35. {fonttools-4.55.8.dist-info → fonttools-4.57.0.dist-info}/LICENSE +0 -0
  36. {fonttools-4.55.8.dist-info → fonttools-4.57.0.dist-info}/entry_points.txt +0 -0
  37. {fonttools-4.55.8.dist-info → fonttools-4.57.0.dist-info}/top_level.txt +0 -0
@@ -79,7 +79,8 @@ def bfs_base_table(
79
79
  """Breadth-first search tree of BaseTables.
80
80
 
81
81
  Args:
82
- the root of the tree.
82
+ root
83
+ the root of the tree.
83
84
  root_accessor (Optional[str]): attribute name for the root table, if any (mostly
84
85
  useful for debugging).
85
86
  skip_root (Optional[bool]): if True, the root itself is not visited, only its
fontTools/ttLib/ttFont.py CHANGED
@@ -27,6 +27,7 @@ class TTFont(object):
27
27
  they're actually accessed. This means that simple operations can be extremely fast.
28
28
 
29
29
  Example usage:
30
+
30
31
  .. code-block:: pycon
31
32
 
32
33
  >>>
@@ -39,8 +40,10 @@ class TTFont(object):
39
40
  >> tt['head'].unitsPerEm
40
41
  2048
41
42
 
42
- For details of the objects returned when accessing each table, see :ref:`tables`.
43
+ For details of the objects returned when accessing each table, see the
44
+ :doc:`tables </ttLib/tables>` documentation.
43
45
  To add a table to the font, use the :py:func:`newTable` function:
46
+
44
47
  .. code-block:: pycon
45
48
 
46
49
  >>>
@@ -50,7 +53,8 @@ class TTFont(object):
50
53
  >> font["OS/2"] = os2
51
54
 
52
55
  TrueType fonts can also be serialized to and from XML format (see also the
53
- :ref:`ttx` binary):
56
+ :doc:`ttx </ttx>` binary):
57
+
54
58
  .. code-block:: pycon
55
59
 
56
60
  >>
@@ -589,10 +593,8 @@ class TTFont(object):
589
593
  # temporary cmap and by the real cmap in case we don't find a unicode
590
594
  # cmap.
591
595
  numGlyphs = int(self["maxp"].numGlyphs)
592
- glyphOrder = [None] * numGlyphs
596
+ glyphOrder = ["glyph%.5d" % i for i in range(numGlyphs)]
593
597
  glyphOrder[0] = ".notdef"
594
- for i in range(1, numGlyphs):
595
- glyphOrder[i] = "glyph%.5d" % i
596
598
  # Set the glyph order, so the cmap parser has something
597
599
  # to work with (so we don't get called recursively).
598
600
  self.glyphOrder = glyphOrder
@@ -602,7 +604,7 @@ class TTFont(object):
602
604
  # this naming table will usually not cover all glyphs in the font.
603
605
  # If the font has no Unicode cmap table, reversecmap will be empty.
604
606
  if "cmap" in self:
605
- reversecmap = self["cmap"].buildReversed()
607
+ reversecmap = self["cmap"].buildReversedMin()
606
608
  else:
607
609
  reversecmap = {}
608
610
  useCount = {}
@@ -612,7 +614,7 @@ class TTFont(object):
612
614
  # If a font maps both U+0041 LATIN CAPITAL LETTER A and
613
615
  # U+0391 GREEK CAPITAL LETTER ALPHA to the same glyph,
614
616
  # we prefer naming the glyph as "A".
615
- glyphName = self._makeGlyphName(min(reversecmap[tempName]))
617
+ glyphName = self._makeGlyphName(reversecmap[tempName])
616
618
  numUses = useCount[glyphName] = useCount.get(glyphName, 0) + 1
617
619
  if numUses > 1:
618
620
  glyphName = "%s.alt%d" % (glyphName, numUses - 1)
@@ -104,16 +104,6 @@ class _TTGlyphSetGlyf(_TTGlyphSet):
104
104
  return _TTGlyphGlyf(self, glyphName, recalcBounds=self.recalcBounds)
105
105
 
106
106
 
107
- class _TTGlyphSetGlyf(_TTGlyphSet):
108
- def __init__(self, font, location, recalcBounds=True):
109
- self.glyfTable = font["glyf"]
110
- super().__init__(font, location, self.glyfTable, recalcBounds=recalcBounds)
111
- self.gvarTable = font.get("gvar")
112
-
113
- def __getitem__(self, glyphName):
114
- return _TTGlyphGlyf(self, glyphName, recalcBounds=self.recalcBounds)
115
-
116
-
117
107
  class _TTGlyphSetCFF(_TTGlyphSet):
118
108
  def __init__(self, font, location):
119
109
  tableTag = "CFF2" if "CFF2" in font else "CFF "
fontTools/ttx.py CHANGED
@@ -101,9 +101,15 @@ Compile options
101
101
  --with-zopfli
102
102
  Use Zopfli instead of Zlib to compress WOFF. The Python
103
103
  extension is available at https://pypi.python.org/pypi/zopfli
104
+ --optimize-font-speed
105
+ Enable optimizations that prioritize speed over file size.
106
+ This mainly affects how glyf t able and gvar / VARC tables are
107
+ compiled. The produced fonts will be larger, but rendering
108
+ performance will be improved with HarfBuzz and other text
109
+ layout engines.
104
110
  """
105
111
 
106
- from fontTools.ttLib import TTFont, TTLibError
112
+ from fontTools.ttLib import OPTIMIZE_FONT_SPEED, TTFont, TTLibError
107
113
  from fontTools.misc.macCreatorType import getMacCreatorAndType
108
114
  from fontTools.unicode import setUnicodeData
109
115
  from fontTools.misc.textTools import Tag, tostr
@@ -141,6 +147,7 @@ class Options(object):
141
147
  recalcTimestamp = None
142
148
  flavor = None
143
149
  useZopfli = False
150
+ optimizeFontSpeed = False
144
151
 
145
152
  def __init__(self, rawOptions, numFiles):
146
153
  self.onlyTables = []
@@ -229,6 +236,8 @@ class Options(object):
229
236
  self.flavor = value
230
237
  elif option == "--with-zopfli":
231
238
  self.useZopfli = True
239
+ elif option == "--optimize-font-speed":
240
+ self.optimizeFontSpeed = True
232
241
  if self.verbose and self.quiet:
233
242
  raise getopt.GetoptError("-q and -v options are mutually exclusive")
234
243
  if self.verbose:
@@ -324,6 +333,8 @@ def ttCompile(input, output, options):
324
333
  recalcBBoxes=options.recalcBBoxes,
325
334
  recalcTimestamp=options.recalcTimestamp,
326
335
  )
336
+ if options.optimizeFontSpeed:
337
+ ttf.cfg[OPTIMIZE_FONT_SPEED] = options.optimizeFontSpeed
327
338
  ttf.importXML(input)
328
339
 
329
340
  if options.recalcTimestamp is None and "head" in ttf and input is not sys.stdin:
@@ -386,6 +397,7 @@ def parseOptions(args):
386
397
  "version",
387
398
  "with-zopfli",
388
399
  "newline=",
400
+ "optimize-font-speed",
389
401
  ],
390
402
  )
391
403
 
@@ -12,13 +12,13 @@ a Glyphs source, eg., using noto-source as an example:
12
12
 
13
13
  .. code-block:: sh
14
14
 
15
- $ fontmake -o ttf-interpolatable -g NotoSansArabic-MM.glyphs
15
+ $ fontmake -o ttf-interpolatable -g NotoSansArabic-MM.glyphs
16
16
 
17
17
  Then you can make a variable-font this way:
18
18
 
19
19
  .. code-block:: sh
20
20
 
21
- $ fonttools varLib master_ufo/NotoSansArabic.designspace
21
+ $ fonttools varLib master_ufo/NotoSansArabic.designspace
22
22
 
23
23
  API *will* change in near future.
24
24
  """
@@ -325,7 +325,6 @@ def _add_gvar(font, masterModel, master_ttfs, tolerance=0.5, optimize=True):
325
325
 
326
326
  for glyph in font.getGlyphOrder():
327
327
  log.debug("building gvar for glyph '%s'", glyph)
328
- isComposite = glyf[glyph].isComposite()
329
328
 
330
329
  allData = [
331
330
  m.glyf._getCoordinatesAndControls(glyph, m.hMetrics, m.vMetrics)
@@ -363,7 +362,7 @@ def _add_gvar(font, masterModel, master_ttfs, tolerance=0.5, optimize=True):
363
362
  endPts = control.endPts
364
363
 
365
364
  for i, (delta, support) in enumerate(zip(deltas[1:], supports[1:])):
366
- if all(v == 0 for v in delta.array) and not isComposite:
365
+ if all(v == 0 for v in delta.array):
367
366
  continue
368
367
  var = TupleVariation(support, delta)
369
368
  if optimize:
@@ -372,16 +371,6 @@ def _add_gvar(font, masterModel, master_ttfs, tolerance=0.5, optimize=True):
372
371
  )
373
372
 
374
373
  if None in delta_opt:
375
- """In composite glyphs, there should be one 0 entry
376
- to make sure the gvar entry is written to the font.
377
-
378
- This is to work around an issue with macOS 10.14 and can be
379
- removed once the behaviour of macOS is changed.
380
-
381
- https://github.com/fonttools/fonttools/issues/1381
382
- """
383
- if all(d is None for d in delta_opt):
384
- delta_opt = [(0, 0)] + [None] * (len(delta_opt) - 1)
385
374
  # Use "optimized" version only if smaller...
386
375
  var_opt = TupleVariation(support, delta_opt)
387
376
 
@@ -490,7 +479,15 @@ def _merge_TTHinting(font, masterModel, master_ttfs):
490
479
 
491
480
  _MetricsFields = namedtuple(
492
481
  "_MetricsFields",
493
- ["tableTag", "metricsTag", "sb1", "sb2", "advMapping", "vOrigMapping"],
482
+ [
483
+ "tableTag",
484
+ "metricsTag",
485
+ "sb1",
486
+ "sb2",
487
+ "advMapping",
488
+ "vOrigMapping",
489
+ "phantomIndex",
490
+ ],
494
491
  )
495
492
 
496
493
  HVAR_FIELDS = _MetricsFields(
@@ -500,6 +497,7 @@ HVAR_FIELDS = _MetricsFields(
500
497
  sb2="RsbMap",
501
498
  advMapping="AdvWidthMap",
502
499
  vOrigMapping=None,
500
+ phantomIndex=0,
503
501
  )
504
502
 
505
503
  VVAR_FIELDS = _MetricsFields(
@@ -509,109 +507,43 @@ VVAR_FIELDS = _MetricsFields(
509
507
  sb2="BsbMap",
510
508
  advMapping="AdvHeightMap",
511
509
  vOrigMapping="VOrgMap",
510
+ phantomIndex=1,
512
511
  )
513
512
 
514
513
 
515
514
  def _add_HVAR(font, masterModel, master_ttfs, axisTags):
516
- _add_VHVAR(font, masterModel, master_ttfs, axisTags, HVAR_FIELDS)
515
+ getAdvanceMetrics = partial(
516
+ _get_advance_metrics, font, masterModel, master_ttfs, axisTags, HVAR_FIELDS
517
+ )
518
+ _add_VHVAR(font, axisTags, HVAR_FIELDS, getAdvanceMetrics)
517
519
 
518
520
 
519
521
  def _add_VVAR(font, masterModel, master_ttfs, axisTags):
520
- _add_VHVAR(font, masterModel, master_ttfs, axisTags, VVAR_FIELDS)
522
+ getAdvanceMetrics = partial(
523
+ _get_advance_metrics, font, masterModel, master_ttfs, axisTags, VVAR_FIELDS
524
+ )
525
+ _add_VHVAR(font, axisTags, VVAR_FIELDS, getAdvanceMetrics)
521
526
 
522
527
 
523
- def _add_VHVAR(font, masterModel, master_ttfs, axisTags, tableFields):
528
+ def _add_VHVAR(font, axisTags, tableFields, getAdvanceMetrics):
524
529
  tableTag = tableFields.tableTag
525
530
  assert tableTag not in font
531
+ glyphOrder = font.getGlyphOrder()
526
532
  log.info("Generating " + tableTag)
527
533
  VHVAR = newTable(tableTag)
528
534
  tableClass = getattr(ot, tableTag)
529
535
  vhvar = VHVAR.table = tableClass()
530
536
  vhvar.Version = 0x00010000
531
537
 
532
- glyphOrder = font.getGlyphOrder()
533
-
534
- # Build list of source font advance widths for each glyph
535
- metricsTag = tableFields.metricsTag
536
- advMetricses = [m[metricsTag].metrics for m in master_ttfs]
537
-
538
- # Build list of source font vertical origin coords for each glyph
539
- if tableTag == "VVAR" and "VORG" in master_ttfs[0]:
540
- vOrigMetricses = [m["VORG"].VOriginRecords for m in master_ttfs]
541
- defaultYOrigs = [m["VORG"].defaultVertOriginY for m in master_ttfs]
542
- vOrigMetricses = list(zip(vOrigMetricses, defaultYOrigs))
543
- else:
544
- vOrigMetricses = None
545
-
546
- metricsStore, advanceMapping, vOrigMapping = _get_advance_metrics(
547
- font,
548
- masterModel,
549
- master_ttfs,
550
- axisTags,
551
- glyphOrder,
552
- advMetricses,
553
- vOrigMetricses,
554
- )
538
+ vhAdvanceDeltasAndSupports, vOrigDeltasAndSupports = getAdvanceMetrics()
555
539
 
556
- vhvar.VarStore = metricsStore
557
- if advanceMapping is None:
558
- setattr(vhvar, tableFields.advMapping, None)
540
+ if vOrigDeltasAndSupports:
541
+ singleModel = False
559
542
  else:
560
- setattr(vhvar, tableFields.advMapping, advanceMapping)
561
- if vOrigMapping is not None:
562
- setattr(vhvar, tableFields.vOrigMapping, vOrigMapping)
563
- setattr(vhvar, tableFields.sb1, None)
564
- setattr(vhvar, tableFields.sb2, None)
565
-
566
- font[tableTag] = VHVAR
567
- return
568
-
569
-
570
- def _get_advance_metrics(
571
- font,
572
- masterModel,
573
- master_ttfs,
574
- axisTags,
575
- glyphOrder,
576
- advMetricses,
577
- vOrigMetricses=None,
578
- ):
579
- vhAdvanceDeltasAndSupports = {}
580
- vOrigDeltasAndSupports = {}
581
- # HACK: we treat width 65535 as a sentinel value to signal that a glyph
582
- # from a non-default master should not participate in computing {H,V}VAR,
583
- # as if it were missing. Allows to variate other glyph-related data independently
584
- # from glyph metrics
585
- sparse_advance = 0xFFFF
586
- for glyph in glyphOrder:
587
- vhAdvances = [
588
- (
589
- metrics[glyph][0]
590
- if glyph in metrics and metrics[glyph][0] != sparse_advance
591
- else None
592
- )
593
- for metrics in advMetricses
594
- ]
595
- vhAdvanceDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(
596
- vhAdvances, round=round
543
+ singleModel = models.allEqual(
544
+ id(v[1]) for v in vhAdvanceDeltasAndSupports.values()
597
545
  )
598
546
 
599
- singleModel = models.allEqual(id(v[1]) for v in vhAdvanceDeltasAndSupports.values())
600
-
601
- if vOrigMetricses:
602
- singleModel = False
603
- for glyph in glyphOrder:
604
- # We need to supply a vOrigs tuple with non-None default values
605
- # for each glyph. vOrigMetricses contains values only for those
606
- # glyphs which have a non-default vOrig.
607
- vOrigs = [
608
- metrics[glyph] if glyph in metrics else defaultVOrig
609
- for metrics, defaultVOrig in vOrigMetricses
610
- ]
611
- vOrigDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(
612
- vOrigs, round=round
613
- )
614
-
615
547
  directStore = None
616
548
  if singleModel:
617
549
  # Build direct mapping
@@ -632,7 +564,7 @@ def _get_advance_metrics(
632
564
  storeBuilder.setSupports(supports)
633
565
  advMapping[glyphName] = storeBuilder.storeDeltas(deltas, round=noRound)
634
566
 
635
- if vOrigMetricses:
567
+ if vOrigDeltasAndSupports:
636
568
  vOrigMap = {}
637
569
  for glyphName in glyphOrder:
638
570
  deltas, supports = vOrigDeltasAndSupports[glyphName]
@@ -644,7 +576,7 @@ def _get_advance_metrics(
644
576
  advMapping = [mapping2[advMapping[g]] for g in glyphOrder]
645
577
  advanceMapping = builder.buildVarIdxMap(advMapping, glyphOrder)
646
578
 
647
- if vOrigMetricses:
579
+ if vOrigDeltasAndSupports:
648
580
  vOrigMap = [mapping2[vOrigMap[g]] for g in glyphOrder]
649
581
 
650
582
  useDirect = False
@@ -668,10 +600,70 @@ def _get_advance_metrics(
668
600
  advanceMapping = None
669
601
  else:
670
602
  metricsStore = indirectStore
671
- if vOrigMetricses:
603
+ if vOrigDeltasAndSupports:
672
604
  vOrigMapping = builder.buildVarIdxMap(vOrigMap, glyphOrder)
673
605
 
674
- return metricsStore, advanceMapping, vOrigMapping
606
+ vhvar.VarStore = metricsStore
607
+ setattr(vhvar, tableFields.advMapping, advanceMapping)
608
+ if vOrigMapping is not None:
609
+ setattr(vhvar, tableFields.vOrigMapping, vOrigMapping)
610
+ setattr(vhvar, tableFields.sb1, None)
611
+ setattr(vhvar, tableFields.sb2, None)
612
+
613
+ font[tableTag] = VHVAR
614
+ return
615
+
616
+
617
+ def _get_advance_metrics(font, masterModel, master_ttfs, axisTags, tableFields):
618
+ tableTag = tableFields.tableTag
619
+ glyphOrder = font.getGlyphOrder()
620
+
621
+ # Build list of source font advance widths for each glyph
622
+ metricsTag = tableFields.metricsTag
623
+ advMetricses = [m[metricsTag].metrics for m in master_ttfs]
624
+
625
+ # Build list of source font vertical origin coords for each glyph
626
+ if tableTag == "VVAR" and "VORG" in master_ttfs[0]:
627
+ vOrigMetricses = [m["VORG"].VOriginRecords for m in master_ttfs]
628
+ defaultYOrigs = [m["VORG"].defaultVertOriginY for m in master_ttfs]
629
+ vOrigMetricses = list(zip(vOrigMetricses, defaultYOrigs))
630
+ else:
631
+ vOrigMetricses = None
632
+
633
+ vhAdvanceDeltasAndSupports = {}
634
+ vOrigDeltasAndSupports = {}
635
+ # HACK: we treat width 65535 as a sentinel value to signal that a glyph
636
+ # from a non-default master should not participate in computing {H,V}VAR,
637
+ # as if it were missing. Allows to variate other glyph-related data independently
638
+ # from glyph metrics
639
+ sparse_advance = 0xFFFF
640
+ for glyph in glyphOrder:
641
+ vhAdvances = [
642
+ (
643
+ metrics[glyph][0]
644
+ if glyph in metrics and metrics[glyph][0] != sparse_advance
645
+ else None
646
+ )
647
+ for metrics in advMetricses
648
+ ]
649
+ vhAdvanceDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(
650
+ vhAdvances, round=round
651
+ )
652
+
653
+ if vOrigMetricses:
654
+ for glyph in glyphOrder:
655
+ # We need to supply a vOrigs tuple with non-None default values
656
+ # for each glyph. vOrigMetricses contains values only for those
657
+ # glyphs which have a non-default vOrig.
658
+ vOrigs = [
659
+ metrics[glyph] if glyph in metrics else defaultVOrig
660
+ for metrics, defaultVOrig in vOrigMetricses
661
+ ]
662
+ vOrigDeltasAndSupports[glyph] = masterModel.getDeltasAndSupports(
663
+ vOrigs, round=round
664
+ )
665
+
666
+ return vhAdvanceDeltasAndSupports, vOrigDeltasAndSupports
675
667
 
676
668
 
677
669
  def _add_MVAR(font, masterModel, master_ttfs, axisTags):
@@ -0,0 +1,113 @@
1
+ from fontTools.misc.roundTools import noRound
2
+ from fontTools.ttLib import TTFont, newTable
3
+ from fontTools.ttLib.tables import otTables as ot
4
+ from fontTools.ttLib.tables.otBase import OTTableWriter
5
+ from fontTools.varLib import HVAR_FIELDS, VVAR_FIELDS, _add_VHVAR
6
+ from fontTools.varLib import builder, models, varStore
7
+ from fontTools.misc.fixedTools import fixedToFloat as fi2fl
8
+ from fontTools.misc.cliTools import makeOutputFileName
9
+ from functools import partial
10
+ import logging
11
+
12
+ log = logging.getLogger("fontTools.varLib.avar")
13
+
14
+
15
+ def _get_advance_metrics(font, axisTags, tableFields):
16
+ # There's two ways we can go from here:
17
+ # 1. For each glyph, at each master peak, compute the value of the
18
+ # advance width at that peak. Then pass these all to a VariationModel
19
+ # builder to compute back the deltas.
20
+ # 2. For each master peak, pull out the deltas of the advance width directly,
21
+ # and feed these to the VarStoreBuilder, forgoing the remoding step.
22
+ # We'll go with the second option, as it's simpler, faster, and more direct.
23
+ gvar = font["gvar"]
24
+ vhAdvanceDeltasAndSupports = {}
25
+ glyphOrder = font.getGlyphOrder()
26
+ phantomIndex = tableFields.phantomIndex
27
+ for glyphName in glyphOrder:
28
+ supports = []
29
+ deltas = []
30
+ variations = gvar.variations.get(glyphName, [])
31
+
32
+ for tv in variations:
33
+ supports.append(tv.axes)
34
+ phantoms = tv.coordinates[-4:]
35
+ phantoms = phantoms[phantomIndex * 2 : phantomIndex * 2 + 2]
36
+ assert len(phantoms) == 2
37
+ phantoms[0] = phantoms[0][phantomIndex] if phantoms[0] is not None else 0
38
+ phantoms[1] = phantoms[1][phantomIndex] if phantoms[1] is not None else 0
39
+ deltas.append(phantoms[1] - phantoms[0])
40
+
41
+ vhAdvanceDeltasAndSupports[glyphName] = (deltas, supports)
42
+
43
+ vOrigDeltasAndSupports = None # TODO
44
+
45
+ return vhAdvanceDeltasAndSupports, vOrigDeltasAndSupports
46
+
47
+
48
+ def add_HVAR(font):
49
+ if "HVAR" in font:
50
+ del font["HVAR"]
51
+ axisTags = [axis.axisTag for axis in font["fvar"].axes]
52
+ getAdvanceMetrics = partial(_get_advance_metrics, font, axisTags, HVAR_FIELDS)
53
+ _add_VHVAR(font, axisTags, HVAR_FIELDS, getAdvanceMetrics)
54
+
55
+
56
+ def add_VVAR(font):
57
+ if "VVAR" in font:
58
+ del font["VVAR"]
59
+ getAdvanceMetrics = partial(_get_advance_metrics, font, axisTags, HVAR_FIELDS)
60
+ axisTags = [axis.axisTag for axis in font["fvar"].axes]
61
+ _add_VHVAR(font, axisTags, VVAR_FIELDS, getAdvanceMetrics)
62
+
63
+
64
+ def main(args=None):
65
+ """Add `HVAR` table to variable font."""
66
+
67
+ if args is None:
68
+ import sys
69
+
70
+ args = sys.argv[1:]
71
+
72
+ from fontTools import configLogger
73
+ from fontTools.designspaceLib import DesignSpaceDocument
74
+ import argparse
75
+
76
+ parser = argparse.ArgumentParser(
77
+ "fonttools varLib.hvar",
78
+ description="Add `HVAR` table from to variable font.",
79
+ )
80
+ parser.add_argument("font", metavar="varfont.ttf", help="Variable-font file.")
81
+ parser.add_argument(
82
+ "-o",
83
+ "--output-file",
84
+ type=str,
85
+ help="Output font file name.",
86
+ )
87
+
88
+ options = parser.parse_args(args)
89
+
90
+ configLogger(level="WARNING")
91
+
92
+ font = TTFont(options.font)
93
+ if not "fvar" in font:
94
+ log.error("Not a variable font.")
95
+ return 1
96
+
97
+ add_HVAR(font)
98
+ if "vmtx" in font:
99
+ add_VVAR(font)
100
+
101
+ if options.output_file is None:
102
+ outfile = makeOutputFileName(options.font, overWrite=True, suffix=".hvar")
103
+ else:
104
+ outfile = options.output_file
105
+ if outfile:
106
+ log.info("Saving %s", outfile)
107
+ font.save(outfile)
108
+
109
+
110
+ if __name__ == "__main__":
111
+ import sys
112
+
113
+ sys.exit(main())
Binary file
@@ -41,7 +41,7 @@ class OnlineVarStoreBuilder(object):
41
41
  def setSupports(self, supports):
42
42
  self._model = None
43
43
  self._supports = list(supports)
44
- if not self._supports[0]:
44
+ if self._supports and not self._supports[0]:
45
45
  del self._supports[0] # Drop base master support
46
46
  self._cache = None
47
47
  self._data = None
@@ -412,25 +412,6 @@ class _Encoding(object):
412
412
  def extend(self, lst):
413
413
  self.items.update(lst)
414
414
 
415
- def get_room(self):
416
- """Maximum number of bytes that can be added to characteristic
417
- while still being beneficial to merge it into another one."""
418
- count = len(self.items)
419
- return max(0, (self.overhead - 1) // count - self.width)
420
-
421
- room = property(get_room)
422
-
423
- def get_gain(self):
424
- """Maximum possible byte gain from merging this into another
425
- characteristic."""
426
- count = len(self.items)
427
- return max(0, self.overhead - count)
428
-
429
- gain = property(get_gain)
430
-
431
- def gain_sort_key(self):
432
- return self.gain, self.chars
433
-
434
415
  def width_sort_key(self):
435
416
  return self.width, self.chars
436
417
 
@@ -534,13 +515,9 @@ def VarStore_optimize(self, use_NO_VARIATION_INDEX=True, quantization=1):
534
515
  # of the old encoding is completely eliminated. However, each row
535
516
  # now would require more bytes to encode, to the tune of one byte
536
517
  # per characteristic bit that is active in the new encoding but not
537
- # in the old one. The number of bits that can be added to an encoding
538
- # while still beneficial to merge it into another encoding is called
539
- # the "room" for that encoding.
518
+ # in the old one.
540
519
  #
541
- # The "gain" of an encodings is the maximum number of bytes we can
542
- # save by merging it into another encoding. The "gain" of merging
543
- # two encodings is how many bytes we save by doing so.
520
+ # The "gain" of merging two encodings is how many bytes we save by doing so.
544
521
  #
545
522
  # High-level algorithm:
546
523
  #
@@ -554,7 +531,11 @@ def VarStore_optimize(self, use_NO_VARIATION_INDEX=True, quantization=1):
554
531
  #
555
532
  # - Put all encodings into a "todo" list.
556
533
  #
557
- # - Sort todo list by decreasing gain (for stability).
534
+ # - Sort todo list (for stability) by width_sort_key(), which is a tuple
535
+ # of the following items:
536
+ # * The "width" of the encoding.
537
+ # * The characteristic bitmap of the encoding, with higher-numbered
538
+ # columns compared first.
558
539
  #
559
540
  # - Make a priority-queue of the gain from combining each two
560
541
  # encodings in the todo list. The priority queue is sorted by
@@ -575,16 +556,7 @@ def VarStore_optimize(self, use_NO_VARIATION_INDEX=True, quantization=1):
575
556
  #
576
557
  # The output is then sorted for stability, in the following way:
577
558
  # - The VarRegionList of the input is kept intact.
578
- # - All encodings are sorted before the main algorithm, by
579
- # gain_key_sort(), which is a tuple of the following items:
580
- # * The gain of the encoding.
581
- # * The characteristic bitmap of the encoding, with higher-numbered
582
- # columns compared first.
583
- # - The VarData is sorted by width_sort_key(), which is a tuple
584
- # of the following items:
585
- # * The "width" of the encoding.
586
- # * The characteristic bitmap of the encoding, with higher-numbered
587
- # columns compared first.
559
+ # - The VarData is sorted by the same width_sort_key() used at the beginning.
588
560
  # - Within each VarData, the items are sorted as vectors of numbers.
589
561
  #
590
562
  # Finally, each VarData is optimized to remove the empty columns and
@@ -626,7 +598,7 @@ def VarStore_optimize(self, use_NO_VARIATION_INDEX=True, quantization=1):
626
598
  front_mapping[(major << 16) + minor] = row
627
599
 
628
600
  # Prepare for the main algorithm.
629
- todo = sorted(encodings.values(), key=_Encoding.gain_sort_key)
601
+ todo = sorted(encodings.values(), key=_Encoding.width_sort_key)
630
602
  del encodings
631
603
 
632
604
  # Repeatedly pick two best encodings to combine, and combine them.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fonttools
3
- Version: 4.55.8
3
+ Version: 4.57.0
4
4
  Summary: Tools to manipulate font files
5
5
  Home-page: http://github.com/fonttools/fonttools
6
6
  Author: Just van Rossum
@@ -378,6 +378,28 @@ Have fun!
378
378
  Changelog
379
379
  ~~~~~~~~~
380
380
 
381
+ 4.57.0 (released 2025-04-03)
382
+ ----------------------------
383
+
384
+ - [ttLib.__main__] Add `--no-recalc-timestamp` flag (#3771)
385
+ - [ttLib.__main__] Add `-b` (recalcBBoxes=False) flag (#3772)
386
+ - [cmap] Speed up glyphOrder loading from cmap (#3774)
387
+ - [ttLib.__main__] Improvements around the `-t` flag (#3776)
388
+ - [Debg] Fix parsing from XML; add roundtrip tests (#3781)
389
+ - [fealib] Support \*Base.MinMax tables (#3783, #3786)
390
+ - [config] Add OPTIMIZE_FONT_SPEED (#3784)
391
+ - [varLib.hvar] New module to add HVAR table to the font (#3780)
392
+ - [otlLib.optimize] Fix crash when the provided TTF does not contain a `GPOS` (#3794)
393
+
394
+ 4.56.0 (released 2025-02-07)
395
+ ----------------------------
396
+
397
+ - [varStore] Sort the input todo list with the same sorting key used for the opimizer's output (#3767).
398
+ - [otData] Fix DeviceTable's ``DeltaValue`` repeat value which caused a crash after importing from XML and then compiling a GPOS containing Device tables (#3758).
399
+ - [feaLib] Make ``FeatureLibError`` pickleable, so client can e.g. use feaLib to can compile features in parallel with multiprocessing (#3762).
400
+ - [varLib/gvar] Removed workaround for old, long-fixed macOS bug about composite glyphs with all zero deltas (#1381, #1788).
401
+ - [Docs] Updated ttLib documentation, beefed up TTFont and TTGlyphSet explanations (#3720).
402
+
381
403
  4.55.8 (released 2025-01-29)
382
404
  ----------------------------
383
405