[feaLib] Emit chains to LigatureSubst even without backtrack or lookahead
https://github.com/behdad/fonttools/issues/506
This commit is contained in:
parent
ea4a4e34b3
commit
dba1d6666b
@ -326,17 +326,19 @@ class LigatureCaretByPosStatement(Statement):
|
|||||||
|
|
||||||
|
|
||||||
class LigatureSubstStatement(Statement):
|
class LigatureSubstStatement(Statement):
|
||||||
def __init__(self, location, prefix, glyphs, suffix, replacement):
|
def __init__(self, location, prefix, glyphs, suffix, replacement,
|
||||||
|
forceChain):
|
||||||
Statement.__init__(self, location)
|
Statement.__init__(self, location)
|
||||||
self.prefix, self.glyphs, self.suffix = (prefix, glyphs, suffix)
|
self.prefix, self.glyphs, self.suffix = (prefix, glyphs, suffix)
|
||||||
self.replacement = replacement
|
self.replacement, self.forceChain = replacement, forceChain
|
||||||
|
|
||||||
def build(self, builder):
|
def build(self, builder):
|
||||||
prefix = [p.glyphSet() for p in self.prefix]
|
prefix = [p.glyphSet() for p in self.prefix]
|
||||||
glyphs = [g.glyphSet() for g in self.glyphs]
|
glyphs = [g.glyphSet() for g in self.glyphs]
|
||||||
suffix = [s.glyphSet() for s in self.suffix]
|
suffix = [s.glyphSet() for s in self.suffix]
|
||||||
builder.add_ligature_subst(
|
builder.add_ligature_subst(
|
||||||
self.location, prefix, glyphs, suffix, self.replacement)
|
self.location, prefix, glyphs, suffix, self.replacement,
|
||||||
|
self.forceChain)
|
||||||
|
|
||||||
|
|
||||||
class LookupFlagStatement(Statement):
|
class LookupFlagStatement(Statement):
|
||||||
|
@ -512,8 +512,8 @@ class Builder(object):
|
|||||||
self.aalt_features_.append((location, featureName))
|
self.aalt_features_.append((location, featureName))
|
||||||
|
|
||||||
def add_ligature_subst(self, location,
|
def add_ligature_subst(self, location,
|
||||||
prefix, glyphs, suffix, replacement):
|
prefix, glyphs, suffix, replacement, forceChain):
|
||||||
if prefix or suffix:
|
if prefix or suffix or forceChain:
|
||||||
chain = self.get_lookup_(location, ChainContextSubstBuilder)
|
chain = self.get_lookup_(location, ChainContextSubstBuilder)
|
||||||
lookup = self.get_chained_lookup_(location, LigatureSubstBuilder)
|
lookup = self.get_chained_lookup_(location, LigatureSubstBuilder)
|
||||||
chain.substitutions.append((prefix, glyphs, suffix, [lookup]))
|
chain.substitutions.append((prefix, glyphs, suffix, [lookup]))
|
||||||
|
@ -52,7 +52,7 @@ class BuilderTest(unittest.TestCase):
|
|||||||
spec5f_ii_1
|
spec5f_ii_1
|
||||||
spec5h1 spec6b_ii spec6d2 spec6e spec6f spec6h_ii spec6h_iii_1 spec8a
|
spec5h1 spec6b_ii spec6d2 spec6e spec6f spec6h_ii spec6h_iii_1 spec8a
|
||||||
spec9b spec9c1 spec9c2 spec9c3
|
spec9b spec9c1 spec9c2 spec9c3
|
||||||
bug463 bug501 bug502 bug505
|
bug463 bug501 bug502 bug505 bug506
|
||||||
""".split()
|
""".split()
|
||||||
|
|
||||||
def __init__(self, methodName):
|
def __init__(self, methodName):
|
||||||
|
@ -241,12 +241,13 @@ class Parser(object):
|
|||||||
|
|
||||||
def parse_glyph_pattern_(self, vertical):
|
def parse_glyph_pattern_(self, vertical):
|
||||||
prefix, glyphs, lookups, values, suffix = ([], [], [], [], [])
|
prefix, glyphs, lookups, values, suffix = ([], [], [], [], [])
|
||||||
|
hasMarks = False
|
||||||
while self.next_token_ not in {"by", "from", ";"}:
|
while self.next_token_ not in {"by", "from", ";"}:
|
||||||
gc = self.parse_glyphclass_(accept_glyphname=True)
|
gc = self.parse_glyphclass_(accept_glyphname=True)
|
||||||
marked = False
|
marked = False
|
||||||
if self.next_token_ == "'":
|
if self.next_token_ == "'":
|
||||||
self.expect_symbol_("'")
|
self.expect_symbol_("'")
|
||||||
marked = True
|
hasMarks = marked = True
|
||||||
if marked:
|
if marked:
|
||||||
glyphs.append(gc)
|
glyphs.append(gc)
|
||||||
elif glyphs:
|
elif glyphs:
|
||||||
@ -277,18 +278,18 @@ class Parser(object):
|
|||||||
|
|
||||||
if not glyphs and not suffix: # eg., "sub f f i by"
|
if not glyphs and not suffix: # eg., "sub f f i by"
|
||||||
assert lookups == []
|
assert lookups == []
|
||||||
return ([], prefix, [None] * len(prefix), values, [])
|
return ([], prefix, [None] * len(prefix), values, [], hasMarks)
|
||||||
else:
|
else:
|
||||||
assert not any(values[:len(prefix)]), values
|
assert not any(values[:len(prefix)]), values
|
||||||
values = values[len(prefix):][:len(glyphs)]
|
values = values[len(prefix):][:len(glyphs)]
|
||||||
return (prefix, glyphs, lookups, values, suffix)
|
return (prefix, glyphs, lookups, values, suffix, hasMarks)
|
||||||
|
|
||||||
def parse_ignore_(self):
|
def parse_ignore_(self):
|
||||||
assert self.is_cur_keyword_("ignore")
|
assert self.is_cur_keyword_("ignore")
|
||||||
location = self.cur_token_location_
|
location = self.cur_token_location_
|
||||||
self.advance_lexer_()
|
self.advance_lexer_()
|
||||||
if self.cur_token_ in ["substitute", "sub"]:
|
if self.cur_token_ in ["substitute", "sub"]:
|
||||||
prefix, glyphs, lookups, values, suffix = \
|
prefix, glyphs, lookups, values, suffix, hasMarks = \
|
||||||
self.parse_glyph_pattern_(vertical=False)
|
self.parse_glyph_pattern_(vertical=False)
|
||||||
self.expect_symbol_(";")
|
self.expect_symbol_(";")
|
||||||
if any(lookups):
|
if any(lookups):
|
||||||
@ -423,7 +424,7 @@ class Parser(object):
|
|||||||
return self.parse_position_mark_(enumerated, vertical)
|
return self.parse_position_mark_(enumerated, vertical)
|
||||||
|
|
||||||
location = self.cur_token_location_
|
location = self.cur_token_location_
|
||||||
prefix, glyphs, lookups, values, suffix = \
|
prefix, glyphs, lookups, values, suffix, hasMarks = \
|
||||||
self.parse_glyph_pattern_(vertical)
|
self.parse_glyph_pattern_(vertical)
|
||||||
self.expect_symbol_(";")
|
self.expect_symbol_(";")
|
||||||
|
|
||||||
@ -518,7 +519,7 @@ class Parser(object):
|
|||||||
assert self.cur_token_ in {"substitute", "sub", "reversesub", "rsub"}
|
assert self.cur_token_ in {"substitute", "sub", "reversesub", "rsub"}
|
||||||
location = self.cur_token_location_
|
location = self.cur_token_location_
|
||||||
reverse = self.cur_token_ in {"reversesub", "rsub"}
|
reverse = self.cur_token_ in {"reversesub", "rsub"}
|
||||||
old_prefix, old, lookups, values, old_suffix = \
|
old_prefix, old, lookups, values, old_suffix, hasMarks = \
|
||||||
self.parse_glyph_pattern_(vertical=False)
|
self.parse_glyph_pattern_(vertical=False)
|
||||||
if any(values):
|
if any(values):
|
||||||
raise FeatureLibError(
|
raise FeatureLibError(
|
||||||
@ -597,7 +598,7 @@ class Parser(object):
|
|||||||
num_lookups == 0):
|
num_lookups == 0):
|
||||||
return ast.LigatureSubstStatement(
|
return ast.LigatureSubstStatement(
|
||||||
location, old_prefix, old, old_suffix,
|
location, old_prefix, old, old_suffix,
|
||||||
list(new[0].glyphSet())[0])
|
list(new[0].glyphSet())[0], forceChain=hasMarks)
|
||||||
|
|
||||||
# GSUB lookup type 8: Reverse chaining substitution.
|
# GSUB lookup type 8: Reverse chaining substitution.
|
||||||
if reverse:
|
if reverse:
|
||||||
|
4
Lib/fontTools/feaLib/testdata/bug506.fea
vendored
Normal file
4
Lib/fontTools/feaLib/testdata/bug506.fea
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# https://github.com/behdad/fonttools/issues/506
|
||||||
|
feature test {
|
||||||
|
sub f' i' by f_i;
|
||||||
|
} test;
|
66
Lib/fontTools/feaLib/testdata/bug506.ttx
vendored
Normal file
66
Lib/fontTools/feaLib/testdata/bug506.ttx
vendored
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ttFont sfntVersion="true" ttLibVersion="3.0">
|
||||||
|
|
||||||
|
<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="0"/>
|
||||||
|
</Feature>
|
||||||
|
</FeatureRecord>
|
||||||
|
</FeatureList>
|
||||||
|
<LookupList>
|
||||||
|
<!-- LookupCount=2 -->
|
||||||
|
<Lookup index="0">
|
||||||
|
<!-- LookupType=6 -->
|
||||||
|
<LookupFlag value="0"/>
|
||||||
|
<!-- SubTableCount=1 -->
|
||||||
|
<ChainContextSubst index="0" Format="3">
|
||||||
|
<!-- BacktrackGlyphCount=0 -->
|
||||||
|
<!-- InputGlyphCount=2 -->
|
||||||
|
<InputCoverage index="0">
|
||||||
|
<Glyph value="f"/>
|
||||||
|
</InputCoverage>
|
||||||
|
<InputCoverage index="1">
|
||||||
|
<Glyph value="i"/>
|
||||||
|
</InputCoverage>
|
||||||
|
<!-- LookAheadGlyphCount=0 -->
|
||||||
|
<!-- SubstCount=1 -->
|
||||||
|
<SubstLookupRecord index="0">
|
||||||
|
<SequenceIndex value="0"/>
|
||||||
|
<LookupListIndex value="1"/>
|
||||||
|
</SubstLookupRecord>
|
||||||
|
</ChainContextSubst>
|
||||||
|
</Lookup>
|
||||||
|
<Lookup index="1">
|
||||||
|
<!-- LookupType=4 -->
|
||||||
|
<LookupFlag value="0"/>
|
||||||
|
<!-- SubTableCount=1 -->
|
||||||
|
<LigatureSubst index="0">
|
||||||
|
<LigatureSet glyph="f">
|
||||||
|
<Ligature components="i" glyph="f_i"/>
|
||||||
|
</LigatureSet>
|
||||||
|
</LigatureSubst>
|
||||||
|
</Lookup>
|
||||||
|
</LookupList>
|
||||||
|
</GSUB>
|
||||||
|
|
||||||
|
</ttFont>
|
Loading…
x
Reference in New Issue
Block a user