1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
#!/usr/bin/env python3
"""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.
"""
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()
|