Merge pull request #1580 from anthrotype/instantiate-gdef-gpos
[partial-instancer] support instantiating GDEF and GPOS
This commit is contained in:
commit
fb03be3182
@ -597,9 +597,15 @@ def _merge_OTL(font, model, master_fonts, axisTags):
|
||||
GDEF = font['GDEF'].table
|
||||
assert GDEF.Version <= 0x00010002
|
||||
except KeyError:
|
||||
font['GDEF']= newTable('GDEF')
|
||||
font['GDEF'] = newTable('GDEF')
|
||||
GDEFTable = font["GDEF"] = newTable('GDEF')
|
||||
GDEF = GDEFTable.table = ot.GDEF()
|
||||
GDEF.GlyphClassDef = None
|
||||
GDEF.AttachList = None
|
||||
GDEF.LigCaretList = None
|
||||
GDEF.MarkAttachClassDef = None
|
||||
GDEF.MarkGlyphSetsDef = None
|
||||
|
||||
GDEF.Version = 0x00010003
|
||||
GDEF.VarStore = store
|
||||
|
||||
|
@ -19,6 +19,7 @@ from fontTools.ttLib.tables.TupleVariation import TupleVariation
|
||||
from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
|
||||
from fontTools.varLib import builder
|
||||
from fontTools.varLib.mvar import MVAR_ENTRIES
|
||||
from fontTools.varLib.merger import MutatorMerger
|
||||
import collections
|
||||
from copy import deepcopy
|
||||
import logging
|
||||
@ -140,9 +141,7 @@ def instantiateCvar(varfont, location):
|
||||
del varfont["cvar"]
|
||||
|
||||
|
||||
def setMvarDeltas(varfont, deltaArray):
|
||||
log.info("Setting MVAR deltas")
|
||||
|
||||
def setMvarDeltas(varfont, deltas):
|
||||
mvar = varfont["MVAR"].table
|
||||
records = mvar.ValueRecord
|
||||
for rec in records:
|
||||
@ -150,9 +149,7 @@ def setMvarDeltas(varfont, deltaArray):
|
||||
if mvarTag not in MVAR_ENTRIES:
|
||||
continue
|
||||
tableTag, itemName = MVAR_ENTRIES[mvarTag]
|
||||
varDataIndex = rec.VarIdx >> 16
|
||||
itemIndex = rec.VarIdx & 0xFFFF
|
||||
delta = deltaArray[varDataIndex][itemIndex]
|
||||
delta = deltas[rec.VarIdx]
|
||||
if delta != 0:
|
||||
setattr(
|
||||
varfont[tableTag],
|
||||
@ -268,8 +265,8 @@ def instantiateItemVariationStore(itemVarStore, fvarAxes, location):
|
||||
May not specify coordinates for all the fvar axes.
|
||||
|
||||
Returns:
|
||||
defaultDeltaArray: the deltas to be added to the default instance (list of list
|
||||
of integers, indexed by outer/inner VarIdx)
|
||||
defaultDeltas: to be added to the default instance, of type dict of ints keyed
|
||||
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).
|
||||
"""
|
||||
@ -287,7 +284,67 @@ def instantiateItemVariationStore(itemVarStore, fvarAxes, location):
|
||||
else:
|
||||
varIndexMapping = None # VarStore is empty
|
||||
|
||||
return defaultDeltaArray, varIndexMapping
|
||||
defaultDeltas = {
|
||||
((major << 16) + minor): delta
|
||||
for major, deltas in enumerate(defaultDeltaArray)
|
||||
for minor, delta in enumerate(deltas)
|
||||
}
|
||||
return defaultDeltas, varIndexMapping
|
||||
|
||||
|
||||
def instantiateOTL(varfont, location):
|
||||
# TODO(anthrotype) Support partial instancing of JSTF and BASE tables
|
||||
|
||||
if "GDEF" not in varfont:
|
||||
return
|
||||
|
||||
if "GPOS" in varfont:
|
||||
msg = "Instantiating GDEF and GPOS tables"
|
||||
else:
|
||||
msg = "Instantiating GDEF table"
|
||||
log.info(msg)
|
||||
|
||||
gdef = varfont["GDEF"].table
|
||||
fvarAxes = varfont["fvar"].axes
|
||||
|
||||
defaultDeltas, varIndexMapping = instantiateItemVariationStore(
|
||||
gdef.VarStore, fvarAxes, location
|
||||
)
|
||||
|
||||
# When VF are built, big lookups may overflow and be broken into multiple
|
||||
# subtables. MutatorMerger (which inherits from AligningMerger) reattaches
|
||||
# them upon instancing, in case they can now fit a single subtable (if not,
|
||||
# they will be split again upon compilation).
|
||||
# This 'merger' also works as a 'visitor' that traverses the OTL tables and
|
||||
# calls specific methods when instances of a given type are found.
|
||||
# Specifically, it adds default deltas to GPOS Anchors/ValueRecords and GDEF
|
||||
# LigatureCarets, and optionally deletes all VariationIndex tables if the
|
||||
# VarStore is fully instanced.
|
||||
merger = MutatorMerger(
|
||||
varfont, defaultDeltas, deleteVariations=(varIndexMapping is None)
|
||||
)
|
||||
merger.mergeTables(varfont, [varfont], ["GDEF", "GPOS"])
|
||||
|
||||
if varIndexMapping:
|
||||
gdef.remap_device_varidxes(varIndexMapping)
|
||||
if "GPOS" in varfont:
|
||||
varfont["GPOS"].table.remap_device_varidxes(varIndexMapping)
|
||||
else:
|
||||
# Downgrade GDEF.
|
||||
del gdef.VarStore
|
||||
gdef.Version = 0x00010002
|
||||
if gdef.MarkGlyphSetsDef is None:
|
||||
del gdef.MarkGlyphSetsDef
|
||||
gdef.Version = 0x00010000
|
||||
|
||||
if not (
|
||||
gdef.LigCaretList
|
||||
or gdef.MarkAttachClassDef
|
||||
or gdef.GlyphClassDef
|
||||
or gdef.AttachList
|
||||
or (gdef.Version >= 0x00010002 and gdef.MarkGlyphSetsDef)
|
||||
):
|
||||
del varfont["GDEF"]
|
||||
|
||||
|
||||
def instantiateFeatureVariations(varfont, location):
|
||||
@ -406,6 +463,8 @@ def instantiateVariableFont(varfont, axis_limits, inplace=False, optimize=True):
|
||||
if "MVAR" in varfont:
|
||||
instantiateMvar(varfont, axis_limits)
|
||||
|
||||
instantiateOTL(varfont, axis_limits)
|
||||
|
||||
instantiateFeatureVariations(varfont, axis_limits)
|
||||
|
||||
# TODO: actually process HVAR instead of dropping it
|
||||
|
@ -833,17 +833,10 @@ class MutatorMerger(AligningMerger):
|
||||
the operation can benefit from many operations that the
|
||||
aligning merger does."""
|
||||
|
||||
def __init__(self, font, location):
|
||||
def __init__(self, font, instancer, deleteVariations=True):
|
||||
Merger.__init__(self, font)
|
||||
self.location = location
|
||||
|
||||
store = None
|
||||
if 'GDEF' in font:
|
||||
gdef = font['GDEF'].table
|
||||
if gdef.Version >= 0x00010003:
|
||||
store = gdef.VarStore
|
||||
|
||||
self.instancer = VarStoreInstancer(store, font['fvar'].axes, location)
|
||||
self.instancer = instancer
|
||||
self.deleteVariations = deleteVariations
|
||||
|
||||
@MutatorMerger.merger(ot.CaretValue)
|
||||
def merge(merger, self, lst):
|
||||
@ -856,14 +849,16 @@ def merge(merger, self, lst):
|
||||
|
||||
instancer = merger.instancer
|
||||
dev = self.DeviceTable
|
||||
del self.DeviceTable
|
||||
if merger.deleteVariations:
|
||||
del self.DeviceTable
|
||||
if dev:
|
||||
assert dev.DeltaFormat == 0x8000
|
||||
varidx = (dev.StartSize << 16) + dev.EndSize
|
||||
delta = otRound(instancer[varidx])
|
||||
self.Coordinate += delta
|
||||
self.Coordinate += delta
|
||||
|
||||
self.Format = 1
|
||||
if merger.deleteVariations:
|
||||
self.Format = 1
|
||||
|
||||
@MutatorMerger.merger(ot.Anchor)
|
||||
def merge(merger, self, lst):
|
||||
@ -880,7 +875,8 @@ def merge(merger, self, lst):
|
||||
if not hasattr(self, tableName):
|
||||
continue
|
||||
dev = getattr(self, tableName)
|
||||
delattr(self, tableName)
|
||||
if merger.deleteVariations:
|
||||
delattr(self, tableName)
|
||||
if dev is None:
|
||||
continue
|
||||
|
||||
@ -891,7 +887,8 @@ def merge(merger, self, lst):
|
||||
attr = v+'Coordinate'
|
||||
setattr(self, attr, getattr(self, attr) + delta)
|
||||
|
||||
self.Format = 1
|
||||
if merger.deleteVariations:
|
||||
self.Format = 1
|
||||
|
||||
@MutatorMerger.merger(otBase.ValueRecord)
|
||||
def merge(merger, self, lst):
|
||||
@ -900,7 +897,6 @@ def merge(merger, self, lst):
|
||||
self.__dict__ = lst[0].__dict__.copy()
|
||||
|
||||
instancer = merger.instancer
|
||||
# TODO Handle differing valueformats
|
||||
for name, tableName in [('XAdvance','XAdvDevice'),
|
||||
('YAdvance','YAdvDevice'),
|
||||
('XPlacement','XPlaDevice'),
|
||||
@ -909,7 +905,8 @@ def merge(merger, self, lst):
|
||||
if not hasattr(self, tableName):
|
||||
continue
|
||||
dev = getattr(self, tableName)
|
||||
delattr(self, tableName)
|
||||
if merger.deleteVariations:
|
||||
delattr(self, tableName)
|
||||
if dev is None:
|
||||
continue
|
||||
|
||||
|
@ -284,7 +284,7 @@ def instantiateVariableFont(varfont, location, inplace=False, overlap=True):
|
||||
gdef = varfont['GDEF'].table
|
||||
instancer = VarStoreInstancer(gdef.VarStore, fvar.axes, loc)
|
||||
|
||||
merger = MutatorMerger(varfont, loc)
|
||||
merger = MutatorMerger(varfont, instancer)
|
||||
merger.mergeTables(varfont, [varfont], ['GDEF', 'GPOS'])
|
||||
|
||||
# Downgrade GDEF.
|
||||
|
@ -1,11 +1,17 @@
|
||||
from __future__ import print_function, division, absolute_import
|
||||
from fontTools.misc.py23 import *
|
||||
from fontTools import ttLib
|
||||
from fontTools import designspaceLib
|
||||
from fontTools.feaLib.builder import addOpenTypeFeaturesFromString
|
||||
from fontTools.ttLib.tables import _f_v_a_r
|
||||
from fontTools.ttLib.tables.TupleVariation import TupleVariation
|
||||
from fontTools import varLib
|
||||
from fontTools.varLib import instancer
|
||||
from fontTools.varLib.mvar import MVAR_ENTRIES
|
||||
from fontTools.varLib import builder
|
||||
from fontTools.varLib import models
|
||||
import collections
|
||||
from copy import deepcopy
|
||||
import os
|
||||
import pytest
|
||||
|
||||
@ -326,7 +332,15 @@ class InstantiateItemVariationStoreTest(object):
|
||||
varStore, fvarAxes, location
|
||||
)
|
||||
|
||||
assert defaultDeltas == expected_deltas
|
||||
defaultDeltaArray = []
|
||||
for varidx, delta in sorted(defaultDeltas.items()):
|
||||
major, minor = varidx >> 16, varidx & 0xFFFF
|
||||
if major == len(defaultDeltaArray):
|
||||
defaultDeltaArray.append([])
|
||||
assert len(defaultDeltaArray[major]) == minor
|
||||
defaultDeltaArray[major].append(delta)
|
||||
|
||||
assert defaultDeltaArray == expected_deltas
|
||||
assert varStore.VarRegionList.RegionCount == num_regions
|
||||
|
||||
|
||||
@ -465,8 +479,7 @@ class TupleVarStoreAdapterTest(object):
|
||||
itemVarStore2 = adapter.asItemVarStore()
|
||||
|
||||
assert [
|
||||
reg.get_support(fvarAxes)
|
||||
for reg in itemVarStore2.VarRegionList.Region
|
||||
reg.get_support(fvarAxes) for reg in itemVarStore2.VarRegionList.Region
|
||||
] == regions
|
||||
|
||||
assert itemVarStore2.VarDataCount == 2
|
||||
@ -477,3 +490,318 @@ class TupleVarStoreAdapterTest(object):
|
||||
]
|
||||
assert itemVarStore2.VarData[1].VarRegionIndex == [3, 4, 5, 6]
|
||||
assert itemVarStore2.VarData[1].Item == [[5, -15, 25, -35], [45, -55, 65, -75]]
|
||||
|
||||
|
||||
def makeTTFont(glyphOrder, features):
|
||||
font = ttLib.TTFont()
|
||||
font.setGlyphOrder(glyphOrder)
|
||||
addOpenTypeFeaturesFromString(font, features)
|
||||
font["name"] = ttLib.newTable("name")
|
||||
return font
|
||||
|
||||
|
||||
def _makeDSAxesDict(axes):
|
||||
dsAxes = collections.OrderedDict()
|
||||
for axisTag, axisValues in axes:
|
||||
axis = designspaceLib.AxisDescriptor()
|
||||
axis.name = axis.tag = axis.labelNames["en"] = axisTag
|
||||
axis.minimum, axis.default, axis.maximum = axisValues
|
||||
dsAxes[axis.tag] = axis
|
||||
return dsAxes
|
||||
|
||||
|
||||
def makeVariableFont(masters, baseIndex, axes, masterLocations):
|
||||
vf = deepcopy(masters[baseIndex])
|
||||
dsAxes = _makeDSAxesDict(axes)
|
||||
fvar = varLib._add_fvar(vf, dsAxes, instances=())
|
||||
axisTags = [axis.axisTag for axis in fvar.axes]
|
||||
normalizedLocs = [models.normalizeLocation(m, dict(axes)) for m in masterLocations]
|
||||
model = models.VariationModel(normalizedLocs, axisOrder=axisTags)
|
||||
varLib._merge_OTL(vf, model, masters, axisTags)
|
||||
return vf
|
||||
|
||||
|
||||
def makeParametrizedVF(glyphOrder, features, values, increments):
|
||||
# Create a test VF with given glyphs and parametrized OTL features.
|
||||
# The VF is built from 9 masters (3 x 3 along wght and wdth), with
|
||||
# locations hard-coded and base master at wght=400 and wdth=100.
|
||||
# 'values' is a list of initial values that are interpolated in the
|
||||
# 'features' string, and incremented for each subsequent master by the
|
||||
# given 'increments' (list of 2-tuple) along the two axes.
|
||||
assert values and len(values) == len(increments)
|
||||
assert all(len(i) == 2 for i in increments)
|
||||
masterLocations = [
|
||||
{"wght": 100, "wdth": 50},
|
||||
{"wght": 100, "wdth": 100},
|
||||
{"wght": 100, "wdth": 150},
|
||||
{"wght": 400, "wdth": 50},
|
||||
{"wght": 400, "wdth": 100}, # base master
|
||||
{"wght": 400, "wdth": 150},
|
||||
{"wght": 700, "wdth": 50},
|
||||
{"wght": 700, "wdth": 100},
|
||||
{"wght": 700, "wdth": 150},
|
||||
]
|
||||
n = len(values)
|
||||
values = list(values)
|
||||
masters = []
|
||||
for _ in range(3):
|
||||
for _ in range(3):
|
||||
master = makeTTFont(glyphOrder, features=features % tuple(values))
|
||||
masters.append(master)
|
||||
for i in range(n):
|
||||
values[i] += increments[i][1]
|
||||
for i in range(n):
|
||||
values[i] += increments[i][0]
|
||||
baseIndex = 4
|
||||
axes = [("wght", (100, 400, 700)), ("wdth", (50, 100, 150))]
|
||||
vf = makeVariableFont(masters, baseIndex, axes, masterLocations)
|
||||
return vf
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def varfontGDEF():
|
||||
glyphOrder = [".notdef", "f", "i", "f_i"]
|
||||
features = (
|
||||
"feature liga { sub f i by f_i;} liga;"
|
||||
"table GDEF { LigatureCaretByPos f_i %d; } GDEF;"
|
||||
)
|
||||
values = [100]
|
||||
increments = [(+30, +10)]
|
||||
return makeParametrizedVF(glyphOrder, features, values, increments)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def varfontGPOS():
|
||||
glyphOrder = [".notdef", "V", "A"]
|
||||
features = "feature kern { pos V A %d; } kern;"
|
||||
values = [-80]
|
||||
increments = [(-10, -5)]
|
||||
return makeParametrizedVF(glyphOrder, features, values, increments)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def varfontGPOS2():
|
||||
glyphOrder = [".notdef", "V", "A", "acutecomb"]
|
||||
features = (
|
||||
"markClass [acutecomb] <anchor 150 -10> @TOP_MARKS;"
|
||||
"feature mark {"
|
||||
" pos base A <anchor %d 450> mark @TOP_MARKS;"
|
||||
"} mark;"
|
||||
"feature kern {"
|
||||
" pos V A %d;"
|
||||
"} kern;"
|
||||
)
|
||||
values = [200, -80]
|
||||
increments = [(+30, +10), (-10, -5)]
|
||||
return makeParametrizedVF(glyphOrder, features, values, increments)
|
||||
|
||||
|
||||
class InstantiateOTLTest(object):
|
||||
@pytest.mark.parametrize(
|
||||
"location, expected",
|
||||
[
|
||||
({"wght": -1.0}, 110), # -60
|
||||
({"wght": 0}, 170),
|
||||
({"wght": 0.5}, 200), # +30
|
||||
({"wght": 1.0}, 230), # +60
|
||||
({"wdth": -1.0}, 160), # -10
|
||||
({"wdth": -0.3}, 167), # -3
|
||||
({"wdth": 0}, 170),
|
||||
({"wdth": 1.0}, 180), # +10
|
||||
],
|
||||
)
|
||||
def test_pin_and_drop_axis_GDEF(self, varfontGDEF, location, expected):
|
||||
vf = varfontGDEF
|
||||
assert "GDEF" in vf
|
||||
|
||||
instancer.instantiateOTL(vf, location)
|
||||
|
||||
assert "GDEF" in vf
|
||||
gdef = vf["GDEF"].table
|
||||
assert gdef.Version == 0x00010003
|
||||
assert gdef.VarStore
|
||||
assert gdef.LigCaretList
|
||||
caretValue = gdef.LigCaretList.LigGlyph[0].CaretValue[0]
|
||||
assert caretValue.Format == 3
|
||||
assert hasattr(caretValue, "DeviceTable")
|
||||
assert caretValue.DeviceTable.DeltaFormat == 0x8000
|
||||
assert caretValue.Coordinate == expected
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"location, expected",
|
||||
[
|
||||
({"wght": -1.0, "wdth": -1.0}, 100), # -60 - 10
|
||||
({"wght": -1.0, "wdth": 0.0}, 110), # -60
|
||||
({"wght": -1.0, "wdth": 1.0}, 120), # -60 + 10
|
||||
({"wght": 0.0, "wdth": -1.0}, 160), # -10
|
||||
({"wght": 0.0, "wdth": 0.0}, 170),
|
||||
({"wght": 0.0, "wdth": 1.0}, 180), # +10
|
||||
({"wght": 1.0, "wdth": -1.0}, 220), # +60 - 10
|
||||
({"wght": 1.0, "wdth": 0.0}, 230), # +60
|
||||
({"wght": 1.0, "wdth": 1.0}, 240), # +60 + 10
|
||||
],
|
||||
)
|
||||
def test_full_instance_GDEF(self, varfontGDEF, location, expected):
|
||||
vf = varfontGDEF
|
||||
assert "GDEF" in vf
|
||||
|
||||
instancer.instantiateOTL(vf, location)
|
||||
|
||||
assert "GDEF" in vf
|
||||
gdef = vf["GDEF"].table
|
||||
assert gdef.Version == 0x00010000
|
||||
assert not hasattr(gdef, "VarStore")
|
||||
assert gdef.LigCaretList
|
||||
caretValue = gdef.LigCaretList.LigGlyph[0].CaretValue[0]
|
||||
assert caretValue.Format == 1
|
||||
assert not hasattr(caretValue, "DeviceTable")
|
||||
assert caretValue.Coordinate == expected
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"location, expected",
|
||||
[
|
||||
({"wght": -1.0}, -85), # +25
|
||||
({"wght": 0}, -110),
|
||||
({"wght": 1.0}, -135), # -25
|
||||
({"wdth": -1.0}, -105), # +5
|
||||
({"wdth": 0}, -110),
|
||||
({"wdth": 1.0}, -115), # -5
|
||||
],
|
||||
)
|
||||
def test_pin_and_drop_axis_GPOS_kern(self, varfontGPOS, location, expected):
|
||||
vf = varfontGPOS
|
||||
assert "GDEF" in vf
|
||||
assert "GPOS" in vf
|
||||
|
||||
instancer.instantiateOTL(vf, location)
|
||||
|
||||
gdef = vf["GDEF"].table
|
||||
gpos = vf["GPOS"].table
|
||||
assert gdef.Version == 0x00010003
|
||||
assert gdef.VarStore
|
||||
|
||||
assert gpos.LookupList.Lookup[0].LookupType == 2 # PairPos
|
||||
pairPos = gpos.LookupList.Lookup[0].SubTable[0]
|
||||
valueRec1 = pairPos.PairSet[0].PairValueRecord[0].Value1
|
||||
assert valueRec1.XAdvDevice
|
||||
assert valueRec1.XAdvDevice.DeltaFormat == 0x8000
|
||||
assert valueRec1.XAdvance == expected
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"location, expected",
|
||||
[
|
||||
({"wght": -1.0, "wdth": -1.0}, -80), # +25 + 5
|
||||
({"wght": -1.0, "wdth": 0.0}, -85), # +25
|
||||
({"wght": -1.0, "wdth": 1.0}, -90), # +25 - 5
|
||||
({"wght": 0.0, "wdth": -1.0}, -105), # +5
|
||||
({"wght": 0.0, "wdth": 0.0}, -110),
|
||||
({"wght": 0.0, "wdth": 1.0}, -115), # -5
|
||||
({"wght": 1.0, "wdth": -1.0}, -130), # -25 + 5
|
||||
({"wght": 1.0, "wdth": 0.0}, -135), # -25
|
||||
({"wght": 1.0, "wdth": 1.0}, -140), # -25 - 5
|
||||
],
|
||||
)
|
||||
def test_full_instance_GPOS_kern(self, varfontGPOS, location, expected):
|
||||
vf = varfontGPOS
|
||||
assert "GDEF" in vf
|
||||
assert "GPOS" in vf
|
||||
|
||||
instancer.instantiateOTL(vf, location)
|
||||
|
||||
assert "GDEF" not in vf
|
||||
gpos = vf["GPOS"].table
|
||||
|
||||
assert gpos.LookupList.Lookup[0].LookupType == 2 # PairPos
|
||||
pairPos = gpos.LookupList.Lookup[0].SubTable[0]
|
||||
valueRec1 = pairPos.PairSet[0].PairValueRecord[0].Value1
|
||||
assert not hasattr(valueRec1, "XAdvDevice")
|
||||
assert valueRec1.XAdvance == expected
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"location, expected",
|
||||
[
|
||||
({"wght": -1.0}, (210, -85)), # -60, +25
|
||||
({"wght": 0}, (270, -110)),
|
||||
({"wght": 0.5}, (300, -122)), # +30, -12
|
||||
({"wght": 1.0}, (330, -135)), # +60, -25
|
||||
({"wdth": -1.0}, (260, -105)), # -10, +5
|
||||
({"wdth": -0.3}, (267, -108)), # -3, +2
|
||||
({"wdth": 0}, (270, -110)),
|
||||
({"wdth": 1.0}, (280, -115)), # +10, -5
|
||||
],
|
||||
)
|
||||
def test_pin_and_drop_axis_GPOS_mark_and_kern(
|
||||
self, varfontGPOS2, location, expected
|
||||
):
|
||||
vf = varfontGPOS2
|
||||
assert "GDEF" in vf
|
||||
assert "GPOS" in vf
|
||||
|
||||
instancer.instantiateOTL(vf, location)
|
||||
|
||||
v1, v2 = expected
|
||||
gdef = vf["GDEF"].table
|
||||
gpos = vf["GPOS"].table
|
||||
assert gdef.Version == 0x00010003
|
||||
assert gdef.VarStore
|
||||
assert gdef.GlyphClassDef
|
||||
|
||||
assert gpos.LookupList.Lookup[0].LookupType == 4 # MarkBasePos
|
||||
markBasePos = gpos.LookupList.Lookup[0].SubTable[0]
|
||||
baseAnchor = markBasePos.BaseArray.BaseRecord[0].BaseAnchor[0]
|
||||
assert baseAnchor.Format == 3
|
||||
assert baseAnchor.XDeviceTable
|
||||
assert baseAnchor.XDeviceTable.DeltaFormat == 0x8000
|
||||
assert not baseAnchor.YDeviceTable
|
||||
assert baseAnchor.XCoordinate == v1
|
||||
assert baseAnchor.YCoordinate == 450
|
||||
|
||||
assert gpos.LookupList.Lookup[1].LookupType == 2 # PairPos
|
||||
pairPos = gpos.LookupList.Lookup[1].SubTable[0]
|
||||
valueRec1 = pairPos.PairSet[0].PairValueRecord[0].Value1
|
||||
assert valueRec1.XAdvDevice
|
||||
assert valueRec1.XAdvDevice.DeltaFormat == 0x8000
|
||||
assert valueRec1.XAdvance == v2
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"location, expected",
|
||||
[
|
||||
({"wght": -1.0, "wdth": -1.0}, (200, -80)), # -60 - 10, +25 + 5
|
||||
({"wght": -1.0, "wdth": 0.0}, (210, -85)), # -60, +25
|
||||
({"wght": -1.0, "wdth": 1.0}, (220, -90)), # -60 + 10, +25 - 5
|
||||
({"wght": 0.0, "wdth": -1.0}, (260, -105)), # -10, +5
|
||||
({"wght": 0.0, "wdth": 0.0}, (270, -110)),
|
||||
({"wght": 0.0, "wdth": 1.0}, (280, -115)), # +10, -5
|
||||
({"wght": 1.0, "wdth": -1.0}, (320, -130)), # +60 - 10, -25 + 5
|
||||
({"wght": 1.0, "wdth": 0.0}, (330, -135)), # +60, -25
|
||||
({"wght": 1.0, "wdth": 1.0}, (340, -140)), # +60 + 10, -25 - 5
|
||||
],
|
||||
)
|
||||
def test_full_instance_GPOS_mark_and_kern(self, varfontGPOS2, location, expected):
|
||||
vf = varfontGPOS2
|
||||
assert "GDEF" in vf
|
||||
assert "GPOS" in vf
|
||||
|
||||
instancer.instantiateOTL(vf, location)
|
||||
|
||||
v1, v2 = expected
|
||||
gdef = vf["GDEF"].table
|
||||
gpos = vf["GPOS"].table
|
||||
assert gdef.Version == 0x00010000
|
||||
assert not hasattr(gdef, "VarStore")
|
||||
assert gdef.GlyphClassDef
|
||||
|
||||
assert gpos.LookupList.Lookup[0].LookupType == 4 # MarkBasePos
|
||||
markBasePos = gpos.LookupList.Lookup[0].SubTable[0]
|
||||
baseAnchor = markBasePos.BaseArray.BaseRecord[0].BaseAnchor[0]
|
||||
assert baseAnchor.Format == 1
|
||||
assert not hasattr(baseAnchor, "XDeviceTable")
|
||||
assert not hasattr(baseAnchor, "YDeviceTable")
|
||||
assert baseAnchor.XCoordinate == v1
|
||||
assert baseAnchor.YCoordinate == 450
|
||||
|
||||
assert gpos.LookupList.Lookup[1].LookupType == 2 # PairPos
|
||||
pairPos = gpos.LookupList.Lookup[1].SubTable[0]
|
||||
valueRec1 = pairPos.PairSet[0].PairValueRecord[0].Value1
|
||||
assert not hasattr(valueRec1, "XAdvDevice")
|
||||
assert valueRec1.XAdvance == v2
|
||||
|
Loading…
x
Reference in New Issue
Block a user