> For the complete documentation index, see [llms.txt](https://overleaf-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://overleaf-pro.ayaka.space/latex/zh-cn/shen-ru-wen-zhang/24-how-tex-calculates-glue-settings-in-an-hbox.md).

# TeX 如何计算 \hbox 中的胶水设置

这是本系列的第三篇，也是最后一篇文章，探讨…… $$\mathrm \TeX$$ 盒子和胶。第一篇文章 [盒子与胶水：使用 LuaTeX 的简短但可视化的介绍](/latex/zh-cn/shen-ru-wen-zhang/11-boxes-and-glue-a-brief-but-visual-introduction-using-luatex.md) 介绍了盒子和胶的概念，随后又有 [潘多拉的 \\\hbox：使用 LuaTeX 揭开 TeX 盒子的面纱](/latex/zh-cn/shen-ru-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`：这一过程称为 *设置胶*。我们将大量使用节点图（在本系列的 [潘多拉的 \\\hbox：使用 LuaTeX 揭开 TeX 盒子的面纱](/latex/zh-cn/shen-ru-wen-zhang/36-pandora-s-hbox-using-luatex-to-lift-the-lid-of-tex-boxes.md) 一文中介绍过），并展示如何使用和解读它们提供的一些数据： `glue_set`, `glue_sign` 和 `glue_order`.

我们将为一个……提供一个完整推导的胶计算示例 `\hbox` 并涵盖许多细节；不过，可能还有一些额外情况和考虑因素，本文无暇讨论，有兴趣的读者可参见……第 77 页 $$\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** *<自然宽度>* **plus** *<拉伸量>* **minus** *<收缩量>*`

对于 *纵向* 你会使用的胶 `**\\vskip** *<自然宽度>* **plus** *<拉伸量>* **minus** *<收缩量>*`.

例如，一些典型的水平胶可表示为 `\\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$$ 会报错，信息为 `非法的度量单位（已插入 pt）`。此时，这些“无穷量级别”听起来可能非常奇怪，但现在先照字面接受它，稍后我们就会看到 $$\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$$ 使用的无穷量阶次范围为 0 到 4，但其他（常见的） $$\mathrm \TeX$$ 引擎范围为 0 到 3。下面的表格显示了分配给每种胶单位类型的胶阶次。

|                           |                |     |     |      |       |
| ------------------------- | -------------- | --- | --- | ---- | ----- |
|                           | 物理单位（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 的方法（见……第 77 页）， $$\text{The } \mathrm \TeX \text{book}$$），但要考虑额外的无穷类型（$$\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://overleaf-pro.ayaka.space/latex/zh-cn/shen-ru-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.
