interpolate_layout.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. """
  2. Interpolate OpenType Layout tables (GDEF / GPOS / GSUB).
  3. """
  4. from fontTools.ttLib import TTFont
  5. from fontTools.varLib import models, VarLibError, load_designspace, load_masters
  6. from fontTools.varLib.merger import InstancerMerger
  7. import os.path
  8. import logging
  9. from copy import deepcopy
  10. from pprint import pformat
  11. log = logging.getLogger("fontTools.varLib.interpolate_layout")
  12. def interpolate_layout(designspace, loc, master_finder=lambda s: s, mapped=False):
  13. """
  14. Interpolate GPOS from a designspace file and location.
  15. If master_finder is set, it should be a callable that takes master
  16. filename as found in designspace file and map it to master font
  17. binary as to be opened (eg. .ttf or .otf).
  18. If mapped is False (default), then location is mapped using the
  19. map element of the axes in designspace file. If mapped is True,
  20. it is assumed that location is in designspace's internal space and
  21. no mapping is performed.
  22. """
  23. if hasattr(designspace, "sources"): # Assume a DesignspaceDocument
  24. pass
  25. else: # Assume a file path
  26. from fontTools.designspaceLib import DesignSpaceDocument
  27. designspace = DesignSpaceDocument.fromfile(designspace)
  28. ds = load_designspace(designspace)
  29. log.info("Building interpolated font")
  30. log.info("Loading master fonts")
  31. master_fonts = load_masters(designspace, master_finder)
  32. font = deepcopy(master_fonts[ds.base_idx])
  33. log.info("Location: %s", pformat(loc))
  34. if not mapped:
  35. loc = {name: ds.axes[name].map_forward(v) for name, v in loc.items()}
  36. log.info("Internal location: %s", pformat(loc))
  37. loc = models.normalizeLocation(loc, ds.internal_axis_supports)
  38. log.info("Normalized location: %s", pformat(loc))
  39. # Assume single-model for now.
  40. model = models.VariationModel(ds.normalized_master_locs)
  41. assert 0 == model.mapping[ds.base_idx]
  42. merger = InstancerMerger(font, model, loc)
  43. log.info("Building interpolated tables")
  44. # TODO GSUB/GDEF
  45. merger.mergeTables(font, master_fonts, ["GPOS"])
  46. return font
  47. def main(args=None):
  48. """Interpolate GDEF/GPOS/GSUB tables for a point on a designspace"""
  49. from fontTools import configLogger
  50. import argparse
  51. import sys
  52. parser = argparse.ArgumentParser(
  53. "fonttools varLib.interpolate_layout",
  54. description=main.__doc__,
  55. )
  56. parser.add_argument(
  57. "designspace_filename", metavar="DESIGNSPACE", help="Input TTF files"
  58. )
  59. parser.add_argument(
  60. "locations",
  61. metavar="LOCATION",
  62. type=str,
  63. nargs="+",
  64. help="Axis locations (e.g. wdth=120",
  65. )
  66. parser.add_argument(
  67. "-o",
  68. "--output",
  69. metavar="OUTPUT",
  70. help="Output font file (defaults to <designspacename>-instance.ttf)",
  71. )
  72. parser.add_argument(
  73. "-l",
  74. "--loglevel",
  75. metavar="LEVEL",
  76. default="INFO",
  77. help="Logging level (defaults to INFO)",
  78. )
  79. args = parser.parse_args(args)
  80. if not args.output:
  81. args.output = os.path.splitext(args.designspace_filename)[0] + "-instance.ttf"
  82. configLogger(level=args.loglevel)
  83. finder = lambda s: s.replace("master_ufo", "master_ttf_interpolatable").replace(
  84. ".ufo", ".ttf"
  85. )
  86. loc = {}
  87. for arg in args.locations:
  88. tag, val = arg.split("=")
  89. loc[tag] = float(val)
  90. font = interpolate_layout(args.designspace_filename, loc, finder)
  91. log.info("Saving font %s", args.output)
  92. font.save(args.output)
  93. if __name__ == "__main__":
  94. import sys
  95. if len(sys.argv) > 1:
  96. sys.exit(main())
  97. import doctest
  98. sys.exit(doctest.testmod().failed)