diff --git a/Lib/fontTools/varLib/avar.py b/Lib/fontTools/varLib/avar.py index bcc4a570e..1233068c3 100644 --- a/Lib/fontTools/varLib/avar.py +++ b/Lib/fontTools/varLib/avar.py @@ -22,15 +22,16 @@ def _pruneLocations(locations, poles, axisTags): model = VariationModel(locations, axisTags) modelMapping = model.mapping modelSupports = model.supports - pins = poles.copy() - for pole in poles.keys(): - location = dict(pole) + pins = {tuple(k.items()): None for k in poles} + for location in poles: i = locations.index(location) i = modelMapping[i] support = modelSupports[i] supportAxes = set(support.keys()) - for supportIndex, (axisTag, (minV, _, maxV)) in enumerate(support.items()): + for axisTag, (minV, _, maxV) in support.items(): for v in (minV, maxV): + if v in (-1, 0, 1): + continue for pin in pins.keys(): pinLocation = dict(pin) pinAxes = set(pinLocation.keys()) @@ -40,21 +41,27 @@ def _pruneLocations(locations, poles, axisTags): continue if pinLocation[axisTag] == v: break - else: - # No pin found. Go through the previous masters - # and find a suitable pin. Going backwards is - # better because it can find a pin that is close - # to the pole in more dimensions, and reducing - # the total number of pins needed. - for candidateIdx in range(supportIndex - 1, -1, -1): - candidate = modelSupports[candidateIdx] - candidateAxes = set(candidate.keys()) - if candidateAxes != supportAxes: - continue - if axisTag not in candidateAxes: - continue - if candidateLocation[axisTag] == v: - pins[tuple(candidateLocation.items())] = None + else: + # No pin found. Go through the previous masters + # and find a suitable pin. Going backwards is + # better because it can find a pin that is close + # to the pole in more dimensions, and reducing + # the total number of pins needed. + for candidateIdx in range(i - 1, -1, -1): + candidate = modelSupports[candidateIdx] + candidateAxes = set(candidate.keys()) + if candidateAxes != supportAxes: + continue + if axisTag not in candidateAxes: + continue + candidate = { + k: defaultV for k, (_, defaultV, _) in candidate.items() + } + if candidate[axisTag] == v: + pins[tuple(candidate.items())] = None + break + else: + assert False, "No pin found" return [dict(t) for t in pins.keys()] @@ -115,7 +122,8 @@ def mappings_from_avar(font, denormalize=True): key=lambda t: (len(t), tuple(axisIndexes[tag] for tag, _ in t)), ) ] - inputLocations = _pruneLocations(inputLocations, poles, axisTags) + poles = [dict(t) for t in poles.keys()] + inputLocations = _pruneLocations(inputLocations, list(poles), axisTags) # Find the output locations, at input locations varIdxMap = avar.table.VarIdxMap @@ -210,6 +218,7 @@ def main(args=None): segments, mappings = mappings_from_avar(font) pprint(segments) pprint(mappings) + print(len(mappings), "mappings") return axisTags = [a.axisTag for a in font["fvar"].axes] diff --git a/Tests/varLib/avar_test.py b/Tests/varLib/avar_test.py new file mode 100644 index 000000000..b4e1132fe --- /dev/null +++ b/Tests/varLib/avar_test.py @@ -0,0 +1,59 @@ +from fontTools.varLib.avar import _pruneLocations +import unittest +import pytest + + +@pytest.mark.parametrize( + "locations, poles, expected", + [ + ( + [ + {"wght": 1}, + {"wght": 0.5}, + ], + [ + {"wght": 0.5}, + ], + [ + {"wght": 0.5}, + ], + ), + ( + [ + {"wght": 1, "wdth": 1}, + {"wght": 0.5, "wdth": 1}, + ], + [ + {"wght": 1, "wdth": 1}, + ], + [ + {"wght": 1, "wdth": 1}, + {"wght": 0.5, "wdth": 1}, + ], + ), + ( + [ + {"wght": 1}, + {"wdth": 1}, + {"wght": 0.5, "wdth": 0.5}, + ], + [ + {"wght": 0.5, "wdth": 0.5}, + ], + [ + {"wght": 0.5, "wdth": 0.5}, + ], + ), + ], +) +def test_pruneLocations(locations, poles, expected): + axisTags = set() + for location in locations: + axisTags.update(location.keys()) + axisTags = sorted(axisTags) + + locations = [{}] + locations + + output = _pruneLocations(locations, poles, axisTags) + + assert output == expected, (output, expected)