tooling: improve misc/tools/rename.py to support fonts with different wws family name
This commit is contained in:
parent
4de5592460
commit
0e3f6d91ab
1 changed files with 86 additions and 26 deletions
|
|
@ -28,8 +28,21 @@ FAMILY_RELATED_IDS = set([
|
||||||
whitespace_re = re.compile(r'\s+')
|
whitespace_re = re.compile(r'\s+')
|
||||||
|
|
||||||
|
|
||||||
def removeWhitespace(s):
|
def remove_whitespace(s):
|
||||||
return whitespace_re.sub("", s)
|
return whitespace_re.sub('', s)
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_whitespace(s):
|
||||||
|
return whitespace_re.sub(' ', s)
|
||||||
|
|
||||||
|
|
||||||
|
def remove_substring(s, substr):
|
||||||
|
# examples of remove_substring(s, "Display"):
|
||||||
|
# "Inter Display" => "Inter"
|
||||||
|
# "Display Lol" => "Lol"
|
||||||
|
# "Foo Display Lol" => "Foo Lol"
|
||||||
|
# " Foo Bar Lol " => "Foo Bar Lol"
|
||||||
|
return normalize_whitespace(s.strip().replace(substr, '')).strip()
|
||||||
|
|
||||||
|
|
||||||
def set_full_name(font, fullName, fullNamePs):
|
def set_full_name(font, fullName, fullNamePs):
|
||||||
|
|
@ -55,6 +68,35 @@ def getFamilyName(font):
|
||||||
return r.toUnicode()
|
return r.toUnicode()
|
||||||
|
|
||||||
|
|
||||||
|
def getFamilyNames(font):
|
||||||
|
nameTable = font["name"]
|
||||||
|
r = None
|
||||||
|
names = dict() # dict in Py >=3.7 maintains insertion order
|
||||||
|
for plat_id, enc_id, lang_id in (WINDOWS_ENGLISH_IDS, MAC_ROMAN_IDS):
|
||||||
|
for name_id in (PREFERRED_FAMILY, LEGACY_FAMILY):
|
||||||
|
r = nameTable.getName(
|
||||||
|
nameID=name_id, platformID=plat_id, platEncID=enc_id, langID=lang_id)
|
||||||
|
if r:
|
||||||
|
names[r.toUnicode()] = True
|
||||||
|
if len(names) == 0:
|
||||||
|
raise ValueError("family name not found")
|
||||||
|
names = list(names.keys())
|
||||||
|
names.sort()
|
||||||
|
names.reverse() # longest first
|
||||||
|
return names
|
||||||
|
|
||||||
|
|
||||||
|
def getStyleName(font):
|
||||||
|
nameTable = font["name"]
|
||||||
|
for plat_id, enc_id, lang_id in (WINDOWS_ENGLISH_IDS, MAC_ROMAN_IDS):
|
||||||
|
for name_id in (TYPO_SUBFAMILY_NAME, SUBFAMILY_NAME):
|
||||||
|
r = nameTable.getName(
|
||||||
|
nameID=name_id, platformID=plat_id, platEncID=enc_id, langID=lang_id)
|
||||||
|
if r is not None:
|
||||||
|
return r.toUnicode()
|
||||||
|
raise ValueError("style name not found")
|
||||||
|
|
||||||
|
|
||||||
def renameStylesGoogleFonts(font):
|
def renameStylesGoogleFonts(font):
|
||||||
familyName = getFamilyName(font)
|
familyName = getFamilyName(font)
|
||||||
|
|
||||||
|
|
@ -72,15 +114,15 @@ def renameStylesGoogleFonts(font):
|
||||||
s = rec.toUnicode()
|
s = rec.toUnicode()
|
||||||
start = s.find(familyName)
|
start = s.find(familyName)
|
||||||
if start != -1:
|
if start != -1:
|
||||||
s = familyName + " " + removeWhitespace(s[start + len(familyName):])
|
s = familyName + " " + remove_whitespace(s[start + len(familyName):])
|
||||||
else:
|
else:
|
||||||
s = removeWhitespace(s)
|
s = remove_whitespace(s)
|
||||||
if s != "Italic" and s.endswith("Italic"):
|
if s != "Italic" and s.endswith("Italic"):
|
||||||
# fixup e.g. "ExtraBoldItalic" -> "ExtraBold Italic"
|
# fixup e.g. "ExtraBoldItalic" -> "ExtraBold Italic"
|
||||||
s = s[:len(s) - len("Italic")] + " Italic"
|
s = s[:len(s) - len("Italic")] + " Italic"
|
||||||
rec.string = s.strip()
|
rec.string = s.strip()
|
||||||
if rid in (SUBFAMILY_NAME, TYPO_SUBFAMILY_NAME) or rid in vfInstanceSubfamilyNameIds:
|
if rid in (SUBFAMILY_NAME, TYPO_SUBFAMILY_NAME) or rid in vfInstanceSubfamilyNameIds:
|
||||||
s = removeWhitespace(rec.toUnicode())
|
s = remove_whitespace(rec.toUnicode())
|
||||||
if s != "Italic" and s.endswith("Italic"):
|
if s != "Italic" and s.endswith("Italic"):
|
||||||
# fixup e.g. "ExtraBoldItalic" -> "ExtraBold Italic"
|
# fixup e.g. "ExtraBoldItalic" -> "ExtraBold Italic"
|
||||||
s = s[:len(s) - len("Italic")] + " Italic"
|
s = s[:len(s) - len("Italic")] + " Italic"
|
||||||
|
|
@ -90,7 +132,7 @@ def renameStylesGoogleFonts(font):
|
||||||
|
|
||||||
def setStyleName(font, newStyleName):
|
def setStyleName(font, newStyleName):
|
||||||
newFullName = getFamilyName(font).strip() + " " + newStyleName
|
newFullName = getFamilyName(font).strip() + " " + newStyleName
|
||||||
newFullNamePs = removeWhitespace(newFullName)
|
newFullNamePs = remove_whitespace(newFullName)
|
||||||
set_full_name(font, newFullName, newFullNamePs)
|
set_full_name(font, newFullName, newFullNamePs)
|
||||||
|
|
||||||
nameTable = font["name"]
|
nameTable = font["name"]
|
||||||
|
|
@ -101,23 +143,26 @@ def setStyleName(font, newStyleName):
|
||||||
|
|
||||||
|
|
||||||
def setFamilyName(font, nextFamilyName):
|
def setFamilyName(font, nextFamilyName):
|
||||||
prevFamilyName = getFamilyName(font)
|
prevFamilyNames = getFamilyNames(font)
|
||||||
if prevFamilyName == nextFamilyName:
|
# if prevFamilyNames[0] == nextFamilyName:
|
||||||
return
|
# return
|
||||||
# raise Exception("identical family name")
|
# # raise Exception("identical family name")
|
||||||
|
|
||||||
def renameRecord(nameRecord, prevFamilyName, nextFamilyName):
|
def renameRecord(nameRecord, prevFamilyNames, nextFamilyName):
|
||||||
# replaces prevFamilyName with nextFamilyName in nameRecord
|
# replaces prevFamilyNames with nextFamilyName in nameRecord
|
||||||
s = nameRecord.toUnicode()
|
s = nameRecord.toUnicode()
|
||||||
start = s.find(prevFamilyName)
|
for prevFamilyName in prevFamilyNames:
|
||||||
if start != -1:
|
start = s.find(prevFamilyName)
|
||||||
|
if start == -1:
|
||||||
|
continue
|
||||||
end = start + len(prevFamilyName)
|
end = start + len(prevFamilyName)
|
||||||
nextFamilyName = s[:start] + nextFamilyName + s[end:]
|
nextFamilyName = s[:start] + nextFamilyName + s[end:]
|
||||||
nameRecord.string = nextFamilyName
|
nameRecord.string = nextFamilyName
|
||||||
|
break
|
||||||
return s, nextFamilyName
|
return s, nextFamilyName
|
||||||
|
|
||||||
# postcript name can't contain spaces
|
# postcript name can't contain spaces
|
||||||
psPrevFamilyName = prevFamilyName.replace(" ", "")
|
psPrevFamilyNames = [s.replace(" ", "") for s in prevFamilyNames]
|
||||||
psNextFamilyName = nextFamilyName.replace(" ", "")
|
psNextFamilyName = nextFamilyName.replace(" ", "")
|
||||||
for rec in font["name"].names:
|
for rec in font["name"].names:
|
||||||
name_id = rec.nameID
|
name_id = rec.nameID
|
||||||
|
|
@ -125,18 +170,24 @@ def setFamilyName(font, nextFamilyName):
|
||||||
# leave uninteresting records unmodified
|
# leave uninteresting records unmodified
|
||||||
continue
|
continue
|
||||||
if name_id == POSTSCRIPT_NAME:
|
if name_id == POSTSCRIPT_NAME:
|
||||||
old, new = renameRecord(rec, psPrevFamilyName, psNextFamilyName)
|
old, new = renameRecord(rec, psPrevFamilyNames, psNextFamilyName)
|
||||||
elif name_id == TRUETYPE_UNIQUE_ID:
|
elif name_id == TRUETYPE_UNIQUE_ID:
|
||||||
# The Truetype Unique ID rec may contain either the PostScript Name or the Full Name
|
# The Truetype Unique ID rec may contain either the PostScript Name
|
||||||
if psPrevFamilyName in rec.toUnicode():
|
# or the Full Name
|
||||||
|
prev_psname = None
|
||||||
|
for s in psPrevFamilyNames:
|
||||||
|
if s in rec.toUnicode():
|
||||||
|
prev_psname = s
|
||||||
|
break
|
||||||
|
if prev_psname is not None:
|
||||||
# Note: This is flawed -- a font called "Foo" renamed to "Bar Lol";
|
# Note: This is flawed -- a font called "Foo" renamed to "Bar Lol";
|
||||||
# if this record is not a PS record, it will incorrectly be rename "BarLol".
|
# if this record is not a PS record, it will incorrectly be rename "BarLol".
|
||||||
# However, in practice this is not abig deal since it's just an ID.
|
# However, in practice this is not a big deal since it's just an ID.
|
||||||
old, new = renameRecord(rec, psPrevFamilyName, psNextFamilyName)
|
old, new = renameRecord(rec, [prev_psname], psNextFamilyName)
|
||||||
else:
|
else:
|
||||||
old, new = renameRecord(rec, prevFamilyName, nextFamilyName)
|
old, new = renameRecord(rec, prevFamilyNames, nextFamilyName)
|
||||||
else:
|
else:
|
||||||
old, new = renameRecord(rec, prevFamilyName, nextFamilyName)
|
old, new = renameRecord(rec, prevFamilyNames, nextFamilyName)
|
||||||
# print(" %r: '%s' -> '%s'" % (rec, old, new))
|
# print(" %r: '%s' -> '%s'" % (rec, old, new))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -153,6 +204,8 @@ def main():
|
||||||
help='Rename style to <name>')
|
help='Rename style to <name>')
|
||||||
a('--google-style', action='store_true',
|
a('--google-style', action='store_true',
|
||||||
help='Rename style names to Google Fonts standards')
|
help='Rename style names to Google Fonts standards')
|
||||||
|
a('--scrub-display', action='store_true',
|
||||||
|
help='Remove "Display" from various family & style records')
|
||||||
a('input', metavar='<file>',
|
a('input', metavar='<file>',
|
||||||
help='Input font file')
|
help='Input font file')
|
||||||
args = argparser.parse_args()
|
args = argparser.parse_args()
|
||||||
|
|
@ -161,19 +214,26 @@ def main():
|
||||||
outfile = args.output or infile
|
outfile = args.output or infile
|
||||||
|
|
||||||
font = TTFont(infile, recalcBBoxes=False, recalcTimestamp=False)
|
font = TTFont(infile, recalcBBoxes=False, recalcTimestamp=False)
|
||||||
|
|
||||||
|
if args.scrub_display:
|
||||||
|
if not args.family: # for setFamilyName()
|
||||||
|
args.family = remove_substring(getFamilyName(font), "Display")
|
||||||
|
if not args.style:
|
||||||
|
args.style = remove_substring(getStyleName(font), "Display")
|
||||||
|
|
||||||
editCount = 0
|
editCount = 0
|
||||||
try:
|
try:
|
||||||
if args.family:
|
if args.family:
|
||||||
editCount += 1
|
|
||||||
setFamilyName(font, args.family)
|
setFamilyName(font, args.family)
|
||||||
|
editCount += 1
|
||||||
|
|
||||||
if args.style:
|
if args.style:
|
||||||
editCount += 1
|
|
||||||
setStyleName(font, args.style)
|
setStyleName(font, args.style)
|
||||||
|
editCount += 1
|
||||||
|
|
||||||
if args.google_style:
|
if args.google_style:
|
||||||
editCount += 1
|
|
||||||
renameStylesGoogleFonts(font)
|
renameStylesGoogleFonts(font)
|
||||||
|
editCount += 1
|
||||||
|
|
||||||
if editCount == 0:
|
if editCount == 0:
|
||||||
print("no rename options provided", file=sys.stderr)
|
print("no rename options provided", file=sys.stderr)
|
||||||
|
|
|
||||||
Reference in a new issue