跳到内容

This is the third, and concluding, article in a series which takes a look at \(\mathrm \TeX\) boxes and glue. The first post Boxes and Glue: A Brief, but Visual, Introduction Using LuaTeX introduced the concepts of boxes and glue and was followed by Pandora’s \hbox: Using LuaTeX to Lift the Lid of TeX Boxes which presented a \(\text{Lua}\mathrm\TeX\)-based Overleaf project to explore the deeper structures of \(\mathrm \TeX\) boxes through the use node graphs. In this final piece we take a “deep dive” into the mechanics of how \(\mathrm \TeX\) calculates glue values in an \hbox: a process referred to as setting the glue. We make extensive use of node graphs (introduced in the Pandora’s \hbox: Using LuaTeX to Lift the Lid of TeX Boxes in this series) and show how to use and interpret some of the data they provide: glue_set, glue_sign and glue_order.

We provide a fully worked example of glue calculations for an \hbox and cover a lot of detail; however, there may be additional circumstances and considerations that we don’t have space to address here and the interested reader is referred to page 77 of \(\text{The } \mathrm \TeX \text{book}\).

The challenge

Suppose we have an \hbox such as this:

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

Here is what this box looks like—for clarity, shown enlarged and with a border:

box

The question is: what is the final value, in \(\mathrm \TeX\) points, of the space (glue) between the following items:

  • A and B
  • B and C
  • C and D
  • D and the end of the box

i.e., we want to calculate the values of \(\mathrm{g}_{1}, \mathrm{g}_{2}, \mathrm{g}_{3} \text{ and } \mathrm{g}_{4}:\)

glue

Here is a node graph representing the above box. Of particular interest are three values contained in the “metadata” section:

  • glue_set
  • glue_sign
  • glue_order

node graph

It is important to note that a particular set of values for glue_set, glue_sign and glue_order only affect glues within the top-level box: they do not affect glues within nested boxes: each nested box (hlist or vlist object) has its own values for these three parameters. Here is an example of an \hbox nested within an outer \hbox. In this example, you can clearly see the different values of glue_set—of course, the nested box can also have different values for glue_sign and glue_order.

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

node graph

Glue types, infinities and orders: A summary

\(\mathrm \TeX\) provides a number of primitive glue-related commands, including:

  • horizontal glue: \hskip, \hfil, \hfill, \hfilneg, \hss;
  • vertical glue:\vskip, \vfil, \vfill, \vfilneg, \vss;

together with \mskip for inserting glue in math expressions.

An item of glue is defined by a set of three values:

  • natural width: how much space it occupies if you don’t stretch or shrink it;
  • stretch component: by how much the glue can stretch;
  • shrink component: by how much the glue can shrink.

What we are going to consider is the use of glue inside an \hbox{...} and the calculations \(\mathrm \TeX\) uses to determine how much space the glue will eventually occupy. The command we’ll be using to create some horizontal glue is \hskip, which takes the form:

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

For vertical glue you would use \vskip <natural width> plus <amount to stretch> minus <amount to shrink>.

For example, some typical horizontal glue would be expressed as \hskip 3pt plus 2pt minus 1pt. You can use other physical units too:

  • \hskip 3mm plus 2mm minus 1mm
  • \hskip 3in plus 2in minus 1in
  • \hskip 1in plus 3cm minus 20mm

\(\mathrm \TeX\) glue and units of “infinity”

For the shrink or stretch component of the glue \(\mathrm \TeX\) introduces another type of unit: so-called “infinities”: \(\text{fil}\), \(\text{fill}\) and \(\text{filll}\). These three “levels of infinity” are such that when listed in a sequence, each one is “more infinite” than the previous:

\(\text{fil} < \text{fill} < \text{filll}\)

Perhaps “infinities” is a slightly confusing name for these units—it might be helpful to also think of them as different levels of priority, because, ultimately, they help to determine which glues actually take part in the process of stretching or shrinking. By having glue with an “infinite” stretch or shrink component, \(\mathrm \TeX\) lets you create glue that can stretch or shrink by any desired amount. Note that for finite glues, \(\mathrm \TeX\) will restrict the amount by which such glues can shrink. An example of “infinite” glue is

\hskip 3pt plus 2fil minus 1fill

Note, we can’t write, say, \hskip 1fil because \(\mathrm \TeX\) will report an error with the message Illegal unit of measure (pt inserted). At this point, these “levels of infinity” may sound very strange but, for now, just accept it at face value and we’ll soon see how \(\mathrm \TeX\) uses these infinities when performing glue calculations.

