Skip to content

Commit

Permalink
Support variable feature syntax (#2432)
Browse files Browse the repository at this point in the history
  • Loading branch information
simoncozens committed Oct 28, 2021
1 parent c194a18 commit 563730f
Show file tree
Hide file tree
Showing 14 changed files with 1,671 additions and 657 deletions.
91 changes: 89 additions & 2 deletions Lib/fontTools/feaLib/ast.py
Expand Up @@ -34,6 +34,7 @@
"ChainContextPosStatement",
"ChainContextSubstStatement",
"CharacterStatement",
"ConditionsetStatement",
"CursivePosStatement",
"ElidedFallbackName",
"ElidedFallbackNameID",
Expand Down Expand Up @@ -1261,11 +1262,21 @@ def build(self, builder):
if not self.replacement and hasattr(self.glyph, "glyphSet"):
for glyph in self.glyph.glyphSet():
builder.add_multiple_subst(
self.location, prefix, glyph, suffix, self.replacement, self.forceChain
self.location,
prefix,
glyph,
suffix,
self.replacement,
self.forceChain,
)
else:
builder.add_multiple_subst(
self.location, prefix, self.glyph, suffix, self.replacement, self.forceChain
self.location,
prefix,
self.glyph,
suffix,
self.replacement,
self.forceChain,
)

def asFea(self, indent=""):
Expand Down Expand Up @@ -2033,3 +2044,79 @@ def asFea(self, res=""):
res += f"location {self.tag} "
res += f"{' '.join(str(i) for i in self.values)};\n"
return res


class ConditionsetStatement(Statement):
"""
A variable layout conditionset
Args:
name (str): the name of this conditionset
conditions (dict): a dictionary mapping axis tags to a
tuple of (min,max) userspace coordinates.
"""

def __init__(self, name, conditions, location=None):
Statement.__init__(self, location)
self.name = name
self.conditions = conditions

def build(self, builder):
builder.add_conditionset(self.name, self.conditions)

def asFea(self, res="", indent=""):
res += indent + f"conditionset {self.name} " + "{\n"
for tag, (minvalue, maxvalue) in self.conditions.items():
res += indent + SHIFT + f"{tag} {minvalue} {maxvalue};\n"
res += indent + "}" + f" {self.name};\n"
return res


class VariationBlock(Block):
"""A variation feature block, applicable in a given set of conditions."""

def __init__(self, name, conditionset, use_extension=False, location=None):
Block.__init__(self, location)
self.name, self.conditionset, self.use_extension = (
name,
conditionset,
use_extension,
)

def build(self, builder):
"""Call the ``start_feature`` callback on the builder object, visit
all the statements in this feature, and then call ``end_feature``."""
builder.start_feature(self.location, self.name)
if (
self.conditionset != "NULL"
and self.conditionset not in builder.conditionsets_
):
raise FeatureLibError(
f"variation block used undefined conditionset {self.conditionset}",
self.location,
)

# language exclude_dflt statements modify builder.features_
# limit them to this block with temporary builder.features_
features = builder.features_
builder.features_ = {}
Block.build(self, builder)
for key, value in builder.features_.items():
items = builder.feature_variations_.setdefault(key, {}).setdefault(
self.conditionset, []
)
items.extend(value)
if key not in features:
features[key] = [] # Ensure we make a feature record
builder.features_ = features
builder.end_feature()

def asFea(self, indent=""):
res = indent + "variation %s " % self.name.strip()
res += self.conditionset + " "
if self.use_extension:
res += "useExtension "
res += "{\n"
res += Block.asFea(self, indent=indent)
res += indent + "} %s;\n" % self.name.strip()
return res

0 comments on commit 563730f

Please sign in to comment.