Pandoc 通常被介绍成通用文档转换器。这个说法准确,却容易把期待引向偏处:人们会把它当成连接任意两种文件格式的神奇胶水。这个项目经得住时间的设计更有纪律:reader 把输入文档解析成 Pandoc 原生抽象语法树,filter 按需修改这棵树,writer 再输出目标格式。[1][2]
这条流水线解释了 Pandoc 为什么能延续至今。它把文档转换从成组增长的两两转换器,改写成一份共享的中间契约。Markdown reader、DOCX writer、LaTeX writer、reveal.js 幻灯片 writer 和 Lua filter 各自只要理解自己接触文档模型的位置,无须理解整个格式世界。[1][2][3]
截至 2026-07-05T01:35:33Z UTC,公开 GitHub API 显示,jgm/pandoc 有 45,219 stars、3,913 forks、1,062 open issues,默认分支为 main,许可证为 GPL-2.0,最新 push 时间戳为 2026-07-04T16:52:14Z。[7] release feed 显示,Pandoc 3.10 发布于 2026-06-04,此前 3.9.0.2 和 3.9.0.1 在 3 月发布。[8] 这些数字只提供维护状态背景。真正的采用问题在于,团队能否在 Pandoc 的文档边界内工作,而不是要求它保留专有桌面文件曾经暗含的一切。
封面图是 Pandoc 原作者 John MacFarlane 在 UC Berkeley 个人资料页上的真实照片。[9] 这张图的用途超出装饰性的传记信息。MacFarlane 自己的工具页把 Pandoc 描述为通用标记转换器,他用它处理讲义、信件、幻灯片和网站,同页还列出 CommonMark、djot、citeproc、texmath、语法高亮工具等相关项目。[4] 以一位工作中的作者的基础设施来理解 Pandoc,比把它看成一次性的命令行技巧更贴近它的设计。
Readers 和 writers 让格式问题保持有界
最清晰的 Pandoc 心智模型应写成 input -> reader -> AST -> writer -> output,比“输入文件变成输出文件”更准确。用户指南直接描述了这种模块化设计:readers 把源格式解析成原生表示,writers 再把这种表示转换成目标格式。[1] 因此,受支持格式列表可以很长,却不需要为每一对格式单独维护一个转换器。
对工程团队而言,这个区别会改变采用测试的方式。Markdown 到 DOCX 的流程、DOCX 到 Markdown 的清理步骤、LaTeX 到 HTML 的导出、EPUB 构建,不能因为命令总是 pandoc 就被看作等价。每条路径都要求不同的 reader 和 writer 通过中间表示达成一致。如果源文件依赖页面几何、隐藏的 Word 样式、特定布局中的表格行为,或者只存在于输出端的功能,这个边界会立刻显现。[1]
Pandoc 对这一限制说得很坦率。它的中间表示比许多可转换格式的表达力更窄,所以用户不能预期每个源格式和每个目标格式之间都能完美转换。它试图保留结构性的文档元素,而不是保留页边距等每个格式细节;部分复杂元素也难以整齐纳入模型。[1] 这条保留说明需要进入评估;它本身就是契约。
实际规则很直接:先选择规范源格式,再选择输出格式。如果事实源是 Markdown 加 metadata,那么 PDF、HTML、EPUB、DOCX 和幻灯片就是生成产物。如果事实源是一份高度样式化的 Word 文件,Pandoc 仍能帮助抽取结构,但它不应被当作 Word 渲染引擎的保版式克隆。流水线在结构比桌面出版保真度更重要时表现最好。
AST 是损耗预算
Pandoc 的 AST 才是架构真正所在。filters 文档解释说,Pandoc 会把文本解析成中间表示,再把这种表示写入目标格式;AST 格式由 pandoc-types 中的 Text.Pandoc.Definition 定义。[2] 这表示 filters、templates、引用处理和下游工具处理的是文档元素,已经超出任意字符串替换。
这个选择给团队带来罕见的杠杆。一个 filter 可以规范标题、添加属性、重写链接、包裹代码块、插入警告、转换图注,或者生成跨格式行为,而不用分别改动每个输出 writer。[2][3] Quarto 文档在这里提供了独立旁证:Quarto 把 Pandoc filters 暴露为扩展机制,并说明引用处理和若干 Quarto 功能都位于这条 filter 通路上。[6]
同一个选择也带来纪律要求。凡是不能干净放入 AST 的信息,都会变成转换决策。它可以变成目标格式专属的 raw content,可以被丢弃,也可以只在某个输出家族里保留。因此,严肃的 Pandoc 工作流应包含测试夹具:一份文档里放入脚注、引用、表格、图片、数学公式、callouts、链接、metadata,以及组织实际使用中最难处理的样例。在宣布工具链已解决前,把它跑过每一个目标。
最强的 Pandoc 部署会把 AST 当成损耗预算。它们会询问哪些信息必须在所有目标中保留,哪些信息可以只服务特定目标,哪些信息应放在源文档之外,由 templates、metadata、CSL styles、build scripts 或 CSS 管理。这样的采用姿态比把 Pandoc 当作黑箱健康得多,也能避免日后责怪它忠实执行了一个比源文件暗示内容更简单的文档模型。[1][2]
Filters 是发布政策写成代码的位置
Pandoc filters 常被介绍成高级定制功能,但它们更接近系统的政策层。官方 filters 文档说,filter 是在 reader 和 writer 之间修改 AST 的程序;传统 filters 可以用任何语言编写,只要消费并产出 JSON 表示。[2] Lua filter 系统把循环收得更紧:filters 可以通过 Pandoc 嵌入式 Lua 环境运行,同时仍然作用在同一棵文档树上。[3]
这一点重要,是因为大多数机构写作系统需要的不只是转换。它们还需要一致的 heading IDs、链接政策、引用行为、图片处理、可访问性属性、自定义 admonitions、内部样式检查,以及面向特定输出的折中。filter 可以在文档模型边界一次性编码这些规则,而不是把规则散落在作者说明和导出后的清理流程里。[2][3][6]
这里也能看出 Pandoc 与一堆格式插件的差异。filter 不是转换之后的自动化步骤。它位于 writer 之前,因此能影响所有理解转换后文档结构的目标。团队可以制定规则,例如“所有外部链接添加一个 class”、“所有带有这个属性的代码块都被包裹起来”,或者“所有缺少 alt text 的图片都让构建失败”,并在 HTML、DOCX、EPUB 或 LaTeX 渲染分叉前应用这些规则。
边界依然存在。Filters 不会自动带来低维护成本。庞大的 filter 栈会演化成没有用户手册的私有标记语言。Lua 代码会编码只有一位维护者理解的假设。其他语言写成的 JSON filters 又会引入依赖和打包问题。合适的模式是保持 filters 小而清楚,记录输入和输出预期,只把真正属于文档模型的规则放进 filters,而不是把单一输出外观的处理塞进去。
引用和 Git 说明 Pandoc 是发布基础设施
放进发布工作流来观察 Pandoc,它的价值会比单条命令更清楚。MacFarlane 的工具页把 Pandoc 和引用、数学、标记语言、语法高亮项目并列,这强烈提示了它预期中的形态:写作源文件应足够朴素,便于编辑;也应足够有结构,便于转换;还应足够显式,便于发布到多种输出。[4]
Haskell Foundation 对 MacFarlane 的访谈提供了历史质感。他描述 Pandoc 源自自己对轻量标记和 Haskell 的使用,早期项目来自实际写作需求,而不是供应商产品计划。[5] 这种出身至今仍能看见。Pandoc 的取向接近优秀 Unix 风格工具的取向:让源文本保持可见,让转换路径可以写脚本,让版本控制、编辑、审阅和分发交给其他工具处理。
Simon Fraser University 关于发布工作流的文章年代更早,但仍提供了有用的独立背景,因为它展示了同一套设计如何走出单个开发者习惯。文章把 Pandoc、Git 和 Gitit 连接成一个工作流,在其中,文本文件持续保持版本化、可编辑,并能通过不同表面发布。[10] 这篇文章不是当前 release 文档,但其要点经得住时间:Pandoc 参与源代码管理下的编辑流程时,比充当最后一刻的导出按钮更有价值。
这也是 Pandoc 对学者、标准组织、文档团队、技术出版者、教育者和开源项目格外有吸引力的原因。产物可以重新生成。样式表可以更换。书目可以重建。同一份源可以生成网页、PDF 讲义、EPUB、幻灯片或归档用纯文本。组织得到的是一条可重复的发布路径,而不是一组 final-final 文件夹。
Pandoc 适合放在哪里
当源文件主要是文本、重要内容具有结构性、输出被有意生成时,Pandoc 最强。它适合研究论文、课程讲义、书稿、政策文档、技术文档、静态网站、幻灯片、内部手册,以及 Git、审阅、templates 和 build scripts 本来就合理的发布流水线。[4][10]
当真正需求是像素级往返、重度视觉页面编排、专有桌面排版行为,或者源格式的意义主要藏在不可见样式状态里时,Pandoc 的能力会变弱。在这些情况下,Pandoc 仍可作为抽取或迁移工具发挥作用,但团队应预期审阅和清理工作。忠实的 AST 不等于忠实的应用渲染器。[1][2]
保守推进方式很清楚。选择一个文档类别。声明事实源。写出一个小型目标矩阵:HTML、PDF、DOCX、EPUB、幻灯片,或者团队真正需要的其他目标。创建一份包含难例的夹具文档。把 templates、CSL styles、filters 和 build commands 放进版本控制。为发布构建固定一个 Pandoc 版本,升级前先用夹具测试,再改动生产流水线。[1][3][8]
这就是这则架构笔记。Pandoc 的价值不来自它宣称会说许多格式。它的价值来自文档转换中段被明确摆上台面。Readers 把源格式接入共享 AST。Filters 在目标分叉前编码政策。Writers 输出交付物。限制也正是力量所在:当团队接受这条边界,文档发布会少一些神秘,多一些可重复性。
Sources
- Pandoc User's Guide - 模块化 reader/writer 设计、AST 框架、转换限制、templates、引用和受支持格式模型。
- Pandoc, "Filters" - reader/AST/writer 流水线、filter 模型、
Text.Pandoc.Definition和 JSON 表示。 - Pandoc, "Lua Filters" - 内置 Lua filter 路径,以及解析和写出之间的 AST 操作。
- John MacFarlane, "Tools" - 作者工具列表,描述 Pandoc、CommonMark、djot、citeproc、texmath 和相关写作基础设施。
- Haskell Foundation Podcast 37, "John MacFarlane" - 关于 Pandoc 起源、Haskell、Markdown 和 MacFarlane 工具制作历史的访谈背景。
- Quarto, "Creating Filters" - 独立下游文档,显示 Pandoc filters 在 Quarto 中作为扩展层。
- GitHub API snapshot for
jgm/pandoc- 2026-07-05 采样的仓库 stars、forks、open issues、license、default branch 和 latest push timestamp。 - GitHub API release listing for
jgm/pandoc- 当前 release tags 和发布时间戳,包括 2026-06-04 的 3.10。 - UC Berkeley Department of Philosophy profile for John MacFarlane - John MacFarlane 的官方资料页,也是文章真实照片图像的来源页。
- Simon Fraser University Publishing, "Building Publishing Workflows with Pandoc and Git" - Pandoc 加 Git 和 Gitit 的独立发布工作流背景。