make-pdf

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

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"
以下配置仅选择稳定路径;不会安装任何内容或获取授权:
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
pdf
capability. Read
references/RUNTIME.md
before attempting installation. Pure planning or document review can continue without it. If unavailable, follow the consent-first
pdf
capability handoff in
references/RUNTIME.md
. It expands to
pdf
,
diagram
, and
browser
; do not guess a checkout-relative setup command.
此工作流需要
pdf
功能。尝试安装前请阅读
references/RUNTIME.md
。纯规划或文档审核可在无此功能的情况下继续。 若此功能不可用,请遵循
references/RUNTIME.md
中“先获取授权”的
pdf
功能移交流程。该功能扩展包含
pdf
diagram
browser
;请勿猜测基于检出路径的设置命令。

make-pdf: publication-quality PDFs from markdown

make-pdf:从Markdown生成出版级PDF

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 (▯). the explicitly approved
pdf
runtime capability attempts to install
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).
将.md文件转换为类似Faber & Faber出版社风格的PDF:1英寸边距、正文左对齐、全程使用Helvetica字体、弯引号和破折号,可添加封面和可点击目录,需要时可添加斜向DRAFT水印。从PDF中复制粘贴可得到干净的文本,不会出现“S a i l i n g”这样的拆分字符。
在Linux系统上,需安装
fonts-liberation
以确保正确渲染——Helvetica和Arial字体默认未预装,Liberation Sans是标准的 metric-compatible 替代字体。CI和Docker构建会通过Dockerfile.ci自动安装该字体。
表情符号需要彩色 emoji 字体。macOS(Apple Color Emoji)和Windows(Segoe UI Emoji)默认自带;大多数Linux发行版和容器则没有,因此表情符号会显示为空框(▯)。已明确批准的
pdf
运行时功能会尝试在Linux系统上安装
fonts-noto-color-emoji
(支持apt/dnf/pacman/apk,尽最大努力),打印CSS会依次回退使用Apple/Segoe/Noto emoji字体族。设置
GSTACK_SKIP_FONTS=1
可跳过安装(适用于无sudo权限的CI环境、受控或离线机器)。

Core patterns

核心使用模式

80% case — memo/letter

80%场景——备忘录/信函

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
一条命令,无需参数。默认生成包含页眉+页码+CONFIDENTIAL页脚的整洁PDF。
bash
$P generate letter.md                 # 生成/tmp/letter.pdf
$P generate letter.md letter.pdf      # 指定输出路径

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.
bash
$P generate --cover --toc --author "Garry Tan" --title "On Horizons" \
  essay.md essay.pdf
Markdown中的每个顶级H1标题都会开启新页面。若备忘录包含多个H1但不需要分页,可使用
--no-chapter-breaks
参数禁用。

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.
bash
$P generate --watermark DRAFT memo.md draft.pdf
在每一页添加透明度为10%的斜向DRAFT水印。草稿定稿后,移除该参数重新生成即可。

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.
bash
$P preview essay.md
使用相同的打印CSS渲染HTML并在浏览器中打开。编辑Markdown时刷新页面即可预览。在准备就绪前可跳过PDF转换步骤。

Brand-free (no CONFIDENTIAL footer)

无品牌标识(无CONFIDENTIAL页脚)

bash
$P generate --no-confidential memo.md memo.pdf
bash
$P generate --no-confidential memo.md memo.pdf

Diagrams — mermaid and excalidraw fences render as pictures

图表——Mermaid和Excalidraw代码块渲染为图片

A column-0
```mermaid
or
```excalidraw
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.
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
```mermaid page=portrait            ← veto auto-landscape for this diagram
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.
Markdown中第0列的
```mermaid
```excalidraw
代码块会渲染为清晰的矢量图,完全离线(使用本地打包文件,无CDN依赖)。缩进的代码块(位于列表内)默认保持纯代码块格式。若代码块存在错误,会生成带有解析错误的红色可见诊断块——绝不会静默显示原始代码。
代码块信息字符串选项:
```mermaid title="Auth flow"        ← 标题 + aria-label
```mermaid render=false             ← 保持为代码块(当前默认行为)
```mermaid page=landscape           ← 强制此图表单独占用横向页面
```mermaid page=portrait            ← 否决此图表的自动横向设置
```excalidraw
代码块包含完整的.excalidraw场景文件(即excalidraw.com保存的格式)。从英文描述生成新图表是
/diagram
的功能——它会生成可编辑的三元组(源代码、.excalidraw、SVG/PNG),并与本工具配合使用:在Markdown中嵌入.mmd源代码,而非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.
Remote (http/https) images are blocked with a visible placeholder by default — offline posture; pass
--allow-network
to fetch them. An image that resolves outside the markdown's directory (even via symlink) still inlines, but warns loudly;
--strict
makes it fatal. Files over 64MB or non-regular files (fifos, devices) degrade to a placeholder instead of hanging the run.
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}
.
本地图片会自动嵌入(相对路径基于Markdown文件所在目录解析)。所有图片都会适配内容框大小——绝不出现截断。过大的照片会缩小至打印分辨率(300dpi),因此文件体积更小且无可见画质损失。
远程(http/https)图片默认会被阻止并显示可见占位符——遵循离线策略;添加
--allow-network
参数可允许获取远程图片。若图片位于Markdown目录之外(即使通过符号链接),仍会嵌入,但会发出强烈警告;使用
--strict
参数会将此情况视为致命错误。超过64MB的文件或非常规文件(管道、设备文件)会降级显示为占位符,避免程序挂起。
图片专属指令,需紧跟在图片之后:
![chart](data.png){width=full}      ← 拉伸至内容框宽度
![chart](data.png){width=50%}       ← 百分比或3in/8cm/200px
![wide](arch.png){page=landscape}   ← 为该图片单独分配横向页面
![wide](shot.png){page=portrait}    ← 否决自动横向设置
宽幅、小字的图表图片会自动升级为单独的横向页面(判断条件:宽高比≥1.8,宽度超过内容框约2.5倍,且alt文本包含图表相关词汇——diagram/architecture/flowchart/chart/graph)。升级后的页面会垂直居中。若启发式判断出错,可使用
{page=portrait}
否决;若未自动升级,只需添加
{page=landscape}
即可。

Other formats — single-file HTML and Word

其他格式——单文件HTML和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.
bash
$P generate readme.md out.html --to html    # 生成单个独立文件:内联
                                            # SVG图表、data-URI图片,
                                            # 无网络依赖,适合屏幕阅读
$P generate readme.md out.docx --to docx    # Word文档:内容保真(标题、
                                            # 表格、代码、图表转为PNG)——
                                            # 布局由Word控制,而非本工具
--to
指定输出格式。
--format
是完全不同的参数(
--page-size
的别名)——请勿混淆。

CI mode — fail loud on missing assets

CI模式——缺失资源时直接报错

bash
$P generate docs.md --strict     # missing, remote, out-of-tree, oversized,
                                 # and non-regular-file images exit non-zero
                                 # instead of warn + placeholder
bash
$P generate docs.md --strict     # 缺失、远程、目录外、过大或
                                 # 非常规文件的图片会导致非零退出码
                                 # 而非警告+占位符

Common 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)
页面布局:
  --margins <dim>            默认1英寸 | 72pt | 2.54cm | 25mm
  --page-size letter|a4|legal

结构:
  --cover                    封面(标题、作者、日期、细线条分隔)
  --toc                      带页码的可点击目录
  --no-chapter-breaks        不在每个H1标题处开启新页面

品牌标识:
  --watermark <text>         斜向水印(如“DRAFT”“CONFIDENTIAL”)
  --header-template <html>   自定义页眉
  --footer-template <html>   自定义页脚(与--page-numbers互斥)
  --no-confidential          隐藏右下角的CONFIDENTIAL页脚

输出:
  --to pdf|html|docx         输出格式(默认:pdf)。html = 单个
                             独立文件;docx = 内容保真。
  --strict                   缺失、远程、目录外、过大或非常规文件的图片
                             会导致运行失败(CI模式)。
  --page-numbers             “第N页/共M页”页脚(默认开启)
  --tagged                   可访问性PDF(默认开启)
  --outline                  从标题生成PDF书签(默认开启)
  --quiet                    抑制stderr中的进度输出
  --verbose                  显示各阶段耗时

网络:
  --allow-network            获取外部图片。默认关闭:远程图片
                             会显示可见的阻止占位符
                             (打印时不会获取跟踪像素)。

元数据:
  --title "..."              文档标题(默认使用第一个H1标题)
  --author "..."             封面及PDF元数据中的作者
  --date "..."               封面日期(默认使用今日日期)

When Claude should run it

Claude应何时运行此工具

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.
留意Markdown转PDF的需求意图。出现以下任一表述时,运行
$P generate
  • “你能把这个Markdown转成PDF吗”
  • “导出为PDF”
  • “把这封信转成PDF”
  • “我需要这篇文章的PDF版本”
  • “帮我把这个打印成PDF”
若用户打开了一个.md文件并说“让它看起来更美观”,可建议使用
$P generate --cover --toc
,并在运行前征得用户同意。

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
    .
  • "[remote image blocked]" placeholder in the output → 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
    .
  • 输出为空/空白 → 检查browse守护进程是否运行:
    $B status
  • 复制粘贴时文本碎片化 → 这是highlight.js的输出(第4阶段)。待
    --no-syntax
    参数推出后可重试。目前,可移除代码块后重新生成。
  • Paged.js超时 → 可能是Markdown中没有标题。移除
    --toc
    参数。
  • 输出中出现“[remote image blocked]”占位符 → 添加
    --allow-network
    参数(请注意,这会赋予Markdown文件从其图片URL获取资源的权限)。
  • 生成的PDF过高/过宽 → 使用
    --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 unavailable
Capture the path:
PDF=$($P generate letter.md)
— then use
$PDF
.
标准输出:/tmp/letter.pdf          ← 仅输出路径,单行
标准错误输出:Rendering HTML...        ← 进度提示(除非使用--quiet)
        Generating PDF...
        Done in 1.5s. 43 words · 22KB · /tmp/letter.pdf

退出码:0 成功 / 1 参数错误 / 2 渲染错误 / 3 Paged.js超时
           / 4 browse不可用
捕获路径:
PDF=$($P generate letter.md)
—— 之后可使用
$PDF
变量。