[feaLib.parser] make Parser accept glyphNames iterable...
... instead of a glyphMap dict. The parser does not actually need a reverse glyph order mapping as it is not interested in knowing the glyphID from the glyph name, but only whether a glyph is in the font or not. This makes it easier for client code (e.g. ufo2ft feature compiler) to use the feaLib Parser, without having to first construct and pass it a glyphMap argument.
This commit is contained in:
parent
a35707560c
commit
e8535f2280
@ -17,8 +17,8 @@ class Parser(object):
|
||||
extensions = {}
|
||||
ast = ast
|
||||
|
||||
def __init__(self, featurefile, glyphMap):
|
||||
self.glyphMap_ = glyphMap
|
||||
def __init__(self, featurefile, glyphNames):
|
||||
self.glyphNames_ = set(glyphNames)
|
||||
self.doc_ = self.ast.FeatureFile()
|
||||
self.anchors_ = SymbolTable()
|
||||
self.glyphclasses_ = SymbolTable()
|
||||
@ -220,7 +220,7 @@ class Parser(object):
|
||||
solutions = []
|
||||
for i in range(len(parts)):
|
||||
start, limit = "-".join(parts[0:i]), "-".join(parts[i:])
|
||||
if start in self.glyphMap_ and limit in self.glyphMap_:
|
||||
if start in self.glyphNames_ and limit in self.glyphNames_:
|
||||
solutions.append((start, limit))
|
||||
if len(solutions) == 1:
|
||||
start, limit = solutions[0]
|
||||
@ -260,7 +260,7 @@ class Parser(object):
|
||||
if self.next_token_type_ is Lexer.NAME:
|
||||
glyph = self.expect_glyph_()
|
||||
location = self.cur_token_location_
|
||||
if '-' in glyph and glyph not in self.glyphMap_:
|
||||
if '-' in glyph and glyph not in self.glyphNames_:
|
||||
start, limit = self.split_glyph_range_(glyph, location)
|
||||
glyphs.add_range(
|
||||
start, limit,
|
||||
|
@ -138,7 +138,7 @@ class BuilderTest(unittest.TestCase):
|
||||
def check_fea2fea_file(self, name, base=None, parser=Parser):
|
||||
font = makeTTFont()
|
||||
fname = (name + ".fea") if '.' not in name else name
|
||||
p = parser(self.getpath(fname), glyphMap=font.getReverseGlyphMap())
|
||||
p = parser(self.getpath(fname), glyphNames=font.getGlyphOrder())
|
||||
doc = p.parse()
|
||||
actual = self.normal_fea(doc.asFea().split("\n"))
|
||||
with open(self.getpath(base or fname), "r", encoding="utf-8") as ofile:
|
||||
|
@ -30,11 +30,7 @@ def mapping(s):
|
||||
return dict(zip(b, c))
|
||||
|
||||
|
||||
def makeGlyphMap(glyphs):
|
||||
return {g: i for i, g in enumerate(glyphs)}
|
||||
|
||||
|
||||
GLYPHMAP = makeGlyphMap(("""
|
||||
GLYPHNAMES = ("""
|
||||
.notdef space A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
|
||||
A.sc B.sc C.sc D.sc E.sc F.sc G.sc H.sc I.sc J.sc K.sc L.sc M.sc
|
||||
N.sc O.sc P.sc Q.sc R.sc S.sc T.sc U.sc V.sc W.sc X.sc Y.sc Z.sc
|
||||
@ -44,7 +40,7 @@ GLYPHMAP = makeGlyphMap(("""
|
||||
n.sc o.sc p.sc q.sc r.sc s.sc t.sc u.sc v.sc w.sc x.sc y.sc z.sc
|
||||
a.swash b.swash x.swash y.swash z.swash
|
||||
foobar foo.09 foo.1234 foo.9876
|
||||
""").split() + ["foo.%d" % i for i in range(1, 200)])
|
||||
""").split() + ["foo.%d" % i for i in range(1, 200)]
|
||||
|
||||
|
||||
class ParserTest(unittest.TestCase):
|
||||
@ -297,31 +293,31 @@ class ParserTest(unittest.TestCase):
|
||||
self.assertEqual(gc.glyphSet(), ("d.sc", "e.sc", "f.sc", "g.sc"))
|
||||
|
||||
def test_glyphclass_range_dash(self):
|
||||
glyphMap = makeGlyphMap("A-foo.sc B-foo.sc C-foo.sc".split())
|
||||
[gc] = self.parse("@range = [A-foo.sc-C-foo.sc];", glyphMap).statements
|
||||
glyphNames = "A-foo.sc B-foo.sc C-foo.sc".split()
|
||||
[gc] = self.parse("@range = [A-foo.sc-C-foo.sc];", glyphNames).statements
|
||||
self.assertEqual(gc.glyphSet(), ("A-foo.sc", "B-foo.sc", "C-foo.sc"))
|
||||
|
||||
def test_glyphclass_range_dash_with_space(self):
|
||||
g = makeGlyphMap("A-foo.sc B-foo.sc C-foo.sc".split())
|
||||
[gc] = self.parse("@range = [A-foo.sc - C-foo.sc];", g).statements
|
||||
gn = "A-foo.sc B-foo.sc C-foo.sc".split()
|
||||
[gc] = self.parse("@range = [A-foo.sc - C-foo.sc];", gn).statements
|
||||
self.assertEqual(gc.glyphSet(), ("A-foo.sc", "B-foo.sc", "C-foo.sc"))
|
||||
|
||||
def test_glyphclass_glyph_name_should_win_over_range(self):
|
||||
# The OpenType Feature File Specification v1.20 makes it clear
|
||||
# that if a dashed name could be interpreted either as a glyph name
|
||||
# or as a range, then the semantics should be the single dashed name.
|
||||
glyphMap = makeGlyphMap(
|
||||
glyphNames = (
|
||||
"A-foo.sc-C-foo.sc A-foo.sc B-foo.sc C-foo.sc".split())
|
||||
[gc] = self.parse("@range = [A-foo.sc-C-foo.sc];", glyphMap).statements
|
||||
[gc] = self.parse("@range = [A-foo.sc-C-foo.sc];", glyphNames).statements
|
||||
self.assertEqual(gc.glyphSet(), ("A-foo.sc-C-foo.sc",))
|
||||
|
||||
def test_glyphclass_range_dash_ambiguous(self):
|
||||
glyphMap = makeGlyphMap("A B C A-B B-C".split())
|
||||
glyphNames = "A B C A-B B-C".split()
|
||||
self.assertRaisesRegex(
|
||||
FeatureLibError,
|
||||
'Ambiguous glyph range "A-B-C"; '
|
||||
'please use "A - B-C" or "A-B - C" to clarify what you mean',
|
||||
self.parse, r"@bad = [A-B-C];", glyphMap)
|
||||
self.parse, r"@bad = [A-B-C];", glyphNames)
|
||||
|
||||
def test_glyphclass_range_digit1(self):
|
||||
[gc] = self.parse("@range = [foo.2-foo.5];").statements
|
||||
@ -1268,7 +1264,7 @@ class ParserTest(unittest.TestCase):
|
||||
self.assertEqual(glyphstr(sub.suffix), "Z")
|
||||
|
||||
def test_substitute_lookups(self): # GSUB LookupType 6
|
||||
doc = Parser(self.getpath("spec5fi1.fea"), GLYPHMAP).parse()
|
||||
doc = Parser(self.getpath("spec5fi1.fea"), GLYPHNAMES).parse()
|
||||
[_, _, _, langsys, ligs, sub, feature] = doc.statements
|
||||
self.assertEqual(feature.statements[0].lookups, [ligs, None, sub])
|
||||
self.assertEqual(feature.statements[1].lookups, [ligs, None, sub])
|
||||
@ -1483,9 +1479,9 @@ class ParserTest(unittest.TestCase):
|
||||
doc = self.parse("table %s { ;;; } %s;" % (table, table))
|
||||
self.assertEqual(doc.statements[0].statements, [])
|
||||
|
||||
def parse(self, text, glyphMap=GLYPHMAP):
|
||||
def parse(self, text, glyphNames=GLYPHNAMES):
|
||||
featurefile = UnicodeIO(text)
|
||||
p = Parser(featurefile, glyphMap)
|
||||
p = Parser(featurefile, glyphNames)
|
||||
return p.parse()
|
||||
|
||||
@staticmethod
|
||||
|
Loading…
x
Reference in New Issue
Block a user