Merge pull request #1104 from anthrotype/fealib-parser-glyphmap
[feaLib.parser] make Parser accept glyphNames iterable...
This commit is contained in:
commit
0ff6be5ff8
@ -17,8 +17,20 @@ class Parser(object):
|
||||
extensions = {}
|
||||
ast = ast
|
||||
|
||||
def __init__(self, featurefile, glyphMap):
|
||||
self.glyphMap_ = glyphMap
|
||||
def __init__(self, featurefile, glyphNames=(), **kwargs):
|
||||
if "glyphMap" in kwargs:
|
||||
from fontTools.misc.loggingTools import deprecateArgument
|
||||
deprecateArgument("glyphMap", "use 'glyphNames' (iterable) instead")
|
||||
if glyphNames:
|
||||
raise TypeError("'glyphNames' and (deprecated) 'glyphMap' are "
|
||||
"mutually exclusive")
|
||||
glyphNames = kwargs.pop("glyphMap")
|
||||
if kwargs:
|
||||
raise TypeError("unsupported keyword argument%s: %s"
|
||||
% ("" if len(kwargs) == 1 else "s",
|
||||
", ".join(repr(k) for k in kwargs)))
|
||||
|
||||
self.glyphNames_ = set(glyphNames)
|
||||
self.doc_ = self.ast.FeatureFile()
|
||||
self.anchors_ = SymbolTable()
|
||||
self.glyphclasses_ = SymbolTable()
|
||||
@ -220,7 +232,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 +272,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:
|
||||
|
@ -4,6 +4,7 @@ from __future__ import unicode_literals
|
||||
from fontTools.feaLib.error import FeatureLibError
|
||||
from fontTools.feaLib.parser import Parser, SymbolTable
|
||||
from fontTools.misc.py23 import *
|
||||
import warnings
|
||||
import fontTools.feaLib.ast as ast
|
||||
import os
|
||||
import unittest
|
||||
@ -30,11 +31,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 +41,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):
|
||||
@ -55,6 +52,25 @@ class ParserTest(unittest.TestCase):
|
||||
if not hasattr(self, "assertRaisesRegex"):
|
||||
self.assertRaisesRegex = self.assertRaisesRegexp
|
||||
|
||||
def test_glyphMap_deprecated(self):
|
||||
glyphMap = {'a': 0, 'b': 1, 'c': 2}
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("always")
|
||||
parser = Parser(UnicodeIO(), glyphMap=glyphMap)
|
||||
|
||||
self.assertEqual(len(w), 1)
|
||||
self.assertEqual(w[-1].category, UserWarning)
|
||||
self.assertIn("deprecated", str(w[-1].message))
|
||||
self.assertEqual(parser.glyphNames_, {'a', 'b', 'c'})
|
||||
|
||||
self.assertRaisesRegex(
|
||||
TypeError, "mutually exclusive",
|
||||
Parser, UnicodeIO(), ("a",), glyphMap={"a": 0})
|
||||
|
||||
self.assertRaisesRegex(
|
||||
TypeError, "unsupported keyword argument",
|
||||
Parser, UnicodeIO(), foo="bar")
|
||||
|
||||
def test_comments(self):
|
||||
doc = self.parse(
|
||||
""" # Initial
|
||||
@ -297,31 +313,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 +1284,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 +1499,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