moved the development build scripts

pull/198/head
Chris Simpkins 2016-01-26 00:51:56 +07:00
parent 0e9b427231
commit 5e43f48752
3 changed files with 383 additions and 0 deletions

@ -0,0 +1,38 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
# dev-versioner.py
# Copyright 2016 Christopher Simpkins
# MIT license
# ------------------------------------------------------------------------------
import sys
from fontTools import ttLib
VERSION_STRING="Version 2.020;DEV-012616;"
SUCCESS_INDICATOR = 0
def main(argv):
for font_variant_path in argv:
tt = ttLib.TTFont(font_variant_path)
namerecord_list = tt['name'].__dict__['names']
for record in namerecord_list:
if record.__dict__['langID'] == 0:
if record.__dict__['nameID'] == 5:
record.__dict__['string'] = VERSION_STRING
path_list = font_variant_path.split(".")
outfile_path = path_list[0] + "-DEV." + path_list[1]
tt.save(outfile_path)
SUCCESS_INDICATOR = 1
print("Updated '" + font_variant_path + "' version string to " + VERSION_STRING)
if SUCCESS_INDICATOR == 0:
print("Unable to complete the name table update for " + font_variant_path)
if __name__ == '__main__':
main(sys.argv[1:])

@ -0,0 +1,148 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
# font-tables.py
# Copyright 2015 Christopher Simpkins
# MIT license
# ------------------------------------------------------------------------------
import sys
import os
import os.path
import hashlib
from fontTools import ttLib
# TODO: expand Python objects within the table values
# TODO: modify TTFA table read to create YAML format from the strings
def main(fontpaths):
"""The main function creates a YAML formatted report on the OpenType tables in one
or more fonts included in the fontpaths function parameter.
:param fontpaths: """
# create a report directory, gracefully fail if it already exists
if not os.path.isdir("otreports"):
os.mkdir("otreports")
# iterate through fonts requested in the command
for fontpath in fontpaths:
if os.path.isfile(fontpath):
# create a fonttools TTFont object using the fontpath
tt = ttLib.TTFont(fontpath)
print("Processing " + fontpath + "...")
# define the outfile path
basename = os.path.basename(fontpath)
basefilename = basename + "-TABLES.yaml"
outfilepath = os.path.join("otreports", basefilename)
# read the font data and create a SHA1 hash digest for the report
fontdata = read_bin(fontpath)
hash_digest = hashlib.sha1(fontdata).hexdigest()
# report strings for file name and SHA1 digest
report_header_string = "FILE: " + fontpath + "\n"
report_header_string += "SHA1: " + hash_digest + "\n\n"
# open outfile write stream, create file, write name + SHA1 header
with open(outfilepath, "w") as writer:
writer.write(report_header_string)
# iterate through the OpenType tables, write table fields in a newline delimited format with YAML syntax
for table in tt.keys():
table_dict = tt[table].__dict__
if len(table_dict) > 0:
table_string = yaml_formatter(table, table_dict)
with open(outfilepath, 'a') as appender:
appender.write(table_string)
print("[✓] " + table)
else:
print("[E] " + table) # indicate missing table data in standard output, do not write to YAML file
print(fontpath + " table report is available in " + outfilepath + "\n")
else: # not a valid filepath
sys.stderr.write("Error: '" + fontpath + "' was not found. Please check the filepath.\n\n")
def yaml_formatter(table_name, table_dict):
"""Creates a YAML formatted string for OpenType table font reports"""
# define the list of tables that require table-specific processing
special_table_list = ['name', 'OS/2', 'TTFA']
if table_name in special_table_list:
if table_name == "name":
return name_yaml_formatter(table_dict)
elif table_name == "OS/2":
return os2_yaml_formatter(table_dict)
elif table_name == "TTFA":
return ttfa_yaml_formatter(table_dict)
else:
table_string = table_name.strip() + ": {\n"
for field in table_dict.keys():
table_string = table_string + (" " * 4) + field + ": " + str(table_dict[field]) + ',\n'
table_string += "}\n\n"
return table_string
def name_yaml_formatter(table_dict):
"""Formats the YAML table string for OpenType name tables"""
table_string = "name: {\n"
namerecord_list = table_dict['names']
for record in namerecord_list:
if record.__dict__['langID'] == 0:
record_name = str(record.__dict__['nameID'])
else:
record_name = str(record.__dict__['nameID']) + "u"
record_field = (" " * 4) + "nameID" + record_name
table_string = table_string + record_field + ": " + str(record.__dict__) + ",\n"
table_string = table_string + "}\n\n"
return table_string
def os2_yaml_formatter(table_dict):
"""Formats the YAML table string for OpenType OS/2 tables"""
table_string = "OS/2: {\n"
for field in table_dict.keys():
if field == "panose":
table_string = table_string + (" "*4) + field + ": {\n"
panose_string = ""
panose_dict = table_dict['panose'].__dict__
for panose_field in panose_dict.keys():
panose_string = panose_string + (" " * 8) + panose_field[1:] + ": " + str(panose_dict[panose_field]) + ",\n"
table_string = table_string + panose_string + (" " * 4) + "}\n"
else:
table_string = table_string + (" "*4) + field + ": " + str(table_dict[field]) + ',\n'
table_string = table_string + "}\n\n"
return table_string
def ttfa_yaml_formatter(table_dict):
"""Formats the YAML table string for the ttfautohint TTFA table"""
data_string = table_dict['data'].strip()
data_list = data_string.split('\n') # split on newlines in the string
table_string = "TTFA: {\n"
for definition_string in data_list:
definition_list = definition_string.split("=")
field = definition_list[0].strip()
if len(definition_list) > 1:
value = definition_list[1].strip()
else:
value = "''"
table_string = table_string + (" " * 4) + field + ": " + value + ",\n"
table_string = table_string + "}\n\n"
return table_string
def read_bin(filepath):
"""read_bin function reads filepath parameter as binary data and returns raw binary to calling code"""
try:
with open(filepath, 'rb') as bin_reader:
data = bin_reader.read()
return data
except Exception as e:
sys.stderr.write("Error: Unable to read file " + filepath + ". " + str(e))
if __name__ == '__main__':
main(sys.argv[1:])

