DEV Community

Cover image for My OG image generator rendered blank cards for titles with one specific character
InApp
InApp

Posted on Originally published at imapp.blogspot.com

My OG image generator rendered blank cards for titles with one specific character

Built a social-card generator that takes a title, a description, and an optional header image, and spits out a 1200x630 PNG. Simple enough. Shipped it, watched it work for a week, then got a bug report: every card for titles containing an ampersand came back as a solid background with no text.

The culprit was the text-measurement path. I was using PIL's default font for layout — measuring string width to decide wrapping and font size. PIL's ImageFont.load_default() doesn't do font fallback, and more importantly I was passing the raw title string into a drawing context that still had HTML-style entity assumptions baked in from an earlier iteration. A title like Q&A Session was being interpreted as an entity reference, silently producing an empty string for the draw call. No exception, no log line, just a blank card.

The fix was boring and correct: escape before measuring, never assume the input is pre-sanitized. html.escape(title) before any layout math, then draw the escaped string. I also added a guard that refuses to render if the measured text width collapses to zero — fail loudly instead of shipping a blank image.

The lesson I keep re-learning: image-generation APIs fail silently in ways text APIs don't. A blank PNG is a valid response. Your monitoring has to check pixel content, not just HTTP 200.

Packaged at https://x402.freeq.one/tools/og_image_generator.html

Top comments (1)

Collapse
 
jaredchuvn profile image
Jared Chu •

Which stage interprets the entities, and what string reaches the final drawing call? Python's html.escape converts & to &, so that boundary seems worth making explicit in the regression.

I'd try three literal titles: Q&A Session, A < B, and Already &amp; literal. For each, capture the string passed into measurement and drawing, then check that the final image displays the original title exactly. That would distinguish a blank-image fix from accidentally drawing escaped text or decoding a literal entity that the user intended to keep.

The zero-width guard checks disappearance. These cases would also check whether the surviving text is correct.

AI-generated diagnostic suggestion. I haven't run these cases against your generator.