🚀 Executive Summary

TL;DR: Manually creating Open Graph (OG) images for blog posts is a repetitive and time-consuming task that leads to workflow friction. This guide provides a Python script utilizing the Pillow library to automate the generation of branded social cards, integrating into CI/CD pipelines for a zero-touch process.

🎯 Key Takeaways

  • The Pillow library is fundamental for Python-based image manipulation, enabling tasks like opening template images, drawing dynamic text, and saving new, customized social cards.
  • Implementing Python’s built-in `textwrap` module is critical for robust OG image generation, ensuring long blog post titles are automatically broken into multiple lines to prevent text overflow.
  • Automated OG image generation can be seamlessly integrated into existing DevOps workflows through CI/CD pipelines for static site generators, pre-commit Git hooks, or scheduled tasks like cron jobs.

Automate generating OG Images (Social Cards) for Blog Posts

Automate generating OG Images (Social Cards) for Blog Posts

Hey everyone, Darian Vance here. Let’s talk about a task that used to be a persistent, low-level annoyance in my workflow: creating social sharing cards (or OG images) for new blog posts. For every article our content team wrote, someone—usually a designer, but sometimes me—had to open a template, paste in the new title, export it, and upload it. It’s not hard, but it’s pure friction. Multiply that by a few posts a week, and you’ve got a real time-sink. I finally decided to automate it, and now it’s a zero-touch process that runs right in our CI pipeline. It saves us a couple of hours a week and ensures our branding is always consistent.

Today, I’m going to walk you through how to build a simple but powerful Python script to do the same thing. This is a classic “invest a little time now, save a lot later” DevOps win.

Prerequisites

Before we dive in, make sure you have the following ready:

  • Python 3 installed on your system.
  • A base template image (e.g., a 1200×630 PNG with your company’s branding).
  • A font file (.ttf or .otf) for your brand’s typography.
  • Basic comfort with the command line.

The Guide: Step-by-Step

Step 1: Setting Up Your Project

First, get your project folder organized. I recommend a structure like this:

  • generate_card.py <– Our Python script.
  • template.png <– Your base image.
  • font.ttf <– Your chosen font file.
  • /output/ <– A directory where the generated images will be saved.

I’ll skip the standard virtualenv setup since you likely have your own workflow for that. The only dependency we need is Pillow, the friendly fork of the Python Imaging Library. You can get it by running this command in your terminal: pip install Pillow. With that done, let’s get to the code.

Step 2: The Core Python Script

Open up your generate_card.py file. The logic is straightforward: we’ll open the template, create a drawing surface on it, load our custom font, and then write the blog post title onto the image before saving it as a new file.

Here’s the complete script. I’ll break down what each part does below.


from PIL import Image, ImageDraw, ImageFont
import textwrap

# --- Configuration ---
TEMPLATE_PATH = "template.png"
FONT_PATH = "font.ttf"
OUTPUT_DIR = "output"

# Text placement and styling
FONT_SIZE = 60
TEXT_COLOR = (255, 255, 255) # White
TEXT_START_X = 60
TEXT_START_Y = 250
MAX_LINE_WIDTH = 30 # In characters, adjust as needed

def generate_og_image(title, output_filename):
    """
    Generates an OG image with the given title.
    """
    try:
        # 1. Open the template image
        img = Image.open(TEMPLATE_PATH).convert("RGB")
        draw = ImageDraw.Draw(img)

        # 2. Load the font
        font = ImageFont.truetype(FONT_PATH, FONT_SIZE)

        # 3. Handle text wrapping for long titles
        wrapped_lines = textwrap.wrap(title, width=MAX_LINE_WIDTH)
        
        y_text = TEXT_START_Y
        line_height = font.getbbox("A")[3] + 15 # Height of a character plus some padding

        # 4. Draw each line of text
        for line in wrapped_lines:
            draw.text((TEXT_START_X, y_text), line, font=font, fill=TEXT_COLOR)
            y_text += line_height

        # 5. Save the new image
        output_path = f"{OUTPUT_DIR}/{output_filename}"
        img.save(output_path)
        print(f"Successfully generated: {output_path}")

    except FileNotFoundError:
        print(f"Error: Could not find template '{TEMPLATE_PATH}' or font '{FONT_PATH}'.")
        return
    except Exception as e:
        print(f"An unexpected error occurred: {e}")
        return

if __name__ == "__main__":
    # --- Example Usage ---
    post_title = "How to Efficiently Scale Your Kubernetes Clusters"
    
    # Create a simple, URL-friendly filename
    safe_filename = "".join(
        [c for c in post_title if c.isalpha() or c.isdigit() or c==' ']
    ).rstrip().replace(" ", "-").lower() + ".png"

    generate_og_image(post_title, safe_filename)

    # Another example
    another_title = "A Deep Dive into Infrastructure as Code with Terraform"
    another_filename = "iac-with-terraform.png"
    generate_og_image(another_title, another_filename)

Breaking it down:

  1. Configuration: At the top, we define constants for our file paths, font size, color, and text position. This makes it easy to tweak the final look without digging through the logic.
  2. Opening the Image: We use Image.open() to load our template. The .convert("RGB") is a good practice to ensure consistency, especially if you’re saving as a JPEG later.
  3. Text Wrapping: This is the most crucial part for a robust solution. A single, long title will run off the edge of the image. Python’s built-in textwrap library is perfect for this. It breaks our long title string into a list of shorter lines.
  4. Drawing Text: We loop through our `wrapped_lines` and draw them one by one. We calculate `line_height` to ensure proper spacing between lines and increment our `y_text` position after drawing each line.
  5. Example Usage (if __name__ == "__main__":): This block shows how to use the function. It’s good practice to generate a “slug” or URL-safe filename from the title to keep things clean.

Pro Tip: Don’t guess the text coordinates (TEXT_START_X, TEXT_START_Y). Open your template in an image editor like GIMP or Photoshop and use the ruler or info tool to find the exact pixel coordinates where you want your text to start. It’ll save you a lot of trial and error.

Step 3: Integrating into Your Workflow

A standalone script is great, but the real power comes from automation. In my production setups, I’ve integrated this script in a few ways:

  • CI/CD Pipeline: If you use a static site generator like Hugo or Jekyll, you can add a step to your build pipeline. The pipeline can find all new markdown files, extract their titles, and run this Python script to generate the corresponding OG image before the site is deployed.
  • Git Hook: For a simpler setup, you could use a pre-commit git hook that runs the script on any new posts you’ve added.
  • Scheduled Task: If your blog posts are pulled from a database, you could set up a simple scheduled task to run nightly, check for posts missing an OG image, and generate one. A simple entry might look like this, running at 2 AM every Monday: 0 2 * * 1 python3 generate_card.py

Common Pitfalls

Here are a few places I’ve stumbled in the past, so you can avoid them:

  • Text Overflows: Forgetting to implement text wrapping. The script I provided handles this, but if you write your own from scratch, don’t just draw the title directly. It will break with a long headline.
  • Font/Template Not Found: The most common error. The script will crash if it can’t find template.png or font.ttf. Always use relative paths from the script’s location or, even better, use absolute paths constructed from the script’s directory for more resilience.
  • Incorrect Font Size/Positioning: The values for FONT_SIZE and MAX_LINE_WIDTH are highly dependent on your specific template and font. You’ll need to experiment a bit to find the perfect combination that looks good and doesn’t overflow your designated text area.

Conclusion

And there you have it. With a simple Python script and the powerful Pillow library, you’ve automated a tedious marketing task, ensuring brand consistency and saving valuable time. This script is a fantastic starting point. You can easily extend it to add more dynamic elements, like the author’s name, a publication date, or even a unique icon for each post category.

Automating small points of friction like this is what DevOps is all about. It frees up your team to focus on the more complex problems. Happy building!

Darian Vance - Lead Cloud Architect

Darian Vance

Lead Cloud Architect & DevOps Strategist

With over 12 years in system architecture and automation, Darian specializes in simplifying complex cloud infrastructures. An advocate for open-source solutions, he founded TechResolve to provide engineers with actionable, battle-tested troubleshooting guides and robust software alternatives.


🤖 Frequently Asked Questions

âť“ How can I automate the creation of social sharing images for my blog posts?

You can automate social sharing image (OG image) creation using a Python script with the Pillow library. The script dynamically takes a blog post title, wraps the text, and overlays it onto a branded template image before saving the new social card.

âť“ How does this Python script approach compare to using online OG image generators or design tools?

This Python script offers full automation and brand consistency by integrating directly into your development workflow, eliminating manual design steps. Online generators require manual input per post, while design tools like Photoshop demand repetitive manual adjustments, making them less scalable for frequent content.

âť“ What is a common implementation pitfall when dynamically adding text to OG images, and how is it solved?

A common pitfall is text overflow, where long titles extend beyond the image boundaries. This is solved by using Python’s `textwrap` library to automatically break the title into multiple lines, ensuring all text fits within the designated area on the template.

Leave a Reply

Discover more from TechResolve - SaaS Troubleshooting & Software Alternatives

Subscribe now to keep reading and get access to the full archive.

Continue reading