[feaLib] Parse substitute from statements

This commit is contained in:
Sascha Brawer 2015-08-11 15:54:54 +02:00
parent 8b77f68dec
commit 72c1f165b4
3 changed files with 46 additions and 5 deletions

View File

@ -28,6 +28,12 @@ class GlyphClassDefinition(object):
self.glyphs = glyphs
class AlternateSubstitution(object):
def __init__(self, location, glyph, from_class):
self.location = location
self.glyph, self.from_class = (glyph, from_class)
class AnchorDefinition(object):
def __init__(self, location, name, x, y, contourpoint):
self.location = location

View File

@ -130,7 +130,7 @@ class Parser(object):
def parse_glyph_pattern_(self):
prefix, glyphs, lookups, suffix = ([], [], [], [])
while self.next_token_ not in {"by", ";"}:
while self.next_token_ not in {"by", "from", ";"}:
gc = self.parse_glyphclass_(accept_glyphname=True)
marked = False
if self.next_token_ == "'":
@ -218,15 +218,31 @@ class Parser(object):
assert self.cur_token_ in {"substitute", "sub"}
location = self.cur_token_location_
old_prefix, old, lookups, old_suffix = self.parse_glyph_pattern_()
if self.next_token_ == "by":
self.expect_keyword_("by")
keyword = self.expect_keyword_("by")
new = [self.parse_glyphclass_(accept_glyphname=True)]
elif self.next_token_ == "from":
keyword = self.expect_keyword_("from")
new = [self.parse_glyphclass_(accept_glyphname=False)]
else:
keyword = None
new = []
self.expect_symbol_(";")
if len(new) is 0 and not any(lookups):
raise ParserError('Expected "by" or explicit lookup references',
self.cur_token_location_)
raise ParserError(
'Expected "by", "from" or explicit lookup references',
self.cur_token_location_)
if keyword == "from":
if len(old) != 1 or len(old[0]) != 1:
raise ParserError('Expected a single glyph before "from"',
location)
if len(new) != 1:
raise ParserError('Expected a single glyphclass after "from"',
location)
return ast.AlternateSubstitution(location, list(old[0])[0], new[0])
rule = ast.SubstitutionRule(location, old, new)
rule.old_prefix, rule.old_suffix = old_prefix, old_suffix
rule.lookups = lookups

View File

@ -289,6 +289,25 @@ class ParserTest(unittest.TestCase):
self.assertEqual(sub.new, [{"A.sc", "B.sc", "C.sc", "D.sc"}])
self.assertEqual(sub.lookups, [None])
def test_substitute_from(self): # GSUB LookupType 3
doc = self.parse("feature test {"
" substitute a from [a.1 a.2 a.3];"
"} test;")
sub = doc.statements[0].statements[0]
self.assertEqual(type(sub), ast.AlternateSubstitution)
self.assertEqual(sub.glyph, "a")
self.assertEqual(sub.from_class, {"a.1", "a.2", "a.3"})
def test_substitute_from_glyphclass(self): # GSUB LookupType 3
doc = self.parse("feature test {"
" @Ampersands = [ampersand.1 ampersand.2];"
" substitute ampersand from @Ampersands;"
"} test;")
[glyphclass, sub] = doc.statements[0].statements
self.assertEqual(type(sub), ast.AlternateSubstitution)
self.assertEqual(sub.glyph, "ampersand")
self.assertEqual(sub.from_class, {"ampersand.1", "ampersand.2"})
def test_substitute_ligature(self): # GSUB LookupType 4
doc = self.parse("feature liga {substitute f f i by f_f_i;} liga;")
sub = doc.statements[0].statements[0]
@ -306,7 +325,7 @@ class ParserTest(unittest.TestCase):
def test_substitute_missing_by(self):
self.assertRaisesRegex(
ParserError, 'Expected "by" or explicit lookup references',
ParserError, 'Expected "by", "from" or explicit lookup references',
self.parse, "feature liga {substitute f f i;} liga;")
def test_subtable(self):