1#!/usr/bin/env python3 2 3# Pre-generates the expected output subset files (via fonttools) for 4# specified subset test suite(s). 5 6import os 7import sys 8import shutil 9import io 10import re 11import tempfile 12 13from difflib import unified_diff 14from fontTools.ttLib import TTFont 15 16from subprocess import check_call 17from subset_test_suite import SubsetTestSuite 18 19 20def usage(): 21 print("Usage: generate-expected-outputs.py hb-subset <test suite file> ...") 22 23 24def strip_check_sum (ttx_string): 25 return re.sub ('checkSumAdjustment value=["]0x([0-9a-fA-F])+["]', 26 'checkSumAdjustment value="0x00000000"', 27 ttx_string, count=1) 28 29 30def generate_expected_output(input_file, unicodes, profile_flags, instance_flags, iup_optimize, output_directory, font_name, no_fonttools): 31 input_path = input_file 32 if not no_fonttools and instance_flags: 33 instance_path = os.path.join(tempfile.mkdtemp (), font_name) 34 args = ["fonttools", "varLib.instancer", 35 "--no-overlap-flag", 36 "--no-recalc-timestamp"] 37 if not iup_optimize: 38 args.extend(["--no-optimize",]) 39 args.extend(["--output=%s" % instance_path, 40 input_file]) 41 args.extend(instance_flags) 42 check_call(args) 43 input_path = instance_path 44 45 fonttools_path = os.path.join(tempfile.mkdtemp (), font_name) 46 args = ["fonttools", "subset", input_path] 47 if instance_flags: 48 args.extend(["--recalc-bounds", "--recalc-average-width"]) 49 args.extend(["--drop-tables+=DSIG,BASE", 50 "--drop-tables-=sbix", 51 "--no-harfbuzz-repacker", # disable harfbuzz repacker so we aren't comparing to ourself. 52 "--output-file=%s" % fonttools_path]) 53 if unicodes != "": 54 args.extend(["--unicodes=%s" % unicodes,]) 55 56 # --gid-map is unsupported in fonttools so don't send it. Tests using 57 # it are crafted to work without fonttools knowing about the flag. 58 args.extend([f for f in profile_flags if not f.startswith("--gid-map")]) 59 # Harfbuzz doesn't support pruning codepage ranges, so disable it in fonttools. 60 args.extend(["--no-prune-codepage-ranges"]) 61 if not no_fonttools: 62 check_call(args) 63 64 with io.StringIO () as fp: 65 with TTFont (fonttools_path) as font: 66 font.saveXML (fp) 67 fonttools_ttx = strip_check_sum (fp.getvalue ()) 68 69 harfbuzz_path = os.path.join(tempfile.mkdtemp (), font_name) 70 args = [ 71 hb_subset, 72 "--font-file=" + input_file, 73 "--output-file=" + harfbuzz_path, 74 "--drop-tables+=DSIG,BASE", 75 "--drop-tables-=sbix"] 76 if unicodes != "": 77 args.extend(["--unicodes=%s" % unicodes,]) 78 args.extend(profile_flags) 79 if instance_flags: 80 args.extend(["--instance=%s" % ','.join(instance_flags)]) 81 if iup_optimize: 82 args.extend(["--optimize",]) 83 check_call(args) 84 85 with io.StringIO () as fp: 86 with TTFont (harfbuzz_path) as font: 87 font.saveXML (fp) 88 harfbuzz_ttx = strip_check_sum (fp.getvalue ()) 89 90 if not no_fonttools and harfbuzz_ttx != fonttools_ttx: 91 for line in unified_diff (fonttools_ttx.splitlines (1), harfbuzz_ttx.splitlines (1), fonttools_path, harfbuzz_path): 92 sys.stdout.write (line) 93 sys.stdout.flush () 94 raise Exception ('ttx for fonttools and harfbuzz does not match.') 95 96 output_path = os.path.join(output_directory, font_name) 97 shutil.copy(harfbuzz_path, output_path) 98 99 100args = sys.argv[1:] 101if not args: 102 usage() 103hb_subset, args = args[0], args[1:] 104if not args: 105 usage() 106 107for path in args: 108 with open(path, mode="r", encoding="utf-8") as f: 109 test_suite = SubsetTestSuite(path, f.read()) 110 output_directory = test_suite.get_output_directory() 111 112 print("Generating output files for %s" % output_directory) 113 for test in test_suite.tests(): 114 unicodes = test.unicodes() 115 font_name = test.get_font_name() 116 no_fonttools = ("no_fonttools" in test.options) 117 print("Creating subset %s/%s" % (output_directory, font_name)) 118 generate_expected_output(test.font_path, unicodes, test.get_profile_flags(), 119 test.get_instance_flags(), test.iup_optimize, output_directory, font_name, no_fonttools=no_fonttools) 120