Skip to content

fix(share): OG image renders CJK glyphs#377

Merged
graydawnc merged 1 commit into
mainfrom
fix/og-image-cjk-glyphs
Jun 9, 2026
Merged

fix(share): OG image renders CJK glyphs#377
graydawnc merged 1 commit into
mainfrom
fix/og-image-cjk-glyphs

Conversation

@graydawnc

Copy link
Copy Markdown
Collaborator

Summary

Titles containing Chinese / Japanese / Korean characters rendered as black "NO GLYPH" placeholder boxes on the /api/og/<id>.png social-card preview. workers-og (the Satori wrapper used for OG rendering) bundles only Inter — any codepoint outside Inter's Latin / Greek / Cyrillic coverage hit Satori's fallback glyph.

Fix

renderOgPng now loads two font subsets in parallel via loadGoogleFont:

  • Inter at weight 500 — covers the Latin portion (wordmark, template name, date)
  • Noto Sans SC at weight 500 — covers CJK Unified Ideographs

Both calls pass text=<glyphs we actually paint> so Google Fonts returns only the codepoints in the current title plus the static "SPOOL · SHARE" wordmark and date string — typically a few KB per render instead of the full ~3 MB CJK blob.

buildOgHtml declares font-family: Inter, 'Noto Sans SC', system-ui, sans-serif. Satori walks the chain per codepoint, so Latin chars resolve to Inter and the rest fall through to Noto Sans SC.

Both font loads degrade gracefully: a Google Fonts outage leaves the OG image rendering with whichever subset arrived — worst case is the pre-fix behaviour with NO GLYPH boxes, never a 500.

Tests

workers-og mocks in og.test.ts and publish.test.ts now stub loadGoogleFont to a 1-byte ArrayBuffer so the suites keep running without hitting the network.

  • pnpm --filter @spool/share-backend test — 220 / 220

Manual verification

Published a new share with a Chinese title via the desktop app; fetched /api/og/<id>.png from the running wrangler instance and confirmed the title renders as Chinese characters rather than the placeholder blocks.

Submitter: @graydawnc

🤖 Generated with Claude Code

Titles with Chinese / Japanese / Korean characters rendered as black
"NO GLYPH" placeholder boxes on the social-card PNG. workers-og ships
only Inter by default; any codepoint outside Inter's Latin/Greek/Cyrillic
coverage hit Satori's fallback.

Fix: load Noto Sans SC alongside Inter via workers-og's loadGoogleFont
and pass both into the ImageResponse fonts list. Satori walks the
font chain per codepoint — Latin resolves to Inter, CJK falls through
to Noto Sans SC.

Font subsets are fetched per render with text=<glyphs we actually paint>,
so each request grabs a few KB instead of the full ~3 MB CJK blob.
Both loads degrade gracefully: a Google Fonts outage leaves the OG
image rendering with whichever subset arrived (worst case: pre-fix
behaviour, NO GLYPH boxes — never a 500).

Test mocks for `workers-og` in og.test.ts + publish.test.ts now stub
`loadGoogleFont` to a 1-byte ArrayBuffer.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@graydawnc
graydawnc added this pull request to the merge queue Jun 9, 2026
Merged via the queue into main with commit c738ad9 Jun 9, 2026
6 checks passed
@graydawnc
graydawnc deleted the fix/og-image-cjk-glyphs branch June 9, 2026 04:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant