CORS blocks the font
The CSS can be readable while the font request is rejected. The response must allow the controlled document origin, or the browser keeps the fallback.
A browser can finish parsing and painting before the intended font is usable. The fallback changes line breaks, card height and alignment even if the font swaps in a few milliseconds later.
Production images wrap one word earlier than local output, use a system font, or shift between otherwise identical captures.
DOMContentLoaded, once after load, and once after document.fonts.ready.document.fonts.check('600 48px Inter', 'Invoice 1042') with representative glyphs; a family being loaded does not prove every requested face or glyph is available.Declare the exact face, wait on the CSS Font Loading API, verify it with representative text and only then capture.
await page.setContent(html, { waitUntil: "load" });
await page.evaluate(async () => {
await document.fonts.ready;
const wanted = '600 48px "Report Sans"';
if (!document.fonts.check(wanted, "Quarterly revenue €12,450")) {
throw new Error(`Font did not load: ${wanted}`);
}
});
await page.screenshot({ path: "card.png", type: "png" });The call above is small. Everything around it is the system — and the system is what you would be signing up to own.
The CSS can be readable while the font request is rejected. The response must allow the controlled document origin, or the browser keeps the fallback.
Loading the family at 400 does not satisfy a 600 face. Synthetic bold can look different across browser and platform versions.
A Latin sample can pass while currency, CJK or emoji still falls back. Check with text representative of the artifact.
System fonts make a development laptop look correct while a minimal production image uses another fallback. Ship or fetch every non-generic face explicitly.
A PNG from an isolated HTML document after fonts and pending images receive a bounded settle window.
One successful, non-cached capture costs one credit. Failures, including platform failures, cost zero; cache hits cost zero.
A configured failed-request policy matches the font URL that did not load.
HTTP 500 · terminal
Navigation or another readiness rule exhausts the capture budget.
HTTP 500 · retryable
Swap the example for a page you care about. The runner keeps every option from this guide, and carries the whole configuration into the playground — no retyping, no starting over.
Written by PageCapture Engineering. Reviewed by PageCapture render maintainers. Verified 2026-08-12 by running the published code against a fixture.
The recipe is normalized and generated snippets are syntax-checked; font readiness claims are tied to the worker settle implementation and CSS Font Loading API.
Sources support the browser and API behaviors named above. PageCapture-specific limits and billing are taken from the public contract; external sources are used for the underlying browser behavior.
Defines FontFaceSet.ready and check().
Provides the runnable inline-HTML surface used by the recipe.
Run inline markup and inspect the artifact.
Add an application marker when font readiness alone is insufficient.
Remote font URLs cross the same network boundary as the page.
Paste the card and see which face the renderer actually used, before wiring the template into your app.