You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I searched existing issues and this hasn't been reported yet
What happened?
Custom fonts never load in generate-pdf.mjs — every generated PDF silently falls back to system fonts. The template's brand typography (Space Grotesk / DM Sans from fonts/*.woff2) is never embedded; on Windows the PDFs embed only Arial. This is platform-independent (Chromium security policy, not an OS quirk) and completely silent: document.fonts.ready resolves anyway, the PDF generates fine, exit code 0.
Found while validating #949 — that PR makes the injected file:// font URLs valid on Windows, but fonts still don't load even with valid URLs, because of a second, independent problem:
Root cause:renderHtmlToPdf() loads the document with page.setContent(html, { baseURL: 'file://...' }). After setContent, the page's actual URL remains about:blank — baseURL only affects relative-URL resolution, not the document origin. Chromium refuses to load local files from a non-file:// page, so every file:///...woff2 request is blocked:
CONSOLE: error Not allowed to load local resource: file:///.../fonts/dm-sans-latin.woff2
REQ FAILED: file:///.../fonts/dm-sans-latin.woff2 -- other
FONTS: [{"family":"DM Sans","status":"error"}]
PAGE URL: about:blank
(Minimal Playwright repro: setContent an HTML with one @font-face pointing at an absolute file:/// woff2 URL, listen to console/requestfailed, inspect document.fonts statuses.)
Checking the fonts actually embedded in a PDF produced from templates/cv-template.html confirms it — only the fallback:
/BaseFont names: AAAAAA+Arial-BoldMT, BAAAAA+ArialMT # no SpaceGrotesk, no DMSans
Inline the fonts as base64 data: URLs during the font injection step in generatePDF() — no origin/security dependency at all, works with setContent unchanged. ~5 lines: read each woff2, replace url('./fonts/X.woff2') with url('data:font/woff2;base64,...').
Write the final HTML to a temp file and page.goto(pathToFileURL(tmp).href) — document origin becomes file://, local subresources load; delete the temp file after rendering.
page.route() interception serving the font bytes.
Happy to send a PR once there's a preferred direction (I'd suggest option 1: it also keeps renderHtmlToPdf()'s API unchanged for other callers).
CLI tool
Claude Code
OS and Node.js version
Windows 11 / Node v24.14.0, Playwright 1.58.1 (Chromium headless shell) — but the blocking behavior is Chromium policy and applies on Linux/macOS as well.
Code of Conduct
Existing issues
What happened?
Custom fonts never load in
generate-pdf.mjs— every generated PDF silently falls back to system fonts. The template's brand typography (Space Grotesk / DM Sans fromfonts/*.woff2) is never embedded; on Windows the PDFs embed only Arial. This is platform-independent (Chromium security policy, not an OS quirk) and completely silent:document.fonts.readyresolves anyway, the PDF generates fine, exit code 0.Found while validating #949 — that PR makes the injected
file://font URLs valid on Windows, but fonts still don't load even with valid URLs, because of a second, independent problem:Root cause:
renderHtmlToPdf()loads the document withpage.setContent(html, { baseURL: 'file://...' }). AftersetContent, the page's actual URL remainsabout:blank—baseURLonly affects relative-URL resolution, not the document origin. Chromium refuses to load local files from a non-file://page, so everyfile:///...woff2request is blocked:(Minimal Playwright repro:
setContentan HTML with one@font-facepointing at an absolutefile:///woff2 URL, listen toconsole/requestfailed, inspectdocument.fontsstatuses.)Checking the fonts actually embedded in a PDF produced from
templates/cv-template.htmlconfirms it — only the fallback:Steps to reproduce
node generate-pdf.mjs templates/cv-template.html output/test.pdf(on any OS, with fix(generate-pdf): silent no-op on Windows - build file:// URLs with pathToFileURL #949 applied or not)/BaseFontnames from the PDF (or open it) → system fallback fonts, not Space Grotesk / DM Sans.Expected behavior
The woff2 fonts shipped in
fonts/are loaded and embedded in the PDF.Verified fix direction
Same machine, same font, same Chromium — navigating to a real
file://page instead ofsetContentloads the font:Options, roughly in order of robustness:
data:URLs during the font injection step ingeneratePDF()— no origin/security dependency at all, works withsetContentunchanged. ~5 lines: read each woff2, replaceurl('./fonts/X.woff2')withurl('data:font/woff2;base64,...').page.goto(pathToFileURL(tmp).href)— document origin becomesfile://, local subresources load; delete the temp file after rendering.page.route()interception serving the font bytes.Happy to send a PR once there's a preferred direction (I'd suggest option 1: it also keeps
renderHtmlToPdf()'s API unchanged for other callers).CLI tool
Claude Code
OS and Node.js version
Windows 11 / Node v24.14.0, Playwright 1.58.1 (Chromium headless shell) — but the blocking behavior is Chromium policy and applies on Linux/macOS as well.