[feaLib] Support compact syntax for chaining to multiple substitutions
https://github.com/behdad/fonttools/issues/445
This commit is contained in:
parent
6c5cb7f06e
commit
45cc496bed
@ -318,16 +318,19 @@ class MarkMarkPosStatement(Statement):
|
|||||||
|
|
||||||
def build(self, builder):
|
def build(self, builder):
|
||||||
builder.add_mark_mark_pos(self.location, self.baseMarks, self.marks)
|
builder.add_mark_mark_pos(self.location, self.baseMarks, self.marks)
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class MultipleSubstStatement(Statement):
|
class MultipleSubstStatement(Statement):
|
||||||
def __init__(self, location, glyph, replacement):
|
def __init__(self, location, prefix, glyph, suffix, replacement):
|
||||||
Statement.__init__(self, location)
|
Statement.__init__(self, location)
|
||||||
self.glyph, self.replacement = glyph, replacement
|
self.prefix, self.glyph, self.suffix = prefix, glyph, suffix
|
||||||
|
self.replacement = replacement
|
||||||
|
|
||||||
def build(self, builder):
|
def build(self, builder):
|
||||||
builder.add_multiple_subst(self.location, self.glyph, self.replacement)
|
prefix = [p.glyphSet() for p in self.prefix]
|
||||||
|
suffix = [s.glyphSet() for s in self.suffix]
|
||||||
|
builder.add_multiple_subst(
|
||||||
|
self.location, prefix, self.glyph, suffix, self.replacement)
|
||||||
|
|
||||||
|
|
||||||
class PairPosStatement(Statement):
|
class PairPosStatement(Statement):
|
||||||
|
@ -404,7 +404,14 @@ class Builder(object):
|
|||||||
lookup = self.get_lookup_(location, LigatureSubstBuilder)
|
lookup = self.get_lookup_(location, LigatureSubstBuilder)
|
||||||
lookup.ligatures[glyphs] = replacement
|
lookup.ligatures[glyphs] = replacement
|
||||||
|
|
||||||
def add_multiple_subst(self, location, glyph, replacements):
|
def add_multiple_subst(self, location,
|
||||||
|
prefix, glyph, suffix, replacements):
|
||||||
|
if prefix or suffix:
|
||||||
|
sub = self.get_chained_lookup_(location, MultipleSubstBuilder)
|
||||||
|
sub.mapping[glyph] = replacements
|
||||||
|
lookup = self.get_lookup_(location, ChainContextSubstBuilder)
|
||||||
|
lookup.substitutions.append((prefix, [{glyph}], suffix, [sub]))
|
||||||
|
return
|
||||||
lookup = self.get_lookup_(location, MultipleSubstBuilder)
|
lookup = self.get_lookup_(location, MultipleSubstBuilder)
|
||||||
if glyph in lookup.mapping:
|
if glyph in lookup.mapping:
|
||||||
raise FeatureLibError(
|
raise FeatureLibError(
|
||||||
|
@ -96,11 +96,6 @@ class BuilderTest(unittest.TestCase):
|
|||||||
addOpenTypeFeatures(path, font)
|
addOpenTypeFeatures(path, font)
|
||||||
return font
|
return font
|
||||||
|
|
||||||
def test_alternateSubst(self):
|
|
||||||
font = makeTTFont()
|
|
||||||
addOpenTypeFeatures(self.getpath("GSUB_3.fea"), font)
|
|
||||||
self.expect_ttx(font, self.getpath("GSUB_3.ttx"))
|
|
||||||
|
|
||||||
def test_alternateSubst_multipleSubstitutionsForSameGlyph(self):
|
def test_alternateSubst_multipleSubstitutionsForSameGlyph(self):
|
||||||
self.assertRaisesRegex(
|
self.assertRaisesRegex(
|
||||||
FeatureLibError,
|
FeatureLibError,
|
||||||
@ -112,11 +107,6 @@ class BuilderTest(unittest.TestCase):
|
|||||||
" sub A from [A.alt1 A.alt2];"
|
" sub A from [A.alt1 A.alt2];"
|
||||||
"} test;")
|
"} test;")
|
||||||
|
|
||||||
def test_multipleSubst(self):
|
|
||||||
font = makeTTFont()
|
|
||||||
addOpenTypeFeatures(self.getpath("GSUB_2.fea"), font)
|
|
||||||
self.expect_ttx(font, self.getpath("GSUB_2.ttx"))
|
|
||||||
|
|
||||||
def test_multipleSubst_multipleSubstitutionsForSameGlyph(self):
|
def test_multipleSubst_multipleSubstitutionsForSameGlyph(self):
|
||||||
self.assertRaisesRegex(
|
self.assertRaisesRegex(
|
||||||
FeatureLibError,
|
FeatureLibError,
|
||||||
@ -139,11 +129,6 @@ class BuilderTest(unittest.TestCase):
|
|||||||
" pos A B 456;\n"
|
" pos A B 456;\n"
|
||||||
"} test;\n")
|
"} test;\n")
|
||||||
|
|
||||||
def test_reverseChainingSingleSubst(self):
|
|
||||||
font = makeTTFont()
|
|
||||||
addOpenTypeFeatures(self.getpath("GSUB_8.fea"), font)
|
|
||||||
self.expect_ttx(font, self.getpath("GSUB_8.ttx"))
|
|
||||||
|
|
||||||
def test_singleSubst_multipleSubstitutionsForSameGlyph(self):
|
def test_singleSubst_multipleSubstitutionsForSameGlyph(self):
|
||||||
self.assertRaisesRegex(
|
self.assertRaisesRegex(
|
||||||
FeatureLibError,
|
FeatureLibError,
|
||||||
@ -173,6 +158,12 @@ class BuilderTest(unittest.TestCase):
|
|||||||
addOpenTypeFeatures(self.getpath("GPOS_%s.fea" % name), font)
|
addOpenTypeFeatures(self.getpath("GPOS_%s.fea" % name), font)
|
||||||
self.expect_ttx(font, self.getpath("GPOS_%s.ttx" % name))
|
self.expect_ttx(font, self.getpath("GPOS_%s.ttx" % name))
|
||||||
|
|
||||||
|
def test_GSUB(self):
|
||||||
|
for name in "2 3 6 8".split():
|
||||||
|
font = makeTTFont()
|
||||||
|
addOpenTypeFeatures(self.getpath("GSUB_%s.fea" % name), font)
|
||||||
|
self.expect_ttx(font, self.getpath("GSUB_%s.ttx" % name))
|
||||||
|
|
||||||
def test_spec(self):
|
def test_spec(self):
|
||||||
for name in "4h1 5d1 5d2 5fi1 5fi2 5h1 6d2 6e 6f 6h_ii".split():
|
for name in "4h1 5d1 5d2 5fi1 5fi2 5h1 6d2 6e 6f 6h_ii".split():
|
||||||
font = makeTTFont()
|
font = makeTTFont()
|
||||||
|
@ -500,13 +500,12 @@ class Parser(object):
|
|||||||
|
|
||||||
# GSUB lookup type 2: Multiple substitution.
|
# GSUB lookup type 2: Multiple substitution.
|
||||||
# Format: "substitute f_f_i by f f i;"
|
# Format: "substitute f_f_i by f f i;"
|
||||||
if (not reverse and len(old_prefix) == 0 and len(old_suffix) == 0 and
|
if (not reverse and
|
||||||
len(old) == 1 and len(old[0].glyphSet()) == 1 and
|
len(old) == 1 and len(old[0].glyphSet()) == 1 and
|
||||||
len(new) > 1 and max([len(n.glyphSet()) for n in new]) == 1 and
|
len(new) > 1 and max([len(n.glyphSet()) for n in new]) == 1 and
|
||||||
num_lookups == 0):
|
num_lookups == 0):
|
||||||
return ast.MultipleSubstStatement(
|
return ast.MultipleSubstStatement(
|
||||||
location,
|
location, old_prefix, tuple(old[0].glyphSet())[0], old_suffix,
|
||||||
tuple(old[0].glyphSet())[0],
|
|
||||||
tuple([list(n.glyphSet())[0] for n in new]))
|
tuple([list(n.glyphSet())[0] for n in new]))
|
||||||
|
|
||||||
# GSUB lookup type 4: Ligature substitution.
|
# GSUB lookup type 4: Ligature substitution.
|
||||||
|
@ -803,7 +803,14 @@ class ParserTest(unittest.TestCase):
|
|||||||
def test_substitute_multiple(self): # GSUB LookupType 2
|
def test_substitute_multiple(self): # GSUB LookupType 2
|
||||||
doc = self.parse("lookup Look {substitute f_f_i by f f i;} Look;")
|
doc = self.parse("lookup Look {substitute f_f_i by f f i;} Look;")
|
||||||
sub = doc.statements[0].statements[0]
|
sub = doc.statements[0].statements[0]
|
||||||
self.assertEqual(type(sub), ast.MultipleSubstStatement)
|
self.assertIsInstance(sub, ast.MultipleSubstStatement)
|
||||||
|
self.assertEqual(sub.glyph, "f_f_i")
|
||||||
|
self.assertEqual(sub.replacement, ("f", "f", "i"))
|
||||||
|
|
||||||
|
def test_substitute_multiple_chained(self): # GSUB LookupType 2
|
||||||
|
doc = self.parse("lookup L {sub [A-C] f_f_i' [X-Z] by f f i;} L;")
|
||||||
|
sub = doc.statements[0].statements[0]
|
||||||
|
self.assertIsInstance(sub, ast.MultipleSubstStatement)
|
||||||
self.assertEqual(sub.glyph, "f_f_i")
|
self.assertEqual(sub.glyph, "f_f_i")
|
||||||
self.assertEqual(sub.replacement, ("f", "f", "i"))
|
self.assertEqual(sub.replacement, ("f", "f", "i"))
|
||||||
|
|
||||||
|
7
Lib/fontTools/feaLib/testdata/GSUB_6.fea
vendored
Normal file
7
Lib/fontTools/feaLib/testdata/GSUB_6.fea
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
lookup ChainedMultipleSubst {
|
||||||
|
substitute [A-C a-c] [D d] E c_t' V [W w] [X-Z x-z] by c t;
|
||||||
|
} ChainedMultipleSubst;
|
||||||
|
|
||||||
|
feature test {
|
||||||
|
lookup ChainedMultipleSubst;
|
||||||
|
} test;
|
139
Lib/fontTools/feaLib/testdata/GSUB_6.ttx
vendored
Normal file
139
Lib/fontTools/feaLib/testdata/GSUB_6.ttx
vendored
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ttFont>
|
||||||
|
|
||||||
|
<GSUB>
|
||||||
|
<Version value="1.0"/>
|
||||||
|
<ScriptList>
|
||||||
|
<!-- ScriptCount=1 -->
|
||||||
|
<ScriptRecord index="0">
|
||||||
|
<ScriptTag value="DFLT"/>
|
||||||
|
<Script>
|
||||||
|
<DefaultLangSys>
|
||||||
|
<ReqFeatureIndex value="65535"/>
|
||||||
|
<!-- FeatureCount=1 -->
|
||||||
|
<FeatureIndex index="0" value="0"/>
|
||||||
|
</DefaultLangSys>
|
||||||
|
<!-- LangSysCount=0 -->
|
||||||
|
</Script>
|
||||||
|
</ScriptRecord>
|
||||||
|
</ScriptList>
|
||||||
|
<FeatureList>
|
||||||
|
<!-- FeatureCount=1 -->
|
||||||
|
<FeatureRecord index="0">
|
||||||
|
<FeatureTag value="test"/>
|
||||||
|
<Feature>
|
||||||
|
<!-- LookupCount=1 -->
|
||||||
|
<LookupListIndex index="0" value="2"/>
|
||||||
|
</Feature>
|
||||||
|
</FeatureRecord>
|
||||||
|
</FeatureList>
|
||||||
|
<LookupList>
|
||||||
|
<!-- LookupCount=3 -->
|
||||||
|
<Lookup index="0">
|
||||||
|
<!-- LookupType=2 -->
|
||||||
|
<LookupFlag value="0"/>
|
||||||
|
<!-- SubTableCount=1 -->
|
||||||
|
<MultipleSubst index="0">
|
||||||
|
<Substitution in="c_t" out="c,t"/>
|
||||||
|
</MultipleSubst>
|
||||||
|
</Lookup>
|
||||||
|
<Lookup index="1">
|
||||||
|
<!-- LookupType=6 -->
|
||||||
|
<LookupFlag value="0"/>
|
||||||
|
<!-- SubTableCount=1 -->
|
||||||
|
<ChainContextSubst index="0" Format="3">
|
||||||
|
<!-- BacktrackGlyphCount=3 -->
|
||||||
|
<BacktrackCoverage index="0">
|
||||||
|
<Glyph value="E"/>
|
||||||
|
</BacktrackCoverage>
|
||||||
|
<BacktrackCoverage index="1">
|
||||||
|
<Glyph value="D"/>
|
||||||
|
<Glyph value="d"/>
|
||||||
|
</BacktrackCoverage>
|
||||||
|
<BacktrackCoverage index="2">
|
||||||
|
<Glyph value="A"/>
|
||||||
|
<Glyph value="B"/>
|
||||||
|
<Glyph value="C"/>
|
||||||
|
<Glyph value="a"/>
|
||||||
|
<Glyph value="b"/>
|
||||||
|
<Glyph value="c"/>
|
||||||
|
</BacktrackCoverage>
|
||||||
|
<!-- InputGlyphCount=1 -->
|
||||||
|
<InputCoverage index="0">
|
||||||
|
<Glyph value="c_t"/>
|
||||||
|
</InputCoverage>
|
||||||
|
<!-- LookAheadGlyphCount=3 -->
|
||||||
|
<LookAheadCoverage index="0">
|
||||||
|
<Glyph value="V"/>
|
||||||
|
</LookAheadCoverage>
|
||||||
|
<LookAheadCoverage index="1">
|
||||||
|
<Glyph value="W"/>
|
||||||
|
<Glyph value="w"/>
|
||||||
|
</LookAheadCoverage>
|
||||||
|
<LookAheadCoverage index="2">
|
||||||
|
<Glyph value="X"/>
|
||||||
|
<Glyph value="Y"/>
|
||||||
|
<Glyph value="Z"/>
|
||||||
|
<Glyph value="x"/>
|
||||||
|
<Glyph value="y"/>
|
||||||
|
<Glyph value="z"/>
|
||||||
|
</LookAheadCoverage>
|
||||||
|
<!-- SubstCount=1 -->
|
||||||
|
<SubstLookupRecord index="0">
|
||||||
|
<SequenceIndex value="0"/>
|
||||||
|
<LookupListIndex value="0"/>
|
||||||
|
</SubstLookupRecord>
|
||||||
|
</ChainContextSubst>
|
||||||
|
</Lookup>
|
||||||
|
<Lookup index="2">
|
||||||
|
<!-- LookupType=6 -->
|
||||||
|
<LookupFlag value="0"/>
|
||||||
|
<!-- SubTableCount=1 -->
|
||||||
|
<ChainContextSubst index="0" Format="3">
|
||||||
|
<!-- BacktrackGlyphCount=3 -->
|
||||||
|
<BacktrackCoverage index="0">
|
||||||
|
<Glyph value="E"/>
|
||||||
|
</BacktrackCoverage>
|
||||||
|
<BacktrackCoverage index="1">
|
||||||
|
<Glyph value="D"/>
|
||||||
|
<Glyph value="d"/>
|
||||||
|
</BacktrackCoverage>
|
||||||
|
<BacktrackCoverage index="2">
|
||||||
|
<Glyph value="A"/>
|
||||||
|
<Glyph value="B"/>
|
||||||
|
<Glyph value="C"/>
|
||||||
|
<Glyph value="a"/>
|
||||||
|
<Glyph value="b"/>
|
||||||
|
<Glyph value="c"/>
|
||||||
|
</BacktrackCoverage>
|
||||||
|
<!-- InputGlyphCount=1 -->
|
||||||
|
<InputCoverage index="0">
|
||||||
|
<Glyph value="c_t"/>
|
||||||
|
</InputCoverage>
|
||||||
|
<!-- LookAheadGlyphCount=3 -->
|
||||||
|
<LookAheadCoverage index="0">
|
||||||
|
<Glyph value="V"/>
|
||||||
|
</LookAheadCoverage>
|
||||||
|
<LookAheadCoverage index="1">
|
||||||
|
<Glyph value="W"/>
|
||||||
|
<Glyph value="w"/>
|
||||||
|
</LookAheadCoverage>
|
||||||
|
<LookAheadCoverage index="2">
|
||||||
|
<Glyph value="X"/>
|
||||||
|
<Glyph value="Y"/>
|
||||||
|
<Glyph value="Z"/>
|
||||||
|
<Glyph value="x"/>
|
||||||
|
<Glyph value="y"/>
|
||||||
|
<Glyph value="z"/>
|
||||||
|
</LookAheadCoverage>
|
||||||
|
<!-- SubstCount=1 -->
|
||||||
|
<SubstLookupRecord index="0">
|
||||||
|
<SequenceIndex value="0"/>
|
||||||
|
<LookupListIndex value="0"/>
|
||||||
|
</SubstLookupRecord>
|
||||||
|
</ChainContextSubst>
|
||||||
|
</Lookup>
|
||||||
|
</LookupList>
|
||||||
|
</GSUB>
|
||||||
|
|
||||||
|
</ttFont>
|
Loading…
x
Reference in New Issue
Block a user