1*e1fe3e4aSElliott Hughesimport pathlib 2*e1fe3e4aSElliott Hughesimport shutil 3*e1fe3e4aSElliott Hughesimport tempfile 4*e1fe3e4aSElliott Hughesimport unittest 5*e1fe3e4aSElliott Hughesfrom io import StringIO 6*e1fe3e4aSElliott Hughes 7*e1fe3e4aSElliott Hughesfrom fontTools.voltLib.voltToFea import VoltToFea 8*e1fe3e4aSElliott Hughes 9*e1fe3e4aSElliott HughesDATADIR = pathlib.Path(__file__).parent / "data" 10*e1fe3e4aSElliott Hughes 11*e1fe3e4aSElliott Hughes 12*e1fe3e4aSElliott Hughesclass ToFeaTest(unittest.TestCase): 13*e1fe3e4aSElliott Hughes @classmethod 14*e1fe3e4aSElliott Hughes def setup_class(cls): 15*e1fe3e4aSElliott Hughes cls.tempdir = None 16*e1fe3e4aSElliott Hughes cls.num_tempfiles = 0 17*e1fe3e4aSElliott Hughes 18*e1fe3e4aSElliott Hughes @classmethod 19*e1fe3e4aSElliott Hughes def teardown_class(cls): 20*e1fe3e4aSElliott Hughes if cls.tempdir: 21*e1fe3e4aSElliott Hughes shutil.rmtree(cls.tempdir, ignore_errors=True) 22*e1fe3e4aSElliott Hughes 23*e1fe3e4aSElliott Hughes @classmethod 24*e1fe3e4aSElliott Hughes def temp_path(cls): 25*e1fe3e4aSElliott Hughes if not cls.tempdir: 26*e1fe3e4aSElliott Hughes cls.tempdir = pathlib.Path(tempfile.mkdtemp()) 27*e1fe3e4aSElliott Hughes cls.num_tempfiles += 1 28*e1fe3e4aSElliott Hughes return cls.tempdir / f"tmp{cls.num_tempfiles}" 29*e1fe3e4aSElliott Hughes 30*e1fe3e4aSElliott Hughes def test_def_glyph_base(self): 31*e1fe3e4aSElliott Hughes fea = self.parse('DEF_GLYPH ".notdef" ID 0 TYPE BASE END_GLYPH') 32*e1fe3e4aSElliott Hughes self.assertEqual( 33*e1fe3e4aSElliott Hughes fea, 34*e1fe3e4aSElliott Hughes "@GDEF_base = [.notdef];\n" 35*e1fe3e4aSElliott Hughes "table GDEF {\n" 36*e1fe3e4aSElliott Hughes " GlyphClassDef @GDEF_base, , , ;\n" 37*e1fe3e4aSElliott Hughes "} GDEF;\n", 38*e1fe3e4aSElliott Hughes ) 39*e1fe3e4aSElliott Hughes 40*e1fe3e4aSElliott Hughes def test_def_glyph_base_2_components(self): 41*e1fe3e4aSElliott Hughes fea = self.parse( 42*e1fe3e4aSElliott Hughes 'DEF_GLYPH "glyphBase" ID 320 TYPE BASE COMPONENTS 2 END_GLYPH' 43*e1fe3e4aSElliott Hughes ) 44*e1fe3e4aSElliott Hughes self.assertEqual( 45*e1fe3e4aSElliott Hughes fea, 46*e1fe3e4aSElliott Hughes "@GDEF_base = [glyphBase];\n" 47*e1fe3e4aSElliott Hughes "table GDEF {\n" 48*e1fe3e4aSElliott Hughes " GlyphClassDef @GDEF_base, , , ;\n" 49*e1fe3e4aSElliott Hughes "} GDEF;\n", 50*e1fe3e4aSElliott Hughes ) 51*e1fe3e4aSElliott Hughes 52*e1fe3e4aSElliott Hughes def test_def_glyph_ligature_2_components(self): 53*e1fe3e4aSElliott Hughes fea = self.parse('DEF_GLYPH "f_f" ID 320 TYPE LIGATURE COMPONENTS 2 END_GLYPH') 54*e1fe3e4aSElliott Hughes self.assertEqual( 55*e1fe3e4aSElliott Hughes fea, 56*e1fe3e4aSElliott Hughes "@GDEF_ligature = [f_f];\n" 57*e1fe3e4aSElliott Hughes "table GDEF {\n" 58*e1fe3e4aSElliott Hughes " GlyphClassDef , @GDEF_ligature, , ;\n" 59*e1fe3e4aSElliott Hughes "} GDEF;\n", 60*e1fe3e4aSElliott Hughes ) 61*e1fe3e4aSElliott Hughes 62*e1fe3e4aSElliott Hughes def test_def_glyph_mark(self): 63*e1fe3e4aSElliott Hughes fea = self.parse('DEF_GLYPH "brevecomb" ID 320 TYPE MARK END_GLYPH') 64*e1fe3e4aSElliott Hughes self.assertEqual( 65*e1fe3e4aSElliott Hughes fea, 66*e1fe3e4aSElliott Hughes "@GDEF_mark = [brevecomb];\n" 67*e1fe3e4aSElliott Hughes "table GDEF {\n" 68*e1fe3e4aSElliott Hughes " GlyphClassDef , , @GDEF_mark, ;\n" 69*e1fe3e4aSElliott Hughes "} GDEF;\n", 70*e1fe3e4aSElliott Hughes ) 71*e1fe3e4aSElliott Hughes 72*e1fe3e4aSElliott Hughes def test_def_glyph_component(self): 73*e1fe3e4aSElliott Hughes fea = self.parse('DEF_GLYPH "f.f_f" ID 320 TYPE COMPONENT END_GLYPH') 74*e1fe3e4aSElliott Hughes self.assertEqual( 75*e1fe3e4aSElliott Hughes fea, 76*e1fe3e4aSElliott Hughes "@GDEF_component = [f.f_f];\n" 77*e1fe3e4aSElliott Hughes "table GDEF {\n" 78*e1fe3e4aSElliott Hughes " GlyphClassDef , , , @GDEF_component;\n" 79*e1fe3e4aSElliott Hughes "} GDEF;\n", 80*e1fe3e4aSElliott Hughes ) 81*e1fe3e4aSElliott Hughes 82*e1fe3e4aSElliott Hughes def test_def_glyph_no_type(self): 83*e1fe3e4aSElliott Hughes fea = self.parse('DEF_GLYPH "glyph20" ID 20 END_GLYPH') 84*e1fe3e4aSElliott Hughes self.assertEqual(fea, "") 85*e1fe3e4aSElliott Hughes 86*e1fe3e4aSElliott Hughes def test_def_glyph_case_sensitive(self): 87*e1fe3e4aSElliott Hughes fea = self.parse( 88*e1fe3e4aSElliott Hughes 'DEF_GLYPH "A" ID 3 UNICODE 65 TYPE BASE END_GLYPH\n' 89*e1fe3e4aSElliott Hughes 'DEF_GLYPH "a" ID 4 UNICODE 97 TYPE BASE END_GLYPH\n' 90*e1fe3e4aSElliott Hughes ) 91*e1fe3e4aSElliott Hughes self.assertEqual( 92*e1fe3e4aSElliott Hughes fea, 93*e1fe3e4aSElliott Hughes "@GDEF_base = [A a];\n" 94*e1fe3e4aSElliott Hughes "table GDEF {\n" 95*e1fe3e4aSElliott Hughes " GlyphClassDef @GDEF_base, , , ;\n" 96*e1fe3e4aSElliott Hughes "} GDEF;\n", 97*e1fe3e4aSElliott Hughes ) 98*e1fe3e4aSElliott Hughes 99*e1fe3e4aSElliott Hughes def test_def_group_glyphs(self): 100*e1fe3e4aSElliott Hughes fea = self.parse( 101*e1fe3e4aSElliott Hughes 'DEF_GROUP "aaccented"\n' 102*e1fe3e4aSElliott Hughes 'ENUM GLYPH "aacute" GLYPH "abreve" GLYPH "acircumflex" ' 103*e1fe3e4aSElliott Hughes 'GLYPH "adieresis" GLYPH "ae" GLYPH "agrave" GLYPH "amacron" ' 104*e1fe3e4aSElliott Hughes 'GLYPH "aogonek" GLYPH "aring" GLYPH "atilde" END_ENUM\n' 105*e1fe3e4aSElliott Hughes "END_GROUP\n" 106*e1fe3e4aSElliott Hughes ) 107*e1fe3e4aSElliott Hughes self.assertEqual( 108*e1fe3e4aSElliott Hughes fea, 109*e1fe3e4aSElliott Hughes "# Glyph classes\n" 110*e1fe3e4aSElliott Hughes "@aaccented = [aacute abreve acircumflex adieresis ae" 111*e1fe3e4aSElliott Hughes " agrave amacron aogonek aring atilde];", 112*e1fe3e4aSElliott Hughes ) 113*e1fe3e4aSElliott Hughes 114*e1fe3e4aSElliott Hughes def test_def_group_groups(self): 115*e1fe3e4aSElliott Hughes fea = self.parse( 116*e1fe3e4aSElliott Hughes 'DEF_GROUP "Group1"\n' 117*e1fe3e4aSElliott Hughes 'ENUM GLYPH "a" GLYPH "b" GLYPH "c" GLYPH "d" END_ENUM\n' 118*e1fe3e4aSElliott Hughes "END_GROUP\n" 119*e1fe3e4aSElliott Hughes 'DEF_GROUP "Group2"\n' 120*e1fe3e4aSElliott Hughes 'ENUM GLYPH "e" GLYPH "f" GLYPH "g" GLYPH "h" END_ENUM\n' 121*e1fe3e4aSElliott Hughes "END_GROUP\n" 122*e1fe3e4aSElliott Hughes 'DEF_GROUP "TestGroup"\n' 123*e1fe3e4aSElliott Hughes 'ENUM GROUP "Group1" GROUP "Group2" END_ENUM\n' 124*e1fe3e4aSElliott Hughes "END_GROUP\n" 125*e1fe3e4aSElliott Hughes ) 126*e1fe3e4aSElliott Hughes self.assertEqual( 127*e1fe3e4aSElliott Hughes fea, 128*e1fe3e4aSElliott Hughes "# Glyph classes\n" 129*e1fe3e4aSElliott Hughes "@Group1 = [a b c d];\n" 130*e1fe3e4aSElliott Hughes "@Group2 = [e f g h];\n" 131*e1fe3e4aSElliott Hughes "@TestGroup = [@Group1 @Group2];", 132*e1fe3e4aSElliott Hughes ) 133*e1fe3e4aSElliott Hughes 134*e1fe3e4aSElliott Hughes def test_def_group_groups_not_yet_defined(self): 135*e1fe3e4aSElliott Hughes fea = self.parse( 136*e1fe3e4aSElliott Hughes 'DEF_GROUP "Group1"\n' 137*e1fe3e4aSElliott Hughes 'ENUM GLYPH "a" GLYPH "b" GLYPH "c" GLYPH "d" END_ENUM\n' 138*e1fe3e4aSElliott Hughes "END_GROUP\n" 139*e1fe3e4aSElliott Hughes 'DEF_GROUP "TestGroup1"\n' 140*e1fe3e4aSElliott Hughes 'ENUM GROUP "Group1" GROUP "Group2" END_ENUM\n' 141*e1fe3e4aSElliott Hughes "END_GROUP\n" 142*e1fe3e4aSElliott Hughes 'DEF_GROUP "TestGroup2"\n' 143*e1fe3e4aSElliott Hughes 'ENUM GROUP "Group2" END_ENUM\n' 144*e1fe3e4aSElliott Hughes "END_GROUP\n" 145*e1fe3e4aSElliott Hughes 'DEF_GROUP "TestGroup3"\n' 146*e1fe3e4aSElliott Hughes 'ENUM GROUP "Group2" GROUP "Group1" END_ENUM\n' 147*e1fe3e4aSElliott Hughes "END_GROUP\n" 148*e1fe3e4aSElliott Hughes 'DEF_GROUP "Group2"\n' 149*e1fe3e4aSElliott Hughes 'ENUM GLYPH "e" GLYPH "f" GLYPH "g" GLYPH "h" END_ENUM\n' 150*e1fe3e4aSElliott Hughes "END_GROUP\n" 151*e1fe3e4aSElliott Hughes ) 152*e1fe3e4aSElliott Hughes self.assertEqual( 153*e1fe3e4aSElliott Hughes fea, 154*e1fe3e4aSElliott Hughes "# Glyph classes\n" 155*e1fe3e4aSElliott Hughes "@Group1 = [a b c d];\n" 156*e1fe3e4aSElliott Hughes "@Group2 = [e f g h];\n" 157*e1fe3e4aSElliott Hughes "@TestGroup1 = [@Group1 @Group2];\n" 158*e1fe3e4aSElliott Hughes "@TestGroup2 = [@Group2];\n" 159*e1fe3e4aSElliott Hughes "@TestGroup3 = [@Group2 @Group1];", 160*e1fe3e4aSElliott Hughes ) 161*e1fe3e4aSElliott Hughes 162*e1fe3e4aSElliott Hughes def test_def_group_glyphs_and_group(self): 163*e1fe3e4aSElliott Hughes fea = self.parse( 164*e1fe3e4aSElliott Hughes 'DEF_GROUP "aaccented"\n' 165*e1fe3e4aSElliott Hughes 'ENUM GLYPH "aacute" GLYPH "abreve" GLYPH "acircumflex" ' 166*e1fe3e4aSElliott Hughes 'GLYPH "adieresis" GLYPH "ae" GLYPH "agrave" GLYPH "amacron" ' 167*e1fe3e4aSElliott Hughes 'GLYPH "aogonek" GLYPH "aring" GLYPH "atilde" END_ENUM\n' 168*e1fe3e4aSElliott Hughes "END_GROUP\n" 169*e1fe3e4aSElliott Hughes 'DEF_GROUP "KERN_lc_a_2ND"\n' 170*e1fe3e4aSElliott Hughes 'ENUM GLYPH "a" GROUP "aaccented" END_ENUM\n' 171*e1fe3e4aSElliott Hughes "END_GROUP" 172*e1fe3e4aSElliott Hughes ) 173*e1fe3e4aSElliott Hughes self.assertEqual( 174*e1fe3e4aSElliott Hughes fea, 175*e1fe3e4aSElliott Hughes "# Glyph classes\n" 176*e1fe3e4aSElliott Hughes "@aaccented = [aacute abreve acircumflex adieresis ae" 177*e1fe3e4aSElliott Hughes " agrave amacron aogonek aring atilde];\n" 178*e1fe3e4aSElliott Hughes "@KERN_lc_a_2ND = [a @aaccented];", 179*e1fe3e4aSElliott Hughes ) 180*e1fe3e4aSElliott Hughes 181*e1fe3e4aSElliott Hughes def test_def_group_range(self): 182*e1fe3e4aSElliott Hughes fea = self.parse( 183*e1fe3e4aSElliott Hughes 'DEF_GLYPH "a" ID 163 UNICODE 97 TYPE BASE END_GLYPH\n' 184*e1fe3e4aSElliott Hughes 'DEF_GLYPH "agrave" ID 194 UNICODE 224 TYPE BASE END_GLYPH\n' 185*e1fe3e4aSElliott Hughes 'DEF_GLYPH "aacute" ID 195 UNICODE 225 TYPE BASE END_GLYPH\n' 186*e1fe3e4aSElliott Hughes 'DEF_GLYPH "acircumflex" ID 196 UNICODE 226 TYPE BASE END_GLYPH\n' 187*e1fe3e4aSElliott Hughes 'DEF_GLYPH "atilde" ID 197 UNICODE 227 TYPE BASE END_GLYPH\n' 188*e1fe3e4aSElliott Hughes 'DEF_GLYPH "c" ID 165 UNICODE 99 TYPE BASE END_GLYPH\n' 189*e1fe3e4aSElliott Hughes 'DEF_GLYPH "ccaron" ID 209 UNICODE 269 TYPE BASE END_GLYPH\n' 190*e1fe3e4aSElliott Hughes 'DEF_GLYPH "ccedilla" ID 210 UNICODE 231 TYPE BASE END_GLYPH\n' 191*e1fe3e4aSElliott Hughes 'DEF_GLYPH "cdotaccent" ID 210 UNICODE 267 TYPE BASE END_GLYPH\n' 192*e1fe3e4aSElliott Hughes 'DEF_GROUP "KERN_lc_a_2ND"\n' 193*e1fe3e4aSElliott Hughes 'ENUM RANGE "a" TO "atilde" GLYPH "b" RANGE "c" TO "cdotaccent" ' 194*e1fe3e4aSElliott Hughes "END_ENUM\n" 195*e1fe3e4aSElliott Hughes "END_GROUP" 196*e1fe3e4aSElliott Hughes ) 197*e1fe3e4aSElliott Hughes self.assertEqual( 198*e1fe3e4aSElliott Hughes fea, 199*e1fe3e4aSElliott Hughes "# Glyph classes\n" 200*e1fe3e4aSElliott Hughes "@KERN_lc_a_2ND = [a - atilde b c - cdotaccent];\n" 201*e1fe3e4aSElliott Hughes "@GDEF_base = [a agrave aacute acircumflex atilde c" 202*e1fe3e4aSElliott Hughes " ccaron ccedilla cdotaccent];\n" 203*e1fe3e4aSElliott Hughes "table GDEF {\n" 204*e1fe3e4aSElliott Hughes " GlyphClassDef @GDEF_base, , , ;\n" 205*e1fe3e4aSElliott Hughes "} GDEF;\n", 206*e1fe3e4aSElliott Hughes ) 207*e1fe3e4aSElliott Hughes 208*e1fe3e4aSElliott Hughes def test_script_without_langsys(self): 209*e1fe3e4aSElliott Hughes fea = self.parse('DEF_SCRIPT NAME "Latin" TAG "latn"\n' "END_SCRIPT") 210*e1fe3e4aSElliott Hughes self.assertEqual(fea, "") 211*e1fe3e4aSElliott Hughes 212*e1fe3e4aSElliott Hughes def test_langsys_normal(self): 213*e1fe3e4aSElliott Hughes fea = self.parse( 214*e1fe3e4aSElliott Hughes 'DEF_SCRIPT NAME "Latin" TAG "latn"\n' 215*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Romanian" TAG "ROM "\n' 216*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 217*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Moldavian" TAG "MOL "\n' 218*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 219*e1fe3e4aSElliott Hughes "END_SCRIPT" 220*e1fe3e4aSElliott Hughes ) 221*e1fe3e4aSElliott Hughes self.assertEqual(fea, "") 222*e1fe3e4aSElliott Hughes 223*e1fe3e4aSElliott Hughes def test_langsys_no_script_name(self): 224*e1fe3e4aSElliott Hughes fea = self.parse( 225*e1fe3e4aSElliott Hughes 'DEF_SCRIPT TAG "latn"\n' 226*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Default" TAG "dflt"\n' 227*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 228*e1fe3e4aSElliott Hughes "END_SCRIPT" 229*e1fe3e4aSElliott Hughes ) 230*e1fe3e4aSElliott Hughes self.assertEqual(fea, "") 231*e1fe3e4aSElliott Hughes 232*e1fe3e4aSElliott Hughes def test_langsys_lang_in_separate_scripts(self): 233*e1fe3e4aSElliott Hughes fea = self.parse( 234*e1fe3e4aSElliott Hughes 'DEF_SCRIPT NAME "Default" TAG "DFLT"\n' 235*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Default" TAG "dflt"\n' 236*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 237*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Default" TAG "ROM "\n' 238*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 239*e1fe3e4aSElliott Hughes "END_SCRIPT\n" 240*e1fe3e4aSElliott Hughes 'DEF_SCRIPT NAME "Latin" TAG "latn"\n' 241*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Default" TAG "dflt"\n' 242*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 243*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Default" TAG "ROM "\n' 244*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 245*e1fe3e4aSElliott Hughes "END_SCRIPT" 246*e1fe3e4aSElliott Hughes ) 247*e1fe3e4aSElliott Hughes self.assertEqual(fea, "") 248*e1fe3e4aSElliott Hughes 249*e1fe3e4aSElliott Hughes def test_langsys_no_lang_name(self): 250*e1fe3e4aSElliott Hughes fea = self.parse( 251*e1fe3e4aSElliott Hughes 'DEF_SCRIPT NAME "Latin" TAG "latn"\n' 252*e1fe3e4aSElliott Hughes 'DEF_LANGSYS TAG "dflt"\n' 253*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 254*e1fe3e4aSElliott Hughes "END_SCRIPT" 255*e1fe3e4aSElliott Hughes ) 256*e1fe3e4aSElliott Hughes self.assertEqual(fea, "") 257*e1fe3e4aSElliott Hughes 258*e1fe3e4aSElliott Hughes def test_feature(self): 259*e1fe3e4aSElliott Hughes fea = self.parse( 260*e1fe3e4aSElliott Hughes 'DEF_SCRIPT NAME "Latin" TAG "latn"\n' 261*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Romanian" TAG "ROM "\n' 262*e1fe3e4aSElliott Hughes 'DEF_FEATURE NAME "Fractions" TAG "frac"\n' 263*e1fe3e4aSElliott Hughes 'LOOKUP "fraclookup"\n' 264*e1fe3e4aSElliott Hughes "END_FEATURE\n" 265*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 266*e1fe3e4aSElliott Hughes "END_SCRIPT\n" 267*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "fraclookup" PROCESS_BASE PROCESS_MARKS ALL ' 268*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 269*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 270*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 271*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 272*e1fe3e4aSElliott Hughes 'SUB GLYPH "one" GLYPH "slash" GLYPH "two"\n' 273*e1fe3e4aSElliott Hughes 'WITH GLYPH "one_slash_two.frac"\n' 274*e1fe3e4aSElliott Hughes "END_SUB\n" 275*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 276*e1fe3e4aSElliott Hughes ) 277*e1fe3e4aSElliott Hughes self.assertEqual( 278*e1fe3e4aSElliott Hughes fea, 279*e1fe3e4aSElliott Hughes "\n# Lookups\n" 280*e1fe3e4aSElliott Hughes "lookup fraclookup {\n" 281*e1fe3e4aSElliott Hughes " sub one slash two by one_slash_two.frac;\n" 282*e1fe3e4aSElliott Hughes "} fraclookup;\n" 283*e1fe3e4aSElliott Hughes "\n" 284*e1fe3e4aSElliott Hughes "# Features\n" 285*e1fe3e4aSElliott Hughes "feature frac {\n" 286*e1fe3e4aSElliott Hughes " script latn;\n" 287*e1fe3e4aSElliott Hughes " language ROM exclude_dflt;\n" 288*e1fe3e4aSElliott Hughes " lookup fraclookup;\n" 289*e1fe3e4aSElliott Hughes "} frac;\n", 290*e1fe3e4aSElliott Hughes ) 291*e1fe3e4aSElliott Hughes 292*e1fe3e4aSElliott Hughes def test_feature_sub_lookups(self): 293*e1fe3e4aSElliott Hughes fea = self.parse( 294*e1fe3e4aSElliott Hughes 'DEF_SCRIPT NAME "Latin" TAG "latn"\n' 295*e1fe3e4aSElliott Hughes 'DEF_LANGSYS NAME "Romanian" TAG "ROM "\n' 296*e1fe3e4aSElliott Hughes 'DEF_FEATURE NAME "Fractions" TAG "frac"\n' 297*e1fe3e4aSElliott Hughes 'LOOKUP "fraclookup\\1"\n' 298*e1fe3e4aSElliott Hughes 'LOOKUP "fraclookup\\1"\n' 299*e1fe3e4aSElliott Hughes "END_FEATURE\n" 300*e1fe3e4aSElliott Hughes "END_LANGSYS\n" 301*e1fe3e4aSElliott Hughes "END_SCRIPT\n" 302*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "fraclookup\\1" PROCESS_BASE PROCESS_MARKS ALL ' 303*e1fe3e4aSElliott Hughes "DIRECTION RTL\n" 304*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 305*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 306*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 307*e1fe3e4aSElliott Hughes 'SUB GLYPH "one" GLYPH "slash" GLYPH "two"\n' 308*e1fe3e4aSElliott Hughes 'WITH GLYPH "one_slash_two.frac"\n' 309*e1fe3e4aSElliott Hughes "END_SUB\n" 310*e1fe3e4aSElliott Hughes "END_SUBSTITUTION\n" 311*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "fraclookup\\2" PROCESS_BASE PROCESS_MARKS ALL ' 312*e1fe3e4aSElliott Hughes "DIRECTION RTL\n" 313*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 314*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 315*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 316*e1fe3e4aSElliott Hughes 'SUB GLYPH "one" GLYPH "slash" GLYPH "three"\n' 317*e1fe3e4aSElliott Hughes 'WITH GLYPH "one_slash_three.frac"\n' 318*e1fe3e4aSElliott Hughes "END_SUB\n" 319*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 320*e1fe3e4aSElliott Hughes ) 321*e1fe3e4aSElliott Hughes self.assertEqual( 322*e1fe3e4aSElliott Hughes fea, 323*e1fe3e4aSElliott Hughes "\n# Lookups\n" 324*e1fe3e4aSElliott Hughes "lookup fraclookup {\n" 325*e1fe3e4aSElliott Hughes " lookupflag RightToLeft;\n" 326*e1fe3e4aSElliott Hughes " # fraclookup\\1\n" 327*e1fe3e4aSElliott Hughes " sub one slash two by one_slash_two.frac;\n" 328*e1fe3e4aSElliott Hughes " subtable;\n" 329*e1fe3e4aSElliott Hughes " # fraclookup\\2\n" 330*e1fe3e4aSElliott Hughes " sub one slash three by one_slash_three.frac;\n" 331*e1fe3e4aSElliott Hughes "} fraclookup;\n" 332*e1fe3e4aSElliott Hughes "\n" 333*e1fe3e4aSElliott Hughes "# Features\n" 334*e1fe3e4aSElliott Hughes "feature frac {\n" 335*e1fe3e4aSElliott Hughes " script latn;\n" 336*e1fe3e4aSElliott Hughes " language ROM exclude_dflt;\n" 337*e1fe3e4aSElliott Hughes " lookup fraclookup;\n" 338*e1fe3e4aSElliott Hughes "} frac;\n", 339*e1fe3e4aSElliott Hughes ) 340*e1fe3e4aSElliott Hughes 341*e1fe3e4aSElliott Hughes def test_lookup_comment(self): 342*e1fe3e4aSElliott Hughes fea = self.parse( 343*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "smcp" PROCESS_BASE PROCESS_MARKS ALL ' 344*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 345*e1fe3e4aSElliott Hughes 'COMMENTS "Smallcaps lookup for testing"\n' 346*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 347*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 348*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 349*e1fe3e4aSElliott Hughes 'SUB GLYPH "a"\n' 350*e1fe3e4aSElliott Hughes 'WITH GLYPH "a.sc"\n' 351*e1fe3e4aSElliott Hughes "END_SUB\n" 352*e1fe3e4aSElliott Hughes 'SUB GLYPH "b"\n' 353*e1fe3e4aSElliott Hughes 'WITH GLYPH "b.sc"\n' 354*e1fe3e4aSElliott Hughes "END_SUB\n" 355*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 356*e1fe3e4aSElliott Hughes ) 357*e1fe3e4aSElliott Hughes self.assertEqual( 358*e1fe3e4aSElliott Hughes fea, 359*e1fe3e4aSElliott Hughes "\n# Lookups\n" 360*e1fe3e4aSElliott Hughes "lookup smcp {\n" 361*e1fe3e4aSElliott Hughes " # Smallcaps lookup for testing\n" 362*e1fe3e4aSElliott Hughes " sub a by a.sc;\n" 363*e1fe3e4aSElliott Hughes " sub b by b.sc;\n" 364*e1fe3e4aSElliott Hughes "} smcp;\n", 365*e1fe3e4aSElliott Hughes ) 366*e1fe3e4aSElliott Hughes 367*e1fe3e4aSElliott Hughes def test_substitution_single(self): 368*e1fe3e4aSElliott Hughes fea = self.parse( 369*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "smcp" PROCESS_BASE PROCESS_MARKS ALL ' 370*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 371*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 372*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 373*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 374*e1fe3e4aSElliott Hughes 'SUB GLYPH "a"\n' 375*e1fe3e4aSElliott Hughes 'WITH GLYPH "a.sc"\n' 376*e1fe3e4aSElliott Hughes "END_SUB\n" 377*e1fe3e4aSElliott Hughes 'SUB GLYPH "b"\n' 378*e1fe3e4aSElliott Hughes 'WITH GLYPH "b.sc"\n' 379*e1fe3e4aSElliott Hughes "END_SUB\n" 380*e1fe3e4aSElliott Hughes "SUB WITH\n" # Empty substitution, will be ignored 381*e1fe3e4aSElliott Hughes "END_SUB\n" 382*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 383*e1fe3e4aSElliott Hughes ) 384*e1fe3e4aSElliott Hughes self.assertEqual( 385*e1fe3e4aSElliott Hughes fea, 386*e1fe3e4aSElliott Hughes "\n# Lookups\n" 387*e1fe3e4aSElliott Hughes "lookup smcp {\n" 388*e1fe3e4aSElliott Hughes " sub a by a.sc;\n" 389*e1fe3e4aSElliott Hughes " sub b by b.sc;\n" 390*e1fe3e4aSElliott Hughes "} smcp;\n", 391*e1fe3e4aSElliott Hughes ) 392*e1fe3e4aSElliott Hughes 393*e1fe3e4aSElliott Hughes def test_substitution_single_in_context(self): 394*e1fe3e4aSElliott Hughes fea = self.parse( 395*e1fe3e4aSElliott Hughes 'DEF_GROUP "Denominators" ENUM GLYPH "one.dnom" GLYPH "two.dnom" ' 396*e1fe3e4aSElliott Hughes "END_ENUM END_GROUP\n" 397*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "fracdnom" PROCESS_BASE PROCESS_MARKS ALL ' 398*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 399*e1fe3e4aSElliott Hughes 'IN_CONTEXT LEFT ENUM GROUP "Denominators" GLYPH "fraction" ' 400*e1fe3e4aSElliott Hughes "END_ENUM\n" 401*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 402*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 403*e1fe3e4aSElliott Hughes 'SUB GLYPH "one"\n' 404*e1fe3e4aSElliott Hughes 'WITH GLYPH "one.dnom"\n' 405*e1fe3e4aSElliott Hughes "END_SUB\n" 406*e1fe3e4aSElliott Hughes 'SUB GLYPH "two"\n' 407*e1fe3e4aSElliott Hughes 'WITH GLYPH "two.dnom"\n' 408*e1fe3e4aSElliott Hughes "END_SUB\n" 409*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 410*e1fe3e4aSElliott Hughes ) 411*e1fe3e4aSElliott Hughes self.assertEqual( 412*e1fe3e4aSElliott Hughes fea, 413*e1fe3e4aSElliott Hughes "# Glyph classes\n" 414*e1fe3e4aSElliott Hughes "@Denominators = [one.dnom two.dnom];\n" 415*e1fe3e4aSElliott Hughes "\n" 416*e1fe3e4aSElliott Hughes "# Lookups\n" 417*e1fe3e4aSElliott Hughes "lookup fracdnom {\n" 418*e1fe3e4aSElliott Hughes " sub [@Denominators fraction] one' by one.dnom;\n" 419*e1fe3e4aSElliott Hughes " sub [@Denominators fraction] two' by two.dnom;\n" 420*e1fe3e4aSElliott Hughes "} fracdnom;\n", 421*e1fe3e4aSElliott Hughes ) 422*e1fe3e4aSElliott Hughes 423*e1fe3e4aSElliott Hughes def test_substitution_single_in_contexts(self): 424*e1fe3e4aSElliott Hughes fea = self.parse( 425*e1fe3e4aSElliott Hughes 'DEF_GROUP "Hebrew" ENUM GLYPH "uni05D0" GLYPH "uni05D1" ' 426*e1fe3e4aSElliott Hughes "END_ENUM END_GROUP\n" 427*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "HebrewCurrency" PROCESS_BASE PROCESS_MARKS ALL ' 428*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 429*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 430*e1fe3e4aSElliott Hughes 'RIGHT GROUP "Hebrew"\n' 431*e1fe3e4aSElliott Hughes 'RIGHT GLYPH "one.Hebr"\n' 432*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 433*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 434*e1fe3e4aSElliott Hughes 'LEFT GROUP "Hebrew"\n' 435*e1fe3e4aSElliott Hughes 'LEFT GLYPH "one.Hebr"\n' 436*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 437*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 438*e1fe3e4aSElliott Hughes 'SUB GLYPH "dollar"\n' 439*e1fe3e4aSElliott Hughes 'WITH GLYPH "dollar.Hebr"\n' 440*e1fe3e4aSElliott Hughes "END_SUB\n" 441*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 442*e1fe3e4aSElliott Hughes ) 443*e1fe3e4aSElliott Hughes self.assertEqual( 444*e1fe3e4aSElliott Hughes fea, 445*e1fe3e4aSElliott Hughes "# Glyph classes\n" 446*e1fe3e4aSElliott Hughes "@Hebrew = [uni05D0 uni05D1];\n" 447*e1fe3e4aSElliott Hughes "\n" 448*e1fe3e4aSElliott Hughes "# Lookups\n" 449*e1fe3e4aSElliott Hughes "lookup HebrewCurrency {\n" 450*e1fe3e4aSElliott Hughes " sub dollar' @Hebrew one.Hebr by dollar.Hebr;\n" 451*e1fe3e4aSElliott Hughes " sub @Hebrew one.Hebr dollar' by dollar.Hebr;\n" 452*e1fe3e4aSElliott Hughes "} HebrewCurrency;\n", 453*e1fe3e4aSElliott Hughes ) 454*e1fe3e4aSElliott Hughes 455*e1fe3e4aSElliott Hughes def test_substitution_single_except_context(self): 456*e1fe3e4aSElliott Hughes fea = self.parse( 457*e1fe3e4aSElliott Hughes 'DEF_GROUP "Hebrew" ENUM GLYPH "uni05D0" GLYPH "uni05D1" ' 458*e1fe3e4aSElliott Hughes "END_ENUM END_GROUP\n" 459*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "HebrewCurrency" PROCESS_BASE PROCESS_MARKS ALL ' 460*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 461*e1fe3e4aSElliott Hughes "EXCEPT_CONTEXT\n" 462*e1fe3e4aSElliott Hughes 'RIGHT GROUP "Hebrew"\n' 463*e1fe3e4aSElliott Hughes 'RIGHT GLYPH "one.Hebr"\n' 464*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 465*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 466*e1fe3e4aSElliott Hughes 'LEFT GROUP "Hebrew"\n' 467*e1fe3e4aSElliott Hughes 'LEFT GLYPH "one.Hebr"\n' 468*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 469*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 470*e1fe3e4aSElliott Hughes 'SUB GLYPH "dollar"\n' 471*e1fe3e4aSElliott Hughes 'WITH GLYPH "dollar.Hebr"\n' 472*e1fe3e4aSElliott Hughes "END_SUB\n" 473*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 474*e1fe3e4aSElliott Hughes ) 475*e1fe3e4aSElliott Hughes self.assertEqual( 476*e1fe3e4aSElliott Hughes fea, 477*e1fe3e4aSElliott Hughes "# Glyph classes\n" 478*e1fe3e4aSElliott Hughes "@Hebrew = [uni05D0 uni05D1];\n" 479*e1fe3e4aSElliott Hughes "\n" 480*e1fe3e4aSElliott Hughes "# Lookups\n" 481*e1fe3e4aSElliott Hughes "lookup HebrewCurrency {\n" 482*e1fe3e4aSElliott Hughes " ignore sub dollar' @Hebrew one.Hebr;\n" 483*e1fe3e4aSElliott Hughes " sub @Hebrew one.Hebr dollar' by dollar.Hebr;\n" 484*e1fe3e4aSElliott Hughes "} HebrewCurrency;\n", 485*e1fe3e4aSElliott Hughes ) 486*e1fe3e4aSElliott Hughes 487*e1fe3e4aSElliott Hughes def test_substitution_skip_base(self): 488*e1fe3e4aSElliott Hughes fea = self.parse( 489*e1fe3e4aSElliott Hughes 'DEF_GROUP "SomeMarks" ENUM GLYPH "marka" GLYPH "markb" ' 490*e1fe3e4aSElliott Hughes "END_ENUM END_GROUP\n" 491*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "SomeSub" SKIP_BASE PROCESS_MARKS ALL ' 492*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 493*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 494*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 495*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 496*e1fe3e4aSElliott Hughes 'SUB GLYPH "A"\n' 497*e1fe3e4aSElliott Hughes 'WITH GLYPH "A.c2sc"\n' 498*e1fe3e4aSElliott Hughes "END_SUB\n" 499*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 500*e1fe3e4aSElliott Hughes ) 501*e1fe3e4aSElliott Hughes self.assertEqual( 502*e1fe3e4aSElliott Hughes fea, 503*e1fe3e4aSElliott Hughes "# Glyph classes\n" 504*e1fe3e4aSElliott Hughes "@SomeMarks = [marka markb];\n" 505*e1fe3e4aSElliott Hughes "\n" 506*e1fe3e4aSElliott Hughes "# Lookups\n" 507*e1fe3e4aSElliott Hughes "lookup SomeSub {\n" 508*e1fe3e4aSElliott Hughes " lookupflag IgnoreBaseGlyphs;\n" 509*e1fe3e4aSElliott Hughes " sub A by A.c2sc;\n" 510*e1fe3e4aSElliott Hughes "} SomeSub;\n", 511*e1fe3e4aSElliott Hughes ) 512*e1fe3e4aSElliott Hughes 513*e1fe3e4aSElliott Hughes def test_substitution_process_base(self): 514*e1fe3e4aSElliott Hughes fea = self.parse( 515*e1fe3e4aSElliott Hughes 'DEF_GROUP "SomeMarks" ENUM GLYPH "marka" GLYPH "markb" ' 516*e1fe3e4aSElliott Hughes "END_ENUM END_GROUP\n" 517*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS ALL ' 518*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 519*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 520*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 521*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 522*e1fe3e4aSElliott Hughes 'SUB GLYPH "A"\n' 523*e1fe3e4aSElliott Hughes 'WITH GLYPH "A.c2sc"\n' 524*e1fe3e4aSElliott Hughes "END_SUB\n" 525*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 526*e1fe3e4aSElliott Hughes ) 527*e1fe3e4aSElliott Hughes self.assertEqual( 528*e1fe3e4aSElliott Hughes fea, 529*e1fe3e4aSElliott Hughes "# Glyph classes\n" 530*e1fe3e4aSElliott Hughes "@SomeMarks = [marka markb];\n" 531*e1fe3e4aSElliott Hughes "\n" 532*e1fe3e4aSElliott Hughes "# Lookups\n" 533*e1fe3e4aSElliott Hughes "lookup SomeSub {\n" 534*e1fe3e4aSElliott Hughes " sub A by A.c2sc;\n" 535*e1fe3e4aSElliott Hughes "} SomeSub;\n", 536*e1fe3e4aSElliott Hughes ) 537*e1fe3e4aSElliott Hughes 538*e1fe3e4aSElliott Hughes def test_substitution_process_marks_all(self): 539*e1fe3e4aSElliott Hughes fea = self.parse( 540*e1fe3e4aSElliott Hughes 'DEF_GROUP "SomeMarks" ENUM GLYPH "marka" GLYPH "markb" ' 541*e1fe3e4aSElliott Hughes "END_ENUM END_GROUP\n" 542*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS "ALL"' 543*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 544*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 545*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 546*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 547*e1fe3e4aSElliott Hughes 'SUB GLYPH "A"\n' 548*e1fe3e4aSElliott Hughes 'WITH GLYPH "A.c2sc"\n' 549*e1fe3e4aSElliott Hughes "END_SUB\n" 550*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 551*e1fe3e4aSElliott Hughes ) 552*e1fe3e4aSElliott Hughes self.assertEqual( 553*e1fe3e4aSElliott Hughes fea, 554*e1fe3e4aSElliott Hughes "# Glyph classes\n" 555*e1fe3e4aSElliott Hughes "@SomeMarks = [marka markb];\n" 556*e1fe3e4aSElliott Hughes "\n" 557*e1fe3e4aSElliott Hughes "# Lookups\n" 558*e1fe3e4aSElliott Hughes "lookup SomeSub {\n" 559*e1fe3e4aSElliott Hughes " sub A by A.c2sc;\n" 560*e1fe3e4aSElliott Hughes "} SomeSub;\n", 561*e1fe3e4aSElliott Hughes ) 562*e1fe3e4aSElliott Hughes 563*e1fe3e4aSElliott Hughes def test_substitution_process_marks_none(self): 564*e1fe3e4aSElliott Hughes fea = self.parse( 565*e1fe3e4aSElliott Hughes 'DEF_GROUP "SomeMarks" ENUM GLYPH "marka" GLYPH "markb" ' 566*e1fe3e4aSElliott Hughes "END_ENUM END_GROUP\n" 567*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "SomeSub" PROCESS_BASE PROCESS_MARKS "NONE"' 568*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 569*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 570*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 571*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 572*e1fe3e4aSElliott Hughes 'SUB GLYPH "A"\n' 573*e1fe3e4aSElliott Hughes 'WITH GLYPH "A.c2sc"\n' 574*e1fe3e4aSElliott Hughes "END_SUB\n" 575*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 576*e1fe3e4aSElliott Hughes ) 577*e1fe3e4aSElliott Hughes self.assertEqual( 578*e1fe3e4aSElliott Hughes fea, 579*e1fe3e4aSElliott Hughes "# Glyph classes\n" 580*e1fe3e4aSElliott Hughes "@SomeMarks = [marka markb];\n" 581*e1fe3e4aSElliott Hughes "\n" 582*e1fe3e4aSElliott Hughes "# Lookups\n" 583*e1fe3e4aSElliott Hughes "lookup SomeSub {\n" 584*e1fe3e4aSElliott Hughes " lookupflag IgnoreMarks;\n" 585*e1fe3e4aSElliott Hughes " sub A by A.c2sc;\n" 586*e1fe3e4aSElliott Hughes "} SomeSub;\n", 587*e1fe3e4aSElliott Hughes ) 588*e1fe3e4aSElliott Hughes 589*e1fe3e4aSElliott Hughes def test_substitution_skip_marks(self): 590*e1fe3e4aSElliott Hughes fea = self.parse( 591*e1fe3e4aSElliott Hughes 'DEF_GROUP "SomeMarks" ENUM GLYPH "marka" GLYPH "markb" ' 592*e1fe3e4aSElliott Hughes "END_ENUM END_GROUP\n" 593*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "SomeSub" PROCESS_BASE SKIP_MARKS ' 594*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 595*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 596*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 597*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 598*e1fe3e4aSElliott Hughes 'SUB GLYPH "A"\n' 599*e1fe3e4aSElliott Hughes 'WITH GLYPH "A.c2sc"\n' 600*e1fe3e4aSElliott Hughes "END_SUB\n" 601*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 602*e1fe3e4aSElliott Hughes ) 603*e1fe3e4aSElliott Hughes self.assertEqual( 604*e1fe3e4aSElliott Hughes fea, 605*e1fe3e4aSElliott Hughes "# Glyph classes\n" 606*e1fe3e4aSElliott Hughes "@SomeMarks = [marka markb];\n" 607*e1fe3e4aSElliott Hughes "\n" 608*e1fe3e4aSElliott Hughes "# Lookups\n" 609*e1fe3e4aSElliott Hughes "lookup SomeSub {\n" 610*e1fe3e4aSElliott Hughes " lookupflag IgnoreMarks;\n" 611*e1fe3e4aSElliott Hughes " sub A by A.c2sc;\n" 612*e1fe3e4aSElliott Hughes "} SomeSub;\n", 613*e1fe3e4aSElliott Hughes ) 614*e1fe3e4aSElliott Hughes 615*e1fe3e4aSElliott Hughes def test_substitution_mark_attachment(self): 616*e1fe3e4aSElliott Hughes fea = self.parse( 617*e1fe3e4aSElliott Hughes 'DEF_GROUP "SomeMarks" ENUM GLYPH "acutecmb" GLYPH "gravecmb" ' 618*e1fe3e4aSElliott Hughes "END_ENUM END_GROUP\n" 619*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "SomeSub" PROCESS_BASE ' 620*e1fe3e4aSElliott Hughes 'PROCESS_MARKS "SomeMarks" \n' 621*e1fe3e4aSElliott Hughes "DIRECTION RTL\n" 622*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 623*e1fe3e4aSElliott Hughes 'SUB GLYPH "A"\n' 624*e1fe3e4aSElliott Hughes 'WITH GLYPH "A.c2sc"\n' 625*e1fe3e4aSElliott Hughes "END_SUB\n" 626*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 627*e1fe3e4aSElliott Hughes ) 628*e1fe3e4aSElliott Hughes self.assertEqual( 629*e1fe3e4aSElliott Hughes fea, 630*e1fe3e4aSElliott Hughes "# Glyph classes\n" 631*e1fe3e4aSElliott Hughes "@SomeMarks = [acutecmb gravecmb];\n" 632*e1fe3e4aSElliott Hughes "\n" 633*e1fe3e4aSElliott Hughes "# Lookups\n" 634*e1fe3e4aSElliott Hughes "lookup SomeSub {\n" 635*e1fe3e4aSElliott Hughes " lookupflag RightToLeft MarkAttachmentType" 636*e1fe3e4aSElliott Hughes " @SomeMarks;\n" 637*e1fe3e4aSElliott Hughes " sub A by A.c2sc;\n" 638*e1fe3e4aSElliott Hughes "} SomeSub;\n", 639*e1fe3e4aSElliott Hughes ) 640*e1fe3e4aSElliott Hughes 641*e1fe3e4aSElliott Hughes def test_substitution_mark_glyph_set(self): 642*e1fe3e4aSElliott Hughes fea = self.parse( 643*e1fe3e4aSElliott Hughes 'DEF_GROUP "SomeMarks" ENUM GLYPH "acutecmb" GLYPH "gravecmb" ' 644*e1fe3e4aSElliott Hughes "END_ENUM END_GROUP\n" 645*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "SomeSub" PROCESS_BASE ' 646*e1fe3e4aSElliott Hughes 'PROCESS_MARKS MARK_GLYPH_SET "SomeMarks" \n' 647*e1fe3e4aSElliott Hughes "DIRECTION RTL\n" 648*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 649*e1fe3e4aSElliott Hughes 'SUB GLYPH "A"\n' 650*e1fe3e4aSElliott Hughes 'WITH GLYPH "A.c2sc"\n' 651*e1fe3e4aSElliott Hughes "END_SUB\n" 652*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 653*e1fe3e4aSElliott Hughes ) 654*e1fe3e4aSElliott Hughes self.assertEqual( 655*e1fe3e4aSElliott Hughes fea, 656*e1fe3e4aSElliott Hughes "# Glyph classes\n" 657*e1fe3e4aSElliott Hughes "@SomeMarks = [acutecmb gravecmb];\n" 658*e1fe3e4aSElliott Hughes "\n" 659*e1fe3e4aSElliott Hughes "# Lookups\n" 660*e1fe3e4aSElliott Hughes "lookup SomeSub {\n" 661*e1fe3e4aSElliott Hughes " lookupflag RightToLeft UseMarkFilteringSet" 662*e1fe3e4aSElliott Hughes " @SomeMarks;\n" 663*e1fe3e4aSElliott Hughes " sub A by A.c2sc;\n" 664*e1fe3e4aSElliott Hughes "} SomeSub;\n", 665*e1fe3e4aSElliott Hughes ) 666*e1fe3e4aSElliott Hughes 667*e1fe3e4aSElliott Hughes def test_substitution_process_all_marks(self): 668*e1fe3e4aSElliott Hughes fea = self.parse( 669*e1fe3e4aSElliott Hughes 'DEF_GROUP "SomeMarks" ENUM GLYPH "acutecmb" GLYPH "gravecmb" ' 670*e1fe3e4aSElliott Hughes "END_ENUM END_GROUP\n" 671*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "SomeSub" PROCESS_BASE ' 672*e1fe3e4aSElliott Hughes "PROCESS_MARKS ALL \n" 673*e1fe3e4aSElliott Hughes "DIRECTION RTL\n" 674*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 675*e1fe3e4aSElliott Hughes 'SUB GLYPH "A"\n' 676*e1fe3e4aSElliott Hughes 'WITH GLYPH "A.c2sc"\n' 677*e1fe3e4aSElliott Hughes "END_SUB\n" 678*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 679*e1fe3e4aSElliott Hughes ) 680*e1fe3e4aSElliott Hughes self.assertEqual( 681*e1fe3e4aSElliott Hughes fea, 682*e1fe3e4aSElliott Hughes "# Glyph classes\n" 683*e1fe3e4aSElliott Hughes "@SomeMarks = [acutecmb gravecmb];\n" 684*e1fe3e4aSElliott Hughes "\n" 685*e1fe3e4aSElliott Hughes "# Lookups\n" 686*e1fe3e4aSElliott Hughes "lookup SomeSub {\n" 687*e1fe3e4aSElliott Hughes " lookupflag RightToLeft;\n" 688*e1fe3e4aSElliott Hughes " sub A by A.c2sc;\n" 689*e1fe3e4aSElliott Hughes "} SomeSub;\n", 690*e1fe3e4aSElliott Hughes ) 691*e1fe3e4aSElliott Hughes 692*e1fe3e4aSElliott Hughes def test_substitution_no_reversal(self): 693*e1fe3e4aSElliott Hughes # TODO: check right context with no reversal 694*e1fe3e4aSElliott Hughes fea = self.parse( 695*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "Lookup" PROCESS_BASE PROCESS_MARKS ALL ' 696*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 697*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 698*e1fe3e4aSElliott Hughes 'RIGHT ENUM GLYPH "a" GLYPH "b" END_ENUM\n' 699*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 700*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 701*e1fe3e4aSElliott Hughes 'SUB GLYPH "a"\n' 702*e1fe3e4aSElliott Hughes 'WITH GLYPH "a.alt"\n' 703*e1fe3e4aSElliott Hughes "END_SUB\n" 704*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 705*e1fe3e4aSElliott Hughes ) 706*e1fe3e4aSElliott Hughes self.assertEqual( 707*e1fe3e4aSElliott Hughes fea, 708*e1fe3e4aSElliott Hughes "\n# Lookups\n" 709*e1fe3e4aSElliott Hughes "lookup Lookup {\n" 710*e1fe3e4aSElliott Hughes " sub a' [a b] by a.alt;\n" 711*e1fe3e4aSElliott Hughes "} Lookup;\n", 712*e1fe3e4aSElliott Hughes ) 713*e1fe3e4aSElliott Hughes 714*e1fe3e4aSElliott Hughes def test_substitution_reversal(self): 715*e1fe3e4aSElliott Hughes fea = self.parse( 716*e1fe3e4aSElliott Hughes 'DEF_GROUP "DFLT_Num_standardFigures"\n' 717*e1fe3e4aSElliott Hughes 'ENUM GLYPH "zero" GLYPH "one" GLYPH "two" END_ENUM\n' 718*e1fe3e4aSElliott Hughes "END_GROUP\n" 719*e1fe3e4aSElliott Hughes 'DEF_GROUP "DFLT_Num_numerators"\n' 720*e1fe3e4aSElliott Hughes 'ENUM GLYPH "zero.numr" GLYPH "one.numr" GLYPH "two.numr" END_ENUM\n' 721*e1fe3e4aSElliott Hughes "END_GROUP\n" 722*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "RevLookup" PROCESS_BASE PROCESS_MARKS ALL ' 723*e1fe3e4aSElliott Hughes "DIRECTION LTR REVERSAL\n" 724*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 725*e1fe3e4aSElliott Hughes 'RIGHT ENUM GLYPH "a" GLYPH "b" END_ENUM\n' 726*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 727*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 728*e1fe3e4aSElliott Hughes 'SUB GROUP "DFLT_Num_standardFigures"\n' 729*e1fe3e4aSElliott Hughes 'WITH GROUP "DFLT_Num_numerators"\n' 730*e1fe3e4aSElliott Hughes "END_SUB\n" 731*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 732*e1fe3e4aSElliott Hughes ) 733*e1fe3e4aSElliott Hughes self.assertEqual( 734*e1fe3e4aSElliott Hughes fea, 735*e1fe3e4aSElliott Hughes "# Glyph classes\n" 736*e1fe3e4aSElliott Hughes "@DFLT_Num_standardFigures = [zero one two];\n" 737*e1fe3e4aSElliott Hughes "@DFLT_Num_numerators = [zero.numr one.numr two.numr];\n" 738*e1fe3e4aSElliott Hughes "\n" 739*e1fe3e4aSElliott Hughes "# Lookups\n" 740*e1fe3e4aSElliott Hughes "lookup RevLookup {\n" 741*e1fe3e4aSElliott Hughes " rsub @DFLT_Num_standardFigures' [a b] by @DFLT_Num_numerators;\n" 742*e1fe3e4aSElliott Hughes "} RevLookup;\n", 743*e1fe3e4aSElliott Hughes ) 744*e1fe3e4aSElliott Hughes 745*e1fe3e4aSElliott Hughes def test_substitution_single_to_multiple(self): 746*e1fe3e4aSElliott Hughes fea = self.parse( 747*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "ccmp" PROCESS_BASE PROCESS_MARKS ALL ' 748*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 749*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 750*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 751*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 752*e1fe3e4aSElliott Hughes 'SUB GLYPH "aacute"\n' 753*e1fe3e4aSElliott Hughes 'WITH GLYPH "a" GLYPH "acutecomb"\n' 754*e1fe3e4aSElliott Hughes "END_SUB\n" 755*e1fe3e4aSElliott Hughes 'SUB GLYPH "agrave"\n' 756*e1fe3e4aSElliott Hughes 'WITH GLYPH "a" GLYPH "gravecomb"\n' 757*e1fe3e4aSElliott Hughes "END_SUB\n" 758*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 759*e1fe3e4aSElliott Hughes ) 760*e1fe3e4aSElliott Hughes self.assertEqual( 761*e1fe3e4aSElliott Hughes fea, 762*e1fe3e4aSElliott Hughes "\n# Lookups\n" 763*e1fe3e4aSElliott Hughes "lookup ccmp {\n" 764*e1fe3e4aSElliott Hughes " sub aacute by a acutecomb;\n" 765*e1fe3e4aSElliott Hughes " sub agrave by a gravecomb;\n" 766*e1fe3e4aSElliott Hughes "} ccmp;\n", 767*e1fe3e4aSElliott Hughes ) 768*e1fe3e4aSElliott Hughes 769*e1fe3e4aSElliott Hughes def test_substitution_multiple_to_single(self): 770*e1fe3e4aSElliott Hughes fea = self.parse( 771*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "liga" PROCESS_BASE PROCESS_MARKS ALL ' 772*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 773*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 774*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 775*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 776*e1fe3e4aSElliott Hughes 'SUB GLYPH "f" GLYPH "i"\n' 777*e1fe3e4aSElliott Hughes 'WITH GLYPH "f_i"\n' 778*e1fe3e4aSElliott Hughes "END_SUB\n" 779*e1fe3e4aSElliott Hughes 'SUB GLYPH "f" GLYPH "t"\n' 780*e1fe3e4aSElliott Hughes 'WITH GLYPH "f_t"\n' 781*e1fe3e4aSElliott Hughes "END_SUB\n" 782*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 783*e1fe3e4aSElliott Hughes ) 784*e1fe3e4aSElliott Hughes self.assertEqual( 785*e1fe3e4aSElliott Hughes fea, 786*e1fe3e4aSElliott Hughes "\n# Lookups\n" 787*e1fe3e4aSElliott Hughes "lookup liga {\n" 788*e1fe3e4aSElliott Hughes " sub f i by f_i;\n" 789*e1fe3e4aSElliott Hughes " sub f t by f_t;\n" 790*e1fe3e4aSElliott Hughes "} liga;\n", 791*e1fe3e4aSElliott Hughes ) 792*e1fe3e4aSElliott Hughes 793*e1fe3e4aSElliott Hughes def test_substitution_reverse_chaining_single(self): 794*e1fe3e4aSElliott Hughes fea = self.parse( 795*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "numr" PROCESS_BASE PROCESS_MARKS ALL ' 796*e1fe3e4aSElliott Hughes "DIRECTION LTR REVERSAL\n" 797*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 798*e1fe3e4aSElliott Hughes "RIGHT ENUM " 799*e1fe3e4aSElliott Hughes 'GLYPH "fraction" ' 800*e1fe3e4aSElliott Hughes 'RANGE "zero.numr" TO "nine.numr" ' 801*e1fe3e4aSElliott Hughes "END_ENUM\n" 802*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 803*e1fe3e4aSElliott Hughes "AS_SUBSTITUTION\n" 804*e1fe3e4aSElliott Hughes 'SUB RANGE "zero" TO "nine"\n' 805*e1fe3e4aSElliott Hughes 'WITH RANGE "zero.numr" TO "nine.numr"\n' 806*e1fe3e4aSElliott Hughes "END_SUB\n" 807*e1fe3e4aSElliott Hughes "END_SUBSTITUTION" 808*e1fe3e4aSElliott Hughes ) 809*e1fe3e4aSElliott Hughes self.assertEqual( 810*e1fe3e4aSElliott Hughes fea, 811*e1fe3e4aSElliott Hughes "\n# Lookups\n" 812*e1fe3e4aSElliott Hughes "lookup numr {\n" 813*e1fe3e4aSElliott Hughes " rsub zero - nine' [fraction zero.numr - nine.numr] by zero.numr - nine.numr;\n" 814*e1fe3e4aSElliott Hughes "} numr;\n", 815*e1fe3e4aSElliott Hughes ) 816*e1fe3e4aSElliott Hughes 817*e1fe3e4aSElliott Hughes # GPOS 818*e1fe3e4aSElliott Hughes # ATTACH_CURSIVE 819*e1fe3e4aSElliott Hughes # ATTACH 820*e1fe3e4aSElliott Hughes # ADJUST_PAIR 821*e1fe3e4aSElliott Hughes # ADJUST_SINGLE 822*e1fe3e4aSElliott Hughes def test_position_attach(self): 823*e1fe3e4aSElliott Hughes fea = self.parse( 824*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "anchor_top" PROCESS_BASE PROCESS_MARKS ALL ' 825*e1fe3e4aSElliott Hughes "DIRECTION RTL\n" 826*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 827*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 828*e1fe3e4aSElliott Hughes "AS_POSITION\n" 829*e1fe3e4aSElliott Hughes 'ATTACH GLYPH "a" GLYPH "e"\n' 830*e1fe3e4aSElliott Hughes 'TO GLYPH "acutecomb" AT ANCHOR "top" ' 831*e1fe3e4aSElliott Hughes 'GLYPH "gravecomb" AT ANCHOR "top"\n' 832*e1fe3e4aSElliott Hughes "END_ATTACH\n" 833*e1fe3e4aSElliott Hughes "END_POSITION\n" 834*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "MARK_top" ON 120 GLYPH acutecomb COMPONENT 1 ' 835*e1fe3e4aSElliott Hughes "AT POS DX 0 DY 450 END_POS END_ANCHOR\n" 836*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "MARK_top" ON 121 GLYPH gravecomb COMPONENT 1 ' 837*e1fe3e4aSElliott Hughes "AT POS DX 0 DY 450 END_POS END_ANCHOR\n" 838*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "top" ON 31 GLYPH a COMPONENT 1 ' 839*e1fe3e4aSElliott Hughes "AT POS DX 210 DY 450 END_POS END_ANCHOR\n" 840*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "top" ON 35 GLYPH e COMPONENT 1 ' 841*e1fe3e4aSElliott Hughes "AT POS DX 215 DY 450 END_POS END_ANCHOR\n" 842*e1fe3e4aSElliott Hughes ) 843*e1fe3e4aSElliott Hughes self.assertEqual( 844*e1fe3e4aSElliott Hughes fea, 845*e1fe3e4aSElliott Hughes "\n# Mark classes\n" 846*e1fe3e4aSElliott Hughes "markClass acutecomb <anchor 0 450> @top;\n" 847*e1fe3e4aSElliott Hughes "markClass gravecomb <anchor 0 450> @top;\n" 848*e1fe3e4aSElliott Hughes "\n" 849*e1fe3e4aSElliott Hughes "# Lookups\n" 850*e1fe3e4aSElliott Hughes "lookup anchor_top {\n" 851*e1fe3e4aSElliott Hughes " lookupflag RightToLeft;\n" 852*e1fe3e4aSElliott Hughes " pos base a\n" 853*e1fe3e4aSElliott Hughes " <anchor 210 450> mark @top;\n" 854*e1fe3e4aSElliott Hughes " pos base e\n" 855*e1fe3e4aSElliott Hughes " <anchor 215 450> mark @top;\n" 856*e1fe3e4aSElliott Hughes "} anchor_top;\n", 857*e1fe3e4aSElliott Hughes ) 858*e1fe3e4aSElliott Hughes 859*e1fe3e4aSElliott Hughes def test_position_attach_mkmk(self): 860*e1fe3e4aSElliott Hughes fea = self.parse( 861*e1fe3e4aSElliott Hughes 'DEF_GLYPH "brevecomb" ID 1 TYPE MARK END_GLYPH\n' 862*e1fe3e4aSElliott Hughes 'DEF_GLYPH "gravecomb" ID 2 TYPE MARK END_GLYPH\n' 863*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "anchor_top" PROCESS_BASE PROCESS_MARKS ALL ' 864*e1fe3e4aSElliott Hughes "DIRECTION RTL\n" 865*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 866*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 867*e1fe3e4aSElliott Hughes "AS_POSITION\n" 868*e1fe3e4aSElliott Hughes 'ATTACH GLYPH "gravecomb"\n' 869*e1fe3e4aSElliott Hughes 'TO GLYPH "acutecomb" AT ANCHOR "top"\n' 870*e1fe3e4aSElliott Hughes "END_ATTACH\n" 871*e1fe3e4aSElliott Hughes "END_POSITION\n" 872*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "MARK_top" ON 1 GLYPH acutecomb COMPONENT 1 ' 873*e1fe3e4aSElliott Hughes "AT POS DX 0 DY 450 END_POS END_ANCHOR\n" 874*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "top" ON 2 GLYPH gravecomb COMPONENT 1 ' 875*e1fe3e4aSElliott Hughes "AT POS DX 210 DY 450 END_POS END_ANCHOR\n" 876*e1fe3e4aSElliott Hughes ) 877*e1fe3e4aSElliott Hughes self.assertEqual( 878*e1fe3e4aSElliott Hughes fea, 879*e1fe3e4aSElliott Hughes "\n# Mark classes\n" 880*e1fe3e4aSElliott Hughes "markClass acutecomb <anchor 0 450> @top;\n" 881*e1fe3e4aSElliott Hughes "\n" 882*e1fe3e4aSElliott Hughes "# Lookups\n" 883*e1fe3e4aSElliott Hughes "lookup anchor_top {\n" 884*e1fe3e4aSElliott Hughes " lookupflag RightToLeft;\n" 885*e1fe3e4aSElliott Hughes " pos mark gravecomb\n" 886*e1fe3e4aSElliott Hughes " <anchor 210 450> mark @top;\n" 887*e1fe3e4aSElliott Hughes "} anchor_top;\n" 888*e1fe3e4aSElliott Hughes "\n" 889*e1fe3e4aSElliott Hughes "@GDEF_mark = [brevecomb gravecomb];\n" 890*e1fe3e4aSElliott Hughes "table GDEF {\n" 891*e1fe3e4aSElliott Hughes " GlyphClassDef , , @GDEF_mark, ;\n" 892*e1fe3e4aSElliott Hughes "} GDEF;\n", 893*e1fe3e4aSElliott Hughes ) 894*e1fe3e4aSElliott Hughes 895*e1fe3e4aSElliott Hughes def test_position_attach_in_context(self): 896*e1fe3e4aSElliott Hughes fea = self.parse( 897*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "test" PROCESS_BASE PROCESS_MARKS ALL ' 898*e1fe3e4aSElliott Hughes "DIRECTION RTL\n" 899*e1fe3e4aSElliott Hughes 'EXCEPT_CONTEXT LEFT GLYPH "a" END_CONTEXT\n' 900*e1fe3e4aSElliott Hughes "AS_POSITION\n" 901*e1fe3e4aSElliott Hughes 'ATTACH GLYPH "a"\n' 902*e1fe3e4aSElliott Hughes 'TO GLYPH "acutecomb" AT ANCHOR "top" ' 903*e1fe3e4aSElliott Hughes 'GLYPH "gravecomb" AT ANCHOR "top"\n' 904*e1fe3e4aSElliott Hughes "END_ATTACH\n" 905*e1fe3e4aSElliott Hughes "END_POSITION\n" 906*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "MARK_top" ON 120 GLYPH acutecomb COMPONENT 1 ' 907*e1fe3e4aSElliott Hughes "AT POS DX 0 DY 450 END_POS END_ANCHOR\n" 908*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "MARK_top" ON 121 GLYPH gravecomb COMPONENT 1 ' 909*e1fe3e4aSElliott Hughes "AT POS DX 0 DY 450 END_POS END_ANCHOR\n" 910*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "top" ON 31 GLYPH a COMPONENT 1 ' 911*e1fe3e4aSElliott Hughes "AT POS DX 210 DY 450 END_POS END_ANCHOR\n" 912*e1fe3e4aSElliott Hughes ) 913*e1fe3e4aSElliott Hughes self.assertEqual( 914*e1fe3e4aSElliott Hughes fea, 915*e1fe3e4aSElliott Hughes "\n# Mark classes\n" 916*e1fe3e4aSElliott Hughes "markClass acutecomb <anchor 0 450> @top;\n" 917*e1fe3e4aSElliott Hughes "markClass gravecomb <anchor 0 450> @top;\n" 918*e1fe3e4aSElliott Hughes "\n" 919*e1fe3e4aSElliott Hughes "# Lookups\n" 920*e1fe3e4aSElliott Hughes "lookup test_target {\n" 921*e1fe3e4aSElliott Hughes " pos base a\n" 922*e1fe3e4aSElliott Hughes " <anchor 210 450> mark @top;\n" 923*e1fe3e4aSElliott Hughes "} test_target;\n" 924*e1fe3e4aSElliott Hughes "\n" 925*e1fe3e4aSElliott Hughes "lookup test {\n" 926*e1fe3e4aSElliott Hughes " lookupflag RightToLeft;\n" 927*e1fe3e4aSElliott Hughes " ignore pos a [acutecomb gravecomb]';\n" 928*e1fe3e4aSElliott Hughes " pos [acutecomb gravecomb]' lookup test_target;\n" 929*e1fe3e4aSElliott Hughes "} test;\n", 930*e1fe3e4aSElliott Hughes ) 931*e1fe3e4aSElliott Hughes 932*e1fe3e4aSElliott Hughes def test_position_attach_cursive(self): 933*e1fe3e4aSElliott Hughes fea = self.parse( 934*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "SomeLookup" PROCESS_BASE PROCESS_MARKS ALL ' 935*e1fe3e4aSElliott Hughes "DIRECTION RTL\n" 936*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 937*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 938*e1fe3e4aSElliott Hughes "AS_POSITION\n" 939*e1fe3e4aSElliott Hughes 'ATTACH_CURSIVE EXIT GLYPH "a" GLYPH "b" ' 940*e1fe3e4aSElliott Hughes 'ENTER GLYPH "a" GLYPH "c"\n' 941*e1fe3e4aSElliott Hughes "END_ATTACH\n" 942*e1fe3e4aSElliott Hughes "END_POSITION\n" 943*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "exit" ON 1 GLYPH a COMPONENT 1 AT POS END_POS END_ANCHOR\n' 944*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "entry" ON 1 GLYPH a COMPONENT 1 AT POS END_POS END_ANCHOR\n' 945*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "exit" ON 2 GLYPH b COMPONENT 1 AT POS END_POS END_ANCHOR\n' 946*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "entry" ON 3 GLYPH c COMPONENT 1 AT POS END_POS END_ANCHOR\n' 947*e1fe3e4aSElliott Hughes ) 948*e1fe3e4aSElliott Hughes self.assertEqual( 949*e1fe3e4aSElliott Hughes fea, 950*e1fe3e4aSElliott Hughes "\n# Lookups\n" 951*e1fe3e4aSElliott Hughes "lookup SomeLookup {\n" 952*e1fe3e4aSElliott Hughes " lookupflag RightToLeft;\n" 953*e1fe3e4aSElliott Hughes " pos cursive a <anchor 0 0> <anchor 0 0>;\n" 954*e1fe3e4aSElliott Hughes " pos cursive c <anchor 0 0> <anchor NULL>;\n" 955*e1fe3e4aSElliott Hughes " pos cursive b <anchor NULL> <anchor 0 0>;\n" 956*e1fe3e4aSElliott Hughes "} SomeLookup;\n", 957*e1fe3e4aSElliott Hughes ) 958*e1fe3e4aSElliott Hughes 959*e1fe3e4aSElliott Hughes def test_position_adjust_pair(self): 960*e1fe3e4aSElliott Hughes fea = self.parse( 961*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "kern1" PROCESS_BASE PROCESS_MARKS ALL ' 962*e1fe3e4aSElliott Hughes "DIRECTION RTL\n" 963*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 964*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 965*e1fe3e4aSElliott Hughes "AS_POSITION\n" 966*e1fe3e4aSElliott Hughes "ADJUST_PAIR\n" 967*e1fe3e4aSElliott Hughes ' FIRST GLYPH "A" FIRST GLYPH "V"\n' 968*e1fe3e4aSElliott Hughes ' SECOND GLYPH "A" SECOND GLYPH "V"\n' 969*e1fe3e4aSElliott Hughes " 1 2 BY POS ADV -30 END_POS POS END_POS\n" 970*e1fe3e4aSElliott Hughes " 2 1 BY POS ADV -25 END_POS POS END_POS\n" 971*e1fe3e4aSElliott Hughes "END_ADJUST\n" 972*e1fe3e4aSElliott Hughes "END_POSITION\n" 973*e1fe3e4aSElliott Hughes ) 974*e1fe3e4aSElliott Hughes self.assertEqual( 975*e1fe3e4aSElliott Hughes fea, 976*e1fe3e4aSElliott Hughes "\n# Lookups\n" 977*e1fe3e4aSElliott Hughes "lookup kern1 {\n" 978*e1fe3e4aSElliott Hughes " lookupflag RightToLeft;\n" 979*e1fe3e4aSElliott Hughes " enum pos A V -30;\n" 980*e1fe3e4aSElliott Hughes " enum pos V A -25;\n" 981*e1fe3e4aSElliott Hughes "} kern1;\n", 982*e1fe3e4aSElliott Hughes ) 983*e1fe3e4aSElliott Hughes 984*e1fe3e4aSElliott Hughes def test_position_adjust_pair_in_context(self): 985*e1fe3e4aSElliott Hughes fea = self.parse( 986*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "kern1" PROCESS_BASE PROCESS_MARKS ALL ' 987*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 988*e1fe3e4aSElliott Hughes 'EXCEPT_CONTEXT LEFT GLYPH "A" END_CONTEXT\n' 989*e1fe3e4aSElliott Hughes "AS_POSITION\n" 990*e1fe3e4aSElliott Hughes "ADJUST_PAIR\n" 991*e1fe3e4aSElliott Hughes ' FIRST GLYPH "A" FIRST GLYPH "V"\n' 992*e1fe3e4aSElliott Hughes ' SECOND GLYPH "A" SECOND GLYPH "V"\n' 993*e1fe3e4aSElliott Hughes " 2 1 BY POS ADV -25 END_POS POS END_POS\n" 994*e1fe3e4aSElliott Hughes "END_ADJUST\n" 995*e1fe3e4aSElliott Hughes "END_POSITION\n" 996*e1fe3e4aSElliott Hughes ) 997*e1fe3e4aSElliott Hughes self.assertEqual( 998*e1fe3e4aSElliott Hughes fea, 999*e1fe3e4aSElliott Hughes "\n# Lookups\n" 1000*e1fe3e4aSElliott Hughes "lookup kern1_target {\n" 1001*e1fe3e4aSElliott Hughes " enum pos V A -25;\n" 1002*e1fe3e4aSElliott Hughes "} kern1_target;\n" 1003*e1fe3e4aSElliott Hughes "\n" 1004*e1fe3e4aSElliott Hughes "lookup kern1 {\n" 1005*e1fe3e4aSElliott Hughes " ignore pos A V' A';\n" 1006*e1fe3e4aSElliott Hughes " pos V' lookup kern1_target A' lookup kern1_target;\n" 1007*e1fe3e4aSElliott Hughes "} kern1;\n", 1008*e1fe3e4aSElliott Hughes ) 1009*e1fe3e4aSElliott Hughes 1010*e1fe3e4aSElliott Hughes def test_position_adjust_single(self): 1011*e1fe3e4aSElliott Hughes fea = self.parse( 1012*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "TestLookup" PROCESS_BASE PROCESS_MARKS ALL ' 1013*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 1014*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 1015*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 1016*e1fe3e4aSElliott Hughes "AS_POSITION\n" 1017*e1fe3e4aSElliott Hughes "ADJUST_SINGLE" 1018*e1fe3e4aSElliott Hughes ' GLYPH "glyph1" BY POS ADV 0 DX 123 END_POS\n' 1019*e1fe3e4aSElliott Hughes ' GLYPH "glyph2" BY POS ADV 0 DX 456 END_POS\n' 1020*e1fe3e4aSElliott Hughes "END_ADJUST\n" 1021*e1fe3e4aSElliott Hughes "END_POSITION\n" 1022*e1fe3e4aSElliott Hughes ) 1023*e1fe3e4aSElliott Hughes self.assertEqual( 1024*e1fe3e4aSElliott Hughes fea, 1025*e1fe3e4aSElliott Hughes "\n# Lookups\n" 1026*e1fe3e4aSElliott Hughes "lookup TestLookup {\n" 1027*e1fe3e4aSElliott Hughes " pos glyph1 <123 0 0 0>;\n" 1028*e1fe3e4aSElliott Hughes " pos glyph2 <456 0 0 0>;\n" 1029*e1fe3e4aSElliott Hughes "} TestLookup;\n", 1030*e1fe3e4aSElliott Hughes ) 1031*e1fe3e4aSElliott Hughes 1032*e1fe3e4aSElliott Hughes def test_position_adjust_single_in_context(self): 1033*e1fe3e4aSElliott Hughes fea = self.parse( 1034*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "TestLookup" PROCESS_BASE PROCESS_MARKS ALL ' 1035*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 1036*e1fe3e4aSElliott Hughes "EXCEPT_CONTEXT\n" 1037*e1fe3e4aSElliott Hughes 'LEFT GLYPH "leftGlyph"\n' 1038*e1fe3e4aSElliott Hughes 'RIGHT GLYPH "rightGlyph"\n' 1039*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 1040*e1fe3e4aSElliott Hughes "AS_POSITION\n" 1041*e1fe3e4aSElliott Hughes "ADJUST_SINGLE" 1042*e1fe3e4aSElliott Hughes ' GLYPH "glyph1" BY POS ADV 0 DX 123 END_POS\n' 1043*e1fe3e4aSElliott Hughes ' GLYPH "glyph2" BY POS ADV 0 DX 456 END_POS\n' 1044*e1fe3e4aSElliott Hughes "END_ADJUST\n" 1045*e1fe3e4aSElliott Hughes "END_POSITION\n" 1046*e1fe3e4aSElliott Hughes ) 1047*e1fe3e4aSElliott Hughes self.assertEqual( 1048*e1fe3e4aSElliott Hughes fea, 1049*e1fe3e4aSElliott Hughes "\n# Lookups\n" 1050*e1fe3e4aSElliott Hughes "lookup TestLookup_target {\n" 1051*e1fe3e4aSElliott Hughes " pos glyph1 <123 0 0 0>;\n" 1052*e1fe3e4aSElliott Hughes " pos glyph2 <456 0 0 0>;\n" 1053*e1fe3e4aSElliott Hughes "} TestLookup_target;\n" 1054*e1fe3e4aSElliott Hughes "\n" 1055*e1fe3e4aSElliott Hughes "lookup TestLookup {\n" 1056*e1fe3e4aSElliott Hughes " ignore pos leftGlyph [glyph1 glyph2]' rightGlyph;\n" 1057*e1fe3e4aSElliott Hughes " pos [glyph1 glyph2]' lookup TestLookup_target;\n" 1058*e1fe3e4aSElliott Hughes "} TestLookup;\n", 1059*e1fe3e4aSElliott Hughes ) 1060*e1fe3e4aSElliott Hughes 1061*e1fe3e4aSElliott Hughes def test_def_anchor(self): 1062*e1fe3e4aSElliott Hughes fea = self.parse( 1063*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "TestLookup" PROCESS_BASE PROCESS_MARKS ALL ' 1064*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 1065*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 1066*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 1067*e1fe3e4aSElliott Hughes "AS_POSITION\n" 1068*e1fe3e4aSElliott Hughes 'ATTACH GLYPH "a"\n' 1069*e1fe3e4aSElliott Hughes 'TO GLYPH "acutecomb" AT ANCHOR "top"\n' 1070*e1fe3e4aSElliott Hughes "END_ATTACH\n" 1071*e1fe3e4aSElliott Hughes "END_POSITION\n" 1072*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "top" ON 120 GLYPH a ' 1073*e1fe3e4aSElliott Hughes "COMPONENT 1 AT POS DX 250 DY 450 END_POS END_ANCHOR\n" 1074*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "MARK_top" ON 120 GLYPH acutecomb ' 1075*e1fe3e4aSElliott Hughes "COMPONENT 1 AT POS DX 0 DY 450 END_POS END_ANCHOR" 1076*e1fe3e4aSElliott Hughes ) 1077*e1fe3e4aSElliott Hughes self.assertEqual( 1078*e1fe3e4aSElliott Hughes fea, 1079*e1fe3e4aSElliott Hughes "\n# Mark classes\n" 1080*e1fe3e4aSElliott Hughes "markClass acutecomb <anchor 0 450> @top;\n" 1081*e1fe3e4aSElliott Hughes "\n" 1082*e1fe3e4aSElliott Hughes "# Lookups\n" 1083*e1fe3e4aSElliott Hughes "lookup TestLookup {\n" 1084*e1fe3e4aSElliott Hughes " pos base a\n" 1085*e1fe3e4aSElliott Hughes " <anchor 250 450> mark @top;\n" 1086*e1fe3e4aSElliott Hughes "} TestLookup;\n", 1087*e1fe3e4aSElliott Hughes ) 1088*e1fe3e4aSElliott Hughes 1089*e1fe3e4aSElliott Hughes def test_def_anchor_multi_component(self): 1090*e1fe3e4aSElliott Hughes fea = self.parse( 1091*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "TestLookup" PROCESS_BASE PROCESS_MARKS ALL ' 1092*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 1093*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 1094*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 1095*e1fe3e4aSElliott Hughes "AS_POSITION\n" 1096*e1fe3e4aSElliott Hughes 'ATTACH GLYPH "f_f"\n' 1097*e1fe3e4aSElliott Hughes 'TO GLYPH "acutecomb" AT ANCHOR "top"\n' 1098*e1fe3e4aSElliott Hughes "END_ATTACH\n" 1099*e1fe3e4aSElliott Hughes "END_POSITION\n" 1100*e1fe3e4aSElliott Hughes 'DEF_GLYPH "f_f" ID 120 TYPE LIGATURE COMPONENTS 2 END_GLYPH\n' 1101*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "top" ON 120 GLYPH f_f ' 1102*e1fe3e4aSElliott Hughes "COMPONENT 1 AT POS DX 250 DY 450 END_POS END_ANCHOR\n" 1103*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "top" ON 120 GLYPH f_f ' 1104*e1fe3e4aSElliott Hughes "COMPONENT 2 AT POS DX 450 DY 450 END_POS END_ANCHOR\n" 1105*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "MARK_top" ON 120 GLYPH acutecomb ' 1106*e1fe3e4aSElliott Hughes "COMPONENT 1 AT POS END_POS END_ANCHOR" 1107*e1fe3e4aSElliott Hughes ) 1108*e1fe3e4aSElliott Hughes self.assertEqual( 1109*e1fe3e4aSElliott Hughes fea, 1110*e1fe3e4aSElliott Hughes "\n# Mark classes\n" 1111*e1fe3e4aSElliott Hughes "markClass acutecomb <anchor 0 0> @top;\n" 1112*e1fe3e4aSElliott Hughes "\n" 1113*e1fe3e4aSElliott Hughes "# Lookups\n" 1114*e1fe3e4aSElliott Hughes "lookup TestLookup {\n" 1115*e1fe3e4aSElliott Hughes " pos ligature f_f\n" 1116*e1fe3e4aSElliott Hughes " <anchor 250 450> mark @top\n" 1117*e1fe3e4aSElliott Hughes " ligComponent\n" 1118*e1fe3e4aSElliott Hughes " <anchor 450 450> mark @top;\n" 1119*e1fe3e4aSElliott Hughes "} TestLookup;\n" 1120*e1fe3e4aSElliott Hughes "\n" 1121*e1fe3e4aSElliott Hughes "@GDEF_ligature = [f_f];\n" 1122*e1fe3e4aSElliott Hughes "table GDEF {\n" 1123*e1fe3e4aSElliott Hughes " GlyphClassDef , @GDEF_ligature, , ;\n" 1124*e1fe3e4aSElliott Hughes "} GDEF;\n", 1125*e1fe3e4aSElliott Hughes ) 1126*e1fe3e4aSElliott Hughes 1127*e1fe3e4aSElliott Hughes def test_anchor_adjust_device(self): 1128*e1fe3e4aSElliott Hughes fea = self.parse( 1129*e1fe3e4aSElliott Hughes 'DEF_ANCHOR "MARK_top" ON 123 GLYPH diacglyph ' 1130*e1fe3e4aSElliott Hughes "COMPONENT 1 AT POS DX 0 DY 456 ADJUST_BY 12 AT 34 " 1131*e1fe3e4aSElliott Hughes "ADJUST_BY 56 AT 78 END_POS END_ANCHOR" 1132*e1fe3e4aSElliott Hughes ) 1133*e1fe3e4aSElliott Hughes self.assertEqual( 1134*e1fe3e4aSElliott Hughes fea, 1135*e1fe3e4aSElliott Hughes "\n# Mark classes\n" 1136*e1fe3e4aSElliott Hughes "#markClass diacglyph <anchor 0 456 <device NULL>" 1137*e1fe3e4aSElliott Hughes " <device 34 12, 78 56>> @top;", 1138*e1fe3e4aSElliott Hughes ) 1139*e1fe3e4aSElliott Hughes 1140*e1fe3e4aSElliott Hughes def test_use_extension(self): 1141*e1fe3e4aSElliott Hughes fea = self.parse( 1142*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "kern1" PROCESS_BASE PROCESS_MARKS ALL ' 1143*e1fe3e4aSElliott Hughes "DIRECTION LTR\n" 1144*e1fe3e4aSElliott Hughes "IN_CONTEXT\n" 1145*e1fe3e4aSElliott Hughes "END_CONTEXT\n" 1146*e1fe3e4aSElliott Hughes "AS_POSITION\n" 1147*e1fe3e4aSElliott Hughes "ADJUST_PAIR\n" 1148*e1fe3e4aSElliott Hughes ' FIRST GLYPH "A" FIRST GLYPH "V"\n' 1149*e1fe3e4aSElliott Hughes ' SECOND GLYPH "A" SECOND GLYPH "V"\n' 1150*e1fe3e4aSElliott Hughes " 1 2 BY POS ADV -30 END_POS POS END_POS\n" 1151*e1fe3e4aSElliott Hughes " 2 1 BY POS ADV -25 END_POS POS END_POS\n" 1152*e1fe3e4aSElliott Hughes "END_ADJUST\n" 1153*e1fe3e4aSElliott Hughes "END_POSITION\n" 1154*e1fe3e4aSElliott Hughes "COMPILER_USEEXTENSIONLOOKUPS\n" 1155*e1fe3e4aSElliott Hughes ) 1156*e1fe3e4aSElliott Hughes self.assertEqual( 1157*e1fe3e4aSElliott Hughes fea, 1158*e1fe3e4aSElliott Hughes "\n# Lookups\n" 1159*e1fe3e4aSElliott Hughes "lookup kern1 useExtension {\n" 1160*e1fe3e4aSElliott Hughes " enum pos A V -30;\n" 1161*e1fe3e4aSElliott Hughes " enum pos V A -25;\n" 1162*e1fe3e4aSElliott Hughes "} kern1;\n", 1163*e1fe3e4aSElliott Hughes ) 1164*e1fe3e4aSElliott Hughes 1165*e1fe3e4aSElliott Hughes def test_unsupported_compiler_flags(self): 1166*e1fe3e4aSElliott Hughes with self.assertLogs(level="WARNING") as logs: 1167*e1fe3e4aSElliott Hughes fea = self.parse("CMAP_FORMAT 0 3 4") 1168*e1fe3e4aSElliott Hughes self.assertEqual(fea, "") 1169*e1fe3e4aSElliott Hughes self.assertEqual( 1170*e1fe3e4aSElliott Hughes logs.output, 1171*e1fe3e4aSElliott Hughes [ 1172*e1fe3e4aSElliott Hughes "WARNING:fontTools.voltLib.voltToFea:Unsupported setting ignored: CMAP_FORMAT" 1173*e1fe3e4aSElliott Hughes ], 1174*e1fe3e4aSElliott Hughes ) 1175*e1fe3e4aSElliott Hughes 1176*e1fe3e4aSElliott Hughes def test_sanitize_lookup_name(self): 1177*e1fe3e4aSElliott Hughes fea = self.parse( 1178*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "Test Lookup" PROCESS_BASE PROCESS_MARKS ALL ' 1179*e1fe3e4aSElliott Hughes "DIRECTION LTR IN_CONTEXT END_CONTEXT\n" 1180*e1fe3e4aSElliott Hughes "AS_POSITION ADJUST_PAIR END_ADJUST END_POSITION\n" 1181*e1fe3e4aSElliott Hughes 'DEF_LOOKUP "Test-Lookup" PROCESS_BASE PROCESS_MARKS ALL ' 1182*e1fe3e4aSElliott Hughes "DIRECTION LTR IN_CONTEXT END_CONTEXT\n" 1183*e1fe3e4aSElliott Hughes "AS_POSITION ADJUST_PAIR END_ADJUST END_POSITION\n" 1184*e1fe3e4aSElliott Hughes ) 1185*e1fe3e4aSElliott Hughes self.assertEqual( 1186*e1fe3e4aSElliott Hughes fea, 1187*e1fe3e4aSElliott Hughes "\n# Lookups\n" 1188*e1fe3e4aSElliott Hughes "lookup Test_Lookup {\n" 1189*e1fe3e4aSElliott Hughes " \n" 1190*e1fe3e4aSElliott Hughes "} Test_Lookup;\n" 1191*e1fe3e4aSElliott Hughes "\n" 1192*e1fe3e4aSElliott Hughes "lookup Test_Lookup_ {\n" 1193*e1fe3e4aSElliott Hughes " \n" 1194*e1fe3e4aSElliott Hughes "} Test_Lookup_;\n", 1195*e1fe3e4aSElliott Hughes ) 1196*e1fe3e4aSElliott Hughes 1197*e1fe3e4aSElliott Hughes def test_sanitize_group_name(self): 1198*e1fe3e4aSElliott Hughes fea = self.parse( 1199*e1fe3e4aSElliott Hughes 'DEF_GROUP "aaccented glyphs"\n' 1200*e1fe3e4aSElliott Hughes 'ENUM GLYPH "aacute" GLYPH "abreve" END_ENUM\n' 1201*e1fe3e4aSElliott Hughes "END_GROUP\n" 1202*e1fe3e4aSElliott Hughes 'DEF_GROUP "aaccented+glyphs"\n' 1203*e1fe3e4aSElliott Hughes 'ENUM GLYPH "aacute" GLYPH "abreve" END_ENUM\n' 1204*e1fe3e4aSElliott Hughes "END_GROUP\n" 1205*e1fe3e4aSElliott Hughes ) 1206*e1fe3e4aSElliott Hughes self.assertEqual( 1207*e1fe3e4aSElliott Hughes fea, 1208*e1fe3e4aSElliott Hughes "# Glyph classes\n" 1209*e1fe3e4aSElliott Hughes "@aaccented_glyphs = [aacute abreve];\n" 1210*e1fe3e4aSElliott Hughes "@aaccented_glyphs_ = [aacute abreve];", 1211*e1fe3e4aSElliott Hughes ) 1212*e1fe3e4aSElliott Hughes 1213*e1fe3e4aSElliott Hughes def test_cli_vtp(self): 1214*e1fe3e4aSElliott Hughes vtp = DATADIR / "Nutso.vtp" 1215*e1fe3e4aSElliott Hughes fea = DATADIR / "Nutso.fea" 1216*e1fe3e4aSElliott Hughes self.cli(vtp, fea) 1217*e1fe3e4aSElliott Hughes 1218*e1fe3e4aSElliott Hughes def test_group_order(self): 1219*e1fe3e4aSElliott Hughes vtp = DATADIR / "NamdhinggoSIL1006.vtp" 1220*e1fe3e4aSElliott Hughes fea = DATADIR / "NamdhinggoSIL1006.fea" 1221*e1fe3e4aSElliott Hughes self.cli(vtp, fea) 1222*e1fe3e4aSElliott Hughes 1223*e1fe3e4aSElliott Hughes def test_cli_ttf(self): 1224*e1fe3e4aSElliott Hughes ttf = DATADIR / "Nutso.ttf" 1225*e1fe3e4aSElliott Hughes fea = DATADIR / "Nutso.fea" 1226*e1fe3e4aSElliott Hughes self.cli(ttf, fea) 1227*e1fe3e4aSElliott Hughes 1228*e1fe3e4aSElliott Hughes def test_cli_ttf_no_TSIV(self): 1229*e1fe3e4aSElliott Hughes from fontTools.voltLib.voltToFea import main as cli 1230*e1fe3e4aSElliott Hughes 1231*e1fe3e4aSElliott Hughes ttf = DATADIR / "Empty.ttf" 1232*e1fe3e4aSElliott Hughes temp = self.temp_path() 1233*e1fe3e4aSElliott Hughes self.assertEqual(1, cli([str(ttf), str(temp)])) 1234*e1fe3e4aSElliott Hughes 1235*e1fe3e4aSElliott Hughes def cli(self, source, fea): 1236*e1fe3e4aSElliott Hughes from fontTools.voltLib.voltToFea import main as cli 1237*e1fe3e4aSElliott Hughes 1238*e1fe3e4aSElliott Hughes temp = self.temp_path() 1239*e1fe3e4aSElliott Hughes cli([str(source), str(temp)]) 1240*e1fe3e4aSElliott Hughes with temp.open() as f: 1241*e1fe3e4aSElliott Hughes res = f.read() 1242*e1fe3e4aSElliott Hughes with fea.open() as f: 1243*e1fe3e4aSElliott Hughes ref = f.read() 1244*e1fe3e4aSElliott Hughes self.assertEqual(ref, res) 1245*e1fe3e4aSElliott Hughes 1246*e1fe3e4aSElliott Hughes def parse(self, text): 1247*e1fe3e4aSElliott Hughes return VoltToFea(StringIO(text)).convert() 1248*e1fe3e4aSElliott Hughes 1249*e1fe3e4aSElliott Hughes 1250*e1fe3e4aSElliott Hughesif __name__ == "__main__": 1251*e1fe3e4aSElliott Hughes import sys 1252*e1fe3e4aSElliott Hughes 1253*e1fe3e4aSElliott Hughes sys.exit(unittest.main()) 1254