[voltLib] Support writing back ast as VOLT data
Adds __str__() method to ast classes that writes back VOLT data. Tries to replicate VOLT syntax idiosyncrasies as much as possible for better round-trip conversion.
This commit is contained in:
parent
3c4f5a75bf
commit
a1df9175ee
@ -10,6 +10,18 @@ class Pos(NamedTuple):
|
||||
dx_adjust_by: dict
|
||||
dy_adjust_by: dict
|
||||
|
||||
def __str__(self):
|
||||
res = ' POS'
|
||||
for attr in ('adv', 'dx', 'dy'):
|
||||
value = getattr(self, attr)
|
||||
if value is not None:
|
||||
res += f' {attr.upper()} {value}'
|
||||
adjust_by = getattr(self, f'{attr}_adjust_by', {})
|
||||
for size, adjustment in adjust_by.items():
|
||||
res += f' ADJUST_BY {adjustment} AT {size}'
|
||||
res += ' END_POS'
|
||||
return res
|
||||
|
||||
|
||||
class Element(object):
|
||||
def __init__(self, location=None):
|
||||
@ -18,6 +30,9 @@ class Element(object):
|
||||
def build(self, builder):
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Statement(Element):
|
||||
pass
|
||||
@ -36,6 +51,9 @@ class VoltFile(Statement):
|
||||
for s in self.statements:
|
||||
s.build(builder)
|
||||
|
||||
def __str__(self):
|
||||
return '\n' + '\n'.join(str(s) for s in self.statements) + ' END\n'
|
||||
|
||||
|
||||
class GlyphDefinition(Statement):
|
||||
def __init__(self, name, gid, gunicode, gtype, components, location=None):
|
||||
@ -46,6 +64,21 @@ class GlyphDefinition(Statement):
|
||||
self.type = gtype
|
||||
self.components = components
|
||||
|
||||
def __str__(self):
|
||||
res = f'DEF_GLYPH "{self.name}" ID {self.id}'
|
||||
if self.unicode is not None:
|
||||
if len(self.unicode) > 1:
|
||||
unicodes = ','.join(f'U+{u:04X}' for u in self.unicode)
|
||||
res += f' UNICODEVALUES "{unicodes}"'
|
||||
else:
|
||||
res += f' UNICODE {self.unicode[0]}'
|
||||
if self.type is not None:
|
||||
res += f' TYPE {self.type}'
|
||||
if self.components is not None:
|
||||
res += f' COMPONENTS {self.components}'
|
||||
res += ' END_GLYPH'
|
||||
return res
|
||||
|
||||
|
||||
class GroupDefinition(Statement):
|
||||
def __init__(self, name, enum, location=None):
|
||||
@ -67,6 +100,10 @@ class GroupDefinition(Statement):
|
||||
self.glyphs_ = self.enum.glyphSet(groups)
|
||||
return self.glyphs_
|
||||
|
||||
def __str__(self):
|
||||
enum = self.enum and str(self.enum) or ''
|
||||
return f'DEF_GROUP "{self.name}"\n{enum}\nEND_GROUP'
|
||||
|
||||
|
||||
class GlyphName(Expression):
|
||||
"""A single glyph name, such as cedilla."""
|
||||
@ -77,6 +114,9 @@ class GlyphName(Expression):
|
||||
def glyphSet(self):
|
||||
return (self.glyph,)
|
||||
|
||||
def __str__(self):
|
||||
return f' GLYPH "{self.glyph}"'
|
||||
|
||||
|
||||
class Enum(Expression):
|
||||
"""An enum"""
|
||||
@ -97,6 +137,10 @@ class Enum(Expression):
|
||||
glyphs.extend(element.glyphSet())
|
||||
return tuple(glyphs)
|
||||
|
||||
def __str__(self):
|
||||
enum = ''.join(str(e) for e in self.enum)
|
||||
return f' ENUM{enum} END_ENUM'
|
||||
|
||||
|
||||
class GroupName(Expression):
|
||||
"""A glyph group"""
|
||||
@ -115,6 +159,9 @@ class GroupName(Expression):
|
||||
'Group "%s" is used but undefined.' % (self.group),
|
||||
self.location)
|
||||
|
||||
def __str__(self):
|
||||
return f' GROUP "{self.group}"'
|
||||
|
||||
|
||||
class Range(Expression):
|
||||
"""A glyph range"""
|
||||
@ -127,6 +174,9 @@ class Range(Expression):
|
||||
def glyphSet(self):
|
||||
return tuple(self.parser.glyph_range(self.start, self.end))
|
||||
|
||||
def __str__(self):
|
||||
return f' RANGE "{self.start}" TO "{self.end}"'
|
||||
|
||||
|
||||
class ScriptDefinition(Statement):
|
||||
def __init__(self, name, tag, langs, location=None):
|
||||
@ -135,6 +185,16 @@ class ScriptDefinition(Statement):
|
||||
self.tag = tag
|
||||
self.langs = langs
|
||||
|
||||
def __str__(self):
|
||||
res = 'DEF_SCRIPT'
|
||||
if self.name is not None:
|
||||
res += f' NAME "{self.name}"'
|
||||
res += f' TAG "{self.tag}"\n\n'
|
||||
for lang in self.langs:
|
||||
res += f'{lang}'
|
||||
res += 'END_SCRIPT'
|
||||
return res
|
||||
|
||||
|
||||
class LangSysDefinition(Statement):
|
||||
def __init__(self, name, tag, features, location=None):
|
||||
@ -143,6 +203,16 @@ class LangSysDefinition(Statement):
|
||||
self.tag = tag
|
||||
self.features = features
|
||||
|
||||
def __str__(self):
|
||||
res = 'DEF_LANGSYS'
|
||||
if self.name is not None:
|
||||
res += f' NAME "{self.name}"'
|
||||
res += f' TAG "{self.tag}"\n\n'
|
||||
for feature in self.features:
|
||||
res += f'{feature}'
|
||||
res += 'END_LANGSYS\n'
|
||||
return res
|
||||
|
||||
|
||||
class FeatureDefinition(Statement):
|
||||
def __init__(self, name, tag, lookups, location=None):
|
||||
@ -151,6 +221,12 @@ class FeatureDefinition(Statement):
|
||||
self.tag = tag
|
||||
self.lookups = lookups
|
||||
|
||||
def __str__(self):
|
||||
res = f'DEF_FEATURE NAME "{self.name}" TAG "{self.tag}"\n'
|
||||
res += ' ' + ' '.join(f'LOOKUP "{l}"' for l in self.lookups) + '\n'
|
||||
res += 'END_FEATURE\n'
|
||||
return res
|
||||
|
||||
|
||||
class LookupDefinition(Statement):
|
||||
def __init__(self, name, process_base, process_marks, mark_glyph_set,
|
||||
@ -168,12 +244,51 @@ class LookupDefinition(Statement):
|
||||
self.sub = sub
|
||||
self.pos = pos
|
||||
|
||||
def __str__(self):
|
||||
res = f'DEF_LOOKUP "{self.name}"'
|
||||
res += f' {self.process_base and "PROCESS_BASE" or "SKIP_BASE"}'
|
||||
if self.process_marks:
|
||||
res += ' PROCESS_MARKS '
|
||||
if self.mark_glyph_set:
|
||||
res += f'MARK_GLYPH_SET "{self.mark_glyph_set}"'
|
||||
elif isinstance(self.process_marks, str):
|
||||
res += f'"{self.process_marks}"'
|
||||
else:
|
||||
res += 'ALL'
|
||||
else:
|
||||
res += ' SKIP_MARKS'
|
||||
if self.direction is not None:
|
||||
res += f' DIRECTION {self.direction}'
|
||||
if self.reversal is not None:
|
||||
res += ' REVERSAL'
|
||||
if self.comments is not None:
|
||||
comments = self.comments.replace('\n', r'\n')
|
||||
res += f'\nCOMMENTS "{comments}"'
|
||||
if self.context:
|
||||
res += '\n' + '\n'.join(str(c) for c in self.context)
|
||||
else:
|
||||
res += '\nIN_CONTEXT\nEND_CONTEXT'
|
||||
if self.sub:
|
||||
res += f'\n{self.sub}'
|
||||
if self.pos:
|
||||
res += f'\n{self.pos}'
|
||||
return res
|
||||
|
||||
|
||||
class SubstitutionDefinition(Statement):
|
||||
def __init__(self, mapping, location=None):
|
||||
Statement.__init__(self, location)
|
||||
self.mapping = mapping
|
||||
|
||||
def __str__(self):
|
||||
res = 'AS_SUBSTITUTION\n'
|
||||
for src, dst in self.mapping.items():
|
||||
src = ''.join(str(s) for s in src)
|
||||
dst = ''.join(str(d) for d in dst)
|
||||
res += f'SUB{src}\nWITH{dst}\nEND_SUB\n'
|
||||
res += 'END_SUBSTITUTION'
|
||||
return res
|
||||
|
||||
|
||||
class SubstitutionSingleDefinition(SubstitutionDefinition):
|
||||
pass
|
||||
@ -197,6 +312,15 @@ class PositionAttachDefinition(Statement):
|
||||
self.coverage = coverage
|
||||
self.coverage_to = coverage_to
|
||||
|
||||
def __str__(self):
|
||||
coverage = ''.join(str(c) for c in self.coverage)
|
||||
res = f'AS_POSITION\nATTACH{coverage}\nTO'
|
||||
for coverage, anchor in self.coverage_to:
|
||||
coverage = ''.join(str(c) for c in coverage)
|
||||
res += f'{coverage} AT ANCHOR "{anchor}"'
|
||||
res += '\nEND_ATTACH\nEND_POSITION'
|
||||
return res
|
||||
|
||||
|
||||
class PositionAttachCursiveDefinition(Statement):
|
||||
def __init__(self, coverages_exit, coverages_enter, location=None):
|
||||
@ -204,6 +328,17 @@ class PositionAttachCursiveDefinition(Statement):
|
||||
self.coverages_exit = coverages_exit
|
||||
self.coverages_enter = coverages_enter
|
||||
|
||||
def __str__(self):
|
||||
res = 'AS_POSITION\nATTACH_CURSIVE'
|
||||
for coverage in self.coverages_exit:
|
||||
coverage = ''.join(str(c) for c in coverage)
|
||||
res += f'\nEXIT {coverage}'
|
||||
for coverage in self.coverages_enter:
|
||||
coverage = ''.join(str(c) for c in coverage)
|
||||
res += f'\nENTER {coverage}'
|
||||
res += '\nEND_ATTACH\nEND_POSITION'
|
||||
return res
|
||||
|
||||
|
||||
class PositionAdjustPairDefinition(Statement):
|
||||
def __init__(self, coverages_1, coverages_2, adjust_pair, location=None):
|
||||
@ -212,12 +347,36 @@ class PositionAdjustPairDefinition(Statement):
|
||||
self.coverages_2 = coverages_2
|
||||
self.adjust_pair = adjust_pair
|
||||
|
||||
def __str__(self):
|
||||
res = 'AS_POSITION\nADJUST_PAIR\n'
|
||||
for coverage in self.coverages_1:
|
||||
coverage = ' '.join(str(c) for c in coverage)
|
||||
res += f' FIRST {coverage}'
|
||||
res += '\n'
|
||||
for coverage in self.coverages_2:
|
||||
coverage = ' '.join(str(c) for c in coverage)
|
||||
res += f' SECOND {coverage}'
|
||||
res += '\n'
|
||||
for (id_1, id_2), (pos_1, pos_2) in self.adjust_pair.items():
|
||||
res += f' {id_1} {id_2} BY{pos_1}{pos_2}\n'
|
||||
res += '\nEND_ADJUST\nEND_POSITION'
|
||||
return res
|
||||
|
||||
|
||||
class PositionAdjustSingleDefinition(Statement):
|
||||
def __init__(self, adjust_single, location=None):
|
||||
Statement.__init__(self, location)
|
||||
self.adjust_single = adjust_single
|
||||
|
||||
def __str__(self):
|
||||
res = 'AS_POSITION\nADJUST_SINGLE'
|
||||
for coverage, pos in self.adjust_single:
|
||||
coverage = ''.join(str(c) for c in coverage)
|
||||
res += f'{coverage} BY{pos}'
|
||||
res += '\nEND_ADJUST\nEND_POSITION'
|
||||
return res
|
||||
|
||||
|
||||
|
||||
class ContextDefinition(Statement):
|
||||
def __init__(self, ex_or_in, left=None, right=None, location=None):
|
||||
@ -226,6 +385,17 @@ class ContextDefinition(Statement):
|
||||
self.left = left if left is not None else []
|
||||
self.right = right if right is not None else []
|
||||
|
||||
def __str__(self):
|
||||
res = self.ex_or_in + '\n'
|
||||
for coverage in self.left:
|
||||
coverage = ''.join(str(c) for c in coverage)
|
||||
res += f' LEFT{coverage}\n'
|
||||
for coverage in self.right:
|
||||
coverage = ''.join(str(c) for c in coverage)
|
||||
res += f' RIGHT{coverage}\n'
|
||||
res += 'END_CONTEXT'
|
||||
return res
|
||||
|
||||
|
||||
class AnchorDefinition(Statement):
|
||||
def __init__(self, name, gid, glyph_name, component, locked,
|
||||
@ -238,9 +408,26 @@ class AnchorDefinition(Statement):
|
||||
self.locked = locked
|
||||
self.pos = pos
|
||||
|
||||
def __str__(self):
|
||||
locked = self.locked and ' LOCKED' or ''
|
||||
return (f'DEF_ANCHOR "{self.name}"'
|
||||
f' ON {self.gid}'
|
||||
f' GLYPH {self.glyph_name}'
|
||||
f' COMPONENT {self.component}'
|
||||
f'{locked}'
|
||||
f' AT {self.pos} END_ANCHOR')
|
||||
|
||||
|
||||
class SettingDefinition(Statement):
|
||||
def __init__(self, name, value, location=None):
|
||||
Statement.__init__(self, location)
|
||||
self.name = name
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
if self.value is True:
|
||||
return f'{self.name}'
|
||||
if isinstance(self.value, (tuple, list)):
|
||||
value = " ".join(str(v) for v in self.value)
|
||||
return f'{self.name} {value}'
|
||||
return f'{self.name} {self.value}'
|
||||
|
@ -37,7 +37,7 @@ class ParserTest(unittest.TestCase):
|
||||
("space", 3, [0x0020], "BASE", None))
|
||||
|
||||
def test_def_glyph_base_with_unicodevalues(self):
|
||||
[def_glyph] = self.parse(
|
||||
[def_glyph] = self.parse_(
|
||||
'DEF_GLYPH "CR" ID 2 UNICODEVALUES "U+0009" '
|
||||
'TYPE BASE END_GLYPH'
|
||||
).statements
|
||||
@ -55,7 +55,7 @@ class ParserTest(unittest.TestCase):
|
||||
("CR", 2, [0x0009, 0x000D], "BASE", None))
|
||||
|
||||
def test_def_glyph_base_with_empty_unicodevalues(self):
|
||||
[def_glyph] = self.parse(
|
||||
[def_glyph] = self.parse_(
|
||||
'DEF_GLYPH "i.locl" ID 269 UNICODEVALUES "" '
|
||||
'TYPE BASE END_GLYPH'
|
||||
).statements
|
||||
@ -106,7 +106,7 @@ class ParserTest(unittest.TestCase):
|
||||
def test_def_glyph_case_sensitive(self):
|
||||
def_glyphs = self.parse(
|
||||
'DEF_GLYPH "A" ID 3 UNICODE 65 TYPE BASE END_GLYPH\n'
|
||||
'DEF_GLYPH "a" ID 4 UNICODE 97 TYPE BASE END_GLYPH\n'
|
||||
'DEF_GLYPH "a" ID 4 UNICODE 97 TYPE BASE END_GLYPH'
|
||||
).statements
|
||||
self.assertEqual((def_glyphs[0].name, def_glyphs[0].id,
|
||||
def_glyphs[0].unicode, def_glyphs[0].type,
|
||||
@ -120,10 +120,10 @@ class ParserTest(unittest.TestCase):
|
||||
def test_def_group_glyphs(self):
|
||||
[def_group] = self.parse(
|
||||
'DEF_GROUP "aaccented"\n'
|
||||
'ENUM GLYPH "aacute" GLYPH "abreve" GLYPH "acircumflex" '
|
||||
' ENUM GLYPH "aacute" GLYPH "abreve" GLYPH "acircumflex" '
|
||||
'GLYPH "adieresis" GLYPH "ae" GLYPH "agrave" GLYPH "amacron" '
|
||||
'GLYPH "aogonek" GLYPH "aring" GLYPH "atilde" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'END_GROUP'
|
||||
).statements
|
||||
self.assertEqual((def_group.name, def_group.enum.glyphSet()),
|
||||
("aaccented",
|
||||
@ -134,14 +134,14 @@ class ParserTest(unittest.TestCase):
|
||||
def test_def_group_groups(self):
|
||||
[group1, group2, test_group] = self.parse(
|
||||
'DEF_GROUP "Group1"\n'
|
||||
'ENUM GLYPH "a" GLYPH "b" GLYPH "c" GLYPH "d" END_ENUM\n'
|
||||
' ENUM GLYPH "a" GLYPH "b" GLYPH "c" GLYPH "d" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'DEF_GROUP "Group2"\n'
|
||||
'ENUM GLYPH "e" GLYPH "f" GLYPH "g" GLYPH "h" END_ENUM\n'
|
||||
' ENUM GLYPH "e" GLYPH "f" GLYPH "g" GLYPH "h" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'DEF_GROUP "TestGroup"\n'
|
||||
'ENUM GROUP "Group1" GROUP "Group2" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
' ENUM GROUP "Group1" GROUP "Group2" END_ENUM\n'
|
||||
'END_GROUP'
|
||||
).statements
|
||||
groups = [g.group for g in test_group.enum.enum]
|
||||
self.assertEqual((test_group.name, groups),
|
||||
@ -151,20 +151,20 @@ class ParserTest(unittest.TestCase):
|
||||
[group1, test_group1, test_group2, test_group3, group2] = \
|
||||
self.parse(
|
||||
'DEF_GROUP "Group1"\n'
|
||||
'ENUM GLYPH "a" GLYPH "b" GLYPH "c" GLYPH "d" END_ENUM\n'
|
||||
' ENUM GLYPH "a" GLYPH "b" GLYPH "c" GLYPH "d" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'DEF_GROUP "TestGroup1"\n'
|
||||
'ENUM GROUP "Group1" GROUP "Group2" END_ENUM\n'
|
||||
' ENUM GROUP "Group1" GROUP "Group2" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'DEF_GROUP "TestGroup2"\n'
|
||||
'ENUM GROUP "Group2" END_ENUM\n'
|
||||
' ENUM GROUP "Group2" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'DEF_GROUP "TestGroup3"\n'
|
||||
'ENUM GROUP "Group2" GROUP "Group1" END_ENUM\n'
|
||||
' ENUM GROUP "Group2" GROUP "Group1" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'DEF_GROUP "Group2"\n'
|
||||
'ENUM GLYPH "e" GLYPH "f" GLYPH "g" GLYPH "h" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
' ENUM GLYPH "e" GLYPH "f" GLYPH "g" GLYPH "h" END_ENUM\n'
|
||||
'END_GROUP'
|
||||
).statements
|
||||
groups = [g.group for g in test_group1.enum.enum]
|
||||
self.assertEqual(
|
||||
@ -195,12 +195,12 @@ class ParserTest(unittest.TestCase):
|
||||
def test_def_group_glyphs_and_group(self):
|
||||
[def_group1, def_group2] = self.parse(
|
||||
'DEF_GROUP "aaccented"\n'
|
||||
'ENUM GLYPH "aacute" GLYPH "abreve" GLYPH "acircumflex" '
|
||||
' ENUM GLYPH "aacute" GLYPH "abreve" GLYPH "acircumflex" '
|
||||
'GLYPH "adieresis" GLYPH "ae" GLYPH "agrave" GLYPH "amacron" '
|
||||
'GLYPH "aogonek" GLYPH "aring" GLYPH "atilde" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'DEF_GROUP "KERN_lc_a_2ND"\n'
|
||||
'ENUM GLYPH "a" GROUP "aaccented" END_ENUM\n'
|
||||
' ENUM GLYPH "a" GROUP "aaccented" END_ENUM\n'
|
||||
'END_GROUP'
|
||||
).statements
|
||||
items = def_group2.enum.enum
|
||||
@ -219,7 +219,7 @@ class ParserTest(unittest.TestCase):
|
||||
'DEF_GLYPH "ccedilla" ID 210 UNICODE 231 TYPE BASE END_GLYPH\n'
|
||||
'DEF_GLYPH "cdotaccent" ID 210 UNICODE 267 TYPE BASE END_GLYPH\n'
|
||||
'DEF_GROUP "KERN_lc_a_2ND"\n'
|
||||
'ENUM RANGE "a" TO "atilde" GLYPH "b" RANGE "c" TO "cdotaccent" '
|
||||
' ENUM RANGE "a" TO "atilde" GLYPH "b" RANGE "c" TO "cdotaccent" '
|
||||
'END_ENUM\n'
|
||||
'END_GROUP'
|
||||
).statements[-1]
|
||||
@ -238,7 +238,7 @@ class ParserTest(unittest.TestCase):
|
||||
'END_GROUP\n'
|
||||
'DEF_GROUP "dupe"\n'
|
||||
'ENUM GLYPH "x" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'END_GROUP'
|
||||
)
|
||||
|
||||
def test_group_duplicate_case_insensitive(self):
|
||||
@ -251,12 +251,12 @@ class ParserTest(unittest.TestCase):
|
||||
'END_GROUP\n'
|
||||
'DEF_GROUP "Dupe"\n'
|
||||
'ENUM GLYPH "x" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'END_GROUP'
|
||||
)
|
||||
|
||||
def test_script_without_langsys(self):
|
||||
[script] = self.parse(
|
||||
'DEF_SCRIPT NAME "Latin" TAG "latn"\n'
|
||||
'DEF_SCRIPT NAME "Latin" TAG "latn"\n\n'
|
||||
'END_SCRIPT'
|
||||
).statements
|
||||
self.assertEqual((script.name, script.tag, script.langs),
|
||||
@ -264,10 +264,10 @@ class ParserTest(unittest.TestCase):
|
||||
|
||||
def test_langsys_normal(self):
|
||||
[def_script] = self.parse(
|
||||
'DEF_SCRIPT NAME "Latin" TAG "latn"\n'
|
||||
'DEF_LANGSYS NAME "Romanian" TAG "ROM "\n'
|
||||
'DEF_SCRIPT NAME "Latin" TAG "latn"\n\n'
|
||||
'DEF_LANGSYS NAME "Romanian" TAG "ROM "\n\n'
|
||||
'END_LANGSYS\n'
|
||||
'DEF_LANGSYS NAME "Moldavian" TAG "MOL "\n'
|
||||
'DEF_LANGSYS NAME "Moldavian" TAG "MOL "\n\n'
|
||||
'END_LANGSYS\n'
|
||||
'END_SCRIPT'
|
||||
).statements
|
||||
@ -285,8 +285,8 @@ class ParserTest(unittest.TestCase):
|
||||
|
||||
def test_langsys_no_script_name(self):
|
||||
[langsys] = self.parse(
|
||||
'DEF_SCRIPT TAG "latn"\n'
|
||||
'DEF_LANGSYS NAME "Default" TAG "dflt"\n'
|
||||
'DEF_SCRIPT TAG "latn"\n\n'
|
||||
'DEF_LANGSYS NAME "Default" TAG "dflt"\n\n'
|
||||
'END_LANGSYS\n'
|
||||
'END_SCRIPT'
|
||||
).statements
|
||||
@ -303,8 +303,8 @@ class ParserTest(unittest.TestCase):
|
||||
VoltLibError,
|
||||
r'.*Expected "TAG"'):
|
||||
[langsys] = self.parse(
|
||||
'DEF_SCRIPT NAME "Latin"\n'
|
||||
'DEF_LANGSYS NAME "Default" TAG "dflt"\n'
|
||||
'DEF_SCRIPT NAME "Latin"\n\n'
|
||||
'DEF_LANGSYS NAME "Default" TAG "dflt"\n\n'
|
||||
'END_LANGSYS\n'
|
||||
'END_SCRIPT'
|
||||
).statements
|
||||
@ -315,12 +315,12 @@ class ParserTest(unittest.TestCase):
|
||||
'Script "DFLT" already defined, '
|
||||
'script tags are case insensitive'):
|
||||
[langsys1, langsys2] = self.parse(
|
||||
'DEF_SCRIPT NAME "Default" TAG "DFLT"\n'
|
||||
'DEF_LANGSYS NAME "Default" TAG "dflt"\n'
|
||||
'DEF_SCRIPT NAME "Default" TAG "DFLT"\n\n'
|
||||
'DEF_LANGSYS NAME "Default" TAG "dflt"\n\n'
|
||||
'END_LANGSYS\n'
|
||||
'END_SCRIPT\n'
|
||||
'DEF_SCRIPT TAG "DFLT"\n'
|
||||
'DEF_LANGSYS NAME "Default" TAG "dflt"\n'
|
||||
'DEF_SCRIPT TAG "DFLT"\n\n'
|
||||
'DEF_LANGSYS NAME "Default" TAG "dflt"\n\n'
|
||||
'END_LANGSYS\n'
|
||||
'END_SCRIPT'
|
||||
).statements
|
||||
@ -336,21 +336,21 @@ class ParserTest(unittest.TestCase):
|
||||
'END_LANGSYS\n'
|
||||
'DEF_LANGSYS NAME "Default" TAG "dflt"\n'
|
||||
'END_LANGSYS\n'
|
||||
'END_SCRIPT\n'
|
||||
'END_SCRIPT'
|
||||
).statements
|
||||
|
||||
def test_langsys_lang_in_separate_scripts(self):
|
||||
[langsys1, langsys2] = self.parse(
|
||||
'DEF_SCRIPT NAME "Default" TAG "DFLT"\n'
|
||||
'DEF_LANGSYS NAME "Default" TAG "dflt"\n'
|
||||
'DEF_SCRIPT NAME "Default" TAG "DFLT"\n\n'
|
||||
'DEF_LANGSYS NAME "Default" TAG "dflt"\n\n'
|
||||
'END_LANGSYS\n'
|
||||
'DEF_LANGSYS NAME "Default" TAG "ROM "\n'
|
||||
'DEF_LANGSYS NAME "Default" TAG "ROM "\n\n'
|
||||
'END_LANGSYS\n'
|
||||
'END_SCRIPT\n'
|
||||
'DEF_SCRIPT NAME "Latin" TAG "latn"\n'
|
||||
'DEF_LANGSYS NAME "Default" TAG "dflt"\n'
|
||||
'DEF_SCRIPT NAME "Latin" TAG "latn"\n\n'
|
||||
'DEF_LANGSYS NAME "Default" TAG "dflt"\n\n'
|
||||
'END_LANGSYS\n'
|
||||
'DEF_LANGSYS NAME "Default" TAG "ROM "\n'
|
||||
'DEF_LANGSYS NAME "Default" TAG "ROM "\n\n'
|
||||
'END_LANGSYS\n'
|
||||
'END_SCRIPT'
|
||||
).statements
|
||||
@ -361,8 +361,8 @@ class ParserTest(unittest.TestCase):
|
||||
|
||||
def test_langsys_no_lang_name(self):
|
||||
[langsys] = self.parse(
|
||||
'DEF_SCRIPT NAME "Latin" TAG "latn"\n'
|
||||
'DEF_LANGSYS TAG "dflt"\n'
|
||||
'DEF_SCRIPT NAME "Latin" TAG "latn"\n\n'
|
||||
'DEF_LANGSYS TAG "dflt"\n\n'
|
||||
'END_LANGSYS\n'
|
||||
'END_SCRIPT'
|
||||
).statements
|
||||
@ -379,18 +379,18 @@ class ParserTest(unittest.TestCase):
|
||||
VoltLibError,
|
||||
r'.*Expected "TAG"'):
|
||||
[langsys] = self.parse(
|
||||
'DEF_SCRIPT NAME "Latin" TAG "latn"\n'
|
||||
'DEF_LANGSYS NAME "Default"\n'
|
||||
'DEF_SCRIPT NAME "Latin" TAG "latn"\n\n'
|
||||
'DEF_LANGSYS NAME "Default"\n\n'
|
||||
'END_LANGSYS\n'
|
||||
'END_SCRIPT'
|
||||
).statements
|
||||
|
||||
def test_feature(self):
|
||||
[def_script] = self.parse(
|
||||
'DEF_SCRIPT NAME "Latin" TAG "latn"\n'
|
||||
'DEF_LANGSYS NAME "Romanian" TAG "ROM "\n'
|
||||
'DEF_SCRIPT NAME "Latin" TAG "latn"\n\n'
|
||||
'DEF_LANGSYS NAME "Romanian" TAG "ROM "\n\n'
|
||||
'DEF_FEATURE NAME "Fractions" TAG "frac"\n'
|
||||
'LOOKUP "fraclookup"\n'
|
||||
' LOOKUP "fraclookup"\n'
|
||||
'END_FEATURE\n'
|
||||
'END_LANGSYS\n'
|
||||
'END_SCRIPT'
|
||||
@ -402,10 +402,10 @@ class ParserTest(unittest.TestCase):
|
||||
"frac",
|
||||
["fraclookup"]))
|
||||
[def_script] = self.parse(
|
||||
'DEF_SCRIPT NAME "Latin" TAG "latn"\n'
|
||||
'DEF_LANGSYS NAME "Romanian" TAG "ROM "\n'
|
||||
'DEF_SCRIPT NAME "Latin" TAG "latn"\n\n'
|
||||
'DEF_LANGSYS NAME "Romanian" TAG "ROM "\n\n'
|
||||
'DEF_FEATURE NAME "Kerning" TAG "kern"\n'
|
||||
'LOOKUP "kern1" LOOKUP "kern2"\n'
|
||||
' LOOKUP "kern1" LOOKUP "kern2"\n'
|
||||
'END_FEATURE\n'
|
||||
'END_LANGSYS\n'
|
||||
'END_SCRIPT'
|
||||
@ -572,12 +572,13 @@ class ParserTest(unittest.TestCase):
|
||||
|
||||
def test_substitution_single_in_context(self):
|
||||
[group, lookup] = self.parse(
|
||||
'DEF_GROUP "Denominators" ENUM GLYPH "one.dnom" GLYPH "two.dnom" '
|
||||
'END_ENUM END_GROUP\n'
|
||||
'DEF_GROUP "Denominators"\n'
|
||||
' ENUM GLYPH "one.dnom" GLYPH "two.dnom" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'DEF_LOOKUP "fracdnom" PROCESS_BASE PROCESS_MARKS ALL '
|
||||
'DIRECTION LTR\n'
|
||||
'IN_CONTEXT LEFT ENUM GROUP "Denominators" GLYPH "fraction" '
|
||||
'END_ENUM\n'
|
||||
'IN_CONTEXT\n'
|
||||
' LEFT ENUM GROUP "Denominators" GLYPH "fraction" END_ENUM\n'
|
||||
'END_CONTEXT\n'
|
||||
'AS_SUBSTITUTION\n'
|
||||
'SUB GLYPH "one"\n'
|
||||
@ -603,17 +604,18 @@ class ParserTest(unittest.TestCase):
|
||||
|
||||
def test_substitution_single_in_contexts(self):
|
||||
[group, lookup] = self.parse(
|
||||
'DEF_GROUP "Hebrew" ENUM GLYPH "uni05D0" GLYPH "uni05D1" '
|
||||
'END_ENUM END_GROUP\n'
|
||||
'DEF_GROUP "Hebrew"\n'
|
||||
' ENUM GLYPH "uni05D0" GLYPH "uni05D1" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'DEF_LOOKUP "HebrewCurrency" PROCESS_BASE PROCESS_MARKS ALL '
|
||||
'DIRECTION LTR\n'
|
||||
'IN_CONTEXT\n'
|
||||
'RIGHT GROUP "Hebrew"\n'
|
||||
'RIGHT GLYPH "one.Hebr"\n'
|
||||
' RIGHT GROUP "Hebrew"\n'
|
||||
' RIGHT GLYPH "one.Hebr"\n'
|
||||
'END_CONTEXT\n'
|
||||
'IN_CONTEXT\n'
|
||||
'LEFT GROUP "Hebrew"\n'
|
||||
'LEFT GLYPH "one.Hebr"\n'
|
||||
' LEFT GROUP "Hebrew"\n'
|
||||
' LEFT GLYPH "one.Hebr"\n'
|
||||
'END_CONTEXT\n'
|
||||
'AS_SUBSTITUTION\n'
|
||||
'SUB GLYPH "dollar"\n'
|
||||
@ -644,8 +646,9 @@ class ParserTest(unittest.TestCase):
|
||||
|
||||
def test_substitution_skip_base(self):
|
||||
[group, lookup] = self.parse(
|
||||
'DEF_GROUP "SomeMarks" ENUM GLYPH "marka" GLYPH "markb" '
|
||||
'END_ENUM END_GROUP\n'
|
||||
'DEF_GROUP "SomeMarks"\n'
|
||||
' ENUM GLYPH "marka" GLYPH "markb" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'DEF_LOOKUP "SomeSub" SKIP_BASE PROCESS_MARKS ALL '
|
||||
'DIRECTION LTR\n'
|
||||
'IN_CONTEXT\n'
|
||||
@ -662,8 +665,9 @@ class ParserTest(unittest.TestCase):
|
||||
|
||||
def test_substitution_process_base(self):
|
||||
[group, lookup] = self.parse(
|
||||
'DEF_GROUP "SomeMarks" ENUM GLYPH "marka" GLYPH "markb" '
|
||||
'END_ENUM END_GROUP\n'
|
||||
'DEF_GROUP "SomeMarks"\n'
|
||||
' ENUM GLYPH "marka" GLYPH "markb" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS ALL '
|
||||
'DIRECTION LTR\n'
|
||||
'IN_CONTEXT\n'
|
||||
@ -680,11 +684,15 @@ class ParserTest(unittest.TestCase):
|
||||
|
||||
def test_substitution_process_marks(self):
|
||||
[group, lookup] = self.parse(
|
||||
'DEF_GROUP "SomeMarks" ENUM GLYPH "marka" GLYPH "markb" '
|
||||
'END_ENUM END_GROUP\n'
|
||||
'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS "SomeMarks" '
|
||||
'DEF_GROUP "SomeMarks"\n'
|
||||
' ENUM GLYPH "marka" GLYPH "markb" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS "SomeMarks"\n'
|
||||
'IN_CONTEXT\n'
|
||||
'END_CONTEXT\n'
|
||||
'AS_SUBSTITUTION\n'
|
||||
'SUB GLYPH "A" WITH GLYPH "A.c2sc"\n'
|
||||
'SUB GLYPH "A"\n'
|
||||
'WITH GLYPH "A.c2sc"\n'
|
||||
'END_SUB\n'
|
||||
'END_SUBSTITUTION'
|
||||
).statements
|
||||
@ -694,9 +702,12 @@ class ParserTest(unittest.TestCase):
|
||||
|
||||
def test_substitution_process_marks_all(self):
|
||||
[lookup] = self.parse(
|
||||
'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS "ALL" '
|
||||
'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS ALL\n'
|
||||
'IN_CONTEXT\n'
|
||||
'END_CONTEXT\n'
|
||||
'AS_SUBSTITUTION\n'
|
||||
'SUB GLYPH "A" WITH GLYPH "A.c2sc"\n'
|
||||
'SUB GLYPH "A"\n'
|
||||
'WITH GLYPH "A.c2sc"\n'
|
||||
'END_SUB\n'
|
||||
'END_SUBSTITUTION'
|
||||
).statements
|
||||
@ -705,10 +716,13 @@ class ParserTest(unittest.TestCase):
|
||||
("SomeSub", True))
|
||||
|
||||
def test_substitution_process_marks_none(self):
|
||||
[lookup] = self.parse(
|
||||
'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS "NONE" '
|
||||
[lookup] = self.parse_(
|
||||
'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS "NONE"\n'
|
||||
'IN_CONTEXT\n'
|
||||
'END_CONTEXT\n'
|
||||
'AS_SUBSTITUTION\n'
|
||||
'SUB GLYPH "A" WITH GLYPH "A.c2sc"\n'
|
||||
'SUB GLYPH "A"\n'
|
||||
'WITH GLYPH "A.c2sc"\n'
|
||||
'END_SUB\n'
|
||||
'END_SUBSTITUTION'
|
||||
).statements
|
||||
@ -732,10 +746,10 @@ class ParserTest(unittest.TestCase):
|
||||
|
||||
def test_substitution_skip_marks(self):
|
||||
[group, lookup] = self.parse(
|
||||
'DEF_GROUP "SomeMarks" ENUM GLYPH "marka" GLYPH "markb" '
|
||||
'END_ENUM END_GROUP\n'
|
||||
'DEF_LOOKUP "SomeSub" PROCESS_BASE SKIP_MARKS '
|
||||
'DIRECTION LTR\n'
|
||||
'DEF_GROUP "SomeMarks"\n'
|
||||
' ENUM GLYPH "marka" GLYPH "markb" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'DEF_LOOKUP "SomeSub" PROCESS_BASE SKIP_MARKS DIRECTION LTR\n'
|
||||
'IN_CONTEXT\n'
|
||||
'END_CONTEXT\n'
|
||||
'AS_SUBSTITUTION\n'
|
||||
@ -750,11 +764,13 @@ class ParserTest(unittest.TestCase):
|
||||
|
||||
def test_substitution_mark_attachment(self):
|
||||
[group, lookup] = self.parse(
|
||||
'DEF_GROUP "SomeMarks" ENUM GLYPH "acutecmb" GLYPH "gravecmb" '
|
||||
'END_ENUM END_GROUP\n'
|
||||
'DEF_GROUP "SomeMarks"\n'
|
||||
' ENUM GLYPH "acutecmb" GLYPH "gravecmb" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'DEF_LOOKUP "SomeSub" PROCESS_BASE '
|
||||
'PROCESS_MARKS "SomeMarks" \n'
|
||||
'DIRECTION RTL\n'
|
||||
'PROCESS_MARKS "SomeMarks" DIRECTION RTL\n'
|
||||
'IN_CONTEXT\n'
|
||||
'END_CONTEXT\n'
|
||||
'AS_SUBSTITUTION\n'
|
||||
'SUB GLYPH "A"\n'
|
||||
'WITH GLYPH "A.c2sc"\n'
|
||||
@ -767,11 +783,13 @@ class ParserTest(unittest.TestCase):
|
||||
|
||||
def test_substitution_mark_glyph_set(self):
|
||||
[group, lookup] = self.parse(
|
||||
'DEF_GROUP "SomeMarks" ENUM GLYPH "acutecmb" GLYPH "gravecmb" '
|
||||
'END_ENUM END_GROUP\n'
|
||||
'DEF_GROUP "SomeMarks"\n'
|
||||
' ENUM GLYPH "acutecmb" GLYPH "gravecmb" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'DEF_LOOKUP "SomeSub" PROCESS_BASE '
|
||||
'PROCESS_MARKS MARK_GLYPH_SET "SomeMarks" \n'
|
||||
'DIRECTION RTL\n'
|
||||
'PROCESS_MARKS MARK_GLYPH_SET "SomeMarks" DIRECTION RTL\n'
|
||||
'IN_CONTEXT\n'
|
||||
'END_CONTEXT\n'
|
||||
'AS_SUBSTITUTION\n'
|
||||
'SUB GLYPH "A"\n'
|
||||
'WITH GLYPH "A.c2sc"\n'
|
||||
@ -784,11 +802,13 @@ class ParserTest(unittest.TestCase):
|
||||
|
||||
def test_substitution_process_all_marks(self):
|
||||
[group, lookup] = self.parse(
|
||||
'DEF_GROUP "SomeMarks" ENUM GLYPH "acutecmb" GLYPH "gravecmb" '
|
||||
'END_ENUM END_GROUP\n'
|
||||
'DEF_LOOKUP "SomeSub" PROCESS_BASE '
|
||||
'PROCESS_MARKS ALL \n'
|
||||
'DEF_GROUP "SomeMarks"\n'
|
||||
' ENUM GLYPH "acutecmb" GLYPH "gravecmb" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS ALL '
|
||||
'DIRECTION RTL\n'
|
||||
'IN_CONTEXT\n'
|
||||
'END_CONTEXT\n'
|
||||
'AS_SUBSTITUTION\n'
|
||||
'SUB GLYPH "A"\n'
|
||||
'WITH GLYPH "A.c2sc"\n'
|
||||
@ -805,7 +825,7 @@ class ParserTest(unittest.TestCase):
|
||||
'DEF_LOOKUP "Lookup" PROCESS_BASE PROCESS_MARKS ALL '
|
||||
'DIRECTION LTR\n'
|
||||
'IN_CONTEXT\n'
|
||||
'RIGHT ENUM GLYPH "a" GLYPH "b" END_ENUM\n'
|
||||
' RIGHT ENUM GLYPH "a" GLYPH "b" END_ENUM\n'
|
||||
'END_CONTEXT\n'
|
||||
'AS_SUBSTITUTION\n'
|
||||
'SUB GLYPH "a"\n'
|
||||
@ -821,15 +841,15 @@ class ParserTest(unittest.TestCase):
|
||||
def test_substitution_reversal(self):
|
||||
lookup = self.parse(
|
||||
'DEF_GROUP "DFLT_Num_standardFigures"\n'
|
||||
'ENUM GLYPH "zero" GLYPH "one" GLYPH "two" END_ENUM\n'
|
||||
' ENUM GLYPH "zero" GLYPH "one" GLYPH "two" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'DEF_GROUP "DFLT_Num_numerators"\n'
|
||||
'ENUM GLYPH "zero.numr" GLYPH "one.numr" GLYPH "two.numr" END_ENUM\n'
|
||||
' ENUM GLYPH "zero.numr" GLYPH "one.numr" GLYPH "two.numr" END_ENUM\n'
|
||||
'END_GROUP\n'
|
||||
'DEF_LOOKUP "RevLookup" PROCESS_BASE PROCESS_MARKS ALL '
|
||||
'DIRECTION LTR REVERSAL\n'
|
||||
'IN_CONTEXT\n'
|
||||
'RIGHT ENUM GLYPH "a" GLYPH "b" END_ENUM\n'
|
||||
' RIGHT ENUM GLYPH "a" GLYPH "b" END_ENUM\n'
|
||||
'END_CONTEXT\n'
|
||||
'AS_SUBSTITUTION\n'
|
||||
'SUB GROUP "DFLT_Num_standardFigures"\n'
|
||||
@ -885,7 +905,7 @@ class ParserTest(unittest.TestCase):
|
||||
'DEF_LOOKUP "numr" PROCESS_BASE PROCESS_MARKS ALL '
|
||||
'DIRECTION LTR REVERSAL\n'
|
||||
'IN_CONTEXT\n'
|
||||
'RIGHT ENUM '
|
||||
' RIGHT ENUM '
|
||||
'GLYPH "fraction" '
|
||||
'RANGE "zero.numr" TO "nine.numr" '
|
||||
'END_ENUM\n'
|
||||
@ -926,7 +946,7 @@ class ParserTest(unittest.TestCase):
|
||||
'DEF_LOOKUP "empty_position" PROCESS_BASE PROCESS_MARKS ALL '
|
||||
'DIRECTION LTR\n'
|
||||
'EXCEPT_CONTEXT\n'
|
||||
'LEFT GLYPH "glyph"\n'
|
||||
' LEFT GLYPH "glyph"\n'
|
||||
'END_CONTEXT\n'
|
||||
'AS_POSITION\n'
|
||||
'END_POSITION'
|
||||
@ -945,13 +965,13 @@ class ParserTest(unittest.TestCase):
|
||||
'END_ATTACH\n'
|
||||
'END_POSITION\n'
|
||||
'DEF_ANCHOR "MARK_top" ON 120 GLYPH acutecomb COMPONENT 1 '
|
||||
'AT POS DX 0 DY 450 END_POS END_ANCHOR\n'
|
||||
'AT POS DX 0 DY 450 END_POS END_ANCHOR\n'
|
||||
'DEF_ANCHOR "MARK_top" ON 121 GLYPH gravecomb COMPONENT 1 '
|
||||
'AT POS DX 0 DY 450 END_POS END_ANCHOR\n'
|
||||
'AT POS DX 0 DY 450 END_POS END_ANCHOR\n'
|
||||
'DEF_ANCHOR "top" ON 31 GLYPH a COMPONENT 1 '
|
||||
'AT POS DX 210 DY 450 END_POS END_ANCHOR\n'
|
||||
'AT POS DX 210 DY 450 END_POS END_ANCHOR\n'
|
||||
'DEF_ANCHOR "top" ON 35 GLYPH e COMPONENT 1 '
|
||||
'AT POS DX 215 DY 450 END_POS END_ANCHOR\n'
|
||||
'AT POS DX 215 DY 450 END_POS END_ANCHOR'
|
||||
).statements
|
||||
pos = lookup.pos
|
||||
coverage = [g.glyph for g in pos.coverage]
|
||||
@ -991,9 +1011,9 @@ class ParserTest(unittest.TestCase):
|
||||
'IN_CONTEXT\n'
|
||||
'END_CONTEXT\n'
|
||||
'AS_POSITION\n'
|
||||
'ATTACH_CURSIVE EXIT GLYPH "a" GLYPH "b" ENTER GLYPH "c"\n'
|
||||
'ATTACH_CURSIVE\nEXIT GLYPH "a" GLYPH "b"\nENTER GLYPH "c"\n'
|
||||
'END_ATTACH\n'
|
||||
'END_POSITION\n'
|
||||
'END_POSITION'
|
||||
).statements
|
||||
exit = [[g.glyph for g in v] for v in lookup.pos.coverages_exit]
|
||||
enter = [[g.glyph for g in v] for v in lookup.pos.coverages_enter]
|
||||
@ -1010,12 +1030,12 @@ class ParserTest(unittest.TestCase):
|
||||
'END_CONTEXT\n'
|
||||
'AS_POSITION\n'
|
||||
'ADJUST_PAIR\n'
|
||||
' FIRST GLYPH "A"\n'
|
||||
' SECOND GLYPH "V"\n'
|
||||
' FIRST GLYPH "A"\n'
|
||||
' SECOND GLYPH "V"\n'
|
||||
' 1 2 BY POS ADV -30 END_POS POS END_POS\n'
|
||||
' 2 1 BY POS ADV -30 END_POS POS END_POS\n'
|
||||
' 2 1 BY POS ADV -30 END_POS POS END_POS\n\n'
|
||||
'END_ADJUST\n'
|
||||
'END_POSITION\n'
|
||||
'END_POSITION'
|
||||
).statements
|
||||
coverages_1 = [[g.glyph for g in v] for v in lookup.pos.coverages_1]
|
||||
coverages_2 = [[g.glyph for g in v] for v in lookup.pos.coverages_2]
|
||||
@ -1034,15 +1054,15 @@ class ParserTest(unittest.TestCase):
|
||||
'DEF_LOOKUP "TestLookup" PROCESS_BASE PROCESS_MARKS ALL '
|
||||
'DIRECTION LTR\n'
|
||||
'IN_CONTEXT\n'
|
||||
# 'LEFT GLYPH "leftGlyph"\n'
|
||||
# 'RIGHT GLYPH "rightGlyph"\n'
|
||||
# ' LEFT GLYPH "leftGlyph"\n'
|
||||
# ' RIGHT GLYPH "rightGlyph"\n'
|
||||
'END_CONTEXT\n'
|
||||
'AS_POSITION\n'
|
||||
'ADJUST_SINGLE'
|
||||
' GLYPH "glyph1" BY POS ADV 0 DX 123 END_POS\n'
|
||||
' GLYPH "glyph1" BY POS ADV 0 DX 123 END_POS'
|
||||
' GLYPH "glyph2" BY POS ADV 0 DX 456 END_POS\n'
|
||||
'END_ADJUST\n'
|
||||
'END_POSITION\n'
|
||||
'END_POSITION'
|
||||
).statements
|
||||
pos = lookup.pos
|
||||
adjust = [[[g.glyph for g in a], b] for (a, b) in pos.adjust_single]
|
||||
@ -1056,11 +1076,11 @@ class ParserTest(unittest.TestCase):
|
||||
def test_def_anchor(self):
|
||||
[anchor1, anchor2, anchor3] = self.parse(
|
||||
'DEF_ANCHOR "top" ON 120 GLYPH a '
|
||||
'COMPONENT 1 AT POS DX 250 DY 450 END_POS END_ANCHOR\n'
|
||||
'COMPONENT 1 AT POS DX 250 DY 450 END_POS END_ANCHOR\n'
|
||||
'DEF_ANCHOR "MARK_top" ON 120 GLYPH acutecomb '
|
||||
'COMPONENT 1 AT POS DX 0 DY 450 END_POS END_ANCHOR\n'
|
||||
'COMPONENT 1 AT POS DX 0 DY 450 END_POS END_ANCHOR\n'
|
||||
'DEF_ANCHOR "bottom" ON 120 GLYPH a '
|
||||
'COMPONENT 1 AT POS DX 250 DY 0 END_POS END_ANCHOR\n'
|
||||
'COMPONENT 1 AT POS DX 250 DY 0 END_POS END_ANCHOR'
|
||||
).statements
|
||||
self.assertEqual(
|
||||
(anchor1.name, anchor1.gid, anchor1.glyph_name, anchor1.component,
|
||||
@ -1084,9 +1104,9 @@ class ParserTest(unittest.TestCase):
|
||||
def test_def_anchor_multi_component(self):
|
||||
[anchor1, anchor2] = self.parse(
|
||||
'DEF_ANCHOR "top" ON 120 GLYPH a '
|
||||
'COMPONENT 1 AT POS DX 250 DY 450 END_POS END_ANCHOR\n'
|
||||
'COMPONENT 1 AT POS DX 250 DY 450 END_POS END_ANCHOR\n'
|
||||
'DEF_ANCHOR "top" ON 120 GLYPH a '
|
||||
'COMPONENT 2 AT POS DX 250 DY 450 END_POS END_ANCHOR\n'
|
||||
'COMPONENT 2 AT POS DX 250 DY 450 END_POS END_ANCHOR'
|
||||
).statements
|
||||
self.assertEqual(
|
||||
(anchor1.name, anchor1.gid, anchor1.glyph_name, anchor1.component),
|
||||
@ -1104,15 +1124,15 @@ class ParserTest(unittest.TestCase):
|
||||
'anchor names are case insensitive',
|
||||
self.parse,
|
||||
'DEF_ANCHOR "dupe" ON 120 GLYPH a '
|
||||
'COMPONENT 1 AT POS DX 250 DY 450 END_POS END_ANCHOR\n'
|
||||
'COMPONENT 1 AT POS DX 250 DY 450 END_POS END_ANCHOR\n'
|
||||
'DEF_ANCHOR "dupe" ON 120 GLYPH a '
|
||||
'COMPONENT 1 AT POS DX 250 DY 450 END_POS END_ANCHOR\n'
|
||||
'COMPONENT 1 AT POS DX 250 DY 450 END_POS END_ANCHOR'
|
||||
)
|
||||
|
||||
def test_def_anchor_locked(self):
|
||||
[anchor] = self.parse(
|
||||
'DEF_ANCHOR "top" ON 120 GLYPH a '
|
||||
'COMPONENT 1 LOCKED AT POS DX 250 DY 450 END_POS END_ANCHOR\n'
|
||||
'COMPONENT 1 LOCKED AT POS DX 250 DY 450 END_POS END_ANCHOR'
|
||||
).statements
|
||||
self.assertEqual(
|
||||
(anchor.name, anchor.gid, anchor.glyph_name, anchor.component,
|
||||
@ -1124,7 +1144,7 @@ class ParserTest(unittest.TestCase):
|
||||
def test_anchor_adjust_device(self):
|
||||
[anchor] = self.parse(
|
||||
'DEF_ANCHOR "MARK_top" ON 123 GLYPH diacglyph '
|
||||
'COMPONENT 1 AT POS DX 0 DY 456 ADJUST_BY 12 AT 34 '
|
||||
'COMPONENT 1 AT POS DX 0 DY 456 ADJUST_BY 12 AT 34 '
|
||||
'ADJUST_BY 56 AT 78 END_POS END_ANCHOR'
|
||||
).statements
|
||||
self.assertEqual(
|
||||
@ -1136,7 +1156,7 @@ class ParserTest(unittest.TestCase):
|
||||
[grid_ppem, pres_ppem, ppos_ppem] = self.parse(
|
||||
'GRID_PPEM 20\n'
|
||||
'PRESENTATION_PPEM 72\n'
|
||||
'PPOSITIONING_PPEM 144\n'
|
||||
'PPOSITIONING_PPEM 144'
|
||||
).statements
|
||||
self.assertEqual(
|
||||
((grid_ppem.name, grid_ppem.value),
|
||||
@ -1149,7 +1169,7 @@ class ParserTest(unittest.TestCase):
|
||||
def test_compiler_flags(self):
|
||||
[setting1, setting2] = self.parse(
|
||||
'COMPILER_USEEXTENSIONLOOKUPS\n'
|
||||
'COMPILER_USEPAIRPOSFORMAT2\n'
|
||||
'COMPILER_USEPAIRPOSFORMAT2'
|
||||
).statements
|
||||
self.assertEqual(
|
||||
((setting1.name, setting1.value),
|
||||
@ -1162,7 +1182,7 @@ class ParserTest(unittest.TestCase):
|
||||
[cmap_format1, cmap_format2, cmap_format3] = self.parse(
|
||||
'CMAP_FORMAT 0 3 4\n'
|
||||
'CMAP_FORMAT 1 0 6\n'
|
||||
'CMAP_FORMAT 3 1 4\n'
|
||||
'CMAP_FORMAT 3 1 4'
|
||||
).statements
|
||||
self.assertEqual(
|
||||
((cmap_format1.name, cmap_format1.value),
|
||||
@ -1174,14 +1194,22 @@ class ParserTest(unittest.TestCase):
|
||||
)
|
||||
|
||||
def test_stop_at_end(self):
|
||||
[def_glyph] = self.parse(
|
||||
doc = self.parse_(
|
||||
'DEF_GLYPH ".notdef" ID 0 TYPE BASE END_GLYPH END\0\0\0\0'
|
||||
).statements
|
||||
)
|
||||
[def_glyph] = doc.statements
|
||||
self.assertEqual((def_glyph.name, def_glyph.id, def_glyph.unicode,
|
||||
def_glyph.type, def_glyph.components),
|
||||
(".notdef", 0, None, "BASE", None))
|
||||
self.assertEqual(str(doc),
|
||||
'\nDEF_GLYPH ".notdef" ID 0 TYPE BASE END_GLYPH END\n')
|
||||
|
||||
def parse_(self, text):
|
||||
return Parser(UnicodeIO(text)).parse()
|
||||
|
||||
def parse(self, text):
|
||||
doc = self.parse_(text)
|
||||
self.assertEqual('\n'.join(str(s) for s in doc.statements), text)
|
||||
return Parser(UnicodeIO(text)).parse()
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
Loading…
x
Reference in New Issue
Block a user