[feaLib] Implement GSUB chain substitution rules
This commit is contained in:
parent
196254ae1e
commit
152dff4361
@ -169,6 +169,12 @@ class SubstitutionRule(Statement):
|
||||
self.old_suffix = []
|
||||
self.lookups = [None] * len(old)
|
||||
|
||||
def build(self, builder):
|
||||
builder.add_substitution(
|
||||
self.location,
|
||||
self.old_prefix, self.old, self.old_suffix,
|
||||
self.new, self.lookups)
|
||||
|
||||
|
||||
class ValueRecord(Statement):
|
||||
def __init__(self, location, xPlacement, yPlacement, xAdvance, yAdvance):
|
||||
|
@ -247,6 +247,19 @@ class Builder(object):
|
||||
self.set_language(location, "dflt",
|
||||
include_default=True, required=False)
|
||||
|
||||
def add_substitution(self, location, old_prefix, old, old_suffix, new,
|
||||
lookups):
|
||||
assert len(new) == 0, new
|
||||
lookup_builders = []
|
||||
for lookup in lookups:
|
||||
if lookup is not None:
|
||||
lookup_builders.append(self.named_lookups_.get(lookup.name))
|
||||
else:
|
||||
lookup_builders.append(None)
|
||||
lookup = self.get_lookup_(location, ChainContextSubstBuilder)
|
||||
lookup.substitutions.append((old_prefix, old, old_suffix,
|
||||
lookup_builders))
|
||||
|
||||
def add_alternate_substitution(self, location, glyph, from_class):
|
||||
lookup = self.get_lookup_(location, AlternateSubstBuilder)
|
||||
if glyph in lookup.alternates:
|
||||
@ -314,6 +327,59 @@ class AlternateSubstBuilder(LookupBuilder):
|
||||
return lookup
|
||||
|
||||
|
||||
class ChainContextSubstBuilder(LookupBuilder):
|
||||
def __init__(self, location, lookup_flag):
|
||||
LookupBuilder.__init__(self, location, 'GSUB', 6, lookup_flag)
|
||||
self.substitutions = [] # (prefix, input, suffix, lookups)
|
||||
|
||||
def equals(self, other):
|
||||
return (LookupBuilder.equals(self, other) and
|
||||
self.substitutions == other.substitutions)
|
||||
|
||||
def build(self):
|
||||
lookup = otTables.Lookup()
|
||||
lookup.SubTable = []
|
||||
for (prefix, input, suffix, lookups) in self.substitutions:
|
||||
st = otTables.ChainContextSubst()
|
||||
lookup.SubTable.append(st)
|
||||
st.Format = 3
|
||||
|
||||
st.BacktrackGlyphCount = len(prefix)
|
||||
st.BacktrackCoverage = []
|
||||
for p in reversed(prefix):
|
||||
coverage = otTables.BacktrackCoverage()
|
||||
coverage.glyphs = sorted(list(p))
|
||||
st.BacktrackCoverage.append(coverage)
|
||||
|
||||
st.InputGlyphCount = len(input)
|
||||
st.InputCoverage = []
|
||||
for i in input:
|
||||
coverage = otTables.InputCoverage()
|
||||
coverage.glyphs = sorted(list(i))
|
||||
st.InputCoverage.append(coverage)
|
||||
|
||||
st.LookAheadGlyphCount = len(suffix)
|
||||
st.LookAheadCoverage = []
|
||||
for s in suffix:
|
||||
coverage = otTables.LookAheadCoverage()
|
||||
coverage.glyphs = sorted(list(s))
|
||||
st.LookAheadCoverage.append(coverage)
|
||||
|
||||
st.SubstCount = len([l for l in lookups if l is not None])
|
||||
st.SubstLookupRecord = []
|
||||
for sequenceIndex, l in enumerate(lookups):
|
||||
if l is not None:
|
||||
rec = otTables.SubstLookupRecord()
|
||||
rec.SequenceIndex = sequenceIndex
|
||||
rec.LookupListIndex = l.lookup_index
|
||||
st.SubstLookupRecord.append(rec)
|
||||
|
||||
lookup.LookupFlag = self.lookup_flag
|
||||
lookup.LookupType = self.lookup_type
|
||||
lookup.SubTableCount = len(lookup.SubTable)
|
||||
return lookup
|
||||
|
||||
|
||||
class LigatureSubstBuilder(LookupBuilder):
|
||||
def __init__(self, location, lookup_flag):
|
||||
LookupBuilder.__init__(self, location, 'GSUB', 4, lookup_flag)
|
||||
|
@ -136,8 +136,7 @@ class BuilderTest(unittest.TestCase):
|
||||
# OpenType Feature File specification, section 5.f.i, example 1.
|
||||
font = TTFont()
|
||||
addOpenTypeFeatures(self.getpath("spec5fi1.fea"), font)
|
||||
# TODO: Fix the implementation until the test case passes.
|
||||
# self.expect_ttx(font, self.getpath("spec5fi1.ttx"))
|
||||
self.expect_ttx(font, self.getpath("spec5fi1.ttx"))
|
||||
|
||||
def test_languagesystem(self):
|
||||
builder = Builder(None, TTFont())
|
||||
|
@ -342,7 +342,7 @@ class ParserTest(unittest.TestCase):
|
||||
|
||||
def test_substitute_lookups(self):
|
||||
doc = Parser(self.getpath("spec5fi1.fea")).parse()
|
||||
[ligs, sub, feature] = doc.statements
|
||||
[langsys, ligs, sub, feature] = doc.statements
|
||||
self.assertEqual(feature.statements[0].lookups, [ligs, None, sub])
|
||||
self.assertEqual(feature.statements[1].lookups, [ligs, None, sub])
|
||||
|
||||
|
4
Lib/fontTools/feaLib/testdata/spec5fi1.fea
vendored
4
Lib/fontTools/feaLib/testdata/spec5fi1.fea
vendored
@ -2,11 +2,13 @@
|
||||
# "Specifying a Chain Sub rule and marking sub-runs"
|
||||
# http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html
|
||||
|
||||
languagesystem latn dflt;
|
||||
|
||||
lookup CNTXT_LIGS {
|
||||
substitute f i by f_i;
|
||||
substitute c t by c_t;
|
||||
} CNTXT_LIGS;
|
||||
|
||||
|
||||
lookup CNTXT_SUB {
|
||||
substitute n by n.end;
|
||||
substitute s by s.end;
|
||||
|
22
Lib/fontTools/feaLib/testdata/spec5fi1.ttx
vendored
22
Lib/fontTools/feaLib/testdata/spec5fi1.ttx
vendored
@ -4,10 +4,28 @@
|
||||
<GSUB>
|
||||
<Version value="1.0"/>
|
||||
<ScriptList>
|
||||
<!-- ScriptCount=0 -->
|
||||
<!-- ScriptCount=1 -->
|
||||
<ScriptRecord index="0">
|
||||
<ScriptTag value="latn"/>
|
||||
<Script>
|
||||
<DefaultLangSys>
|
||||
<ReqFeatureIndex value="65535"/>
|
||||
<!-- FeatureCount=1 -->
|
||||
<FeatureIndex index="0" value="0"/>
|
||||
</DefaultLangSys>
|
||||
<!-- LangSysCount=0 -->
|
||||
</Script>
|
||||
</ScriptRecord>
|
||||
</ScriptList>
|
||||
<FeatureList>
|
||||
<!-- FeatureCount=0 -->
|
||||
<!-- FeatureCount=1 -->
|
||||
<FeatureRecord index="0">
|
||||
<FeatureTag value="test"/>
|
||||
<Feature>
|
||||
<!-- LookupCount=1 -->
|
||||
<LookupListIndex index="0" value="2"/>
|
||||
</Feature>
|
||||
</FeatureRecord>
|
||||
</FeatureList>
|
||||
<LookupList>
|
||||
<!-- LookupCount=3 -->
|
||||
|
Loading…
x
Reference in New Issue
Block a user