ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

gstack make-pdf 实战指南:把 Markdown 变成出版级 PDF 的完整管线

gstack make-pdf 实战指南:把 Markdown 变成出版级 PDF 的完整管线 gstack make-pdf 实战指南把 Markdown 变成出版级 PDF 的完整管线【免费下载链接】gstackUse Garry Tans exact Claude Code setup: 23 opinionated tools that serve as CEO, Designer, Eng Manager, Release Manager, Doc Engineer, and QA项目地址: https://gitcode.com/GitHub_Trending/gs/gstackgstack 的make-pdf技能将任意 Markdown 文件渲染为出版级 PDF1 英寸页边距、智能分页、页码、封面、running header、弯引号与破折号、可点击目录、斜向 DRAFT 水印。本文基于 make-pdf/SKILL.md 的完整功能说明结合make-pdf/src/下的源码实现讲清楚每条命令的用法、每个参数的语义、渲染管线的分层结构以及排障时该看哪里。读完后你能在自己的 gstack 环境中直接调用$P generate产出成品文档并能读懂其底层实现。一、这是什么gstack 的 make-pdf 技能make-pdf是 gstack 技能集合中的文档生成技能frontmatter 中name: make-pdf、preamble-tier: 1见 SKILL.md 文件头。它产出的 PDF 被定位为finished artifact而非草稿正文左对齐、全程 Helvetica、弯引号curly quotes与 em dash 排版、复制粘贴出的是干净单词而不是S a i l i n g这种按字形切散的结果。触发意图很宽用户说make a PDF、export to PDF、turn this markdown into a PDF、generate a document甚至语音转写别名make this a pdf、pdf this markdown 等都应该调用该技能。技能前置检查$P 是什么SKILL.md 中所有命令都通过$P变量引用二进制它在技能启动时按以下优先级解析来自 SKILL.md 的 MAKE-PDF SETUP 检查块_ROOT$(git rev-parse --show-toplevel 2/dev/null) P # 1. 显式环境变量 [ -n $MAKE_PDF_BIN ] [ -x $MAKE_PDF_BIN ] P$MAKE_PDF_BIN # 2. 仓库内 vendored 安装 [ -z $P ] [ -n $_ROOT ] [ -x $_ROOT/.claude/skills/gstack/make-pdf/dist/pdf ] P$_ROOT/.claude/skills/gstack/make-pdf/dist/pdf # 3. 用户目录安装默认 [ -z $P ] P$HOME/.claude/skills/gstack/make-pdf/dist/pdf打印MAKE_PDF_READY: $P后后续所有命令都写$P而不是硬编码路径保证可移植性打印MAKE_PDF_NOT_AVAILABLE时说明二进制还没构建——需在 gstack 仓库执行./setup构建后重试。平台字体前提PDF 的观感高度依赖字体SKILL.md 给出了两个平台级前提Linux 需安装fonts-liberation。Helvetica 和 Arial 在 Linux 上默认不存在Liberation Sans 是标准的度量兼容metric-compatible回退字体。CI 与 Docker 构建通过 Dockerfile.ci 自动安装。Emoji 需要彩色 emoji 字体。macOSApple Color Emoji与 WindowsSegoe UI Emoji自带大多数 Linux 发行版和容器不带emoji 会渲染成空心方块▯。./setup会在 Linux 上自动安装fonts-noto-color-emojiapt/dnf/pacman/apk尽力而为打印 CSS 的字体栈依次回退到 Apple / Segoe / Noto emoji 族。CI 无 sudo、托管或离线机器可设GSTACK_SKIP_FONTS1跳过安装。字体策略在源码 make-pdf/src/print-css.ts 中有单一事实来源single source of truth// Metric-compatible sans stack: Helvetica (macOS), Liberation Sans (Linux), Arial (Windows) const SANS_STACK Helvetica, Liberation Sans, Arial; // CJK fallback families追加到正文字体栈末尾 const CJK_STACK PingFang SC, Heiti SC, Noto Sans CJK SC, ...; // 彩色 emoji 族Apple / Segoe / Noto放在通用 sans-serif 之前 const EMOJI_FAMILIES Apple Color Emoji, Segoe UI Emoji, Noto Color Emoji;注释还解释了为什么不打包 webfont规避 Chromium 逐字形Tj的 bug——那会破坏复制粘贴时的文本提取。CJK 文档苹方、黑体、Noto Sans CJK 等与 emoji 都在字体栈中有显式位置。二、命令速览与输出契约命令注册表在 make-pdf/src/commands.ts 中集中定义是 CLI 分发、SKILL.md 文档生成和测试共用的单一事实来源。四个命令命令作用$P generate input.md [output.pdf]Markdown 渲染为 PDF80% 的使用场景$P generate --cover --toc essay.md out.pdf完整出版版式封面 可点击目录$P generate --watermark DRAFT memo.md draft.pdf斜向 DRAFT 水印$P preview input.md渲染 HTML 并在浏览器打开快速迭代$P setup校验 browse Chromium pdftotext 并跑冒烟测试$P --help完整 flag 参考输出契约SKILL.md 明确约定也是 make-pdf/src/cli.ts 文件头注释的 DX 规格stdout: 成功时只有输出路径一行别无其他 stderr: 进度输出Rendering HTML... Generating PDF...除非 --quiet exit code: 0 成功 / 1 参数错误 / 2 渲染错误 / 3 Paged.js 超时 / 4 browse 不可用捕获输出路径的标准写法PDF$($P generate letter.md)之后直接使用$PDF。退出码常量定义在 make-pdf/src/types.tsexport const ExitCode { Success: 0, BadArgs: 1, RenderError: 2, PagedJsTimeout: 3, BrowseUnavailable: 4, } as const;cli.ts的main()按错误类型映射到对应退出码BrowseClientErrorbrowse CLI 外壳调用失败→ 4PagedJsTimeout→ 3ENOENT文件不存在→ 1其余 → 2。这让 CI 脚本可以精确区分参数写错了、渲染炸了、Paged.js 卡死和浏览器没起来四类故障。布尔 flag 的解析细节一个曾经踩过的坑generate的参数解析在 make-pdf/src/cli.ts 的parseArgs()中。源码注释记录了一个真实回归#2514旧解析器会把任何紧跟在 flag 后的非 flag 词都当作 flag 的值导致技能自己文档里的用法$P generate --cover --toc essay.md essay.pdf只是靠 flag 相邻的运气才工作而$P generate --toc essay.md会把essay.md吃掉当作--toc的值并报 missing input。现在的实现用一个布尔 flag 白名单来区分export const BOOLEAN_FLAGS new Set([ cover, toc, no-chapter-breaks, confidential, no-confidential, page-numbers, no-page-numbers, tagged, no-tagged, outline, no-outline, quiet, verbose, allow-network, strict, ]);只有不在该集合中的 flag如--margins、--watermark、--title才会消费下一个词作为值。这一细节意味着布尔开关可以与文件参数任意相邻排列不会互相吞噬。三、核心使用模式1. 备忘录/信函模式80% 场景一条命令、无 flag得到干净 PDF默认带 running header、页码以及右下角的 CONFIDENTIAL 页脚。$P generate letter.md # 写到 /tmp/letter.pdf $P generate letter.md letter.pdf # 显式输出路径默认输出路径规则见 make-pdf/src/orchestrator.ts省略输出参数时写/tmp/slug.pdf其中 slug 由输入文件名派生去扩展名、非法字符替换为-、截断 64 字符。2. 出版模式封面 目录 章节分页$P generate --cover --toc --author Garry Tan --title On Horizons \ essay.md essay.pdf行为要点每个顶层 H1 起始一个新页面chapter break。对恰好有多个 H1 的备忘录可用--no-chapter-breaks关闭--title缺省时取第一个 H1--date缺省为今天--author用于封面和 PDF 元数据见 make-pdf/src/types.ts 的GenerateOptions注释封面版式在 make-pdf/src/print-css.ts 中锁定56pt 标题、13pt 元信息、padding-top 1.4in的海报式上三分之一落位v1.58.0.0 的改版决定——早期 32pt 封面在印刷中显得太小不用 flexbox、不做垂直居中目录可点击make-pdf/src/render.ts 的addHeadingIds()按目录扫描顺序给每个 H1–H3 分配idtoc-N已带 id 的标题保留原 id保证目录链接在 PDFChromium outline 书签和--to html输出中都能解析到真实锚点。3. 草稿阶段水印$P generate --watermark DRAFT memo.md draft.pdf每页斜向 10% 透明度的 DRAFT 水印定稿时去掉 flag 重新生成即可。水印实现为正文前的div classwatermark见 render.ts 组装逻辑打印调用相应开启printBackgroundorchestrator.ts 中printBackground: !!opts.watermark。4. preview快速迭代$P preview essay.md用同一份打印 CSS 渲染 HTML 并在浏览器打开xdg-open/open/start编辑 markdown 后刷新即可跳过 PDF 往返。源码上有一个重要的诚实差异make-pdf/src/orchestrator.ts 的preview()preview 刻意跳过图表/图片预处理不经过 browse daemon 往返因此图表 fence 在 preview 里只显示为代码块、本地图片可能解析不到——它会显式打印一条preview note提醒你generate 才会完整渲染这些内容防止你在缺了内容的预览上签字。5. 无品牌去掉 CONFIDENTIAL 页脚$P generate --no-confidential memo.md memo.pdfCONFIDENTIAL 页脚默认开启confidential默认true通过page { bottom-right { content: CONFIDENTIAL; ... } }注入print-css.ts。四、图表与图片离线矢量渲染与尺寸策略mermaid / excalidraw fence 渲染为图片markdown 中位于第 0 列的![mermaid](https://web-api.gitcode.com/mermaid/svg/eNpLUHjWMU0hAQAMZwMF)excalidrawfence 会渲染为清晰的矢量图完全离线vendored bundle不走 CDN。缩进的 fence列表内按设计保留为普通代码块。坏掉的 fence 产出可见的红色诊断块并附带解析错误——绝不静默降级为原始代码。fence 的 info-string 选项![mermaid](https://web-api.gitcode.com/mermaid/svg/eNorySzJSbVVciwtyVBIy8kvV1KAgkdtExSeLWh_uWjGi_Vbns3oU9BWSCzKTNTNSUxKzQEANdIV4g)mermaid renderfalse ← 保留为代码块 ![mermaid](https://web-api.gitcode.com/mermaid/svg/eNorSExPtc1JzEspTk4sSFVAgEdtExSe7tn1tGPbi_VLn87e97x7zdPeBc9WrHo6YeLLhVsBmuQcow)mermaid pageportrait ← 否决该图的自动横排实现上这是一个预扫描阶段make-pdf/src/diagram-prepass.ts由 orchestrator 在 Stage 1.5 调用先extractDiagramFences把 fence 抽走换成占位 tokenHTML 渲染完成后在专用的渲染 tabbundle tab里逐张渲染再substituteSlots回填。如果渲染 tab 不可用每个占位符会被替换为 Diagram not rendered — diagram-render bundle unavailable 的可见诊断块而不是留裸 token。SKILL.md 还划清了分工从英文描述创作新图是/diagram技能的职责——它产出可编辑的三元组source、.excalidraw、SVG/PNG与 make-pdf 配对的正确姿势是把.mmd源码嵌进 markdown而不是嵌 PNG。图片缩放正确永不截断本地图片自动内联相对路径相对 markdown 文件解析规则是每张图片都被内容盒封顶——零截断过大的照片会缩到印刷分辨率300dpi体积小而肉眼无损。远程图片默认拦截http/https 图片默认渲染为可见的拦截占位符离线姿态避免打印时拉取 tracking pixel显式传--allow-network才允许抓取。解析到 markdown 目录之外的图片即使通过符号链接仍会内联但大声警告--strict则让它直接失败。超过 64MB 的文件或非普通文件fifo、设备文件降级为占位符而不是挂死整次运行。逐图指令写在图片标记后面chart{widthfull} ← 拉伸到内容盒宽度 chart{width50%} ← 百分比或 3in/8cm/200px wide{pagelandscape} ← 独占横向页 wide{pageportrait} ← 否决自动横排指令的解析在 make-pdf/src/image-policy.tsapplyImageDirectives()在 marked 之后、净化器之前运行把alt{width50%}翻译成data-gstack-*属性净化器保留>const MIN_ASPECT 1.8; // 宽高比下限 const SHRINK_LIMIT 2.5; // 内宽超过内容盒 2.5 倍才提升≈1600px 6.5in letter 盒 const ALT_HINT_TOKENS [diagram, architecture, flowchart, chart, graph];即宽高比 ≥ 1.8且内宽超过内容盒约 2.5 倍缩到原尺寸 ~40% 以下就不可读了且有图表来源渲染出的 fence或 alt 文本含图表类词。假阴性代价低、假阳性代价高所以刻意保守提升后的页面垂直居中。启发式猜错时用{pageportrait}否决漏判时用{pagelandscape}手动提升。横向页的落地依赖 Chromium 的 CSS 命名页print-css.ts 生成page wide { size: size landscape; }与.page-wide { page: wide; }而 orchestrator仅在确实存在提升块时才给打印调用传preferCSSPageSize: truehasLandscape ? true : undefined对其它所有文档保持最小行为变更。垂直居中不用 CSS flex/min-height——源码注释记录了实测flex 的.page-wide在 Chromium 里会碎片化成幽灵横向空白页landscape-gate 测试数出 3 个提升却 5 页改为由 image-policy 依据每个块的宽高比计算内联margin-top来实现。e2e 门禁测试在 make-pdf/test/e2e/landscape-gate.test.ts横排提升、diagram-gate.test.ts图表渲染、emoji-gate.test.ts、format-gate.test.ts、combined-gate.test.ts配套 fixture 在 make-pdf/test/fixtures/。五、其他输出格式单文件 HTML 与 Word$P generate readme.md out.html --to html # 单一自包含文件内联 SVG 图、 #>$P generate docs.md --strict缺失图片、远程图片、树外图片、超大图片、非普通文件——默认行为是警告 占位符继续跑--strict下这些全部以非零退出码失败CI mode为需要确定性的文档流水线设计。types.ts 中的注释把它定位为 eng-review D6.1。七、常用参数完整参考以下参数表继承自 SKILL.md默认值与 make-pdf/src/types.ts 的GenerateOptions一致Page layout: --margins dim 1in默认| 72pt | 2.54cm | 25mm --page-size letter|a4|legal 别名 --format还支持 tabloid见 PageSize 类型 Structure: --cover 封面页标题、作者、日期、细线分隔 --toc 带页码的可点击目录 --no-chapter-breaks 不在每个 H1 处另起一页 Branding: --watermark text 斜向水印DRAFT、CONFIDENTIAL --header-template html 自定义 running header --footer-template html 自定义页脚与 --page-numbers 互斥 --no-confidential 关闭右下角 CONFIDENTIAL 页脚 Output: --to pdf|html|docx 输出格式默认 pdf。html 单文件自包含 docx 内容保真 --strict 缺失/远程/树外/超大/非普通文件图片直接失败CI 模式 --page-numbers N of M 页脚默认开 --tagged 无障碍 PDF默认开 --outline 由标题生成的 PDF 书签默认开 --quiet 抑制 stderr 进度 --verbose 每阶段耗时 Network: --allow-network 抓取外部图片。默认关闭远程图片渲染为 可见的拦截占位符 Metadata: --title ... 文档标题默认取第一个 H1 --author ... 封面 PDF 元数据的作者 --date ... 封面日期默认今天页边距还支持单侧覆盖--margin-top/--margin-right/--margin-bottom/--margin-leftcommands.ts 的 flag 列表单侧值优先于--margins。一个容易被忽略的实现细节页码在 CSS 层是唯一事实来源——orchestrator 总是关闭 Chromium 原生页码pageNumbers: falsepage { bottom-center { content: counter(page) of counter(pages); } }负责显示设置--footer-template时会同时压掉 CSS 页码render.tsshowPageNumbers pageNumbers ! false !footerTemplate避免双页脚。八、渲染管线源码剖析把 make-pdf/src/orchestrator.ts 的generate()按进度阶段串起来完整管线是Reading markdown— 读文件不存在则抛错Stage 1.5 图表预扫描—extractDiagramFences抽出 mermaid/excalidraw fence替换为占位 tokenRendering HTML— 纯函数render()make-pdf/src/render.tsstripFrontmatter去掉 YAML frontmattermarked 不认 frontmatter会把它渲染成一页正文段落marked解析为 HTMLapplyImageDirectives消费{...}图片指令净化器剥离script、iframe、object、embed、link、meta、base、form、全部on*事件处理器与javascript:URL注释不可信 markdown 可以内嵌原始 HTML解码 marked 产出的quot;/#39;等实体不解码smartypants 的正则永远匹配不到smartypants排版变换元数据推导、打印 CSS 生成、cover/TOC/章节分块、水印块组装成完整 HTML 文档。图表渲染 图片内联— 专用 bundle tab 渲染 fence、回填占位inlineLocalImages内联本地图片过大时缩到 300dpi此时才懒开渲染 tabimage policy— 宽度规则 自动横排判定docx 路径在此补一步图表光栅化--to html到此直接写盘返回Opening tab → Loading HTML into Chromium— 每次 generate 开专用 tab$B newtab --json所有 load-html/js/pdf 都带--tab-id并在try/finally中关闭——并行的$P generate调用永远不会竞争活动 tabGenerating PDF— browse 的pdf调用--toc时内部等待 Paged.js 分页完成。每个阶段的进度由ProgressReporter输出到 stderr--quiet时静默但失败信息永远输出——那是错误路径--verbose时给出每阶段毫秒耗时与总耗时Done in 1.5s. 43 words · 22KB · /tmp/letter.pdf。smartypants只改正文不碰代码、标签和 URL排版变换实现于 make-pdf/src/smartypants.tsquoted→ 弯引号、dont→ 右单引号、--→ em dash、...→ 省略号。关键是保护区机制先用占位符 token 挖出pre/code/script/style块、所有 HTML 标签、所有http(s)://URL只对剩余纯文本做变换再拼回去。源码里两个细节值得注意em dash 规则要求两侧有单词/空格边界避免把散文里的--verbose这类 flag 弄坏占位符以\u0000包裹且 URL 正则显式排除\u0000——否则紧贴标签的 URLa href...https://ex.com/a会让\S吞掉占位符导致标签永久丢失单次 restore 无法恢复。setup五步冒烟测试$P setupmake-pdf/src/setup.ts按 CEO 计划的 CLI UX 规格执行五步校验 browse 二进制存在且可响应失败 → 退出码 4启动 Chromium$B newtab about:blank开一个专用 tab 冒烟失败会提示cd ~/.claude/skills/gstack ./setup校验pdftotext可选只警告不失败——它是 CI 复制粘贴门禁用的macOS 装brew install popplerUbuntu 装sudo apt-get install poppler-utils用内联两段式 fixture 生成一份冒烟 PDF——内容特意包含弯引号、em dash、省略号以验证排版变换失败 → 退出码 2打印三条命令速查表default memo / full publication / diagonal watermark。九、调试与故障排查SKILL.md 的 Debugging 一节结合源码定位症状原因与处理输出空白/空页检查 browse daemon 是否在运行$B status复制粘贴出碎片化文本highlight.js 输出Phase 4 计划项。目前临时方案删掉围栏代码块重新生成Paged.js 超时退出码 3通常因为 markdown 里没有标题去掉--toc输出里出现 [remote image blocked] 占位符加--allow-network——你是在授权该 markdown 文件按其中图片 URL 抓取资源PDF 太高/太宽--page-size a4或--margins 0.75in十、技能调用时机与运行约定SKILL.md 还定义了 Claude 何时应运行它任何 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 意图都直接跑$P generate用户打开着.md文件说make it look nice时主动提议$P generate --cover --toc并在运行前确认。两个运行层面的约定来自技能文档Plan Mode 安全操作在 plan 模式下浏览/查看类命令、写~/.gstack/、写计划文件、open生成的产物均被允许它们为计划提供信息技能 preamble技能启动时先执行一段通用 preamble更新检查、会话状态、遥测开关、路由规则检查等见 SKILL.md 的 Preamble 与 Artifacts Sync 部分与 make-pdf 具体功能解耦PROACTIVEfalse时不主动建议技能UPDATE_CHECKfalse时跳过升级检查输出。小结make-pdf的设计可以概括为三条主线输出契约严格到机器可依赖stdout 只有一行路径、五档退出码、--quiet/--verbose分离进度与错误默认姿态是离线与安全远程图片拦截、HTML 净化、fence 坏损可见化、64MB 与非普通文件防挂死排版是刻意的工程决策Helvetica/Liberation Sans 度量兼容栈、CSS 为页码唯一事实来源、命名页实现单页横排、smartypants 的保护区机制。所有行为在 make-pdf/test/ 下有对应单测与 e2e 门禁如cli-args.test.ts、render-offline-sanitize.test.ts、image-policy.test.ts与test/e2e/的各 gate使这条markdown → 出版级 PDF管线的每个承诺都可验证。【免费下载链接】gstackUse Garry Tans exact Claude Code setup: 23 opinionated tools that serve as CEO, Designer, Eng Manager, Release Manager, Doc Engineer, and QA项目地址: https://gitcode.com/GitHub_Trending/gs/gstack创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表