Update Meme Generator project

This commit is contained in:
2026-01-04 14:03:10 -08:00
parent 155f0c9c6d
commit 433f2ba034
9 changed files with 66 additions and 28 deletions

View File

@@ -11,6 +11,23 @@ class MemeEngine:
"""Initialize meme engine with path to save generated memes.""" """Initialize meme engine with path to save generated memes."""
self.output_path = output_path self.output_path = output_path
def resize_image(self, img, width=500):
"""Resize image to specified width while maintaining aspect ratio."""
if img.size[0] > width:
ratio = width / (img.size[0] * 1.0)
height = ratio * img.size[1]
img = img.resize((int(width), int(height)), Image.NEAREST)
return img
def draw_text(self, image, message):
"""Draw text on the image."""
draw = ImageDraw.Draw(image)
font = ImageFont.truetype(
"./_data/font/calibri_regular.ttf", int(image.size[1] / 20)
)
draw.text((20, 20), message, font=font, fill="white")
return image
def make_meme(self, img_path, text, author, width=500) -> str: def make_meme(self, img_path, text, author, width=500) -> str:
"""Generate a meme image with given text and author.""" """Generate a meme image with given text and author."""
output_file = f"{self.output_path}/{random.randint(0, 10000)}.jpg" output_file = f"{self.output_path}/{random.randint(0, 10000)}.jpg"
@@ -18,17 +35,9 @@ class MemeEngine:
try: try:
with Image.open(img_path) as img: with Image.open(img_path) as img:
# Resize image while maintaining aspect ratio image_resize = self.resize_image(img, width=width)
width = 500 if img.size[0] > 500 else img.size[0] image_draw = self.draw_text(image_resize, message)
ratio = width / (img.size[0] * 1.0) image_draw.save(output_file)
height = ratio * img.size[1]
img = img.resize((int(width), int(height)), Image.NEAREST)
draw = ImageDraw.Draw(img)
font = ImageFont.truetype(
"./_data/font/calibri_regular.ttf", int(height / 20)
)
draw.text((20, 20), message, font=font, fill="white")
img.save(output_file)
except Exception as err: except Exception as err:
print(f"Error: {err}") print(f"Error: {err}")

View File

@@ -1 +1,6 @@
"""MemeEngine package.
Provides the MemeEngine class for generating meme images from templates.
"""
from .MemeEngine import MemeEngine from .MemeEngine import MemeEngine

View File

@@ -19,5 +19,6 @@ class IngestorInterface(ABC):
@classmethod @classmethod
@abstractmethod @abstractmethod
def parse(cls, path: str) -> List[QuoteModel]: def parse(cls, path: str) -> List[QuoteModel]:
"""Abstract method to parse the file and return a list of QuoteModel objects.""" """Abstract method to parse the file and return a list of QuoteModel
objects."""
pass pass

View File

@@ -23,7 +23,7 @@ class PDFIngestor(IngestorInterface):
tmp = f"./tmp/{random.randint(0, 10000)}.txt" tmp = f"./tmp/{random.randint(0, 10000)}.txt"
try: try:
# pdftotext <input-pdf> <output-text-file> # pdftotext <input-pdf> <output-text-file>
call = subprocess.call(["pdftotext", path, tmp]) subprocess.call(["pdftotext", path, tmp])
with open(tmp, "r") as file: with open(tmp, "r") as file:
lines = file.readlines() lines = file.readlines()
except FileNotFoundError as err: except FileNotFoundError as err:

View File

@@ -10,5 +10,5 @@ class QuoteModel:
self.author = author self.author = author
def __repr__(self): def __repr__(self):
"""String representation of the QuoteModel object.""" """Return a string representation of the QuoteModel."""
return f"{self.body} - {self.author}" return f"{self.body} - {self.author}"

View File

@@ -1,3 +1,8 @@
"""QuoteEngine package.
Provides tools for parsing and representing quote data.
"""
from .IngestorInterface import IngestorInterface from .IngestorInterface import IngestorInterface
from .CSVIngestor import CSVIngestor from .CSVIngestor import CSVIngestor
from .DocxIngestor import DocxIngestor from .DocxIngestor import DocxIngestor

View File

