1*e1fe3e4aSElliott Hughesfrom fontTools.voltLib import ast 2*e1fe3e4aSElliott Hughesfrom fontTools.voltLib.error import VoltLibError 3*e1fe3e4aSElliott Hughesfrom fontTools.voltLib.parser import Parser 4*e1fe3e4aSElliott Hughesfrom io import StringIO 5*e1fe3e4aSElliott Hughesimport unittest 6*e1fe3e4aSElliott Hughes 7*e1fe3e4aSElliott Hughes 8*e1fe3e4aSElliott Hughesclass ParserTest(unittest.TestCase): 9*e1fe3e4aSElliott Hughes def __init__(self, methodName): 10*e1fe3e4aSElliott Hughes unittest.TestCase.__init__(self, methodName) 11*e1fe3e4aSElliott Hughes # Python 3 renamed assertRaisesRegexp to assertRaisesRegex, 12*e1fe3e4aSElliott Hughes # and fires deprecation warnings if a program uses the old name. 13*e1fe3e4aSElliott Hughes if not hasattr(self, "assertRaisesRegex"): 14*e1fe3e4aSElliott Hughes self.assertRaisesRegex = self.assertRaisesRegexp 15*e1fe3e4aSElliott Hughes 16*e1fe3e4aSElliott Hughes def assertSubEqual(self, sub, glyph_ref, replacement_ref): 17*e1fe3e4aSElliott Hughes glyphs = [[g.glyph for g in v] for v in sub.mapping.keys()] 18*e1fe3e4aSElliott Hughes replacement = [[g.glyph for g in v] for v in sub.mapping.values()] 19*e1fe3e4aSElliott Hughes 20*e1fe3e4aSElliott Hughes self.assertEqual(glyphs, glyph_ref) 21*e1fe3e4aSElliott Hughes self.assertEqual(replacement, replacement_ref) 22*e1fe3e4aSElliott Hughes 23*e1fe3e4aSElliott Hughes def test_def_glyph_base(self): 24*e1fe3e4aSElliott Hughes [def_glyph] = self.parse( 25*e1fe3e4aSElliott Hughes 'DEF_GLYPH ".notdef" ID 0 TYPE BASE END_GLYPH' 26*e1fe3e4aSElliott Hughes ).statements 27*e1fe3e4aSElliott Hughes self.assertEqual( 28*e1fe3e4aSElliott Hughes ( 29*e1fe3e4aSElliott Hughes def_glyph.name, 30*e1fe3e4aSElliott Hughes def_glyph.id, 31*e1fe3e4aSElliott Hughes def_glyph.unicode, 32*e1fe3e4aSElliott Hughes def_glyph.type, 33*e1fe3e4aSElliott Hughes def_glyph.components, 34*e1fe3e4aSElliott Hughes ), 35*e1fe3e4aSElliott Hughes (".notdef", 0, None, "BASE", None), 36*e1fe3e4aSElliott Hughes ) 37*e1fe3e4aSElliott Hughes 38*e1fe3e4aSElliott Hughes def test_def_glyph_base_with_unicode(self): 39*e1fe3e4aSElliott Hughes [def_glyph] = self.parse( 40*e1fe3e4aSElliott Hughes 'DEF_GLYPH "space" ID 3 UNICODE 32 TYPE BASE END_GLYPH' 41*e1fe3e4aSElliott Hughes ).statements 42*e1fe3e4aSElliott Hughes self.assertEqual( 43*e1fe3e4aSElliott Hughes ( 44*e1fe3e4aSElliott Hughes def_glyph.name, 45*e1fe3e4aSElliott Hughes def_glyph.id, 46*e1fe3e4aSElliott Hughes def_glyph.unicode, 47*e1fe3e4aSElliott Hughes def_glyph.type, 48*e1fe3e4aSElliott Hughes def_glyph.components, 49*e1fe3e4aSElliott Hughes ), 50*e1fe3e4aSElliott Hughes ("space", 3, [0x0020], "BASE", None), 51*e1fe3e4aSElliott Hughes ) 52*e1fe3e4aSElliott Hughes 53*e1fe3e4aSElliott Hughes def test_def_glyph_base_with_unicodevalues(self): 54*e1fe3e4aSElliott Hughes [def_glyph] = self.parse_( 55*e1fe3e4aSElliott Hughes 'DEF_GLYPH "CR" ID 2 UNICODEVALUES "U+0009" ' "TYPE BASE END_GLYPH" 56*e1fe3e4aSElliott Hughes ).statements 57*e1fe3e4aSElliott Hughes self.assertEqual( 58*e1fe3e4aSElliott Hughes ( 59*e1fe3e4aSElliott Hughes def_glyph.name, 60*e1fe3e4aSElliott Hughes def_glyph.id, 61*e1fe3e4aSElliott Hughes def_glyph.unicode, 62*e1fe3e4aSElliott Hughes def_glyph.type, 63*e1fe3e4aSElliott Hughes def_glyph.components, 64*e1fe3e4aSElliott Hughes ), 65*e1fe3e4aSElliott Hughes ("CR", 2, [0x0009], "BASE", None), 66*e1fe3e4aSElliott Hughes ) 67*e1fe3e4aSElliott Hughes 68*e1fe3e4aSElliott Hughes def test_def_glyph_base_with_mult_unicodevalues(self): 69*e1fe3e4aSElliott Hughes [def_glyph] = self.parse( 70*e1fe3e4aSElliott Hughes 'DEF_GLYPH "CR" ID 2 UNICODEVALUES "U+0009,U+000D" ' "TYPE BASE END_GLYPH" 71*e1fe3e4aSElliott Hughes ).statements 72*e1fe3e4aSElliott Hughes self.assertEqual( 73*e1fe3e4aSElliott Hughes ( 74*e1fe3e4aSElliott Hughes def_glyph.name, 75*e1fe3e4aSElliott Hughes def_glyph.id, 76*e1fe3e4aSElliott Hughes def_glyph.unicode, 77*e1fe3e4aSElliott Hughes def_glyph.type, 78*e1fe3e4aSElliott Hughes def_glyph.components, 79*e1fe3e4aSElliott Hughes ), 80*e1fe3e4aSElliott Hughes ("CR", 2, [0x0009, 0x000D], "BASE", None), 81*e1fe3e4aSElliott Hughes ) 82*e1fe3e4aSElliott Hughes 83*e1fe3e4aSElliott Hughes def test_def_glyph_base_with_empty_unicodevalues(self): 84*e1fe3e4aSElliott Hughes [def_glyph] = self.parse_( 85*e1fe3e4aSElliott Hughes 'DEF_GLYPH "i.locl" ID 269 UNICODEVALUES "" ' "TYPE BASE END_GLYPH" 86*e1fe3e4aSElliott Hughes ).statements 87*e1fe3e4aSElliott Hughes self.assertEqual( 88*e1fe3e4aSElliott Hughes ( 89*e1fe3e4aSElliott Hughes def_glyph.name, 90*e1fe3e4aSElliott Hughes def_glyph.id, 91*e1fe3e4aSElliott Hughes def_glyph.unicode, 92*e1fe3e4aSElliott Hughes def_glyph.type, 93*e1fe3e4aSElliott Hughes def_glyph.components, 94*e1fe3e4aSElliott Hughes ), 95*e1fe3e4aSElliott Hughes ("i.locl", 269, None, "BASE", None), 96*e1fe3e4aSElliott Hughes ) 97*e1fe3e4aSElliott Hughes 98*e1fe3e4aSElliott Hughes def test_def_glyph_base_2_components(self): 99*e1fe3e4aSElliott Hughes [def_glyph] = self.parse( 100*e1fe3e4aSElliott Hughes 'DEF_GLYPH "glyphBase" ID 320 TYPE BASE COMPONENTS 2 END_GLYPH' 101*e1fe3e4aSElliott Hughes ).statements 102*e1fe3e4aSElliott Hughes self.assertEqual( 103*e1fe3e4aSElliott Hughes ( 104*e1fe3e4aSElliott Hughes def_glyph.name, 105*e1fe3e4aSElliott Hughes def_glyph.id, 106*e1fe3e4aSElliott Hughes def_glyph.unicode, 107*e1fe3e4aSElliott Hughes def_glyph.type, 108*e1fe3e4aSElliott Hughes def_glyph.components, 109*e1fe3e4aSElliott Hughes ), 110*e1fe3e4aSElliott Hughes ("glyphBase", 320, None, "BASE", 2), 111*e1fe3e4aSElliott Hughes ) 112*e1fe3e4aSElliott Hughes 113*e1fe3e4aSElliott Hughes def test_def_glyph_ligature_2_components(self): 114*e1fe3e4aSElliott Hughes [def_glyph] = self.parse( 115*e1fe3e4aSElliott Hughes 'DEF_GLYPH "f_f" ID 320 TYPE LIGATURE COMPONENTS 2 END_GLYPH' 116*e1fe3e4aSElliott Hughes ).statements 117*e1fe3e4aSElliott Hughes self.assertEqual( 118*e1fe3e4aSElliott Hughes ( 119*e1fe3e4aSElliott Hughes def_glyph.name, 120*e1fe3e4aSElliott Hughes def_glyph.id, 121*e1fe3e4aSElliott Hughes def_glyph.unicode, 122*e1fe3e4aSElliott Hughes def_glyph.type, 123*e1fe3e4aSElliott Hughes def_glyph.components, 124*e1fe3e4aSElliott Hughes ), 125*e1fe3e4aSElliott Hughes ("f_f", 320, None, "LIGATURE", 2), 126*e1fe3e4aSElliott Hughes ) 127*e1fe3e4aSElliott Hughes 128*e1fe3e4aSElliott Hughes def test_def_glyph_mark(self): 129*e1fe3e4aSElliott Hughes [def_glyph] = self.parse( 130*e1fe3e4aSElliott Hughes 'DEF_GLYPH "brevecomb" ID 320 TYPE MARK END_GLYPH' 131*e1fe3e4aSElliott Hughes ).statements 132*e1fe3e4aSElliott Hughes self.assertEqual( 133*e1fe3e4aSElliott Hughes ( 134*e1fe3e4aSElliott Hughes def_glyph.name, 135*e1fe3e4aSElliott Hughes def_glyph.id, 136*e1fe3e4aSElliott Hughes def_glyph.unicode, 137*e1fe3e4aSElliott Hughes def_glyph.type, 138*e1fe3e4aSElliott Hughes def_glyph.components, 139*e1fe3e4aSElliott Hughes ), 140*e1fe3e4aSElliott Hughes ("brevecomb", 320, None, "MARK", None), 141*e1fe3e4aSElliott Hughes ) 142*e1fe3e4aSElliott Hughes 143*e1fe3e4aSElliott Hughes def test_def_glyph_component(self): 144*e1fe3e4aSElliott Hughes [def_glyph] = self.parse( 145*e1fe3e4aSElliott Hughes 'DEF_GLYPH "f.f_f" ID 320 TYPE COMPONENT END_GLYPH' 146*e1fe3e4aSElliott Hughes ).statements 147*e1fe3e4aSElliott Hughes self.assertEqual( 148*e1fe3e4aSElliott Hughes ( 149*e1fe3e4aSElliott Hughes def_glyph.name, 150*e1fe3e4aSElliott Hughes def_glyph.id, 151*e1fe3e4aSElliott Hughes def_glyph.unicode, 152*e1fe3e4aSElliott Hughes def_glyph.type, 153*e1fe3e4aSElliott Hughes def_glyph.components, 154*e1fe3e4aSElliott Hughes ), 155*e1fe3e4aSElliott Hughes ("f.f_f", 320, None, "COMPONENT", None), 156*e1fe3e4aSElliott Hughes ) 157*e1fe3e4aSElliott Hughes 158*e1fe3e4aSElliott Hughes def test_def_glyph_no_type(self): 159*e1fe3e4aSElliott Hughes [def_glyph] = self.parse('DEF_GLYPH "glyph20" ID 20 END_GLYPH').statements 160*e1fe3e4aSElliott Hughes self.assertEqual( 161*e1fe3e4aSElliott Hughes ( 162*e1fe3e4aSElliott Hughes def_glyph.name, 163*e1fe3e4aSElliott Hughes def_glyph.id, 164*e1fe3e4aSElliott Hughes def_glyph.unicode, 165*e1fe3e4aSElliott Hughes def_glyph.type, 166*e1fe3e4aSElliott Hughes def_glyph.components, 167*e1fe3e4aSElliott Hughes ), 168*e1fe3e4aSElliott Hughes ("glyph20", 20, None, None, None), 169*e1fe3e4aSElliott Hughes ) 170*e1fe3e4aSElliott Hughes 171*e1fe3e4aSElliott Hughes def test_def_glyph_case_sensitive(self): 172*e1fe3e4aSElliott Hughes def_glyphs = self.parse( 173*e1fe3e4aSElliott Hughes 'DEF_GLYPH "A" ID 3 UNICODE 65 TYPE BASE END_GLYPH\n' 174*e1fe3e4aSElliott Hughes 'DEF_GLYPH "a" ID 4 UNICODE 97 TYPE BASE END_GLYPH' 175*e1fe3e4aSElliott Hughes ).statements 176*e1fe3e4aSElliott Hughes self.assertEqual( 177*e1fe3e4aSElliott Hughes ( 178*e1fe3e4aSElliott Hughes def_glyphs[0].name, 179*e1fe3e4aSElliott Hughes def_glyphs[0].id, 180*e1fe3e4aSElliott Hughes def_glyphs[0].unicode, 181*e1fe3e4aSElliott Hughes def_glyphs[0].type, 182*e1fe3e4aSElliott Hughes def_glyphs[0].components, 183*e1fe3e4aSElliott Hughes ), 184*e1fe3e4aSElliott Hughes ("A", 3, [0x41], "BASE", None), 185*e1fe3e4aSElliott Hughes ) 186*e1fe3e4aSElliott Hughes self.assertEqual( 187*e1fe3e4aSElliott Hughes ( 188*e1fe3e4aSElliott Hughes def_glyphs[1].name, 189*e1fe3e4aSElliott Hughes def_glyphs[1].id, 190*e1fe3e4aSElliott Hughes def_glyphs[1].unicode, 191*e1fe3e4aSElliott Hughes def_glyphs[1].type, 192*e1fe3e4aSElliott Hughes def_glyphs[1].components, 193*e1fe3e4aSElliott Hughes ), 194*e1fe3e4aSElliott Hughes ("a", 4, [0x61], "BASE", None), 195*e1fe3e4aSElliott Hughes ) 196*e1fe3e4aSElliott Hughes 197*e1fe3e4aSElliott Hughes def test_def_group_glyphs(self): 198*e1fe3e4aSElliott Hughes [def_group] = self.parse( 199*e1fe3e4aSElliott Hughes 'DEF_GROUP "aaccented"\n' 200*e1fe3e4aSElliott Hughes ' ENUM GLYPH "aacute" GLYPH "abreve" GLYPH "acircumflex" ' 201*e1fe3e4aSElliott Hughes 'GLYPH "adieresis" GLYPH "ae" GLYPH "agrave" GLYPH "amacron" ' 202*e1fe3e4aSElliott Hughes 'GLYPH "aogonek" GLYPH "aring" GLYPH "atilde" END_ENUM\n' 203*e1fe3e4aSElliott Hughes "END_GROUP" 204*e1fe3e4aSElliott Hughes ).statements 205*e1fe3e4aSElliott Hughes self.assertEqual( 206*e1fe3e4aSElliott Hughes (def_group.name, def_group.enum.glyphSet()), 207*e1fe3e4aSElliott Hughes ( 208*e1fe3e4aSElliott Hughes "aaccented", 209*e1fe3e4aSElliott Hughes ( 210*e1fe3e4aSElliott Hughes "aacute", 211*e1fe3e4aSElliott Hughes "abreve", 212*e1fe3e4aSElliott Hughes "acircumflex", 213*e1fe3e4aSElliott Hughes "adieresis", 214*e1fe3e4aSElliott Hughes "ae", 215*e1fe3e4aSElliott Hughes "agrave", 216*e1fe3e4aSElliott Hughes "amacron", 217*e1fe3e4aSElliott Hughes "aogonek", 218*e1fe3e4aSElliott Hughes "aring", 219*e1fe3e4aSElliott Hughes "atilde", 220*e1fe3e4aSElliott Hughes ), 221*e1fe3e4aSElliott Hughes ), 222*e1fe3e4aSElliott Hughes ) 223*e1fe3e4aSElliott Hughes 224*e1fe3e4aSElliott Hughes def test_def_group_groups(self): 225*e1fe3e4aSElliott Hughes [group1, group2, test_group] = self.parse( 226*e1fe3e4aSElliott Hughes 'DEF_GROUP "Group1"\n' 227*e1fe3e4aSElliott Hughes ' ENUM GLYPH "a" GLYPH "b" GLYPH "c" GLYPH "d" END_ENUM\n' 228*e1fe3e4aSElliott Hughes "END_GROUP\n" 229*e1fe3e4aSElliott Hughes 'DEF_GROUP "Group2"\n' 230*e1fe3e4aSElliott Hughes ' ENUM GLYPH "e" GLYPH "f" GLYPH "g" GLYPH "h" END_ENUM\n' 231*e1fe3e4aSElliott Hughes "END_GROUP\n" 232*e1fe3e4aSElliott Hughes 'DEF_GROUP "TestGroup"\n' 233*e1fe3e4aSElliott Hughes ' ENUM GROUP "Group1" GROUP "Group2" END_ENUM\n' 234*e1fe3e4aSElliott Hughes "END_GROUP" 235*e1fe3e4aSElliott Hughes ).statements 236*e1fe3e4aSElliott Hughes groups = [g.group for g in test_group.enum.enum] 237*e1fe3e4aSElliott Hughes self.assertEqual((test_group.name, groups), ("TestGroup", ["Group1", "Group2"])) 238*e1fe3e4aSElliott Hughes 239*e1fe3e4aSElliott Hughes def test_def_group_groups_not_yet_defined(self): 240*e1fe3e4aSElliott Hughes [group1, test_group1, test_group2, test_group3, group2] = self.parse( 241*e1fe3e4aSElliott Hughes 'DEF_GROUP "Group1"\n' 242*e1fe3e4aSElliott Hughes ' ENUM GLYPH "a" GLYPH "b" GLYPH "c" GLYPH "d" END_ENUM\n' 243*e1fe3e4aSElliott Hughes "END_GROUP\n" 244*e1fe3e4aSElliott Hughes 'DEF_GROUP "TestGroup1"\n' 245*e1fe3e4aSElliott Hughes ' ENUM GROUP "Group1" GROUP "Group2" END_ENUM\n' 246*e1fe3e4aSElliott Hughes "END_GROUP\n" 247*e1fe3e4aSElliott Hughes 'DEF_GROUP "TestGroup2"\n' 248*e1fe3e4aSElliott Hughes ' ENUM GROUP "Group2" END_ENUM\n' 249*e1fe3e4aSElliott Hughes "END_GROUP\n" 250*e1fe3e4aSElliott Hughes 'DEF_GROUP "TestGroup3"\n' 251*e1fe3e4aSElliott Hughes ' ENUM GROUP "Group2" GROUP "Group1" END_ENUM\n' 252*e1fe3e4aSElliott Hughes "END_GROUP\n" 253*e1fe3e4aSElliott Hughes 'DEF_GROUP "Group2"\n' 254*e1fe3e4aSElliott Hughes ' ENUM GLYPH "e" GLYPH "f" GLYPH "g" GLYPH "h" END_ENUM\n' 255*e1fe3e4aSElliott Hughes "END_GROUP" 256*e1fe3e4aSElliott Hughes ).statements 257*e1fe3e4aSElliott Hughes groups = [g.group for g in test_group1.enum.enum] 258*e1fe3e4aSElliott Hughes self.assertEqual( 259*e1fe3e4aSElliott Hughes (test_group1.name, groups), ("TestGroup1", ["Group1", "Group2"]) 260*e1fe3e4aSElliott Hughes ) 261*e1fe3e4aSElliott Hughes groups = [g.group for g in test_group2.enum.enum] 262*e1fe3e4aSElliott Hughes self.assertEqual((test_group2.name, groups), ("TestGroup2", ["Group2"])) 263*e1fe3e4aSElliott Hughes groups = [g.group for g in test_group3.enum.enum] 264*e1fe3e4aSElliott Hughes self.assertEqual( 265*e1fe3e4aSElliott Hughes (test_group3.name, groups), ("TestGroup3", ["Group2", "Group1"]) 266*e1fe3e4aSElliott Hughes ) 267*e1fe3e4aSElliott Hughes 268*e1fe3e4aSElliott Hughes # def test_def_group_groups_undefined(self): 269*e1fe3e4aSElliott Hughes # with self.assertRaisesRegex( 270*e1fe3e4aSElliott Hughes # VoltLibError, 271*e1fe3e4aSElliott Hughes # r'Group "Group2" is used but undefined.'): 272*e1fe3e4aSElliott Hughes # [group1, test_group, group2] = self.parse( 273*e1fe3e4aSElliott Hughes # 'DEF_GROUP "Group1"\n' 274*e1fe3e4aSElliott Hughes # 'ENUM GLYPH "a" GLYPH "b" GLYPH "c" GLYPH "d" END_ENUM\n' 275*e1fe3e4aSElliott Hughes # 'END_GROUP\n' 276*e1fe3e4aSElliott Hughes # 'DEF_GROUP "TestGroup"\n' 277*e1fe3e4aSElliott Hughes # 'ENUM GROUP "Group1" GROUP "Group2" END_ENUM\n' 278*e1fe3e4aSElliott Hughes # 'END_GROUP\n' 279*e1fe3e4aSElliott Hughes # ).statements 280*e1fe3e4aSElliott Hughes 281*e1fe3e4aSElliott Hughes def test_def_group_glyphs_and_group(self): 282*e1fe3e4aSElliott Hughes [def_group1, def_group2] = self.parse( 283*e1fe3e4aSElliott Hughes 'DEF_GROUP "aaccented"\n' 284*e1fe3e4aSElliott Hughes ' ENUM GLYPH "aacute" GLYPH "abreve" GLYPH "acircumflex" ' 285*e1fe3e4aSElliott Hughes 'GLYPH "adieresis" GLYPH "ae" GLYPH "agrave" GLYPH "amacron" ' 286*e1fe3e4aSElliott Hughes 'GLYPH "aogonek" GLYPH "aring" GLYPH "atilde" END_ENUM\n' 287*e1fe3e4aSElliott Hughes "END_GROUP\n" 288*e1fe3e4aSElliott Hughes 'DEF_GROUP "KERN_lc_a_2ND"\n' 289*e1fe3e4aSElliott Hughes ' ENUM GLYPH "a" GROUP "aaccented" END_ENUM\n' 290*e1fe3e4aSElliott Hughes "END_GROUP" 291*e1fe3e4aSElliott Hughes ).statements 292*e1fe3e4aSElliott Hughes items = def_group2.enum.enum 293*e1fe3e4aSElliott Hughes self.assertEqual( 294*e1fe3e4aSElliott Hughes (def_group2.name, items[0].glyphSet(), items[1].group), 295*e1fe3e4aSElliott Hughes ("KERN_lc_a_2ND", ("a",), "aaccented"), 296*e1fe3e4aSElliott Hughes ) 297*e1fe3e4aSElliott Hughes 298*e1fe3e4aSElliott Hughes def test_def_group_range(self): 299*e1fe3e4aSElliott Hughes def_group = self.parse( 300*e1fe3e4aSElliott Hughes 'DEF_GLYPH "a" ID 163 UNICODE 97 TYPE BASE END_GLYPH\n' 301*e1fe3e4aSElliott Hughes 'DEF_GLYPH "agrave" ID 194 UNICODE 224 TYPE BASE END_GLYPH\n' 302*e1fe3e4aSElliott Hughes 'DEF_GLYPH "aacute" ID 195 UNICODE 225 TYPE BASE END_GLYPH\n' 303*e1fe3e4aSElliott Hughes 'DEF_GLYPH "acircumflex" ID 196 UNICODE 226 TYPE BASE END_GLYPH\n' 304*e1fe3e4aSElliott Hughes 'DEF_GLYPH "atilde" ID 197 UNICODE 227 TYPE BASE END_GLYPH\n' 305*e1fe3e4aSElliott Hughes 'DEF_GLYPH "c" ID 165 UNICODE 99 TYPE BASE END_GLYPH\n' 306*e1fe3e4aSElliott Hughes 'DEF_GLYPH "ccaron" ID 209 UNICODE 269 TYPE BASE END_GLYPH\n' 307*e1fe3e4aSElliott Hughes 'DEF_GLYPH "ccedilla" ID 210 UNICODE 231 TYPE BASE END_GLYPH\n' 308*e1fe3e4aSElliott Hughes 'DEF_GLYPH "cdotaccent" ID 210 UNICODE 267 TYPE BASE END_GLYPH\n' 309*e1fe3e4aSElliott Hughes 'DEF_GROUP "KERN_lc_a_2ND"\n' 310*e1fe3e4aSElliott Hughes ' ENUM RANGE "a" TO "atilde" GLYPH "b" RANGE "c" TO "cdotaccent" ' 311*e1fe3e4aSElliott Hughes "END_ENUM\n" 312*e1fe3e4aSElliott Hughes "END_GROUP" 313*e1fe3e4aSElliott Hughes ).statements[-1] 314*e1fe3e4aSElliott Hughes self.assertEqual( 315*e1fe3e4aSElliott Hughes (def_group.name, def_group.enum.glyphSet()), 316*e1fe3e4aSElliott Hughes ( 317*e1fe3e4aSElliott Hughes "KERN_lc_a_2ND", 318*e1fe3e4aSElliott Hughes ( 319*e1fe3e4aSElliott Hughes "a", 320*e1fe3e4aSElliott Hughes "agrave", 321*e1fe3e4aSElliott Hughes "aacute", 322*e1fe3e4aSElliott Hughes "acircumflex", 323*e1fe3e4aSElliott Hughes "atilde", 324*e1fe3e4aSElliott Hughes "b", 325*e1fe3e4aSElliott Hughes "c", 326*e1fe3e4aSElliott Hughes "ccaron", 327*e1fe3e4aSElliott Hughes "ccedilla", 328*e1fe3e4aSElliott Hughes "cdotaccent", 329*e1fe3e4aSElliott Hughes ), 330*e1fe3e4aSElliott Hughes ), 331*e1fe3e4aSElliott Hughes ) 332*e1fe3e4aSElliott Hughes 333*e1fe3e4aSElliott Hughes def test_group_duplicate(self): 334*e1fe3e4aSElliott Hughes self.assertRaisesRegex( 335*e1fe3e4aSElliott Hughes VoltLibError, 336*e1fe3e4aSElliott Hughes 'Glyph group "dupe" already defined, ' "group names are case insensitive", 337*e1fe3e4aSElliott Hughes self.parse, 338*e1fe3e4aSElliott Hughes 'DEF_GROUP "dupe"\n' 339*e1fe3e4aSElliott Hughes 'ENUM GLYPH "a" GLYPH "b" END_ENUM\n' 340*e1fe3e4aSElliott Hughes "END_GROUP\n" 341*e1fe3e4aSElliott Hughes 'DEF_GROUP "dupe"\n' 342*e1fe3e4aSElliott Hughes 'ENUM GLYPH "x" END_ENUM\n' 343*e1fe3e4aSElliott Hughes "END_GROUP", 344*e1fe3e4aSElliott Hughes ) 345*e1fe3e4aSElliott Hughes 346*e1fe3e4aSElliott Hughes def test_group_duplicate_case_insensitive(self): 347*e1fe3e4aSElliott Hughes self.assertRaisesRegex( 348*e1fe3e4aSElliott Hughes VoltLibError, 349*e1fe3e4aSElliott Hughes 'Glyph group "Dupe" already defined, ' "group names are case insensitive", 350*e1fe3e4aSElliott Hughes self.parse, 351*e1fe3e4aSElliott Hughes 'DEF_GROUP "dupe"\n' 352*e1fe3e4aSElliott Hughes 'ENUM GLYPH "a" GLYPH "b" END_ENUM\n' 353*e1fe3e4aSElliott Hughes "END_GROUP\n" 354*e1fe3e4aSElliott Hughes 'DEF_GROUP "Dupe"\n' 355*e1fe3e4aSElliott Hughes 'ENUM GLYPH "x" END_ENUM\n' 356*e1fe3e4aSElliott Hughes "END_GROUP", 357*e1fe3e4aSElliott Hughes ) 358*e1fe3e4aSElliott Hughes 359*e1fe3e4aSElliott Hughes def test_script_without_langsys(self): 360*e1fe3e4aSElliott Hughes [script] = self.parse( 361*e1fe3e4aSElliott Hughes 'DEF_SCRIPT NAME "Latin" TAG "latn"\n\n' "END_SCRIPT" 362*e1fe3e4aSElliott Hughes ).statements 363*e1fe3e4aSElliott Hughes self.assertEqual((script.name, script.tag, script.langs), ("Latin", "latn", [])) 364*e1fe3e4aSElliott Hughes 365*e1fe3e4aSElliott Hughes def test_langsys_normal(self): 366*e1fe3e4aSElliott Hughes [def_script] = self.parse( 367*e1fe3e4aSElliott Hughes 'DEF_SCRIPT NAME "Latin" TAG "latn"\n\n' 368*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Romanian" TAG "ROM "\n\n' 369*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 370*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Moldavian" TAG "MOL "\n\n' 371*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 372*e1fe3e4aSElliott Hughes "END_SCRIPT" 373*e1fe3e4aSElliott Hughes ).statements 374*e1fe3e4aSElliott Hughes self.assertEqual((def_script.name, def_script.tag), ("Latin", "latn")) 375*e1fe3e4aSElliott Hughes def_lang = def_script.langs[0] 376*e1fe3e4aSElliott Hughes self.assertEqual((def_lang.name, def_lang.tag), ("Romanian", "ROM ")) 377*e1fe3e4aSElliott Hughes def_lang = def_script.langs[1] 378*e1fe3e4aSElliott Hughes self.assertEqual((def_lang.name, def_lang.tag), ("Moldavian", "MOL ")) 379*e1fe3e4aSElliott Hughes 380*e1fe3e4aSElliott Hughes def test_langsys_no_script_name(self): 381*e1fe3e4aSElliott Hughes [langsys] = self.parse( 382*e1fe3e4aSElliott Hughes 'DEF_SCRIPT TAG "latn"\n\n' 383*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Default" TAG "dflt"\n\n' 384*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 385*e1fe3e4aSElliott Hughes "END_SCRIPT" 386*e1fe3e4aSElliott Hughes ).statements 387*e1fe3e4aSElliott Hughes self.assertEqual((langsys.name, langsys.tag), (None, "latn")) 388*e1fe3e4aSElliott Hughes lang = langsys.langs[0] 389*e1fe3e4aSElliott Hughes self.assertEqual((lang.name, lang.tag), ("Default", "dflt")) 390*e1fe3e4aSElliott Hughes 391*e1fe3e4aSElliott Hughes def test_langsys_no_script_tag_fails(self): 392*e1fe3e4aSElliott Hughes with self.assertRaisesRegex(VoltLibError, r'.*Expected "TAG"'): 393*e1fe3e4aSElliott Hughes [langsys] = self.parse( 394*e1fe3e4aSElliott Hughes 'DEF_SCRIPT NAME "Latin"\n\n' 395*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Default" TAG "dflt"\n\n' 396*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 397*e1fe3e4aSElliott Hughes "END_SCRIPT" 398*e1fe3e4aSElliott Hughes ).statements 399*e1fe3e4aSElliott Hughes 400*e1fe3e4aSElliott Hughes def test_langsys_duplicate_script(self): 401*e1fe3e4aSElliott Hughes with self.assertRaisesRegex( 402*e1fe3e4aSElliott Hughes VoltLibError, 403*e1fe3e4aSElliott Hughes 'Script "DFLT" already defined, ' "script tags are case insensitive", 404*e1fe3e4aSElliott Hughes ): 405*e1fe3e4aSElliott Hughes [langsys1, langsys2] = self.parse( 406*e1fe3e4aSElliott Hughes 'DEF_SCRIPT NAME "Default" TAG "DFLT"\n\n' 407*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Default" TAG "dflt"\n\n' 408*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 409*e1fe3e4aSElliott Hughes "END_SCRIPT\n" 410*e1fe3e4aSElliott Hughes 'DEF_SCRIPT TAG "DFLT"\n\n' 411*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Default" TAG "dflt"\n\n' 412*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 413*e1fe3e4aSElliott Hughes "END_SCRIPT" 414*e1fe3e4aSElliott Hughes ).statements 415*e1fe3e4aSElliott Hughes 416*e1fe3e4aSElliott Hughes def test_langsys_duplicate_lang(self): 417*e1fe3e4aSElliott Hughes with self.assertRaisesRegex( 418*e1fe3e4aSElliott Hughes VoltLibError, 419*e1fe3e4aSElliott Hughes 'Language "dflt" already defined in script "DFLT", ' 420*e1fe3e4aSElliott Hughes "language tags are case insensitive", 421*e1fe3e4aSElliott Hughes ): 422*e1fe3e4aSElliott Hughes [langsys] = self.parse( 423*e1fe3e4aSElliott Hughes 'DEF_SCRIPT NAME "Default" TAG "DFLT"\n' 424*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Default" TAG "dflt"\n' 425*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 426*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Default" TAG "dflt"\n' 427*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 428*e1fe3e4aSElliott Hughes "END_SCRIPT" 429*e1fe3e4aSElliott Hughes ).statements 430*e1fe3e4aSElliott Hughes 431*e1fe3e4aSElliott Hughes def test_langsys_lang_in_separate_scripts(self): 432*e1fe3e4aSElliott Hughes [langsys1, langsys2] = self.parse( 433*e1fe3e4aSElliott Hughes 'DEF_SCRIPT NAME "Default" TAG "DFLT"\n\n' 434*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Default" TAG "dflt"\n\n' 435*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 436*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Default" TAG "ROM "\n\n' 437*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 438*e1fe3e4aSElliott Hughes "END_SCRIPT\n" 439*e1fe3e4aSElliott Hughes 'DEF_SCRIPT NAME "Latin" TAG "latn"\n\n' 440*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Default" TAG "dflt"\n\n' 441*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 442*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Default" TAG "ROM "\n\n' 443*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 444*e1fe3e4aSElliott Hughes "END_SCRIPT" 445*e1fe3e4aSElliott Hughes ).statements 446*e1fe3e4aSElliott Hughes self.assertEqual( 447*e1fe3e4aSElliott Hughes (langsys1.langs[0].tag, langsys1.langs[1].tag), ("dflt", "ROM ") 448*e1fe3e4aSElliott Hughes ) 449*e1fe3e4aSElliott Hughes self.assertEqual( 450*e1fe3e4aSElliott Hughes (langsys2.langs[0].tag, langsys2.langs[1].tag), ("dflt", "ROM ") 451*e1fe3e4aSElliott Hughes ) 452*e1fe3e4aSElliott Hughes 453*e1fe3e4aSElliott Hughes def test_langsys_no_lang_name(self): 454*e1fe3e4aSElliott Hughes [langsys] = self.parse( 455*e1fe3e4aSElliott Hughes 'DEF_SCRIPT NAME "Latin" TAG "latn"\n\n' 456*e1fe3e4aSElliott Hughes 'DEF_LANGSYS TAG "dflt"\n\n' 457*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 458*e1fe3e4aSElliott Hughes "END_SCRIPT" 459*e1fe3e4aSElliott Hughes ).statements 460*e1fe3e4aSElliott Hughes self.assertEqual((langsys.name, langsys.tag), ("Latin", "latn")) 461*e1fe3e4aSElliott Hughes lang = langsys.langs[0] 462*e1fe3e4aSElliott Hughes self.assertEqual((lang.name, lang.tag), (None, "dflt")) 463*e1fe3e4aSElliott Hughes 464*e1fe3e4aSElliott Hughes def test_langsys_no_langsys_tag_fails(self): 465*e1fe3e4aSElliott Hughes with self.assertRaisesRegex(VoltLibError, r'.*Expected "TAG"'): 466*e1fe3e4aSElliott Hughes [langsys] = self.parse( 467*e1fe3e4aSElliott Hughes 'DEF_SCRIPT NAME "Latin" TAG "latn"\n\n' 468*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Default"\n\n' 469*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 470*e1fe3e4aSElliott Hughes "END_SCRIPT" 471*e1fe3e4aSElliott Hughes ).statements 472*e1fe3e4aSElliott Hughes 473*e1fe3e4aSElliott Hughes def test_feature(self): 474*e1fe3e4aSElliott Hughes [def_script] = self.parse( 475*e1fe3e4aSElliott Hughes 'DEF_SCRIPT NAME "Latin" TAG "latn"\n\n' 476*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Romanian" TAG "ROM "\n\n' 477*e1fe3e4aSElliott Hughes 'DEF_FEATURE NAME "Fractions" TAG "frac"\n' 478*e1fe3e4aSElliott Hughes ' LOOKUP "fraclookup"\n' 479*e1fe3e4aSElliott Hughes "END_FEATURE\n" 480*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 481*e1fe3e4aSElliott Hughes "END_SCRIPT" 482*e1fe3e4aSElliott Hughes ).statements 483*e1fe3e4aSElliott Hughes def_feature = def_script.langs[0].features[0] 484*e1fe3e4aSElliott Hughes self.assertEqual( 485*e1fe3e4aSElliott Hughes (def_feature.name, def_feature.tag, def_feature.lookups), 486*e1fe3e4aSElliott Hughes ("Fractions", "frac", ["fraclookup"]), 487*e1fe3e4aSElliott Hughes ) 488*e1fe3e4aSElliott Hughes [def_script] = self.parse( 489*e1fe3e4aSElliott Hughes 'DEF_SCRIPT NAME "Latin" TAG "latn"\n\n' 490*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Romanian" TAG "ROM "\n\n' 491*e1fe3e4aSElliott Hughes 'DEF_FEATURE NAME "Kerning" TAG "kern"\n' 492*e1fe3e4aSElliott Hughes ' LOOKUP "kern1" LOOKUP "kern2"\n' 493*e1fe3e4aSElliott Hughes "END_FEATURE\n" 494*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 495*e1fe3e4aSElliott Hughes "END_SCRIPT" 496*e1fe3e4aSElliott Hughes ).statements 497*e1fe3e4aSElliott Hughes def_feature = def_script.langs[0].features[0] 498*e1fe3e4aSElliott Hughes self.assertEqual( 499*e1fe3e4aSElliott Hughes (def_feature.name, def_feature.tag, def_feature.lookups), 500*e1fe3e4aSElliott Hughes ("Kerning", "kern", ["kern1", "kern2"]), 501*e1fe3e4aSElliott Hughes ) 502*e1fe3e4aSElliott Hughes 503*e1fe3e4aSElliott Hughes def test_lookup_duplicate(self): 504*e1fe3e4aSElliott Hughes with self.assertRaisesRegex( 505*e1fe3e4aSElliott Hughes VoltLibError, 506*e1fe3e4aSElliott Hughes 'Lookup "dupe" already defined, ' "lookup names are case insensitive", 507*e1fe3e4aSElliott Hughes ): 508*e1fe3e4aSElliott Hughes [lookup1, lookup2] = self.parse( 509*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "dupe"\n' 510*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 511*e1fe3e4aSElliott Hughes 'SUB GLYPH "a"\n' 512*e1fe3e4aSElliott Hughes 'WITH GLYPH "a.alt"\n' 513*e1fe3e4aSElliott Hughes "END_SUB\n" 514*e1fe3e4aSElliott Hughes "END_SUBSTITUTION\n" 515*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "dupe"\n' 516*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 517*e1fe3e4aSElliott Hughes 'SUB GLYPH "b"\n' 518*e1fe3e4aSElliott Hughes 'WITH GLYPH "b.alt"\n' 519*e1fe3e4aSElliott Hughes "END_SUB\n" 520*e1fe3e4aSElliott Hughes "END_SUBSTITUTION\n" 521*e1fe3e4aSElliott Hughes ).statements 522*e1fe3e4aSElliott Hughes 523*e1fe3e4aSElliott Hughes def test_lookup_duplicate_insensitive_case(self): 524*e1fe3e4aSElliott Hughes with self.assertRaisesRegex( 525*e1fe3e4aSElliott Hughes VoltLibError, 526*e1fe3e4aSElliott Hughes 'Lookup "Dupe" already defined, ' "lookup names are case insensitive", 527*e1fe3e4aSElliott Hughes ): 528*e1fe3e4aSElliott Hughes [lookup1, lookup2] = self.parse( 529*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "dupe"\n' 530*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 531*e1fe3e4aSElliott Hughes 'SUB GLYPH "a"\n' 532*e1fe3e4aSElliott Hughes 'WITH GLYPH "a.alt"\n' 533*e1fe3e4aSElliott Hughes "END_SUB\n" 534*e1fe3e4aSElliott Hughes "END_SUBSTITUTION\n" 535*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "Dupe"\n' 536*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 537*e1fe3e4aSElliott Hughes 'SUB GLYPH "b"\n' 538*e1fe3e4aSElliott Hughes 'WITH GLYPH "b.alt"\n' 539*e1fe3e4aSElliott Hughes "END_SUB\n" 540*e1fe3e4aSElliott Hughes "END_SUBSTITUTION\n" 541*e1fe3e4aSElliott Hughes ).statements 542*e1fe3e4aSElliott Hughes 543*e1fe3e4aSElliott Hughes def test_lookup_name_starts_with_letter(self): 544*e1fe3e4aSElliott Hughes with self.assertRaisesRegex( 545*e1fe3e4aSElliott Hughes VoltLibError, r'Lookup name "\\lookupname" must start with a letter' 546*e1fe3e4aSElliott Hughes ): 547*e1fe3e4aSElliott Hughes [lookup] = self.parse( 548*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "\\lookupname"\n' 549*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 550*e1fe3e4aSElliott Hughes 'SUB GLYPH "a"\n' 551*e1fe3e4aSElliott Hughes 'WITH GLYPH "a.alt"\n' 552*e1fe3e4aSElliott Hughes "END_SUB\n" 553*e1fe3e4aSElliott Hughes "END_SUBSTITUTION\n" 554*e1fe3e4aSElliott Hughes ).statements 555*e1fe3e4aSElliott Hughes 556*e1fe3e4aSElliott Hughes def test_lookup_comments(self): 557*e1fe3e4aSElliott Hughes [lookup] = self.parse( 558*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "test" PROCESS_BASE PROCESS_MARKS ALL DIRECTION LTR\n' 559*e1fe3e4aSElliott Hughes 'COMMENTS "Hello\\nWorld"\n' 560*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 561*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 562*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 563*e1fe3e4aSElliott Hughes 'SUB GLYPH "a"\n' 564*e1fe3e4aSElliott Hughes 'WITH GLYPH "b"\n' 565*e1fe3e4aSElliott Hughes "END_SUB\n" 566*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 567*e1fe3e4aSElliott Hughes ).statements 568*e1fe3e4aSElliott Hughes self.assertEqual(lookup.name, "test") 569*e1fe3e4aSElliott Hughes self.assertEqual(lookup.comments, "Hello\nWorld") 570*e1fe3e4aSElliott Hughes 571*e1fe3e4aSElliott Hughes def test_substitution_empty(self): 572*e1fe3e4aSElliott Hughes with self.assertRaisesRegex(VoltLibError, r"Expected SUB"): 573*e1fe3e4aSElliott Hughes [lookup] = self.parse( 574*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "empty_substitution" PROCESS_BASE PROCESS_MARKS ' 575*e1fe3e4aSElliott Hughes "ALL DIRECTION LTR\n" 576*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 577*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 578*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 579*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 580*e1fe3e4aSElliott Hughes ).statements 581*e1fe3e4aSElliott Hughes 582*e1fe3e4aSElliott Hughes def test_substitution_invalid_many_to_many(self): 583*e1fe3e4aSElliott Hughes with self.assertRaisesRegex(VoltLibError, r"Invalid substitution type"): 584*e1fe3e4aSElliott Hughes [lookup] = self.parse( 585*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "invalid_substitution" PROCESS_BASE PROCESS_MARKS ' 586*e1fe3e4aSElliott Hughes "ALL DIRECTION LTR\n" 587*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 588*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 589*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 590*e1fe3e4aSElliott Hughes 'SUB GLYPH "f" GLYPH "i"\n' 591*e1fe3e4aSElliott Hughes 'WITH GLYPH "f.alt" GLYPH "i.alt"\n' 592*e1fe3e4aSElliott Hughes "END_SUB\n" 593*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 594*e1fe3e4aSElliott Hughes ).statements 595*e1fe3e4aSElliott Hughes 596*e1fe3e4aSElliott Hughes def test_substitution_invalid_reverse_chaining_single(self): 597*e1fe3e4aSElliott Hughes with self.assertRaisesRegex(VoltLibError, r"Invalid substitution type"): 598*e1fe3e4aSElliott Hughes [lookup] = self.parse( 599*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "invalid_substitution" PROCESS_BASE PROCESS_MARKS ' 600*e1fe3e4aSElliott Hughes "ALL DIRECTION LTR REVERSAL\n" 601*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 602*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 603*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 604*e1fe3e4aSElliott Hughes 'SUB GLYPH "f" GLYPH "i"\n' 605*e1fe3e4aSElliott Hughes 'WITH GLYPH "f_i"\n' 606*e1fe3e4aSElliott Hughes "END_SUB\n" 607*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 608*e1fe3e4aSElliott Hughes ).statements 609*e1fe3e4aSElliott Hughes 610*e1fe3e4aSElliott Hughes def test_substitution_invalid_mixed(self): 611*e1fe3e4aSElliott Hughes with self.assertRaisesRegex(VoltLibError, r"Invalid substitution type"): 612*e1fe3e4aSElliott Hughes [lookup] = self.parse( 613*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "invalid_substitution" PROCESS_BASE PROCESS_MARKS ' 614*e1fe3e4aSElliott Hughes "ALL DIRECTION LTR\n" 615*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 616*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 617*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 618*e1fe3e4aSElliott Hughes 'SUB GLYPH "fi"\n' 619*e1fe3e4aSElliott Hughes 'WITH GLYPH "f" GLYPH "i"\n' 620*e1fe3e4aSElliott Hughes "END_SUB\n" 621*e1fe3e4aSElliott Hughes 'SUB GLYPH "f" GLYPH "l"\n' 622*e1fe3e4aSElliott Hughes 'WITH GLYPH "f_l"\n' 623*e1fe3e4aSElliott Hughes "END_SUB\n" 624*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 625*e1fe3e4aSElliott Hughes ).statements 626*e1fe3e4aSElliott Hughes 627*e1fe3e4aSElliott Hughes def test_substitution_single(self): 628*e1fe3e4aSElliott Hughes [lookup] = self.parse( 629*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "smcp" PROCESS_BASE PROCESS_MARKS ALL ' 630*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 631*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 632*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 633*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 634*e1fe3e4aSElliott Hughes 'SUB GLYPH "a"\n' 635*e1fe3e4aSElliott Hughes 'WITH GLYPH "a.sc"\n' 636*e1fe3e4aSElliott Hughes "END_SUB\n" 637*e1fe3e4aSElliott Hughes 'SUB GLYPH "b"\n' 638*e1fe3e4aSElliott Hughes 'WITH GLYPH "b.sc"\n' 639*e1fe3e4aSElliott Hughes "END_SUB\n" 640*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 641*e1fe3e4aSElliott Hughes ).statements 642*e1fe3e4aSElliott Hughes self.assertEqual(lookup.name, "smcp") 643*e1fe3e4aSElliott Hughes self.assertSubEqual(lookup.sub, [["a"], ["b"]], [["a.sc"], ["b.sc"]]) 644*e1fe3e4aSElliott Hughes 645*e1fe3e4aSElliott Hughes def test_substitution_single_in_context(self): 646*e1fe3e4aSElliott Hughes [group, lookup] = self.parse( 647*e1fe3e4aSElliott Hughes 'DEF_GROUP "Denominators"\n' 648*e1fe3e4aSElliott Hughes ' ENUM GLYPH "one.dnom" GLYPH "two.dnom" END_ENUM\n' 649*e1fe3e4aSElliott Hughes "END_GROUP\n" 650*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "fracdnom" PROCESS_BASE PROCESS_MARKS ALL ' 651*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 652*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 653*e1fe3e4aSElliott Hughes ' LEFT ENUM GROUP "Denominators" GLYPH "fraction" END_ENUM\n' 654*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 655*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 656*e1fe3e4aSElliott Hughes 'SUB GLYPH "one"\n' 657*e1fe3e4aSElliott Hughes 'WITH GLYPH "one.dnom"\n' 658*e1fe3e4aSElliott Hughes "END_SUB\n" 659*e1fe3e4aSElliott Hughes 'SUB GLYPH "two"\n' 660*e1fe3e4aSElliott Hughes 'WITH GLYPH "two.dnom"\n' 661*e1fe3e4aSElliott Hughes "END_SUB\n" 662*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 663*e1fe3e4aSElliott Hughes ).statements 664*e1fe3e4aSElliott Hughes context = lookup.context[0] 665*e1fe3e4aSElliott Hughes 666*e1fe3e4aSElliott Hughes self.assertEqual(lookup.name, "fracdnom") 667*e1fe3e4aSElliott Hughes self.assertEqual(context.ex_or_in, "IN_CONTEXT") 668*e1fe3e4aSElliott Hughes self.assertEqual(len(context.left), 1) 669*e1fe3e4aSElliott Hughes self.assertEqual(len(context.left[0]), 1) 670*e1fe3e4aSElliott Hughes self.assertEqual(len(context.left[0][0].enum), 2) 671*e1fe3e4aSElliott Hughes self.assertEqual(context.left[0][0].enum[0].group, "Denominators") 672*e1fe3e4aSElliott Hughes self.assertEqual(context.left[0][0].enum[1].glyph, "fraction") 673*e1fe3e4aSElliott Hughes self.assertEqual(context.right, []) 674*e1fe3e4aSElliott Hughes self.assertSubEqual( 675*e1fe3e4aSElliott Hughes lookup.sub, [["one"], ["two"]], [["one.dnom"], ["two.dnom"]] 676*e1fe3e4aSElliott Hughes ) 677*e1fe3e4aSElliott Hughes 678*e1fe3e4aSElliott Hughes def test_substitution_single_in_contexts(self): 679*e1fe3e4aSElliott Hughes [group, lookup] = self.parse( 680*e1fe3e4aSElliott Hughes 'DEF_GROUP "Hebrew"\n' 681*e1fe3e4aSElliott Hughes ' ENUM GLYPH "uni05D0" GLYPH "uni05D1" END_ENUM\n' 682*e1fe3e4aSElliott Hughes "END_GROUP\n" 683*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "HebrewCurrency" PROCESS_BASE PROCESS_MARKS ALL ' 684*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 685*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 686*e1fe3e4aSElliott Hughes ' RIGHT GROUP "Hebrew"\n' 687*e1fe3e4aSElliott Hughes ' RIGHT GLYPH "one.Hebr"\n' 688*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 689*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 690*e1fe3e4aSElliott Hughes ' LEFT GROUP "Hebrew"\n' 691*e1fe3e4aSElliott Hughes ' LEFT GLYPH "one.Hebr"\n' 692*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 693*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 694*e1fe3e4aSElliott Hughes 'SUB GLYPH "dollar"\n' 695*e1fe3e4aSElliott Hughes 'WITH GLYPH "dollar.Hebr"\n' 696*e1fe3e4aSElliott Hughes "END_SUB\n" 697*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 698*e1fe3e4aSElliott Hughes ).statements 699*e1fe3e4aSElliott Hughes context1 = lookup.context[0] 700*e1fe3e4aSElliott Hughes context2 = lookup.context[1] 701*e1fe3e4aSElliott Hughes 702*e1fe3e4aSElliott Hughes self.assertEqual(lookup.name, "HebrewCurrency") 703*e1fe3e4aSElliott Hughes 704*e1fe3e4aSElliott Hughes self.assertEqual(context1.ex_or_in, "IN_CONTEXT") 705*e1fe3e4aSElliott Hughes self.assertEqual(context1.left, []) 706*e1fe3e4aSElliott Hughes self.assertEqual(len(context1.right), 2) 707*e1fe3e4aSElliott Hughes self.assertEqual(len(context1.right[0]), 1) 708*e1fe3e4aSElliott Hughes self.assertEqual(len(context1.right[1]), 1) 709*e1fe3e4aSElliott Hughes self.assertEqual(context1.right[0][0].group, "Hebrew") 710*e1fe3e4aSElliott Hughes self.assertEqual(context1.right[1][0].glyph, "one.Hebr") 711*e1fe3e4aSElliott Hughes 712*e1fe3e4aSElliott Hughes self.assertEqual(context2.ex_or_in, "IN_CONTEXT") 713*e1fe3e4aSElliott Hughes self.assertEqual(len(context2.left), 2) 714*e1fe3e4aSElliott Hughes self.assertEqual(len(context2.left[0]), 1) 715*e1fe3e4aSElliott Hughes self.assertEqual(len(context2.left[1]), 1) 716*e1fe3e4aSElliott Hughes self.assertEqual(context2.left[0][0].group, "Hebrew") 717*e1fe3e4aSElliott Hughes self.assertEqual(context2.left[1][0].glyph, "one.Hebr") 718*e1fe3e4aSElliott Hughes self.assertEqual(context2.right, []) 719*e1fe3e4aSElliott Hughes 720*e1fe3e4aSElliott Hughes def test_substitution_skip_base(self): 721*e1fe3e4aSElliott Hughes [group, lookup] = self.parse( 722*e1fe3e4aSElliott Hughes 'DEF_GROUP "SomeMarks"\n' 723*e1fe3e4aSElliott Hughes ' ENUM GLYPH "marka" GLYPH "markb" END_ENUM\n' 724*e1fe3e4aSElliott Hughes "END_GROUP\n" 725*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "SomeSub" SKIP_BASE PROCESS_MARKS ALL ' 726*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 727*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 728*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 729*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 730*e1fe3e4aSElliott Hughes 'SUB GLYPH "A"\n' 731*e1fe3e4aSElliott Hughes 'WITH GLYPH "A.c2sc"\n' 732*e1fe3e4aSElliott Hughes "END_SUB\n" 733*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 734*e1fe3e4aSElliott Hughes ).statements 735*e1fe3e4aSElliott Hughes self.assertEqual((lookup.name, lookup.process_base), ("SomeSub", False)) 736*e1fe3e4aSElliott Hughes 737*e1fe3e4aSElliott Hughes def test_substitution_process_base(self): 738*e1fe3e4aSElliott Hughes [group, lookup] = self.parse( 739*e1fe3e4aSElliott Hughes 'DEF_GROUP "SomeMarks"\n' 740*e1fe3e4aSElliott Hughes ' ENUM GLYPH "marka" GLYPH "markb" END_ENUM\n' 741*e1fe3e4aSElliott Hughes "END_GROUP\n" 742*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS ALL ' 743*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 744*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 745*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 746*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 747*e1fe3e4aSElliott Hughes 'SUB GLYPH "A"\n' 748*e1fe3e4aSElliott Hughes 'WITH GLYPH "A.c2sc"\n' 749*e1fe3e4aSElliott Hughes "END_SUB\n" 750*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 751*e1fe3e4aSElliott Hughes ).statements 752*e1fe3e4aSElliott Hughes self.assertEqual((lookup.name, lookup.process_base), ("SomeSub", True)) 753*e1fe3e4aSElliott Hughes 754*e1fe3e4aSElliott Hughes def test_substitution_process_marks(self): 755*e1fe3e4aSElliott Hughes [group, lookup] = self.parse( 756*e1fe3e4aSElliott Hughes 'DEF_GROUP "SomeMarks"\n' 757*e1fe3e4aSElliott Hughes ' ENUM GLYPH "marka" GLYPH "markb" END_ENUM\n' 758*e1fe3e4aSElliott Hughes "END_GROUP\n" 759*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS "SomeMarks"\n' 760*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 761*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 762*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 763*e1fe3e4aSElliott Hughes 'SUB GLYPH "A"\n' 764*e1fe3e4aSElliott Hughes 'WITH GLYPH "A.c2sc"\n' 765*e1fe3e4aSElliott Hughes "END_SUB\n" 766*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 767*e1fe3e4aSElliott Hughes ).statements 768*e1fe3e4aSElliott Hughes self.assertEqual((lookup.name, lookup.process_marks), ("SomeSub", "SomeMarks")) 769*e1fe3e4aSElliott Hughes 770*e1fe3e4aSElliott Hughes def test_substitution_process_marks_all(self): 771*e1fe3e4aSElliott Hughes [lookup] = self.parse( 772*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS ALL\n' 773*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 774*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 775*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 776*e1fe3e4aSElliott Hughes 'SUB GLYPH "A"\n' 777*e1fe3e4aSElliott Hughes 'WITH GLYPH "A.c2sc"\n' 778*e1fe3e4aSElliott Hughes "END_SUB\n" 779*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 780*e1fe3e4aSElliott Hughes ).statements 781*e1fe3e4aSElliott Hughes self.assertEqual((lookup.name, lookup.process_marks), ("SomeSub", True)) 782*e1fe3e4aSElliott Hughes 783*e1fe3e4aSElliott Hughes def test_substitution_process_marks_none(self): 784*e1fe3e4aSElliott Hughes [lookup] = self.parse_( 785*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS "NONE"\n' 786*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 787*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 788*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 789*e1fe3e4aSElliott Hughes 'SUB GLYPH "A"\n' 790*e1fe3e4aSElliott Hughes 'WITH GLYPH "A.c2sc"\n' 791*e1fe3e4aSElliott Hughes "END_SUB\n" 792*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 793*e1fe3e4aSElliott Hughes ).statements 794*e1fe3e4aSElliott Hughes self.assertEqual((lookup.name, lookup.process_marks), ("SomeSub", False)) 795*e1fe3e4aSElliott Hughes 796*e1fe3e4aSElliott Hughes def test_substitution_process_marks_bad(self): 797*e1fe3e4aSElliott Hughes with self.assertRaisesRegex( 798*e1fe3e4aSElliott Hughes VoltLibError, "Expected ALL, NONE, MARK_GLYPH_SET or an ID" 799*e1fe3e4aSElliott Hughes ): 800*e1fe3e4aSElliott Hughes self.parse( 801*e1fe3e4aSElliott Hughes 'DEF_GROUP "SomeMarks" ENUM GLYPH "marka" GLYPH "markb" ' 802*e1fe3e4aSElliott Hughes "END_ENUM END_GROUP\n" 803*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS SomeMarks ' 804*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 805*e1fe3e4aSElliott Hughes 'SUB GLYPH "A" WITH GLYPH "A.c2sc"\n' 806*e1fe3e4aSElliott Hughes "END_SUB\n" 807*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 808*e1fe3e4aSElliott Hughes ) 809*e1fe3e4aSElliott Hughes 810*e1fe3e4aSElliott Hughes def test_substitution_skip_marks(self): 811*e1fe3e4aSElliott Hughes [group, lookup] = self.parse( 812*e1fe3e4aSElliott Hughes 'DEF_GROUP "SomeMarks"\n' 813*e1fe3e4aSElliott Hughes ' ENUM GLYPH "marka" GLYPH "markb" END_ENUM\n' 814*e1fe3e4aSElliott Hughes "END_GROUP\n" 815*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "SomeSub" PROCESS_BASE SKIP_MARKS DIRECTION LTR\n' 816*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 817*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 818*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 819*e1fe3e4aSElliott Hughes 'SUB GLYPH "A"\n' 820*e1fe3e4aSElliott Hughes 'WITH GLYPH "A.c2sc"\n' 821*e1fe3e4aSElliott Hughes "END_SUB\n" 822*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 823*e1fe3e4aSElliott Hughes ).statements 824*e1fe3e4aSElliott Hughes self.assertEqual((lookup.name, lookup.process_marks), ("SomeSub", False)) 825*e1fe3e4aSElliott Hughes 826*e1fe3e4aSElliott Hughes def test_substitution_mark_attachment(self): 827*e1fe3e4aSElliott Hughes [group, lookup] = self.parse( 828*e1fe3e4aSElliott Hughes 'DEF_GROUP "SomeMarks"\n' 829*e1fe3e4aSElliott Hughes ' ENUM GLYPH "acutecmb" GLYPH "gravecmb" END_ENUM\n' 830*e1fe3e4aSElliott Hughes "END_GROUP\n" 831*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "SomeSub" PROCESS_BASE ' 832*e1fe3e4aSElliott Hughes 'PROCESS_MARKS "SomeMarks" DIRECTION RTL\n' 833*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 834*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 835*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 836*e1fe3e4aSElliott Hughes 'SUB GLYPH "A"\n' 837*e1fe3e4aSElliott Hughes 'WITH GLYPH "A.c2sc"\n' 838*e1fe3e4aSElliott Hughes "END_SUB\n" 839*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 840*e1fe3e4aSElliott Hughes ).statements 841*e1fe3e4aSElliott Hughes self.assertEqual((lookup.name, lookup.process_marks), ("SomeSub", "SomeMarks")) 842*e1fe3e4aSElliott Hughes 843*e1fe3e4aSElliott Hughes def test_substitution_mark_glyph_set(self): 844*e1fe3e4aSElliott Hughes [group, lookup] = self.parse( 845*e1fe3e4aSElliott Hughes 'DEF_GROUP "SomeMarks"\n' 846*e1fe3e4aSElliott Hughes ' ENUM GLYPH "acutecmb" GLYPH "gravecmb" END_ENUM\n' 847*e1fe3e4aSElliott Hughes "END_GROUP\n" 848*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "SomeSub" PROCESS_BASE ' 849*e1fe3e4aSElliott Hughes 'PROCESS_MARKS MARK_GLYPH_SET "SomeMarks" DIRECTION RTL\n' 850*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 851*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 852*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 853*e1fe3e4aSElliott Hughes 'SUB GLYPH "A"\n' 854*e1fe3e4aSElliott Hughes 'WITH GLYPH "A.c2sc"\n' 855*e1fe3e4aSElliott Hughes "END_SUB\n" 856*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 857*e1fe3e4aSElliott Hughes ).statements 858*e1fe3e4aSElliott Hughes self.assertEqual((lookup.name, lookup.mark_glyph_set), ("SomeSub", "SomeMarks")) 859*e1fe3e4aSElliott Hughes 860*e1fe3e4aSElliott Hughes def test_substitution_process_all_marks(self): 861*e1fe3e4aSElliott Hughes [group, lookup] = self.parse( 862*e1fe3e4aSElliott Hughes 'DEF_GROUP "SomeMarks"\n' 863*e1fe3e4aSElliott Hughes ' ENUM GLYPH "acutecmb" GLYPH "gravecmb" END_ENUM\n' 864*e1fe3e4aSElliott Hughes "END_GROUP\n" 865*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS ALL ' 866*e1fe3e4aSElliott Hughes "DIRECTION RTL\n" 867*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 868*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 869*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 870*e1fe3e4aSElliott Hughes 'SUB GLYPH "A"\n' 871*e1fe3e4aSElliott Hughes 'WITH GLYPH "A.c2sc"\n' 872*e1fe3e4aSElliott Hughes "END_SUB\n" 873*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 874*e1fe3e4aSElliott Hughes ).statements 875*e1fe3e4aSElliott Hughes self.assertEqual((lookup.name, lookup.process_marks), ("SomeSub", True)) 876*e1fe3e4aSElliott Hughes 877*e1fe3e4aSElliott Hughes def test_substitution_no_reversal(self): 878*e1fe3e4aSElliott Hughes # TODO: check right context with no reversal 879*e1fe3e4aSElliott Hughes [lookup] = self.parse( 880*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "Lookup" PROCESS_BASE PROCESS_MARKS ALL ' 881*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 882*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 883*e1fe3e4aSElliott Hughes ' RIGHT ENUM GLYPH "a" GLYPH "b" END_ENUM\n' 884*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 885*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 886*e1fe3e4aSElliott Hughes 'SUB GLYPH "a"\n' 887*e1fe3e4aSElliott Hughes 'WITH GLYPH "a.alt"\n' 888*e1fe3e4aSElliott Hughes "END_SUB\n" 889*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 890*e1fe3e4aSElliott Hughes ).statements 891*e1fe3e4aSElliott Hughes self.assertEqual((lookup.name, lookup.reversal), ("Lookup", None)) 892*e1fe3e4aSElliott Hughes 893*e1fe3e4aSElliott Hughes def test_substitution_reversal(self): 894*e1fe3e4aSElliott Hughes lookup = self.parse( 895*e1fe3e4aSElliott Hughes 'DEF_GROUP "DFLT_Num_standardFigures"\n' 896*e1fe3e4aSElliott Hughes ' ENUM GLYPH "zero" GLYPH "one" GLYPH "two" END_ENUM\n' 897*e1fe3e4aSElliott Hughes "END_GROUP\n" 898*e1fe3e4aSElliott Hughes 'DEF_GROUP "DFLT_Num_numerators"\n' 899*e1fe3e4aSElliott Hughes ' ENUM GLYPH "zero.numr" GLYPH "one.numr" GLYPH "two.numr" END_ENUM\n' 900*e1fe3e4aSElliott Hughes "END_GROUP\n" 901*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "RevLookup" PROCESS_BASE PROCESS_MARKS ALL ' 902*e1fe3e4aSElliott Hughes "DIRECTION LTR REVERSAL\n" 903*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 904*e1fe3e4aSElliott Hughes ' RIGHT ENUM GLYPH "a" GLYPH "b" END_ENUM\n' 905*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 906*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 907*e1fe3e4aSElliott Hughes 'SUB GROUP "DFLT_Num_standardFigures"\n' 908*e1fe3e4aSElliott Hughes 'WITH GROUP "DFLT_Num_numerators"\n' 909*e1fe3e4aSElliott Hughes "END_SUB\n" 910*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 911*e1fe3e4aSElliott Hughes ).statements[-1] 912*e1fe3e4aSElliott Hughes self.assertEqual((lookup.name, lookup.reversal), ("RevLookup", True)) 913*e1fe3e4aSElliott Hughes 914*e1fe3e4aSElliott Hughes def test_substitution_single_to_multiple(self): 915*e1fe3e4aSElliott Hughes [lookup] = self.parse( 916*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "ccmp" PROCESS_BASE PROCESS_MARKS ALL ' 917*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 918*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 919*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 920*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 921*e1fe3e4aSElliott Hughes 'SUB GLYPH "aacute"\n' 922*e1fe3e4aSElliott Hughes 'WITH GLYPH "a" GLYPH "acutecomb"\n' 923*e1fe3e4aSElliott Hughes "END_SUB\n" 924*e1fe3e4aSElliott Hughes 'SUB GLYPH "agrave"\n' 925*e1fe3e4aSElliott Hughes 'WITH GLYPH "a" GLYPH "gravecomb"\n' 926*e1fe3e4aSElliott Hughes "END_SUB\n" 927*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 928*e1fe3e4aSElliott Hughes ).statements 929*e1fe3e4aSElliott Hughes self.assertEqual(lookup.name, "ccmp") 930*e1fe3e4aSElliott Hughes self.assertSubEqual( 931*e1fe3e4aSElliott Hughes lookup.sub, 932*e1fe3e4aSElliott Hughes [["aacute"], ["agrave"]], 933*e1fe3e4aSElliott Hughes [["a", "acutecomb"], ["a", "gravecomb"]], 934*e1fe3e4aSElliott Hughes ) 935*e1fe3e4aSElliott Hughes 936*e1fe3e4aSElliott Hughes def test_substitution_multiple_to_single(self): 937*e1fe3e4aSElliott Hughes [lookup] = self.parse( 938*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "liga" PROCESS_BASE PROCESS_MARKS ALL ' 939*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 940*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 941*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 942*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 943*e1fe3e4aSElliott Hughes 'SUB GLYPH "f" GLYPH "i"\n' 944*e1fe3e4aSElliott Hughes 'WITH GLYPH "f_i"\n' 945*e1fe3e4aSElliott Hughes "END_SUB\n" 946*e1fe3e4aSElliott Hughes 'SUB GLYPH "f" GLYPH "t"\n' 947*e1fe3e4aSElliott Hughes 'WITH GLYPH "f_t"\n' 948*e1fe3e4aSElliott Hughes "END_SUB\n" 949*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 950*e1fe3e4aSElliott Hughes ).statements 951*e1fe3e4aSElliott Hughes self.assertEqual(lookup.name, "liga") 952*e1fe3e4aSElliott Hughes self.assertSubEqual(lookup.sub, [["f", "i"], ["f", "t"]], [["f_i"], ["f_t"]]) 953*e1fe3e4aSElliott Hughes 954*e1fe3e4aSElliott Hughes def test_substitution_reverse_chaining_single(self): 955*e1fe3e4aSElliott Hughes [lookup] = self.parse( 956*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "numr" PROCESS_BASE PROCESS_MARKS ALL ' 957*e1fe3e4aSElliott Hughes "DIRECTION LTR REVERSAL\n" 958*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 959*e1fe3e4aSElliott Hughes " RIGHT ENUM " 960*e1fe3e4aSElliott Hughes 'GLYPH "fraction" ' 961*e1fe3e4aSElliott Hughes 'RANGE "zero.numr" TO "nine.numr" ' 962*e1fe3e4aSElliott Hughes "END_ENUM\n" 963*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 964*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 965*e1fe3e4aSElliott Hughes 'SUB RANGE "zero" TO "nine"\n' 966*e1fe3e4aSElliott Hughes 'WITH RANGE "zero.numr" TO "nine.numr"\n' 967*e1fe3e4aSElliott Hughes "END_SUB\n" 968*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 969*e1fe3e4aSElliott Hughes ).statements 970*e1fe3e4aSElliott Hughes 971*e1fe3e4aSElliott Hughes mapping = lookup.sub.mapping 972*e1fe3e4aSElliott Hughes glyphs = [[(r.start, r.end) for r in v] for v in mapping.keys()] 973*e1fe3e4aSElliott Hughes replacement = [[(r.start, r.end) for r in v] for v in mapping.values()] 974*e1fe3e4aSElliott Hughes 975*e1fe3e4aSElliott Hughes self.assertEqual(lookup.name, "numr") 976*e1fe3e4aSElliott Hughes self.assertEqual(glyphs, [[("zero", "nine")]]) 977*e1fe3e4aSElliott Hughes self.assertEqual(replacement, [[("zero.numr", "nine.numr")]]) 978*e1fe3e4aSElliott Hughes 979*e1fe3e4aSElliott Hughes self.assertEqual(len(lookup.context[0].right), 1) 980*e1fe3e4aSElliott Hughes self.assertEqual(len(lookup.context[0].right[0]), 1) 981*e1fe3e4aSElliott Hughes enum = lookup.context[0].right[0][0] 982*e1fe3e4aSElliott Hughes self.assertEqual(len(enum.enum), 2) 983*e1fe3e4aSElliott Hughes self.assertEqual(enum.enum[0].glyph, "fraction") 984*e1fe3e4aSElliott Hughes self.assertEqual( 985*e1fe3e4aSElliott Hughes (enum.enum[1].start, enum.enum[1].end), ("zero.numr", "nine.numr") 986*e1fe3e4aSElliott Hughes ) 987*e1fe3e4aSElliott Hughes 988*e1fe3e4aSElliott Hughes # GPOS 989*e1fe3e4aSElliott Hughes # ATTACH_CURSIVE 990*e1fe3e4aSElliott Hughes # ATTACH 991*e1fe3e4aSElliott Hughes # ADJUST_PAIR 992*e1fe3e4aSElliott Hughes # ADJUST_SINGLE 993*e1fe3e4aSElliott Hughes def test_position_empty(self): 994*e1fe3e4aSElliott Hughes with self.assertRaisesRegex( 995*e1fe3e4aSElliott Hughes VoltLibError, "Expected ATTACH, ATTACH_CURSIVE, ADJUST_PAIR, ADJUST_SINGLE" 996*e1fe3e4aSElliott Hughes ): 997*e1fe3e4aSElliott Hughes [lookup] = self.parse( 998*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "empty_position" PROCESS_BASE PROCESS_MARKS ALL ' 999*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 1000*e1fe3e4aSElliott Hughes "EXCEPT_CONTEXT\n" 1001*e1fe3e4aSElliott Hughes ' LEFT GLYPH "glyph"\n' 1002*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 1003*e1fe3e4aSElliott Hughes "AS_POSITION\n" 1004*e1fe3e4aSElliott Hughes "END_POSITION" 1005*e1fe3e4aSElliott Hughes ).statements 1006*e1fe3e4aSElliott Hughes 1007*e1fe3e4aSElliott Hughes def test_position_attach(self): 1008*e1fe3e4aSElliott Hughes [lookup, anchor1, anchor2, anchor3, anchor4] = self.parse( 1009*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "anchor_top" PROCESS_BASE PROCESS_MARKS ALL ' 1010*e1fe3e4aSElliott Hughes "DIRECTION RTL\n" 1011*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 1012*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 1013*e1fe3e4aSElliott Hughes "AS_POSITION\n" 1014*e1fe3e4aSElliott Hughes 'ATTACH GLYPH "a" GLYPH "e"\n' 1015*e1fe3e4aSElliott Hughes 'TO GLYPH "acutecomb" AT ANCHOR "top" ' 1016*e1fe3e4aSElliott Hughes 'GLYPH "gravecomb" AT ANCHOR "top"\n' 1017*e1fe3e4aSElliott Hughes "END_ATTACH\n" 1018*e1fe3e4aSElliott Hughes "END_POSITION\n" 1019*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "MARK_top" ON 120 GLYPH acutecomb COMPONENT 1 ' 1020*e1fe3e4aSElliott Hughes "AT POS DX 0 DY 450 END_POS END_ANCHOR\n" 1021*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "MARK_top" ON 121 GLYPH gravecomb COMPONENT 1 ' 1022*e1fe3e4aSElliott Hughes "AT POS DX 0 DY 450 END_POS END_ANCHOR\n" 1023*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "top" ON 31 GLYPH a COMPONENT 1 ' 1024*e1fe3e4aSElliott Hughes "AT POS DX 210 DY 450 END_POS END_ANCHOR\n" 1025*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "top" ON 35 GLYPH e COMPONENT 1 ' 1026*e1fe3e4aSElliott Hughes "AT POS DX 215 DY 450 END_POS END_ANCHOR" 1027*e1fe3e4aSElliott Hughes ).statements 1028*e1fe3e4aSElliott Hughes pos = lookup.pos 1029*e1fe3e4aSElliott Hughes coverage = [g.glyph for g in pos.coverage] 1030*e1fe3e4aSElliott Hughes coverage_to = [[[g.glyph for g in e], a] for (e, a) in pos.coverage_to] 1031*e1fe3e4aSElliott Hughes self.assertEqual( 1032*e1fe3e4aSElliott Hughes (lookup.name, coverage, coverage_to), 1033*e1fe3e4aSElliott Hughes ( 1034*e1fe3e4aSElliott Hughes "anchor_top", 1035*e1fe3e4aSElliott Hughes ["a", "e"], 1036*e1fe3e4aSElliott Hughes [[["acutecomb"], "top"], [["gravecomb"], "top"]], 1037*e1fe3e4aSElliott Hughes ), 1038*e1fe3e4aSElliott Hughes ) 1039*e1fe3e4aSElliott Hughes self.assertEqual( 1040*e1fe3e4aSElliott Hughes ( 1041*e1fe3e4aSElliott Hughes anchor1.name, 1042*e1fe3e4aSElliott Hughes anchor1.gid, 1043*e1fe3e4aSElliott Hughes anchor1.glyph_name, 1044*e1fe3e4aSElliott Hughes anchor1.component, 1045*e1fe3e4aSElliott Hughes anchor1.locked, 1046*e1fe3e4aSElliott Hughes anchor1.pos, 1047*e1fe3e4aSElliott Hughes ), 1048*e1fe3e4aSElliott Hughes ("MARK_top", 120, "acutecomb", 1, False, (None, 0, 450, {}, {}, {})), 1049*e1fe3e4aSElliott Hughes ) 1050*e1fe3e4aSElliott Hughes self.assertEqual( 1051*e1fe3e4aSElliott Hughes ( 1052*e1fe3e4aSElliott Hughes anchor2.name, 1053*e1fe3e4aSElliott Hughes anchor2.gid, 1054*e1fe3e4aSElliott Hughes anchor2.glyph_name, 1055*e1fe3e4aSElliott Hughes anchor2.component, 1056*e1fe3e4aSElliott Hughes anchor2.locked, 1057*e1fe3e4aSElliott Hughes anchor2.pos, 1058*e1fe3e4aSElliott Hughes ), 1059*e1fe3e4aSElliott Hughes ("MARK_top", 121, "gravecomb", 1, False, (None, 0, 450, {}, {}, {})), 1060*e1fe3e4aSElliott Hughes ) 1061*e1fe3e4aSElliott Hughes self.assertEqual( 1062*e1fe3e4aSElliott Hughes ( 1063*e1fe3e4aSElliott Hughes anchor3.name, 1064*e1fe3e4aSElliott Hughes anchor3.gid, 1065*e1fe3e4aSElliott Hughes anchor3.glyph_name, 1066*e1fe3e4aSElliott Hughes anchor3.component, 1067*e1fe3e4aSElliott Hughes anchor3.locked, 1068*e1fe3e4aSElliott Hughes anchor3.pos, 1069*e1fe3e4aSElliott Hughes ), 1070*e1fe3e4aSElliott Hughes ("top", 31, "a", 1, False, (None, 210, 450, {}, {}, {})), 1071*e1fe3e4aSElliott Hughes ) 1072*e1fe3e4aSElliott Hughes self.assertEqual( 1073*e1fe3e4aSElliott Hughes ( 1074*e1fe3e4aSElliott Hughes anchor4.name, 1075*e1fe3e4aSElliott Hughes anchor4.gid, 1076*e1fe3e4aSElliott Hughes anchor4.glyph_name, 1077*e1fe3e4aSElliott Hughes anchor4.component, 1078*e1fe3e4aSElliott Hughes anchor4.locked, 1079*e1fe3e4aSElliott Hughes anchor4.pos, 1080*e1fe3e4aSElliott Hughes ), 1081*e1fe3e4aSElliott Hughes ("top", 35, "e", 1, False, (None, 215, 450, {}, {}, {})), 1082*e1fe3e4aSElliott Hughes ) 1083*e1fe3e4aSElliott Hughes 1084*e1fe3e4aSElliott Hughes def test_position_attach_cursive(self): 1085*e1fe3e4aSElliott Hughes [lookup] = self.parse( 1086*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "SomeLookup" PROCESS_BASE PROCESS_MARKS ALL ' 1087*e1fe3e4aSElliott Hughes "DIRECTION RTL\n" 1088*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 1089*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 1090*e1fe3e4aSElliott Hughes "AS_POSITION\n" 1091*e1fe3e4aSElliott Hughes 'ATTACH_CURSIVE\nEXIT GLYPH "a" GLYPH "b"\nENTER GLYPH "c"\n' 1092*e1fe3e4aSElliott Hughes "END_ATTACH\n" 1093*e1fe3e4aSElliott Hughes "END_POSITION" 1094*e1fe3e4aSElliott Hughes ).statements 1095*e1fe3e4aSElliott Hughes exit = [[g.glyph for g in v] for v in lookup.pos.coverages_exit] 1096*e1fe3e4aSElliott Hughes enter = [[g.glyph for g in v] for v in lookup.pos.coverages_enter] 1097*e1fe3e4aSElliott Hughes self.assertEqual( 1098*e1fe3e4aSElliott Hughes (lookup.name, exit, enter), ("SomeLookup", [["a", "b"]], [["c"]]) 1099*e1fe3e4aSElliott Hughes ) 1100*e1fe3e4aSElliott Hughes 1101*e1fe3e4aSElliott Hughes def test_position_adjust_pair(self): 1102*e1fe3e4aSElliott Hughes [lookup] = self.parse( 1103*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "kern1" PROCESS_BASE PROCESS_MARKS ALL ' 1104*e1fe3e4aSElliott Hughes "DIRECTION RTL\n" 1105*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 1106*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 1107*e1fe3e4aSElliott Hughes "AS_POSITION\n" 1108*e1fe3e4aSElliott Hughes "ADJUST_PAIR\n" 1109*e1fe3e4aSElliott Hughes ' FIRST GLYPH "A"\n' 1110*e1fe3e4aSElliott Hughes ' SECOND GLYPH "V"\n' 1111*e1fe3e4aSElliott Hughes " 1 2 BY POS ADV -30 END_POS POS END_POS\n" 1112*e1fe3e4aSElliott Hughes " 2 1 BY POS ADV -30 END_POS POS END_POS\n\n" 1113*e1fe3e4aSElliott Hughes "END_ADJUST\n" 1114*e1fe3e4aSElliott Hughes "END_POSITION" 1115*e1fe3e4aSElliott Hughes ).statements 1116*e1fe3e4aSElliott Hughes coverages_1 = [[g.glyph for g in v] for v in lookup.pos.coverages_1] 1117*e1fe3e4aSElliott Hughes coverages_2 = [[g.glyph for g in v] for v in lookup.pos.coverages_2] 1118*e1fe3e4aSElliott Hughes self.assertEqual( 1119*e1fe3e4aSElliott Hughes (lookup.name, coverages_1, coverages_2, lookup.pos.adjust_pair), 1120*e1fe3e4aSElliott Hughes ( 1121*e1fe3e4aSElliott Hughes "kern1", 1122*e1fe3e4aSElliott Hughes [["A"]], 1123*e1fe3e4aSElliott Hughes [["V"]], 1124*e1fe3e4aSElliott Hughes { 1125*e1fe3e4aSElliott Hughes (1, 2): ( 1126*e1fe3e4aSElliott Hughes (-30, None, None, {}, {}, {}), 1127*e1fe3e4aSElliott Hughes (None, None, None, {}, {}, {}), 1128*e1fe3e4aSElliott Hughes ), 1129*e1fe3e4aSElliott Hughes (2, 1): ( 1130*e1fe3e4aSElliott Hughes (-30, None, None, {}, {}, {}), 1131*e1fe3e4aSElliott Hughes (None, None, None, {}, {}, {}), 1132*e1fe3e4aSElliott Hughes ), 1133*e1fe3e4aSElliott Hughes }, 1134*e1fe3e4aSElliott Hughes ), 1135*e1fe3e4aSElliott Hughes ) 1136*e1fe3e4aSElliott Hughes 1137*e1fe3e4aSElliott Hughes def test_position_adjust_single(self): 1138*e1fe3e4aSElliott Hughes [lookup] = self.parse( 1139*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "TestLookup" PROCESS_BASE PROCESS_MARKS ALL ' 1140*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 1141*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 1142*e1fe3e4aSElliott Hughes # ' LEFT GLYPH "leftGlyph"\n' 1143*e1fe3e4aSElliott Hughes # ' RIGHT GLYPH "rightGlyph"\n' 1144*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 1145*e1fe3e4aSElliott Hughes "AS_POSITION\n" 1146*e1fe3e4aSElliott Hughes "ADJUST_SINGLE" 1147*e1fe3e4aSElliott Hughes ' GLYPH "glyph1" BY POS ADV 0 DX 123 END_POS' 1148*e1fe3e4aSElliott Hughes ' GLYPH "glyph2" BY POS ADV 0 DX 456 END_POS\n' 1149*e1fe3e4aSElliott Hughes "END_ADJUST\n" 1150*e1fe3e4aSElliott Hughes "END_POSITION" 1151*e1fe3e4aSElliott Hughes ).statements 1152*e1fe3e4aSElliott Hughes pos = lookup.pos 1153*e1fe3e4aSElliott Hughes adjust = [[[g.glyph for g in a], b] for (a, b) in pos.adjust_single] 1154*e1fe3e4aSElliott Hughes self.assertEqual( 1155*e1fe3e4aSElliott Hughes (lookup.name, adjust), 1156*e1fe3e4aSElliott Hughes ( 1157*e1fe3e4aSElliott Hughes "TestLookup", 1158*e1fe3e4aSElliott Hughes [ 1159*e1fe3e4aSElliott Hughes [["glyph1"], (0, 123, None, {}, {}, {})], 1160*e1fe3e4aSElliott Hughes [["glyph2"], (0, 456, None, {}, {}, {})], 1161*e1fe3e4aSElliott Hughes ], 1162*e1fe3e4aSElliott Hughes ), 1163*e1fe3e4aSElliott Hughes ) 1164*e1fe3e4aSElliott Hughes 1165*e1fe3e4aSElliott Hughes def test_def_anchor(self): 1166*e1fe3e4aSElliott Hughes [anchor1, anchor2, anchor3] = self.parse( 1167*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "top" ON 120 GLYPH a ' 1168*e1fe3e4aSElliott Hughes "COMPONENT 1 AT POS DX 250 DY 450 END_POS END_ANCHOR\n" 1169*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "MARK_top" ON 120 GLYPH acutecomb ' 1170*e1fe3e4aSElliott Hughes "COMPONENT 1 AT POS DX 0 DY 450 END_POS END_ANCHOR\n" 1171*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "bottom" ON 120 GLYPH a ' 1172*e1fe3e4aSElliott Hughes "COMPONENT 1 AT POS DX 250 DY 0 END_POS END_ANCHOR" 1173*e1fe3e4aSElliott Hughes ).statements 1174*e1fe3e4aSElliott Hughes self.assertEqual( 1175*e1fe3e4aSElliott Hughes ( 1176*e1fe3e4aSElliott Hughes anchor1.name, 1177*e1fe3e4aSElliott Hughes anchor1.gid, 1178*e1fe3e4aSElliott Hughes anchor1.glyph_name, 1179*e1fe3e4aSElliott Hughes anchor1.component, 1180*e1fe3e4aSElliott Hughes anchor1.locked, 1181*e1fe3e4aSElliott Hughes anchor1.pos, 1182*e1fe3e4aSElliott Hughes ), 1183*e1fe3e4aSElliott Hughes ("top", 120, "a", 1, False, (None, 250, 450, {}, {}, {})), 1184*e1fe3e4aSElliott Hughes ) 1185*e1fe3e4aSElliott Hughes self.assertEqual( 1186*e1fe3e4aSElliott Hughes ( 1187*e1fe3e4aSElliott Hughes anchor2.name, 1188*e1fe3e4aSElliott Hughes anchor2.gid, 1189*e1fe3e4aSElliott Hughes anchor2.glyph_name, 1190*e1fe3e4aSElliott Hughes anchor2.component, 1191*e1fe3e4aSElliott Hughes anchor2.locked, 1192*e1fe3e4aSElliott Hughes anchor2.pos, 1193*e1fe3e4aSElliott Hughes ), 1194*e1fe3e4aSElliott Hughes ("MARK_top", 120, "acutecomb", 1, False, (None, 0, 450, {}, {}, {})), 1195*e1fe3e4aSElliott Hughes ) 1196*e1fe3e4aSElliott Hughes self.assertEqual( 1197*e1fe3e4aSElliott Hughes ( 1198*e1fe3e4aSElliott Hughes anchor3.name, 1199*e1fe3e4aSElliott Hughes anchor3.gid, 1200*e1fe3e4aSElliott Hughes anchor3.glyph_name, 1201*e1fe3e4aSElliott Hughes anchor3.component, 1202*e1fe3e4aSElliott Hughes anchor3.locked, 1203*e1fe3e4aSElliott Hughes anchor3.pos, 1204*e1fe3e4aSElliott Hughes ), 1205*e1fe3e4aSElliott Hughes ("bottom", 120, "a", 1, False, (None, 250, 0, {}, {}, {})), 1206*e1fe3e4aSElliott Hughes ) 1207*e1fe3e4aSElliott Hughes 1208*e1fe3e4aSElliott Hughes def test_def_anchor_multi_component(self): 1209*e1fe3e4aSElliott Hughes [anchor1, anchor2] = self.parse( 1210*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "top" ON 120 GLYPH a ' 1211*e1fe3e4aSElliott Hughes "COMPONENT 1 AT POS DX 250 DY 450 END_POS END_ANCHOR\n" 1212*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "top" ON 120 GLYPH a ' 1213*e1fe3e4aSElliott Hughes "COMPONENT 2 AT POS DX 250 DY 450 END_POS END_ANCHOR" 1214*e1fe3e4aSElliott Hughes ).statements 1215*e1fe3e4aSElliott Hughes self.assertEqual( 1216*e1fe3e4aSElliott Hughes (anchor1.name, anchor1.gid, anchor1.glyph_name, anchor1.component), 1217*e1fe3e4aSElliott Hughes ("top", 120, "a", 1), 1218*e1fe3e4aSElliott Hughes ) 1219*e1fe3e4aSElliott Hughes self.assertEqual( 1220*e1fe3e4aSElliott Hughes (anchor2.name, anchor2.gid, anchor2.glyph_name, anchor2.component), 1221*e1fe3e4aSElliott Hughes ("top", 120, "a", 2), 1222*e1fe3e4aSElliott Hughes ) 1223*e1fe3e4aSElliott Hughes 1224*e1fe3e4aSElliott Hughes def test_def_anchor_duplicate(self): 1225*e1fe3e4aSElliott Hughes self.assertRaisesRegex( 1226*e1fe3e4aSElliott Hughes VoltLibError, 1227*e1fe3e4aSElliott Hughes 'Anchor "dupe" already defined, ' "anchor names are case insensitive", 1228*e1fe3e4aSElliott Hughes self.parse, 1229*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "dupe" ON 120 GLYPH a ' 1230*e1fe3e4aSElliott Hughes "COMPONENT 1 AT POS DX 250 DY 450 END_POS END_ANCHOR\n" 1231*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "dupe" ON 120 GLYPH a ' 1232*e1fe3e4aSElliott Hughes "COMPONENT 1 AT POS DX 250 DY 450 END_POS END_ANCHOR", 1233*e1fe3e4aSElliott Hughes ) 1234*e1fe3e4aSElliott Hughes 1235*e1fe3e4aSElliott Hughes def test_def_anchor_locked(self): 1236*e1fe3e4aSElliott Hughes [anchor] = self.parse( 1237*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "top" ON 120 GLYPH a ' 1238*e1fe3e4aSElliott Hughes "COMPONENT 1 LOCKED AT POS DX 250 DY 450 END_POS END_ANCHOR" 1239*e1fe3e4aSElliott Hughes ).statements 1240*e1fe3e4aSElliott Hughes self.assertEqual( 1241*e1fe3e4aSElliott Hughes ( 1242*e1fe3e4aSElliott Hughes anchor.name, 1243*e1fe3e4aSElliott Hughes anchor.gid, 1244*e1fe3e4aSElliott Hughes anchor.glyph_name, 1245*e1fe3e4aSElliott Hughes anchor.component, 1246*e1fe3e4aSElliott Hughes anchor.locked, 1247*e1fe3e4aSElliott Hughes anchor.pos, 1248*e1fe3e4aSElliott Hughes ), 1249*e1fe3e4aSElliott Hughes ("top", 120, "a", 1, True, (None, 250, 450, {}, {}, {})), 1250*e1fe3e4aSElliott Hughes ) 1251*e1fe3e4aSElliott Hughes 1252*e1fe3e4aSElliott Hughes def test_anchor_adjust_device(self): 1253*e1fe3e4aSElliott Hughes [anchor] = self.parse( 1254*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "MARK_top" ON 123 GLYPH diacglyph ' 1255*e1fe3e4aSElliott Hughes "COMPONENT 1 AT POS DX 0 DY 456 ADJUST_BY 12 AT 34 " 1256*e1fe3e4aSElliott Hughes "ADJUST_BY 56 AT 78 END_POS END_ANCHOR" 1257*e1fe3e4aSElliott Hughes ).statements 1258*e1fe3e4aSElliott Hughes self.assertEqual( 1259*e1fe3e4aSElliott Hughes (anchor.name, anchor.pos), 1260*e1fe3e4aSElliott Hughes ("MARK_top", (None, 0, 456, {}, {}, {34: 12, 78: 56})), 1261*e1fe3e4aSElliott Hughes ) 1262*e1fe3e4aSElliott Hughes 1263*e1fe3e4aSElliott Hughes def test_ppem(self): 1264*e1fe3e4aSElliott Hughes [grid_ppem, pres_ppem, ppos_ppem] = self.parse( 1265*e1fe3e4aSElliott Hughes "GRID_PPEM 20\n" "PRESENTATION_PPEM 72\n" "PPOSITIONING_PPEM 144" 1266*e1fe3e4aSElliott Hughes ).statements 1267*e1fe3e4aSElliott Hughes self.assertEqual( 1268*e1fe3e4aSElliott Hughes ( 1269*e1fe3e4aSElliott Hughes (grid_ppem.name, grid_ppem.value), 1270*e1fe3e4aSElliott Hughes (pres_ppem.name, pres_ppem.value), 1271*e1fe3e4aSElliott Hughes (ppos_ppem.name, ppos_ppem.value), 1272*e1fe3e4aSElliott Hughes ), 1273*e1fe3e4aSElliott Hughes (("GRID_PPEM", 20), ("PRESENTATION_PPEM", 72), ("PPOSITIONING_PPEM", 144)), 1274*e1fe3e4aSElliott Hughes ) 1275*e1fe3e4aSElliott Hughes 1276*e1fe3e4aSElliott Hughes def test_compiler_flags(self): 1277*e1fe3e4aSElliott Hughes [setting1, setting2] = self.parse( 1278*e1fe3e4aSElliott Hughes "COMPILER_USEEXTENSIONLOOKUPS\n" "COMPILER_USEPAIRPOSFORMAT2" 1279*e1fe3e4aSElliott Hughes ).statements 1280*e1fe3e4aSElliott Hughes self.assertEqual( 1281*e1fe3e4aSElliott Hughes ((setting1.name, setting1.value), (setting2.name, setting2.value)), 1282*e1fe3e4aSElliott Hughes ( 1283*e1fe3e4aSElliott Hughes ("COMPILER_USEEXTENSIONLOOKUPS", True), 1284*e1fe3e4aSElliott Hughes ("COMPILER_USEPAIRPOSFORMAT2", True), 1285*e1fe3e4aSElliott Hughes ), 1286*e1fe3e4aSElliott Hughes ) 1287*e1fe3e4aSElliott Hughes 1288*e1fe3e4aSElliott Hughes def test_cmap(self): 1289*e1fe3e4aSElliott Hughes [cmap_format1, cmap_format2, cmap_format3] = self.parse( 1290*e1fe3e4aSElliott Hughes "CMAP_FORMAT 0 3 4\n" "CMAP_FORMAT 1 0 6\n" "CMAP_FORMAT 3 1 4" 1291*e1fe3e4aSElliott Hughes ).statements 1292*e1fe3e4aSElliott Hughes self.assertEqual( 1293*e1fe3e4aSElliott Hughes ( 1294*e1fe3e4aSElliott Hughes (cmap_format1.name, cmap_format1.value), 1295*e1fe3e4aSElliott Hughes (cmap_format2.name, cmap_format2.value), 1296*e1fe3e4aSElliott Hughes (cmap_format3.name, cmap_format3.value), 1297*e1fe3e4aSElliott Hughes ), 1298*e1fe3e4aSElliott Hughes ( 1299*e1fe3e4aSElliott Hughes ("CMAP_FORMAT", (0, 3, 4)), 1300*e1fe3e4aSElliott Hughes ("CMAP_FORMAT", (1, 0, 6)), 1301*e1fe3e4aSElliott Hughes ("CMAP_FORMAT", (3, 1, 4)), 1302*e1fe3e4aSElliott Hughes ), 1303*e1fe3e4aSElliott Hughes ) 1304*e1fe3e4aSElliott Hughes 1305*e1fe3e4aSElliott Hughes def test_do_not_touch_cmap(self): 1306*e1fe3e4aSElliott Hughes [option1, option2, option3, option4] = self.parse( 1307*e1fe3e4aSElliott Hughes "DO_NOT_TOUCH_CMAP\n" 1308*e1fe3e4aSElliott Hughes "CMAP_FORMAT 0 3 4\n" 1309*e1fe3e4aSElliott Hughes "CMAP_FORMAT 1 0 6\n" 1310*e1fe3e4aSElliott Hughes "CMAP_FORMAT 3 1 4" 1311*e1fe3e4aSElliott Hughes ).statements 1312*e1fe3e4aSElliott Hughes self.assertEqual( 1313*e1fe3e4aSElliott Hughes ( 1314*e1fe3e4aSElliott Hughes (option1.name, option1.value), 1315*e1fe3e4aSElliott Hughes (option2.name, option2.value), 1316*e1fe3e4aSElliott Hughes (option3.name, option3.value), 1317*e1fe3e4aSElliott Hughes (option4.name, option4.value), 1318*e1fe3e4aSElliott Hughes ), 1319*e1fe3e4aSElliott Hughes ( 1320*e1fe3e4aSElliott Hughes ("DO_NOT_TOUCH_CMAP", True), 1321*e1fe3e4aSElliott Hughes ("CMAP_FORMAT", (0, 3, 4)), 1322*e1fe3e4aSElliott Hughes ("CMAP_FORMAT", (1, 0, 6)), 1323*e1fe3e4aSElliott Hughes ("CMAP_FORMAT", (3, 1, 4)), 1324*e1fe3e4aSElliott Hughes ), 1325*e1fe3e4aSElliott Hughes ) 1326*e1fe3e4aSElliott Hughes 1327*e1fe3e4aSElliott Hughes def test_stop_at_end(self): 1328*e1fe3e4aSElliott Hughes doc = self.parse_('DEF_GLYPH ".notdef" ID 0 TYPE BASE END_GLYPH END\0\0\0\0') 1329*e1fe3e4aSElliott Hughes [def_glyph] = doc.statements 1330*e1fe3e4aSElliott Hughes self.assertEqual( 1331*e1fe3e4aSElliott Hughes ( 1332*e1fe3e4aSElliott Hughes def_glyph.name, 1333*e1fe3e4aSElliott Hughes def_glyph.id, 1334*e1fe3e4aSElliott Hughes def_glyph.unicode, 1335*e1fe3e4aSElliott Hughes def_glyph.type, 1336*e1fe3e4aSElliott Hughes def_glyph.components, 1337*e1fe3e4aSElliott Hughes ), 1338*e1fe3e4aSElliott Hughes (".notdef", 0, None, "BASE", None), 1339*e1fe3e4aSElliott Hughes ) 1340*e1fe3e4aSElliott Hughes self.assertEqual( 1341*e1fe3e4aSElliott Hughes str(doc), '\nDEF_GLYPH ".notdef" ID 0 TYPE BASE END_GLYPH END\n' 1342*e1fe3e4aSElliott Hughes ) 1343*e1fe3e4aSElliott Hughes 1344*e1fe3e4aSElliott Hughes def parse_(self, text): 1345*e1fe3e4aSElliott Hughes return Parser(StringIO(text)).parse() 1346*e1fe3e4aSElliott Hughes 1347*e1fe3e4aSElliott Hughes def parse(self, text): 1348*e1fe3e4aSElliott Hughes doc = self.parse_(text) 1349*e1fe3e4aSElliott Hughes self.assertEqual("\n".join(str(s) for s in doc.statements), text) 1350*e1fe3e4aSElliott Hughes return Parser(StringIO(text)).parse() 1351*e1fe3e4aSElliott Hughes 1352*e1fe3e4aSElliott Hughes 1353*e1fe3e4aSElliott Hughesif __name__ == "__main__": 1354*e1fe3e4aSElliott Hughes import sys 1355*e1fe3e4aSElliott Hughes 1356*e1fe3e4aSElliott Hughes sys.exit(unittest.main()) 1357