[feaLib] Parse anchors in format A, B, C, D, and E

This commit is contained in:
Sascha Brawer 2015-12-07 22:48:10 +01:00
parent bdc72e1198
commit 6240593839
3 changed files with 156 additions and 0 deletions

View File

@ -18,6 +18,14 @@ class Statement(object):
pass pass
class Expression(object):
def __init__(self, location):
self.location = location
def build(self, builder):
pass
class Block(Statement): class Block(Statement):
def __init__(self, location): def __init__(self, location):
Statement.__init__(self, location) Statement.__init__(self, location)
@ -74,12 +82,27 @@ class AlternateSubstitution(Statement):
self.from_class) self.from_class)
class Anchor(Expression):
def __init__(self, location, x, y, contourpoint,
xDeviceTable, yDeviceTable):
Expression.__init__(self, location)
self.x, self.y, self.contourpoint = x, y, contourpoint
self.xDeviceTable, self.yDeviceTable = xDeviceTable, yDeviceTable
class AnchorDefinition(Statement): class AnchorDefinition(Statement):
def __init__(self, location, name, x, y, contourpoint): def __init__(self, location, name, x, y, contourpoint):
Statement.__init__(self, location) Statement.__init__(self, location)
self.name, self.x, self.y, self.contourpoint = name, x, y, contourpoint self.name, self.x, self.y, self.contourpoint = name, x, y, contourpoint
class CursiveAttachmentPositioning(Statement):
def __init__(self, location, glyphclass, entryAnchor, exitAnchor):
Statement.__init__(self, location)
self.glyphclass = glyphclass
self.entryAnchor, self.exitAnchor = entryAnchor, exitAnchor
class LanguageStatement(Statement): class LanguageStatement(Statement):
def __init__(self, location, language, include_default, required): def __init__(self, location, language, include_default, required):
Statement.__init__(self, location) Statement.__init__(self, location)

View File

@ -46,6 +46,45 @@ class Parser(object):
self.cur_token_location_) self.cur_token_location_)
return self.doc_ return self.doc_
def parse_anchor_(self):
self.expect_symbol_("<")
self.expect_keyword_("anchor")
location = self.cur_token_location_
if self.next_token_ == "NULL":
self.expect_keyword_("NULL")
self.expect_symbol_(">")
return None
if self.next_token_type_ == Lexer.NAME:
name = self.expect_name_()
anchordef = self.anchors_.resolve(name)
if anchordef is None:
raise FeatureLibError(
'Unknown anchor "%s"' % name,
self.cur_token_location_)
self.expect_symbol_(">")
return ast.Anchor(location, anchordef.x, anchordef.y,
anchordef.contourpoint,
xDeviceTable=None, yDeviceTable=None)
x, y = self.expect_number_(), self.expect_number_()
contourpoint = None
if self.next_token_ == "contourpoint":
self.expect_keyword_("contourpoint")
contourpoint = self.expect_number_()
if self.next_token_ == "<":
xDeviceTable = self.parse_device_()
yDeviceTable = self.parse_device_()
else:
xDeviceTable, yDeviceTable = None, None
self.expect_symbol_(">")
return ast.Anchor(location, x, y, contourpoint,
xDeviceTable, yDeviceTable)
def parse_anchordef_(self): def parse_anchordef_(self):
assert self.is_cur_keyword_("anchorDef") assert self.is_cur_keyword_("anchorDef")
location = self.cur_token_location_ location = self.cur_token_location_
@ -212,6 +251,20 @@ class Parser(object):
def parse_position_(self, enumerated, vertical): def parse_position_(self, enumerated, vertical):
assert self.cur_token_ in {"position", "pos"} assert self.cur_token_ in {"position", "pos"}
location = self.cur_token_location_ location = self.cur_token_location_
if self.next_token_ == "cursive":
self.expect_keyword_("cursive")
if enumerated:
raise FeatureLibError(
'"enumerate" is not allowed with '
'cursive attachment positioning',
location)
glyphclass = self.parse_glyphclass_(accept_glyphname=True)
entryAnchor = self.parse_anchor_()
exitAnchor = self.parse_anchor_()
self.expect_symbol_(";")
return ast.CursiveAttachmentPositioning(
location, glyphclass, entryAnchor, exitAnchor)
gc2, value2 = None, None gc2, value2 = None, None
gc1 = self.parse_glyphclass_(accept_glyphname=True) gc1 = self.parse_glyphclass_(accept_glyphname=True)
if self.is_next_glyphclass_(): if self.is_next_glyphclass_():

View File

@ -20,6 +20,76 @@ class ParserTest(unittest.TestCase):
if not hasattr(self, "assertRaisesRegex"): if not hasattr(self, "assertRaisesRegex"):
self.assertRaisesRegex = self.assertRaisesRegexp self.assertRaisesRegex = self.assertRaisesRegexp
def test_anchor_format_a(self):
doc = self.parse(
"feature test {"
" pos cursive A <anchor 120 -20> <anchor NULL>;"
"} test;")
anchor = doc.statements[0].statements[0].entryAnchor
self.assertEqual(type(anchor), ast.Anchor)
self.assertEqual(anchor.x, 120)
self.assertEqual(anchor.y, -20)
self.assertIsNone(anchor.contourpoint)
self.assertIsNone(anchor.xDeviceTable)
self.assertIsNone(anchor.yDeviceTable)
def test_anchor_format_b(self):
doc = self.parse(
"feature test {"
" pos cursive A <anchor 120 -20 contourpoint 5> <anchor NULL>;"
"} test;")
anchor = doc.statements[0].statements[0].entryAnchor
self.assertEqual(type(anchor), ast.Anchor)
self.assertEqual(anchor.x, 120)
self.assertEqual(anchor.y, -20)
self.assertEqual(anchor.contourpoint, 5)
self.assertIsNone(anchor.xDeviceTable)
self.assertIsNone(anchor.yDeviceTable)
def test_anchor_format_c(self):
doc = self.parse(
"feature test {"
" pos cursive A "
" <anchor 120 -20 <device 11 111, 12 112> <device NULL>>"
" <anchor NULL>;"
"} test;")
anchor = doc.statements[0].statements[0].entryAnchor
self.assertEqual(type(anchor), ast.Anchor)
self.assertEqual(anchor.x, 120)
self.assertEqual(anchor.y, -20)
self.assertIsNone(anchor.contourpoint)
self.assertEqual(anchor.xDeviceTable, ((11, 111), (12, 112)))
self.assertIsNone(anchor.yDeviceTable)
def test_anchor_format_d(self):
doc = self.parse(
"feature test {"
" pos cursive A <anchor 120 -20> <anchor NULL>;"
"} test;")
anchor = doc.statements[0].statements[0].exitAnchor
self.assertIsNone(anchor)
def test_anchor_format_e(self):
doc = self.parse(
"feature test {"
" anchorDef 120 -20 contourpoint 7 Foo;"
" pos cursive A <anchor Foo> <anchor NULL>;"
"} test;")
anchor = doc.statements[0].statements[1].entryAnchor
self.assertEqual(type(anchor), ast.Anchor)
self.assertEqual(anchor.x, 120)
self.assertEqual(anchor.y, -20)
self.assertEqual(anchor.contourpoint, 7)
self.assertIsNone(anchor.xDeviceTable)
self.assertIsNone(anchor.yDeviceTable)
def test_anchor_format_e_undefined(self):
self.assertRaisesRegex(
FeatureLibError, 'Unknown anchor "UnknownName"', self.parse,
"feature test {"
" position cursive A <anchor UnknownName> <anchor NULL>;"
"} test;")
def test_anchordef(self): def test_anchordef(self):
[foo] = self.parse("anchorDef 123 456 foo;").statements [foo] = self.parse("anchorDef 123 456 foo;").statements
self.assertEqual(type(foo), ast.AnchorDefinition) self.assertEqual(type(foo), ast.AnchorDefinition)
@ -353,6 +423,16 @@ class ParserTest(unittest.TestCase):
self.assertEqual(pos.glyphclass2, {"a", "b", "c"}) self.assertEqual(pos.glyphclass2, {"a", "b", "c"})
self.assertIsNone(pos.valuerecord2) self.assertIsNone(pos.valuerecord2)
def test_gpos_type_3(self):
doc = self.parse("feature kern {"
" position cursive A <anchor 12 -2> <anchor 2 3>;"
"} kern;")
pos = doc.statements[0].statements[0]
self.assertEqual(type(pos), ast.CursiveAttachmentPositioning)
self.assertEqual(pos.glyphclass, {"A"})
self.assertEqual((pos.entryAnchor.x, pos.entryAnchor.y), (12, -2))
self.assertEqual((pos.exitAnchor.x, pos.exitAnchor.y), (2, 3))
def test_rsub_format_a(self): def test_rsub_format_a(self):
doc = self.parse("feature test {rsub a [b B] c' d [e E] by C;} test;") doc = self.parse("feature test {rsub a [b B] c' d [e E] by C;} test;")
rsub = doc.statements[0].statements[0] rsub = doc.statements[0].statements[0]