You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
145 lines
3.8 KiB
145 lines
3.8 KiB
from abc import ABCMeta, abstractmethod |
|
from PIL import Image, ImageDraw, ImageFont |
|
import typing |
|
|
|
from pseudbot.pastas import concat_pasta |
|
from pseudbot.util import styled_wrap, text_wrap |
|
|
|
|
|
class Meme(metaclass=ABCMeta): |
|
fonts = { |
|
"old_console": "font/clacon.ttf", |
|
"phone_sans": "font/Cantarell-VF.otf", |
|
} |
|
styles = {} |
|
style_methods = {} |
|
offsets = {} |
|
error = False |
|
image = None |
|
reason = "" |
|
post = None |
|
posts = None |
|
|
|
def __init__( |
|
self, |
|
top_text: dict = None, |
|
bottom_text: str = None, |
|
): |
|
self.top_text = top_text |
|
self.bottom_text = bottom_text |
|
|
|
def init_style_posts(self): |
|
self.check_posts() |
|
self.check_style() |
|
|
|
def check_posts(self): |
|
if not "posts" in self.top_text or len(self.top_text["posts"]) < 1: |
|
self.error = True |
|
self.reason = 'No "posts" in top_text!' |
|
else: |
|
self.posts = self.top_text["posts"] |
|
self.post = self.top_text["posts"][0] |
|
|
|
if not "screen_name" in self.post: |
|
self.post["screen_name"] = "⁉" |
|
if not "text" in self.post: |
|
self.post["text"] = concat_pasta() |
|
if not "timestamp" in self.post: |
|
self.post["timestamp"] = "Today at time" |
|
|
|
def check_style(self): |
|
if self.style not in self.styles: |
|
self.error = True |
|
self.reason = "Invalid style!" |
|
elif self.style not in self.style_methods: |
|
self.error = True |
|
self.reason = "Style not implemented!" |
|
|
|
@abstractmethod |
|
def help(self) -> str: |
|
""" |
|
User-callable help for meme generator. |
|
""" |
|
pass |
|
|
|
@abstractmethod |
|
def mk_pixmap(self): |
|
pass |
|
|
|
def get_styles(self) -> [str]: |
|
return self.styles |
|
|
|
def help(self) -> str: |
|
""" |
|
User-callable help for meme generator. |
|
""" |
|
return "TODO" |
|
|
|
def get_text(self) -> tuple: |
|
return (self.top_text, self.bottom_text) |
|
|
|
def get_pixmap(self): |
|
if self.image is None: |
|
if self.error is True: |
|
return self.error_pixmap(reason=self.reason) |
|
else: |
|
self.mk_pixmap() |
|
|
|
return self.image |
|
|
|
def draw_styled_text( |
|
self, img: Image, text: str, style: dict, offset: tuple = None |
|
) -> Image: |
|
if offset is None: |
|
offset = style["offset"] |
|
|
|
if "wrap" in style: |
|
text = styled_wrap(text, style["wrap"]) |
|
|
|
align = style["align"] if "align" in style else "left" |
|
|
|
image = self.draw_text( |
|
img=img, |
|
text=text, |
|
offset=offset, |
|
size=style["size"], |
|
fill=style["color"], |
|
variation=style["style"], |
|
fontname=style["font"], |
|
align=align, |
|
) |
|
return image |
|
|
|
def draw_text( |
|
self, |
|
img: Image, |
|
text: str, |
|
offset: tuple, |
|
size: int, |
|
fill: tuple, |
|
variation: str = "Regular", |
|
fontname: str = "phone_sans", |
|
align="left", |
|
) -> Image: |
|
font = ImageFont.truetype(self.fonts[fontname], size) |
|
font.set_variation_by_name(variation) |
|
|
|
d = ImageDraw.Draw(img) |
|
d.text(xy=offset, text=text, fill=fill, font=font, align=align) |
|
|
|
return img |
|
|
|
def error_pixmap(self, reason: str) -> Image: |
|
efont = ImageFont.truetype("font/clacon.ttf", 70) |
|
|
|
reason = text_wrap(text=reason, width=16, height=5) |
|
|
|
with Image.open("img/pseud-error.png").convert("RGBA") as base_img: |
|
text_img = Image.new("RGBA", base_img.size, (255, 255, 255, 0)) |
|
d = ImageDraw.Draw(text_img) |
|
|
|
d.text((292, 466), reason, font=efont, fill=(0, 0, 0, 255)) |
|
|
|
out = Image.alpha_composite(base_img, text_img) |
|
|
|
return out
|
|
|