@@ -37,3 +37,16 @@ Open link `http://127.0.0.1:5000` in web browser
`DocxIngestor`: Module for ingesting Docx files containing quotes. \ `DocxIngestor`: Module for ingesting Docx files containing quotes. \
`TextIngestor`: Module for ingesting text files containing quotes. \ `TextIngestor`: Module for ingesting text files containing quotes. \
`PDFIngestor`: Module for ingesting PDF files containing quotes. `PDFIngestor`: Module for ingesting PDF files containing quotes.
### Format checking
```sh
$ pip install docformatter
$ pip install autopep8
$ pip install black
$ pip install flake8
$ docformatter -i -r --wrap-summaries 79 --wrap-descriptions 79 .
$ autopep8 --in-place --recursive --aggressive .
$ black .
$ flake8 .
```

View File

@@ -1,3 +1,5 @@
"""Flask app to generate memes."""
import random import random
import os import os
import requests import requests
@@ -6,17 +8,17 @@ from flask import Flask, render_template, abort, request
from QuoteEngine import Ingestor from QuoteEngine import Ingestor
from MemeEngine import MemeEngine from MemeEngine import MemeEngine
# Create the Flask application object. "__name__" tells Flask where to find templates and static files. # Create the Flask application object. "__name__" tells Flask,
# where to find templates and static files.
app = Flask(__name__) app = Flask(__name__)
# Create a global MemeEngine instance that will write generated memes into "./static" # Create a global MemeEngine instance that will write generated memes
# so that the images can be served by the web server. # into "./static" so that the images can be served by the web server.
meme = MemeEngine("./static") meme = MemeEngine("./static")
def setup(): def setup():
"""Load all resources""" """Load all resources."""
quote_files = [ quote_files = [
"./_data/DogQuotes/DogQuotesTXT.txt", "./_data/DogQuotes/DogQuotesTXT.txt",
"./_data/DogQuotes/DogQuotesDOCX.docx", "./_data/DogQuotes/DogQuotesDOCX.docx",
@@ -45,7 +47,7 @@ quotes, imgs = setup()
@app.route("/") @app.route("/")
def meme_rand(): def meme_rand():
"""Generate a random meme """Generate a random meme.
Steps: Steps:
- Pick a random image from imgs - Pick a random image from imgs
@@ -63,7 +65,7 @@ def meme_rand():
@app.route("/create", methods=["GET"]) @app.route("/create", methods=["GET"])
def meme_form(): def meme_form():
"""User input for meme information """User input for meme information.
This route renders a form where the user can input: This route renders a form where the user can input:
- image_url: URL of the source image - image_url: URL of the source image
@@ -75,7 +77,7 @@ def meme_form():
@app.route("/create", methods=["POST"]) @app.route("/create", methods=["POST"])
def meme_post(): def meme_post():
"""Create a user defined meme """Create a user defined meme.
This route: This route:
- Reads form data sent via POST from the meme_form page - Reads form data sent via POST from the meme_form page
@@ -95,8 +97,8 @@ def meme_post():
tmp_file = f"tmp/{random.randint(0, 10000)}.jpg" tmp_file = f"tmp/{random.randint(0, 10000)}.jpg"
with open(tmp_file, "wb") as file: with open(tmp_file, "wb") as file:
file.write(image.content) file.write(image.content)
except: except OSError as e:
print("Failed to generate meme") print("Failed to generate meme:", e)
path = None path = None
if os.path.exists(tmp_file): if os.path.exists(tmp_file):
os.remove(tmp_file) os.remove(tmp_file)

View File

@@ -1,3 +1,5 @@
"""Generate a meme from an image and a quote."""
import os import os
import random import random
import argparse import argparse
@@ -6,7 +8,7 @@ from QuoteEngine import QuoteModel, Ingestor
def generate_meme(path=None, body=None, author=None): def generate_meme(path=None, body=None, author=None):
"""Generate a meme given an path and a quote""" """Generate a meme given an path and a quote."""
img = None img = None
quote = None quote = None
@@ -45,7 +47,8 @@ def generate_meme(path=None, body=None, author=None):
if __name__ == "__main__": if __name__ == "__main__":
"""Add path, quote, and author arguments for CLI, then print meme generation.""" """Add path, quote, and author arguments for CLI, then print meme
generation."""
parser = argparse.ArgumentParser(description="Meme Generator") parser = argparse.ArgumentParser(description="Meme Generator")
parser.add_argument("--path", type=str, help="Image path") parser.add_argument("--path", type=str, help="Image path")
parser.add_argument("--body", type=str, help="Quote adding to meme") parser.add_argument("--body", type=str, help="Quote adding to meme")