Levels of infinity (“glue order”)

Internally, when \(\mathrm \TeX\) performs its glue calculations it considers that each level of infinity is a “glue order” ranging from 0–3 where the 0th order is for glue with physical dimensions such as bp, pt, mm and so forth. However, with \(\text{Lua}\mathrm\TeX\) there is a slight deviation because it actually has an extra type (order) of infinity not present in many other \(\mathrm \TeX\) engines: \(\text{fi}\) (see explanation below). If you read \(\text{The } \mathrm \TeX\text{book}\) you will not see any mention of the \(\text{fi}\) infinity—simply because it is not implemented in Knuth’s original \(\mathrm \TeX\) software. Consequently, we have a slight “disconnect” between \(\text{Lua}\mathrm\TeX\text{'s}\) order of infinities and those you might see in books on \(\mathrm \TeX\). \(\text{Lua}\mathrm\TeX\) uses infinities whose order ranges from 0–4 but other (common) \(\mathrm \TeX\) engines range from 0–3. Here is a table showing the glue order assigned to each type of glue unit.

Physical units (pt, mm, in) fi fil fill filll
\(\text{Lua}\mathrm\TeX\) 0 1 2 3 4
Other engines 0 N/A 1 2 3

Notes On \(\text{Lua}\mathrm\TeX\): Why have an extra infinity?

\(\text{Lua}\mathrm\TeX\) is derived from a number of projects and code libraries, including one called Omega. \(\text{Lua}\mathrm\TeX\) incorporated certain aspects of Omega’s code and that includes a new type of infinite glue called \(\text{fi}\). From the Omega manual:

“A new infinity level \(\text{fi}\) has been added. It is smaller than \(\text{fil}\) but bigger than any finite quantity. Its original intention was for inter-letter stretching: either filling-in-the-black, as is done for calligraphic scripts such as Arabic; or for emphasis, as in Russian; all this without having to rewrite existing macro packages. There is therefore a new keyword, \(\text{fi}\), and two new primitives, \hfi and \vfi.”

Back to our challenge

Following Knuth’s model, let’s define two quantities:

  • the desired width of a box: \(\mathrm{W}_{\mathrm{D}}\)—how big do we want it to be;
  • the natural width a box: \(\mathrm{W}_{\mathrm{N}}\)—the total space occupied by its constituent elements before any glues are stretched or shrunk.

The natural width of a box

The natural width of a box is the total width of all components in that box: characters, kerns, nested boxes and any glue. For glue inside the box its natural width ignores any stretch or shrink of the glue: i.e., its size before any stretching or shrinking takes place.

Once again, here is the box we are examining:

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

Clearly, we want the box to be 100pt wide, hence \(\mathrm{W}_{\mathrm{D}}=100\mathrm{pt}\) but what about its natural width, \(\mathrm{W}_{\mathrm{N}} \)? To calculate the natural width it is clear that we need the widths of the four characters (A, B, C and D) plus the natural widths of the four glues.

\( \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*)\\ } \)

Where \( \text{width}\) is just a notation to denote the natural width of an item. We can obtain the natural widths of the four characters (A, B, C and D) from our node graph:

node graph

From the above node graph we can see that:

\( \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}\\ } \)

Now, all we need are the natural widths of our glues which are easily obtained by ignoring the stretch and shrink components:

\( \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}\\ }\)

Hence:

\( \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}\\ } \)

We now have two key pieces of information:

\( \eqalign{ \mathrm{W}_{\mathrm{D}} & = 100\text{pt}\\ \mathrm{W}_{\mathrm{N}} & = 33.4445\text{pt}\\ } \)

Clearly, \( \mathrm{W}_{\mathrm{D}} > \mathrm{W}_{\mathrm{N}}\) and the difference is \( (100-33.4445)\text{pt}=66.5555\text{pt}\); this excess space has to be filled by stretching the glues—but which ones and by how much?

Who has the most stretch?

Following Knuth’s methodology (page 77 of \(\text{The } \mathrm \TeX \text{book}\)), but allowing for the additional infinity type (\(\text{fi}\)) provided by \(\text{Lua}\mathrm \TeX\), the next step is to write down the box’s total stretch in the form:

\( \text{total stretch} = y_{0}+ y_{1}\text{fi} +y_{2}\text{fil} +y_{3}\text{fill} +y_{4}\text{filll} \)

