[instancer] Instantiate CFF2!
This commit is contained in:
parent
969d09dcaf
commit
bb9a23702b
@ -97,6 +97,12 @@ from fontTools import varLib
|
||||
# we import the `subset` module because we use the `prune_lookups` method on the GSUB
|
||||
# table class, and that method is only defined dynamically upon importing `subset`
|
||||
from fontTools import subset # noqa: F401
|
||||
from fontTools.cffLib.specializer import (
|
||||
programToCommands,
|
||||
commandsToProgram,
|
||||
specializeCommands,
|
||||
generalizeCommands,
|
||||
)
|
||||
from fontTools.varLib import builder
|
||||
from fontTools.varLib.mvar import MVAR_ENTRIES
|
||||
from fontTools.varLib.merger import MutatorMerger
|
||||
@ -566,6 +572,121 @@ def changeTupleVariationAxisLimit(var, axisTag, axisLimit):
|
||||
return out
|
||||
|
||||
|
||||
def instantiateCFF2(varfont, axisLimits, round=round):
|
||||
log.info("Instantiating CFF2 table")
|
||||
|
||||
fvarAxes = varfont["fvar"].axes
|
||||
|
||||
cff = varfont["CFF2"].cff
|
||||
cff.desubroutinize()
|
||||
topDict = cff.topDictIndex[0]
|
||||
|
||||
varStore = topDict.VarStore.otVarStore
|
||||
|
||||
def getNumRegions(vsindex):
|
||||
return len(
|
||||
varStore.VarData[vsindex if vsindex is not None else 0].VarRegionIndex
|
||||
)
|
||||
|
||||
charStrings = topDict.CharStrings.values()
|
||||
allCommands = []
|
||||
for cs in charStrings:
|
||||
commands = programToCommands(cs.program, getNumRegions=getNumRegions)
|
||||
# commands = specializeCommands(commands) # While we're here...
|
||||
commands = generalizeCommands(commands) # While we're here...
|
||||
allCommands.append(commands)
|
||||
|
||||
def storeBlendsToVarStore(arg):
|
||||
if not isinstance(arg, list):
|
||||
return
|
||||
for subarg in arg[:-1]:
|
||||
if isinstance(subarg, list):
|
||||
raise NotImplementedError("Nested blend lists not supported (yet)")
|
||||
storeBlendsToVarStore(subarg)
|
||||
count = arg[-1]
|
||||
assert (len(arg) - 1) % count == 0
|
||||
nRegions = (len(arg) - 1) // count - 1
|
||||
assert nRegions == getNumRegions(vsindex)
|
||||
for i in range(count, len(arg) - 1, nRegions):
|
||||
deltas = arg[i : i + nRegions]
|
||||
assert len(deltas) == nRegions
|
||||
varData = varStore.VarData[vsindex]
|
||||
varData.Item.append(deltas)
|
||||
varData.ItemCount += 1
|
||||
|
||||
def fetchBlendsFromVarStore(arg):
|
||||
if not isinstance(arg, list):
|
||||
return
|
||||
for subarg in arg[:-1]:
|
||||
if isinstance(subarg, list):
|
||||
raise NotImplementedError("Nested blend lists not supported (yet)")
|
||||
fetchBlendsFromVarStore(subarg)
|
||||
|
||||
count = arg[-1]
|
||||
assert (len(arg) - 1) % count == 0
|
||||
numRegions = getNumRegions(vsindex)
|
||||
newDefaults = []
|
||||
newDeltas = []
|
||||
for i in range(count):
|
||||
|
||||
defaultValue = arg[i]
|
||||
|
||||
major = vsindex
|
||||
minor = varDataCursor[major]
|
||||
varDataCursor[major] += 1
|
||||
|
||||
varIdx = (major << 16) + minor
|
||||
|
||||
defaultValue += round(defaultDeltas[varIdx])
|
||||
newDefaults.append(defaultValue)
|
||||
|
||||
varData = varStore.VarData[major]
|
||||
deltas = varData.Item[minor]
|
||||
assert len(deltas) == numRegions
|
||||
assert len(deltas) # TODO: relax
|
||||
newDeltas.extend(deltas)
|
||||
|
||||
arg[:-1] = newDefaults + newDeltas
|
||||
|
||||
# Check VarData's are empty
|
||||
for varData in varStore.VarData:
|
||||
assert varData.Item == []
|
||||
assert varData.ItemCount == 0
|
||||
|
||||
# Add blend lists to VarStore so we can instantiate them
|
||||
for commands in allCommands:
|
||||
vsindex = 0
|
||||
for command in commands:
|
||||
if command[0] == "vsindex":
|
||||
vsindex = command[1][0]
|
||||
continue
|
||||
for arg in command[1]:
|
||||
storeBlendsToVarStore(arg)
|
||||
|
||||
# Instantiate VarStore
|
||||
defaultDeltas = instantiateItemVariationStore(varStore, fvarAxes, axisLimits)
|
||||
|
||||
# Read back new blends from the instantiated VarStore
|
||||
varDataCursor = [0] * len(varStore.VarData)
|
||||
for commands in allCommands:
|
||||
vsindex = 0
|
||||
for command in commands:
|
||||
if command[0] == "vsindex":
|
||||
vsindex = command[1][0]
|
||||
continue
|
||||
for arg in command[1]:
|
||||
fetchBlendsFromVarStore(arg)
|
||||
|
||||
# Empty out the VarStore
|
||||
for i, varData in enumerate(varStore.VarData):
|
||||
assert varDataCursor[i] == varData.ItemCount
|
||||
varData.Item = []
|
||||
varData.ItemCount = 0
|
||||
|
||||
for cs, commands in zip(charStrings, allCommands):
|
||||
cs.program = commandsToProgram(commands)
|
||||
|
||||
|
||||
def _instantiateGvarGlyph(
|
||||
glyphname, glyf, gvar, hMetrics, vMetrics, axisLimits, optimize=True
|
||||
):
|
||||
@ -1222,9 +1343,6 @@ def sanityCheckVariableTables(varfont):
|
||||
if "gvar" in varfont:
|
||||
if "glyf" not in varfont:
|
||||
raise ValueError("Can't have gvar without glyf")
|
||||
# TODO(anthrotype) Remove once we do support partial instancing CFF2
|
||||
if "CFF2" in varfont:
|
||||
raise NotImplementedError("Instancing CFF2 variable fonts is not supported yet")
|
||||
|
||||
|
||||
def instantiateVariableFont(
|
||||
@ -1297,6 +1415,9 @@ def instantiateVariableFont(
|
||||
log.info("Updating name table")
|
||||
names.updateNameTable(varfont, axisLimits)
|
||||
|
||||
if "CFF2" in varfont:
|
||||
instantiateCFF2(varfont, normalizedLimits)
|
||||
|
||||
if "gvar" in varfont:
|
||||
instantiateGvar(varfont, normalizedLimits, optimize=optimize)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user