Merge pull request #1583 from anthrotype/partial-instantiate-HVAR

[instancer] support instantiating HVAR and VVAR  (TTF only for now)
This commit is contained in:
Cosimo Lupo 2019-05-01 12:50:45 +02:00 committed by GitHub
commit 691547b00b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 180 additions and 55 deletions

View File

@ -362,9 +362,9 @@ class table__g_l_y_f(DefaultTable.DefaultTable):
"coord" is an array of GlyphCoordinates which must include the four "coord" is an array of GlyphCoordinates which must include the four
"phantom points". "phantom points".
Only the horizontal advance and sidebearings in "hmtx" table are updated Both the horizontal/vertical advances and left/top sidebearings in "hmtx"
from the first two phantom points. The last two phantom points for and "vmtx" tables (if any) are updated from four phantom points and
vertical typesetting are currently ignored. the glyph's bounding boxes.
""" """
# TODO: Create new glyph if not already present # TODO: Create new glyph if not already present
assert glyphName in self.glyphs assert glyphName in self.glyphs
@ -372,8 +372,6 @@ class table__g_l_y_f(DefaultTable.DefaultTable):
# Handle phantom points for (left, right, top, bottom) positions. # Handle phantom points for (left, right, top, bottom) positions.
assert len(coord) >= 4 assert len(coord) >= 4
if not hasattr(glyph, 'xMin'):
glyph.recalcBounds(self)
leftSideX = coord[-4][0] leftSideX = coord[-4][0]
rightSideX = coord[-3][0] rightSideX = coord[-3][0]
topSideY = coord[-2][1] topSideY = coord[-2][1]
@ -400,9 +398,15 @@ class table__g_l_y_f(DefaultTable.DefaultTable):
# https://github.com/fonttools/fonttools/pull/1198 # https://github.com/fonttools/fonttools/pull/1198
horizontalAdvanceWidth = 0 horizontalAdvanceWidth = 0
leftSideBearing = otRound(glyph.xMin - leftSideX) leftSideBearing = otRound(glyph.xMin - leftSideX)
# TODO Handle vertical metrics?
ttFont["hmtx"].metrics[glyphName] = horizontalAdvanceWidth, leftSideBearing ttFont["hmtx"].metrics[glyphName] = horizontalAdvanceWidth, leftSideBearing
if "vmtx" in ttFont:
verticalAdvanceWidth = otRound(topSideY - bottomSideY)
if verticalAdvanceWidth < 0: # unlikely but do the same as horizontal
verticalAdvanceWidth = 0
topSideBearing = otRound(topSideY - glyph.yMax)
ttFont["vmtx"].metrics[glyphName] = verticalAdvanceWidth, topSideBearing
_GlyphControls = namedtuple( _GlyphControls = namedtuple(
"_GlyphControls", "numberOfContours endPts flags components" "_GlyphControls", "numberOfContours endPts flags components"

View File

@ -367,20 +367,20 @@ def _merge_TTHinting(font, masterModel, master_ttfs, tolerance=0.5):
var = TupleVariation(support, delta) var = TupleVariation(support, delta)
cvar.variations.append(var) cvar.variations.append(var)
MetricsFields = namedtuple('MetricsFields', _MetricsFields = namedtuple('_MetricsFields',
['tableTag', 'metricsTag', 'sb1', 'sb2', 'advMapping', 'vOrigMapping']) ['tableTag', 'metricsTag', 'sb1', 'sb2', 'advMapping', 'vOrigMapping'])
hvarFields = MetricsFields(tableTag='HVAR', metricsTag='hmtx', sb1='LsbMap', HVAR_FIELDS = _MetricsFields(tableTag='HVAR', metricsTag='hmtx', sb1='LsbMap',
sb2='RsbMap', advMapping='AdvWidthMap', vOrigMapping=None) sb2='RsbMap', advMapping='AdvWidthMap', vOrigMapping=None)
vvarFields = MetricsFields(tableTag='VVAR', metricsTag='vmtx', sb1='TsbMap', VVAR_FIELDS = _MetricsFields(tableTag='VVAR', metricsTag='vmtx', sb1='TsbMap',
sb2='BsbMap', advMapping='AdvHeightMap', vOrigMapping='VOrgMap') sb2='BsbMap', advMapping='AdvHeightMap', vOrigMapping='VOrgMap')
def _add_HVAR(font, masterModel, master_ttfs, axisTags): def _add_HVAR(font, masterModel, master_ttfs, axisTags):
_add_VHVAR(font, masterModel, master_ttfs, axisTags, hvarFields) _add_VHVAR(font, masterModel, master_ttfs, axisTags, HVAR_FIELDS)
def _add_VVAR(font, masterModel, master_ttfs, axisTags): def _add_VVAR(font, masterModel, master_ttfs, axisTags):
_add_VHVAR(font, masterModel, master_ttfs, axisTags, vvarFields) _add_VHVAR(font, masterModel, master_ttfs, axisTags, VVAR_FIELDS)
def _add_VHVAR(font, masterModel, master_ttfs, axisTags, tableFields): def _add_VHVAR(font, masterModel, master_ttfs, axisTags, tableFields):

View File

@ -17,6 +17,7 @@ from fontTools.varLib.models import supportScalar, normalizeValue, piecewiseLine
from fontTools.ttLib import TTFont from fontTools.ttLib import TTFont
from fontTools.ttLib.tables.TupleVariation import TupleVariation from fontTools.ttLib.tables.TupleVariation import TupleVariation
from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
from fontTools import varLib
from fontTools.varLib import builder from fontTools.varLib import builder
from fontTools.varLib.mvar import MVAR_ENTRIES from fontTools.varLib.mvar import MVAR_ENTRIES
from fontTools.varLib.merger import MutatorMerger from fontTools.varLib.merger import MutatorMerger
@ -82,8 +83,8 @@ def instantiateGvarGlyph(varfont, glyphname, location, optimize=True):
if defaultDeltas: if defaultDeltas:
coordinates += GlyphCoordinates(defaultDeltas) coordinates += GlyphCoordinates(defaultDeltas)
# this will also set the hmtx advance widths and sidebearings from # this will also set the hmtx/vmtx advance widths and sidebearings from
# the fourth-last and third-last phantom points (and glyph.xMin) # the four phantom points and glyph bounding boxes
glyf.setCoordinates(glyphname, coordinates, varfont) glyf.setCoordinates(glyphname, coordinates, varfont)
if not tupleVarStore: if not tupleVarStore:
@ -158,24 +159,74 @@ def setMvarDeltas(varfont, deltas):
) )
def instantiateMvar(varfont, location): def instantiateMVAR(varfont, location):
log.info("Instantiating MVAR table") log.info("Instantiating MVAR table")
mvar = varfont["MVAR"].table mvar = varfont["MVAR"].table
fvarAxes = varfont["fvar"].axes fvarAxes = varfont["fvar"].axes
defaultDeltas, varIndexMapping = instantiateItemVariationStore( varStore = mvar.VarStore
mvar.VarStore, fvarAxes, location defaultDeltas = instantiateItemVariationStore(varStore, fvarAxes, location)
)
setMvarDeltas(varfont, defaultDeltas) setMvarDeltas(varfont, defaultDeltas)
if varIndexMapping: if varStore.VarRegionList.Region:
varIndexMapping = varStore.optimize()
for rec in mvar.ValueRecord: for rec in mvar.ValueRecord:
rec.VarIdx = varIndexMapping[rec.VarIdx] rec.VarIdx = varIndexMapping[rec.VarIdx]
else: else:
# Delete table if no more regions left.
del varfont["MVAR"] del varfont["MVAR"]
def _remapVarIdxMap(table, attrName, varIndexMapping, glyphOrder):
oldMapping = getattr(table, attrName).mapping
newMapping = [varIndexMapping[oldMapping[glyphName]] for glyphName in glyphOrder]
setattr(table, attrName, builder.buildVarIdxMap(newMapping, glyphOrder))
# TODO(anthrotype) Add support for HVAR/VVAR in CFF2
def _instantiateVHVAR(varfont, location, tableFields):
tableTag = tableFields.tableTag
fvarAxes = varfont["fvar"].axes
# Deltas from gvar table have already been applied to the hmtx/vmtx. For full
# instances (i.e. all axes pinned), we can simply drop HVAR/VVAR and return
if set(location).issuperset(axis.axisTag for axis in fvarAxes):
log.info("Dropping %s table", tableTag)
del varfont[tableTag]
return
log.info("Instantiating %s table", tableTag)
vhvar = varfont[tableTag].table
varStore = vhvar.VarStore
# since deltas were already applied, the return value here is ignored
instantiateItemVariationStore(varStore, fvarAxes, location)
if varStore.VarRegionList.Region:
# Only re-optimize VarStore if the HVAR/VVAR already uses indirect AdvWidthMap
# or AdvHeightMap. If a direct, implicit glyphID->VariationIndex mapping is
# used for advances, skip re-optimizing and maintain original VariationIndex.
if getattr(vhvar, tableFields.advMapping):
varIndexMapping = varStore.optimize()
glyphOrder = varfont.getGlyphOrder()
_remapVarIdxMap(vhvar, tableFields.advMapping, varIndexMapping, glyphOrder)
if getattr(vhvar, tableFields.sb1): # left or top sidebearings
_remapVarIdxMap(vhvar, tableFields.sb1, varIndexMapping, glyphOrder)
if getattr(vhvar, tableFields.sb2): # right or bottom sidebearings
_remapVarIdxMap(vhvar, tableFields.sb2, varIndexMapping, glyphOrder)
if tableTag == "VVAR" and getattr(vhvar, tableFields.vOrigMapping):
_remapVarIdxMap(
vhvar, tableFields.vOrigMapping, varIndexMapping, glyphOrder
)
else:
del varfont[tableTag]
def instantiateHVAR(varfont, location):
return _instantiateVHVAR(varfont, location, varLib.HVAR_FIELDS)
def instantiateVVAR(varfont, location):
return _instantiateVHVAR(varfont, location, varLib.VVAR_FIELDS)
class _TupleVarStoreAdapter(object): class _TupleVarStoreAdapter(object):
def __init__(self, regions, axisOrder, tupleVarData, itemCounts): def __init__(self, regions, axisOrder, tupleVarData, itemCounts):
self.regions = regions self.regions = regions
@ -249,6 +300,8 @@ class _TupleVarStoreAdapter(object):
) )
regionList = builder.buildVarRegionList(self.regions, self.axisOrder) regionList = builder.buildVarRegionList(self.regions, self.axisOrder)
itemVarStore = builder.buildVarStore(regionList, varDatas) itemVarStore = builder.buildVarStore(regionList, varDatas)
# remove unused regions from VarRegionList
itemVarStore.prune_regions()
return itemVarStore return itemVarStore
@ -258,6 +311,10 @@ def instantiateItemVariationStore(itemVarStore, fvarAxes, location):
Remove regions in which all axes were instanced, and scale the deltas of Remove regions in which all axes were instanced, and scale the deltas of
the remaining regions where only some of the axes were instanced. the remaining regions where only some of the axes were instanced.
The number of VarData subtables, and the number of items within each, are
not modified, in order to keep the existing VariationIndex valid.
One may call VarStore.optimize() method after this to further optimize those.
Args: Args:
varStore: An otTables.VarStore object (Item Variation Store) varStore: An otTables.VarStore object (Item Variation Store)
fvarAxes: list of fvar's Axis objects fvarAxes: list of fvar's Axis objects
@ -267,8 +324,6 @@ def instantiateItemVariationStore(itemVarStore, fvarAxes, location):
Returns: Returns:
defaultDeltas: to be added to the default instance, of type dict of ints keyed defaultDeltas: to be added to the default instance, of type dict of ints keyed
by VariationIndex compound values: i.e. (outer << 16) + inner. by VariationIndex compound values: i.e. (outer << 16) + inner.
varIndexMapping: a mapping from old to new VarIdx after optimization (None if
varStore was fully instanced thus left empty).
""" """
tupleVarStore = _TupleVarStoreAdapter.fromItemVarStore(itemVarStore, fvarAxes) tupleVarStore = _TupleVarStoreAdapter.fromItemVarStore(itemVarStore, fvarAxes)
defaultDeltaArray = tupleVarStore.instantiate(location) defaultDeltaArray = tupleVarStore.instantiate(location)
@ -278,18 +333,12 @@ def instantiateItemVariationStore(itemVarStore, fvarAxes, location):
assert itemVarStore.VarDataCount == newItemVarStore.VarDataCount assert itemVarStore.VarDataCount == newItemVarStore.VarDataCount
itemVarStore.VarData = newItemVarStore.VarData itemVarStore.VarData = newItemVarStore.VarData
if itemVarStore.VarRegionList.Region:
# optimize VarStore, and get a map from old to new VarIdx after optimization
varIndexMapping = itemVarStore.optimize()
else:
varIndexMapping = None # VarStore is empty
defaultDeltas = { defaultDeltas = {
((major << 16) + minor): delta ((major << 16) + minor): delta
for major, deltas in enumerate(defaultDeltaArray) for major, deltas in enumerate(defaultDeltaArray)
for minor, delta in enumerate(deltas) for minor, delta in enumerate(deltas)
} }
return defaultDeltas, varIndexMapping return defaultDeltas
def instantiateOTL(varfont, location): def instantiateOTL(varfont, location):
@ -305,11 +354,10 @@ def instantiateOTL(varfont, location):
log.info(msg) log.info(msg)
gdef = varfont["GDEF"].table gdef = varfont["GDEF"].table
varStore = gdef.VarStore
fvarAxes = varfont["fvar"].axes fvarAxes = varfont["fvar"].axes
defaultDeltas, varIndexMapping = instantiateItemVariationStore( defaultDeltas = instantiateItemVariationStore(varStore, fvarAxes, location)
gdef.VarStore, fvarAxes, location
)
# When VF are built, big lookups may overflow and be broken into multiple # When VF are built, big lookups may overflow and be broken into multiple
# subtables. MutatorMerger (which inherits from AligningMerger) reattaches # subtables. MutatorMerger (which inherits from AligningMerger) reattaches
@ -321,11 +369,12 @@ def instantiateOTL(varfont, location):
# LigatureCarets, and optionally deletes all VariationIndex tables if the # LigatureCarets, and optionally deletes all VariationIndex tables if the
# VarStore is fully instanced. # VarStore is fully instanced.
merger = MutatorMerger( merger = MutatorMerger(
varfont, defaultDeltas, deleteVariations=(varIndexMapping is None) varfont, defaultDeltas, deleteVariations=(not varStore.VarRegionList.Region)
) )
merger.mergeTables(varfont, [varfont], ["GDEF", "GPOS"]) merger.mergeTables(varfont, [varfont], ["GDEF", "GPOS"])
if varIndexMapping: if varStore.VarRegionList.Region:
varIndexMapping = varStore.optimize()
gdef.remap_device_varidxes(varIndexMapping) gdef.remap_device_varidxes(varIndexMapping)
if "GPOS" in varfont: if "GPOS" in varfont:
varfont["GPOS"].table.remap_device_varidxes(varIndexMapping) varfont["GPOS"].table.remap_device_varidxes(varIndexMapping)
@ -439,6 +488,9 @@ def sanityCheckVariableTables(varfont):
if "gvar" in varfont: if "gvar" in varfont:
if "glyf" not in varfont: if "glyf" not in varfont:
raise ValueError("Can't have gvar without glyf") 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(varfont, axis_limits, inplace=False, optimize=True): def instantiateVariableFont(varfont, axis_limits, inplace=False, optimize=True):
@ -461,15 +513,18 @@ def instantiateVariableFont(varfont, axis_limits, inplace=False, optimize=True):
instantiateCvar(varfont, axis_limits) instantiateCvar(varfont, axis_limits)
if "MVAR" in varfont: if "MVAR" in varfont:
instantiateMvar(varfont, axis_limits) instantiateMVAR(varfont, axis_limits)
if "HVAR" in varfont:
instantiateHVAR(varfont, axis_limits)
if "VVAR" in varfont:
instantiateVVAR(varfont, axis_limits)
instantiateOTL(varfont, axis_limits) instantiateOTL(varfont, axis_limits)
instantiateFeatureVariations(varfont, axis_limits) instantiateFeatureVariations(varfont, axis_limits)
# TODO: actually process HVAR instead of dropping it
del varfont["HVAR"]
return varfont return varfont

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.39"> <ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.40">
<GlyphOrder> <GlyphOrder>
<!-- The 'id' attribute is only for humans; it is ignored when parsed. --> <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
@ -12,12 +12,12 @@
<!-- Most of this table will be recalculated by the compiler --> <!-- Most of this table will be recalculated by the compiler -->
<tableVersion value="1.0"/> <tableVersion value="1.0"/>
<fontRevision value="2.001"/> <fontRevision value="2.001"/>
<checkSumAdjustment value="0x45c905be"/> <checkSumAdjustment value="0x9180a393"/>
<magicNumber value="0x5f0f3cf5"/> <magicNumber value="0x5f0f3cf5"/>
<flags value="00000000 00000011"/> <flags value="00000000 00000011"/>
<unitsPerEm value="1000"/> <unitsPerEm value="1000"/>
<created value="Tue Mar 5 00:05:14 2019"/> <created value="Tue Mar 5 00:05:14 2019"/>
<modified value="Mon Mar 25 12:51:29 2019"/> <modified value="Sat Apr 20 17:03:24 2019"/>
<xMin value="40"/> <xMin value="40"/>
<yMin value="-200"/> <yMin value="-200"/>
<xMax value="450"/> <xMax value="450"/>
@ -570,7 +570,7 @@
</VarRegionList> </VarRegionList>
<!-- VarDataCount=1 --> <!-- VarDataCount=1 -->
<VarData index="0"> <VarData index="0">
<!-- ItemCount=3 --> <!-- ItemCount=2 -->
<NumShorts value="0"/> <NumShorts value="0"/>
<!-- VarRegionCount=5 --> <!-- VarRegionCount=5 -->
<VarRegionIndex index="0" value="2"/> <VarRegionIndex index="0" value="2"/>
@ -578,18 +578,22 @@
<VarRegionIndex index="2" value="4"/> <VarRegionIndex index="2" value="4"/>
<VarRegionIndex index="3" value="5"/> <VarRegionIndex index="3" value="5"/>
<VarRegionIndex index="4" value="6"/> <VarRegionIndex index="4" value="6"/>
<Item index="0" value="[0, 0, 0, 0, 0]"/> <Item index="0" value="[-4, -48, -11, 31, 55]"/>
<Item index="1" value="[-4, -48, -11, 31, 55]"/> <Item index="1" value="[0, 0, 0, 0, 0]"/>
<Item index="2" value="[0, 0, 0, 0, 0]"/>
</VarData> </VarData>
</VarStore> </VarStore>
<AdvWidthMap>
<Map glyph=".notdef" outer="0" inner="1"/>
<Map glyph="hyphen" outer="0" inner="0"/>
<Map glyph="space" outer="0" inner="1"/>
</AdvWidthMap>
</HVAR> </HVAR>
<MVAR> <MVAR>
<Version value="0x00010000"/> <Version value="0x00010000"/>
<Reserved value="0"/> <Reserved value="0"/>
<ValueRecordSize value="8"/> <ValueRecordSize value="8"/>
<!-- ValueRecordCount=3 --> <!-- ValueRecordCount=4 -->
<VarStore Format="1"> <VarStore Format="1">
<Format value="1"/> <Format value="1"/>
<VarRegionList> <VarRegionList>
@ -632,7 +636,7 @@
</VarRegionAxis> </VarRegionAxis>
</Region> </Region>
</VarRegionList> </VarRegionList>
<!-- VarDataCount=1 --> <!-- VarDataCount=2 -->
<VarData index="0"> <VarData index="0">
<!-- ItemCount=3 --> <!-- ItemCount=3 -->
<NumShorts value="1"/> <NumShorts value="1"/>
@ -644,7 +648,7 @@
<Item index="1" value="[100, 0, -20]"/> <Item index="1" value="[100, 0, -20]"/>
<Item index="2" value="[50, -30, -20]"/> <Item index="2" value="[50, -30, -20]"/>
</VarData> </VarData>
<VarData> <VarData index="1">
<!-- ItemCount=1 --> <!-- ItemCount=1 -->
<NumShorts value="0"/> <NumShorts value="0"/>
<!-- VarRegionCount=1 --> <!-- VarRegionCount=1 -->
@ -1016,4 +1020,10 @@
</glyphVariations> </glyphVariations>
</gvar> </gvar>
<vmtx>
<mtx name=".notdef" height="1000" tsb="100"/>
<mtx name="hyphen" height="536" tsb="229"/>
<mtx name="space" height="600" tsb="0"/>
</vmtx>
</ttFont> </ttFont>

View File

@ -67,7 +67,7 @@ class InstantiateGvarTest(object):
(247, 229), (247, 229),
(0, 0), (0, 0),
(274, 0), (274, 0),
(0, 1000), (0, 536),
(0, 0), (0, 0),
] ]
}, },
@ -83,7 +83,7 @@ class InstantiateGvarTest(object):
(265, 229), (265, 229),
(0, 0), (0, 0),
(298, 0), (298, 0),
(0, 1000), (0, 536),
(0, 0), (0, 0),
] ]
}, },
@ -101,7 +101,7 @@ class InstantiateGvarTest(object):
(282, 229), (282, 229),
(0, 0), (0, 0),
(322, 0), (322, 0),
(0, 1000), (0, 536),
(0, 0), (0, 0),
] ]
}, },
@ -133,7 +133,7 @@ class InstantiateGvarTest(object):
(265, 229), (265, 229),
(0, 0), (0, 0),
(298, 0), (298, 0),
(0, 1000), (0, 536),
(0, 0), (0, 0),
] ]
@ -169,7 +169,7 @@ class InstantiateCvarTest(object):
assert "cvar" not in varfont assert "cvar" not in varfont
class InstantiateMvarTest(object): class InstantiateMVARTest(object):
@pytest.mark.parametrize( @pytest.mark.parametrize(
"location, expected", "location, expected",
[ [
@ -217,7 +217,7 @@ class InstantiateMvarTest(object):
assert mvar.VarStore.VarData[1].VarRegionCount == 1 assert mvar.VarStore.VarData[1].VarRegionCount == 1
assert all(len(item) == 1 for item in mvar.VarStore.VarData[1].Item) assert all(len(item) == 1 for item in mvar.VarStore.VarData[1].Item)
instancer.instantiateMvar(varfont, location) instancer.instantiateMVAR(varfont, location)
for mvar_tag, expected_value in expected.items(): for mvar_tag, expected_value in expected.items():
table_tag, item_name = MVAR_ENTRIES[mvar_tag] table_tag, item_name = MVAR_ENTRIES[mvar_tag]
@ -268,7 +268,7 @@ class InstantiateMvarTest(object):
], ],
) )
def test_full_instance(self, varfont, location, expected): def test_full_instance(self, varfont, location, expected):
instancer.instantiateMvar(varfont, location) instancer.instantiateMVAR(varfont, location)
for mvar_tag, expected_value in expected.items(): for mvar_tag, expected_value in expected.items():
table_tag, item_name = MVAR_ENTRIES[mvar_tag] table_tag, item_name = MVAR_ENTRIES[mvar_tag]
@ -277,6 +277,62 @@ class InstantiateMvarTest(object):
assert "MVAR" not in varfont assert "MVAR" not in varfont
class InstantiateHVARTest(object):
# the 'expectedDeltas' below refer to the VarData item deltas for the "hyphen"
# glyph in the PartialInstancerTest-VF.ttx test font, that are left after
# partial instancing
@pytest.mark.parametrize(
"location, expectedRegions, expectedDeltas",
[
({"wght": -1.0}, [{"wdth": (-1.0, -1.0, 0)}], [-59]),
({"wght": 0}, [{"wdth": (-1.0, -1.0, 0)}], [-48]),
({"wght": 1.0}, [{"wdth": (-1.0, -1.0, 0)}], [7]),
(
{"wdth": -1.0},
[
{"wght": (-1.0, -1.0, 0.0)},
{"wght": (0.0, 0.61, 1.0)},
{"wght": (0.61, 1.0, 1.0)},
],
[-11, 31, 51],
),
({"wdth": 0}, [{"wght": (0.61, 1.0, 1.0)}], [-4]),
],
)
def test_partial_instance(
self, varfont, fvarAxes, location, expectedRegions, expectedDeltas
):
instancer.instantiateHVAR(varfont, location)
assert "HVAR" in varfont
hvar = varfont["HVAR"].table
varStore = hvar.VarStore
regions = varStore.VarRegionList.Region
assert [reg.get_support(fvarAxes) for reg in regions] == expectedRegions
assert len(varStore.VarData) == 1
assert varStore.VarData[0].ItemCount == 2
assert hvar.AdvWidthMap is not None
advWithMap = hvar.AdvWidthMap.mapping
assert advWithMap[".notdef"] == advWithMap["space"]
varIdx = advWithMap[".notdef"]
# these glyphs have no metrics variations in the test font
assert varStore.VarData[varIdx >> 16].Item[varIdx & 0xFFFF] == (
[0] * varStore.VarData[0].VarRegionCount
)
varIdx = advWithMap["hyphen"]
assert varStore.VarData[varIdx >> 16].Item[varIdx & 0xFFFF] == expectedDeltas
def test_full_instance(self, varfont):
instancer.instantiateHVAR(varfont, {"wght": 0, "wdth": 0})
assert "HVAR" not in varfont
class InstantiateItemVariationStoreTest(object): class InstantiateItemVariationStoreTest(object):
def test_VarRegion_get_support(self): def test_VarRegion_get_support(self):
axisOrder = ["wght", "wdth", "opsz"] axisOrder = ["wght", "wdth", "opsz"]
@ -328,7 +384,7 @@ class InstantiateItemVariationStoreTest(object):
def test_instantiate_default_deltas( def test_instantiate_default_deltas(
self, varStore, fvarAxes, location, expected_deltas, num_regions self, varStore, fvarAxes, location, expected_deltas, num_regions
): ):
defaultDeltas, _ = instancer.instantiateItemVariationStore( defaultDeltas = instancer.instantiateItemVariationStore(
varStore, fvarAxes, location varStore, fvarAxes, location
) )