%D \module %D [ file=page-mul, % was: core-mul %D version=1998.03.15, %D title=\CONTEXT\ Page Macros, %D subtitle=Multi Column Output, %D author=Hans Hagen, %D date=\currentdate, %D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] %C %C This module is part of the \CONTEXT\ macro||package and is %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. \writestatus{loading}{ConTeXt Page Macros / Simple Multi Column} %D This module is mostly a copy from the original multi column %D routine as implemented in \type {core-mul}. When the main %D OTR macro's were isolated in modules and column sets were %D introduced, this module became part of the OTR modules. As %D a result this module is no longer generic. It also needs %D an overhaul. \unprotect % TO DO ! \let\OTRMULsetvsize \OTRONEsetvsize \let\OTRMULsethsize \OTRONEsethsize \let\OTRMULdopagecontents \OTRONEdopagecontents \let\OTRMULfinalsidefloatoutput\OTRONEfinalsidefloatoutput % ??? \let\OTRMULflushfloatbox \OTRONEflushfloatbox \let\OTRMULdosettopinserts \relax \let\OTRMULdosetbotinserts \relax \let\OTRMULdotopinsertions \relax \let\OTRMULdobotinsertions \relax \let\OTRMULdosetbothinserts \relax \let\OTRMULflushsavedfloats \relax \let\OTRMULflushsidefloats \forgetsidefloats % \relax \let\OTRMULsynchronizesidefloats\forgetsidefloats % \relax \newtoks \OTRMULoutput \def\OTRMULgotonextpage {\ejectpage} \def\OTRMULgotonextpageX % will become obsolete {\superejectpage} % check \count multiplications % some day try this in balancing routine % % \ifdim\pagetotal>\pagegoal % \eject % \else % \goodbreak % \fi %D The following macro's implement a multi||column output %D routine. The original implementation was based on Donald %D Knuth's implementation, which was adapted by Craig Platt to %D support balancing of the last page. I gradually adapted %D Platt's version to our needs but under certain %D circumstances things still went wrong. I considered all %D calls to Platt's \type{\balancingerror} as undesirable. %D This completely new implementation can handle enough %D situations for everyday documents, but is still far from %D perfect. While at the moment the routine doesn't support %D all kind of floats, it does support: %D %D \startitemize[packed] %D \item an unlimitted number of columns %D \item ragged or not ragged bottoms %D \item optional balancing without \type{\balancingerrors} %D \item different \type{\baselineskips}, \type{\spacing}, %D \type{\topskip} and \type{\maxdepth} %D \item left- and right indentation, e.g. within lists %D \item moving columns floats to the next column or page %D \item handling of floats that are to wide for a columns %D \stopitemize %D %D One could wonder why single and multi||columns modes are %D still separated. One reason for this is that \TeX\ is not %D suited well for handling multi||columns. As a result, the %D single columns routines are more robust. Handling one %D column as a special case of multi||columns is posible but at %D the cost of worse float handling, worse page breaking, %D worse etc. Complicated multi||column page handling should %D be done in \cap{DTP}||systems anyway. %D %D There are three commands provided for entering and leaving %D multi||column mode and for going to the next column: %D %D \interface \type{\beginmulticolumns} \\ \\ %D \interface \type{\endmulticolumns} \\ \\ %D \interface \type{\ejectcolumn} \\ \\ %D %D This routines are sort of stand||alone. They communicate %D with the rest of \CONTEXT\ by means of some interface %D macro's, which we only mention. %D %D \interface \type{\nofcolumns} \\ %D the number of columns \\ %D \interface \type{\minbalancetoplines} \\ %D the minimum number op balanced top lines \\ %D \interface \type{\betweencolumns} \\ %D the stuff between columns \\ %D \interface \type{\finaloutput{action}{box}} \\ %D some kind of \type{\pagebody} and \type{\shipout} \\ %D %D \interface \type{\ifbalancecolumns} \\ %D balancing the colums or not \\ %D \interface \type{\ifstretchcolumns} \\ %D ragging the bottom or not \\ %D %D \interface \type{\ifheightencolumns} \\ %D fix the heigh tor not \\ %D \interface \type{\fixedcolumnheight} \\ %D the optional fixed height \\ %D %D \interface \type{\ifinheritcolumns} \\ %D handle ragging or not \\ %D \interface \type{\ifr@ggedbottom} \\ %D use ragged bottoms \\ %D \interface \type{\ifb@selinebottom} \\ %D put the bottom line on the baseline \\ %D \interface \type{\ifnormalbottom} \\ %D put the bottom line at the baseline \\ %D %D \interface \type{\ifreversecolumns} \\ %D reverse the order in wich columns are flushed \\ %D %D \interface \type{\usercolumnwidth} \\ %D the calculated width of a column \\ %D \interface \type{\columntextwidth} \\ %D the maximum width of a column \\ %D \interface \type{\columntextheight} \\ %D the minimum width of a column \\ %D %D \interface \type{\spacingfactor} \\ %D the spacing factor \\ %D \interface \type{\bodyfontsize} \\ %D the (local) bodyfontsize \\ %D \interface \type{\openlineheight} \\ %D the lineheight (including \type{\spacing}) \\ %D %D \interface \type{\EveryBodyFont} \\ %D communication channel to font switching routines \\ %D %D \interface \type{\global\settopskip} \\ %D set \type{\topskip} \\ %D \interface \type{\setvsize} \\ %D set \type{\vsize} and \type{\pagegoal} \\ %D \interface \type{\sethsize} \\ %D set \type{\hsize} \\ %D %D \interface \type{\flushcolumnfloats} \\ %D push saved column floats (next page) \\ %D \interface \type{\flushcolumnfloat} \\ %D push saved column floats (next column) \\ %D \interface \type{\setcolumnfloats} \\ %D initialize column floats \\ %D %D \interface \type{\finishcolumnbox} \\ %D do something special (a hook) \\ %D \interface \type{\postprocesscolumnpagebox} \\ %D do something with each columnbox (also a hook) \\ %D \interface \type{\postprocesscolumnbox} \\ %D do something with each columnbox (also a hook) \\ %D \interface \type{\postprocesscolumnline} \\ %D do something with each columnline (also a hook) \\ %D \interface \type{\currentcolumn} \\ %D the current column \\ %D %D These interface macro's are called upon or initialized %D by the multi||column macro's. %D A lot of footnote stuff added! \def\finalcolumntextwidth {\makeupwidth} \def\finalcolumntextheight {\textheight} \def\columntextwidth {\makeupwidth} \def\columntextheight {\textheight} \def\usercolumnwidth {\textwidth} \def\columntextoffset {\!!zeropoint} \def\fixedcolumnheight {\textheight} \def\betweencolumns {\hskip\bodyfontsize} \let\setcolumnfloats \relax % in CONTEXT used for floats \let\flushcolumnfloats \relax % in CONTEXT used for floats \let\flushcolumnfloat \relax % in CONTEXT used for floats \let\finishcolumnbox \relax % in CONTEXT used for backgrounds % %D In fact, the column height and width are set by means of % %D two macro's. One can change their meaning if needed: % % \def\setcolumntextheight % {\def\columntextheight{\teksthoogte}} % % \def\setcolumntextwidth % {\def\columntextwidth{\zetbreedte}} %D Both macros are redefined in \CONTEXT\ when backgrounds %D are applied to columns. The final values are used when %D flushing the columns. \newtoks\singlecolumnout % remove that one %D It's more convenient to use \type {\columnwidth} instead %D of messing around with boxes each time. \newdimen\columnwidth \newdimen\gutterwidth \def\determinecolumnwidth {\bgroup \setbox\scratchbox\hbox {\setcolumnhsize \global\columnwidth\usercolumnwidth \global\gutterwidth\intercolumnwidth}% \egroup} %D Going to a new columns is done by means of a %D \type{\ejectcolumn}. The following definition does not %D always work. \def\ejectcolumn {\goodbreak\showmessage\m!columns2\empty} %D The next macro should never be called so let's deal with it. %D There were several solutions to these kind of errors. First %D we check for a good breakpoint before firing up the %D multi||column routine (\type{\break} or \type{\allowbreak}). %D We do the same at the end of the routine %D (\type{\allowbreak}). These allowances are definitely %D needed! %D %D Some on first sight redundant calls to for instance %D \type{\setvsize} in the flushing, splitting and balancing %D macro's can definitely not be omitted! Some are just there %D to handle situations that only few times arise. One of %D those can be that the output routine is invoked before %D everything is taken care of. This happens when we %D flush (part of) the current page with an \type{\unvbox} %D with a \type{\pagetotal}~$\approx$ \type{\pagegoal}. One %D simply cannot balance columns that are just balanced. %D %D I hope one never sees the following message. Because it %D took me a lot of time to develop the multi||columns %D routines, every (although seldom) warning gives me the %D creeps! \def\balancingerror {\showmessage\m!columns3\empty \finaloutput\unvbox\normalpagebox} \def\OTRMULsometopsfloat{\showmessage\m!columns4\empty \someherefloat} \def\OTRMULsomebotsfloat{\showmessage\m!columns5\empty \someherefloat} \def\OTRMULsomeherefloat{\OTRONEsomeherefloat} %D The local column width is available in the dimension %D register \type{\localcolumnwidth}, which is calculated as: \def\setcolumnhsize % beware, this one is available for use in macros {\setbox\scratchbox\hbox{\parindent\zeropoint\betweencolumns}% \intercolumnwidth\wd\scratchbox \localcolumnwidth\columntextwidth \advance\localcolumnwidth -\leftskip \advance\localcolumnwidth -\rightskip % new \advance\localcolumnwidth -\colleftskip \advance\localcolumnwidth -\colrightskip % \advance\localcolumnwidth -\nofcolumns\intercolumnwidth \advance\localcolumnwidth \intercolumnwidth \divide \localcolumnwidth \nofcolumns \scratchdimen\columntextoffset \multiply\scratchdimen \plustwo \advance\localcolumnwidth -\scratchdimen \usercolumnwidth\localcolumnwidth \hsize\localcolumnwidth} % we don't do it \global %D Torture test: %D %D \startbuffer %D \startbuffer[b] %D \startcolumns %D \input tufte %D \stopcolumns %D \stopbuffer %D \typebuffer[b] \getbuffer[b] %D %D \startbuffer[b] %D \startnarrower %D \input tufte %D \stopnarrower %D \stopbuffer %D \typebuffer[b] \getbuffer[b] %D %D \startbuffer[b] %D \startcolumns \startnarrower %D \input tufte %D \stopnarrower \stopcolumns %D \stopbuffer %D \typebuffer[b] \getbuffer[b] %D %D \startbuffer[b] %D \startnarrower \startcolumns %D \input tufte %D \stopcolumns \stopnarrower %D \stopbuffer %D \typebuffer[b] \getbuffer[b] %D %D \startbuffer[b] %D \startcolumns \startnarrower[left] %D \input tufte %D \stopnarrower \stopcolumns %D \stopbuffer %D \typebuffer[b] \getbuffer[b] %D %D \startbuffer[b] %D \startnarrower[left] \startcolumns %D \input tufte %D \stopcolumns \stopnarrower %D \stopbuffer %D \typebuffer[b] \getbuffer[b] %D %D \startbuffer[b] %D \startnarrower \startcolumns \startnarrower %D \input tufte %D \stopnarrower\stopcolumns \stopnarrower %D \stopbuffer %D \typebuffer[b] \getbuffer[b] %D %D \startbuffer[b] %D \startnarrower[left] \startcolumns \startnarrower %D \input tufte %D \stopnarrower\stopcolumns \stopnarrower %D \stopbuffer %D \typebuffer[b] \getbuffer[b] %D \stopbuffer %D %D \start %D \def\postprocesscolumnline#1{\ruledhbox{\strut\box#1}\hss} %D \getbuffer %D \stop %D One should be aware that when font related dimensions are %D used in typesetting the in||between material, these %D dimensions are influenced by bodyfont switches inside %D multi||column mode. \newdimen\mcscratchdimen \newcount\nofcolumnlines \chardef\multicolumnlinemethod\zerocount % 0: overshoot (old default), 1: tight % \chardef\multicolumnlinemethod\plusone \def\getmulticolumnlines {\mcscratchdimen-\columntextoffset \multiply\mcscratchdimen \plustwo \advance\mcscratchdimen \columntextheight \ifdim\precolumnboxheight>\zeropoint \advance\mcscratchdimen -\precolumnboxheight \fi \settotalinsertionheight \advance\mcscratchdimen -\totalinsertionheight \ifcase\multicolumnlinemethod \getnoflines\mcscratchdimen \or \getrawnoflines\mcscratchdimen \else \getrawnoflines\mcscratchdimen \fi % added 30/7/2004 \ifnum\layoutlines>\zerocount \ifnum\noflines>\layoutlines \noflines\layoutlines \fi \fi \nofcolumnlines\noflines} \def\multicolumnovershootratio{.5} % {\ifgridsnapping0\else.5\fi} \def\setcolumnvsize {\getmulticolumnlines \mcscratchdimen\nofcolumnlines\openlineheight \advance\mcscratchdimen \multicolumnovershootratio\openlineheight % collect enough data \global\vsize\nofcolumns\mcscratchdimen \global\pagegoal\vsize} % let's do it only here %D It really starts here. After some checks and initializations %D we change the output routine to continous multi||column %D mode. This mode handles columns that fill the current and %D next full pages. The method used is (more or less) %D multiplying \type{\vsize} and dividing \type{\hsize} by %D \type{\nofcolumns}. More on this can be found in the %D \TeX book. We save the top of the current page in box %D \type{\precolumnbox}. %D %D We manipulate \type{\topskip} a bit, just to be shure that %D is has no flexibility. This has te be done every time a %D font switch takles place, because \type{\topskip} can depend %D on this. %D %D Watch the trick with the \type{\vbox}. This way we get the %D right interlining and white space. \def\beginmulticolumns {\par \flushnotes \xdef\precolumndepth{\the\prevdepth}% \begingroup % new \leftskip1\leftskip \rightskip1\rightskip \edef\colleftskip {\the\leftskip}% \edef\colrightskip{\the\rightskip}% \leftskip\zeropoint \rightskip\zeropoint % \dontshowcomposition %\setcolumntextwidth\relax %\setcolumntextheight\relax \widowpenalty\zerocount % is gewoon beter \clubpenalty \zerocount % zeker bij grids \ifsomefloatwaiting \showmessage\m!columns6{\the\savednoffloats}% \global\setbox\savedfloatlist\box\floatlist \xdef\restoresavedfloats {\global\savednoffloats\the\savednoffloats \global\setbox\floatlist\box\savedfloatlist \global\noexpand\somefloatwaitingtrue}% \global\savednoffloats\zerocount \global\somefloatwaitingfalse \else \global\let\restoresavedfloats\relax \fi \dimen0\pagetotal \advance\dimen0 \parskip \advance\dimen0 \openlineheight \ifdim\dimen0<\pagegoal \allowbreak \else \break % Sometimes fails \fi \appendtoks\topskip1\topskip\to\everybodyfont \the\everybodyfont % ugly here \saveinterlinespace % ugly here \initializecolumns\nofcolumns \hangafter\zerocount \hangindent\zeropoint \everypar\emptytoks \ifdim\pagetotal=\zeropoint \else \verticalstrut \vskip-\struttotal \fi \global\savedpagetotal\pagetotal \global\singlecolumnout\output %\global\output{\global\setbox\precolumnbox\vbox{\unvbox\normalpagebox}}% \global\output{\global\setbox\precolumnbox\vbox{\dotopinsertions\unvbox\normalpagebox}}% \eject % no \holdinginserts=1, can make footnote disappear ! \global\precolumnboxheight\ht\precolumnbox \global\output{\continuousmulticolumnsout}% \setcolumnfloats \dohandleallcolumns {\global\setbox\currenttopcolumnbox\emptybox}% \checkbegincolumnfootnotes \activateotr{MUL}{ONE}% todo ! ! ! ! \let\sethsize\setcolumnhsize \let\setvsize\setcolumnvsize \sethsize \setvsize \showcomposition} %D When we leave the multi||column mode, we have to process the %D not yet shipped out part of the columns. When we don't %D balance, we simply force a continuous output, but a balanced %D output is more tricky. %D First we try to fill up the page and when all or something %D is left we try to balance things. This is another useful %D adaption of the ancesters of these macro's. It takes some %D reasoning to find out what happens and maybe I'm making %D some mistake, but it works. %D %D Voiding box \type{\precolumnbox} is sometimes necessary, %D e.g. when there is no text given between \type{\begin..} %D and \type{\end..}. The \type{\par} is needed! \chardef\multicolumnendsyncmethod\plusone % 1: old sync 2: new sync (cont-loc/project) / may fail ! ! ! ! \def\endmulticolumns {%\par \ifnum\multicolumnendsyncmethod=\plustwo \synchronizeoutput \else % don't combine these \vskip\lineheight \vskip-\lineheight % take footnotes into account \fi \dontshowcomposition \doflushcolumnfloat % added recently %\doflushcolumnfloats % no, since it results in wrong top floats \flushnotes % before start of columns \par \ifbalancecolumns \ifnum\multicolumnendsyncmethod=\plusone \global\output{\continuousmulticolumnsout}% \goodbreak \fi \global\output{\balancedmulticolumnsout}% \else \goodbreak \fi \eject % the prevdepth is important, try e.g. toclist in \prevdepth\zeropoint % columns before some noncolumned text text \global\output\singlecolumnout \global\output{\the\mainoutput}% % % % % todo \ifvoid\precolumnbox\else \unvbox\precolumnbox \fi \global\precolumnboxheight\zeropoint \endgroup % here \nofcolumns\plusone \setvsize % the outer one! \synchronizeoutput % new may 2004 / we need to: \pagegoal\vsize \checkendcolumnfootnotes \dosomebreak\allowbreak \restoresavedfloats} %D Because some initializations happen three times, we %D defined a macro for them. Erasing \type{\everypar} is %D needed because we don't want anything to interfere. \def\setmulticolumnsout {\everypar\emptytoks \dontcomplain \settopskip \setmaxdepth \topskip1\topskip \splittopskip\topskip \splitmaxdepth\maxdepth \boxmaxdepth\maxdepth % dangerous \emergencystretch\zeropoint\relax} % sometimes needed ! %D Flushing the page comes to pasting the columns together and %D appending the result to box \type{\precolumnbox}, if not %D void. I've seen a lot of implementations in which some skip %D was put between normal text and multi||column text. When we %D don't want this, the baselines can be messed up. I hope the %D seemingly complicated calculation of a correction %D \type{\kern} is adequate to overcome this. Although not %D watertight, spacing is taken into account and even multiple %D mode changes on one page go well. But cross your fingers and %D don't blame me. %D %D One of the complications of flushing out the boxes is that %D \type{\precolumnbox} needs to be \type{\unvbox}'ed, otherwise %D there is too less flexibility in the page when using %D \type{\r@ggedbottom}. It took a lot of time before these %D kind of problems were overcome. Using \type{\unvbox} at the %D wrong moment can generate \type{\balancingerror}'s. %D %D One can use the macros \type {\maxcolumnheight} and \type %D {\maxcolumndepth} when generating material between columns %D as well as postprocessing column lines. \let\maxcolumnheight=\zeropoint \let\maxcolumndepth =\zeropoint \newbox\columnpagebox \def\setmaxcolumndimensions {\let\maxcolumnheight\!!zeropoint \let\maxcolumndepth \!!zeropoint \dohandleallcolumns {\ifdim\ht\currentcolumnbox>\maxcolumnheight \edef\maxcolumnheight{\the\ht\currentcolumnbox}% \fi \ifdim\dp\currentcolumnbox>\maxcolumndepth \edef\maxcolumndepth{\the\dp\currentcolumnbox}% \fi}} \chardef\multicolumntopflushmethod\plusone % 0: no correction, 1: correction when topstuff, 2: correction, 3: correction++ \chardef\multicolumntopalignmethod\plustwo % 0: nothing, 1: force grid, 2: follow grid \def\flushprecolumnboxnogrid {\unvbox\precolumnbox} \def\flushprecolumnboxongrid {\scratchdimen\savedpagetotal \advance\scratchdimen -\ht\precolumnbox \advance\scratchdimen -\dp\precolumnbox \advance\scratchdimen -\topskip \box\precolumnbox \kern\scratchdimen} \newconditional\someprecolumncontent \def\flushcolumnedpage#1% {\bgroup \ifvoid\precolumnbox \setfalse\someprecolumncontent % will be set elsewhere \else \settrue\someprecolumncontent \mkprocessboxcontents\precolumnbox \fi \forgetall \setmulticolumnsout \showcomposition \setmaxcolumndimensions \dohandleallcolumns {\mkprocesscolumncontents\currentcolumnbox}% \postprocesscolumns \dohandleallcolumns % \hbox i.v.m. \showcomposition {\global\setbox\currentcolumnbox\hbox to \localcolumnwidth {\box\currentcolumnbox}% \wd\currentcolumnbox\localcolumnwidth \ifheightencolumns \ht\currentcolumnbox\fixedcolumnheight \fi}% \setmaxcolumndimensions \overlaycolumnfootnotes \setbox\columnpagebox\vbox {\hbox to \finalcolumntextwidth {\hskip\colleftskip\relax % new, \relax needed \ifreversecolumns \popsplitproperties % else wrong color stack \@EA\dohandlerevcolumns \else \@EA\dohandleallcolumns \fi {\finishcolumnbox{\hbox {\ifx\finishcolumnbox\relax\else\strut\fi \box\currentcolumnbox}}% \hfil}% \unskip \hskip\colrightskip}}% new \scratchdimen\zeropoint \dohandleallcolumns {\ifdim-\ht\currenttopcolumnbox<\scratchdimen \scratchdimen-\ht\currenttopcolumnbox \fi \global\setbox\currenttopcolumnbox\emptybox}% \advance\scratchdimen \ht\columnpagebox \setbox\scratchbox\hbox to \columntextwidth {\vrule \!!width\zeropoint \!!height\scratchdimen \!!depth\dp\columnpagebox \dostepwiserecurse2\nofcolumns1{\hfil\betweencolumns}\hfil}% \setbox\columnpagebox\hbox {\box\columnpagebox \hskip-\columntextwidth \restoretextcolor{\box\scratchbox}}% \postprocesscolumnpagebox % new, acts upon \box\columnpagebox \ifconditional\someprecolumncontent \settrue\someprecolumncontent % next some incredible crappy code \ifcase\multicolumntopalignmethod \flushprecolumnboxnogrid % not on grid \or \flushprecolumnboxongrid % force on grid \else\ifgridsnapping % somehow this junk fails in pascal \flushprecolumnboxongrid % obey grid settings, force on grid \else \flushprecolumnboxnogrid % ignore grid settings, not on grid \fi \fi \fi \global\precolumnboxheight\zeropoint \setvsize \dosomebreak\nobreak % hm, only needed when topstuff \ifgridsnapping \else \ifcase\multicolumntopflushmethod % sometimes method 1 goes wrong, so we need a way out; best sort this out % when we run into it again \or % \input tufte \startcolumns \showbaselines \input tufte \stopcolumns \input tufte \ifconditional\someprecolumncontent % \scratchdimen\topskip % \advance\scratchdimen -\openstrutheight % \nointerlineskip % \vskip-\scratchdimen \nointerlineskip \vskip\dimexpr\openstrutheight-\topskip\relax \fi \or % \scratchdimen\topskip % \advance\scratchdimen -\openstrutheight % \nointerlineskip % \vskip-\scratchdimen \nointerlineskip \vskip\dimexpr\openstrutheight-\topskip\relax \or % untested but maybe handy % \scratchdimen\topskip % \advance\scratchdimen -\openstrutheight % \nointerlineskip % \vskip-\scratchdimen % \vskip-\lineheight % \vbox{\strut}% \nointerlineskip \vskip\dimexpr\openstrutheight-\topskip-\lineheight\relax \vbox{\strut}% \fi \fi \prevdepth\openstrutdepth \nointerlineskip \dp\columnpagebox\zeropoint \global\finalcolumnheights\ht\columnpagebox \getnoflines\finalcolumnheights \global\finalcolumnlines\noflines \ifcase#1\else % messy correction, we need to rewrite this module (newcolumns) \setbox\columnpagebox\vbox {\offinterlineskip \scratchdimen\ht\columnpagebox \advance\scratchdimen\dp\columnpagebox % we probably lost that one already \box\columnpagebox \vskip-\scratchdimen}% \scratchdimen\noflines\openlineheight \advance\scratchdimen-\openstrutdepth \advance\scratchdimen-\openlineheight \advance\scratchdimen\topskip \ht\columnpagebox\scratchdimen \dp\columnpagebox\openstrutdepth % end of mess \fi \box\columnpagebox \egroup} %D In case one didn't notice, finaly \type{\finishcolumnbox} is %D applied to all boxes. One can use these hooks for special %D purposes. %D %D Once upon a time I wanted to manipulate the individual lines %D in a column. This feature is demonstrated in the two examples %D below. %D %D \startbuffer %D \def\postprocesscolumnline#1% or \postprocesscolumnbox %D {\ruledhbox{\box#1}\hss} %D %D \startcolumns[n=4] %D \dorecurse{25}{line: \recurselevel\par} %D \stopcolumns %D \stopbuffer %D %D \typebuffer %D %D Here we show the natural width of the lines: %D %D {\getbuffer} %D %D The next example does a bit more advanced manipulation: %D %D \startbuffer %D \def\postprocesscolumnline#1% %D {\ifodd\currentcolumn %D \hfill\unhbox#1\relax %D \else %D \relax\unhbox#1\hfill %D \fi} %D %D \startcolumns[n=4] %D \dorecurse{25}{line \recurselevel\par} %D \stopcolumns %D \stopbuffer %D %D \typebuffer %D %D Here we also see an application of \type{\currentcolumn}: %D %D {\getbuffer} %D %D This feature is implemented using the reshape macros %D presented in \type{supp-box}. \def\postprocesscolumns {\ifx\postprocesscolumnline\undefined \else \dohandleallcolumns {\global\setbox\currentcolumnbox\vtop {\beginofshapebox \unvbox\currentcolumnbox \unskip\unskip \endofshapebox \reshapebox {\dimen0\ht\shapebox \dimen2\dp\shapebox \setbox\shapebox\hbox to \hsize {\postprocesscolumnline\shapebox}% \ht\shapebox\dimen0 \dp\shapebox\dimen2 \box\shapebox}% \flushshapebox \everypar\emptytoks \parskip\zeropoint % = \forgetall \verticalstrut \vskip-\struttotal \vfil}}% \fi \ifx\postprocesscolumnbox\undefined \else \dohandleallcolumns {\global\setbox\currentcolumnbox\hbox {\postprocesscolumnbox\currentcolumnbox}} \fi} %D We default to doing nothing! \let\postprocesscolumnline =\undefined \let\postprocesscolumnbox =\undefined \let\postprocesscolumnpagebox=\relax %D \macros %D {reversecolumnstrue} %D %D We can force the macro that takes care of combining %D the columns, to flush them in the revere order. Of %D course, by default we don't reverse. \newif\ifreversecolumns %D Here comes the simple splitting routine. It's a bit %D longer than expected because of ragging bottoms or not. %D This part can be a bit shorter but I suppose that I will %D forget what happens. The splitting takes some already %D present material (think of floats) into account! %D %D First we present some auxiliary routines. Any material, %D like for instance floats, that is already present in the %D boxes is preserved. \def\splitcolumn#1from \box#2to \dimen#3 top \box#4% {\bgroup \ifdim\ht#4>\zeropoint \dimen0\dimen#3\relax \dimen2\dimen0 \advance\dimen0 -\ht#4% \columnfootnotecorrection{#1}{\dimen0}% \setbox0\vsplit#2 to \dimen0 \global\setbox#1\vbox to \dimen2 {\ifgridsnapping \dimen0-\openstrutheight \advance\dimen0 \topskip \vskip\dimen0\copy#4\vskip-\dimen0 \else \unvcopy#4% \fi \fuzzysnappedbox\unvbox0 \fakecolumnfootnotes{#1}}% \else \ifcase\clevernotes \global\setbox#1\vsplit#2 to \dimen#3% \global\setbox#1\vbox {\fuzzysnappedbox\unvbox{#1}}% % or \box ? \else \columnfootnotecorrection{#1}{\dimen#3}% \setbox0\vsplit#2 to \dimen#3% \global\setbox#1\vbox to \dimen#3% {\fuzzysnappedbox\unvbox0 \fakecolumnfootnotes{#1}}% \fi \fi \egroup} \def\splitcurrentcolumn from \box#1to \dimen#2% {\splitcolumn\currentcolumnbox from \box#1 to \dimen#2 top \box\currenttopcolumnbox} \def\splitfirstcolumn from \box#1to \dimen#2% {\splitcolumn\firstcolumnbox from \box#1 to \dimen#2 top \box\firsttopcolumnbox} \def\splitlastcolumn from \box#1to \dimen#2% {\global\setbox\lastcolumnbox\vbox {\unvcopy\lasttopcolumnbox \fuzzysnappedbox\unvbox{#1}% \fakecolumnfootnotes\lastcolumnbox}} %D NEW: still to be documented. \def\fakecolumnfootnotes#1% {\relax \ifcase\clevernotes\else \ifnum#1=\lastcolumnbox \fakenotes \fi \fi} \def\columnfootnotecorrection#1#2% {\relax \ifcase\clevernotes % page notes \or \ifnum#1=\firstcolumnbox\relax \calculatetotalclevernoteheight \advance#2 -\totalnoteheight \fi \else \ifnum#1=\lastcolumnbox\relax \calculatetotalclevernoteheight \advance#2 -\totalnoteheight \fi \fi} \def\overlaycolumnfootnotes {\relax \ifcase\clevernotes % page notes \else \checknotepresence \ifnotespresent % the note box has the depth of the notefont % because a column (i.e. first column has no depth, % we need to anchor top down) \bgroup \ifcase\clevernotes\or \getmulticolumnlines \advance\nofcolumnlines \minustwo \scratchdimen\nofcolumnlines\lineheight \advance\scratchdimen \topskip \setbox0\hbox {\lower\scratchdimen\vbox{\placenoteinserts}}% \ht0=\openstrutheight % \strutht \dp0=\openstrutdepth % \strutdp \scratchdimen\ht\firstcolumnbox \global\setbox\firstcolumnbox\vbox to \scratchdimen {\box\firstcolumnbox \vskip-\scratchdimen \restoretextcolor{\box0}}% \else % maybe here also \getmulticolumnlines \scratchdimen\ht\firstcolumnbox \advance\scratchdimen -\openstrutdepth % \strutdp \getnoflines\scratchdimen \advance\noflines \minustwo \scratchdimen\noflines\lineheight \advance\scratchdimen \topskip \setbox0\hbox {\lower\scratchdimen\vbox{\placenoteinserts}}% \ht0=\openstrutheight % \strutht \dp0=\openstrutdepth % \strutdp \scratchdimen\ht\lastcolumnbox \global\setbox\lastcolumnbox\vbox to \scratchdimen {\box\lastcolumnbox \vskip-\scratchdimen \restoretextcolor{\box0}}% \fi \egroup \fi \fi} %D Here comes the routine that splits the long box in columns. %D The macro \type{\flushcolumnfloats} can be used to flush %D either floats that were present before the multi||column %D mode was entered, or floats that migrate to next columns. %D Flushing floats is a delicate process. \def\continuousmulticolumnsout {\bgroup \forgetall \setmulticolumnsout \dontshowcomposition % \dimen0=\columntextheight % \advance\dimen0 -\precolumnboxheight % \settotalinsertionheight % \advance\dimen0 -\totalinsertionheight % \ifgridsnapping % evt altijd, nog testen % \getnoflines{\dimen0} % \dimen0=\noflines\openlineheight % \fi \getmulticolumnlines \dimen0=\nofcolumnlines\openlineheight \dohandleallcolumns {\splitcurrentcolumn from \box\normalpagebox to \dimen0}% \setbox\restofpage\vbox{\unvbox\normalpagebox}% \ifinheritcolumns \ifr@ggedbottom % vreemd \dohandleallcolumns {\global\setbox\currentcolumnbox\vbox to \ht\firstcolumnbox {\dimen0\dp\currentcolumnbox \unvbox\currentcolumnbox \vskip-\dimen0 \vskip\openstrutdepth % \strutdp \prevdepth\openstrutdepth % \strutdp \vfill}}% \ifbottomnotes \else \dimen0\ht\firstcolumnbox \fi \fi \ifn@rmalbottom \advance\dimen0 \maxdepth \dohandleallcolumns {\global\setbox\currentcolumnbox\vbox to \dimen0 {\unvbox\currentcolumnbox}}% \fi \ifb@selinebottom % the columns are on top of the baseline \fi \else \dohandleallcolumns {\global\setbox\currentcolumnbox\vbox to \dimen0 {\ifstretchcolumns \unvbox\currentcolumnbox \else \unvbox\currentcolumnbox % wel of niet \unvbox ? \vfill \fi}}% \dohandleallcolumns {\ht\currentcolumnbox\dimen0}% redundant \fi \setbox\precolumnbox\vbox{\flushcolumnedpage\zerocount}% \finaloutput\box\precolumnbox \sethsize \setvsize \flushcolumnfloats \unvbox\restofpage % \penalty\outputpenalty % gaat gruwelijk mis in opsommingen \egroup} %D And this is the balancing stuff. Again, part of the routine %D is dedicated to handling ragged bottoms, but here we also %D see some handling concerning the stretching of columns. %D We set \type{\widowpenalty} at~0, which enables us to %D balance columns with few lines. The use of \type{\box2} and %D \type{\box4} garantees a more robust check when skips are %D used. \def\multicolumnsbalancemax{250} % 100 is too small when floats are involved \def\balancedmulticolumnsout {\bgroup \setmulticolumnsout \dontshowcomposition \widowpenalty\zerocount \setbox0\vbox{\unvbox\normalpagebox}% \ifdim\ht0>\openlineheight % at least one line \ifnum\minbalancetoplines<2 % balance anyway \donetrue \else % check criterium to available lines \getnoflines{\ht0}% \divide\noflines \nofcolumns \relax \ifnum\noflines<\minbalancetoplines \relax \dimen0\ht0 \advance\dimen0 \ht\firsttopcolumnbox \advance\dimen0 \openlineheight \relax % let's play safe \ifdim\dimen0>\columntextheight % column exceeding text height \donetrue \else % it seems to fit \donefalse \fi \else % balance indeed \donetrue \fi \fi \else % balancing does not make sense \donefalse \fi \ifdone % start balancing %\ifdim\ht0>\openlineheight \dimen0\ht0 \advance\dimen0 \topskip \advance\dimen0 -\baselineskip \dohandleallcolumns {\advance\dimen0 \ht\currenttopcolumnbox}% \divide\dimen0 \nofcolumns \vbadness\!!tenthousand\relax \count255=\zerocount \bgroup \ifgridsnapping \dimen2\lineheight \else \dimen2=\onepoint % RUBISH \dimen2=\spacingfactor\dimen2 \fi \doloop {\advance\count255 \plusone \global\setbox\restofpage\copy0\relax \splitfirstcolumn from \box\restofpage to \dimen0 \dohandlemidcolumns {\splitcurrentcolumn from \box\restofpage to \dimen0}% \splitlastcolumn from \box\restofpage to \dimen0 \setbox2\vbox{\unvcopy\firstcolumnbox}% \dimen4\zeropoint \dohandleallcolumns {\setbox4\vbox {\unvcopy\currentcolumnbox %rather new, test this on pdftex-z.tex \unpenalty\unskip\unpenalty\unskip}% maybe better in main splitter %\writestatus{balance}{\the\currentcolumnbox: \the\ht4}% % \dimen6\ht4 \ifdim\dimen6>\dimen4 \dimen4=\dimen6 \fi}% \ifdim\ht4>\dimen4 \dimen4=\ht4 \fi}% \advance\dimen4 -.0005pt % get rid of accurracy problem, pretty new \ifnum\count255>\multicolumnsbalancemax\relax \exitloop \else\ifdim\dimen4>\ht2 \advance\dimen0 \dimen2\relax \else \exitloop \fi\fi}% \dohandleallcolumns {\global\setbox\currentcolumnbox\vbox{\unvcopy\currentcolumnbox}}% NIEUW \ifnum\count255>\multicolumnsbalancemax\relax \showmessage\m!columns7\empty \else \showmessage\m!columns8{\the\count255\space}% \fi \egroup \ifinheritcolumns % We cannot assume that the first column is the tallest, if % only because we may have an aborted balance (one line in the % first column and a graphic in the second one). % % \dimen0\ht\firstcolumnbox % \dimen2\ht\firstcolumnbox % \dimen0=\zeropoint \dohandleallcolumns {\ifdim\ht\currentcolumnbox>\dimen0 \dimen0=\ht\currentcolumnbox \fi}% \dimen2\dimen0 % so far \advance\dimen2 -\openlineheight \dohandleallcolumns {\dimen4\ht\currentcolumnbox \dimen6=10\openlineheight % funny value \global\setbox\currentcolumnbox\vbox to \dimen0 {\unvbox\currentcolumnbox \ifdim\dimen4>\dimen6 \ifdim\dimen4<\dimen0 \ifdim\dimen4>\dimen2 \vskip\zeropoint % !! \else \vskip\openlineheight \vfill \fi \else \vskip\zeropoint \fi \else \vskip\openlineheight \vfill \fi}}% \else \bgroup \ifstretchcolumns \dimen0\ht\firstcolumnbox \dimen2=\bottomtolerance\ht\firstcolumnbox \setbox0\vbox{\unvcopy\lastcolumnbox}% \advance\dimen0 -\ht0\relax \advance\dimen0 -\dp0\relax \ifdim\dimen0>\openlineheight\relax \ifdim\dimen0>\dimen2\relax % \stretchcolumnsfalse % beter goed slecht dan slecht goed \showmessage\m!columns9\empty \fi \fi \fi \dohandleallcolumns {\global\setbox\currentcolumnbox\vbox to \ht\firstcolumnbox {\ifstretchcolumns \unvbox\currentcolumnbox \else \box\currentcolumnbox \vfill \fi}}% \egroup \fi \else % a one liner is not properly handled here, so best rewrite the text then \showmessage\m!columns{10}\empty \global\setbox\firstcolumnbox\vbox{\unvbox0}% \fi \global\output{\balancingerror}% \b@selinebottomtrue % forces depth in separation rule \flushcolumnedpage\plusone \multicolumnseject \egroup} \def\multicolumnseject {%\ifdim\pagetotal>\textheight % \eject % new, but wrong as fails on mixed-001.tex (wrong pagetotal at this point) %\else \allowbreak }%\fi} %D The multicolumn mechanism is incorporated in a \CONTEXT\ %D interface, which acts like: %D %D \starttyping %D \startcolumns[n=4,balance=no] %D some text %D \stopcolumns %D \stoptyping %D %D The setup is optional. The default behaviour of columns %D can be set up with: %D %D \starttyping %D \setupcolumns %D [n=2, %D balance=yes] %D \stoptyping %D %D In this case, stretching is according to the way it's %D done outside columns (\type{\inheritcolumnstrue}). Also %D we can setup the \type{tolerance} within a column, the %D \type{distance} between columns and the fixed %D \type{height} of a column. %D Multi||column output: the float routines %D %D Here come the routines that handle the placement of column %D floats. Floats that are to big migrate to the next %D column. Floats that are too wide, migrate to the top of the %D next page, where they span as much columns as needed. %D Floats that are left over from outside the multi||column %D mode are flushed first. In macro \type{\finaloutput} the %D topfloats that are left from previous text should be set. %D %D When there are some floats in the queue, we inhibit the %D flushing of floats on top of columns. The number of %D waiting floats is preswent in \type{\savednoftopfloats} and %D is saved. As long as there are floats waiting, the topfloats %D are places as if we are outside multi||column mode. This is %D neccessary for e.g. multicolumn lists. %D %D When all those floats are flushed, we switch to the local %D flushing routine. \def\setcolumnfloats {\xdef\globalsavednoffloats{\the\savednoffloats}% \ifnum\globalsavednoffloats>\zerocount \setglobalcolumnfloats \else \setlocalcolumnfloats \fi} \def\setglobalcolumnfloats {\everypar\emptytoks \let\flushcolumnfloat\relax %\let\doroomfloat\relax \let\docheckiffloatfits\relax \let\flushcolumnfloats\noflushcolumnfloats} \def\setlocalcolumnfloats {\everypar{\flushnotes\flushcolumnfloat\flushmargincontents\checkindentation}% \let\flushcolumnfloat\doflushcolumnfloat %\let\doroomfloat\docolumnroomfloat \let\docheckiffloatfits\docolumnroomfloat \let\flushcolumnfloats\doflushcolumnfloats \let\doflushfloats\doflushcolumnfloats % new \let\dosetbothinserts\relax \let\dotopinsertions\relax} \def\noflushcolumnfloats {\bgroup \xdef\localsavednoffloats{\the\savednoffloats}% \global\savednoffloats\globalsavednoffloats \dotopinsertions \xdef\globalsavenoffloats{\the\savednoffloats}% \ifnum\globalsavednoffloats=\zerocount \setlocalcolumnfloats \fi \global\savednoffloats\localsavednoffloats \egroup} %D We need to calculate the amount of free space in a columns. %D When there is not enough room, we migrate the float to the %D next column. These macro's are alternatives (and %D look||alikes) of \type{\doroomfloat}. When a float is to %D wide, for one column, it is moved to the top of the next %D page. Of course such moved floats have to be taken into %D account when we calculate the available space. It's a pitty %D that such things are no integral part of \TEX. \def\getcolumnstatus\column#1\total#2\goal#3\\% {\dimen0=\ifdim\pagegoal<\maxdimen \pagetotal \else \zeropoint \fi \dimen2=\zeropoint \count255=\zerocount \dimen8=\columntextheight \advance\dimen8 -\precolumnboxheight \def\dogetcolumnstatus {\advance\count255 \plusone \advance\dimen2 \ht\currenttopcolumnbox \advance\dimen2 \dp\currenttopcolumnbox \dimen4\dimen2 \advance\dimen4 \dimen0 \dimen6=\count255\dimen8 \ifdim\dimen4>\dimen6 \else \let\dogetcolumnstatus\relax \fi}% \dohandleallcolumns{\dogetcolumnstatus}% \ifnum\count255=0 \count255=1 \fi #1=\count255 #2=\dimen4 #3=\dimen6 } \def\getinsertionheight {\ifdim\pagegoal<\maxdimen \bgroup \dimen0=\columntextheight \advance\dimen0 -\pagegoal \xdef\insertionheight{\the\dimen0}% \egroup \else \global\let\insertionheight\zeropoint \fi} \def\docolumnroomfloat {\ifpostponecolumnfloats \global\roomforfloatfalse \else\ifnofloatpermitted \global\roomforfloatfalse \else \bgroup \getcolumnstatus\column\count255\total\dimen0\goal\dimen2\\% \advance\dimen0 2\openlineheight % nog nodig ? %\ifnum\count255=\nofcolumns % \getinsertionheight % %\message{\insertionheight}\wait % \advance\dimen0 \insertionheight %\fi \setbox\scratchbox\vbox % tricky met objecten ? {\blank[\@@bkspacebefore] \snaptogrid\vbox{\copy\floatbox}}% \advance\dimen0 \ht\scratchbox \advance\dimen0 .5\lineheight % needed because goal a bit higher %\message{column: \the\count255; total: \the\dimen0; goal: \the\dimen2}\wait \ifdim\dimen0>\dimen2 \global\roomforfloatfalse \else \global\roomforfloattrue \fi \ifdim\wd\floatbox>\hsize \showmessage\m!columns{11}\empty \global\roomforfloatfalse \fi \egroup \fi\fi} %D Flushing one float is done as soon as possible, i.e. %D \type{\everypar}. This means that (at the moment) %D sidefloats are not supported (overulled)! \newif\ifflushingcolumnfloats \flushingcolumnfloatstrue \def\doflushcolumnfloat {\ifpostponecolumnfloats\else\ifflushingcolumnfloats\ifprocessingverbatim\else\ifsomefloatwaiting \bgroup \forgetall \let\doflushcolumnfloat\relax \getcolumnstatus\column\mofcolumns\total\dimen0\goal\dimen2\\% \ifdim\dimen0>\zeropoint \dogetfloat \ifdim\wd\floatbox>\hsize \doresavefloat \else %\setbox2=\vbox % {\blank[\@@bkspacebefore] % \snaptogrid\vbox{\copy\floatbox}% % \blank[\@@bkspaceafter] \setbox2=\vbox {\blank[\@@bkspacebefore] \snaptogrid\vbox{\copy\floatbox}}% \advance\dimen0 \ht2 \ifdim\dimen0>\dimen2 \ifnum\mofcolumns<\nofcolumns \advance\mofcolumns \plusone %% bug %% \edef\currenttopcolumnbox{\getvalue{\@@topcol\the\count255}}% \ifdim\ht\currenttopcolumnbox=\zeropoint \global\setbox\currenttopcolumnbox\vbox {\snaptogrid\vbox{\copy\floatbox} \whitespace % nodig ? \blank[\@@bkspaceafter]}% \dimen4=\ht\currenttopcolumnbox \advance\dimen4 \dp\currenttopcolumnbox \global\advance\vsize -\dimen4 \advance\dimen4 -\pagegoal \global\pagegoal-\dimen4 \showmessage\m!columns{12}a% \else \showmessage\m!columns{12}b% \doresavefloat \fi \else \showmessage\m!columns{12}c% \doresavefloat \fi \else \ifhmode{\setbox0\lastbox}\fi% waar is die er in geslopen \par \ifdim\prevdepth<\zeropoint \else % anders bovenaan kolom witruimte \nobreak \blank[\@@bkspacebefore] \nobreak \fi \flushfloatbox \blank[\@@bkspaceafter] \fi \fi \fi \egroup \fi\fi\fi\fi} %D This one looks complicated. Upto \type{\nofcolumns} floats %D are placed, taking the width of a float into account. This %D routine can be improved on different ways: %D %D \startitemize[intro,packed] %D \item taking into account some imaginary baseline, just to %D get the captions in line %D \item multipass flushing until as many floats are displaced %D as possible %D \stopitemize %D %D When handling lots of (small) floats spacing can get worse %D because of lining out the columns. \def\doflushcolumnfloats {\ifpostponecolumnfloats\else \bgroup \forgetall \ifsomefloatwaiting \dimen8\zeropoint \dimen4\zeropoint \count0\zerocount % count0 can be used local \count2\nofcolumns % count2 can be used local \dohandleallcolumns {\ifnum\count0>\zerocount % the wide one's reserved space \global\setbox\currenttopcolumnbox\vbox {\snaptogrid\vbox {\copy\currenttopcolumnbox \hbox{\vphantom{\copy\floatbox}}} \whitespace % nodig ? \blank[\@@bkspaceafter]}% \else \dogetfloat \ifdim\wd\floatbox>\finalcolumntextwidth % better somewhere else too \global\setbox\floatbox\hbox to \finalcolumntextwidth{\hss\box\floatbox\hss}% \fi % otherwise the graphic may disappear \ifdim\wd\floatbox>\hsize \dimen0\wd\floatbox \advance\dimen0 \intercolumnwidth \dimen2\hsize \advance\dimen2 \intercolumnwidth \advance\dimen0 .5pt % hm, why 1 \advance\dimen2 .5pt % hm, why 2 \divide\dimen0 \dimen2 \count0\dimen0 \advance\count0 \plusone \ifnum\count0>\count2 \doresavefloat \count0\zerocount \else \dimen0=\count0\hsize \advance\dimen0 \count0\intercolumnwidth \advance\dimen0 -\intercolumnwidth \global\setbox\floatbox\hbox to \dimen0 %{\hss\hbox{\copy\floatbox}\hss}% {\processaction[\@@bklocation] % how easy to forget [ \v!left=>\copy\floatbox\hss, \v!right=>\hss\copy\floatbox, \s!default=>\hss\copy\floatbox\hss, \s!unknown=>\hss\copy\floatbox\hss]}% \fi \showmessage\m!columns{13}\empty \else % \showmessage\m!columns{13}\empty \fi \ifdim\ht\floatbox>\zeropoint\relax \global\setbox\currenttopcolumnbox\vbox {\snaptogrid\vbox {\copy\currenttopcolumnbox \copy\floatbox} \whitespace % nodig ? \blank[\@@bkspaceafter]}% \fi \dimen6\ht\currenttopcolumnbox \advance\dimen6 \dp\currenttopcolumnbox \fi \ifdim\dimen4<\ht\currenttopcolumnbox \dimen4\ht\currenttopcolumnbox \fi \advance\dimen8 \dimen6 \advance\count2 \minusone \advance\count0 \minusone }% \setvsize \global\advance\vsize -\dimen8 \global\pagegoal\vsize \else %\doflushfloats % does not snap! \fi \egroup \fi} %D The next macro can be used to flush floats in the current %D stream. No width checking is (yet) done. \def\insertcolumnfloats {\doloop {\ifsomefloatwaiting \bgroup \forgetall % no check for width \dogetfloat \blank[\@@bkspacebefore] \snaptogrid\vbox{\copy\floatbox} \blank[\@@bkspaceafter] \egroup \else \exitloop \fi}} %D This were the multi||column routines. They can and need to %D be improved but at the moment their behaviour is acceptable. %D %D One inprovement can be to normalize the height of floats %D to $n\times$\type{\lineheight} with a macro like: %D %D \starttyping %D \normalizevbox{...} %D \stoptyping % border case, should fit on one page % % \startcolumns % % 1 \input tufte \par \plaatsfiguur{}{\omlijnd[breedte=\hsize,hoogte=3cm]{1}} % 2 \input tufte \par \plaatsfiguur{}{\omlijnd[breedte=\hsize,hoogte=3cm]{2}} % 3 \input tufte \par \plaatsfiguur{}{\omlijnd[breedte=\hsize,hoogte=3cm]{3}} % % \stopcolumns \def\setupcolumns {\dosingleempty\dosetupcolumns} \def\dosetupcolumns[#1]% {\getparameters[\??kl][#1]% \nofcolumns\@@kln\relax \processaction [\@@klrule] [ \v!on=>\let\betweencolumns\linebetweencolumns, \v!off=>\let\betweencolumns\spacebetweencolumns, \s!default=>\let\betweencolumns\spacebetweencolumns, \s!unknown=>\let\betweencolumns\@@klrule]} \def\linebetweencolumns {\bgroup \starttextproperties \ifdim\@@kldistance>\zeropoint \dimen0=\@@kldistance \else \dimen0=\linewidth \fi \advance\dimen0 -\linewidth \hskip.5\dimen0 \vrule \!!width\linewidth \ifb@selinebottom\!!depth\strutdepth\fi \hskip.5\dimen0\relax \stoptextproperties \egroup} \def\spacebetweencolumns {\hskip\@@kldistance} \presetlocalframed[\??kl] \def\backgroundfinishcolumnbox {\doifinsetelse\@@kloffset{\v!none,\v!overlay} {\let\@@kloffset\!!zeropoint} {\scratchdimen\@@kloffset \advance\scratchdimen -\@@klrulethickness \edef\@@kloffset{\the\scratchdimen}}% \localframed [\??kl] [\c!strut=\v!no, \c!width=\v!fit, \c!height=\v!fit, \c!align=]} \let\restorecolumnsettings\relax \definecomplexorsimpleempty\startcolumns \def\complexstartcolumns[#1]% %% \startcolumns {\bgroup \let\stopcolumns\egroup \ifinsidecolumns \else \setupcolumns[#1]% \ifnum\@@kln>1\relax \whitespace \begingroup \doif\@@kloption\v!background {\let\finishcolumnbox\backgroundfinishcolumnbox \let\columntextoffset\@@kloffset}% \ifx\@@klcommand\empty\else \let\postprocesscolumnline\@@klcommand \fi \doifelsenothing\@@klheight \heightencolumnsfalse \heightencolumnstrue \doifelse\@@kldirection\v!right \reversecolumnsfalse \reversecolumnstrue \doifelse\@@klbalance\v!yes \balancecolumnstrue \balancecolumnsfalse \installalign\v!yes {\stretchcolumnstrue \inheritcolumnsfalse}% todo: new key \installalign\v!no {\stretchcolumnsfalse\inheritcolumnsfalse}% todo: new key \installalign\v!text{\stretchcolumnsfalse\inheritcolumnstrue }% \stretchcolumnsfalse \inheritcolumnstrue \doifsomething\@@klalign{\expanded{\setupalign[\@@klalign]}}% % \processaction % [\@@klalign] % [ \v!yes=>\stretchcolumnstrue % \inheritcolumnsfalse, % \v!no=>\stretchcolumnsfalse % \inheritcolumnsfalse, % \v!text=>\stretchcolumnsfalse % \inheritcolumnstrue]% \nofcolumns=\@@kln % % probably more is needed, and how about nesting save's % \savecurrentblank \savecurrentwhitespace \def\restorecolumnsettings {\boxmaxdepth\maxdimen % done elsewhere \restorecurrentblank \restorecurrentwhitespace}% % \edef\fixedcolumnheight{\@@klheight}% \edef\minbalancetoplines{\@@klntop}% \setuptolerance[\@@kltolerance]% %% \startcolumns \setupblank[\@@klblank]% \ifdim\ctxparskip>\zeropoint\relax \setupwhitespace[\@@klblank]% \fi \def\stopcolumns {\endmulticolumns \global\insidecolumnsfalse \endgroup \egroup}% \global\insidecolumnstrue \beginmulticolumns \fi \fi} \installcolumnbreakhandler {MUL} \v!preference {\goodbreak} \installcolumnbreakhandler {MUL} \v!yes {\par % todo: since {\testrulewidth\zeropoint\ruledvskip\textheight}% we misuse a \penalty-200 % side effect \vskip-\textheight }% bugged : \prevdepth-\thousandpoint} % signals top of column to \blank %D New: only at start of columns; may change ! Rather %D interwoven and therefore to be integrated when the multi %D column modules are merged. (moved from cont-new.tex) \def\setupcolumnspan[#1]% {\getparameters[\??ks][#1]} \presetlocalframed [\??ks] \setupcolumnspan [\c!n=2, \c!offset=\v!overlay, \c!frame=\v!off] \newbox\columnspanbox \let\postprocesscolumnspanbox\gobbleoneargument \def\dostartcolumnspan[#1]% {\bgroup \setupcolumnspan[#1]% \forgetall \ifinsidecolumns \advance\hsize \intercolumnwidth \hsize\@@ksn\hsize \advance\hsize -\intercolumnwidth \fi \dowithnextbox {\setbox\columnspanbox\flushnextbox \ifinsidecolumns\wd\columnspanbox\hsize\fi \postprocesscolumnspanbox\columnspanbox \scratchdimen\ht\columnspanbox \setbox\columnspanbox\hbox % depth to be checked, probably option! {\localframed[\??ks][\c!offset=\v!overlay]{\box\columnspanbox}}% \ht\columnspanbox\scratchdimen \dp\columnspanbox\strutdp \wd\columnspanbox\hsize \ifinsidecolumns \ifnum\@@ksn>1 \setvsize \dohandleallcolumns {\ifnum\currentcolumn>\@@ksn\else \global\setbox\currenttopcolumnbox=\vbox {\ifnum\currentcolumn=1 \snaptogrid\vbox{\copy\columnspanbox} \else \snaptogrid\vbox{\vphantom{\copy\columnspanbox}} \fi}% \wd\currenttopcolumnbox\hsize \global\advance\vsize -\ht\currenttopcolumnbox \fi} \global\pagegoal\vsize \else \snaptogrid\vbox{\box\columnspanbox} \fi \else \snaptogrid\vbox{\box\columnspanbox} \fi \endgraf \ifvmode\prevdepth\strutdp\fi \egroup} \vbox\bgroup %\topskipcorrection % becomes an option ! \EveryPar{\begstrut\EveryPar{}}} % also ! \def\startcolumnspan {\dosingleempty\dostartcolumnspan} \def\stopcolumnspan {\egroup} \setupcolumns [\c!n=2, \c!ntop=1, \c!command=, \c!direction=\v!right, \c!rule=\v!off, \c!tolerance=\v!tolerant, \c!distance=1.5\bodyfontsize, % influenced by switching \c!height=, \c!balance=\v!yes, \c!align=\v!text, \c!blank={\v!line,\v!fixed}, \c!option=, \c!rulethickness=\linewidth, \c!offset=.5\bodyfontsize] %D Undocumented and still under development. \def\startsimplecolumns {\dosingleempty\dostartsimplecolumns} \def\dostartsimplecolumns[#1]% {\bgroup \nopenalties \getparameters[\??kl] [\c!width=\hsize,\c!distance=1.5\bodyfontsize,% \c!n=2,\c!lines=0,#1]% \let\rigidcolumnlines\@@kllines \setrigidcolumnhsize\@@klwidth\@@kldistance\@@kln \setbox\scratchbox\vbox\bgroup \forgetall} % \blank[\v!disable] \def\stopsimplecolumns {\removebottomthings \egroup \rigidcolumnbalance\scratchbox \egroup} \protect \endinput