2

On p.235 of TeXbook it is said:

TeX reads an entire \halign{...} specification into its memory before typesetting anything, and it keeps track of the maximum width of each column, assuming that each column is set without stretching or shrinking the glue. Then it goes back and puts every entry into a box, setting the glue so that each box has the maximum column width.

In the first example badness is exceeded, but no warning is issued. In the second example entries are left-justified by default - why? And again, why no underfull warning is given?

\halign{\indent#&\quad#\cr
Horizontal lists&Chapter 14\cr
Vertical lists&Chapter 15\cr
Math lists&Chapter 17\cr}

\bigskip

\halign{\indent#&\quad#\cr
Horizontal&Chapter 14\cr
Vertical&Chapter 15\cr
Math&Chapter 17\cr}

\end
18
  • In the second one there is no glue to stretch, so everything stays where it is. Jan 15 '20 at 5:44
  • You probably won't get around reading the relevant parts of tex.web: github.com/TeX-Live/texlive-source/blob/… Jan 15 '20 at 5:55
  • @HenriMenke So, such behavior is "by definition". Where in TeXbook is it defined then? Jan 15 '20 at 6:53
  • 1
    Specifically, see §810 of the program. Note some similarity to §657–§660, but without any underfull/overfull reporting. Jan 15 '20 at 8:19
  • 2
    @IgorLiferenko \hbox to 1000pt{hello world} does contain glue, and the rules for its setting are described p. 77. OTOH, \hbox to 1000pt{hello} does not, and I don't know if the TeXbook describes how this case is handled. Experimentation suggests that in such a case, the box has the prescribed width and the contents is typeset flush left inside the box.
    – frougon
    Jan 16 '20 at 13:11
5

(Basically already answered in the comments, but adding some details and making an answer, to get this question off the unanswered list.)

  1. First TeX computes the width of each column (as mentioned in the snippet from The TeXbook quoted in the question, i.e. as the maximum of the natural widths of that column from each row).

  2. After this, if the alignment itself—the prototype row, with those column widths—is overfull/underfull, then TeX will show an overfull/underfull message. So (as I understand it) you cannot ever get this error message with just a \halign{...} but you can easily get this with \halign to... or \halign spread....

  3. Finally, when setting the glue for each box in that column, TeX simply computes the "glue set" (namely "glue set ratio" and "glue set order") and sets the glue; no underfull/overfull messages are printed.

    • It is a natural consequence of the way glue setting works (in general) that when there's no stretch available (and in particular when there's no glue), the boxes are simply laid out in order, i.e. flush left.

For the third point above, implementation details (of the computation of the glue set for alignment entries) are in section §810 of the program (also elaborated below) — this is pretty much the same as the usual computation (in §658 and §664 in hpack), except without the reporting of underfull/overfull boxes.


A more detailed account, with understanding gained from some late-night adventures with gdb, of how TeX handles \halign. Consider the first test-case in the question:

% \noindent This is a test.
\halign{\indent#&\quad#\cr
Horizontal lists&Chapter 14\cr
Vertical lists&Chapter 15\cr
Math lists&Chapter 17\cr}
% \noindent End of test.

which results in

first

