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, stroke_size=0): 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, stroke_size=stroke_size) 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, stroke_size=stroke_size) def write_text(self, xy, text, font_filename, font_size=11, color=(0, 0, 0), max_width=None, max_height=None, stroke_color = None, stroke_size = 0): 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, stroke_size=stroke_size) text_size = self.get_text_size(font_filename, font_size, text, stroke_size=stroke_size) 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, stroke_width=stroke_size, stroke_fill=stroke_color) return text_size def get_text_size(self, font_filename, font_size, text, stroke_size = 0): font = ImageFont.truetype(font_filename, font_size) return font.getsize(text, stroke_width=stroke_size) 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, stroke_color = None, stroke_size = 0, calculate_only = 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, stroke_size=stroke_size) 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 max_x_size = -1 for index, line in enumerate(lines): if place == 'left': total_size = self.get_text_size(font_filename, font_size, line) if not calculate_only: self.write_text((x, height), line, font_filename, font_size, color, stroke_color=stroke_color, stroke_size=stroke_size) elif place == 'right': total_size = self.get_text_size(font_filename, font_size, line) x_left = x + box_width - total_size[0] if not calculate_only: self.write_text((x_left, height), line, font_filename, font_size, color, stroke_color=stroke_color, stroke_size=stroke_size) elif place == 'center': total_size = self.get_text_size(font_filename, font_size, line) x_left = int(x + ((box_width - total_size[0]) / 2)) if not calculate_only: self.write_text((x_left, height), line, font_filename, font_size, color, stroke_color=stroke_color, stroke_size=stroke_size) elif place == 'justify': words = line.split() 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 line_without_spaces = ''.join(words) total_size = self.get_text_size(font_filename, font_size, line_without_spaces, stroke_size=stroke_size) space_width = (box_width - total_size[0]) / (len(words) - 1.0) start_x = x for word in words[:-1]: if not calculate_only: self.write_text((start_x, height), word, font_filename, font_size, color, stroke_size=stroke_size) word_size = self.get_text_size(font_filename, font_size, word, stroke_size=stroke_size) start_x += word_size[0] + space_width last_word_size = self.get_text_size(font_filename, font_size, words[-1], stroke_size=stroke_size) last_word_x = x + box_width - last_word_size[0] 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) height += text_height if total_size[0] > max_x_size: max_x_size = total_size[0] return (total_size[0], height - y, box_width, max_x_size)