DEV Community

InApp
InApp

Posted on Originally published at imapp.blogspot.com

Canvas doesn't wrap text — my OG image generator painted titles off the 1200px edge

When I built an OG image generator — POST a title and description, get back a 1200x630 social-card PNG — the first render looked perfect. The second, a ~90-character title about database indexing, ran clean off the right edge and clipped mid-word.

The root cause was embarrassing: Canvas's fillText() doesn't wrap. Ever. It draws one line and happily paints 3,000px of text onto a 1,200px image. Browsers wrap HTML; canvas is a paint API, not a layout engine. If you want wrapped text, you build the line-breaking logic yourself.

What ended up being the real core of the product:

  1. Measure, never estimate. Every word's width comes from ctx.measureText(). My first heuristic (average character width × length) broke instantly on ALL-CAPS titles and wide glyphs.
  2. Greedy line packing against the safe area: 1200px minus 64px padding per side = 1072px usable. Append a word; overflow means a new line.
  3. Cap at three lines with an ellipsis — but only append "…" if the last word genuinely doesn't fit. My first version chopped a word in half and glued the dots on.
  4. Layout runs twice: once to count lines so vertical centering is correct, once to draw.

Two follow-on lessons from the same bug hunt: my minimal container had no fonts beyond the base set, so em dashes and curly quotes rendered as tofu boxes — I now ship explicit font files plus a glyph-stress test string (accents, dashes, quotes, an emoji, some CJK) that fails the render if anything comes back as a missing glyph. And descenders hang below the baseline, so a line-height of 1.35x was the smallest multiplier where g's and y's stopped colliding.

The generator now truncates predictably instead of painting off-canvas, and I ended up packaging it as the OG Image Generator API (https://x402.freeq.one/tools/og_image_generator.html) with 60/120-char caps on title and description — long enough for real headlines, short enough that three lines at 64px is the honest maximum a feed card can carry.

Top comments (0)