xref: /aosp_15_r20/external/fonttools/Tests/varLib/interpolatable_test.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1*e1fe3e4aSElliott Hughesfrom fontTools.ttLib import TTFont
2*e1fe3e4aSElliott Hughesfrom fontTools.varLib.interpolatable import main as interpolatable_main
3*e1fe3e4aSElliott Hughesimport os
4*e1fe3e4aSElliott Hughesimport shutil
5*e1fe3e4aSElliott Hughesimport sys
6*e1fe3e4aSElliott Hughesimport tempfile
7*e1fe3e4aSElliott Hughesimport unittest
8*e1fe3e4aSElliott Hughesimport pytest
9*e1fe3e4aSElliott Hughes
10*e1fe3e4aSElliott Hughestry:
11*e1fe3e4aSElliott Hughes    import scipy
12*e1fe3e4aSElliott Hughesexcept:
13*e1fe3e4aSElliott Hughes    scipy = None
14*e1fe3e4aSElliott Hughes
15*e1fe3e4aSElliott Hughestry:
16*e1fe3e4aSElliott Hughes    import munkres
17*e1fe3e4aSElliott Hughesexcept ImportError:
18*e1fe3e4aSElliott Hughes    munkres = None
19*e1fe3e4aSElliott Hughes
20*e1fe3e4aSElliott Hughes
21*e1fe3e4aSElliott Hughes@unittest.skipUnless(scipy or munkres, "scipy or munkres not installed")
22*e1fe3e4aSElliott Hughesclass InterpolatableTest(unittest.TestCase):
23*e1fe3e4aSElliott Hughes    def __init__(self, methodName):
24*e1fe3e4aSElliott Hughes        unittest.TestCase.__init__(self, methodName)
25*e1fe3e4aSElliott Hughes        # Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
26*e1fe3e4aSElliott Hughes        # and fires deprecation warnings if a program uses the old name.
27*e1fe3e4aSElliott Hughes        if not hasattr(self, "assertRaisesRegex"):
28*e1fe3e4aSElliott Hughes            self.assertRaisesRegex = self.assertRaisesRegexp
29*e1fe3e4aSElliott Hughes
30*e1fe3e4aSElliott Hughes    def setUp(self):
31*e1fe3e4aSElliott Hughes        self.tempdir = None
32*e1fe3e4aSElliott Hughes        self.num_tempfiles = 0
33*e1fe3e4aSElliott Hughes
34*e1fe3e4aSElliott Hughes    def tearDown(self):
35*e1fe3e4aSElliott Hughes        if self.tempdir:
36*e1fe3e4aSElliott Hughes            shutil.rmtree(self.tempdir)
37*e1fe3e4aSElliott Hughes
38*e1fe3e4aSElliott Hughes    @staticmethod
39*e1fe3e4aSElliott Hughes    def get_test_input(*test_file_or_folder):
40*e1fe3e4aSElliott Hughes        path, _ = os.path.split(__file__)
41*e1fe3e4aSElliott Hughes        return os.path.join(path, "data", *test_file_or_folder)
42*e1fe3e4aSElliott Hughes
43*e1fe3e4aSElliott Hughes    @staticmethod
44*e1fe3e4aSElliott Hughes    def get_file_list(folder, suffix, prefix=""):
45*e1fe3e4aSElliott Hughes        all_files = os.listdir(folder)
46*e1fe3e4aSElliott Hughes        file_list = []
47*e1fe3e4aSElliott Hughes        for p in all_files:
48*e1fe3e4aSElliott Hughes            if p.startswith(prefix) and p.endswith(suffix):
49*e1fe3e4aSElliott Hughes                file_list.append(os.path.abspath(os.path.join(folder, p)))
50*e1fe3e4aSElliott Hughes        return sorted(file_list)
51*e1fe3e4aSElliott Hughes
52*e1fe3e4aSElliott Hughes    def temp_path(self, suffix):
53*e1fe3e4aSElliott Hughes        self.temp_dir()
54*e1fe3e4aSElliott Hughes        self.num_tempfiles += 1
55*e1fe3e4aSElliott Hughes        return os.path.join(self.tempdir, "tmp%d%s" % (self.num_tempfiles, suffix))
56*e1fe3e4aSElliott Hughes
57*e1fe3e4aSElliott Hughes    def temp_dir(self):
58*e1fe3e4aSElliott Hughes        if not self.tempdir:
59*e1fe3e4aSElliott Hughes            self.tempdir = tempfile.mkdtemp()
60*e1fe3e4aSElliott Hughes
61*e1fe3e4aSElliott Hughes    def compile_font(self, path, suffix, temp_dir):
62*e1fe3e4aSElliott Hughes        ttx_filename = os.path.basename(path)
63*e1fe3e4aSElliott Hughes        savepath = os.path.join(temp_dir, ttx_filename.replace(".ttx", suffix))
64*e1fe3e4aSElliott Hughes        font = TTFont(recalcBBoxes=False, recalcTimestamp=False)
65*e1fe3e4aSElliott Hughes        font.importXML(path)
66*e1fe3e4aSElliott Hughes        font.save(savepath, reorderTables=None)
67*e1fe3e4aSElliott Hughes        return font, savepath
68*e1fe3e4aSElliott Hughes
69*e1fe3e4aSElliott Hughes    # -----
70*e1fe3e4aSElliott Hughes    # Tests
71*e1fe3e4aSElliott Hughes    # -----
72*e1fe3e4aSElliott Hughes
73*e1fe3e4aSElliott Hughes    def test_interpolatable_ttf(self):
74*e1fe3e4aSElliott Hughes        suffix = ".ttf"
75*e1fe3e4aSElliott Hughes        ttx_dir = self.get_test_input("master_ttx_interpolatable_ttf")
76*e1fe3e4aSElliott Hughes
77*e1fe3e4aSElliott Hughes        self.temp_dir()
78*e1fe3e4aSElliott Hughes        ttx_paths = self.get_file_list(ttx_dir, ".ttx", "TestFamily2-")
79*e1fe3e4aSElliott Hughes        for path in ttx_paths:
80*e1fe3e4aSElliott Hughes            self.compile_font(path, suffix, self.tempdir)
81*e1fe3e4aSElliott Hughes
82*e1fe3e4aSElliott Hughes        ttf_paths = self.get_file_list(self.tempdir, suffix)
83*e1fe3e4aSElliott Hughes        self.assertIsNone(interpolatable_main(ttf_paths))
84*e1fe3e4aSElliott Hughes
85*e1fe3e4aSElliott Hughes    def test_interpolatable_otf(self):
86*e1fe3e4aSElliott Hughes        suffix = ".otf"
87*e1fe3e4aSElliott Hughes        ttx_dir = self.get_test_input("master_ttx_interpolatable_otf")
88*e1fe3e4aSElliott Hughes
89*e1fe3e4aSElliott Hughes        self.temp_dir()
90*e1fe3e4aSElliott Hughes        ttx_paths = self.get_file_list(ttx_dir, ".ttx", "TestFamily2-")
91*e1fe3e4aSElliott Hughes        for path in ttx_paths:
92*e1fe3e4aSElliott Hughes            self.compile_font(path, suffix, self.tempdir)
93*e1fe3e4aSElliott Hughes
94*e1fe3e4aSElliott Hughes        otf_paths = self.get_file_list(self.tempdir, suffix)
95*e1fe3e4aSElliott Hughes        self.assertIsNone(interpolatable_main(otf_paths))
96*e1fe3e4aSElliott Hughes
97*e1fe3e4aSElliott Hughes    def test_interpolatable_ufo(self):
98*e1fe3e4aSElliott Hughes        ttx_dir = self.get_test_input("master_ufo")
99*e1fe3e4aSElliott Hughes        ufo_paths = self.get_file_list(ttx_dir, ".ufo", "TestFamily2-")
100*e1fe3e4aSElliott Hughes        self.assertIsNone(interpolatable_main(ufo_paths))
101*e1fe3e4aSElliott Hughes
102*e1fe3e4aSElliott Hughes    def test_designspace(self):
103*e1fe3e4aSElliott Hughes        designspace_path = self.get_test_input("InterpolateLayout.designspace")
104*e1fe3e4aSElliott Hughes        self.assertIsNone(interpolatable_main([designspace_path]))
105*e1fe3e4aSElliott Hughes
106*e1fe3e4aSElliott Hughes    def test_glyphsapp(self):
107*e1fe3e4aSElliott Hughes        pytest.importorskip("glyphsLib")
108*e1fe3e4aSElliott Hughes        glyphsapp_path = self.get_test_input("InterpolateLayout.glyphs")
109*e1fe3e4aSElliott Hughes        self.assertIsNone(interpolatable_main([glyphsapp_path]))
110*e1fe3e4aSElliott Hughes
111*e1fe3e4aSElliott Hughes    def test_VF(self):
112*e1fe3e4aSElliott Hughes        suffix = ".ttf"
113*e1fe3e4aSElliott Hughes        ttx_dir = self.get_test_input("master_ttx_varfont_ttf")
114*e1fe3e4aSElliott Hughes
115*e1fe3e4aSElliott Hughes        self.temp_dir()
116*e1fe3e4aSElliott Hughes        ttx_paths = self.get_file_list(ttx_dir, ".ttx", "SparseMasters-")
117*e1fe3e4aSElliott Hughes        for path in ttx_paths:
118*e1fe3e4aSElliott Hughes            self.compile_font(path, suffix, self.tempdir)
119*e1fe3e4aSElliott Hughes
120*e1fe3e4aSElliott Hughes        ttf_paths = self.get_file_list(self.tempdir, suffix)
121*e1fe3e4aSElliott Hughes
122*e1fe3e4aSElliott Hughes        problems = interpolatable_main(["--quiet"] + ttf_paths)
123*e1fe3e4aSElliott Hughes        self.assertIsNone(problems)
124*e1fe3e4aSElliott Hughes
125*e1fe3e4aSElliott Hughes    def test_sparse_interpolatable_ttfs(self):
126*e1fe3e4aSElliott Hughes        suffix = ".ttf"
127*e1fe3e4aSElliott Hughes        ttx_dir = self.get_test_input("master_ttx_interpolatable_ttf")
128*e1fe3e4aSElliott Hughes
129*e1fe3e4aSElliott Hughes        self.temp_dir()
130*e1fe3e4aSElliott Hughes        ttx_paths = self.get_file_list(ttx_dir, ".ttx", "SparseMasters-")
131*e1fe3e4aSElliott Hughes        for path in ttx_paths:
132*e1fe3e4aSElliott Hughes            self.compile_font(path, suffix, self.tempdir)
133*e1fe3e4aSElliott Hughes
134*e1fe3e4aSElliott Hughes        ttf_paths = self.get_file_list(self.tempdir, suffix)
135*e1fe3e4aSElliott Hughes
136*e1fe3e4aSElliott Hughes        # without --ignore-missing
137*e1fe3e4aSElliott Hughes        problems = interpolatable_main(["--quiet"] + ttf_paths)
138*e1fe3e4aSElliott Hughes        self.assertEqual(
139*e1fe3e4aSElliott Hughes            problems["a"],
140*e1fe3e4aSElliott Hughes            [{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
141*e1fe3e4aSElliott Hughes        )
142*e1fe3e4aSElliott Hughes        self.assertEqual(
143*e1fe3e4aSElliott Hughes            problems["s"],
144*e1fe3e4aSElliott Hughes            [{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
145*e1fe3e4aSElliott Hughes        )
146*e1fe3e4aSElliott Hughes        self.assertEqual(
147*e1fe3e4aSElliott Hughes            problems["edotabove"],
148*e1fe3e4aSElliott Hughes            [{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
149*e1fe3e4aSElliott Hughes        )
150*e1fe3e4aSElliott Hughes        self.assertEqual(
151*e1fe3e4aSElliott Hughes            problems["dotabovecomb"],
152*e1fe3e4aSElliott Hughes            [{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
153*e1fe3e4aSElliott Hughes        )
154*e1fe3e4aSElliott Hughes
155*e1fe3e4aSElliott Hughes        # normal order, with --ignore-missing
156*e1fe3e4aSElliott Hughes        self.assertIsNone(interpolatable_main(["--ignore-missing"] + ttf_paths))
157*e1fe3e4aSElliott Hughes        # purposely putting the sparse master (medium) first
158*e1fe3e4aSElliott Hughes        self.assertIsNone(
159*e1fe3e4aSElliott Hughes            interpolatable_main(
160*e1fe3e4aSElliott Hughes                ["--ignore-missing"] + [ttf_paths[1]] + [ttf_paths[0]] + [ttf_paths[2]]
161*e1fe3e4aSElliott Hughes            )
162*e1fe3e4aSElliott Hughes        )
163*e1fe3e4aSElliott Hughes        # purposely putting the sparse master (medium) last
164*e1fe3e4aSElliott Hughes        self.assertIsNone(
165*e1fe3e4aSElliott Hughes            interpolatable_main(
166*e1fe3e4aSElliott Hughes                ["--ignore-missing"] + [ttf_paths[0]] + [ttf_paths[2]] + [ttf_paths[1]]
167*e1fe3e4aSElliott Hughes            )
168*e1fe3e4aSElliott Hughes        )
169*e1fe3e4aSElliott Hughes
170*e1fe3e4aSElliott Hughes    def test_sparse_interpolatable_ufos(self):
171*e1fe3e4aSElliott Hughes        ttx_dir = self.get_test_input("master_ufo")
172*e1fe3e4aSElliott Hughes        ufo_paths = self.get_file_list(ttx_dir, ".ufo", "SparseMasters-")
173*e1fe3e4aSElliott Hughes
174*e1fe3e4aSElliott Hughes        # without --ignore-missing
175*e1fe3e4aSElliott Hughes        problems = interpolatable_main(["--quiet"] + ufo_paths)
176*e1fe3e4aSElliott Hughes        self.assertEqual(
177*e1fe3e4aSElliott Hughes            problems["a"],
178*e1fe3e4aSElliott Hughes            [{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
179*e1fe3e4aSElliott Hughes        )
180*e1fe3e4aSElliott Hughes        self.assertEqual(
181*e1fe3e4aSElliott Hughes            problems["s"],
182*e1fe3e4aSElliott Hughes            [{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
183*e1fe3e4aSElliott Hughes        )
184*e1fe3e4aSElliott Hughes        self.assertEqual(
185*e1fe3e4aSElliott Hughes            problems["edotabove"],
186*e1fe3e4aSElliott Hughes            [{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
187*e1fe3e4aSElliott Hughes        )
188*e1fe3e4aSElliott Hughes        self.assertEqual(
189*e1fe3e4aSElliott Hughes            problems["dotabovecomb"],
190*e1fe3e4aSElliott Hughes            [{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
191*e1fe3e4aSElliott Hughes        )
192*e1fe3e4aSElliott Hughes
193*e1fe3e4aSElliott Hughes        # normal order, with --ignore-missing
194*e1fe3e4aSElliott Hughes        self.assertIsNone(interpolatable_main(["--ignore-missing"] + ufo_paths))
195*e1fe3e4aSElliott Hughes        # purposely putting the sparse master (medium) first
196*e1fe3e4aSElliott Hughes        self.assertIsNone(
197*e1fe3e4aSElliott Hughes            interpolatable_main(
198*e1fe3e4aSElliott Hughes                ["--ignore-missing"] + [ufo_paths[1]] + [ufo_paths[0]] + [ufo_paths[2]]
199*e1fe3e4aSElliott Hughes            )
200*e1fe3e4aSElliott Hughes        )
201*e1fe3e4aSElliott Hughes        # purposely putting the sparse master (medium) last
202*e1fe3e4aSElliott Hughes        self.assertIsNone(
203*e1fe3e4aSElliott Hughes            interpolatable_main(
204*e1fe3e4aSElliott Hughes                ["--ignore-missing"] + [ufo_paths[0]] + [ufo_paths[2]] + [ufo_paths[1]]
205*e1fe3e4aSElliott Hughes            )
206*e1fe3e4aSElliott Hughes        )
207*e1fe3e4aSElliott Hughes
208*e1fe3e4aSElliott Hughes    def test_sparse_designspace(self):
209*e1fe3e4aSElliott Hughes        designspace_path = self.get_test_input("SparseMasters_ufo.designspace")
210*e1fe3e4aSElliott Hughes
211*e1fe3e4aSElliott Hughes        problems = interpolatable_main(["--quiet", designspace_path])
212*e1fe3e4aSElliott Hughes        self.assertEqual(
213*e1fe3e4aSElliott Hughes            problems["a"],
214*e1fe3e4aSElliott Hughes            [{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
215*e1fe3e4aSElliott Hughes        )
216*e1fe3e4aSElliott Hughes        self.assertEqual(
217*e1fe3e4aSElliott Hughes            problems["s"],
218*e1fe3e4aSElliott Hughes            [{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
219*e1fe3e4aSElliott Hughes        )
220*e1fe3e4aSElliott Hughes        self.assertEqual(
221*e1fe3e4aSElliott Hughes            problems["edotabove"],
222*e1fe3e4aSElliott Hughes            [{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
223*e1fe3e4aSElliott Hughes        )
224*e1fe3e4aSElliott Hughes        self.assertEqual(
225*e1fe3e4aSElliott Hughes            problems["dotabovecomb"],
226*e1fe3e4aSElliott Hughes            [{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
227*e1fe3e4aSElliott Hughes        )
228*e1fe3e4aSElliott Hughes
229*e1fe3e4aSElliott Hughes        # normal order, with --ignore-missing
230*e1fe3e4aSElliott Hughes        self.assertIsNone(interpolatable_main(["--ignore-missing", designspace_path]))
231*e1fe3e4aSElliott Hughes
232*e1fe3e4aSElliott Hughes    def test_sparse_glyphsapp(self):
233*e1fe3e4aSElliott Hughes        pytest.importorskip("glyphsLib")
234*e1fe3e4aSElliott Hughes        glyphsapp_path = self.get_test_input("SparseMasters.glyphs")
235*e1fe3e4aSElliott Hughes
236*e1fe3e4aSElliott Hughes        problems = interpolatable_main(["--quiet", glyphsapp_path])
237*e1fe3e4aSElliott Hughes        self.assertEqual(
238*e1fe3e4aSElliott Hughes            problems["a"],
239*e1fe3e4aSElliott Hughes            [{"type": "missing", "master": "Sparse Masters-Medium", "master_idx": 1}],
240*e1fe3e4aSElliott Hughes        )
241*e1fe3e4aSElliott Hughes        self.assertEqual(
242*e1fe3e4aSElliott Hughes            problems["s"],
243*e1fe3e4aSElliott Hughes            [{"type": "missing", "master": "Sparse Masters-Medium", "master_idx": 1}],
244*e1fe3e4aSElliott Hughes        )
245*e1fe3e4aSElliott Hughes        self.assertEqual(
246*e1fe3e4aSElliott Hughes            problems["edotabove"],
247*e1fe3e4aSElliott Hughes            [{"type": "missing", "master": "Sparse Masters-Medium", "master_idx": 1}],
248*e1fe3e4aSElliott Hughes        )
249*e1fe3e4aSElliott Hughes        self.assertEqual(
250*e1fe3e4aSElliott Hughes            problems["dotabovecomb"],
251*e1fe3e4aSElliott Hughes            [{"type": "missing", "master": "Sparse Masters-Medium", "master_idx": 1}],
252*e1fe3e4aSElliott Hughes        )
253*e1fe3e4aSElliott Hughes
254*e1fe3e4aSElliott Hughes        # normal order, with --ignore-missing
255*e1fe3e4aSElliott Hughes        self.assertIsNone(interpolatable_main(["--ignore-missing", glyphsapp_path]))
256*e1fe3e4aSElliott Hughes
257*e1fe3e4aSElliott Hughes    def test_interpolatable_varComposite(self):
258*e1fe3e4aSElliott Hughes        input_path = self.get_test_input(
259*e1fe3e4aSElliott Hughes            "..", "..", "ttLib", "data", "varc-ac00-ac01.ttf"
260*e1fe3e4aSElliott Hughes        )
261*e1fe3e4aSElliott Hughes        # This particular test font which was generated by machine-learning
262*e1fe3e4aSElliott Hughes        # exhibits an "error" in one of the masters; it's a false-positive.
263*e1fe3e4aSElliott Hughes        # Just make sure the code runs.
264*e1fe3e4aSElliott Hughes        interpolatable_main((input_path,))
265*e1fe3e4aSElliott Hughes
266*e1fe3e4aSElliott Hughes
267*e1fe3e4aSElliott Hughesif __name__ == "__main__":
268*e1fe3e4aSElliott Hughes    sys.exit(unittest.main())
269