2022-08-09 02:04:21 +00:00
|
|
|
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
|
|
|
|
|
2022-08-15 23:08:51 +00:00
|
|
|
def get_font_size(self, text, font, max_width=None, max_height=None, stroke_size=0):
|
2022-08-09 02:04:21 +00:00
|
|
|
if max_width is None and max_height is None:
|
|
|
|
raise ValueError('You need to pass max_width or max_height')
|
|
|
|
font_size = 1
|
2022-08-15 23:08:51 +00:00
|
|
|
text_size = self.get_text_size(font, font_size, text, stroke_size=stroke_size)
|
2022-08-09 02:04:21 +00:00
|
|
|
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
|
2022-08-15 23:08:51 +00:00
|
|
|
text_size = self.get_text_size(font, font_size, text, stroke_size=stroke_size)
|
2022-08-09 02:04:21 +00:00
|
|
|
|
|
|
|
def write_text(self, xy, text, font_filename, font_size=11,
|
2022-08-13 00:30:05 +00:00
|
|
|
color=(0, 0, 0), max_width=None, max_height=None, stroke_color = None, stroke_size = 0):
|
2022-08-09 02:04:21 +00:00
|
|
|
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,
|
2022-08-15 23:08:51 +00:00
|
|
|
max_height, stroke_size=stroke_size)
|
|
|
|
text_size = self.get_text_size(font_filename, font_size, text, stroke_size=stroke_size)
|
2022-08-09 02:04:21 +00:00
|
|
|
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
|
2022-08-13 00:30:05 +00:00
|
|
|
self.draw.text((x, y), text, font=font, fill=color, stroke_width=stroke_size, stroke_fill=stroke_color)
|
2022-08-09 02:04:21 +00:00
|
|
|
return text_size
|
|
|
|
|
2022-08-15 23:08:51 +00:00
|
|
|
def get_text_size(self, font_filename, font_size, text, stroke_size = 0):
|
2022-08-09 02:04:21 +00:00
|
|
|
font = ImageFont.truetype(font_filename, font_size)
|
2022-08-15 23:08:51 +00:00
|
|
|
return font.getsize(text, stroke_width=stroke_size)
|
2022-08-09 02:04:21 +00:00
|
|
|
|
|
|
|
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:
|
2022-08-12 23:13:18 +00:00
|
|
|
width, height, box_width, _ = self.write_text_box(xy, text, box_width,
|
2022-08-09 02:04:21 +00:00
|
|
|
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',
|
2022-08-20 22:18:44 +00:00
|
|
|
justify_last_line=False, stroke_color = None, stroke_size = 0, calculate_only = False):
|
2022-08-09 02:04:21 +00:00
|
|
|
x, y = xy
|
|
|
|
|
|
|
|
lines = []
|
|
|
|
line = []
|
|
|
|
words = text.split()
|
|
|
|
for word in words:
|
|
|
|
new_line = ' '.join(line + [word])
|
2022-08-15 23:08:51 +00:00
|
|
|
size = self.get_text_size(font_filename, font_size, new_line, stroke_size=stroke_size)
|
2022-08-09 02:04:21 +00:00
|
|
|
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
|
2022-08-12 23:13:18 +00:00
|
|
|
max_x_size = -1
|
2022-08-09 02:04:21 +00:00
|
|
|
for index, line in enumerate(lines):
|
|
|
|
if place == 'left':
|
|
|
|
total_size = self.get_text_size(font_filename, font_size, line)
|
2022-08-20 22:18:44 +00:00
|
|
|
if not calculate_only:
|
|
|
|
self.write_text((x, height), line, font_filename, font_size,
|
|
|
|
color, stroke_color=stroke_color, stroke_size=stroke_size)
|
2022-08-09 02:04:21 +00:00
|
|
|
elif place == 'right':
|
|
|
|
total_size = self.get_text_size(font_filename, font_size, line)
|
|
|
|
x_left = x + box_width - total_size[0]
|
2022-08-20 22:18:44 +00:00
|
|
|
if not calculate_only:
|
|
|
|
self.write_text((x_left, height), line, font_filename,
|
|
|
|
font_size, color, stroke_color=stroke_color, stroke_size=stroke_size)
|
2022-08-09 02:04:21 +00:00
|
|
|
elif place == 'center':
|
|
|
|
total_size = self.get_text_size(font_filename, font_size, line)
|
|
|
|
x_left = int(x + ((box_width - total_size[0]) / 2))
|
2022-08-20 22:18:44 +00:00
|
|
|
if not calculate_only:
|
|
|
|
self.write_text((x_left, height), line, font_filename,
|
|
|
|
font_size, color, stroke_color=stroke_color, stroke_size=stroke_size)
|
2022-08-09 02:04:21 +00:00
|
|
|
elif place == 'justify':
|
|
|
|
words = line.split()
|
2022-08-20 22:18:44 +00:00
|
|
|
if (index == len(lines) - 1 and not justify_last_line) or len(words) == 1:
|
|
|
|
if not calculate_only:
|
|
|
|
self.write_text((x, height), line, font_filename, font_size, color, stroke_color=stroke_color, stroke_size=stroke_size)
|
|
|
|
continue
|
2022-08-09 02:04:21 +00:00
|
|
|
line_without_spaces = ''.join(words)
|
|
|
|
total_size = self.get_text_size(font_filename, font_size,
|
2022-08-15 23:08:51 +00:00
|
|
|
line_without_spaces, stroke_size=stroke_size)
|
2022-08-09 02:04:21 +00:00
|
|
|
space_width = (box_width - total_size[0]) / (len(words) - 1.0)
|
|
|
|
start_x = x
|
|
|
|
for word in words[:-1]:
|
2022-08-20 22:18:44 +00:00
|
|
|
if not calculate_only:
|
|
|
|
self.write_text((start_x, height), word, font_filename,
|
|
|
|
font_size, color, stroke_size=stroke_size)
|
2022-08-09 02:04:21 +00:00
|
|
|
word_size = self.get_text_size(font_filename, font_size,
|
2022-08-15 23:08:51 +00:00
|
|
|
word, stroke_size=stroke_size)
|
2022-08-09 02:04:21 +00:00
|
|
|
start_x += word_size[0] + space_width
|
|
|
|
last_word_size = self.get_text_size(font_filename, font_size,
|
2022-08-15 23:08:51 +00:00
|
|
|
words[-1], stroke_size=stroke_size)
|
2022-08-09 02:04:21 +00:00
|
|
|
last_word_x = x + box_width - last_word_size[0]
|
2022-08-20 22:18:44 +00:00
|
|
|
if not calculate_only:
|
|
|
|
self.write_text((last_word_x, height), words[-1], font_filename,
|
|
|
|
font_size, color, stroke_color=stroke_color, stroke_size=stroke_size)
|
2022-08-09 02:04:21 +00:00
|
|
|
height += text_height
|
2022-08-12 23:13:18 +00:00
|
|
|
if total_size[0] > max_x_size:
|
|
|
|
max_x_size = total_size[0]
|
|
|
|
return (total_size[0], height - y, box_width, max_x_size)
|