Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Using Pillow to center multi-line text without it going off the image

For context, I am trying to make a ‘sigma-male’ meme generator. The idea is I can feed in a load of pre-defined ‘sigma-male’ jokes/quotes and overaly it on an image of a ‘sigma-male’. The format of these pictures should be that in the center of the image there will be a line saying ‘Sigma-Male Rule #X’ and underneath there would be some bad life advice e.g. ‘Don’t be part of the problem, be the whole problem’.
Here is the picture I am starting with (note that for my purposes all images are the same size, 1080×1080. So issues of variable image size shouldn’t be a problem):

Image of character

But when I try to add the ‘life-advice’ I end up with this:
Character with text

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

As you can see, the text runs straight off of the image. One fix I have tried is by breaking it up into several lines: Character with text after breaking line up

Theoretically, this could be useful but the problem is I would manually have to adjust each different quote, which would stop this from being an automatic procedure.

Ideally, what I would like is an image like this: Desired output

Something else I would need to account for is if I have a short quote that’s only one line, how can I also guarentee that this will be centered appropriatley relative to the ‘Sigma Male Rule#X

Here is my code:

import numpy as np
import cv2
from PIL import Image, ImageDraw, ImageFont
import textwrap

my_image = Image.open("Bateman1Raw resized.jpg")
title_font = ImageFont.truetype('Bebas-Regular.ttf', 100)


title_text = "Sigma Male Rule #1"
text_test='Don\'t be part of the\n problem, be the whole problem'


image_editable = ImageDraw.Draw(my_image)
image_editable.text((200,400), title_text, (255, 255, 255), font=title_font,stroke_width=2,stroke_fill='black')
image_editable.text((0,500), words,(255, 255, 255),align='center',font=title_font,stroke_width=2,stroke_fill='black')

my_image.save("result.jpg")

>Solution :

ImageFont has getlength(text) which you can use to split text to lines.

I first split all text to words and I add next word to text and check getlength(text). If it is shorter then I check with next word. If it is longer then I add this line to results and start with empty text to create new line.

width, height = my_image.size

text_test = "Don't be part of the problem, be the whole problem"

all_words = text_test.split(' ')
all_lines = []
line = []

while all_words:
    word = all_words[0]
    new_text = ' '.join(line + [word])
    #print('>', word, new_text)

    if title_font.getlength(new_text) > width:
        # if longer then keep shorter line and start with new line
        all_lines.append(' '.join(line))
        line = []
    else:
        # if shorter then add word to line, and remove word from list 
        line += [word]
        all_words = all_words[1:]
   
# add last line to results 
if line:
    all_lines.append(' '.join(line))
print(all_lines)    

And next I can use for-loop to draw lines:

x = 0
y = 500
for text in all_lines:
    #x = (width - title_font.getlength(text))//2
    image_editable.text((x, y), text, (255, 255, 255), align='center', font=title_font, stroke_width=2, stroke_fill='black')
    y += 100

Full working code

from PIL import Image, ImageDraw, ImageFont

my_image = Image.open("lenna.jpg")
my_image = my_image.resize((1080,1080))

width, height = my_image.size

title_font = ImageFont.truetype('Arial.ttf', 100)

text_test = "Don't be part of the problem, be the whole problem"

all_words = text_test.split(' ')
all_lines = []
line = []
while all_words:
    word = all_words[0]
    new_text = ' '.join(line + [word])
    print('>', word, new_text)
    if title_font.getlength(new_text) > width:
        all_lines.append(' '.join(line))
        line = []
    else:
        line += [word]
        all_words = all_words[1:]
    
if line:
    all_lines.append(' '.join(line))
print(all_lines)    

image_editable = ImageDraw.Draw(my_image)

title_text = "Sigma Male Rule #1"
image_editable.text((200, 400), title_text, (255, 255, 255), font=title_font, stroke_width=2, stroke_fill='black')

y = 500
for text in all_lines:
    image_editable.text((0, y), text, (255, 255, 255), align='center', font=title_font, stroke_width=2, stroke_fill='black')
    y += 100

my_image.save("result.jpg")

my_image.show()

Image Lenna from Wikipedia

Result:
enter image description here

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading