fix(generate-pdf): inline local fonts as data: URLs so they actually embed#952
fix(generate-pdf): inline local fonts as data: URLs so they actually embed#952shuhia wants to merge 3 commits into
Conversation
The main-module guard compared import.meta.url against a hand-built
`file://${resolve(process.argv[1])}` string. On Windows, resolve()
returns backslash paths (C:\... or \\wsl.localhost\... UNC), so the
comparison never matched: the CLI entry point never ran and the script
exited 0 silently without generating anything.
Use pathToFileURL() like the other CLI scripts already do (scan.mjs,
scan-ats-full.mjs, update-system.mjs, followup-cadence.mjs), and fix
the two other hand-built file:// URLs in the same file (fonts
injection and the Playwright baseURL), which produced invalid
backslash URLs on Windows.
Fixes santifer#948
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…embed
renderHtmlToPdf() loads documents with page.setContent(), which leaves
the page at about:blank. Chromium blocks file:// subresource loads from
non-file pages ('Not allowed to load local resource'), so the file://
font URLs injected by generatePDF() never loaded and every PDF silently
fell back to system fonts -- on all platforms, since the tool shipped.
Replace the file:// URL injection with inlineLocalFonts(), which inlines
url('./fonts/...') references as base64 data: URLs inside
renderHtmlToPdf(). data: URLs carry no origin restriction, and doing it
in the renderer also covers generate-cover-letter.mjs, which calls
renderHtmlToPdf() directly without any font handling.
Verified on Windows: PDFs now embed Space Grotesk + DM Sans subsets
(Type3 with ToUnicode maps; extracted text is byte-identical to the
fallback PDFs, so ATS parseability is unchanged).
Fixes santifer#951
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis PR inlines local ./fonts/* references into base64 data: URLs via a new exported inlineLocalFonts(html), removes the previous file:// rewrite, integrates inlining into renderHtmlToPdf() (using pathToFileURL(baseDir).href), updates the isMain check, and adds tests covering inlining, missing files, and path-escape rejection. ChangesFont inlining for PDF generation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@generate-pdf.mjs`:
- Around line 244-261: inlineLocalFonts currently only checks for '..' in the
filename which allows absolute/path-traversal escapes; fix by computing fontsDir
= resolve(__dirname, 'fonts') and for each name compute resolved =
resolve(fontsDir, name) and verify containment (e.g. path.relative(fontsDir,
resolved) doesn't start with '..' or using startsWith on resolved normalized
paths) before calling readFile; if the resolved path is outside fontsDir, skip
and warn (keep original reference), otherwise readFile(resolved) and continue
using the resolved buffer when building data URL.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 198c69ba-7824-4b5a-b17d-3863a32c941a
📒 Files selected for processing (2)
generate-pdf.mjstest-all.mjs
The previous guard only rejected names containing '..', but the regex admits names starting with '/', and resolve(fontsDir, '/etc/passwd') returns the absolute path verbatim -- escaping fonts/. Resolve each reference and require it to stay inside fonts/ via path.relative(). Flagged by CodeRabbit on santifer#952. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fixes #951
What
Custom fonts have never been embedded in generated PDFs (issue #951):
renderHtmlToPdf()usespage.setContent(), which leaves the page atabout:blank, and Chromium blocksfile://subresource loads from non-file pages — so thefile://font URLs injected bygeneratePDF()were always silently dropped and every PDF fell back to system fonts, on all platforms.This PR takes option 1 from the issue:
inlineLocalFonts(html)(new, exported): replacesurl('./fonts/<file>')references with base64data:URLs read from the repo'sfonts/directory.data:URLs carry no origin restriction, so they load from any page — no change to how the document is loaded. Missing files keep their original reference (with a warning);..traversal outsidefonts/is refused.renderHtmlToPdf(), notgeneratePDF(), sogenerate-cover-letter.mjs— which callsrenderHtmlToPdf()directly and previously had no font handling at all — is covered too.file://regex injection ingeneratePDF()(prefix replace + quote-fixup) is removed; it was both the broken mechanism and dead weight once inlining happens in the renderer.test-all.mjs(section 17): inlining producesdata:font/woff2URLs, missing fonts keep their reference, traversal is refused.Verification (Windows 11, Node v24.14.0, Playwright 1.58.1)
Embedded fonts in a PDF rendered from
templates/cv-template.html, extracted from the PDF's font descriptors:/FontNameentriesAAAAAA+Arial-BoldMT,BAAAAA+ArialMT— system fallbackAAAAAA+Space-Grotesk-Light-Light,BAAAAA+DM-Sans-9pt-14pt-Thin,CAAAAA+Space-Grotesk-Light-Light,DAAAAA+DM-Sans-9pt-14pt-ThinChromium emits the variable woff2 fonts as Type3 glyph programs with
/ToUnicodemaps (4 present). ATS parseability is unchanged: text extracted with pdf.js from the before/after PDFs is byte-identical (386 chars, same content).node test-all.mjs: 248 passed (245 + the 3 new checks) — remaining failures on this machine are the same pre-existing Windows-environment issues as on unmodifiedmain(Go toolchain, SKILL.md symlink checkout,chmod); Linux CI should be green.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Tests