Last active
January 25, 2022 18:48
-
-
Save arrowtype/98e98405d1575672f86fdce307c11b08 to your computer and use it in GitHub Desktop.
Go through UFOs in directory and orient contour paths in a counter-clockwise direction. See https://github.com/googlefonts/fontmake/issues/846
This file contains hidden or 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
""" | |
⚠️⚠️⚠️ | |
OOPS. Don’t use this. FontParts already has BaseGlyph.correctDirection() | |
⚠️⚠️⚠️ | |
Go through UFOs in directory and orient contour paths in a counter-clockwise direction, | |
as they should be for cubic/postscript/CFF/"OTF" curves. | |
DEPENDENCIES: | |
- fontParts | |
- ufonormalizer | |
USAGE: | |
On the command line, call this script and add a directory of UFOs as an argument. | |
Add "--normalize" to normalize the UFOs after saving. | |
python3 <path_to_script>/orient-glyph-contours-UFOs_in_dir.py <path_to_directory_of_UFOs> --normalize | |
DISCLAIMERS: | |
- May break compatibility on some glyphs, as start points can change. Consider using Prepolator, afterwards. | |
- May result in some broken drawings for certain edge cases (e.g. if a counter path | |
somehow goes further left than the main exterior path). | |
- Doesn't handle glyphs with more than two counters, unless they all have the same | |
path direction (i.e. no counters), but adds these to a list for manual review. | |
- May not work for your project. ¯\_(ツ)_/¯ ALWAYS USE GIT / VERSION CONTROL or another | |
form of backup before running any script like this! | |
LICENSE: | |
- MIT. Use/remix this if you want to. | |
""" | |
import argparse | |
import os | |
from fontParts.world import * | |
from ufonormalizer import normalizeUFO | |
def orientPathsCCW(glyph): | |
""" | |
Orient all paths in glyph counter-clockwise. | |
Only use this on glyphs with no counter/cutout shapes. | |
""" | |
for contour in glyph: | |
if contour.clockwise == True: | |
contour.reverse() | |
def main(): | |
# get arguments from argparse | |
args = parser.parse_args() | |
sourceFolderPath = args.dir | |
# get UFO paths and open each of them | |
for subPath in os.listdir(sourceFolderPath): | |
if subPath.endswith(".ufo"): | |
ufoPath = os.path.join(sourceFolderPath, subPath) | |
f = OpenFont(ufoPath, showInterface=False) | |
print(f"Analyzing: {f.info.styleName}...") | |
# a list to track glyphs with many contours, for manual review | |
manyContours = [] | |
# go through glyphs in the font | |
for g in f: | |
# if all contours have the same path direction, orient the paths CCW | |
if len(set([c.clockwise for c in g.contours])) == 1: | |
orientPathsCCW(g) | |
# else if contours have more than one direction | |
else: | |
# check for exactly two contours | |
if len(g.contours) == 2: | |
# find outer shape, then make it CCW | |
# if first contour is further left than second contour, assume it is exterior and orient first to CCW | |
# bounds is (xMin, yMin, xMax, yMax) | |
if g.contours[0].bounds[0] < g.contours[1].bounds[0]: | |
if g.contours[0].clockwise: | |
g.contours[0].reverse() | |
g.contours[1].reverse() | |
# if first contour is not further left, assume it is the counter, and make it CW | |
else: | |
if g.contours[0].clockwise == False: | |
g.contours[0].reverse() | |
g.contours[1].reverse() | |
# if too many contours, just add to a list for manual review/handling | |
if len(g.contours) == 3: | |
manyContours.append(g.name) | |
# report list for manual review | |
if len(manyContours) >= 1: | |
print("The following glyphs have more than two contours, and multiple path directions:") | |
print(" ".join(manyContours)) | |
print() | |
f.save() | |
if args.normalize: | |
normalizeUFO(ufoPath) | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser(description='Orient path directions in all glyphs for all UFOs in a directory.') | |
parser.add_argument('dir', | |
help='Relative path to a directory of one or more UFO font sources') | |
parser.add_argument("-n", "--normalize", | |
action='store_true', | |
help='Normalizes UFOs with ufoNormalizer.') | |
main() |
This file contains hidden or 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
""" | |
⚠️⚠️⚠️ | |
OOPS. Don’t use this. FontParts already has BaseGlyph.correctDirection() | |
⚠️⚠️⚠️ | |
Go through UFOs in directory and make all contours clockwise in specified glyphs. | |
Steps to usage: | |
1. First, use orient-glyph-contours-UFOs_in_dir.py to fix simpler glyphs and report complex glyphs. | |
2. Open one of the UFOs and manually check which of the complex glyphs have incorrect paths. | |
3. Add glyphs with backwards paths and counters to the "reverse" space-separated string, below. | |
4. Add glyphs with differing paths and no counters to the "coordinate" space-separated string, below. | |
5. Run the script on a directory of UFOs. Add "--normalize" arg to normalize UFOs. | |
DEPENDENCIES: | |
- fontParts | |
- ufonormalizer | |
DISCLAIMERS: | |
- May break compatibility on some glyphs, as start points might change. Consider using Prepolator, afterwards. | |
- May result in some broken drawings for certain edge cases (e.g. if a counter path | |
somehow goes further left than the main exterior path). | |
- May not work for your project. ¯\_(ツ)_/¯ ALWAYS USE GIT / VERSION CONTROL or another | |
form of backup before running any script like this! | |
LICENSE: | |
- MIT. Use/remix this if you want to. | |
""" | |
import argparse | |
import os | |
import collections | |
from fontParts.world import * | |
from ufonormalizer import normalizeUFO | |
# ----------------------------------------- | |
# CONFIGURE GLYPHS BELOW | |
# Yes, these could instead be command line args, but this is easier. Feel free to modify if you want! | |
glyphsToReverse = "eth copyright" | |
glyphsToCoordinate = "divide" | |
# CONFIGURE GLYPHS ABOVE | |
# ----------------------------------------- | |
def orientPathsCCW(glyph): | |
""" | |
Orient all paths in glyph counter-clockwise. | |
Only use this on glyphs with no counter/cutout shapes! | |
""" | |
for contour in glyph: | |
if contour.clockwise == True: | |
contour.reverse() | |
def main(): | |
# get arguments from argparse | |
args = parser.parse_args() | |
sourceFolderPath = args.dir | |
for subPath in os.listdir(sourceFolderPath): | |
if subPath.endswith(".ufo"): | |
ufoPath = os.path.join(sourceFolderPath, subPath) | |
f = OpenFont(ufoPath, showInterface=False) | |
print(f"Fixing: {f.info.styleName}...") | |
try: | |
# go through list of glyphs to reverse | |
for name in glyphsToReverse.split(" "): | |
# make orderedDict of contours, by leftmost bound | |
contoursLeftBounds = {} | |
for i, c in enumerate(f[name].contours): | |
# add contours to dict, by bounds[0]: contourIndex | |
contoursLeftBounds[c.bounds[0]] = i | |
contoursLeftBoundsSorted = collections.OrderedDict(sorted(contoursLeftBounds.items())) | |
# get index of leftmost contour | |
leftmostContourIndex = list(contoursLeftBoundsSorted.items())[0][1] | |
# if the leftmost contour is clockwise, assume all paths are wrong and reverse them | |
if f[name].contours[leftmostContourIndex].clockwise == True: | |
for c in f[name].contours: | |
c.reverse() | |
# go through list to coordinate | |
for name in glyphsToCoordinate.split(" "): | |
orientPathsCCW(f[name]) | |
except KeyError: | |
pass | |
f.save() | |
if args.normalize: | |
normalizeUFO(ufoPath) | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser(description='Orient path directions in all glyphs for all UFOs in a directory.') | |
parser.add_argument('dir', | |
help='Relative path to a directory of one or more UFO font sources') | |
parser.add_argument("-n", "--normalize", | |
action='store_true', | |
help='Normalizes UFOs with ufoNormalizer.') | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment