[feaLib] Support CID glyph names

Resolves https://github.com/behdad/fonttools/issues/459
This commit is contained in:
Sascha Brawer 2016-01-12 10:58:00 +01:00
parent f2806c4de2
commit bfc3bff0e3
2 changed files with 82 additions and 10 deletions

View File

@ -172,8 +172,10 @@ class Parser(object):
return glyphclass
def parse_glyphclass_(self, accept_glyphname):
if accept_glyphname and self.next_token_type_ is Lexer.NAME:
return ast.GlyphName(self.cur_token_location_, self.expect_name_())
if (accept_glyphname and
self.next_token_type_ in (Lexer.NAME, Lexer.CID)):
glyph = self.expect_glyph_()
return ast.GlyphName(self.cur_token_location_, glyph)
if self.next_token_type_ is Lexer.GLYPHCLASS:
self.advance_lexer_()
gc = self.glyphclasses_.resolve(self.cur_token_)
@ -193,15 +195,25 @@ class Parser(object):
self.advance_lexer_()
if self.cur_token_type_ is Lexer.NAME:
if self.next_token_ == "-":
range_location_ = self.cur_token_location_
range_location = self.cur_token_location_
range_start = self.cur_token_
self.expect_symbol_("-")
range_end = self.expect_name_()
glyphs.update(self.make_glyph_range_(range_location_,
glyphs.update(self.make_glyph_range_(range_location,
range_start,
range_end))
else:
glyphs.add(self.cur_token_)
elif self.cur_token_type_ is Lexer.CID:
if self.next_token_ == "-":
range_location = self.cur_token_location_
range_start = self.cur_token_
self.expect_symbol_("-")
range_end = self.expect_cid_()
glyphs.update(self.make_cid_range_(range_location,
range_start, range_end))
else:
glyphs.add("cid%05d" % self.cur_token_)
elif self.cur_token_type_ is Lexer.GLYPHCLASS:
gc = self.glyphclasses_.resolve(self.cur_token_)
if gc is None:
@ -389,10 +401,6 @@ class Parser(object):
markClass.addDefinition(mcdef)
return mcdef
def is_next_glyphclass_(self):
return (self.next_token_ == "[" or
self.next_token_type_ in (Lexer.GLYPHCLASS, Lexer.NAME))
def parse_position_(self, enumerated, vertical):
assert self.cur_token_ in {"position", "pos"}
if self.next_token_ == "cursive": # GPOS type 3
@ -859,6 +867,21 @@ class Parser(object):
raise FeatureLibError("Expected @NAME", self.cur_token_location_)
return self.cur_token_
def expect_cid_(self):
self.advance_lexer_()
if self.cur_token_type_ is Lexer.CID:
return self.cur_token_
raise FeatureLibError("Expected a CID", self.cur_token_location_)
def expect_glyph_(self):
self.advance_lexer_()
if self.cur_token_type_ is Lexer.NAME:
return self.cur_token_
elif self.cur_token_type_ is Lexer.CID:
return "cid%05d" % self.cur_token_
raise FeatureLibError("Expected a glyph name or CID",
self.cur_token_location_)
def expect_markClass_reference_(self):
name = self.expect_class_name_()
mc = self.glyphclasses_.resolve(name)
@ -942,8 +965,18 @@ class Parser(object):
"""'abc' --> 'cba'"""
return ''.join(reversed(list(s)))
def make_cid_range_(self, location, start, limit):
"""(location, 999, 1001) --> {"cid00999", "cid01000", "cid01001"}"""
result = set()
if start > limit:
raise FeatureLibError(
"Bad range: start should be less than limit", location)
for cid in range(start, limit + 1):
result.add("cid%05d" % cid)
return result
def make_glyph_range_(self, location, start, limit):
"""("a.sc", "d.sc") --> {"a.sc", "b.sc", "c.sc", "d.sc"}"""
"""(location, "a.sc", "d.sc") --> {"a.sc", "b.sc", "c.sc", "d.sc"}"""
result = set()
if len(start) != len(limit):
raise FeatureLibError(

View File

@ -184,6 +184,17 @@ class ParserTest(unittest.TestCase):
self.assertEqual(doc.statements[-1].glyphSet(),
{"acute", "cedilla", "grave", "ogonek"})
def test_glyphclass_range_cid(self):
[gc] = self.parse(r"@GlyphClass = [\999-\1001];").statements
self.assertEqual(gc.name, "GlyphClass")
self.assertEqual(gc.glyphs, {"cid00999", "cid01000", "cid01001"})
def test_glyphclass_range_cid_bad(self):
self.assertRaisesRegex(
FeatureLibError,
"Bad range: start should be less than limit",
self.parse, r"@bad = [\998-\995];")
def test_glyphclass_range_uppercase(self):
[gc] = self.parse("@swashes = [X.swash-Z.swash];").statements
self.assertEqual(gc.name, "swashes")
@ -724,6 +735,15 @@ class ParserTest(unittest.TestCase):
self.assertEqual(rsub.mapping, {"c": "C"})
self.assertEqual(glyphstr(rsub.old_suffix), "d [E e]")
def test_rsub_format_a_cid(self):
doc = self.parse(r"feature test {rsub \1 [\2 \3] \4' \5 by \6;} test;")
rsub = doc.statements[0].statements[0]
self.assertEqual(type(rsub), ast.ReverseChainSingleSubstStatement)
self.assertEqual(glyphstr(rsub.old_prefix),
"cid00001 [cid00002 cid00003]")
self.assertEqual(rsub.mapping, {"cid00004": "cid00006"})
self.assertEqual(glyphstr(rsub.old_suffix), "cid00005")
def test_rsub_format_b(self):
doc = self.parse(
"feature smcp {"
@ -789,7 +809,7 @@ class ParserTest(unittest.TestCase):
def test_sub_single_format_a(self): # GSUB LookupType 1
doc = self.parse("feature smcp {substitute a by a.sc;} smcp;")
sub = doc.statements[0].statements[0]
self.assertEqual(type(sub), ast.SingleSubstStatement)
self.assertIsInstance(sub, ast.SingleSubstStatement)
self.assertEqual(glyphstr(sub.prefix), "")
self.assertEqual(sub.mapping, {"a": "a.sc"})
self.assertEqual(glyphstr(sub.suffix), "")
@ -802,6 +822,14 @@ class ParserTest(unittest.TestCase):
self.assertEqual(glyphstr(sub.prefix), "[A a]")
self.assertEqual(glyphstr(sub.suffix), "C")
def test_sub_single_format_a_cid(self): # GSUB LookupType 1
doc = self.parse(r"feature smcp {substitute \12345 by \78987;} smcp;")
sub = doc.statements[0].statements[0]
self.assertIsInstance(sub, ast.SingleSubstStatement)
self.assertEqual(glyphstr(sub.prefix), "")
self.assertEqual(sub.mapping, {"cid12345": "cid78987"})
self.assertEqual(glyphstr(sub.suffix), "")
def test_sub_single_format_b(self): # GSUB LookupType 1
doc = self.parse(
"feature smcp {"
@ -905,6 +933,17 @@ class ParserTest(unittest.TestCase):
self.assertEqual(glyphstr(sub.suffix), "[Y y] Z")
self.assertEqual(glyphstr([sub.replacement]), "[a.1 a.2 a.3]")
def test_substitute_from_cid(self): # GSUB LookupType 3
doc = self.parse(r"feature test {"
r" substitute \7 from [\111 \222];"
r"} test;")
sub = doc.statements[0].statements[0]
self.assertIsInstance(sub, ast.AlternateSubstStatement)
self.assertEqual(glyphstr(sub.prefix), "")
self.assertEqual(glyphstr([sub.glyph]), "cid00007")
self.assertEqual(glyphstr(sub.suffix), "")
self.assertEqual(glyphstr([sub.replacement]), "[cid00111 cid00222]")
def test_substitute_from_glyphclass(self): # GSUB LookupType 3
doc = self.parse("feature test {"
" @Ampersands = [ampersand.1 ampersand.2];"