xref: /aosp_15_r20/external/fonttools/Tests/voltLib/volttofea_test.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
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