map.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. from fontTools.varLib.models import normalizeValue
  2. def _denormalize(v, triplet):
  3. if v >= 0:
  4. return triplet[1] + v * (triplet[2] - triplet[1])
  5. else:
  6. return triplet[1] + v * (triplet[1] - triplet[0])
  7. def map(
  8. font, location, *, inputNormalized=False, outputNormalized=False, dropZeroes=False
  9. ):
  10. if "fvar" not in font:
  11. return None
  12. fvar = font["fvar"]
  13. axes = {a.axisTag: (a.minValue, a.defaultValue, a.maxValue) for a in fvar.axes}
  14. if not inputNormalized:
  15. location = {
  16. tag: normalizeValue(value, axes[tag]) for tag, value in location.items()
  17. }
  18. if "avar" in font:
  19. location = font["avar"].renormalizeLocation(location, font, dropZeroes)
  20. if not outputNormalized:
  21. location = {
  22. tag: _denormalize(value, axes[tag]) for tag, value in location.items()
  23. }
  24. return location
  25. def main(args=None):
  26. """Map variation coordinates through the `avar` table."""
  27. from fontTools.ttLib import TTFont
  28. import argparse
  29. if args is None:
  30. import sys
  31. args = sys.argv[1:]
  32. parser = argparse.ArgumentParser(
  33. "fonttools varLib.avar.map",
  34. description="Map variation coordinates through the `avar` table.",
  35. )
  36. parser.add_argument("font", metavar="varfont.ttf", help="Variable-font file.")
  37. parser.add_argument(
  38. "coords",
  39. metavar="[AXIS=value...]",
  40. help="Coordinates to map, e.g. 'wght=700 wdth=75'.",
  41. nargs="*",
  42. default=None,
  43. )
  44. parser.add_argument(
  45. "-f", action="store_true", help="Do not omit axes at default location."
  46. )
  47. parser.add_argument(
  48. "-i", action="store_true", help="Input coordinates are normalized (-1..1)."
  49. )
  50. parser.add_argument(
  51. "-o", action="store_true", help="Output coordinates as normalized (-1..1)."
  52. )
  53. options = parser.parse_args(args)
  54. if not options.coords:
  55. parser.error(
  56. "No coordinates provided. Please specify at least one axis coordinate (e.g., wght=500)"
  57. )
  58. if options.font.endswith(".designspace"):
  59. from .build import build
  60. font = TTFont()
  61. build(font, options.font)
  62. else:
  63. font = TTFont(options.font)
  64. if "fvar" not in font:
  65. parser.error(f"Font '{options.font}' does not contain an 'fvar' table.")
  66. location = {
  67. tag: float(value) for tag, value in (item.split("=") for item in options.coords)
  68. }
  69. mapped = map(
  70. font,
  71. location,
  72. inputNormalized=options.i,
  73. outputNormalized=options.o,
  74. dropZeroes=not options.f,
  75. )
  76. assert mapped is not None
  77. for tag in mapped:
  78. v = mapped[tag]
  79. v = int(v) if v == int(v) else v
  80. print(f"{tag}={v:g}")
  81. if __name__ == "__main__":
  82. import sys
  83. sys.exit(main())