Merge pull request #3042 from fonttools/conditionset-consult-avar

Conditionset: consult avar to normalize values
This commit is contained in:
Nikolaus Waxweiler 2023-03-15 09:43:20 +00:00 committed by GitHub
commit a55a545b12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 70 additions and 10 deletions

View File

@ -2065,7 +2065,7 @@ class ConditionsetStatement(Statement):
self.conditions = conditions self.conditions = conditions
def build(self, builder): def build(self, builder):
builder.add_conditionset(self.name, self.conditions) builder.add_conditionset(self.location, self.name, self.conditions)
def asFea(self, res="", indent=""): def asFea(self, res="", indent=""):
res += indent + f"conditionset {self.name} " + "{\n" res += indent + f"conditionset {self.name} " + "{\n"

View File

@ -34,7 +34,7 @@ from fontTools.otlLib.error import OpenTypeLibError
from fontTools.varLib.varStore import OnlineVarStoreBuilder from fontTools.varLib.varStore import OnlineVarStoreBuilder
from fontTools.varLib.builder import buildVarDevTable from fontTools.varLib.builder import buildVarDevTable
from fontTools.varLib.featureVars import addFeatureVariationsRaw from fontTools.varLib.featureVars import addFeatureVariationsRaw
from fontTools.varLib.models import normalizeValue from fontTools.varLib.models import normalizeValue, piecewiseLinearMap
from collections import defaultdict from collections import defaultdict
import itertools import itertools
from io import StringIO from io import StringIO
@ -947,11 +947,7 @@ class Builder(object):
feature_vars = {} feature_vars = {}
has_any_variations = False has_any_variations = False
# Sort out which lookups to build, gather their indices # Sort out which lookups to build, gather their indices
for ( for (_, _, feature_tag), variations in self.feature_variations_.items():
script_,
language,
feature_tag,
), variations in self.feature_variations_.items():
feature_vars[feature_tag] = [] feature_vars[feature_tag] = []
for conditionset, builders in variations.items(): for conditionset, builders in variations.items():
raw_conditionset = self.conditionsets_[conditionset] raw_conditionset = self.conditionsets_[conditionset]
@ -1572,10 +1568,11 @@ class Builder(object):
def add_vhea_field(self, key, value): def add_vhea_field(self, key, value):
self.vhea_[key] = value self.vhea_[key] = value
def add_conditionset(self, key, value): def add_conditionset(self, location, key, value):
if not "fvar" in self.font: if "fvar" not in self.font:
raise FeatureLibError( raise FeatureLibError(
"Cannot add feature variations to a font without an 'fvar' table" "Cannot add feature variations to a font without an 'fvar' table",
location,
) )
# Normalize # Normalize
@ -1592,6 +1589,18 @@ class Builder(object):
for tag, (bottom, top) in value.items() for tag, (bottom, top) in value.items()
} }
# NOTE: This might result in rounding errors (off-by-ones) compared to
# rules in Designspace files, since we're working with what's in the
# `avar` table rather than the original values.
if "avar" in self.font:
mapping = self.font["avar"].segments
value = {
axis: tuple(
piecewiseLinearMap(v, mapping[axis]) for v in condition_range
)
for axis, condition_range in value.items()
}
self.conditionsets_[key] = value self.conditionsets_[key] = value
def makeOpenTypeAnchor(self, location, anchor): def makeOpenTypeAnchor(self, location, anchor):

View File

@ -975,6 +975,57 @@ class BuilderTest(unittest.TestCase):
f'{name}.fea:{line}:12: Ambiguous "ignore {sub}", there should be least one marked glyph' f'{name}.fea:{line}:12: Ambiguous "ignore {sub}", there should be least one marked glyph'
) )
def test_condition_set_avar(self):
"""Test that the `avar` table is consulted when normalizing user-space
values."""
features = """
languagesystem DFLT dflt;
lookup conditional_sub {
sub e by a;
} conditional_sub;
conditionset test {
wght 600 1000;
} test;
variation rlig test {
lookup conditional_sub;
} rlig;
"""
def make_mock_vf():
font = makeTTFont()
font["name"] = newTable("name")
addFvar(font, [("wght", 0, 0, 1000, "Weight")], [])
del font["name"]
return font
# Without `avar`:
font = make_mock_vf()
addOpenTypeFeaturesFromString(font, features)
assert (
font.tables["GSUB"]
.table.FeatureVariations.FeatureVariationRecord[0]
.ConditionSet.ConditionTable[0]
.FilterRangeMinValue
== 0.6 # user-space 600
)
# With `avar`, shifting the positive midpoint 0.5 a bit to the right:
font = make_mock_vf()
font["avar"] = newTable("avar")
font["avar"].segments = {"wght": {-1.0: -1.0, 0.0: 0.0, 0.5: 0.625, 1.0: 1.0}}
addOpenTypeFeaturesFromString(font, features)
assert (
font.tables["GSUB"]
.table.FeatureVariations.FeatureVariationRecord[0]
.ConditionSet.ConditionTable[0]
.FilterRangeMinValue
== 0.7 # user-space 600 shifted to the right,
)
def generate_feature_file_test(name): def generate_feature_file_test(name):
return lambda self: self.check_feature_file(name) return lambda self: self.check_feature_file(name)