用 Markdown 生成符合中文排版规范的正式 Word 文档:typeset 工具详解

把 Markdown 转成 Word,pandoc 是一条常见路径。但 pandoc x.md -o x.docx 出来的文档,给英文博客用没问题,换成中文合同、协议、服务确认单、方案书或正式函件,就处处不对了。纸型、字体、行距、页眉页脚、表格跨页、标题孤行、签章区散落——每一样都要重新调,而且有些问题只有打开 Word 才看得到。

typeset 这个工具解决的就是这个问题。它把 Markdown 生成排版规范的中文 .docx,同时附带一套检查机制,帮你发现版面问题。

安装与依赖

typeset 本身是一个 Agent Skill,但也提供了独立的命令行工具。安装之前,先装好底层依赖:

brew install pandoc poppler
brew install --cask libreoffice
  • pandoc:核心转换引擎,负责 Markdown → docx。
  • popplerverify.py 渲染检查时用于处理 PDF。
  • LibreOffice:用于无头模式(--headless)把 docx 转 PDF,再逐页渲染成 JPG 供人工检查。

克隆仓库到对应目录即可使用。按 Agent Skills 开放标准,Codex CLI 读取 ~/.agents/skills/,Claude Code 读取 ~/.claude/skills/。装一份、两处都能用:

git clone https://github.com/cerul-ai/typeset.git ~/.agents/skills/typeset
ln -s ../../.agents/skills/typeset ~/.claude/skills/typeset

之后在 Claude Code 或 Codex 里说“帮我把这份合同做成 Word”,就会自动调用。

直接当命令行工具用也行,核心脚本就两个:

  • scripts/build.py:Markdown → .docx
  • scripts/verify.py:结构 lint + 渲染检查

排版参数差异

pandoc 默认的 docx 输出和中文正式文书需要的差距,不只是“字体对不对”这么简单。

项目 pandoc 默认 中文正式文书需要
纸型 Letter A4
标题 #0F4761 青蓝色、不加粗 纯黑色、加粗、黑体
西文字体 Aptos Times New Roman
中文字体 主题回退,跨机器不一致 宋体 / 黑体显式指定
行距 单倍 1.5 倍(预留批注空间)
页眉页脚 横线 + 第 X 页 / 共 Y 页

这些差异的直接影响是:用 pandoc 默认模板生成的 docx,在中文正式文书场景下几乎都需要二次手工调整。typeset 做的事情,就是把这一套调整固化为可复用的构建脚本。

基本用法

从模板起步:

cp templates/contract.md 我的合同.md

编辑 Markdown 内容后,执行构建:

python3 scripts/build.py 合同.md

默认输出同目录下的 合同.docx,排版参数按国内公司合同最常见的范式(方案 A)生成。

如果需要更多样式选择,可以用 --all 一次生成四套不同封面与签章方案的版本:

python3 scripts/build.py 合同.md --all

输出会带上方案后缀,方便打开对比挑一个。

验证:结构 lint + 渲染检查

verify.py 提供了两层检查,两层都通过后依然需要在 Word 里做最终确认,但这两层能提前过滤掉大部分问题。

python3 scripts/verify.py 合同.docx --render

第一层:结构 lint

检查 docx 内部的结构是否符合预期,不依赖任何外部渲染器,跑得很快。能检出常见的结构问题,比如表格跨页被劈成两半、条款标题孤零零留在页底、签章区甲方在上一页乙方在下一页——这些都是“在 Word 里打开才能看见”但确实会破坏正式文书观感的问题。

第二层:转 PDF + 逐页渲染成 JPG

--render 会调用 LibreOffice 把 docx 转成 PDF,再把每一页渲染成独立的 JPG 图片。然后你会看到类似这样的输出:

第 1 页: 封面
第 2 页: 条款 1-3
第 3 页: 条款 4-6(表格跨页)
...

这一步的真正价值是让你真的去看那些 JPG。前面每一步都通过、文档在 Word 里能打开,版面依然可能很难看。只有一页一页扫过渲染图,才能发现表格被拦腰切断、签章区只剩甲方、页眉横线没对齐这类问题。

字体替换问题与分页差异

verify.py 会自动检测字体是否被替换并给出提示。这一点比想象中更重要。

