|
@@ -1,18 +1,30 @@
|
|
#!/usr/bin/env python3
|
|
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
-#----------------------------------------------------------------
|
|
|
|
|
|
+#------------------------------------------------------------------------------------------------------
|
|
# checksum.py
|
|
# checksum.py
|
|
# A SHA1 hash checksum list generator for fonts and fontTools
|
|
# A SHA1 hash checksum list generator for fonts and fontTools
|
|
-# XML dumps of font OpenType table data
|
|
|
|
|
|
+# XML dumps of font OpenType table data + checksum testing
|
|
|
|
+# script
|
|
#
|
|
#
|
|
# Copyright 2018 Christopher Simpkins
|
|
# Copyright 2018 Christopher Simpkins
|
|
# MIT License
|
|
# MIT License
|
|
#
|
|
#
|
|
|
|
+# Dependencies: Python fontTools library
|
|
|
|
+#
|
|
# Usage: checksum.py (options) [file path 1]...[file path n]
|
|
# Usage: checksum.py (options) [file path 1]...[file path n]
|
|
#
|
|
#
|
|
-# Dependencies: Python fontTools library
|
|
|
|
-#----------------------------------------------------------------
|
|
|
|
|
|
+# Options:
|
|
|
|
+# -h, --help Help
|
|
|
|
+# -t, --ttx Calculate SHA1 hash values from ttx dump of XML (default = font binary)
|
|
|
|
+# -s, --stdout Stream to standard output stream (default = write to disk as 'checksum.txt')
|
|
|
|
+# -c, --check Test SHA1 checksum values against a valid checksum file
|
|
|
|
+#
|
|
|
|
+# Options, --ttx only:
|
|
|
|
+# -e, --exclude Excluded OpenType table (may be used more than once, mutually exclusive with -i)
|
|
|
|
+# -i, --include Included OpenType table (may be used more than once, mutually exclusive with -e)
|
|
|
|
+# -n, --noclean Do not discard .ttx files that are used to calculate SHA1 hashes
|
|
|
|
+#-------------------------------------------------------------------------------------------------------
|
|
|
|
|
|
import argparse
|
|
import argparse
|
|
import hashlib
|
|
import hashlib
|
|
@@ -24,7 +36,7 @@ from os.path import basename
|
|
from fontTools.ttLib import TTFont
|
|
from fontTools.ttLib import TTFont
|
|
|
|
|
|
|
|
|
|
-def main(filepaths, stdout_write=False, use_ttx=False, include_tables=None, exclude_tables=None, do_not_cleanup=False):
|
|
|
|
|
|
+def write_checksum(filepaths, stdout_write=False, use_ttx=False, include_tables=None, exclude_tables=None, do_not_cleanup=False):
|
|
checksum_dict = {}
|
|
checksum_dict = {}
|
|
for path in filepaths:
|
|
for path in filepaths:
|
|
if not os.path.exists(path):
|
|
if not os.path.exists(path):
|
|
@@ -42,6 +54,8 @@ def main(filepaths, stdout_write=False, use_ttx=False, include_tables=None, excl
|
|
temp_ttx_path = path + ".ttx"
|
|
temp_ttx_path = path + ".ttx"
|
|
|
|
|
|
tt = TTFont(path)
|
|
tt = TTFont(path)
|
|
|
|
+ # important to keep the newlinestr value defined here as hash values will change across platforms
|
|
|
|
+ # if platform specific newline values are assumed
|
|
tt.saveXML(temp_ttx_path, newlinestr="\n", skipTables=exclude_tables, tables=include_tables)
|
|
tt.saveXML(temp_ttx_path, newlinestr="\n", skipTables=exclude_tables, tables=include_tables)
|
|
checksum_path = temp_ttx_path
|
|
checksum_path = temp_ttx_path
|
|
else:
|
|
else:
|
|
@@ -55,7 +69,7 @@ def main(filepaths, stdout_write=False, use_ttx=False, include_tables=None, excl
|
|
sys.exit(1)
|
|
sys.exit(1)
|
|
checksum_path = path
|
|
checksum_path = path
|
|
|
|
|
|
- file_contents = read_binary(checksum_path)
|
|
|
|
|
|
+ file_contents = _read_binary(checksum_path)
|
|
|
|
|
|
# store SHA1 hash data and associated file path basename in the checksum_dict dictionary
|
|
# store SHA1 hash data and associated file path basename in the checksum_dict dictionary
|
|
checksum_dict[basename(checksum_path)] = hashlib.sha1(file_contents).hexdigest()
|
|
checksum_dict[basename(checksum_path)] = hashlib.sha1(file_contents).hexdigest()
|
|
@@ -78,7 +92,48 @@ def main(filepaths, stdout_write=False, use_ttx=False, include_tables=None, excl
|
|
file.write(checksum_out_data)
|
|
file.write(checksum_out_data)
|
|
|
|
|
|
|
|
|
|
-def read_binary(filepath):
|
|
|
|
|
|
+def check_checksum(filepaths):
|
|
|
|
+ check_failed = False
|
|
|
|
+ for path in filepaths:
|
|
|
|
+ if not os.path.exists(path):
|
|
|
|
+ sys.stderr.write("[checksum.py] ERROR: " + filepath + " is not a valid filepath" + os.linesep)
|
|
|
|
+ sys.exit(1)
|
|
|
|
+
|
|
|
|
+ with open(path, mode='r') as file:
|
|
|
|
+ for line in file.readlines():
|
|
|
|
+ cleaned_line = line.rstrip()
|
|
|
|
+ line_list = cleaned_line.split(" ")
|
|
|
|
+ # eliminate empty strings parsed from > 1 space characters
|
|
|
|
+ line_list = list(filter(None, line_list))
|
|
|
|
+ if len(line_list) == 2:
|
|
|
|
+ expected_sha1 = line_list[0]
|
|
|
|
+ test_path = line_list[1]
|
|
|
|
+ else:
|
|
|
|
+ sys.stderr.write("[checksum.py] ERROR: failed to parse checksum file values" + os.linesep)
|
|
|
|
+ sys.exit(1)
|
|
|
|
+
|
|
|
|
+ if not os.path.exists(test_path):
|
|
|
|
+ print(test_path + ": Filepath is not valid, ignored")
|
|
|
|
+ else:
|
|
|
|
+ file_contents = _read_binary(test_path)
|
|
|
|
+ observed_sha1 = hashlib.sha1(file_contents).hexdigest()
|
|
|
|
+ if observed_sha1 == expected_sha1:
|
|
|
|
+ print(test_path + ": OK")
|
|
|
|
+ else:
|
|
|
|
+ print("-" * 80)
|
|
|
|
+ print(test_path + ": === FAIL ===")
|
|
|
|
+ print("Expected vs. Observed:")
|
|
|
|
+ print(expected_sha1)
|
|
|
|
+ print(observed_sha1)
|
|
|
|
+ print("-" * 80)
|
|
|
|
+ check_failed = True
|
|
|
|
+
|
|
|
|
+ # exit with status code 1 if any fails detected across all tests in the check
|
|
|
|
+ if check_failed is True:
|
|
|
|
+ sys.exit(1)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+def _read_binary(filepath):
|
|
with open(filepath, mode='rb') as file:
|
|
with open(filepath, mode='rb') as file:
|
|
return file.read()
|
|
return file.read()
|
|
|
|
|
|
@@ -88,6 +143,7 @@ if __name__ == '__main__':
|
|
parser.add_argument("-t", "--ttx", help="Calculate from ttx file", action="store_true")
|
|
parser.add_argument("-t", "--ttx", help="Calculate from ttx file", action="store_true")
|
|
parser.add_argument("-s", "--stdout", help="Write output to stdout stream", action="store_true")
|
|
parser.add_argument("-s", "--stdout", help="Write output to stdout stream", action="store_true")
|
|
parser.add_argument("-n", "--noclean", help="Do not discard *.ttx files used to calculate SHA1 hashes", action="store_true")
|
|
parser.add_argument("-n", "--noclean", help="Do not discard *.ttx files used to calculate SHA1 hashes", action="store_true")
|
|
|
|
+ parser.add_argument("-c", "--check", help="Verify checksum values vs. files", action="store_true")
|
|
parser.add_argument("filepaths", nargs="+", help="One or more file paths to font binary files")
|
|
parser.add_argument("filepaths", nargs="+", help="One or more file paths to font binary files")
|
|
|
|
|
|
parser.add_argument("-i", "--include", action="append", help="Included OpenType tables for ttx data dump")
|
|
parser.add_argument("-i", "--include", action="append", help="Included OpenType tables for ttx data dump")
|
|
@@ -95,4 +151,7 @@ if __name__ == '__main__':
|
|
|
|
|
|
args = parser.parse_args(sys.argv[1:])
|
|
args = parser.parse_args(sys.argv[1:])
|
|
|
|
|
|
- main(args.filepaths, stdout_write=args.stdout, use_ttx=args.ttx, do_not_cleanup=args.noclean, include_tables=args.include, exclude_tables=args.exclude)
|
|
|
|
|
|
+ if args.check is True:
|
|
|
|
+ check_checksum(args.filepaths)
|
|
|
|
+ else:
|
|
|
|
+ write_checksum(args.filepaths, stdout_write=args.stdout, use_ttx=args.ttx, do_not_cleanup=args.noclean, include_tables=args.include, exclude_tables=args.exclude)
|