|
| 1 | +#!/usr/bin/env python3 |
| 2 | +# -*- coding: utf-8 -*- |
| 3 | + |
| 4 | +#---------------------------------------------------------------- |
| 5 | +# checksum.py |
| 6 | +# A SHA1 hash checksum list generator for fonts and fontTools |
| 7 | +# XML dumps of font OpenType table data |
| 8 | +# |
| 9 | +# Copyright 2018 Christopher Simpkins |
| 10 | +# MIT License |
| 11 | +# |
| 12 | +# Usage: checksum.py (options) [file path 1]...[file path n] |
| 13 | +# |
| 14 | +# Dependencies: Python fontTools library |
| 15 | +#---------------------------------------------------------------- |
| 16 | + |
| 17 | +import argparse |
| 18 | +import hashlib |
| 19 | +import os |
| 20 | +import sys |
| 21 | + |
| 22 | +from os.path import basename |
| 23 | + |
| 24 | +from fontTools.ttLib import TTFont |
| 25 | + |
| 26 | + |
| 27 | +def main(filepaths, stdout_write=False, use_ttx=False, include_tables=None, exclude_tables=None, do_not_cleanup=False): |
| 28 | + checksum_dict = {} |
| 29 | + for path in filepaths: |
| 30 | + if not os.path.exists(path): |
| 31 | + sys.stderr.write("[checksum.py] ERROR: " + path + " is not a valid file path" + os.linesep) |
| 32 | + sys.exit(1) |
| 33 | + |
| 34 | + if use_ttx: |
| 35 | + # append a .ttx extension to existing extension to maintain data about the binary that |
| 36 | + # was used to generate the .ttx XML dump. This creates unique checksum path values for |
| 37 | + # paths that would otherwise not be unique with a file extension replacement with .ttx |
| 38 | + # An example is woff and woff2 web font files that share the same base file name: |
| 39 | + # |
| 40 | + # coolfont-regular.woff ==> coolfont-regular.ttx |
| 41 | + # coolfont-regular.woff2 ==> coolfont-regular.ttx (KAPOW! checksum data lost as this would overwrite dict value) |
| 42 | + temp_ttx_path = path + ".ttx" |
| 43 | + |
| 44 | + tt = TTFont(path) |
| 45 | + tt.saveXML(temp_ttx_path, newlinestr="\n", skipTables=exclude_tables, tables=include_tables) |
| 46 | + checksum_path = temp_ttx_path |
| 47 | + else: |
| 48 | + if include_tables is not None: |
| 49 | + sys.stderr.write("[checksum.py] -i and --include are not supported for font binary filepaths. \ |
| 50 | + Use these flags for checksums with the --ttx flag.") |
| 51 | + sys.exit(1) |
| 52 | + if exclude_tables is not None: |
| 53 | + sys.stderr.write("[checksum.py] -e and --exclude are not supported for font binary filepaths. \ |
| 54 | + Use these flags for checksums with the --ttx flag.") |
| 55 | + sys.exit(1) |
| 56 | + checksum_path = path |
| 57 | + |
| 58 | + file_contents = read_binary(checksum_path) |
| 59 | + |
| 60 | + # store SHA1 hash data and associated file path basename in the checksum_dict dictionary |
| 61 | + checksum_dict[basename(checksum_path)] = hashlib.sha1(file_contents).hexdigest() |
| 62 | + |
| 63 | + # remove temp ttx files when present |
| 64 | + if use_ttx and do_not_cleanup is False: |
| 65 | + os.remove(temp_ttx_path) |
| 66 | + |
| 67 | + # generate the checksum list string for writes |
| 68 | + checksum_out_data = "" |
| 69 | + for key in checksum_dict.keys(): |
| 70 | + checksum_out_data += checksum_dict[key] + " " + key + "\n" |
| 71 | + |
| 72 | + # write to stdout stream or file based upon user request (default = file write) |
| 73 | + if stdout_write: |
| 74 | + sys.stdout.write(checksum_out_data) |
| 75 | + else: |
| 76 | + checksum_report_filepath = "checksum.txt" |
| 77 | + with open(checksum_report_filepath, "w") as file: |
| 78 | + file.write(checksum_out_data) |
| 79 | + |
| 80 | + |
| 81 | +def read_binary(filepath): |
| 82 | + with open(filepath, mode='rb') as file: |
| 83 | + return file.read() |
| 84 | + |
| 85 | + |
| 86 | +if __name__ == '__main__': |
| 87 | + parser = argparse.ArgumentParser(prog="checksum.py") |
| 88 | + parser.add_argument("-t", "--ttx", help="Calculate from ttx file", action="store_true") |
| 89 | + parser.add_argument("-s", "--stdout", help="Write output to stdout stream", action="store_true") |
| 90 | + parser.add_argument("-n", "--noclean", help="Do not discard *.ttx files used to calculate SHA1 hashes", action="store_true") |
| 91 | + parser.add_argument("filepaths", nargs="+", help="One or more file paths to font binary files") |
| 92 | + |
| 93 | + parser.add_argument("-i", "--include", action="append", help="Included OpenType tables for ttx data dump") |
| 94 | + parser.add_argument("-e", "--exclude", action="append", help="Excluded OpenType tables for ttx data dump") |
| 95 | + |
| 96 | + args = parser.parse_args(sys.argv[1:]) |
| 97 | + |
| 98 | + main(args.filepaths, stdout_write=args.stdout, use_ttx=args.ttx, do_not_cleanup=args.noclean, include_tables=args.include, exclude_tables=args.exclude) |
0 commit comments