
如果你做过长文档交付应该有这样的经历一份使用手册官网放一版打印 PDF 一版内部培训讲义又是一版每一版都“差不多”但从来没有哪两版能真正同步。维护到后期改一个数据要打开三四个文件改完还得人工核对哪里漏了非常痛苦。我这两年一直在折腾开源教材和长篇技术文档的自动化输出试过各种方案最后把项目压在了 Pretext 上。PretextPreTeXt不是常见的网站排版工具也不是那种把 Markdown 美化一下就发布成博客的小工具。它是一个面向“结构化长文档”的文本排版引擎核心思路特别朴素把内容、结构和视觉表现分开维护。内容是 XML结构由语义标签决定视觉表现交给一套相对独立的样式机制。同一份源文件可以直接构建成带目录、可交互数学公式的 HTML也能编译成印刷级的 PDF。当然它不是只有优点也有一定的上手门槛尤其是对完全没接触过 XML 的作者来说前半小时会非常不习惯。但一旦理解它的设计思路再回头看那些被各种格式细节绑架的文档项目应该会有不一样的视角。这篇文章我就从实际使用的角度把 Pretext 的定位、核心原理、完整实操流程和常见坑一次讲透。适合需要长期维护一套内容、同步产出多种阅读格式的教材作者、技术文档维护者和对排版工具感兴趣的人。1. 一个排版引擎到底在解决什么问题1.1 被格式绑死的内容是大多数文档项目最大的坑很多人在做文档时其实混淆了两件事内容是什么和内容长什么样。打开 Word 排一份标书你在写“第一章 项目背景”的时候其实是在手动维护层级编号、字体、行距、页眉页脚。内容一旦变化后面所有对格式的调整都要跟着走一遍。甚至很多时候文档字号改了里面的标题层次、图表编号、交叉引用却忘了更新最终交付的版本又乱又难改。更麻烦的是输出渠道多了之后。同一个内容在网页上、在 PDF 里、在电子讲义上对版面、尺寸、交互能力的要求完全不同。如果从一开始就把内容“锁定”在某种版面里后续每个渠道都得重新排版。这不是排版软件不好用而是工作流出了问题内容源没有独立出来。Pretext 的第一层价值就在这里。它强制你把文字、章节层级、数学公式、代码示例、图片表格这些“内容成分”写进结构化的 XML 文件里。文件记录的是“这一章讲什么”“这个公式是什么”“这张表有哪些行列”而不是“这行字要用多大字号”“这里要空几行”。排版格式在构建阶段才被附加进去。这个思路和写 HTML 时把 CSS 分离出来有点像但它做得更彻底并且不是为短网页设计的而是为几百页、带大量交叉引用的书这种体量准备的。1.2 内容与表现分离到底分了些什么如果你以前写过 HTML可能会觉得这种分离不新鲜。但 Pretext 的分离跟常见网页开发有个重要区别。它不只分离“样式”还分离了“结构语义”。在 Pretext 中你很少直接写div这种无意义的容器而是写section、exercise、theorem、figure这类有学科含义的标签。这样做的直接好处是一段带编号、带结论的定理在 HTML 里可以渲染成带边框的卡片在 PDF 里可以渲染成严谨的定理环境给屏幕阅读器读的时候又能自动读出层级关系。同一个源文件出口不同表现方式完全不同。在我自己维护的一本开源教材里书里有大量定理、例题和习题。以前用 LaTeX 写确实可以做到内容和部分样式分离但 LaTeX 里太多命令其实还是在干排版的事而且它的 HTML 输出能力一直比较麻烦。用 Pretext 后我可以定义“本节学习目标”“思考题”“案例”这些结构块构建时选择不同的皮肤和格式长期维护的体验比直接用 LaTeX 舒服不少。1.3 Pretext 和 Markdown、LaTeX、DocBook 的边界在哪第一次了解 Pretext 的人总会把它和几个熟悉的名字对比。其实它是从 DocBook 这类语义化 XML 体系演进而来并从 MathBook XML 改名而来。它和 LaTeX、Markdown、DocBook 有清晰的边界并不冲突。Markdown入门快写普通文章非常顺手但它的结构性能力比较弱。数学公式要依赖外部解析器交叉引用基本靠手动维护编号脚注、图表标题这类复杂元素能表达但不严谨。一旦文档到几百页的规模Markdown 的“自由”就会变成混乱。LaTeX在学术排版领域依然是王PDF 输出质量极高。但它的核心假设是“内容作者最后要交付 PDF”。如果你想同时维护网页版、交互版、屏幕阅读器版本需要借助额外工具链而且它从头到尾把内容和排版混在一个文件里。DocBook严格基于 XML 的文档结构标准很多企业级技术文档在用。Pretext 可以看作 DocBook 理念在教材、学术文章、开放出版领域的现代演化它对数学、编程实践、交互式习题有更好的原生支持。Pretext如果你要长期维护一套带数学公式、代码示例、大量交叉引用的教材级内容并且需要从一套源文件同时得到高质量网页和印刷 PDF它是目前几种方案里最顺滑的。简单概括Markdown 适合轻量快速写作LaTeX 适合只看重最终 PDF 的论文写作DocBook 适合纯软件文档而 Pretext 站在“书籍和教材级结构化排版”这一层。2. Pretext 的核心设计理念拆解2.1 语义标签写“这是什么”而不是“它长什么样”Pretext 的源文件使用的是 XML 方言。整个文档的最外层一般是book接着是frontmatter、chapter、section这样的内容层次。每一层都有明确职责标签名基本不用猜。关键点是这些标签描述的是“逻辑结构”。比如你想强调某个词语用的是em这样的语义标签它默认渲染成斜体但你可以通过样式把它改成特殊颜色或者由屏幕阅读器用特定语气朗读。样式层和内容层改起来互不侵犯。这种“强迫你做结构化思考”的机制对长期维护非常友好。我以前处理老文档时经常遇到一个段落既像正文又像注释作者自己也说不清它是附注还是例外说明。最后整理时只能凭内容猜测。而使用 Pretext 写新内容时系统会逼你决定每一块内容到底是什么角色。文档的逻辑一致性首先在编码阶段得到保障而不是靠美编和编辑人员后期脑补。一个简单的章节结构通常长这样book title示例教材/title frontmatter p这是前言。/p /frontmatter chapter xml:idchapter_introduction title导论/title section xml:idsection_about title关于本书/title p本书面向……/p /section /chapter /book我建议第一次接触时不要把 XML 想得太复杂。实际上 Pretext 的作者在写内容时很少需要关心最终的视觉结果。所见即所得在这个工具里不存在初期会有点不适应但写多了你会意识到编辑器不再干扰你的思路你只需要把结构组织好。2.2 数学、代码、图表这些“非纯文本内容”怎么编排普通排字系统处理纯文字没问题遇到数学公式、代码块、图表这类“特殊物体”时才会真正暴露设计功力。Pretext 对这几类的处理方式非常贴近作者实际需求。数学公式是它最擅长的一块。行内公式用m.../m独立成行的公式用me.../me语法大体沿用 LaTeX 数学模式的习惯。作者在源文件里写x^2 y^2 z^2构建成 HTML 时由 MathJax 渲染成交互式公式构建成 PDF 时由 LaTeX 的公式排版引擎处理。你不需要在两种目标格式里各自维护一份公式代码。这一点非常加分。拿我自己的体验说教材里涉及积分和矩阵的地方很多以前在网页版和 PDF 版之间来回同步公式是最痛苦的差事之一现在公式只有一个源头。代码块在 Pretext 里的处理也很有结构感。它不是简单地把代码当作“灰色等宽文本”而是把代码区块视为文档结构中的独立成分有输入、输出、语言类型等信息。代码块的定位不是“看起来像代码”而是让你可以方便地加入高亮、行号、复制按钮甚至在 HTML 版里做可折叠运行。这种把“内容类型”放第一位的设计是很多轻量标记语言做不到的。图表也一样。Pretext 中图片不用人工编号它会把图表的标题单独存储并在构建时自动生成图号。正文里靠引用标签指向图编号完全自动化。后期在第三章前插入两张新图后面的全部图号、表号和正文引用会自动重排。这在传统的 Word 式排版里是最容易出错的操作在 Pretext 里基本不用操心。2.3 页面级控制从整个输出框架到一行字符很多人会担心用语义化 XML 写作还能不能精细控制页面效果。这是一个非常典型且合理的问题。我的答案是Pretext 把页面控制分成两个层面。第一层是输出目标的选择。你可以针对不同终端预设框架网页、印刷 PDF、屏幕 PDF、教案讲义。每一个目标都有一套可配置的参数比如纸张大小、页边距、页眉页脚、字体链接、习题是否显示答案等。这些配置在项目文件里统一管理不需要深入每个页面去改。第二层是“逃生舱”。因为底层构建流程里PDF 最终要生成 LaTeX 中间文件再编译所以你可以在文档元信息位置嵌入自定义的 LaTeX 包和命令HTML 需要特殊效果时也可以挂自定义样式表或脚本。也就是说Pretext 默认帮你做掉了 90% 的常见排版决定剩余那 10% 的极端需求仍然可以向下介入。它不像很多工具那样强行封闭而是提供了合适的扩展接口。对我个人来说这套机制最舒服的地方在于它把“我排版排得好不好”从“我内容写得好不好”里剥离了。同样是作者我不需要为了在网页上把一张图做得居中去写一堆内联样式也不需要在改版式时把每个章节都翻一遍。3. 实操从零建一本带数学公式和代码的手册3.1 环境安装与项目创建Pretext 的 CLI 工具是 pretext。官方在不同阶段推荐过不同的安装方式一种是直接下载官方发布的命令行工具另一种是通过 Python 环境和官方源码仓库运行。我个人建议优先参考项目官方文档的最新安装指引因为 Pretext 的版本迭代速度并不慢很多早年的安装命令现在已经变了。比较通用的做法是先确保电脑上有 Python 3.8 以上的解释器然后获取官方 CLI 仓库代码按 README 的说明做依赖安装。装好后在终端里执行pretext --version如果能正常输出版本号就说明环境通了。创建新项目我的习惯是单独建一个目录存放所有源文件项目名用英文小写加下划线pretext new mybook cd mybook生成的项目骨架里有几个关键文件project.ptx项目级配置定义源文件路径、构建目标和输出参数。source/main.ptx文档的 XML 根文件整本书的内容入口。source/目录下面一般还会有存放图片、代码示例的子目录。如果你是第一次用建议先别急着改配置。直接构建一次默认的 HTML确认“模板本身能跑通”再开始写自己的内容。这一点很重要能把环境问题和内容问题分开排查。3.2 编写第一段结构化内容打开source/main.ptx你会看到一套完整的示例结构。我们可以先替换成自己的第一段正文。book title我的第一本 Pretext 文档/title frontmatter p欢迎使用 Pretext。本文档用于演示基本的章节、公式和代码块。/p /frontmatter chapter xml:idch_first title第一个章节/title section xml:idsec_hello title你好世界/title p 这一节展示一个行内公式 ma^2b^2c^2/m 以及一个独立公式。 /p me\int_0^1 x^2\, dx \frac{1}{3}/me /section /chapter /book写完这几十行 XML其实你就已经理解了 Pretext 的核心工作流。每个元素都有含义chapter代表章section代表节p是普通段落。xml:id是文档内唯一标识后面做交叉引用时全靠它。关于文字内的空格可以放行但不建议刻意用空格控制缩进。XML 解析之后连续的空白不一定原样保留正确的做法是用结构标签来区分语义而不是用空白来制造视觉层次。3.3 交叉引用和自动编号的正确用法假如在文档某处提到“前面第某节讲过”很多人会下意识手动写上“第 2.3 节”。在 Pretext 里我强烈建议不要这么做。手动写的编号一旦前面插入或删除章节就全线崩溃。正确做法是用xml:id配合交叉引用标签。给一个节设置好xml:idsection xml:idsec_install title安装方式/title p这里是正文。/p /section后面任何地方想引用它只需写p 详细的安装步骤请参见 xref refsec_install/。 /p构建 HTML 时它自动变成带链接的文字构建 PDF 时它会替换成对应章节编号甚至能够生成指向该章节的超链接。如果你对默认的提示文字不满意也可以给引用指定自定义文本。这套机制对图、表、公式、习题同样有效。在一本两百页的教材里交叉引用数量可能多达几百处全部自动化带来的维护成本下降非常可观。Pretext 中很多标签都支持xml:id建议在创建章节、小节、图表、习题时顺手写上清晰易读的 id 命名比如sec-install、fig-architecture、exercise-3-4。前期花十秒钟命名后期引用时能剩下大量时间。3.4 数学公式和代码块实战数学公式在我的项目里是刚需。Pretext 对公式的处理前面提过这里看实际写法。行内公式用m标签比如p设变量 mn/m 是正整数且满足 mn! gt; 10/m。/p注意在 XML 里要写作gt;否则解析器可能报错。如果你写的是小于号同样要转成lt;。这是 XML 语法的基本要求刚入门时最常在这里翻车。独立成行的公式用me标签me\lim_{n \to \infty} \left(1 \frac{1}{n}\right)^n e/me公式不一定非要 LaTeX 语法Pretext 支持接近 LaTeX 的数学语法构建 HTML 后由 MathJax 渲染构建 PDF 后由 LaTeX 引擎处理。也就是说你只需要学一种公式写法发布端自动适配。代码块也值得单独练一下。下面是一个带 Python 代码的演示program languagepython input ![CDATA[ def hello(name): print(fHello, {name}!) hello(Pretext) ]] /input /program这里用 CDATA 包裹代码内容可以避开 XML 对特殊字符的严格限制。不管是、还是在 CDATA 块内都会原样保留。需要注意的是不同版本的 Pretext 对代码块的具体标签可能有差异比如某些旧版课程资料里是用programlisting之类的旧标签。这不是你写错而是工具版本演进带来的差异。新版中更多采用program、input、output这种更语义化的组合。3.5 把源文件构建成 HTML 和 PDF内容写好后构建操作非常简单。进入项目目录执行pretext build --htmlHTML 产物会出现在output目录里用浏览器打开就能看。想同时构建多种格式可以pretext build --html --pdfPDF 构建过程会先生成 LaTeX 中间文件再调用系统里的 LaTeX 编译器完成最终排版。如果本机没有安装完整的 TeX 发行版这一步大概率会失败。一个比较快速的验证方式是执行which xelatex如果能正常输出路径说明系统里已经有 XeLaTeX 了。Pretext 的 PDF 输出主要依赖 XeLaTeX 来保证字体和现代排版特性。想预览本地 HTML 效果时我一般用pretext view它会在本地启动一个静态服务器并在浏览器中打开页面适合边写边看。4. 多格式输出的工程化细节4.1 HTML 产物不只是“网页版 PDF”刚开始用 Pretext 时我的预期只是“功能够用的网页”但看多了之后发现它在 HTML 语义方面做得挺细。生成的 HTML 保留了清晰的标题层级数学公式可以用 MathJax 做可视化渲染图表编号、交叉引用、目录结构也都自动生成。更重要的是它输出的 HTML 对屏幕阅读器相对友好因为源文件本身就是结构化的没有把内容堆在一堆div里。这意味着同一份源文件可以做多版本的阅读体验。默认网页版适合显示器阅读打印样式可以单独控制也可以加上自定义脚本实现章节折叠、答题反馈、代码复制这类交互。当然能交互的前提是源文件里已经用对了相应标签比如习题用了exercise而不是手写一个编号。结构化带来的自由度在输出端才真正体现出价值。4.2 PDF 输出背后发生了什么Pretext 构建 PDF 并不是简单地把内容直接画到纸面上。它会先把 XML 转换成一整套 LaTeX 文件再调用 XeLaTeX 编译。这样设计有一个很大的好处Pretext 不需要重写底层的断行、断页、字体排版这些极其复杂的算法而是借助 LaTeX 成熟的排版能力。正因如此Pretext 在 PDF 端有了印刷级质量的可能。比如段落内的孤行寡行控制、数学公式的精细排版、跨页表格的处理都继承了 LaTeX 生态几十年的工程积累。对于排斥直接使用 LaTeX 的写作者来说你在 Pretext 中几乎接触不到 LaTeX 源码但仍然能拿到 LaTeX 级别的 PDF 成品。对熟悉 LaTeX 的老用户来说当默认配置不满足特殊需求时也能在文档元信息中嵌入自定义宏包把它当成一个可以下探的底层接口。我在实际项目中通常把 HTML 用于在线阅读和快速预览把 PDF 用于出版社送审和课程打印。两者都从一个源文件生成后期只需要在配置文件里维护两个目标不用再复制粘贴核心内容到不同工具。4.3 用 Git 协作与自动化构建结构化 XML 源文件天然适合 Git 版本管理因为它是纯文本方便做逐行 diff。同一个文档项目多人协作时每个人负责不同章节分别编辑自己的 XML 文件通过分支和合并请求来整合冲突比 Word 时代少很多。更妙的一点是Pretext 的目录、编号、交叉引用都由构建系统自动生成不同人插入内容导致的编号变化不会像 Word 那样留下手动编号的“残留”。搭建自动化构建也很直接。在持续集成环境中安装好 Pretext 依赖和 TeX 发行版后执行pretext build --html --pdf然后把 PDF 作为构建产物发布。这就意味着每个版本更新都走同一条流水线少了“我本机能编服务器上不行”这种环境差异问题。处理教材这类长期更新项目时自动化发布的价值会随着文档版本的增加越来越大。5. 实际使用中的常见问题与排查思路5.1 XML 语法错误最常见的入门“劝退点”Pretext 的源文件是 XML这门语言对格式错误可以说是零容忍。标签没闭合、属性值忘加引号、写了裸的都会导致解析阶段直接失败。很多新用户第一次跑pretext build遇到这类报错还以为是自己写错了业务逻辑其实就是最基础的语法问题。我的排查经验很简单先看报错给出的文件路径和行号再检查是不是特殊字符没有转义第三看标签是否成对。比如在正文里写“当 n 10 时”这里的小于号必须写成lt;。如果是在数学标签m里一般语法解析器会友好一些但普通段落里出错频率很高。强烈建议使用支持 XML 语法高亮的编辑器来写源文件犯错时编辑器就能提前提醒大半。5.2 PDF 编译失败问题不一定在内容HTML 构建成功但 PDF 构建失败是很多人都会撞上的情况。这时候脑子里的第一反应不应该是“我的 XML 写错了”而是去查 LaTeX 工具链是否完整。常见原因包括系统缺少 XeLaTeX、缺少中文字体、LaTeX 包没有更新。一个有效的排查办法是找到 Pretext 生成的中间 LaTeX 文件看看它停在哪一步。直接查看中间产物里的错误信息比对着抽象的“build failed”干瞪眼有用得多。如果你本机不是完整 TeX 环境建议先安装完整版 TeX Live 或 MacTeX如果只装了精简版某些宏包缺失会导致编译中断。安装了之后最好在终端手动跑一次xelatex --version确保命令真的可用。5.3 中文内容与字体配置Pretext 本身不会刻意限制中文写作但中文排版对字体和行距有额外要求需要做一定设置。HTML 输出基本不用操心字体由浏览器接管。PDF 输出则要保证 XeLaTeX 环境里能找到合适的中文字体。如果不做任何配置默认字体列表可能不含中文字形编译出来的 PDF 里中文可能是空白和乱码。解决思路是在项目配置或文档元信息里挂载中文字体并在导出的 LaTeX 层做字体回退。如果你以前用过 LaTeX 的 ctex 或者 fontspec理解起来很快如果没用过最简单的方法是找一份已经配置好中文的 Pretext 示例项目做参照复制其字体配置再替换成自己系统里有的字体。千万别在文档写到一半才想起来做字体适配最好在项目初始化阶段就确定最终交付语言和字体方案。5.4 什么时候不应该选 Pretext没有万能工具Pretext 也不适合所有场景。如果只是写几篇博客、发布一个几十页的短报告或者团队里没有任何人愿意学 XML那用 Markdown 加 Pandoc 会更轻快。如果你的目标期刊只接收 LaTeX 源码编辑要求你直接提交.tex文件那就老实写 LaTeX走 Pretext 反而多一层转换。Pretext 的真正甜区是需要长期维护、频繁更新版本、输出多种媒介、且要有数学公式或交叉引用体系的中大型文档项目。我个人在实际操作中最深的体会是决定用 Pretext 前一定要先确认团队能否接受“写作时不直接看到最终效果”这种工作方式。它是结构化写作不是所见即所得。更接近程序员写代码再用编译器构建产物的习惯对编辑和美工人员来说需要一段适应期。如果整个流程只有你自己其实很快就能习惯因为它的构建速度并不慢一次构建几秒钟就能出结果。最后再分享一个小技巧。我通常会把 Pretext 项目里的output目录放进 Git 忽略清单只提交源文件和配置文件。产物随时能重新构建没必要把它当成版本管理的一部分。需要发布时再通过本地命令或 CI 自动生成并导出 PDF。这样既保持源文件目录干净也避免合并产物时的无意义冲突。长期维护下来这套“文本核心、按需构建”的工作流会让很多原来因为格式和内容纠缠不清而耗费的时间重新回到真正的内容创作上。