@ -0,0 +1,197 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
# otmod.py
# Copyright 2015 Christopher Simpkins
# MIT license
# ------------------------------------------------------------------------------
import sys
import os.path
import codecs
import unicodedata
from fontTools import ttLib
from yaml import load
try:
from yaml import CLoader as Loader
except ImportError:
from yaml import Loader
# ------------------------------------------------------------------------------
# [ Argument Class ]
# all command line arguments (object inherited from Python list)
# ------------------------------------------------------------------------------
class Argument(list):
"""Argument class is a list for command line arguments. It provides methods for positional argument parsing"""
def __init__(self, argv):
self.argv = argv
list.__init__(self, self.argv)
# return argument at position specified by the 'position' parameter
def get_arg(self, position):
if self.argv and (len(self.argv) > position):
return self.argv[position]
else:
return ""
# return position of user specified argument in the argument list
def get_arg_position(self, test_arg):
if self.argv:
if test_arg in self.argv:
return self.argv.index(test_arg)
else:
return -1 # TODO: change the return code that indicates an error
# return the argument at the next position following a user specified positional argument
# (e.g. for argument to an option in cmd)
def get_arg_next(self, position):
if len(self.argv) > (position + 1):
return self.argv[position + 1]
else:
return ""
def generate_outfile_path(filepath):
filepath_list = os.path.split(filepath)
directory_path = filepath_list[0]
basefile_path = filepath_list[1]
basefile_list = basefile_path.split(".")
new_basefile_name = basefile_list[0] + "-new." + basefile_list[1]
outfile = os.path.join(directory_path, new_basefile_name)
return outfile
def read_utf8(filepath):
"""read_utf8() is a function that reads text in as UTF-8 NFKD normalized text strings from filepath
:param filepath: the filepath to the text input file
"""
try:
f = codecs.open(filepath, encoding='utf_8', mode='r')
except IOError as ioe:
sys.stderr.write("[otmod.py] ERROR: Unable to open '" + filepath + "' for read.\n")
raise ioe
try:
textstring = f.read()
norm_text = unicodedata.normalize('NFKD', textstring) # NKFD normalization of the unicode data before returns
return norm_text
except Exception as e:
sys.stderr.write("[otmod.py] ERROR: Unable to read " + filepath + " with UTF-8 encoding using the read_utf8() method.\n")
raise e
finally:
f.close()
def main(arguments):
args = Argument(arguments)
# Command line syntax + parsing
# LONG OPTIONS: otmod.py --in <font infile path> --opentype <Open Type changes YAML path> --out <font outfile path> --quiet
# SHORT OPTIONS: otmod.py -i <font infile path> -t <Open Type changes YAML path> -o <font outfile path> -q
# Quiet flag (default to False, if set to True does not print changes that occurred to std output)
quiet = False
if "--quiet" in args.argv or "-q" in args.argv:
quiet = True
# font infile path
if "--in" in args.argv:
infile = args.get_arg_next(args.get_arg_position("--in"))
if infile is "":
sys.stderr.write("[otmod.py] ERROR: please define the font input file path as an argument to the --in command line option.\n")
sys.exit(1)
elif "-i" in args.argv:
infile = args.get_arg_next(args.get_arg_position("-i"))
if infile is "":
sys.stderr.write("[otmod.py] ERROR: please define the font input file path as an argument to the -i command line option.\n")
sys.exit(1)
else:
sys.stderr.write("[otmod.py] ERROR: Please include the `--in` option with an input font file defined as an argument.\n")
sys.exit(1)
# OpenType change YAML file path
if "--opentype" in args.argv:
otpath = args.get_arg_next(args.get_arg_position("--opentype"))
if otpath is "":
sys.stderr.write("[otmod.py] ERROR: please define the YAML OpenType changes file path as an argument to the --opentype command line option.\n")
sys.exit(1)
elif "-t" in args.argv:
otpath = args.get_arg_next(args.get_arg_position("-t"))
if otpath is "":
sys.stderr.write("[otmod.py] ERROR: please define the YAML OpenType changes file path as an argument to the -t command line option.\n")
sys.exit(1)
else:
sys.stderr.write("[otmod.py] ERROR: Please include the `--opentype` option and define it with an path argument to the YAML formatted OpenType changes file.\n")
sys.exit(1)
# font outfile path (allows for font name change in outfile)
if "--out" in args.argv:
outfile = args.get_arg_next(args.get_arg_position("--out"))
if outfile is "":
outfile = generate_outfile_path(infile)
elif "-o" in args.argv:
outfile = args.get_arg_next(args.get_arg_position("-o"))
if outfile is "":
outfile = generate_outfile_path(infile)
else:
outfile = generate_outfile_path(infile)
# Test for existing file paths
if not os.path.isfile(infile):
sys.stderr.write("[otmod.py] ERROR: Unable to locate font at the infile path '" + infile + "'.\n")
sys.exit(1)
if not os.path.isfile(otpath):
sys.stderr.write("[otmod.py] ERROR: Unable to locate the OpenType modification settings YAML file at '" + otpath + "'.\n")
sys.exit(1)
# Read YAML OT table changes settings file and convert to Python object
try:
yaml_text = read_utf8(otpath)
# Python dictionary definitions with structure `otmods_obj['OS/2']['sTypoLineGap']`
otmods_obj = load(yaml_text, Loader=Loader)
except Exception as e:
sys.stderr.write("[otmod.py] ERROR: There was an error during the attempt to parse the YAML file. " + str(e) + "\n")
sys.exit(1)
# Read font infile and create a fontTools OT table object
try:
tt = ttLib.TTFont(infile)
except Exception as e:
sys.stderr.write("[otmod.py] ERROR: There was an error during the attempt to parse the OpenType tables in the font file '" + infile + "'. " + str(e) + "\n")
sys.exit(1)
# iterate through OT tables in the Python fonttools OT table object
for ot_table in otmods_obj:
# Confirm that the requested table name for a change is an actual table in the font
if ot_table in tt.keys():
# iterate through the items that require modification in the table
for field in otmods_obj[ot_table]:
# confirm that the field exists in the existing font table
if field in tt[ot_table].__dict__.keys():
# modify the field definition in memory
tt[ot_table].__dict__[field] = otmods_obj[ot_table][field]
# notify user if quiet flag is not set
if not quiet:
print("(" + infile + ")[" + ot_table + "][" + field + "] changed to " + str(tt[ot_table].__dict__[field]))
else:
print("[otmod.py] WARNING: '" + ot_table + "' table field '" + field + "' was not a table found in the font '" + infile + "'. No change was made to this table field.")
else:
print("[otmod.py] WARNING: '" + ot_table + "' was not a table found in the font '" + infile + "'. No change was made to this table.")
# Write updated font to disk
try:
tt.save(outfile)
if not quiet:
print("[otmod.py] '" + infile + "' was updated and the new font write took place on the path '" + outfile + "'.")
except Exception as e:
sys.stderr.write("[otmod.py] ERROR: There was an error during the attempt to write the file '" + outfile + "' to disk. " + str(e) + "\n")
sys.exit(1)
if __name__ == '__main__':
if len(sys.argv) > 1:
main(sys.argv[1:])
else:
sys.stderr.write("[otmod.py] ERROR: no arguments detected in your command.\n")
sys.exit(1)