[feaLib] Handle the ignore pos statement

https://github.com/behdad/fonttools/issues/503
This commit is contained in:
Sascha Brawer 2016-02-06 11:13:58 +01:00
parent 4a23f8eb85
commit b5d1124f43
3 changed files with 71 additions and 12 deletions

View File

@ -273,6 +273,20 @@ class FeatureReferenceStatement(Statement):
builder.add_feature_reference(self.location, self.featureName)
class IgnorePosStatement(Statement):
def __init__(self, location, chainContexts):
Statement.__init__(self, location)
self.chainContexts = chainContexts
def build(self, builder):
for prefix, glyphs, suffix in self.chainContexts:
prefix = [p.glyphSet() for p in prefix]
glyphs = [g.glyphSet() for g in glyphs]
suffix = [s.glyphSet() for s in suffix]
builder.add_chain_context_pos(
self.location, prefix, glyphs, suffix, [])
class IgnoreSubstStatement(Statement):
def __init__(self, location, chainContexts):
Statement.__init__(self, location)

View File

@ -284,29 +284,42 @@ class Parser(object):
values = values[len(prefix):][:len(glyphs)]
return (prefix, glyphs, lookups, values, suffix, hasMarks)
def parse_chain_context_(self):
location = self.cur_token_location_
prefix, glyphs, lookups, values, suffix, hasMarks = \
self.parse_glyph_pattern_(vertical=False)
chainContext = [(prefix, glyphs, suffix)]
hasLookups = any(lookups)
while self.next_token_ == ",":
self.expect_symbol_(",")
prefix, glyphs, lookups, values, suffix, hasMarks = \
self.parse_glyph_pattern_(vertical=False)
chainContext.append((prefix, glyphs, suffix))
hasLookups = hasLookups or any(lookups)
self.expect_symbol_(";")
return chainContext, hasLookups
def parse_ignore_(self):
assert self.is_cur_keyword_("ignore")
location = self.cur_token_location_
self.advance_lexer_()
if self.cur_token_ in ["substitute", "sub"]:
prefix, glyphs, lookups, values, suffix, hasMarks = \
self.parse_glyph_pattern_(vertical=False)
chainContext = [(prefix, glyphs, suffix)]
hasLookups = any(lookups)
while self.next_token_ == ",":
self.expect_symbol_(",")
prefix, glyphs, lookups, values, suffix, hasMarks = \
self.parse_glyph_pattern_(vertical=False)
chainContext.append((prefix, glyphs, suffix))
hasLookups = hasLookups or any(lookups)
self.expect_symbol_(";")
chainContext, hasLookups = self.parse_chain_context_()
if hasLookups:
raise FeatureLibError(
"No lookups can be specified for \"ignore sub\"",
location)
return ast.IgnoreSubstStatement(location, chainContext)
if self.cur_token_ in ["position", "pos"]:
chainContext, hasLookups = self.parse_chain_context_()
if hasLookups:
raise FeatureLibError(
"No lookups can be specified for \"ignore pos\"",
location)
return ast.IgnorePosStatement(location, chainContext)
raise FeatureLibError(
"Expected \"substitute\"", self.next_token_location_)
"Expected \"substitute\" or \"position\"",
self.cur_token_location_)
def parse_language_(self):
assert self.is_cur_keyword_("language")

View File

@ -289,6 +289,38 @@ class ParserTest(unittest.TestCase):
self.assertIsNone(s.markGlyphs)
self.assertIsNone(s.componentGlyphs)
def test_ignore_pos(self):
doc = self.parse("feature test {ignore pos e t' c, q u' u' x;} test;")
sub = doc.statements[0].statements[0]
self.assertIsInstance(sub, ast.IgnorePosStatement)
[(pref1, glyphs1, suff1), (pref2, glyphs2, suff2)] = sub.chainContexts
self.assertEqual(glyphstr(pref1), "e")
self.assertEqual(glyphstr(glyphs1), "t")
self.assertEqual(glyphstr(suff1), "c")
self.assertEqual(glyphstr(pref2), "q")
self.assertEqual(glyphstr(glyphs2), "u u")
self.assertEqual(glyphstr(suff2), "x")
def test_ignore_position(self):
doc = self.parse(
"feature test {"
" ignore position f [a e] d' [a u]' [e y];"
"} test;")
sub = doc.statements[0].statements[0]
self.assertIsInstance(sub, ast.IgnorePosStatement)
[(prefix, glyphs, suffix)] = sub.chainContexts
self.assertEqual(glyphstr(prefix), "f [a e]")
self.assertEqual(glyphstr(glyphs), "d [a u]")
self.assertEqual(glyphstr(suffix), "[e y]")
def test_ignore_position_with_lookup(self):
self.assertRaisesRegex(
FeatureLibError,
'No lookups can be specified for "ignore pos"',
self.parse,
"lookup L { pos [A A.sc] -100; } L;"
"feature test { ignore pos f' i', A' lookup L; } test;")
def test_ignore_sub(self):
doc = self.parse("feature test {ignore sub e t' c, q u' u' x;} test;")
sub = doc.statements[0].statements[0]