diff options
Diffstat (limited to 'Tests/subset/subset_test.py')
-rw-r--r-- | Tests/subset/subset_test.py | 422 |
1 files changed, 250 insertions, 172 deletions
diff --git a/Tests/subset/subset_test.py b/Tests/subset/subset_test.py index d195af0f..facafb2a 100644 --- a/Tests/subset/subset_test.py +++ b/Tests/subset/subset_test.py @@ -1,5 +1,6 @@ import io -from fontTools.misc.testTools import getXML +import fontTools.ttLib.tables.otBase +from fontTools.misc.testTools import getXML, stripVariableItemsFromTTX from fontTools.misc.textTools import tobytes, tostr from fontTools import subset from fontTools.fontBuilder import FontBuilder @@ -19,44 +20,36 @@ import pathlib import pytest -class SubsetTest(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 +class SubsetTest: + @classmethod + def setup_class(cls): + cls.tempdir = None + cls.num_tempfiles = 0 - def setUp(self): - self.tempdir = None - self.num_tempfiles = 0 - - def tearDown(self): - if self.tempdir: - shutil.rmtree(self.tempdir) + @classmethod + def teardown_class(cls): + if cls.tempdir: + shutil.rmtree(cls.tempdir, ignore_errors=True) @staticmethod def getpath(testfile): path, _ = os.path.split(__file__) return os.path.join(path, "data", testfile) - def temp_path(self, suffix): - if not self.tempdir: - self.tempdir = tempfile.mkdtemp() - self.num_tempfiles += 1 - return os.path.join(self.tempdir, - "tmp%d%s" % (self.num_tempfiles, suffix)) - - def read_ttx(self, path): - lines = [] - with open(path, "r", encoding="utf-8") as ttx: - for line in ttx.readlines(): - # Elide ttFont attributes because ttLibVersion may change. - if line.startswith("<ttFont "): - lines.append("<ttFont>\n") - else: - lines.append(line.rstrip() + "\n") - return lines + @classmethod + def temp_path(cls, suffix): + if not cls.tempdir: + cls.tempdir = tempfile.mkdtemp() + cls.num_tempfiles += 1 + return os.path.join(cls.tempdir, + "tmp%d%s" % (cls.num_tempfiles, suffix)) + + @staticmethod + def read_ttx(path): + with open(path, "r", encoding="utf-8") as f: + ttx = f.read() + # don't care whether TTF or OTF, thus strip sfntVersion as well + return stripVariableItemsFromTTX(ttx, sfntVersion=True).splitlines(True) def expect_ttx(self, font, expected_ttx, tables=None): path = self.temp_path(suffix=".ttx") @@ -67,21 +60,21 @@ class SubsetTest(unittest.TestCase): for line in difflib.unified_diff( expected, actual, fromfile=expected_ttx, tofile=path): sys.stdout.write(line) - self.fail("TTX output is different from expected") + pytest.fail("TTX output is different from expected") def compile_font(self, path, suffix): savepath = self.temp_path(suffix=suffix) font = TTFont(recalcBBoxes=False, recalcTimestamp=False) font.importXML(path) font.save(savepath, reorderTables=None) - return font, savepath + return savepath # ----- # Tests # ----- def test_layout_scripts(self): - _, fontpath = self.compile_font(self.getpath("layout_scripts.ttx"), ".otf") + fontpath = self.compile_font(self.getpath("layout_scripts.ttx"), ".otf") subsetpath = self.temp_path(".otf") subset.main([fontpath, "--glyphs=*", "--layout-features=*", "--layout-scripts=latn,arab.URD,arab.dflt", @@ -91,41 +84,41 @@ class SubsetTest(unittest.TestCase): ["GPOS", "GSUB"]) def test_no_notdef_outline_otf(self): - _, fontpath = self.compile_font(self.getpath("TestOTF-Regular.ttx"), ".otf") + fontpath = self.compile_font(self.getpath("TestOTF-Regular.ttx"), ".otf") subsetpath = self.temp_path(".otf") subset.main([fontpath, "--no-notdef-outline", "--gids=0", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) self.expect_ttx(subsetfont, self.getpath("expect_no_notdef_outline_otf.ttx"), ["CFF "]) def test_no_notdef_outline_cid(self): - _, fontpath = self.compile_font(self.getpath("TestCID-Regular.ttx"), ".otf") + fontpath = self.compile_font(self.getpath("TestCID-Regular.ttx"), ".otf") subsetpath = self.temp_path(".otf") subset.main([fontpath, "--no-notdef-outline", "--gids=0", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) self.expect_ttx(subsetfont, self.getpath("expect_no_notdef_outline_cid.ttx"), ["CFF "]) def test_no_notdef_outline_ttf(self): - _, fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--no-notdef-outline", "--gids=0", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) self.expect_ttx(subsetfont, self.getpath("expect_no_notdef_outline_ttf.ttx"), ["glyf", "hmtx"]) def test_subset_ankr(self): - _, fontpath = self.compile_font(self.getpath("TestANKR.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestANKR.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) self.expect_ttx(subsetfont, self.getpath("expect_ankr.ttx"), ["ankr"]) def test_subset_ankr_remove(self): - _, fontpath = self.compile_font(self.getpath("TestANKR.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestANKR.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--glyphs=two", "--output-file=%s" % subsetpath]) - self.assertNotIn("ankr", TTFont(subsetpath)) + assert "ankr" not in TTFont(subsetpath) def test_subset_bsln_format_0(self): - _, fontpath = self.compile_font(self.getpath("TestBSLN-0.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestBSLN-0.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) @@ -138,7 +131,7 @@ class SubsetTest(unittest.TestCase): # a subsetted font with {zero, one} and the implicit .notdef, all # glyphs in the resulting font use the Roman baseline. In this case, # we expect a format 0 'bsln' table because it is the most compact. - _, fontpath = self.compile_font(self.getpath("TestBSLN-1.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestBSLN-1.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--unicodes=U+0030-0031", "--output-file=%s" % subsetpath]) @@ -154,7 +147,7 @@ class SubsetTest(unittest.TestCase): # subsetted font, we expect a format 1 'bsln' table whose default # is Roman, but with an override that uses the ideographic baseline # for uni2EA2. - _, fontpath = self.compile_font(self.getpath("TestBSLN-1.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestBSLN-1.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--unicodes=U+0030-0031,U+2EA2", "--output-file=%s" % subsetpath]) @@ -165,7 +158,7 @@ class SubsetTest(unittest.TestCase): # The 'bsln' table in TestBSLN-2 refers to control points in glyph 'P' # for defining its baselines. Therefore, the subsetted font should # include this glyph even though it is not requested explicitly. - _, fontpath = self.compile_font(self.getpath("TestBSLN-2.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestBSLN-2.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) @@ -179,7 +172,7 @@ class SubsetTest(unittest.TestCase): # baseline measurement, all glyphs in the resulting font use the Roman # baseline. In this case, we expect a format 2 'bsln' table because it # is the most compact encoding. - _, fontpath = self.compile_font(self.getpath("TestBSLN-3.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestBSLN-3.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--unicodes=U+0030", "--output-file=%s" % subsetpath]) @@ -195,7 +188,7 @@ class SubsetTest(unittest.TestCase): # subsetted font, we expect a format 1 'bsln' table whose default # is Roman, but with an override that uses the ideographic baseline # for uni2EA2. - _, fontpath = self.compile_font(self.getpath("TestBSLN-3.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestBSLN-3.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--unicodes=U+0030-0031,U+2EA2", "--output-file=%s" % subsetpath]) @@ -203,35 +196,35 @@ class SubsetTest(unittest.TestCase): self.expect_ttx(subsetfont, self.getpath("expect_bsln_3.ttx"), ["bsln"]) def test_subset_clr(self): - _, fontpath = self.compile_font(self.getpath("TestCLR-Regular.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestCLR-Regular.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--glyphs=smileface", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) self.expect_ttx(subsetfont, self.getpath("expect_keep_colr.ttx"), ["GlyphOrder", "hmtx", "glyf", "COLR", "CPAL"]) def test_subset_gvar(self): - _, fontpath = self.compile_font(self.getpath("TestGVAR.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestGVAR.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--unicodes=U+002B,U+2212", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) self.expect_ttx(subsetfont, self.getpath("expect_keep_gvar.ttx"), ["GlyphOrder", "avar", "fvar", "gvar", "name"]) def test_subset_gvar_notdef_outline(self): - _, fontpath = self.compile_font(self.getpath("TestGVAR.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestGVAR.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--unicodes=U+0030", "--notdef_outline", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) self.expect_ttx(subsetfont, self.getpath("expect_keep_gvar_notdef_outline.ttx"), ["GlyphOrder", "avar", "fvar", "gvar", "name"]) def test_subset_lcar_remove(self): - _, fontpath = self.compile_font(self.getpath("TestLCAR-0.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestLCAR-0.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) - self.assertNotIn("lcar", subsetfont) + assert "lcar" not in subsetfont def test_subset_lcar_format_0(self): - _, fontpath = self.compile_font(self.getpath("TestLCAR-0.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestLCAR-0.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--unicodes=U+FB01", "--output-file=%s" % subsetpath]) @@ -239,7 +232,7 @@ class SubsetTest(unittest.TestCase): self.expect_ttx(subsetfont, self.getpath("expect_lcar_0.ttx"), ["lcar"]) def test_subset_lcar_format_1(self): - _, fontpath = self.compile_font(self.getpath("TestLCAR-1.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestLCAR-1.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--unicodes=U+FB01", "--output-file=%s" % subsetpath]) @@ -247,14 +240,14 @@ class SubsetTest(unittest.TestCase): self.expect_ttx(subsetfont, self.getpath("expect_lcar_1.ttx"), ["lcar"]) def test_subset_math(self): - _, fontpath = self.compile_font(self.getpath("TestMATH-Regular.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestMATH-Regular.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--unicodes=U+0041,U+0028,U+0302,U+1D400,U+1D435", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) self.expect_ttx(subsetfont, self.getpath("expect_keep_math.ttx"), ["GlyphOrder", "CFF ", "MATH", "hmtx"]) def test_subset_math_partial(self): - _, fontpath = self.compile_font(self.getpath("test_math_partial.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("test_math_partial.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--text=A", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) @@ -265,21 +258,21 @@ class SubsetTest(unittest.TestCase): # the Optical Bounds table. When subsetting, we do not request any # of those glyphs. Therefore, the produced subsetted font should # not contain an 'opbd' table. - _, fontpath = self.compile_font(self.getpath("TestOPBD-0.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestOPBD-0.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) - self.assertNotIn("opbd", subsetfont) + assert "opbd" not in subsetfont def test_subset_opbd_format_0(self): - _, fontpath = self.compile_font(self.getpath("TestOPBD-0.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestOPBD-0.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--glyphs=A", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) self.expect_ttx(subsetfont, self.getpath("expect_opbd_0.ttx"), ["opbd"]) def test_subset_opbd_format_1(self): - _, fontpath = self.compile_font(self.getpath("TestOPBD-1.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestOPBD-1.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--glyphs=A", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) @@ -288,12 +281,12 @@ class SubsetTest(unittest.TestCase): def test_subset_prop_remove_default_zero(self): # If all glyphs have an AAT glyph property with value 0, # the "prop" table should be removed from the subsetted font. - _, fontpath = self.compile_font(self.getpath("TestPROP.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestPROP.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--unicodes=U+0041", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) - self.assertNotIn("prop", subsetfont) + assert "prop" not in subsetfont def test_subset_prop_0(self): # If all glyphs share the same AAT glyph properties, the "prop" table @@ -302,7 +295,7 @@ class SubsetTest(unittest.TestCase): # Unless the shared value is zero, in which case the subsetted font # should have no "prop" table at all. But that case has already been # tested above in test_subset_prop_remove_default_zero(). - _, fontpath = self.compile_font(self.getpath("TestPROP.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestPROP.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--unicodes=U+0030-0032", "--no-notdef-glyph", "--output-file=%s" % subsetpath]) @@ -313,7 +306,7 @@ class SubsetTest(unittest.TestCase): # If not all glyphs share the same AAT glyph properties, the subsetted # font should contain a "prop" table in format 1. To save space, the # DefaultProperties should be set to the most frequent value. - _, fontpath = self.compile_font(self.getpath("TestPROP.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestPROP.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--unicodes=U+0030-0032", "--notdef-outline", "--output-file=%s" % subsetpath]) @@ -323,32 +316,32 @@ class SubsetTest(unittest.TestCase): def test_options(self): # https://github.com/fonttools/fonttools/issues/413 opt1 = subset.Options() - self.assertTrue('Xyz-' not in opt1.layout_features) + assert 'Xyz-' not in opt1.layout_features opt2 = subset.Options() opt2.layout_features.append('Xyz-') - self.assertTrue('Xyz-' in opt2.layout_features) - self.assertTrue('Xyz-' not in opt1.layout_features) + assert 'Xyz-' in opt2.layout_features + assert 'Xyz-' not in opt1.layout_features def test_google_color(self): - _, fontpath = self.compile_font(self.getpath("google_color.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("google_color.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--gids=0,1", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) - self.assertTrue("CBDT" in subsetfont) - self.assertTrue("CBLC" in subsetfont) - self.assertTrue("x" in subsetfont['CBDT'].strikeData[0]) - self.assertFalse("y" in subsetfont['CBDT'].strikeData[0]) + assert "CBDT" in subsetfont + assert "CBLC" in subsetfont + assert "x" in subsetfont['CBDT'].strikeData[0] + assert "y" not in subsetfont['CBDT'].strikeData[0] def test_google_color_all(self): - _, fontpath = self.compile_font(self.getpath("google_color.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("google_color.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--unicodes=*", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) - self.assertTrue("x" in subsetfont['CBDT'].strikeData[0]) - self.assertTrue("y" in subsetfont['CBDT'].strikeData[0]) + assert "x" in subsetfont['CBDT'].strikeData[0] + assert "y" in subsetfont['CBDT'].strikeData[0] def test_sbix(self): - _, fontpath = self.compile_font(self.getpath("sbix.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("sbix.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--gids=0,1", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) @@ -356,7 +349,7 @@ class SubsetTest(unittest.TestCase): "expect_sbix.ttx"), ["sbix"]) def test_timing_publishes_parts(self): - _, fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf") options = subset.Options() options.timing = True @@ -367,15 +360,15 @@ class SubsetTest(unittest.TestCase): subsetter.subset(font) logs = captor.records - self.assertTrue(len(logs) > 5) - self.assertEqual(len(logs), len([l for l in logs if 'msg' in l.args and 'time' in l.args])) + assert len(logs) > 5 + assert len(logs) == len([l for l in logs if 'msg' in l.args and 'time' in l.args]) # Look for a few things we know should happen - self.assertTrue(filter(lambda l: l.args['msg'] == "load 'cmap'", logs)) - self.assertTrue(filter(lambda l: l.args['msg'] == "subset 'cmap'", logs)) - self.assertTrue(filter(lambda l: l.args['msg'] == "subset 'glyf'", logs)) + assert filter(lambda l: l.args['msg'] == "load 'cmap'", logs) + assert filter(lambda l: l.args['msg'] == "subset 'cmap'", logs) + assert filter(lambda l: l.args['msg'] == "subset 'glyf'", logs) def test_passthrough_tables(self): - _, fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf") font = TTFont(fontpath) unknown_tag = 'ZZZZ' unknown_table = newTable(unknown_tag) @@ -388,17 +381,17 @@ class SubsetTest(unittest.TestCase): subsetfont = TTFont(subsetpath) # tables we can't subset are dropped by default - self.assertFalse(unknown_tag in subsetfont) + assert unknown_tag not in subsetfont subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--passthrough-tables", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) # unknown tables are kept if --passthrough-tables option is passed - self.assertTrue(unknown_tag in subsetfont) + assert unknown_tag in subsetfont def test_non_BMP_text_arg_input(self): - _, fontpath = self.compile_font( + fontpath = self.compile_font( self.getpath("TestTTF-Regular_non_BMP_char.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") text = tostr(u"A\U0001F6D2", encoding='utf-8') @@ -406,11 +399,11 @@ class SubsetTest(unittest.TestCase): subset.main([fontpath, "--text=%s" % text, "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) - self.assertEqual(subsetfont['maxp'].numGlyphs, 3) - self.assertEqual(subsetfont.getGlyphOrder(), ['.notdef', 'A', 'u1F6D2']) + assert subsetfont['maxp'].numGlyphs == 3 + assert subsetfont.getGlyphOrder() == ['.notdef', 'A', 'u1F6D2'] def test_non_BMP_text_file_input(self): - _, fontpath = self.compile_font( + fontpath = self.compile_font( self.getpath("TestTTF-Regular_non_BMP_char.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") text = tobytes(u"A\U0001F6D2", encoding='utf-8') @@ -424,12 +417,12 @@ class SubsetTest(unittest.TestCase): finally: os.remove(tmp.name) - self.assertEqual(subsetfont['maxp'].numGlyphs, 3) - self.assertEqual(subsetfont.getGlyphOrder(), ['.notdef', 'A', 'u1F6D2']) + assert subsetfont['maxp'].numGlyphs == 3 + assert subsetfont.getGlyphOrder() == ['.notdef', 'A', 'u1F6D2'] def test_no_hinting_CFF(self): ttxpath = self.getpath("Lobster.subset.ttx") - _, fontpath = self.compile_font(ttxpath, ".otf") + fontpath = self.compile_font(ttxpath, ".otf") subsetpath = self.temp_path(".otf") subset.main([fontpath, "--no-hinting", "--notdef-outline", "--output-file=%s" % subsetpath, "*"]) @@ -439,7 +432,7 @@ class SubsetTest(unittest.TestCase): def test_desubroutinize_CFF(self): ttxpath = self.getpath("Lobster.subset.ttx") - _, fontpath = self.compile_font(ttxpath, ".otf") + fontpath = self.compile_font(ttxpath, ".otf") subsetpath = self.temp_path(".otf") subset.main([fontpath, "--desubroutinize", "--notdef-outline", "--output-file=%s" % subsetpath, "*"]) @@ -449,7 +442,7 @@ class SubsetTest(unittest.TestCase): def test_desubroutinize_hinted_subrs_CFF(self): ttxpath = self.getpath("test_hinted_subrs_CFF.ttx") - _, fontpath = self.compile_font(ttxpath, ".otf") + fontpath = self.compile_font(ttxpath, ".otf") subsetpath = self.temp_path(".otf") subset.main([fontpath, "--desubroutinize", "--notdef-outline", "--output-file=%s" % subsetpath, "*"]) @@ -459,7 +452,7 @@ class SubsetTest(unittest.TestCase): def test_desubroutinize_cntrmask_CFF(self): ttxpath = self.getpath("test_cntrmask_CFF.ttx") - _, fontpath = self.compile_font(ttxpath, ".otf") + fontpath = self.compile_font(ttxpath, ".otf") subsetpath = self.temp_path(".otf") subset.main([fontpath, "--desubroutinize", "--notdef-outline", "--output-file=%s" % subsetpath, "*"]) @@ -469,7 +462,7 @@ class SubsetTest(unittest.TestCase): def test_no_hinting_desubroutinize_CFF(self): ttxpath = self.getpath("test_hinted_subrs_CFF.ttx") - _, fontpath = self.compile_font(ttxpath, ".otf") + fontpath = self.compile_font(ttxpath, ".otf") subsetpath = self.temp_path(".otf") subset.main([fontpath, "--no-hinting", "--desubroutinize", "--notdef-outline", "--output-file=%s" % subsetpath, "*"]) @@ -478,7 +471,7 @@ class SubsetTest(unittest.TestCase): "expect_no_hinting_desubroutinize_CFF.ttx"), ["CFF "]) def test_no_hinting_TTF(self): - _, fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--no-hinting", "--notdef-outline", "--output-file=%s" % subsetpath, "*"]) @@ -486,11 +479,11 @@ class SubsetTest(unittest.TestCase): self.expect_ttx(subsetfont, self.getpath( "expect_no_hinting_TTF.ttx"), ["glyf", "maxp"]) for tag in subset.Options().hinting_tables: - self.assertTrue(tag not in subsetfont) + assert tag not in subsetfont def test_notdef_width_cid(self): # https://github.com/fonttools/fonttools/pull/845 - _, fontpath = self.compile_font(self.getpath("NotdefWidthCID-Regular.ttx"), ".otf") + fontpath = self.compile_font(self.getpath("NotdefWidthCID-Regular.ttx"), ".otf") subsetpath = self.temp_path(".otf") subset.main([fontpath, "--no-notdef-outline", "--gids=0,1", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) @@ -503,18 +496,18 @@ class SubsetTest(unittest.TestCase): head = font['head'] bounds = [head.xMin, head.yMin, head.xMax, head.yMax] - _, fontpath = self.compile_font(ttxpath, ".ttf") + fontpath = self.compile_font(ttxpath, ".ttf") subsetpath = self.temp_path(".ttf") # by default, the subsetter does not recalculate the bounding box subset.main([fontpath, "--output-file=%s" % subsetpath, "*"]) head = TTFont(subsetpath)['head'] - self.assertEqual(bounds, [head.xMin, head.yMin, head.xMax, head.yMax]) + assert bounds == [head.xMin, head.yMin, head.xMax, head.yMax] subset.main([fontpath, "--recalc-bounds", "--output-file=%s" % subsetpath, "*"]) head = TTFont(subsetpath)['head'] bounds = [132, 304, 365, 567] - self.assertEqual(bounds, [head.xMin, head.yMin, head.xMax, head.yMax]) + assert bounds == [head.xMin, head.yMin, head.xMax, head.yMax] def test_recalc_bounds_otf(self): ttxpath = self.getpath("TestOTF-Regular.ttx") @@ -523,76 +516,76 @@ class SubsetTest(unittest.TestCase): head = font['head'] bounds = [head.xMin, head.yMin, head.xMax, head.yMax] - _, fontpath = self.compile_font(ttxpath, ".otf") + fontpath = self.compile_font(ttxpath, ".otf") subsetpath = self.temp_path(".otf") # by default, the subsetter does not recalculate the bounding box subset.main([fontpath, "--output-file=%s" % subsetpath, "*"]) head = TTFont(subsetpath)['head'] - self.assertEqual(bounds, [head.xMin, head.yMin, head.xMax, head.yMax]) + assert bounds == [head.xMin, head.yMin, head.xMax, head.yMax] subset.main([fontpath, "--recalc-bounds", "--output-file=%s" % subsetpath, "*"]) head = TTFont(subsetpath)['head'] bounds = [132, 304, 365, 567] - self.assertEqual(bounds, [head.xMin, head.yMin, head.xMax, head.yMax]) + assert bounds == [head.xMin, head.yMin, head.xMax, head.yMax] def test_recalc_timestamp_ttf(self): ttxpath = self.getpath("TestTTF-Regular.ttx") font = TTFont() font.importXML(ttxpath) modified = font['head'].modified - _, fontpath = self.compile_font(ttxpath, ".ttf") + fontpath = self.compile_font(ttxpath, ".ttf") subsetpath = self.temp_path(".ttf") # by default, the subsetter does not recalculate the modified timestamp subset.main([fontpath, "--output-file=%s" % subsetpath, "*"]) - self.assertEqual(modified, TTFont(subsetpath)['head'].modified) + assert modified == TTFont(subsetpath)['head'].modified subset.main([fontpath, "--recalc-timestamp", "--output-file=%s" % subsetpath, "*"]) - self.assertLess(modified, TTFont(subsetpath)['head'].modified) + assert modified < TTFont(subsetpath)['head'].modified def test_recalc_timestamp_otf(self): ttxpath = self.getpath("TestOTF-Regular.ttx") font = TTFont() font.importXML(ttxpath) modified = font['head'].modified - _, fontpath = self.compile_font(ttxpath, ".otf") + fontpath = self.compile_font(ttxpath, ".otf") subsetpath = self.temp_path(".otf") # by default, the subsetter does not recalculate the modified timestamp subset.main([fontpath, "--output-file=%s" % subsetpath, "*"]) - self.assertEqual(modified, TTFont(subsetpath)['head'].modified) + assert modified == TTFont(subsetpath)['head'].modified subset.main([fontpath, "--recalc-timestamp", "--output-file=%s" % subsetpath, "*"]) - self.assertLess(modified, TTFont(subsetpath)['head'].modified) + assert modified < TTFont(subsetpath)['head'].modified def test_recalc_max_context(self): ttxpath = self.getpath("Lobster.subset.ttx") font = TTFont() font.importXML(ttxpath) max_context = font['OS/2'].usMaxContext - _, fontpath = self.compile_font(ttxpath, ".otf") + fontpath = self.compile_font(ttxpath, ".otf") subsetpath = self.temp_path(".otf") # by default, the subsetter does not recalculate the usMaxContext subset.main([fontpath, "--drop-tables+=GSUB,GPOS", "--output-file=%s" % subsetpath]) - self.assertEqual(max_context, TTFont(subsetpath)['OS/2'].usMaxContext) + assert max_context == TTFont(subsetpath)['OS/2'].usMaxContext subset.main([fontpath, "--recalc-max-context", "--drop-tables+=GSUB,GPOS", "--output-file=%s" % subsetpath]) - self.assertEqual(0, TTFont(subsetpath)['OS/2'].usMaxContext) + assert 0 == TTFont(subsetpath)['OS/2'].usMaxContext def test_retain_gids_ttf(self): - _, fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf") font = TTFont(fontpath) - self.assertEqual(font["hmtx"]["A"], (500, 132)) - self.assertEqual(font["hmtx"]["B"], (400, 132)) + assert font["hmtx"]["A"] == (500, 132) + assert font["hmtx"]["B"] == (400, 132) - self.assertGreater(font["glyf"]["A"].numberOfContours, 0) - self.assertGreater(font["glyf"]["B"].numberOfContours, 0) + assert font["glyf"]["A"].numberOfContours > 0 + assert font["glyf"]["B"].numberOfContours > 0 subsetpath = self.temp_path(".ttf") subset.main( @@ -606,29 +599,29 @@ class SubsetTest(unittest.TestCase): ) subsetfont = TTFont(subsetpath) - self.assertEqual(subsetfont.getGlyphOrder(), font.getGlyphOrder()[0:3]) + assert subsetfont.getGlyphOrder() == font.getGlyphOrder()[0:3] hmtx = subsetfont["hmtx"] - self.assertEqual(hmtx["A"], ( 0, 0)) - self.assertEqual(hmtx["B"], (400, 132)) + assert hmtx["A"] == (0, 0) + assert hmtx["B"] == (400, 132) glyf = subsetfont["glyf"] - self.assertEqual(glyf["A"].numberOfContours, 0) - self.assertGreater(glyf["B"].numberOfContours, 0) + assert glyf["A"].numberOfContours == 0 + assert glyf["B"].numberOfContours > 0 def test_retain_gids_cff(self): - _, fontpath = self.compile_font(self.getpath("TestOTF-Regular.ttx"), ".otf") + fontpath = self.compile_font(self.getpath("TestOTF-Regular.ttx"), ".otf") font = TTFont(fontpath) - self.assertEqual(font["hmtx"]["A"], (500, 132)) - self.assertEqual(font["hmtx"]["B"], (400, 132)) - self.assertEqual(font["hmtx"]["C"], (500, 0)) + assert font["hmtx"]["A"] == (500, 132) + assert font["hmtx"]["B"] == (400, 132) + assert font["hmtx"]["C"] == (500, 0) font["CFF "].cff[0].decompileAllCharStrings() cs = font["CFF "].cff[0].CharStrings - self.assertGreater(len(cs["A"].program), 0) - self.assertGreater(len(cs["B"].program), 0) - self.assertGreater(len(cs["C"].program), 0) + assert len(cs["A"].program) > 0 + assert len(cs["B"].program) > 0 + assert len(cs["C"].program) > 0 subsetpath = self.temp_path(".otf") subset.main( @@ -642,29 +635,30 @@ class SubsetTest(unittest.TestCase): ) subsetfont = TTFont(subsetpath) - self.assertEqual(subsetfont.getGlyphOrder(), font.getGlyphOrder()[0:3]) + assert subsetfont.getGlyphOrder() == font.getGlyphOrder()[0:3] hmtx = subsetfont["hmtx"] - self.assertEqual(hmtx["A"], (0, 0)) - self.assertEqual(hmtx["B"], (400, 132)) + assert hmtx["A"] == (0, 0) + assert hmtx["B"] == (400, 132) subsetfont["CFF "].cff[0].decompileAllCharStrings() cs = subsetfont["CFF "].cff[0].CharStrings - self.assertEqual(cs["A"].program, ["endchar"]) - self.assertGreater(len(cs["B"].program), 0) + assert cs["A"].program == ["endchar"] + assert len(cs["B"].program) > 0 def test_retain_gids_cff2(self): ttx_path = self.getpath("../../varLib/data/master_ttx_varfont_otf/TestCFF2VF.ttx") - font, fontpath = self.compile_font(ttx_path, ".otf") + fontpath = self.compile_font(ttx_path, ".otf") + font = TTFont(fontpath) - self.assertEqual(font["hmtx"]["A"], (600, 31)) - self.assertEqual(font["hmtx"]["T"], (600, 41)) + assert font["hmtx"]["A"] == (600, 31) + assert font["hmtx"]["T"] == (600, 41) font["CFF2"].cff[0].decompileAllCharStrings() cs = font["CFF2"].cff[0].CharStrings - self.assertGreater(len(cs["A"].program), 0) - self.assertGreater(len(cs["T"].program), 0) + assert len(cs["A"].program) > 0 + assert len(cs["T"].program) > 0 subsetpath = self.temp_path(".otf") subset.main( @@ -677,33 +671,33 @@ class SubsetTest(unittest.TestCase): ) subsetfont = TTFont(subsetpath) - self.assertEqual(len(subsetfont.getGlyphOrder()), len(font.getGlyphOrder()[0:3])) + assert len(subsetfont.getGlyphOrder()) == len(font.getGlyphOrder()[0:3]) hmtx = subsetfont["hmtx"] - self.assertEqual(hmtx["glyph00001"], ( 0, 0)) - self.assertEqual(hmtx["T"], (600, 41)) + assert hmtx["glyph00001"] == (0, 0) + assert hmtx["T"] == (600, 41) subsetfont["CFF2"].cff[0].decompileAllCharStrings() cs = subsetfont["CFF2"].cff[0].CharStrings - self.assertEqual(cs["glyph00001"].program, []) - self.assertGreater(len(cs["T"].program), 0) + assert cs["glyph00001"].program == [] + assert len(cs["T"].program) > 0 def test_HVAR_VVAR(self): - _, fontpath = self.compile_font(self.getpath("TestHVVAR.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestHVVAR.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--text=BD", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) self.expect_ttx(subsetfont, self.getpath("expect_HVVAR.ttx"), ["GlyphOrder", "HVAR", "VVAR", "avar", "fvar"]) def test_HVAR_VVAR_retain_gids(self): - _, fontpath = self.compile_font(self.getpath("TestHVVAR.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestHVVAR.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--text=BD", "--retain-gids", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) self.expect_ttx(subsetfont, self.getpath("expect_HVVAR_retain_gids.ttx"), ["GlyphOrder", "HVAR", "VVAR", "avar", "fvar"]) def test_subset_flavor(self): - _, fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf") font = TTFont(fontpath) woff_path = self.temp_path(".woff") @@ -717,7 +711,7 @@ class SubsetTest(unittest.TestCase): ) woff = TTFont(woff_path) - self.assertEqual(woff.flavor, "woff") + assert woff.flavor == "woff" woff2_path = self.temp_path(".woff2") subset.main( @@ -730,7 +724,7 @@ class SubsetTest(unittest.TestCase): ) woff2 = TTFont(woff2_path) - self.assertEqual(woff2.flavor, "woff2") + assert woff2.flavor == "woff2" ttf_path = self.temp_path(".ttf") subset.main( @@ -742,13 +736,13 @@ class SubsetTest(unittest.TestCase): ) ttf = TTFont(ttf_path) - self.assertEqual(ttf.flavor, None) + assert ttf.flavor is None def test_subset_context_subst_format_3(self): # https://github.com/fonttools/fonttools/issues/1879 # Test font contains 'calt' feature with Format 3 ContextSubst lookup subtables ttx = self.getpath("TestContextSubstFormat3.ttx") - font, fontpath = self.compile_font(ttx, ".ttf") + fontpath = self.compile_font(ttx, ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--unicodes=*", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) @@ -756,13 +750,14 @@ class SubsetTest(unittest.TestCase): self.expect_ttx(subsetfont, ttx) def test_cmap_prune_format12(self): - _, fontpath = self.compile_font(self.getpath("CmapSubsetTest.ttx"), ".ttf") + fontpath = self.compile_font(self.getpath("CmapSubsetTest.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") subset.main([fontpath, "--glyphs=a", "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) self.expect_ttx(subsetfont, self.getpath("CmapSubsetTest.subset.ttx"), ["cmap"]) - def test_GPOS_PairPos_Format2_useClass0(self): + @pytest.mark.parametrize("text, n", [("!", 1), ("#", 2)]) + def test_GPOS_PairPos_Format2_useClass0(self, text, n): # Check two things related to class 0 ('every other glyph'): # 1) that it's reused for ClassDef1 when it becomes empty as the subset glyphset # is intersected with the table's Coverage @@ -772,29 +767,27 @@ class SubsetTest(unittest.TestCase): # The test font (from Harfbuzz test suite) is constructed to trigger these two # situations depending on the input subset --text. # https://github.com/fonttools/fonttools/pull/2221 - _, fontpath = self.compile_font( + fontpath = self.compile_font( self.getpath("GPOS_PairPos_Format2_PR_2221.ttx"), ".ttf" ) subsetpath = self.temp_path(".ttf") - for n, text in enumerate("!#", start=1): - expected_ttx = self.getpath( - f"GPOS_PairPos_Format2_ClassDef{n}_useClass0.subset.ttx" - ) - with self.subTest(text=text, expected_ttx=expected_ttx): - subset.main( - [ - fontpath, - f"--text='{text}'", - "--layout-features+=test", - "--output-file=%s" % subsetpath, - ] - ) - subsetfont = TTFont(subsetpath) - self.expect_ttx(subsetfont, expected_ttx, ["GPOS"]) + expected_ttx = self.getpath( + f"GPOS_PairPos_Format2_ClassDef{n}_useClass0.subset.ttx" + ) + subset.main( + [ + fontpath, + f"--text='{text}'", + "--layout-features+=test", + "--output-file=%s" % subsetpath, + ] + ) + subsetfont = TTFont(subsetpath) + self.expect_ttx(subsetfont, expected_ttx, ["GPOS"]) def test_GPOS_SinglePos_prune_post_subset_no_value(self): - _, fontpath = self.compile_font( + fontpath = self.compile_font( self.getpath("GPOS_SinglePos_no_value_issue_2312.ttx"), ".ttf" ) subsetpath = self.temp_path(".ttf") @@ -806,6 +799,91 @@ class SubsetTest(unittest.TestCase): ["GlyphOrder", "GPOS"], ) + @pytest.mark.parametrize( + "installed, enabled, ok", + [ + pytest.param(True, None, True, id="installed-auto-ok"), + pytest.param(True, None, False, id="installed-auto-fail"), + pytest.param(True, True, True, id="installed-enabled-ok"), + pytest.param(True, True, False, id="installed-enabled-fail"), + pytest.param(True, False, True, id="installed-disabled"), + pytest.param(False, True, True, id="not_installed-enabled"), + pytest.param(False, False, True, id="not_installed-disabled"), + ], + ) + def test_harfbuzz_repacker(self, caplog, monkeypatch, installed, enabled, ok): + # Use a mock to test the pure-python serializer is used when uharfbuzz + # returns an error or is not installed + have_uharfbuzz = fontTools.ttLib.tables.otBase.have_uharfbuzz + if installed: + if not have_uharfbuzz: + pytest.skip("uharfbuzz is not installed") + if not ok: + # pretend hb.repack returns an error + import uharfbuzz as hb + + def mock_repack(data, obj_list): + raise hb.RepackerError("mocking") + + monkeypatch.setattr(hb, "repack", mock_repack) + else: + if have_uharfbuzz: + # pretend uharfbuzz is not installed + monkeypatch.setattr( + fontTools.ttLib.tables.otBase, "have_uharfbuzz", False + ) + + fontpath = self.compile_font(self.getpath("harfbuzz_repacker.ttx"), ".otf") + subsetpath = self.temp_path(".otf") + args = [ + fontpath, + "--unicodes=0x53a9", + "--layout-features=*", + f"--output-file={subsetpath}", + ] + if enabled is True: + args.append("--harfbuzz-repacker") + elif enabled is False: + args.append("--no-harfbuzz-repacker") + # elif enabled is None: ... is the default + + if enabled is True and not installed: + # raise if enabled but not installed + with pytest.raises(ImportError, match="uharfbuzz"): + subset.main(args) + return + + with caplog.at_level(logging.DEBUG, "fontTools.ttLib.tables.otBase"): + subset.main(args) + + subsetfont = TTFont(subsetpath) + # both hb.repack and pure-python serializer compile to the same ttx + self.expect_ttx( + subsetfont, self.getpath("expect_harfbuzz_repacker.ttx"), ["GSUB"] + ) + + if enabled or enabled is None: + if installed: + assert "serializing 'GSUB' with hb.repack" in caplog.text + + if enabled is None and not installed: + assert ( + "uharfbuzz not found, compiling 'GSUB' with pure-python serializer" + ) in caplog.text + + if enabled is False: + assert ( + "hb.repack disabled, compiling 'GSUB' with pure-python serializer" + ) in caplog.text + + # test we emit a log.error if hb.repack fails (and we don't if successful) + assert ( + ( + "hb.repack failed to serialize 'GSUB', reverting to " + "pure-python serializer; the error message was: RepackerError: mocking" + ) in caplog.text + ) ^ ok + @pytest.fixture def featureVarsTestFont(): |