Firstly, if we write down the \(\text{total glue}\):

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

we can then see that the \(\text{total stretch}\) is:

\( \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}\\ } \)

Comparing this to \(\text{total stretch} = y_{0}+ y_{1}\text{fi} +y_{2}\text{fil} +y_{3}\text{fill} +y_{4}\text{filll}\space\)we can see that:

\( \eqalign{ y_0 &=3\text{pt}\\ y_1 &=0\\ y_2 &=2\\ y_3&=5\\ y_4&=0\\ } \)

\(\mathrm\TeX\) next “asks itself”: looking at the \(\text{total stretch}\), what is the highest level of infinity with a non-zero value? On inspection of our box’s \(\text{total stretch}\) it is clear that the “most infinite” non-zero stretch component is \(\text{fill}\) and we have \(y_3=5\) units of that: it is glues with a \(\text{fill}\) stretch component that provide all the stretching. The subscript 3 of \(y_3\) tells us the glue_order of the glue that will be used—in this case for stretching. Now, if we look at the “metadata” section within our node diagram for this \hbox we can now make sense of two more “metadata values” (we’ll address glue_set in the next section)

metadata

  • glue_sign: tells you whether the glue is set to its natural length, stretched or shrunk:
    • 0=set to natural width
    • 1=stretch
    • 2=shrink
    • In our example, glue_sign has the value of 1, meaning that participating glues are to be stretched.

  • glue_order tells you which “infinity” is involved; for \(\text{Lua}\mathrm \TeX\) a value of 3 tells you that glues with a \(\text{fill}\) component will participate in glue calculations—in our case they will stretch.

Any glue that does not have a stretch component defined in units of \(\text{fill}\) will be set to its natural length: i.e., it will not (in our case) stretch at all.

How much to stretch or shrink: calculating glue_set

To summarize where we are and what we know:

  1. desired width of box: \(\mathrm{W}_{\mathrm{D}} = 100\text{pt}\);
  2. natural width of box: \(\mathrm{W}_{\mathrm{N}} = 33.4445\text{pt}\);
  3. glue will have to stretch but only glues with a \(\text{fill}\) stretch component will do that stretching;
  4. we have a total of \((2+3)=5\) units of \(\text{fill}\) available.

The next question is: by how much will those glues actually stretch? Enter the glue set ratio—referred to as glue_set in our node graph. What \(\mathrm \TeX\) does is to work out how much space must be filled and then distributes that amount of space among the appropriate glues in proportion to the size of their stretch component. If you look back at our actual \hbox you can see exactly which glues have stretch components containing units of \(\text{fill}\):

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

The \(\textit{glue set ratio }(\text{or } \verb*glue_set*)\) is calculated as follows:

\( \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})\\ } \)

And now the final step in the \(\mathrm\TeX\) algorithm is applied:

  1. for each item of glue whose stretch component matches the desired glue_order (3 in our case) the length of that glue will become:
  2. \(\text{stretched value} = \text{natural length} + (\verb*glue_set* \times \text{value of stretch component})\)

  3. all other glues are set to their natural length—i.e., they do not stretch at all.

Looking at the glues in our box:

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

we can go through them to calculate their final values:

  1. Between A and B: \hskip 4pt plus3pt minus 2pt. The stretch component is 3pt, which is order 0. The required glue_order is 3: the stretch component is ignored and this glue assumes its natural width of 4pt.
  2. Between B and C: \hskip 0pt plus 2fil. The stretch component is 2fil, which is order 2. The required glue_order is 3: the stretch component is ignored and this glue assumes its natural width of 0pt.
  3. Between C and D: \hskip 0pt plus 2fill. The stretch component is 2fill, which is order 3 and matches the required glue_order of 3. This glue will be stretched to:
  4. \( \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. Between D and the end of the box: \hskip 0pt plus 3fill. The stretch component is 3fill, which is order 3 and matches the required glue_order of 3. This glue will be stretched to:
  6. \( \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}\\ } \)

And finally: Checking the total width

The process of calculating the actual space occupied by glue is called setting the glue so we can now check if we have filled the box to the desired width, \((\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} }\)

We now know the widths of all glues and can prepare a graphic which answers the question posed at the start of this article: here are the glue widths between the characters in our \hbox:

glue

Overleaf guides

LaTeX Basics

Mathematics

Figures and tables

References and Citations

Languages

Document structure

Formatting

Fonts

Presentations

Commands

Field specific

Class files

Advanced TeX/LaTeX