agentleFS
jsathjsath/claude-skills/skills/dev/html-to-pdf/SKILL.md

html-to-pdf

Skill14 starsChanged 31 days agoMITView on GitHub

Builds polished PDF documents (one-pagers, reports, guides, proposals, briefs) as HTML and CSS with exact page layout, renders them with headless Chromium through Playwright, then checks every page as an image before handing the file over. Fixed page sizes, controlled page breaks, no split tables, no orphan headings, a page count you can promise. Use when someone says \"make a PDF\", \"turn this into a PDF report\", \"make a one-pager\", \"PDF version of this doc\", \"export this as a printable guide\", \"it has to fit on two pages\" or \"the PDF layout is broken\".

  • Installs packages

10 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

7 rules. 0 are common, 5 are distinctive, and 2 are very specific to this project.

  • 0

    Common

    Most of the 7 edited versions we read keep these.

  • 5

    Distinctive

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Plan the pages: Before writing HTML, write a one-line outline per page: what goes on page 1, page 2, and so on.

    line 31
  2. Very specific:

    Use print units (mm, in, pt) for page boxes, margins and type.

    line 52
  3. Very specific:

    Keep things whole: break-inside: avoid on cards, metric grids, callouts, figures and table rows.

    line 53
  4. Distinctive:

    No empty pages, and the last page should not be mostly blank.

    line 60
  5. Distinctive:

    Run the script, then check each page image for:

    line 64
  6. Distinctive:

    Images and charts: every image loaded, every SVG drawn, labels readable.

    line 70
  7. Distinctive:

    After any fix, even a one-line CSS change, re-render the PDF and regenerate every PNG before saying it passed.

    line 77

“Common” is measured: kept by at least 6 in 10 of the 7 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

HTML to PDF

Most converters give you a PDF but not control: a half-empty last page, a lone heading at the bottom, a table cut in half. This skill writes the document as HTML and CSS, prints it with headless Chromium, and looks at every page before calling it done.

What it does

  1. Turns content (notes, a draft, data, a brief) into a single self-contained HTML file laid out page by page.
  2. Renders it to PDF with headless Chromium. No window ever opens.
  3. Exports each page as a PNG, checks it by eye and by script, fixes problems and renders again.
  4. Hands over the PDF with its page count, size and the QA it passed.

What you need

  • Python 3.9 or newer.
  • Playwright and Chromium:
    pip install playwright pypdf pypdfium2
    python -m playwright install chromium
    pypdfium2 is only for PNG export. If it is missing, the script falls back to pdftoppm from poppler.
  • The content, the audience, the paper size (A4 or Letter) and a target page count. If any are missing and cannot be inferred, ask once; otherwise default to A4 and the shortest length that fits.
  • Puppeteer works too (same engine, same CSS).

Steps

  1. Plan the pages. Before writing HTML, write a one-line outline per page: what goes on page 1, page 2, and so on. A one-pager is one page. A report is usually a cover plus 2 to 6 pages. Decide the page count now, not after rendering.
  2. Copy the template. Start from templates/document.html. It has a cover page, a content page, a key-number grid, two columns, a table, a callout and a footer, all in neutral styling with [placeholders]. Swap the colour and font tokens in :root if the reader has a brand.
  3. Fill one .page block per planned page. Real content, not lorem ipsum. Lead with the point.
  4. Render:
    python scripts/render_pdf.py report.html out/report.pdf --png-dir qa/ --expect-pages 3
    It prints the page count, file size and PNG paths, and warns about overflowing content, fonts that did not load, broken images, and a mismatch between .page boxes and PDF pages. Add --strict to make any warning a failure.
  5. Look at every PNG against the QA list below. The script catches overflow; only your eyes catch ugly.
  6. Fix and render again. Repeat steps 4 and 5 until every page passes. Old PNGs are cleared on each run so you never review a stale render.

Layout rules

Two ways to lay out pages. Pick one per document.

  • Fixed pages (default, used by the template). Each <div class="page"> is exactly one sheet: width and height set to the paper size, overflow: hidden, break-after: page. You decide what goes on each page, so the page count is guaranteed. Best for one-pagers, covers, proposals and anything with a promised length.
  • Flowing text. For long prose (guides, manuals) let content flow and use @page { size: A4; margin: 20mm; } with break rules. Page count is then an output, not an input, so check it every render.

Always:

  • Set the paper size once, in @page { size: A4; margin: 0; } (or Letter), and render with prefer_css_page_size=True. Letter is 8.5in by 11in, A4 is 210mm by 297mm. Never mix them.
  • Use print units (mm, in, pt) for page boxes, margins and type. Body text 10 to 11pt, captions 8pt, nothing under 7.5pt.
  • Keep things whole: break-inside: avoid on cards, metric grids, callouts, figures and table rows. break-after: avoid on headings so a heading never ends a page.
  • orphans: 3; widows: 3; on paragraphs so a single line is never stranded.
  • Tables: thead { display: table-header-group; } repeats the header if a long table must continue. Otherwise move the whole table to the next page or split it at a logical row.
  • Turn on print-color-adjust: exact and render with print backgrounds, or tinted panels vanish.
  • Self-contained file: inline CSS, inline SVG charts, images as local files or data URIs. Every remote font or image is a chance to render with a fallback.
  • Fonts: system font stacks are safest. If you load a web font, keep the fallback stack and check the script's font warnings.
  • Pin footers with margin-top: auto in a flex column, not absolute positioning, so overflow is detectable.
  • No empty pages, and the last page should not be mostly blank. Consolidate or cut copy.

QA

Run the script, then check each page image for:

  • Overflow: text clipped at the bottom or right edge, or content hidden under the footer.
  • Orphans: a heading at the foot of a page with its text on the next, or one stray line of a paragraph.
  • Cut tables and cards: rows, metric grids or callouts split across pages.
  • Fonts: the intended typeface, not a Times fallback. The script lists any font that failed to load.
  • Images and charts: every image loaded, every SVG drawn, labels readable.
  • Contrast: bold text, links and labels inside dark or tinted panels are still visible. A global text colour often hides nested strong on dark cards.
  • Page count: matches the plan. Use --expect-pages N so the script fails if it drifts.
  • Folios and footers: page numbers correct ("2 / 3"), no stale totals left from an earlier draft.
  • File size: a few MB at most for email (the script warns over 10 MB). Resize large photos before embedding.
  • Text check (optional): extract the text (pdftotext -layout) and search for leftover [placeholders], typos in names and figures that must match the source.

After any fix, even a one-line CSS change, re-render the PDF and regenerate every PNG before saying it passed. Screenshots from before a fix prove nothing.

Output

  • The PDF, named in kebab-case for what it is: q3-board-report.pdf, onboarding-guide.pdf.
  • The HTML source next to it, so the next edit is a re-render.
  • A short note: page count, paper size, file size, and anything you could not verify.
  • QA images stay out of the deliverable folder.

Honest limits

  • Chromium print is not a typesetting engine. Hyphenation, footnotes and running headers that change by chapter are weak. For a book, use a dedicated tool.
  • Fixed pages hide overflow by design (overflow: hidden). That is why the script measures it; never skip the warnings.
  • break-inside: avoid is a request. A block taller than a page will still split.
  • Remote fonts and images can fail silently on slow networks. Self-contain the file when it matters.
  • Visual QA needs a model or person that can see images.
  • It produces a print PDF, not an accessible tagged PDF or a fillable form.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.