Pillow, centering of text not working, how is this accomplished?

I have tested the calculations and the math is correct (and takes into account the height and width of the font), but after Python creates the image and I put it into Photoshop, the vertical and horizontal centering of the text is not correct. Should I be doing something else with my code?

from PIL import Image, ImageDraw, ImageFont

# base = Image.open("Images/Phones/KK17018_Navy_KH10089.jpg").convert("RGBA")
base = Image.open('Images/Tablets/KK17076_Hunter_KH235.jpg').convert('RGB')

# Create blank rectangle to write on
draw = ImageDraw.Draw(base)

message = 'GGS'
num1, num2 = base.size
bounding_box = [0, 0, num1, num2]
x1, y1, x2, y2 = bounding_box  # For easy reading

# font = ImageFont.truetype('Fonts/Circle/Circle Monograms Three White.ttf', size=413)

font = ImageFont.truetype('Fonts/Modern/Vhiena_Monoline.otf', size=800)
font2 = ImageFont.truetype('Fonts/Modern/Vhiena_Base.otf', size=800)
font3 = ImageFont.truetype('Fonts/Modern/Vhiena_Extrude A.otf', size=800)

# Calculate the width and height of the text to be drawn, given font size
w, h = draw.textsize(message, font=font3)

# Calculate the mid points and offset by the upper left corner of the bounding box
x = (x2 - x1 - w) / 2 + x1
y = (y2 - y1 - h) / 2 + y1

# Write the text to the image, where (x,y) is the top left corner of the text

draw.text((x, y), message, align='center', font=font3, fill='orange')
draw.text((x, y), message, align='center', font=font2, fill='black')
draw.text((x, y), message, align='center', font=font, fill='white')

# Draw the bounding box to show that this works
# draw.rectangle([x1, y1, x2, y2])


The text's upper left x,y coordinates should be (874,1399.5), but in Photoshop, they show as (875,1586). The Python code above does calculate (874,1399.5) correctly, but something is placing the font lower than it should be.

Also, I'm stacking fonts like this because it gives a regular font, a shadow font and a font that makes it look beveled in the middle of the font. Would there be a better method or is stacking fonts an ok practice?

EDIT: Upon further testing, something is adding a 22% top margin to the font as the font size increases. I could account for this, but this seems rather odd.

Asked by: Maddie500 | Posted: 06-12-2021

Answer 1

I don't have your exact font handy to test, but this is probably because Pillow's textsize and text methods, by default, anchor to the ascender height instead of the actual rendered top; see this note in the docs. Try using textbbox instead of textsize, and specifying the top anchor in both that and the text method, and see if that behaves more intuitively.

Note that you could probably just anchor the text to 'mm', middle/middle, to center it based on the coordinates of the image's midpoint. This anchors vertically to halfway between the ascenders and descenders, though, so it may not actually look centered, depending on the font and what glyphs you render.

Answered by: Byron888 | Posted: 07-01-2022

Similar questions

python - How would a system tray application be accomplished on other platforms?

Windows has the "system tray" that houses the clock and alway-running services like MSN, Steam, etc. I'd like to develop a wxPython tray application but I'm wondering how well this will port to other platforms. What is the tray equivalent on each platform, and how much manual work would be required to support Windows, OSX and Linux (which shells in particular would be friendliest).

Python + ZMQ: Operation cannot be accomplished in current state

I am trying to get a python program to communicate with another python program via zeromq by using the request-reply pattern. The client program should send a request to the server program which replies. I have two servers such that when one server fails the other takes over. Communication works perfect when the first server works, however, when the first server fails and when I make a request to the second server,...

python - 'if' statement only accomplished only once in a loop

I am simulating a cache system by performing a cumulative sum of bytes and, when this sums up to 0.95 the size of the cache, forces the value to be 0.9 of the full size. Final result should evolve as a saw tooth. This is the code I implement to force this condition. cache_size=1e11 #B high_mark=0.95 low_mark=0.50 ordered_datestamp['Cache']=0 for i in range(1,rows_number): ordered_datestamp...

Still can't find your answer? Check out these communities...

PySlackers | Full Stack Python | NHS Python | Pythonist Cafe | Hacker Earth | Discord Python