From cb5aa96702b6434fafb4ff77b8eee80b4ada7083 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Mon, 24 Apr 2023 11:22:53 +0100 Subject: [PATCH] add missing __main__.py file to ttLib package, plus some tests Fixes https://github.com/fonttools/fonttools/issues/17 --- Lib/fontTools/ttLib/__init__.py | 97 ------------------------- Lib/fontTools/ttLib/__main__.py | 100 ++++++++++++++++++++++++++ Tests/ttLib/main_test.py | 121 ++++++++++++++++++++++++++++++++ 3 files changed, 221 insertions(+), 97 deletions(-) create mode 100644 Lib/fontTools/ttLib/__main__.py create mode 100644 Tests/ttLib/main_test.py diff --git a/Lib/fontTools/ttLib/__init__.py b/Lib/fontTools/ttLib/__init__.py index 97e2f4cca..ed00764f7 100644 --- a/Lib/fontTools/ttLib/__init__.py +++ b/Lib/fontTools/ttLib/__init__.py @@ -2,7 +2,6 @@ from fontTools.misc.loggingTools import deprecateFunction import logging -import sys log = logging.getLogger(__name__) @@ -25,99 +24,3 @@ def debugmsg(msg): from fontTools.ttLib.ttFont import * from fontTools.ttLib.ttCollection import TTCollection - - -def main(args=None): - """Open/save fonts with TTFont() or TTCollection() - - ./fonttools ttLib [-oFILE] [-yNUMBER] files... - - If multiple files are given on the command-line, - they are each opened (as a font or collection), - and added to the font list. - - If -o (output-file) argument is given, the font - list is then saved to the output file, either as - a single font, if there is only one font, or as - a collection otherwise. - - If -y (font-number) argument is given, only the - specified font from collections is opened. - - The above allow extracting a single font from a - collection, or combining multiple fonts into a - collection. - - If --lazy or --no-lazy are give, those are passed - to the TTFont() or TTCollection() constructors. - """ - from fontTools import configLogger - - if args is None: - args = sys.argv[1:] - - import argparse - - parser = argparse.ArgumentParser( - "fonttools ttLib", - description="Open/save fonts with TTFont() or TTCollection()", - epilog=""" - If multiple files are given on the command-line, - they are each opened (as a font or collection), - and added to the font list. - - The above, when combined with -o / --output, - allows for extracting a single font from a - collection, or combining multiple fonts into a - collection. - """, - ) - parser.add_argument("font", metavar="font", nargs="*", help="Font file.") - parser.add_argument( - "-o", "--output", metavar="FILE", default=None, help="Output file." - ) - parser.add_argument( - "-y", metavar="NUMBER", default=-1, help="Font number to load from collections." - ) - parser.add_argument( - "--lazy", action="store_true", default=None, help="Load fonts lazily." - ) - parser.add_argument( - "--no-lazy", dest="lazy", action="store_false", help="Load fonts immediately." - ) - parser.add_argument( - "--flavor", - dest="flavor", - default=None, - help="Flavor of output font. 'woff' or 'woff2'.", - ) - options = parser.parse_args(args) - - fontNumber = int(options.y) if options.y is not None else None - outFile = options.output - lazy = options.lazy - flavor = options.flavor - - fonts = [] - for f in options.font: - try: - font = TTFont(f, fontNumber=fontNumber, lazy=lazy) - fonts.append(font) - except TTLibFileIsCollectionError: - collection = TTCollection(f, lazy=lazy) - fonts.extend(collection.fonts) - - if outFile is not None: - if len(fonts) == 1: - fonts[0].flavor = flavor - fonts[0].save(outFile) - else: - if flavor is not None: - raise TTLibError("Cannot set flavor for collections.") - collection = TTCollection() - collection.fonts = fonts - collection.save(outFile) - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/Lib/fontTools/ttLib/__main__.py b/Lib/fontTools/ttLib/__main__.py new file mode 100644 index 000000000..d9b2a465d --- /dev/null +++ b/Lib/fontTools/ttLib/__main__.py @@ -0,0 +1,100 @@ +import sys +from fontTools.ttLib import TTLibError, TTLibFileIsCollectionError +from fontTools.ttLib.ttFont import * +from fontTools.ttLib.ttCollection import TTCollection + + +def main(args=None): + """Open/save fonts with TTFont() or TTCollection() + + ./fonttools ttLib [-oFILE] [-yNUMBER] files... + + If multiple files are given on the command-line, + they are each opened (as a font or collection), + and added to the font list. + + If -o (output-file) argument is given, the font + list is then saved to the output file, either as + a single font, if there is only one font, or as + a collection otherwise. + + If -y (font-number) argument is given, only the + specified font from collections is opened. + + The above allow extracting a single font from a + collection, or combining multiple fonts into a + collection. + + If --lazy or --no-lazy are give, those are passed + to the TTFont() or TTCollection() constructors. + """ + from fontTools import configLogger + + if args is None: + args = sys.argv[1:] + + import argparse + + parser = argparse.ArgumentParser( + "fonttools ttLib", + description="Open/save fonts with TTFont() or TTCollection()", + epilog=""" + If multiple files are given on the command-line, + they are each opened (as a font or collection), + and added to the font list. + + The above, when combined with -o / --output, + allows for extracting a single font from a + collection, or combining multiple fonts into a + collection. + """, + ) + parser.add_argument("font", metavar="font", nargs="*", help="Font file.") + parser.add_argument( + "-o", "--output", metavar="FILE", default=None, help="Output file." + ) + parser.add_argument( + "-y", metavar="NUMBER", default=-1, help="Font number to load from collections." + ) + parser.add_argument( + "--lazy", action="store_true", default=None, help="Load fonts lazily." + ) + parser.add_argument( + "--no-lazy", dest="lazy", action="store_false", help="Load fonts immediately." + ) + parser.add_argument( + "--flavor", + dest="flavor", + default=None, + help="Flavor of output font. 'woff' or 'woff2'.", + ) + options = parser.parse_args(args) + + fontNumber = int(options.y) if options.y is not None else None + outFile = options.output + lazy = options.lazy + flavor = options.flavor + + fonts = [] + for f in options.font: + try: + font = TTFont(f, fontNumber=fontNumber, lazy=lazy) + fonts.append(font) + except TTLibFileIsCollectionError: + collection = TTCollection(f, lazy=lazy) + fonts.extend(collection.fonts) + + if outFile is not None: + if len(fonts) == 1: + fonts[0].flavor = flavor + fonts[0].save(outFile) + else: + if flavor is not None: + raise TTLibError("Cannot set flavor for collections.") + collection = TTCollection() + collection.fonts = fonts + collection.save(outFile) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/Tests/ttLib/main_test.py b/Tests/ttLib/main_test.py new file mode 100644 index 000000000..78b01cc33 --- /dev/null +++ b/Tests/ttLib/main_test.py @@ -0,0 +1,121 @@ +import subprocess +import sys +import tempfile +from pathlib import Path + +from fontTools.ttLib import TTFont, TTCollection + +import pytest + + +TEST_DATA = Path(__file__).parent / "data" + + +@pytest.fixture +def ttfont_path(): + font = TTFont() + font.importXML(TEST_DATA / "TestTTF-Regular.ttx") + with tempfile.NamedTemporaryFile(suffix=".ttf", delete=False) as fp: + font_path = Path(fp.name) + font.save(font_path) + yield font_path + font_path.unlink() + + +@pytest.fixture +def ttcollection_path(): + font1 = TTFont() + font1.importXML(TEST_DATA / "TestTTF-Regular.ttx") + font2 = TTFont() + font2.importXML(TEST_DATA / "TestTTF-Regular.ttx") + coll = TTCollection() + coll.fonts = [font1, font2] + with tempfile.NamedTemporaryFile(suffix=".ttf", delete=False) as fp: + collection_path = Path(fp.name) + coll.save(collection_path) + yield collection_path + collection_path.unlink() + + +@pytest.fixture(params=[None, "woff"]) +def flavor(request): + return request.param + + +def test_ttLib_open_ttfont(ttfont_path): + subprocess.check_call([sys.executable, "-m", "fontTools.ttLib", ttfont_path]) + + +def test_ttLib_open_save_ttfont(tmp_path, ttfont_path, flavor): + output_path = tmp_path / "TestTTF-Regular.ttf" + args = [sys.executable, "-m", "fontTools.ttLib", "-o", output_path, ttfont_path] + if flavor is not None: + args.extend(["--flavor", flavor]) + subprocess.check_call(args) + assert output_path.exists() + assert TTFont(output_path).getGlyphOrder() == TTFont(ttfont_path).getGlyphOrder() + + +def test_ttLib_open_ttcollection(ttcollection_path): + subprocess.check_call( + [sys.executable, "-m", "fontTools.ttLib", "-y", "0", ttcollection_path] + ) + + +def test_ttLib_open_ttcollection_save_single_font(tmp_path, ttcollection_path, flavor): + for i in range(2): + output_path = tmp_path / f"TestTTF-Regular#{i}.ttf" + args = [ + sys.executable, + "-m", + "fontTools.ttLib", + "-y", + str(i), + "-o", + output_path, + ttcollection_path, + ] + if flavor is not None: + args.extend(["--flavor", flavor]) + subprocess.check_call(args) + assert output_path.exists() + assert ( + TTFont(output_path).getGlyphOrder() + == TTCollection(ttcollection_path)[i].getGlyphOrder() + ) + + +def test_ttLib_open_ttcollection_save_ttcollection(tmp_path, ttcollection_path): + output_path = tmp_path / "TestTTF.ttc" + subprocess.check_call( + [ + sys.executable, + "-m", + "fontTools.ttLib", + "-o", + output_path, + ttcollection_path, + ] + ) + assert output_path.exists() + assert len(TTCollection(output_path)) == len(TTCollection(ttcollection_path)) + + +def test_ttLib_open_multiple_fonts_save_ttcollection(tmp_path, ttfont_path): + output_path = tmp_path / "TestTTF.ttc" + subprocess.check_call( + [ + sys.executable, + "-m", + "fontTools.ttLib", + "-o", + output_path, + ttfont_path, + ttfont_path, + ] + ) + assert output_path.exists() + + coll = TTCollection(output_path) + assert len(coll) == 2 + assert coll[0].getGlyphOrder() == coll[1].getGlyphOrder()