| #!/usr/bin/env python |
| """Script to add a suffix to all family names in the input font's `name` table, |
| and to optionally rename the output files with the given suffix. |
| |
| The current family name substring is searched in the nameIDs 1, 3, 4, 6, 16, |
| and 21, and if found the suffix is inserted after it; or else the suffix is |
| appended at the end. |
| """ |
| from __future__ import print_function, absolute_import, unicode_literals |
| import os |
| import argparse |
| import logging |
| from fontTools.ttLib import TTFont |
| from fontTools.misc.cliTools import makeOutputFileName |
| |
| |
| logger = logging.getLogger() |
| |
| WINDOWS_ENGLISH_IDS = 3, 1, 0x409 |
| MAC_ROMAN_IDS = 1, 0, 0 |
| |
| FAMILY_RELATED_IDS = dict( |
| LEGACY_FAMILY=1, |
| TRUETYPE_UNIQUE_ID=3, |
| FULL_NAME=4, |
| POSTSCRIPT_NAME=6, |
| PREFERRED_FAMILY=16, |
| WWS_FAMILY=21, |
| ) |
| |
| |
| def get_current_family_name(table): |
| family_name_rec = None |
| for plat_id, enc_id, lang_id in (WINDOWS_ENGLISH_IDS, MAC_ROMAN_IDS): |
| for name_id in ( |
| FAMILY_RELATED_IDS["PREFERRED_FAMILY"], |
| FAMILY_RELATED_IDS["LEGACY_FAMILY"], |
| ): |
| family_name_rec = table.getName( |
| nameID=name_id, |
| platformID=plat_id, |
| platEncID=enc_id, |
| langID=lang_id, |
| ) |
| if family_name_rec is not None: |
| break |
| if family_name_rec is not None: |
| break |
| if not family_name_rec: |
| raise ValueError("family name not found; can't add suffix") |
| return family_name_rec.toUnicode() |
| |
| |
| def insert_suffix(string, family_name, suffix): |
| # check whether family_name is a substring |
| start = string.find(family_name) |
| if start != -1: |
| # insert suffix after the family_name substring |
| end = start + len(family_name) |
| new_string = string[:end] + suffix + string[end:] |
| else: |
| # it's not, we just append the suffix at the end |
| new_string = string + suffix |
| return new_string |
| |
| |
| def rename_record(name_record, family_name, suffix): |
| string = name_record.toUnicode() |
| new_string = insert_suffix(string, family_name, suffix) |
| name_record.string = new_string |
| return string, new_string |
| |
| |
| def rename_file(filename, family_name, suffix): |
| filename, ext = os.path.splitext(filename) |
| ps_name = family_name.replace(" ", "") |
| if ps_name in filename: |
| ps_suffix = suffix.replace(" ", "") |
| return insert_suffix(filename, ps_name, ps_suffix) + ext |
| else: |
| return insert_suffix(filename, family_name, suffix) + ext |
| |
| |
| def add_family_suffix(font, suffix): |
| table = font["name"] |
| |
| family_name = get_current_family_name(table) |
| logger.info(" Current family name: '%s'", family_name) |
| |
| # postcript name can't contain spaces |
| ps_family_name = family_name.replace(" ", "") |
| ps_suffix = suffix.replace(" ", "") |
| for rec in table.names: |
| name_id = rec.nameID |
| if name_id not in FAMILY_RELATED_IDS.values(): |
| continue |
| if name_id == FAMILY_RELATED_IDS["POSTSCRIPT_NAME"]: |
| old, new = rename_record(rec, ps_family_name, ps_suffix) |
| elif name_id == FAMILY_RELATED_IDS["TRUETYPE_UNIQUE_ID"]: |
| # The Truetype Unique ID rec may contain either the PostScript |
| # Name or the Full Name string, so we try both |
| if ps_family_name in rec.toUnicode(): |
| old, new = rename_record(rec, ps_family_name, ps_suffix) |
| else: |
| old, new = rename_record(rec, family_name, suffix) |
| else: |
| old, new = rename_record(rec, family_name, suffix) |
| logger.info(" %r: '%s' -> '%s'", rec, old, new) |
| |
| return family_name |
| |
| |
| def main(args=None): |
| parser = argparse.ArgumentParser( |
| description=__doc__, |
| formatter_class=argparse.RawDescriptionHelpFormatter, |
| ) |
| parser.add_argument("-s", "--suffix", required=True) |
| parser.add_argument("input_fonts", metavar="FONTFILE", nargs="+") |
| output_group = parser.add_mutually_exclusive_group() |
| output_group.add_argument("-i", "--inplace", action="store_true") |
| output_group.add_argument("-d", "--output-dir") |
| output_group.add_argument("-o", "--output-file") |
| parser.add_argument("-R", "--rename-files", action="store_true") |
| parser.add_argument("-v", "--verbose", action="count", default=0) |
| options = parser.parse_args(args) |
| |
| if not options.verbose: |
| level = "WARNING" |
| elif options.verbose == 1: |
| level = "INFO" |
| else: |
| level = "DEBUG" |
| logging.basicConfig(level=level, format="%(message)s") |
| |
| if options.output_file and len(options.input_fonts) > 1: |
| parser.error( |
| "argument -o/--output-file can't be used with multiple inputs" |
| ) |
| if options.rename_files and (options.inplace or options.output_file): |
| parser.error("argument -R not allowed with arguments -i or -o") |
| |
| for input_name in options.input_fonts: |
| logger.info("Renaming font: '%s'", input_name) |
| |
| font = TTFont(input_name) |
| family_name = add_family_suffix(font, options.suffix) |
| |
| if options.inplace: |
| output_name = input_name |
| elif options.output_file: |
| output_name = options.output_file |
| else: |
| if options.rename_files: |
| input_name = rename_file( |
| input_name, family_name, options.suffix |
| ) |
| output_name = makeOutputFileName(input_name, options.output_dir) |
| |
| font.save(output_name) |
| logger.info("Saved font: '%s'", output_name) |
| |
| font.close() |
| del font |
| |
| logger.info("Done!") |
| |
| |
| if __name__ == "__main__": |
| main() |