Get bezier path from text.
I'd like to read a bezier outline from text.
I know i can draw a bezier path with canvas or ui.Path but i want the opposite: i want to pass a text/single letter and return it's bezier path with all the points etc...
Is this possible in Pythonista with built in libraries?
Here is some ObjC code which does what you are asking. Most of this is just handling multiple letters, etc. If you want a single letter, it looks like
CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyph, NULL);
It should be possible to convert this code (getting the CtFont, and CgGlyph, then calling CTFontCreatePathForGlyph) using the c bindings.
It is then possible to turn that into a UIBezierPath, which then lets you access the control points, if memory serves.
Okay, worked up an example:
This takes a string a generates a ui.Path. You can access the underlying UIBezierPath using ObjCInstance(p).
I tried and got
Fatal Python error: Segmentation fault
Current thread 0x000000016e2f3000 (most recent call first):
File "/private/var/mobile/Containers/Shared/AppGroup/BC53E549-355D-4E77-BC46-64C3D3E0BDAF/Pythonista3/Documents/text to bezier.py", line 11 in <module>
Hmm, are you using python3.6 or 2.7 interpreter by default? this probably requires 3.x.
This version may be more robust:
I use Python 3.5 as default.
The new version doesn't crash but nothing appears in the sheet....
@JonB I think you need to set
CTFontCreatePathForGlyph. It may happen to work for you because you are using (I think) a 32-bit device.
I use an IPad mini 4, thus 64bits processor
@omz yes, quite right, i get lazy when i try things and they work on my device.
This should hopefully work on 64bit as well.
Always a blank sheet on my iPad mini 4, sorry
This one works for me (changed slightly from an earlier version), latest one crashes, can't quite figure out why right now.
from objc_util import * import ui font=ObjCClass('UIFont').systemFontOfSize_weight_(64,1) s='Hello world' p=ui.Path() w=0 c.CTFontCreatePathForGlyph.restype = c_void_p c.CTFontCreatePathForGlyph.argtypes = [c_void_p, c_void_p, c_void_p] for x in s.encode('ascii'): glyph= font._defaultGlyphForChar_(x) if not x==32: #space letter = ObjCInstance(c.CTFontCreatePathForGlyph(font, glyph, None)) letterBezier=UIBezierPath.bezierPathWithCGPath_(letter) #transform it so we shift and flip y letterBezier.applyTransform_(CGAffineTransform(1,0,0,-1,w,font.capHeight())) ObjCInstance(p).appendBezierPath_(letterBezier) w+=font.advancementForGlyph(glyph).width class myview(ui.View): def __init__(self): self.frame=(0,0,500,500) self.bg_color='white' def draw(self): ui.set_color('red') p.line_width=1 p.stroke() myview().present('sheet')
Wow what a lively community here!
Thanks for all the responses! This works and will be very helpful.
By the way... Here is a simulation of what i am trying to do:
Right now i'm just displaying images depending on touch location. That's already a heavy operation with 200 images and it's just one axis interpolation. I imagine drawing paths would be much faster and lightweight.
My goal is to simulate what variable fonts can do in a fun and interactive way (i will probably incorporate motion control).
But maybe a more than a simulation can be achieved?
@JonB mentioned CTFontCreatePathForGlyph and while i googled it i found this under "Working With Font Variations":
Not sure if this means it is possible to open variable fonts and work with them...