Last active
August 26, 2021 15:37
-
-
Save arrowtype/315a1016f6fd4fad2d9c883fbdbf1c78 to your computer and use it in GitHub Desktop.
RoboFont script to find unexpected kerning exceptions in a UFO, then remove those kerns and replace them with kerns using groups.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Find kerning exceptions in a UFO, then remove those kerns and replace them with kerns using groups. | |
Why? | |
Sometimes, if you add kerning to a font early, you can wind up with unexpected kerning exceptions | |
when you add new characters to the font. E.g. ('G', 'asterisk') makes sense until you add 'Gbreve'. | |
USAGE: | |
Configure side1_ignore & side2_ignore lists below, then run this script in RoboFont. | |
- It won’t autosave your UFOs, because you should probably review the results before saving. | |
- Better yet, you should use Git to version your files, so you can see exactly what changes | |
before sticking with these batch edits. | |
DISCLAIMER: | |
This code is messy and written to solve an immediate problem for a few UFOs. It could be further improved. | |
""" | |
from vanilla.dialogs import * | |
from mojo.UI import OutputWindow | |
# --------------------------------------------- | |
# glyph names to ignore on side 1 and side 2 of kerns (e.g. because T.ultra has intentional exceptions) | |
side1_ignore = "T T.ultra tcaron".split(" ") | |
side2_ignore = "T T.ultra Oslash".split(" ") | |
# --------------------------------------------- | |
# prompt user to open UFOs in RoboFont | |
files = getFile("Select files to edit", | |
allowsMultipleSelection=True, fileTypes=["ufo"]) | |
OutputWindow().clear() | |
output = "" | |
for filepath in files: | |
font = OpenFont(filepath) | |
output += "\n" | |
output += "✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨\n" | |
output += "✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨\n" | |
output += "✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨\n" | |
output += "\n" | |
output += f"{font.info.familyName} {font.info.styleName}\n" | |
double_exceptions = [] | |
side1_exceptions = [] | |
side2_exceptions = [] | |
for kern in font.kerning: | |
if "kern1" not in kern[0] and "kern2" not in kern[1]: | |
if "kern1" in " ".join(font.groups.findGlyph(kern[0])) and "kern2" in " ".join(font.groups.findGlyph(kern[1])): | |
double_exceptions.append(kern) | |
# move on to next kern to avoid putting into side1 / side2 lists | |
continue | |
# if side 1 is not a group | |
if "kern1" not in kern[0]: | |
# check if it is in a side 1 group | |
if "kern1" in " ".join(font.groups.findGlyph(kern[0])): | |
side1_exceptions.append(kern) | |
# if side 2 is not a group | |
if "kern2" not in kern[1]: | |
# check if it is in a side 2 group | |
if "kern2" in " ".join(font.groups.findGlyph(kern[1])): | |
side2_exceptions.append(kern) | |
## uncomment to see just lists of kerns with exceptions | |
# output += "\n\nℹ️ SIDE 1+2 EXCEPTIONS\n" | |
# output += "\n".join([" ".join(list(kern)) for kern in double_exceptions]) + "\n" | |
# output += "\n\nℹ️ SIDE 1 EXCEPTIONS\n" | |
# output += "\n".join([" ".join(list(kern)) for kern in side1_exceptions]) + "\n" | |
# output += "\n\nℹ️ SIDE 2 EXCEPTIONS\n" | |
# output += "\n".join([" ".join(list(kern)) for kern in side2_exceptions]) + "\n" | |
output += "\n" | |
output += "\n" | |
output += "----------------------------------------------------------------------------\n" | |
output += "SIDE 1 + 2 EXCEPTIONS REMOVED\n" | |
output += "\n" | |
for pair in double_exceptions: | |
# if side not in "ignore" list | |
if pair[0] not in side1_ignore and pair[1] not in side2_ignore: | |
# get kern value and remove pair | |
value = font.kerning.pop(pair) | |
# make kern with the group instead | |
newPair = tuple([font.groups.findGlyph(pair[0])[0], font.groups.findGlyph(pair[1])[-1]]) | |
font.kerning[newPair] = value | |
oldPair, newPair = " | ".join(list(pair)), " | ".join(list(newPair)) | |
output += f"{oldPair.rjust(40)} → {newPair.ljust(50,' ')}: {value}\n\n" | |
output += "\n" | |
output += "\n" | |
output += "----------------------------------------------------------------------------\n" | |
output += "SIDE 1 EXCEPTIONS REMOVED\n" | |
output += "\n" | |
for pair in side1_exceptions: | |
# if side not in "ignore" list | |
if pair[0] not in side1_ignore and pair[1] not in side2_ignore: | |
# get kern value and remove pair | |
value = font.kerning.pop(pair) | |
# make kern with the group instead | |
newPair = tuple([font.groups.findGlyph(pair[0])[0], pair[1]]) | |
font.kerning[newPair] = value | |
oldPair, newPair = " | ".join(list(pair)), " | ".join(list(newPair)) | |
output += f"{oldPair.rjust(40)} → {newPair.ljust(50,' ')}: {value}\n\n" | |
output += "\n" | |
output += "\n" | |
output += "----------------------------------------------------------------------------\n" | |
output += "SIDE 2 EXCEPTIONS REMOVED\n" | |
output += "\n" | |
for pair in side2_exceptions: | |
# if side not in "ignore" list | |
if pair[0] not in side1_ignore and pair[1] not in side2_ignore: | |
# get kern value and remove pair | |
value = font.kerning.pop(pair) | |
# make kern with the group instead | |
newPair = tuple([pair[0], font.groups.findGlyph(pair[1])[-1]]) | |
font.kerning[newPair] = value | |
oldPair, newPair = " | ".join(list(pair)), " | ".join(list(newPair)) | |
output += f"{oldPair.rjust(40)} → {newPair.ljust(50)}: {value}\n\n" | |
## uncomment to automatically save and close, but only do this if you are confident in the results | |
# font.save() | |
# font.close() | |
print(output) | |
OutputWindow().show() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This will output text to show what has changed, like: