-
-
Save turicas/1455973 to your computer and use it in GitHub Desktop.
| #!/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) | |
| img.save('sample-imagetext.png') |
| #!/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> | |
| import Image | |
| import ImageDraw | |
| import 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.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.filename = None | |
| self.draw = ImageDraw.Draw(self.image) | |
| self.encoding = encoding | |
| def save(self, filename=None): | |
| self.image.save(filename or self.filename) | |
| 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, (x, y), text, font_filename, font_size=11, | |
| color=(0, 0, 0), max_width=None, max_height=None): | |
| if isinstance(text, str): | |
| text = text.decode(self.encoding) | |
| 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 write_text_box(self, (x, y), text, box_width, font_filename, | |
| font_size=11, color=(0, 0, 0), place='left', | |
| justify_last_line=False): | |
| 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): | |
| height += text_height | |
| if place == 'left': | |
| 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) | |
| return (box_width, height - y) |
Great script dude, thanks!
really helpful tool here! got the job done properly!
I expanded the work from @josephkern in a previous comment, and added support for a bunch of stuff:
- Vertical centering (middle aligning)
- Vertical bottom aligning
- Can now also send a PIL.Image instance instead of only image size or path
- Supports vertical line spacing too
- Fixed a bug where the first line would always be printed at the wrong Y position
Here it is for everyone: https://gist.github.com/pojda/8bf989a0556845aaf4662cd34f21d269
I recently had to implement the same thing. I have created a package on pypi which might come in handy.
You can add text to an image with this steps:
-
Download an image:
curl https://i.imgur.com/XQCKcC9.jpg -o ./image.jpg -
Download a font:
curl https://fonts.google.com/download?family=Roboto -o ./roboto.zip ; unzip ./roboto.zip -d ./Roboto -
pip install pynter
from pynter.pynter import generate_captioned
font_path = './Roboto/Roboto-Regular.ttf'
image_path = './image.jpg'
im = generate_captioned('China lands rover on Mars'.upper(), image_path=image_path, size=(1080, 1350),
font_path=font_path, filter_color=(0, 0, 0, 40))
im.show()
im.convert('RGB').save('drawn_image.jpg')
This will be the result:
Hi! Long Time!
I ran into an error.
After importing "from image_utils import ImageText" it says
def write_text(self, (x, y), text, font_filename, font_size=11,
^
SyntaxError: invalid syntax
The caret symbol is exactly below the first bracket of (x, y).
Any idea why? I am using Python 3.8.
Thank You so much.
@simucentral , you can't have (x,y) as an argument in Python 3.x.x. You'd have to split out x,y as individual arguments:
def write_text(self, x, y, text, font_filename, font_size=11,
justify option not working properly with RTL languages like Arabic/Persian

Just had a go at a (probably very shoddy performance wise) implementation of using up all available x and y space in a box. Hope others find it useful.