Html to pdf
Skill t0ddharris/compound-marketing/.claude/skills/html-to-pdf
Export HTML pages to print-quality vector PDFs using Playwright's page.pdf() API. Trigger with /html-to-pdf or when the user wants to export an HTML page to PDF, create a print-ready PDF from HTML, or generate a PDF of a handout, card, flyer, or any HTML design.From its SKILL.md
npx -y skills add t0ddharris/compound-marketing --skill html-to-pdfAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
7.7 KB, ~1.9k tokens by cl100k_base, as published. Nobody here has run it
HTML to PDF — Print-Quality Export
Export HTML pages to high-quality, print-ready vector PDFs using Playwright's page.pdf() API.
When to Use
Trigger with /html-to-pdf or when the user asks to:
- Export an HTML page to PDF
- Create a print-ready PDF from HTML
- Generate a PDF of a handout, card, flyer, or any HTML design
Critical Rules
-
NEVER screenshot-to-PDF. Never render HTML as a screenshot (PNG) and convert to PDF. This rasterizes text into pixels — unacceptable for print. Always use Playwright's
page.pdf()which produces vector PDFs with selectable, scalable text. -
Always emulate screen media. Chromium's PDF renderer defaults to
printmedia which washes out colors (dark backgrounds turn grey, accents look dull). Forcescreenmedia to preserve exact browser colors:await page.emulateMedia({ media: 'screen', colorScheme: 'dark' }); -
Always set
printBackground: true. Without this, backgrounds are stripped entirely. -
RGB output only. Generate RGB PDFs. the user handles CMYK conversion in Adobe Acrobat with proper ICC profiles. Pillow's RGB→CMYK conversion produces off colors — never attempt it programmatically.
How It Works
The skill uses Playwright (Node.js) via a temporary script to:
- Launch headless Chromium
- Set viewport to match the HTML page dimensions
- Emulate screen media + dark color scheme for accurate color rendering
- Navigate to the local HTML file
- Export as PDF with exact page dimensions, zero margins, backgrounds enabled
Page Size — Pixels vs. Physical Units
Critical: Playwright's page.pdf() interprets pixel dimensions at 96 DPI. If your HTML body is 1056×1632px and you pass width: '1056px', the PDF page will be 11"×17" (1056÷96 = 11).
To get the correct physical size, you need two things:
- Physical units (
in,mm,cm) inpage.pdf()for the page dimensions scalefactor to shrink the viewport content to fit the smaller page
The viewport renders at the full HTML pixel size (e.g. 1056×1632). Without scale, the content overflows the smaller physical page. The scale factor = physical size ÷ (pixel size / 96).
Example: HTML body is 1056×1632px, target is 5.5"×8.5" at 192 DPI:
- PDF page at 96 DPI = 528×816 CSS px, but viewport is 1056×1632
- Scale = 528/1056 = 0.5 (equivalently: target DPI 96 ÷ design DPI 192 = 0.5)
- Result:
width: '5.5in', height: '8.5in', scale: 0.5
Quick formula: scale = 96 / designDPI. For 192 DPI → 0.5. For 144 DPI → 0.667. For 96 DPI → 1.0 (no scaling needed).
Export Script
Create this script at /tmp/pdf-export.mjs and run it with cd /tmp && node pdf-export.mjs:
import { chromium } from 'playwright-core';
// ── Configure these per export ──
const exports = [
// { name: 'descriptive-name', file: 'filename-without-extension' },
];
const baseDir = './marketing/events'; // adjust per job
const pageWidth = 1056; // match the HTML body width (px)
const pageHeight = 1632; // match the HTML body height (px)
const pdfWidth = '5.5in'; // physical page width — use in/mm/cm, NOT px
const pdfHeight = '8.5in'; // physical page height — use in/mm/cm, NOT px
// ─────────────────────────────────
const browser = await chromium.launch();
for (const p of exports) {
const context = await browser.newContext({
viewport: { width: pageWidth, height: pageHeight },
colorScheme: 'dark',
forcedColors: 'none',
});
const page = await context.newPage();
// Force screen media — critical for accurate colors
await page.emulateMedia({ media: 'screen', colorScheme: 'dark' });
await page.goto(`file://${baseDir}/${p.file}.html`, { waitUntil: 'networkidle' });
await page.pdf({
path: `${baseDir}/${p.file}.pdf`,
width: pdfWidth,
height: pdfHeight,
scale: 0.5, // 96 / designDPI — adjust if DPI changes
printBackground: true,
preferCSSPageSize: false,
margin: { top: '0', right: '0', bottom: '0', left: '0' },
});
console.log(`${p.name}: PDF saved → ${p.file}.pdf`);
await context.close();
}
await browser.close();
Step-by-Step Workflow
-
Read the HTML file to get the page dimensions from the CSS (
body { width: Xpx; height: Ypx; }). -
Create the export script at
/tmp/pdf-export.mjswith the correct:exportsarray (one entry per HTML file)baseDirpointing to the folder containing the HTML filespageWidthandpageHeightmatching the HTML body dimensions
-
Ensure playwright-core is installed:
cd /tmp && npm list playwright-core 2>/dev/null || npm install playwright-core -
Run the export:
cd /tmp && node pdf-export.mjs -
Open the PDF for review:
open /path/to/output.pdf -
Verify quality: Colors should match the browser exactly. Text should be selectable and crisp at any zoom level. Dark backgrounds should be true black, not washed-out grey.
Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
| Washed-out / grey backgrounds | Print media default | Add page.emulateMedia({ media: 'screen' }) |
| No backgrounds at all | Missing flag | Add printBackground: true |
| PDF is wrong physical size (e.g. 11×17 instead of 5.5×8.5) | Using px units in page.pdf(), or missing scale | Use physical units (in, mm) for width/height AND set scale: 96/designDPI (e.g. 0.5 for 192 DPI). Physical units alone cause clipping; scale alone leaves wrong page metadata. |
| Content clipped on right | Viewport too narrow | Match viewport.width to HTML body width exactly |
| Fonts not rendering | Google Fonts not loaded | Use waitUntil: 'networkidle' |
| Colors dull after printing | RGB→CMYK shift | Convert to CMYK in Adobe Acrobat with ICC profiles (not programmatic) |
Figma Handoff (Optional Post-Export Step)
After exporting to PDF, you can push the HTML design into Figma for team editing and visual refinement:
- Serve the HTML locally — The HTML file needs to be accessible via URL for Figma capture.
- Capture into Figma — Use
mcp__figma-remote-mcp__generate_figma_designto import the rendered page as a Figma design. - Choose output mode:
newFile— creates a fresh Figma file (good for new documents)existingFile— adds to an existing Figma file (good for adding pages to an ongoing project)
What this gives you: The design imports as a visual snapshot that the team can annotate, adjust typography, reposition elements, or polish in Figma's editor.
What it doesn't give you: Fully editable Figma components with proper text layers. The capture is a high-fidelity visual import, not a native Figma rebuild. For most whitepaper/datasheet workflows, this is the right trade-off: the HTML/CSS file remains the source of truth for content and layout, while Figma handles collaborative visual refinement.
When to use: Whitepapers, datasheets, one-pagers, or any multi-page branded document where the team wants to review or refine the design collaboratively in Figma before final delivery.
Output Location
Output saves alongside the source file.
Dependencies
- playwright-core (npm) — installed locally in
/tmp/node_modules/ - No global install needed;
cd /tmp && npm install playwright-coreis sufficient - Uses system Chromium bundled with Playwright
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.