monogram with initials UKR

Customizing Wedding Cards With Python

Updated: Under: projects Tags: #Python #ChatGPT

Our wedding is coming up, and I’m running out of time. It’s a bit hectic, trying to plan two events in two different places hundred kilometers apart.

I was planning to give my friends invites but was running out of time fast, Had to send the invites digitally; via WhatsApp to be precise.

Physical cards, hot off the printers a week back
Image 1: Physical cards, hot off the printers a week back

I can’t just send an blank invite, that’s too impersonal! Have to at least address it to the invitee.

Automating it!

As you can see my card is fairly simple, just need to tack on some text in the middle and it works. I decided to ask ChatGPT to make me a script for that. With the following requirements,

  • Take an input text file with a guest name in each new line
  • Open a PNG of my wedding card
  • Iterate and add horizontally centered name with a predefined vertical offset

It failed at it, It misunderstood my need at first (probably my fault) but most importantly it wasn’t aware of deprecated API’s of Pillow (the library of choice for image processing in Python)

After hacking on the script for a couple of minutes, I was able to get it to working.


from PIL import Image, ImageDraw, ImageFont

FONT_SIZE = 60
FONT_TTF = "DancingScript-Regular.ttf"

def add_names_to_invitation(suffix, invitation_path, y_offset, names_file_path, output_folder):
    # Open the wedding invitation image
    invitation_image = Image.open(invitation_path)

    # Define coordinates for the center alignment
    width, height = invitation_image.size
    center_x = width // 2

    # Load a font for the names
    font = ImageFont.truetype(FONT_TTF, FONT_SIZE)

    # Open the file containing names
    with open(names_file_path, 'r') as file:
        for idx, name in enumerate(file):
            # Remove newline characters
            name = name.strip()
            
            # Make copy of the image
            cur = invitation_image.copy()
            draw = ImageDraw.Draw(cur)

            # Calculate position for each name
            text_width = draw.textlength(name, font=font)
            name_x = center_x - text_width // 2
            name_y = y_offset - FONT_SIZE // 2

            # Add the name to the invitation
            draw.text((name_x, name_y), name, fill="black", font=font)

            # Save the invitation with the name
            output_path = f"{output_folder}/invitation_{name}_{suffix}.png"
            cur.save(output_path)
            print(f"Writing {output_path}")

if __name__ == "__main__":
    names_file_path = "guest_names.txt"
    output_folder = "output"
    add_names_to_invitation("cmb", "Colombo Card.png", 1160, names_file_path, output_folder)
    add_names_to_invitation("kdy", "Kandy Card.png", 1095, names_file_path, output_folder)

I threw in a custom script font which is attributed1 as public domain. I had two different cards, for which I just made the method functional with parameters for the y axis offset and a file name suffix.

The output folder with all the named cards!
Image 2: The output folder with all the named cards!

Why not do it manually?

I get so frustrated at times doing things manually, especially in the realm of computing as the task of the computer is to reduce repetition, so I wouldn’t have. Also two cards per person, would have made it a fun task2.

But thanks to LLM giving me a good enough starting point, was able to get it done in a short amount of time, the code is yucky, but it serves it’s purpose.

Appropriate XKCD
Image 3: Appropriate XKCD

It didn’t turn out to be like the Automation XKCD, which I’m happy about. My fiancĂ©e also made use of it to send some digital cards.


  1. Font Licensing is tricky I hear ↩︎

  2. For the two events, I made two cards, in retrospect should have made a third alternative with both event detail ↩︎