instancer_test: add tests for restricting axis ranges (L3)

This commit is contained in:
Cosimo Lupo 2019-09-25 17:21:50 +01:00
parent 0b746bc38d
commit b8500ac97c
No known key found for this signature in database
GPG Key ID: 20D4A261E4A0E642

View File

@ -1,4 +1,5 @@
from fontTools.misc.py23 import * from fontTools.misc.py23 import *
from fontTools.misc.fixedTools import floatToFixedToFloat
from fontTools import ttLib from fontTools import ttLib
from fontTools import designspaceLib from fontTools import designspaceLib
from fontTools.feaLib.builder import addOpenTypeFeaturesFromString from fontTools.feaLib.builder import addOpenTypeFeaturesFromString
@ -493,33 +494,40 @@ class TupleVarStoreAdapterTest(object):
[TupleVariation({"wdth": (-1.0, -1.0, 0)}, [-12, 8])], [TupleVariation({"wdth": (-1.0, -1.0, 0)}, [-12, 8])],
] ]
def test_dropAxes(self): def test_rebuildRegions(self):
regions = [ regions = [
{"wght": (-1.0, -1.0, 0)}, {"wght": (-1.0, -1.0, 0)},
{"wght": (0.0, 1.0, 1.0)}, {"wght": (0.0, 1.0, 1.0)},
{"wdth": (-1.0, -1.0, 0)}, {"wdth": (-1.0, -1.0, 0)},
{"opsz": (0.0, 1.0, 1.0)},
{"wght": (-1.0, -1.0, 0), "wdth": (-1.0, -1.0, 0)}, {"wght": (-1.0, -1.0, 0), "wdth": (-1.0, -1.0, 0)},
{"wght": (0, 0.5, 1.0), "wdth": (-1.0, -1.0, 0)}, {"wght": (0, 1.0, 1.0), "wdth": (-1.0, -1.0, 0)},
{"wght": (0.5, 1.0, 1.0), "wdth": (-1.0, -1.0, 0)},
] ]
axisOrder = ["wght", "wdth", "opsz"] axisOrder = ["wght", "wdth"]
adapter = instancer._TupleVarStoreAdapter(regions, axisOrder, [], itemCounts=[]) variations = []
for region in regions:
variations.append(TupleVariation(region, [100]))
tupleVarData = [variations[:3], variations[3:]]
adapter = instancer._TupleVarStoreAdapter(
regions, axisOrder, tupleVarData, itemCounts=[1, 1]
)
adapter.dropAxes({"wdth"}) adapter.rebuildRegions()
assert adapter.regions == regions
del tupleVarData[0][2]
tupleVarData[1][0].axes = {"wght": (-1.0, -0.5, 0)}
tupleVarData[1][1].axes = {"wght": (0, 0.5, 1.0)}
adapter.rebuildRegions()
assert adapter.regions == [ assert adapter.regions == [
{"wght": (-1.0, -1.0, 0)}, {"wght": (-1.0, -1.0, 0)},
{"wght": (0.0, 1.0, 1.0)}, {"wght": (0.0, 1.0, 1.0)},
{"opsz": (0.0, 1.0, 1.0)}, {"wght": (-1.0, -0.5, 0)},
{"wght": (0.0, 0.5, 1.0)}, {"wght": (0, 0.5, 1.0)},
{"wght": (0.5, 1.0, 1.0)},
] ]
adapter.dropAxes({"wght", "opsz"})
assert adapter.regions == []
def test_roundtrip(self, fvarAxes): def test_roundtrip(self, fvarAxes):
regions = [ regions = [
{"wght": (-1.0, -1.0, 0)}, {"wght": (-1.0, -1.0, 0)},
@ -924,6 +932,208 @@ class InstantiateAvarTest(object):
assert "avar" not in varfont assert "avar" not in varfont
@staticmethod
def quantizeF2Dot14Floats(mapping):
return {
floatToFixedToFloat(k, 14): floatToFixedToFloat(v, 14)
for k, v in mapping.items()
}
# the following values come from NotoSans-VF.ttf
DFLT_WGHT_MAPPING = {
-1.0: -1.0,
-0.6667: -0.7969,
-0.3333: -0.5,
0: 0,
0.2: 0.18,
0.4: 0.38,
0.6: 0.61,
0.8: 0.79,
1.0: 1.0,
}
DFLT_WDTH_MAPPING = {-1.0: -1.0, -0.6667: -0.7, -0.3333: -0.36664, 0: 0, 1.0: 1.0}
@pytest.fixture
def varfont(self):
fvarAxes = ("wght", (100, 400, 900)), ("wdth", (62.5, 100, 100))
avarSegments = {
"wght": self.quantizeF2Dot14Floats(self.DFLT_WGHT_MAPPING),
"wdth": self.quantizeF2Dot14Floats(self.DFLT_WDTH_MAPPING),
}
varfont = ttLib.TTFont()
varfont["name"] = ttLib.newTable("name")
varLib._add_fvar(varfont, _makeDSAxesDict(fvarAxes), instances=())
avar = varfont["avar"] = ttLib.newTable("avar")
avar.segments = avarSegments
return varfont
@pytest.mark.parametrize(
"axisLimits, expectedSegments",
[
pytest.param(
{"wght": (100, 900)},
{"wght": DFLT_WGHT_MAPPING, "wdth": DFLT_WDTH_MAPPING},
id="wght=100:900",
),
pytest.param(
{"wght": (400, 900)},
{
"wght": {
-1.0: -1.0,
0: 0,
0.2: 0.18,
0.4: 0.38,
0.6: 0.61,
0.8: 0.79,
1.0: 1.0,
},
"wdth": DFLT_WDTH_MAPPING,
},
id="wght=400:900",
),
pytest.param(
{"wght": (100, 400)},
{
"wght": {
-1.0: -1.0,
-0.6667: -0.7969,
-0.3333: -0.5,
0: 0,
1.0: 1.0,
},
"wdth": DFLT_WDTH_MAPPING,
},
id="wght=100:400",
),
pytest.param(
{"wght": (400, 800)},
{
"wght": {
-1.0: -1.0,
0: 0,
0.25: 0.22784,
0.50006: 0.48103,
0.75: 0.77214,
1.0: 1.0,
},
"wdth": DFLT_WDTH_MAPPING,
},
id="wght=400:800",
),
pytest.param(
{"wght": (400, 700)},
{
"wght": {
-1.0: -1.0,
0: 0,
0.3334: 0.2951,
0.66675: 0.623,
1.0: 1.0,
},
"wdth": DFLT_WDTH_MAPPING,
},
id="wght=400:700",
),
pytest.param(
{"wght": (400, 600)},
{
"wght": {-1.0: -1.0, 0: 0, 0.5: 0.47363, 1.0: 1.0},
"wdth": DFLT_WDTH_MAPPING,
},
id="wght=400:600",
),
pytest.param(
{"wdth": (62.5, 100)},
{
"wght": DFLT_WGHT_MAPPING,
"wdth": {
-1.0: -1.0,
-0.6667: -0.7,
-0.3333: -0.36664,
0: 0,
1.0: 1.0,
},
},
id="wdth=62.5:100",
),
pytest.param(
{"wdth": (70, 100)},
{
"wght": DFLT_WGHT_MAPPING,
"wdth": {
-1.0: -1.0,
-0.8334: -0.85364,
-0.4166: -0.44714,
0: 0,
1.0: 1.0,
},
},
id="wdth=70:100",
),
pytest.param(
{"wdth": (75, 100)},
{
"wght": DFLT_WGHT_MAPPING,
"wdth": {-1.0: -1.0, -0.49994: -0.52374, 0: 0, 1.0: 1.0},
},
id="wdth=75:100",
),
pytest.param(
{"wdth": (77, 100)},
{
"wght": DFLT_WGHT_MAPPING,
"wdth": {-1.0: -1.0, -0.54346: -0.56696, 0: 0, 1.0: 1.0},
},
id="wdth=77:100",
),
pytest.param(
{"wdth": (87.5, 100)},
{"wght": DFLT_WGHT_MAPPING, "wdth": {-1.0: -1.0, 0: 0, 1.0: 1.0}},
id="wdth=87.5:100",
),
],
)
def test_limit_axes(self, varfont, axisLimits, expectedSegments):
instancer.instantiateAvar(varfont, axisLimits)
newSegments = varfont["avar"].segments
expectedSegments = {
axisTag: self.quantizeF2Dot14Floats(mapping)
for axisTag, mapping in expectedSegments.items()
}
assert newSegments == expectedSegments
@pytest.mark.parametrize(
"invalidSegmentMap",
[
pytest.param({0.5: 0.5}, id="missing-required-maps-1"),
pytest.param({-1.0: -1.0, 1.0: 1.0}, id="missing-required-maps-2"),
pytest.param(
{-1.0: -1.0, 0: 0, 0.5: 0.5, 0.6: 0.4, 1.0: 1.0},
id="retrograde-value-maps",
),
],
)
def test_drop_invalid_segment_map(self, varfont, invalidSegmentMap, caplog):
varfont["avar"].segments["wght"] = invalidSegmentMap
with caplog.at_level(logging.WARNING, logger="fontTools.varLib.instancer"):
instancer.instantiateAvar(varfont, {"wght": (100, 400)})
assert "Invalid avar" in caplog.text
assert "wght" not in varfont["avar"].segments
def test_isValidAvarSegmentMap(self):
assert instancer._isValidAvarSegmentMap("FOOO", {})
assert instancer._isValidAvarSegmentMap("FOOO", {-1.0: -1.0, 0: 0, 1.0: 1.0})
assert instancer._isValidAvarSegmentMap(
"FOOO", {-1.0: -1.0, 0: 0, 0.5: 0.5, 1.0: 1.0}
)
assert instancer._isValidAvarSegmentMap(
"FOOO", {-1.0: -1.0, 0: 0, 0.5: 0.5, 0.7: 0.5, 1.0: 1.0}
)
class InstantiateFvarTest(object): class InstantiateFvarTest(object):
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -1321,12 +1531,204 @@ class InstantiateFeatureVariationsTest(object):
assert rec1.ConditionSet.ConditionTable[0].Format == 2 assert rec1.ConditionSet.ConditionTable[0].Format == 2
class LimitTupleVariationAxisRangesTest:
def check_limit_single_var_axis_range(self, var, axisTag, axisRange, expected):
result = instancer.limitTupleVariationAxisRange(var, axisTag, axisRange)
print(result)
assert len(result) == len(expected)
for v1, v2 in zip(result, expected):
assert v1.coordinates == pytest.approx(v2.coordinates)
assert v1.axes.keys() == v2.axes.keys()
for k in v1.axes:
p, q = v1.axes[k], v2.axes[k]
assert p == pytest.approx(q)
@pytest.mark.parametrize(
"var, axisTag, newMax, expected",
[
(
TupleVariation({"wght": (0.0, 1.0, 1.0)}, [100, 100]),
"wdth",
0.5,
[TupleVariation({"wght": (0.0, 1.0, 1.0)}, [100, 100])],
),
(
TupleVariation({"wght": (0.0, 1.0, 1.0)}, [100, 100]),
"wght",
0.5,
[TupleVariation({"wght": (0.0, 1.0, 1.0)}, [50, 50])],
),
(
TupleVariation({"wght": (0.0, 1.0, 1.0)}, [100, 100]),
"wght",
0.8,
[TupleVariation({"wght": (0.0, 1.0, 1.0)}, [80, 80])],
),
(
TupleVariation({"wght": (0.0, 1.0, 1.0)}, [100, 100]),
"wght",
1.0,
[TupleVariation({"wght": (0.0, 1.0, 1.0)}, [100, 100])],
),
(TupleVariation({"wght": (0.0, 1.0, 1.0)}, [100, 100]), "wght", 0.0, []),
(TupleVariation({"wght": (0.5, 1.0, 1.0)}, [100, 100]), "wght", 0.4, []),
(
TupleVariation({"wght": (0.0, 0.5, 1.0)}, [100, 100]),
"wght",
0.5,
[TupleVariation({"wght": (0.0, 1.0, 1.0)}, [100, 100])],
),
(
TupleVariation({"wght": (0.0, 0.5, 1.0)}, [100, 100]),
"wght",
0.4,
[TupleVariation({"wght": (0.0, 1.0, 1.0)}, [80, 80])],
),
(
TupleVariation({"wght": (0.0, 0.5, 1.0)}, [100, 100]),
"wght",
0.6,
[TupleVariation({"wght": (0.0, 0.833334, 1.666667)}, [100, 100])],
),
(
TupleVariation({"wght": (0.0, 0.2, 1.0)}, [100, 100]),
"wght",
0.4,
[
TupleVariation({"wght": (0.0, 0.5, 1.99994)}, [100, 100]),
TupleVariation({"wght": (0.5, 1.0, 1.0)}, [8.33333, 8.33333]),
],
),
(
TupleVariation({"wght": (0.0, 0.2, 1.0)}, [100, 100]),
"wght",
0.5,
[TupleVariation({"wght": (0.0, 0.4, 1.99994)}, [100, 100])],
),
(
TupleVariation({"wght": (0.5, 0.5, 1.0)}, [100, 100]),
"wght",
0.5,
[TupleVariation({"wght": (1.0, 1.0, 1.0)}, [100, 100])],
),
],
)
def test_positive_var(self, var, axisTag, newMax, expected):
axisRange = instancer.NormalizedAxisRange(0, newMax)
self.check_limit_single_var_axis_range(var, axisTag, axisRange, expected)
@pytest.mark.parametrize(
"var, axisTag, newMin, expected",
[
(
TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [100, 100]),
"wdth",
-0.5,
[TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [100, 100])],
),
(
TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [100, 100]),
"wght",
-0.5,
[TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [50, 50])],
),
(
TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [100, 100]),
"wght",
-0.8,
[TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [80, 80])],
),
(
TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [100, 100]),
"wght",
-1.0,
[TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [100, 100])],
),
(TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [100, 100]), "wght", 0.0, []),
(
TupleVariation({"wght": (-1.0, -1.0, -0.5)}, [100, 100]),
"wght",
-0.4,
[],
),
(
TupleVariation({"wght": (-1.0, -0.5, 0.0)}, [100, 100]),
"wght",
-0.5,
[TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [100, 100])],
),
(
TupleVariation({"wght": (-1.0, -0.5, 0.0)}, [100, 100]),
"wght",
-0.4,
[TupleVariation({"wght": (-1.0, -1.0, 0.0)}, [80, 80])],
),
(
TupleVariation({"wght": (-1.0, -0.5, 0.0)}, [100, 100]),
"wght",
-0.6,
[TupleVariation({"wght": (-1.666667, -0.833334, 0.0)}, [100, 100])],
),
(
TupleVariation({"wght": (-1.0, -0.2, 0.0)}, [100, 100]),
"wght",
-0.4,
[
TupleVariation({"wght": (-2.0, -0.5, -0.0)}, [100, 100]),
TupleVariation({"wght": (-1.0, -1.0, -0.5)}, [8.33333, 8.33333]),
],
),
(
TupleVariation({"wght": (-1.0, -0.2, 0.0)}, [100, 100]),
"wght",
-0.5,
[TupleVariation({"wght": (-2.0, -0.4, 0.0)}, [100, 100])],
),
(
TupleVariation({"wght": (-1.0, -0.5, -0.5)}, [100, 100]),
"wght",
-0.5,
[TupleVariation({"wght": (-1.0, -1.0, -1.0)}, [100, 100])],
),
],
)
def test_negative_var(self, var, axisTag, newMin, expected):
axisRange = instancer.NormalizedAxisRange(newMin, 0)
self.check_limit_single_var_axis_range(var, axisTag, axisRange, expected)
@pytest.mark.parametrize(
"oldRange, newRange, expected",
[
((1.0, -1.0), (-1.0, 1.0), None), # invalid oldRange min > max
((0.6, 1.0), (0, 0.5), None),
((-1.0, -0.6), (-0.5, 0), None),
((0.4, 1.0), (0, 0.5), (0.8, 1.0)),
((-1.0, -0.4), (-0.5, 0), (-1.0, -0.8)),
((0.4, 1.0), (0, 0.4), (1.0, 1.0)),
((-1.0, -0.4), (-0.4, 0), (-1.0, -1.0)),
((-0.5, 0.5), (-0.4, 0.4), (-1.0, 1.0)),
((0, 1.0), (-1.0, 0), (0, 0)), # or None?
((-1.0, 0), (0, 1.0), (0, 0)), # or None?
],
)
def test_limitFeatureVariationConditionRange(oldRange, newRange, expected):
condition = featureVars.buildConditionTable(0, *oldRange)
result = instancer._limitFeatureVariationConditionRange(
condition, instancer.NormalizedAxisRange(*newRange)
)
assert result == expected
@pytest.mark.parametrize( @pytest.mark.parametrize(
"limits, expected", "limits, expected",
[ [
(["wght=400", "wdth=100"], {"wght": 400, "wdth": 100}), (["wght=400", "wdth=100"], {"wght": 400, "wdth": 100}),
(["wght=400:900"], {"wght": (400, 900)}), (["wght=400:900"], {"wght": (400, 900)}),
(["slnt=11.4"], {"slnt": 11.4}), (["slnt=11.4"], {"slnt": pytest.approx(11.399994)}),
(["ABCD=drop"], {"ABCD": None}), (["ABCD=drop"], {"ABCD": None}),
], ],
) )
@ -1350,9 +1752,9 @@ def test_normalizeAxisLimits_tuple(varfont):
def test_normalizeAxisLimits_no_avar(varfont): def test_normalizeAxisLimits_no_avar(varfont):
del varfont["avar"] del varfont["avar"]
normalized = instancer.normalizeAxisLimits(varfont, {"wght": (500, 600)}) normalized = instancer.normalizeAxisLimits(varfont, {"wght": (400, 500)})
assert normalized["wght"] == pytest.approx((0.2, 0.4), 1e-4) assert normalized["wght"] == pytest.approx((0, 0.2), 1e-4)
def test_normalizeAxisLimits_missing_from_fvar(varfont): def test_normalizeAxisLimits_missing_from_fvar(varfont):