From 60d12267f3d9dcba8a930e8e08332b415ccadc6c Mon Sep 17 00:00:00 2001 From: HeyMoon <101842728+UnironicHeyMoon@users.noreply.github.com> Date: Mon, 15 Aug 2022 18:08:51 -0500 Subject: [PATCH] fix strokes --- image_utils.py | 24 ++-- pillow_fight.py | 312 ------------------------------------------------ 2 files changed, 12 insertions(+), 324 deletions(-) delete mode 100644 pillow_fight.py diff --git a/image_utils.py b/image_utils.py index 4a0d618..feb5929 100644 --- a/image_utils.py +++ b/image_utils.py @@ -26,11 +26,11 @@ class ImageText(object): def get_image(self): return self.image - def get_font_size(self, text, font, max_width=None, max_height=None): + def get_font_size(self, text, font, max_width=None, max_height=None, stroke_size=0): if max_width is None and max_height is None: raise ValueError('You need to pass max_width or max_height') font_size = 1 - text_size = self.get_text_size(font, font_size, text) + text_size = self.get_text_size(font, font_size, text, stroke_size=stroke_size) if (max_width is not None and text_size[0] > max_width) or \ (max_height is not None and text_size[1] > max_height): raise ValueError("Text can't be filled in only (%dpx, %dpx)" % \ @@ -40,7 +40,7 @@ class ImageText(object): (max_height is not None and text_size[1] >= max_height): return font_size - 1 font_size += 1 - text_size = self.get_text_size(font, font_size, text) + text_size = self.get_text_size(font, font_size, text, stroke_size=stroke_size) def write_text(self, xy, text, font_filename, font_size=11, color=(0, 0, 0), max_width=None, max_height=None, stroke_color = None, stroke_size = 0): @@ -53,8 +53,8 @@ class ImageText(object): if font_size == 'fill' and \ (max_width is not None or max_height is not None): font_size = self.get_font_size(text, font_filename, max_width, - max_height) - text_size = self.get_text_size(font_filename, font_size, text) + max_height, stroke_size=stroke_size) + text_size = self.get_text_size(font_filename, font_size, text, stroke_size=stroke_size) font = ImageFont.truetype(font_filename, font_size) if x == 'center': x = (self.size[0] - text_size[0]) / 2 @@ -63,9 +63,9 @@ class ImageText(object): self.draw.text((x, y), text, font=font, fill=color, stroke_width=stroke_size, stroke_fill=stroke_color) return text_size - def get_text_size(self, font_filename, font_size, text): + def get_text_size(self, font_filename, font_size, text, stroke_size = 0): font = ImageFont.truetype(font_filename, font_size) - return font.getsize(text) + return font.getsize(text, stroke_width=stroke_size) def fill_text_box(self, xy, text, box_width, box_height, font_filename, start_font_size=1, color=(0,0,0), place='left', @@ -96,7 +96,7 @@ class ImageText(object): words = text.split() for word in words: new_line = ' '.join(line + [word]) - size = self.get_text_size(font_filename, font_size, new_line) + size = self.get_text_size(font_filename, font_size, new_line, stroke_size=stroke_size) text_height = size[1] if size[0] <= box_width: line.append(word) @@ -133,17 +133,17 @@ class ImageText(object): continue line_without_spaces = ''.join(words) total_size = self.get_text_size(font_filename, font_size, - line_without_spaces) + line_without_spaces, stroke_size=stroke_size) space_width = (box_width - total_size[0]) / (len(words) - 1.0) start_x = x for word in words[:-1]: self.write_text((start_x, height), word, font_filename, - font_size, color) + font_size, color, stroke_size=stroke_size) word_size = self.get_text_size(font_filename, font_size, - word) + word, stroke_size=stroke_size) start_x += word_size[0] + space_width last_word_size = self.get_text_size(font_filename, font_size, - words[-1]) + words[-1], stroke_size=stroke_size) last_word_x = x + box_width - last_word_size[0] self.write_text((last_word_x, height), words[-1], font_filename, font_size, color, stroke_color=stroke_color, stroke_size=stroke_size) diff --git a/pillow_fight.py b/pillow_fight.py deleted file mode 100644 index 072186d..0000000 --- a/pillow_fight.py +++ /dev/null @@ -1,312 +0,0 @@ -import math -from typing import Union -from PIL import Image -from PIL import ImageDraw -from PIL import ImageFont -from PIL import ImageOps -import requests -import io -from image_utils import ImageText -from os.path import exists - -TEST_MODE = True - -class ColorScheme: - BLACK = 0 - WHITE_WITH_BLACK_BORDER = 1 - -def create_soy_vs_chad_meme(emoji1, emoji2, caption1, caption2): - LEFT_MARGIN_COLUMN = 20 - CONTENT_COLUMN = 300 - MIDDLE_MARGIN_COLUMN = 80 - RIGHT_MARGIN_COLUMN = LEFT_MARGIN_COLUMN - TOP_MARGIN_ROW = 80 - IMAGE_ROW = 300 - MIDDLE_MARGIN_ROW = 20 - TEXT_ROW = 100 - BOTTOM_MARGIN_ROW = 200 - - total_image_size_x = 2*CONTENT_COLUMN + LEFT_MARGIN_COLUMN + RIGHT_MARGIN_COLUMN + MIDDLE_MARGIN_COLUMN - total_image_size_y = TOP_MARGIN_ROW + IMAGE_ROW + MIDDLE_MARGIN_ROW + TEXT_ROW + BOTTOM_MARGIN_ROW - - left_image = get_emoji_from_rdrama(emoji1) - right_image = get_emoji_from_rdrama(emoji2) - - #Resize images - left_image = ImageOps.contain(left_image, (CONTENT_COLUMN, IMAGE_ROW)) - right_image = ImageOps.contain(right_image, (CONTENT_COLUMN, IMAGE_ROW)) - - right_image = ImageOps.mirror(right_image) - - #Base image - base = Image.new(mode="RGB", size=(total_image_size_x,total_image_size_y), color=(255,255,255)) - - #Add images - center_and_paste(base, left_image, (LEFT_MARGIN_COLUMN,TOP_MARGIN_ROW), (CONTENT_COLUMN, IMAGE_ROW)) - center_and_paste(base, right_image, (LEFT_MARGIN_COLUMN+CONTENT_COLUMN+MIDDLE_MARGIN_COLUMN,TOP_MARGIN_ROW), (CONTENT_COLUMN, IMAGE_ROW)) - - #Text regions - add_text_box(base, - caption1, - (CONTENT_COLUMN, TEXT_ROW), - (LEFT_MARGIN_COLUMN, TOP_MARGIN_ROW+IMAGE_ROW+MIDDLE_MARGIN_ROW), - init_font_size=50, - align="cht", - font="impact.ttf", - color=ColorScheme.WHITE_WITH_BLACK_BORDER) - add_text_box(base, - caption2, - (CONTENT_COLUMN, TEXT_ROW), - (LEFT_MARGIN_COLUMN+CONTENT_COLUMN+MIDDLE_MARGIN_COLUMN, TOP_MARGIN_ROW+IMAGE_ROW+MIDDLE_MARGIN_ROW), - init_font_size=50, - align="cht", - font="impact.ttf", - color=ColorScheme.WHITE_WITH_BLACK_BORDER) - return add_watermark(base) - -class WebcomicPanel(): - PANEL_SIZE = 400 - FONT = "Little Story.ttf" - - def __init__(self): - pass - - def create_image(self) -> Image: - return Image.new(mode="RGB", size=(self.PANEL_SIZE,self.PANEL_SIZE), color=(255,255,255)) - -class OneCharacterWebcomicPanel(WebcomicPanel): - def __init__(self, emoji, caption, words_in_background): - self.emoji = emoji - self.caption = caption - self.words_in_background = words_in_background - - def create_image(self) -> Image: - base = super().create_image() - panel_size_x, panel_size_y = base.size - - # We put the text in the background of the panel. - text_region_x_size = int(panel_size_x) - text_region_y_size = int(panel_size_y if self.words_in_background else panel_size_y/2) - add_text(base, self.caption, (text_region_x_size, text_region_y_size), (0,0), font=super().FONT) - - # We put marsey in the bottom left quadrant - emoji_region_x_size = int(panel_size_x) - emoji_region_y_size = int(panel_size_y/2) - emoji_placement_position_x = 0 - emoji_placement_position_y = int(panel_size_y/2) - emoji = get_emoji_from_rdrama(self.emoji) - center_and_paste(base, emoji, (emoji_placement_position_x, emoji_placement_position_y), (emoji_region_x_size, emoji_region_y_size)) - - return add_border_to_image(base) - -class TwoCharacterWebcomicPanel(WebcomicPanel): - def __init__(self, left_emoji, left_caption, right_emoji, right_caption): - self.left_emoji = left_emoji - self.left_caption = left_caption - self.right_emoji = right_emoji - self.right_caption = right_caption - - def create_image(self) -> Image: - base = super().create_image() - panel_size_x, panel_size_y = base.size - - # We put the left marsey in the bottom left quadrant - left_emoji_region_x_size = int(panel_size_x/2) - left_emoji_region_y_size = int(panel_size_y/2) - left_emoji_placement_position_x = 0 - left_emoji_placement_position_y = int(panel_size_y/2) - left_emoji = get_emoji_from_rdrama(self.left_emoji) - center_and_paste(base, left_emoji, (left_emoji_placement_position_x, left_emoji_placement_position_y), (left_emoji_region_x_size, left_emoji_region_y_size)) - - # We put the right marsey in the bottom right quadrant - right_emoji_region_x_size = int(panel_size_x/2) - right_emoji_region_y_size = int(panel_size_y/2) - right_emoji_placement_position_x = int(panel_size_x/2) - right_emoji_placement_position_y = int(panel_size_y/2) - right_emoji = get_emoji_from_rdrama(self.right_emoji) - center_and_paste(base, right_emoji, (right_emoji_placement_position_x, right_emoji_placement_position_y), (right_emoji_region_x_size, right_emoji_region_y_size)) - - #Each text caption will get 5/8 of the width, and 1/4 of the height of the panel. - #The left caption will be all the way to the left. The right caption will be 3/8 to the right. - - CAPTION_UNITS = 5 - CAPTION_DIVISOR = 8 - - # We put the text in the top half of the panel. - left_text_region_x_size = int(CAPTION_UNITS*(panel_size_x/CAPTION_DIVISOR)) - left_text_region_y_size = int(panel_size_y/4) - left_text_region_x_position = 0 - left_text_region_y_position = 0 - add_text_box(base, self.left_caption, (left_text_region_x_size, left_text_region_y_size), (left_text_region_x_position,left_text_region_y_position), font=super().FONT, align="bl") - - # We put the text in the top half of the panel. - right_text_region_x_size = int(CAPTION_UNITS*(panel_size_x/CAPTION_DIVISOR)) - right_text_region_y_size = int(panel_size_y/4) - right_text_region_x_position = int((CAPTION_DIVISOR-CAPTION_UNITS)*(panel_size_x/8)) - right_text_region_y_position = int(panel_size_y/4) - add_text_box(base, self.right_caption, (right_text_region_x_size, right_text_region_y_size), (right_text_region_x_position,right_text_region_y_position), font=super().FONT, align="br") - - return add_border_to_image(base) - -class TitleCardWebcomicPanel(WebcomicPanel): - def __init__(self, caption): - self.caption = caption - - def create_image(self) -> Image: - base = super().create_image() - - add_text_box(base, self.caption, base.size, (0,0), font=super().FONT, init_font_size=90, align="cvch") - - return add_border_to_image(base) - -def create_webcomic(layout : 'list[WebcomicPanel]'): - assumed_panel_x_size, assumed_panel_y_size = layout[0].create_image().size - total_image_x_size = assumed_panel_x_size * 2 - total_image_y_size = assumed_panel_x_size * math.ceil(len(layout)/2) - - image = Image.new(mode="RGB", size=(total_image_x_size, total_image_y_size), color=(255,255,255)) - for i in range(len(layout)): - panel = layout[i] - x = i%2 - y = math.floor(i/2) - image.paste(panel.create_image(), (x*assumed_panel_x_size, y*assumed_panel_y_size)) - return add_watermark(image) - -def add_text_box(base : Image, caption : str, region_size : tuple[int, int], coordinates : tuple[int, int], font : str= "arial.ttf", init_font_size = 45, align :str = "", color = ColorScheme.BLACK): - if caption == "": - return - if init_font_size == 0: - print("Retard moment") - return - region_x_size, region_y_size = region_size - line_image = ImageText((region_x_size, region_y_size)) - if color == ColorScheme.BLACK: - fill_color = (0,0,0) - stroke = None - stroke_size = 0 - if color == ColorScheme.WHITE_WITH_BLACK_BORDER: - fill_color = (255,255,255) - stroke = (0,0,0) - stroke_size = 3 - actual_text_box_size = line_image.write_text_box((0,0), caption, region_x_size, font_size=init_font_size, font_filename=font, color=fill_color, stroke_color=stroke, stroke_size=stroke_size) - _, actual_text_box_y_size, input_text_block_x_size, actual_text_box_x_size = actual_text_box_size - if actual_text_box_y_size <= region_y_size: - actual_paste_x_coordinates, actual_paste_y_coordinates = coordinates - if align != "": - y_difference = region_y_size - actual_text_box_y_size - x_difference = region_x_size - actual_text_box_x_size - # Horizontal Align - if "t" in align: - #This is the default - pass - elif "b" in align: - actual_paste_y_coordinates+=y_difference - elif "cv" in align: - actual_paste_y_coordinates += int(y_difference/2) - - if "l" in align: - #This is the default - pass - elif "r" in align: - - actual_paste_x_coordinates+=x_difference - elif "ch" in align: - actual_paste_x_coordinates += int(x_difference/2) - - if TEST_MODE: - image_draw = ImageDraw.Draw(base) - image_draw.rectangle((coordinates, (coordinates[0]+region_size[0], coordinates[1]+region_size[1])), fill="blue") - image_draw.rectangle(((actual_paste_x_coordinates, actual_paste_y_coordinates), (actual_paste_x_coordinates+actual_text_box_x_size, actual_text_box_y_size+actual_paste_y_coordinates)), fill="tan") - - base.paste(line_image.image, (actual_paste_x_coordinates, actual_paste_y_coordinates), line_image.image) - else: - add_text_box(base, caption, region_size, coordinates, font=font, init_font_size=init_font_size-1, align=align, color=color) - -def add_text(base : Image, caption : str, region_size : tuple[int, int], coordinates : tuple[int, int], font : str= "arial.ttf"): - if caption == "": - return - region_x_size, region_y_size = region_size - line_image = ImageText((region_x_size, region_y_size)) - line_image.fill_text_box((0,0), caption, region_x_size, region_y_size, font_filename=font) - base.paste(line_image.image, coordinates, line_image.image) - -def add_watermark(image : Image): - WATERMARK_HEIGHT = 30 - image_size_x, image_size_y = image.size - base = Image.new(mode="RGB", size=(image_size_x, image_size_y + WATERMARK_HEIGHT), color=(255,255,255)) - base.paste(image) - - marsey = get_emoji_from_rdrama("marseyjamming") - marsey = ImageOps.contain(marsey, (WATERMARK_HEIGHT, WATERMARK_HEIGHT)) - base.paste(marsey, (0, image_size_y), marsey) - - text_line_size = int(WATERMARK_HEIGHT/2) - - add_text(base, "A meme by HeyMoon and Foo", (image_size_x, text_line_size), (WATERMARK_HEIGHT, image_size_y)) - add_text(base, "For instructions on how to legally build a pipe bomb, go to rdrama.net", (image_size_x, text_line_size), (WATERMARK_HEIGHT, image_size_y+text_line_size)) - - return base - -def center_and_paste(base : Image, to_paste : Image, coordinates: tuple[int, int], box_size : tuple[int, int]): - to_paste = ImageOps.contain(to_paste, box_size) - - image_size_x, image_size_y = to_paste.size - box_size_x, box_size_y = box_size - - extra_space_x = box_size_x - image_size_x - extra_space_y = box_size_y - image_size_y - - offset_x = int(extra_space_x/2) - offset_y = int(extra_space_y/2) - - box_coordinate_x, box_coordinate_y = coordinates - - x, y = offset_x + box_coordinate_x, offset_y + box_coordinate_y - - base.paste(to_paste, (x, y), to_paste) - -def add_border_to_image(image : Image, thickness : int = 5): - inner_image_x_size, inner_image_y_size = image.size - outer_image_x_size, outer_image_y_size = inner_image_x_size + 2*thickness, inner_image_y_size + 2*thickness - outside_image = Image.new(mode="RGB", size=(outer_image_x_size,outer_image_y_size), color=(0,0,0)) - - outside_image.paste(image, (thickness, thickness)) - return outside_image - -def wrap_text_for_font(text, region_size, font_size) -> Image: - image = Image.new(mode="RGB", size=region_size, color=(255,255,255)) - imageDraw = ImageDraw.ImageDraw() - imageDraw.multiline_text() - -def get_emoji_from_rdrama(emoji_name): - cleaned_emoji_name : str = emoji_name - should_flip = False - if '!' in emoji_name: - cleaned_emoji_name = cleaned_emoji_name.replace("!", "") - should_flip = True - - if (exists(f"emoji_cache/{cleaned_emoji_name}.webp")): - image = Image.open(f"emoji_cache/{cleaned_emoji_name}.webp") - else: - image = get_image_file_from_url(f"https://www.rdrama.net/e/{cleaned_emoji_name}.webp") - image.save(f"emoji_cache/{cleaned_emoji_name}.webp") - if should_flip: - image = ImageOps.mirror(image) - return image - -def get_image_file_from_url(url): - r = requests.get(url) - image_file = io.BytesIO(r.content) - im = Image.open(image_file) - return im - -create_soy_vs_chad_meme("bigsmilesoyjak", "!marseyshooting", "I have fun new toys and games for your children", "Die").show() - -# create_webcomic([ -# TwoCharacterWebcomicPanel("soyjak", "Black people deserve the rope", "marseyconfused", "Why?"), -# TwoCharacterWebcomicPanel("seethejak", "Because they are degenerate!!!", "marseysmug", "Kinda like you?"), -# OneCharacterWebcomicPanel("soycry", "No!! I am a pure aryan white boy and I am special Hitler told me so, so fuck off you nigger cunt. God I hate you so much, if I commit suicide its gonna be your fault, fuck you, its not fair, why do you always have to bully me around, you are such a bitch, i hate you so much, hitler was right and six million wasnt nearly enough, the jews are responsible for this and they will be punished on the day of the rope, youll be sorry then, plus i am armed and dangerous and i have a gun and i will shoot you if you keep making fun of me plus youll be in troulbe if I kill myself so how do you like that you stupid nigger bitch. fuck", words_in_background=True), -# TitleCardWebcomicPanel("One Hour Later..."), -# TwoCharacterWebcomicPanel("marseytombstone", "", "!marseycry", "He had so much to live for"), -# OneCharacterWebcomicPanel("marseylaugh", "Just Kidding!", False) -# ]).show() \ No newline at end of file