LibreOffice 在 macOS 上默认匹配不到“宋体/黑体”,会回退到 Arial Unicode MS。替换字体的度量不同,连页数都会变——同一份 docx,Word 打开是 17 页,LibreOffice 转出来可能变成 27 页。差 10 页不是小数目,签章区、附录、空白页的位置全都不一样。

所以 verify.py--render 模式用 LibreOffice 转 PDF/JPG,定位是:

  • 验结构:表格有没有被劈开、签章区有没有散、标题孤行有没有出现
  • 不代替 Word 做最终排版确认

最终分页与字体外观,必须在 Word 里打开确认。如果某个页面在 LibreOffice 渲染图里看起来“版面有点怪”,但结构 lint 没报错,再去 Word 里核对——很多时候 Word 里的实际效果是好的,这就是字体度量差异导致的分页漂移。

四套封面与签章方案

--all 生成的四个版本,封面和签章区布局不同,适用场景也不同:

方案 特点 适用场景
A 复刻参考版 国内公司合同最常见的范式 默认方案,没特殊要求时选它
B 严格对齐版 标签-值两列 + 填空线 打印后手填的纸质版合同
C 公文庄重版 信息块加外框、标题放大 正式函件、对上级/对外公文
D 现代简洁版 左对齐 + 细线包夹,无页眉线 对内方案书、服务确认单

这四个方案只在封面和签章区有区别,正文字体、行距、页眉页脚保持一致。所以选方案其实就是选“第一页长什么样”和“最后一页怎么落款”。

实际操作中,如果拿不准选哪个,跑一次 --all,四份都打开,封面页截图放在一起对比,挑视觉上最顺眼的那个。后面内容改动了,再用对应方案重新生成。

修改排版风格

排版参数集中在 scripts/build.py 顶部的常量块,想换字体、字号、边距,改那里就行,不用翻到代码各处去改。

取值依据写在 references/house-style.md 里。比如为什么标题用黑体而不是宋体加粗,为什么行距定 1.5 倍而不是“多倍行距 1.25”,这些判断在 house-style 里都有说明。改参数前先看依据,避免凭感觉调出一个“看起来差不多但整体不协调”的版本。

快速操作清单

  1. 安装依赖:brew install pandoc poppler + brew install --cask libreoffice
  2. 克隆仓库到 ~/.agents/skills/typeset(或任意目录后软链到对应位置)
  3. 复制模板:cp templates/contract.md 我的合同.md
  4. 编辑内容,然后构建:python3 scripts/build.py 我的合同.md
  5. 需要对比方案时用 --all 生成四份
  6. 验证:python3 scripts/verify.py 我的合同.docx --render
  7. 查看每页渲染 JPG,重点关注表格跨页和签章区
  8. 最后在 Word 里打开确认字体和最终分页
  9. 改排版参数时只改 scripts/build.py 顶部的常量块

FAQ

Q:pandoc 直接转不就行了吗,为什么还要这套工具?

A:pandoc 默认模板面向英文场景,中文正式文书需要的 A4 纸型、宋体/黑体显式指定、1.5 倍行距、页眉页脚页码都不在默认配置里。手工调一份可以,每次都要调就不如固化下来。

Q:verify.py--render 检查通过后,还需要在 Word 里看吗?

A:需要。--render 依赖 LibreOffice 做转换,而 LibreOffice 在 macOS 上会把宋体/黑体替换成 Arial Unicode MS,导致分页与 Word 不一致。--render 的意义在于验结构(表格、签章区、孤行),最终字体和分页在 Word 里确认。

Q:--all 生成四份文档,内容一样吗?

A:正文内容完全一样,只有封面和签章区的排版方案不同。

Q:字体被替换了怎么办?

A:verify.py 会检测到并给出提示。如果你需要在 Linux/macOS 无 GUI 环境里做更准确的渲染验证,可以在系统里安装中文字体(如 fonts-noto-cjk),但即便如此,LibreOffice 和 Word 的排版引擎仍然有差异,不能完全替代 Word 确认。

Q:我只想改页眉文字,不想动其他参数,改哪里?

A:在 scripts/build.py 顶部的常量块里找 HEADER_TEXT 相关变量。不要在转换后的 docx 里手工改——下次构建会被覆盖。所有定制都回到 Markdown 源文件和 build.py 的常量块。

Q:这个工具只能生成合同吗?

A:不只合同。协议、服务确认单、方案书、正式函件都适用。模板是合同骨架,但排版参数是通用的中文正式文书规范。