fonttools 4.59.1__cp310-cp310-win_amd64.whl → 4.60.0__cp310-cp310-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 (51) hide show
  1. fontTools/__init__.py +1 -1
  2. fontTools/annotations.py +30 -0
  3. fontTools/cu2qu/cu2qu.c +1185 -971
  4. fontTools/cu2qu/cu2qu.cp310-win_amd64.pyd +0 -0
  5. fontTools/cu2qu/cu2qu.py +36 -4
  6. fontTools/feaLib/builder.py +9 -3
  7. fontTools/feaLib/lexer.c +18 -12
  8. fontTools/feaLib/lexer.cp310-win_amd64.pyd +0 -0
  9. fontTools/feaLib/parser.py +11 -1
  10. fontTools/feaLib/variableScalar.py +6 -1
  11. fontTools/misc/bezierTools.c +18 -12
  12. fontTools/misc/bezierTools.cp310-win_amd64.pyd +0 -0
  13. fontTools/misc/enumTools.py +23 -0
  14. fontTools/misc/textTools.py +4 -2
  15. fontTools/pens/filterPen.py +218 -26
  16. fontTools/pens/momentsPen.c +18 -12
  17. fontTools/pens/momentsPen.cp310-win_amd64.pyd +0 -0
  18. fontTools/pens/pointPen.py +40 -6
  19. fontTools/qu2cu/qu2cu.c +30 -16
  20. fontTools/qu2cu/qu2cu.cp310-win_amd64.pyd +0 -0
  21. fontTools/subset/__init__.py +1 -0
  22. fontTools/ttLib/tables/_a_v_a_r.py +4 -2
  23. fontTools/ttLib/tables/_n_a_m_e.py +11 -6
  24. fontTools/ttLib/tables/_p_o_s_t.py +5 -5
  25. fontTools/ufoLib/__init__.py +279 -176
  26. fontTools/ufoLib/converters.py +14 -5
  27. fontTools/ufoLib/filenames.py +16 -6
  28. fontTools/ufoLib/glifLib.py +286 -190
  29. fontTools/ufoLib/kerning.py +32 -12
  30. fontTools/ufoLib/utils.py +41 -13
  31. fontTools/ufoLib/validators.py +121 -97
  32. fontTools/varLib/__init__.py +80 -1
  33. fontTools/varLib/avar/__init__.py +0 -0
  34. fontTools/varLib/avar/__main__.py +72 -0
  35. fontTools/varLib/avar/build.py +79 -0
  36. fontTools/varLib/avar/map.py +108 -0
  37. fontTools/varLib/avar/plan.py +1004 -0
  38. fontTools/varLib/{avar.py → avar/unbuild.py} +70 -59
  39. fontTools/varLib/avarPlanner.py +3 -999
  40. fontTools/varLib/instancer/__init__.py +56 -18
  41. fontTools/varLib/interpolatableHelpers.py +3 -0
  42. fontTools/varLib/iup.c +24 -14
  43. fontTools/varLib/iup.cp310-win_amd64.pyd +0 -0
  44. {fonttools-4.59.1.dist-info → fonttools-4.60.0.dist-info}/METADATA +43 -2
  45. {fonttools-4.59.1.dist-info → fonttools-4.60.0.dist-info}/RECORD +51 -44
  46. {fonttools-4.59.1.data → fonttools-4.60.0.data}/data/share/man/man1/ttx.1 +0 -0
  47. {fonttools-4.59.1.dist-info → fonttools-4.60.0.dist-info}/WHEEL +0 -0
  48. {fonttools-4.59.1.dist-info → fonttools-4.60.0.dist-info}/entry_points.txt +0 -0
  49. {fonttools-4.59.1.dist-info → fonttools-4.60.0.dist-info}/licenses/LICENSE +0 -0
  50. {fonttools-4.59.1.dist-info → fonttools-4.60.0.dist-info}/licenses/LICENSE.external +0 -0
  51. {fonttools-4.59.1.dist-info → fonttools-4.60.0.dist-info}/top_level.txt +0 -0
