| """ |
| Interpolate OpenType Layout tables (GDEF / GPOS / GSUB). |
| """ |
| from fontTools.ttLib import TTFont |
| from fontTools.varLib import models, VarLibError, load_designspace, load_masters |
| from fontTools.varLib.merger import InstancerMerger |
| import os.path |
| import logging |
| from copy import deepcopy |
| from pprint import pformat |
| |
| log = logging.getLogger("fontTools.varLib.interpolate_layout") |
| |
| |
| def interpolate_layout(designspace, loc, master_finder=lambda s:s, mapped=False): |
| """ |
| Interpolate GPOS from a designspace file and location. |
| |
| If master_finder is set, it should be a callable that takes master |
| filename as found in designspace file and map it to master font |
| binary as to be opened (eg. .ttf or .otf). |
| |
| If mapped is False (default), then location is mapped using the |
| map element of the axes in designspace file. If mapped is True, |
| it is assumed that location is in designspace's internal space and |
| no mapping is performed. |
| """ |
| if hasattr(designspace, "sources"): # Assume a DesignspaceDocument |
| pass |
| else: # Assume a file path |
| from fontTools.designspaceLib import DesignSpaceDocument |
| designspace = DesignSpaceDocument.fromfile(designspace) |
| |
| ds = load_designspace(designspace) |
| log.info("Building interpolated font") |
| |
| log.info("Loading master fonts") |
| master_fonts = load_masters(designspace, master_finder) |
| font = deepcopy(master_fonts[ds.base_idx]) |
| |
| log.info("Location: %s", pformat(loc)) |
| if not mapped: |
| loc = {name: ds.axes[name].map_forward(v) for name,v in loc.items()} |
| log.info("Internal location: %s", pformat(loc)) |
| loc = models.normalizeLocation(loc, ds.internal_axis_supports) |
| log.info("Normalized location: %s", pformat(loc)) |
| |
| # Assume single-model for now. |
| model = models.VariationModel(ds.normalized_master_locs) |
| assert 0 == model.mapping[ds.base_idx] |
| |
| merger = InstancerMerger(font, model, loc) |
| |
| log.info("Building interpolated tables") |
| # TODO GSUB/GDEF |
| merger.mergeTables(font, master_fonts, ['GPOS']) |
| return font |
| |
| |
| def main(args=None): |
| """Interpolate GDEF/GPOS/GSUB tables for a point on a designspace""" |
| from fontTools import configLogger |
| import argparse |
| import sys |
| |
| parser = argparse.ArgumentParser( |
| "fonttools varLib.interpolate_layout", |
| description=main.__doc__, |
| ) |
| parser.add_argument('designspace_filename', metavar='DESIGNSPACE', |
| help="Input TTF files") |
| parser.add_argument('locations', metavar='LOCATION', type=str, nargs='+', |
| help="Axis locations (e.g. wdth=120") |
| parser.add_argument('-o', '--output', metavar='OUTPUT', |
| help="Output font file (defaults to <designspacename>-instance.ttf)") |
| parser.add_argument('-l', '--loglevel', metavar='LEVEL', default="INFO", |
| help="Logging level (defaults to INFO)") |
| |
| |
| args = parser.parse_args(args) |
| |
| if not args.output: |
| args.output = os.path.splitext(args.designspace_filename)[0] + '-instance.ttf' |
| |
| configLogger(level=args.loglevel) |
| |
| finder = lambda s: s.replace('master_ufo', 'master_ttf_interpolatable').replace('.ufo', '.ttf') |
| |
| loc = {} |
| for arg in args.locations: |
| tag,val = arg.split('=') |
| loc[tag] = float(val) |
| |
| font = interpolate_layout(args.designspace_filename, loc, finder) |
| log.info("Saving font %s", args.output) |
| font.save(args.output) |
| |
| |
| if __name__ == "__main__": |
| import sys |
| if len(sys.argv) > 1: |
| sys.exit(main()) |
| import doctest |
| sys.exit(doctest.testmod().failed) |