@gferreira Thank you! That is much clearer now. I found that in my example I have a lot of transformations and somewhere it gets weird again, but on its own it works!

@gferreira Thank you! That is much clearer now. I found that in my example I have a lot of transformations and somewhere it gets weird again, but on its own it works!

@gferreira That seems important! Thank you!
@frederik No, I do not want to make my typesetter, definitely. Thanks for the heads up. What Gustavo explained is what I am talking about...
This is exactly what I need. However, my font doesn't react normally to it... The same code with the same arguments, only different fonts:
for char in TEXT:
src.text(char, (0, 0), font='someFont', fontSize=FONTSIZE)
drawPath(src)
advance = textSize(char)[0]

But if I typeset it in another way, it works:
font('someFont', fontSize=FONTSIZE)
text(TEXT, (0, 0))

That's why I am confused... 
Thank you, @frederik. That's exactly what I was wondering! I am trying to access the side bearings but what is the method for that (leftMargin, rightMargin)?
If I use the drawBot text():
font('Helvetica', 200)
txt = 'ABCW'
text(txt, (0,0))
The spacing is perfectly rendered from the font. How does drawBot know when to use the kerning pairs for [AA], [AW]?
If I manipulate the characters one by one and then render them, how can I use the existing font bearings to maintain proper spacing?
I end up with this kind of nonsense:
def drawText(txt, x, y, fSize):
for char in txt, range(len(txt))):
aWidth = fSize * 0.6 * i
if char =="A":
aWidth += i * 40 # + (glyph.leftMargin?)
if char =="B":
aWidth += i * 36
...
renderChar(char, x + aWidth, y, fSize)
Hello guys,
I was wondering how Rfont and RGlyph work? I can set a myFont.newGlyph(char) but I am missing something.
from fontParts.world import RFont, RGlyph
# create a font object
myFont = RFont(showInterface=False)
fontName = "LucidaGrande"
char = 'A'
glyph = myFont.newGlyph(char)
print(glyph)
I get, as expected:
<RGlyph 'A' ('public.default') at 4470687248>
but then the glyph is empty if I:
print(glyph.isEmpty())
Usually you would do
glyph = CurrentGlyph()
but I am working in drawBot. Can I just set this glyph to a typeface I specify and still get access to all the methods of a RGlyph() object?
Hello @frederik,
thank you for sheding some light on the issue. Maybe I will try the DrawBot package.
Sorry for the confusion about the example, I meant this which is from the official documentation of Vanilla. I also understand now that a print() command prints in a specified window (I guess) and not the console.
Thanks! It is exciting to see all sliders and buttons

Hello there,
I have a question about some UI within drawbot. I used the standard Variable[] but it is not really working for my purpose.
In short: I have a string ('SOME TEXT') that renders each letter, but there are multiple methods that can be chosen for each of them.

Here, I am just hardcoding the dictionary 'options' for each character...
If I use the default UI from Drawbot I can render the text

but I also want to show under it, based on the string, a multiple choice for each letter:

I saw that the vanilla library is meant for this, but what approach could I take to enter the string and based on it to render a dictionary which can be modified by the user? And btw these examples from RoboFont don't print anything, not sure if they work on my machine. No errors but I am not sure what I should do.
Thanks a lot!
Great, that makes sense. Is it also possible in DrawBot directly? I would like to animate it with other letters later.
Hi @frederik, thanks, I got RoboFont now and labelled them, but how can I references them.
What method could find the labelled points in an array of points?
If I use:
for points in srcShape.points:
print(points)
I get all points (x, y), but they have no 'label' attribute.
Hello guys,
I want to try some point manipulation in DrawBot. I have used bezier paths before but it seems like it is very unclear how to start it all.
In short I have a drawn font and I want to:
Here I attach a gif of what I have in mind as the end result:

β Can I label specific points from Glyphs to access them easily later?
β Perhaps there is already something similar asked in the forum but I didn't find it. Can you reference it here?
thank you!
Hello all,
I am working on a variable font that wasn't supposed to be variable but hey, why not? We drew a font and this is then imported in DrawBot to be "messed up". @justvanrossum helped me set it up already and I have a few examples.
Original:

