diff --git a/Lib/fontTools/feaLib/ast.py b/Lib/fontTools/feaLib/ast.py index 75b2c1060..5cc244e6b 100644 --- a/Lib/fontTools/feaLib/ast.py +++ b/Lib/fontTools/feaLib/ast.py @@ -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) diff --git a/Lib/fontTools/feaLib/parser.py b/Lib/fontTools/feaLib/parser.py index 6ce1273aa..25ee72c1a 100644 --- a/Lib/fontTools/feaLib/parser.py +++ b/Lib/fontTools/feaLib/parser.py @@ -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") diff --git a/Lib/fontTools/feaLib/parser_test.py b/Lib/fontTools/feaLib/parser_test.py index 7b0ef0934..c4c616a38 100644 --- a/Lib/fontTools/feaLib/parser_test.py +++ b/Lib/fontTools/feaLib/parser_test.py @@ -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]