Crea imagenes para tus posts con Python

Published on
Tiempo de lectura: 6 min
––– visitas
thumbnail-image

Cuando empecé este blog, mi idea era mantenerlo lo más minimalista posible. La información debería ser lo más importante, así que decidí no usar ninguna imagen. Después de terminar los primeros posts me di cuenta de que cuando comparto estos posts en las redes sociales necesitaría una imagen para las etiquetas meta de opengraph.

Pero no podía simplemente "perder el tiempo" creando imágenes para cada post. Así que decidí automatizar este proceso con Python.

La idea

La idea era crear un script que tomara el título del post y creara una imagen con el título en ella. Ya había creado una plantilla con un cuadro donde debería colocarse el título, y en el script tendría que darle algunos límites para que el título encajara en el cuadro.

Instalar las dependencias

pip install opencv-python pillow

El script - Version para Windows

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):

    """
    Añade texto en un rectangulo en la imagen proporcionada, y guarda la imagen modificada.

    Args:
        input_image_path (str): Ruta a la imagen de entrada.
        output_image_path (str): Ruta para guardar la imagen de salida.
        text (str): Texto que se añadirá a la imagen.
        rectangle_coords (tuple): Coordenadas (x, y, ancho, alto) del rectángulo donde debe caber el texto.
        text_color (str): Código hexadecimal de color para el color del texto.
    """
    # Abre la imagen usando PIL para preservar la transparencia
    pil_image = Image.open(input_image_path).convert("RGBA")

    # Obtiene un contexto de dibujo en la imagen
    draw = ImageDraw.Draw(pil_image)

    # Define una tipografia (puedes cambiar la tipografia y el tamaño según tus preferencias)
    font = ImageFont.truetype("consola.ttf", 36)

    # Obtiene el ancho y el alto del rectángulo
    rect_x, rect_y, rect_width, rect_height = rectangle_coords

    # Divide el texto en palabras
    words = text.split()
    wrapped_lines = []
    current_line = words[0]

    # Envuelve el texto dentro del rectángulo
    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)

    # Obtiene el cuadro delimitador del texto
    wrapped_text_bbox = draw.textbbox((0, 0), wrapped_text, font=font)

    # Calcula la posición para que el texto encaje dentro del rectángulo
    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

    # Añade texto a la imagen con el color de texto especificado
    draw.text((text_x, text_y), wrapped_text, font=font, fill=text_color)

    # Reemplaza los espacios con guiones en el texto para generar el nombre de la imagen de salida
    output_image_name = text.replace(" ", "-") + ".png"
    output_image_path = output_image_name

    # Guarda la imagen en formato PNG
    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"

    # Convierte los espacios en el texto en guiones para el nombre de la imagen de salida
    output_image_name = text.replace(" ", "-") + ".png"

     # Obtiene el texto del argumento de la línea de comandos
    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

    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)

El script - version para macOS

He tenido que modificar el script para que funcione en macOS, ya que me daba un OSError cuando intentaba usar la tipografia consola.ttf.

En macOS tendremos que poner la tipografia en el mismo directorio que el script, y luego usar el método os.path.join para obtener la ruta a la tipografia.

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):
     """
    Añade texto en un rectangulo en la imagen proporcionada, y guarda la imagen modificada.

    Args:
        input_image_path (str): Ruta a la imagen de entrada.
        output_image_path (str): Ruta para guardar la imagen de salida.
        text (str): Texto que se añadirá a la imagen.
        rectangle_coords (tuple): Coordenadas (x, y, ancho, alto) del rectángulo donde debe caber el texto.
        text_color (str): Código hexadecimal de color para el color del texto.
    """
    # Abre la imagen usando PIL para preservar la transparencia
    pil_image = Image.open(input_image_path).convert("RGBA")

    # Obtiene un contexto de dibujo en la imagen
    draw = ImageDraw.Draw(pil_image)

    font = ImageFont.truetype(font_path, 36)

    rect_x, rect_y, rect_width, rect_height = rectangle_coords

    words = text.split()
    wrapped_lines = []
    current_line = words[0]

    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)

    wrapped_text_bbox = draw.textbbox((0, 0), wrapped_text, font=font)

    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

    draw.text((text_x, text_y), wrapped_text, font=font, fill=text_color)

    output_image_name = text.replace(" ", "-") + ".png"
    output_image_path = output_image_name

    pil_image.save(output_image_path, "PNG")

# Example usage
if __name__ == "__main__":

    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"

    text = args.text

    output_image_name = text.replace(" ", "-") + ".png"

    script_dir = os.path.dirname(os.path.realpath(__file__))

    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

    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)

Ejemplo

Example