--- name: make-pdf preamble-tier: 1 version: 1.0.0 description: | Turn any markdown file into a publication-quality PDF. Proper 1in margins, intelligent page breaks, page numbers, cover pages, running headers, curly quotes and em dashes, clickable TOC, diagonal DRAFT watermark. Not a draft artifact — a finished artifact. Use when asked to "make a PDF", "export to PDF", "turn this markdown into a PDF", or "generate a document". (gstack) voice-triggers: - "make this a pdf" - "make it a pdf" - "export to pdf" - "turn this into a pdf" - "turn this markdown into a pdf" - "generate a pdf" - "make a pdf from" - "pdf this markdown" triggers: - markdown to pdf - generate pdf - make pdf - export pdf allowed-tools: - Bash - Read - AskUserQuestion --- {{PREAMBLE}} # make-pdf: publication-quality PDFs from markdown Turn `.md` files into PDFs that look like Faber & Faber essays: 1in margins, left-aligned body, Helvetica throughout, curly quotes and em dashes, optional cover page and clickable TOC, diagonal DRAFT watermark when you need it. Copy-paste from the PDF produces clean words, never "S a i l i n g". On Linux, install `fonts-liberation` for correct rendering — Helvetica and Arial aren't present by default, and Liberation Sans is the standard metric-compatible fallback. CI and Docker builds install it automatically via Dockerfile.ci. Emoji need a color-emoji font. macOS (Apple Color Emoji) and Windows (Segoe UI Emoji) ship one; most Linux distros and containers ship none, so emoji render as empty boxes (▯). `./setup` auto-installs `fonts-noto-color-emoji` on Linux (apt/dnf/pacman/apk, best-effort) and the print CSS falls back through Apple / Segoe / Noto emoji families. Set `GSTACK_SKIP_FONTS=1` to skip the install (CI without sudo, managed or offline machines). ## Core patterns ### 80% case — memo/letter One command, no flags. Gets a clean PDF with running header + page numbers + CONFIDENTIAL footer by default. ```bash $P generate letter.md # writes /tmp/letter.pdf $P generate letter.md letter.pdf # explicit output path ``` ### Publication mode — cover + TOC + chapter breaks ```bash $P generate --cover --toc --author "Garry Tan" --title "On Horizons" \ essay.md essay.pdf ``` Each top-level H1 in the markdown starts a new page. Disable with `--no-chapter-breaks` for memos that happen to have multiple H1s. ### Draft-stage watermark ```bash $P generate --watermark DRAFT memo.md draft.pdf ``` Diagonal 10% opacity DRAFT across every page. When the draft is final, drop the flag and regenerate. ### Fast iteration via preview ```bash $P preview essay.md ``` Renders HTML with the same print CSS and opens it in your browser. Refresh as you edit the markdown. Skip the PDF round trip until you're ready. ### Brand-free (no CONFIDENTIAL footer) ```bash $P generate --no-confidential memo.md memo.pdf ``` ### Diagrams — mermaid and excalidraw fences render as pictures Any ` ```mermaid ` or ` ```excalidraw ` fence in the markdown renders as a crisp vector diagram, fully offline (vendored bundle, no CDN). A broken fence produces a visible red diagnostic block with the parse error — never silent raw code. Fence info-string options: ``` ```mermaid title="Auth flow" ← caption + aria-label ```mermaid render=false ← keep it as a code block (today's behavior) ```mermaid page=landscape ← force this diagram onto a landscape page ``` A ` ```excalidraw ` fence contains a full .excalidraw scene file (what excalidraw.com saves). Authoring NEW diagrams from English is `/diagram`'s job — it emits an editable triplet (source, .excalidraw, SVG/PNG) and pairs with this skill: embed the `.mmd` source in your markdown, not the PNG. ### Images — scaled right, never truncated Local images inline automatically (relative paths resolve against the markdown file). Every image caps at the content box — zero truncation, ever. Oversized photos downscale to print resolution (300dpi) so payloads stay small with no visible quality loss. Per-image directives, written immediately after the image: ``` ![chart](data.png){width=full} ← stretch to content-box width ![chart](data.png){width=50%} ← percentage or 3in/8cm/200px ![wide](arch.png){page=landscape} ← give it its own landscape page ![wide](shot.png){page=portrait} ← veto auto-landscape ``` Wide, small-text diagram images auto-promote to their own landscape page (conservative: aspect ≥ 1.8, width over ~2.5x the content box, AND a diagram-ish alt word — diagram/architecture/flowchart/chart/graph). The promoted page is vertically centered. When the heuristic guesses wrong, `{page=portrait}` vetoes it; false negatives just need `{page=landscape}`. ### Other formats — single-file HTML and Word ```bash $P generate readme.md out.html --to html # ONE self-contained file: inline # SVG diagrams, data-URI images, # zero network refs, screen-readable $P generate readme.md out.docx --to docx # Word: content fidelity (headings, # tables, code, diagrams as PNG) — # layout is Word's, not ours ``` `--to` is the output format. `--format` is something else entirely (a `--page-size` alias) — don't confuse them. ### CI mode — fail loud on missing assets ```bash $P generate docs.md --strict # missing/remote images exit non-zero # instead of warn + placeholder ``` ## Common flags ``` Page layout: --margins 1in (default) | 72pt | 2.54cm | 25mm --page-size letter|a4|legal Structure: --cover Cover page (title, author, date, hairline rule) --toc Clickable TOC with page numbers --no-chapter-breaks Don't start a new page at every H1 Branding: --watermark Diagonal watermark ("DRAFT", "CONFIDENTIAL") --header-template Custom running header --footer-template Custom footer (mutex with --page-numbers) --no-confidential Suppress the CONFIDENTIAL right-footer Output: --to pdf|html|docx Output format (default: pdf). html = single self-contained file; docx = content fidelity. --strict Missing/remote images fail the run (CI mode). --page-numbers "N of M" footer (default on) --tagged Accessible PDF (default on) --outline PDF bookmarks from headings (default on) --quiet Suppress progress on stderr --verbose Per-stage timings Network: --allow-network Fetch external images. Off by default (blocks tracking pixels). Metadata: --title "..." Document title (defaults to first H1) --author "..." Author for cover + PDF metadata --date "..." Date for cover (defaults to today) ``` ## When Claude should run it Watch for markdown-to-PDF intent. Any of these patterns → run `$P generate`: - "Can you make this markdown a PDF" - "Export it as a PDF" - "Turn this letter into a PDF" - "I need a PDF of the essay" - "Print this as a PDF for me" If the user has a `.md` file open and says "make it look nice", propose `$P generate --cover --toc` and ask before running. ## Debugging - Output looks empty / blank → check browse daemon is running: `$B status`. - Fragmented text on copy-paste → highlight.js output (Phase 4). Retry with `--no-syntax` once that flag exists. For now, remove fenced code blocks and regenerate. - Paged.js timeout → probably no headings in the markdown. Drop `--toc`. - External image missing → add `--allow-network` (understand you're giving the markdown file permission to fetch from its image URLs). - Generated PDF too tall/wide → `--page-size a4` or `--margins 0.75in`. ## Output contract ``` stdout: /tmp/letter.pdf ← just the path, one line stderr: Rendering HTML... ← progress spinner (unless --quiet) Generating PDF... Done in 1.5s. 43 words · 22KB · /tmp/letter.pdf exit code: 0 success / 1 bad args / 2 render error / 3 Paged.js timeout / 4 browse unavailable ``` Capture the path: `PDF=$($P generate letter.md)` — then use `$PDF`.