|
from PIL import Image, ImageDraw, ImageFont,ImageStat,ImageFilter,ImageEnhance |
|
import constants |
|
import os |
|
|
|
def wrap_text(draw, text, font, max_width): |
|
words = text.split() |
|
lines = [] |
|
current_line = words[0] |
|
|
|
for word in words[1:]: |
|
test_line = current_line + " " + word |
|
if draw.textlength(test_line, font) <= max_width: |
|
current_line = test_line |
|
else: |
|
lines.append(current_line) |
|
current_line = word |
|
|
|
lines.append(current_line) |
|
return lines |
|
|
|
def draw_text_centered(draw, lines, position, font, max_width, padding, fill='yellow'): |
|
y = position[1] |
|
|
|
for line in lines: |
|
text_width = draw.textlength(line, font) |
|
x = position[0] + (max_width - text_width) // 2 |
|
draw.text((x, y), line, font=font, fill=fill) |
|
y += font.getsize('hg')[1] + padding |
|
|
|
def get_wrapped_text_size(draw, lines, font, padding): |
|
line_height = font.getsize('hg')[1] |
|
total_height = len(lines) * (line_height + padding) - padding |
|
max_line_width = max(draw.textlength(line, font) for line in lines) |
|
return total_height, max_line_width |
|
|
|
def dynamically_adjust_font(draw, text, font, max_width, max_height, padding): |
|
lines = wrap_text(draw, text, font, max_width) |
|
total_height, _ = get_wrapped_text_size(draw, lines, font, padding) |
|
while total_height > max_height and font.size > 10: |
|
font = ImageFont.truetype(font.path, font.size - 1) |
|
lines = wrap_text(draw, text, font, max_width) |
|
total_height, _ = get_wrapped_text_size(draw, lines, font, padding) |
|
return font, lines |
|
|
|
|
|
def is_image_dark(image,threshold=128): |
|
"""Determine if the image is predominantly dark, light or in the middle and return a suitable color for overlay""" |
|
grayscale = image.convert('L') |
|
stat = ImageStat.Stat(grayscale) |
|
avg_brightness = stat.mean[0] |
|
|
|
if avg_brightness < threshold: |
|
return ('dark','yellow') |
|
else: |
|
return ('light','red') |
|
|
|
|
|
def add_text_to_image(image_path, text, is_title=True, save_to=None): |
|
|
|
image = Image.open(image_path) |
|
|
|
|
|
resized_image = image.resize((360, 740)) |
|
|
|
|
|
image_width, image_height = resized_image.size |
|
|
|
|
|
image_brightness_level = is_image_dark(resized_image) |
|
|
|
|
|
draw = ImageDraw.Draw(resized_image) |
|
|
|
|
|
font_paths = { |
|
"weaselic" : os.path.join(constants.FONT_BASE_DIR,'Weaselic.ttf'), |
|
"black": os.path.join(constants.FONT_BASE_DIR,'Roboto/Roboto-Black.ttf'), |
|
"bold": os.path.join(constants.FONT_BASE_DIR,'Roboto/Roboto-Bold.ttf'), |
|
"medium": os.path.join(constants.FONT_BASE_DIR,'Roboto/Roboto-Medium.ttf'), |
|
"light": os.path.join(constants.FONT_BASE_DIR,'Roboto/Roboto-Light.ttf'), |
|
"thin": os.path.join(constants.FONT_BASE_DIR,'Roboto/Roboto-Thin.ttf') |
|
} |
|
|
|
|
|
fonts = { |
|
"weaselic" : ImageFont.truetype(font_paths['weaselic'], 43), |
|
"black": ImageFont.truetype(font_paths['black'], 40), |
|
"bold": ImageFont.truetype(font_paths['bold'], 35), |
|
"medium": ImageFont.truetype(font_paths['medium'], 40), |
|
"light": ImageFont.truetype(font_paths['light'], 30), |
|
"thin": ImageFont.truetype(font_paths['thin'], 30) |
|
} |
|
|
|
padding = 5 |
|
margin_between = 50 |
|
safe_margin = 10 |
|
|
|
if is_title: |
|
font = fonts["bold"] |
|
max_width = image_width - 2 * safe_margin |
|
max_height = (image_height - 2 * safe_margin) // 2 |
|
|
|
|
|
font, lines = dynamically_adjust_font(draw, text, font, max_width, max_height, padding) |
|
|
|
|
|
total_height, _ = get_wrapped_text_size(draw, lines, font, padding) |
|
|
|
|
|
if total_height > image_height - 2 * safe_margin: |
|
print("Text does not fit within the image boundaries.") |
|
else: |
|
position = (safe_margin, safe_margin + (image_height - total_height) // 2 - 120) |
|
|
|
|
|
rect_x0 = safe_margin |
|
rect_x1 = image_width - safe_margin |
|
rect_y0 = position[1] - padding |
|
rect_y1 = rect_y0 + total_height + 2 * padding |
|
draw.rectangle([(rect_x0, rect_y0), (rect_x1, rect_y1)], fill="yellow") |
|
|
|
|
|
draw_text_centered(draw, lines, (safe_margin, rect_y0 + padding), font, rect_x1 - rect_x0, padding, fill='black') |
|
|
|
else: |
|
font = fonts["weaselic"] |
|
max_width = image_width - 2 * safe_margin |
|
max_height = (image_height - 2 * safe_margin) // 2 |
|
|
|
|
|
font, lines = dynamically_adjust_font(draw, text, font, max_width, max_height, padding) |
|
|
|
|
|
total_height, _ = get_wrapped_text_size(draw, lines, font, padding) |
|
|
|
|
|
if total_height > image_height - 2 * safe_margin: |
|
print("Text does not fit within the image boundaries.") |
|
else: |
|
position = (safe_margin, safe_margin + (image_height - total_height) // 2) |
|
|
|
|
|
description_color = image_brightness_level[1] |
|
draw_text_centered(draw, lines, position, font, max_width, padding,fill = description_color) |
|
|
|
|
|
if save_to: |
|
resized_image.save(save_to) |
|
|
|
|
|
if __name__ == '__main__': |
|
title_text = "Top 5 mountains in the world" |
|
description_text = "Mount Everest" |
|
|
|
add_text_to_image('wow.png', title_text, is_title=True, save_to='output_title.png') |
|
add_text_to_image('wow.png', description_text, is_title=False, save_to='output_description.png') |
|
|