You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
105 lines
3.2 KiB
105 lines
3.2 KiB
4 months ago
|
"""
|
||
|
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)
|