|  | @@ -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)
 |