Skip to content

Instantly share code, notes, and snippets.

@avalonv
Created September 23, 2022 18:40
Show Gist options
  • Save avalonv/3331167eeb548004946eebddc623c85d to your computer and use it in GitHub Desktop.
Save avalonv/3331167eeb548004946eebddc623c85d to your computer and use it in GitHub Desktop.
Python function to wrap and scale text to fit Pillow images
from PIL import Image, ImageFont, ImageDraw
def fit_textbox(length:int, height:int, text:str, fontname:str, fmin=15, fmax=40):
# this will dynamically wrap and scale text with a font size that can fill
# a given textbox, both length and height wise. manually setting fmin and
# fmax is recommended when working with larger batches of text.
# returns wrapped text and a ImageFont.FreeTypeFont object
def get_wrapped_text(text:str, font:ImageFont.FreeTypeFont, line_length:int):
# adapted from Chris Collett https://stackoverflow.com/a/67203353/8225672
lines = ['']
for word in text.split():
line = f'{lines[-1]} {word}'.strip()
if font.getlength(line) <= line_length:
lines[-1] = line
else:
lines.append(word)
return '\n'.join(lines)
font = ImageFont.truetype(fontname, fmin)
lines = get_wrapped_text(text, font, length)
boxheight = image.multiline_textbbox((15,15),lines, font)[-1]
for fsize in range(fmin, fmax+1):
font = font.font_variant(size=fsize)
lines = get_wrapped_text(text, font, length)
boxheight = image.multiline_textbbox((15,15),lines, font)[-1]
# print(boxheight)
if boxheight > height:
fsize -= 1
font = font.font_variant(size=fsize)
lines = get_wrapped_text(text, font, length)
# print(f"FSIZE={fsize}")
break
return lines, font
# example
text = "Miracles of artistry were everywhere. Great iron crates with wheels sat silent on steel rails. Beautifully carved works with lights of red, amber, and green dangled over every street. As they moved away from the massive sculptures, they found a great array of smaller ones. Some were covered in glass or brick, but many were composed of materials they had never before encountered. The sheer variety of colors and styles was staggering."
v_margin = 15
h_margin = 15
out = Image.new(mode='RGB', size=(600,800), color='white')
image = ImageDraw.Draw(out)
lines, font = fit_textbox(565, 745, text, 'bookerly.ttf')
image.multiline_text((h_margin, v_margin), lines, 'grey', font)
out.save("forestofmyth.jpg")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment