Create Images for your Posts with Python

Published on
7 mins read
––– views
thumbnail-image

When i first started this blog, my idea was to keep it as minimal as possible. The information should be the most important thing, so i decided to not use any images. After finishing the first posts i realized that when sharing these posts on Social media i'd need an image for the opengraph meta tags.

But i couldn't just "waste time" on creating images for each post. So i decided to automate this process with Python.

The Idea

The idea was to create a script that takes the title of the post and creates an image with the title on it. I've already created a template with a box where the title should be placed, and in the script i'd have to give it some boundaries so that the title would fit in the box.

Installing the dependencies

pip install opencv-python pillow

The Script - Windows version

import cv2
from PIL import Image, ImageDraw, ImageFont
import argparse

def add_text_to_image(input_image_path, output_image_path, text, rectangle_coords, text_color):
    """
    Adds wrapped text to the input image within the specified rectangle, and saves the modified image.

    Args:
        input_image_path (str): Path to the input image file.
        output_image_path (str): Path to save the output image file.
        text (str): Text to be added to the image.
        rectangle_coords (tuple): Coordinates (x, y, width, height) of the rectangle where text should fit.
        text_color (str): Hexadecimal color code for the text color.
    """
    # Open the image using PIL to preserve transparency
    pil_image = Image.open(input_image_path).convert("RGBA")

    # Get a drawing context on the image
    draw = ImageDraw.Draw(pil_image)

    # Define a font (you can change the font and size as per your preference)
    font = ImageFont.truetype("consola.ttf", 36)

    # Get the width and height of the rectangle
    rect_x, rect_y, rect_width, rect_height = rectangle_coords

    # Split the text into words
    words = text.split()
    wrapped_lines = []
    current_line = words[0]

    # Wrap text within the rectangle
    for word in words[1:]:
        test_line = current_line + " " + word
        test_size = draw.textbbox((0, 0), test_line, font=font)

        if test_size[2] <= rect_width:
            current_line = test_line
        else:
            wrapped_lines.append(current_line)
            current_line = word

    wrapped_lines.append(current_line)
    wrapped_text = "\n".join(wrapped_lines)

    # Get the bounding box of the wrapped text
    wrapped_text_bbox = draw.textbbox((0, 0), wrapped_text, font=font)

    # Calculate the position to fit the wrapped text inside the rectangle
    text_x = rect_x + (rect_width - (wrapped_text_bbox[2] - wrapped_text_bbox[0])) // 2
    text_y = rect_y + (rect_height - (wrapped_text_bbox[3] - wrapped_text_bbox[1])) // 2

    # Add wrapped text to the image with the specified text color
    draw.text((text_x, text_y), wrapped_text, font=font, fill=text_color)

    # Replace spaces with hyphens in the text to generate the output image's name
    output_image_name = text.replace(" ", "-") + ".png"
    output_image_path = output_image_name

    # Save the image in PNG format to preserve transparency
    pil_image.save(output_image_path, "PNG")

# Example usage
if __name__ == "__main__":
    # Argument parser for getting text input from the command line
    parser = argparse.ArgumentParser(description="Add text to an image within a specified rectangle.")
    parser.add_argument("text", help="Text to be added to the image.")
    args = parser.parse_args()

    input_image_path = "social-banner.png"

    # Convert spaces in text to hyphens for the output image's name
    output_image_name = text.replace(" ", "-") + ".png"

     # Get text from command line argument
    text = args.text

    rectangle_coords = (354, 260, 494, 158)  # (x, y, width, height) of the rectangle
    text_color = "#2a2a2a"  # Hex color code for the text color

        # Use the generated output image name
    output_image_path = os.path.join(script_dir, output_image_name)

    add_text_to_image(input_image_path, output_image_path, text, rectangle_coords, text_color)

The Script - macOS version

I have to modify the script to get it to work on macOS, since i was getting an OSError when trying to use the consola.ttf font.

In macOs we'll have to put the font in the same directory as the script, and then use the os.path.join method to get the path to the font.

import cv2
from PIL import Image, ImageDraw, ImageFont
import argparse

def add_text_to_image(input_image_path, output_image_path, text, rectangle_coords, text_color, font_path):
    """
    Adds wrapped text to the input image within the specified rectangle, and saves the modified image.

    Args:
        input_image_path (str): Path to the input image file.
        output_image_path (str): Path to save the output image file.
        text (str): Text to be added to the image.
        rectangle_coords (tuple): Coordinates (x, y, width, height) of the rectangle where text should fit.
        text_color (str): Hexadecimal color code for the text color.
    """
    # Open the image using PIL to preserve transparency
    pil_image = Image.open(input_image_path).convert("RGBA")

    # Get a drawing context on the image
    draw = ImageDraw.Draw(pil_image)

    # Define a font (you can change the font and size as per your preference)
    font = ImageFont.truetype(font_path, 36)

    # Get the width and height of the rectangle
    rect_x, rect_y, rect_width, rect_height = rectangle_coords

    # Split the text into words
    words = text.split()
    wrapped_lines = []
    current_line = words[0]

    # Wrap text within the rectangle
    for word in words[1:]:
        test_line = current_line + " " + word
        test_size = draw.textbbox((0, 0), test_line, font=font)

        if test_size[2] <= rect_width:
            current_line = test_line
        else:
            wrapped_lines.append(current_line)
            current_line = word

    wrapped_lines.append(current_line)
    wrapped_text = "\n".join(wrapped_lines)

    # Get the bounding box of the wrapped text
    wrapped_text_bbox = draw.textbbox((0, 0), wrapped_text, font=font)

    # Calculate the position to fit the wrapped text inside the rectangle
    text_x = rect_x + (rect_width - (wrapped_text_bbox[2] - wrapped_text_bbox[0])) // 2
    text_y = rect_y + (rect_height - (wrapped_text_bbox[3] - wrapped_text_bbox[1])) // 2

    # Add wrapped text to the image with the specified text color
    draw.text((text_x, text_y), wrapped_text, font=font, fill=text_color)

    # Replace spaces with hyphens in the text to generate the output image's name
    output_image_name = text.replace(" ", "-") + ".png"
    output_image_path = output_image_name

    # Save the image in PNG format to preserve transparency
    pil_image.save(output_image_path, "PNG")

# Example usage
if __name__ == "__main__":
    # Argument parser for getting text input from the command line
    parser = argparse.ArgumentParser(description="Add text to an image within a specified rectangle.")
    parser.add_argument("text", help="Text to be added to the image.")
    args = parser.parse_args()

    input_image_path = "social-banner.png"

    # Get text from command line argument
    text = args.text

    # Convert spaces in text to hyphens for the output image's name
    output_image_name = text.replace(" ", "-") + ".png"

    # Get the path to the script's directory
    script_dir = os.path.dirname(os.path.realpath(__file__))

    # Specify the font file (consola.ttf) in the same directory as the script
    font_path = os.path.join(script_dir, "consola.ttf")

    rectangle_coords = (354, 260, 494, 158)  # (x, y, width, height) of the rectangle
    text_color = "#2a2a2a"  # Hex color code for the text color

    # Use the generated output image name
    output_image_path = os.path.join(script_dir, output_image_name)

    add_text_to_image(input_image_path, output_image_path, text, rectangle_coords, text_color, font_path)

Usage

Example