1*e1fe3e4aSElliott Hughes#!/usr/bin/env python3 2*e1fe3e4aSElliott Hughes"""Script to add a suffix to all family names in the input font's `name` table, 3*e1fe3e4aSElliott Hughesand to optionally rename the output files with the given suffix. 4*e1fe3e4aSElliott Hughes 5*e1fe3e4aSElliott HughesThe current family name substring is searched in the nameIDs 1, 3, 4, 6, 16, 6*e1fe3e4aSElliott Hughesand 21, and if found the suffix is inserted after it; or else the suffix is 7*e1fe3e4aSElliott Hughesappended at the end. 8*e1fe3e4aSElliott Hughes""" 9*e1fe3e4aSElliott Hughesimport os 10*e1fe3e4aSElliott Hughesimport argparse 11*e1fe3e4aSElliott Hughesimport logging 12*e1fe3e4aSElliott Hughesfrom fontTools.ttLib import TTFont 13*e1fe3e4aSElliott Hughesfrom fontTools.misc.cliTools import makeOutputFileName 14*e1fe3e4aSElliott Hughes 15*e1fe3e4aSElliott Hughes 16*e1fe3e4aSElliott Hugheslogger = logging.getLogger() 17*e1fe3e4aSElliott Hughes 18*e1fe3e4aSElliott HughesWINDOWS_ENGLISH_IDS = 3, 1, 0x409 19*e1fe3e4aSElliott HughesMAC_ROMAN_IDS = 1, 0, 0 20*e1fe3e4aSElliott Hughes 21*e1fe3e4aSElliott HughesFAMILY_RELATED_IDS = dict( 22*e1fe3e4aSElliott Hughes LEGACY_FAMILY=1, 23*e1fe3e4aSElliott Hughes TRUETYPE_UNIQUE_ID=3, 24*e1fe3e4aSElliott Hughes FULL_NAME=4, 25*e1fe3e4aSElliott Hughes POSTSCRIPT_NAME=6, 26*e1fe3e4aSElliott Hughes PREFERRED_FAMILY=16, 27*e1fe3e4aSElliott Hughes WWS_FAMILY=21, 28*e1fe3e4aSElliott Hughes) 29*e1fe3e4aSElliott Hughes 30*e1fe3e4aSElliott Hughes 31*e1fe3e4aSElliott Hughesdef get_current_family_name(table): 32*e1fe3e4aSElliott Hughes family_name_rec = None 33*e1fe3e4aSElliott Hughes for plat_id, enc_id, lang_id in (WINDOWS_ENGLISH_IDS, MAC_ROMAN_IDS): 34*e1fe3e4aSElliott Hughes for name_id in ( 35*e1fe3e4aSElliott Hughes FAMILY_RELATED_IDS["PREFERRED_FAMILY"], 36*e1fe3e4aSElliott Hughes FAMILY_RELATED_IDS["LEGACY_FAMILY"], 37*e1fe3e4aSElliott Hughes ): 38*e1fe3e4aSElliott Hughes family_name_rec = table.getName( 39*e1fe3e4aSElliott Hughes nameID=name_id, 40*e1fe3e4aSElliott Hughes platformID=plat_id, 41*e1fe3e4aSElliott Hughes platEncID=enc_id, 42*e1fe3e4aSElliott Hughes langID=lang_id, 43*e1fe3e4aSElliott Hughes ) 44*e1fe3e4aSElliott Hughes if family_name_rec is not None: 45*e1fe3e4aSElliott Hughes break 46*e1fe3e4aSElliott Hughes if family_name_rec is not None: 47*e1fe3e4aSElliott Hughes break 48*e1fe3e4aSElliott Hughes if not family_name_rec: 49*e1fe3e4aSElliott Hughes raise ValueError("family name not found; can't add suffix") 50*e1fe3e4aSElliott Hughes return family_name_rec.toUnicode() 51*e1fe3e4aSElliott Hughes 52*e1fe3e4aSElliott Hughes 53*e1fe3e4aSElliott Hughesdef insert_suffix(string, family_name, suffix): 54*e1fe3e4aSElliott Hughes # check whether family_name is a substring 55*e1fe3e4aSElliott Hughes start = string.find(family_name) 56*e1fe3e4aSElliott Hughes if start != -1: 57*e1fe3e4aSElliott Hughes # insert suffix after the family_name substring 58*e1fe3e4aSElliott Hughes end = start + len(family_name) 59*e1fe3e4aSElliott Hughes new_string = string[:end] + suffix + string[end:] 60*e1fe3e4aSElliott Hughes else: 61*e1fe3e4aSElliott Hughes # it's not, we just append the suffix at the end 62*e1fe3e4aSElliott Hughes new_string = string + suffix 63*e1fe3e4aSElliott Hughes return new_string 64*e1fe3e4aSElliott Hughes 65*e1fe3e4aSElliott Hughes 66*e1fe3e4aSElliott Hughesdef rename_record(name_record, family_name, suffix): 67*e1fe3e4aSElliott Hughes string = name_record.toUnicode() 68*e1fe3e4aSElliott Hughes new_string = insert_suffix(string, family_name, suffix) 69*e1fe3e4aSElliott Hughes name_record.string = new_string 70*e1fe3e4aSElliott Hughes return string, new_string 71*e1fe3e4aSElliott Hughes 72*e1fe3e4aSElliott Hughes 73*e1fe3e4aSElliott Hughesdef rename_file(filename, family_name, suffix): 74*e1fe3e4aSElliott Hughes filename, ext = os.path.splitext(filename) 75*e1fe3e4aSElliott Hughes ps_name = family_name.replace(" ", "") 76*e1fe3e4aSElliott Hughes if ps_name in filename: 77*e1fe3e4aSElliott Hughes ps_suffix = suffix.replace(" ", "") 78*e1fe3e4aSElliott Hughes return insert_suffix(filename, ps_name, ps_suffix) + ext 79*e1fe3e4aSElliott Hughes else: 80*e1fe3e4aSElliott Hughes return insert_suffix(filename, family_name, suffix) + ext 81*e1fe3e4aSElliott Hughes 82*e1fe3e4aSElliott Hughes 83*e1fe3e4aSElliott Hughesdef add_family_suffix(font, suffix): 84*e1fe3e4aSElliott Hughes table = font["name"] 85*e1fe3e4aSElliott Hughes 86*e1fe3e4aSElliott Hughes family_name = get_current_family_name(table) 87*e1fe3e4aSElliott Hughes logger.info(" Current family name: '%s'", family_name) 88*e1fe3e4aSElliott Hughes 89*e1fe3e4aSElliott Hughes # postcript name can't contain spaces 90*e1fe3e4aSElliott Hughes ps_family_name = family_name.replace(" ", "") 91*e1fe3e4aSElliott Hughes ps_suffix = suffix.replace(" ", "") 92*e1fe3e4aSElliott Hughes for rec in table.names: 93*e1fe3e4aSElliott Hughes name_id = rec.nameID 94*e1fe3e4aSElliott Hughes if name_id not in FAMILY_RELATED_IDS.values(): 95*e1fe3e4aSElliott Hughes continue 96*e1fe3e4aSElliott Hughes if name_id == FAMILY_RELATED_IDS["POSTSCRIPT_NAME"]: 97*e1fe3e4aSElliott Hughes old, new = rename_record(rec, ps_family_name, ps_suffix) 98*e1fe3e4aSElliott Hughes elif name_id == FAMILY_RELATED_IDS["TRUETYPE_UNIQUE_ID"]: 99*e1fe3e4aSElliott Hughes # The Truetype Unique ID rec may contain either the PostScript 100*e1fe3e4aSElliott Hughes # Name or the Full Name string, so we try both 101*e1fe3e4aSElliott Hughes if ps_family_name in rec.toUnicode(): 102*e1fe3e4aSElliott Hughes old, new = rename_record(rec, ps_family_name, ps_suffix) 103*e1fe3e4aSElliott Hughes else: 104*e1fe3e4aSElliott Hughes old, new = rename_record(rec, family_name, suffix) 105*e1fe3e4aSElliott Hughes else: 106*e1fe3e4aSElliott Hughes old, new = rename_record(rec, family_name, suffix) 107*e1fe3e4aSElliott Hughes logger.info(" %r: '%s' -> '%s'", rec, old, new) 108*e1fe3e4aSElliott Hughes 109*e1fe3e4aSElliott Hughes return family_name 110*e1fe3e4aSElliott Hughes 111*e1fe3e4aSElliott Hughes 112*e1fe3e4aSElliott Hughesdef main(args=None): 113*e1fe3e4aSElliott Hughes parser = argparse.ArgumentParser( 114*e1fe3e4aSElliott Hughes description=__doc__, 115*e1fe3e4aSElliott Hughes formatter_class=argparse.RawDescriptionHelpFormatter, 116*e1fe3e4aSElliott Hughes ) 117*e1fe3e4aSElliott Hughes parser.add_argument("-s", "--suffix", required=True) 118*e1fe3e4aSElliott Hughes parser.add_argument("input_fonts", metavar="FONTFILE", nargs="+") 119*e1fe3e4aSElliott Hughes output_group = parser.add_mutually_exclusive_group() 120*e1fe3e4aSElliott Hughes output_group.add_argument("-i", "--inplace", action="store_true") 121*e1fe3e4aSElliott Hughes output_group.add_argument("-d", "--output-dir") 122*e1fe3e4aSElliott Hughes output_group.add_argument("-o", "--output-file") 123*e1fe3e4aSElliott Hughes parser.add_argument("-R", "--rename-files", action="store_true") 124*e1fe3e4aSElliott Hughes parser.add_argument("-v", "--verbose", action="count", default=0) 125*e1fe3e4aSElliott Hughes options = parser.parse_args(args) 126*e1fe3e4aSElliott Hughes 127*e1fe3e4aSElliott Hughes if not options.verbose: 128*e1fe3e4aSElliott Hughes level = "WARNING" 129*e1fe3e4aSElliott Hughes elif options.verbose == 1: 130*e1fe3e4aSElliott Hughes level = "INFO" 131*e1fe3e4aSElliott Hughes else: 132*e1fe3e4aSElliott Hughes level = "DEBUG" 133*e1fe3e4aSElliott Hughes logging.basicConfig(level=level, format="%(message)s") 134*e1fe3e4aSElliott Hughes 135*e1fe3e4aSElliott Hughes if options.output_file and len(options.input_fonts) > 1: 136*e1fe3e4aSElliott Hughes parser.error("argument -o/--output-file can't be used with multiple inputs") 137*e1fe3e4aSElliott Hughes if options.rename_files and (options.inplace or options.output_file): 138*e1fe3e4aSElliott Hughes parser.error("argument -R not allowed with arguments -i or -o") 139*e1fe3e4aSElliott Hughes 140*e1fe3e4aSElliott Hughes for input_name in options.input_fonts: 141*e1fe3e4aSElliott Hughes logger.info("Renaming font: '%s'", input_name) 142*e1fe3e4aSElliott Hughes 143*e1fe3e4aSElliott Hughes font = TTFont(input_name) 144*e1fe3e4aSElliott Hughes family_name = add_family_suffix(font, options.suffix) 145*e1fe3e4aSElliott Hughes 146*e1fe3e4aSElliott Hughes if options.inplace: 147*e1fe3e4aSElliott Hughes output_name = input_name 148*e1fe3e4aSElliott Hughes elif options.output_file: 149*e1fe3e4aSElliott Hughes output_name = options.output_file 150*e1fe3e4aSElliott Hughes else: 151*e1fe3e4aSElliott Hughes if options.rename_files: 152*e1fe3e4aSElliott Hughes input_name = rename_file(input_name, family_name, options.suffix) 153*e1fe3e4aSElliott Hughes output_name = makeOutputFileName(input_name, options.output_dir) 154*e1fe3e4aSElliott Hughes 155*e1fe3e4aSElliott Hughes font.save(output_name) 156*e1fe3e4aSElliott Hughes logger.info("Saved font: '%s'", output_name) 157*e1fe3e4aSElliott Hughes 158*e1fe3e4aSElliott Hughes font.close() 159*e1fe3e4aSElliott Hughes del font 160*e1fe3e4aSElliott Hughes 161*e1fe3e4aSElliott Hughes logger.info("Done!") 162*e1fe3e4aSElliott Hughes 163*e1fe3e4aSElliott Hughes 164*e1fe3e4aSElliott Hughesif __name__ == "__main__": 165*e1fe3e4aSElliott Hughes main() 166