2017-03-06 02:52:06 -08:00
|
|
|
from fontTools.ttLib import TTFont
|
|
|
|
from fontTools.varLib.interpolatable import main as interpolatable_main
|
|
|
|
import os
|
|
|
|
import shutil
|
|
|
|
import sys
|
|
|
|
import tempfile
|
|
|
|
import unittest
|
2023-04-06 10:58:31 -04:00
|
|
|
import pytest
|
2017-03-06 02:52:06 -08:00
|
|
|
|
2017-03-07 11:35:22 +00:00
|
|
|
try:
|
|
|
|
import scipy
|
|
|
|
except:
|
|
|
|
scipy = None
|
2017-03-06 02:52:06 -08:00
|
|
|
|
2017-03-07 11:35:22 +00:00
|
|
|
try:
|
|
|
|
import munkres
|
|
|
|
except ImportError:
|
|
|
|
munkres = None
|
|
|
|
|
|
|
|
|
|
|
|
@unittest.skipUnless(scipy or munkres, "scipy or munkres not installed")
|
2017-03-06 02:52:06 -08:00
|
|
|
class InterpolatableTest(unittest.TestCase):
|
|
|
|
def __init__(self, methodName):
|
|
|
|
unittest.TestCase.__init__(self, methodName)
|
|
|
|
# Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
|
|
|
|
# and fires deprecation warnings if a program uses the old name.
|
|
|
|
if not hasattr(self, "assertRaisesRegex"):
|
|
|
|
self.assertRaisesRegex = self.assertRaisesRegexp
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
self.tempdir = None
|
|
|
|
self.num_tempfiles = 0
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
if self.tempdir:
|
|
|
|
shutil.rmtree(self.tempdir)
|
|
|
|
|
|
|
|
@staticmethod
|
2023-01-31 13:44:20 -07:00
|
|
|
def get_test_input(*test_file_or_folder):
|
2017-03-06 02:52:06 -08:00
|
|
|
path, _ = os.path.split(__file__)
|
2023-01-31 13:44:20 -07:00
|
|
|
return os.path.join(path, "data", *test_file_or_folder)
|
2017-03-06 02:52:06 -08:00
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def get_file_list(folder, suffix, prefix=""):
|
|
|
|
all_files = os.listdir(folder)
|
|
|
|
file_list = []
|
|
|
|
for p in all_files:
|
|
|
|
if p.startswith(prefix) and p.endswith(suffix):
|
|
|
|
file_list.append(os.path.abspath(os.path.join(folder, p)))
|
2023-11-20 08:59:11 -07:00
|
|
|
return sorted(file_list)
|
2017-03-06 02:52:06 -08:00
|
|
|
|
|
|
|
def temp_path(self, suffix):
|
|
|
|
self.temp_dir()
|
|
|
|
self.num_tempfiles += 1
|
|
|
|
return os.path.join(self.tempdir, "tmp%d%s" % (self.num_tempfiles, suffix))
|
|
|
|
|
|
|
|
def temp_dir(self):
|
|
|
|
if not self.tempdir:
|
|
|
|
self.tempdir = tempfile.mkdtemp()
|
|
|
|
|
|
|
|
def compile_font(self, path, suffix, temp_dir):
|
|
|
|
ttx_filename = os.path.basename(path)
|
|
|
|
savepath = os.path.join(temp_dir, ttx_filename.replace(".ttx", suffix))
|
|
|
|
font = TTFont(recalcBBoxes=False, recalcTimestamp=False)
|
|
|
|
font.importXML(path)
|
|
|
|
font.save(savepath, reorderTables=None)
|
|
|
|
return font, savepath
|
|
|
|
|
|
|
|
# -----
|
|
|
|
# Tests
|
|
|
|
# -----
|
|
|
|
|
|
|
|
def test_interpolatable_ttf(self):
|
|
|
|
suffix = ".ttf"
|
|
|
|
ttx_dir = self.get_test_input("master_ttx_interpolatable_ttf")
|
|
|
|
|
|
|
|
self.temp_dir()
|
|
|
|
ttx_paths = self.get_file_list(ttx_dir, ".ttx", "TestFamily2-")
|
|
|
|
for path in ttx_paths:
|
|
|
|
self.compile_font(path, suffix, self.tempdir)
|
|
|
|
|
|
|
|
ttf_paths = self.get_file_list(self.tempdir, suffix)
|
|
|
|
self.assertIsNone(interpolatable_main(ttf_paths))
|
|
|
|
|
2017-03-15 22:51:42 -07:00
|
|
|
def test_interpolatable_otf(self):
|
|
|
|
suffix = ".otf"
|
|
|
|
ttx_dir = self.get_test_input("master_ttx_interpolatable_otf")
|
|
|
|
|
|
|
|
self.temp_dir()
|
|
|
|
ttx_paths = self.get_file_list(ttx_dir, ".ttx", "TestFamily2-")
|
|
|
|
for path in ttx_paths:
|
|
|
|
self.compile_font(path, suffix, self.tempdir)
|
|
|
|
|
|
|
|
otf_paths = self.get_file_list(self.tempdir, suffix)
|
|
|
|
self.assertIsNone(interpolatable_main(otf_paths))
|
2023-04-05 20:48:34 -04:00
|
|
|
|
2024-10-29 11:42:37 -07:00
|
|
|
@pytest.mark.skipif(
|
|
|
|
sys.version_info[:2] == (3, 8), reason="Fails on Python 3.8 for unknown reasons"
|
|
|
|
)
|
2024-10-21 23:39:08 -06:00
|
|
|
def test_interpolatable_cff2(self):
|
|
|
|
suffix = ".otf"
|
|
|
|
ttx_dir = self.get_test_input("variable_ttx_interpolatable_cff2")
|
|
|
|
ttx_path = os.path.abspath(os.path.join(ttx_dir, "interpolatable-test.ttx"))
|
|
|
|
|
|
|
|
self.temp_dir()
|
|
|
|
self.compile_font(ttx_path, suffix, self.tempdir)
|
|
|
|
|
|
|
|
otf_path = self.get_file_list(self.tempdir, suffix)[0]
|
|
|
|
|
|
|
|
problems = interpolatable_main([otf_path])
|
|
|
|
print(problems)
|
|
|
|
self.assertEqual(
|
|
|
|
problems["uni0408"],
|
|
|
|
[
|
|
|
|
{
|
|
|
|
"type": "underweight",
|
|
|
|
"contour": 0,
|
|
|
|
"master_1": "'wght=200.0 opsz=20.0'",
|
|
|
|
"master_2": "'wght=200.0 opsz=60.0'",
|
|
|
|
"master_1_idx": 2,
|
|
|
|
"master_2_idx": 3,
|
|
|
|
"tolerance": 0.9184032411892079,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2023-04-05 20:48:34 -04:00
|
|
|
def test_interpolatable_ufo(self):
|
|
|
|
ttx_dir = self.get_test_input("master_ufo")
|
|
|
|
ufo_paths = self.get_file_list(ttx_dir, ".ufo", "TestFamily2-")
|
|
|
|
self.assertIsNone(interpolatable_main(ufo_paths))
|
|
|
|
|
|
|
|
def test_designspace(self):
|
|
|
|
designspace_path = self.get_test_input("InterpolateLayout.designspace")
|
|
|
|
self.assertIsNone(interpolatable_main([designspace_path]))
|
|
|
|
|
|
|
|
def test_glyphsapp(self):
|
2023-04-06 10:58:31 -04:00
|
|
|
pytest.importorskip("glyphsLib")
|
2023-04-05 20:48:34 -04:00
|
|
|
glyphsapp_path = self.get_test_input("InterpolateLayout.glyphs")
|
|
|
|
self.assertIsNone(interpolatable_main([glyphsapp_path]))
|
2023-04-05 22:33:38 -04:00
|
|
|
|
2023-04-05 20:48:34 -04:00
|
|
|
def test_VF(self):
|
|
|
|
suffix = ".ttf"
|
|
|
|
ttx_dir = self.get_test_input("master_ttx_varfont_ttf")
|
|
|
|
|
|
|
|
self.temp_dir()
|
|
|
|
ttx_paths = self.get_file_list(ttx_dir, ".ttx", "SparseMasters-")
|
|
|
|
for path in ttx_paths:
|
|
|
|
self.compile_font(path, suffix, self.tempdir)
|
|
|
|
|
|
|
|
ttf_paths = self.get_file_list(self.tempdir, suffix)
|
2023-04-05 22:33:38 -04:00
|
|
|
|
2023-04-05 20:48:34 -04:00
|
|
|
problems = interpolatable_main(["--quiet"] + ttf_paths)
|
|
|
|
self.assertIsNone(problems)
|
2023-04-05 22:33:38 -04:00
|
|
|
|
2023-04-05 16:35:03 -04:00
|
|
|
def test_sparse_interpolatable_ttfs(self):
|
|
|
|
suffix = ".ttf"
|
|
|
|
ttx_dir = self.get_test_input("master_ttx_interpolatable_ttf")
|
|
|
|
|
|
|
|
self.temp_dir()
|
|
|
|
ttx_paths = self.get_file_list(ttx_dir, ".ttx", "SparseMasters-")
|
|
|
|
for path in ttx_paths:
|
|
|
|
self.compile_font(path, suffix, self.tempdir)
|
|
|
|
|
|
|
|
ttf_paths = self.get_file_list(self.tempdir, suffix)
|
2023-04-05 22:33:38 -04:00
|
|
|
|
2023-04-05 16:35:03 -04:00
|
|
|
# without --ignore-missing
|
|
|
|
problems = interpolatable_main(["--quiet"] + ttf_paths)
|
2023-04-05 22:33:38 -04:00
|
|
|
self.assertEqual(
|
2023-11-20 08:59:11 -07:00
|
|
|
problems["a"],
|
|
|
|
[{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
|
2023-04-05 22:33:38 -04:00
|
|
|
)
|
|
|
|
self.assertEqual(
|
2023-11-20 08:59:11 -07:00
|
|
|
problems["s"],
|
|
|
|
[{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
|
2023-04-05 22:33:38 -04:00
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
problems["edotabove"],
|
2023-11-20 08:59:11 -07:00
|
|
|
[{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
|
2023-04-05 22:33:38 -04:00
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
problems["dotabovecomb"],
|
2023-11-20 08:59:11 -07:00
|
|
|
[{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
|
2023-04-05 22:33:38 -04:00
|
|
|
)
|
|
|
|
|
2023-04-05 16:35:03 -04:00
|
|
|
# normal order, with --ignore-missing
|
|
|
|
self.assertIsNone(interpolatable_main(["--ignore-missing"] + ttf_paths))
|
|
|
|
# purposely putting the sparse master (medium) first
|
2023-04-05 22:33:38 -04:00
|
|
|
self.assertIsNone(
|
|
|
|
interpolatable_main(
|
|
|
|
["--ignore-missing"] + [ttf_paths[1]] + [ttf_paths[0]] + [ttf_paths[2]]
|
|
|
|
)
|
|
|
|
)
|
2023-04-05 16:35:03 -04:00
|
|
|
# purposely putting the sparse master (medium) last
|
2023-04-05 22:33:38 -04:00
|
|
|
self.assertIsNone(
|
|
|
|
interpolatable_main(
|
|
|
|
["--ignore-missing"] + [ttf_paths[0]] + [ttf_paths[2]] + [ttf_paths[1]]
|
|
|
|
)
|
|
|
|
)
|
2017-03-15 22:51:42 -07:00
|
|
|
|
2023-04-05 20:48:34 -04:00
|
|
|
def test_sparse_interpolatable_ufos(self):
|
|
|
|
ttx_dir = self.get_test_input("master_ufo")
|
|
|
|
ufo_paths = self.get_file_list(ttx_dir, ".ufo", "SparseMasters-")
|
2023-04-05 22:33:38 -04:00
|
|
|
|
2023-04-05 20:48:34 -04:00
|
|
|
# without --ignore-missing
|
|
|
|
problems = interpolatable_main(["--quiet"] + ufo_paths)
|
2023-04-05 22:33:38 -04:00
|
|
|
self.assertEqual(
|
2023-11-20 08:59:11 -07:00
|
|
|
problems["a"],
|
|
|
|
[{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
|
2023-04-05 22:33:38 -04:00
|
|
|
)
|
|
|
|
self.assertEqual(
|
2023-11-20 08:59:11 -07:00
|
|
|
problems["s"],
|
|
|
|
[{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
|
2023-04-05 22:33:38 -04:00
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
problems["edotabove"],
|
2023-11-20 08:59:11 -07:00
|
|
|
[{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
|
2023-04-05 22:33:38 -04:00
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
problems["dotabovecomb"],
|
2023-11-20 08:59:11 -07:00
|
|
|
[{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
|
2023-04-05 22:33:38 -04:00
|
|
|
)
|
|
|
|
|
2023-04-05 20:48:34 -04:00
|
|
|
# normal order, with --ignore-missing
|
|
|
|
self.assertIsNone(interpolatable_main(["--ignore-missing"] + ufo_paths))
|
|
|
|
# purposely putting the sparse master (medium) first
|
2023-04-05 22:33:38 -04:00
|
|
|
self.assertIsNone(
|
|
|
|
interpolatable_main(
|
|
|
|
["--ignore-missing"] + [ufo_paths[1]] + [ufo_paths[0]] + [ufo_paths[2]]
|
|
|
|
)
|
|
|
|
)
|
2023-04-05 20:48:34 -04:00
|
|
|
# purposely putting the sparse master (medium) last
|
2023-04-05 22:33:38 -04:00
|
|
|
self.assertIsNone(
|
|
|
|
interpolatable_main(
|
|
|
|
["--ignore-missing"] + [ufo_paths[0]] + [ufo_paths[2]] + [ufo_paths[1]]
|
|
|
|
)
|
|
|
|
)
|
2023-04-05 20:48:34 -04:00
|
|
|
|
|
|
|
def test_sparse_designspace(self):
|
|
|
|
designspace_path = self.get_test_input("SparseMasters_ufo.designspace")
|
2023-04-05 22:33:38 -04:00
|
|
|
|
2023-04-05 20:48:34 -04:00
|
|
|
problems = interpolatable_main(["--quiet", designspace_path])
|
2023-04-05 22:33:38 -04:00
|
|
|
self.assertEqual(
|
2023-11-20 08:59:11 -07:00
|
|
|
problems["a"],
|
|
|
|
[{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
|
2023-04-05 22:33:38 -04:00
|
|
|
)
|
|
|
|
self.assertEqual(
|
2023-11-20 08:59:11 -07:00
|
|
|
problems["s"],
|
|
|
|
[{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
|
2023-04-05 22:33:38 -04:00
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
problems["edotabove"],
|
2023-11-20 08:59:11 -07:00
|
|
|
[{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
|
2023-04-05 22:33:38 -04:00
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
problems["dotabovecomb"],
|
2023-11-20 08:59:11 -07:00
|
|
|
[{"type": "missing", "master": "SparseMasters-Medium", "master_idx": 1}],
|
2023-04-05 22:33:38 -04:00
|
|
|
)
|
|
|
|
|
2023-04-05 20:48:34 -04:00
|
|
|
# normal order, with --ignore-missing
|
|
|
|
self.assertIsNone(interpolatable_main(["--ignore-missing", designspace_path]))
|
|
|
|
|
2023-04-06 10:58:31 -04:00
|
|
|
def test_sparse_glyphsapp(self):
|
|
|
|
pytest.importorskip("glyphsLib")
|
2023-04-05 20:48:34 -04:00
|
|
|
glyphsapp_path = self.get_test_input("SparseMasters.glyphs")
|
2023-04-05 22:33:38 -04:00
|
|
|
|
2023-04-05 20:48:34 -04:00
|
|
|
problems = interpolatable_main(["--quiet", glyphsapp_path])
|
2023-04-05 22:33:38 -04:00
|
|
|
self.assertEqual(
|
2023-11-20 08:59:11 -07:00
|
|
|
problems["a"],
|
|
|
|
[{"type": "missing", "master": "Sparse Masters-Medium", "master_idx": 1}],
|
2023-04-05 22:33:38 -04:00
|
|
|
)
|
|
|
|
self.assertEqual(
|
2023-11-20 08:59:11 -07:00
|
|
|
problems["s"],
|
|
|
|
[{"type": "missing", "master": "Sparse Masters-Medium", "master_idx": 1}],
|
2023-04-05 22:33:38 -04:00
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
problems["edotabove"],
|
2023-11-20 08:59:11 -07:00
|
|
|
[{"type": "missing", "master": "Sparse Masters-Medium", "master_idx": 1}],
|
2023-04-05 22:33:38 -04:00
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
problems["dotabovecomb"],
|
2023-11-20 08:59:11 -07:00
|
|
|
[{"type": "missing", "master": "Sparse Masters-Medium", "master_idx": 1}],
|
2023-04-05 22:33:38 -04:00
|
|
|
)
|
|
|
|
|
2023-04-05 20:48:34 -04:00
|
|
|
# normal order, with --ignore-missing
|
|
|
|
self.assertIsNone(interpolatable_main(["--ignore-missing", glyphsapp_path]))
|
|
|
|
|
2023-01-31 13:44:20 -07:00
|
|
|
def test_interpolatable_varComposite(self):
|
|
|
|
input_path = self.get_test_input(
|
|
|
|
"..", "..", "ttLib", "data", "varc-ac00-ac01.ttf"
|
|
|
|
)
|
|
|
|
# Just make sure the code runs.
|
|
|
|
interpolatable_main((input_path,))
|
|
|
|
|
2017-03-15 22:51:42 -07:00
|
|
|
|
2017-03-06 02:52:06 -08:00
|
|
|
if __name__ == "__main__":
|
|
|
|
sys.exit(unittest.main())
|