diff --git a/src/cogs/meme.py b/src/cogs/meme.py index 31dafbc..1ec475d 100644 --- a/src/cogs/meme.py +++ b/src/cogs/meme.py @@ -70,39 +70,7 @@ class Meme(commands.Cog): await interaction.followup.send(file=discord.File(output, "deepfry.jpg")) @staticmethod - def __format_text(text: str, textbox_width: int) -> tuple[str, ImageFont.FreeTypeFont]: - """ - Formats text to fit in a textbox of a given width. - - Parameters - ---------- - text (str): Text to be formatted. - textbox_width (int): Width of the textbox. - - Returns - ---------- - tuple[str, ImageFont.FreeTypeFont]: The formatted text and the font, with its size set, to be used. - """ - - if len(text) <= 1: - font_size = 150 - else: - font_size = textbox_width // (len(text) // 6) - - if font_size < 20: - font_size = 20 - elif font_size > 150: - font_size = 150 - - font = ImageFont.truetype("./src/assets/fonts/RobotoMono-Medium.ttf", font_size) - - line_length = int(textbox_width // font_size * 1.5) - text = textwrap.fill(text, width=line_length) - - return text, font - - @staticmethod - def __draw_text(image: Image.Image, text: str, font: ImageFont.FreeTypeFont, offset: tuple = (0, 0)): + def __draw_text(image: Image.Image, text: str, offset: tuple = (0, 0)): """ Draws meme text on preferred meme template. @@ -115,13 +83,59 @@ class Meme(commands.Cog): """ box_size = (540, 540) - box = Image.new("RGB", box_size, (255, 255, 255)) draw = ImageDraw.Draw(box) - text_box = draw.multiline_textbbox( - (box_size[0], box_size[1]), text=text, font=font, align="center", anchor="mm" - ) + font_path = "./src/assets/fonts/RobotoMono-Medium.ttf" + + # Calculate initial font size + # This is done by increasing the font size until the text is too wide + max_width, max_height = box_size[0] * 0.9, box_size[1] * 0.9 # 90% of the box size + + font_size = 20 # Initiale size. I consider this the smallest readable size + font_width = 0 # Initiale width. This is just to make sure the while loop runs at least once + while font_width < max_width: + font = ImageFont.truetype(font_path, font_size) + + font_box = font.getbbox(text) + font_width = font_box[2] - font_box[0] + + font_size += 2 + + # Wrap the text based on our current font size + text = textwrap.fill(text, width=font_size) + + # We then have to calculate the font size again + # + # The problem is, font.getbbox() doesn't work with multiline text so it returns the size without the linebreaks + # We can get around this by calculating the size of the longest line in the text + # and then increasing the font size until that line is too wide + # + # We also have to make sure it doesn't get too tall + longest_line = max(text.split("\n"), key=len) + longest_line_font_box = font.getbbox(longest_line) + font_width = longest_line_font_box[2] - longest_line_font_box[0] + + # Estimate font height + # This is done by multiplying the height of the original non-wrapped text with the number of lines + # and then adding 20 to account for the line spacing. + # This of course is dependant on font size itself, but it's good enough for now + font_height = (font_box[3] - font_box[1] + 20) * text.count("\n") + max_height = box_size[1] * 0.9 + + while font_width < max_width and font_height < max_height: + font = ImageFont.truetype(font_path, font_size) + + longest_line_font_box = font.getbbox(longest_line) + font_width = longest_line_font_box[2] - longest_line_font_box[0] + + font_box = font.getbbox(text) + font_height = (font_box[3] - font_box[1] + 20) * text.count("\n") + + font_size += 2 + + # Draw textbox and text + text_box = draw.multiline_textbbox(box_size, text=text, font=font, align="center", anchor="mm") text_box = box.size[0] // 2, box.size[1] // 2 draw.multiline_text(text_box, text, font=font, fill=(0, 0, 0, 255), align="center", anchor="mm") @@ -146,13 +160,9 @@ class Meme(commands.Cog): # Fetch meme template template = Image.open("./src/assets/misc/sivert_goodbad.jpg") - # Calculate font sizes - top_text, top_font = Meme.__format_text(dårlig_tekst, 540) - bottom_text, bottom_font = Meme.__format_text(bra_tekst, 540) - # Draw text - Meme.__draw_text(template, top_text, top_font, offset=(540, 0)) - Meme.__draw_text(template, bottom_text, bottom_font, offset=(540, 540)) + Meme.__draw_text(template, dårlig_tekst, offset=(540, 0)) + Meme.__draw_text(template, bra_tekst, offset=(540, 540)) # Save image to buffer output = BytesIO()