[varLib.mutator] Add interpolation of glyph metrics.

Added interpolation of glyph advance width, from HVAR/hmtx, and derivation of new LSB.
Updated tests to match.
Needed to cherrypick from another branch an update for psCharstrings to allow the CFFSubr.draw() method to work.
This commit is contained in:
ReadRoberts 2018-11-20 12:44:21 -08:00
parent 6cb84deccf
commit c2abd045b3
4 changed files with 65 additions and 32 deletions

View File

@ -1,7 +1,9 @@
from fontTools.misc import psCharStrings
from fontTools import ttLib
from fontTools.pens.basePen import NullPen
from fontTools.pens.boundsPen import BoundsPen
from fontTools.misc.fixedTools import otRound
from fontTools.varLib.varStore import VarStoreInstancer
def _add_method(*clazzes):
"""Returns a decorator function that adds a new method to one or
@ -587,6 +589,7 @@ def interpolate_cff2_PrivateDict(topDict, interpolateFromDeltas):
def interpolate_cff2_charstrings(topDict, interpolateFromDeltas, glyphOrder):
charstrings = topDict.CharStrings
for gname in glyphOrder:
# Interpolate charstring
charstring = charstrings[gname]
pd = charstring.private
vsindex = pd.vsindex if (hasattr(pd, 'vsindex')) else 0
@ -620,4 +623,50 @@ def interpolate_cff2_charstrings(topDict, interpolateFromDeltas, glyphOrder):
charstring.program = new_program
def interpolate_cff2_metrics(varfont, topDict, glyphOrder, loc):
"""Unlike TrueType glyphs, neither advance width nor bounding box
info is stored in a CFF2 charstring. The width data exists only in
the hmtx and HVAR tables. Since LSB data cannot be interpolated
reliably from the master LSB values in the hmtx table, we traverse
the charstring to determine the actual bound box. """
charstrings = topDict.CharStrings
boundsPen = BoundsPen(glyphOrder)
hmtx = varfont['hmtx']
hvar_table = None
if 'HVAR' in varfont:
hvar_table = varfont['HVAR'].table
fvar = varfont['fvar']
varStoreInstancer = VarStoreInstancer(hvar_table.VarStore, fvar.axes, loc)
for gid, gname in enumerate(glyphOrder):
entry = list(hmtx[gname])
# get width delta.
if hvar_table:
if hvar_table.AdvWidthMap:
width_idx = hvar_table.AdvWidthMap.mapping[gname]
else:
width_idx = gid
width_delta = otRound(varStoreInstancer[width_idx])
else:
width_delta = 0
# get LSB.
boundsPen.init()
charstring = charstrings[gname]
charstring.draw(boundsPen)
if boundsPen.bounds is None:
# Happens with non-marking glyphs
lsb_delta = 0
else:
lsb = boundsPen.bounds[0]
lsb_delta = entry[1] - lsb
if lsb_delta or width_delta:
if width_delta:
entry[0] += width_delta
if lsb_delta:
entry[1] = lsb
hmtx[gname] = tuple(entry)

View File

@ -17,7 +17,9 @@ from fontTools.varLib.merger import MutatorMerger
from fontTools.varLib.varStore import VarStoreInstancer
from fontTools.varLib.mvar import MVAR_ENTRIES
from fontTools.varLib.iup import iup_delta
from fontTools.subset.cffLib import interpolate_cff2_PrivateDict, interpolate_cff2_charstrings
from fontTools.subset.cffLib import (interpolate_cff2_PrivateDict,
interpolate_cff2_charstrings,
interpolate_cff2_metrics)
import os.path
import logging
@ -106,7 +108,7 @@ def instantiateVariableFont(varfont, location, inplace=False):
if 'CFF2' in varfont:
log.info("Mutating CFF2 table")
glyphOrder = varfont.getGlyphOrder()
CFF2= varfont['CFF2']
CFF2 = varfont['CFF2']
topDict = CFF2.cff.topDictIndex[0]
vsInstancer = VarStoreInstancer(topDict.VarStore.otVarStore,
fvar.axes, loc)
@ -115,6 +117,9 @@ def instantiateVariableFont(varfont, location, inplace=False):
CFF2.desubroutinize(varfont)
interpolate_cff2_charstrings(topDict, interpolateFromDeltas,
glyphOrder)
interpolate_cff2_metrics(varfont, topDict, glyphOrder, loc)
del topDict.rawDict['VarStore']
del topDict.VarStore
if 'MVAR' in varfont:
log.info("Mutating MVAR table")

View File

@ -1,6 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<ttFont sfntVersion="OTTO" ttLibVersion="3.32">
<hmtx>
<mtx name=".notdef" width="600" lsb="84"/>
<mtx name="A" width="600" lsb="50"/>
<mtx name="T" width="600" lsb="50"/>
<mtx name="dollar" width="600" lsb="102"/>
<mtx name="glyph00003" width="600" lsb="102"/>
</hmtx>
<CFF2>
<major value="2"/>
<minor value="0"/>
@ -89,35 +97,6 @@
158 860 -30 4 -158 -860 rlineto
</CharString>
</CharStrings>
<VarStore Format="1">
<Format value="1"/>
<VarRegionList>
<!-- RegionAxisCount=1 -->
<!-- RegionCount=2 -->
<Region index="0">
<VarRegionAxis index="0">
<StartCoord value="-1.0"/>
<PeakCoord value="-1.0"/>
<EndCoord value="0.0"/>
</VarRegionAxis>
</Region>
<Region index="1">
<VarRegionAxis index="0">
<StartCoord value="0.0"/>
<PeakCoord value="1.0"/>
<EndCoord value="1.0"/>
</VarRegionAxis>
</Region>
</VarRegionList>
<!-- VarDataCount=1 -->
<VarData index="0">
<!-- ItemCount=0 -->
<NumShorts value="0"/>
<!-- VarRegionCount=2 -->
<VarRegionIndex index="0" value="0"/>
<VarRegionIndex index="1" value="1"/>
</VarData>
</VarStore>
</CFFFont>
<GlobalSubrs>

View File

@ -165,7 +165,7 @@ class MutatorTest(unittest.TestCase):
otf_vf_path = self.get_test_input('TestCFF2VF.otf')
expected_ttx_name = 'InterpolateTestCFF2VF'
tables = ["fvar", "CFF2"]
tables = ["hmtx", "CFF2"]
loc = {'wght':float(200)}
varfont = TTFont(otf_vf_path)