make-pdf
Original:🇺🇸 English
Translated
3 scripts
Turn any Markdown file into a publication-quality, print-ready PDF with proper margins, page numbers, cover pages, running headers, and a clickable table of contents. Mermaid and Excalidraw fences render as vector diagrams fully offline, and --to html or --to docx emit a self-contained web page or Word document. Use when asked to "make a PDF", "export to PDF", "turn this markdown into a document", or "generate a document".
3installs
Sourcetime-attack/gstack
Added on
NPX Install
npx skill4agent add time-attack/gstack make-pdfTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →Host-neutral runtime bindings
These assignments select stable paths only; they do not install anything or grant consent:
bash
GSTACK_HOME="${GSTACK_HOME:-$HOME/.gstack}"
GSTACK_ROOT="$GSTACK_HOME"
GSTACK_STATE_ROOT="$GSTACK_HOME"
GSTACK_BIN="$GSTACK_HOME/bin"
BUN_CMD="$GSTACK_BIN/bun"
B="$GSTACK_BIN/browse"
P="$GSTACK_BIN/make-pdf"Optional runtime binding
This workflow requires the capability. Read before attempting installation. Pure planning or document review can continue without it.
If unavailable, follow the consent-first capability handoff in . It expands to , , and ; do not guess a checkout-relative setup command.
pdfreferences/RUNTIME.mdpdfreferences/RUNTIME.mdpdfdiagrambrowsermake-pdf: publication-quality PDFs from markdown
Turn 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".
.mdOn Linux, install 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.
fonts-liberationEmoji 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 (▯). the explicitly approved runtime capability attempts to install on Linux
(apt/dnf/pacman/apk, best-effort) and the print CSS falls back through Apple /
Segoe / Noto emoji families. Set to skip the install (CI
without sudo, managed or offline machines).
pdffonts-noto-color-emojiGSTACK_SKIP_FONTS=1Core 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 pathPublication mode — cover + TOC + chapter breaks
bash
$P generate --cover --toc --author "Garry Tan" --title "On Horizons" \
essay.md essay.pdfEach top-level H1 in the markdown starts a new page. Disable with
for memos that happen to have multiple H1s.
--no-chapter-breaksDraft-stage watermark
bash
$P generate --watermark DRAFT memo.md draft.pdfDiagonal 10% opacity DRAFT across every page. When the draft is final, drop
the flag and regenerate.
Fast iteration via preview
bash
$P preview essay.mdRenders 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.pdfDiagrams — mermaid and excalidraw fences render as pictures
A column-0 or fence in the markdown renders
as a crisp vector diagram, fully offline (vendored bundle, no CDN). Indented
fences (inside lists) stay plain code blocks by design. A broken fence
produces a visible red diagnostic block with the parse error — never silent
raw code.
```mermaid```excalidrawFence 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
```mermaid page=portrait ← veto auto-landscape for this diagramA fence contains a full .excalidraw scene file (what
excalidraw.com saves). Authoring NEW diagrams from English is 's
job — it emits an editable triplet (source, .excalidraw, SVG/PNG) and pairs
with this skill: embed the source in your markdown, not the PNG.
```excalidraw/diagram.mmdImages — 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.
Remote (http/https) images are blocked with a visible placeholder by
default — offline posture; pass to fetch them. An image
that resolves outside the markdown's directory (even via symlink) still
inlines, but warns loudly; makes it fatal. Files over 64MB or
non-regular files (fifos, devices) degrade to a placeholder instead of
hanging the run.
--allow-network--strictPer-image directives, written immediately after the image:
{width=full} ← stretch to content-box width
{width=50%} ← percentage or 3in/8cm/200px
{page=landscape} ← give it its own landscape page
{page=portrait} ← veto auto-landscapeWide, 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,
vetoes it; false negatives just need .
{page=portrait}{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--format--page-sizeCI mode — fail loud on missing assets
bash
$P generate docs.md --strict # missing, remote, out-of-tree, oversized,
# and non-regular-file images exit non-zero
# instead of warn + placeholderCommon flags
Page layout:
--margins <dim> 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 <text> Diagonal watermark ("DRAFT", "CONFIDENTIAL")
--header-template <html> Custom running header
--footer-template <html> 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, out-of-tree, oversized, or
non-regular-file 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: remote
images render as a visible blocked placeholder
(no tracking pixels fetch at print time).
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 file open and says "make it look nice", propose
and ask before running.
.md$P generate --cover --tocDebugging
- Output looks empty / blank → check browse daemon is running: .
$B status - Fragmented text on copy-paste → highlight.js output (Phase 4). Retry with
once that flag exists. For now, remove fenced code blocks and regenerate.
--no-syntax - Paged.js timeout → probably no headings in the markdown. Drop .
--toc - "[remote image blocked]" placeholder in the output → add (understand you're giving the markdown file permission to fetch from its image URLs).
--allow-network - Generated PDF too tall/wide → or
--page-size a4.--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 unavailableCapture the path: — then use .
PDF=$($P generate letter.md)$PDF