Makes life easier if ranged limits are wired from start

This commit is contained in:
Rod Sheeter 2019-03-06 21:54:15 -08:00
parent b6501a9406
commit ced09ff3fd

View File

@ -16,14 +16,15 @@ from fontTools.misc.fixedTools import floatToFixedToFloat
from fontTools.varLib import _GetCoordinates, _SetCoordinates
from fontTools.varLib.models import (
supportScalar,
normalizeLocation,
normalizeValue,
piecewiseLinearMap,
)
from fontTools.varLib.iup import iup_delta
from fontTools.ttLib import TTFont
from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
import os
import logging
import os
import re
log = logging.getLogger("fontTools.varlib.partialInstancer")
@ -101,33 +102,42 @@ def instantiateGvar(varfont, location):
instantiateGvarGlyph(varfont, location, glyphname)
def instantiateVariableFont(varfont, location, inplace=False):
def normalize(value, triple, avar_mapping):
value = normalizeValue(value, triple)
if avar_mapping:
value = piecewiseLinearMap(value, avar_mapping)
# Quantize to F2Dot14, to avoid surprise interpolations.
return floatToFixedToFloat(value, 14)
def normalizeAxisLimits(varfont, axis_limits):
fvar = varfont['fvar']
bad_limits = axis_limits.keys() - {a.axisTag for a in fvar.axes}
if bad_limits:
raise ValueError('Cannot limit: {} not present in fvar'.format(bad_limits))
axes = {a.axisTag: (a.minValue, a.defaultValue, a.maxValue)
for a in fvar.axes if a.axisTag in axis_limits}
avar_segments = {}
if 'avar' in varfont:
avar_segments = varfont['avar'].segments
for axis_tag, triple in axes.items():
avar_mapping = avar_segments.get(axis_tag, None)
axis_limits[axis_tag] = tuple(normalize(v, triple, avar_mapping)
for v in axis_limits[axis_tag])
def instantiateVariableFont(varfont, axis_limits, inplace=False):
if not inplace:
varfont = deepcopy(varfont)
normalizeAxisLimits(varfont, axis_limits)
fvar = varfont['fvar']
pinnedAxes = {
a.axisTag: (a.minValue, a.defaultValue, a.maxValue)
for a in fvar.axes
if a.axisTag in location
}
if not pinnedAxes:
return varfont # nothing to do
location = normalizeLocation(location, pinnedAxes)
if 'avar' in varfont:
# 'warp' the default normalization using avar
maps = varfont['avar'].segments
location = {
axis: piecewiseLinearMap(v, maps[axis]) for axis, v in location.items()
}
# Quantize to F2Dot14, to avoid surprise interpolations.
location = {axis: floatToFixedToFloat(v, 14) for axis, v in location.items()}
# Location is normalized now
log.info("Normalized location: %s", location)
log.info("Normalized limits: %s", axis_limits)
if "gvar" in varfont:
instantiateGvar(varfont, location)
# TODO: support range, stop dropping max value
axis_limits = {tag: minv for tag, (minv, maxv) in axis_limits.items()}
print(axis_limits)
instantiateGvar(varfont, axis_limits)
# TODO: actually process HVAR instead of dropping it
del varfont["HVAR"]
@ -135,7 +145,28 @@ def instantiateVariableFont(varfont, location, inplace=False):
return varfont
def main(args=None):
def parseLimits(limits):
result = {}
for limit_string in limits:
match = re.match(r'^(\w{1,4})=([^:]+)(?:[:](.+))?$', limit_string)
if not match:
parser.error("invalid location format: %r" % limit_string)
tag = match.group(1).ljust(4)
lbound = float(match.group(2))
ubound = lbound
if match.group(3):
ubound = float(match.group(3))
result[tag] = (lbound, ubound)
return result
def parseArgs(args):
"""Parse argv.
Returns:
3-tuple (infile, outfile, axis_limits)
axis_limits is a map axis_tag:(min,max), meaning limit this axis to
range."""
from fontTools import configLogger
import argparse
@ -148,8 +179,8 @@ def main(args=None):
parser.add_argument(
"locargs", metavar="AXIS=LOC", nargs="*",
help="List of space separated locations. A location consist in "
"the name of a variation axis, followed by '=' and a number. E.g.: "
" wdth=100")
"the name of a variation axis, followed by '=' and a number or"
"number:number. E.g.: wdth=100 or wght=75.0:125.0")
parser.add_argument(
"-o", "--output", metavar="OUTPUT.ttf", default=None,
help="Output instance TTF file (default: INPUT-instance.ttf).")
@ -160,9 +191,9 @@ def main(args=None):
"-q", "--quiet", action="store_true", help="Turn verbosity off.")
options = parser.parse_args(args)
varfilename = options.input
infile = options.input
outfile = (
os.path.splitext(varfilename)[0] + '-partial.ttf'
os.path.splitext(infile)[0] + '-partial.ttf'
if not options.output else options.output)
configLogger(
level=(
@ -172,20 +203,20 @@ def main(args=None):
)
)
loc = {}
for arg in options.locargs:
try:
tag, val = arg.split('=')
assert len(tag) <= 4
loc[tag.ljust(4)] = float(val)
except (ValueError, AssertionError):
parser.error("invalid location argument format: %r" % arg)
log.info("Location: %s", loc)
axis_limits = parseLimits(options.locargs)
if len(axis_limits) != len(options.locargs):
raise ValueError('Specified multiple limits for the same axis')
return (infile, outfile, axis_limits)
def main(args=None):
infile, outfile, axis_limits = parseArgs(args)
log.info("Restricting axes: %s", axis_limits)
log.info("Loading variable font")
varfont = TTFont(varfilename)
varfont = TTFont(infile)
instantiateVariableFont(varfont, loc, inplace=True)
instantiateVariableFont(varfont, axis_limits, inplace=True)
log.info("Saving partial variable font %s", outfile)
varfont.save(outfile)