Slightly modified:

Heavily modified:

Modification is randomly applied to each character:

Now the "variation" only happens in this intensity of messing it up. I am interested how I can let the user create this intensity in InDesign or other software, instead of it being hard-coded.
Here is my code:
from fontParts.world import RFont
from fontTools.agl import UV2AGL
import math
import random
from defcon import Font
# create a font object
myFont = RFont(showInterface=False)
# some default example values
myFont.info.descender=-250
myFont.info.xHeight=500
myFont.info.ascender=750
myFont.info.capHeight=750
myFont.info.unitsPerEm=1000
myFont.info.unitsPerEm=1000
myFont.info.familyName="Bogota"
Variable([
dict(name="offsetValue", ui="Slider",
args=dict(
value=0.0,
minValue=-2.0,
maxValue=2.0)),
dict(name="Save_As_UFO", ui="CheckBox"),
dict(name="Reset_Offset", ui="CheckBox"),
], globals())
if Reset_Offset == True:
offsetValue = 0.0
fontName = "PanamaDisplay-Regular.otf"
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 = offsetValue
# 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):
# 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()
font(fontName)
fontSize(1000)
w, h = textSize(char)
myGlyph.width = w
# draw path into glyph with pen
path.drawToPen(pen)
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)
# offsetValue = random.uniform(-0.5, 0.5)
bez = BezierPath()
bez.text(charSet[i], font=fontName, fontSize=840)
xMin, yMin, xMax, yMax = bez.bounds()
cx = (xMin + xMax) / 6
cy = 0
bez.translate(cx, cy)
# drawPath(bez)
contours = extractContours(bez)
# print(contours)
drawContours(contours, charSet[i])
if Save_As_UFO:
# save the ufo
myFont.save("Bogota.ufo")
@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 or ufo2ft 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?
@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?
@frederik Thank you! I don't exactly know how to use the ufo2fdk, but I will try.
@frederik maybe you can help me with that one?
Hello @habakuk,
first of all you should definitely post your code. How else are we supposed to help you? If there is a lot of "mess" (commented or unusable code), clean it up before posting.
Now, it seems like you just want to play around. Start following the basics shapes (primitives), paths, text, images and you will learn a lot. It's super nice that you already started doing your own stuff, but it will be good to know what's already there.
I am saying this because you can probably figure how to make two opposing paths from there.
Here is a short sample of what I think I understand from your post.
# Define a function to draw your shape
def Blob(offset):
# Let's make a Bezier Path shape
bez = BezierPath()
bez.moveTo((100 + offset, 100 + offset * 0.1))
bez.curveTo((60 + offset, 64 + 0.2 * offset), (-4, 136), (72 + offset * 0.5, 170 + offset * 0.5))
bez.curveTo((74 + offset, 92 + 0.1 * offset), (110, 264), (126 + offset * 0.8, 114 + offset * 0.3))
bez.closePath()
# Here I make Another shape, but it's going the opposite direction
bez.moveTo((110, 140))
bez.lineTo((64, 136))
bez.lineTo((68, 106))
bez.lineTo((106, 114))
bez.closePath()
# Drawing the shape here
drawPath(bez)
numFrames = 60
for i in range(numFrames):
t = i / numFrames
newPage(1000, 1000)
fill(1)
rect(0, 0, 1000, 1000)
scale(4)
fill(0)
# The function takes an argument "offset" to create the movement
offset = sin(t * 100) * 10
print(offset)
Blob(offset)
saveImage("~/Desktop/blob.gif")
Hello @geancarle,
I searched Wikipedia for the Skia typeface and found out we are dealing with GX Variations. This article on AxisPraxis (very good resource for variable fonts btw) sheds some light on the technology: https://www.axis-praxis.org/blog/2016-11-14/6/article-gx-variations-a-typographic-revival
I suppose you can find more by googling the term or check this: http://www.gxfanclub.com/gxfonts.html
Otherwise, we are just talking about Variable fonts and you can find many open-source on AxisPraxis or somewhere else on the web.
Good luck!
@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.
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")