Exporting a .ufo file with fontParts doesn't create a valid file
-
Hello there,
Frederik helped me a few months ago and using his script, I managed to save shapes as glyphs in a .UFO file but when I open them they have:
-
no width (I probably need to specify this when saving but I didn't know where);
-
you can't type with them (in Glyphs at least). I suspect the unicode names for each glyph are not specified well enough.
Everything happens in saveAsUFO(), where "bez" is my vector shape and "char" is specifying which glyph I want this shape to be saved in.
from fontParts.world import RFont import math # create a font object myFont = RFont(showInterface=False) font = "LucidaGrande" charSet = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","0","1","2","3","4","5","6","7","8","9",".",",","!","?","[","]","-","—",'“','”','‘','’','"',"'"] def calculateDistance(pt1, pt2): x1, y1 = pt1 x2, y2 = pt2 dist = math.sqrt((x2 - x1)**2 + (y2 - y1)**2) return dist def calculateHandles(pt1, pt2): x1, y1 = pt1 x2, y2 = pt2 dx = x2 - x1 dy = y2 - y1 offset = -0.2 # vector (y, -x) is perpendicular to vector (x, y): handleOffsetX = offset * dy handleOffsetY = -offset * dx h1Pos = 0.25 h2Pos = 0.75 h1x = x1 + h1Pos * dx + handleOffsetX h1y = y1 + h1Pos * dy + handleOffsetY h2x = x1 + h2Pos * dx + handleOffsetX h2y = y1 + h2Pos * dy + handleOffsetY return (h1x, h1y), (h2x, h2y) def extractContours(bez): contours = [] for cont in bez.contours: poly = [] for seg in cont: for pt in seg: poly.append(pt) contours.append(poly) return contours def saveAsUFO(path, char): # create a glyph object myGlyph = myFont.newGlyph(char) # get the pen myChar = myGlyph.getPen() path.drawToPen(myChar) def drawContours(contours, char): bez = BezierPath() for contour in contours: prevPoint = contour[0] bez.moveTo(prevPoint) for pt in contour[1:]: h1, h2 = calculateHandles(prevPoint, pt) bez.curveTo(h1, h2, pt) # bez.lineTo(pt) prevPoint = pt bez.closePath() drawPath(bez) saveAsUFO(bez, char) # Convert the letter into Bezier Path for i in range(len(charSet)): newPage(1000, 1000) bez = BezierPath() bez.text(charSet[i], font=font, fontSize=840) xMin, yMin, xMax, yMax = bez.bounds() cx = (xMin + xMax) / 6 cy = (yMin + yMax) / 2 bez.translate(cx, cy) contours = extractContours(bez) drawContours(contours, charSet[i]) # save the ufo # myFont.save("Bogota.ufo")
-
-
hello @michelangelo,
starting with (2): your character set is defined as unicode characters, but to create glyphs in a UFO font you need glyph names (for example AGL names, but there are other schemes like GNFUL).
to get the glyph names you’ll need to convert first from unicode characters to unicode values. in Python3 this is easy:
ord(char)
. then you can use the unicode value to look up the glyph name in your scheme of choice.here’s an example using fontTools, which is embedded in DrawBot like FontParts: (see this gist)
from fontTools.agl import UV2AGL def saveAsUFO(path, char): # get unicode for character uni = ord(char) # get glyph name for unicode glyphName = UV2AGL.get(uni) # create a glyph object myGlyph = myFont.newGlyph(glyphName) # set unicode myGlyph.unicode = uni # get the pen pen = myGlyph.getPen() # draw path into glyph with pen path.drawToPen(pen)
-
regarding (1): you can get glyph widths by setting each character with the source font and measuring the text:
# `font` is a DrawBot command and cannot be used as variable name fontName = "LucidaGrande" font(fontName) fontSize(1000) w, h = textSize(char) myGlyph.width = w
the text font size is set to
1000
because this is the default UPM size when creating a new font, and we need a 1:1 scale in order to get width value.
-
@gferreira Thanks a lot. Everything works now!
Follow-up questions will be:
-
As I have an offsetValue to tweak the bending of the letters, would it be simple to do all of this inside Glyphs? This way I can open Glyphs, tweak it as much as I want and export right away as .OTF.
-
Or how can I export an .OTF directly from DrawBot? I tried with myFont.generate() but something went wrong...
formats = { 'OpenType-CFF (.otf)' : 'otfcff', 'OpenType-TTF (.ttf)' : 'otfttf', 'PostScript (.pfa)' : 'pctype1ascii' } # save the ufo if ExportUFO == True: myFont.save("Bogota.ufo") for format in formats.keys(): print('Generating %s font...' % format) print(myFont.generate(formats[format])) --------------------------------------------- Generating OpenType-CFF (.otf) font... Traceback (most recent call last): File "Bogota5.py", line 126, in <module> File "/Applications/DrawBot.app/Contents/Resources/lib/python3.6/fontParts/base/font.py", line 362, in generate File "/Applications/DrawBot.app/Contents/Resources/lib/python3.6/fontParts/base/font.py", line 397, in _generate File "/Applications/DrawBot.app/Contents/Resources/lib/python3.6/fontParts/base/base.py", line 232, in raiseNotImplementedError NotImplementedError: The RFont subclass does not implement this method.
-
-
@frederik maybe you can help me with that one?
-
font.generate
is RoboFont method and is not available inside DrawBot.However with ufo2fdk (which is embedded into DrawBot) you must be able to generate binaries (you will need to install FDK locally)
or install
fontMake
and build a compiled font from a ufo.good luck!!!
-
@frederik Thank you! I don't exactly know how to use the ufo2fdk, but I will try.
-
Im pushing you to figure it out by yourself first
see the documentation and the source of ufo2fdk
and see documentation of fontMake
-
@frederik Thank you, Frederik. I tried both ufo2fdk I don't how to use, the documentation is a bit confusing for me.
Anyways, with fontParts I ran the script but it gave me
KeyError: 'unitsPerEm'
Is it related to my font or to fontParts script in your opinion?
-
ufo2fdk needs some basic font info settings like
descender
,xHeight
,ascender
,capHeight
,unitsPerEm
# some default example values font.info.descender=-250 font.info.xHeight=500 font.info.ascender=750 font.info.capHeight=750 font.info.unitsPerEm=1000
-
@frederik Thank you, I managed! It works smoothly now—I was just missing the
font.familyName
but after adding this, it works.I was wondering if I can do it inside DrawBot right away? I tried to import
fontmake
orufo2ft
as a module but it doesn't work. Could it be that pip doesn't install it globally and DrawBot cannot find it—if that's the case where should I put it so DrawBot can see it?
-
yes you can use fontMake or ufo2ft, you just have to install it locally for the python3.6 (this is the embedded python in DrawBot)
If you dont have python3.6 installed, you need to get it from https://www.python.org/downloads/.
and install fontMake and/or ufo2ft with pip3.6 for py3.6
good luck!