Merge pull request #1583 from anthrotype/partial-instantiate-HVAR
[instancer] support instantiating HVAR and VVAR (TTF only for now)
This commit is contained in:
commit
691547b00b
@ -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"
|
||||||
|
@ -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):
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user