Merge pull request #2588 from fonttools/hb-repack-mock-test

mock hb.repack error to test pure-python serializer gets used on failure
This commit is contained in:
Cosimo Lupo 2022-04-19 16:19:10 +01:00 committed by GitHub
commit cc6b12582c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,4 +1,5 @@
import io
import fontTools.ttLib.tables.otBase
from fontTools.misc.testTools import getXML, stripVariableItemsFromTTX
from fontTools.misc.textTools import tobytes, tostr
from fontTools import subset
@ -19,35 +20,32 @@ 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)
@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))
@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))
def read_ttx(self, path):
@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
@ -62,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",
@ -86,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)
@ -133,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])
@ -149,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])
@ -160,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)
@ -174,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])
@ -190,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])
@ -198,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])
@ -234,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])
@ -242,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)
@ -260,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)
@ -283,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
@ -297,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])
@ -308,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])
@ -318,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)
@ -351,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
@ -362,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)
@ -383,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')
@ -401,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')
@ -419,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, "*"])
@ -434,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, "*"])
@ -444,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, "*"])
@ -454,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, "*"])
@ -464,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, "*"])
@ -473,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, "*"])
@ -481,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)
@ -498,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")
@ -518,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(
@ -601,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(
@ -637,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(
@ -672,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")
@ -712,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(
@ -725,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(
@ -737,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)
@ -751,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
@ -767,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")
@ -801,15 +799,60 @@ class SubsetTest(unittest.TestCase):
["GlyphOrder", "GPOS"],
)
def test_harfbuzz_repacker(self):
_, fontpath = self.compile_font(self.getpath("harfbuzz_repacker.ttx"), ".otf")
subsetpath = self.temp_path(".otf")
subset.main([fontpath, "--unicodes=0x53a9", "--layout-features=*",
"--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath)
self.expect_ttx(subsetfont, self.getpath("expect_harfbuzz_repacker.ttx"),
["GSUB"])
@pytest.mark.parametrize(
"installed, ok",
[
pytest.param(True, True, id="installed-ok"),
pytest.param(True, False, id="installed-fail"),
pytest.param(False, True, id="not_installed"),
],
)
def test_harfbuzz_repacker(self, caplog, monkeypatch, installed, 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")
with caplog.at_level(logging.ERROR, "fontTools.ttLib.tables.otBase"):
subset.main(
[
fontpath,
"--unicodes=0x53a9",
"--layout-features=*",
f"--output-file={subsetpath}",
]
)
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"]
)
# test we emit a log.error if hb.repack fails (and we don't if successful)
assert (
(
"hb.repack failed, reverting to pure-python serializer; "
"the error message was: mocking"
) in caplog.text
) ^ ok
@pytest.fixture