• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from fontTools.ttLib import TTFont
2from fontTools.varLib import build
3from fontTools.varLib.interpolate_layout import interpolate_layout
4from fontTools.varLib.interpolate_layout import main as interpolate_layout_main
5from fontTools.designspaceLib import DesignSpaceDocument, DesignSpaceDocumentError
6from fontTools.feaLib.builder import addOpenTypeFeaturesFromString
7import difflib
8import os
9import shutil
10import sys
11import tempfile
12import unittest
13
14
15class InterpolateLayoutTest(unittest.TestCase):
16    def __init__(self, methodName):
17        unittest.TestCase.__init__(self, methodName)
18        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
19        # and fires deprecation warnings if a program uses the old name.
20        if not hasattr(self, "assertRaisesRegex"):
21            self.assertRaisesRegex = self.assertRaisesRegexp
22
23    def setUp(self):
24        self.tempdir = None
25        self.num_tempfiles = 0
26
27    def tearDown(self):
28        if self.tempdir:
29            shutil.rmtree(self.tempdir)
30
31    @staticmethod
32    def get_test_input(test_file_or_folder):
33        path, _ = os.path.split(__file__)
34        return os.path.join(path, "data", test_file_or_folder)
35
36    @staticmethod
37    def get_test_output(test_file_or_folder):
38        path, _ = os.path.split(__file__)
39        return os.path.join(path, "data", "test_results", test_file_or_folder)
40
41    @staticmethod
42    def get_file_list(folder, suffix, prefix=''):
43        all_files = os.listdir(folder)
44        file_list = []
45        for p in all_files:
46            if p.startswith(prefix) and p.endswith(suffix):
47                file_list.append(os.path.abspath(os.path.join(folder, p)))
48        return file_list
49
50    def temp_path(self, suffix):
51        self.temp_dir()
52        self.num_tempfiles += 1
53        return os.path.join(self.tempdir,
54                            "tmp%d%s" % (self.num_tempfiles, suffix))
55
56    def temp_dir(self):
57        if not self.tempdir:
58            self.tempdir = tempfile.mkdtemp()
59
60    def read_ttx(self, path):
61        lines = []
62        with open(path, "r", encoding="utf-8") as ttx:
63            for line in ttx.readlines():
64                # Elide ttFont attributes because ttLibVersion may change.
65                if line.startswith("<ttFont "):
66                    lines.append("<ttFont>\n")
67                else:
68                    lines.append(line.rstrip() + "\n")
69        return lines
70
71    def expect_ttx(self, font, expected_ttx, tables):
72        path = self.temp_path(suffix=".ttx")
73        font.saveXML(path, tables=tables)
74        actual = self.read_ttx(path)
75        expected = self.read_ttx(expected_ttx)
76        if actual != expected:
77            for line in difflib.unified_diff(
78                    expected, actual, fromfile=expected_ttx, tofile=path):
79                sys.stdout.write(line)
80            self.fail("TTX output is different from expected")
81
82    def check_ttx_dump(self, font, expected_ttx, tables, suffix):
83        """Ensure the TTX dump is the same after saving and reloading the font."""
84        path = self.temp_path(suffix=suffix)
85        font.save(path)
86        self.expect_ttx(TTFont(path), expected_ttx, tables)
87
88    def compile_font(self, path, suffix, temp_dir, features=None):
89        ttx_filename = os.path.basename(path)
90        savepath = os.path.join(temp_dir, ttx_filename.replace('.ttx', suffix))
91        font = TTFont(recalcBBoxes=False, recalcTimestamp=False)
92        font.importXML(path)
93        if features:
94            addOpenTypeFeaturesFromString(font, features)
95        font.save(savepath, reorderTables=None)
96        return font, savepath
97
98# -----
99# Tests
100# -----
101
102    def test_varlib_interpolate_layout_GSUB_only_ttf(self):
103        """Only GSUB, and only in the base master.
104
105        The variable font will inherit the GSUB table from the
106        base master.
107        """
108        suffix = '.ttf'
109        ds_path = self.get_test_input('InterpolateLayout.designspace')
110        ufo_dir = self.get_test_input('master_ufo')
111        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
112
113        self.temp_dir()
114        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
115        for path in ttx_paths:
116            self.compile_font(path, suffix, self.tempdir)
117
118        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
119        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
120
121        tables = ['GSUB']
122        expected_ttx_path = self.get_test_output('InterpolateLayout.ttx')
123        self.expect_ttx(instfont, expected_ttx_path, tables)
124        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
125
126
127    def test_varlib_interpolate_layout_no_GSUB_ttf(self):
128        """The base master has no GSUB table.
129
130        The variable font will end up without a GSUB table.
131        """
132        suffix = '.ttf'
133        ds_path = self.get_test_input('InterpolateLayout2.designspace')
134        ufo_dir = self.get_test_input('master_ufo')
135        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
136
137        self.temp_dir()
138        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
139        for path in ttx_paths:
140            self.compile_font(path, suffix, self.tempdir)
141
142        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
143        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
144
145        tables = ['GSUB']
146        expected_ttx_path = self.get_test_output('InterpolateLayout2.ttx')
147        self.expect_ttx(instfont, expected_ttx_path, tables)
148        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
149
150
151    def test_varlib_interpolate_layout_GSUB_only_no_axes_ttf(self):
152        """Only GSUB, and only in the base master.
153        Designspace file has no <axes> element.
154
155        The variable font will inherit the GSUB table from the
156        base master.
157        """
158        ds_path = self.get_test_input('InterpolateLayout3.designspace')
159        with self.assertRaisesRegex(DesignSpaceDocumentError, "No axes defined"):
160            instfont = interpolate_layout(ds_path, {'weight': 500})
161
162    def test_varlib_interpolate_layout_GPOS_only_size_feat_same_val_ttf(self):
163        """Only GPOS; 'size' feature; same values in all masters.
164        """
165        suffix = '.ttf'
166        ds_path = self.get_test_input('InterpolateLayout.designspace')
167        ufo_dir = self.get_test_input('master_ufo')
168        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
169
170        fea_str = """
171        feature size {
172            parameters 10.0 0;
173        } size;
174        """
175        features = [fea_str] * 2
176
177        self.temp_dir()
178        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
179        for i, path in enumerate(ttx_paths):
180            self.compile_font(path, suffix, self.tempdir, features[i])
181
182        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
183        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
184
185        tables = ['GPOS']
186        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_size_feat_same.ttx')
187        self.expect_ttx(instfont, expected_ttx_path, tables)
188        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
189
190
191    def test_varlib_interpolate_layout_GPOS_only_LookupType_1_same_val_ttf(self):
192        """Only GPOS; LookupType 1; same values in all masters.
193        """
194        suffix = '.ttf'
195        ds_path = self.get_test_input('InterpolateLayout.designspace')
196        ufo_dir = self.get_test_input('master_ufo')
197        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
198
199        fea_str = """
200        feature xxxx {
201            pos A <-80 0 -160 0>;
202        } xxxx;
203        """
204        features = [fea_str] * 2
205
206        self.temp_dir()
207        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
208        for i, path in enumerate(ttx_paths):
209            self.compile_font(path, suffix, self.tempdir, features[i])
210
211        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
212        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
213
214        tables = ['GPOS']
215        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_1_same.ttx')
216        self.expect_ttx(instfont, expected_ttx_path, tables)
217        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
218
219
220    def test_varlib_interpolate_layout_GPOS_only_LookupType_1_diff_val_ttf(self):
221        """Only GPOS; LookupType 1; different values in each master.
222        """
223        suffix = '.ttf'
224        ds_path = self.get_test_input('InterpolateLayout.designspace')
225        ufo_dir = self.get_test_input('master_ufo')
226        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
227
228        fea_str_0 = """
229        feature xxxx {
230            pos A <-80 0 -160 0>;
231        } xxxx;
232        """
233        fea_str_1 = """
234        feature xxxx {
235            pos A <-97 0 -195 0>;
236        } xxxx;
237        """
238        features = [fea_str_0, fea_str_1]
239
240        self.temp_dir()
241        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
242        for i, path in enumerate(ttx_paths):
243            self.compile_font(path, suffix, self.tempdir, features[i])
244
245        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
246        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
247
248        tables = ['GPOS']
249        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_1_diff.ttx')
250        self.expect_ttx(instfont, expected_ttx_path, tables)
251        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
252
253
254    def test_varlib_interpolate_layout_GPOS_only_LookupType_1_diff2_val_ttf(self):
255        """Only GPOS; LookupType 1; different values and items in each master.
256        """
257        suffix = '.ttf'
258        ds_path = self.get_test_input('InterpolateLayout.designspace')
259        ufo_dir = self.get_test_input('master_ufo')
260        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
261
262        fea_str_0 = """
263        feature xxxx {
264            pos A <-80 0 -160 0>;
265            pos a <-55 0 -105 0>;
266        } xxxx;
267        """
268        fea_str_1 = """
269        feature xxxx {
270            pos A <-97 0 -195 0>;
271        } xxxx;
272        """
273        features = [fea_str_0, fea_str_1]
274
275        self.temp_dir()
276        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
277        for i, path in enumerate(ttx_paths):
278            self.compile_font(path, suffix, self.tempdir, features[i])
279
280        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
281        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
282
283        tables = ['GPOS']
284        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_1_diff2.ttx')
285        self.expect_ttx(instfont, expected_ttx_path, tables)
286        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
287
288
289    def test_varlib_interpolate_layout_GPOS_only_LookupType_2_spec_pairs_same_val_ttf(self):
290        """Only GPOS; LookupType 2 specific pairs; same values in all masters.
291        """
292        suffix = '.ttf'
293        ds_path = self.get_test_input('InterpolateLayout.designspace')
294        ufo_dir = self.get_test_input('master_ufo')
295        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
296
297        fea_str = """
298        feature xxxx {
299            pos A a -53;
300        } xxxx;
301        """
302        features = [fea_str] * 2
303
304        self.temp_dir()
305        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
306        for i, path in enumerate(ttx_paths):
307            self.compile_font(path, suffix, self.tempdir, features[i])
308
309        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
310        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
311
312        tables = ['GPOS']
313        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_spec_same.ttx')
314        self.expect_ttx(instfont, expected_ttx_path, tables)
315        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
316
317
318    def test_varlib_interpolate_layout_GPOS_only_LookupType_2_spec_pairs_diff_val_ttf(self):
319        """Only GPOS; LookupType 2 specific pairs; different values in each master.
320        """
321        suffix = '.ttf'
322        ds_path = self.get_test_input('InterpolateLayout.designspace')
323        ufo_dir = self.get_test_input('master_ufo')
324        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
325
326        fea_str_0 = """
327        feature xxxx {
328            pos A a -53;
329        } xxxx;
330        """
331        fea_str_1 = """
332        feature xxxx {
333            pos A a -27;
334        } xxxx;
335        """
336        features = [fea_str_0, fea_str_1]
337
338        self.temp_dir()
339        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
340        for i, path in enumerate(ttx_paths):
341            self.compile_font(path, suffix, self.tempdir, features[i])
342
343        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
344        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
345
346        tables = ['GPOS']
347        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_spec_diff.ttx')
348        self.expect_ttx(instfont, expected_ttx_path, tables)
349        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
350
351
352    def test_varlib_interpolate_layout_GPOS_only_LookupType_2_spec_pairs_diff2_val_ttf(self):
353        """Only GPOS; LookupType 2 specific pairs; different values and items in each master.
354        """
355        suffix = '.ttf'
356        ds_path = self.get_test_input('InterpolateLayout.designspace')
357        ufo_dir = self.get_test_input('master_ufo')
358        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
359
360        fea_str_0 = """
361        feature xxxx {
362            pos A a -53;
363        } xxxx;
364        """
365        fea_str_1 = """
366        feature xxxx {
367            pos A a -27;
368            pos a a 19;
369        } xxxx;
370        """
371        features = [fea_str_0, fea_str_1]
372
373        self.temp_dir()
374        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
375        for i, path in enumerate(ttx_paths):
376            self.compile_font(path, suffix, self.tempdir, features[i])
377
378        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
379        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
380
381        tables = ['GPOS']
382        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_spec_diff2.ttx')
383        self.expect_ttx(instfont, expected_ttx_path, tables)
384        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
385
386
387    def test_varlib_interpolate_layout_GPOS_only_LookupType_2_class_pairs_same_val_ttf(self):
388        """Only GPOS; LookupType 2 class pairs; same values in all masters.
389        """
390        suffix = '.ttf'
391        ds_path = self.get_test_input('InterpolateLayout.designspace')
392        ufo_dir = self.get_test_input('master_ufo')
393        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
394
395        fea_str = """
396        feature xxxx {
397            pos [A] [a] -53;
398        } xxxx;
399        """
400        features = [fea_str] * 2
401
402        self.temp_dir()
403        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
404        for i, path in enumerate(ttx_paths):
405            self.compile_font(path, suffix, self.tempdir, features[i])
406
407        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
408        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
409
410        tables = ['GPOS']
411        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_class_same.ttx')
412        self.expect_ttx(instfont, expected_ttx_path, tables)
413        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
414
415
416    def test_varlib_interpolate_layout_GPOS_only_LookupType_2_class_pairs_diff_val_ttf(self):
417        """Only GPOS; LookupType 2 class pairs; different values in each master.
418        """
419        suffix = '.ttf'
420        ds_path = self.get_test_input('InterpolateLayout.designspace')
421        ufo_dir = self.get_test_input('master_ufo')
422        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
423
424        fea_str_0 = """
425        feature xxxx {
426            pos [A] [a] -53;
427        } xxxx;
428        """
429        fea_str_1 = """
430        feature xxxx {
431            pos [A] [a] -27;
432        } xxxx;
433        """
434        features = [fea_str_0, fea_str_1]
435
436        self.temp_dir()
437        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
438        for i, path in enumerate(ttx_paths):
439            self.compile_font(path, suffix, self.tempdir, features[i])
440
441        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
442        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
443
444        tables = ['GPOS']
445        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_class_diff.ttx')
446        self.expect_ttx(instfont, expected_ttx_path, tables)
447        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
448
449
450    def test_varlib_interpolate_layout_GPOS_only_LookupType_2_class_pairs_diff2_val_ttf(self):
451        """Only GPOS; LookupType 2 class pairs; different values and items in each master.
452        """
453        suffix = '.ttf'
454        ds_path = self.get_test_input('InterpolateLayout.designspace')
455        ufo_dir = self.get_test_input('master_ufo')
456        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
457
458        fea_str_0 = """
459        feature xxxx {
460            pos [A] [a] -53;
461        } xxxx;
462        """
463        fea_str_1 = """
464        feature xxxx {
465            pos [A] [a] -27;
466            pos [a] [a] 19;
467        } xxxx;
468        """
469        features = [fea_str_0, fea_str_1]
470
471        self.temp_dir()
472        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
473        for i, path in enumerate(ttx_paths):
474            self.compile_font(path, suffix, self.tempdir, features[i])
475
476        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
477        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
478
479        tables = ['GPOS']
480        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_2_class_diff2.ttx')
481        self.expect_ttx(instfont, expected_ttx_path, tables)
482        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
483
484
485    def test_varlib_interpolate_layout_GPOS_only_LookupType_3_same_val_ttf(self):
486        """Only GPOS; LookupType 3; same values in all masters.
487        """
488        suffix = '.ttf'
489        ds_path = self.get_test_input('InterpolateLayout.designspace')
490        ufo_dir = self.get_test_input('master_ufo')
491        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
492
493        fea_str = """
494        feature xxxx {
495            pos cursive a <anchor 60 15> <anchor 405 310>;
496        } xxxx;
497        """
498        features = [fea_str] * 2
499
500        self.temp_dir()
501        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
502        for i, path in enumerate(ttx_paths):
503            self.compile_font(path, suffix, self.tempdir, features[i])
504
505        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
506        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
507
508        tables = ['GPOS']
509        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_3_same.ttx')
510        self.expect_ttx(instfont, expected_ttx_path, tables)
511        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
512
513
514    def test_varlib_interpolate_layout_GPOS_only_LookupType_3_diff_val_ttf(self):
515        """Only GPOS; LookupType 3; different values in each master.
516        """
517        suffix = '.ttf'
518        ds_path = self.get_test_input('InterpolateLayout.designspace')
519        ufo_dir = self.get_test_input('master_ufo')
520        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
521
522        fea_str_0 = """
523        feature xxxx {
524            pos cursive a <anchor 60 15> <anchor 405 310>;
525        } xxxx;
526        """
527        fea_str_1 = """
528        feature xxxx {
529            pos cursive a <anchor 38 42> <anchor 483 279>;
530        } xxxx;
531        """
532        features = [fea_str_0, fea_str_1]
533
534        self.temp_dir()
535        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
536        for i, path in enumerate(ttx_paths):
537            self.compile_font(path, suffix, self.tempdir, features[i])
538
539        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
540        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
541
542        tables = ['GPOS']
543        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_3_diff.ttx')
544        self.expect_ttx(instfont, expected_ttx_path, tables)
545        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
546
547
548    def test_varlib_interpolate_layout_GPOS_only_LookupType_4_same_val_ttf(self):
549        """Only GPOS; LookupType 4; same values in all masters.
550        """
551        suffix = '.ttf'
552        ds_path = self.get_test_input('InterpolateLayout.designspace')
553        ufo_dir = self.get_test_input('master_ufo')
554        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
555
556        fea_str = """
557        markClass uni0303 <anchor 0 500> @MARKS_ABOVE;
558        feature xxxx {
559            pos base a <anchor 260 500> mark @MARKS_ABOVE;
560        } xxxx;
561        """
562        features = [fea_str] * 2
563
564        self.temp_dir()
565        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
566        for i, path in enumerate(ttx_paths):
567            self.compile_font(path, suffix, self.tempdir, features[i])
568
569        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
570        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
571
572        tables = ['GPOS']
573        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_4_same.ttx')
574        self.expect_ttx(instfont, expected_ttx_path, tables)
575        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
576
577
578    def test_varlib_interpolate_layout_GPOS_only_LookupType_4_diff_val_ttf(self):
579        """Only GPOS; LookupType 4; different values in each master.
580        """
581        suffix = '.ttf'
582        ds_path = self.get_test_input('InterpolateLayout.designspace')
583        ufo_dir = self.get_test_input('master_ufo')
584        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
585
586        fea_str_0 = """
587        markClass uni0303 <anchor 0 500> @MARKS_ABOVE;
588        feature xxxx {
589            pos base a <anchor 260 500> mark @MARKS_ABOVE;
590        } xxxx;
591        """
592        fea_str_1 = """
593        markClass uni0303 <anchor 0 520> @MARKS_ABOVE;
594        feature xxxx {
595            pos base a <anchor 285 520> mark @MARKS_ABOVE;
596        } xxxx;
597        """
598        features = [fea_str_0, fea_str_1]
599
600        self.temp_dir()
601        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
602        for i, path in enumerate(ttx_paths):
603            self.compile_font(path, suffix, self.tempdir, features[i])
604
605        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
606        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
607
608        tables = ['GPOS']
609        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_4_diff.ttx')
610        self.expect_ttx(instfont, expected_ttx_path, tables)
611        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
612
613
614    def test_varlib_interpolate_layout_GPOS_only_LookupType_5_same_val_ttf(self):
615        """Only GPOS; LookupType 5; same values in all masters.
616        """
617        suffix = '.ttf'
618        ds_path = self.get_test_input('InterpolateLayout.designspace')
619        ufo_dir = self.get_test_input('master_ufo')
620        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
621
622        fea_str = """
623        markClass uni0330 <anchor 0 -50> @MARKS_BELOW;
624        feature xxxx {
625            pos ligature f_t <anchor 115 -50> mark @MARKS_BELOW
626                ligComponent <anchor 430 -50> mark @MARKS_BELOW;
627        } xxxx;
628        """
629        features = [fea_str] * 2
630
631        self.temp_dir()
632        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
633        for i, path in enumerate(ttx_paths):
634            self.compile_font(path, suffix, self.tempdir, features[i])
635
636        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
637        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
638
639        tables = ['GPOS']
640        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_5_same.ttx')
641        self.expect_ttx(instfont, expected_ttx_path, tables)
642        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
643
644
645    def test_varlib_interpolate_layout_GPOS_only_LookupType_5_diff_val_ttf(self):
646        """Only GPOS; LookupType 5; different values in each master.
647        """
648        suffix = '.ttf'
649        ds_path = self.get_test_input('InterpolateLayout.designspace')
650        ufo_dir = self.get_test_input('master_ufo')
651        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
652
653        fea_str_0 = """
654        markClass uni0330 <anchor 0 -50> @MARKS_BELOW;
655        feature xxxx {
656            pos ligature f_t <anchor 115 -50> mark @MARKS_BELOW
657                ligComponent <anchor 430 -50> mark @MARKS_BELOW;
658        } xxxx;
659        """
660        fea_str_1 = """
661        markClass uni0330 <anchor 0 -20> @MARKS_BELOW;
662        feature xxxx {
663            pos ligature f_t <anchor 173 -20> mark @MARKS_BELOW
664                ligComponent <anchor 577 -20> mark @MARKS_BELOW;
665        } xxxx;
666        """
667        features = [fea_str_0, fea_str_1]
668
669        self.temp_dir()
670        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
671        for i, path in enumerate(ttx_paths):
672            self.compile_font(path, suffix, self.tempdir, features[i])
673
674        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
675        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
676
677        tables = ['GPOS']
678        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_5_diff.ttx')
679        self.expect_ttx(instfont, expected_ttx_path, tables)
680        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
681
682
683    def test_varlib_interpolate_layout_GPOS_only_LookupType_6_same_val_ttf(self):
684        """Only GPOS; LookupType 6; same values in all masters.
685        """
686        suffix = '.ttf'
687        ds_path = self.get_test_input('InterpolateLayout.designspace')
688        ufo_dir = self.get_test_input('master_ufo')
689        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
690
691        fea_str = """
692        markClass uni0303 <anchor 0 500> @MARKS_ABOVE;
693        feature xxxx {
694            pos mark uni0308 <anchor 0 675> mark @MARKS_ABOVE;
695        } xxxx;
696        """
697        features = [fea_str] * 2
698
699        self.temp_dir()
700        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
701        for i, path in enumerate(ttx_paths):
702            self.compile_font(path, suffix, self.tempdir, features[i])
703
704        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
705        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
706
707        tables = ['GPOS']
708        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_6_same.ttx')
709        self.expect_ttx(instfont, expected_ttx_path, tables)
710        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
711
712
713    def test_varlib_interpolate_layout_GPOS_only_LookupType_6_diff_val_ttf(self):
714        """Only GPOS; LookupType 6; different values in each master.
715        """
716        suffix = '.ttf'
717        ds_path = self.get_test_input('InterpolateLayout.designspace')
718        ufo_dir = self.get_test_input('master_ufo')
719        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
720
721        fea_str_0 = """
722        markClass uni0303 <anchor 0 500> @MARKS_ABOVE;
723        feature xxxx {
724            pos mark uni0308 <anchor 0 675> mark @MARKS_ABOVE;
725        } xxxx;
726        """
727        fea_str_1 = """
728        markClass uni0303 <anchor 0 520> @MARKS_ABOVE;
729        feature xxxx {
730            pos mark uni0308 <anchor 0 730> mark @MARKS_ABOVE;
731        } xxxx;
732        """
733        features = [fea_str_0, fea_str_1]
734
735        self.temp_dir()
736        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
737        for i, path in enumerate(ttx_paths):
738            self.compile_font(path, suffix, self.tempdir, features[i])
739
740        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
741        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
742
743        tables = ['GPOS']
744        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_6_diff.ttx')
745        self.expect_ttx(instfont, expected_ttx_path, tables)
746        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
747
748
749    def test_varlib_interpolate_layout_GPOS_only_LookupType_7_same_val_ttf(self):
750        """Only GPOS; LookupType 7; same values in all masters.
751        """
752        suffix = '.ttf'
753        ds_path = self.get_test_input('InterpolateLayout.designspace')
754        ufo_dir = self.get_test_input('master_ufo')
755        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
756
757        fea_str = """
758        markClass uni0303 <anchor 0 500> @MARKS_ABOVE;
759        lookup CNTXT_PAIR_POS {
760            pos A a -23;
761        } CNTXT_PAIR_POS;
762
763        lookup CNTXT_MARK_TO_BASE {
764            pos base a <anchor 260 500> mark @MARKS_ABOVE;
765        } CNTXT_MARK_TO_BASE;
766
767        feature xxxx {
768            pos A' lookup CNTXT_PAIR_POS a' @MARKS_ABOVE' lookup CNTXT_MARK_TO_BASE;
769        } xxxx;
770        """
771        features = [fea_str] * 2
772
773        self.temp_dir()
774        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
775        for i, path in enumerate(ttx_paths):
776            self.compile_font(path, suffix, self.tempdir, features[i])
777
778        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
779        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
780
781        tables = ['GPOS']
782        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_7_same.ttx')
783        self.expect_ttx(instfont, expected_ttx_path, tables)
784        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
785
786
787    def test_varlib_interpolate_layout_GPOS_only_LookupType_7_diff_val_ttf(self):
788        """Only GPOS; LookupType 7; different values in each master.
789        """
790        suffix = '.ttf'
791        ds_path = self.get_test_input('InterpolateLayout.designspace')
792        ufo_dir = self.get_test_input('master_ufo')
793        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
794
795        fea_str_0 = """
796        markClass uni0303 <anchor 0 500> @MARKS_ABOVE;
797        lookup CNTXT_PAIR_POS {
798            pos A a -23;
799        } CNTXT_PAIR_POS;
800
801        lookup CNTXT_MARK_TO_BASE {
802            pos base a <anchor 260 500> mark @MARKS_ABOVE;
803        } CNTXT_MARK_TO_BASE;
804
805        feature xxxx {
806            pos A' lookup CNTXT_PAIR_POS a' @MARKS_ABOVE' lookup CNTXT_MARK_TO_BASE;
807        } xxxx;
808        """
809        fea_str_1 = """
810        markClass uni0303 <anchor 0 520> @MARKS_ABOVE;
811        lookup CNTXT_PAIR_POS {
812            pos A a 57;
813        } CNTXT_PAIR_POS;
814
815        lookup CNTXT_MARK_TO_BASE {
816            pos base a <anchor 285 520> mark @MARKS_ABOVE;
817        } CNTXT_MARK_TO_BASE;
818
819        feature xxxx {
820            pos A' lookup CNTXT_PAIR_POS a' @MARKS_ABOVE' lookup CNTXT_MARK_TO_BASE;
821        } xxxx;
822        """
823        features = [fea_str_0, fea_str_1]
824
825        self.temp_dir()
826        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-')
827        for i, path in enumerate(ttx_paths):
828            self.compile_font(path, suffix, self.tempdir, features[i])
829
830        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
831        instfont = interpolate_layout(ds_path, {'weight': 500}, finder)
832
833        tables = ['GPOS']
834        expected_ttx_path = self.get_test_output('InterpolateLayoutGPOS_7_diff.ttx')
835        self.expect_ttx(instfont, expected_ttx_path, tables)
836        self.check_ttx_dump(instfont, expected_ttx_path, tables, suffix)
837
838
839    def test_varlib_interpolate_layout_main_ttf(self):
840        """Mostly for testing varLib.interpolate_layout.main()
841        """
842        suffix = '.ttf'
843        ds_path = self.get_test_input('Build.designspace')
844        ufo_dir = self.get_test_input('master_ufo')
845        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')
846
847        self.temp_dir()
848        ttf_dir = os.path.join(self.tempdir, 'master_ttf_interpolatable')
849        os.makedirs(ttf_dir)
850        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily-')
851        for path in ttx_paths:
852            self.compile_font(path, suffix, ttf_dir)
853
854        finder = lambda s: s.replace(ufo_dir, ttf_dir).replace('.ufo', suffix)
855        varfont, _, _ = build(ds_path, finder)
856        varfont_name = 'InterpolateLayoutMain'
857        varfont_path = os.path.join(self.tempdir, varfont_name + suffix)
858        varfont.save(varfont_path)
859
860        ds_copy = os.path.splitext(varfont_path)[0] + '.designspace'
861        shutil.copy2(ds_path, ds_copy)
862        args = [ds_copy, 'weight=500', 'contrast=50']
863        interpolate_layout_main(args)
864
865        instfont_path = os.path.splitext(varfont_path)[0] + '-instance' + suffix
866        instfont = TTFont(instfont_path)
867        tables = [table_tag for table_tag in instfont.keys() if table_tag != 'head']
868        expected_ttx_path = self.get_test_output(varfont_name + '.ttx')
869        self.expect_ttx(instfont, expected_ttx_path, tables)
870
871
872if __name__ == "__main__":
873    sys.exit(unittest.main())
874