Forum Archive

Get bezier path from text.

marcin_dybas

Hello,

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?

JonB

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.

JonB

Okay, worked up an example:

https://gist.github.com/jsbain/b1d1f69974706735fa352af247f33102

This takes a string a generates a ui.Path. You can access the underlying UIBezierPath using ObjCInstance(p).

cvp

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

JonB

Hmm, are you using python3.6 or 2.7 interpreter by default? this probably requires 3.x.
This version may be more robust:
https://gist.github.com/b5a56a632996041888be9df56a5d27e3

cvp

I use Python 3.5 as default.
The new version doesn't crash but nothing appears in the sheet....

omz

@JonB I think you need to set restype and argtypes for CTFontCreatePathForGlyph. It may happen to work for you because you are using (I think) a 32-bit device.

cvp

I use an IPad mini 4, thus 64bits processor

JonB

@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.
https://gist.github.com/761de9d628d2a6689d73a00d24ac2a53

cvp

Always a blank sheet on my iPad mini 4, sorry

omz

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')
cvp

Ok now
Thanks to @JonB and @omz
Hoping it's what @marcin_dybas wanted

marcin_dybas

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:
https://www.instagram.com/p/BSL9ea8g0dv/?taken-by=marcin.dybas

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":
https://developer.apple.com/reference/coretext/ctfont-q6r#1666125

Not sure if this means it is possible to open variable fonts and work with them...