> 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/24-how-tex-calculates-glue-settings-in-an-hbox.md).

# TeX 如何在 \hbox 中计算粘连设置

这是本系列的第三篇，也是最后一篇，本文将探讨 $$\mathrm \TeX$$ 盒子和胶水。第一篇文章 [盒子与胶水：使用 LuaTeX 的简明而直观的介绍](/latex/zh-cn/shen-du-wen-zhang/11-boxes-and-glue-a-brief-but-visual-introduction-using-luatex.md) 介绍了盒子和胶水的概念，随后是 [Pandora’s \hbox: Using LuaTeX to Lift the Lid of TeX Boxes](/latex/zh-cn/shen-du-wen-zhang/36-pandora-s-hbox-using-luatex-to-lift-the-lid-of-tex-boxes.md) ，其中展示了一个 [$$\text{Lua}\mathrm\TeX$$基于的 Overleaf 项目](https://www.overleaf.com/latex/examples/exploring-the-structure-of-tex-boxes-with-luatex/pwdrypmtdbgs) 来探索 $$\mathrm \TeX$$ 通过使用节点图来研究盒子的更深层结构。在最后这一部分中，我们将“深入探究” $$\mathrm \TeX$$ 如何在一个 `\hbox`中计算胶水值：这一过程被称为 *设置胶水*。我们大量使用节点图（在本系列的 [Pandora’s \hbox: Using LuaTeX to Lift the Lid of TeX Boxes](/latex/zh-cn/shen-du-wen-zhang/36-pandora-s-hbox-using-luatex-to-lift-the-lid-of-tex-boxes.md) 中介绍过），并展示如何使用和解读它们提供的一些数据： `glue_set`, `glue_sign` 和 `glue_order`.

我们将给出一个完整演算示例，计算一个 `\hbox` 中的胶水，并涵盖大量细节；不过，这里可能还有一些我们没有篇幅讨论的额外情况和注意事项，感兴趣的读者可参见 $$\text{The } \mathrm \TeX \text{book}$$.

## 挑战

假设我们有一个 `\hbox` ，如下所示：

```latex
\hbox to100pt{%
A\hskip4pt plus3pt minus 2pt%
B\hskip 0pt plus 2fil%
C\hskip 0pt plus 2fill%
D\hskip 0pt plus 3fill%
}
```

这是这个盒子的样子——为了清晰起见，这里放大并加了边框显示：

![盒子](/files/c9031f71d024717337c5908367bf2dcda4c08661)

问题是：以下项目之间的空间（胶水）的最终值是多少，单位是 $$\mathrm \TeX$$ 磅：

* A 和 B 之间
* B 和 C 之间
* C 和 D 之间
* D 和盒子末尾之间

也就是说，我们想计算 $$\mathrm{g}*{1}, \mathrm{g}*{2}, \mathrm{g}*{3} \text{ and } \mathrm{g}*{4}:$$

![间距](/files/28e66d20786abac9155a653f8e0e3e8f486f2bf4)

这里有一张表示上述盒子的节点图。特别值得关注的是“元数据”部分中的三个值：

* `glue_set`
* `glue_sign`
* `glue_order`

![节点图](/files/442bb94f3cf1b0f078a4ed6ed654b3f03e775e74)

需要注意的是， `glue_set`, `glue_sign` 和 `glue_order` 的一组特定值只会影响顶层盒子中的胶水：它们不会影响 *嵌套* 盒子中的胶水：每个嵌套盒子（hlist 或 vlist 对象）对这三个参数都有自己的一组值。下面是一个 `\hbox` 嵌套在一个外层 `\hbox`中的示例。在这个例子中，你可以清楚地看到 `glue_set`的不同取值——当然，嵌套盒子也可以对 `glue_sign` 和 `glue_order`.

```latex
\hbox to 75pt{\hfill ABC\hbox to15pt{\hfill D}}
```

![节点图](/files/872239d9e48ee3ac4a6bbc7a25223863a00e6850)

## 胶水类型、无穷与阶次：总结

$$\mathrm \TeX$$ 提供了若干与胶水相关的原始命令，包括：

* 水平胶水： `\hskip`, `\hfil`, `\hfill`, `\hfilneg`, `\hss`;
* 垂直胶水：`\vskip`, `\vfil`, `\vfill`, `\vfilneg`, `\vss`;

以及 `\mskip` ，用于在数学表达式中插入胶水。

一个胶水项由三个值定义：

* **自然宽度**：如果不拉伸或压缩它，它占据多少空间；
* **拉伸分量**：胶水可以拉伸多少；
* **压缩分量**：胶水可以压缩多少。

我们要考虑的是在一个 `\hbox{...}` 中使用胶水，以及 $$\mathrm \TeX$$ 用于确定胶水最终会占据多少空间的计算。我们将用来创建一些 *横向* 胶水的命令是 `\hskip`，其形式为：

`**\hskip** *<natural width>* **plus** *<amount to stretch>* **minus** *<amount to shrink>*`

对于 *纵向* 你会使用的胶水 `**\vskip** *<natural width>* **plus** *<amount to stretch>* **minus** *<amount to shrink>*`.

例如，一些典型的水平胶水可表示为 `\hskip 3pt plus 2pt minus 1pt`。你也可以使用其他物理单位：

* `\hskip 3mm plus 2mm minus 1mm`
* `\hskip 3in plus 2in minus 1in`
* `\hskip 1in plus 3cm minus 20mm`

## $$\mathrm \TeX$$ 胶水和“无穷”单位

对于胶水的压缩或拉伸分量， $$\mathrm \TeX$$ 引入了另一种单位：所谓的“无穷”单位： $$\text{fil}$$, $$\text{fill}$$ 和 $$\text{filll}$$。这三个“无穷级别”在按顺序列出时，每一级都比前一级“更无穷”：

$$\text{fil} < \text{fill} < \text{filll}$$

也许“无穷”这个名字对这些单位来说有点令人困惑——也可以把它们看作不同的 *优先级*，因为归根结底，它们有助于确定哪些胶水真正参与拉伸或压缩的过程。通过让胶水具有“无穷”的拉伸或压缩分量， $$\mathrm \TeX$$ 使你能够创建可按任意所需数量拉伸或压缩的胶水。请注意，对于有限胶水， $$\mathrm \TeX$$ 会限制这类胶水可压缩的量。“无穷”胶水的一个例子是

`\hskip 3pt plus 2fil minus 1fill`

注意，我们不能写成，例如， `\hskip 1fil` 因为 $$\mathrm \TeX$$ 会报错，提示信息为 `Illegal unit of measure (pt inserted)`。到这里，这些“无穷级别”听起来可能很奇怪，但现在只需先按字面接受它，稍后我们就会看到 $$\mathrm \TeX$$ 在执行胶水计算时如何使用这些无穷。

### 无穷级别（“胶水阶次”）

在内部，当 $$\mathrm \TeX$$ 执行胶水计算时，它会将每一级无穷视为一个“胶水阶次”，范围从 0 到 3，其中第 0 阶用于具有物理尺寸的胶水，如 bp、pt、mm 等。不过，对于 $$\text{Lua}\mathrm\TeX$$ ，这里有一点不同，因为它实际上多了一种在许多其他 $$\mathrm \TeX$$ 引擎中并不存在的无穷类型（阶次）： $$\text{fi}$$ （见下方解释）。如果你阅读 $$\text{The } \mathrm \TeX\text{book}$$ ，你不会看到关于 $$\text{fi}$$ 无穷的任何提及——原因很简单，它并未在 Knuth 的原始 $$\mathrm \TeX$$ 软件中实现。因此，我们在 $$\text{Lua}\mathrm\TeX\text{'s}$$ 中的无穷阶次与你在关于 $$\mathrm \TeX$$. $$\text{Lua}\mathrm\TeX$$ 的书中可能看到的有所脱节，因为 $$\mathrm \TeX$$ 使用的无穷阶次范围是 0 到 4，而其他（常见）

|                           |                |     |     |      |       |
| ------------------------- | -------------- | --- | --- | ---- | ----- |
|                           | 物理单位（pt、mm、in） | fi  | fil | fill | filll |
| $$\text{Lua}\mathrm\TeX$$ | 0              | 1   | 2   | 3    | 4     |
| 其他引擎                      | 0              | 不适用 | 1   | 2    | 3     |

### 关于 $$\text{Lua}\mathrm\TeX$$：为什么要有额外的无穷？

$$\text{Lua}\mathrm\TeX$$ 源自多个项目和代码库，其中包括一个名为 [Omega](https://en.wikipedia.org/wiki/Omega_\(TeX\)). $$\text{Lua}\mathrm\TeX$$ 的项目，该项目吸收了 Omega 代码的某些部分，其中包括一种称为 $$\text{fi}$$的新型无穷胶水。来自 Omega 手册：

> “一个新的无穷级别 $$\text{fi}$$ 已被加入。它比 $$\text{fil}$$ 小，但比任何有限数量都大。它最初的用途是字母间拉伸：要么像阿拉伯语等书法文字那样填充黑色空隙；要么用于强调，如俄语；所有这些都无需重写现有的宏包。因此，新增了一个关键字， $$\text{fi}$$，以及两个新的原语， `\hfi` 和 `\vfi`。”

## 回到我们的挑战

按照 Knuth 的模型，让我们定义两个量：

* 盒子的期望宽度： $$\mathrm{W}\_{\mathrm{D}}$$——我们希望它有多大；
* 盒子的自然宽度： $$\mathrm{W}\_{\mathrm{N}}$$——在任何胶水被拉伸或压缩之前，其组成元素占据的总空间。

### 盒子的自然宽度

盒子的自然宽度是该盒中所有组件的总宽度：字符、kern、嵌套盒以及任何胶水。对于盒中的胶水，其自然宽度会忽略胶水的任何拉伸或压缩：也就是说，它是发生任何拉伸或压缩之前的尺寸。

再一次，这里是我们正在考察的盒子：

```latex
\hbox to100pt{%
A\hskip4pt plus3pt minus 2pt%
B\hskip 0pt plus 2fil%
C\hskip 0pt plus 2fill%
D\hskip 0pt plus 3fill%
}
```

显然，我们希望这个盒子宽 100pt，因此 $$\mathrm{W}*{\mathrm{D}}=100\mathrm{pt}$$ 但它的自然宽度， $$\mathrm{W}*{\mathrm{N}}$$，又是多少呢？要计算自然宽度，很明显我们需要四个字符（A、B、C 和 D）的宽度，加上四个胶水的自然宽度。

$$\eqalign{\mathrm{W}\_{\mathrm{N}} &= &\text{width(A)} + \text{width(B)} + \text{width(C)} + \text{width(D)} \ & &+ \text{width}(\verb\*\hskip 4pt plus3pt minus 2pt\*)\ & &+\text{width}(\verb\*\hskip 0pt plus 2fil\*)\ & &+\text{width}(\verb\*\hskip 0pt plus 2fill)*\ & &+\text{width}(\verb*\hskip 0pt plus 3fill\*)\ }$$

其中 $$\text{width}$$ 只是用来表示某个项目自然宽度的记号。我们可以从节点图中获得四个字符（A、B、C 和 D）的自然宽度：

![节点图](/files/00c05764b6d315abde55de3f993248def440d50f)

从上面的节点图可以看出：

$$\eqalign{ \text{width(A)} &= 7.50002\text{pt}\ \text{width(B)} &= 7.08336\text{pt}\ \text{width(C)} &= 7.22223\text{pt}\ \text{width(D)} &= 7.6389\text{pt}\ }$$

现在，我们只需要胶水的自然宽度，而这可以通过忽略拉伸和压缩分量轻松得到：

$$\eqalign{ &\text{width}(\verb\*\hskip 4pt plus3pt minus 2pt\*) & = 4\text{pt}\ &\text{width}(\verb\*\hskip 0pt plus 2fil\*) &=0\text{pt}\ &\text{width}(\verb\*\hskip 0pt plus 2fill)*&=0\text{pt}\ &\text{width}(\verb*\hskip 0pt plus 3fill\*)&=0\text{pt}\ }$$

因此：

$$\eqalign{ \mathrm{W}\_{\mathrm{N}} & = \text{widths of characters} + \text{width of all glues}\ &= 7.50002\text{pt}+ 7.08336\text{pt} + 7.22223\text{pt} + 7.6389\text{pt} + 4\text{pt}\space \text{(from }\verb\*\hskip\*\text{)}\ &=33.4445\text{pt}\ }$$

现在我们有了两个关键信息：

$$\eqalign{ \mathrm{W}*{\mathrm{D}} & = 100\text{pt}\ \mathrm{W}*{\mathrm{N}} & = 33.4445\text{pt}\ }$$

显然， $$\mathrm{W}*{\mathrm{D}} > \mathrm{W}*{\mathrm{N}}$$ 而差值是 $$(100-33.4445)\text{pt}=66.5555\text{pt}$$；这部分多出来的空间必须通过拉伸胶水来填充——但由哪些胶水来填，又要填多少呢？

### 谁拥有最大的拉伸量？

按照 Knuth 的方法（见 $$\text{The } \mathrm \TeX \text{book}$$第 77 页），并考虑到$$\text{fi}$$提供的额外无穷类型（ $$\text{Lua}\mathrm \TeX$$），下一步是写出盒子的 *总* 拉伸量，形式如下：

$$\text{total stretch} = y\_{0}+ y\_{1}\text{fi} +y\_{2}\text{fil} +y\_{3}\text{fill} +y\_{4}\text{filll}$$

首先，如果我们写下 $$\text{total glue}$$:

$$\text{total glue } = (\verb*4pt plus3pt minus 2pt*) + (\verb*0pt plus 2fil*) + (\verb*0pt plus 2fill*) + (\verb*0pt plus 3fill*)$$

，那么我们就可以看到 $$\text{total stretch}$$ 的一般形式是：

$$\eqalign{ \text{total stretch} & = 3\text{pt}+ 0\text{fi} + (2\text{fil}) + (2\text{fill} + 3\text{fill}) + 0\text{filll}\ &=3\text{pt}+ 0\text{fi} + 2\text{fil} + 5\text{fill} + 0\text{filll}\ }$$

将其与 $$\text{total stretch} = y\_{0}+ y\_{1}\text{fi} +y\_{2}\text{fil} +y\_{3}\text{fill} +y\_{4}\text{filll}\space$$比较，我们可以看到：

$$\eqalign{ y\_0 &=3\text{pt}\ y\_1 &=0\ y\_2 &=2\ y\_3&=5\ y\_4&=0\ }$$

$$\mathrm\TeX$$ 接下来“自问”：观察 $$\text{total stretch}$$，哪个无穷级别的非零值最高？检查我们盒子的 $$\text{total stretch}$$ 后，很明显，“最无穷”的非零拉伸分量是 $$\text{fill}$$ ，并且我们有 $$y\_3=5$$ 个这样的单位：提供全部拉伸的是具有 $$\text{fill}$$ 拉伸分量的胶水。 $$y\_3$$ 的下标 3 告诉我们将要使用的胶水的 `glue_order` ——在这里是用于拉伸。现在，如果我们查看这个 `\hbox` 的节点图中的“元数据”部分，我们就能理解另外两个“元数据值”了（我们将在下一节讨论 `glue_set` ）

![元数据](/files/73cf11219f155079dd4923a3d25c6f6951265421)

* `glue_sign`：告诉你胶水是处于自然长度、已拉伸还是已压缩：
* 0=设为自然宽度
* 1=拉伸
* 2=压缩

在我们的示例中， `glue_sign` 的值为 `1`，这意味着参与的胶水将被拉伸。

* `glue_order` 告诉你涉及的是哪一种“无穷”；对于 $$\text{Lua}\mathrm \TeX$$ ，值 3 表示具有 $$\text{fill}$$ 分量的胶水将参与胶水计算——在我们的例子中，它们将被拉伸。

任何没有用 $$\text{fill}$$ 单位定义拉伸分量的胶水都将 **设为其自然长度**：也就是说，它在我们的例子中根本不会拉伸。

### 要拉伸或压缩多少：计算 glue\_set

总结一下我们现在所处的位置和已知内容：

1. 盒子的期望宽度： $$\mathrm{W}\_{\mathrm{D}} = 100\text{pt}$$;
2. 盒子的自然宽度： $$\mathrm{W}\_{\mathrm{N}} = 33.4445\text{pt}$$;
3. 胶水必须拉伸，但只有具有 $$\text{fill}$$ 拉伸分量的胶水才会执行这种拉伸；
4. 我们总共有 $$(2+3)=5$$ 个 $$\text{fill}$$ 单位可用。

下一个问题是：这些胶水实际会拉伸多少？这就要用到 *胶水设定比率*——在我们的节点图中称为 `glue_set` 。其作用是计算需要填补多少空间，然后按各自拉伸分量大小的比例，把这些空间分配给相应的胶水。如果你回头看我们的实际 $$\mathrm \TeX$$ ，你就能准确看到哪些胶水的拉伸分量包含 `\hbox` 单位。 $$\text{fill}$$:

```latex
\hbox to100pt{%
A\hskip4pt plus3pt minus 2pt%
B\hskip 0pt plus 2fil%
C\hskip 0pt plus 2fill%
D\hskip 0pt plus 3fill%
}
```

该 $$\textit{glue set ratio }(\text{or } \verb*glue\_set*)$$ 的计算如下：

$$\eqalign{ \text{glue set ratio}\space (\verb*glue\_set*) = & {\text{amount to stretch}}\over{\text{value of highest infinity}}\ =& {\mathrm{W}*{\mathrm{D}}-\mathrm{W}*{\mathrm{N}}}\over{y\_3}\ =& {(100-33.4445)}\over{5}\ =& {66.5555}\over{5}\ =& 13.3111\space (\text{to 4 decimal places})\ }$$

现在进入 $$\mathrm\TeX$$ 算法的最后一步：

1. 对于每一个其拉伸分量与目标 `glue_order` （在我们的例子中是 3）相匹配的胶水项，该胶水的长度将变为：

$$\text{stretched value} = \text{natural length} + (\verb*glue\_set* \times \text{value of stretch component})$$

3. 其余所有胶水都设为其自然长度——也就是说，它们根本不拉伸。

查看我们盒子中的胶水：

```latex
\hbox to100pt{%
A\hskip4pt plus3pt minus 2pt%
B\hskip 0pt plus 2fil%
C\hskip 0pt plus 2fill%
D\hskip 0pt plus 3fill%
}
```

我们可以逐一计算它们的最终值：

1. **在 A 和 B 之间**: `\hskip 4pt plus3pt minus 2pt`。拉伸分量是 `3pt`，其阶次为 `0`。所需的 `glue_order` 是 `3`：忽略拉伸分量，这个胶水将采用其自然宽度 `4pt`.
2. **在 B 和 C 之间**: `\hskip 0pt plus 2fil`。拉伸分量是 `2fil`，其阶次为 `2`。所需的 `glue_order` 是 `3`：忽略拉伸分量，这个胶水将采用其自然宽度 `0pt`.
3. **在 C 和 D 之间**: `\hskip 0pt plus 2fill`。拉伸分量是 `2fill`，其阶次为 `3` 并匹配所需的 `glue_order` 的记号值 `3`。这个胶水将被拉伸到： $$\eqalign{ \text{stretched value}\space = & \text{natural width} + (\verb*glue\_set* \times \text{value of stretch component})\ = & 0\text{pt} + 13.3111 \times 2 \ = & 26.6222\text{pt}\ }$$5. **在 D 和盒子末尾之间**: `\hskip 0pt plus 3fill`。拉伸分量是 `3fill`，其阶次为 `3` 并匹配所需的 `glue_order` 的记号值 `3`。这个胶水将被拉伸到： $$\eqalign{ \text{stretched value}\space = &\text{natural width} + (\verb*glue\_set* \times \text{value of stretch component})\ =& 0\text{pt} + 13.3111 \times 3 \ = &39.9333\text{pt}\ }$$

## 最后：检查总宽度

计算胶水实际占据空间的过程称为 *设置胶水* 因此我们现在可以检查是否已将盒子填充到所需宽度， $$(\mathrm{W}\_{\mathrm{D}} = \text{100pt})$$:

$$\eqalign{ \mathrm{W}\_{\mathrm{D}} & = \text{width of all characters} + \text{width of all }\textbf{set}\text{ glue values}\ & = \text{A:7.50002pt} + \text{B:7.08336pt} + \text{C:7.22223pt} + \text{D:7.6389pt}\ & + \text{4pt} + \text{0pt} + \text{26.6222pt} + \text{39.9333pt}\ & = \text{100.00pt} }$$

我们现在知道了所有胶水的宽度，并且可以准备一张图，回答本文开头提出的问题：下面是我们这个盒子中字符之间的胶水宽度 `\hbox`:

![间距](/files/619edfe64d3b58a2d0635d3bfa785e91db11be15)


---

# 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/24-how-tex-calculates-glue-settings-in-an-hbox.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.
