-
-
Save jasondilworth56/27764ae8ed2327a34ceebb06e75c30ea to your computer and use it in GitHub Desktop.
Layer on top of Python Imaging Library (PIL) to write text in images easily
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# coding: utf-8 | |
# You need PIL <http://www.pythonware.com/products/pil/> to run this script | |
# Download unifont.ttf from <http://unifoundry.com/unifont.html> (or use | |
# any TTF you have) | |
# Copyright 2011 Álvaro Justen [alvarojusten at gmail dot com] | |
# License: GPL <http://www.gnu.org/copyleft/gpl.html> | |
from image_utils import ImageText | |
color = (50, 50, 50) | |
text = 'Python is a cool programming language. You should learn it!' | |
font = 'unifont.ttf' | |
img = ImageText((800, 600), background=(255, 255, 255, 200)) # 200 = alpha | |
#write_text_box will split the text in many lines, based on box_width | |
#`place` can be 'left' (default), 'right', 'center' or 'justify' | |
#write_text_box will return (box_width, box_calculed_height) so you can | |
#know the size of the wrote text | |
img.write_text_box((300, 50), text, box_width=200, font_filename=font, | |
font_size=15, color=color) | |
img.write_text_box((300, 125), text, box_width=200, font_filename=font, | |
font_size=15, color=color, place='right') | |
img.write_text_box((300, 200), text, box_width=200, font_filename=font, | |
font_size=15, color=color, place='center') | |
img.write_text_box((300, 275), text, box_width=200, font_filename=font, | |
font_size=15, color=color, place='justify') | |
#You don't need to specify text size: can specify max_width or max_height | |
# and tell write_text to fill the text in this space, so it'll compute font | |
# size automatically | |
#write_text will return (width, height) of the wrote text | |
img.write_text((100, 350), 'test fill', font_filename=font, | |
font_size='fill', max_height=150, color=color) | |
# fill_text_box will attempt to use up all available space in both x and | |
# y dimensions, where the above example will only stick to one line | |
img.fill_text_box((100,100), text, box_width=600, box_height=400, | |
font_filename=font) | |
img.save('sample-imagetext.png') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# coding: utf-8 | |
# Copyright 2011 Álvaro Justen [alvarojusten at gmail dot com] | |
# License: GPL <http://www.gnu.org/copyleft/gpl.html> | |
from PIL import Image, ImageDraw, ImageFont | |
class ImageText(object): | |
def __init__(self, filename_or_size, mode='RGBA', background=(0, 0, 0, 0), | |
encoding='utf8'): | |
if isinstance(filename_or_size, str): | |
self.filename = filename_or_size | |
self.image = Image.open(self.filename) | |
self.initial_image = Image.open(self.filename) | |
self.size = self.image.size | |
elif isinstance(filename_or_size, (list, tuple)): | |
self.size = filename_or_size | |
self.image = Image.new(mode, self.size, color=background) | |
self.initial_image = Image.new(mode, self.size, color=background) | |
self.filename = None | |
self.draw = ImageDraw.Draw(self.image) | |
self.encoding = encoding | |
def reset_image(self): | |
self.image = self.initial_image | |
self.draw = ImageDraw.Draw(self.image) | |
def save(self, filename=None): | |
self.image.save(filename or self.filename) | |
def get_image(self): | |
return self.image | |
def get_font_size(self, text, font, max_width=None, max_height=None): | |
if max_width is None and max_height is None: | |
raise ValueError('You need to pass max_width or max_height') | |
font_size = 1 | |
text_size = self.get_text_size(font, font_size, text) | |
if (max_width is not None and text_size[0] > max_width) or \ | |
(max_height is not None and text_size[1] > max_height): | |
raise ValueError("Text can't be filled in only (%dpx, %dpx)" % \ | |
text_size) | |
while True: | |
if (max_width is not None and text_size[0] >= max_width) or \ | |
(max_height is not None and text_size[1] >= max_height): | |
return font_size - 1 | |
font_size += 1 | |
text_size = self.get_text_size(font, font_size, text) | |
def write_text(self, xy, text, font_filename, font_size=11, | |
color=(0, 0, 0), max_width=None, max_height=None): | |
x, y = xy | |
if isinstance(text, str): | |
try: | |
text = text.decode(self.encoding) | |
except Exception as e: | |
pass | |
if font_size == 'fill' and \ | |
(max_width is not None or max_height is not None): | |
font_size = self.get_font_size(text, font_filename, max_width, | |
max_height) | |
text_size = self.get_text_size(font_filename, font_size, text) | |
font = ImageFont.truetype(font_filename, font_size) | |
if x == 'center': | |
x = (self.size[0] - text_size[0]) / 2 | |
if y == 'center': | |
y = (self.size[1] - text_size[1]) / 2 | |
self.draw.text((x, y), text, font=font, fill=color) | |
return text_size | |
def get_text_size(self, font_filename, font_size, text): | |
font = ImageFont.truetype(font_filename, font_size) | |
return font.getsize(text) | |
def fill_text_box(self, xy, text, box_width, box_height, font_filename, | |
start_font_size=1, color=(0,0,0), place='left', | |
justify_last_line=False): | |
font_size = start_font_size | |
while True: | |
width, height, box_width = self.write_text_box(xy, text, box_width, | |
font_filename, font_size, color, place) | |
if (width >= box_width) or (height >= box_height): | |
font_size -= 1 | |
self.reset_image() | |
self.write_text_box(xy, text, box_width, | |
font_filename, font_size, color, place) | |
break | |
font_size += 1 | |
return (box_width, box_height) | |
def write_text_box(self, xy, text, box_width, font_filename, | |
font_size=11, color=(0, 0, 0), place='left', | |
justify_last_line=False): | |
x, y = xy | |
lines = [] | |
line = [] | |
words = text.split() | |
for word in words: | |
new_line = ' '.join(line + [word]) | |
size = self.get_text_size(font_filename, font_size, new_line) | |
text_height = size[1] | |
if size[0] <= box_width: | |
line.append(word) | |
else: | |
lines.append(line) | |
line = [word] | |
if line: | |
lines.append(line) | |
lines = [' '.join(line) for line in lines if line] | |
height = y | |
for index, line in enumerate(lines): | |
if place == 'left': | |
total_size = self.get_text_size(font_filename, font_size, line) | |
self.write_text((x, height), line, font_filename, font_size, | |
color) | |
elif place == 'right': | |
total_size = self.get_text_size(font_filename, font_size, line) | |
x_left = x + box_width - total_size[0] | |
self.write_text((x_left, height), line, font_filename, | |
font_size, color) | |
elif place == 'center': | |
total_size = self.get_text_size(font_filename, font_size, line) | |
x_left = int(x + ((box_width - total_size[0]) / 2)) | |
self.write_text((x_left, height), line, font_filename, | |
font_size, color) | |
elif place == 'justify': | |
words = line.split() | |
if (index == len(lines) - 1 and not justify_last_line) or \ | |
len(words) == 1: | |
self.write_text((x, height), line, font_filename, font_size, | |
color) | |
continue | |
line_without_spaces = ''.join(words) | |
total_size = self.get_text_size(font_filename, font_size, | |
line_without_spaces) | |
space_width = (box_width - total_size[0]) / (len(words) - 1.0) | |
start_x = x | |
for word in words[:-1]: | |
self.write_text((start_x, height), word, font_filename, | |
font_size, color) | |
word_size = self.get_text_size(font_filename, font_size, | |
word) | |
start_x += word_size[0] + space_width | |
last_word_size = self.get_text_size(font_filename, font_size, | |
words[-1]) | |
last_word_x = x + box_width - last_word_size[0] | |
self.write_text((last_word_x, height), words[-1], font_filename, | |
font_size, color) | |
height += text_height | |
return (total_size[0], height - y, box_width) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hey @waymanls, thanks for the compliment.
To be completely honest I'm not sure, and I am unlikely to get a chance to check any time soon. I didn't want to leave you hanging with no response. I would assume that if using something like the below (you'd need some of the rest of
example_image_utils.py
) doesn't work then it'd need some work on either this orpilmoji
's end:Sorry I can't be more help!