[feaLib] Parse anchors in format A, B, C, D, and E
This commit is contained in:
parent
bdc72e1198
commit
6240593839
@ -18,6 +18,14 @@ class Statement(object):
|
||||
pass
|
||||
|
||||
|
||||
class Expression(object):
|
||||
def __init__(self, location):
|
||||
self.location = location
|
||||
|
||||
def build(self, builder):
|
||||
pass
|
||||
|
||||
|
||||
class Block(Statement):
|
||||
def __init__(self, location):
|
||||
Statement.__init__(self, location)
|
||||
@ -74,12 +82,27 @@ class AlternateSubstitution(Statement):
|
||||
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):
|
||||
def __init__(self, location, name, x, y, contourpoint):
|
||||
Statement.__init__(self, location)
|
||||
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):
|
||||
def __init__(self, location, language, include_default, required):
|
||||
Statement.__init__(self, location)
|
||||
|
@ -46,6 +46,45 @@ class Parser(object):
|
||||
self.cur_token_location_)
|
||||
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):
|
||||
assert self.is_cur_keyword_("anchorDef")
|
||||
location = self.cur_token_location_
|
||||
@ -212,6 +251,20 @@ class Parser(object):
|
||||
def parse_position_(self, enumerated, vertical):
|
||||
assert self.cur_token_ in {"position", "pos"}
|
||||
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
|
||||
gc1 = self.parse_glyphclass_(accept_glyphname=True)
|
||||
if self.is_next_glyphclass_():
|
||||
|
@ -20,6 +20,76 @@ class ParserTest(unittest.TestCase):
|
||||
if not hasattr(self, "assertRaisesRegex"):
|
||||
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):
|
||||
[foo] = self.parse("anchorDef 123 456 foo;").statements
|
||||
self.assertEqual(type(foo), ast.AnchorDefinition)
|
||||
@ -353,6 +423,16 @@ class ParserTest(unittest.TestCase):
|
||||
self.assertEqual(pos.glyphclass2, {"a", "b", "c"})
|
||||
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):
|
||||
doc = self.parse("feature test {rsub a [b B] c' d [e E] by C;} test;")
|
||||
rsub = doc.statements[0].statements[0]
|
||||
|
Loading…
x
Reference in New Issue
Block a user