The CV lives in src/pages/cv-print.astro — a fully static, print-optimized HTML page with noindex. Playwright visits it headlessly and saves the output to public/, where the download link already points.
pnpm dev # start local server on :4321
pnpm cv:pdf # run scripts/generate-cv-pdf.mjs
The generation script:
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('http://localhost:4321/cv-print', { waitUntil: 'networkidle' });
await page.pdf({
path: 'public/Christian_Elias_Cruz_Gonzalez_esp.pdf',
format: 'A4',
printBackground: false,
margin: { top: '18mm', right: '18mm', bottom: '14mm', left: '18mm' },
});
await browser.close();
The page uses @page { size: A4; margin: ... } so margins are declared twice — once in CSS for browser preview, once in page.pdf() for the headless render. In practice Playwright ignores @page margins and uses the ones passed to pdf().
Why not astro-pdf
astro-pdf hooks into the build and visits pages post-build via a preview server. That works but adds build time and a dev dependency that trails Astro major releases. A standalone script is simpler: edit the page, run pnpm cv:pdf, commit the PDF. No build coupling.
ATS considerations
The resulting PDF is text-based (not a rasterized image), so ATS parsers extract content correctly. A few patterns that break ATS even in text PDFs:
- Em dashes (
—) in date ranges — some parsers (Greenhouse, Lever) misread them as unknown characters. Use simple hyphens. +Nprefix on numbers inside bullets — some parsers treat+as a boolean search operator. Writemás de Norover N.→and<in prose — replace with plain text equivalents before generating.