[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
|
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)
|
||||||
|
@ -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_():
|
||||||
|
@ -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]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user