If a Puppeteer PDF created in Docker shows blank squares, missing letters, or glyphs that look different from your browser screenshot, start by checking fonts inside the container. A CSS font-family declaration does not install that font. The usual container-specific causes are missing coverage for the affected script, Linux font fallback to another family, or a font that has not loaded when print output is generated. Reproduce the exact characters, identify their script, inspect the runtime image, then verify print CSS and font readiness.
1. Reproduce the exact failure first
Do not begin by adding random delays or disabling Chrome’s sandbox. Create a minimal page containing the characters that fail in production, including punctuation, combining marks, emoji, or symbols. Keep the same Docker image, Chromium build, locale, Puppeteer version, and URL-loading code used by the application.
const html = `
Japanese: 日本語 中文: 中文 ไทย: ไทย ខ្មែរ: ខ្មែរ
Arabic: العربية Hebrew: עברית Symbols: € ✓ — —
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsSpecial offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Brother DCP-L2640DW Wireless Compact Monochrome Multi-Function Printer, Copy, Scan, Duplex, Mobile Printing
- BEST FOR SMALL BUSINESSES – Engineered for extraordinary productivity, the Brother DCP-L2640DW Monochrome (Black & White) 3-in-1 combines laser printer, scanner, copier in one compact footprint and delivers high-quality black & white prints
- FAST PRINTER WITH EFFICIENT SCANNING – Produces documents quickly with print speeds up to 36 ppm(2) and scan speeds up to 23.6/7.9 ipm(3) (black/color). A 50-page auto document feeder(4) allows for convenient, time saving multi-page scanning and copying
- FLEXIBLE CONNECTION OPTIONS – Easily navigate the changing demands of your business with secure multi-device connectivity via built-in dual-band wireless (2.4GHz / 5GHz) and Ethernet. Or connect locally to a single computer via USB interface
- BROTHER MOBILE CONNECT APP – Print, scan, and manage your wireless printer anytime, from almost anywhere from your mobile device. Order Brother Genuine Supplies, track toner usage, and complete more work on-the-go(5)
- CHOOSE BROTHER GENUINE TONER – When it’s time to replace your toner, be sure to choose Brother Genuine TN830 or TN830XL replacement toner. And with Refresh EZ Print Subscription Service, you’ll never worry about running out of toner again and you’ll enjoy savings of up to 50%(6) on Brother Genuine Toner. Get started with Refresh today with a Free Trial(1)
`;
Render this HTML both as a browser screenshot and as a PDF from the same container. If the screenshot is already wrong, investigate fonts and CSS before PDF settings. If the screenshot is correct but the PDF differs, inspect print media rules, font loading, and the PDF viewer.
2. Confirm which font and script are actually required
Identify the requested family
Inspect the computed font-family for the failing element, including print styles. A declaration such as font-family: "Noto Sans CJK JP" only requests a family; it does not copy font files into Linux. Fonts installed on your host operating system are not automatically present in Docker.
Identify the script and glyph coverage
Determine whether the missing text is Japanese, Chinese, Korean, Thai, Khmer, Arabic, Hebrew, a mathematical symbol, an emoji, or another script. A font can cover Latin characters while lacking the affected glyphs. Test the exact code points rather than assuming that “Unicode” is one coverage category.
Inspect the image that launches Chrome
Run font inspection commands in the runtime container, not merely in a build stage:
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →fc-list | head
fc-match "Noto Sans"
fc-match ":lang=ja"
fc-match ":lang=ar"
locale
If fc-list is unavailable, install the distribution’s fontconfig utilities temporarily or inspect the package database. The important question is whether a usable font file exists in the final image and whether fontconfig can match it.
Rank #2
- BEST FOR HOMES & HOME OFFICES – Engineered for consistent, premium print quality, the Brother HL-L2405W Monochrome (Black & White) Laser Printer delivers sharp, crisp prints at an affordable price. Prints one-sided documents at speeds up to 30ppm(2)
- COMPACT, CONNECTED PRINTER – Flexible connection options make this an ideal printer for home use and at-home offices. Securely connect to multiple devices with built-in dual-band wireless (2.4GHz/5GHz) or locally to a single computer via USB interface
- BROTHER MOBILE CONNECT APP – Manage your printer remotely and print from your mobile device anytime, from almost anywhere. Order Brother Genuine Supplies, track toner usage, and complete more work on-the-go(3)
- VERSATILE PAPER HANDLING – Enjoy seamless, reliable everyday printing with the 250-sheet paper tray(4) and a manual feed slot that enables printing on envelopes and specialty pape
- BROTHER IS AT YOUR SIDE – Backed by Brother with a 1-year limited warranty and free online, call, or live chat support for the life of your printer
3. Install script-appropriate fonts in Docker
Add fonts to the image that actually runs Puppeteer. Puppeteer’s maintained Dockerfile starts from a Node Bookworm image, sets LANG=en_US.UTF-8, and installs examples such as Japanese, Chinese, Thai, Khmer, Arabic/Hebrew coverage packages and FreeFont. Package names and contents vary by distribution, so select packages for your base image instead of copying a list blindly. See the maintained Puppeteer Dockerfile and its Linux and Docker troubleshooting guide.
# Debian/Ubuntu example; verify names for your release
RUN apt-get update
&& apt-get install -y --no-install-recommends
fontconfig
fonts-ipafont-gothic
fonts-wqy-zenhei
fonts-thai-tlwg
fonts-khmeros
fonts-kacst
fonts-freefont-ttf
&& rm -rf /var/lib/apt/lists/*
Those packages are examples covering several scripts, not a guarantee of complete Unicode support. Installing every available font increases image size and maintenance cost. Prefer the smallest set that covers your content and fallback requirements, then rebuild and run fc-match again.
System fonts versus web fonts
For a self-contained service, system fonts make rendering independent of external requests. If your page uses @font-face, make sure the font URL is reachable from the container, credentials are supplied when required, and the response is a real font rather than an HTML error page. You can also package the font files in the image and reference them with a local URL.
Free tools Windows power users keep installed
One-click scans. No signup required.
4. Understand fallback and changed glyph shapes
Linux font matching can substitute another family when the requested face is absent or lacks a glyph. Chromium’s Linux PDF implementation delegates this kind of substitution to fontconfig; the result may render successfully but look unlike your design. The Chromium PDFium font helper source documents this implementation context.
Use an explicit fallback stack appropriate to your content:
Rank #3
- FAST PRINT SPEEDS: Print up to 19 pages per minute.
- COMPACT DESIGN: Space-saving, compact design fits anywhere in your home, school or small office.
- WIRELESS CONNECTIVITY: Print from almost anywhere in your workspace using your compatible mobile device.
- PAPER CAPACITY: Up to 150 sheets.
- SUSTAINABILITY: Uses less than 2 watts in Energy Saver mode.
body {
font-family: "Noto Sans", "Noto Sans CJK JP", "Noto Sans Thai", sans-serif;
}
Do not assume that naming a family forces Chromium to use it. Verify the family is installed and contains the required glyphs. If appearance matters, embed and select the exact licensed font, and test representative characters from every script you publish.
5. Check Puppeteer’s PDF timing and print CSS
PDF uses print media
page.pdf() generates output using print CSS media. A print stylesheet can select a different family, hide text, alter writing direction, or change colors. Compare screen and print rules with:
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchawait page.emulateMediaType('print');
console.log(await page.$eval('body', el => getComputedStyle(el).fontFamily));
Then inspect any @media print block and print-specific components.
Wait for fonts deliberately
In Puppeteer 25.12.0, page.pdf() waits for fonts by default. The PDFOptions waitForFonts option waits for document.fonts.ready and defaults to true; the documentation notes that bringing a background page to the foreground may be needed for this wait to resolve. See the PDFOptions interface and Page.pdf() documentation.
await page.goto(url, { waitUntil: 'networkidle0' });
await page.bringToFront();
await page.evaluate(() => document.fonts.ready);
await page.pdf({
path: '/tmp/output.pdf',
printBackground: true,
waitForFonts: true
});
For older Puppeteer versions, confirm whether waitForFonts exists and what its default is. An arbitrary setTimeout is not a substitute for fixing a failed font request or a missing glyph.
Rank #4
- BEST FOR HOME OFFICES & SMALL TEAMS – Engineered for consistent, premium print quality, the Brother HL-L2460DW Monochrome (Black & White) Laser Printer produces documents that are clear, crisp, and easy to review and share, all at an affordable price
- COMPACT, CONNECTED, EXCEPTIONALLY EFFICIENT– Connect with built-in dual-band wireless (2.4GHz/5GHz), Ethernet, or to a single computer via USB interface. Prints at speeds up to 36ppm(2), plus automatic duplex printing saves time and reduces paper waste
- BROTHER MOBILE CONNECT APP – Manage your wireless printer remotely and print from your mobile device anytime, from almost anywhere. Order Brother Genuine Supplies, track toner usage, and complete more work on-the-go(3)
- VERSATILE PAPER HANDLING – Tackle high-volume black & white printing with the 250-sheet capacity paper tray.(4) The manual feed slot enables printing on envelopes and specialty paper
- BROTHER IS AT YOUR SIDE – Backed by Brother with a 1-year limited warranty and free online, call, or live chat support for the life of your printer
Diagnose font requests
page.on('requestfailed', request => {
console.error('Request failed:', request.url(), request.failure());
});
page.on('response', response => {
if (response.url().match(/.(woff2?|ttf|otf)(?|$)/i)) {
console.log('Font', response.status(), response.url());
}
});
Check certificate errors, DNS, authentication, CORS policy, redirects, and content type. A network-idle wait can complete even when a font request failed.
6. Keep locale, browser dependencies, and fonts separate
The official Docker setup configures LANG=en_US.UTF-8 and installs browser dependencies. Locale controls text encoding and language behavior; it does not provide font files. A missing shared library can prevent Chrome from launching, while missing glyph coverage leaves Chrome running but produces a square or fallback character. Treat these as separate checks.
Use a maintained, version-compatible image and pin Puppeteer and Chromium versions together where possible. Consult Puppeteer’s troubleshooting documentation for required Linux libraries. Avoid using --no-sandbox as a font fix: Puppeteer discusses it as a separate security issue and strongly discourages running without a sandbox.
7. A complete minimal PDF test
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: true,
args: []
});
try {
const page = await browser.newPage();
await page.setContent(`
<meta charset="utf-8">
<style>
@font-face {
font-family: TestFont;
src: url("https://example.com/fonts/test.woff2") format("woff2");
}
@media print {
body { font-family: TestFont, "Noto Sans", sans-serif; }
}
body { font-size: 22px; }
</style>
<p>日本語 中文 ไทย ខ្មែរ العربية עברית € ✓</p>`,
{ waitUntil: 'networkidle0' }
);
await page.bringToFront();
await page.evaluate(() => document.fonts.ready);
console.log(await page.evaluate(() => ({
status: document.fonts.status,
loaded: document.fonts.check('22px TestFont'),
family: getComputedStyle(document.querySelector('p')).fontFamily
})));
await page.pdf({
path: '/tmp/unicode-test.pdf',
format: 'A4',
printBackground: true,
waitForFonts: true
});
} finally {
await browser.close();
}
})();
Replace the example font URL with a resource you control. A true result from document.fonts.check indicates that a face is available to the browser, not that it contains every glyph; the PDF still needs a font with coverage for each character.
8. Troubleshooting by symptom
| Symptom | Likely cause | Fix |
|---|---|---|
| Blank square or tofu glyph | No installed font covers the code point | Identify the script, install a suitable package in the final image, rebuild, and verify with fc-match. |
| Text appears, but shape or metrics change | Fontconfig substituted another family | Install the requested family or an intentional fallback and verify computed print styles. |
| Browser screenshot is correct; PDF is wrong | Print CSS selects another family or media-specific rule | Emulate print media and inspect computed styles and @media print. |
| Web-font text intermittently falls back | Font request failed or was not ready | Log failed requests, check response status and credentials, await document.fonts.ready, and use the version-appropriate waitForFonts. |
| Chrome will not start | Missing Linux shared library or sandbox configuration | Install documented browser dependencies; do not treat --no-sandbox as a glyph solution. |
| Different viewers show different results | Viewer font rendering or historical platform differences | Inspect the PDF in more than one viewer and compare the embedded/output fonts. Puppeteer issue #3668 is an anecdotal report, not proof of a universal viewer defect. |
9. Validate the rebuilt artifact
- Rebuild without reusing an old image layer that omitted the font package.
- Run the minimal page and production template from the same container.
- Check the generated PDF in at least two viewers if the discrepancy is viewer-specific.
- Archive the exact Dockerfile, package versions, Puppeteer version, Chromium revision, and test string so future image updates can be compared.
Or skip the browser setup
If you only need a reliable website capture or PDF endpoint rather than maintaining Chrome and fonts yourself, ScreenshotNeo provides a GET API and an MCP server. It accepts consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. AI agents can use its MCP tools take_screenshot, get_page_info, and capture_pdf.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
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 PDF options, custom CSS and JavaScript, device and viewport settings, font-related page controls, asynchronous jobs, and webhooks. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
Best Value
- FROM AMERICA'S MOST TRUSTED PRINTER BRAND – Perfect for small teams printing professional-quality black & white documents and reports. Perfect for 1-3 people
- WORLD'S SMALLEST LASER IN ITS CLASS – Precision laser printing that fits anywhere
- FAST PRINT SPEEDS – Up to 21 black-and-white pages per minute single-sided
- WIRELESS WITH SELF-RESET – Helps you stay connected
- PRINT FROM ANY DEVICE – Wireless printing from any mobile device, PC or tablet. Works with Microsoft, Mac, AirPrint, Android, Chromebook and more
FAQ
Does setting LANG=en_US.UTF-8 install Unicode fonts?
No. It configures locale behavior. Font files and glyph coverage must be installed separately.
Should I add a long delay before calling page.pdf()?
Not as a first fix. In Puppeteer 25.12.0, PDF generation waits for fonts by default. Diagnose failed requests, missing glyph coverage, and print CSS first.
Why does a character render in one font but not another?
Unicode assigns code points, but each font covers a different set of scripts and symbols. Select a family with the required coverage or an intentional fallback.
Are package names identical on Alpine and Debian?
No. Font package names and available files are distribution-specific. Consult the package index for the base image you deploy.
The Bottom Line
Make the container’s fonts, print CSS, and font-loading state explicit. Test the exact failing characters after every image or Puppeteer upgrade.
Quick Recap
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.

