> For the complete documentation index, see [llms.txt](https://ayakaleaf-pro.ayaka.space/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ayakaleaf-pro.ayaka.space/latex/zh-cn/shen-du-wen-zhang/14-how-do-tex-engines-typeset-tables.md).

# TeX 引擎如何排版表格

## TeX 引擎如何排版表格

## 引言：本系列涵盖什么内容？

制作美观的表格可能是一项耗时的任务——无论你使用的是可视化布局工具、LaTeX，还是 HTML 或 markdown 之类的标记语言。对于 LaTeX 用户来说，表格排版尤其常被列为许多人的“痛点”之一，这也许可以从“tables”成为 tex.stackexchange 上最常见的 [高频标记话题之一看出来](https://tex.stackexchange.com/tags).

除了 tex.stackexchange 上的回答和示例之外，哪怕只是粗略浏览一下有关使用 LaTeX 进行表格排版的支持资源，也会发现不少与表格相关的信息来源：

* Overleaf 的 [帮助页面](https://www.overleaf.com/learn/latex/tables) 以及其他网站，例如 [learnlatex.org](https://www.learnlatex.org/en/lesson-08)
* CTAN，也就是 Comprehensive TeX Network（综合 TeX 网络），列出了超过 [70 个 TeX/LaTeX 宏包](https://ctan.org/topic/table) 与创建表格相关
* 整本 [使用 LaTeX 排版表格的书](https://www.amazon.co.uk/Typesetting-Tables-LaTeX-Herbert-Voss/dp/1906860254)
* 一个优秀的 [在线 LaTeX 表格生成器](https://www.tablesgenerator.com/latex_tables)

简单搜索一下 [LaTeX tables](https://www.google.com/search?q=latex+tables) 就会得到大量结果，列出许多提供帮助、建议、示例和解释的网站。

### TeX，而不是 LaTeX

鉴于现有关于使用 LaTeX 进行表格排版的文献如此丰富，是否还有必要再写些关于它们的内容——更多表格示例，以及宏包命令的枚举/演示？是否有一种方式来讨论表格排版这一主题，从而引出或聚焦于表格排版的基本原理和概念？有，不过这需要剥开 LaTeX 的洋葱……

我们决定撰写一个文章系列，旨在为读者提供有关其底层 *机制* 的背景信息和解释，讨论基于 TeX 的表格排版。我们不会把重点放在使用特定的 LaTeX 宏/宏包来排版表格上，而是会探索 TeX 引擎的 *底层行为* ：研究支撑 LaTeX 宏命令构建的底层排版机制。最终目标是提炼并解释基于 TeX 的表格排版的核心方法和算法——希望能帮助读者/用户更好地理解表格为何会以那样的方式运行。剥离 LaTeX 宏这层保护性外壳所带来的一个不可避免的后果，是会暴露出那些令人头疼的底层细节，而用户通常（并且乐于）通过多层 LaTeX 宏代码被这些细节所遮蔽。

为了研究、撰写并配图说明这些文章，我们花费了大量时间，因此我们希望它们能够成为文献中的一个有价值的补充，提供一些材料来帮助读者并帮助他们更好地理解 TeX 排版这一复杂领域。我们应当强调，本系列文章将 *不* 涉及讨论 *美学* 以及表格设计——这是一个充满主观偏好的话题，其争论必须在别处进行……

### 探索 TeX 表格的机制：该怎么做？

为了探索并撰写 TeX 引擎内部正在发生的更底层机制和过程，例如表格排版，Overleaf 使用 Martin Ruckert 的 [Web2W 过程](https://w3-o.cs.hm.edu/users/ruckert/public_html/web2w/index.html).

构建（编译）了 Knuth 的 TeX 引擎的“调试”版本。传统上，构建 TeX 使用的是一个称为 Web2C 的过程，它在 TeX Live 中使用，通过将 TeX 原始 Pascal 源代码转换为其 C 等价物来生成 C 代码。这个过程生成的 C 代码从未打算给人类阅读，只供 C 编译器使用。机械生成的 C 代码 *极其* 难以阅读或修改，因而不适合用于实验。

相比之下，Web2W 会生成 C 源代码（可在 [此处](https://w3-o.cs.hm.edu/users/ruckert/public_html/web2w/ctex.c)获取），其 *可读性远远* 高于 Web2C 生成的代码。因此，Web2W 的 C 源代码更便于出于学习/实验目的进行修改。

Web2W 生成的是一个 TeX 版本（“CTeX”），它 *极其* 接近 Knuth 的原始程序：“CTeX”不包括 Web2C 过程中引入的许多改动和增强——例如 SyncTeX、命令行处理以及通过 Kpathsea 的文件搜索。尽管你牺牲了这些有价值的增强功能，生成的（Web2W）C 代码仍然相对容易借助 Knuth 公开的 TeX 源代码来浏览，尽管 TeX 是用 Pascal 编写的。

* **关于名称的说明：** 严格来说，名称“TeX”只能指 Donald Knuth 编写并发布的原始软件。对其软件所做的任何修改，都必须为最终得到的基于 TeX 的排版软件使用不同的名称。这里，我们使用 Web2W 过程构建了一个引擎，实际上它仍然是 Knuth 的原始软件。不过，为了避免歧义，我们将使用“CTeX”来表示用 Web2W 构建的特定版本，同时也会使用“TeX”来指 Knuth 的原始引擎，或指基于 Knuth 的 TeX 原理的排版语言这一泛称。我们希望读者能够原谅我们在严格正确术语的应用/使用上偶尔出现的疏漏：我们希望含义/意图能够通过上下文传达出来。

CTeX 的调试版本使用 [Eclipse IDE](https://www.eclipse.org/downloads/packages/)运行，这使得可以实时观察实现 TeX 低层原语（内建）命令以及由 Knuth 设计用于支持表格排版的算法的 C 代码处理过程。

下面这段简短的视频（约 90 秒）展示了 CTeX 引擎在 [Eclipse IDE](https://www.eclipse.org/downloads/packages/):

{% embed url="<https://videos.ctfassets.net/nrgyaltdicpt/7drdFwYR6h5xD88XnurDIH/36511f504755ab274f4da2e3f3fc1ce5/TeXtables.mp4>" %}

除了 CTeX 之外，我们还编译了 e-TeX，以便使用 Knuth 原始软件中没有的一些额外原语。虽然如今 CTeX 和 e-TeX 都是较老的 TeX 引擎，但它们仍然适合作为探索表格排版机制的基础，因为这些底层原理仍然适用于所有基于 TeX 的排版引擎。

### 为什么要使用较旧的 TeX 引擎来探索表格？

首先，那本印刷版著作， [TeX:The Program](https://www.amazon.co.uk/Computers-Typesetting-TeX-Program-TEX/dp/0201134373)，其中列出了并解释了 TeX 的源代码，即使在出版已超过 30 年（1986 年）的今天，仍然是理解 TeX 内部运作这些晦涩内容的极其方便的指南。当然，你也可以自己排版 TeX 源代码文档，例如参见 Overleaf 项目 [为 TeX、e-TeX 或 pdfTeX 排版源代码文档](https://www.overleaf.com/latex/examples/typeset-the-source-code-documentation-for-tex-e-tex-or-pdftex/qkgfgyspnhcv)。自 1986 年《TeX:The Program》出版以来，新的 TeX 引擎不断演化，包括 pdfTeX、XeTeX 和 LuaTeX，它们都引入了《TeX:The Program》中没有记录的功能和命令，仅仅是因为这些功能在 Knuth 的原始软件中并不存在。

对于许多核心过程，例如 TeX 的表格排版，《TeX:The Program》中记录的代码仍然适合作为研究基础——尽管 TeX 的源代码是用 Pascal 编写的。此外，Knuth 的 TeX 编译起来相对容易，而且很快——尤其是借助极其实用的 [Web2W](https://w3-o.cs.hm.edu/users/ruckert/public_html/web2w/index.html) 过程，由 Martin Ruckert 开发。编译的容易性/速度使得以简单方式修改 TeX 变得更加方便——例如在本系列文章后面会用到的 SVG 图形的创建。

### 理解 TeX 和 LaTeX 的区别

许多读者可能已经知道，LaTeX 实际上并不是一个 *可执行的* 排版程序，而是一大组命令（*宏*），这些命令最终都是用一种称为 TeX 的更低层排版/编程语言编写的。你的 LaTeX 代码只有在被一个称为 *TeX 引擎*的可执行程序处理之后，才会生成排版输出——该软件位于你的 LaTeX 代码（文档）和排版后的 PDF 之间。如今，用户可以选择不同的 TeX 引擎来排版他们的 LaTeX 代码，包括 pdfTeX、XeTeX 以及 LuaTeX 的各种变体。

对于刚接触 TeX/LaTeX 生态的人来说，面对遇到的那些听起来晦涩的工具名称——TeX、LaTeX、pdfTeX、pdfLaTeX、XeTeX、XeLaTeX、LuaTeX 和 LuaLaTeX——常常会感到困惑，这完全可以理解。如果你也有同感，Overleaf 文章 [《名字里有什么：TeX 的多种“风味”指南》](/latex/zh-cn/shen-du-wen-zhang/55-what-s-in-a-name-a-guide-to-the-many-flavours-of-tex.md) 可以为你解释这些术语的来源和含义。

#### 方言与 TeX 原语

每个 TeX 引擎都有一组称为 *原语* 的内建命令，它们共同构成了该引擎所使用的 TeX 排版语言“方言”，反映了每个引擎内置的能力。这里，用来描述 TeX 引擎内建命令的“primitive”一词并 *不* 不意味着这些命令基础或简单，而是指它们是基础性的、不可再分的：不是由其他命令构造出来的（不同于宏）。所有 TeX 引擎都共享一大套核心原语，但有些引擎确实包含该引擎特有的原语——这便形成了 TeX 的一种“方言”的概念。

无论使用哪种 TeX 引擎来排版你的 LaTeX 文档，它的工作都是处理（“执行”）用于编写和构建文档的那组 LaTeX 命令（即宏）。实际上，TeX 引擎会把你的 LaTeX 代码（宏）“转换”回 *其构成的* TeX 引擎原语命令，供引擎执行以完成实际排版。你并不 *必须* 一定要使用 LaTeX 命令才能用 TeX 引擎排版文档——你 *可以* 选择完全用 TeX 原语来构建文档，也就是直接使用底层的 *TeX 编程语言* ，这门语言内置于 TeX 引擎中。然而，以今天的标准来看，TeX 语言相当晦涩，通常被认为是一门很难用来编程的语言——它还可能需要大量内建原语才能实现你的排版目标，因此这项编程任务既容易出错，又可能重复繁琐。

为了避免直接用 TeX 语言编写，或不断重复输入同一系列命令，TeX 引擎允许你创建称为 *宏*的“快捷方式”。通过创建宏，你可以定义自己的命令，把可能很长且复杂的 TeX 语言原语序列（或其他宏）打包成一个单一的“更高层”命令。TeX 程序员可以编写非常复杂的宏，在一个命令中封装大量功能——例如 LaTeX 提供的那些命令。通过使用宏包，例如 LaTeX（或 [ConTeXt](https://wiki.contextgarden.net/Main_Page)），文档作者（在大多数情况下）可以免受许多繁琐细节的困扰，从而能够专注于写作和排版，而不必不断与 TeX 语言的复杂性和细微差别周旋。

## 一开始……

在为数学排版和复杂的自动断行设计算法的同时，Knuth 还面临着为他的 TeX 软件编写表格排版程序的挑战。显然，任何表格构造算法都不能过于受限，因为那样会使需要自由创建几乎无限种表格布局的用户感到受挫。此外，表格单元格可以包含范围广泛的内容，包括数学、图形以及被断成优美排版行的文本片段——事实上，任何 TeX 能够排版的内容都可以放进去。要实现这种灵活性，就要求 TeX 的表格构造算法必须与 TeX 排版机制的其他部分协同工作。

然而，TeX 引擎为其表格构造能力提供的灵活性付出了一定代价：内建（原语）表格排版命令在底层行为上存在许多微妙而复杂的细节，其中有 9 个：

* **`\halign`**, **`\valign`**：核心表格构造命令
* **`\tabskip`**：放置在 \halign 列之间或 \valign 行之间的弹性间距
* **`\cr`**：表格中所有行的“回车”式必需终止符
* **`\noalign`**：在 \halign 行之间或 \valign 列之间插入材料
* **`\everycr`**：在检测到 \cr 后读取的命令（标记寄存器）
* **`\span`**：一个双功能命令：\span 创建跨列或跨行的单元格，或者扩展表格导言中的命令（我们将对此进行详细讨论）
* **`\omit`**：跳过某个特定单元格的模板
* **`\crcr`**：在宏中使用，以避免用户忘记必需的 \cr 时出错

在我们探索表格构造的旅程中，我们会遇到这些命令。

### 挑战的遥远回声

埋藏在 [TeX 的源代码中](https://www.overleaf.com/latex/examples/typeset-the-source-code-documentation-for-tex-e-tex-or-pdftex/qkgfgyspnhcv) 的是对实现用于表格排版的底层命令 \halign 和 \valign 的一个略显令人望而生畏的介绍：

> “每当 \halign 和 \valign 能正常工作时，这差不多都像个奇迹，因为它们横跨了 TeX 的许多控制结构。因此，当前这一页也许不是初学者开始阅读这个程序的最佳地点；最好先掌握其他所有内容。”

Knuth 接着说道

> “请注意，在处理 \halign 时，我们大胆地把控制权交给 TeX 的其余部分。在关键时刻，才会调用一个对齐例程出来做一点小动作，但大多数时候这些例程只是潜伏在后台。这有点像后催眠暗示。”

从这些评论看来，似乎可以合理地得出结论：即便对 Knuth 来说，实现 TeX 的表格排版也是“某种挑战”——既要为用户提供控制与灵活性，同时又要确保 TeX 自动化的表格构造算法与 TeX 的核心排版过程协调运作。

作者坦率承认 TeX 表格排版能力背后的代码和算法之复杂，同时也由衷钦佩其中所包含的巨大功能，尽管这些代码（无论是 Pascal 还是 C）相当密集，却只占用了相对不多的篇幅。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://ayakaleaf-pro.ayaka.space/latex/zh-cn/shen-du-wen-zhang/14-how-do-tex-engines-typeset-tables.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
