DEV Community

dhruvkumar-dot
dhruvkumar-dot

Posted on

Creating curved text from PNG images of individual characters in Python

I am trying to create curved text in Python using PNG images of individual characters. I have a set of PNG images, each representing a single character. My goal is to arrange these characters in a curved pattern to form a specific text. How can I achieve this using Python?

I have a collection of PNG images named after each character (e.g., 'A.png', 'B.png', 'C.png', etc.). Each image has a transparent background, and the character itself is filled with a specific color. I want to arrange these characters in a curved pattern, forming the desired text (e.g., "STACKOVERFLOW").

I have tried to achieve this by the following code:

from PIL import Image
import math

def generate_curve_text(input_text, spacing):
    letter_images = []
    arc_radius = 100  # Adjust the radius of the arc as needed
    total_angle = math.pi  # Adjust the total angle of the arc as needed

    angle_per_letter = total_angle / (len(input_text) - 1) if len(input_text) > 1 else 0

    for i, char in enumerate(input_text):
        angle = i * angle_per_letter - total_angle / 2
        x = int(arc_radius * math.cos(angle))
        y = int(arc_radius * math.sin(angle))

        letter_image = Image.open("./images/" + char.upper() + ".png").convert("RGBA")
        transparent_image = Image.new("RGBA", letter_image.size)

        for px in range(letter_image.width):
            for py in range(letter_image.height):
                pixel = letter_image.getpixel((px, py))
                if pixel[:3] == (0, 0, 0):
                    transparent_image.putpixel((px, py), (0, 0, 0, 0))
                else:
                    transparent_image.putpixel((px, py), pixel)

        letter_images.append((transparent_image, (x, y)))

    # Calculate the bounding box of all letter images
    min_x = min(x for _, (x, _) in letter_images)
    max_x = max(x + image.width for image, (x, _) in letter_images)
    min_y = min(y for _, (_, y) in letter_images)
    max_y = max(y + image.height for image, (_, y) in letter_images)

    # Create a new image with the size of the bounding box
    width = max_x - min_x + spacing * (len(letter_images) - 1)
    height = max_y - min_y
    output_image = Image.new('RGBA', (width, height))

    # Paste each letter image at its respective position
    for image, (x, y) in letter_images:
        output_image.paste(image, (x - min_x, y - min_y))

    return output_image

# Save or display the result
generated_curve_image = generate_curve_text('STACKOVERFLOW', 20)
generated_curve_image.save('curve_output.png')
Enter fullscreen mode Exit fullscreen mode

getting output png as:

Image description

But desired output png should be like:

Image description

I would appreciate any guidance or suggestions on how to accomplish this. Specifically, I would like to know how to calculate the positions of each character along the curve and how to adjust the spacing between characters to prevent overlapping. Additionally, any tips on optimizing the code or improving the overall design would be greatly appreciated.

Thank you in advance for your help!

Top comments (0)