I am trying to center a text inside a box using Pillow. I have followed the instructions in this stackoverflow post, which gives the desired result. However, if I add an line break between the words, the text does not get resized properly and it gets offcentered.
Here an example of the code without the line break:
title_text = "Hello World"
img = Image.new(size=(400, 300), mode='RGB')
draw = ImageDraw.Draw(img)
font_path = "/content/Charlie don't surf.ttf"
# draw white rectangle 200x100 with center in 200,150
draw.rectangle((200-100, 150-50, 200+100, 150+50), fill='white')
draw.line(((0, 150), (400, 150)), 'gray')
draw.line(((200, 0), (200, 300)), 'gray')
# find font size for text `"Hello World"` to fit in rectangle 200x100
selected_size = 1
for size in range(1, 150):
title_font = ImageFont.FreeTypeFont(font_path, size=size)
w, h = title_font.getsize(title_text)
if w > 200 or h > 100:
break
selected_size = size
# draw text in center of rectangle 200x100
title_font = ImageFont.FreeTypeFont(font_path, size=selected_size)
draw.text((200-(w//2), 150-h//2), title_text, fill='red', font=title_font)
display(img)
Note that the text is centered and resized as desired.
But if I change title_text = "Hello World"
to title_text = "Hello\nWorld"
to add an line break. I get this:
title_text = "Hello\nWorld"
img = Image.new(size=(400, 300), mode='RGB')
draw = ImageDraw.Draw(img)
font_path = "/content/Charlie don't surf.ttf"
# draw white rectangle 200x100 with center in 200,150
draw.rectangle((200-100, 150-50, 200+100, 150+50), fill='white')
draw.line(((0, 150), (400, 150)), 'gray')
draw.line(((200, 0), (200, 300)), 'gray')
# find font size for text `"Hello World"` to fit in rectangle 200x100
selected_size = 1
for size in range(1, 150):
title_font = ImageFont.FreeTypeFont(font_path, size=size)
w, h = title_font.getsize(title_text)
if w > 200 or h > 100:
break
selected_size = size
# draw text in center of rectangle 200x100
title_font = ImageFont.FreeTypeFont(font_path, size=selected_size)
draw.text((200-(w//2), 150-h//2), title_text, fill='red', font=title_font)
display(img)
Note that with the line break, the text is not properly resized and is also not centered. Apparently the .getsize()
method does not handle line breaks well.
I am using Pillow version 7.1.2 and running all this in a Google Colab Notebook (that is the reason why I am running display(img)
).
What can I do to fix this issue? My end goal is to use Python to create cards for a game. Some cards will have two words and other cards will have only one word. I would like my code to be able to resize and center the text regardless the number of words.
Edit: After some test, I have noticed that .getsize()
treats the string "Hello\nWorld"
as a single line of text and not as two lines. My question could be: how to get the size of a text with line breaks?
I have found the solution. The final code looks like this:
title_text = "Hello\nWorld"
img = Image.new(size=(400, 300), mode='RGB')
draw = ImageDraw.Draw(img)
font_path = "/content/Charlie don't surf.ttf"
# draw white rectangle 200x100 with center in 200,150
draw.rectangle((200-100, 150-50, 200+100, 150+50), fill='white')
draw.line(((0, 150), (400, 150)), 'gray')
draw.line(((200, 0), (200, 300)), 'gray')
# find font size for text `"Hello World"` to fit in rectangle 200x100
selected_size = 1
for size in range(1, 150):
title_font = ImageFont.FreeTypeFont(font_path, size=size)
#w, h = title_font.getsize(title_text)
w, h = draw.textsize(title_text, title_font)
if w > 200 or h > 100:
break
selected_size = size
# draw text in center of rectangle 200x100
title_font = ImageFont.FreeTypeFont(font_path, size=selected_size)
draw.text((200-(w//2)+5, 150-h//2), title_text, fill='red', font=title_font, align = "center")
display(img)
The final result is:
I have used resources I found on this page. More specifically I found out that you can use draw.textsize(title_text, title_font)
to get a more precise size of the text, especially when it has line breaks.