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