@@ -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:
File without changes
@@ -0,0 +1,72 @@
1
+ import logging
2
+
3
+ log = logging.getLogger("fontTools.varLib.avar")
4
+
5
+
6
+ def main(args=None):
7
+ from fontTools.ttLib import TTFont
8
+ from fontTools.misc.cliTools import makeOutputFileName
9
+ from fontTools import configLogger
10
+ import argparse
11
+ import sys
12
+
13
+ print(
14
+ "WARNING: This script is deprecated. Use `fonttools varLib.avar.build` "
15
+ "or `fonttools varLib.avar.unbuild` instead.\n",
16
+ file=sys.stderr,
17
+ )
18
+
19
+ if args is None:
20
+ args = sys.argv[1:]
21
+
22
+ parser = argparse.ArgumentParser(
23
+ "fonttools varLib.avar",
24
+ description="Add `avar` table from designspace file to variable font.",
25
+ )
26
+ parser.add_argument("font", metavar="varfont.ttf", help="Variable-font file.")
27
+ parser.add_argument(
28
+ "designspace",
29
+ metavar="family.designspace",
30
+ help="Designspace file.",
31
+ nargs="?",
32
+ default=None,
33
+ )
34
+ parser.add_argument(
35
+ "-o",
36
+ "--output-file",
37
+ type=str,
38
+ help="Output font file name.",
39
+ )
40
+ parser.add_argument(
41
+ "-v", "--verbose", action="store_true", help="Run more verbosely."
42
+ )
43
+
44
+ options = parser.parse_args(args)
45
+
46
+ configLogger(level=("INFO" if options.verbose else "WARNING"))
47
+
48
+ font = TTFont(options.font)
49
+
50
+ if options.designspace is None:
51
+ from .unbuild import unbuild
52
+
53
+ unbuild(font)
54
+ return 0
55
+
56
+ from .build import build
57
+
58
+ build(font, options.designspace)
59
+
60
+ if options.output_file is None:
61
+ outfile = makeOutputFileName(options.font, overWrite=True, suffix=".avar")
62
+ else:
63
+ outfile = options.output_file
64
+ if outfile:
65
+ log.info("Saving %s", outfile)
66
+ font.save(outfile)
67
+
68
+
69
+ if __name__ == "__main__":
70
+ import sys
71
+
72
+ sys.exit(main())
@@ -0,0 +1,79 @@
1
+ from fontTools.varLib import _add_fvar, _add_avar, load_designspace
2
+ from fontTools.ttLib import newTable
3
+ import logging
4
+
5
+ log = logging.getLogger("fontTools.varLib.avar")
6
+
7
+
8
+ def build(font, designspace_file):
9
+ ds = load_designspace(designspace_file, require_sources=False)
10
+
11
+ if not "fvar" in font:
12
+ # if "name" not in font:
13
+ font["name"] = newTable("name")
14
+ _add_fvar(font, ds.axes, ds.instances)
15
+
16
+ axisTags = [a.axisTag for a in font["fvar"].axes]
17
+
18
+ if "avar" in font:
19
+ log.warning("avar table already present, overwriting.")
20
+ del font["avar"]
21
+
22
+ _add_avar(font, ds.axes, ds.axisMappings, axisTags)
23
+
24
+
25
+ def main(args=None):
26
+ """Add `avar` table from designspace file to variable font."""
27
+
28
+ from fontTools.ttLib import TTFont
29
+ from fontTools.misc.cliTools import makeOutputFileName
30
+ from fontTools import configLogger
31
+ import argparse
32
+
33
+ if args is None:
34
+ import sys
35
+
36
+ args = sys.argv[1:]
37
+
38
+ parser = argparse.ArgumentParser(
39
+ "fonttools varLib.avar.build",
40
+ description="Add `avar` table from designspace file to variable font.",
41
+ )
42
+ parser.add_argument("font", metavar="varfont.ttf", help="Variable-font file.")
43
+ parser.add_argument(
44
+ "designspace",
45
+ metavar="family.designspace",
46
+ help="Designspace file.",
47
+ default=None,
48
+ )
49
+ parser.add_argument(
50
+ "-o",
51
+ "--output-file",
52
+ type=str,
53
+ help="Output font file name.",
54
+ )
55
+ parser.add_argument(
56
+ "-v", "--verbose", action="store_true", help="Run more verbosely."
57
+ )
58
+
59
+ options = parser.parse_args(args)
60
+
61
+ configLogger(level=("INFO" if options.verbose else "WARNING"))
62
+
63
+ font = TTFont(options.font)
64
+
65
+ build(font, options.designspace)
66
+
67
+ if options.output_file is None:
68
+ outfile = makeOutputFileName(options.font, overWrite=True, suffix=".avar")
69
+ else:
70
+ outfile = options.output_file
71
+ if outfile:
72
+ log.info("Saving %s", outfile)
73
+ font.save(outfile)
74
+
75
+
76
+ if __name__ == "__main__":
77
+ import sys
78
+
79
+ sys.exit(main())
@@ -0,0 +1,108 @@
1
+ from fontTools.varLib.models import normalizeValue
2
+
3
+
4
+ def _denormalize(v, triplet):
5
+ if v >= 0:
6
+ return triplet[1] + v * (triplet[2] - triplet[1])
7
+ else:
8
+ return triplet[1] + v * (triplet[1] - triplet[0])
9
+
10
+
11
+ def map(
12
+ font, location, *, inputNormalized=False, outputNormalized=False, dropZeroes=False
13
+ ):
14
+ if "fvar" not in font:
15
+ return None
16
+
17
+ fvar = font["fvar"]
18
+ axes = {a.axisTag: (a.minValue, a.defaultValue, a.maxValue) for a in fvar.axes}
19
+
20
+ if not inputNormalized:
21
+ location = {
22
+ tag: normalizeValue(value, axes[tag]) for tag, value in location.items()
23
+ }
24
+
25
+ if "avar" in font:
26
+ location = font["avar"].renormalizeLocation(location, font, dropZeroes)
27
+
28
+ if not outputNormalized:
29
+ location = {
30
+ tag: _denormalize(value, axes[tag]) for tag, value in location.items()
31
+ }
32
+
33
+ return location
34
+
35
+
36
+ def main(args=None):
37
+ """Map variation coordinates through the `avar` table."""
38
+
39
+ from fontTools.ttLib import TTFont
40
+ import argparse
41
+
42
+ if args is None:
43
+ import sys
44
+
45
+ args = sys.argv[1:]
46
+
47
+ parser = argparse.ArgumentParser(
48
+ "fonttools varLib.avar.map",
49
+ description="Map variation coordinates through the `avar` table.",
50
+ )
51
+ parser.add_argument("font", metavar="varfont.ttf", help="Variable-font file.")
52
+ parser.add_argument(
53
+ "coords",
54
+ metavar="[AXIS=value...]",
55
+ help="Coordinates to map, e.g. 'wght=700 wdth=75'.",
56
+ nargs="*",
57
+ default=None,
58
+ )
59
+ parser.add_argument(
60
+ "-f", action="store_true", help="Do not omit axes at default location."
61
+ )
62
+ parser.add_argument(
63
+ "-i", action="store_true", help="Input coordinates are normalized (-1..1)."
64
+ )
65
+ parser.add_argument(
66
+ "-o", action="store_true", help="Output coordinates as normalized (-1..1)."
67
+ )
68
+
69
+ options = parser.parse_args(args)
70
+
71
+ if not options.coords:
72
+ parser.error(
73
+ "No coordinates provided. Please specify at least one axis coordinate (e.g., wght=500)"
74
+ )
75
+
76
+ if options.font.endswith(".designspace"):
77
+ from .build import build
78
+
79
+ font = TTFont()
80
+ build(font, options.font)
81
+ else:
82
+ font = TTFont(options.font)
83
+ if "fvar" not in font:
84
+ parser.error(f"Font '{options.font}' does not contain an 'fvar' table.")
85
+
86
+ location = {
87
+ tag: float(value) for tag, value in (item.split("=") for item in options.coords)
88
+ }
89
+
90
+ mapped = map(
91
+ font,
92
+ location,
93
+ inputNormalized=options.i,
94
+ outputNormalized=options.o,
95
+ dropZeroes=not options.f,
96
+ )
97
+ assert mapped is not None
98
+
99
+ for tag in mapped:
100
+ v = mapped[tag]
101
+ v = int(v) if v == int(v) else v
102
+ print(f"{tag}={v:g}")
103
+
104
+
105
+ if __name__ == "__main__":
106
+ import sys
107
+
108
+ sys.exit(main())