SOLVED Error "fs.errors.ResourceNotFound" when trying to save script-generated, slanted UFO
-
I am working on a script to take in UFO paths and copy them as generated slanted fonts. Ultimately, I am prototyping sources for the
ital
axis of a variable font, and once I am happy with the overall slant, I will manually fix up the shapes.I have borrowed heavily from the Slanter extension for this, though I’ve added a bit of logic, e.g. to avoid adding extreme points (which break compatibility).
However, even though I got this working pretty well earlier today, over time I started getting the following error. It became worse once I started adding in some handling for copying groups & kerning, but there was seemingly no single moment that it started crashing. Does this seem like an upstream issue, or could it be something to do with my computer’s RAM, or have I made some mistake in how I’m approaching this?
Thanks so much for any insight! I’m happy to share my script as GitHub Gist or something, once I get this working well.
Traceback (most recent call last): File "/Applications/RoboFont.app/Contents/Resources/lib/python3.7/fs/osfs.py", line 646, in open FileNotFoundError: [Errno 2] No such file or directory: b'/var/folders/x0/q5zt3sx15ssdz5mjcm9rgvh40000gn/T/tmp7ncr5598/temp.ufo/glyphs/E_.glif' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/Applications/RoboFont.app/Contents/Resources/lib/python3.7/fontTools/ufoLib/glifLib.py", line 291, in getGLIF File "/Applications/RoboFont.app/Contents/Resources/lib/python3.7/fs/wrapfs.py", line 342, in readbytes File "/Applications/RoboFont.app/Contents/Resources/lib/python3.7/fs/base.py", line 603, in readbytes File "/Applications/RoboFont.app/Contents/Resources/lib/python3.7/fs/osfs.py", line 646, in open File "/Applications/RoboFont.app/Contents/Resources/lib/python3.7/fs/error_tools.py", line 90, in __exit__ File "six.pyc", line 702, in reraise File "/Applications/RoboFont.app/Contents/Resources/lib/python3.7/fs/osfs.py", line 646, in open fs.errors.ResourceNotFound: resource 'E_.glif' not found During handling of the above exception, another exception occurred: Traceback (most recent call last): File "prep-faux-italics.py", line 187, in <module> File "lib/fontObjects/fontPartsWrappers.pyc", line 1516, in save File "/Applications/RoboFont.app/Contents/Resources/lib/python3.7/fontParts/base/font.py", line 208, in save File "lib/fontObjects/fontPartsWrappers.pyc", line 1521, in _save File "lib/fontObjects/doodleFont.pyc", line 249, in save File "/Applications/RoboFont.app/Contents/Resources/lib/python3.7/defcon/objects/layer.py", line 581, in _stampGlyphDataState File "/Applications/RoboFont.app/Contents/Resources/lib/python3.7/fontTools/ufoLib/glifLib.py", line 295, in getGLIF fontTools.ufoLib.errors.GlifLibError: The file 'E_.glif' associated with glyph 'E' in contents.plist does not exist on <osfs '/var/folders/x0/q5zt3sx15ssdz5mjcm9rgvh40000gn/T/tmp7ncr5598/temp.ufo'>/glyphs
Here’s my script so far:
""" A script to take the sources of Name Sans and output slanted versions of these, for the purposes of A) prototyping & B) jumpstarting the italic drawings. Based largely on the Slanter extension: https://github.com/roboDocs/slanterRoboFontExtension/blob/57d7ac8ad9c91b0dce480cccb5a5dc2d4d4c51c4/Slanter.roboFontExt/lib/slanter.py GOALS: Copy all sources as italic sources, then: 1. Start from the slanter extension and slant to 10.24 degrees, but: 2. Slant round glyphs by less – maybe 7 degrees? # TODO 3. don’t add extreme points (or any points) (Then, outside of script) make a designspace that will build these into Italic statics and an ital axis. """ from vanilla.dialogs import * from math import radians from fontTools.misc.transform import Transform from mojo.roboFont import CurrentGlyph, CurrentFont, RGlyph, RPoint import os # ------------------------------ # set preferences below openNewFonts = False saveNewFonts = True addExtremesToNewFonts = False outputFolder = "faux-italics" # glyphs that get double-slanted by slanter code # glyphsToDecompose = "b q u IJ arrowleft arrowright greater".split() setSkew = 10.24 setRotation = 0 rounds = "O o e c".split() roundSkew = 10.24 # TODO: set this to 7? # TODO: if you skew rounds differently, you must move them to match the movement of everything else dialogMessage = "Select UFOs to copy into faux-italics" # ------------------------------ # Function mostly copied from the Slanter extension def getGlyph(glyph, skew, rotation, addComponents=False, skipComponents=False, addExtremes=False): skew = radians(skew) rotation = radians(-rotation) dest = glyph.copy() if not addComponents: for component in dest.components: pointPen = DecomposePointPen(glyph.layer, dest.getPointPen(), component.transformation) component.drawPoints(pointPen) dest.removeComponent(component) for contour in list(dest): if contour.open: dest.removeContour(contour) if skew == 0 and rotation == 0: return dest for contour in dest: for bPoint in contour.bPoints: bcpIn = bPoint.bcpIn bcpOut = bPoint.bcpOut if bcpIn == (0, 0): continue if bcpOut == (0, 0): continue if bcpIn[0] == bcpOut[0] and bcpIn[1] != bcpOut[1]: bPoint.anchorLabels = ["extremePoint"] if rotation and bcpIn[0] != bcpOut[0] and bcpIn[1] == bcpOut[1]: bPoint.anchorLabels = ["extremePoint"] cx, cy = 0, 0 box = glyph.bounds if box: cx = box[0] + (box[2] - box[0]) * .5 cy = box[1] + (box[3] - box[1]) * .5 t = Transform() t = t.skew(skew) t = t.translate(cx, cy).rotate(rotation).translate(-cx, -cy) if not skipComponents: dest.transformBy(tuple(t)) else: for contour in dest.contours: contour.transformBy(tuple(t)) # this seems to work !!! for component in dest.components: # get component center _box = glyph.layer[component.baseGlyph].bounds if not _box: continue _cx = _box[0] + (_box[2] - _box[0]) * .5 _cy = _box[1] + (_box[3] - _box[1]) * .5 # calculate origin in relation to base glyph dx = cx - _cx dy = cy - _cy # create transformation matrix tt = Transform() tt = tt.skew(skew) tt = tt.translate(dx, dy).rotate(rotation).translate(-dx, -dy) # apply transformation matrix to component offset P = RPoint() P.position = component.offset P.transformBy(tuple(tt)) # set component offset position component.offset = P.position # check if "add extremes" is set to True if addExtremes: dest.extremePoints(round=0) for contour in dest: for point in contour.points: if "extremePoint" in point.labels: point.selected = True point.smooth = True else: point.selected = False dest.removeSelection() dest.round() return dest # Function adapted from the Slanter extension def generateFont(fontToCopy): outFont = RFont(showInterface=False) outFont.info.update(fontToCopy.info.asDict()) outFont.features.text = fontToCopy.features.text for glyph in fontToCopy: outFont.newGlyph(glyph.name) outGlyph = outFont[glyph.name] outGlyph.width = glyph.width outGlyph.unicodes = glyph.unicodes if glyph.name not in rounds: resultGlyph = getGlyph(glyph, setSkew, setRotation, addComponents=True, skipComponents=True, addExtremes=addExtremesToNewFonts) else: resultGlyph = getGlyph(glyph, roundSkew, setRotation, addComponents=True, skipComponents=True, addExtremes=addExtremesToNewFonts) outGlyph.appendGlyph(resultGlyph) # copy glyph order outFont.templateGlyphOrder = fontToCopy.templateGlyphOrder # copy groups & kerning outFont.groups.update(fontToCopy.groups.asDict()) outFont.kerning.update(fontToCopy.kerning.asDict()) # quick/lazy update to relative feature link outFont.features.text = "include(../../features/features.fea);" outFont.info.styleName = outFont.info.styleName + " Italic" return outFont # Get input font paths inputFonts = getFile(dialogMessage, allowsMultipleSelection=True, fileTypes=["ufo"]) # Go through input paths & use to generate slanted fonts for fontPath in inputFonts: font = OpenFont(fontPath, showInterface=False) slantedFont = generateFont(font) fontDir, fontFile = os.path.split(fontPath) italicDir = fontDir + "/" + outputFolder if not os.path.exists(italicDir): os.makedirs(italicDir) slantedFontPath = italicDir + "/" + f"{fontFile.replace('.ufo','_Italic.ufo')}" if saveNewFonts: slantedFont.save(slantedFontPath) if openNewFonts: slantedFont.openInterface() else: slantedFont.close() font.close()
-
Okay, I think that fixes it!
I will probably evolve this a bit further, but here is the code now:
https://gist.github.com/arrowtype/a3c4445cd6b9a6174f885eccff38e1c9
(Caveat: Not sure whether it would work for UFOZ)
-
Thanks for these questions!
Does this traceback occur every time?
are you overwriting ufos?Ahh this seems like it might be where the issue is.
Regardless of the UFO (based on a few tests), I don’t get the error when writing to a brand-new italic UFO, but I always get this error if I am generating a faux-italic UFO for the second time, i.e. overwriting an existing one.
That would explain why I didn’t get this right away, but started to after I had already generated UFOs.
So, I think probably the solution will be checking if the UFO already exists, and deleting it before making a new one.
are you using ufozzzz?
Nope!
-
does this traceback occurs every time?
are you overwriting ufos?
are you using ufozzzz?
-
Worth mentioning:
The font seems to save just fine, and the
E
is there, slanted.In previous runs, it was flagging other glyphs, like
G.RECT
, which were also saving.The main problem with this error is that it prevents me from running through all my sources in one go.
For now, I’ve made the lazy fix of just adding a try/except:
try: if saveNewFonts: slantedFont.save(slantedFontPath) except: print("file not found error")
...but, I’m worried that this might introduce an error I’m missing. So, any recommendations about how I might want to proceed would be amazing. Thanks again!