1
0
Fork 0
mirror of https://github.com/kemayo/leech synced 2025-12-06 16:33:16 +01:00

Code style fixups, duplication cleanup, PIL textsize removal

This commit is contained in:
David Lynch 2024-11-23 14:22:10 -06:00
parent f557a48ac4
commit 542774543a
3 changed files with 58 additions and 92 deletions

View file

@ -1,9 +1,10 @@
from PIL import Image, ImageDraw, ImageFont from PIL import Image, ImageDraw
from io import BytesIO from io import BytesIO
import textwrap import textwrap
import requests import requests
import logging import logging
from . import image
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -15,14 +16,14 @@ def make_cover(title, author, width=600, height=800, fontname="Helvetica", fonts
title = textwrap.fill(title, wrapat) title = textwrap.fill(title, wrapat)
author = textwrap.fill(author, wrapat) author = textwrap.fill(author, wrapat)
font = _safe_font(fontname, size=fontsize) font = image._safe_font(fontname, size=fontsize)
title_size = textsize(draw, title, font=font) title_size = image.textsize(draw, title, font=font)
draw_text_outlined(draw, ((width - title_size[0]) / 2, 100), title, textcolor, font=font) image.draw_text_outlined(draw, ((width - title_size[0]) / 2, 100), title, textcolor, font=font)
# draw.text(((width - title_size[0]) / 2, 100), title, textcolor, font=font) # draw.text(((width - title_size[0]) / 2, 100), title, textcolor, font=font)
font = _safe_font(fontname, size=fontsize - 2) font = image._safe_font(fontname, size=fontsize - 2)
author_size = textsize(draw, author, font=font) author_size = image.textsize(draw, author, font=font)
draw_text_outlined(draw, ((width - author_size[0]) / 2, 100 + title_size[1] + 70), author, textcolor, font=font) image.draw_text_outlined(draw, ((width - author_size[0]) / 2, 100 + title_size[1] + 70), author, textcolor, font=font)
output = BytesIO() output = BytesIO()
img.save(output, "PNG") img.save(output, "PNG")
@ -44,7 +45,7 @@ def make_cover_from_url(url, title, author):
cover.seek(0) cover.seek(0)
if imgformat != "PNG": if imgformat != "PNG":
cover = _convert_to_png(cover) cover = image._convert_to_new_format(cover, "PNG")
except Exception as e: except Exception as e:
logger.info("Encountered an error downloading cover: " + str(e)) logger.info("Encountered an error downloading cover: " + str(e))
cover = make_cover(title, author) cover = make_cover(title, author)
@ -52,46 +53,6 @@ def make_cover_from_url(url, title, author):
return cover return cover
def _convert_to_png(image_bytestream):
png_image = BytesIO()
Image.open(image_bytestream).save(png_image, format="PNG")
png_image.name = 'cover.png'
png_image.seek(0)
return png_image
def _safe_font(preferred, *args, **kwargs):
for font in (preferred, "Helvetica", "FreeSans", "Arial"):
try:
return ImageFont.truetype(*args, font=font, **kwargs)
except IOError:
pass
# This is pretty terrible, but it'll work regardless of what fonts the
# system has. Worst issue: can't set the size.
return ImageFont.load_default()
def textsize(draw, text, **kwargs):
left, top, right, bottom = draw.multiline_textbbox((0, 0), text, **kwargs)
width, height = right - left, bottom - top
return width, height
def draw_text_outlined(draw, xy, text, fill=None, font=None, anchor=None):
x, y = xy
# Outline
draw.text((x - 1, y), text=text, fill=(0, 0, 0), font=font, anchor=anchor)
draw.text((x + 1, y), text=text, fill=(0, 0, 0), font=font, anchor=anchor)
draw.text((x, y - 1), text=text, fill=(0, 0, 0), font=font, anchor=anchor)
draw.text((x, y + 1), text=text, fill=(0, 0, 0), font=font, anchor=anchor)
# Fill
draw.text(xy, text=text, fill=fill, font=font, anchor=anchor)
if __name__ == '__main__': if __name__ == '__main__':
f = make_cover('Test of a Title which is quite long and will require multiple lines', 'Some Dude') f = make_cover('Test of a Title which is quite long and will require multiple lines', 'Some Dude')
with open('output.png', 'wb') as out: with open('output.png', 'wb') as out:

View file

@ -13,38 +13,6 @@ from typing import Tuple
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def make_image(
message: str,
width=600,
height=300,
fontname="Helvetica",
font_size=40,
bg_color=(0, 0, 0),
textcolor=(255, 255, 255),
wrap_at=30
):
"""
This function should only be called if get_image_from_url() fails
"""
img = Image.new("RGB", (width, height), bg_color)
draw = ImageDraw.Draw(img)
message = textwrap.fill(message, wrap_at)
font = _safe_font(fontname, size=font_size)
message_size = draw.textsize(message, font=font)
draw_text_outlined(
draw, ((width - message_size[0]) / 2, 100), message, textcolor, font=font)
# draw.text(((width - title_size[0]) / 2, 100), title, textcolor, font=font)
output = BytesIO()
img.save(output, "JPEG")
output.name = 'cover.jpeg'
# writing left the cursor at the end of the file, so reset it
output.seek(0)
return output
def get_size_format(b, factor=1000, suffix="B"): def get_size_format(b, factor=1000, suffix="B"):
""" """
Scale bytes to its proper byte format Scale bytes to its proper byte format
@ -159,34 +127,63 @@ def get_image_from_url(
image.seek(0) image.seek(0)
PIL_image = Image.open(image) PIL_image = Image.open(image)
img_format = str(PIL_image.format)
if img_format.lower() == "gif": if str(PIL_image.format).lower() == "gif":
PIL_image = Image.open(image) PIL_image = Image.open(image)
if PIL_image.info['version'] not in [b"GIF89a", "GIF89a"]: if PIL_image.info['version'] not in [b"GIF89a", "GIF89a"]:
PIL_image.info['version'] = b"GIF89a" PIL_image.info['version'] = b"GIF89a"
return PIL_Image_to_bytes(PIL_image, "GIF"), "gif", "image/gif" return PIL_Image_to_bytes(PIL_image, "GIF"), "gif", "image/gif"
if compress_images: if compress_images:
PIL_image = compress_image(image, max_image_size, img_format) PIL_image = compress_image(image, max_image_size, str(PIL_image.format))
return PIL_Image_to_bytes(PIL_image, image_format), image_format, f"image/{image_format.lower()}" return PIL_Image_to_bytes(PIL_image, image_format), image_format, f"image/{image_format.lower()}"
except Exception as e: except Exception as e:
logger.info("Encountered an error downloading image: " + str(e)) logger.info("Encountered an error downloading image: " + str(e))
cover = make_image("There was a problem downloading this image.").read() image = make_fallback_image("There was a problem downloading this image.").read()
return cover, "jpeg", "image/jpeg" return image, "jpeg", "image/jpeg"
def make_fallback_image(
message: str,
width=600,
height=300,
fontname="Helvetica",
font_size=40,
bg_color=(0, 0, 0),
textcolor=(255, 255, 255),
wrap_at=30
):
"""
This function should only be called if get_image_from_url() fails
"""
img = Image.new("RGB", (width, height), bg_color)
draw = ImageDraw.Draw(img)
message = textwrap.fill(message, wrap_at)
font = _safe_font(fontname, size=font_size)
message_size = textsize(draw, message, font=font)
draw_text_outlined(
draw, ((width - message_size[0]) / 2, 100), message, textcolor, font=font)
# draw.text(((width - title_size[0]) / 2, 100), title, textcolor, font=font)
output = BytesIO()
img.save(output, "JPEG")
# writing left the cursor at the end of the file, so reset it
output.seek(0)
return output
def _convert_to_new_format(image_bytestream, image_format: str): def _convert_to_new_format(image_bytestream, image_format: str):
new_image = BytesIO() new_image = BytesIO()
try: try:
Image.open(image_bytestream).save(new_image, format=image_format.upper()) Image.open(image_bytestream).save(new_image, format=image_format.upper())
new_image.name = f'cover.{image_format.lower()}'
new_image.seek(0) new_image.seek(0)
except Exception as e: except Exception as e:
logger.info(f"Encountered an error converting image to {image_format}\nError: {e}") logger.info(f"Encountered an error converting image to {image_format}\nError: {e}")
new_image = make_image("There was a problem converting this image.") new_image = make_fallback_image("There was a problem converting this image.")
return new_image return new_image
@ -202,6 +199,12 @@ def _safe_font(preferred, *args, **kwargs):
return ImageFont.load_default() return ImageFont.load_default()
def textsize(draw, text, **kwargs):
left, top, right, bottom = draw.multiline_textbbox((0, 0), text, **kwargs)
width, height = right - left, bottom - top
return width, height
def draw_text_outlined(draw, xy, text, fill=None, font=None, anchor=None): def draw_text_outlined(draw, xy, text, fill=None, font=None, anchor=None):
x, y = xy x, y = xy
@ -216,7 +219,9 @@ def draw_text_outlined(draw, xy, text, fill=None, font=None, anchor=None):
if __name__ == '__main__': if __name__ == '__main__':
f = make_image( f = make_fallback_image(
'Test of a Title which is quite long and will require multiple lines') 'Test of a Title which is quite long and will require multiple lines',
'output.png'
)
with open('output.png', 'wb') as out: with open('output.png', 'wb') as out:
out.write(f.read()) out.write(f.read())