# Basically the same as cover.py with some minor differences from PIL import Image, ImageDraw, ImageFont from io import BytesIO import textwrap import requests import logging 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("RGBA", (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, "PNG") output.name = 'cover.png' # writing left the cursor at the end of the file, so reset it output.seek(0) return output def get_image_from_url(url: str): """ Basically the same as make_cover_from_url() """ try: if url.startswith("https://www.filepicker.io/api/"): logger.warning("Filepicker.io image detected, converting to Fiction.live image. This might fail.") url = f"https://cdn3.fiction.live/fp/{url.split('/')[-1]}?&quality=95" img = requests.Session().get(url) logger.info("Downloading image from " + url) cover = BytesIO(img.content) img_format = Image.open(cover).format # The `Image.open` read a few bytes from the stream to work out the # format, so reset it: cover.seek(0) if img_format != "PNG": cover = _convert_to_png(cover) except Exception as e: logger.info("Encountered an error downloading cover: " + str(e)) cover = make_image("There was a problem downloading this image.") 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 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__': f = make_image( 'Test of a Title which is quite long and will require multiple lines') with open('output.png', 'wb') as out: out.write(f.read())