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 io
import fontTools.ttLib.tables.otBase
from fontTools.misc.testTools import getXML, stripVariableItemsFromTTX from fontTools.misc.testTools import getXML, stripVariableItemsFromTTX
from fontTools.misc.textTools import tobytes, tostr from fontTools.misc.textTools import tobytes, tostr
from fontTools import subset from fontTools import subset
@ -19,35 +20,32 @@ import pathlib
import pytest import pytest
class SubsetTest(unittest.TestCase): class SubsetTest:
def __init__(self, methodName): @classmethod
unittest.TestCase.__init__(self, methodName) def setup_class(cls):
# Python 3 renamed assertRaisesRegexp to assertRaisesRegex, cls.tempdir = None
# and fires deprecation warnings if a program uses the old name. cls.num_tempfiles = 0
if not hasattr(self, "assertRaisesRegex"):
self.assertRaisesRegex = self.assertRaisesRegexp
def setUp(self): @classmethod
self.tempdir = None def teardown_class(cls):
self.num_tempfiles = 0 if cls.tempdir:
shutil.rmtree(cls.tempdir)
def tearDown(self):
if self.tempdir:
shutil.rmtree(self.tempdir)
@staticmethod @staticmethod
def getpath(testfile): def getpath(testfile):
path, _ = os.path.split(__file__) path, _ = os.path.split(__file__)
return os.path.join(path, "data", testfile) return os.path.join(path, "data", testfile)
def temp_path(self, suffix): @classmethod
if not self.tempdir: def temp_path(cls, suffix):
self.tempdir = tempfile.mkdtemp() if not cls.tempdir:
self.num_tempfiles += 1 cls.tempdir = tempfile.mkdtemp()
return os.path.join(self.tempdir, cls.num_tempfiles += 1
"tmp%d%s" % (self.num_tempfiles, suffix)) 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: with open(path, "r", encoding="utf-8") as f:
ttx = f.read() ttx = f.read()
# don't care whether TTF or OTF, thus strip sfntVersion as well # 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( for line in difflib.unified_diff(
expected, actual, fromfile=expected_ttx, tofile=path): expected, actual, fromfile=expected_ttx, tofile=path):
sys.stdout.write(line) 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): def compile_font(self, path, suffix):
savepath = self.temp_path(suffix=suffix) savepath = self.temp_path(suffix=suffix)
font = TTFont(recalcBBoxes=False, recalcTimestamp=False) font = TTFont(recalcBBoxes=False, recalcTimestamp=False)
font.importXML(path) font.importXML(path)
font.save(savepath, reorderTables=None) font.save(savepath, reorderTables=None)
return font, savepath return savepath
# ----- # -----
# Tests # Tests
# ----- # -----
def test_layout_scripts(self): 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") subsetpath = self.temp_path(".otf")
subset.main([fontpath, "--glyphs=*", "--layout-features=*", subset.main([fontpath, "--glyphs=*", "--layout-features=*",
"--layout-scripts=latn,arab.URD,arab.dflt", "--layout-scripts=latn,arab.URD,arab.dflt",
@ -86,41 +84,41 @@ class SubsetTest(unittest.TestCase):
["GPOS", "GSUB"]) ["GPOS", "GSUB"])
def test_no_notdef_outline_otf(self): 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") subsetpath = self.temp_path(".otf")
subset.main([fontpath, "--no-notdef-outline", "--gids=0", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--no-notdef-outline", "--gids=0", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
self.expect_ttx(subsetfont, self.getpath("expect_no_notdef_outline_otf.ttx"), ["CFF "]) self.expect_ttx(subsetfont, self.getpath("expect_no_notdef_outline_otf.ttx"), ["CFF "])
def test_no_notdef_outline_cid(self): 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") subsetpath = self.temp_path(".otf")
subset.main([fontpath, "--no-notdef-outline", "--gids=0", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--no-notdef-outline", "--gids=0", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
self.expect_ttx(subsetfont, self.getpath("expect_no_notdef_outline_cid.ttx"), ["CFF "]) self.expect_ttx(subsetfont, self.getpath("expect_no_notdef_outline_cid.ttx"), ["CFF "])
def test_no_notdef_outline_ttf(self): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--no-notdef-outline", "--gids=0", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--no-notdef-outline", "--gids=0", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
self.expect_ttx(subsetfont, self.getpath("expect_no_notdef_outline_ttf.ttx"), ["glyf", "hmtx"]) self.expect_ttx(subsetfont, self.getpath("expect_no_notdef_outline_ttf.ttx"), ["glyf", "hmtx"])
def test_subset_ankr(self): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
self.expect_ttx(subsetfont, self.getpath("expect_ankr.ttx"), ["ankr"]) self.expect_ttx(subsetfont, self.getpath("expect_ankr.ttx"), ["ankr"])
def test_subset_ankr_remove(self): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--glyphs=two", "--output-file=%s" % subsetpath]) 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): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
@ -133,7 +131,7 @@ class SubsetTest(unittest.TestCase):
# a subsetted font with {zero, one} and the implicit .notdef, all # a subsetted font with {zero, one} and the implicit .notdef, all
# glyphs in the resulting font use the Roman baseline. In this case, # 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. # 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--unicodes=U+0030-0031", subset.main([fontpath, "--unicodes=U+0030-0031",
"--output-file=%s" % subsetpath]) "--output-file=%s" % subsetpath])
@ -149,7 +147,7 @@ class SubsetTest(unittest.TestCase):
# subsetted font, we expect a format 1 'bsln' table whose default # subsetted font, we expect a format 1 'bsln' table whose default
# is Roman, but with an override that uses the ideographic baseline # is Roman, but with an override that uses the ideographic baseline
# for uni2EA2. # 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--unicodes=U+0030-0031,U+2EA2", subset.main([fontpath, "--unicodes=U+0030-0031,U+2EA2",
"--output-file=%s" % subsetpath]) "--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' # The 'bsln' table in TestBSLN-2 refers to control points in glyph 'P'
# for defining its baselines. Therefore, the subsetted font should # for defining its baselines. Therefore, the subsetted font should
# include this glyph even though it is not requested explicitly. # 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
@ -174,7 +172,7 @@ class SubsetTest(unittest.TestCase):
# baseline measurement, all glyphs in the resulting font use the Roman # baseline measurement, all glyphs in the resulting font use the Roman
# baseline. In this case, we expect a format 2 'bsln' table because it # baseline. In this case, we expect a format 2 'bsln' table because it
# is the most compact encoding. # 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--unicodes=U+0030", subset.main([fontpath, "--unicodes=U+0030",
"--output-file=%s" % subsetpath]) "--output-file=%s" % subsetpath])
@ -190,7 +188,7 @@ class SubsetTest(unittest.TestCase):
# subsetted font, we expect a format 1 'bsln' table whose default # subsetted font, we expect a format 1 'bsln' table whose default
# is Roman, but with an override that uses the ideographic baseline # is Roman, but with an override that uses the ideographic baseline
# for uni2EA2. # 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--unicodes=U+0030-0031,U+2EA2", subset.main([fontpath, "--unicodes=U+0030-0031,U+2EA2",
"--output-file=%s" % subsetpath]) "--output-file=%s" % subsetpath])
@ -198,35 +196,35 @@ class SubsetTest(unittest.TestCase):
self.expect_ttx(subsetfont, self.getpath("expect_bsln_3.ttx"), ["bsln"]) self.expect_ttx(subsetfont, self.getpath("expect_bsln_3.ttx"), ["bsln"])
def test_subset_clr(self): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--glyphs=smileface", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--glyphs=smileface", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
self.expect_ttx(subsetfont, self.getpath("expect_keep_colr.ttx"), ["GlyphOrder", "hmtx", "glyf", "COLR", "CPAL"]) self.expect_ttx(subsetfont, self.getpath("expect_keep_colr.ttx"), ["GlyphOrder", "hmtx", "glyf", "COLR", "CPAL"])
def test_subset_gvar(self): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--unicodes=U+002B,U+2212", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--unicodes=U+002B,U+2212", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
self.expect_ttx(subsetfont, self.getpath("expect_keep_gvar.ttx"), ["GlyphOrder", "avar", "fvar", "gvar", "name"]) self.expect_ttx(subsetfont, self.getpath("expect_keep_gvar.ttx"), ["GlyphOrder", "avar", "fvar", "gvar", "name"])
def test_subset_gvar_notdef_outline(self): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--unicodes=U+0030", "--notdef_outline", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--unicodes=U+0030", "--notdef_outline", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
self.expect_ttx(subsetfont, self.getpath("expect_keep_gvar_notdef_outline.ttx"), ["GlyphOrder", "avar", "fvar", "gvar", "name"]) self.expect_ttx(subsetfont, self.getpath("expect_keep_gvar_notdef_outline.ttx"), ["GlyphOrder", "avar", "fvar", "gvar", "name"])
def test_subset_lcar_remove(self): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
self.assertNotIn("lcar", subsetfont) assert "lcar" not in subsetfont
def test_subset_lcar_format_0(self): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--unicodes=U+FB01", subset.main([fontpath, "--unicodes=U+FB01",
"--output-file=%s" % subsetpath]) "--output-file=%s" % subsetpath])
@ -234,7 +232,7 @@ class SubsetTest(unittest.TestCase):
self.expect_ttx(subsetfont, self.getpath("expect_lcar_0.ttx"), ["lcar"]) self.expect_ttx(subsetfont, self.getpath("expect_lcar_0.ttx"), ["lcar"])
def test_subset_lcar_format_1(self): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--unicodes=U+FB01", subset.main([fontpath, "--unicodes=U+FB01",
"--output-file=%s" % subsetpath]) "--output-file=%s" % subsetpath])
@ -242,14 +240,14 @@ class SubsetTest(unittest.TestCase):
self.expect_ttx(subsetfont, self.getpath("expect_lcar_1.ttx"), ["lcar"]) self.expect_ttx(subsetfont, self.getpath("expect_lcar_1.ttx"), ["lcar"])
def test_subset_math(self): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--unicodes=U+0041,U+0028,U+0302,U+1D400,U+1D435", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--unicodes=U+0041,U+0028,U+0302,U+1D400,U+1D435", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
self.expect_ttx(subsetfont, self.getpath("expect_keep_math.ttx"), ["GlyphOrder", "CFF ", "MATH", "hmtx"]) self.expect_ttx(subsetfont, self.getpath("expect_keep_math.ttx"), ["GlyphOrder", "CFF ", "MATH", "hmtx"])
def test_subset_math_partial(self): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--text=A", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--text=A", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
@ -260,21 +258,21 @@ class SubsetTest(unittest.TestCase):
# the Optical Bounds table. When subsetting, we do not request any # the Optical Bounds table. When subsetting, we do not request any
# of those glyphs. Therefore, the produced subsetted font should # of those glyphs. Therefore, the produced subsetted font should
# not contain an 'opbd' table. # 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--glyphs=one", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
self.assertNotIn("opbd", subsetfont) assert "opbd" not in subsetfont
def test_subset_opbd_format_0(self): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--glyphs=A", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--glyphs=A", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
self.expect_ttx(subsetfont, self.getpath("expect_opbd_0.ttx"), ["opbd"]) self.expect_ttx(subsetfont, self.getpath("expect_opbd_0.ttx"), ["opbd"])
def test_subset_opbd_format_1(self): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--glyphs=A", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--glyphs=A", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
@ -283,12 +281,12 @@ class SubsetTest(unittest.TestCase):
def test_subset_prop_remove_default_zero(self): def test_subset_prop_remove_default_zero(self):
# If all glyphs have an AAT glyph property with value 0, # If all glyphs have an AAT glyph property with value 0,
# the "prop" table should be removed from the subsetted font. # 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--unicodes=U+0041", subset.main([fontpath, "--unicodes=U+0041",
"--output-file=%s" % subsetpath]) "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
self.assertNotIn("prop", subsetfont) assert "prop" not in subsetfont
def test_subset_prop_0(self): def test_subset_prop_0(self):
# If all glyphs share the same AAT glyph properties, the "prop" table # 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 # 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 # should have no "prop" table at all. But that case has already been
# tested above in test_subset_prop_remove_default_zero(). # 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--unicodes=U+0030-0032", "--no-notdef-glyph", subset.main([fontpath, "--unicodes=U+0030-0032", "--no-notdef-glyph",
"--output-file=%s" % subsetpath]) "--output-file=%s" % subsetpath])
@ -308,7 +306,7 @@ class SubsetTest(unittest.TestCase):
# If not all glyphs share the same AAT glyph properties, the subsetted # 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 # font should contain a "prop" table in format 1. To save space, the
# DefaultProperties should be set to the most frequent value. # 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--unicodes=U+0030-0032", "--notdef-outline", subset.main([fontpath, "--unicodes=U+0030-0032", "--notdef-outline",
"--output-file=%s" % subsetpath]) "--output-file=%s" % subsetpath])
@ -318,32 +316,32 @@ class SubsetTest(unittest.TestCase):
def test_options(self): def test_options(self):
# https://github.com/fonttools/fonttools/issues/413 # https://github.com/fonttools/fonttools/issues/413
opt1 = subset.Options() opt1 = subset.Options()
self.assertTrue('Xyz-' not in opt1.layout_features) assert 'Xyz-' not in opt1.layout_features
opt2 = subset.Options() opt2 = subset.Options()
opt2.layout_features.append('Xyz-') opt2.layout_features.append('Xyz-')
self.assertTrue('Xyz-' in opt2.layout_features) assert 'Xyz-' in opt2.layout_features
self.assertTrue('Xyz-' not in opt1.layout_features) assert 'Xyz-' not in opt1.layout_features
def test_google_color(self): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--gids=0,1", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--gids=0,1", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
self.assertTrue("CBDT" in subsetfont) assert "CBDT" in subsetfont
self.assertTrue("CBLC" in subsetfont) assert "CBLC" in subsetfont
self.assertTrue("x" in subsetfont['CBDT'].strikeData[0]) assert "x" in subsetfont['CBDT'].strikeData[0]
self.assertFalse("y" in subsetfont['CBDT'].strikeData[0]) assert "y" not in subsetfont['CBDT'].strikeData[0]
def test_google_color_all(self): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--unicodes=*", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--unicodes=*", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
self.assertTrue("x" in subsetfont['CBDT'].strikeData[0]) assert "x" in subsetfont['CBDT'].strikeData[0]
self.assertTrue("y" in subsetfont['CBDT'].strikeData[0]) assert "y" in subsetfont['CBDT'].strikeData[0]
def test_sbix(self): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--gids=0,1", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--gids=0,1", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
@ -351,7 +349,7 @@ class SubsetTest(unittest.TestCase):
"expect_sbix.ttx"), ["sbix"]) "expect_sbix.ttx"), ["sbix"])
def test_timing_publishes_parts(self): 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 = subset.Options()
options.timing = True options.timing = True
@ -362,15 +360,15 @@ class SubsetTest(unittest.TestCase):
subsetter.subset(font) subsetter.subset(font)
logs = captor.records logs = captor.records
self.assertTrue(len(logs) > 5) assert 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) == 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 # Look for a few things we know should happen
self.assertTrue(filter(lambda l: l.args['msg'] == "load 'cmap'", logs)) assert filter(lambda l: l.args['msg'] == "load 'cmap'", logs)
self.assertTrue(filter(lambda l: l.args['msg'] == "subset 'cmap'", logs)) assert 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'] == "subset 'glyf'", logs)
def test_passthrough_tables(self): 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) font = TTFont(fontpath)
unknown_tag = 'ZZZZ' unknown_tag = 'ZZZZ'
unknown_table = newTable(unknown_tag) unknown_table = newTable(unknown_tag)
@ -383,17 +381,17 @@ class SubsetTest(unittest.TestCase):
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
# tables we can't subset are dropped by default # 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--passthrough-tables", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--passthrough-tables", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
# unknown tables are kept if --passthrough-tables option is passed # 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): 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") self.getpath("TestTTF-Regular_non_BMP_char.ttx"), ".ttf")
subsetpath = self.temp_path(".ttf") subsetpath = self.temp_path(".ttf")
text = tostr(u"A\U0001F6D2", encoding='utf-8') 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]) subset.main([fontpath, "--text=%s" % text, "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
self.assertEqual(subsetfont['maxp'].numGlyphs, 3) assert subsetfont['maxp'].numGlyphs == 3
self.assertEqual(subsetfont.getGlyphOrder(), ['.notdef', 'A', 'u1F6D2']) assert subsetfont.getGlyphOrder() == ['.notdef', 'A', 'u1F6D2']
def test_non_BMP_text_file_input(self): 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") self.getpath("TestTTF-Regular_non_BMP_char.ttx"), ".ttf")
subsetpath = self.temp_path(".ttf") subsetpath = self.temp_path(".ttf")
text = tobytes(u"A\U0001F6D2", encoding='utf-8') text = tobytes(u"A\U0001F6D2", encoding='utf-8')
@ -419,12 +417,12 @@ class SubsetTest(unittest.TestCase):
finally: finally:
os.remove(tmp.name) os.remove(tmp.name)
self.assertEqual(subsetfont['maxp'].numGlyphs, 3) assert subsetfont['maxp'].numGlyphs == 3
self.assertEqual(subsetfont.getGlyphOrder(), ['.notdef', 'A', 'u1F6D2']) assert subsetfont.getGlyphOrder() == ['.notdef', 'A', 'u1F6D2']
def test_no_hinting_CFF(self): def test_no_hinting_CFF(self):
ttxpath = self.getpath("Lobster.subset.ttx") ttxpath = self.getpath("Lobster.subset.ttx")
_, fontpath = self.compile_font(ttxpath, ".otf") fontpath = self.compile_font(ttxpath, ".otf")
subsetpath = self.temp_path(".otf") subsetpath = self.temp_path(".otf")
subset.main([fontpath, "--no-hinting", "--notdef-outline", subset.main([fontpath, "--no-hinting", "--notdef-outline",
"--output-file=%s" % subsetpath, "*"]) "--output-file=%s" % subsetpath, "*"])
@ -434,7 +432,7 @@ class SubsetTest(unittest.TestCase):
def test_desubroutinize_CFF(self): def test_desubroutinize_CFF(self):
ttxpath = self.getpath("Lobster.subset.ttx") ttxpath = self.getpath("Lobster.subset.ttx")
_, fontpath = self.compile_font(ttxpath, ".otf") fontpath = self.compile_font(ttxpath, ".otf")
subsetpath = self.temp_path(".otf") subsetpath = self.temp_path(".otf")
subset.main([fontpath, "--desubroutinize", "--notdef-outline", subset.main([fontpath, "--desubroutinize", "--notdef-outline",
"--output-file=%s" % subsetpath, "*"]) "--output-file=%s" % subsetpath, "*"])
@ -444,7 +442,7 @@ class SubsetTest(unittest.TestCase):
def test_desubroutinize_hinted_subrs_CFF(self): def test_desubroutinize_hinted_subrs_CFF(self):
ttxpath = self.getpath("test_hinted_subrs_CFF.ttx") 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") subsetpath = self.temp_path(".otf")
subset.main([fontpath, "--desubroutinize", "--notdef-outline", subset.main([fontpath, "--desubroutinize", "--notdef-outline",
"--output-file=%s" % subsetpath, "*"]) "--output-file=%s" % subsetpath, "*"])
@ -454,7 +452,7 @@ class SubsetTest(unittest.TestCase):
def test_desubroutinize_cntrmask_CFF(self): def test_desubroutinize_cntrmask_CFF(self):
ttxpath = self.getpath("test_cntrmask_CFF.ttx") ttxpath = self.getpath("test_cntrmask_CFF.ttx")
_, fontpath = self.compile_font(ttxpath, ".otf") fontpath = self.compile_font(ttxpath, ".otf")
subsetpath = self.temp_path(".otf") subsetpath = self.temp_path(".otf")
subset.main([fontpath, "--desubroutinize", "--notdef-outline", subset.main([fontpath, "--desubroutinize", "--notdef-outline",
"--output-file=%s" % subsetpath, "*"]) "--output-file=%s" % subsetpath, "*"])
@ -464,7 +462,7 @@ class SubsetTest(unittest.TestCase):
def test_no_hinting_desubroutinize_CFF(self): def test_no_hinting_desubroutinize_CFF(self):
ttxpath = self.getpath("test_hinted_subrs_CFF.ttx") 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") subsetpath = self.temp_path(".otf")
subset.main([fontpath, "--no-hinting", "--desubroutinize", "--notdef-outline", subset.main([fontpath, "--no-hinting", "--desubroutinize", "--notdef-outline",
"--output-file=%s" % subsetpath, "*"]) "--output-file=%s" % subsetpath, "*"])
@ -473,7 +471,7 @@ class SubsetTest(unittest.TestCase):
"expect_no_hinting_desubroutinize_CFF.ttx"), ["CFF "]) "expect_no_hinting_desubroutinize_CFF.ttx"), ["CFF "])
def test_no_hinting_TTF(self): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--no-hinting", "--notdef-outline", subset.main([fontpath, "--no-hinting", "--notdef-outline",
"--output-file=%s" % subsetpath, "*"]) "--output-file=%s" % subsetpath, "*"])
@ -481,11 +479,11 @@ class SubsetTest(unittest.TestCase):
self.expect_ttx(subsetfont, self.getpath( self.expect_ttx(subsetfont, self.getpath(
"expect_no_hinting_TTF.ttx"), ["glyf", "maxp"]) "expect_no_hinting_TTF.ttx"), ["glyf", "maxp"])
for tag in subset.Options().hinting_tables: for tag in subset.Options().hinting_tables:
self.assertTrue(tag not in subsetfont) assert tag not in subsetfont
def test_notdef_width_cid(self): def test_notdef_width_cid(self):
# https://github.com/fonttools/fonttools/pull/845 # 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") subsetpath = self.temp_path(".otf")
subset.main([fontpath, "--no-notdef-outline", "--gids=0,1", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--no-notdef-outline", "--gids=0,1", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
@ -498,18 +496,18 @@ class SubsetTest(unittest.TestCase):
head = font['head'] head = font['head']
bounds = [head.xMin, head.yMin, head.xMax, head.yMax] 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") subsetpath = self.temp_path(".ttf")
# by default, the subsetter does not recalculate the bounding box # by default, the subsetter does not recalculate the bounding box
subset.main([fontpath, "--output-file=%s" % subsetpath, "*"]) subset.main([fontpath, "--output-file=%s" % subsetpath, "*"])
head = TTFont(subsetpath)['head'] 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, "*"]) subset.main([fontpath, "--recalc-bounds", "--output-file=%s" % subsetpath, "*"])
head = TTFont(subsetpath)['head'] head = TTFont(subsetpath)['head']
bounds = [132, 304, 365, 567] 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): def test_recalc_bounds_otf(self):
ttxpath = self.getpath("TestOTF-Regular.ttx") ttxpath = self.getpath("TestOTF-Regular.ttx")
@ -518,76 +516,76 @@ class SubsetTest(unittest.TestCase):
head = font['head'] head = font['head']
bounds = [head.xMin, head.yMin, head.xMax, head.yMax] 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") subsetpath = self.temp_path(".otf")
# by default, the subsetter does not recalculate the bounding box # by default, the subsetter does not recalculate the bounding box
subset.main([fontpath, "--output-file=%s" % subsetpath, "*"]) subset.main([fontpath, "--output-file=%s" % subsetpath, "*"])
head = TTFont(subsetpath)['head'] 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, "*"]) subset.main([fontpath, "--recalc-bounds", "--output-file=%s" % subsetpath, "*"])
head = TTFont(subsetpath)['head'] head = TTFont(subsetpath)['head']
bounds = [132, 304, 365, 567] 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): def test_recalc_timestamp_ttf(self):
ttxpath = self.getpath("TestTTF-Regular.ttx") ttxpath = self.getpath("TestTTF-Regular.ttx")
font = TTFont() font = TTFont()
font.importXML(ttxpath) font.importXML(ttxpath)
modified = font['head'].modified modified = font['head'].modified
_, fontpath = self.compile_font(ttxpath, ".ttf") fontpath = self.compile_font(ttxpath, ".ttf")
subsetpath = self.temp_path(".ttf") subsetpath = self.temp_path(".ttf")
# by default, the subsetter does not recalculate the modified timestamp # by default, the subsetter does not recalculate the modified timestamp
subset.main([fontpath, "--output-file=%s" % subsetpath, "*"]) 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, "*"]) 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): def test_recalc_timestamp_otf(self):
ttxpath = self.getpath("TestOTF-Regular.ttx") ttxpath = self.getpath("TestOTF-Regular.ttx")
font = TTFont() font = TTFont()
font.importXML(ttxpath) font.importXML(ttxpath)
modified = font['head'].modified modified = font['head'].modified
_, fontpath = self.compile_font(ttxpath, ".otf") fontpath = self.compile_font(ttxpath, ".otf")
subsetpath = self.temp_path(".otf") subsetpath = self.temp_path(".otf")
# by default, the subsetter does not recalculate the modified timestamp # by default, the subsetter does not recalculate the modified timestamp
subset.main([fontpath, "--output-file=%s" % subsetpath, "*"]) 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, "*"]) 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): def test_recalc_max_context(self):
ttxpath = self.getpath("Lobster.subset.ttx") ttxpath = self.getpath("Lobster.subset.ttx")
font = TTFont() font = TTFont()
font.importXML(ttxpath) font.importXML(ttxpath)
max_context = font['OS/2'].usMaxContext max_context = font['OS/2'].usMaxContext
_, fontpath = self.compile_font(ttxpath, ".otf") fontpath = self.compile_font(ttxpath, ".otf")
subsetpath = self.temp_path(".otf") subsetpath = self.temp_path(".otf")
# by default, the subsetter does not recalculate the usMaxContext # by default, the subsetter does not recalculate the usMaxContext
subset.main([fontpath, "--drop-tables+=GSUB,GPOS", subset.main([fontpath, "--drop-tables+=GSUB,GPOS",
"--output-file=%s" % subsetpath]) "--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", subset.main([fontpath, "--recalc-max-context",
"--drop-tables+=GSUB,GPOS", "--drop-tables+=GSUB,GPOS",
"--output-file=%s" % subsetpath]) "--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): 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) font = TTFont(fontpath)
self.assertEqual(font["hmtx"]["A"], (500, 132)) assert font["hmtx"]["A"] == (500, 132)
self.assertEqual(font["hmtx"]["B"], (400, 132)) assert font["hmtx"]["B"] == (400, 132)
self.assertGreater(font["glyf"]["A"].numberOfContours, 0) assert font["glyf"]["A"].numberOfContours > 0
self.assertGreater(font["glyf"]["B"].numberOfContours, 0) assert font["glyf"]["B"].numberOfContours > 0
subsetpath = self.temp_path(".ttf") subsetpath = self.temp_path(".ttf")
subset.main( subset.main(
@ -601,29 +599,29 @@ class SubsetTest(unittest.TestCase):
) )
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
self.assertEqual(subsetfont.getGlyphOrder(), font.getGlyphOrder()[0:3]) assert subsetfont.getGlyphOrder() == font.getGlyphOrder()[0:3]
hmtx = subsetfont["hmtx"] hmtx = subsetfont["hmtx"]
self.assertEqual(hmtx["A"], ( 0, 0)) assert hmtx["A"] == (0, 0)
self.assertEqual(hmtx["B"], (400, 132)) assert hmtx["B"] == (400, 132)
glyf = subsetfont["glyf"] glyf = subsetfont["glyf"]
self.assertEqual(glyf["A"].numberOfContours, 0) assert glyf["A"].numberOfContours == 0
self.assertGreater(glyf["B"].numberOfContours, 0) assert glyf["B"].numberOfContours > 0
def test_retain_gids_cff(self): 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) font = TTFont(fontpath)
self.assertEqual(font["hmtx"]["A"], (500, 132)) assert font["hmtx"]["A"] == (500, 132)
self.assertEqual(font["hmtx"]["B"], (400, 132)) assert font["hmtx"]["B"] == (400, 132)
self.assertEqual(font["hmtx"]["C"], (500, 0)) assert font["hmtx"]["C"] == (500, 0)
font["CFF "].cff[0].decompileAllCharStrings() font["CFF "].cff[0].decompileAllCharStrings()
cs = font["CFF "].cff[0].CharStrings cs = font["CFF "].cff[0].CharStrings
self.assertGreater(len(cs["A"].program), 0) assert len(cs["A"].program) > 0
self.assertGreater(len(cs["B"].program), 0) assert len(cs["B"].program) > 0
self.assertGreater(len(cs["C"].program), 0) assert len(cs["C"].program) > 0
subsetpath = self.temp_path(".otf") subsetpath = self.temp_path(".otf")
subset.main( subset.main(
@ -637,29 +635,30 @@ class SubsetTest(unittest.TestCase):
) )
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
self.assertEqual(subsetfont.getGlyphOrder(), font.getGlyphOrder()[0:3]) assert subsetfont.getGlyphOrder() == font.getGlyphOrder()[0:3]
hmtx = subsetfont["hmtx"] hmtx = subsetfont["hmtx"]
self.assertEqual(hmtx["A"], (0, 0)) assert hmtx["A"] == (0, 0)
self.assertEqual(hmtx["B"], (400, 132)) assert hmtx["B"] == (400, 132)
subsetfont["CFF "].cff[0].decompileAllCharStrings() subsetfont["CFF "].cff[0].decompileAllCharStrings()
cs = subsetfont["CFF "].cff[0].CharStrings cs = subsetfont["CFF "].cff[0].CharStrings
self.assertEqual(cs["A"].program, ["endchar"]) assert cs["A"].program == ["endchar"]
self.assertGreater(len(cs["B"].program), 0) assert len(cs["B"].program) > 0
def test_retain_gids_cff2(self): def test_retain_gids_cff2(self):
ttx_path = self.getpath("../../varLib/data/master_ttx_varfont_otf/TestCFF2VF.ttx") 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)) assert font["hmtx"]["A"] == (600, 31)
self.assertEqual(font["hmtx"]["T"], (600, 41)) assert font["hmtx"]["T"] == (600, 41)
font["CFF2"].cff[0].decompileAllCharStrings() font["CFF2"].cff[0].decompileAllCharStrings()
cs = font["CFF2"].cff[0].CharStrings cs = font["CFF2"].cff[0].CharStrings
self.assertGreater(len(cs["A"].program), 0) assert len(cs["A"].program) > 0
self.assertGreater(len(cs["T"].program), 0) assert len(cs["T"].program) > 0
subsetpath = self.temp_path(".otf") subsetpath = self.temp_path(".otf")
subset.main( subset.main(
@ -672,33 +671,33 @@ class SubsetTest(unittest.TestCase):
) )
subsetfont = TTFont(subsetpath) 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"] hmtx = subsetfont["hmtx"]
self.assertEqual(hmtx["glyph00001"], ( 0, 0)) assert hmtx["glyph00001"] == (0, 0)
self.assertEqual(hmtx["T"], (600, 41)) assert hmtx["T"] == (600, 41)
subsetfont["CFF2"].cff[0].decompileAllCharStrings() subsetfont["CFF2"].cff[0].decompileAllCharStrings()
cs = subsetfont["CFF2"].cff[0].CharStrings cs = subsetfont["CFF2"].cff[0].CharStrings
self.assertEqual(cs["glyph00001"].program, []) assert cs["glyph00001"].program == []
self.assertGreater(len(cs["T"].program), 0) assert len(cs["T"].program) > 0
def test_HVAR_VVAR(self): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--text=BD", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--text=BD", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
self.expect_ttx(subsetfont, self.getpath("expect_HVVAR.ttx"), ["GlyphOrder", "HVAR", "VVAR", "avar", "fvar"]) self.expect_ttx(subsetfont, self.getpath("expect_HVVAR.ttx"), ["GlyphOrder", "HVAR", "VVAR", "avar", "fvar"])
def test_HVAR_VVAR_retain_gids(self): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--text=BD", "--retain-gids", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--text=BD", "--retain-gids", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
self.expect_ttx(subsetfont, self.getpath("expect_HVVAR_retain_gids.ttx"), ["GlyphOrder", "HVAR", "VVAR", "avar", "fvar"]) self.expect_ttx(subsetfont, self.getpath("expect_HVVAR_retain_gids.ttx"), ["GlyphOrder", "HVAR", "VVAR", "avar", "fvar"])
def test_subset_flavor(self): 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) font = TTFont(fontpath)
woff_path = self.temp_path(".woff") woff_path = self.temp_path(".woff")
@ -712,7 +711,7 @@ class SubsetTest(unittest.TestCase):
) )
woff = TTFont(woff_path) woff = TTFont(woff_path)
self.assertEqual(woff.flavor, "woff") assert woff.flavor == "woff"
woff2_path = self.temp_path(".woff2") woff2_path = self.temp_path(".woff2")
subset.main( subset.main(
@ -725,7 +724,7 @@ class SubsetTest(unittest.TestCase):
) )
woff2 = TTFont(woff2_path) woff2 = TTFont(woff2_path)
self.assertEqual(woff2.flavor, "woff2") assert woff2.flavor == "woff2"
ttf_path = self.temp_path(".ttf") ttf_path = self.temp_path(".ttf")
subset.main( subset.main(
@ -737,13 +736,13 @@ class SubsetTest(unittest.TestCase):
) )
ttf = TTFont(ttf_path) ttf = TTFont(ttf_path)
self.assertEqual(ttf.flavor, None) assert ttf.flavor is None
def test_subset_context_subst_format_3(self): def test_subset_context_subst_format_3(self):
# https://github.com/fonttools/fonttools/issues/1879 # https://github.com/fonttools/fonttools/issues/1879
# Test font contains 'calt' feature with Format 3 ContextSubst lookup subtables # Test font contains 'calt' feature with Format 3 ContextSubst lookup subtables
ttx = self.getpath("TestContextSubstFormat3.ttx") ttx = self.getpath("TestContextSubstFormat3.ttx")
font, fontpath = self.compile_font(ttx, ".ttf") fontpath = self.compile_font(ttx, ".ttf")
subsetpath = self.temp_path(".ttf") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--unicodes=*", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--unicodes=*", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
@ -751,13 +750,14 @@ class SubsetTest(unittest.TestCase):
self.expect_ttx(subsetfont, ttx) self.expect_ttx(subsetfont, ttx)
def test_cmap_prune_format12(self): 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") subsetpath = self.temp_path(".ttf")
subset.main([fontpath, "--glyphs=a", "--output-file=%s" % subsetpath]) subset.main([fontpath, "--glyphs=a", "--output-file=%s" % subsetpath])
subsetfont = TTFont(subsetpath) subsetfont = TTFont(subsetpath)
self.expect_ttx(subsetfont, self.getpath("CmapSubsetTest.subset.ttx"), ["cmap"]) 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'): # 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 # 1) that it's reused for ClassDef1 when it becomes empty as the subset glyphset
# is intersected with the table's Coverage # is intersected with the table's Coverage
@ -767,16 +767,14 @@ class SubsetTest(unittest.TestCase):
# The test font (from Harfbuzz test suite) is constructed to trigger these two # The test font (from Harfbuzz test suite) is constructed to trigger these two
# situations depending on the input subset --text. # situations depending on the input subset --text.
# https://github.com/fonttools/fonttools/pull/2221 # https://github.com/fonttools/fonttools/pull/2221
_, fontpath = self.compile_font( fontpath = self.compile_font(
self.getpath("GPOS_PairPos_Format2_PR_2221.ttx"), ".ttf" self.getpath("GPOS_PairPos_Format2_PR_2221.ttx"), ".ttf"
) )
subsetpath = self.temp_path(".ttf") subsetpath = self.temp_path(".ttf")
for n, text in enumerate("!#", start=1):
expected_ttx = self.getpath( expected_ttx = self.getpath(
f"GPOS_PairPos_Format2_ClassDef{n}_useClass0.subset.ttx" f"GPOS_PairPos_Format2_ClassDef{n}_useClass0.subset.ttx"
) )
with self.subTest(text=text, expected_ttx=expected_ttx):
subset.main( subset.main(
[ [
fontpath, fontpath,
@ -789,7 +787,7 @@ class SubsetTest(unittest.TestCase):
self.expect_ttx(subsetfont, expected_ttx, ["GPOS"]) self.expect_ttx(subsetfont, expected_ttx, ["GPOS"])
def test_GPOS_SinglePos_prune_post_subset_no_value(self): 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" self.getpath("GPOS_SinglePos_no_value_issue_2312.ttx"), ".ttf"
) )
subsetpath = self.temp_path(".ttf") subsetpath = self.temp_path(".ttf")
@ -801,15 +799,60 @@ class SubsetTest(unittest.TestCase):
["GlyphOrder", "GPOS"], ["GlyphOrder", "GPOS"],
) )
def test_harfbuzz_repacker(self): @pytest.mark.parametrize(
_, fontpath = self.compile_font(self.getpath("harfbuzz_repacker.ttx"), ".otf") "installed, ok",
subsetpath = self.temp_path(".otf") [
subset.main([fontpath, "--unicodes=0x53a9", "--layout-features=*", pytest.param(True, True, id="installed-ok"),
"--output-file=%s" % subsetpath]) pytest.param(True, False, id="installed-fail"),
subsetfont = TTFont(subsetpath) pytest.param(False, True, id="not_installed"),
self.expect_ttx(subsetfont, self.getpath("expect_harfbuzz_repacker.ttx"), ],
["GSUB"]) )
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 @pytest.fixture