diff --git a/Lib/fontTools/subset/cffLib.py b/Lib/fontTools/subset/cffLib.py index 42f63393e..db0382eca 100644 --- a/Lib/fontTools/subset/cffLib.py +++ b/Lib/fontTools/subset/cffLib.py @@ -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) + diff --git a/Lib/fontTools/varLib/mutator.py b/Lib/fontTools/varLib/mutator.py index 39bd71d02..136179667 100644 --- a/Lib/fontTools/varLib/mutator.py +++ b/Lib/fontTools/varLib/mutator.py @@ -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") diff --git a/Tests/varLib/data/test_results/InterpolateTestCFF2VF.ttx b/Tests/varLib/data/test_results/InterpolateTestCFF2VF.ttx index 21e4f7fac..e2d0f71cd 100644 --- a/Tests/varLib/data/test_results/InterpolateTestCFF2VF.ttx +++ b/Tests/varLib/data/test_results/InterpolateTestCFF2VF.ttx @@ -1,6 +1,14 @@ + + + + + + + + @@ -89,35 +97,6 @@ 158 860 -30 4 -158 -860 rlineto - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Tests/varLib/mutator_test.py b/Tests/varLib/mutator_test.py index ce8bfca78..8625de3c6 100644 --- a/Tests/varLib/mutator_test.py +++ b/Tests/varLib/mutator_test.py @@ -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)