Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

If an emoji appears as an empty square in a PDF, the renderer usually cannot find a font with a usable glyph for it. Make the font available to the process that generates the PDF, check the PDF-specific styles and test the exact emoji sequence in that pipeline. If the sequence still is not supported, use an image or a clear text alternative.

Why emojis become boxes in PDFs

The empty square, sometimes called a tofu glyph, is a font fallback failure: the selected font and the fonts the renderer checks next cannot provide a usable glyph for the character. In Blink, the browser engine used by Chromium, CSS fonts are tried first, then system fonts; if those still leave a gap, Blink renders the primary font’s .notdef glyph. Blink’s font documentation describes this fallback behavior.

Adding a font name to CSS is not enough by itself. The matching font must be installed or otherwise accessible to the PDF renderer, and its glyph coverage must include the character or emoji sequence that failed. The browser on your laptop and a PDF worker in a container may have different fonts and font-discovery settings.

Reproduce the failure in the PDF runtime

  1. Identify the renderer and environment. Record the HTML-to-PDF engine and version, operating system, and whether PDF generation runs locally, in a container, or on a remote worker.
  2. Make a minimal test page. Include the exact failing emoji as it appears in the source. Preserve variation selectors, skin-tone modifiers, regional indicator characters in flags, and zero-width joiners (ZWJ); changing or omitting one can change the sequence being tested.
  3. Generate a PDF through the same path as production. A successful display in a desktop browser is not proof that the PDF worker can find the same font or will use the same CSS.
  4. Check the resulting PDF. Look at the visual output, and, if portability or extraction matters, inspect it in more than one viewer. A font may be embedded without guaranteeing identical rendering in every viewer.

Check fonts and fallback in your renderer

WeasyPrint

WeasyPrint relies on fonts Pango can find; Pango uses Fontconfig on Linux, Windows, and macOS. Run font-discovery commands in the environment where the PDF is generated, not only on your development workstation:

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • fc-list lists fonts visible to Fontconfig.
  • fc-match 'Font Family Name' shows the font Fontconfig matches for a family name.

WeasyPrint’s 70.0 API reference says fonts are embedded in PDFs and subset by default to include glyphs used in the document. It also notes that if neither the selected font nor fallback supports a character, WeasyPrint displays .notdef and logs a warning. Check those warnings during diagnosis. Fontconfig’s default rules may provide colored emoji variants, but configuration can affect how those interact with CSS font rules.

Chromium and Puppeteer

Confirm that the browser process generating the PDF can see the required fonts and that its CSS font stack allows fallback. For Puppeteer, inspect the print styles as well as the regular page styles: Page.pdf() uses print CSS by default. A rule inside @media print can select a different font stack or override the screen styles. See Puppeteer’s Page.pdf() documentation.

If you specifically want the PDF to use screen media styles, Puppeteer documents calling page.emulateMediaType('screen') before page.pdf(). That changes the media type; it does not install a missing font or add emoji coverage.

wkhtmltopdf

An issue opened in 2016 reports an empty square instead of the coffee emoji in a generated PDF. It is an individual user report, not a general diagnosis or a verified fix. The wkhtmltopdf repository is archived as of January 2, 2023; consider that maintenance status when choosing a renderer for a new pipeline.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Test the actual emoji sequence, not just one symbol

A font that covers a basic emoji character may not correctly render a more complex sequence. Flags, keycaps, skin-tone combinations, and ZWJ sequences can depend on support for the sequence in the particular font and renderer. Test every kind of emoji your documents actually use in the deployed environment. The Unicode Emoji specification describes emoji sequences and their components; code-point coverage alone is not proof that a renderer will display a combined sequence as intended.

When comparing a font or a different rendering approach, check its coverage for your exact sequences, availability in the production process, PDF font embedding behavior, print-media styles, and the renderer’s maintenance status. Do not assume that a font family name in CSS guarantees availability or universal emoji support.

When font fallback is not enough

If no font-and-renderer combination in your pipeline reliably supports a required sequence, avoid silently shipping tofu. Use an image asset for the emoji’s visual appearance, or replace it with a concise text alternative where the meaning matters more than the pictogram. Choose based on whether the PDF must preserve selectable text, accessibility, or a specific visual style.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

For a clean screenshot of a rendered web page, ScreenshotNeo can return an image or PDF with one GET request. The example below captures a page as WebP; replace the URL with the page you want. It is a screenshot API, not a way to repair a missing emoji glyph in an existing PDF.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for request options. ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

Learn about ScreenshotNeo, or sign up free for 1,000 screenshots a month with no card.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.