10

I am trying to get some text to overlap on an image, which is I have the following code for.

from PIL import Image, ImageDraw, ImageFont

msg = "This is a test phrase, so please shrink the text."

im = Image.open("test.jpg")
draw = ImageDraw.Draw(im)

W, H = im.size

myFont =             
ImageFont.truetype("/usr/share/fonts/truetype/customfonts/KeepCalm-Medium.ttf")

w, h = draw.textsize(msg, font=myFont)
draw.text(((W-w)/2,(H-h)/2), msg, fill="black", font=myFont)

im.save("sample-out.png", "PNG")

What I need is the text to scale in the middle, but between the pixel width and height of 1600,300. Whichever one it achieve first.

I imagine it has something to do with fontsize increasing, but I can't figure it out.

Luke Appel
  • 361
  • 3
  • 11
  • 1
    IS not this [question](https://stackoverflow.com/questions/4902198/pil-how-to-scale-text-size-in-relation-to-the-size-of-the-image) what you wanted from the start!! – ASHu2 Jun 09 '19 at 04:37

3 Answers3

10

So figured it out with a bit of luck, here is the below code. Please note that some variables changed names from the original code above, but this works.

from PIL import ImageFont, ImageDraw, Image

image = Image.open('test.jpg')
draw = ImageDraw.Draw(image)
txt = "share/fonts/truetype/customfonts/KeepC"
fontsize = 1  # starting font size

W, H = image.size

# portion of image width you want text width to be
blank = Image.new('RGB',(1000, 300))


font = ImageFont.truetype("/usr/share/fonts/truetype/customfonts/KeepCalm-Medium.ttf", fontsize)
print image.size
print blank.size

while (font.getsize(txt)[0] < blank.size[0]) and (font.getsize(txt)[1] < blank.size[1]):
    # iterate until the text size is just larger than the criteria
    fontsize += 1
    font = ImageFont.truetype("/usr/share/fonts/truetype/customfonts/KeepCalm-Medium.ttf", fontsize)

# optionally de-increment to be sure it is less than criteria
fontsize -= 1
font = ImageFont.truetype("/usr/share/fonts/truetype/customfonts/KeepCalm-Medium.ttf", fontsize)

w, h = draw.textsize(txt, font=font)

print 'final font size',fontsize
draw.text(((W-w)/2,(H-h)/2), txt, font=font, fill="black") # put the text on the image
image.save('sample-out.png') # save it
Luke Appel
  • 361
  • 3
  • 11
  • Thanks for pasting this. I was running into the same problem and couldn't find anything. I wonder if there is a better solution to this. – Moondra Jun 06 '19 at 00:05
2

You can increase the font size with the second parameter of the function ImageFont.truetype().

myFont = ImageFont.truetype("/usr/share/fonts/truetype/customfonts/KeepCalm-Medium.ttf", 32)
Raphael
  • 517
  • 4
  • 18
0

This is a function I made to wrap text and autofit it according to the textarea input you give as a tuple (width, height) in pixels. I am sharing it here for anyone looking for something similar.

text is the input text you want to fit inside textarea, font_name is the font name you are running it for ex: 'Arial.ttf' Should be placed in the directory you are running. And pixel_gap is the gap you want to keep between each line in the text.

It returns the wrapped (text) and the font size (point_size)You can then use pillow draw.text to overlay it on the image.

from PIL import Image, ImageDraw, ImageFont
from string import ascii_letters
import textwrap

def get_font_size(textarea, text, font_name, pixel_gap = 11):
    text_width = int(textarea[0])
    text_height = int(textarea[1])
    
    for point_size in range(5, 90):
        wrapped_text = []
        font = ImageFont.truetype(font_name, point_size)
                
        avg_char_width = sum(font.getbbox(char)[2] for char in ascii_letters) / len(ascii_letters)
        max_char_height = max(font.getbbox(char)[3] - font.getbbox(char)[1] for char in ascii_letters)
        
        # Translate this average length into a character count
        max_char_count = int( (text_width) / avg_char_width)
        text = textwrap.fill(text=text, width=max_char_count)
        num_line = len(text.splitlines())
        
        wrapped_text.append(text)
        
        if (max_char_height * num_line) + (pixel_gap * (num_line + 1)) >= text_height:
            
            point_size = point_size - 1
            text = wrapped_text[-1]
            
            # print("\n --> SIZE: ", point_size)
            break
        
    return text, point_size
Gaurav Hazra
  • 333
  • 1
  • 11