26
An environment for multicolumn output *† Frank Mittelbach Email: see top of the source file Printed January 24, 2000 Abstract This article describes the use and the implementation of the multicols environment. This environment allows switching between one and multicolumn format on the same page. Footnotes are handled correctly (for the most part), but will be placed at the bottom of the page and not under each column. L A T E X’s float mechanism, however, is partly disabled in the current implementation. At the moment only page-wide floats (i.e., star-forms) can be used within the scope of the environment. Preface to version 1.5 This new release contains two major changes: multicols will now support up to 10 columns and two more tuning possibilities have been added to the balanc- ing routine. The balancing rou- tine now checks the badness of the resulting columns and rejects solutions that are larger than a certain treshold. At the same time multicols has been upgraded to run under L A T E X2 ε . I apologise for the state of the code documentation but the work on L A T E X2 ε kept me too busy to do a proper job. This will hope- fully be corrected in the near fu- ture. 1 Introduction Switching between two column and one column layout is pos- sible in L A T E X, but every use of \twocolumn or \onecolumn starts a new page. More- over, the last page of two column output isn’t balanced and this often results in an empty, or nearly empty, right col- umn. When I started to write macros for doc.sty (see “The doc–Option”, TUGboat volume 10 #2, pp. 245–273) I thought that it would be nice to place the index on the same page as the bibliography. And balanc- ing the last page would not only look better, it also would save space; provided of course that it is also possible to start the next article on the same page. Rewriting the index environment was comparatively easy, but the next goal, designing an environ- ment which takes care of foot- notes, floats etc., was a harder task. It took me a whole week- end 1 to get together the few lines of code below and there is still a good chance that I missed some- thing after all. Try it and, hopefully, enjoy it; and please direct bug reports and suggestions back to Mainz. * This file has version number v1.5w, last revised 1999/10/21. Note: This package is released under terms which affect its use in commercial applications. Please see the details at the top of the source file. 1 I started with the algorithm given in the T E Xbook on page 417. Without this help a weekend would not have been enough. 1

An environment for multicolumn output…y

  • Upload
    others

  • View
    14

  • Download
    0

Embed Size (px)

Citation preview

An environment for multicolumn output∗†

Frank MittelbachEmail: see top of the source file

Printed January 24, 2000

Abstract

This article describes the use and the implementation of the multicols environment. This environmentallows switching between one and multicolumn format on the same page. Footnotes are handled correctly(for the most part), but will be placed at the bottom of the page and not under each column. LATEX’s floatmechanism, however, is partly disabled in the current implementation. At the moment only page-widefloats (i.e., star-forms) can be used within the scope of the environment.

Preface to version 1.5

This new release contains twomajor changes: multicols willnow support up to 10 columnsand two more tuning possibilitieshave been added to the balanc-ing routine. The balancing rou-tine now checks the badness of

the resulting columns and rejectssolutions that are larger than acertain treshold.

At the same time multicolshas been upgraded to run underLATEX2ε.

I apologise for the state of the

code documentation but the workon LATEX2ε kept me too busy todo a proper job. This will hope-fully be corrected in the near fu-ture.

1 Introduction

Switching between two columnand one column layout is pos-sible in LATEX, but every useof \twocolumn or \onecolumnstarts a new page. More-over, the last page of twocolumn output isn’t balancedand this often results in anempty, or nearly empty, right col-umn. When I started to writemacros for doc.sty (see “Thedoc–Option”, TUGboat volume

10 #2, pp. 245–273) I thoughtthat it would be nice to placethe index on the same page asthe bibliography. And balanc-ing the last page would not onlylook better, it also would savespace; provided of course thatit is also possible to start thenext article on the same page.Rewriting the index environmentwas comparatively easy, but thenext goal, designing an environ-

ment which takes care of foot-notes, floats etc., was a hardertask. It took me a whole week-end1 to get together the few linesof code below and there is still agood chance that I missed some-thing after all.

Try it and, hopefully, enjoy it;and please direct bug reports andsuggestions back to Mainz.

∗This file has version number v1.5w, last revised 1999/10/21.†Note: This package is released under terms which affect its use in commercial applications. Please see the details at the

top of the source file.1I started with the algorithm given in the TEXbook on page 417. Without this help a weekend would not have been enough.

1

2 The User Interface

To use the environment one sim-ply says

\begin{multicols}{〈number〉}〈multicolumn text〉

\end{multicols}

where 〈number〉 is the requirednumber of columns and 〈multi-column text〉 may contain arbi-trary LATEX commands, exceptthat floats and marginpars arenot allowed in the current imple-mentation2.

As its first action, the multicolsenvironment measures the cur-rent page to determine whetherthere is enough room for someportion of multicolumn out-put. This is controlled by the〈dimen〉 variable \premulticolswhich can be changed by theuser with ordinary LATEX com-mands. If the space is less than\premulticols, a new page isstarted. Otherwise, a \vskip of\multicolsep is added.3

When the end of the mul-ticols environment is encoun-tered, an analogous mechanismis employed, but now we testwhether there is a space largerthan \postmulticols available.Again we add \multicolsep orstart a new page.

It is often convenient to spreadsome text over all columns, justbefore the multicolumn output,without any page break in be-tween. To achieve this the multi-cols environment has an optionalsecond argument which can beused for this purpose. For exam-ple, the text you are now readingwas started with

\begin{multicols}{3}[\section{The User

Interface}] ...

If such text is unusually

long (or short) the value of\premulticols might need ad-justing to prevent a bad pagebreak. We therefore provide athird argument which can beused to overwrite the defaultvalue of \premulticols just forthis occasion. So if you wantto combine some longer singlecolumn text with a multicols en-vironment you could write

\begin{multicols}{3}[\section{Index}This index contains ...][6cm]

...

The space between columns iscontrolled by the length param-eter \columnsep. The widthfor the individual columns isautomatically calculated fromthis parameter and the current\linewidth. In this article avalue of 18.0pt was used.

Separation of columns withvertical rules is achievedby setting the parameter\columnseprule to some posi-tive value. In this article a valueof .4pt was used.

Since narrow columns tendto need adjustments in in-terline spacing we also pro-vide a 〈skip〉 parameter called\multicolbaselineskip whichis added to the \baselineskipparameter inside the multicols en-vironment. Please use this pa-rameter with care or leave italone; it is intended only forpackage file designers since evensmall changes might produce to-tally unexpected changes to yourdocument.

2.1 Balancing columns

Besides the previously mentionedparameters, some others are pro-vided to influence the layout ofthe columns generated.

Paragraphing in TEX is con-trolled by several parameters.One of the most important iscalled \tolerance: this controlsthe allowed ‘looseness’ (i.e. theamount of blank space betweenwords). Its default value is 200(the LATEX \fussy) which is toosmall for narrow columns. On theother hand the \sloppy declara-tion (which sets \tolerance to10000 = ∞) is too large, allow-ing really bad spacing.4

We therefore use a\multicoltolerance parameterfor the \tolerance value insidethe multicols environment. Itsdefault value is 9999 whichis less than infinity but ‘bad’enough for most paragraphsin a multicolumn environment.Changing its value should bedone outside the multicols envi-ronment. Since \tolerance isset to \multicoltolerance atthe beginning of every multicolsenvironment one can locallyoverwrite this default by as-signing \tolerance = 〈desiredvalue〉. There also exists a\multicolpretolerance pa-rameter holding the valuefor \pretolerance within amulticols environment. Bothparameters are usually used onlyby package designers.

Generation of multicolumnoutput can be divided into twoparts. In the first part we arecollecting material for a page,shipping it out, collecting mate-rial for the next page, and so on.As a second step, balancing will

2This is dictated by lack of time. To implement floats one has to reimplement the whole LATEX output routine.3Actually the added space may be less because we use \addvspace (see the LATEX manual for further information about this

command).4Look at the next paragraph, it was set with the \sloppy declaration.

2

be done when the end of the mul-ticols environment is reached. Inthe first step TEX might considermore material whilst finding thefinal columns than it actuallyuse when shipping out the page.This might cause a problem ifa footnote is encountered in thepart of the input considered, butnot used, on the current page. Inthis case the footnote might showup on the current page, while thefootnotemark corresponding tothis footnote might be set on thenext one.5 Therefore the multi-cols environment gives a warningmessage6 whenever it is unableto use all the material consideredso far.

If you don’t use footnotes toooften the chances of somethingactually going wrong are veryslim, but if this happens you canhelp TEX by using a \pagebreakcommand in the final document.Another way to influence the be-havior of TEX in this respectis given by the counter variable‘collectmore’. If you use the\setcounter declaration to setthis counter to 〈number〉, TEXwill consider 〈number〉 more (orless) lines before making its fi-nal decision. So a value of −1may solve all your problems atthe cost of slightly less optimalcolumns.

In the second step (balanc-ing columns) we have other bellsand whistles. First of all youcan say \raggedcolumns if youdon’t want the bottom lines tobe aligned. The default is\flushcolumns, so TEX will nor-mally try to make both thetop and bottom baselines of allcolumns align.

Additionally you can setanother counter, the ‘unbal-ance’ counter, to some positive〈number〉. This will make all butthe right-most column 〈number〉of lines longer than they wouldnormally have been. ‘Lines’ inthis context refer to normal textlines (i.e. one \baselineskipapart); thus, if your columnscontain displays, for example,you may need a higher 〈number〉to shift something from one col-umn into another.

Unlike ‘collectmore,’ the ‘unbal-ance’ counter is reset to zero atthe end of the environment so itonly applies to one multicols en-vironment.

The two methods may be com-bined but I suggest using thesefeatures only when fine tuningimportant publications.

Two more general tuning pos-sibilities were added with ver-sion 1.5. TEX allows to mea-sure the badness of a column interms of an integer value, where0 means optimal and any highervalue means a certain amountof extra white space. 10000 isconsidered to be infinitely bad(TEX does not distinguish anyfurther). In addition the specialvalue 100000 means overfull (i.e.,the column contains more textthan could possibly fit into it).

The new release now measuresevery generated column and ig-nores solutions where at leastone column has a badness be-ing larger than the value of thecounter columnbadness. The de-fault value for this counter is10000, thus TEX will accept allsolutions except those being over-full. By setting the counter to asmaller value you can force thealgorithm to search for solutionsthat do not have columns with a

lot of white space.However, if the setting is too

low, the algorithm may not findany acceptable solution at all andwill then finally choose the ex-treme solution of placing all textinto the first column.

Often, when colunms are bal-anced, it is impossible to find asolution that distributes the textevenly over all columns. If thatis the case the last column usu-ally has less text than the oth-ers. In the earlier releases thistext was stretched to produce acolumn with the same height asall others, sometimes resulting inreally ugly looking columns.

In the new release this stretch-ing is only done if the badnessof the final column is not largerthan the value of the counter fi-nalcolumnbadness. The defaultsetting is 9999, thus preventingthe stretching for all columnsthat TEX would consider in-finitely bad. In that case the fi-nal column is allowed to run shortwhich gives a much better result.

And there are two moreparameters of some exper-imental nature, one called\multicolovershoot the other\multicolundershoot. Theycontrol the amount of space acolumn is allowed to be “too full”or “too short” without affectingthe column badness. They areset to 2pt by default.

2.2 Not balancing thecolumns

Although this package was writ-ten to solve the problem of bal-ancing columns, I got repeatedrequests to provide a versionwhere all white space is auto-matically placed in the last col-umn or columns. Since version

5The reason behind this behavior is the asynchronous character of the TEX page builder. However, this could be avoidedby defining very complicated output routines which don’t use TEX primitives like \insert but do everything by hand. This isclearly beyond the scope of a weekend problem.

6This message will be generated even if there are no footnotes in this part of the text.

3

v1.5q this now exists: if youuse multicols* instead of theusual environment the columnson the last page are not balanced.Of course, this environment onlyworks on top-level, e.g., inside abox one has to balance to deter-mine a column height in absenseof a fixed value.

2.3 Manually breakingcolumns

Another request often voicedwas: “How to I tell LATEX thatit should break the first columnafter this particular line?”. The\pagebreak command (whichworks with the two-column op-tion of LATEX) is of no use heresince it would end the collectionphase of multicols and thus allcolumns on that page. So withversion 1.5u the \columnbreakcommand was added. If usedwithin a paragraph it marks theend of the current line as the de-sired breakpoint. You can ob-serve its effect on the previouspage where three lines of texthave been artifically forced intothe second column (resulting insome white space between para-graphs in the first column).

2.4 Floats inside a mul-ticols environment

Within the multicols environmentthe usual star float commandsare available but their function issomewhat different as in the two-column mode of standard LATEX.Stared floats, e.g., figure*, de-note page wide floats that arehandled in a similar fashion asnormal floats outside the multi-cols environment. However, theywill never show up on the pagewhere they are encountered. Inother words, one can influencetheir placement by specifying acombination of t, b, and/or pin their optional argument, but

h doesn’t work because the firstpossible place is the top of thenext page. One should also note,that this means that their place-ment behavior is determined bythe values of \topfraction, etc.rather then by \dbl....

2.5 Warnings

Under certain circumstances theuse of the multicols environmentmay result in some warnings fromTEX or LATEX. Here is a list of theimportant ones and the possiblecause:

Underfull \hbox (badness...)

As the columns are often verynarrow TEX wasn’t able to finda good way to break the para-graph. Underfull denotes a looseline but as long the badness val-ues is below 10000 the result isprobably acceptable.

Underfull \vbox ... while\output is active

If a column contains an charac-ter with an unusual depth, forexample a ‘(’, in the bottom linethen this message may show up.It usually has no significance aslong as the value is not morethan a few points.

LaTeX Warning: I movedsome lines to the nextpage

As mentioned above, multicolssometimes screws up the foot-note numbering. As a pre-caution, whenever there is afootnote on a page that wheremulticols had to leave a re-mainder for the following pagethis warning appears. Checkthe footnote numbering on thispage. If it turns out that itis wrong you have to manuallybreak the page using \newpageor \pagebreak[..].

Floats and marginpars notallowed inside ‘multicols’environment!

This message appears if you tryto use the \marginpar com-mand or an unstared version ofthe figure or table environment.Such floats will disappear!

2.6 Tracing the output

To understand the reasoning be-hind the decisions TEX makeswhen processing a multicols envi-ronment, a tracing mechanism isprovided. If you set the counter‘multicols’ to a positive 〈number〉you then will get some tracing in-formation on the terminal and inthe transcript file:

〈number〉 = 1. TEX will nowtell you, whenever it entersor leaves a multicols environ-ment, the number of columns itis working on and its decisionabout starting a new page be-fore or after the environment.

〈number〉 = 2. In this caseyou also get information fromthe balancing routine: theheights tried for the left andright-most columns, informa-tion about shrinking if the\raggedcolumns declaration isin force and the value of the‘unbalance’ counter if positive.

〈number〉 = 3. Setting〈number〉 to this value will ad-ditionally trace the mark han-dling algorithm. It will showwhat marks are found, whatmarks are considered, etc. Tofully understand this informa-tion you will probably have toread carefully trough the imple-mentation.

〈number〉 ≥ 4. Setting〈number〉 to such a high value

4

will additionally place an\hrule into your output, sep-arating the part of text whichhad already been considered

on the previous page from therest. Clearly this setting shouldnot be used for the final out-put. It will also activate even

more debugging code for markhandling.

3 Prefaces to older versions

3.1 Preface to version 1.4

Beside fixing some bugs as men-tioned in the multicol.bug file thisnew release enhances the multi-cols environment by allowing forbalancing in arbitrary contexts.It is now, for example, possibleto balance text within a multicolsor a minipage as shown in 2 wherea multicols environment within aquote environment was used. Itis now even possible to nest mul-ticols environments.

The only restriction to suchinner multicols environments(nested, or within TEX’s internalvertical mode) is that such vari-

ants will produce a box with thebalanced material in it, so thatthey can not be broken acrosspages or columns.

Additionally I rewrote the al-gorithm for balancing so that itwill now produce slightly betterresults.

I updated the source documen-tation but like to apologize in ad-vance for some ‘left over’ partsthat slipped through the revision.

A note to people who liketo improve the balancing algo-rithm of multicols: The balanc-ing routine in now placed into

a single macro which is called\balance@columns. This meansthat one can easily try differentbalancing routines by rewritingthis macro. The interface for itis explained in table 1. Thereare several improvements possi-ble, one can think of integratingthe \badness function of TEX3,define a faster algorithm for find-ing the right column height, etc.If somebody thinks he/she has anenhancement I would be pleasedto learn about it. But please obeythe copyright notice and don’tchange multicol.dtx directly!

3.2 Preface to version 1.2

After the article about the mul-ticols environment was publishedin TUGboat 10#3, I got numer-ous requests for these macros.However, I also got a changedversion of my style file, togetherwith a letter asking me if I wouldinclude the changes to get betterparagraphing results in the caseof narrow lines. The main dif-ferences to my original style op-tion were additional parameters(like \multicoladjdemerits tobe used for \adjdemerits, etc.)which would influence the linebreaking algorithm.

But actually resetting such pa-rameters to zero or even worse toa negative value won’t give bet-ter line breaks inside the multicolsenvironment. TEXs line break-ing algorithm will only look atthose possible line breaks whichcan be reached without a badnesshigher than the current value of

\tolerance (or \pretolerancein the first pass). If this isn’t pos-sible, then, as a last resort, TEXwill produce overfull boxes. Allthose (and only those) possiblebreak points will be consideredand finally the sequence which re-sults in the fewest demerits willbe chosen. This means that avalue of −1000 for \adjdemeritsinstructs TEX to prefer visibly in-compatible lines instead of pro-ducing better line breaks.

However, with TEX 3.0 it ispossible to get decent line breakseven in small columns by setting\emergencystretch to an appro-priate value. I implemented aversion which is capable of run-ning both in the old and the newTEX (actually it will simply ig-nore the new feature if it is notavailable). The calculation of\emergencystretch is probablyincorrect. I made a few tests but

of course one has have much moreexperience with the new possi-bilities to achieve the maximumquality.

Version 1.1a had a nice ‘fea-ture’: the penalty for usingthe forbidden floats was theirultimate removal from LATEXs\@freelist so that after a few\marginpars inside the multi-cols environment floats where dis-abled forever. (Thanks to ChrisRowley for pointing this out.) Iremoved this misbehaviour andat the same time decided to al-low at least floats spanning allcolumns, e.g., generated by thefigure* environment. You cansee the new functionality in ta-ble 2 which was inserted at thisvery point. However single col-umn floats are still forbidden andI don’t think I will have time totackle this problem in the near fu-ture. As an advice for all who

5

The macro \balance@columns that containsthe code for balancing gathered material is amacro without parameters. It assumes thatthe material for balancing is stored in the box\mult@box which is a \vbox. It also “knows”about all parameters set up by the multicolsenvironment, like \col@number, etc. It canalso assume that \@colroom is the still avail-able space on the current page.

When it finishes it must return the individ-ual columns in boxes suitable for further pro-cessing with \page@sofar. This means thatthe left column should be stored in box reg-

ister \mult@gfirstbox, the next in register\mult@firstbox + 2, . . . , only the last oneas an exception in register \[email protected] it has to set up two the macros\kept@firstmark and \kept@botmark to holdthe values for the first and bottom mark asfound in the individual columns. There aresome helper functions defined in section 5.1which may be used for this. Getting the marksright “by hand” is non-trivial and it may payoff to first take a look at the documentationand implementation of \balance@columns be-low before trying anew.

Table 1: Interface description for \balance@columns

\setemergencystretch: This is a hook for peoplewho like to play around. It is supposed to set the\emergencystretch 〈dimen〉 register provided inthe new TEX 3.0. The first argument is the num-ber of columns and the second one is the current\hsize. At the moment the default definition is

4pt × #1, i.e. the \hsize isn’t used at all. Butmaybe there are better formulae.\set@floatcmds: This is the hook for the expertswho like to implement a full float mechanism forthe multicols environment. The @ in the nameshould signal that this might not be easy.

Table 2: The new commands of multicol.sty version 1.2. Both commands might be removed if good solutionsto these open problems are found. I hope that these commands will prevent that nearly identical style filesderived from this one are floating around.

want to try: wait for TEX 3.0.It has a few features which willmake life much easier in multi-column surroundings. Neverthe-less we are working here at the

edge of TEXs capabilities, reallyperfect solutions would need adifferent approach than it wasdone in TEXs page builder.

The text below is nearly un-

changed, I only added documen-tation at places where new codewas added.

4 The Implementation

We are now switching to two-column output to show the abilities of this environment (and bad layoutdecisions).

4.1 The documentation driver file

The next bit of code contains the documentationdriver file for TEX, i.e., the file that will produce thedocumentation you are currently reading. It will beextracted from this file by the docstrip program.Since this is the first code in this file one can producethe documentation simply by running LATEX on the.dtx file.

1 〈∗driver〉2 \documentclass{ltxdoc}

We use the balancingshow option when loadingmulticols so that full tracing is produced. This has to

be done before the doc package is loaded, since docotherwise requires multicols without any options.

3 \usepackage{multicol}[1999/05/25]4 \usepackage{doc}

First we set up the page layout suitable for this ar-ticle.

5 \setlength{\textwidth}{39pc}6 \setlength{\textheight}{54pc}7 \setlength{\parindent}{1em}8 \setlength{\parskip}{0pt plus 1pt}9 \setlength{\oddsidemargin}{0pc}

6

10 \setlength{\marginparwidth}{0pc}11 \setlength{\topmargin}{-2.5pc}12 \setlength{\headsep}{20pt}13 \setlength{\columnsep}{1.5pc}

We want a rule between columns.14 \setlength\columnseprule{.4pt}

We also want to ensure that a new multicols envi-ronment finds enough space at the bottom of thepage.15 \setlength\premulticols{6\baselineskip}

When balancing columns we disregard solutions thatare too bad. Also, if the last column is too bad wetypeset it without stretch.16 \setcounter{columnbadness}{7000}17 \setcounter{finalcolumnbadness}{7000}

The index is supposed to come out in four columns.And we don’t show macro names in the margin.18 \setcounter{IndexColumns}{4}

19 \let\DescribeMacro\SpecialUsageIndex20 \let\DescribeEnv\SpecialEnvIndex21 \renewcommand\PrintMacroName[1]{}22 \CodelineIndex23 %\DisableCrossrefs % Partial index24 \RecordChanges % Change log

Line numbers are very small for this article.

25 \renewcommand{\theCodelineNo}26 {\scriptsize\rm\arabic{CodelineNo}}27 \settowidth\MacroIndent{\scriptsize\rm 00\ }28

29 \begin{document}30 \typeout31 {****************************************32 ^^J* Expect some Under- and overfull boxes.33 ^^J****************************************}34 \DocInput{multicol.dtx}35 \end{document}36 〈/driver〉

4.2 Identification and option processing

We start by identifying the package. Since it makesuse of features only available in LATEX2ε we ensurethat this format is available. (Now this is done ear-lier in the file.)37 〈∗package〉38 % \NeedsTeXFormat{LaTeX2e}39 % \ProvidesPackage{multicol}[..../../..40 % v... multicolum formatting]

Next we declare options supported by multicols.Twocolumn mode and multicols do not work to-gether so we warn about possible problems. How-ever, since you can revert to \onecolumn in whichcase multicols does work, we don’t make this an er-ror.41 \DeclareOption{twocolumn}42 {\PackageWarning{multicol}{May not work

43 with the twocolumn option}}

Tracing is done using a counter. However it is alsopossible to invoke the tracing using the options de-clared below.

44 \newcount\c@tracingmulticols45 \DeclareOption{errorshow}46 {\c@tracingmulticols\z@}47 \DeclareOption{infoshow}48 {\c@tracingmulticols\@ne}49 \DeclareOption{balancingshow}50 {\c@tracingmulticols\tw@}51 \DeclareOption{markshow}52 {\c@tracingmulticols\thr@@}53 \DeclareOption{debugshow}54 {\c@tracingmulticols5\relax}55 \ProcessOptions

4.3 Starting and Ending the multicols Environment

As mentioned before, the multicols environment hasone mandatory argument (the number of columns)and up to two optional ones. We start by readingthe number of columns into the \col@number regis-ter.

56 \def\multicols#1{\col@number#1\relax

If the user forgot the argument, TEX will complainabout a missing number at this point. The errorrecovery mechanism will then use zero, which isn’ta good choice in this case. So we should now testwhether everything is okay. The minimum is two

columns at the moment.

57 \ifnum\col@number<\tw@58 \PackageWarning{multicol}%59 {Using ‘\number\col@number’60 columns doesn’t seem a good idea.^^J61 I therefore use two columns instead}%62 \col@number\tw@ \fi

We have only enough box registers for ten columns,so we need to check that the user hasn’t asked formore.

63 \ifnum\col@number>1064 \PackageError{multicol}%

7

65 {Too many columns}%66 {Current implementation doesn’t67 support more than 10 columns.%68 \MessageBreak69 I therefore use 10 columns instead}%70 \col@number10 \fi

Within the environment we need a special versionof the kernel \@footnotetext command since theoriginal sets the the \hsize to \columnwidth whichis not correct in the multicol environment. Here\columnwidth refers to the width of the individualcolumn and the footnote should be in \textwidth.Since \@footnotetext has a different definition in-side a minipage environment we do not redefine itdirectly. Instead we locally set \columnwidth to\textwidth and call the original (current) defini-tion stored in \orig@footnotetext.

71 \let\orig@footnotetext\@footnotetext72 \long\def\@footnotetext##1{\begingroup73 \columnwidth\textwidth74 \orig@footnotetext{##1}\endgroup}%

Now we can safely look for the optional arguments.

75 \@ifnextchar[\mult@cols{\mult@cols[]}}

The \mult@cols macro grabs the first optional ar-gument (if any) and looks for the second one.

76 \def\mult@cols[#1]{\@ifnextchar[%

This argument should be a 〈dimen〉 denoting theminimum free space needed on the current page tostart the environment. If the user didn’t supply one,we use \premulticols as a default.

77 {\mult@@cols{#1}}%78 {\mult@@cols{#1}[\premulticols]}}

After removing all arguments from the input we areable to start with \mult@@cols.

79 \def\mult@@cols#1[#2]{%

First thing we do is to decide whether or not this isan unbounded multicols environment, i.e. one thatmay split across pages, or one that has to be typesetinto a box. If we are in TEX’s “inner” mode (e.g.,inside a box already) then we have a boxed versionof multicols therefore we set the @boxedmulticolsswitch to true. The multicols should start in verticalmode. If we are not already there we now force itwith \par since otherwise the test for “inner” modewouldn’t show if we are in a box.

80 \par81 \ifinner \@boxedmulticolstrue

Otherwise we check \doublecol@number. Thiscounter is zero outside a multicols environment butpositive inside (this happens a little later on). In

the second case we need to process the current mul-ticols also in “boxed mode” and so change the switchaccordingly.82 \else83 \ifnum \doublecol@number>\z@84 \@boxedmulticolstrue85 \fi86 \fi

Then we look to see if statistics are requested:87 \mult@info\z@88 {Starting environment with89 \the\col@number\space columns%

In boxed mode we add some more info.90 \if@boxedmulticols\MessageBreak91 (boxed mode)\fi92 }%

Then we measure the current page to see whether auseful portion of the multicolumn environment canbe typeset. This routine might start a new page.93 \enough@room{#2}%

Now we output the first argument and produce ver-tical space above the columns. (Note that this ar-gument corresponds to the first optional argumentof the multicols environment.) For many releasesthis argument was typeset in a group to get a sim-ilar effect as \twocolumn[..] where the argumentis also implicitly surrounded by braces. However,this conflicts with local changes done by things likesectioning commands (which account for the major-ity of commands used in that argument) messing upvertical spacing etc. later in the document so thatfrom version v1.5q on this argument is again typesetat the outer level.94 #1\par\addvspace\multicolsep

We start a new grouping level to hide all subsequentchanges (done in \prepare@multicols for exam-ple).95 \begingroup96 \prepare@multicols

If we are in boxed mode we now open a box to type-set all material from the multicols body into it, oth-erwise we simply go ahead.97 \if@boxedmulticols98 \setbox\mult@box\vbox\bgroup

We may have to reset some parameters at this point,perhaps \@parboxrestore would be the right actionbut I leave it for the moment.99 \fi

We finish by suppressing initial spaces.100 \ignorespaces}

8

Here is the switch and the box for “boxed” multicolscode.

101 \newif\if@boxedmulticols102 \@boxedmulticolsfalse103 \newbox\mult@box

The \enough@room macro used above isn’t perfectbut works reasonably well in this context. We mea-sure the free space on the current page by subtract-ing \pagetotal from \pagegoal. This isn’t en-tirely correct since it doesn’t take the ‘shrinking’(i.e. \pageshrink) into account. The ‘recent con-tribution list’ might be nonempty so we start with\par and an explicit \penalty.7 Actually, we use\addpenalty to ensure that a following \addvspacewill ‘see’ the vertical space that might be present.The use of \addpenalty will have the effect that allitems from the recent contributions will be movedto the main vertical list and the \pagetotal valuewill be updated correctly. However, the penalty willbe placed in front of any dangling glue item withthe result that the main vertical list may alreadybe overfull even if TEX is not invoking the outputroutine.

104 \def\enough@room#1{%

Measuring makes only sense when we are not in“boxed mode” so the routine does nothing if theswitch is true.

105 \if@boxedmulticols\else106 \par

To empty the contribution list the first release con-tained a penalty zero but this had the result that\addvspace couldn’t detect preceding glue. So thiswas changed to \addpenalty. But this turned outto be not enough as \addpenalty will not add apenalty when @nobreak is true. Therefore we forcethis switch locally to false. As a result there maybe a break between preceding text and the start ofa multicols environment, but this seems acceptablesince there is the optional argument for exactly thisreason.

107 \bgroup\@nobreakfalse\addpenalty\z@\egroup108 \page@free \pagegoal109 \advance \page@free -\pagetotal

To be able to output the value we need to assign itto a register first since it might be a register (de-fault) in which case we need to use \the or it mightbe a plain value in which case \the would be wrong.

110 \@tempskipa#1\relax

Now we test whether tracing information is required:

111 \mult@info\z@112 {Current page:\MessageBreak113 height=%114 \the\pagegoal: used \the\pagetotal115 \space -> free=\the\page@free116 \MessageBreak117 needed \the\@tempskipa118 \space(for #1)}%

Our last action is to force a page break if there isn’tenough room left.

119 \ifdim \page@free <#1\newpage \fi120 \fi}

When preparing for multicolumn output severalthings must be done.

121 \def\prepare@multicols{%

We start saving the current \@totalleftmarginand then resetting the \parshape in case we areinside some list environment. The correct inden-tation for the multicols environment in such a casewill be produced by moving the result to the rightby \multicol@leftmargin later on. If we woulduse the value of of \@totalleftmargin directly thenlists inside the multicols environment could cause ashift of the output.

122 \multicol@leftmargin\@totalleftmargin123 \@totalleftmargin\z@124 \parshape\z@

We also set the register \doublecol@number forlater use. This register should contain 2 ×\col@number. This is also an indicator that we arewithin a multicols environment as mentioned above.

125 \doublecol@number\col@number126 \multiply\doublecol@number\tw@127 \advance\doublecol@number\mult@rightbox

128 \if@boxedmulticols129 \let\l@kept@firstmark\kept@firstmark130 \let\l@kept@botmark\kept@botmark131 \global\let\kept@firstmark\@empty132 \global\let\kept@botmark\@empty133 \else

We add an empty box to the main vertical list toensure that we catch any insertions (held over or in-serted at the top of the page). Otherwise it mighthappen that the \eject is discarded without callingthe output routine. Inside the output routine we re-move this box again. Again this code applies onlyif we are on the main vertical list and not withina box. However, it is not enough to turn off inter-line spacing, we also have to clear \topskip beforeadding this box, since \topskip is always inserted

7See the documentation of \endmulticols for further details.

9

before the first box on a page which would leave uswith an extra space of \topskip if multicols start ona fresh sheet.

134 \nointerlineskip {\topskip\z@\null}%135 \output{%136 \global\setbox\partial@page\vbox137 {%

Now we have to make sure that we catch one spe-cial situation which may result in loss of text! Ifthe user has a huge amount of vertical materialwithin the first optional argument that is larger then\premulticols and we are near the bottom of thepage then it can happen that not the \eject istriggering this special output routine but rather theoverfull main vertical list. In that case we get an-other breakpoint through the \eject penalty. Asa result this special output routine would be calledtwice and the contents of \partial@page, i.e. thematerial before the multicols environment gets lost.There are several solutions to avoid this problem,but for now we will simply detect this and inform theuser that he/she has to enlarge the \premulticolsby using a suitable value for the second argument.

138 〈∗check〉139 \ifvoid\partial@page\else140 \PackageError{multicol}%141 {Error saving partial page}%142 {The part of the page before143 the multicols environment was144 nearly full with^^Jthe result145 that starting the environment146 will produce an overfull147 page. Some^^Jtext may be lost!148 Please increase \premulticols149 either generally or for this%150 ^^Jenvironment by specifying a151 suitable value in the second152 optional argument to^^Jthe153 multicols environment.}154 \unvbox\partial@page155 \box\last@line156 \fi157 〈/check〉158 \unvbox\@cclv159 \global\setbox\last@line\lastbox160 }%

Finally we need to record the marks that are presentwithin the \partial@page so that we can constructcorrect first and bottom marks later on. This is doneby the following code.

161 \prep@keptmarks

Finally we have to initialize \kept@topmark whichshould ideally be initialized with the mark that iscurrent on “top” of this page. Unfortunately we

can’t use \topmark because this register will not al-ways contain what its name promises because LATEXsometimes calls the output routine for float manage-ment.8 Therefore we use the second best solution byinitializing it with \firstmark. In fact, for our pur-pose this doesn’t matter as we use \kept@topmarkonly to initialize \firstmark and \botmark of a fol-lowing page if we don’t find any marks on the currentone.

162 \global\let\kept@topmark\firstmark163 }\eject

The next thing to do is to assign a new value to\vsize. LATEX maintains the free room on the page(i.e. the page height without the space for alreadycontributed floats) in the register \@colroom. Wemust subtract the height of \partial@page to putthe actual free room into this variable.

164 \advance\@colroom-\ht\partial@page

Then we have to calulate the \vsize value to useduring column assembly. \set@mult@vsize takesan argument which allows to make the setting local(\relax) or global (\global). The latter variant isused inside the output routine below. At this pointhere we have to make a local change to \vsize be-cause we want to get the original value for \vsizerestored in case this multicols environment ends onthe same page where it has started.

165 \set@mult@vsize\relax

Now we switch to a new \output routine which willbe used to put the gathered column material to-gether.

166 \output{\multi@column@out}%

Finally we handle the footnote insertions. We haveto multiply the magnification factor and the extraskip by the number of columns since each footnotereduces the space for every column (remember thatwe have pagewide footnotes). If, on the other hand,footnotes are typeset at the very end of the docu-ment, our scheme still works since \count\footinsis zero then, so it will not change. To allow evenfurther customization the setting of the \footinsparameters is done in a separate macro.

167 \init@mult@footins

For the same reason (pagewide footnotes), the〈dimen〉 register controlling the maximum spaceused for footnotes isn’t changed. Having done this,we must reinsert all the footnotes which are alreadypresent (i.e. those encountered when the materialsaved in \partial@page was first processed). Thiswill reduce the free space (i.e. \pagetotal) by the

8During such a call the \botmark gets globally copied to \topmark by the TEX program.

10

appropriate amount since we have changed the mag-nification factor, etc. above.

168 \reinsert@footnotes

All the code above was only necessary for the un-restricted multicols version, i.e. the one that allowspage breaks. If we are within a box there is no pointin setting up special output routines or \vsize, etc.

169 \fi

But now we are coming to code that is necessaryin all cases. We assign new values to \vbadness,\hbadness and \tolerance since it’s rather hardfor TEX to produce ‘good’ paragraphs within nar-row columns.

170 \vbadness\@Mi \hbadness5000171 \tolerance\multicoltolerance

Since nearly always the first pass will fail we ignoreit completely telling TEX to hyphenate directly. Infact, we now use another register to keep the valuefor the multicol pre-tolerance, so that a designer mayallow to use \pretolerance.

172 \pretolerance\multicolpretolerance

For use with the new TEX we set\emergencystretch to \col@number × 4pt. How-ever this is only a guess so at the moment this isdone in a macro \setemergencystretch which getsthe current \hsize and the number of columns asarguments. Therefore users are able to figure outtheir own formula.

173 \setemergencystretch\col@number\hsize

Another hook to allow people adding their ownextensions without making a new package is\set@floatcmds which handles any redefinitions ofLATEXs internal float commands to work with themulticols environment. At the moment it is onlyused to redefine \@dblfloat and \end@dblfloat.

174 \set@floatcmds

Additionally, we advance \baselineskip by\multicolbaselineskip to allow corrections fornarrow columns.

175 \advance\baselineskip\multicolbaselineskip

The \hsize of the columns is given by the formula:

\linewidth− (\col@number− 1)× \columnsep\col@number

The formula above has changed from release torelease. We now start with the current value of

\linewidth so that the column width is properlycalculated when we are inside a minipage or a listor some other environment. This will be achievedwith:

176 \hsize\linewidth \advance\hsize\columnsep177 \advance\hsize-\col@number\columnsep178 \divide\hsize\col@number

We also set \linewidth and \columnwidth to\hsize In the past \columnwidth was left un-changed. This is inconsistent, but \columnwidth isused only by floats (which aren’t allowed in theircurrent implementation) and by the \footnotemacro. Since we want pagewide footnotes9 this sim-ple trick saved us from rewriting the \footnotemacros. However, some applications refered to\columnwidth as the “width of the current column”to typeset displays (the amsmath package, for exam-ple) and to allow the use of such applications to-gether with multicol this is now changed.

Before we change \linewidth to the new valuewe record its old value in some register called\full@width. This value is used later on when wepackage all columns together.

179 \full@width\linewidth180 \linewidth\hsize181 \columnwidth\hsize182 }

This macro is used to set up the parameters asso-ciated with footnote floats. It can be redefined byapplications that require different amount of spaceswhen typesetting footnotes.

183 \def\init@mult@footins{%184 \multiply\count\footins\col@number185 \multiply\skip \footins\col@number186 }

Since we have to set \col@umber columns on onepage, each with a height of \@colroom, we have toassign \vsize = \col@number × \@colroom in or-der to collect enough material before entering the\output routine again. In fact we have to addanother (\col@number − 1) × (\baselineskip −\topskip) if you think about it.

187 \def\set@mult@vsize#1{%188 \vsize\@colroom189 \@tempdima\baselineskip190 \advance\@tempdima-\topskip191 \advance\vsize\@tempdima192 \vsize\col@number\vsize193 \advance\vsize-\@tempdima

9I’m not sure that I really want pagewide footnotes. But balancing of the last page can only be achieved with this approachor with a multi-path algorithm which is complicated and slow. But it’s a challenge to everybody to prove me wrong! Anotherpossibility is to reimplement a small part of the fire up procedure in TEX (the program). I think that this is the best solutionif you are interested in complex page makeup, but it has the disadvantage that the resulting program cannot be called TEXthereafter.

11

But this might not be enough since we use \vsplitlater to extract the columns from the gathered ma-terial. Therefore we add some ‘extra lines,’ the num-ber depending on the value of the ‘multicols’ counter.The final value is assigned globally if #1 is \globalbecause we want to use this macro later inside theoutput routine too.

194 #1\advance\vsize195 \c@collectmore\baselineskip}

Here is the dimen register we need for saving awaythe outer value of \@totalleftmargin.

196 \newdimen\multicol@leftmargin

When the end of the multicols environment is sensedwe have to balance the gathered material. Depend-ing on whether or not we are inside a boxed multicoldifferent things must happen. But first we end thecurrent paragraph with a \par command.

197 \def\endmulticols{\par198 \if@boxedmulticols

In boxed mode we have to close the box in which wehave gathered all material for the columns.

199 \egroup

Now we call \balance@columns the routine thatbalances material stored in the box \mult@box.

200 \balance@columns

After balancing the result has to be returned by thecommand \page@sofar. But before we do this wereinsert any marks found in box \mult@box.

201 \return@nonemptymark{first}%202 \kept@firstmark203 \return@nonemptymark{bot}%204 \kept@botmark205 \page@sofar

206 \global\let\kept@firstmark207 \l@kept@firstmark208 \global\let\kept@botmark209 \l@kept@botmark210 〈∗marktrace〉211 \mult@info\tw@212 {Restore kept marks to\MessageBreak213 first: \meaning\kept@firstmark214 \MessageBreak bot\space\space:215 \meaning\kept@botmark }%216 〈/marktrace〉

This finishes the code for the “boxed” case.

217 \else

If we are in an unrestricted multicols environmentwe end the current paragraph with \par but thisisn’t sufficient since TEXs page builder will not to-tally empty the contribution list.10 Therefore wemust also add an explicit \penalty. Now the con-tribution list will be emptied and, if its materialdoesn’t all fit onto the current page then the outputroutine will be called before we change it. At thispoint we need to use \penalty not \addpenalty toensure that a) the recent contributions are emptiedand b) that the very last item on the main verticallist is a valid break point so that TEX breaks thepage in case it is overfull.

218 \penalty\z@

Now it’s safe to change the output routine in orderto balance the columns.

219 \output{\balance@columns@out}\eject

If the multicols environment body was completelyempty or if a multi-page multicols just ends at apage boundary we have the unusual case that the\eject will have no effect (since the main verticallist is empty)—thus no output routine is called atall. As a result the material preceding the multicols(stored in \partial@page will get lost if we don’ttake of this by hand.

220 \ifvbox\partial@page221 \unvbox\partial@page\fi

After the output routine has acted we restore thekept marks to their initial value.

222 \global\let\kept@firstmark\@empty223 \global\let\kept@botmark\@empty224 〈∗marktrace〉225 \mult@info\tw@226 {Make kept marks empty}%227 〈/marktrace〉228 \fi

The output routine above will take care of the\vsize and reinsert the balanced columns, etc. Butit can’t reinsert the \footnotes because we firsthave to restore the \footins parameter since weare returning to one column mode. This will bedone in the next line of code; we simply close thegroup started in \multicols.

To fix an obscure bug which is the result of thecurrent definition of the \begin . . . \end macros,we check that we are still (logically speaking) in themulticols environment. If, for example, we forget toclose some environment inside the multicols environ-ment, the following \endgroup would be incorrectly

10This once caused a puzzling bug where some of the material was balanced twice, resulting in some overprints. The reasonwas the \eject which was placed at the end of the contribution list. Then the page builder was called (an explicit \penaltywill empty the contribution list), but the line with the \eject didn’t fit onto the current page. It was then reconsidered afterthe output routine had ended, causing a second break after one line.

12

considered to be the closing of this environment.229 \@checkend{multicols}%230 \endgroup

Now it’s time to return any footnotes if we are inunrestricted mode:

231 \if@boxedmulticols\else232 \reinsert@footnotes233 \fi

We also set the ‘unbalance’ counter to its default.This is done globally since LATEX counters are al-ways changed this way.11

234 \global\c@unbalance\z@

We also take a look at the amount of free space onthe current page to see if it’s time for a page break.The vertical space added thereafter will vanish if\enough@room starts a new page.

235 \enough@room\postmulticols236 \addvspace\multicolsep

If statistics are required we finally report that wehave finished everything.

237 \mult@info\z@238 {Ending environment239 \if@boxedmulticols240 \space(boxed mode)\fi241 }}

Let us end this section by allocating all the registersused so far.

242 \newcount\c@unbalance

243 \newcount\c@collectmore

In the new LATEX release \col@number is already al-located by the kernel, so we don’t allocate it again.

244 %\newcount\col@number245 \newcount\doublecol@number246 \newcount\multicoltolerance247 \newcount\multicolpretolerance248 \newdimen\full@width249 \newdimen\page@free250 \newdimen\premulticols251 \newdimen\postmulticols252 \newskip\multicolsep253 \newskip\multicolbaselineskip254 \newbox\partial@page255 \newbox\last@line

And here are their default values:

256 \c@unbalance = 0257 \c@collectmore = 0

To allow checking whether some macro is usedwithin the multicols environment the counter\col@number gets a default of 1 outside the the en-vironment.

258 \col@number = 1259 \multicoltolerance = 9999260 \multicolpretolerance = -1261 \premulticols = 50pt262 \postmulticols= 20pt263 \multicolsep = 12pt plus 4pt minus 3pt264 \multicolbaselineskip=0pt

4.4 The output routines

We first start with some simple macros. When type-setting the page we save the columns either in thebox registers 0, 2, 4,. . . (locally) or 1, 3, 5,. . . (glob-ally). This is Plain TEX policy to avoid an overflowof the save stack.

Therefore we define a \process@cols macro to helpus in using these registers in the output routinesbelow. It has two arguments: the first one is anumber; the second one is the processing informa-tion. It loops starting with \count@=#1 (\count@ isa scratch register defined in Plain TEX), processesargument #2, adds two to \count@, processes ar-gument #2 again, etc. until \count@ is higher than\doublecol@number. It might be easier to under-stand it through an example, so we define it nowand explain its usage afterwards.

265 \def\process@cols#1#2{\count@#1\relax266 \loop267 〈∗debug〉

268 \typeout{Looking at box \the\count@}269 〈/debug〉270 #2%271 \advance\count@\tw@272 \ifnum\count@<\doublecol@number273 \repeat}

We now define \page@sofar to give an exampleof the \process@cols macro. \page@sofar shouldoutput everything prepared by the balancing routine\balance@columns.

274 \def\page@sofar{%

\balance@columns prepares its output in the evennumbered scratch box registers. Now we outputthe columns gathered assuming that they are savedin the box registers 2 (left column), 4 (second col-umn), . . . However, the last column (i.e. the right-

11Actually, we are still in a group started by the \begin macro, so \global must be used anyway.

13

most) should be saved in box register 0.12 Firstwe ensure that the columns have equal width. Weuse \process@cols for this purpose, starting with\count@ = \mult@rightbox. Therefore \count@loops through \mult@rightbox, \mult@rightbox+2,. . . (to \doublecol@number).

275 \process@cols\mult@rightbox

We have to check if the box in question is void, be-cause the operation \wd〈number〉 on a void box willnot change its dimension (sigh).

276 {\ifvoid\count@277 \setbox\count@\hbox to\hsize{}%278 \else279 \wd\count@\hsize280 \fi}%

Now we give some tracing information.

281 \mult@info\z@282 {Column spec:\MessageBreak283 (\the\multicol@leftmargin\space -->284 \the\full@width\space = \the\hsize285 \space x \the\col@number)%286 }%

At this point we should always be in vertical mode.

287 \ifvmode\else\errmessage{Multicol Error}\fi

Now we put all columns together in an\hbox of width \full@width (shifting it by\multicol@leftmargin to the right so that it willbe placed correctly if we are within a list environ-ment)

288 \moveright\multicol@leftmargin289 \hbox to\full@width{%

and separating the columns with a rule if desired.

290 \process@cols\mult@gfirstbox{\box\count@291 \hss\vrule\@width\columnseprule\hss}%

As you will have noticed, we started with box regis-ter \mult@gfirstbox (i.e. the left column). So thistime \count@ looped through 2, 4,. . . (plus the ap-propriate offset). Finally we add box 0 and close the\hbox.

292 \box\mult@rightbox

The depths of the columns depend on their last lines.To ensure that we will always get a similar look asfar as the rules are concerned we force the depth atleast the depth of a letter ‘p’.

293 % \strut294 \rlap{\phantom p}%295 }%296 }

Before we tackle the bigger output routines wedefine just one more macro which will help usto find our way through the mysteries later.\reinsert@footnotes will do what its name in-dicates: it reinserts the footnotes present in\footinbox so that they will be reprocessed byTEX’s page builder.

Instead of actually reinserting the footnotes weinsert an empty footnote. This will trigger insertionmechanism as well and since the old footnotes arestill in their box and we are on a fresh page \skipfootins should be correctly taken into account.

297 \def\reinsert@footnotes{\ifvoid\footins\else298 \insert\footins{}\fi}

Now we can’t postpone the difficulties any longer.The \multi@column@out routine will be called intwo situations. Either the page is full (i.e. wehave collected enough material to generate all therequired columns) or a float or marginpar (or a\clearpage is sensed. In the latter case the\outputpenalty is less than −10000, otherwise thepenalty which triggered the output routine is higher.Therefore it’s easy to distinguish both cases: we sim-ply test this register.

299 \def\multi@column@out{%300 \ifnum\outputpenalty <-\@M

If this was a \clearpage, a float or a marginpar wecall \speci@ls

301 \speci@ls \else

otherwise we construct the final page. For the nextblock of code see comments in section 7.2.

302 〈∗colbreak〉303 \ifvoid\colbreak@box\else304 \mult@info\@ne{Re-adding forced305 break(s) for splitting}%306 \setbox\@cclv\vbox{%307 \unvbox\colbreak@box308 \penalty-\@Mv\unvbox\@cclv}%309 \fi310 〈/colbreak〉Let us now consider the normal case. We have to\vsplit the columns from the accumulated mate-rial in box 255. Therefore we first assign appropriatevalues to \splittopskip and \splitmaxdepth.

311 \splittopskip\topskip312 \splitmaxdepth\maxdepth

Then we calculate the current column height (in\dimen@). Note that the height of \partial@pageis already subtracted from \@colroom so we can useits value as a starter.

313 \dimen@\@colroom

12You will see the reason for this numbering when we look at the output routines \multi@column@out and\balance@columns@out.

14

But we must also subtract the space occupied byfootnotes on the current page. Note that we firsthave to reset the skip register to its normal value.Again, the actual action is carried out in a utilitymacro, so that other applications can modify it.

314 \divide\skip\footins\col@number315 \ifvoid\footins \else316 \leave@mult@footins317 \fi

Now we are able to \vsplit off all but the last col-umn. Recall that these columns should be saved inthe box registers 2, 4,. . . (plus offset).

318 \process@cols\mult@gfirstbox{%319 \setbox\count@320 \vsplit\@cclv to\dimen@

After splitting we update the kept marks.321 \set@keptmarks

If \raggedcolumns is in force we add a vfill at thebottom by unboxing the split box.

322 \ifshr@nking323 \setbox\count@324 \vbox to\dimen@325 {\unvbox\count@\vfill}%326 \fi327 }%

Then the last column follows.328 \setbox\mult@rightbox329 \vsplit\@cclv to\dimen@330 \set@keptmarks331 \ifshr@nking332 \setbox\mult@rightbox\vbox to\dimen@333 {\unvbox\mult@rightbox\vfill}%334 \fi

Having done this we hope that box 255 is emptied.If not, we reinsert its contents.

335 \ifvoid\@cclv \else336 \unvbox\@cclv337 \penalty\outputpenalty

In this case a footnote that happens to fall intothe leftover bit will be typeset on the wrong page.Therefore we warn the user if the current page con-tains footnotes. The older versions of multicols pro-duced this warning regardless of whether or not foot-notes were present, resulting in many unnecessarywarnings.

338 \ifvoid\footins\else339 \PackageWarning{multicol}%340 {I moved some lines to341 the next page.\MessageBreak342 Footnotes on page343 \thepage\space might be wrong}%344 \fi

If the ‘tracingmulticols’ counter is 4 or higher we alsoadd a rule.

345 \ifnum \c@tracingmulticols>\thr@@346 \hrule\allowbreak \fi347 \fi

To get a correct marks for the current pagewe have to (locally redefine \firstmark and\botmark. If \kept@firstmark is non-empty then\kept@botmark must be non-empty too so we canuse their values. Otherwise we use the value of\kept@topmark which was first initialized when wegathered the \partical@page and later on was up-dated to the \botmark for the preceding page

348 \ifx\@empty\kept@firstmark349 \let\firstmark\kept@topmark350 \let\botmark\kept@topmark351 \else352 \let\firstmark\kept@firstmark353 \let\botmark\kept@botmark354 \fi

We also initalize \topmark with \[email protected] will make this mark okay for all middle pagesof the multicols environment.

355 \let\topmark\kept@topmark356 〈∗marktrace〉357 \mult@info\tw@358 {Use kept top mark:\MessageBreak359 \meaning\kept@topmark360 \MessageBreak361 Use kept first mark:\MessageBreak362 \meaning\kept@firstmark363 \MessageBreak364 Use kept bot mark:\MessageBreak365 \meaning\kept@botmark366 \MessageBreak367 Produce first mark:\MessageBreak368 \meaning\firstmark369 \MessageBreak370 Produce bot mark:\MessageBreak371 \meaning\botmark372 \@gobbletwo}%373 〈/marktrace〉With a little more effort we could have done bet-ter. If we had, for example, recorded the shrinkageof the material in \partial@page it would be nowpossible to try higher values for \dimen@ (i.e. thecolumn height) to overcome the problem with thenonempty box 255. But this would make the codeeven more complex so I skipped it in the currentimplementation.

Now we use LATEX’s standard output mecha-nism.13 Admittedly this is a funny way to do it.

13This will produce a lot of overhead since both output routines are held in memory. The correct solution would be toredesign the whole output routine used in LATEX.

15

374 \setbox\@cclv\vbox{\unvbox\partial@page375 \page@sofar}%

The macro \@makecol adds all floats assigned forthe current page to this page. \@outputpage shipsout the resulting box. Note that it is just possiblethat such floats are present even if we do not allowany inside a multicols environment.

376 \@makecol\@outputpage

After the page is shipped out we have to pre-pare the kept marks for the following page.\kept@firstmark and \kept@botmark reinitilizedby setting them to \@empty. The value of \botmarkis then assigned to \kept@topmark.

377 \global\let\kept@topmark\botmark378 \global\let\kept@firstmark\@empty379 \global\let\kept@botmark\@empty380 〈∗marktrace〉381 \mult@info\tw@382 {(Re)Init top mark:\MessageBreak383 \meaning\kept@topmark384 \@gobbletwo}%385 〈/marktrace〉Now we reset \@colroom to \@colht which isLATEX’s saved value of \textheight.

386 \global\@colroom\@colht

Then we process deferred floats waiting for theirchance to be placed on the next page.

387 \process@deferreds388 \@whilesw\if@fcolmade\fi{\@outputpage389 \global\@colroom\@colht390 \process@deferreds}%

If the user is interested in statistics we inform himabout the amount of space reserved for floats.

391 \mult@info\@ne392 {Colroom:\MessageBreak393 \the\@colht\space394 after float space removed395 = \the\@colroom \@gobble}%

Having done all this we must prepare to tackle thenext page. Therefore we assign a new value to\vsize. New, because \partial@page is now emptyand \@colroom might be reduced by the space re-served for floats.

396 \set@mult@vsize \global

The \footins skip register will be adjusted whenthe output group is closed.

397 \fi}

This macro is used to subtract the amount of spaceoccupied by footnotes for the current space from thespace available for the current column. The spacecurrent column is stored in \dimen@. See above forthe description of the default action.

398 \def\leave@mult@footins{%399 \advance\dimen@-\skip\footins400 \advance\dimen@-\ht\footins401 }

We left out two macros: \process@deferreds and\speci@ls.

402 \def\speci@ls{%403 \ifnum\outputpenalty <-\@Mi

If the document ends in the middle of a mul-ticols environment, e.g., if the user forgot the\end{multicols}, TEX adds a very negativepenalty to the end of the galley which is intendedto signal the output routine that it is time to pre-pare for shipping out everything remaining. Sinceinside multicols the output routine of LATEX is dis-abled sometimes we better check for this case: ifwe find a very negative penalty we produce an errormessage and run the default output routine for thiscase.

404 \ifnum \outputpenalty<-\@MM405 \PackageError{multicol}{Document end406 inside multicols environment}\@ehd407 \@specialoutput408 \else

For the next block of code see comments in sec-tion 7.2.

409 〈∗colbreak〉410 \ifnum\outputpenalty = -\@Mv411 \mult@info\@ne{Forced column412 break seen}%413 \global\advance\vsize-\pagetotal414 \global\setbox\colbreak@box415 \vbox{\ifvoid\colbreak@box416 \else417 \unvbox\colbreak@box418 \penalty-\@Mv419 \fi420 \unvbox\@cclv}421 \reinsert@footnotes422 \else423 〈/colbreak〉

If we encounter a float or a marginpar in the cur-rent implementation we simply warn the user thatthis is not allowed. Then we reinsert the page andits footnotes.

424 \PackageWarning{multicol}%425 {Floats and marginpars not426 allowed inside ‘multicols’427 environment!428 \@gobble}%429 \unvbox\@cclv\reinsert@footnotes

16

Additionally we empty the \@currlist to avoidlater error messages when the LATEX output routineis again in force. But first we have to place theboxes back onto the \@freelist. (\@elts default is\relax so this is possible with \xdef.)

430 \xdef\@freelist{\@freelist\@currlist}%431 \gdef\@currlist{}%432 〈∗colbreak〉433 \fi434 〈/colbreak〉435 \fi

If the penalty is −10001 it will come from a\clearpage and we will execute \@doclearpage toget rid of any deferred floats.

436 \else \@doclearpage \fi437 }

\process@deferreds is a simplified version ofLATEX’s \@startpage. We first call the macro\@floatplacement to save the current user parame-ters in internal registers. Then we start a new groupand save the \@deferlist temporarily in the macro\@tempb.

438 \def\process@deferreds{%439 \@floatplacement440 \@tryfcolumn\@deferlist441 \if@fcolmade\else442 \begingroup443 \let\@tempb\@deferlist

Our next action is to (globally) empty \@deferlistand assign a new meaning to \@elt. Here\@scolelt is a macro that looks at the boxes ina list to decide whether they should be placed onthe next page (i.e. on \@toplist or \@botlist) orshould wait for further processing.

444 \gdef\@deferlist{}%445 \let\@elt\@scolelt

Now we call \@tempb which has the form

\@elt〈box register〉\@elt〈box register〉. . .

So \@elt (i.e. \@scolelt) will distribute the boxesto the three lists.

446 \@tempb \endgroup447 \fi}

The \raggedcolumns and \flushcolumns declara-tions are defined with the help of a new \if...macro.

448 \newif\ifshr@nking

The actual definitions are simple: we just switch totrue or false depending on the desired action. Toavoid extra spaces in the output we enclose thesechanges in \@bsphack. . . \@esphack.

449 \def\raggedcolumns{%450 \@bsphack\shr@nkingtrue\@esphack}451 \def\flushcolumns{%452 \@bsphack\shr@nkingfalse\@esphack}

Now for the last part of the show: the column bal-ancing output routine. Since this code is called withan explicit penalty (\eject) there is no need tocheck for something special (eg floats). We startby balancing the material gathered.

453 \def\balance@columns@out{%

For this we need to put the contents of box 255 into\mult@box.

454 〈−colbreak〉 \setbox\mult@box455 〈−colbreak〉 \vbox{\unvbox\@cclv}%

For the next block of code see comments in sec-tion 7.2.

456 〈∗colbreak〉457 \setbox\mult@box\vbox{%458 \ifvoid\colbreak@box\else459 \unvbox\colbreak@box\break460 \mult@info\@ne{Re-adding461 forced break(s) in balancing}%462 \fi463 \unvbox\@cclv}%464 〈/colbreak〉465 \balance@columns

This will bring us into the position to apply\page@sofar. But first we have to set \vsize toa value suitable for one column output.

466 \global\vsize\@colroom467 \global\advance\vsize\ht\partial@page

Then we \unvbox the \partial@page (which maybe void if we are not prcessing the first page of thismulticols environment.

468 \unvbox\partial@page

Then we return the first and bottom mark and thegathered material to the main vertical list.

469 \return@nonemptymark{first}\kept@firstmark470 \return@nonemptymark{bot}\kept@botmark471 \page@sofar

We need to add a penalty at this point which allowsto break at this point since calling the output rou-tine may have removed the only permissible breakpoint thereby “glueing” any following skip to thebalanced box. In case there are any weird settingsfor \multicolsep etc. this could produce funny re-sults.

472 \penalty\z@473 }

As we already know, reinserting of footnotes will bedone in the macro \endmulticols.

17

This macro now does the actual balancing.474 \def\balance@columns{%

We start by setting the kept marks by updatingthem with any marks from this box. This has tobe done before we add a penalty of −10000 to thetop of the box, otherwise only an empty box will beconsidered.

475 \get@keptmarks\mult@box

We then contine by resetting trying to remove anydiscardable stuff at the end of \mult@box. This israther experimental. We also add a forced breakpoint at the very beginning, so that we can split thebox to height zero later on, thereby adding a known\splittopskip glue at the beginning.

476 \setbox\mult@box\vbox{%477 \penalty-\@M478 \unvbox\mult@box479 \remove@discardable@items480 }%

Then follow values assignments to get the\vsplitting right. We use the natural part of\topskip as the natural part for \splittopskipand allow for a bit of undershoot and overshoot byadding some stretch and shrink.

481 \@tempdima\topskip482 \splittopskip\@tempdima483 \@plus\multicolundershoot484 \@minus\multicolovershoot485 \splitmaxdepth\maxdepth

The next step is a bit tricky: when TEX assem-bles material in a box, the first line isn’t precededby interline glue, i.e. there is no parameter like\boxtopskip in TEX. This means that the baselineof the first line in our box is at some unpredictablepoint depending on the height of the largest charac-ter in this line. But of course we want all columnsto align properly at the baselines of their first lines.For this reason we have opened \mult@box with a\penalty -10000. This will now allow us to splitoff from \mult@box a tiny bit (in fact nothing sincethe first possible break-point is the first item in thebox). The result is that \splittopskip is insertedat the top of \mult@box which is exactly what welike to achieve.

486 \setbox\@tempboxa\vsplit\mult@box to\z@

Next we try to find a suitable starting point forthe calculation of the column height. It should beless than the height finally chosen, but large enoughto reach this final value in only a few iterations.The formula which is now implemented will try tostart with the nearest value which is a multiple of\baselineskip. The coding is slightly tricky in TEXand there are perhaps better ways . . .

487 \@tempdima\ht\mult@box488 \advance\@tempdima\dp\mult@box489 \divide\@tempdima\col@number

The code above sets \@tempdima to the length ofa column if we simply divide the whole box intoequal pieces. To get to the next lower multiple of\baselineskip we convert this dimen to a num-ber (the number of scaled points) then divide thisby \baselineskip (also in scaled points) and thenmultiply this result with \baselineskip assigningthe result to \dimen@. This makes \dimen@ ≤ to\@tempdimena.

490 \count@\@tempdima491 \divide\count@\baselineskip492 \dimen@\count@\baselineskip

Next step is to correct our result by takinginto account the difference between \topskip and\baselineskip. We start by adding \topskip; ifthis makes the result too large then we have to sub-tract one \baselineskip.

493 \advance\dimen@\topskip494 \ifdim \dimen@ >\@tempdima495 \advance\dimen@-\baselineskip496 \fi

At the user’s request we start with a higher value (orlower, but this usually only increases the number oftries).

497 \advance\dimen@\c@unbalance\baselineskip

We type out statistics if we were asked to do so.

498 \mult@info\@ne499 {Balance columns\on@line:500 \ifnum\c@unbalance=\z@\else501 (off balance=\number\c@unbalance)\fi502 \@gobbletwo}%

But we don’t allow nonsense values for a start.

503 \ifnum\dimen@<\topskip504 \mult@info\@ne505 {Start value506 \the\dimen@ \space ->507 \the\topskip \space (corrected)}%508 \dimen@\topskip509 \fi

Now we try to find the final column height. We startby setting \vbadness to infinity (i.e. 10000) to sup-press underfull box reports while we are trying tofind an acceptable solution. We do not need to doit in a group since at the end of the output routineeverything will be restored. The setting of the finalcolumns will nearly always produce underfull boxeswith badness 10000 so there is no point in warningthe user about it.

510 \vbadness\@M

18

We also allow for overfull boxes while we trying tosplit the columns.

511 \vfuzz \col@number\baselineskip

The variable \last@try will hold the dimensionused in the previous trial splitting. We initializeit with a negative value.

512 \last@try-\p@513 \loop

In order not to clutter up TEX’s valuable mainmemory with things that are no longer needed, weempty all globally used box registers. This is neces-sary if we return to this point after an unsuccessfultrial. We use \process@cols for this purpose, start-ing with \mult@grightbox. Note the extra bracesaround this macro call. They are needed sincePlain TEX’s \loop. . . \repeat mechanism cannotbe nested on the same level of grouping.

514 {\process@cols\mult@grightbox515 {\global\setbox\count@516 \box\voidb@x}}%

The contents of box \mult@box are now copied glob-ally to box \mult@grightbox. (This will be theright-most column, as we shall see later.)

517 \global\setbox\mult@grightbox518 \copy\mult@box

We start with the assumption that the trial will besuccessful. If we end up with a solution that is toobad we set too@bad to true.

519 〈∗badness〉520 \global\too@badfalse521 〈/badness〉Using \vsplit we extract the other columns frombox register \mult@grightbox. This leaves box reg-ister \mult@box untouched so that we can start overagain if this trial was unsuccessful.

522 {\process@cols\mult@firstbox{%523 \global\setbox\count@524 \vsplit\mult@grightbox to\dimen@

After every split we check the badness of the result-ing column, normally the amount of extra white inthe column.

525 〈∗badness〉526 \ifnum\c@tracingmulticols>\@ne527 \@tempcnta\count@528 \advance\@tempcnta-\mult@grightbox529 \divide\@tempcnta \tw@530 \message{^^JColumn531 \number\@tempcnta\space532 badness: \the\badness\space}%533 \fi

If this badness is larger than the allowed columnbadness we reject this solution by setting too@badto true.

534 \ifnum\badness>\c@columnbadness535 \ifnum\c@tracingmulticols>\@ne536 \message{too bad537 (>\the\c@columnbadness)}%538 \fi539 \global\too@badtrue540 \fi541 〈/badness〉542 }}%

There is one subtle point here: while all other con-structed boxes have a depth that is determined by\splitmaxdepth the last box will get a naturaldepth disregarding the original setting and the valueof \splitmaxdepth or \boxmaxdepth. This meansthat we may end up with a very large depth in box\mult@grightbox which would make the result ofthe testing incorrect. So we change the value byunboxing the box into itself.

543 \boxmaxdepth\maxdepth544 \global\setbox\mult@grightbox545 \vbox{\unvbox\mult@grightbox}%

We also save a copy \mult@firstbox at its “natu-ral” size for later use.

546 \setbox\mult@nat@firstbox547 \vbox{\unvcopy\mult@firstbox}%

After \process@cols has done its job we have thefollowing situation:

box \mult@rightbox ←− all materialbox \mult@gfirstbox ←− first column

box \mult@gfirstbox + 2 ←− second column...

...box \mult@grightbox ←− last column

We report the height of the first column, in bracketsthe natural size is given.

548 \ifnum\c@tracingmulticols>\@ne549 \message{^^JFirst column550 = \the\dimen@\space551 (\the\ht\mult@nat@firstbox)}\fi

If \raggedcolumns is in force older releases of thisfile also shrank the first column to its natural heightat this point. This was done so that the first col-umn doesn’t run short compared to later columnsbut it is actually producing incorrect results (over-printing of text) in boundary cases, so since versionv1.5q \raggedcolumns means allows for all columnsto run slightly short.

552 % \ifshr@nking553 % \global\setbox\mult@firstbox554 % \copy\mult@nat@firstbox555 % \fi

19

Then we give information about the last column.14

556 \ifnum\c@tracingmulticols>\@ne557 \message{<> last column =558 \the\ht\mult@grightbox^^J}%

Some tracing code that we don’t compile into theproduction version unless asked for. It will producehuge listings of the boxes involved in balancing inthe transcript file.

559 〈∗debug〉560 \ifnum\c@tracingmulticols>4561 {\showoutput562 \batchmode563 \process@cols\@ne564 {\showbox\count@}}%565 \errorstopmode566 \fi567 〈/debug〉568 \fi

We check whether our trial was successful. The testused is very simple: we merely compare the first andthe last column. Thus the intermediate columnsmay be longer than the first if \raggedcolumnsis used. If the right-most column is longer thanthe first then we start over with a larger value for\dimen@.

569 \ifdim\ht\mult@grightbox >\dimen@

If the height of the last box is too large we mark thistrial as unsuccessful.

570 〈∗badness〉571 \too@badtrue572 \ifnum\c@tracingmulticols>\@ne573 \typeout{Rejected: last574 column too large!}%575 \fi576 \else

To ensure that there isn’t a forced break in the lastcolumn we try to split off a box of size \maxdimenfrom \mult@grightbox (or rather from a copy of it).This should result in a void box after the split, un-less there was a forced break somewhere within thecolumn in which case the material after the breakwould have stayed in the box.

577 〈∗colbreak〉578 \setbox\@tempboxa579 \copy\mult@grightbox580 \setbox\z@\vsplit\@tempboxa to\maxdimen581 \ifvoid\@tempboxa582 〈/colbreak〉Thus if \@tempboxa is void we have a valid solution.In this case we take a closer look at the last columnto decide if this column should be made as long asall other columns or if it should be allowed to be

shorter. For this we first have to rebox the columninto a box of the appropriate height. If tracing isenabled we then display the badness for this box.

583 \global\setbox\mult@grightbox584 \vbox to\dimen@585 {\unvbox\mult@grightbox}%586 \ifnum\c@tracingmulticols>\@ne587 \message{Final badness:588 \the\badness}%589 \fi

We then compare this badness with the allowed bad-ness for the final column. If it does not exceed thisvalue we use the box, otherwise we rebox it oncemore and add some glue at the bottom.

590 \ifnum\badness>\c@finalcolumnbadness591 \global\setbox\mult@grightbox592 \vbox to\dimen@593 {\unvbox\mult@grightbox\vfill}%594 \ifnum\c@tracingmulticols>\@ne595 \message{ setting natural596 (> \the\c@finalcolumnbadness)}%597 \fi598 \fi

If \@tempboxa above was not void our trial was un-successful and we report this fact and try again.

599 〈∗colbreak〉600 \else601 \too@badtrue602 \ifnum\c@tracingmulticols>\@ne603 \typeout{Rejected: unprocessed604 forced break(s) in last column!}%605 \fi606 \fi607 \fi608 〈/colbreak〉If the natural height of the first box is smaller thanthe current trial size but is larger than the previoustrial size it is likely that we have missed a potien-tially better solution. (This could have happened iffor some reason our first trial size was too high.) Inthat case we dismiss this trial and restart using thenatural height for the next trial.

609 \ifdim\ht\mult@nat@firstbox<\dimen@610 \ifdim\ht\mult@nat@firstbox>\last@try611 \too@badtrue612 \ifnum\c@tracingmulticols>\@ne613 \typeout{Retry: using natural614 height of first column!}%615 \fi616 \dimen@\ht\mult@nat@firstbox617 \last@try\dimen@618 \advance\dimen@-\p@619 \fi620 \fi

14With TEX version 3.141 it is now possible to use LATEX’s \newlinechar in the \message command, but people with olderTEX versions will now get ^^J instead of a new line on the screen.

20

Finally the switch too@bad is tested. If it was madetrue either earlier on or due to a rightmost columnbeing too large we try again with a slightly largervalue for \dimen@.

621 \iftoo@bad622 〈/badness〉623 \advance\dimen@\p@624 \repeat

At that point \dimen@ holds the height that was de-termined by the balancing loop. If that height forthe columns turns out to be larger than the availablespace (which is \@colroom) we sqeeze the columnsinto the space assuming that they will have enoughshrinkability to allow this.15

625 \ifdim\dimen@>\@colroom626 \dimen@\@colroom627 \fi

Then we move the contents of the odd-numbered

box registers to the even-numbered ones, shrinkingthem if requested. We have to use \vbox not \vtop(as it was done in the first versions) since otherwisethe resulting boxes will have no height (TEXbookpage 81). This would mean that extra \topskipis added when the boxes are returned to the page-builder via \page@sofar.

628 \process@cols\mult@rightbox629 {\@tempcnta\count@630 \advance\@tempcnta\@ne631 \setbox\count@\vbox to\dimen@632 {%

633 \vskip \z@634 \@plus-\multicolundershoot635 \@minus-\multicolovershoot636 \unvbox\@tempcnta637 \ifshr@nking\vfill\fi}}%638 }

4.5 The box allocations

Early releases of these macros used the first boxregisters 0, 2, 4,. . . for global boxes and 1, 3, 5,. . .for the corresponding local boxes. (You might stillfind some traces of this setup in the documentation,sigh.) This produced a problem at the moment wehad more than 5 columns because then officially allo-cated boxes were overwritten by the algorithm. Thenew release now uses private box registers

639 \newbox\mult@rightbox640 \newbox\mult@grightbox641 \newbox\mult@gfirstbox

642 \newbox\mult@firstbox643 \newbox\@tempa\newbox\@tempa644 \newbox\@tempa\newbox\@tempa645 \newbox\@tempa\newbox\@tempa646 \newbox\@tempa\newbox\@tempa647 \newbox\@tempa\newbox\@tempa648 \newbox\@tempa\newbox\@tempa649 \newbox\@tempa\newbox\@tempa650 \newbox\@tempa\newbox\@tempa651 \newbox\@tempa652 \let\@tempa\relax

5 New macros and hacks for version 1.2

If we don’t use TEX 3.0 \emergencystretch is un-defined so in this case we simply add it as an unused〈dimen〉 register.

653 \@ifundefined{emergencystretch}654 {\newdimen\emergencystretch}{}

My tests showed that the following for-mula worked pretty well. Nevertheless the\setemergencystretch macro also gets \hsize assecond argument to enable the user to try differentformulae.

655 \def\setemergencystretch#1#2{%656 \emergencystretch 4pt657 \multiply\emergencystretch#1}

Even if this should be used as a hook we use a @ inthe name since it is more for experts.

658 \def\set@floatcmds{%659 \let\@dblfloat\@dbflt660 \def\end@dblfloat{\par661 \vskip\z@662 \egroup663 \color@endbox664 \@largefloatcheck665 \outer@nobreak

This is cheap (defering the floats until after the cur-rent page) but any other solution would go deep intoLATEX’s output routine and I don’t like to work on ituntil I know which parts of the output routine haveto be reimplemented anyway for LATEX3.

666 \ifnum\@floatpenalty<\z@

15This might be wrong, since the shrinkability that accounts for the amount of material might be present only in somecolumns. But it is better to try then to give up directly.

21

We have to add the float to the \@deferlist be-cause we assume that outside the multicols environ-ment we are in one column mode. This is not en-tirely correct, I already used the multicols environ-ment inside of LATEXs \twocolumn declaration butit will do for most applications.

667 \@cons\@deferlist\@currbox668 \fi669 \ifnum\@floatpenalty=-\@Mii670 \@Esphack671 \fi}}

5.1 Maintaining the mark registers

This section contains the routines that set the marksso that they will be handled correctly. They havebeen introduced with version 1.4.

First thing we do is to reserve three macro namesto hold the replacement text for TEX’s primitives\firstmark, \botmark and \topmark. We initial-ize the first two to be empty and \kept@topmark tocontain two empty pair of braces. This is necessarysince \kept@topmark is supposed to contain the lastmark from a preceding page and in LATEX any “real”mark must contain two parts representing left andright mark information.

672 \def\kept@topmark{{}{}}673 \let\kept@firstmark\@empty674 \let\kept@botmark\@empty

Sometimes we want to return the value of a“kept” mark into a \mark node on the mainvertical list. This is done by the function\return@nonemptymark. As the name suggests itonly acts if the replacement text of the kept mark isnon-empty. This is done to avoid adding an emptymark when no mark was actually present. If wewould nevertheless add such a mark it would be re-garded as a valid \firstmark later on.

675 \def\return@nonemptymark#1#2{%676 \ifx#2\@empty677 \else

For debugging purposes we take a look at the valueof the kept mark that we are about to return. Thiscode will get stripped out for production.

678 〈∗marktrace〉679 \mult@info\tw@680 {Returned #1 mark:\MessageBreak681 \meaning#2}%682 % \nobreak683 % \fi684 〈/marktrace〉Since the contents of the mark may be arbitraryLATEX code we better make sure that it doesn’t getexpanded any further. (Some expansion have beendone already during the execution of \markright or

\markboth.) We therefore use the usual mechanismof a toks register to prohibit expansion.16

685 \toks@\expandafter{#2}%686 \mark{\the\toks@}%

We don’t want any breakpoint between such a re-turned mark and the following material (which isusually just the box where the mark came from).

687 \nobreak688 \fi}

If we have some material in a box register we maywant to get the first and the last mark out of thisbox. This can be done with \get@keptmarks whichtakes one argument: the box register number or itsnick name defined by \newbox.

689 \def\get@keptmarks#1{%

For debugging purposes we take a look at the cur-rent dimensions of the box since in earlier versionsof the code I made some mistakes in this area.

690 〈∗debug〉691 \typeout{Mark box #1 before:692 ht \the\ht#1, dp \the\dp#1}%693 〈/debug〉

Now we open a new group an locally copy the box toitself. As a result any operation, i.e. \vsplit, willonly have a local effect. Without this trick the boxcontent would get lost up to the level where the lastassignment to the box register was done.

694 \begingroup695 \vbadness\@M696 \setbox#1\copy#1%

Now we split the box to the maximal possible di-mension. This should split off the full contents ofthe box so that effectively everything is split off. Asa result \splitfirstmark and \splitbotmark willcontain the first and last mark in the box respec-tively.

697 \setbox#1\vsplit#1to\maxdimen

16Due to the current definition of \markright etc. it wouldn’t help to define the \protect command to prohibit expansionas any \protect has already vanished due to earlier expansions.

22

Therefore we can now set the kept marks which isa global operation and afterwards close the group.This will restore the original box contents.

698 \set@keptmarks699 \endgroup

For debugging we take again a look at the box di-mension which shouldn’t have changed.

700 〈∗debug〉701 \typeout{Mark box #1 \space after:702 ht \the\ht#1, dp \the\dp#1}%703 〈/debug〉704 }

The macro \set@keptmarks is responsible for set-ting \kept@firstmark and \kept@botmark, bychecking the current values for \splitfirstmarkand \splitbotmark.

705 \def\set@keptmarks{%

If \kept@firstmark is empty we assume that itisn’t set. This is strictly speaking not correct aswe loose the ability to have marks that are explic-itly empty, but for standard LATEX application it issufficient. If it is non-empty we don’t change thevalue—within the output routines it will then be re-stored to \@empty.

706 \ifx\kept@firstmark\@empty

We now put the contents of \splitfirstmark into\kept@firstmark. In the case that there wasn’tany mark at all \kept@firstmark will not changeby that operation.

707 \expandafter\gdef\expandafter708 \kept@firstmark709 \expandafter{\splitfirstmark}%

When debugging we show the assignment but onlywhen something actually happened.

710 〈∗marktrace〉711 \ifx\kept@firstmark\@empty\else712 \mult@info\tw@713 {Set kept first mark:\MessageBreak714 \meaning\kept@firstmark%715 \@gobbletwo}%716 \fi717 〈/marktrace〉718 \fi

We always try to set the bottom mark to the\splitbotmark but of course only when there hasbeen a \splitbotmark at all. Again, we assumethat an empty \splitbotmark means that the splitoff box part didn’t contain any marks at all.

719 \expandafter\def\expandafter\@tempa720 \expandafter{\splitbotmark}%721 \ifx\@tempa\@empty\else722 \global\let\kept@botmark\@tempa

723 〈∗marktrace〉724 \mult@info\tw@725 {Set kept bot mark:\MessageBreak726 \meaning\kept@botmark%727 \@gobbletwo}%728 〈/marktrace〉729 \fi}%

The \prep@keptmarks function is used to initializethe kept marks from the contents of \partial@page,i.e. the box that holds everything from the top of thecurrent page prior to starting the multicols environ-ment. However, such a box is only available if weare not producing a boxed multicols.

730 \def\prep@keptmarks{%731 \if@boxedmulticols \else732 \get@keptmarks\partial@page733 \fi}

734 \def\remove@discardable@items{%735 〈∗debug〉736 \edef\@tempa{s=\the\lastskip,737 p=\the\lastpenalty,738 k=\the\lastkern}%739 \typeout\@tempa740 〈/debug〉741 \unskip\unpenalty\unkern742 〈∗debug〉743 \edef\@tempa{s=\the\lastskip,744 p=\the\lastpenalty,745 k=\the\lastkern}%746 \typeout\@tempa747 〈/debug〉748 \unskip\unpenalty\unkern749 〈∗debug〉750 \edef\@tempa{s=\the\lastskip,751 p=\the\lastpenalty,752 k=\the\lastkern}%753 \typeout\@tempa754 〈/debug〉755 \unskip\unpenalty\unkern756 〈∗debug〉757 \edef\@tempa{s=\the\lastskip,758 p=\the\lastpenalty,759 k=\the\lastkern}%760 \typeout\@tempa761 〈/debug〉762 \unskip\unpenalty\unkern763 }

764 〈∗badness〉765 \newif\iftoo@bad

766 \newcount\c@columnbadness767 \c@columnbadness=10000768 \newcount\c@finalcolumnbadness769 \c@finalcolumnbadness=9999

23

770

771 \newdimen\last@try772

773 \newdimen\multicolovershoot774 \multicolovershoot=2pt775 \newdimen\multicolundershoot776 \multicolundershoot=2pt777 \newbox\mult@nat@firstbox778 〈/badness〉

A helper for producing info messages

779 \def\mult@info#1#2{%780 \ifnum\c@tracingmulticols>#1%781 \GenericWarning782 {(multicol)\@spaces\@spaces}%783 {Package multicol: #2}%784 \fi785 }

6 Fixing the \columnwidth

If we store the current column width in\columnwidth we have to redefine the internal\@footnotetext macro to use \textwidth for thewidth of the footnotes rather then using the originaldefinition.

Starting with version v1.5r this is now done in away that the original definition is still used, execptthat locally \columnwidth is set to \textwidth.

This solves two problems: first redefinitions of

\@footnotetext done by a class will correctlysurvive and second if multicols is used insidea minipage environment the special definition of\@footnotetext in that environment will be pickedup and not the one for the main galley (the lat-ter would result in all footnotes getting lost in thatcase).

See the definition of the \multicols commandfurther up for the exact code.

7 Further extensions

This section does contain code for extensions addedto this package over time. Not all of them may beactive, some might sit dormant and wait for beingactivated in some later release.

7.1 Not balancing the columns

This is fairly trivial to implement. we just have todisable the balancing output routine and replace itby the one that ships out the other pages.

The code for this environment was suggested byMatthias Clasen.

786 〈∗nobalance〉787 \@namedef{multicols*}{%

If we are not on the main galley, i.e., inside a boxof some sort, that approach will not work since wedon’t have a vertical size for the box so we betterwarn that we balance anyway.

788 \ifinner789 \PackageWarning{multicol}%790 {multicols* inside a box does791 not make sense.\MessageBreak792 Going to balance anyway}%793 \else794 \let\balance@columns@out795 \multi@column@out796 \fi797 \begin{multicols}798 }

When ending the environment we simply end the in-ner multicols environment, except that we betteralso stick in some stretchable vertical glue so thatthe last column still containing text is not verticallystretched out.

799 \@namedef{endmulticols*}{\vfill800 \end{multicols}}801 〈/nobalance〉

7.2 Manual column breaking

The problem with manual page breaks within multi-cols is the fact that during collection of material forall columns a page-forcing penalty (i.e. -10000 orhigher) would stop the collecting pass which is notquite what is desired. On the other hand, using apenalty like -9999 would mean that there would beoccasions where the \vspliting operations withinmulticols would ignore that penalty and still choosea different break point.

For this reason the current implementation usesa completely different approach. In a nutshell itextends the LATEX output routine handling by in-troducing an additional penalty flag (i.e., a penaltywhich is forcing but higher than -10000 so that theoutput routine can look at this value and thus knowswhy it has been called).

Inside the output routine we test for this valueand if it appears we do two things: save the galley

24

up to this point in a special box for later use and re-duce the \vsize by the height of the material seen.This way the forcing penalty is now hidden in thatbox and we can restart the collection process forthe remaining columns. (This is done in \speci@lsabove.)

In the output routines that do the \vsplittingeither for balancing or for a full page we simply com-bine box 255 with the saved box thus getting a singlebox for splitting which now contains forcing breaksin the right positions.

\columnbreak is modelled after \pagebreak exceptthat we generate a penalty -10005.

802 〈∗colbreak〉803 \mathchardef\@Mv=10005804 \def\columnbreak{%

We have to ensure that it is only used within a mul-ticols environment since if that penalty would beseen by the unmodified LATEX output routine strangethings would happen.

805 \ifnum\col@number<\tw@806 \PackageError{multicol}%807 {\noexpand\columnbreak outside multicols}%808 {This command can only be used within809 a multicols or multicols* environment.}%810 \else811 \ifvmode812 \penalty -\@Mv\relax813 \else814 \@bsphack815 \vadjust{\penalty -\@Mv\relax}%816 \@esphack817 \fi818 \fi}

Need a box to collect the galley up to the columnbreak.

819 \newbox\colbreak@box820 〈/colbreak〉821 〈/package〉

25

Index

Numbers written in italic refer to the page where the corresponding entry is described, the ones underlinedto the code line of the definition, the rest to the code lines where the entry is used.

Symbols\@footnotetext . . 786

B\balance@columns 474\balance@columns@out

. . . . . . . . . . 453

C\c@collectmore . . 241\c@columnbadness 766\c@finalcolumnbadness

. . . . . . . . . . 766\c@unbalance . . . . 242\col@number . . . . . 241\colbreak@box . . . 819\columnbreak . . . . 802\columnsep . . . . . . . 2\columnseprule . . . . 2

D\doublecol@number 241

E\emergencystretch 653

\endmulticols . . . 197\endmulticols* . . 799\enough@room . . . . 104

F\flushcolumns . . . 448

G\get@keptmarks . . 689

I\if@boxedmulticols 101\ifshr@nking . . . . 448\init@mult@footins 183

K\kept@botmark . . . 672\kept@firstmark . 672\kept@topmark . . . 672

L\leave@mult@footins

. . . . . . . . . . 398

M\mult@@cols . . . . . . 79

\mult@cols . . . . . . 76\mult@firstbox . . 639\mult@footnotetext 786\mult@gfirstbox . 639\mult@grightbox . 639\mult@info . . . . . 779\mult@rightbox . . 639\multi@column@out 299\multicol@leftmargin

. . . . . . . . . . 196\multicolbaselineskip

. . . . . . . . 2, 241\multicolpretolerance

. . . . . . . . 2, 241\multicols . . . . . . 56\multicols* . . . . . 786\multicolsep . . 2, 241\multicoltolerance

. . . . . . . . 2, 241

P\page@free . . . . . 241\page@sofar . . . . . 274\partial@page . . . 241

\postmulticols 2, 241\premulticols . 2, 241\prep@keptmarks . 730\prepare@multicols 121\process@cols . . . 265\process@deferreds 438

R\raggedcolumns . . 448\reinsert@footnotes

. . . . . . . . . . 297\remove@discardable@items

. . . . . . . . . . 734\return@nonemptymark

. . . . . . . . . . 675

S\set@floatcmds . . 658\set@keptmarks . . 705\set@mult@vsize . 187\setemergencystretch

. . . . . . . . . . 653\speci@ls . . . . . . 402

26