[feaLib] Support CID glyph names
Resolves https://github.com/behdad/fonttools/issues/459
This commit is contained in:
parent
f2806c4de2
commit
bfc3bff0e3
@ -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(
|
||||
|
@ -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];"
|
||||
|
Loading…
x
Reference in New Issue
Block a user