%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. % todo: basecolumns as parent for columns and itemize % !!! there are some issues with hsize an vsize as well as flushing % !!! floats but this module will be redone anyway % % can have some vpack and hpack \writestatus{loading}{ConTeXt Page Macros / Simple Multi Column} %D This module is mostly a copy from the original multi column routine as %D implemented in \type {core-mul}. When the main OTR macro's were %D isolated in modules and column sets were introduced, this module became %D part of the OTR modules. As a result this module is no longer generic. \unprotect \definesystemvariable {ks} % KolomSpan % check \count multiplications %D The following macro's implement a multi||column output routine. The original %D implementation was based on Donald Knuth's implementation, which was adapted by %D Craig Platt to support balancing of the last page. I gradually adapted Platt's %D version to our needs but under certain circumstances things still went wrong. I %D considered all calls to Platt's \type{\page_mul_routine_error} as undesirable. %D %D This completely new implementation can handle enough situations for everyday %D documents, but is still far from perfect. While at the moment the routine doesn't %D support 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{\page_mul_routine_errors} %D \item different \type{\baselineskips}, \type{\spacing}, \type {\topskip} and %D \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 still separated. One %D reason for this is that \TeX\ is not suited well for handling multi||columns. As %D a result, the single columns routines are more robust. Handling one column as a %D special case of multi||columns is posible but at the cost of worse float %D handling, worse page breaking, worse etc. Complicated multi||column page handling %D should be done in \cap{DTP}||systems anyway. \installcorenamespace {columns} \installframedcommandhandler \??columns {columns} \??columns %D Going to a new columns is done by means of a \type {\ejectcolumn}. The %D following definition does not always work. \unexpanded\def\ejectcolumn % not good enough {\goodbreak \showmessage\m!columns2\empty} %D The output routines plug into a more generic mechanism as can be seen at the %D end of this file. So, occasionally we need to define some plugin code. \unexpanded\def\page_mul_place_float_top {\showmessage\m!columns4\empty\page_one_place_float_here} \unexpanded\def\page_mul_place_float_bottom {\showmessage\m!columns5\empty\page_one_place_float_here} \unexpanded\def\page_mul_place_float_here {\page_one_place_float_here} \unexpanded\def\page_mul_place_float_force {\page_one_place_float_force} \unexpanded\def\page_mul_command_side_float_output {\page_otr_construct_and_shipout\unvbox\normalpagebox\zerocount} % three arguments \unexpanded\def\page_mul_command_flush_side_floats {\page_sides_forget_floats} \unexpanded\def\page_mul_command_synchronize_side_floats {\page_sides_forget_floats} \unexpanded\def\page_mul_command_next_page {\page_otr_eject_page} \unexpanded\def\page_mul_command_next_page_and_inserts {\page_otr_eject_page_and_flush_inserts} \let\page_mul_initialize_floats\relax \let\page_mul_flush_floats \relax \let\page_mul_flush_float \relax %D A hook: \let\finishcolumnbox\relax % todo in mkiv %D This will change to a local one: \ifdefined\nofcolumns \else \newcount\nofcolumn \fi \ifdefined\mofcolumns \else \newcount\mofcolumn \fi \appendtoks \nofcolumns\columnsparameter\c!n\relax \to \everysetupcolumns %D Columns are separated by spacing or rules or whatever suits. \installcorenamespace{columnseparators} \setvalue{\??columnseparators\v!on }{\let\page_mul_between_columns\page_mul_between_columns_rule} \setvalue{\??columnseparators\v!off }{\let\page_mul_between_columns\page_mul_between_columns_space} \setvalue{\??columnseparators }{\let\page_mul_between_columns\page_mul_between_columns_space} \setvalue{\??columnseparators\s!unknown}{\let\page_mul_between_columns\p_page_mul_rule} \def\page_mul_between_columns_rule {\bgroup \starttextproperties \scratchdistance\dimexpr\columnsparameter\c!distance/2\relax \hskip\scratchdistance\relax \vrule \s!width\linewidth \ifnum\bottomraggednessmode=\plustwo % baselinebottom \s!depth\strutdepth \fi \hskip\scratchdistance\relax \stoptextproperties \egroup} \def\page_mul_between_columns_space {\hskip\columnsparameter\c!distance\relax} \let\page_mul_between_columns\page_mul_between_columns_space %D We declare some registers: \newdimen \d_page_mul_available_width \newdimen \d_page_mul_distance \newdimen \d_page_mul_leftskip \newdimen \d_page_mul_rightskip \newdimen \d_page_mul_offset \newdimen \d_page_mul_forced_height \newdimen \d_page_mul_used_width \newdimen \d_page_mul_temp \newcount \c_page_mul_balance_minimum \newcount \c_page_mul_n_of_lines \newbox \b_page_mul_preceding \newdimen \d_page_mul_preceding_height \newdimen \d_page_mul_preceding_depth \newconditional\c_page_mul_preceding_present \newbox \b_page_mul_preceding_rest_of_page \newconditional\c_page_mul_reverse \newconditional\c_page_mul_trace %D The next dimensions reports the final column height .. todo \newdimen\finalcolumnheights \newcount\finalcolumnlines \newdimen\savedpagetotal % brrr \newif\ifstretchcolumns \stretchcolumnsfalse \newif\ifheightencolumns \heightencolumnsfalse \newif\ifinheritcolumns \inheritcolumnsfalse \newif\ifbalancecolumns %\balancecolumnstrue %D An important one: \unexpanded\def\page_mul_command_set_hsize % beware, this one is available for use in macros {\setbox\scratchbox\hbox{\page_mul_between_columns}% \d_page_mul_distance\wd\scratchbox \d_page_mul_available_width\dimexpr \makeupwidth -\d_page_mul_leftskip -\d_page_mul_rightskip -\nofcolumns\d_page_mul_distance +\d_page_mul_distance \relax \d_page_mul_used_width\dimexpr \d_page_mul_available_width/\nofcolumns -\d_page_mul_offset*\plustwo \relax \textwidth\d_page_mul_used_width % needs thinking ... grouping etc \hsize\d_page_mul_used_width} %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 used in typesetting the %D in||between material, these dimensions are influenced by bodyfont switches inside %D multi||column mode. \setnewconstant\multicolumnlinemethod\zerocount % 0=normal 1=raw \def\multicolumnovershootratio{.5} % {\ifgridsnapping0\else.5\fi} \unexpanded\def\page_mul_set_n_of_lines {\settotalinsertionheight \d_page_mul_temp\dimexpr -\d_page_mul_offset*\plustwo +\textheight \ifdim\d_page_mul_preceding_height>\zeropoint -\d_page_mul_preceding_height \fi -\totalinsertionheight \relax \ifcase\multicolumnlinemethod \getnoflines \d_page_mul_temp \or \getrawnoflines\d_page_mul_temp \else \getrawnoflines\d_page_mul_temp \fi % added 30/7/2004 \ifnum\layoutlines>\zerocount \ifnum\noflines>\layoutlines \noflines\layoutlines \fi \fi \c_page_mul_n_of_lines\noflines} \unexpanded\def\page_mul_command_set_vsize {\page_one_command_set_vsize % indeed? \page_mul_set_n_of_lines \d_page_mul_temp\nofcolumns\dimexpr \c_page_mul_n_of_lines\openlineheight +\multicolumnovershootratio\openlineheight % collect enough data \relax \global\vsize\d_page_mul_temp \pagegoal \d_page_mul_temp} % let's do it only here %D It really starts here. After some checks and initializations we change the output %D routine to continous multi||column mode. This mode handles columns that fill the %D current and next full pages. The method used is (more or less) multiplying \type %D {\vsize} and dividing \type {\hsize} by \type {\nofcolumns}. More on this can be %D found in the \TeX book. We save the top of the current page in box %D \type {\b_page_mul_preceding}. %D %D We manipulate \type {\topskip} a bit, just to be shure that is has no %D flexibility. This has te be done every time a font switch takles place, because %D \type {\topskip} can depend on this. \newconstant\c_page_mul_routine \setnewconstant\c_page_mul_routine_regular \zerocount \setnewconstant\c_page_mul_routine_intercept \plusone \setnewconstant\c_page_mul_routine_continue \plustwo \setnewconstant\c_page_mul_routine_balance \plusthree \setnewconstant\c_page_mul_routine_error \plusfour \unexpanded\def\page_mul_command_routine {\ifcase\c_page_mul_routine \page_one_command_routine \or \page_mul_routine_intercept \or \page_mul_routine_continue \or \page_mul_routine_balance \or \page_mul_routine_error \fi} \def\page_mul_routine_intercept {\global\setbox\b_page_mul_preceding\vbox {\page_otr_command_flush_top_insertions \unvbox\normalpagebox}} \def\page_mul_routine_error {\showmessage\m!columns3\empty \page_otr_construct_and_shipout\unvbox\normalpagebox\zerocount} % three arguments %D When we leave the multi||column mode, we have to process the not yet shipped out %D part of the columns. When we don't balance, we simply force a continuous output, %D but a balanced output is more tricky. %D %D First we try to fill up the page and when all or something is left we try to %D balance things. This is another useful adaption of the ancesters of these %D macro's. It takes some reasoning to find out what happens and maybe I'm making %D some mistake, but it works. %D %D Voiding box \type {\b_page_mul_preceding} is sometimes necessary, e.g. when there is no %D text given between \type {\begin..} and \type {\end..}. The \type {\par} is %D needed! %D Because some initializations happen three times, we defined a macro for %D them. Erasing \type{\everypar} is needed because we don't want anything %D to interfere. \unexpanded\def\page_mul_initialize_variables {\reseteverypar \dontcomplain \settopskip \setmaxdepth \topskip 1\topskip \splittopskip \topskip \splitmaxdepth \maxdepth \boxmaxdepth \maxdepth % dangerous \emergencystretch\zeropoint \relax} % sometimes needed ! %D Flushing the page comes to pasting the columns together and appending the result %D to box \type {\b_page_mul_preceding}, if not void. I've seen a lot of implementations in %D which some skip was put between normal text and multi||column text. When we don't %D want this, the baselines can be messed up. I hope the seemingly complicated %D calculation of a correction \type {\kern} is adequate to overcome this. Although %D not watertight, spacing is taken into account and even multiple mode changes on %D one page go well. But cross your fingers and don't blame me. %D %D One of the complications of flushing out the boxes is that \type {\b_page_mul_preceding} %D needs to be \type {\unvbox}'ed, otherwise there is too less flexibility in the %D page when using \type {\raggedbottom}. It took a lot of time before these kind of %D problems were overcome. Using \type {\unvbox} at the wrong moment can generate %D \type {\page_mul_routine_error}'s. %D %D One can use the macros \type {\maxcolumnheight} and \type {\maxcolumndepth} when %D generating material between columns as well as postprocessing column lines. \newdimen\maxcolumnheight \newdimen\maxcolumndepth \newbox\columnpagebox \def\page_mul_calculate_column_result_dimensions {\maxcolumnheight\zeropoint \maxcolumndepth \zeropoint \dohandleallcolumnscs\page_mul_calculate_column_result_dimensions_step} \def\page_mul_calculate_column_result_dimensions_step {\ifdim\ht\currentcolumnbox>\maxcolumnheight \maxcolumnheight\ht\currentcolumnbox \fi \ifdim\dp\currentcolumnbox>\maxcolumndepth \maxcolumndepth\dp\currentcolumnbox \fi} \setnewconstant\multicolumntopflushmethod\plusone % 0: no correction, 1: correction when topstuff, 2: correction, 3: correction++ \setnewconstant\multicolumntopalignmethod\plustwo % 0: nothing, 1: force grid, 2: follow grid \def\page_mul_flush_preceding_normal {\unvbox\b_page_mul_preceding} \def\page_mul_flush_preceding_ongrid {\scratchdimen\dimexpr \savedpagetotal -\d_page_mul_preceding_height -\d_page_mul_preceding_depth -\topskip \relax \box\b_page_mul_preceding \kern\scratchdimen} \def\page_mul_flush_packaged_columns_continued {\bgroup \page_mul_flush_packaged_columns_indeed \box\columnpagebox \egroup} \def\page_mul_flush_packaged_columns_balanced {\bgroup \page_mul_flush_packaged_columns_indeed % messy correction, we need to rewrite this module (newcolumns) \setbox\columnpagebox\vbox {\offinterlineskip \scratchdimen\htdp\columnpagebox \box\columnpagebox \vskip-\scratchdimen}% \ht\columnpagebox\dimexpr \noflines\openlineheight -\openstrutdepth \ifgridsnapping % quick hack (at least it works with itemize) \else -\openlineheight +\topskip \fi \relax \dp\columnpagebox\openstrutdepth % end of mess \box\columnpagebox \egroup} \def\page_mul_synchronize_marks {\dohandleallcolumns{\page_marks_synchronize_column\plusone\nofcolumns\mofcolumns\currentcolumnbox}} \def\page_mul_flush_packaged_columns_indeed {\ifvoid\b_page_mul_preceding \setfalse\c_page_mul_preceding_present % will be set elsewhere \else \settrue\c_page_mul_preceding_present \page_apply_postprocessors_box\b_page_mul_preceding \fi \forgetall \page_mul_initialize_variables \page_mul_calculate_column_result_dimensions \page_mul_postprocess_linenumbers \page_mul_synchronize_marks \page_mul_postprocess_lines \page_mul_postprocess_columns \dohandleallcolumns {\global\setbox\currentcolumnbox\hpack to \d_page_mul_used_width {\box\currentcolumnbox}% \wd\currentcolumnbox\d_page_mul_used_width \ifheightencolumns \ht\currentcolumnbox\d_page_mul_forced_height \fi}% \page_mul_calculate_column_result_dimensions \overlaycolumnfootnotes \setbox\columnpagebox\vpack % \vbox {\ifconditional\c_page_mul_reverse\reversehpack\else\naturalhpack\fi to \makeupwidth {\hskip\ifconditional\c_page_mul_reverse\d_page_mul_rightskip\else\d_page_mul_leftskip\fi\relax \dohandleallcolumns {\finishcolumnbox {\setbox\scratchbox\hpack {\ifx\finishcolumnbox\relax\else\strut\fi \box\currentcolumnbox}% hm, why strut \anch_mark_column_box\scratchbox\currentcolumn \box\scratchbox}% \hfil}% \unskip \hskip\ifconditional\c_page_mul_reverse\d_page_mul_leftskip\else\d_page_mul_rightskip\fi}}% \scratchdimen\zeropoint \dohandleallcolumns {\ifdim-\ht\currenttopcolumnbox<\scratchdimen \scratchdimen-\ht\currenttopcolumnbox \fi \global\setbox\currenttopcolumnbox\emptybox}% \advance\scratchdimen \ht\columnpagebox \setbox\scratchbox\hbox to \makeupwidth % between can be something so no \hpack {\vrule \s!width \zeropoint \s!height\scratchdimen \s!depth \dp\columnpagebox \dostepwiserecurse\plustwo\nofcolumns\plusone{\hfil\page_mul_between_columns}\hfil}% \setbox\columnpagebox\hpack {\box\columnpagebox \hskip-\makeupwidth \box\scratchbox}% \page_mul_postprocess_page \ifconditional\c_page_mul_preceding_present \settrue\c_page_mul_preceding_present % next some incredible crappy code \ifcase\multicolumntopalignmethod \page_mul_flush_preceding_normal % not on grid \or \page_mul_flush_preceding_ongrid % force on grid \else\ifgridsnapping \page_mul_flush_preceding_ongrid % obey grid settings, force on grid \else \page_mul_flush_preceding_normal % ignore grid settings, not on grid \fi \fi \fi \global\d_page_mul_preceding_height\zeropoint \page_otr_command_set_vsize \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\c_page_mul_preceding_present % \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} %D In case one didn't notice, finaly \type{\finishcolumnbox} is applied to %D all boxes. One can use these hooks for special purposes. %D %D Once upon a time I wanted to manipulate the individual lines in a column. %D This feature is demonstrated in the two examples 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 presented %D in \type{supp-box}. %ifdefined\page_postprocessors_column\else\let\page_postprocessors_column\relax\fi % operates on passed box \ifdefined\postprocesscolumnline \else\let\postprocesscolumnline \relax\fi % operates on passed box \ifdefined\postprocesscolumnbox \else\let\postprocesscolumnbox \relax\fi % operates on passed box \ifdefined\postprocesscolumnpagebox \else\let\postprocesscolumnpagebox \relax\fi % operates on passed box %def\page_mul_postprocess_linenumbers{\ifx\page_postprocessors_column\relax\else\page_mul_postprocess_linenumbers_indeed\fi} \def\page_mul_postprocess_lines {\ifx\postprocesscolumnline \relax\else\page_mul_postprocess_lines_indeed \fi} \def\page_mul_postprocess_columns {\ifx\postprocesscolumnbox \relax\else\page_mul_postprocess_columns_indeed \fi} \def\page_mul_postprocess_page {\ifx\postprocesscolumnpagebox \relax\else\page_mul_postprocess_page_indeed \fi} % \def\page_mul_postprocess_linenumbers_indeed % {\dohandleallcolumns{\page_apply_postprocessors_column\currentcolumnbox}} \def\page_mul_postprocess_linenumbers {\dohandleallcolumns{\page_apply_postprocessors_column\currentcolumnbox}} \def\page_mul_postprocess_lines_indeed {\dohandleallcolumnscs\page_mul_postprocess_lines_step} \def\page_mul_postprocess_lines_step % TODO: use lua solution instead {\global\setbox\currentcolumnbox\vtop {\beginofshapebox \unvbox\currentcolumnbox \unskip\unskip \endofshapebox \reshapebox {\scratchheight\ht\shapebox \scratchdepth \dp\shapebox \setbox\shapebox\hbox to \hsize {\postprocesscolumnline\shapebox}% \ht\shapebox\scratchheight \dp\shapebox\scratchdepth \box\shapebox}% \flushshapebox \reseteverypar \parskip\zeropoint % = \forgetall \verticalstrut \vskip-\struttotal \vfil}} \def\page_mul_postprocess_columns_indeed {\dohandleallcolumnscs\page_mul_postprocess_columns_step} \def\page_mul_postprocess_columns_step {\global\setbox\currentcolumnbox\hbox {\postprocesscolumnbox\currentcolumnbox}} \def\page_mul_postprocess_page_indeed {\postprocesscolumnpagebox\columnpagebox} %D Here comes the simple splitting routine. It's a bit longer than expected because %D of ragging bottoms or not. This part can be a bit shorter but I suppose that I %D will forget what happens. The splitting takes some already present material %D (think of floats) into account! %D %D First we present some auxiliary routines. Any material, like for instance floats, %D that is already present in the boxes is preserved. \newdimen\d_page_mul_split_height_used \newdimen\d_page_mul_split_height_max \def\page_mul_split_column#1#2#3#4% copy or box {\bgroup \ifdim\ht#4>\zeropoint \d_page_mul_split_height_used#3\relax \d_page_mul_split_height_max\d_page_mul_split_height_used \advance\d_page_mul_split_height_used -\ht#4% \columnfootnotecorrection{#1}\d_page_mul_split_height_used \setbox\scratchbox\vsplit#2 to \d_page_mul_split_height_used \global\setbox#1\vbox to \d_page_mul_split_height_max {\ifgridsnapping \scratchdimen\dimexpr\topskip-\openstrutheight\relax \vskip\scratchdimen \copy#4% \vskip-\scratchdimen \else \unvcopy#4% \fi \fuzzysnappedbox\unvbox\scratchbox \fakecolumnfootnotes{#1}}% \else\ifcase\c_strc_notes_page_location \global\setbox#1\vsplit#2 to #3% \global\setbox#1\vbox {\fuzzysnappedbox\unvbox{#1}}% % or \box ? \else \columnfootnotecorrection{#1}{#3}% \setbox\scratchbox\vsplit#2 to #3% \global\setbox#1\vbox to #3% {\fuzzysnappedbox\unvbox\scratchbox \fakecolumnfootnotes{#1}}% \fi \fi \egroup} \def\page_mul_split_current_column#1#2% {\page_mul_split_column\currentcolumnbox{#1}{#2}\currenttopcolumnbox} \def\page_mul_split_first_column#1#2% {\page_mul_split_column\firstcolumnbox{#1}{#2}\firsttopcolumnbox} \def\page_mul_split_last_column#1#2% {\global\setbox\lastcolumnbox\vbox {\unvcopy\lasttopcolumnbox \fuzzysnappedbox\unvbox{#1}% \fakecolumnfootnotes\lastcolumnbox}} %D NEW: still to be documented. \def\fakecolumnfootnotes#1% {\relax \ifcase\c_strc_notes_page_location\else \ifnum#1=\lastcolumnbox \fakenotes \fi \fi} \def\columnfootnotecorrection#1#2% {\relax \ifcase\c_strc_notes_page_location % 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\c_strc_notes_page_location % page \or \checknotepresence \ifnotespresent \page_mul_notes_flush_first_column \fi \or \checknotepresence \ifnotespresent \page_mul_notes_flush_last_column \fi \fi} \newbox\b_page_mul_notes \def\page_mul_notes_flush_first_column {\begingroup \setbox\b_page_mul_notes\vbox{\placenoteinserts}% \ifzeropt\ht\b_page_mul_notes % can't happen as we already checked \else \page_mul_set_n_of_lines \advance\c_page_mul_n_of_lines \minustwo \scratchdimen\dimexpr\c_page_mul_n_of_lines\lineheight+\topskip\relax \setbox\b_page_mul_notes\hpack{\lower\scratchdimen\box\b_page_mul_notes}% \ht\b_page_mul_notes\openstrutheight \dp\b_page_mul_notes\openstrutdepth \wd\b_page_mul_notes\zeropoint \scratchdimen\ht\firstcolumnbox \global\setbox\firstcolumnbox\vbox to \scratchdimen {\box\firstcolumnbox \vskip-\scratchdimen \box\b_page_mul_notes}% \fi \endgroup} \def\page_mul_notes_flush_last_column {\begingroup \setbox\b_page_mul_notes\vbox{\placenoteinserts}% \ifzeropt\ht\b_page_mul_notes % can't happen as we already checked \else % maybe here also \page_mul_set_n_of_lines \scratchdimen\dimexpr\ht\firstcolumnbox-\openstrutdepth\relax % \strutdp \getnoflines\scratchdimen \advance\noflines \minustwo \scratchdimen\dimexpr\noflines\lineheight+\topskip\relax \setbox\b_page_mul_notes\hpack{\lower\scratchdimen\box\b_page_mul_notes}% \ht\b_page_mul_notes\openstrutheight \dp\b_page_mul_notes\openstrutdepth \wd\b_page_mul_notes\zeropoint \scratchdimen\ht\lastcolumnbox \global\setbox\lastcolumnbox\vbox to \scratchdimen {\box\lastcolumnbox \vskip-\scratchdimen \box\b_page_mul_notes}% \fi \endgroup} %D Here comes the routine that splits the long box in columns. The macro \type %D {\page_mul_flush_floats} can be used to flush either floats that were present before %D the multi||column mode was entered, or floats that migrate to next columns. %D Flushing floats is a delicate process. \def\page_mul_routine_continue {\bgroup \forgetall \page_mul_initialize_variables % \dimen0=\makeupheight % \advance\dimen0 -\d_page_mul_preceding_height % \settotalinsertionheight % \advance\dimen0 -\totalinsertionheight % \ifgridsnapping % evt altijd, nog testen % \getnoflines{\dimen0} % \dimen0=\noflines\openlineheight % \fi \page_mul_set_n_of_lines \d_page_mul_balance_target\c_page_mul_n_of_lines\openlineheight \ifconditional\c_page_mul_trace \writestatus\m!columns{continue: lines=\the\c_page_mul_n_of_lines, target=\the\d_page_mul_balance_target, textheight=\the\textheight}% \fi \dohandleallcolumns {\page_mul_split_current_column\normalpagebox\d_page_mul_balance_target}% \setbox\b_page_mul_preceding_rest_of_page\vbox{\unvbox\normalpagebox}% \ifinheritcolumns \ifcase\bottomraggednessmode % 0 = ragged \dohandleallcolumns {\global\setbox\currentcolumnbox\vbox to \ht\firstcolumnbox {\scratchdepth\dp\currentcolumnbox \unvbox\currentcolumnbox \vskip\dimexpr\openstrutdepth-\scratchdepth\relax \prevdepth\openstrutdepth % \strutdp \vfill}}% \strc_notes_check_if_bottom_present %\ifconditional\c_notes_bottom_present \else % \dimen0\ht\firstcolumnbox % ?? %\fi \or % 1 = normal \advance\d_page_mul_balance_target\maxdepth \dohandleallcolumns {\global\setbox\currentcolumnbox\vbox to \d_page_mul_balance_target {\unvbox\currentcolumnbox}}% \or % 2 = baseline % the columns are on top of the baseline \fi \else \dohandleallcolumns {\global\setbox\currentcolumnbox\vbox to \d_page_mul_balance_target {\ifstretchcolumns \unvbox\currentcolumnbox \else \unvbox\currentcolumnbox % wel of niet \unvbox ? \vfill \fi}}% \dohandleallcolumns {\ht\currentcolumnbox\d_page_mul_balance_target}% redundant \fi \setbox\b_page_mul_preceding\vbox{\page_mul_flush_packaged_columns_continued}% \page_otr_construct_and_shipout\box\b_page_mul_preceding\zerocount % three arguments \page_otr_command_set_hsize \page_otr_command_set_vsize \page_mul_flush_floats \unvbox\b_page_mul_preceding_rest_of_page % \penalty\outputpenalty % gaat gruwelijk mis in opsommingen \egroup} %D And this is the balancing stuff. Again, part of the routine is dedicated to %D handling ragged bottoms, but here we also see some handling concerning the %D stretching of columns. We set \type {\widowpenalty} at~0, which enables us to %D balance columns with few lines. The use of \type {\box2} and \type {\box4} %D garantees a more robust check when skips are used. \newbox \b_page_mul_balance_content \newbox \b_page_mul_balance_first_column \newbox \b_page_mul_balance_column \newconstant \c_page_mul_balance_tries_max \newcount \c_page_mul_balance_tries \newdimen \d_page_mul_balance_target \newdimen \d_page_mul_balance_target_less \newdimen \d_page_mul_balance_natural_height \newdimen \d_page_mul_balance_regular_height \newdimen \d_page_mul_balance_step \newdimen \d_page_mul_balance_fuzzyness \newdimen \d_page_mul_balance_threshold \newconditional\c_page_mul_balance_possible \c_page_mul_balance_tries_max 250 % 100 is too small when floats are involved \def\page_mul_routine_balance {\bgroup % why no \forgetall here \page_mul_initialize_variables \widowpenalty\zerocount \setbox\b_page_mul_balance_content\vbox{\unvbox\normalpagebox}% \ifdim\ht\b_page_mul_balance_content>\openlineheight % at least one line \ifnum\c_page_mul_balance_minimum<\plustwo % balance anyway \settrue\c_page_mul_balance_possible \else % check criterium to available lines \getnoflines{\ht\b_page_mul_balance_content}% \divide\noflines \nofcolumns \relax \ifnum\noflines<\c_page_mul_balance_minimum \relax \ifdim\dimexpr\ht\b_page_mul_balance_content+\ht\firsttopcolumnbox+\openlineheight\relax>\makeupheight \settrue\c_page_mul_balance_possible % column exceeding text height \else \setfalse\c_page_mul_balance_possible % it seems to fit \fi \else \settrue\c_page_mul_balance_possible % balance indeed \fi \fi \else \setfalse\c_page_mul_balance_possible % balancing does not make sense \fi \ifconditional\c_page_mul_balance_possible % start balancing, was: \ifdim\ht\b_page_mul_balance_content>\openlineheight \page_mul_balance_try_one \ifinheritcolumns \page_mul_balance_try_two \else \page_mul_balance_try_three \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{\unvbox\b_page_mul_balance_content}% \fi \c_page_mul_routine\c_page_mul_routine_error \baselinebottom % forces depth in separation rule \page_mul_flush_packaged_columns_balanced \page_mul_eject_page \egroup} \def\page_mul_eject_page {%\ifdim\pagetotal>\textheight % \page_otr_trigger_output_routine % new, but wrong as fails on mixed-001.tex (wrong pagetotal at this point) %\else \allowbreak }%\fi} \def\page_mul_balance_try_one {\d_page_mul_balance_target\dimexpr\ht\b_page_mul_balance_content+\topskip-\baselineskip\relax \dohandleallcolumns {\advance\d_page_mul_balance_target \ht\currenttopcolumnbox}% \divide\d_page_mul_balance_target \nofcolumns \vbadness\plustenthousand \c_page_mul_balance_tries\zerocount \bgroup \ifgridsnapping \d_page_mul_balance_step\lineheight \else \d_page_mul_balance_step\spacingfactor\onepoint % rubish \fi \doloop\page_mul_balance_try_one_attempt \page_mul_balance_try_one_attempt_final \ifnum\c_page_mul_balance_tries>\c_page_mul_balance_tries_max\relax \showmessage\m!columns7\empty \else \showmessage\m!columns8{\the\c_page_mul_balance_tries}% \fi \egroup} \def\page_mul_balance_try_one_attempt {\advance\c_page_mul_balance_tries \plusone \global\setbox\b_page_mul_preceding_rest_of_page\copy\b_page_mul_balance_content\relax \page_mul_split_first_column\b_page_mul_preceding_rest_of_page\d_page_mul_balance_target \dohandlemidcolumns {\page_mul_split_current_column\b_page_mul_preceding_rest_of_page\d_page_mul_balance_target}% \page_mul_split_last_column\b_page_mul_preceding_rest_of_page\d_page_mul_balance_target \setbox\b_page_mul_balance_first_column\vbox{\unvcopy\firstcolumnbox}% \d_page_mul_balance_natural_height\zeropoint \dohandleallcolumns\page_mul_balance_try_one_attempt_step \advance\d_page_mul_balance_natural_height -.0005pt % (33sp) get rid of accurracy problem, pretty new \ifnum\c_page_mul_balance_tries>\c_page_mul_balance_tries_max\relax \exitloop \else\ifdim\d_page_mul_balance_natural_height>\ht\b_page_mul_balance_first_column \advance\d_page_mul_balance_target \d_page_mul_balance_step\relax \else \exitloop \fi\fi} % \def\page_mul_balance_try_one_attempt_final % {\dohandleallcolumns % {\global\setbox\currentcolumnbox\vbox{\unvcopy\currentcolumnbox}}} % % \def\page_mul_balance_try_one_attempt_step % {\setbox\b_page_mul_balance_column\vbox % {\unvcopy\currentcolumnbox % \unpenalty % \unskip % \unpenalty % \unskip}% maybe better in main splitter % \ifdim\ht\b_page_mul_balance_column>\d_page_mul_balance_natural_height % \d_page_mul_balance_natural_height\ht\b_page_mul_balance_column % \fi} % % In mkiv we juggle node lists and we cannot work on copies that easily but in % practice context hardly uses copies except here. So, the next variant doesn't % use copies but the original in the final result, for which we need an extra % split pass. A simple test case is: % % \starttext % \startcolumns % \inleft{!} x \par x % \stopcolumns % \stoptext \def\page_mul_balance_try_one_attempt_final % we need this because we want to have the non copied content {\global\setbox\b_page_mul_preceding_rest_of_page\box\b_page_mul_balance_content \page_mul_split_first_column\b_page_mul_preceding_rest_of_page\d_page_mul_balance_target \dohandlemidcolumns {\page_mul_split_current_column\b_page_mul_preceding_rest_of_page\d_page_mul_balance_target}% \page_mul_split_last_column\b_page_mul_preceding_rest_of_page\d_page_mul_balance_target \dohandleallcolumns {\global\setbox\currentcolumnbox\vbox{\unvbox\currentcolumnbox}}} \def\page_mul_balance_try_one_attempt_step {\setbox\b_page_mul_balance_column\vbox {\unvbox\currentcolumnbox % was copy but not needed with the above \unpenalty \unskip \unpenalty \unskip}% maybe better in main splitter \ifdim\ht\b_page_mul_balance_column>\d_page_mul_balance_natural_height \d_page_mul_balance_natural_height\ht\b_page_mul_balance_column \fi} % 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). \def\page_mul_balance_try_two {\d_page_mul_balance_target\zeropoint \dohandleallcolumns {\ifdim\ht\currentcolumnbox>\d_page_mul_balance_target \d_page_mul_balance_target\ht\currentcolumnbox \fi}% \d_page_mul_balance_target_less\dimexpr\d_page_mul_balance_target-\openlineheight\relax \dohandleallcolumnscs\page_mul_balance_try_two_step} \def\page_mul_balance_try_two_step {\d_page_mul_balance_regular_height\ht\currentcolumnbox \d_page_mul_balance_threshold\plusten\openlineheight % funny value \global\setbox\currentcolumnbox\vbox to \d_page_mul_balance_target {\unvbox\currentcolumnbox \ifdim\d_page_mul_balance_regular_height>\d_page_mul_balance_threshold \ifdim\d_page_mul_balance_regular_height<\d_page_mul_balance_target \ifdim\d_page_mul_balance_regular_height>\d_page_mul_balance_target_less \vskip\zeropoint % !! \else \vskip\openlineheight \vfill \fi \else \vskip\zeropoint \fi \else \vskip\openlineheight \vfill \fi}} \def\page_mul_balance_try_three {\bgroup \ifstretchcolumns \d_page_mul_balance_target\ht\firstcolumnbox \d_page_mul_balance_fuzzyness\bottomtolerance\ht\firstcolumnbox \setbox\b_page_mul_balance_content\vbox{\unvcopy\lastcolumnbox}% \advance\d_page_mul_balance_target-\htdp\b_page_mul_balance_content\relax \ifdim\d_page_mul_balance_target>\openlineheight\relax \ifdim\d_page_mul_balance_target>\d_page_mul_balance_fuzzyness\relax % \stretchcolumnsfalse % beter good bad than bad good \showmessage\m!columns9\empty \fi \fi \fi \dohandleallcolumnscs\page_mul_balance_try_three_step \egroup} \def\page_mul_balance_try_three_step {\global\setbox\currentcolumnbox\vbox to \ht\firstcolumnbox {\ifstretchcolumns \unvbox\currentcolumnbox \else \box\currentcolumnbox \vfill \fi}} %D The multicolumn mechanism is incorporated in a \CONTEXT\ interface, %D 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 can be set %D 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 done outside columns %D (\type{\inheritcolumnstrue}). Also we can setup the \type{tolerance} within a %D column, the \type{distance} between columns and the fixed \type{height} of a %D column. %D %D Here come the routines that handle the placement of column floats. Floats that %D are to big migrate to the next column. Floats that are too wide, migrate to the %D top of the next page, where they span as much columns as needed. Floats that are %D left over from outside the multi||column mode are flushed first. In macro %D \type{\page_otr_construct_and_shipout} the topfloats that are left from previous %D text should be set. %D %D When there are some floats in the queue, we inhibit the flushing of floats on top %D of columns. The number of waiting floats is preswent in \type{\savednoftopfloats} %D and is saved. As long as there are floats waiting, the topfloats are places as if %D we are outside multi||column mode. This is neccessary for e.g. multicolumn lists. %D %D When all those floats are flushed, we switch to the local flushing routine. % \newbox \floatlist % \newbox \savedfloatlist % % \def\page_floats_column_push_saved % {\ifconditional\c_page_floats_some_waiting % \showmessage\m!columns6{\the\savednoffloats}% % \global\setbox\savedfloatlist\box\floatlist % \xdef\page_floats_column_pop_saved % {\global\savednoffloats\the\savednoffloats % \global\setbox\floatlist\box\savedfloatlist % \global\noexpand\settrue\c_page_floats_some_waiting}% % \global\savednoffloats\zerocount % \global\setfalse\c_page_floats_some_waiting % \else % \glet\page_floats_column_pop_saved\relax % \fi} % % \let\page_floats_column_pop_saved\relax % \def\page_mul_initialize_floats % messy as it adapts everypar % {\xdef\globalsavednoffloats{\the\savednoffloats}% % \ifnum\globalsavednoffloats>\zerocount % \setglobalcolumnfloats % hm, we always push so this never happens % \else % \setlocalcolumnfloats % \fi} \def\page_mul_initialize_floats % messy as it adapts everypar, we need to adapt this {\setlocalcolumnfloats} \newconditional\onlylocalcolumnfloats % temp hack as we will redo floats (grid snapping is also messy now) \newtoks \everylocalcolumnfloatspar \unexpanded\def\page_mul_command_flush_floats {\ifconditional\onlylocalcolumnfloats \doflushcolumnfloats \else \page_one_command_flush_floats \fi} \unexpanded\def\page_mul_command_check_if_float_fits {\ifconditional\onlylocalcolumnfloats \docolumnroomfloat \fi} \unexpanded\def\page_mul_command_flush_saved_floats {\ifconditional\onlylocalcolumnfloats\relax \else \page_one_command_flush_saved_floats \fi} \unexpanded\def\page_mul_command_flush_top_insertions {\ifconditional\onlylocalcolumnfloats\relax \else \page_one_command_flush_top_insertions \fi} \appendtoks \flushnotes \page_mul_flush_float %\flushmargincontents \checkindentation \to \everylocalcolumnfloatspar \def\setlocalcolumnfloats {\settrue\onlylocalcolumnfloats \everypar\everylocalcolumnfloatspar \let\page_mul_flush_float \doflushcolumnfloat \let\page_mul_flush_floats\doflushcolumnfloats} \def\setglobalcolumnfloats {\setfalse\onlylocalcolumnfloats \reseteverypar \let\page_mul_flush_float \relax \let\page_mul_flush_floats\noflushcolumnfloats} % \def\noflushcolumnfloats % {\bgroup % \xdef\localsavednoffloats{\the\savednoffloats}% % \global\savednoffloats\globalsavednoffloats % \page_otr_command_flush_top_insertions % \xdef\globalsavenoffloats{\the\savednoffloats}% % \ifnum\globalsavednoffloats=\zerocount % \setlocalcolumnfloats % \fi % \global\savednoffloats\localsavednoffloats % \egroup} % \def\noflushcolumnfloats{\doflushcolumnfloats} % not yet redone %D We need to calculate the amount of free space in a columns. When there is not %D enough room, we migrate the float to the next column. These macro's are %D alternatives (and look||alikes) of \type {\doroomfloat}. When a float is to wide, %D for one column, it is moved to the top of the next page. Of course such moved %D floats have to be taken into account when we calculate the available space. It's %D a pitty that such things are no integral part of \TEX. \def\getcolumnstatus#1#2#3% {\dimen0=\ifdim\pagegoal<\maxdimen \pagetotal \else \zeropoint \fi \dimen2=\zeropoint \count255=\zerocount \dimen8=\makeupheight \advance\dimen8 -\d_page_mul_preceding_height \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=\makeupheight \advance\dimen0 -\pagegoal \xdef\insertionheight{\the\dimen0}% \egroup \else \glet\insertionheight\zeropoint \fi} \def\docolumnroomfloat {\ifpostponecolumnfloats \global\setfalse\c_page_floats_room \else\ifconditional\c_page_floats_not_permitted \global\setfalse\c_page_floats_room \else \bgroup \getcolumnstatus{\count255}{\dimen0}{\dimen2}% \page_floats_get_info\s!text \setbox\scratchbox\vbox % tricky met objecten ? {\blank[\rootfloatparameter\c!spacebefore] \snaptogrid\vbox{\vskip\floatheight}}% copy? \advance\dimen0\dimexpr\ht\scratchbox+2\openlineheight+.5\lineheight\relax\relax % needed because goal a bit higher \ifdim\dimen0>\dimen2 \global\setfalse\c_page_floats_room \else \global\settrue\c_page_floats_room \fi \ifdim\floatwidth>\hsize \showmessage\m!columns{11}\empty \global\setfalse\c_page_floats_room \fi \egroup \fi\fi} %D Flushing one float is done as soon as possible, i.e. \type {\everypar}. %D This means that (at the moment) sidefloats are not supported (overulled)! \newif\ifflushingcolumnfloats \flushingcolumnfloatstrue \def\doflushcolumnfloat {\ifpostponecolumnfloats\else\ifflushingcolumnfloats\ifconditional\c_page_floats_some_waiting \doflushcolumnfloatindeed \fi\fi\fi} \def\doflushcolumnfloatindeed {\bgroup \forgetall \let\doflushcolumnfloat\relax \getcolumnstatus{\mofcolumns}{\dimen0}{\dimen2}% \ifdim\dimen0>\zeropoint \page_floats_get_info\s!text \ifdim\floatwidth>\hsize % dropped ? \else \setbox2\vbox {\blank[\rootfloatparameter\c!spacebefore] \snaptogrid\vbox{\vskip\floatheight}}% \advance\dimen0 \ht2 \ifdim\dimen0>\dimen2 \ifnum\mofcolumns<\nofcolumns \advance\mofcolumns \plusone \ifdim\ht\currenttopcolumnbox=\zeropoint \page_floats_flush\s!text\plusone \global\setbox\currenttopcolumnbox\vbox {\snaptogrid\vbox{\box\floatbox} \whitespace % nodig ? \blank[\rootfloatparameter\c!spaceafter]}% \dimen4=\htdp\currenttopcolumnbox \global\advance\vsize -\dimen4 \advance\dimen4 -\pagegoal \pagegoal-\dimen4 \showmessage\m!columns{12}a% \else \showmessage\m!columns{12}b% \fi \else \showmessage\m!columns{12}c% \fi \else \ifhmode{\setbox0\lastbox}\fi% waar is die er in geslopen \par \ifdim\prevdepth<\zeropoint \else % anders bovenaan kolom witruimte \nobreak \blank[\rootfloatparameter\c!spacebefore] \nobreak \fi \page_floats_flush\s!text\plusone \page_otr_command_flush_float_box \blank[\rootfloatparameter\c!spaceafter] \fi \fi \fi \egroup} %D This one looks complicated. Upto \type{\nofcolumns} floats are placed, %D taking the width of a float into account. This routine can be improved %D on different ways: %D %D \startitemize[intro,packed] %D \item taking into account some imaginary baseline, just to get the %D captions in line %D \item multipass flushing until as many floats are displaced as possible %D \stopitemize %D %D When handling lots of (small) floats spacing can get worse because of %D lining out the columns. \def\doflushcolumnfloats {\ifpostponecolumnfloats\else \bgroup \forgetall \ifconditional\c_page_floats_some_waiting \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 \hpack{\vphantom{\vskip\floatheight}}}% known from previous \whitespace % nodig ? \blank[\rootfloatparameter\c!spaceafter]}% \else \page_floats_get_info\s!text \ifdim\floatwidth>\hsize \dimen0\dimexpr\floatwidth+\d_page_mul_distance+.5pt\relax \dimen2\dimexpr\hsize +\d_page_mul_distance+.5pt\relax \divide\dimen0 \dimen2 \count0\dimen0 \advance\count0 \plusone \ifnum\count0>\count2 \count0\zerocount \else \dimen0\dimexpr\count0\hsize+\count0\d_page_mul_distance-\d_page_mul_distance\relax \page_floats_flush\s!text\plusone \ifdim\floatwidth>\makeupwidth % better somewhere else too \global\setbox\floatbox\hbox to \makeupwidth{\hss\box\floatbox\hss}% \fi % otherwise the graphic may disappear \global\setbox\floatbox\hbox to \dimen0 {\processaction[\rootfloatparameter\c!location] % how easy to forget [ \v!left=>\box\floatbox\hss, \v!right=>\hss\box\floatbox, \s!default=>\hss\box\floatbox\hss, \s!unknown=>\hss\box\floatbox\hss]}% \fi \showmessage\m!columns{13}\empty \else \page_floats_flush\s!text\plusone \ifdim\floatwidth>\makeupwidth % better somewhere else too \global\setbox\floatbox\hbox to \makeupwidth{\hss\box\floatbox\hss}% \fi % otherwise the graphic may disappear % \showmessage\m!columns{13}\empty \fi \ifdim\ht\floatbox>\zeropoint\relax \global\setbox\currenttopcolumnbox\vbox {\snaptogrid\vbox {\box\currenttopcolumnbox % was copy \box\floatbox} \whitespace % nodig ? \blank[\rootfloatparameter\c!spaceafter]}% \fi \dimen6\htdp\currenttopcolumnbox \fi \ifdim\dimen4<\ht\currenttopcolumnbox \dimen4\ht\currenttopcolumnbox \fi \advance\dimen8 \dimen6 \advance\count2 \minusone \advance\count0 \minusone }% \page_otr_command_set_vsize \global\advance\vsize -\dimen8 \pagegoal\vsize \else % \page_mul_command_flush_floats % does not snap! \fi \egroup \fi} %D The next macro can be used to flush floats in the current stream. No %D width checking is (yet) done. \def\insertcolumnfloats {\doloop {\ifconditional\c_page_floats_some_waiting \bgroup \forgetall % no check for width \page_floats_get \blank[\rootfloatparameter\c!spacebefore] \snaptogrid\vbox{\copy\floatbox} \blank[\rootfloatparameter\c!spaceafter] \egroup \else \exitloop \fi}} %D This were the multi||column routines. They can and need to be improved %D but at the moment their behaviour is acceptable. %D %D One inprovement can be to normalize the height of floats to $ n \times $ %D \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 \placefigure{}{\framed[width=\hsize,height=3cm]{1}} % 2 \input tufte \par \placefigure{}{\framed[width=\hsize,height=3cm]{2}} % 3 \input tufte \par \placefigure{}{\framed[width=\hsize,height=3cm]{3}} % \stopcolumns \def\backgroundfinishcolumnbox {\inheritedcolumnsframed} % [\c!strut=\v!no, % \c!width=\v!fit, % \c!height=\v!fit, % \c!align=]} % to be reconsidered ... (in any case they need to be unexpandable sinze 2011.12.30) \unexpanded\def\page_columns_align_option_yes {\stretchcolumnstrue \inheritcolumnsfalse}% todo: new key \unexpanded\def\page_columns_align_option_no {\stretchcolumnsfalse\inheritcolumnsfalse}% todo: new key \unexpanded\def\page_columns_align_option_text{\stretchcolumnsfalse\inheritcolumnstrue }% \newtoks\t_page_mul_initialize \unexpanded\def\startcolumns {\dosingleempty\page_mul_start} \def\page_mul_start[#1]% %% \startcolumns {\bgroup \ifinsidecolumns \page_mul_start_nop \else \iffirstargument \setupcolumns[#1]% \fi \nofcolumns\columnsparameter\c!n\relax \ifnum\nofcolumns>\plusone \page_mul_start_yes \else \page_mul_start_nop \fi \fi} \unexpanded\def\page_mul_start_nop {\let\stopcolumns\page_mul_stop_nop} \unexpanded\def\page_mul_stop_nop {\egroup} \unexpanded\def\page_mul_start_yes {\whitespace \begingroup \let\stopcolumns\page_mul_stop_indeed \global\insidecolumnstrue \the\t_page_mul_initialize % \flushnotes \begingroup % \d_page_mul_leftskip\leftskip \d_page_mul_rightskip\rightskip \leftskip\zeropoint \rightskip\zeropoint % \widowpenalty\zerocount % will become option \clubpenalty \zerocount % will become option % \page_floats_column_push_saved % \ifdim\dimexpr\pagetotal+\parskip+\openlineheight\relax<\pagegoal \allowbreak \else \break % sometimes fails \fi \appendtoks \topskip1\topskip % best a switch \to \everybodyfont \the\everybodyfont % ugly here \saveinterlinespace % ugly here % \initializecolumns\nofcolumns % \hangafter\zerocount \hangindent\zeropoint \reseteverypar \ifdim\pagetotal=\zeropoint \else \verticalstrut \vskip-\struttotal \fi \global\savedpagetotal\pagetotal \setupoutputroutine[\s!multicolumn]% \c_page_mul_routine\c_page_mul_routine_intercept \page_otr_trigger_output_routine % no \holdinginserts=1, can make footnote disappear ! \global\d_page_mul_preceding_height\ht\b_page_mul_preceding \c_page_mul_routine\c_page_mul_routine_continue \page_mul_initialize_floats \dohandleallcolumns{\global\setbox\currenttopcolumnbox\emptybox}% \checkbegincolumnfootnotes \page_otr_command_set_hsize \page_otr_command_set_vsize} \setnewconstant\multicolumnendsyncmethod\plusone % 1: old sync 2: new sync (cont-loc/project) / may fail ! ! ! ! \unexpanded\def\page_mul_stop_indeed {\relax \ifnum\multicolumnendsyncmethod=\plustwo \synchronizeoutput \else % don't collapse these \vskip \lineheight \vskip-\lineheight % take footnotes into account \fi \doflushcolumnfloat % added recently %\doflushcolumnfloats % no, since it results in wrong top floats \flushnotes % before start of columns \par \ifbalancecolumns \ifnum\multicolumnendsyncmethod=\plusone \c_page_mul_routine\c_page_mul_routine_continue \goodbreak \fi \c_page_mul_routine\c_page_mul_routine_balance \else \goodbreak \fi % still the multi column routine \page_otr_trigger_output_routine % the prevdepth is important, try e.g. toclist in \prevdepth\zeropoint % columns before some noncolumned text text % \c_page_mul_routine\c_page_mul_routine_regular % \ifvoid\b_page_mul_preceding\else \unvbox\b_page_mul_preceding \fi \global\d_page_mul_preceding_height\zeropoint \endgroup % here \nofcolumns\plusone \page_otr_command_set_vsize \checkendcolumnfootnotes \dosomebreak\allowbreak \page_floats_column_pop_saved % \global\insidecolumnsfalse \endgroup \egroup}% \appendtoks \edef\p_option{\columnsparameter\c!option}% \ifx\p_option\v!background \let\finishcolumnbox\backgroundfinishcolumnbox \doifelseinset{\columnsparameter\c!offset}{\v!none,\v!overlay} {\d_page_mul_offset\zeropoint}% {\d_page_mul_offset\dimexpr\columnsparameter\c!offset-\columnsparameter\c!rulethickness\relax}% \else \d_page_mul_offset\zeropoint \fi \edef\p_command{\columnsparameter\c!command}% \ifx\p_command\empty \else \let\postprocesscolumnline\p_command \fi \edef\p_height{\columnsparameter\c!height}% \ifx\p_height\empty \d_page_mul_forced_height\textheight \heightencolumnsfalse \else \d_page_mul_forced_height\p_height\relax \heightencolumnstrue \fi \edef\p_direction{\columnsparameter\c!direction}% \ifx\p_direction\v!right \setfalse\c_page_mul_reverse \else \settrue\c_page_mul_reverse \fi \edef\p_balance{\columnsparameter\c!balance}% \ifx\p_balance\v!yes \balancecolumnstrue \else \balancecolumnsfalse \fi % % this won't work (blocked by check for overloading; too fuzzy anyway) % \installalign\v!yes {\page_columns_align_option_yes }% \stretchcolumnstrue \inheritcolumnsfalse % \installalign\v!no {\page_columns_align_option_no }% \stretchcolumnsfalse\inheritcolumnsfalse % \installalign\v!text{\page_columns_align_option_text}% \stretchcolumnsfalse\inheritcolumnstrue % % \stretchcolumnsfalse \inheritcolumnstrue \edef\p_align{\columnsparameter\c!align}% \ifx\p_align\empty \else \setupalign[\p_align]% \fi \edef\p_tolerance{\columnsparameter\c!tolerance}% \ifx\p_tolerance\empty \else \setuptolerance[\p_tolerance]% \fi \edef\p_blank{\columnsparameter\c!blank}% \ifx\p_blank\empty \else \setupblank[\p_blank]% \fi \ifdim\s_spac_whitespace_parskip>\zeropoint\relax \setupwhitespace[\p_blank]% \fi \c_page_mul_balance_minimum\columnsparameter\c!ntop\relax \edef\p_page_mul_rule{\columnsparameter\c!rule}% \expandnamespacemacro\??columnseparators\p_page_mul_rule\s!unknown \to \t_page_mul_initialize %D Columns breaks \installcolumnbreakmethod \s!multicolumn \v!preference {\goodbreak} % \installcolumnbreakmethod \s!multicolumn \v!yes % {\vskip\textheight % \penalty-200 % we can mark and intercept this % \vskip-\textheight} \installcolumnbreakmethod \s!multicolumn \v!yes {\vskip .5\pagegoal \penalty-200 % we can mark and intercept this \vskip-.5\pagegoal} %D Next we initialize the lot: \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 New: only at start of columns; may change ! Rather interwoven and therefore %D to be integrated when the multi column modules are merged. \unexpanded\def\setupcolumnspan[#1]% {\getparameters[\??ks][#1]} \presetlocalframed [\??ks] \setupcolumnspan [\c!n=2, \c!offset=\v!overlay, \c!frame=\v!off] \newbox\b_page_columns_span \let\page_mul_postprocess_spanbox\gobbleoneargument \unexpanded\def\startcolumnspan {\dosingleempty\dostartcolumnspan} \unexpanded\def\stopcolumnspan {\egroup} \def\dostartcolumnspan[#1]% {\bgroup \setupcolumnspan[#1]% \forgetall \ifinsidecolumns \advance\hsize \d_page_mul_distance \hsize\@@ksn\hsize \advance\hsize -\d_page_mul_distance \fi \dowithnextboxcs\dofinishcolumnsetspan\vbox\bgroup %\topskipcorrection % becomes an option ! \EveryPar{\begstrut\EveryPar{}}} % also ! \def\dofinishcolumnsetspan {\setbox\b_page_columns_span\flushnextbox \ifinsidecolumns\wd\b_page_columns_span\hsize\fi \page_mul_postprocess_spanbox\b_page_columns_span \scratchdimen\ht\b_page_columns_span \setbox\b_page_columns_span\hbox % depth to be checked, probably option! {\localframed[\??ks][\c!offset=\v!overlay]{\box\b_page_columns_span}}% \ht\b_page_columns_span\scratchdimen \dp\b_page_columns_span\strutdp \wd\b_page_columns_span\hsize \ifinsidecolumns \ifnum\@@ksn>1 \page_otr_command_set_vsize \dohandleallcolumns {\ifnum\currentcolumn>\@@ksn\else \global\setbox\currenttopcolumnbox=\vbox {\ifnum\currentcolumn=1 \snaptogrid\vbox{\copy\b_page_columns_span} \else \snaptogrid\vbox{\vphantom{\copy\b_page_columns_span}} \fi}% \wd\currenttopcolumnbox\hsize \global\advance\vsize -\ht\currenttopcolumnbox \fi} \pagegoal\vsize \else \snaptogrid\vbox{\box\b_page_columns_span} \fi \else \snaptogrid\vbox{\box\b_page_columns_span} \fi \endgraf \ifvmode\prevdepth\strutdp\fi \egroup} %D Undocumented and still under development.\ifdefined\startsimplecolumns \else \unexpanded\def\startsimplecolumns {\dosingleempty\page_mul_simple_start} \def\page_mul_simple_start[#1]% {\bgroup \setsimplecolumnshsize[#1]% \nopenalties \setbox\scratchbox\vbox\bgroup \forgetall} % \blank[\v!disable] \unexpanded\def\stopsimplecolumns {\removebottomthings \egroup \rigidcolumnbalance\scratchbox \egroup} \unexpanded\def\setsimplecolumnshsize[#1]% {\getdummyparameters [\c!width=\hsize, \c!distance=1.5\bodyfontsize, \c!n=2, \c!lines=0, #1]% \edef\rigidcolumnlines {\directdummyparameter\c!lines}% \setrigidcolumnhsize {\directdummyparameter\c!width}% {\directdummyparameter\c!distance}% {\directdummyparameter\c!n}} %D Moved here: \unexpanded\def\page_mul_command_test_column {\dodoubleempty\page_mul_command_test_column_indeed} \unexpanded\def\page_mul_command_test_column_indeed[#1][#2]% works on last column {\page_otr_command_flush_top_insertions\endgraf \ifdim\pagegoal<\maxdimen \ifdim\pagetotal<\pagegoal \d_page_tests_test\dimexpr \pagegoal -\pagetotal \ifdim\lastskip<\parskip+\parskip\fi \ifsecondargument+#2\fi \relax \getrawnoflines\d_page_tests_test % (raw) \ifnum#1>\noflines \column \fi \else \penalty-\plustenthousand % (untested) \fi \fi} %D but fragile anyway. \let\page_mul_command_package_contents\page_one_command_package_contents \let\page_mul_command_flush_float_box \page_one_command_flush_float_box \let\page_mul_command_flush_all_floats\page_one_command_flush_all_floats \defineoutputroutine [\s!multicolumn] [\s!page_otr_command_routine =\page_mul_command_routine, \s!page_otr_command_package_contents =\page_mul_command_package_contents, \s!page_otr_command_set_vsize =\page_mul_command_set_vsize, \s!page_otr_command_set_hsize =\page_mul_command_set_hsize, % \s!page_otr_command_synchronize_hsize =\page_mul_command_synchronize_hsize, \s!page_otr_command_next_page =\page_mul_command_next_page, \s!page_otr_command_next_page_and_inserts =\page_mul_command_next_page_and_inserts, % \s!page_otr_command_set_top_insertions =\page_mul_command_set_top_insertions, % \s!page_otr_command_set_bottom_insertions =\page_mul_command_set_bottom_insertions, \s!page_otr_command_flush_top_insertions =\page_mul_command_flush_top_insertions, % \s!page_otr_command_flush_bottom_insertions=\page_mul_command_flush_bottom_insertions, \s!page_otr_command_check_if_float_fits =\page_mul_command_check_if_float_fits, % \s!page_otr_command_set_float_hsize =\page_mul_command_set_float_hsize, \s!page_otr_command_flush_float_box =\page_mul_command_flush_float_box, \s!page_otr_command_side_float_output =\page_mul_command_side_float_output, \s!page_otr_command_synchronize_side_floats=\page_mul_command_synchronize_side_floats, \s!page_otr_command_flush_floats =\page_mul_command_flush_floats, \s!page_otr_command_flush_side_floats =\page_mul_command_flush_side_floats, \s!page_otr_command_flush_saved_floats =\page_mul_command_flush_saved_floats, \s!page_otr_command_flush_all_floats =\page_mul_command_flush_all_floats, % \s!page_otr_command_flush_margin_blocks =\page_mul_command_flush_margin_blocks, % not used \s!page_otr_command_test_column =\page_mul_command_test_column ] \installfloatmethod \s!multicolumn \v!here \page_mul_place_float_here \installfloatmethod \s!multicolumn \v!force \page_mul_place_float_force \installfloatmethod \s!multicolumn \v!top \page_mul_place_float_top \installfloatmethod \s!multicolumn \v!bottom \page_mul_place_float_bottom \appendtoks \flushingcolumnfloatsfalse \to \everybeforesectionheadhandle \appendtoks \flushingcolumnfloatstrue \to \everyaftersectionheadhandle \protect \endinput