\part Islands\\and the\\Output Routine\label{ISLANDS}\endpart \chapter Packaging figures, tables, \dots, with captions\label{PI}\endchapter \runningchapter{Packaging figures, tables, \dots\null} The previous Parts have covered almost all the standard \lamstex\ constructions that involve *\label*'s (as well as miscellaneous constructions that don't). But *\caption*'s, in a \footnote{In version~*1* of \lamstex@, *\table* was used to indicate a class of *\island*'s in the main file, and to indicate a specific table construction in the ``tables'' file produced with \lamstbl@; this implicit inconsistency became explicit in \muttontex@, where the tables are specified within the main file. Consequently, the *\table* class of *\island*'s has been renamed *\Table*. For consistency, *\figure* has likewise been renamed *\Figure*, and then *\figureproofing* was renamed *\Figureproofing* (although it does not act on *\Figure*'s per se, but only on *\hbyw*'s).} *\Figure*, *\Table*, or the more general *\island* construction, have been postponed to this Part, because they interact so intimately with the *\output* routine. \section{Preliminaries} First we want to disable certain commands from *plain* \tex@, since they would conflict with \lamstex's approach to these insertions: \C** \let\topinsert=\undefined \let\midinsert=\undefined \let\pageinsert=\undefined ** Next, we deal with the rather simple matter of producing crop marks around an *\hybw* or *\Hbyw*. We first need a flag for telling whether *\Figureproofing* or *\noFigureproofing* is in force: \C** \newif\iffigproofing@ \def\Figureproofing{\figproofing@true} \def\noFigureproofing{\figproofing@false} ** The special *\Hbyw* construction is reduced to *\hbyw*, but also sets a flag, to indicate that this *\hbyw* will need further processing later on: \C** \newif\ifHby@ \def\Hbyw#1{\global\Hby@true\hbyw\vsize{#1}} ** The *\global* is needed because the later processing will occur after the current group is completed. An *\hbyw#1#2* is basically going to be ** \vbox to#1{\hbox to#2{}\vfil} ** (\tex\ allows *\hbox to#2{}* without an *\hfil* within the braces, but the *\vfil* is necessary, since there is already something in the *\vbox*.) We will actually use \setbox0\hbox{\} \setbox1\hbox{\} ** \hbox{"box0 \vbox to#1{\hbox to#2{}\vfil} "box1} ** when crop marks are required, which we will produce by first adding the ones on the left side, and then those on the right. To produce the crops marks, we first store a horizontal rule 5~points wide (and default width *.4pt*) in *\box0*, and declare its height to be *0pt*, so that it won't interfere with anything else: ** \setbox0=\vbox{\hrule \width5pt}\ht0=0pt ** Now ** \vbox to#1{\hrule \height5pt \width.4pt\vfil \hrule \height5pt \width.4pt} ** gives the vertical parts of the left crops, $$\vbox to.5in{\hrule \height5pt \width.4pt\vfil \hrule \height5pt \width.4pt} $$ with the baseline at the bottom of this box. To add in the horizontal parts, $$ \setbox0\vbox{\hrule \width5pt}\ht0=0pt \vbox to.5in{\hrule \height5pt \width.4pt\vfil\hrule \height5pt \width.4pt} \kern-.4pt\rlap{\copy0}\raise.5in\hbox{\rlap{\copy0}} $$ we move back the *.4pt* width of these marks, and then *\rlap* a copy of *\box0*, containing the horizontal rule, to give the bottom horizontal rule, and also *\rlap* a copy that has been raised by the height *#1*, to give the top one. ** \vbox to#1{\hrule \height5pt \width.4pt\vfil\hrule \height5pt \width.4pt} \kern-.4pt\rlap{\copy0}\raise#1\hbox{\rlap{\copy0}} ** A similar construction produces the crop marks on the right, using *\llap* instead of *\rlap*: ** \vbox to#1{\hrule \height5pt \width.4pt\vfil\hrule \height5pt \width.4pt} \kern-.4pt\llap{\copy0}\raise#1\hbox{\llap{\box0}} ** Of course, we only want to add these crop marks when *\iffigproofing@* is true. But we also don't want to add the crop marks if *\ifHby@* happens to be true---in that case, the final height is to be determined later, based on the height of the caption, and we will deal with the crop mark problem later.\pagelabel{HBY} \C** \def\hbyw#1#2{% \hbox{% \ifHby@ \else "2 \iffigproofing@ \setbox0=\vbox{\hrule \width5pt}\ht0=0pt \vbox to#1{\hrule \height5pt \width.4pt\vfil\hrule \height5pt \width.4pt}% \kern-.4pt\rlap{\copy0}\raise#1\hbox{\rlap{\copy0}}% \fi \fi "2 \vbox to#1{\hbox to#2{}\vfil}% "2 \ifHby@ "2 \else \iffigproofing@ \vbox to#1{\hrule \height5pt \width.4pt\vfil\hrule \height5pt \width.4pt}% \kern-.4pt\llap{\copy0}\raise#1\hbox{\llap{\box0}}% \fi \fi}} ** \section{Starting an \CS{island}}Next comes the general mechanism whereby \setbox0\hbox{\} ** \island "box0 \caption{...} \endisland ** packages the \ together with the caption into a box. At first sight, *\island*'s and *\claim*'s seem to be quite similar, since they can be associated into different ``classes'', and since there is a *\newisland* construction much like *\newclaim*. But they function somewhat differently, since *\island*'s are not themselves numbered---% it is the *\caption*'s on the *\island*'s that must be numbered (and one uses *\caption""...""* to get a quoted number, etc.); consequently, the *\claim* definitions cannot be mimicked too directly. \medskip We begin by declaring the counter and control sequences associated to general *\island*'s: \C** \newcount\island@C \let\island@P=\empty \let\island@Q=\empty \def\island@S#1{#1\null.} \let\island@N=\arabic \def\island@F{\rm} ** We add the *\null* in case *#1* ends with an upper-case letter (compare page~\ref{ANULL}). Just as *\claim*'s have a *\claimclass@*, our *\island*'s will have an *\islandclass@*, which will be `*...*' when we use ** \island \c{...} ** (or when we use something created by *\newisland* with this *\c{...}*), and empty if we omit the *\c{...}*. Similarly, a construction created with *\newisland*, say *\map*, will define\linebreak *\islandtype@* to be *\map*, analogous to *\claimtype@*, while an ordinary *\island* will define *\islandtype@* to be *\island*. Analogous to *\claim@@@P*, etc., we define \C** \def\island@@@P{\csname\exxx@\islandtype@ @P\endcsname} "2 \def\island@@@Q{\csname\exxx@\islandtype@ @Q\endcsname} "2 \def\island@@@S{\csname\exxx@\islandtype@ @S\endcsname} "2 \def\island@@@N{\csname\exxx@\islandtype@ @N\endcsname} "2 \def\island@@@F{\csname\exxx@\islandtype@ @F\endcsname} ** while for *\island@@@C* we \C** \def\island@@@C{\csname island@C\islandclass@\endcsname} ** We want to give an error message if an *\island* is not used within some sort of *\...place*, so we declare a flag \C** \newif\ifplace@ ** which such constructions can set true, and which each *\island* will eventually set false. Moreover, we want a flag \C** \newif\ifisland@ ** which each *\island* will set true, so that constructions like *\Aplace{...}* can give an error message if `*...*' is not some sort of *\island*. If *\island* is used when *\ifplace@* is false, we will give an error message. Otherwise, we will initialize *\islandclass@* to be empty and *\islandtype@* to be *\island*, and then use a *\futurelet* to see if a *\c* comes next: \C** \def\island{% \ifplace@ \def\next@{\let\islandclass@=\empty \def\islandtype@{\island}% \futurelet\next\island@}% \else \long\def\next@##1\endisland{\Err@{\noexpand\island must be used after some type of \string\...place}}% \fi \next@} ** Notice that when we give the error message, we also swallow the whole *\island"allowbreak...."allowbreak\endisland* construction, to eliminate confusion. The *\long* is required because the *\caption* before the *\endisland* is allowed to contain a blank line. For the *\noexpand*, see section~\Sref{SACN}. If *\island* is followed by *\c* we will use *\island@c*. Otherwise we will need yet another *\futurelet*, to see if the next token is a *{*, since we want to give an error message if a suitable group *{...}* for the ``caption prefix'' doesn't come next: \C** \def\island@{\ifx\next\c\let\next@=\island@c\else \def\next@{\futurelet\next\island@@}\fi\next@} ** Here *\island@@* will call *\island@@@* if a group *{...}* does follow, but give an error message otherwise: \C** \def\island@@{% \ifcat\bgroup\noexpand\next \let\next@=\island@@@ \else \def\next@{\Err@{\noexpand\island must be followed by a {prefix} for \string\caption's}}% \fi \next@} ** (See section~\Sref{SACN} for the use of *\noexpand* in the error message.) Finally, if we've made it this far without an error message, i.e., if we've been scanning ** \island {...} ** then we will save `*...*' in *\captionprefix@*, and start to store everything in a box: ** \global\setbox\islandbox@=\vbox\bgroup ** Although the *\global* really isn't necessary here, some of our routines will require *\global*, so we will use it in all cases. We therefore first want to declare a new box *\islandbox@*. Moreover, we also want a new counter *\captioncount@*, which is quite different from *\island@C*: it records the number of *\caption*'s that occur within the current *\island* (remember that an *\island* can have more than one *\caption*): \C** \newbox\islandbox@ \newcount\captioncount@ "slip \def\island@@@#1{\def\captionprefix@{#1}% \captioncount@=0 \global\setbox\islandbox@=\vbox\bgroup} ** The *\egroup* matching this *\bgroup* might be supplied by the *\endisland*; in most cases, however, it will be supplied by a *\caption*, which will then go on to store the *\caption* argument, and then combine this properly with *\box\islandbox@*. \medskip Leaving these details aside for the moment, let us now consider *\island@c*, which we obtain when we have ** \island \c{...} ** As we might expect from our experience with *\newclaim*, a construction created by *\newisland* is going to lead us directly to *\island\c{...}*. Since such constructions must also be used only within a suitable *\...place* construction, this means that we {\it must repeat our *\ifplace@* test}. When *\ifplace@* is false, we again want to give an error message, but instead of the general message ** \island must be used after some type of \...place ** we want a more specific message mentioning our particular construction, say *\map*, which has been recorded in *\islandtype@*. Moreover, as before, we want to swallow the entire ** \map ... \endmap ** text. This requires much the same tomfoolery that we have engaged in before (compare page~\ref{TOMF}). If we {\advance\litindent-40pt ** \edef\next@{\long\def\noexpand\next@####1\expandafter \noexpand\csname end\exxx@\islandtype@\endcsname{\noexpand\Err@ {\noexpand\noexpand\expandafter\noexpand\islandtype@ must be used after some type of \noexpand\string\noexpand\...place}}} ** then} this *\edef* makes *\next@* mean ** \long\def\next@##1\endmap{\Err@{\noexpand\map must be used in some type of \string\...place}} ** Consequently, *\next@* executes this definition. So calling *\next@* yet once again produces the error message, swallowing the *\map...\endmap* text in the process. If *\island\c{...}* is used when *\ifplace@* is true, we will define *\islandclass@* to be `*...*'. Then, similarly to *\claim*, we want to use ** \csname island@C...\endcsname ** as the counter. So we will create this counter, using *\newcount@* (see section~\Sref{NEWNEW} and page~\ref{NWNW}), and set it to~*0* if it does not already exist. Before proceeding further however, we now need yet another *\futurelet* to check that this ** \island \c{...} ** is followed by yet another group *{...}* to specify a caption prefix; in fact, we will need *\FNSS@*, to skip over any space after the *\c{...}*: \C** \def\island@c\c#1{% \ifplace@ \def\next@{\def\islandclass@{#1}% "2 \expandafter\ifx\csname island@C#1\endcsname\relax "2 \expandafter\newcount@\csname island@C#1\endcsname \global\csname island@C#1\endcsname=0 \fi \FNSS@\island@c@}% "2 \else \def\next@{% \edef\next@{\def\noexpand\next@########1\expandafter \noexpand\csname end\expandafter\exxx@ \islandtype@\endcsname "2 {\noexpand\Err@{\noexpand\noexpand\expandafter\noexpand \islandtype@ must be used in some type of \noexpand\string\noexpand\...place}}}% \next@\next@}% \fi \next@} ** Then *\island@c@* works analogously to the way *\island@@* worked before: \C** \def\island@c@{% \ifcat\bgroup\noexpand\next \let\next@=\island@c@@ \else \def\next@{\Err@{\noexpand\island\string\c {\expandafter\string\islandclass@} must be followed by a {prefix} for \string\caption's}}% \fi \next@} "slip \def\island@c@@#1{\def\captionprefix@{#1}% \captioncount@=0 \global\setbox\islandbox@=\vbox\bgroup} ** \section{Starting a \CS{caption}} As we are setting *\box\islandbox@* we will probably encounter a *\caption*, which we have to consider next, before worrying about the *\endisland*. First of all, we \C** \rightadd@\caption\to\nofrillslist@ ** since the default style adds a period at the end of the *\caption* automatically, and we want to allow *\nopunct* to override this. \small This is a change from version~*1*: Even though captions are often whole sentences, where it might seem normal for the user to add the punctuation, it is preferable to require that this punctuation not appear in the input file, because it could create problems in the *.tic* file, where the caption might be followed by something like dot leaders. \endsmall Then we will need a box, \C** \newbox\captionbox@ ** to store *\caption*'s, and since we allow more than one *\caption* in an *\island*, we will need another box, \C** \newbox\Captionbox@ ** to store the accumulated *\caption*'s. A *\caption* will first be encountered within an *\island*, where we have already begun ** \global\setbox\islandbox@=\vbox\bgroup ** and it will normally first supply the matching *\egroup* that will finish *\box\islandbox@*, before scanning its argument. But we don't want to add an *\egroup* if our *\caption* is simply a subsequent *\caption* within the same *\island*. So we want something like ** \def\caption{% \ifnum\captioncount@=0 \let\next@=\egroup \else \let\next@=\relax \fi \next@ \advance\captioncount@ by 1 . . . ** But there is an extra detail to worry about: If the *\caption* was preceded by *\nopunct*, then we have *\nopunct@true* after the *\caption*, but this will then be hidden by the *\egroup*. So we actually use \C** \def\caption{% \ifnum\captioncount@=0 "2 \ifnopunct@ \def\next@{\egroup\nopunct@true}% "2 \else \let\next@=\egroup \fi "2 \else \let\next@=\relax \fi "2 \next@ \advance\captioncount@ by 1 \futurelet\next\caption@} ** Note that the *\captioncount@* is always set or advanced outside of the various boxes that we build, so we don't need to use *\global* with it. Note also that we didn't worry about *\nospace*. That's because this control sequence, although allowed before *\caption*, won't have any effect (in the default style, where nothing follows the final punctuation of a caption; if for some reason a style did have a space after this punctuation, a slightly more complicated routine would be needed). The *\futurelet* at the end of the definition is needed to see whether *\caption* is followed by a quoted number *""...""*, and *\caption@* simply calls *\caption@q* when a quoted number follows, and *\caption@@* in general: \C** \def\caption@{\ifx\next""\expandafter\caption@q\else \expandafter\caption@@\fi} ** Here *\caption@q* and *\caption@@* each first define *\Thelabel@*, \dots, *\Thelabel@@@@*, and then call ** \finishcaption@ ** where *\finishcaption@* will be defined in the next section. All this is quite analogous to the situation for *\claim*. Since *\caption*'s will be written to the *.tic* file, we also need some of the subsidiary information that was added for *\HL* and *\hl*: *\caption@q* will set *\ifquoted@* to be true and define *\Qlabel@@@@*, while *\caption@@* will set it to be false, so that we can use the *\QorThelabel@@@@* construction that was used for *\HL* and *\hl*: \C** \def\caption@q""#1""{\quoted@true {\noexpands@ "2 \let\pre=\island@@@P \let\post=\island@@@Q \let\style=\island@@@S \let\numstyle=\island@@@N "2 \Qlabel@{#1}\let\style=\relax\xdef\Qpref@{#1}}% \finishcaption@} "slip \def\caption@@{\quoted@false \global\advance\island@@@C by 1 "2 {\noexpands@ \xdef\Thelabel@@@{\number\island@@@C}% \xdefThelabel@\island@@@N "2 \xdef\Thelabel@@@@{\island@@@P\Thelabel@\island@@@Q}% \xdefThelabel@@\island@@@S \xdef\Thepref@{\Thelabel@@@@}}% "2 \finishcaption@} ** \section{Formatting a \CS{caption}\label{FMTC}} Analogous to *\claimformat@*, we want to have *\captionformat@*, to describe, in the very simplest terms, how a caption should be formatted ** \long\def\captionformat@#1#2#3{% #1 {\island@@@F#2} #3\punct@.} ** Here *#1* will be the ``caption prefix'', *#2* will be the properly formatted caption number, and *#3* will be the argument of *\caption*. We use *\long* because we allow *#3* to contain a blank line (the default style doesn't treat new paragraphs in a *\caption* in any particularly interesting way, but other styles might.) The `*\punct@.*' allows a *\nopunct* before the *\caption* to eliminate the period (for a caption that ends with a~! for example); a *\null* is not added here (compare pages~\ref{QW3} and~ \ref{QW4}) since nothing follows the period. A *\nospace* before a *\caption* will have no effect, in the default style, but is allowed. Unfortunately, the actual formatting of a caption may be quite a complex affair. In the default style, for example, if the caption can be set on a single line whose width is less than the width of *\box\islandbox@*, we center that line under *\box\islandbox@*; otherwise, we set the caption as a paragraph whose width is the same as that of *\box\islandbox@*. In addition, when a single *\island* has multiple *\caption*'s, we have the usual problem of stacking these paragraphs on top of each other, so we will want to insert struts. Many, many, other arrangements might be used; captions might be printed on the side of the figure, etc. It is impossible to anticipate all the things that different style files might do, but necessary modifications for other styles should be clear from a careful study of the proceedings used throughout this chapter. We begin by adding struts to the definition of *\captionformat@*: \C** \long\def\captionformat@#1#2#3{\rm\strut #1 {\island@@@F#2} #3\punct@.\strut} ** In other styles, *\rm* might be replaced by something like *\tenpoint*, so that the caption will be in 10~point type no matter what the current font may be; the *\strut* should follow, so that it will be the right size. Of course, some styles might even need different *\captionformat@*'s, for different sorts of *\island*'s. Then we want to have ** \widerthanisland@#1#2#3 ** to test whether *\captionformat@#1#2#3* produces an *\hbox* that is wider than *\box\islandbox@*, setting *\iftest@* to be true if it is wider, and setting it to be false otherwise. Basically, we want to ** \setbox0=\hbox{\captionformat@{#1}{#2}{#3}} ** and measure the width of *\box0*. This fails, however, if the caption contains a displayed formula, like ** $$\alpha=...$$ ** Indeed, in restricted horizontal mode *$$* simply stands for an empty formula ({\it The \tex book}, page~287), so the *\alpha* is outside of math mode, and will give a (mysterious) error message. (It also fails if the caption contains a *\linebreak*, although presumably this will only be put to avoid an *Overfull \hbox* when the caption is longer than the island.) So instead (compare page~\ref{BASICA}) we first ** \setbox0=\vbox{\hsize=\maxdimen \noindent@@\captionformat@{#1}{#2}{#3}% \par \setbox0=\lastbox} ** where the inner *\setbox0=\lastbox* simply removes \footnote{Recall that *\lastbox* removes the last box (something like *\global\setbox1=\lastbox* keeps the last box in *\box1* if we need it)\pagelabel{KLB}.} the last box in (the main)~ *\box0*. (See Chapter~\ref{EVPAR} for the *\noindent@@*.) If our caption argument *#3* consists of simple text, everything will have been set on one line, which will be *\lastbox*, so our final *\box0* will be empty, and therefore have width~*0pt* (compare the footnote on page~\ref{MAXD}). In this case, we will simply set the same material in *\box\captionbox@*, and set *\iftest@* to be true if the width of this box is greater than the width of *\box\islandbox@* and false otherwise. But if *#3* contains a displayed formula, or more than one paragraph of text, or anything else that creates more than one line of text (including perhaps, so much text that it won't even fit on a line of length *\maxdimen*), then there is still a line left after we eliminate *\lastbox*, so the final *\box0* does not have width~*0pt*. In this case, we also want *\iftest@* to be true, since we again want to reformat *#3* within a *\vbox* having the width of *\box\islandbox@*. After we have set a box for testing purposes, we must be careful to *\unlabel@* (section~\Sref{relax2}), to ignore any *\label*'s for future setting of~*#3*, for otherwise we will be using a \ more than once when we do the final setting. We will also use *\noset* (section~\Sref{relax}), just in case *\Reset* or *\Offset* appear. We define our test by: \C** \long\def\widerthanisland@#1#2#3{% \test@true \setbox0=\vbox{\hsize=\maxdimen "2 \noindent@@\captionformat@{#1}{#2}{#3}% \par\setbox0=\lastbox}% "2 \ifdim\wd0=0pt \setbox\captionbox@=\hbox{\noset@\unlabel@ \captionformat@{#1}{#2}{#3}}% \ifdim\wd\captionbox@ > \wd\islandbox@\else\test@false\fi \fi} ** Now we can define *\captionformat@@*, which does the actual formatting by: \C** \long\def\captionformat@@#1#2#3{% \widerthanisland@{#1}{#2}{#3}% "2 \iftest@ \global\setbox\captionbox@=\vbox{\hsize=\wd\islandbox@ \vskip-\parskip \noindent@@\noset@\unlabel@ \captionformat@{#1}{#2}{#3}\par}% "2 \else \global\setbox\captionbox@= \hbox to\wd\islandbox@{\hfil\box\captionbox@\hfil}% \fi} ** The *\vskip-\parskip* is used to eliminate any extra *\parskip* glue before our *\noindent@@*'ed paragraph. Finally, *\finishcaption@#1*, where *#1* will be the argument of *\caption*, first stores *#1* in *\entry@*, ** \def\entry@{#1} ** for writing to the *.tic* file later (section~\Sref{TICWRITE}), and then {\litindent=0pt takes care of details like ignoring initial and final spaces, ** {\locallabel@ \captionformat@@{\expandafter\ignorespaces\captionprefix@\unskip}% {\ifx\thelabel@@\empty\unskip\else\thelabel@@\fi}% {\ignorespaces#1\unskip}} ** If} there is only one *\caption* so far, our final *\box\Captionbox@* will just be a *\vbox* containing *\box\captionbox@*: ** \global\setbox\Captionbox@=\vbox{\box\captionbox@}% ** (We do this so that *\box\Captionbox@* is always a *\vbox*; that way, we can always *\unvbox\Captionbox@* when necessary.) Otherwise, we want to create a new *\box\Captionbox@* by combining the old *\box\Captionbox@* with *\box\captionbox@* with a *\smallskip* between them: {\litindent=0pt ** \global\setbox\Captionbox@=\vbox{\unvbox\Captionbox@ \smallskip\box\captionbox@}% ** In addition to the *\smallskip*, *\strut*'s will give the proper line spacing.} Note that the *\unvbox*'ed *\Captionbox@* and *\box\captionbox@* will not have any additional space added between them. \footnote{{\it The \tex book}, page~282: ``The vertical list inside that box is appended to the current vertical list, without changing it in any way.'' {\it The \tex book\/} goes on to say that ``The value of *\prevdepth* is not affected.'' That is, the value of *\prevdepth* after the *\unvbox* is the value it had before the *\unvbox*, {\it not\/} the depth of the last box produced by the *\unvbox*. This will be important later \pagelabel{NEXG} (page~\ref{USEFN}).} At this point we also want to add appropriate *\write\tic@*'s for writing to the *.tic* file (page~\ref{TICFILE}). Although it would seem reasonable to include these *\write*'s within *\box\Captionbox@*, \footnote{Note that, unlike *\insert*'s, which have an effect only if they ``migrate'' out to the main vertical list, *\write*'s will take place when enclosed in boxes, no matter to what depth.} we will instead attach them to *\box\islandbox@*, which will always appear on the same page as the corresponding captions. (At some preliminary stage of the macros, *\box\Captionbox@* was subjected to further processing, which would have been complicated by the presence of *\write*'s. That is no longer the case, so that the *\write*'s could be included in *\box\Captionbox@*, but there's nothing wrong with the other approach, and I didn't want to rewrite this part of the routines, with the attendant risk of introducing some new bugs.) We will define *\ticwrite@*, the appropriate collection of *\write\tic@*'s, in section~\Sref{TICWRITE}, {\litindent=0pt and we will use ** \ifnum\captioncount@=1 \global\setbox\islandbox@=\vbox{\ticwrite@\vbox{\box\islandbox@}}% \global\setbox\Captionbox@=\vbox{\box\captionbox@} \else \global\setbox\islandbox@=\vbox{\unvbox\islandbox@ \setbox0=\lastbox \ticwrite@\box0 } \global\setbox\Captionbox@=\vbox{\unvbox\Captionbox@ \smallskip\box\captionbox@} \fi ** Note} that when *\captioncount@* is~*1*, our new *\box\islandbox@* is a *\vbox* containing *\ticwrite@* at top, and then the old *\box\islandbox@* enclosed in another layer of *\vbox*'ing. The reason for this is that *\box\islandbox@* might contain more than one thing (in particular, it might contain the *\write* for a *\pagelabel*), and we don't want to uncover any of these layers when we *\unvbox\islandbox@* in the next case, where *\captioncount@* is~*>1*. In that latter case, the ** \unvbox\islandbox@ \setbox0=\lastbox@ ** temporarily removes the old *\box\islandbox@* from the list, inserts the *\ticwrite@* (after any *\write*'s that were inserted from previous steps),\pagelabel{RECALL} and then puts back the old *\box\islandbox@*. This procedure keeps the *\write*'s for multiple *\caption*'s in the correct order, and also keeps them at the top of the *\vbox*, so that if necessary we can always use *\lastbox* to examine the non-*\write* part. And after all that, we simply reset *\ifnopunct@* and *\ifnospace@* to be false (we need to do this for *\ifnospace@*, even though *\nospace* has no effect on a *\caption*, because *\nospace* before a *\caption* will not give an error message, and will not reset *\ifnospace@* to be false): \C** \long\def\finishcaption@#1{\def\entry@{#1}% {\locallabel@ \captionformat@@ {\expandafter\ignorespaces\captionprefix@\unskip}% {\ifx\thelabel@@\empty\unskip\else\thelabel@@\fi}% {\ignorespaces#1\unskip}}% "2 \ifnum\captioncount@=1 \global\setbox\islandbox@ =\vbox{\ticwrite@\vbox{\box\islandbox@}}% \global\setbox\Captionbox@=\vbox{\box\captionbox@}% "2 \else \global\setbox\islandbox@ =\vbox{\unvbox\islandbox@ \setbox0=\lastbox \ticwrite@\box0}% "2 \global\setbox\Captionbox@ "2 =\vbox{\unvbox\Captionbox@ "2 \smallskip "2 \box\captionbox@}% \fi \nopunct@false\nospace@false} ** \section{\CS{ticwrite\@}\label{TICWRITE}} Analogous to *\Sixtoc@* (section~\Sref{WLTL}), we define the routine *\Sixtic@* by \C** \def\Sixtic@{\ifx\macdef@\empty\else \def\next@##1##2\next@{\def\macdef@{##1##2}}% \expandafter\next@\macdef@\next \edef\next@ {\noexpand\six@\tic@\macdef@ \space\space\space\space\space\space \space\space\space\space\space\space\noexpand\six@}% \next@\let\macdef@=\relax\fi} ** When *\tocfile* has been specified, and the input file has something like \setbox0\hbox{\} \setbox2\hbox{\} \setbox3\hbox{\} ** \island \c{"copy0}{"copy2} . . . \caption{"box3} \endisland ** we will want to write \setbox3\hbox{\} \setbox4\hbox{\} ** \island \c{"copy0}{"copy2}{"copy4} "box3 \Page ... ** to the *.tic* file (with line breaks in \ after every sixth space). If the file has something like \setbox3\hbox{\} ** \Figure . . . \caption{"copy3} \endFigure ** using *\Figure* or *\Table*, or anything else created by *\newisland*, then we want to write something like ** \Figure {"copy4} "copy3 \Page ... ** (Multiple *\caption*'s for a single *\island* will simply give rise to multiple copies of such data, with the corresponding \'s.) We also want to have *\nopunct* appear before the *\island* or *\Figure* or whatever, when it appears before the corresponding *\caption*. Finally, as with *\HL* and *\hl*, quoted numbers should appear with quotes also (and *\style* should be allowed to appear within the quotes).\pagelabel{SUB111} For the first line, we use the following code, resorting to the standard\linebreak *\expandafter\noexpand* trick (compare pages~\ref{EXNE} and~\ref{EXNE2}): ** \def\next@{\island} \edef\next@{\write\tic@{\ifnopunct@ \noexpand\noexpand\noexpand\nopunct\fi \ifx\islandtype@\next@\noexpand\noexpand\noexpand\island \noexpand\string\noexpand\c{\islandclass@} {\captionprefix@}{\QorThelabel@@@@}}} \else \noexpand\noexpand\expandafter\noexpand \islandtype@{\QorThelabel@@@@}}\fi} \next@ ** We use *\string\c* rather than *\noexpand\c*, because a space after *\c* usually looks extraneous in the *.tic* file. As with *\HLtoc@* and *\hltoc@*, this should all be done within a group with *\noexpands@* and *\let\style=\relax*. Next we use ** \expandafter\unmacro@\meaning\entry@\unmacro@ \Sixtic@ ** to produce *\write*'s for the *\caption*, and finally we add the *\write* for the page number: \C** \def\ticwrite@{% \iftoc@ {\noexpands@\let\style=\relax "2 \def\next@{\island}% \edef\next@{\write\tic@{% \ifnopunct@\noexpand\noexpand\noexpand\nopunct\fi "2 \ifx\islandtype@\next@ \noexpand\noexpand\noexpand\island \noexpand\string\noexpand\c{\islandclass@}% {\captionprefix@}{\QorThelabel@@@@}% "2 \else \noexpand\noexpand\expandafter\noexpand \islandtype@{\QorThelabel@@@@}}\fi}% \next@}% "2 \expandafter\unmacro@\meaning\entry@\unmacro@ \Sixtic@ \write\tic@{\noexpand\Page{\number\pageno}{\page@N}% {\page@P}{\page@Q}^^J}% \fi} ** For a style where *\captionformat@* involved *\addspace@*, so that\linebreak *\nospace* before a *\caption* would have an effect, we would probably want to add ** \ifnospace@\noexpand\noexpand\noexpand\nospace\fi ** in the *\edef*. \section{\CS{Htrim\@}} A special procedure is needed to deal with an *\Hbyw*. This has been temporarily set to a box of height *\vsize*, so that *\box\islandbox@* will also have this height. The procedure ** \Htrim@#1 ** is used with *#1* being the amount of space that we want to leave between the tall box and the caption, and is used only when *\ifHby@* is true: ** \def\Htrim@#1{% \ifHby@ . . . \fi} ** It is supposed to change *\box\islandbox@* to a box of height \Math \hbox{*\vsize*}-\hbox{*#1*}-\hbox{height of the caption box}, \endMath so that this new box, together with the caption box and *#1* space between them, will take up the entire page. In addition, if *\ifFigureproofing* is true, then we need to put crop marks around this new *\box\islandbox@*. So we start out by setting ** \dimen@=\vsize ** If there was a caption we ** \advance\dimen@ by -\ht\Captionbox@ \advance\dimen@ by -#1 ** Now we basically want to ** \setbox\islandbox@=\vbox {\hbyw\dimen@{\wd\islandbox@}} ** In order to allow crop marks around this box, however, we need to have *\ifHby@* be false (page~\ref{HBY}). But we'll need to know the value of *\ifHby@* later on (page~\ref{IFHBY}~ff\.). So we will set *\global\Hby@false* before we do this, and restore *\global\Hby@true* afterwards (remember that this whole routine is being done only when *\ifHby@* is true; we use *\global* assignments here since they were necessary previously). Before we actually ** \global\setbox\islandbox@=\vbox "8GLIS"8 {\hbyw\dimen@{\wd\islandbox@}}} ** we must first extract any *\ticwrite*'s that may have appeared in the old\linebreak *\box\islandbox@*, and make sure that they appear in the new one. To do this, we can first store *\wd\islandbox@*, ** \dimen@ii=\wd\islandbox@ ** and then ** \global\setbox\islandbox@=\vbox {\unvbox\islandbox@ \setbox0=\lastbox \hbyw\dimen@\dimen@ii} ** Recall (page~\ref{RECALL}) that the ** \unvbox\islandbox@\setbox0=\lastbox ** leaves the *\write*'s at the top of *\box\islandbox@*, while deleting the other part. Unfortunately, that's not quite adequate, because we will then loose any *\pagelabel* information that may have been in *\box\islandbox@*. So instead we use ** \global\setbox\islandbox@=\vbox {\unvbox\islandbox@ \setbox0=\lastbox \vbox to0pt{\vss\box0}\nointerlineskip \hbyw\dimen@\dimen@ii} ** Although no extra space is inserted before the *\vbox to0pt* (see the footnote on page~\ref{NEXG}), we need *\nointerlineskip* to prevent extra space between that box and the next *\hbyw*. The complete code is: \C** \def\Htrim@#1{% \ifHby@ "2 \dimen@=\vsize \ifnum\captioncount@=0 "2 \else \advance\dimen@ by -\ht\Captionbox@ \advance\dimen@ by -#1% \fi "2 \global\Hby@false \dimen@ii=\wd\islandbox@ "2 \global\setbox\islandbox@=\vbox {\unvbox\islandbox@ \setbox0=\lastbox \vbox to0pt{\vss\box0}\nointerlineskip \hbyw\dimen@\dimen@ii}% \global\Hby@true \fi} ** \section{Other accoutrements for \CS{endisland}} The *\islandpairdata* and\linebreak *\islandtripledata* constructions (section~\Sref{MNPI}) essentially operate by temporarily creating *\islands* and storing *\box\islandbox@* in other places. During such processes we want to abort most of the additional operations of *\endisland*, so we need a flag \C** \newif\ifdata@ ** to tell us when we are performing such temporary maneuvers. We also need a way of testing for the class of an *\island*, since this will determine how the caption is attached to the *\island*; in the default style, for example, an *\island* with class~ *T* (i.e., a *\Table*), has the caption above, instead of below. The definition of *\iclasstest@* is analogous to *\classtest@* for *\claim* (section~\Sref{MODC}): \C** \def\iclasstest@#1{\def\next@{#1}\ifx\next@\islandclass@ \test@true\else\test@false\fi} ** We don't bother with a corresponding *\itypetest@*, because the class of an *\island* should be sufficient information: Unlike the situation for *\claim*, where, for example, *\claim\c{T}{Theorem}* and *\claim\c{T}{Lemma}* might be used so that Theorem's and Lemma's are numbered together, it's unlikely that any one would want Tables and Figures, or Maps and Plates to share a common numbering. Of course, a style file that did want to allow such things could introduce a suitable *\itypetest@*. \section{\CS{endisland}} Finally, we are ready for *\endisland*. If there was no *\caption*, then it must provide the *\egroup* that matches the *\bgroup* in *\island*, ** \ifnum\captioncount@=0 \expandafter\egroup\fi ** Next come the instructions for putting the island and caption together, which will {\it not\/} be done when we are simply collecting data for constructions like *\islandpairdata*, etc., ** \ifdata@ \else . . . . . . \fi ** In the case of a table, the default style leaves a *\bigskip* between the caption and the island, so we want to start with something like ** \iclasstest@{T} \iftest@ \Htrim@\bigskipamount ** Remember that *\Htrim@* operates only when we have an *\Hbyw*. In this case, it makes *\box\islandbox@* just high enough so that its height plus the height of *\box\Captionbox@* plus *\bigskipamount* is *\vsize*. Actually, instead of *\bigskip*, we really want less space, because the last line of the caption above the table will have a depth of *3.5pt* (the depth of a strut). So we really want something like ** \skip@ = -3.5pt \advance\skip@ by \bigskipamount \Htrim@\skip@ ** That way, we know exactly how much space we are getting: enough to make the baseline of the first line of the caption *12pt* plus *\bigskipamount* below the *\island*. We will actually use something like ** "hskip-30pt{\rm\global\skip1 = -\dp\strutbox}\global\advance\skip1 by "hskip-30pt \bigskipamount "hskip-30pt\Htrim@{\skip1} ** to remind other style files to insert something like *\tenpoint* for the *\rm*, so that *\skip1* will have the proper value. We need *\skip1* because of the *\global* assignment. Since it turns out that *\skip1* will be used quite frequently, we will ** \skipdef\skipi@=1 ** so that we can use *\skipi@* to abbreviate *\skip1*. Then we want ** \global\setbox\islandbox@=\vbox {\ifnum\captioncount@=0 \else \box\Captionbox@ \nointerlineskip \vskip\skipi@ \fi \box\islandbox@} ** In addition to the *\vskip\skipi@* that we provide, there would normally also be *\lineskip* glue between the caption and *\box\islandbox@*; we use *\nointerlineskip* to suppress this, so that there is just one glue between the two boxes. \pagelabel{ONEGLUE} The new *\box\islandbox@* will eventually be taken apart by the *\output* routine (knowing that there is just one glue between the two boxes makes this easier),\pagelabel{APART} and the pieces will be put back on the page in the proper way. In the default style, *\box\Captionbox@* and (the original) *\box\islandbox@* will each be centered; at that time, the *\vskip\skipi@* glue between them will be able to stretch or shrink with the other glue on the page. {\litindent=15pt In the default style, all *\island*'s other than *\Table*'s have their captions below, with a *\medskip* between them. In this case we actually want to {\it increase\/} the *\medskip* by ~*3.5pt*, since the first line of the caption has a strut of height~*8.5pt*: ** \else {\rm\global\skipi@ = \dp\strutbox} \global\advance\skipi@ by \medskipamount "2 \Htrim@\skipi@ "2 \global\setbox\islandbox@=\vbox {\box\islandbox@ \ifnum\captioncount@=0 "2 \else \nointerlineskip \vskip\skipi@ \box\Captionbox@ \fi} \fi ** }% The only thing left to do is to give an error message in case the final *\box\islandbox@* ends up larger than *\vsize*, and reset the height of *\box\islandbox@* to *\vsize*. The message should be something like \setbox0\hbox{etc.,} ** \island is larger than page \Figure is larger than page "box0 ** when there are no captions, and \setbox0\hbox{etc.,} ** \island with \caption is larger than page \Figure with \caption is larger than page "box0 ** otherwise. After all this (occurring within an *\ifdata@\else...\fi* region), we can reset *\Hby@false* and set *\island@true*, to give proper information to *\Aplace*, etc. The complete code is: \C** \skipdef\skipi@=1 "slip \def\endisland{% "8EIROUTINE"8 \ifnum\captioncount@=0 \expandafter\egroup\fi "2 \ifdata@ \else "2 \iclasstest@{T}% \iftest@ {\rm \global\skipi@ = -\dp\strutbox}% \global\advance\skipi@\bigskipamount "2 \Htrim@\skip@ \global\setbox\islandbox@=\vbox {\ifnum\captioncount@=0 \else \box\Captionbox@ \nointerlineskip \vskip\skipi@ \fi \box\islandbox@}% "2 \else {\rm \global\skipi@ = \dp\strutbox}% \global\advance\skipi@ by \medskipamount \Htrim@\skipi@ "2 \global\setbox\islandbox@=\vbox {\box\islandbox@ \ifnum\captioncount@=0 \else \nointerlineskip \vskip\skipi@ \box\Captionbox@ \fi}% "2 \fi "2 \ifHby@ "8IFHBY"8 "2 \else \dimen@=\ht\islandbox@ \advance\dimen@ by \dp\islandbox@ "2 \ifdim\dimen@ > \vsize \def\next@{\island}% \Err@{% \ifx\Islandtype@\next@\noexpand\island\else \expandafter\noexpand\islandtype@\fi "2 \ifnum\captioncount@=0 \else with \noexpand\caption\fi is larger than page}% \ht\islandbox@=\vsize "2 \fi \fi \fi "2 \global\Hby@false \island@true} ** \section{\CS{newisland}}The *\newisland* construction is similar to *\newclaim*, but, as with *\NameHL* and *\Namehl*, we write the information to the *.tic* file (and, as with *\newclaim*, we first make sure that *#1* hasn't already been defined). As before, we use *\newcount@* (section~\Sref{NEWNEW} and page~\ref{NWNW}): \C** \def\newisland#1\c#2#3{\define#1{}% \iftoc@\immediate\write\tic@{\noexpand\newisland \noexpand#1\string\c{#2}{#3}^^J}\fi "2 \expandafter\def\csname\exstring@#1@S\endcsname{\island@S}% "2 \expandafter\def\csname\exstring@#1@N\endcsname{\island@N}% "2 \expandafter\def\csname\exstring@#1@P\endcsname{\island@P}% "2 \expandafter\def\csname\exstring@#1@Q\endcsname{\island@Q}% "2 \expandafter\def\csname\exstring@#1@F\endcsname{\island@F}% "2 \expandafter\def\csname end\exstring@#1\endcsname {\endisland}% "2 \expandafter \ifx\csname island@C#2\endcsname\relax \expandafter\newcount@\csname island@C#2\endcsname \global\csname island@C#2\endcsname=0 \fi "2 \edef\next@{\noexpand\expandafter\noexpand\let\noexpand \csname\exstring@#1@C\noexpand\endcsname= \csname island@C#2\endcsname}% \next@ \def#1{\def\islandtype@{#1}\island@c\c{#2}{#3}}} ** \lamstex\ defines *\Figure* and *\Table* directly in terms of *\newisland*: \C** \newisland\Figure\c{F}{Figure} \newisland\Table\c{T}{Table} ** \section{Manipulating \CS{island}'s\label{MNPI}} The constructions *\islandpairdata* and\linebreak *\islandtripledata*, the basic constructions behind *\Figurepair*, etc., store the *\island*'s and *\caption*'s from their arguments in appropriate boxes, which we first declare: \C** \newbox\islandboxi \newbox\islandboxii \newbox\islandboxiii \newbox\captionboxi \newbox\captionboxii \newbox\captionboxiii ** (These boxes, as well as *\islandpairdata*, etc., have no *@*'s in them, so that they will be accessible to the user.) *\islandpairdata{...}{...}* is meant to be used when each *...* is some sort of *\island* like ** \Figure _ _ _ \endFigure ** Basically, we just want to execute the first ** \Figure _ _ _ \endFigure ** without any *\...place*, and store *\box\islandbox@* in *\box\islandboxi* and *\box\Captionbox@* in *\box\captionboxi*, and similarly for the second ** \Figure _ _ _ \endFigure ** Since *\island*'s normally work only when *\ifplace@* is true, we will simply temporarily declare this within a group. We will also set *\ifdata@* to be true to disable most of the *\endisland* routine. \C** \long\def\islandpairdata#1#2{% {\place@true \data@true #1% \global\setbox\islandboxi=\box\islandbox@ \global\setbox\captionboxi=\box\Captionbox@ #2% \global\setbox\islandboxii=\box\islandbox@ \global\setbox\captionboxii=\box\Captionbox@ }} ** The definition of *\islandpairbox* has been changed from version~*1* (which appears on page~73 of the \lamstex~Manual). First of all, we have to allow for pairs of *\island*'s neither of which has a *\caption*. Moreover, the *\medskip* should be modified as in *\endisland*. The biggest change, however, is that instead of simply creating a *\vbox*, we instead *\setbox\islandbox@* to this *\vbox*: \C** \long\def\islandpairbox#1#2{% \islandpairdata{#1}{#2}% "2 \dimen@=\ht\captionboxi \ifdim\ht\captionboxii > \dimen@ \dimen@=\ht\captionboxii\fi "2 "9\ifdim\dimen@ > 0pt"9 \ifdim\ht\captionboxi < \dimen@ \global\setbox\captionboxi =\vbox to\dimen@{\unvbox\captionboxi\vfil}\fi "2 \ifdim\ht\captionboxii < \dimen@ \global\setbox\captionboxii =\vbox to\dimen@{\unvbox\captionboxii\vfil}\fi "9\fi"9 "2 "9\global\setbox\islandbox@="9\vbox{\hbox to\hsize {\hfil\box\islandboxi\hfil\box\islandboxii\hfil}% "2 "9\ifdim\dimen@ > 0pt"9 \nointerlineskip "9{\rm \global\skipi@=\dp\strutbox}"9 "9\global\advance\skipi@ by \medskipamount"9 "9\vskip\skipi@"9 \hbox to\hsize {\hfil\box\captionboxi\hfil\box\captionboxii\hfil} "9\fi"9}% } ** Then *\Figurepair* is defined by \C** \def\Figurepair#1\and#2\endFigurepair{\island@true \islandpairbox{\Figure#1\endFigure}{\Figure#2\endFigure}} ** Thus, *\Figurepair* produces the same effect as if we had made an *\island* where the \ is the combination of the \'s for the two islands, and where the caption is the combination of the captions for the two islands. In version~*1* the definition was somewhat different: *\Figurepair* treated the entire combination as a single new island without a caption. The advantage of the new approach is that when our *\box\islandbox@* is taken apart by the *\output* routine (page~\ref{APART}), there will be *\vskip\skipi@* glue between the combined \'s and the combined captions, which will again be able to stretch or shrink with the other glue on the page. In particular, if a *\Figure* and a *\Figurepair* appear on the same page, the space before the caption will have stretched or shrunk by the same amount for each. The definitions of *\islandparboxa*, to put the captions above the combined spaces, and *\Tablepair*, which uses this, won't be given, since they are quite analogous. Similarly, *\islandtripledata*, etc., follow quite similarly. \chapter An overview\\ Placing the packaged figures, tables, \dots \endchapter \runningchapter{An overview of the placement routines} We come now to what is perhaps the most interesting part of \lamstex's general document processing features---its handling of inserted Figures and other *\island*'s. Chapter 8 of the \lamstex\ Manual describes the default style's fairly rigorous set of rules for figure placement. Of course, innumerable different rules might be specified by a style designer, so we certainly cannot claim to have anticipated all problems. In fact, as the Manual concedes (pages~68 and~70), although the current solution will probably be quite adequate in practice, it isn't ideal. The aim of \lamstex's automatic placement mechanism is not to provide perfection, but to get as much mileage as possible out of \tex's own *\insert* mechanism. Although the details of the \lamstex\ constructions take up the next few chapters, they are fairly simple in concept; a \tex pert intending to change the *\output* routine should not rush in, but at least this is not a region where \tex~\hbox{angels} fear to tread (a thorough understanding of pages~122@--125 of {\it The \tex book\/} is in order, however). \section{\CS{place}} Before we get to the interesting part, we will consider the ``low level'' construction *\place*, used in a construction like ** \place{\island ... \endisland} ** to simply insert *\box\islandbox@* at the current point in the file. The *\place* construction begins by declaring *\place@true*, so that the *\island* construction won't give an error message, and *\island@false*, so *\ifisland@* will be true only if the argument of *\place@* actually involves an *\island* of some sort: ** \def\place#1{\place@true \island@false #1 . . . ** The argument *#1*, presumably *\island"allowbreak..."allowbreak\endisland*, will simply create *\box\islandbox@* and set *\island@true*. So if *\ifisland@* is still false, we will give an error message: ** \Err@{Whoa ... there's no \string\Figure, \string\Table, etc., here} ** Otherwise, we will just use ** \box\islandbox@ ** to typeset the box at that point. Then we will reset *\ifplace@* to be false: \C** \def\place#1{\place@true\island@false #1% \ifisland@ \box\islandbox@ \else \Err@{Whoa ... there's no \string\Figure, \string\Table, etc., here}% \fi \place@false} ** \section{Automatic placement} Having gotten that out of the way, we are ready, willing, and able to tackle the automatic placement routines. Here is the general principal. Whenever something is *\...place*'d it will simply be added to a certain insertion class; for this purpose we might as well pre\"empt *plain* \tex's *\topins* insertion class, since we have already disallowed its use for its original purposes. Remember that for every insertion class, like *\topins*, there is \bullist \item An associated box, *\box\topins*, which is where the material from that insertion class appears when a page has been completed and the *\output* routine is called. At that time, the height of *\box255* may well be different than *\vsize*, since the *\output* routine is going to combine *\box255* with the material in *\box\topins* (and possibly other material), to make the box that \tex\ will *\shipout*. In *plain* \tex@, material from the *\footins* and *\topins* insertion classes are handled rather simply: the *\pagecontents* macro simply *\unvbox*'es each of these boxes, keeping everything in *\box\topins* at the top of the page, and everything in *\box\footins* at the bottom. But there is no need for such a restricted approach: it's quite possible for the *\output* routine to extract various pieces from *\box\topins*, putting some pieces at the top and other pieces at the bottom. Thus, some figures in the *\topins* insertion class can just as well be placed at the bottom of the page (hopefully, before the footnotes) as at the top of the page. \item An associated counter *\count\topins*, which tells \tex\ what proportion of the height plus depth of *\box\topins* to subtract from *\vsize* in computing the proper height for *\box255*. In *plain* \tex@, *\count\topins=1000*, indicating that *\box255* should leave room for the full height plus depth of *\box\topins*; this will remain the same in \lamstex. \item An associated dimension register, *\dimen\topins*, which indicates the maximum size of *\box\topins* that will be allowed per page. \item An associated glue register, *\skip\topins*, which indicates extra space to leave blank in *\box255* if there happens to be anything in *\box\topins*. \endbullist As we will soon see, *\dimen\topins* and *\skip\topins* are the keys to making *\topins* work for us. \medskip Notice that \tex's insertion mechanism already does most of the work we want: boxes are added and withdrawn from any insertion class in a ``first in first out'' manner; and \tex\ does all the calculations to make sure that *\box255* is made sufficiently short to accommodate anything in the insertion class. Of course, we usually don't want \tex\ to allow more than two things in *\topins* to appear per page. But this can be controlled by *\dimen\topins* (which can be set to any desired value at any time); if *\dimen\topins* is the sum of the height plus depths of the first two boxes in *\topins*, then \tex\ will allow only these two boxes to be added to *\box\topins*.\pagelabel{INCDIMT} There is also the question of the glue to be left below a figure at the top of a page, which we will call *\belowtopfigskip*, or above a figure at the bottom of the page, which we will call *\abovebotfigskip*. For the moment, let's pretend that these two values are the same. Then before *\box\islandbox@* is *\insert*'ed into *\topins*, we will simply declare the height of *\box\islandbox@* to be greater than it already is by the \ part of *\abovebotfigskip*, and we will similarly increase *\dimen\topins* enough to leave space for the \ part of this glue. Thus, when a box in *\box\topins* is returned to its proper height, there will be room to add *\abovebotfigskip* glue. The only question is, what should we do when the two amounts of glue that we want to leave are different, as, for example, in the default style, which has ** \belowtopfigskip = 15pt plus 5pt minus 5pt \abovebotfigskip = 18pt plus 6pt minus 6pt ** Fortunately, *\skip\topins* allows us to deal with this: \list\item We will be increasing *\dimen\topins* by an additional *18pt* for each *\Aplace*. \item But we will also set \setbox0=\hbox{\ part of } ** \skip\topins = "box0\abovebotfigskip - \belowtopfigskip = -3pt "8EXTRAS"8 ** \endlist Then: \bullist \item If no members of the insertion class appear on the page, all these quantities are irrelevant. \item If just one member of the insertion class appears (at top), then \tex\ leaves *-3pt* extra space for it; i.e., *\box255* will be *3pt* {\it longer\/} than it should be to allow the insertion plus *18pt* space to fit on a page. Consequently, when we add only *15pt* instead of the *18pt*, everything will work out just right. \item If one member of the insertion class appears at the top and one (or more) at the bottom, everything still works, because \tex\ only leaves the *-3pt* extra space once. This properly adjusts the top member of the insertion class, while the bottom member, with *18pt* glue above it, also works out right. \endbullist The analysis seems equally valid (and easier) if *\belowtopfigskip* is greater than *\abovebotfigskip*; however, as we'll see at the end of section ~\Sref{FLOAT} and in section~ ~\Sref{255TOO}, it really introduces quite different problems. \section{Setting things up\label{STU}} First we declare the glues below figures at the top of the page and above figures at the bottom of the page, and give them their default values; at the same time, we introduce a \ register for the minimum amount of text to be allowed on a page that is otherwise filled with figures: \C** \newskip\belowtopfigskip \belowtopfigskip=15pt plus 5pt minus5pt \newskip\abovebotfigskip \abovebotfigskip=18pt plus 6pt minus6pt \newdimen\minpagesize \minpagesize=5pc ** To get *\skip\topins* to be the \ part of $\hbox{*\belowtopfigskip*} -\hbox{*\abovebotfigskip*}$, we use the following code (compare page~\ref{ELIM}): \C** \dimen@=\belowtopfigskip \advance\dimen@ by -\abovebotfigskip \skip\topins=\dimen@ "8EXTRAS22"8 ** (If a style file changes *\belowtopfigskip* and *\abovebotfigskip*, these lines should be repeated, to reset *\skip\topins* correctly.) Initially, *\dimen\topins* will be *0pt*; it will be increased only as we *\Aplace* things. \C** \dimen\topins=0pt ** In order to set *\dimen\topins* correctly, we are going to have to keep track of various things. In particular, we will use a counter \C** \newcount\topinscount@ ** to keep track of how many boxes still remain in the *\topins* insert. \section{How \CS{Aplace} works} An extra layer of complications envelops \lamstex's automatic placing mechanism because of the existence of so many different varieties of *\...place*'s. In order to get a better idea of the main features, in this section we will consider a simpler style in which there is only *\Aplace*, and we will also assume that *\Aplace* is being used only in vertical mode. Code surrounded by dashed lines rather than solid lines will be changed when we get to the final definitions later on. \Pitem Each ** \Aplace{\island ... \endisland} ** will essentially ** \insert\topins\box\islandbox@ ** where *\box\islandbox@* is the completed box---containing both the *\island* and its caption(s), which the *\island"allowbreak..."allowbreak\endisland* creates---except that we will first increase the height of *\box\islandbox@* by the \ part of *\abovebotfigskip*, so that \tex\ will be trying to leaving room for this box plus this amount of space. \tex\ doesn't allow us to say ** \advance\ht\islandbox@ by \abovebotfigskip ** so we have to take a roundabout route and say \Litbox0=** \dimen@=\ht\islandbox@ \advance\dimen@ by \abovebotfigskip \ht\islandbox@=\dimen@ ** $$\pagelabel{EXTRAS2}\vcenter{\box0} \tag"\style{\bf A}"$$ \pitem Then we will ** \advance\dimen@ by \dp\islandbox@ ** so that *\dimen@* is the height plus depth of *\box\islandbox@*, plus the \ part of *\abovebotfigskip*. The value of *\dimen@* is information that we are going to store, whether or not we plan to allow the *\Aplace*'d *\island* to appear in *\box\topins*. Instead of keeping a list of these dimensions, it is simpler to store empty *\hbox*'s of the corresponding width inside a *\vbox*, which we call *\topinsdims@*; the routine for doing this will be called *\storedim@*: \C** \newbox\topinsdims@ "slip \def\storedim@{\global\setbox\topinsdims@ =\vbox{\hbox to\dimen@{}% \unvbox\topinsdims@}} ** Notice that we put the new information on the {\it top\/} of the *\vbox*. \pagelabel{STOREDIM} That is because it is easiest to get information by taking things off of the {\it bottom\/} of the *\vbox*, with *\lastbox* (see the footnote on page~\ref{KLB}). Note also that no glue will be added between the *\hbox*'s (see the footnote on page~\ref{NEXG}). \pitem Each time we encounter an *\Aplace*, we must decide whether or not the corresponding \setbox0\hbox{,} ** \box\islandbox@ "box0 ** which we have placed in the *\topins* insertion class, should be allowed to appear in *\box\topins* at the next *\output* routine. The default style uses the rule that there should be at most two *\island*'s per page. Recall that *\topinscount@* is supposed to be the number of insertions currently in the *\topins* insertion class. So if *\topinscount@* is greater than or equal to~*2*, there are already at least two insertions waiting to go on the current page. In this case, we don't want to allow the current insertion to appear in *\box\topins*. But if *\topinscount@* is less than~*2*, so that there is at most one insertion waiting to go on the current page, then we do want to allow the current insertion to appear in *\box\topins* (of course, it will then be up to \tex\ to decide whether there is actually room on the page for this insertion). We initially have ** \dimen\topins=0pt ** This means that initially no inserts will be placed in *\box\topins* during the *\output* routine. If we decide to allow the current insertion to appear in *\box\topins*, we need to communicate that decision to \tex@, as indicated on page~\ref{INCDIMT}, by increasing *\dimen\topins* by *\dimen@*, \setbox0\hbox{\} ** \ifnum\topinscount@ > 1 \else "box0 \fi ** Again, we will need a roundabout route for increasing *\dimen\topins*, ** \advance\dimen@ by\dimen\topins \global\dimen\topins=\dimen@ ** We define a routine for doing this, *\advancedimtopins*, which contains one more element, \C** \def\advancedimtopins@{% \ifnum\pageno=1 \else \advance\dimen@ by \dimen\topins \global\dimen\topins=\dimen@ \fi} ** In other words, aside from any other considerations influencing our decision as to whether or not an *\Aplace*'d *\island* should be allowed to appear in *\box\topins*, it will never be allowed on page~*1*. Simple modifications of *\advancedimtopins@* allow numerous other style decisions.\pagelabel{MODADT} For example, in the *book* style, each *\chapter* starts a new page and then sets *\iffirstchapterpage@* to be true; then in the definition of\linebreak *\advancedimtopins@*, the clause ** \ifnumpageno=1 \else ** is replaced by ** \iffirstchapterpage@ \else ** This prevents anything from appearing in *\box\topins* when the first page of any *\chapter* is presented to the *\output* routine. \pitem Once we have properly increased *\dimen\topins*, we can *\insert* our *\box\islandbox* into *\topins* (it is important to do the *\insert* {\it after\/} taking care of *\dimen\topins*, because \tex\ decides whether or not to move an *\insert* onto the current page at the time the *\insert* is made). \pitem Finally, the counter *\topinscount@* is supposed to be the number of boxes currently in the *\topins* insertion class, so each time an *\Aplace* occurs we need to ** \global\advance\topinscount@ by 1 ** (it will be the duty of the *\output* routine to suitably {\it decrease\/} *\topinscount@* by the number of *\island*'s that finally get placed on that page).\pagelabel{AXXA} \medskip For the actual definition, *\Aplace*, like *\place*, will begin by setting *\place@true* and *\island@false*, and end by resetting *\place@false*. After calling the argument *#1* to set a box, and set *\ifisland@* to be true, we give an error message if *\ifisland@* is still false, ** \def\Aplace#1{\place@true \island@false #1% \ifisland@ . . . . . . \else \Err@{Whoa ... there's no \string\Figure, \string\Table, etc., here} \fi \place@false} ** %\pagebreak \Pitem Assuming we don't give an error message, the first thing will be ** \dimen@=\ht\islandbox@ \advance\dimen@ by \abovebotfigskip \ht\islandbox@=\dimen@ ** %\pagebreak \pitem This will be followed by ** \advance\dimen@ by \dp\islandbox@ \storedim@ ** \pitem Next comes ** \ifnum\topinscount@ > 1 \else \advancedimtopins@ \fi ** \pitem Then we are ready to ** \insert\topins{\penalty0 \splittopskip=0pt \floatingpenalty=0 \box\islandbox@} ** The *\penalty0* allows this insertion to be treated as a split insertion, and thus float to the next page if it does not fit on this page ({\it The \tex book}, pages~123@--124). Other penalties, like *plain* \tex's *\penalty200*, might be used by style designers to encourage \tex\ to keep the insertion on the same page. The *\splittopskip=0pt* insures that only *0pt*~ glue precedes this split insertion; this is probably unnecessary, since the inserted box is quite unlikely to be smaller than *\topskip*, but there's no point taking chances, especially since at one point (page~\ref{WHYSPLITZ})\pagelabel{SPLITZ} it will be crucial to know that this glue is indeed ~*0pt*. The *\floatingpenatly=0* means that there will be no extra page breaking penalties if later inserts are allowed to split also (i.e., to appear on later pages); this is also not really necessary, since *\floatingpenalty* has the default value~*0*. \pitem Finally, we ** \global\advance\topinscount@ by 1 ** So our definition is \dashC** \def\Aplace#1{\place@true \island@false "8APLACE"8 #1% "2 \ifisland@ \dimen@=\ht\islandbox@ \advance\dimen@ by \abovebotfigskip \ht\islandbox@=\dimen@ "2 \advance\dimen@ by \dp\islandbox@ \storedim@ "2 \ifnum\topinscount@ > 1 \else \advancedimtopins@ \fi "2 \insert\topins{\penalty0 \splittopskip=0pt \floatingpenalty=0 \box\islandbox@}% \global\advance\topinscount@ by 1 "2 \else \Err@{Whoa ... there's no \string\Figure, \string\Table, etc., here}% \fi \place@false} "8GDF2"8 ** \section{How the \CS{output} routine works} Now let's consider what the *\ouput* routine must do. At the time that it is invoked, *\box\topins* will contain zero, one, or two boxes, with all other members of the insertion class still held over. The boxes in *\box\topins* occur in the order that they were *\insert*'ed, i.e., the first *\insert* produces the top box, the second *\insert* produces the box below it. Unfortunately, that's the exact reverse of the order we want, because the only way that we can extract boxes from *\box\topins* is by means of a construction like ** \setbox0=\vbox{% \unvbox\topins \globally\setbox1=\lastbox} ** which makes *\box1* be the bottom box. In the case where *\box\topins* contains at most two boxes, this isn't really all that burdensome, but in the general situation, where something like *\AAplace* may have been used, the reverse order becomes a true impediment. So we will define a routine ** \fliptopins@ "8FLIPT"8 ** that recreates *\box\topins* with the sub-boxes in reverse order. At the same time, the number of boxes will be stored in a counter, *\flipcount@*, whose value will be used later on. {\bf(1)} To begin, we set *\flipcount@* to ~*0*. Then we *\unvbox\topins* within another box, into which *\vskip1pt* is inserted at the beginning, as a marker (compare page~\ref{VSMARK}). And within this box, in which *\box\topins* has already been destroyed, we initialize the new *\box\topins*: ** \def\fliptopins@{\global\flipcount@=0 \setbox0=\vbox{% \vskip1pt \unvbox\topins \global\setbox\topins=\vbox{} ** If *\box0* looks like \setbox1\hbox{\} \setbox2\hbox{\} \setbox3\hbox{\} ** \vskip1pt \penalty0 "box1 \penalty0 "box2 \penalty0 "box3 . . . ** then, as we start to take off boxes and penalties from the bottom of this list, *\lastskip* will be ~*1pt* when we have reached the top; at any other point *\lastskip* will be~ *0pt*, since there is no other glue on the list. So the procedure ** \loop \ifdim\lastskip=0pt \global\advance\flipcount@ by 1 \setbox0=\lastbox \global\setbox\topins=\vbox{\unvbox\topins\box0} \unpenalty \repeat} ** should set *\flipcount@* to the number of boxes, and also reconstruct *\box\topins*, in the reverse order. But *\box0* can have a different structure also: when a member of *\topins* has been deferred to a later page (i.e., split), *\box\topins* looks like \setbox1\hbox{\} \setbox2\hbox{\} \setbox3\hbox{\} ** \vskip0pt "box1 \penalty0 "box2 \penalty0 "box3 . . . ** where the *\vskip0pt* is the *\splittopskip* glue, which we have made certain will be~ *0pt* (page~\ref{SPLITZ}).\pagelabel{WHYSPLITZ} So we have to use the more complicated ** \loop \test@false "2 \ifdim\lastskip=0pt \unskip "2 \ifdim\lastskip=0pt \test@true \fi \fi "2 \iftest@ \setbox0=\lastbox \global\setbox\topins=\vbox{\unvbox\topins\box0} \unpenalty \repeat} ** We want to avoid all this rigamorole if *\box\topins* is void to begin with, so our final definition is \C** \newcount\flipcount@ "slip \def\fliptopins@{\global\flipcount@=0 \ifvoid\topins\else \setbox0=\vbox{% "2 \vskip1pt "2 \unvbox\topins "2 \global\setbox\topins=\vbox{}% "2 \loop \test@false "2 \ifdim\lastskip=0pt \unskip \ifdim\lastskip=0pt \test@true \fi \fi "2 \iftest@ \global\advance\flipcount@ by 1 \setbox0=\lastbox \global\setbox\topins=\vbox{\unvbox\topins\box0}% \unpenalty \repeat}% \fi} ** {\bf(2)} After the *\output* routine has done ** \fliptopins@ ** to get *\box\topins* properly set up, it essentially does ** \shipout\vbox{\makeheadline\pagebody\makefootline} ** just like the *plain* \tex\ *\output* routine. As in *plain* \tex, *\pagebody* is not much more than ** \vbox to\vsize{\boxmaxdepth=\maxdepth \pagecontents} ** Actually, the \lamstex\ *\pagebody* routine is a little more complicated, because marginal notes for index entries are attached at this point, but for now let's ignore that complication. *\pagecontents* is the routine where interesting things happen. In *plain* \tex@, *\pagecontents* basically stacks up \setbox0\hbox{things in *\box\topins*} \setbox1\hbox{contents of *\box255*} \setbox2\hbox{things in *\box\footins*, preceded by footnote rule} $$ \vbox{\halign{#\hfil\cr \box0 \cr \box1 \cr \box2 \cr}} $$ But we need a more complicated routine, because if *\box\topins* contains two boxes, we need to put the first box (i.e., the {\it bottom\/} box, since we've already applied *\fliptopins@*) at the top of the page, with suitable space below it, and the other box at the bottom. \Evaluatenref{EXTRAS}\edef\thevalue{\Nref}% \Evaluatenref{EXTRAS2}% \ifnum\thevalue=\Nref \edef\theref{page \Nref}\else \edef\theref{pages\noexpand~\thevalue\ and\noexpand~\Nref}\fi We start with ** \def\pagecontents{% \ifnum\flipcount@ > 0 \setbox\topins=\vbox{\unvbox\topins\global\setbox1=\lastbox} ** to get the bottom box of *\box\topins* into *\box1*. Then we want to take this *\box1* apart, to get the caption and island boxes that constitute it, together with the glue between them, ** \setbox0=\vbox{\unvbox1 \global\setbox1=\lastbox \global\skipi@=\lastskip \unskip \global\setbox3=\lastbox} ** Here we use the fact that there is just one glue between the two boxes (page~\ref{ONEGLUE}). Then we use ** \centerline{\box3} \nointerlineskip \vskip\skipi@ \centerline{\box1} \vskip\belowtopfigskip ** to put everything back, with the island box and the caption box both centered, and *\belowtopfigskip* following; the *\nointerlineskip* is used to make certain that no extra glue creeps in. The original *\box0*, appearing in *\box\topins*, had its height artificially increased by *\abovebotfigskip*. But the individual pieces of this box still have their correct heights, so the ** \centerline{\box3} \nointerlineskip \vskip\skipi@ \centerline{\box1} ** causes a net decrease of *\abovebotfigskip* in height, to which we have then added a net increase of *\belowtopfigskip*. As explained on \theref, \tex\ has already left exactly enough space to allow this change from\linebreak *\abovebotfigskip* to *\belowtopfigskip*, so it looks like everything should work out just right. Actually, however, we will change the ** \vskip\belowtopfigskip ** to ** \ifdim\ht255 < \minpagesize \else \vskip\belowtopfigskip \fi ** for reasons discussed in section~\Sref{WHATH}. \small If\pagelabel{NOCAP} our *\island* had no caption, then *\box1* would actually be the *\hbyw*, or other material, *\skipi@* would be ~*0pt*, and *\box3* would be void. This makes no difference in the current situation, where all the sub-boxes are treated the same, but other styles might have to make explicit checks to see whether *\box0* contained a caption or not. \andsmall It is even conceivable that the style would need to know what class of *\island* was contained in *\box0*. This information would probably best be recorded in a list like the list *\AAlist@* discussed in sections~\Sref{AandAA}, \Sref{BPLACE}, and~\Sref{RSDTNS}. \endsmall Once we've taken care of figures at the top of the page we use ** \ifdim\ht255 < \minpagesize \vfil \else \unvbox255 \fi ** to print the text in *\box255*, unless there is too little to go on the page; then we finish the definition off with a routine *\bottomfigs@*, which adds the figure at the bottom if *\box\topins* still has a box left in it, after which we add the footnotes, just as in *plain* \tex@: \dashC** \def\pagecontents{% "8PAGECT"8 \ifnum\flipcount@ > 0 "2 \setbox\topins=\vbox{\unvbox\topins \global\setbox1=\lastbox}% "2 \setbox0=\vbox{\unvbox1 \global\setbox1=\lastbox \global\skipi@=\lastskip \unskip \global\setbox3=\lastbox}% "2 \centerline{\box3}\nointerlineskip \vskip\skipi@ \centerline{\box1}% "2 \ifdim\ht255 < \minpagesize \else \vskip\belowtopfigskip \fi \fi "2 \ifdim\ht255 < \minpagesize \vfil \else \unvbox255 \fi "2 \bottomfigs@ \ifvoid\footins\else \vskip\footins\footnoterule\unvbox\footins\fi} "slip \def\bottomfigs@{% "8BFIGS"8 \ifnum\flipcount@ > 1 "2 \nointerlineskip \vskip\abovebotfigskip "2 \setbox0=\vbox{\unvbox\topins \setbox0=\lastbox \unvbox0 \global\setbox1=\lastbox \global\skipi@=\lastskip \unskip \global\setbox3=\lastbox}% "2 \centerline{\box3}\nointerlineskip \vskip\skipi@ \centerline{\box1}% \fi} ** Note that the *\nointerlineskip* at the very beginning of the definition of *\bottomfigs@* is quite essential: without it, extra glue would be added before the *\centerline{\box3}*. Even extra *\lineskip* glue might not fit at this stage, but, in fact, the glue might be bigger, because *\box3* could be empty (for example, an empty caption above a table), in which case *\baselineskip* glue would be used! \small Since we went to the trouble of taking apart *\box\islandbox@*, in order to allow the glue between the island and the caption to stretch or shrink on the page, we might also want to take apart *\box\Captionbox@*, so that the glue between multiple captions could also participate. \andsmall When we have an *\island* at top, followed by material from *\box255*, ** . . . \centerline{\box1} \vskip\belowtopfigskip \unvbox255 . . . ** the first line of *\box255* will have glue above it determined by the value of *\topskip*; for example, in the default style there will usually be enough glue to make the height of the first line be~*10pt*. So the space between the bottom of *\box1* and the baseline of this first line will be $\hbox{*\belowtopfigskip*}+\hbox{\tt10pt}$. If we were being fussy, we might increase *\belowtopfigskip* by~ *2pt* to compensate for this. However, even that wouldn't work precisely if *\box1* has some depth. Probably none of this matters much when *\belowtopfigskip* is a fairly large value, but a more precise routine could be made: for *\island*'s like *\Figure*, with their caption below, in the *\endisland* routine (page~\ref{EIROUTINE}) we could add ** \kern-\prevdepth ** after the *\box\Captionbox@* in the final *\vbox* \endsmall {\bf(3)} After the *\shipout*, we will use ** \advancepageno ** to advance the page number. ({\bf 4}) Now that *\pagecontents* has properly arranged the boxes in *\topins* on the page, the *\output* routine must set things up properly for the next page. First of all ** \global\advance\topinscount@ by -\flipcount@ ** insures that *\topinscount@* will still represent the number of elements in the *\topins* insertion class. Taking care of *\dimen\topins* requires more work. \def\1#1{\hbox to#1pt{\leaders\hrule\hfil}} At the time that the *\output* routine was called, *\dimen\topins* was the height plus depth, plus extra *\abovebotfigskip* space, for the first two boxes in the *\topins* insertion class. After the *\output* routine is done, we have to reset *\dimen\topins* to the same quantity for the first two boxes that remain in this class. Remember that *\box\topinsdims@* is a *\vbox* of empty *\hbox*'s whose widths are the appropriate dimensions for the various boxes in the *\topins* insertion class. Since these boxes were added at the {\it top\/} (page~\ref{STOREDIM}), the bottom box has the dimension for the first box in *\topins*, and so forth. In order to keep our information current, we will remove boxes from the bottom, the number of boxes we remove being *\flipcount@*, and reset *\dimen\topins* to the sum of the widths of the next two bottom boxes: this will ensure that on the next page, at most two of the next boxes in *\topins* will actually be allowed to appear in *\box\topins*. To do this, we first ** \global\setbox\topinsdims@= \vbox{% \unvbox\topinsdims@ "2 \count@=0 \loop \ifnum\count@ < \flipcount@ \setbox0=\lastbox \advance\count@ by 1 \repeat ** In other words, the new *\box\topinsdims@* will start out with all the boxes originally in this box (from the *\unvbox\topinsdims@*); and then we will use *\setbox0=\lastbox* the appropriate number of times to get rid of *\flipcount@* boxes at the bottom. Before finishing off this box, we need to set *\dimen\topins* to the sum of the widths of the next two bottom boxes. The only way we can get at these widths is by using *\lastbox*; since we don't want to throw away these next two boxes, however, we will store them in *\box1*, and then *\unvbox1* before completing the box. For purposes of generalization later on, we will write this as a *\loop* also: ** \dimen@=0pt \count@=0 \setbox2=\vbox{} "2 \loop \ifnum\count@ < 2 \setbox0=\lastbox \advance\dimen@ by \wd0 \setbox2=\vbox{\box0 \unvbox2} \advance\count@ by 1 \repeat "2 \unvbox2 \global\dimen\topins@=\dimen@ ** The whole routine will be called *\resetdimtopins@*: \dashC** \def\resetdimtopins@{% "8RESETDT"8 \global\advance\topinscount@ by -\flipcount@ "2 \global\setbox\topinsdims@= \vbox{% "2 \unvbox\topinsdims@ \count@=0 "2 \loop \ifnum\count@ < \flipcount@ \setbox0=\lastbox \advance\count@ by 1 \repeat "2 \dimen@=0pt \count@=0 \setbox2=\vbox{}% "2 \loop \ifnum\count@ < 2 \setbox0=\lastbox \advance\dimen@ by \wd0 \setbox2=\vbox{\box0 \unvbox2}% \advance\count@ by 1 \repeat "2 \unvbox2 \global\dimen\topins=\dimen@}} ** \medskip We aren't going to formally define an *\output* routine for our specialized style, saving for Chapter~\ref{OUTPUT} numerous additional details (some discussed in the next section of this chapter). But here is a summary of the main points: \list \newfontstyle\list1\bf \item The \pagelabel{OUTLIST} *\output* routine first uses *\fliptopins@* to get *\box\topins* into the right order, computing *\flipcount@* in the process. \item Then it ships out a *\vbox* whose main part is *\pagecontents*, which we have already considered in detail. \item At this point, when any delayed *\write*'s have been done, we can use ** \advancepageno "8APRD"8 ** to advance the page number. \item Then we use ** \resetdimtopins@ ** to properly adjust *\topinscount@* and *\dimen\topins* for the next page. \item\label{UNV255}\pagelabel{PUNV255} Unlike the *plain* routine, where *\box255* is always used up, our routine will not use *\box255* if its height is less than *\minpagesize*. So at this point we use ** \ifvoid 255 \else \unvbox255 \penalty\outputpenalty ** This puts the material in *\box255* back on the top of the list, together with whatever *\penalty* precedes it, since this was recorded in *\outputpenalty* ({\it The \tex book}, pages~125 and~254). \item Then we end, as in *plain* \tex@, with ** \ifnum\outputpenalty > -20000 \else \dosupereject \fi ** \endlist \section{When insertions float\label{FLOAT}} Although the theory behind our definition of *\Aplace* may seem fine, it has one annoying flaw that becomes particularly significant when we are dealing with straight text, unrelieved by displayed formulas, or other material that introduces shrink or stretch on the page. To take a particular example, let us suppose that we have set \setbox0\hbox{($=\hbox{39}\times\hbox{*12pt*}+\hbox{*10pt*}$)} ** \parskip=0pt \topskip=10pt \baselineskip=12pt \vsize=478pt "box0 ** so that exactly 40~lines of text will fit on a page, without any stretch or shrink allowed between paragraphs. (Styles with such specifications usually arrange for the heights of any Chapter headings, etc., to be an integer multiple of *\baselineskip*. When illustrations are to be included, *\belowtopfigskip* and *\abovebotfigskip* should probably have stretch and shrink at least half of *\baselineskip*, unless care is taken to insure that the heights of all *\island*'s, when added to these *\vskip*'s, are also an integer number times *\baselineskip*. However, for purposes of illustration, we will simply continue to use our default values of *\belowtopfigskip* and *\abovebotfigskip*.) Now suppose that near the bottom of page~2 we *\Aplace* something that will not fit on that page, so that the *\insert* will split. If `*\showbox\topins*' is added to the *\output* routine, \pagelabel{SHOWBOX} then before page ~*[1]* is shipped out, the *.log* file will contain the following information about *\box\topins* (which happens to be *\box253*): ** > \box253=void ** But before page~*[2]* is shipped out, we will have ** > \box253= \vbox(0.0+0.0)x0.0 ** this *\vbox* being the part that is split off from the ** \insert\topins{\penalty0 ... } ** This might seem like an insignificant difference, but it's not: Since \tex\ has added something (albeit something trivial) to *\box\topins*, it has also allowed for *\skip\topins=-3pt* glue. If `*\showbox255*' is added to the *\output* routine, then the log file will have ** > \box255= \vbox(481.0+...)x... .\glue(\topskip) . . . ** In other words, *\box255* is now *3pt*~{\it too high}. In our particular example, it will probably be an *Underfull* box. \tex\ doesn't report *Underfull* boxes when *\box255* is packaged for use by the *\output* routine ({\it The \tex book}, page~400), and when we *\unvbox255*, everything will probably still turn out all right. But if *\skip\topins* were as large as *12pt*, then we would definitely have an extra line on our page. Consequently, the *\output* routine will have to take care to trim *\box255* down to the proper height, and anything left over will have to be put back on the main vertical list at the end of the *\output* routine. (Such a final step in the *\output* routine will be needed for other reasons also; for example, when there are figures at both the top and bottom of the page, and the space between is less than *\minpagesize*, so that no text is allowed to appear on the page, everything in *\box255* has to be put back). Notice that if *\belowtopfigskip* were {\it greater\/} than *\abovebotfigskip*, then our problem would be much more severe: In this case, *\box255* would be {\it too short}, and there would be no way to rectify the situation. This is discussed further in section~\Sref{255TOO}. \nopunct\sectiong{What happens to an \CS{Hbyw}?\label{WHATH}} When we have an *\island* involving an *\Hbyw*, the height of *\box0* will have been increased so that it is *18pt* larger than *\vsize*. \tex\ has chosen *\box255* so that \setbox5\hbox{height of *\box255*} \setbox6\hbox{*\vsize*} \setbox7\hbox{height of *\box0*} \setbox8\hbox{*\vskip\topins*} \Math \align \copy6&= \copy5+\copy7+\copy8\\ &=\copy5+\copy6+\hbox{\tt18pt}-\hbox{\tt3pt}\\ &=\copy5+\copy6+\hbox{\tt15pt} \endalign \endMath So *\box255* will be an empty box of height~ *-15pt* (see below for further elucidation), which would make things come out just right when it is added back in with the other material. But we won't be adding the material from *\box255* in this situation, since its height is less than *\minpagesize*; \footnote{When we *\unvbox255* at the end of the *\output* routine---% see~(\ref{UNV255}) on page~\ref{PUNV255}, and page~\ref{PPUNV255}---% to put unused material from *\box255* back at the top of the current page, the contents of such an empty box will simply disappear, since \tex\ discards such glue at the top of the current page.} consequently, if we were to add *\vskip\belowtopfigskip* in this case, we would get an *Overfull* box that is ~*15pt* too high. That is why we had to use ** \ifdim\ht255 < \minpagesize \else \vskip\belowtopfigskip \fi ** In our final definition, page~\ref{FINALPC}, we will actually use more refined emendations. \medskip This apparently anomalous situation, where *\box255* has negative height, more or less follows from the rules on page~123 of {\it The \tex book}. The first time that an *\Hbyw* insertion is considered, it will be split, according to {\bf Step 4}, at the *\penalty0*, leaving a box of height $\hbox{*\vsize*} + \hbox{\tt15pt}$. When the next page is made, this box will definitely be added, at {\bf Step~ 1}, which will decrease the *\pagegoal* {\it g\/} by this height, so that it is now *-15pt*.\pagelabel{NEG255} \medskip Note that, as a consequence, an *\Hbyw* will always appear at least one page after it is *\Aplace*'ed, even if there is nothing else on the page at the time. \endsmall \chapter \CS{Aplace}, \CS{AAplace} and \CS{Bplace}\label{AAB}\endchapter We are now ready to consider the real definition of *\Aplace*, together with *\AAplace* and *\Bplace*. Unlike *\Cplace*, *\Mplace* and *\MXplace*, which can only be used between paragraphs, or in a special *\Par...\endPar* region, *\Aplace*, *\AAplace*, and *\Bplace* can be used directly within paragraphs. Nevertheless, it is conceivable that the latter will also be used in a *\Par...\endPar* region (probably because some *\Cplace*, *\Mplace*, or *\MXplace* is also being used in that region). So we first have to take care of some preliminaries related to *\Par...\endPar* regions. The \lamstex\ Manual states, on page~67, that blank lines within a *\Par"allowbreak..."allowbreak\endPar* region will give an error message, and this was indeed true in version~*1* of \lamstex@. But there is really no need for this, and the implementation was quite imperfect, so that feature has been dropped in version~*2*. \section{Figures, etc., within \CS{Par}{\tt...}\CS{endPar}} First we need a flag to tell whether we are in such a paragraph, a counter to tell how many times a *\...place* occurred within this paragraph, and a box in which to store the text of such a paragraph: \C** \newif\ifPar@ \newcount\Parcount@ \newbox\Parbox@ ** The *\island*'s that are *\...place*'ed within such regions will be stored in other boxes, \C** \expandafter\newbox\csname Parfigbox1\endcsname \expandafter\newbox\csname Parfigbox2\endcsname \expandafter\newbox\csname Parfigbox3\endcsname \expandafter\newbox\csname Parfigbox4\endcsname \expandafter\newbox\csname Parfigbox5\endcsname ** Creating these boxes with *\csname"allowbreak..."allowbreak\endcsname* is easier and more convenient than trying to make a `*\Parfigbox*' macro with an argument. It turns out that we are also going to need \ registers to store the depths of various paragraphs: \C** \expandafter\newdimen\csname Parprev1\endcsname \expandafter\newdimen\csname Parprev2\endcsname \expandafter\newdimen\csname Parprev3\endcsname \expandafter\newdimen\csname Parprev4\endcsname \expandafter\newdimen\csname Parprev5\endcsname \expandafter\newdimen\csname Parprev6\endcsname ** We need six \ registers because `*\Parprev1*' will store the depth of the paragraph before the *\Par...\endPar* region, `*\Parprev2*' will store the depth of the text before the first *\island*, etc.\pagelabel{PARPREV} As before (compare pages~\ref{QTS1}, \ref{QTS2}, \ref{QTS3}, and~\ref{QTS4}), we use quotes around *\Parprev1* to indicate that it is a single control word. In addition to all this, within each *\Par...\endPar* region we will be keeping a list, ** \Parlist@ ** which will simply be a list of letters. The definition of *\Par* begins by ending the previous paragraph, storing the depth of the last line of that paragraph in `*\Parprev1*', setting *\ifPar@* to be true, and initializing *\Parcount@* to be *1* and *\Parlist@* to be empty: ** \par \csname Parprev1\endcsname=\prevdepth \Par@true \global\Parcount@=1 \global\let\Parlist@=\empty ** Then we begin to set *\box\Parbox@*, ** \setbox\Parbox@=\vbox\bgroup ** We want this *\vbox* to begin with a *\break* for latter purposes (section~\Sref{ENDPAR}).\pagelabel{NBREAK} Our whole definition is: \C** \def\Par{\par \csname Parprev1\endcsname=\prevdepth \global\Parcount@=1 \Par@true\global\let\Parlist@=\empty \global\setbox\Parbox@=\vbox\bgroup\break} ** The definition of *\endPar* will not be given until section~\Sref{ENDPAR}. \section{\CS{place\@}\label{place}}The constructions *\Aplace*, *\AAplace*, and *\Bplace* have much in common, and they are going to be defined in terms of a control sequence *\place@#1#2* with a rather strange combinations of arguments: \setbox0\hbox{will be defined in terms of} ** \Aplace "copy0 \place@ a\Aplace@ \AAplace "copy0 \place@ A\AAplace@ \Bplace "copy0 \place@ b\Bplace@ . . . ** Here the first argument is simply a convenient single character marker, whose role will eventually become clear; the second argument, *\Aplace@*, *\AAplace@*, or *\Bplace@*, will then do the main work. As an example of how this is going to work out, let's consider the definition of *\Aplace*, which will actually come later: ** \def\Aplace#1{\prevanish@ \place@true \island@false #1% \place@ a\Aplace@ \postvanish@} ** where we begin with *\prevanish@* and end with *\postvanish@* to make the *\Aplace* invisible. The definition of *\place@* will be of the form ** \def\place@#1#2{% \ifisland@ "2 . . . \else \Err@{Whoa ... there's no \string\Figure, \string\Table, etc., here} \fi \place@false} ** to produce an error message if the argument of *\Aplace* didn't involves some sort of *\island*. If we are in vertical mode, we will just call argument *#2*, but in horizontal mode we call *\vadjust{#2}*. For example, we will call *\Aplace@* when we are using *\Aplace* in vertical mode and ** \vadjust{\Aplace@} ** in horizontal mode. So far we haven't even mentioned argument *#1*. And, quite concomitantly, we haven't mentioned what happens if we happen to be in a *\Par...\endPar* In this case, we want to globally set the box `*\Parfigbox1*' to be\linebreak *\box\islandbox@* if this is the first *\...place@*, or globally set the box `*\Parfigbox2*' to *\box\islandbox@* if it is the second, etc. We will keep track of which *\...place@* we are at with the counter *\Parcount@*, which was set to~*1* by the *\Par*, and which will be increased with each *\...place*. So the box we want to be setting is ** \csname Parfigbox\number\Parcount@\endsname ** and to globally set this box to *\box\islandbox@* we need to {\litindent=0pt say ** \expandafter\expandafter\expandafter \global\expandafter\setbox \csname Parfigbox\number\Parcount@\endcsname=\box\islandbox@ ** followed by} ** \global\advance\Parcount@ by 1 ** to keep the *\Parcount@* counter accurate. And, finally, here is where argument *#1* comes in. When it comes time to properly tear apart *\box\Parbox@*, we will need to know what sort of *\...place*'s produced the various *\Parfigbox*'s. We keep this information in the list *\Parlist@*, which, for example, will be `*abaAb*' when our *\Par"allowbreak..."allowbreak\endPar* region contains the sequence ** \Aplace \Bplace \Aplace \AApalce \Bplace ** Each time we *\place@*, we will simply add the marker, argument *#1*, at the right of *\Parlist@*, ** \xdef\Parlist@{\Parlist@#1} ** The only other detail is that an error message should be produced if more than five *\...place*'s appear in the *\Par...\endPar* region. Here is the complete code: \C** \def\place@#1#2{% \ifisland@ \ifhmode "2 \ifPar@ "2 \ifnum\Parcount@ > 5 \Err@{Only 5 \string\place's allowed per \string\Par...\noexpand\endPar paragraph}% "2 \else \expandafter\expandafter\expandafter \global\expandafter\setbox \csname Parfigbox\number\Parcount@\endcsname =\box\islandbox@ "2 \global\advance\Parcount@ by 1 \xdef\Parlist@{\Parlist@#1}% "2 \fi "2 \else \vadjust{#2}% \fi "2 \else #2% \fi "2 \else \Err@{Whoa ... there's no \string\Figure, \string\Table, etc., here}% \fi \place@false} ** \section{\CS{Aplace} and \CS{AAplace}\label{AandAA}} As we have already indicated, *\Aplace* is defined directly in terms of *\place@*, \C** \long\def\Aplace#1{\prevanish@ \place@true \island@false #1% \place@ a\Aplace@ \postvanish@} ** so that it calls *\Aplace@*. The *\long* is needed since *#1* can contain a *\caption* with blank lines. The definition of *\Aplace@* will then involve most of the preliminary definition of *\Aplace* on page~\ref{APLACE}: ** \dimen@=\ht\islandbox@ \advance\dimen@ by \abovebotfigskip \ht\islandbox@=\dimen@ "2 \advance\dimen@ by \dp\islandbox@ \storedim@ "2 \ifnum\topinscount@ > 1 \else \advancedimtopins@ \fi "2 \insert\topins{\penalty0 \splittopskip=0pt \floatingpenalty=0 \box\islandbox@} \global\advance\topinscount@ by 1 ** But *Aplace@* is going to be more complicated, for several reasons. First of all, we are going to define *\AAplace* indirectly in terms of *\Aplace@* by using a flag *\ifAA@*: \C** \long\def\AAplace#1{\prevanish@ \place@true \island@false #1% \place@ A\AAplace@ \postvanish@} "slip \newif\ifAA@ "slip \def\AAplace@{\AA@true\Aplace@\AA@false} ** Even if we weren't going to use this approach, we would still need a way of knowing whether a box in the *\topins* insertion class was produced with *\Aplace* or *\Bplace* on the one hand, or by *\AAplace* on the other hand. We will keep this information in a list *\AAlist@*, initially defined to be empty, with *0*~ representing the *\Aplace* or *\Bplace* case, and *1*~representing the *\AAplace* case. When *\ifAA@* is true, so that we considering an *\AAplace*, we will add~*1* to the end of this list, otherwise we will add~*0*. Moreover, in the *\AAplace* case, we always want to call the *\advancedimtopins@* routine, no matter what value *\topinscount@* has, since *\AAplace*'s are supposed to allow any number of *\island*'s per page. Finally, we want the definition of *\Aplace@* to begin with an *\allowbreak*, because this may help \tex\ check whether the insertion fits. \small Reason: When a *\vadjust{\Aplace@}* occurs within a paragraph, the main vertical list will have something like \setbox0\hbox{*\hbox{*line {\it L}$_{\text1}$ of text*}*} \setbox1\hbox{*\hbox{*line {\it L}$_{\text2}$ of text*}*} \setbox2\hbox{*\baselineskip* glue} ** "box0 \Aplace@ "box2 "box1 ** Suppose that the line {\it L}$_{\text1}$ won't fit on the page, so that \tex\ will break the page before this line, which will be at, or near, the top of the next page. \tex\ computes the page total {\it t\/} at line {\it L}$_{\text1}$ {\it after\/} a penalty or glue that follows this line ({\it The \tex book}, page~133, lines~17@--18). Consequently, if the *\Aplace@* contains no penalty or glue, \tex\ will read the *\Aplace@* before it knows that line {\it L}$_{\text1}$ must go on the next page; thus, it will conclude that the insertion doesn't fit (near the bottom of the current page), instead of concluding that it does fit (near the top of the next page). The *\allowbreak* causes \tex\ to compute the page total {\it t\/} before it sees the *\Aplace@*, so that it will realize in time that the line doesn't fit. (The procedure isn't foolproof, however; even if line {\it L}$_{\text1}$ does fit, \tex\ may end up breaking before this line if a break point with smaller cost occurred earlier). \endsmall The full definition of *\Aplace@* is: \C** \let\AAlist@=\empty "slip \def\Aplace@{\allowbreak "2 \dimen@=\ht\islandbox@ \advance\dimen@ by \abovebotfigskip \ht\islandbox@=\dimen@ "2 \advance\dimen@ by \dp\islandbox@ \storedim@ "2 \ifAA@ \xdef\AAlist@{\AAlist@1}% \advancedimtopins@ "2 \else \xdef\AAlist@{\AAlist@0}% \ifnum\topinscount@>1 \else \advancedimtopins@ \fi \fi "2 \insert\topins{\penalty0 \splittopskip=0pt \floatingpenalty=0 \box\islandbox@}% \global\advance\topinscount@ by 1 } ** \section{\CS{Bplace}\label{BPLACE}} The definition of *\Bplace* is analogous to that for *\Aplace*: \C** \long\def\Bplace#1{\prevanish@ \place@true \island@false #1% \place@ b\Bplace@ \postvanish@} ** with all the work going into the definition of *\Bplace@*. A *\Bplace* is supposed to force an *\island* to the bottom of the page if it would normally go at the top of the current page. In other words ** \ifnum\topinscount@=0 ** we need to force the *\island* to the bottom of the page. To do this, we will basically first *\Aplace* an island consisting of an empty box ** \vbox to -\belowtopfigskip{} ** of negative height *-\belowtopfigskip*, so that when this island is followed by *\vskip\belowtopfigskip* we will be back at the top of the page. We actually want to our island to be a box of the form ** \vbox{\vbox to -\belowtopfigskip{}} ** so that it can be taken apart, like any other *\box\islandbox@*, with our *\unvbox*'ing mechanism, so we start with ** \setbox0=\vbox{\vbox to -\belowtopfigskip{}} ** The next steps, ** \dimen@=\ht0 \advance\dimen@ by \abovebotfigskip ** set *\dimen@* to $\hbox{*\abovebotfigskip*}-\hbox{*\belowtopfgiskip*}$, and are thus equivalent to \setbox0\hbox{(c.f\. page \ref{EXTRAS22})} ** \dimen@=-\skip\topins "box0 ** after which we want to set ** \ht0=\dimen@ ** and then ** \storedim@ \advancedimtopins@ \insert\topins{\box0} \global\advance\topinscount@ by 1 ** as well as ** \xdef\AAlist@{\AAlist@0} ** This should then be followed by the instructions for *\Aplace*'ing the \linebreak *\box\islandbox@* that we want to force to the bottom: \C** \def\Bplace@{\allowbreak \ifnum\topinscount@=0 \setbox0=\vbox{\vbox to -\belowtopfigskip{}}% "2 \dimen@=-\skip\topins \ht0 =\dimen@ \storedim@ "2 \advancedimtopins@ "2 \insert\topins{\box0}% \global\advance\topinscount@ by 1 \xdef\AAlist@{\AAlist@0}% \fi "2 \dimen@=\ht\islandbox@ \advance\dimen@ by \abovebotfigskip \ht\islandbox@=\dimen@ "2 \advance\dimen@ by \dp\islandbox@ \storedim@ "2 \xdef\AAlist@{\AAlist@0}% "2 \ifnum\topinscount@>1 \else \advancedimtopins@ \fi "2 \insert\topins{\penalty0 \splittopskip=0pt \floatingpenalty=0 \box\islandbox@}% \global\advance\topinscount@ by 1 } ** \section{Changing \CS{pagecontents}\label{CPCT}}Our definition of *\Bplace* forces the first change in our preliminary definition of *\pagecontents* (page~\ref{PAGECT}). Recall that we first ** \setbox\topins=\vbox{\unvbox\topins \global\setbox1=\lastbox} ** so that *\box1* now contains the material we want to put on top, and then took *\box1* apart with ** \setbox0=\vbox{\unvbox1 "8BREAKISLAND"8 \global\setbox1=\lastbox "2 \global\skipi@=\lastskip \unskip \global\setbox3=\lastbox} ** In the case where *\box1* is ** \vbox{\vbox to -\belowtopfigskip{}} ** the new *\box1* will be *\vbox to -\belowtopfigksip{}*, while *\skipi@* will be ~*0pt* and *\box3* will be empty (compare the small print remark on page~\ref{NOCAP}). The problem now is that when we add the combination ** \centerline{\box3} \nointerlineskip \vskip\skipi@ \centerline{\box1} ** the last *\hbox*, \setbox0\hbox{$=(\,\hbox{*\hbox to\hsize{...}*}\,)$} ** \centerline{\box1}"box0 ** will not have the negative height *-\belowtopfigskip* that we want it to have: an *\hbox* never has negative height or depth ({\it The \tex book}, page~77). So we must change this to ** \centerline{\box3} "8PRINTISLAND"8 \nointerlineskip \vskip\skipi@ \ifdim\ht1 < 0pt \box1 \else \centerline{\box1}\fi ** \section{\CS{breakisland\@} and \CS{printisland\@}} The construction by which we just took apart *\box1* will appear several times, so we introduce the abbreviation \C** \def\breakisland@{\global\setbox1=\lastbox \global\skipi@=\lastskip \unskip \global\setbox3=\lastbox}% ** Similarly, for the combination that puts the *\island* back on the page we introduce the abbreviation \C** \def\printisland@{\centerline{\box3}% \nobreak \nointerlineskip \vskip\skipi@ \ifdim\ht1 < 0pt \box1 \else \centerline{\box1}\fi} ** Of course, the final *\ifdim...\fi* clause will normally be equivalent to *\centerline{\box1}*, and will be operative only when our macros have created a *\box1* of negative height. Similarly, although the added *\nobreak* will sometimes be needed (page~\ref{NIREQ}), at other times it will be superfluous. \small For other styles, which treat *\island*'s differently, a completely different\linebreak *\printisland@* routine might be in order; in such cases *\endisland* may combine *\box\islandbox@* and *\box\Captionbox@* in an entirely different way. \endsmall \section{\CS{bottomfigs\@}} The possibility of *\AAplace*'s, and thus more than two *\island*'s per page, forces us to change the preliminary version of *\bottomfigs@* (page~\ref{BFIGS}) to a loop. We also use the newly defined routines *\breakisland@* and *\printisland@* in the definition. \C** \def\bottomfigs@{% \count@=1 "2 \loop \ifnum\count@ < \flipcount@ \nointerlineskip \vskip\abovebotfigskip "2 \setbox\topins=\vbox{\unvbox\topins\setbox0=\lastbox \unvbox0 \breakisland@}% \printisland@ "2 \advance\count@ by 1 \repeat} ** As we will see later (page~\ref{NEGB}), there may be occasions during the use of this *\bottomfigs@* routine when the *\box1* in the *\printisland@* routine has negative height.\pagelabel{USENEGB} \section{\CS{resetdimtopins\@}\label{RSDTNS}} We also have to change the definition of the routine *\resetdimtopins@*. Recall that at the time *\resetdimtopins@* is called, *\box\topinsdims@* will be a *\vbox* of the form \setbox1\hbox{\} \setbox2\hbox{\} \setbox3\hbox{\} ** . . . \hbox to "box3{} \hbox to "box2{} \hbox to "box1{} ** where \ is the width of the first box that was still in the *\topins* insertion class just before this page was made, \ is the width of the second such box, etc. Of these boxes in the *\topins* insertion class, *\flipcount@* actually appeared in *\box\topins*, and were thus put on the page; the first thing we do is to diminish *\topinscount@* by *\flipcount@*, so that *\topinscount@* is then the number of boxes still in the insertion class. At this time, the list *\AAlist@* (of *0*'s and *1*'s) will have as many members as *\box\topinsdims@*. As we perform the next step, ** \global\setbox\topinsdims@=\vbox{% \unvbox\topinsdims@ \count@=0 \loop \ifnum\count@ < \flipcount@ \setbox0=\lastbox \advance\count@ by 1 \repeat ** which removes the bottom *\flipcount@* boxes from *\box\topinsdims@*, we are also going to want to remove the first *\flipcount@* members from (the left end of) *\AAlist@*. To do this, we use ** \global\setbox\topinsdims@=\vbox{% \unvbox\topinsdims@ "2 \count@=0 "9\def\next@##1##2\next@{\gdef\AAlist@{##2}}"9 "2 \loop \ifnum\count@ < \flipcount@ \setbox0=\lastbox "9\expandafter\next@\AAlist@\next@"9 \advance\count@ by 1 \repeat ** The next *\loop* in the definition, ** \loop \ifnum\count@ < 2 \setbox0=\lastbox \advance\dimen@ by \wd0 \setbox2=\vbox{\box0 \unvbox2} \advance\count@ by 1 \repeat ** requires more modifications. After the second iteration of the *\loop*, we want to continue as long as the boxes we encounter correspond to *\AAplace*'d material. To determine whether that is the case, we ** \edef\nextiii@{\AAlist@} \def\next@##1##2\next@{\def\nextii@{##1}\def\nextiii@{##2}} ** so that *\expandafter\next@\nextiii@\next* defines *\nextii@* to be the next element of *\AAlist@*, and removes it from the copy *\nextiii@* of *\AAlist@* (we use a copy because we don't want these elements to be removed once the *\vbox* is finished). Our *\loop* will iterate at least two times and at most *\topinscount@* times. For values of *\count@* in between, we continue the iteration only when *\nextii@* is~*1*: ** \loop \test@false \ifnum\count@ < \topinscount@ \expandafter\next@\nextiii@\next@ "2 \ifnum\count@ < 2 \test@true "2 \else \if\nextii@ 1\test@true\fi \fi \fi "2 \iftest@ \setbox0=\lastbox \advance\dimen@ by \wd0 \setbox2=\vbox{\box0 \unvbox2}% \advance\count@ by 1 \repeat ** Thus, our whole (and final) definition is: \C** \def\resetdimtopins@{% \global\advance\topinscount@ by -\flipcount@ "2 \global\setbox\topinsdims@=\vbox {\unvbox\topinsdims@ "2 \count@=0 \def\next@##1##2\next@{\gdef\AAlist@{##2}}% "2 \loop \ifnum\count@ < \flipcount@ \setbox0=\lastbox \expandafter\next@\AAlist@\next@ \advance\count@ by 1 \repeat "2 \dimen@=0pt \count@=0 "2 \setbox2=\vbox{}% \edef\nextiii@{\AAlist@}% \def\next@##1##2\next@{\def\nextii@{##1}% \def\nextiii@{##2}}% "2 \loop \test@false "2 \ifnum\count@ < \topinscount@ \expandafter\next@\nextiii@\next@ "2 \ifnum\count@ < 2 \test@true \else \if\nextii@ 1\test@true\fi \fi \fi "2 \iftest@ \setbox0=\lastbox \advance\dimen@ by \wd0 \setbox2=\vbox{\box0 \unvbox2}% \advance\count@ by 1 \repeat "2 \unvbox2 \global\dimen\topins=\dimen@}} ** \chapter \CS{Cplace}, \CS{Mplace}, and \CS{MXplace}\endchapter Next we consider the variants *\Cplace*, *\Mplace*, and *\MXplace*, which must be used between paragraphs, or in the special *\Par"allowbreak..."allowbreak\endPar* construction. \section{\CS{Place\@}\label{PLACE}} Just as *\Aplace*, *\AAplace*, and *\Bplace* are defined in terms of *\place@*, the *\Cplace*, *\Mplace*, *\MXplace* constructions are defined in terms of *\Place@*. This is similar to *\place@*, except that (1)~in horizontal mode if we are not in a *\Par"allowbreak..."allowbreak\endPar* region, we will give an error message rather than using a *\vadjust*, and (2)~if we are in a *\Par"allowbreak..."allowbreak\endPar* region we will add a *\vadjust{\break}*: \C** \def\Place@#1#2{% \ifisland@ "2 \ifhmode "2 \ifPar@ \ifnum\Parcount@ > 5 \Err@{Only 5 \string\place's allowed per \string\Par...\noexpand\endPar paragraph}% "2 \else \expandafter\expandafter\expandafter \global\expandafter\setbox \csname Parfigbox\number\Parcount@\endcsname =\box\islandbox@ "2 \global\advance\Parcount@ by 1 "2 \xdef\Parlist@{\Parlist@#1}% \vadjust{\break}% "2 \fi "2 \else \Err@{\noexpand#2allowed only in a \string\Par...\noexpand\endPar paragraph}% \fi "2 \else #2% \fi "2 \else \Err@{Whoa ... there's no \string\Figure, \string\Table, etc., here}% \fi \place@false} ** Note that the *\vadjust{\break}* occurs within the *\box\Parbox@* that we are setting; it will be used (in section~\Sref{ENDPAR}) when we pull the box apart. \section{\CS{Cplace\@}}We first declare a new flag and a new \ register: \C** \newif\ifC@ \newdimen\Cdim@ ** The definition of *\Cplace* is analogous to all the previous *\...place*'s, \C** \long\def\Cplace#1{\prevanish@ \place@true \island@false #1% \Place@ c\Cplace@ \postvanish@} ** leaving all the work to *\Cplace@*. *\Cplace@* basically just calls *\Aplace@*, except that when *\topinscount@* is ~*0*, so that there are no insertions waiting to go on the page, it sets the flag *\ifC@* to be true, and the \ *\Cdim@* to *\pagetotal*; this information will then be used by the *\output* routine. \C** \def\Cplace@{\allowbreak \ifnum\topinscount@ > 0 \else \global\C@true \global\Cdim@=\pagetotal \fi \Aplace@} ** Here we start with *\allowbreak*, even though this occurs in the *\Aplace@*, because it might give \tex\ the opportunity to call the *\output* routine, which will reset *\topinscount@*. \section{\CS{Mplace\@} and \CS{MXplace\@}} The *\Mplace* and *\MXplace* constructions are going to be defined together, much like *\Aplace* and *\AAplace*. First we define \C** \long\def\Mplace#1{\prevanish@ \place@true \island@false #1% \Place@ m\Mplace@ \postvanish@} ** so that the work goes into defining *\Mplace@*. Similarly, we define \C** \long\def\MXplace#1{\prevanish@ \place@true \island@false #1% \Place@ M\MXplace@ \postvanish@} ** where we define *\MXplace@* in terms of *\Mplace@* and a flag: \C** \newif\ifMX@ \def\MXplace@{\MX@true\Mplace@\MX@false} ** The definition of *\Mplace@* begins with *\allowbreak*, ** \def\Mplace@{\allowbreak . . . ** just as with other *\...place*'s. Then, however, it is quite different, because we must calculate whether or not *\box\islandbox@* will fit on the current page, together with *\abovebotfigskip* glue above it (there is no need to consider the glue below the box, since it is allowed to disappear at a page break). Actually, *\abovebotfigskip* is merely supposed to be the minimum amount of glue before the box: if *\lastskip* is less than *\abovebotfigskip*, then we will *\unskip* before adding this glue, and if *\lastskip* is equal or greater to *\abovebotfigskip*, we will do nothing. Moreover, this glue will disappear if there is nothing else on the current page. We begin with ** \dimen@=\ht\islandbox@ \advance\dimen@ by \dp\islandbox@ \ifdim\pagetotal=0pt \else \ifdim \lastskip < \abovebotfigskip \advance\dimen@ by \abovebotfigskip \advance\dimen@ by -\lastskip \fi \fi ** so that *\dimen@* is the amount of space required for the box. Then we ** \advance\dimen@ by \pagetotal ** and use the test ** \ifdim\dimen@ > \pagegoal ** If this test is true, then there is {\it not\/} enough space for the box, so we use ** \Aplace@ ** to convert the *\Mplace* to an *\Aplace*. On the other hand, when the test ** \ifdim\dimen@ > \pagegoal ** is false, so that *\box\islandbox@* {\it does\/} fit, we basically want to treat the *\Mplace* like a *\place*, except for adding the requisite space: ** \nointerlineskip \ifdim\lastskip < \abovebotfigskip \removelastskip \vskip\abovebotfigskip \fi \setbox0=\vbox{\unvbox\islandbox@ \breakisland@} \printisland@ ** Unlike the situation for *\bottomfigs@*, which simply adds things to the\linebreak *\vbox* created by *\pagebody*, the *\nobreak* appearing in the definition of\linebreak *\printisland@* is required here,\pagelabel{NIREQ} when we are simply contributing things to the main vertical list. Now we have to make sure that no later *\Aplace* will end up at the top of this page, and hence out of order. We handle this in much the same way as *\Bplace*, by essentially *\Aplace*'ing a box of height *-\belowtopfigskip* if *\topinscount@* is~*0*: ** \ifnum\topinscount@=0 \setbox0=\vbox{\vbox to-\belowtopfigskip{}} \dimen@ = -\skip\topins \ht0 = \dimen@ \storedim@ \advancedimtopins@ \insert\topins{\box0} \global\advance\topinscount@ by 1 \xdef\AAlist@{\AAlist0} \fi ** Finally, suppose that *\ifMX@* is true. Then we are considering an *\MXplace*, which means that we want to prohibit an *\island* from appearing at the bottom of the page. To do this, we essentially want to *\Aplace* a box of height *-\abovebotfigskip* if *\topinscount@* is~*1*. (This box of negative height will be contributed to the page by *\bottomfigs@*,\pagelabel{NEGB} a circumstance alluded to on page~\ref{USENEGB}.) So we start with ** \setbox0=\vbox{\vbox to-\abovebotfigskip{}} ** In this case, making the height of *\box0* bigger by *\abovebotfigskip* is just the same as making it *0pt*, and we set *\dimen@* to this value before the *\storedim@*: ** \ht0=0pt \dimen@=0pt \storedim@ \advancedimtopins@ \insert\topins{\box0} \global\advance\topinscount@ by 1 \xdef\AAlist@{\AAlist@0} ** And after all that, we add ** \nointerlineskip \vskip\belowtopfigskip ** below our *\Mplace*'d *\island*: \C** \def\Mplace@{\allowbreak "2 \dimen@=\ht\islandbox@ \advance\dimen@ by \dp\islandbox@ "2 \ifdim\pagetotal=0pt \else \ifdim\lastskip < \abovebotfigskip \advance\dimen@ by \abovebotfigskip \advance\dimen@ by -\lastskip \fi \fi "2 \advance\dimen@ by \pagetotal "2 \ifdim\dimen@ > \pagegoal \Aplace@ "2 \else \nointerlineskip \ifdim\lastskip < \abovebotfigskip \removelastskip \vskip\abovebotfigskip \fi "2 \setbox0=\vbox{\unvbox\islandbox@ \breakisland@}% "2 \printisland@ "2 \ifnum\topinscount@=0 \setbox0=\vbox{\vbox to-\belowtopfigskip{}}% "2 \dimen@=-\skip\topins \ht0=\dimen@ "2 \storedim@ \advancedimtopins@ "2 \insert\topins{\box0}% \global\advance\topinscount@ by 1 \xdef\AAlist@{\AAlist@0}% \fi "2 \ifMX@ \ifnum\topinscount@=1 "2 \setbox0=\vbox{\vbox to-\abovebotfigskip{}}% \ht0=0pt "2 \dimen@=0pt \storedim@ "2 \advancedimtopins@ \insert\topins{\box0}% \global\advance\topinscount@ by 1 \xdef\AAlist@{\AAlist@0}% \fi \fi \nointerlineskip \vskip\belowtopfigskip \fi} ** \section{\CS{endPar}\label{ENDPAR}} An *\Aplace*, *\AAplace*, or *\Bplace* within a *\Par"allowbreak..."allowbreak\endPar* region simply stores the corresponding *\box\islandbox@* in some `*\Parfigbox*\,{\it n}' and the letter *a*, *A*, or *b* in *\Parlist@* (section~\Sref{place}), while a *\Cplace*, *\Mplace*, or *\MXplace* stores the *\box\islandbox@*, and the letter *c*, *m* or *M*, and also adds a *\vadjust{\break}* within the *\box\Parbox@* that we are setting (section~\Sref{PLACE}). When we get to the *\endPar* that matches the *\Par*, we will first supply the *\egroup* that ends the setting of *\box\Parbox@*, ** \def\endPar{\egroup . . . ** The remaining task is to take the material in *\box\Parbox@* and restructure it as if all the *\Cplace*'s, *\Mplace*'s and *\MXplace*'s actually occurred between paragraphs. The idea is to use *\vsplit* to take *\box\Parbox@* apart, splitting it after the lines where *\vadjust{\break}*'s were added at these *\...place*'s, and treat the pieces as separate paragraphs. The pieces into which we split *\box\Parbox* will be stored in new boxes that we declare: \C** \expandafter\newbox\csname Parbox1\endcsname \expandafter\newbox\csname Parbox2\endcsname \expandafter\newbox\csname Parbox3\endcsname \expandafter\newbox\csname Parbox4\endcsname \expandafter\newbox\csname Parbox5\endcsname ** To split *\box\Parbox@*, we use a *\loop*, ** \count@=1 \loop \ifnum\count@ < \Parcount@ ** This *\loop* will be enclosed in a group where we have set ** \vfuzz=\maxdimen \vbadness=10000 ** so that *Overfull* and *Underfull* *\vbox*'es will not be reported. In this group we will also set *\splitmaxdepth* to *\maxdimen*, since we already know where our boxes will split, and don't want to impose any extraneous constraints on their depths, and we will set *\splittopskip=\ht\strutbox*, so that in essence a strut will have been added at the beginning of each box.\pagelabel{VFUZZ} Before beginning the *\loop*, we will ** \setbox0=\vsplit\Parbox@ to\ht\Parbox@ ** Because of the *\break* at the beginning of *\box\Parbox@* (page~\ref{NBREAK}), this simply splits off the *\break* and the following *\parskip* glue into *\box0* (which will not be used), so that the remaining *\box\Parbox@* doesn't begin with extraneous glue. Now we will continue to ** \vsplit\Parbox@ to \ht\Parbox@ ** storing the results in `*\Parbox1*', `*\Parbox2*', \dots. The *\vadjust{\break}*'s that we inserted by *\Cplace*, *\Mplace*, or *\MXplace* will force these new (\,*Underfull*) boxes to end after the line in which this *\...place*'s appeared. At the same time, we want to store the depth of `*\Parbox1*' in the \ register `*\Parprev2*', the depth of `*\Parbox2*' in `*\Parprev3*', \dots\ (recall, page~\ref{PARPREV}, that `*\Parprev1*' is used to store the depth of the line before the *\Par* begins): ** \loop \ifnum\count@ < \Parcount@ \expandafter\expandafter\expandafter\global \expandafter\setbox \csname Parbox\number\count@\endcsname =\vsplit\Parbox@ to \ht\Parbox@ \count@@=\count@ \advance\count@@ by 1 \global\csname Parprev\number\count@@\endcsname =\dp\csname Parbox\number\count@\endcsname \advance\count@ by 1 \repeat ** When this *\loop* is over, *\box\Parbox@* will contain the text after the line containing the last *\...place* (or will be void if there was no such text). Even though we need to *\global\setbox*`*\Parbox*\,{\it n}', the *\vsplit* operation defines the remainder of the split *\box\Parbox@* globally, so the final *\box\Parbox@* will persist after the end of the \pagelabel{PERSIST} group. \footnote{This does not seem to be stated explicitly anywhere in {\it The \tex book}, though page~259 deals with related matters.} Having finished with this, we now insert the *\parskip* glue, and get ready for another *\loop* to put things back: ** \vskip\parskip \count@=1 ** For this loop we {\advance\litindent-20pt first define a routine ** \def\nextv@##1##2\nextv@{\def\next@{##1}\gdef\Parlist@{##2}} ** that} extracts the first letter from *\Parlist@* and stores it in *\next@*. \footnote{We chose *\nextv@*, which appears nowhere else in \lamstex\ except in the definition of *\root*, to make certain that this will not be redefined by any of the routines to follow.} Our *\loop* has several stages: \list \item We begin ** \loop \ifnum\count@ < \Parcount@ \dimen@=\csname Parprev\number\count@\endcsname \advance\dimen@ by \ht\strutbox \ifdim\dimen@ < \baselineskip \advance\dimen@ by -\baselineskip \vskip -\dimen@ \else \vskip\lineskip \fi ** Thus, we first set *\dimen@* to be the depth of the previous line plus the height of a strut, which will be the height of the first line of the next piece of text (unless this contains some extra tall symbol). If *\dimen@* is less than *\baselineskip*, then we want to insert \setbox0\hbox{*\baselineskip*} \setbox1\hbox{*\dimen@*} $$ \box0-\box1 $$ extra glue, or equivalently, \setbox0\hbox{*\baselineskip*} \setbox1\hbox{*\dimen@*} $$ -(\box1-\box0) $$ hence the code ** \advance\dimen@ by -\baselineskip \vskip -\dimen@ ** Otherwise, the two lines are already too far apart for *\baselineskip* glue to be used, so we just add the *\lineskip* glue. \item Next we ** \unvbox\csname Parbox\number\count@\endcsname ** to put the next part of the text on the vertical list; recall (see the footnote on page~\ref{NEXG}), that no extra space is added before or after this material, and that its depth does not influence *\prevdepth*, which is why we have stored the various depths in registers, and have added the extra space between these various pieces by hand.\pagelabel{USEFN} \item The next step is to deal with the corresponding *\...place*'d material, stored in ** \box\csname Parfigbox\number\count@\endcsname ** We first move this material into *\box\islandbox@*, ** \global\setbox\islandbox@ =\box\csname Parfigbox\number\count@\endcsname ** and then use ** \expandafter\nextv@\Parlist@\nextv@ ** so that *\next@* will have the value \setbox6\hbox{\ \ if the material was *\...place*'d with\ \ } \Math \vbox{\halign{#\hfil&\copy6 #\hfil\cr *a*&*\Aplace*\cr *A*&*\AAplace*\cr *b*&*\Bplace*\cr *c*&*\Cplace*\cr *m*&*\mplace*\cr *M*&*\Mplace*\cr}}\endMath Then we simply pretend that we are performing the corresponding *\...place* on *\box\islandbox@* at this point (where we are in vertical mode): ** \if a\next@\Aplace@\else \if A\next@\AAplace@\else \if b\next@\Bplace@\else \if c\next@\Cplace@\else \if m\next@\Mplace@\else \if M\next@\MXplace@\fi\fi\fi\fi\fi\fi ** After all this is done we can reset *\ifPar@* to be false. \item If the *\Par"allowbreak..."allowbreak\endPar* region happened to end with some sort of *\...place*, our final *\box\Parbox@* will be void, and we want to set ** \prevdepth=\csname Parpev\number\count@\endcsname ** so that *\prevdepth* has the value for the last line of the last piece of text that was *\unvbox*'ed. Otherwise, we first have to add glue before this final piece, as before, ** \dimen@=\csname Parprev\number\count@\endcsname \advance\dimen@ by \ht\strutbox \ifdim\dimen@ < \baselineskip \advance\dimen@ by -\baselineskip \vskip -\dimen@ \else \vskip\lineskip \fi ** and then store the depth of this remaining *\box\Parbox@* before *\unvbox*'ing it, so that we can finally set *\prevdepth* to that depth: ** \dimen@=\dp\Parbox@ \unvbox\Parbox@ \prevdepth=\dimen@ ** \endlist Our whole definition reads: \C** \def\endPar{\egroup "2 \count@=1 "2 {\vbadness=10000 \vfuzz=\maxdimen \splitmaxdepth=\maxdimen \splittopskip=\ht\strutbox "2 \setbox0=\vsplit\Parbox@ to\ht\Parbox@ "2 \loop \ifnum\count@ < \Parcount@ \expandafter\expandafter\expandafter\global \expandafter\setbox \csname Parbox\number\count@\endcsname =\vsplit\Parbox@ to\ht\Parbox@ "2 \count@@=\count@ \advance\count@@ by 1 \global\csname Parprev\number\count@@\endcsname =\dp\csname Parbox\number\count@\endcsname "2 \advance\count@ by 1 \repeat}% "2 \vskip\parskip \count@=1 "2 \def\nextv@##1##2\nextv@{\def\next@{##1}% \gdef\Parlist@{##2}}% \loop \ifnum\count@ < \Parcount@ "2 \dimen@=\csname Parprev\number\count@\endcsname \advance\dimen@ by \ht\strutbox "2 \ifdim\dimen@ < \baselineskip \advance\dimen@ by -\baselineskip \vskip -\dimen@ \else \vskip\lineskip \fi "2 \unvbox\csname Parbox\number\count@\endcsname "2 \global\setbox\islandbox@ =\box\csname Parfigbox\number\count@\endcsname \expandafter\nextv@\Parlist@\nextv@ "2 \if a\next@\Aplace@\else \if A\next@\AAplace@\else "2 \if b\next@\Bplace@\else \if c\next@\Cplace@\else "2 \if m\next@\Mplace@\else \if M\next@\MXplace@\fi\fi\fi\fi\fi\fi "2 \advance\count@ by 1 \repeat "2 \global\Par@false "2 \ifvoid\Parbox@ \prevdepth=\csname Parprev\number\count@\endcsname "2 \else \dimen@=\csname Parprev\number\count@\endcsname \advance\dimen@ by \ht\strutbox "2 \ifdim\dimen@ < \baselineskip \advance\dimen@ by -\baselineskip \vskip -\dimen@ "2 \else \vskip\lineskip \fi "2 \dimen@=\dp\Parbox@ \unvbox\Parbox@ \prevdepth=\dimen@ \fi} ** \chapter The \CS{output} routine\\ Ta-ran-ta-ra! Ta-ran-ta-ra! Ta-ran-ta-ra!\label{OUTPUT}\endchapter \runningchapter{The \CS{output} routine} The \lamstex\ default *\output* routine mimics as closely as possible the *plain* \tex\ *\output* routine, defined in terms of *\makeheadline*, *\pagebody*, *\makefootline*, etc. To begin with, we redefine *\folio* to reflect \lamstex's approach to page numbers (and numbering in general): \C** \def\folio{{\page@F \page@S{\page@P\pag@N{\number\page@C}\page@Q}} ** Since we are not using negative values of *\pageno* to indicate roman numerals, we might as well also simplify the definition of *\advancepageno*: \C** \def\advancepageno{\global\advance\pageno by 1 } ** \section{\CS{plainoutput}\label{SO}} Since *plain* \tex\ sets *\output={\plainoutput}*, our new *\output* routine will be specified by modifying *\plainoutput*, which is defined in *plain* \tex\ by ** \def\plainoutput{\shipout\vbox{\makeheadline \pagebody\makefootline} \advancepagno \ifnum\outputpenalty > -20000 \else\dosupereject\fi} ** We will need a new flag ** \newif\ifspecialsplit@ ** which should be set true when *\box\topins* is ** \vbox(0.0+0.0)x0.0 ** because of a split floating insertion (section~\Sref{FLOAT}). At the very beginning of the definition of *\plainoutput* (even before the *\fliptopins@*), we will use the code ** \specialsplit@false \ifvoid\topins\else\ifdim\ht\topins=0pt \specialsplit@true \fi\fi ** to set *\ifspecialsplit@* to be true only if *\box\topins* has height *0pt*, but isn't void (the latter test must be made explicitly because a void *\box\topins* will also satisfy the condition *\ifdim\ht\topins=0pt*), and thus precisely when (section~\Sref{FLOAT}) our *\box255* is too high by *-\skip\topins* ($={}$*3pt*), in which case we will eventually have to prune down *\box255*. Remember that we are going to be using code like ** \ifdim\ht255 < \minpagesize \else\unvbox255 \fi ** so that we are not going to *\unvbox255* at all when the test \Math \hbox{*\ht255*} < \hbox{*\minpagesize*} \endMath is true. But when *\ifspecialsplit@* is true, we really want the test \Math \hbox{*\ht255*} < \hbox{*\minpagesize*} - \hbox{*\skip\topins*} \endMath ({\bf0}) To achieve this, we will simply use ** \specialsplit@false \ifvoid\topins\else\ifdim\ht\topins=0pt \specialsplit@true \advance\minpagesize by -\skip\topins \fi\fi ** (*\minpagesize* will be restored to its usual value at the end of the *\output* routine, since \tex\ implicitly encloses the *\output* routine within a group.) After this step, we follow the procedure outlined on page~\ref{OUTLIST}: \medskip ({\bf1}) We use *\fliptopins@*, to prepare *\box\topins* for use by\linebreak *\pagecontents*, which will take care of properly positioning any *\island*'s that have shown up in *\box\topins*. ({\bf2}) Instead of *plain* \tex's direct *\shipout*, we will declare a new box ** \newbox\outbox@ ** and also ** \let\shipout@=\shipout ** and then ** \setbox\outbox@=\vbox{\makeheadline\pagebody\makefootline} {\noexpands@\let\style=\relax \shipout@\box\outbox@} ** The *\noexpands@* and *\let\style=\relax* are necessary before the\linebreak *\shipout@* because various *\write*'s in *\pagebody* will contain page numbering control sequences, whose expansion we want to prohibit (compare pages~\ref{SUB1} and \ref{SUB99}), and they might also contain *\style* (pages~\ref{SUB11} and ~\ref{SUB111}). \small The only reason for this approach is that it makes the *\output* routine easier to modify in certain ways. For example, if we want a style file that produces ``crop marks'' around the completed pages (as was done for the \lamstex~Manual, and for this manual), instead of redefining *\plainoutput*, we just have to define a suitable routine ** \def\crop#1{\vbox{ ... #1 ... }} ** which puts crop marks around a box *#1*, and then ** \def\shipout@{\shipout\crop{\box\outbox@}} ** \endsmall ({\bf3})--({\bf6}) Then come\pagelabel{PPUNV255} ** \advancepageno \resetdimtopins@ \ifvoid 255 \else \unvbox255 \penalty\outputpenalty \ifnum\outputpenalty > -20000 \else \dosupereject \fi ** The whole definition is: \C** \newif\ifspecialsplit@ \newbox\outbox@ \let\shipout@=\shipout "slip \def\plainoutput{\specialsplit@false "8DEFPLOUT"8 \ifvoid\topins\else \ifdim\ht\topins=0pt \specialsplit@true \advance\minpagesize by -\skip\topins \fi\fi \fliptopins@ \setbox\outbox@=\vbox{\makeheadline \pagebody\makefootline}% {\noexpands@ \let\style=\relax \shipout@\box\outbox@}% \advancepageno \resetdimtopins@ \ifvoid 255 \else \unvbox 255 \penalty\outputpenalty \fi \ifnum\outputpenalty > -20000 \else \dosupereject \fi} ** Notice that the *\noexpands@...\shipout@* occurs after *\box\outbox@* is set, so any font control sequences in *\headline* or *\footline* will not be *\relax* at that time. \footnote{This is a somewhat unnecessary precaution, since *\headline* and *\footline* normally contain things like *\tenrm* or *\tenit*, rather than *\rm* or *\it*, which may have different values if a different pointsize command is currently in effect; but *\tenpoint* or *\tenpoint\it* would be equally valid.} We also put the *\noexpands@...\shipout@* inside a group. In this particular definition, that extra group is irrelevant, but it would be important if the *\output* routine were modified to add additional material at the top of the next page, before the *\unvbox255* material. For example, in the index, continuation lines are added containing `*{\it continued\/}* (see page~\ref{CONLINES}), and we don't want *\it* to be *\relax* when these lines are added.\pagelabel{SHIPOUTGROUP} \section{\CS{pagebody}} The default \lamstex\ style keeps *\makeheadline* and *\makefootline* the same as in *plain*~ \tex@, and *\pagebody* is the same except that any index entries placed in the *\margin@* insertion class, for proofing, are placed at the side: \C** \def\pagebody{\vbox to\vsize{\boxmaxdepth=\maxdepth \ifvoid\margin@ "2 \else "2 \rlap{\kern\hsize\vbox to0pt{\kern4pt\box\margin@\vss}}% \fi \pagecontents} ** The *\kern\hsize* moves the entries over to the right side of the page, the *\rlap* allows them to stick into the right margin, the *\vbox to0pt* allows them to be arbitrarily long, and the *\kern4pt*, chosen empirically, starts the entries slightly below the top line, to look better. \section{\CS{pagecontents}\label{PAGECT2}} The only thing left to define is *\pagecontents*, for which we have given a preliminary definition on page~\ref{PAGECT}, with the modification of section~\Sref{CPCT}. We will need one final flag \C** \newif\ifonlytop@ ** which will be set true when the only thing on the page is a figure on top. The definition of *\pagecontents* begins ** \def\pagecontents{\onlytop@false \ifdim\ht255 < \minpagesize \ifnum\flipcount@ < 2 \ifvoid\footins \onlytop@true \fi\fi\fi ** Thus, we set *\onlytop@true* only when three conditions all hold: (1)~*\ht255* is less than *\minpagesize*, so that we will not *\unvbox255* after the top figure; (2)~*\flipcount@* is less than~*2*, so that there are no figures to go after the one on top; (3)~*\box\footins* is void, so that there are no footnotes to go after the figure on top either. The main change in *\pagecontents* is that special work may be needed if *\flipcount@* has the value~*1* and *\ifC@* is true, so that there is just one *\island* in *\box\topins* and it was *\Cplace*'d; in this case, *\Cdim@* has the value of *\pagetotal* at the time of the *\Cplace*, so that *\Cdim@* is the amount of text preceding the *\Cplace*. We will want to change the placement of this *\island* from top to bottom if *\Cdim@* is greater than half *\ht255* (i.e., if the *\island* appears in the lower half of the page). To test for this we use ** \test@false \ifC@ "2 \ifnum\flipcount@=1 \global\multiply\Cdim@ by 2 "2 \ifdim\Cdim@ > \ht255 \test@true \fi \fi \fi \global\C@false ** (taking the opportunity to reset *\ifC@* as soon as the test has been made). ({\bf a}) When *\iftest@* is true, we basically want to put the *\island* below the rest of the page instead of at the top, ** \iftest@ \unvbox255 \setbox\topins=\vbox{\unvbox\topins \global\setbox0=\lastbox} \setbox0=\vbox{\unvbox0 \breakisland@} \nointerlineskip \vskip\abovebotfigskip \printisland@ ** But this doesn't quite work, since there is really just enough space for the *\vskip\belowtopfigskip*, not for the *\vskip\abovebotfigskip* (which might differ considerably in some styles). So we will first set ** \dimen@=\ht 255 \advance\dimen@ by \skip\topins ** so that *\dimen@* is *\ht255* plus the difference in the space we are going to add (page~\ref{EXTRAS22}), and then replace *\unvbox 255* with ** \setbox1=\vsplit 255 to \dimen@ \unvbox1 ** (anything remaining in *\box255* will be put back on the main vertical list later). During the *\vsplit* we want to set *\vfuzz=\maxdimen*, *\vbadness=10000*, *\splitmaxdeth=\maxdepth*, and *\splittopskip=\topskip*, as in the definition of *\endPar* (page~\ref{VFUZZ}): ** {\vfuzz=\maxdimen \vbadness=10000 \splitmaxdepth=\maxdepth \splittopskip=\topskip \setbox1=\vsplit 255 to \dimen@ \unvbox1} ** The new value of *\box255* will persist after the end of the group (see page~\ref{PERSIST}). \small If *\skip\topins* is negative, i.e., if *\belowtopfigskip* is greater than\linebreak *\abovebotfigskip*, our *\vsplit* simply gives the original *\box255*, and we just have to hope that the glue can stretch enough to make up the difference. Compare section~\Sref{255TOO}. \endsmall And then, having put the proper amount of *\box255* at the top of the page, we add the *\island* in *\box\topins*, with the proper amount of space above it: ** \setbox\topins=\vbox{\unvbox\topins \global\setbox0=\lastbox} \setbox0=\vbox{\unvbox0 \breakisland@} \nointerlineskip \vskip\abovebotfigskip \printisland@ ** ({\bf b}) When *\iftest@* is false (the usual case), we simply put the first *\island* in *\box\topins*, if any, at the top, ** \ifnum\flipcount@ > 0 \setbox\topins=\vbox{\unvbox\topins \global\setbox0=\lastbox}% \setbox0=\vbox{\unvbox0 \breakisland@}% \printisland@ ** Normally, this should be followed by *\belowtopfigskip*. But we don't want to do that if nothing else goes on the page, because the figure may be too large to allow *\belowtopfigskip* glue below it. Instead we use ** \ifonlytop@ \kern-\prevdepth \vfill \else \vskip\belowtopfigskip \fi ** The *\kern-\prevdepth* is inserted for the following reason.\pagelabel{KMPR} \footnote{Compare the `*\ifr@ggedbottom\kern-\dimen@\vfil\fi*' in the *plain* \tex\ definition of *\pagecontents* ({\it The \tex book}, page~364).} We might have an island whose height is less than or equal to *\vsize*, but whose height plus depth exceeds *\vsize*. Since *\pagecontents* will become \setbox0\hbox{\} ** \vbox to\vsize{"box0 \vfill} ** this will give an *Overfull \vbox* (without the *\vfill* we wouldn't have this problem, because the depth of the *\island* would just become the depth of the *\vbox to\vsize*). The `*\kern-\prevdepth*' eliminates this extra depth, so that *\vfill* can safely be added. Our preliminary definition of *\pagecontents* then had ** \ifdim\ht255 < \minpagesize \vfill \else \unvbox255 \fi ** but now we will have to prune down *\box255* when *\ifspecialsplit@* is true. This is similar to the routine used for a *\Cplace*: ** \ifspecialsplit@ "8DOSPECSPLIT"8 {\vfuzz=\maxdimen \vbadness=10000 \splitmaxdepth=\maxdepth \splittopskip=\topskip \dimen@ii=\ht255 \advance\dimen@ii by \skip\topins \setbox1=\vsplit255 to\dimen@ii \unvbox1}% \else \unvbox255 \fi ** We might as well also replace the ** \ifdim\ht255 < \minpagesize \vfill ** with ** \ifdim\ht255 < \minpagesize \ifonlytop@ \else \vfill \fi ** so that a page containing a single large figure will have *\vfill* below it rather than two *\vfill*'s (this just might make a difference if some one were trying to reposition things). Finally, having taken care of the text, we just have to add *\bottomfigs@*, and the footnote material, if any: \C** \def\pagecontents{\onlytop@false "8FINALPC"8 \ifdim\ht255 <\minpagesize \ifnum\flipcount@ < 2 \ifvoid\footins \onlytop@true \fi\fi\fi "2 \test@false "2 \ifC@ \ifnum\flipcount@=1 \global\multiply\Cdim@ by 2 \ifdim\Cdim@ > \ht255 \test@true \fi \fi \fi "2 \global\C@false "2 \iftest@ \dimen@=\ht255 \advance\dimen@ by \skip\topins "2 {\vfuzz=\maxdimen \vbadness=10000 \splitmaxdepth=\maxdepth \splittopskip=\topskip \setbox0=\vsplit255 to\dimen@ \unvbox0}% "2 \global\setbox\topins=\vbox{\unvbox\topins \global\setbox1=\lastbox}% \setbox0=\vbox{\unvbox1 \breakisland@}% "2 \nointerlineskip \vskip\abovebotfigskip \printisland@ "2 \else \ifnum\flipcount@ > 0 "2 \global\setbox\topins=\vbox{\unvbox\topins \global\setbox1=\lastbox}% \setbox0=\vbox{\unvbox1 \breakisland@}% \printisland@ "2 \ifonlytop@ \kern-\prevdepth \vfil \else \vskip\belowtopfigskip \fi \fi \fi "2 \ifdim\ht255 < \minpagesize \ifonlytop@ \else \vfill \fi "2 \else "2 \ifspecialsplit@ {\vfuzz=\maxdimen \vbadness=10000 \splitmaxdepth=\maxdepth \splittopskip=\topskip \dimen@ii=\ht255 \advance\dimen@ii by \skip\topins \setbox0=\vsplit255 to\dimen@ii \unvbox0}% "2 \else \unvbox255 \fi \fi \bottomfigs@ \ifvoid\footins\else \vskip\skip\footins\footnoterule\unvbox\footins\fi} ** \small When *\vskip\topins* is small we might elect simply to forget about the flag\linebreak *\ifspecialsplit@*, and always *\unvbox255* when its height is greater than or equal to *\minpagesize*. Only very very rarely would we encounter difficulties, and these could always be fixed by hand---after all, nothing is perfect (compare section~\ref{FWARN}). \endsmall \nopunct\section{And we are done!\label{DONE}}This finishes off everything concerned with the first part of the \lamstex~Manual, except for front and back matter, which are dealt with in Part~\ref{FBM}@. Commutative diagrams are dealt with in Volume~II@, but all that material could just as well be considered as separate files that we could *\input*. And all the material for tables, except for a few things like *\paste* and *\measuretable* are actually in a separate file (except in \muttontex); these are also dealt with in Volume~II@. So let us now skip to the very end of the *lamstex.tex* file. First, we introduce the more formal *\enddocument* as a synonym for *\bye*: \C** \let\enddocument=\bye ** Then, since we have no *\new...* constructions to use, we restore *\alloc@* to its original *plain* meaning (see page~\ref{NEWALLOC2}), so that any further uses of the *\new...* constructions will write to the *.log* file: \C** \def\alloc@#1#2#3#4#5{\global\advance\count1#1by\@ne \ch@ck#1#4#2\allocationnumber=\count1#1 \global#3#5=\allocationnumber \wlog{\string#5=\string#2\the\allocationnumber}} ** And finally, we make *@* active: \C** \catcode`\@=\active ** \sectiong{When \CS{box255} is too small\label{255TOO}} At the end of section~\Sref{FLOAT}, we mentioned the fact that *\box255* will be too small, rather than too big, when *\belowtopfigskip* is greater than *\abovebotfigskip*, so the splitting maneuvers used in defining *\pagecontents* won't do us any good. Fortunately, that situation is unlikely to occur, since good style design calls for more space to be left above constructions than below them. (This holds for headers also; they look better when the space above exceeds the space below, something that more \tex\ style file designers should bear in mind.) I don't see any easy way to deal with the problem of a style that chooses a value of *\belowtopfigskip* greater than *\abovebotfigskip*. The best I can suggest in that case is the following. First of all, *\Aplace* will also have to be disallowed within paragraphs (except for special *\Par"allowbreak..."allowbreak\endPar* regions, which reduce back to use between paragraphs). Then, when we *\Aplace* an *\island* we would make a special check if *\topinscount@* is ~*0* (so that this is the first *\island* to be considered for the page). In this case, we would use the same calculations as for *\Mplace*, to see if the *\island* will fit. If it does fit, we would simply insert it (essentially converting the *\Aplace* to an *\Mplace*). If it doesn't fit, so that it will have to float, we would set ** \skip\topins=0pt ** so that *\box255* for this page will have just the right size. Finally, we would add \setbox0\hbox{\} ** \global\skip\topins="box0 ** at the end of the *\output* routine. \endsmall \sectiong{The endgame}Although the *\dosupereject*'s in the *\output* routine ensure that all *\Aplace*'d material will eventually make its way onto the page, some of this material may have to be printed after all the other text, with the final pages consisting entirely of *\island*'s. These *\island*'s will still be allocated only two to a page, so it will probably be necessary to change some of the final *\Aplace*'s to *\Mplace*'s or *\AAplace*'s in order to improve the appearance of these final pages. There doesn't seem to be any easy way to have \lamstex\ do this automatically. We might try to change *\dimen\topins* to *\vsize* when we have run out of text (i.e., when it is no longer true that \hbox{*\outputpenalty > -20000*}), but this change would be made too late---after \tex\ has already decided not to include some members of the *\topins* insertion class in *\box\topins*, since that decision is made at the time of the *\insert*. One possibility is to have a box, say *\box\remainingplaces@*, in which we store copies of all the *\...place*'d *\islands*, eliminating copies as *\pagecontents* uses up boxes in *\box\topins*. Then, when we run out of text, we would change *\pagecontents* so that it ignores *\box\topins*, and instead takes things from *\box\remainingplaces@*, putting as many on the page as will fit. At this stage we would change the definition of *\dosupereject* from ** \ifnum\insertpenalties > 0 \line{}\kern-\topskip\nobreak\vfill\supereject\fi ** to ** \ifvoid\remainingplaces@ \else \line{}\kern-\topskip\nobreak\vfill\supereject\fi ** \endsmall \sectiong{The endgame once again\label{FLUSHSEC}} In addition, there is one minor defect that is encountered even in *plain* \tex@. The *plain* \tex\ file ** \vsize=22pt \hsize=2in \raggedright Here is some stuff, taking up more than a line. \pageinsert \hbox{A}\vfil\hbox{B}\endinsert \bye ** will produce {\it three\/} pages of output: The text will all occur on page~1; the ** \vbox to\vsize{\hbox{A}\vfil\hbox{B}} ** will take up page~2; and page~3 will be {\it blank\/} (except for the page number). If we add ** \def\plainoutput{% \showbox255 \showbox\topins \showthe\outputpenalty \shipout\vbox{\makeheadline\pagebody\makefootline}% \advancepageno \ifnum\outputpenalty>-20000 \else\dosupereject\fi} ** at the beginning of this file (compare page~\ref{SHOWBOX}), the *.log* file will show that before page~*[1]* is shipped out *\box255* contains the text, *\box\topins* is ** > \box253= \vbox(0.0+0.0)x0.0 ** (because of the split insert) and the *\outputpenalty* is *10000* (set by \tex\ because the chosen breakpoint for the page was not a penalty item). Before page~*[2]* is shipped out, *\box255* will be an empty box, while *\box\topins* will now be the *\vbox to\vsize{\hbox{A}\vfil\hbox{B}}* (and the value of *\outputpenalty* will still be *10000*). Finally, before page~*[3]* is shipped out, we will have \Litbox0=** > \box255= \vbox(22.0+0.0)x144.54, glue set 12.0fill .\glue(\topskip) 10.0 .\hbox(0.0+0.0)x144.54 .\glue 0.0 plus 1.0fill ** $$ \vcenter{\box0} \tag"\style{\bf A}" $$ and *\outputpenalty* will be *-1073741824* ($={}$*-'10000000000*). As explained in {\it The \tex book}, page~264, \tex\ inserted the equivalent of ** \line{} \vfill \penalty -'1000000000 "8WEIRDPEN"8 ** into the main vertical list when it saw the *\end* from the *\bye* (apparently because this *\end* was seen before the text had been output, though I don't understand the details). The \lamstex\ file ** \vsize=22pt \hsize=2in \raggedright \minpagesize=22pt \Figureproofing Here is some stuff, taking up more than a line. \Aplace{\Figure \Hbyw{1in} \endFigure} \bye ** will also produce a blank third page, \footnote{If we left *\minpagesize* at~*5pc* we'd get infinitely many pages!} although the *.log* file will tell a somewhat different story if we add the appropriate *\show*'s to the definition of *\plainoutput* in *lamstex.tex*. Before page *[1]* is shipped out, *\outputpenalty* will be *-20000* (presumably from a *\dosupereject*). Before page~*[2]* is shipped out, *\box255* will be ** \vbox(-15.0+0.0)x0.0 ** (see page~\ref{NEG255}), and *\outputpenalty* will again be *-20000*. Before page~*[3]* is shipped out, *\outputpenalty* will again be *-20000*, and we will have \Litbox0=** > \box255= \vbox(22.0+0.0)x144.54, glue set 22.0fill .\glue(\topskip) 10.0 .\hbox(0.0+0.0)x144.54 .\kern -10.0 .\penalty 10000 .\glue 0.0 plus 1.0fill ** $$ \vcenter{\box0}\tag"\style{\bf B}" $$ This *\vbox*, somewhat different from ({\bf A}), has come from the material inserted by *\dosupereject*. [With some choices of \tex\ parameters, the *plain* \tex\ file may cause a page break to be taken before the *\pageinsert* is seen, so that we might end up with ({\bf B)} instead of ({\bf A}).] \medskip Such empty pages present a formidable problem if they occur at the end of a book chapter, rather than at the end of a complete document, since they can cause the next chapter to begin with the wrong page number. For such situations we can add a check for empty pages. For example, at the beginning of the \lamstex\ definition of *\plainoutput* we can test if *\box\footins* is void and *\box\topins* is either void or has height ~*0pt*, and if so we can then add ** \setbox0=\vbox{\unvcopy255 \unskip} \ifdim\ht0=0pt \global\advance\pageno by -1 \fi ** so that the blank page ({\bf B}) would simply have the same page number as the previous page. Since we might encounter the *plain* \tex\ situation ({\bf A}) also, the more complex test ** \setbox0=\vbox{\unvcopy255 "8BPGTEST"8 \unskip \unpenalty \unkern \global\setbox1=\lastbox \unskip} \ifdim\ht0=0pt \ifdim\ht1=0pt \global\advance\pageno by -1 \fi\fi ** is actually required. Of course, this test is memory-intensive, since we essentially have to keep two copies of *\box255* in memory at once. The book style (Chapter~\ref{BOOKSTYLE}) allows the user to type *\FlushedFigs* when this test is appropriate, but to avoid the test with *\NoFlushedFigs*.\pagelabel{FLUSHFIGS} \endsmall \sectiong{The endgame once again} \endsmall \sectiong{The endgame once again} \endsmall \sectiong{The endgame once again} \endsmall \sectiong{The endgame once again} \endsmall \sectiong{The endgame once again} \endsmall \tenrm \setbox0\hbox{*. . .*} \line{\hskip60pt\box0 \hfil} \bigskip \flushpar Finally, let's hope that our *\output* routine doesn't allow this to happen! \bigskip \sectiongg{A final warning\label{FWARN}} With all the subtleties involved in the \lamstex\ *\output* routine, it should perhaps be mentioned that sometimes even the *plain* \tex\ *\output* routine will produce incorrect results. On page~\ref{SGG}, the page might have been broken before the final \bigskip \noindent \hskip35pt where $$\align \hbox{V}_{\text1}&= \hbox{value of {\tt\bs ref\lcurly}\{\tt\rcurly}}\\ \hbox{V}_{\text2}&= \hbox{value of {\tt\bs Ref\lcurly}\{\tt\rcurly}}\\ \hbox{V}_{\text3}&= \hbox{value of {\tt\bs nref\lcurly}\{\tt\rcurly}}\\ \hbox{V}_{\text4}&= \hbox{value of {\tt\bs pref\lcurly}\{\tt\rcurly}} \endalign $$ \bigskip \noindent with a badness of only *295*. But \tex\ included this material on the page, and then reported {\litindent0pt an \setbox0\hbox{\,\,\bf!!} ** Overfull \vbox (0.22726pt too high) while \output is active"box0 ** I must have printed thousands, if not tens of thousands, of} pages of \tex\ output before I ever encountered this problem, but it can occur, so you might want to know what happened. \medbreak The footnote on the page has a height of *57.5pt* and a depth of *2.5pt*, and, as in *plain* \tex, *\skip\footins* was *12pt plus4pt minus4pt*. The thick footnote rule contributed no extra space [in *plain* \tex@, *\footnoterule* is ** \kern-3pt \hrule width2truein \kern2.6pt ** for a total height of *0pt*, and in this manual it was ** \kern-3pt \hrule height1pt width 2truein \kern2pt ** again having a total height of *0pt*]. At the time the footnote was considered, in the middle of the page, \tex\ carefully checked that the height plus depth of the footnote, plus the height plus depth of the page so far, plus *\skip\footins*, was less than the page goal, which was the *\vsize* of *480pt*. Since this was true, so that the insert fit, the goal {\it g\/} for the page was reduced by *72pt* [the height plus depth of the footnote plus the \ part of *\skip\footins*], to *408pt*. (The stretch and shrink part of *\skip\topins* was added to the page total~ {\it t\/}). After that, \tex\ considered the page total {\it t\/} each time it encountered a possible break point; this page total reflects only the {\it height\/} of the material so far, not its depth. At the point where the page was finally broken, \tex\ calculated that ** t=444.72726 plus 35.0 minus 37.0 g=408.0 ** with a badness of *97*. The `*minus 37.0*' meant that the page total could be shrunk down to *407.72726*, which seems to allow just enough room for the footnote in *\pagebody*: \Math \align \hbox{*407.72726pt*}+\hbox{*12pt*}+\hbox{*60pt*}&=\hbox{*479.72726pt*}\\ &<\hbox{*480pt*}\endalign \endMath But there are two flaws in this calculation: \list \item In the first place, when we make *\pagebody*, which is a *\vbox to\vsize*, the depth *2.5pt* of the footnote at the bottom of the box is irrelevant, since it does not contribute to the height of the box. Thus, we seem to have an extra *2.5pt* of breathing room. \item Unfortunately, the depth of *\box255*, which no longer appears at the bottom of the *\vbox to\vsize*, is {\it not\/} irrelevant, and must be added in. \endlist It happens that the depth of the display at the bottom of page~\ref{SGG} is *3pt* (there is a strut of depth ~*3pt* inserted into each line of \amstex's *\align*, which was used for the display). Consequently, the height of *\pagebody* is \Math \hbox{*407.72726pt*}+\hbox{*3pt*}+\hbox{*12pt*}+\hbox{*57.5pt*} =\hbox{*480.22726pt*} \endMath leading to the\ \ *Overfull \vbox (0.22726pt too high)*. \medskip (You can easily simulate this phenomenon with the *plain* \tex\ file ** \vsize=480pt \topskip=0pt \insert\footins{\hbox{\vrule height 57.5pt depth 2.5pt width 0pt}} %% the ``footnote'' \hbox{} \nointerlineskip \vskip0pt minus34pt %% the shrink \hbox{\vrule height 444.72726pt depth 3pt width 0pt} %% the ``text'' \bye ** which gives a single page with an\ \ *Overfull \vbox*, instead of two pages with a seriously\ \ *Underfull \vbox* on the first page. However, the two-page output will occur once the depth of the ``text'' exceeds *4pt*, the value of *\maxdepth*.) \medskip \noindent Moral: In theory, the *\output* routine really ought to examine the depth of *\box255* before blithely *\unvbox*'ing it, because it might prove necessary to prune down *\box255*, just as we did when *\ifspecialsplit@* was true (page~\ref{DOSPECSPLIT}). In practice, this refinement is probably just a terrible waste of time. It might become important when there is almost no stretch or shrink on a page (though one would presume that *\vskip\footins* would need to have some stretch and shrink in such cases). \endsmall