What happens in this case (and the general case is similar) is:

  1. TeX first scans the preamble (in this case \indent#&\quad# ,i.e. two columns \indent# and \quad#), and builds a internal "preamble" list, that looks like this:

    preamble

  2. Then for each row, it reads the contents basically as if in restricted horizontal mode, inserting tabskips as in the preamble and reading from the "u" and "v" token lists for each column whenever appropriate. Eventually it appends an unset box to the vertical list. So in this case it appends the following rows (separated by \baselineksip glue, not shown here) (using to denote the \indent and \quad glues, and for the stretchable interword glue):

    unset

    For example, the middle row above would be shown as the following in TeX output notation:

    \hbox(6.94444+1.94444)x133.50018
    .\glue(\tabskip) 0.0
    .\unsetbox(6.94444+0.0)x74.5834, stretch 1.66666, shrink 1.11111
    ..\hbox(0.0+0.0)x20.0
    ..\tenrm V
    ..\kern-0.83334
    ..\tenrm e
    ..\tenrm r
    ..\tenrm t
    ..\tenrm i
    ..\tenrm c
    ..\tenrm a
    ..\tenrm l
    ..\glue 3.33333 plus 1.66666 minus 1.11111
    ..\tenrm l
    ..\tenrm i
    ..\tenrm s
    ..\tenrm t
    ..\tenrm s
    .\glue(\tabskip) 0.0
    .\unsetbox(6.94444+1.94444)x58.91678, stretch 1.66666, shrink 1.11111
    ..\glue 10.00002
    ..\tenrm C
    ..\tenrm h
    ..\tenrm a
    ..\tenrm p
    ..\tenrm t
    ..\tenrm e
    ..\tenrm r
    ..\glue 3.33333 plus 1.66666 minus 1.11111
    ..\tenrm 1
    ..\tenrm 5
    .\glue(\tabskip) 0.0
    
  3. When all rows have been read, the preamble's alignrecords are updated to the max widths encountered for the columns respectively. This gives the "prototype row", and this is the place where underfull/overfull warnings may occur:

    prototype

    In TeX notation:

    \glue(\tabskip) 0.0
    \unsetbox(0.0+0.0)x86.25012
    \glue(\tabskip) 0.0
    \unsetbox(0.0+0.0)x58.91678
    \glue(\tabskip) 0.0
    
  4. Finally, glue is set in all the unset boxes that were earlier appended to the vlist (again, baselineskip glue not shown here):

    set

    In TeX notation, this is (I've collapsed the \tenrm rows for brevity):

    \glue(\baselineskip) 5.05556
    \hbox(6.94444+1.94444)x145.1669
    .\glue(\tabskip) 0.0
    .\hbox(6.94444+1.94444)x86.25012
    ..\hbox(0.0+0.0)x20.0
    ..\tenrm Horizon\kern-0.27779tal
    ..\glue 3.33333 plus 1.66666 minus 1.11111
    ..\tenrm lists
    .\glue(\tabskip) 0.0
    .\hbox(6.94444+1.94444)x58.91678
    ..\glue 10.00002
    ..\tenrm Chapter
    ..\glue 3.33333 plus 1.66666 minus 1.11111
    ..\tenrm 14
    .\glue(\tabskip) 0.0
    \glue(\baselineskip) 3.11111
    \hbox(6.94444+1.94444)x145.1669
    .\glue(\tabskip) 0.0
    .\hbox(6.94444+1.94444)x86.25012, glue set 7.00008
    ..\hbox(0.0+0.0)x20.0
    ..\tenrm V\kern-0.83334ertical
    ..\glue 3.33333 plus 1.66666 minus 1.11111
    ..\tenrm lists
    .\glue(\tabskip) 0.0
    .\hbox(6.94444+1.94444)x58.91678
    ..\glue 10.00002
    ..\tenrm Chapter
    ..\glue 3.33333 plus 1.66666 minus 1.11111
    ..\tenrm 15
    .\glue(\tabskip) 0.0
    \glue(\baselineskip) 3.11111
    \hbox(6.94444+1.94444)x145.1669
    .\glue(\tabskip) 0.0
    .\hbox(6.94444+1.94444)x86.25012, glue set 13.18344
    ..\hbox(0.0+0.0)x20.0
    ..\tenrm Math
    ..\glue 3.33333 plus 1.66666 minus 1.11111
    ..\tenrm lists
    .\glue(\tabskip) 0.0
    .\hbox(6.94444+1.94444)x58.91678
    ..\glue 10.00002
    ..\tenrm Chapter
    ..\glue 3.33333 plus 1.66666 minus 1.11111
    ..\tenrm 17
    .\glue(\tabskip) 0.0
    

And that's what goes into the output.


The question was specifically about the last step (Step 4) above I guess. So here's a bit more detail on how TeX sets glue in each entry. (It is explained in The TeXbook at the top of page 77.) For simplicity, let's consider only the "stretching" case, where the natural width x is less than the desired width w. First TeX computes the total available stretch in the box, by adding up the stretch of each glue in the box and, in the resulting sum, taking only the highest "order of infinity" that has nonzero stretch. For example, let's say there's "7 fil" of total available stretch. Then, as we want to achieve w-x of stretching, each glue whose stretch is (s) fil (for some value s) will be stretched by (w - x) * s/7, so that when these respective s components (which, remember, must add up to 7 by assumption) are added, we'll have a total of (w-x) of stretch, as desired. In this case we'll say that the glue set ratio is (w-x)/7 (as that's what each stretch s is multiplied by) and the glue set order is fil.

In the first example above, this plays out as follows, for column 1:

  1. Row 1: x = w, so no stretch is needed: the glue set ratio is 0, and the glue remains at its natural width.

  2. Row 2: the natural width of "\indent Vertical lists" was 74.5834pt and we wanted it to occupy 86.25012pt, so we need to stretch by 11.66672pt. The available stretch is 1.66666pt, from the interword glue. So we decide that the "glue set ratio" is 11.66672pt/1.66666pt ≈ 7.0008 (when you do the calculations in sp, i.e. (5652488sp - 4887898sp)/(109226sp)). This means that, left-to-right, the boxes become:

    • \indent, at its natural width of 20pt (it has no available stretch)

    • The letters and kerns in "Vertical", at their natural width of 53.91672pt.

    • the inter-word glue of 3.3333pt, additionally stretched by 7.0008 * 1.66666pt = 11.66672 pt. So effectively, takes up a width of 15.00005pt.

    • the letters in "lists", at their natural width of 17.33336pt.

    The total is (53.91672pt + 15.00005pt + 17.33336pt) = 86.25012pt, as desired.

  3. Row 3: Similar: the natural width of "\indent Math lists" is 64.27786pt so we need to stretch by 86.25012pt - 64.27786pt = 21.97226pt, the available stretch is 1.66666pt, so we decide that the "glue set ratio" is 21.97226pt/1.66666pt = 13.18344. This means, as in Row 2, that the inter-word glue stretches by 1.66666 pt * 13.18344 = 21.97226pt.

This also explains the second example, of:

% \noindent This is a test.
\halign{\indent#&\quad#\cr
Horizontal&Chapter 14\cr
Vertical&Chapter 15\cr
Math&Chapter 17\cr}
% \noindent End of test.

When there is no available stretch at all (such as when there's no glue), then it does not matter what the glue set ratio is. There is no glue that can be stretched, so "each stretchable glue is stretched by r times its stretch component" is vacuously true, whatever the value of r. (Internally, TeX sets r=0 but AFAICT this affects only the debug output.) So when the boxes are laid out, there's only "\indent" and "Vertical" (etc.), which explains the output of everything being flush left:

second

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.