import argparse from collections import defaultdict import csv import time import sys from pathlib import Path from typing import Any, Iterable, List, Optional, Sequence, Tuple from fontTools.ttLib import TTFont from fontTools.otlLib.optimize import compact MODES = [str(c) for c in range(1, 10)] def main(args: Optional[List[str]] = None): parser = argparse.ArgumentParser() parser.add_argument("fonts", type=Path, nargs="+", help="Path to TTFs.") parsed_args = parser.parse_args(args) runtimes = defaultdict(list) rows = [] font_path: Path for font_path in parsed_args.fonts: font = TTFont(font_path) if "GPOS" not in font: print(f"No GPOS in {font_path.name}, skipping.", file=sys.stderr) continue size_orig = len(font.getTableData("GPOS")) / 1024 print(f"Measuring {font_path.name}...", file=sys.stderr) fonts = {} font_paths = {} sizes = {} for mode in MODES: print(f" Running mode={mode}", file=sys.stderr) fonts[mode] = TTFont(font_path) before = time.perf_counter() compact(fonts[mode], mode=str(mode)) runtimes[mode].append(time.perf_counter() - before) font_paths[mode] = ( font_path.parent / "compact" / (font_path.stem + f"_{mode}" + font_path.suffix) ) font_paths[mode].parent.mkdir(parents=True, exist_ok=True) fonts[mode].save(font_paths[mode]) fonts[mode] = TTFont(font_paths[mode]) sizes[mode] = len(fonts[mode].getTableData("GPOS")) / 1024 print(f" Runtimes:", file=sys.stderr) for mode, times in runtimes.items(): print( f" {mode:10} {' '.join(f'{t:5.2f}' for t in times)}", file=sys.stderr, ) # Bonus: measure WOFF2 file sizes. print(f" Measuring WOFF2 sizes", file=sys.stderr) size_woff_orig = woff_size(font, font_path) / 1024 sizes_woff = { mode: woff_size(fonts[mode], font_paths[mode]) / 1024 for mode in MODES } rows.append( ( font_path.name, size_orig, size_woff_orig, *flatten( ( sizes[mode], pct(sizes[mode], size_orig), sizes_woff[mode], pct(sizes_woff[mode], size_woff_orig), ) for mode in MODES ), ) ) write_csv(rows) def woff_size(font: TTFont, path: Path) -> int: font.flavor = "woff2" woff_path = path.with_suffix(".woff2") font.save(woff_path) return woff_path.stat().st_size def write_csv(rows: List[Tuple[Any]]) -> None: sys.stdout.reconfigure(encoding="utf-8") sys.stdout.write("\uFEFF") writer = csv.writer(sys.stdout, lineterminator="\n") writer.writerow( [ "File", "Original GPOS Size", "Original WOFF2 Size", *flatten( ( f"mode={mode}", f"Change {mode}", f"mode={mode} WOFF2 Size", f"Change {mode} WOFF2 Size", ) for mode in MODES ), ] ) for row in rows: writer.writerow(row) def pct(new: float, old: float) -> float: return -(1 - (new / old)) def flatten(seq_seq: Iterable[Iterable[Any]]) -> List[Any]: return [thing for seq in seq_seq for thing in seq] if __name__ == "__main__": main()