fonttools/Tests/ttx/ttx_test.py

1028 lines
30 KiB
Python
Raw Normal View History

2017-03-27 04:04:02 -07:00
from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
from fontTools.misc.testTools import parseXML
from fontTools.misc.timeTools import timestampSinceEpoch
from fontTools.ttLib import TTFont, TTLibError
2017-03-27 04:04:02 -07:00
from fontTools import ttx
import getopt
import logging
2017-03-27 04:04:02 -07:00
import os
import shutil
import sys
2017-03-27 04:04:02 -07:00
import tempfile
import unittest
import pytest
2017-03-27 04:04:02 -07:00
try:
import zopfli
except ImportError:
zopfli = None
try:
import brotli
except ImportError:
brotli = None
2018-05-22 00:41:22 -04:00
2017-03-27 04:04:02 -07:00
class TTXTest(unittest.TestCase):
2018-05-22 00:41:22 -04:00
2017-03-27 04:04:02 -07:00
def __init__(self, methodName):
unittest.TestCase.__init__(self, methodName)
# Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
# and fires deprecation warnings if a program uses the old name.
if not hasattr(self, "assertRaisesRegex"):
self.assertRaisesRegex = self.assertRaisesRegexp
def setUp(self):
self.tempdir = None
self.num_tempfiles = 0
def tearDown(self):
if self.tempdir:
shutil.rmtree(self.tempdir)
@staticmethod
def getpath(testfile):
path, _ = os.path.split(__file__)
return os.path.join(path, "data", testfile)
def temp_dir(self):
if not self.tempdir:
self.tempdir = tempfile.mkdtemp()
def temp_font(self, font_path, file_name):
self.temp_dir()
temppath = os.path.join(self.tempdir, file_name)
shutil.copy2(font_path, temppath)
return temppath
@staticmethod
def read_file(file_path):
with open(file_path, "r", encoding="utf-8") as f:
return f.readlines()
2018-05-22 00:41:22 -04:00
# -----
# Tests
# -----
2017-03-27 04:04:02 -07:00
def test_parseOptions_no_args(self):
with self.assertRaises(getopt.GetoptError) as cm:
ttx.parseOptions([])
2018-05-22 00:41:22 -04:00
self.assertTrue(
"Must specify at least one input file" in str(cm.exception)
)
2017-03-27 04:04:02 -07:00
def test_parseOptions_invalid_path(self):
2018-05-22 00:41:22 -04:00
file_path = "invalid_font_path"
2017-03-27 04:04:02 -07:00
with self.assertRaises(getopt.GetoptError) as cm:
ttx.parseOptions([file_path])
self.assertTrue('File not found: "%s"' % file_path in str(cm.exception))
def test_parseOptions_font2ttx_1st_time(self):
2018-05-22 00:41:22 -04:00
file_name = "TestOTF.otf"
2017-03-27 04:04:02 -07:00
font_path = self.getpath(file_name)
temp_path = self.temp_font(font_path, file_name)
jobs, _ = ttx.parseOptions([temp_path])
2018-05-22 00:41:22 -04:00
self.assertEqual(jobs[0][0].__name__, "ttDump")
self.assertEqual(
jobs[0][1:],
(
os.path.join(self.tempdir, file_name),
os.path.join(self.tempdir, file_name.split(".")[0] + ".ttx"),
),
)
2017-03-27 04:04:02 -07:00
def test_parseOptions_font2ttx_2nd_time(self):
2018-05-22 00:41:22 -04:00
file_name = "TestTTF.ttf"
2017-03-27 04:04:02 -07:00
font_path = self.getpath(file_name)
temp_path = self.temp_font(font_path, file_name)
2018-05-22 00:41:22 -04:00
_, _ = ttx.parseOptions([temp_path]) # this is NOT a mistake
2017-03-27 04:04:02 -07:00
jobs, _ = ttx.parseOptions([temp_path])
2018-05-22 00:41:22 -04:00
self.assertEqual(jobs[0][0].__name__, "ttDump")
self.assertEqual(
jobs[0][1:],
(
os.path.join(self.tempdir, file_name),
os.path.join(self.tempdir, file_name.split(".")[0] + "#1.ttx"),
),
)
2017-03-27 04:04:02 -07:00
def test_parseOptions_ttx2font_1st_time(self):
2018-05-22 00:41:22 -04:00
file_name = "TestTTF.ttx"
2017-03-27 04:04:02 -07:00
font_path = self.getpath(file_name)
temp_path = self.temp_font(font_path, file_name)
jobs, _ = ttx.parseOptions([temp_path])
2018-05-22 00:41:22 -04:00
self.assertEqual(jobs[0][0].__name__, "ttCompile")
self.assertEqual(
jobs[0][1:],
(
os.path.join(self.tempdir, file_name),
os.path.join(self.tempdir, file_name.split(".")[0] + ".ttf"),
),
)
2017-03-27 04:04:02 -07:00
def test_parseOptions_ttx2font_2nd_time(self):
2018-05-22 00:41:22 -04:00
file_name = "TestOTF.ttx"
2017-03-27 04:04:02 -07:00
font_path = self.getpath(file_name)
temp_path = self.temp_font(font_path, file_name)
2018-05-22 00:41:22 -04:00
_, _ = ttx.parseOptions([temp_path]) # this is NOT a mistake
2017-03-27 04:04:02 -07:00
jobs, _ = ttx.parseOptions([temp_path])
2018-05-22 00:41:22 -04:00
self.assertEqual(jobs[0][0].__name__, "ttCompile")
self.assertEqual(
jobs[0][1:],
(
os.path.join(self.tempdir, file_name),
os.path.join(self.tempdir, file_name.split(".")[0] + "#1.otf"),
),
)
2017-03-27 04:04:02 -07:00
def test_parseOptions_multiple_fonts(self):
2018-05-22 00:41:22 -04:00
file_names = ["TestOTF.otf", "TestTTF.ttf"]
2017-03-27 04:04:02 -07:00
font_paths = [self.getpath(file_name) for file_name in file_names]
2018-05-22 00:41:22 -04:00
temp_paths = [
self.temp_font(font_path, file_name)
for font_path, file_name in zip(font_paths, file_names)
]
2017-03-27 04:04:02 -07:00
jobs, _ = ttx.parseOptions(temp_paths)
for i in range(len(jobs)):
2018-05-22 00:41:22 -04:00
self.assertEqual(jobs[i][0].__name__, "ttDump")
self.assertEqual(
jobs[i][1:],
(
os.path.join(self.tempdir, file_names[i]),
os.path.join(
self.tempdir, file_names[i].split(".")[0] + ".ttx"
),
),
)
2017-03-27 04:04:02 -07:00
def test_parseOptions_mixed_files(self):
2018-05-22 00:41:22 -04:00
operations = ["ttDump", "ttCompile"]
extensions = [".ttx", ".ttf"]
file_names = ["TestOTF.otf", "TestTTF.ttx"]
2017-03-27 04:04:02 -07:00
font_paths = [self.getpath(file_name) for file_name in file_names]
2018-05-22 00:41:22 -04:00
temp_paths = [
self.temp_font(font_path, file_name)
for font_path, file_name in zip(font_paths, file_names)
]
2017-03-27 04:04:02 -07:00
jobs, _ = ttx.parseOptions(temp_paths)
for i in range(len(jobs)):
self.assertEqual(jobs[i][0].__name__, operations[i])
2018-05-22 00:41:22 -04:00
self.assertEqual(
jobs[i][1:],
(
os.path.join(self.tempdir, file_names[i]),
os.path.join(
self.tempdir,
file_names[i].split(".")[0] + extensions[i],
),
),
)
2017-03-27 04:04:02 -07:00
def test_parseOptions_splitTables(self):
2018-05-22 00:41:22 -04:00
file_name = "TestTTF.ttf"
font_path = self.getpath(file_name)
temp_path = self.temp_font(font_path, file_name)
2018-05-22 00:41:22 -04:00
args = ["-s", temp_path]
jobs, options = ttx.parseOptions(args)
ttx_file_path = jobs[0][2]
temp_folder = os.path.dirname(ttx_file_path)
self.assertTrue(options.splitTables)
self.assertTrue(os.path.exists(ttx_file_path))
ttx.process(jobs, options)
# Read the TTX file but strip the first two and the last lines:
# <?xml version="1.0" encoding="UTF-8"?>
# <ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.22">
# ...
# </ttFont>
parsed_xml = parseXML(self.read_file(ttx_file_path)[2:-1])
for item in parsed_xml:
if not isinstance(item, tuple):
continue
# the tuple looks like this:
# (u'head', {u'src': u'TestTTF._h_e_a_d.ttx'}, [])
2018-05-22 00:41:22 -04:00
table_file_name = item[1].get("src")
table_file_path = os.path.join(temp_folder, table_file_name)
self.assertTrue(os.path.exists(table_file_path))
def test_parseOptions_splitGlyphs(self):
2018-05-22 00:41:22 -04:00
file_name = "TestTTF.ttf"
font_path = self.getpath(file_name)
temp_path = self.temp_font(font_path, file_name)
2018-05-22 00:41:22 -04:00
args = ["-g", temp_path]
jobs, options = ttx.parseOptions(args)
ttx_file_path = jobs[0][2]
temp_folder = os.path.dirname(ttx_file_path)
self.assertTrue(options.splitGlyphs)
# splitGlyphs also forces splitTables
self.assertTrue(options.splitTables)
self.assertTrue(os.path.exists(ttx_file_path))
ttx.process(jobs, options)
# Read the TTX file but strip the first two and the last lines:
# <?xml version="1.0" encoding="UTF-8"?>
# <ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.22">
# ...
# </ttFont>
for item in parseXML(self.read_file(ttx_file_path)[2:-1]):
if not isinstance(item, tuple):
continue
# the tuple looks like this:
# (u'head', {u'src': u'TestTTF._h_e_a_d.ttx'}, [])
table_tag = item[0]
2018-05-22 00:41:22 -04:00
table_file_name = item[1].get("src")
table_file_path = os.path.join(temp_folder, table_file_name)
self.assertTrue(os.path.exists(table_file_path))
if table_tag != "glyf":
continue
# also strip the enclosing 'glyf' element
for item in parseXML(self.read_file(table_file_path)[4:-3]):
if not isinstance(item, tuple):
continue
# glyphs without outline data only have 'name' attribute
2018-05-22 00:41:22 -04:00
glyph_file_name = item[1].get("src")
if glyph_file_name is not None:
2018-05-22 00:41:22 -04:00
glyph_file_path = os.path.join(temp_folder, glyph_file_name)
self.assertTrue(os.path.exists(glyph_file_path))
2017-03-27 04:04:02 -07:00
def test_guessFileType_ttf(self):
2018-05-22 00:41:22 -04:00
file_name = "TestTTF.ttf"
2017-03-27 04:04:02 -07:00
font_path = self.getpath(file_name)
2018-05-22 00:41:22 -04:00
self.assertEqual(ttx.guessFileType(font_path), "TTF")
2017-03-27 04:04:02 -07:00
def test_guessFileType_otf(self):
2018-05-22 00:41:22 -04:00
file_name = "TestOTF.otf"
2017-03-27 04:04:02 -07:00
font_path = self.getpath(file_name)
2018-05-22 00:41:22 -04:00
self.assertEqual(ttx.guessFileType(font_path), "OTF")
2017-03-27 04:04:02 -07:00
def test_guessFileType_woff(self):
2018-05-22 00:41:22 -04:00
file_name = "TestWOFF.woff"
2017-03-27 04:04:02 -07:00
font_path = self.getpath(file_name)
2018-05-22 00:41:22 -04:00
self.assertEqual(ttx.guessFileType(font_path), "WOFF")
2017-03-27 04:04:02 -07:00
def test_guessFileType_woff2(self):
2018-05-22 00:41:22 -04:00
file_name = "TestWOFF2.woff2"
2017-03-27 04:04:02 -07:00
font_path = self.getpath(file_name)
2018-05-22 00:41:22 -04:00
self.assertEqual(ttx.guessFileType(font_path), "WOFF2")
2017-03-27 04:04:02 -07:00
def test_guessFileType_ttc(self):
2018-05-22 00:41:22 -04:00
file_name = "TestTTC.ttc"
2017-03-27 04:04:02 -07:00
font_path = self.getpath(file_name)
2018-05-22 00:41:22 -04:00
self.assertEqual(ttx.guessFileType(font_path), "TTC")
2017-03-27 04:04:02 -07:00
def test_guessFileType_dfont(self):
2018-05-22 00:41:22 -04:00
file_name = "TestDFONT.dfont"
2017-03-27 04:04:02 -07:00
font_path = self.getpath(file_name)
2018-05-22 00:41:22 -04:00
self.assertEqual(ttx.guessFileType(font_path), "TTF")
2017-03-27 04:04:02 -07:00
def test_guessFileType_ttx_ttf(self):
2018-05-22 00:41:22 -04:00
file_name = "TestTTF.ttx"
2017-03-27 04:04:02 -07:00
font_path = self.getpath(file_name)
2018-05-22 00:41:22 -04:00
self.assertEqual(ttx.guessFileType(font_path), "TTX")
2017-03-27 04:04:02 -07:00
def test_guessFileType_ttx_otf(self):
2018-05-22 00:41:22 -04:00
file_name = "TestOTF.ttx"
2017-03-27 04:04:02 -07:00
font_path = self.getpath(file_name)
2018-05-22 00:41:22 -04:00
self.assertEqual(ttx.guessFileType(font_path), "OTX")
2017-03-27 04:04:02 -07:00
def test_guessFileType_ttx_bom(self):
2018-05-22 00:41:22 -04:00
file_name = "TestBOM.ttx"
2017-03-27 04:04:02 -07:00
font_path = self.getpath(file_name)
2018-05-22 00:41:22 -04:00
self.assertEqual(ttx.guessFileType(font_path), "TTX")
2017-03-27 04:04:02 -07:00
def test_guessFileType_ttx_no_sfntVersion(self):
2018-05-22 00:41:22 -04:00
file_name = "TestNoSFNT.ttx"
2017-03-27 04:04:02 -07:00
font_path = self.getpath(file_name)
2018-05-22 00:41:22 -04:00
self.assertEqual(ttx.guessFileType(font_path), "TTX")
2017-03-27 04:04:02 -07:00
def test_guessFileType_ttx_no_xml(self):
2018-05-22 00:41:22 -04:00
file_name = "TestNoXML.ttx"
2017-03-27 04:04:02 -07:00
font_path = self.getpath(file_name)
self.assertIsNone(ttx.guessFileType(font_path))
def test_guessFileType_invalid_path(self):
2018-05-22 00:41:22 -04:00
font_path = "invalid_font_path"
2017-03-27 04:04:02 -07:00
self.assertIsNone(ttx.guessFileType(font_path))
# -----------------------
# ttx.Options class tests
# -----------------------
2018-05-22 00:41:22 -04:00
def test_options_flag_h(capsys):
with pytest.raises(SystemExit):
ttx.Options([("-h", None)], 1)
out, err = capsys.readouterr()
assert "TTX -- From OpenType To XML And Back" in out
2018-05-22 00:41:22 -04:00
def test_options_flag_version(capsys):
with pytest.raises(SystemExit):
ttx.Options([("--version", None)], 1)
out, err = capsys.readouterr()
version_list = out.split(".")
assert len(version_list) >= 3
assert version_list[0].isdigit()
assert version_list[1].isdigit()
assert version_list[2].strip().isdigit()
2018-05-22 00:41:22 -04:00
def test_options_d_goodpath(tmpdir):
temp_dir_path = str(tmpdir)
tto = ttx.Options([("-d", temp_dir_path)], 1)
assert tto.outputDir == temp_dir_path
2018-05-22 00:41:22 -04:00
def test_options_d_badpath():
with pytest.raises(getopt.GetoptError):
ttx.Options([("-d", "bogusdir")], 1)
2018-05-22 00:41:22 -04:00
def test_options_o():
tto = ttx.Options([("-o", "testfile.ttx")], 1)
assert tto.outputFile == "testfile.ttx"
2018-05-22 00:41:22 -04:00
def test_options_f():
tto = ttx.Options([("-f", "")], 1)
assert tto.overWrite is True
2018-05-22 00:41:22 -04:00
def test_options_v():
tto = ttx.Options([("-v", "")], 1)
assert tto.verbose is True
assert tto.logLevel == logging.DEBUG
2018-05-22 00:41:22 -04:00
def test_options_q():
tto = ttx.Options([("-q", "")], 1)
assert tto.quiet is True
assert tto.logLevel == logging.WARNING
2018-05-22 00:41:22 -04:00
def test_options_l():
tto = ttx.Options([("-l", "")], 1)
assert tto.listTables is True
2018-05-22 00:41:22 -04:00
def test_options_t_nopadding():
tto = ttx.Options([("-t", "CFF2")], 1)
assert len(tto.onlyTables) == 1
assert tto.onlyTables[0] == "CFF2"
2018-05-22 00:41:22 -04:00
def test_options_t_withpadding():
tto = ttx.Options([("-t", "CFF")], 1)
assert len(tto.onlyTables) == 1
assert tto.onlyTables[0] == "CFF "
2018-05-22 00:41:22 -04:00
def test_options_s():
tto = ttx.Options([("-s", "")], 1)
assert tto.splitTables is True
assert tto.splitGlyphs is False
2018-05-22 00:41:22 -04:00
def test_options_g():
tto = ttx.Options([("-g", "")], 1)
assert tto.splitGlyphs is True
assert tto.splitTables is True
2018-05-22 00:41:22 -04:00
def test_options_i():
tto = ttx.Options([("-i", "")], 1)
assert tto.disassembleInstructions is False
2018-05-22 00:41:22 -04:00
def test_options_z_validoptions():
valid_options = ("raw", "row", "bitwise", "extfile")
for option in valid_options:
tto = ttx.Options([("-z", option)], 1)
assert tto.bitmapGlyphDataFormat == option
2018-05-22 00:41:22 -04:00
def test_options_z_invalidoption():
with pytest.raises(getopt.GetoptError):
ttx.Options([("-z", "bogus")], 1)
2018-05-22 00:41:22 -04:00
def test_options_y_validvalue():
tto = ttx.Options([("-y", "1")], 1)
assert tto.fontNumber == 1
2018-05-22 00:41:22 -04:00
def test_options_y_invalidvalue():
with pytest.raises(ValueError):
ttx.Options([("-y", "A")], 1)
2018-05-22 00:41:22 -04:00
def test_options_m():
tto = ttx.Options([("-m", "testfont.ttf")], 1)
assert tto.mergeFile == "testfont.ttf"
2018-05-22 00:41:22 -04:00
def test_options_b():
tto = ttx.Options([("-b", "")], 1)
assert tto.recalcBBoxes is False
2018-05-22 00:41:22 -04:00
def test_options_a():
tto = ttx.Options([("-a", "")], 1)
assert tto.allowVID is True
2018-05-22 00:41:22 -04:00
def test_options_e():
tto = ttx.Options([("-e", "")], 1)
assert tto.ignoreDecompileErrors is False
2018-05-22 00:41:22 -04:00
def test_options_unicodedata():
tto = ttx.Options([("--unicodedata", "UnicodeData.txt")], 1)
assert tto.unicodedata == "UnicodeData.txt"
2018-05-22 00:41:22 -04:00
def test_options_newline_lf():
tto = ttx.Options([("--newline", "LF")], 1)
assert tto.newlinestr == "\n"
2018-05-22 00:41:22 -04:00
def test_options_newline_cr():
tto = ttx.Options([("--newline", "CR")], 1)
assert tto.newlinestr == "\r"
2018-05-22 00:41:22 -04:00
def test_options_newline_crlf():
tto = ttx.Options([("--newline", "CRLF")], 1)
assert tto.newlinestr == "\r\n"
2018-05-22 00:41:22 -04:00
def test_options_newline_invalid():
with pytest.raises(getopt.GetoptError):
ttx.Options([("--newline", "BOGUS")], 1)
2018-05-22 00:41:22 -04:00
def test_options_recalc_timestamp():
tto = ttx.Options([("--recalc-timestamp", "")], 1)
assert tto.recalcTimestamp is True
def test_options_recalc_timestamp():
tto = ttx.Options([("--no-recalc-timestamp", "")], 1)
assert tto.recalcTimestamp is False
2018-05-22 00:41:22 -04:00
def test_options_flavor():
tto = ttx.Options([("--flavor", "woff")], 1)
assert tto.flavor == "woff"
2018-05-22 00:41:22 -04:00
def test_options_with_zopfli():
tto = ttx.Options([("--with-zopfli", ""), ("--flavor", "woff")], 1)
assert tto.useZopfli is True
2018-05-22 00:41:22 -04:00
def test_options_with_zopfli_fails_without_woff_flavor():
with pytest.raises(getopt.GetoptError):
ttx.Options([("--with-zopfli", "")], 1)
2018-05-22 00:41:22 -04:00
def test_options_quiet_and_verbose_shouldfail():
with pytest.raises(getopt.GetoptError):
ttx.Options([("-q", ""), ("-v", "")], 1)
2018-05-22 00:41:22 -04:00
def test_options_mergefile_and_flavor_shouldfail():
with pytest.raises(getopt.GetoptError):
ttx.Options([("-m", "testfont.ttf"), ("--flavor", "woff")], 1)
2018-05-22 00:41:22 -04:00
def test_options_onlytables_and_skiptables_shouldfail():
with pytest.raises(getopt.GetoptError):
ttx.Options([("-t", "CFF"), ("-x", "CFF2")], 1)
2018-05-22 00:41:22 -04:00
def test_options_mergefile_and_multiplefiles_shouldfail():
with pytest.raises(getopt.GetoptError):
ttx.Options([("-m", "testfont.ttf")], 2)
2018-05-22 00:41:22 -04:00
def test_options_woff2_and_zopfli_shouldfail():
with pytest.raises(getopt.GetoptError):
ttx.Options([("--with-zopfli", ""), ("--flavor", "woff2")], 1)
2018-05-22 00:41:22 -04:00
# ----------------------------
# ttx.ttCompile function tests
# ----------------------------
2018-05-22 00:41:22 -04:00
def test_ttcompile_otf_compile_default(tmpdir):
inttx = os.path.join("Tests", "ttx", "data", "TestOTF.ttx")
# outotf = os.path.join(str(tmpdir), "TestOTF.otf")
outotf = tmpdir.join("TestOTF.ttx")
2018-05-22 00:41:22 -04:00
default_options = ttx.Options([], 1)
ttx.ttCompile(inttx, str(outotf), default_options)
# confirm that font was built
assert outotf.check(file=True)
# confirm that it is valid OTF file, can instantiate a TTFont, has expected OpenType tables
ttf = TTFont(str(outotf))
2018-05-22 00:41:22 -04:00
expected_tables = (
"head",
"hhea",
"maxp",
"OS/2",
"name",
"cmap",
"post",
"CFF ",
"hmtx",
"DSIG",
)
for table in expected_tables:
assert table in ttf
2018-05-22 00:41:22 -04:00
def test_ttcompile_otf_to_woff_without_zopfli(tmpdir):
inttx = os.path.join("Tests", "ttx", "data", "TestOTF.ttx")
outwoff = tmpdir.join("TestOTF.woff")
2018-05-22 00:41:22 -04:00
options = ttx.Options([], 1)
options.flavor = "woff"
ttx.ttCompile(inttx, str(outwoff), options)
# confirm that font was built
assert outwoff.check(file=True)
# confirm that it is valid TTF file, can instantiate a TTFont, has expected OpenType tables
ttf = TTFont(str(outwoff))
2018-05-22 00:41:22 -04:00
expected_tables = (
"head",
"hhea",
"maxp",
"OS/2",
"name",
"cmap",
"post",
"CFF ",
"hmtx",
"DSIG",
)
for table in expected_tables:
assert table in ttf
@pytest.mark.skipif(zopfli is None, reason="zopfli not installed")
2018-05-22 00:41:22 -04:00
def test_ttcompile_otf_to_woff_with_zopfli(tmpdir):
inttx = os.path.join("Tests", "ttx", "data", "TestOTF.ttx")
outwoff = tmpdir.join("TestOTF.woff")
2018-05-22 00:41:22 -04:00
options = ttx.Options([], 1)
options.flavor = "woff"
options.useZopfli = True
ttx.ttCompile(inttx, str(outwoff), options)
# confirm that font was built
assert outwoff.check(file=True)
# confirm that it is valid TTF file, can instantiate a TTFont, has expected OpenType tables
ttf = TTFont(str(outwoff))
2018-05-22 00:41:22 -04:00
expected_tables = (
"head",
"hhea",
"maxp",
"OS/2",
"name",
"cmap",
"post",
"CFF ",
"hmtx",
"DSIG",
)
for table in expected_tables:
assert table in ttf
@pytest.mark.skipif(brotli is None, reason="brotli not installed")
2018-05-22 00:41:22 -04:00
def test_ttcompile_otf_to_woff2(tmpdir):
inttx = os.path.join("Tests", "ttx", "data", "TestOTF.ttx")
outwoff2 = tmpdir.join("TestTTF.woff2")
2018-05-22 00:41:22 -04:00
options = ttx.Options([], 1)
options.flavor = "woff2"
ttx.ttCompile(inttx, str(outwoff2), options)
# confirm that font was built
assert outwoff2.check(file=True)
# confirm that it is valid TTF file, can instantiate a TTFont, has expected OpenType tables
ttf = TTFont(str(outwoff2))
# DSIG should not be included from original ttx as per woff2 spec (https://dev.w3.org/webfonts/WOFF2/spec/)
assert "DSIG" not in ttf
2018-05-22 00:41:22 -04:00
expected_tables = (
"head",
"hhea",
"maxp",
"OS/2",
"name",
"cmap",
"post",
"CFF ",
"hmtx",
)
for table in expected_tables:
assert table in ttf
2018-05-22 00:41:22 -04:00
def test_ttcompile_ttf_compile_default(tmpdir):
inttx = os.path.join("Tests", "ttx", "data", "TestTTF.ttx")
outttf = tmpdir.join("TestTTF.ttf")
2018-05-22 00:41:22 -04:00
default_options = ttx.Options([], 1)
ttx.ttCompile(inttx, str(outttf), default_options)
# confirm that font was built
assert outttf.check(file=True)
# confirm that it is valid TTF file, can instantiate a TTFont, has expected OpenType tables
ttf = TTFont(str(outttf))
2018-05-22 00:41:22 -04:00
expected_tables = (
"head",
"hhea",
"maxp",
"OS/2",
"name",
"cmap",
"hmtx",
"fpgm",
"prep",
"cvt ",
"loca",
"glyf",
"post",
"gasp",
"DSIG",
)
for table in expected_tables:
assert table in ttf
2018-05-22 00:41:22 -04:00
def test_ttcompile_ttf_to_woff_without_zopfli(tmpdir):
inttx = os.path.join("Tests", "ttx", "data", "TestTTF.ttx")
outwoff = tmpdir.join("TestTTF.woff")
2018-05-22 00:41:22 -04:00
options = ttx.Options([], 1)
options.flavor = "woff"
ttx.ttCompile(inttx, str(outwoff), options)
# confirm that font was built
assert outwoff.check(file=True)
# confirm that it is valid TTF file, can instantiate a TTFont, has expected OpenType tables
ttf = TTFont(str(outwoff))
2018-05-22 00:41:22 -04:00
expected_tables = (
"head",
"hhea",
"maxp",
"OS/2",
"name",
"cmap",
"hmtx",
"fpgm",
"prep",
"cvt ",
"loca",
"glyf",
"post",
"gasp",
"DSIG",
)
for table in expected_tables:
assert table in ttf
@pytest.mark.skipif(zopfli is None, reason="zopfli not installed")
2018-05-22 00:41:22 -04:00
def test_ttcompile_ttf_to_woff_with_zopfli(tmpdir):
inttx = os.path.join("Tests", "ttx", "data", "TestTTF.ttx")
outwoff = tmpdir.join("TestTTF.woff")
2018-05-22 00:41:22 -04:00
options = ttx.Options([], 1)
options.flavor = "woff"
options.useZopfli = True
ttx.ttCompile(inttx, str(outwoff), options)
# confirm that font was built
assert outwoff.check(file=True)
# confirm that it is valid TTF file, can instantiate a TTFont, has expected OpenType tables
ttf = TTFont(str(outwoff))
2018-05-22 00:41:22 -04:00
expected_tables = (
"head",
"hhea",
"maxp",
"OS/2",
"name",
"cmap",
"hmtx",
"fpgm",
"prep",
"cvt ",
"loca",
"glyf",
"post",
"gasp",
"DSIG",
)
for table in expected_tables:
assert table in ttf
@pytest.mark.skipif(brotli is None, reason="brotli not installed")
2018-05-22 00:41:22 -04:00
def test_ttcompile_ttf_to_woff2(tmpdir):
inttx = os.path.join("Tests", "ttx", "data", "TestTTF.ttx")
outwoff2 = tmpdir.join("TestTTF.woff2")
2018-05-22 00:41:22 -04:00
options = ttx.Options([], 1)
options.flavor = "woff2"
ttx.ttCompile(inttx, str(outwoff2), options)
# confirm that font was built
assert outwoff2.check(file=True)
# confirm that it is valid TTF file, can instantiate a TTFont, has expected OpenType tables
ttf = TTFont(str(outwoff2))
# DSIG should not be included from original ttx as per woff2 spec (https://dev.w3.org/webfonts/WOFF2/spec/)
assert "DSIG" not in ttf
2018-05-22 00:41:22 -04:00
expected_tables = (
"head",
"hhea",
"maxp",
"OS/2",
"name",
"cmap",
"hmtx",
"fpgm",
"prep",
"cvt ",
"loca",
"glyf",
"post",
"gasp",
)
for table in expected_tables:
assert table in ttf
@pytest.mark.parametrize(
"inpath, outpath1, outpath2",
[
("TestTTF.ttx", "TestTTF1.ttf", "TestTTF2.ttf"),
("TestOTF.ttx", "TestOTF1.otf", "TestOTF2.otf"),
],
)
2018-05-22 07:50:23 -04:00
def test_ttcompile_timestamp_calcs(inpath, outpath1, outpath2, tmpdir):
inttx = os.path.join("Tests", "ttx", "data", inpath)
outttf1 = tmpdir.join(outpath1)
outttf2 = tmpdir.join(outpath2)
2018-05-22 00:41:22 -04:00
options = ttx.Options([], 1)
# build with default options = do not recalculate timestamp
ttx.ttCompile(inttx, str(outttf1), options)
# confirm that font was built
assert outttf1.check(file=True)
# confirm that timestamp is same as modified time on ttx file
mtime = os.path.getmtime(inttx)
epochtime = timestampSinceEpoch(mtime)
ttf = TTFont(str(outttf1))
2018-05-22 00:41:22 -04:00
assert ttf["head"].modified == epochtime
# reset options to recalculate the timestamp and compile new font
options.recalcTimestamp = True
ttx.ttCompile(inttx, str(outttf2), options)
# confirm that font was built
assert outttf2.check(file=True)
# confirm that timestamp is more recent than modified time on ttx file
mtime = os.path.getmtime(inttx)
epochtime = timestampSinceEpoch(mtime)
ttf = TTFont(str(outttf2))
2018-05-22 00:41:22 -04:00
assert ttf["head"].modified > epochtime
# --no-recalc-timestamp will keep original timestamp
options.recalcTimestamp = False
ttx.ttCompile(inttx, str(outttf2), options)
assert outttf2.check(file=True)
inttf = TTFont()
inttf.importXML(inttx)
assert inttf["head"].modified == TTFont(str(outttf2))["head"].modified
# -------------------------
# ttx.ttList function tests
# -------------------------
2018-05-22 00:41:22 -04:00
def test_ttlist_ttf(capsys, tmpdir):
inpath = os.path.join("Tests", "ttx", "data", "TestTTF.ttf")
fakeoutpath = tmpdir.join("TestTTF.ttx")
2018-05-22 00:41:22 -04:00
options = ttx.Options([], 1)
options.listTables = True
ttx.ttList(inpath, str(fakeoutpath), options)
out, err = capsys.readouterr()
2018-05-22 00:41:22 -04:00
expected_tables = (
"head",
"hhea",
"maxp",
"OS/2",
"name",
"cmap",
"hmtx",
"fpgm",
"prep",
"cvt ",
"loca",
"glyf",
"post",
"gasp",
"DSIG",
)
# confirm that expected tables are printed to stdout
for table in expected_tables:
assert table in out
# test for one of the expected tag/checksum/length/offset strings
assert "OS/2 0x67230FF8 96 376" in out
2018-05-22 00:41:22 -04:00
def test_ttlist_otf(capsys, tmpdir):
2018-05-14 17:15:28 -04:00
inpath = os.path.join("Tests", "ttx", "data", "TestOTF.otf")
fakeoutpath = tmpdir.join("TestOTF.ttx")
2018-05-22 00:41:22 -04:00
options = ttx.Options([], 1)
options.listTables = True
ttx.ttList(inpath, str(fakeoutpath), options)
out, err = capsys.readouterr()
2018-05-22 00:41:22 -04:00
expected_tables = (
"head",
"hhea",
"maxp",
"OS/2",
"name",
"cmap",
"post",
"CFF ",
"hmtx",
"DSIG",
)
# confirm that expected tables are printed to stdout
for table in expected_tables:
assert table in out
# test for one of the expected tag/checksum/length/offset strings
assert "OS/2 0x67230FF8 96 272" in out
2018-05-22 00:41:22 -04:00
def test_ttlist_woff(capsys, tmpdir):
inpath = os.path.join("Tests", "ttx", "data", "TestWOFF.woff")
fakeoutpath = tmpdir.join("TestWOFF.ttx")
2018-05-22 00:41:22 -04:00
options = ttx.Options([], 1)
options.listTables = True
options.flavor = "woff"
ttx.ttList(inpath, str(fakeoutpath), options)
out, err = capsys.readouterr()
2018-05-22 00:41:22 -04:00
expected_tables = (
"head",
"hhea",
"maxp",
"OS/2",
"name",
"cmap",
"post",
"CFF ",
"hmtx",
"DSIG",
)
# confirm that expected tables are printed to stdout
for table in expected_tables:
assert table in out
# test for one of the expected tag/checksum/length/offset strings
assert "OS/2 0x67230FF8 84 340" in out
@pytest.mark.skipif(brotli is None, reason="brotli not installed")
2018-05-22 00:41:22 -04:00
def test_ttlist_woff2(capsys, tmpdir):
inpath = os.path.join("Tests", "ttx", "data", "TestWOFF2.woff2")
fakeoutpath = tmpdir.join("TestWOFF2.ttx")
2018-05-22 00:41:22 -04:00
options = ttx.Options([], 1)
options.listTables = True
options.flavor = "woff2"
ttx.ttList(inpath, str(fakeoutpath), options)
out, err = capsys.readouterr()
2018-05-22 00:41:22 -04:00
expected_tables = (
"head",
"hhea",
"maxp",
"OS/2",
"name",
"cmap",
"hmtx",
"fpgm",
"prep",
"cvt ",
"loca",
"glyf",
"post",
"gasp",
)
# confirm that expected tables are printed to stdout
for table in expected_tables:
assert table in out
# test for one of the expected tag/checksum/length/offset strings
assert "OS/2 0x67230FF8 96 0" in out
2018-05-22 00:41:22 -04:00
# -------------------
# main function tests
# -------------------
2018-05-22 00:41:22 -04:00
def test_main_default_ttf_dump_to_ttx(tmpdir):
inpath = os.path.join("Tests", "ttx", "data", "TestTTF.ttf")
outpath = tmpdir.join("TestTTF.ttx")
args = ["-o", str(outpath), inpath]
ttx.main(args)
assert outpath.check(file=True)
2018-05-22 00:41:22 -04:00
def test_main_default_ttx_compile_to_ttf(tmpdir):
inpath = os.path.join("Tests", "ttx", "data", "TestTTF.ttx")
outpath = tmpdir.join("TestTTF.ttf")
args = ["-o", str(outpath), inpath]
ttx.main(args)
assert outpath.check(file=True)
2018-05-22 00:41:22 -04:00
def test_main_getopterror_missing_directory():
with pytest.raises(SystemExit):
with pytest.raises(getopt.GetoptError):
inpath = os.path.join("Tests", "ttx", "data", "TestTTF.ttf")
args = ["-d", "bogusdir", inpath]
ttx.main(args)
2018-05-22 00:41:22 -04:00
def test_main_keyboard_interrupt(tmpdir, monkeypatch, capsys):
with pytest.raises(SystemExit):
inpath = os.path.join("Tests", "ttx", "data", "TestTTF.ttx")
outpath = tmpdir.join("TestTTF.ttf")
args = ["-o", str(outpath), inpath]
2018-05-22 00:41:22 -04:00
monkeypatch.setattr(
ttx, "process", (lambda x, y: raise_exception(KeyboardInterrupt))
)
ttx.main(args)
out, err = capsys.readouterr()
assert "(Cancelled.)" in err
2018-05-22 00:41:22 -04:00
@pytest.mark.skipif(
sys.platform == "win32",
reason="waitForKeyPress function causes test to hang on Windows platform",
)
def test_main_system_exit(tmpdir, monkeypatch):
with pytest.raises(SystemExit):
inpath = os.path.join("Tests", "ttx", "data", "TestTTF.ttx")
outpath = tmpdir.join("TestTTF.ttf")
args = ["-o", str(outpath), inpath]
2018-05-22 00:41:22 -04:00
monkeypatch.setattr(
ttx, "process", (lambda x, y: raise_exception(SystemExit))
)
ttx.main(args)
2018-05-22 00:41:22 -04:00
def test_main_ttlib_error(tmpdir, monkeypatch, capsys):
with pytest.raises(SystemExit):
inpath = os.path.join("Tests", "ttx", "data", "TestTTF.ttx")
outpath = tmpdir.join("TestTTF.ttf")
args = ["-o", str(outpath), inpath]
2018-05-22 00:41:22 -04:00
monkeypatch.setattr(
ttx,
"process",
(lambda x, y: raise_exception(TTLibError("Test error"))),
)
ttx.main(args)
out, err = capsys.readouterr()
assert "Test error" in err
2018-05-22 00:41:22 -04:00
@pytest.mark.skipif(
sys.platform == "win32",
reason="waitForKeyPress function causes test to hang on Windows platform",
)
def test_main_base_exception(tmpdir, monkeypatch, capsys):
with pytest.raises(SystemExit):
inpath = os.path.join("Tests", "ttx", "data", "TestTTF.ttx")
outpath = tmpdir.join("TestTTF.ttf")
args = ["-o", str(outpath), inpath]
2018-05-22 00:41:22 -04:00
monkeypatch.setattr(
ttx,
"process",
(lambda x, y: raise_exception(Exception("Test error"))),
)
ttx.main(args)
out, err = capsys.readouterr()
assert "Unhandled exception has occurred" in err
2018-05-22 00:41:22 -04:00
# ---------------------------
# support functions for tests
# ---------------------------
def raise_exception(exception):
raise exception