rotate text around its center in pycairo
At least on the version of cairo available on my machine (1.8.8), the following approach works for me:
def text(ctx, string, pos, theta = 0.0, face = 'Georgia', font_size = 18):
ctx.save()
# build up an appropriate font
ctx.select_font_face(face , cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
ctx.set_font_size(font_size)
fascent, fdescent, fheight, fxadvance, fyadvance = ctx.font_extents()
x_off, y_off, tw, th = ctx.text_extents(string)[:4]
nx = -tw/2.0
ny = fheight/2
ctx.translate(pos[0], pos[1])
ctx.rotate(theta)
ctx.translate(nx, ny)
ctx.move_to(0,0)
ctx.show_text(string)
ctx.restore()
Which can be used in the following way:
width = 500
height = 500
surface = cairo.ImageSurface(cairo.FORMAT_RGB24, width, height)
ctx = cairo.Context(surface)
ctx.set_source_rgb(1,1,1)
rect(ctx, (0,0), (width, height), stroke=False)
ctx.set_source_rgb(0,0,0)
for i in xrange(5):
for j in xrange(5):
x = 100 * i + 20
y = 100 * j + 20
theta = math.pi*0.25*(5*i+j)
text(ctx, 'hello world', (x, y), theta, font_size=15)
surface.write_to_png('text-demo.png')
OK so cairo allows for text move_to and rotate. This means that what you want is to figure out (x,y) for move_to (T), such that when you rotate (R), the center point of your text is at your desired location, c=(cx,cy):
So you have to solve the equation Mv = c, where v is the text center relative to the text origin:
M = T*R
T = (1 0 x)
(0 1 y)
(0 0 1)
R = (cos r -sin r 0)
(sin r cos r 0)
(0 0 1)
v = (w/2, h', 1)
c = (cx, cy, 1)
h' = h/2 - (h - y_bearing)
Sanity checks:
- when r is 0 (no rotation), you get x=cx-w/2, y=cy-h', which you know is the correct answer
- when r=-90 (text sideways, with "up" towards the right), you get what you expect, ie x = cx - h' and y = cy + w/2
For python code, you will have to rewrite the above equation so you end up with A*t=b, where t=(x,y), and you will compute t = inv(A)*b. Then, you will simply do
cr.move_to(x, y)
cr.rotate(r)
cr.show_text(yourtext)
Note that the coordinate system in cairo has +y going down so there will be a couple signs to fix, and maybe y_bearing is not correct, but you get the idea.