page-mul.mkiv /size: 64 Kb    last modification: 2021-10-28 13:50
1%D \module
2%D   [       file=page-mul, % was: core-mul
3%D        version=1998.03.15,
4%D          title=\CONTEXT\ Page Macros,
5%D       subtitle=Multi Column Output,
6%D         author=Hans Hagen,
7%D           date=\currentdate,
8%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
9%C
10%C This module is part of the \CONTEXT\ macro||package and is
11%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
12%C details.
13
14% todo: basecolumns as parent for columns and itemize
15
16% !!! there are some issues with hsize an vsize as well as flushing
17% !!! floats but this module will be redone anyway
18%
19% can have some vpack and hpack
20
21\writestatus{loading}{ConTeXt Page Macros / Simple Multi Column}
22
23%D This module is mostly a copy from the original multi column routine as
24%D implemented in \type {core-mul}. When the main OTR macro's were
25%D isolated in modules and column sets were introduced, this module became
26%D part of the OTR modules. As a result this module is no longer generic.
27
28\unprotect
29
30\definesystemvariable {ks}   % KolomSpan
31
32% check \count<insert> multiplications
33
34%D The following macro's implement a multi||column output routine. The original
35%D implementation was based on Donald Knuth's implementation, which was adapted by
36%D Craig Platt to support balancing of the last page. I gradually adapted Platt's
37%D version to our needs but under certain circumstances things still went wrong. I
38%D considered all calls to Platt's \type{\page_mul_routine_error} as undesirable.
39%D
40%D This completely new implementation can handle enough situations for everyday
41%D documents, but is still far from perfect. While at the moment the routine doesn't
42%D support all kind of floats, it does support:
43%D
44%D \startitemize[packed]
45%D \item  an unlimitted number of columns
46%D \item  ragged or not ragged bottoms
47%D \item  optional balancing without \type{\page_mul_routine_errors}
48%D \item  different \type{\baselineskips}, \type{\spacing}, \type {\topskip} and
49%D        \type {\maxdepth}
50%D \item  left- and right indentation, e.g. within lists
51%D \item  moving columns floats to the next column or page
52%D \item  handling of floats that are to wide for a columns
53%D \stopitemize
54%D
55%D One could wonder why single and multi||columns modes are still separated. One
56%D reason for this is that \TeX\ is not suited well for handling multi||columns. As
57%D a result, the single columns routines are more robust. Handling one column as a
58%D special case of multi||columns is posible but at the cost of worse float
59%D handling, worse page breaking, worse etc. Complicated multi||column page handling
60%D should be done in \cap{DTP}||systems anyway.
61
62\installcorenamespace {columns}
63
64\installframedcommandhandler \??columns {columns} \??columns
65
66%D Going to a new columns is done by means of a \type {\ejectcolumn}. The
67%D following definition does not always work.
68
69    \unexpanded\def\ejectcolumn % not good enough
70      {\goodbreak
71       \showmessage\m!columns2\empty}
72
73%D The output routines plug into a more generic mechanism as can be seen at the
74%D end of this file. So, occasionally we need to define some plugin code.
75
76\unexpanded\def\page_mul_place_float_top
77  {\showmessage\m!columns4\empty\page_one_place_float_here}
78
79\unexpanded\def\page_mul_place_float_bottom
80  {\showmessage\m!columns5\empty\page_one_place_float_here}
81
82\unexpanded\def\page_mul_place_float_here
83  {\page_one_place_float_here}
84
85\unexpanded\def\page_mul_place_float_force
86  {\page_one_place_float_force}
87
88\unexpanded\def\page_mul_command_side_float_output
89  {\page_otr_construct_and_shipout\unvbox\normalpagebox\zerocount} % three arguments
90
91\unexpanded\def\page_mul_command_flush_side_floats
92  {\page_sides_forget_floats}
93
94\unexpanded\def\page_mul_command_synchronize_side_floats
95  {\page_sides_forget_floats}
96
97\unexpanded\def\page_mul_command_next_page
98  {\page_otr_eject_page}
99
100\unexpanded\def\page_mul_command_next_page_and_inserts
101  {\page_otr_eject_page_and_flush_inserts}
102
103\let\page_mul_initialize_floats\relax
104\let\page_mul_flush_floats     \relax
105\let\page_mul_flush_float      \relax
106
107%D A hook:
108
109\let\finishcolumnbox\relax % todo in mkiv
110
111%D This will change to a local one:
112
113\ifdefined\nofcolumns \else \newcount\nofcolumn \fi
114\ifdefined\mofcolumns \else \newcount\mofcolumn \fi
115
116\appendtoks
117    \nofcolumns\columnsparameter\c!n\relax
118\to \everysetupcolumns
119
120%D Columns are separated by spacing or rules or whatever suits.
121
122\installcorenamespace{columnseparators}
123
124\setvalue{\??columnseparators\v!on     }{\let\page_mul_between_columns\page_mul_between_columns_rule}
125\setvalue{\??columnseparators\v!off    }{\let\page_mul_between_columns\page_mul_between_columns_space}
126\setvalue{\??columnseparators          }{\let\page_mul_between_columns\page_mul_between_columns_space}
127\setvalue{\??columnseparators\s!unknown}{\let\page_mul_between_columns\p_page_mul_rule}
128
129\def\page_mul_between_columns_rule
130  {\bgroup
131   \starttextproperties
132   \scratchdistance\dimexpr\columnsparameter\c!distance/2\relax
133   \hskip\scratchdistance\relax
134   \vrule
135     \s!width\linewidth
136     \ifnum\bottomraggednessmode=\plustwo % baselinebottom
137       \s!depth\strutdepth
138     \fi
139   \hskip\scratchdistance\relax
140   \stoptextproperties
141   \egroup}
142
143\def\page_mul_between_columns_space
144  {\hskip\columnsparameter\c!distance\relax}
145
146\let\page_mul_between_columns\page_mul_between_columns_space
147
148%D We declare some registers:
149
150\newdimen      \d_page_mul_available_width
151\newdimen      \d_page_mul_distance
152\newdimen      \d_page_mul_leftskip
153\newdimen      \d_page_mul_rightskip
154\newdimen      \d_page_mul_offset
155\newdimen      \d_page_mul_forced_height
156\newdimen      \d_page_mul_used_width
157\newdimen      \d_page_mul_temp
158
159\newcount      \c_page_mul_balance_minimum
160\newcount      \c_page_mul_n_of_lines
161
162\newbox        \b_page_mul_preceding
163\newdimen      \d_page_mul_preceding_height
164\newdimen      \d_page_mul_preceding_depth
165\newconditional\c_page_mul_preceding_present
166
167\newbox        \b_page_mul_preceding_rest_of_page
168
169\newconditional\c_page_mul_reverse
170\newconditional\c_page_mul_trace
171
172%D The next dimensions reports the final column height .. todo
173
174    \newdimen\finalcolumnheights
175    \newcount\finalcolumnlines
176
177    \newdimen\savedpagetotal % brrr
178
179    \newif\ifstretchcolumns       \stretchcolumnsfalse
180    \newif\ifheightencolumns      \heightencolumnsfalse
181    \newif\ifinheritcolumns       \inheritcolumnsfalse
182    \newif\ifbalancecolumns       %\balancecolumnstrue
183
184%D An important one:
185
186\unexpanded\def\page_mul_command_set_hsize % beware, this one is available for use in macros
187  {\setbox\scratchbox\hbox{\page_mul_between_columns}%
188   \d_page_mul_distance\wd\scratchbox
189   \d_page_mul_available_width\dimexpr
190      \makeupwidth
191     -\d_page_mul_leftskip
192     -\d_page_mul_rightskip
193     -\nofcolumns\d_page_mul_distance
194     +\d_page_mul_distance
195   \relax
196   \d_page_mul_used_width\dimexpr
197      \d_page_mul_available_width/\nofcolumns
198     -\d_page_mul_offset*\plustwo
199   \relax
200   \textwidth\d_page_mul_used_width % needs thinking ... grouping etc
201   \hsize\d_page_mul_used_width}
202
203%D Torture test:
204%D
205%D \startbuffer
206%D \startbuffer[b]
207%D \startcolumns
208%D   \input tufte
209%D \stopcolumns
210%D \stopbuffer
211%D \typebuffer[b] \getbuffer[b]
212%D
213%D \startbuffer[b]
214%D \startnarrower
215%D   \input tufte
216%D \stopnarrower
217%D \stopbuffer
218%D \typebuffer[b] \getbuffer[b]
219%D
220%D \startbuffer[b]
221%D \startcolumns \startnarrower
222%D   \input tufte
223%D \stopnarrower \stopcolumns
224%D \stopbuffer
225%D \typebuffer[b] \getbuffer[b]
226%D
227%D \startbuffer[b]
228%D \startnarrower \startcolumns
229%D   \input tufte
230%D \stopcolumns \stopnarrower
231%D \stopbuffer
232%D \typebuffer[b] \getbuffer[b]
233%D
234%D \startbuffer[b]
235%D \startcolumns \startnarrower[left]
236%D   \input tufte
237%D \stopnarrower \stopcolumns
238%D \stopbuffer
239%D \typebuffer[b] \getbuffer[b]
240%D
241%D \startbuffer[b]
242%D \startnarrower[left] \startcolumns
243%D   \input tufte
244%D \stopcolumns \stopnarrower
245%D \stopbuffer
246%D \typebuffer[b] \getbuffer[b]
247%D
248%D \startbuffer[b]
249%D \startnarrower \startcolumns \startnarrower
250%D   \input tufte
251%D \stopnarrower\stopcolumns \stopnarrower
252%D \stopbuffer
253%D \typebuffer[b] \getbuffer[b]
254%D
255%D \startbuffer[b]
256%D \startnarrower[left] \startcolumns \startnarrower
257%D   \input tufte
258%D \stopnarrower\stopcolumns \stopnarrower
259%D \stopbuffer
260%D \typebuffer[b] \getbuffer[b]
261%D \stopbuffer
262%D
263%D \start
264%D \def\postprocesscolumnline#1{\ruledhbox{\strut\box#1}\hss}
265%D \getbuffer
266%D \stop
267
268%D One should be aware that when font related dimensions are used in typesetting the
269%D in||between material, these dimensions are influenced by bodyfont switches inside
270%D multi||column mode.
271
272\setnewconstant\multicolumnlinemethod\zerocount % 0=normal 1=raw
273
274\def\multicolumnovershootratio{.5} % {\ifgridsnapping0\else.5\fi}
275
276\unexpanded\def\page_mul_set_n_of_lines
277  {\settotalinsertionheight
278   \d_page_mul_temp\dimexpr
279     -\d_page_mul_offset*\plustwo
280     +\textheight
281      \ifdim\d_page_mul_preceding_height>\zeropoint -\d_page_mul_preceding_height \fi
282     -\totalinsertionheight
283   \relax
284   \ifcase\multicolumnlinemethod
285     \getnoflines   \d_page_mul_temp \or
286     \getrawnoflines\d_page_mul_temp \else
287     \getrawnoflines\d_page_mul_temp
288   \fi
289   % added 30/7/2004
290   \ifnum\layoutlines>\zerocount \ifnum\noflines>\layoutlines
291     \noflines\layoutlines
292   \fi \fi
293   \c_page_mul_n_of_lines\noflines}
294
295\unexpanded\def\page_mul_command_set_vsize
296  {\page_one_command_set_vsize % indeed?
297   \page_mul_set_n_of_lines
298   \d_page_mul_temp\nofcolumns\dimexpr
299      \c_page_mul_n_of_lines\openlineheight
300     +\multicolumnovershootratio\openlineheight % collect enough data
301   \relax
302   \global\vsize\d_page_mul_temp
303   \pagegoal    \d_page_mul_temp} % let's do it only here
304
305%D It really starts here. After some checks and initializations we change the output
306%D routine to continous multi||column mode. This mode handles columns that fill the
307%D current and next full pages. The method used is (more or less) multiplying \type
308%D {\vsize} and dividing \type {\hsize} by \type {\nofcolumns}. More on this can be
309%D found in the \TeX book. We save the top of the current page in box
310%D \type {\b_page_mul_preceding}.
311%D
312%D We manipulate \type {\topskip} a bit, just to be shure that is has no
313%D flexibility. This has te be done every time a font switch takles place, because
314%D \type {\topskip} can depend on this.
315
316\newconstant\c_page_mul_routine
317
318\setnewconstant\c_page_mul_routine_regular   \zerocount
319\setnewconstant\c_page_mul_routine_intercept \plusone
320\setnewconstant\c_page_mul_routine_continue  \plustwo
321\setnewconstant\c_page_mul_routine_balance   \plusthree
322\setnewconstant\c_page_mul_routine_error     \plusfour
323
324\unexpanded\def\page_mul_command_routine
325  {\ifcase\c_page_mul_routine
326     \page_one_command_routine
327   \or
328     \page_mul_routine_intercept
329   \or
330     \page_mul_routine_continue
331   \or
332     \page_mul_routine_balance
333   \or
334     \page_mul_routine_error
335   \fi}
336
337\def\page_mul_routine_intercept
338  {\global\setbox\b_page_mul_preceding\vbox
339     {\page_otr_command_flush_top_insertions
340      \unvbox\normalpagebox}}
341
342\def\page_mul_routine_error
343  {\showmessage\m!columns3\empty
344   \page_otr_construct_and_shipout\unvbox\normalpagebox\zerocount} % three arguments
345
346%D When we leave the multi||column mode, we have to process the not yet shipped out
347%D part of the columns. When we don't balance, we simply force a continuous output,
348%D but a balanced output is more tricky.
349%D
350%D First we try to fill up the page and when all or something is left we try to
351%D balance things. This is another useful adaption of the ancesters of these
352%D macro's. It takes some reasoning to find out what happens and maybe I'm making
353%D some mistake, but it works.
354%D
355%D Voiding box \type {\b_page_mul_preceding} is sometimes necessary, e.g. when there is no
356%D text given between \type {\begin..} and \type {\end..}. The \type {\par} is
357%D needed!
358
359%D Because some initializations happen three times, we defined a macro for
360%D them. Erasing \type{\everypar} is needed because we don't want anything
361%D to interfere.
362
363\unexpanded\def\page_mul_initialize_variables
364  {\reseteverypar
365   \dontcomplain
366   \settopskip
367   \setmaxdepth
368   \topskip        1\topskip
369   \splittopskip    \topskip
370   \splitmaxdepth   \maxdepth
371   \boxmaxdepth     \maxdepth % dangerous
372   \emergencystretch\zeropoint
373   \relax} % sometimes needed !
374
375%D Flushing the page comes to pasting the columns together and appending the result
376%D to box \type {\b_page_mul_preceding}, if not void. I've seen a lot of implementations in
377%D which some skip was put between normal text and multi||column text. When we don't
378%D want this, the baselines can be messed up. I hope the seemingly complicated
379%D calculation of a correction \type {\kern} is adequate to overcome this. Although
380%D not watertight, spacing is taken into account and even multiple mode changes on
381%D one page go well. But cross your fingers and don't blame me.
382%D
383%D One of the complications of flushing out the boxes is that \type {\b_page_mul_preceding}
384%D needs to be \type {\unvbox}'ed, otherwise there is too less flexibility in the
385%D page when using \type {\raggedbottom}. It took a lot of time before these kind of
386%D problems were overcome. Using \type {\unvbox} at the wrong moment can generate
387%D \type {\page_mul_routine_error}'s.
388%D
389%D One can use the macros \type {\maxcolumnheight} and \type {\maxcolumndepth} when
390%D generating material between columns as well as postprocessing column lines.
391
392\newdimen\maxcolumnheight
393\newdimen\maxcolumndepth
394
395\newbox\columnpagebox
396
397\def\page_mul_calculate_column_result_dimensions
398  {\maxcolumnheight\zeropoint
399   \maxcolumndepth \zeropoint
400   \dohandleallcolumnscs\page_mul_calculate_column_result_dimensions_step}
401
402\def\page_mul_calculate_column_result_dimensions_step
403  {\ifdim\ht\currentcolumnbox>\maxcolumnheight
404     \maxcolumnheight\ht\currentcolumnbox
405   \fi
406   \ifdim\dp\currentcolumnbox>\maxcolumndepth
407     \maxcolumndepth\dp\currentcolumnbox
408   \fi}
409
410\setnewconstant\multicolumntopflushmethod\plusone % 0: no correction, 1: correction when topstuff, 2: correction, 3: correction++
411\setnewconstant\multicolumntopalignmethod\plustwo % 0: nothing, 1: force grid, 2: follow grid
412
413\def\page_mul_flush_preceding_normal
414  {\unvbox\b_page_mul_preceding}
415
416\def\page_mul_flush_preceding_ongrid
417  {\scratchdimen\dimexpr
418     \savedpagetotal
419    -\d_page_mul_preceding_height
420    -\d_page_mul_preceding_depth
421    -\topskip
422   \relax
423   \box\b_page_mul_preceding
424   \kern\scratchdimen}
425
426\def\page_mul_flush_packaged_columns_continued
427  {\bgroup
428   \page_mul_flush_packaged_columns_indeed
429   \box\columnpagebox
430   \egroup}
431
432\def\page_mul_flush_packaged_columns_balanced
433  {\bgroup
434   \page_mul_flush_packaged_columns_indeed
435   % messy correction, we need to rewrite this module (newcolumns)
436   \setbox\columnpagebox\vbox
437     {\offinterlineskip
438      \scratchdimen\htdp\columnpagebox
439      \box\columnpagebox
440      \vskip-\scratchdimen}%
441   \ht\columnpagebox\dimexpr
442     \noflines\openlineheight
443     -\openstrutdepth
444     \ifgridsnapping
445        % quick hack (at least it works with itemize)
446     \else
447       -\openlineheight
448       +\topskip
449     \fi
450   \relax
451   \dp\columnpagebox\openstrutdepth
452   % end of mess
453   \box\columnpagebox
454   \egroup}
455
456\def\page_mul_synchronize_marks
457  {\dohandleallcolumns{\page_marks_synchronize_column\plusone\nofcolumns\mofcolumns\currentcolumnbox}}
458
459\def\page_mul_flush_packaged_columns_indeed
460  {\ifvoid\b_page_mul_preceding
461     \setfalse\c_page_mul_preceding_present % will be set elsewhere
462   \else
463     \settrue\c_page_mul_preceding_present
464     \page_apply_postprocessors_box\b_page_mul_preceding
465   \fi
466   \forgetall
467   \page_mul_initialize_variables
468   \page_mul_calculate_column_result_dimensions
469   \page_mul_postprocess_linenumbers
470   \page_mul_synchronize_marks
471   \page_mul_postprocess_lines
472   \page_mul_postprocess_columns
473   \dohandleallcolumns
474     {\global\setbox\currentcolumnbox\hpack to \d_page_mul_used_width
475        {\box\currentcolumnbox}%
476      \wd\currentcolumnbox\d_page_mul_used_width
477      \ifheightencolumns
478        \ht\currentcolumnbox\d_page_mul_forced_height
479      \fi}%
480   \page_mul_calculate_column_result_dimensions
481   \overlaycolumnfootnotes
482   \setbox\columnpagebox\vpack % \vbox
483     {\ifconditional\c_page_mul_reverse\reversehpack\else\naturalhpack\fi to \makeupwidth
484        {\hskip\ifconditional\c_page_mul_reverse\d_page_mul_rightskip\else\d_page_mul_leftskip\fi\relax
485         \dohandleallcolumns
486           {\finishcolumnbox
487               {\setbox\scratchbox\hpack
488                   {\ifx\finishcolumnbox\relax\else\strut\fi
489                   \box\currentcolumnbox}% hm, why strut
490               \anch_mark_column_box\scratchbox\currentcolumn
491               \box\scratchbox}%
492               \hfil}%
493         \unskip
494         \hskip\ifconditional\c_page_mul_reverse\d_page_mul_leftskip\else\d_page_mul_rightskip\fi}}%
495   \scratchdimen\zeropoint
496   \dohandleallcolumns
497     {\ifdim-\ht\currenttopcolumnbox<\scratchdimen
498        \scratchdimen-\ht\currenttopcolumnbox
499      \fi
500      \global\setbox\currenttopcolumnbox\emptybox}%
501   \advance\scratchdimen \ht\columnpagebox
502   \setbox\scratchbox\hbox to \makeupwidth % between can be something so no \hpack
503     {\vrule
504        \s!width \zeropoint
505        \s!height\scratchdimen
506        \s!depth \dp\columnpagebox
507      \dostepwiserecurse\plustwo\nofcolumns\plusone{\hfil\page_mul_between_columns}\hfil}%
508   \setbox\columnpagebox\hpack
509     {\box\columnpagebox
510      \hskip-\makeupwidth
511      \box\scratchbox}%
512   \page_mul_postprocess_page
513   \ifconditional\c_page_mul_preceding_present
514     \settrue\c_page_mul_preceding_present
515     % next some incredible crappy code
516     \ifcase\multicolumntopalignmethod
517       \page_mul_flush_preceding_normal % not on grid
518     \or
519       \page_mul_flush_preceding_ongrid % force on grid
520     \else\ifgridsnapping
521       \page_mul_flush_preceding_ongrid % obey grid settings, force on grid
522     \else
523       \page_mul_flush_preceding_normal % ignore grid settings, not on grid
524     \fi \fi
525   \fi
526   \global\d_page_mul_preceding_height\zeropoint
527   \page_otr_command_set_vsize
528   \dosomebreak\nobreak % hm, only needed when topstuff
529   \ifgridsnapping \else
530     \ifcase\multicolumntopflushmethod
531       % sometimes method 1 goes wrong, so we need a way out; best sort this out
532       % when we run into it again
533     \or
534       % \input tufte \startcolumns \showbaselines \input tufte \stopcolumns \input tufte
535       \ifconditional\c_page_mul_preceding_present
536        %          \scratchdimen\topskip
537        %          \advance\scratchdimen -\openstrutheight
538        %          \nointerlineskip
539        %          \vskip-\scratchdimen
540         \nointerlineskip
541         \vskip\dimexpr\openstrutheight-\topskip\relax
542       \fi
543     \or
544        %        \scratchdimen\topskip
545        %        \advance\scratchdimen -\openstrutheight
546        %        \nointerlineskip
547        %        \vskip-\scratchdimen
548       \nointerlineskip
549       \vskip\dimexpr\openstrutheight-\topskip\relax
550     \or
551       % untested but maybe handy
552        %        \scratchdimen\topskip
553        %        \advance\scratchdimen -\openstrutheight
554        %        \nointerlineskip
555        %        \vskip-\scratchdimen
556        %        \vskip-\lineheight
557        %        \vbox{\strut}%
558       \nointerlineskip
559       \vskip\dimexpr\openstrutheight-\topskip-\lineheight\relax
560       \vbox{\strut}%
561     \fi
562   \fi
563   \prevdepth\openstrutdepth
564   \nointerlineskip
565   \dp\columnpagebox\zeropoint
566   \global\finalcolumnheights\ht\columnpagebox
567   \getnoflines\finalcolumnheights
568   \global\finalcolumnlines\noflines}
569
570%D In case one didn't notice, finaly \type{\finishcolumnbox} is applied to
571%D all boxes. One can use these hooks for special purposes.
572%D
573%D Once upon a time I wanted to manipulate the individual lines in a column.
574%D This feature is demonstrated in the two examples below.
575%D
576%D \startbuffer
577%D \def\postprocesscolumnline#1% or \postprocesscolumnbox
578%D   {\ruledhbox{\box#1}\hss}
579%D
580%D \startcolumns[n=4]
581%D \dorecurse{25}{line: \recurselevel\par}
582%D \stopcolumns
583%D \stopbuffer
584%D
585%D \typebuffer
586%D
587%D Here we show the natural width of the lines:
588%D
589%D {\getbuffer}
590%D
591%D The next example does a bit more advanced manipulation:
592%D
593%D \startbuffer
594%D \def\postprocesscolumnline#1%
595%D   {\ifodd\currentcolumn
596%D      \hfill\unhbox#1\relax
597%D    \else
598%D      \relax\unhbox#1\hfill
599%D    \fi}
600%D
601%D \startcolumns[n=4]
602%D \dorecurse{25}{line \recurselevel\par}
603%D \stopcolumns
604%D \stopbuffer
605%D
606%D \typebuffer
607%D
608%D Here we also see an application of \type{\currentcolumn}:
609%D
610%D {\getbuffer}
611%D
612%D This feature is implemented using the reshape macros presented
613%D in \type{supp-box}.
614
615%ifdefined\page_postprocessors_column\else\let\page_postprocessors_column\relax\fi % operates on passed box
616\ifdefined\postprocesscolumnline     \else\let\postprocesscolumnline     \relax\fi % operates on passed box
617\ifdefined\postprocesscolumnbox      \else\let\postprocesscolumnbox      \relax\fi % operates on passed box
618\ifdefined\postprocesscolumnpagebox  \else\let\postprocesscolumnpagebox  \relax\fi % operates on passed box
619
620%def\page_mul_postprocess_linenumbers{\ifx\page_postprocessors_column\relax\else\page_mul_postprocess_linenumbers_indeed\fi}
621\def\page_mul_postprocess_lines      {\ifx\postprocesscolumnline     \relax\else\page_mul_postprocess_lines_indeed      \fi}
622\def\page_mul_postprocess_columns    {\ifx\postprocesscolumnbox      \relax\else\page_mul_postprocess_columns_indeed    \fi}
623\def\page_mul_postprocess_page       {\ifx\postprocesscolumnpagebox  \relax\else\page_mul_postprocess_page_indeed       \fi}
624
625% \def\page_mul_postprocess_linenumbers_indeed
626%   {\dohandleallcolumns{\page_apply_postprocessors_column\currentcolumnbox}}
627
628\def\page_mul_postprocess_linenumbers
629  {\dohandleallcolumns{\page_apply_postprocessors_column\currentcolumnbox}}
630
631\def\page_mul_postprocess_lines_indeed
632  {\dohandleallcolumnscs\page_mul_postprocess_lines_step}
633
634\def\page_mul_postprocess_lines_step % TODO: use lua solution instead
635   {\global\setbox\currentcolumnbox\vtop
636      {\beginofshapebox
637       \unvbox\currentcolumnbox
638       \unskip\unskip
639       \endofshapebox
640       \reshapebox
641         {\scratchheight\ht\shapebox
642          \scratchdepth \dp\shapebox
643          \setbox\shapebox\hbox to \hsize
644            {\postprocesscolumnline\shapebox}%
645          \ht\shapebox\scratchheight
646          \dp\shapebox\scratchdepth
647          \box\shapebox}%
648       \flushshapebox
649       \reseteverypar
650       \parskip\zeropoint % = \forgetall
651       \verticalstrut
652       \vskip-\struttotal
653       \vfil}}
654
655\def\page_mul_postprocess_columns_indeed
656  {\dohandleallcolumnscs\page_mul_postprocess_columns_step}
657
658\def\page_mul_postprocess_columns_step
659  {\global\setbox\currentcolumnbox\hbox
660     {\postprocesscolumnbox\currentcolumnbox}}
661
662\def\page_mul_postprocess_page_indeed
663  {\postprocesscolumnpagebox\columnpagebox}
664
665%D Here comes the simple splitting routine. It's a bit longer than expected because
666%D of ragging bottoms or not. This part can be a bit shorter but I suppose that I
667%D will forget what happens. The splitting takes some already present material
668%D (think of floats) into account!
669%D
670%D First we present some auxiliary routines. Any material, like for instance floats,
671%D that is already present in the boxes is preserved.
672
673\newdimen\d_page_mul_split_height_used
674\newdimen\d_page_mul_split_height_max
675
676\def\page_mul_split_column#1#2#3#4% copy or box
677  {\bgroup
678   \ifdim\ht#4>\zeropoint
679     \d_page_mul_split_height_used#3\relax
680     \d_page_mul_split_height_max\d_page_mul_split_height_used
681     \advance\d_page_mul_split_height_used -\ht#4%
682     \columnfootnotecorrection{#1}\d_page_mul_split_height_used
683     \setbox\scratchbox\vsplit#2 to \d_page_mul_split_height_used
684     \global\setbox#1\vbox to \d_page_mul_split_height_max
685       {\ifgridsnapping
686          \scratchdimen\dimexpr\topskip-\openstrutheight\relax
687          \vskip\scratchdimen
688          \copy#4%
689          \vskip-\scratchdimen
690        \else
691          \unvcopy#4%
692        \fi
693        \unvbox\scratchbox
694        \fakecolumnfootnotes{#1}}%
695   \else\ifcase\c_strc_notes_page_location
696     \global\setbox#1\vsplit#2 to #3%
697     \global\setbox#1\vbox
698       {\unvbox{#1}}% % or \box ?
699   \else
700     \columnfootnotecorrection{#1}{#3}%
701     \setbox\scratchbox\vsplit#2 to #3%
702     \global\setbox#1\vbox to #3%
703       {\unvbox\scratchbox
704        \fakecolumnfootnotes{#1}}%
705   \fi \fi
706   \egroup}
707
708\def\page_mul_split_current_column#1#2%
709  {\page_mul_split_column\currentcolumnbox{#1}{#2}\currenttopcolumnbox}
710
711\def\page_mul_split_first_column#1#2%
712  {\page_mul_split_column\firstcolumnbox{#1}{#2}\firsttopcolumnbox}
713
714\def\page_mul_split_last_column#1#2%
715  {\global\setbox\lastcolumnbox\vbox
716     {\unvcopy\lasttopcolumnbox
717      \unvbox{#1}%
718      \fakecolumnfootnotes\lastcolumnbox}}
719
720%D NEW: still to be documented.
721
722    \def\fakecolumnfootnotes#1%
723      {\relax
724       \ifcase\c_strc_notes_page_location\else
725         \ifnum#1=\lastcolumnbox
726           \fakenotes
727         \fi
728       \fi}
729
730    \def\columnfootnotecorrection#1#2%
731      {\relax
732       \ifcase\c_strc_notes_page_location
733         % page notes
734       \or
735         \ifnum#1=\firstcolumnbox\relax
736           \calculatetotalclevernoteheight
737           \advance#2 -\totalnoteheight
738         \fi
739       \else
740         \ifnum#1=\lastcolumnbox\relax
741           \calculatetotalclevernoteheight
742           \advance#2 -\totalnoteheight
743         \fi
744       \fi}
745
746    \def\overlaycolumnfootnotes
747      {\relax
748       \ifcase\c_strc_notes_page_location
749         % page
750       \or
751         \checknotepresence \ifnotespresent \page_mul_notes_flush_first_column \fi
752       \or
753         \checknotepresence \ifnotespresent \page_mul_notes_flush_last_column \fi
754       \fi}
755
756    \newbox\b_page_mul_notes
757
758    \def\page_mul_notes_flush_first_column
759      {\begingroup
760       \setbox\b_page_mul_notes\vbox{\placenoteinserts}%
761       \ifzeropt\ht\b_page_mul_notes
762         % can't happen as we already checked
763       \else
764         \page_mul_set_n_of_lines
765         \advance\c_page_mul_n_of_lines \minustwo
766         \scratchdimen\dimexpr\c_page_mul_n_of_lines\lineheight+\topskip\relax
767         \setbox\b_page_mul_notes\hpack{\lower\scratchdimen\box\b_page_mul_notes}%
768         \ht\b_page_mul_notes\openstrutheight
769         \dp\b_page_mul_notes\openstrutdepth
770         \wd\b_page_mul_notes\zeropoint
771         \scratchdimen\ht\firstcolumnbox
772         \global\setbox\firstcolumnbox\vbox to \scratchdimen
773           {\box\firstcolumnbox
774            \vskip-\scratchdimen
775            \box\b_page_mul_notes}%
776       \fi
777       \endgroup}
778
779    \def\page_mul_notes_flush_last_column
780      {\begingroup
781       \setbox\b_page_mul_notes\vbox{\placenoteinserts}%
782       \ifzeropt\ht\b_page_mul_notes
783         % can't happen as we already checked
784       \else
785         % maybe here also \page_mul_set_n_of_lines
786         \scratchdimen\dimexpr\ht\firstcolumnbox-\openstrutdepth\relax % \strutdp
787         \getnoflines\scratchdimen
788         \advance\noflines \minustwo
789         \scratchdimen\dimexpr\noflines\lineheight+\topskip\relax
790         \setbox\b_page_mul_notes\hpack{\lower\scratchdimen\box\b_page_mul_notes}%
791         \ht\b_page_mul_notes\openstrutheight
792         \dp\b_page_mul_notes\openstrutdepth
793         \wd\b_page_mul_notes\zeropoint
794         \scratchdimen\ht\lastcolumnbox
795         \global\setbox\lastcolumnbox\vbox to \scratchdimen
796           {\box\lastcolumnbox
797            \vskip-\scratchdimen
798            \box\b_page_mul_notes}%
799       \fi
800       \endgroup}
801
802%D Here comes the routine that splits the long box in columns. The macro \type
803%D {\page_mul_flush_floats} can be used to flush either floats that were present before
804%D the multi||column mode was entered, or floats that migrate to next columns.
805%D Flushing floats is a delicate process.
806
807\def\page_mul_routine_continue
808  {\bgroup
809   \forgetall
810   \page_mul_initialize_variables
811 % \dimen0=\makeupheight
812 % \advance\dimen0 -\d_page_mul_preceding_height
813 % \settotalinsertionheight
814 % \advance\dimen0 -\totalinsertionheight
815 % \ifgridsnapping % evt altijd, nog testen
816 %   \getnoflines{\dimen0}
817 %   \dimen0=\noflines\openlineheight
818 % \fi
819   \page_mul_set_n_of_lines
820   \d_page_mul_balance_target\c_page_mul_n_of_lines\openlineheight
821   \ifconditional\c_page_mul_trace
822     \writestatus\m!columns{continue: lines=\the\c_page_mul_n_of_lines, target=\the\d_page_mul_balance_target, textheight=\the\textheight}%
823   \fi
824   \dohandleallcolumns
825     {\page_mul_split_current_column\normalpagebox\d_page_mul_balance_target}%
826   \setbox\b_page_mul_preceding_rest_of_page\vbox{\unvbox\normalpagebox}%
827   \ifinheritcolumns
828     \ifcase\bottomraggednessmode
829       % 0 = ragged
830       \dohandleallcolumns
831         {\global\setbox\currentcolumnbox\vbox to \ht\firstcolumnbox
832            {\scratchdepth\dp\currentcolumnbox
833             \unvbox\currentcolumnbox
834             \vskip\dimexpr\openstrutdepth-\scratchdepth\relax
835             \prevdepth\openstrutdepth % \strutdp
836             \vfill}}%
837       \strc_notes_check_if_bottom_present
838      %\ifconditional\c_notes_bottom_present \else
839      %  \dimen0\ht\firstcolumnbox % ??
840      %\fi
841     \or
842       % 1 = normal
843       \advance\d_page_mul_balance_target\maxdepth
844       \dohandleallcolumns
845         {\global\setbox\currentcolumnbox\vbox to \d_page_mul_balance_target
846            {\unvbox\currentcolumnbox}}%
847     \or
848       % 2 = baseline
849       % the columns are on top of the baseline
850     \fi
851   \else
852     \dohandleallcolumns
853       {\global\setbox\currentcolumnbox\vbox to \d_page_mul_balance_target
854          {\ifstretchcolumns
855             \unvbox\currentcolumnbox
856           \else
857             \unvbox\currentcolumnbox % wel of niet \unvbox ?
858             \vfill
859           \fi}}%
860     \dohandleallcolumns
861       {\ht\currentcolumnbox\d_page_mul_balance_target}% redundant
862   \fi
863   \setbox\b_page_mul_preceding\vbox{\page_mul_flush_packaged_columns_continued}%
864   \page_otr_construct_and_shipout\box\b_page_mul_preceding\zerocount % three arguments
865   \page_otr_command_set_hsize
866   \page_otr_command_set_vsize
867   \page_mul_flush_floats
868   \unvbox\b_page_mul_preceding_rest_of_page
869   % \penalty\outputpenalty % gaat gruwelijk mis in opsommingen
870   \egroup}
871
872%D And this is the balancing stuff. Again, part of the routine is dedicated to
873%D handling ragged bottoms, but here we also see some handling concerning the
874%D stretching of columns. We set \type {\widowpenalty} at~0, which enables us to
875%D balance columns with few lines. The use of \type {\box2} and \type {\box4}
876%D garantees a more robust check when skips are used.
877
878\newbox        \b_page_mul_balance_content
879\newbox        \b_page_mul_balance_first_column
880\newbox        \b_page_mul_balance_column
881\newconstant   \c_page_mul_balance_tries_max
882\newcount      \c_page_mul_balance_tries
883\newdimen      \d_page_mul_balance_target
884\newdimen      \d_page_mul_balance_target_less
885\newdimen      \d_page_mul_balance_natural_height
886\newdimen      \d_page_mul_balance_regular_height
887\newdimen      \d_page_mul_balance_step
888\newdimen      \d_page_mul_balance_fuzzyness
889\newdimen      \d_page_mul_balance_threshold
890\newconditional\c_page_mul_balance_possible
891
892\c_page_mul_balance_tries_max 250 % 100 is too small when floats are involved
893
894\def\page_mul_routine_balance
895  {\bgroup
896   % why no \forgetall here
897   \page_mul_initialize_variables
898   \widowpenalty\zerocount
899   \setbox\b_page_mul_balance_content\vbox{\unvbox\normalpagebox}%
900   \ifdim\ht\b_page_mul_balance_content>\openlineheight % at least one line
901     \ifnum\c_page_mul_balance_minimum<\plustwo % balance anyway
902       \settrue\c_page_mul_balance_possible
903     \else % check criterium to available lines
904       \getnoflines{\ht\b_page_mul_balance_content}%
905       \divide\noflines \nofcolumns \relax
906       \ifnum\noflines<\c_page_mul_balance_minimum \relax
907         \ifdim\dimexpr\ht\b_page_mul_balance_content+\ht\firsttopcolumnbox+\openlineheight\relax>\makeupheight
908           \settrue\c_page_mul_balance_possible % column exceeding text height
909         \else
910           \setfalse\c_page_mul_balance_possible % it seems to fit
911         \fi
912       \else
913         \settrue\c_page_mul_balance_possible % balance indeed
914       \fi
915     \fi
916   \else
917     \setfalse\c_page_mul_balance_possible % balancing does not make sense
918   \fi
919   \ifconditional\c_page_mul_balance_possible % start balancing, was: \ifdim\ht\b_page_mul_balance_content>\openlineheight
920     \page_mul_balance_try_one
921     \ifinheritcolumns
922       \page_mul_balance_try_two
923     \else
924       \page_mul_balance_try_three
925     \fi
926   \else
927     % a one liner is not properly handled here, so best rewrite the text then
928     \showmessage\m!columns{10}\empty
929     \global\setbox\firstcolumnbox\vbox{\unvbox\b_page_mul_balance_content}%
930   \fi
931   \c_page_mul_routine\c_page_mul_routine_error
932   \baselinebottom % forces depth in separation rule
933   \page_mul_flush_packaged_columns_balanced
934   \page_mul_eject_page
935   \egroup}
936
937\def\page_mul_eject_page
938  {%\ifdim\pagetotal>\textheight
939   %  \page_otr_trigger_output_routine % new, but wrong as fails on mixed-001.tex (wrong pagetotal at this point)
940   %\else
941     \allowbreak
942   }%\fi}
943
944\def\page_mul_balance_try_one
945  {\d_page_mul_balance_target\dimexpr\ht\b_page_mul_balance_content+\topskip-\baselineskip\relax
946   \dohandleallcolumns
947     {\advance\d_page_mul_balance_target \ht\currenttopcolumnbox}%
948   \divide\d_page_mul_balance_target \nofcolumns
949   \vbadness\plustenthousand
950   \c_page_mul_balance_tries\zerocount
951   \bgroup
952   \ifgridsnapping
953     \d_page_mul_balance_step\lineheight
954   \else
955     \d_page_mul_balance_step\spacingfactor\onepoint % rubish
956   \fi
957   \doloop\page_mul_balance_try_one_attempt
958   \page_mul_balance_try_one_attempt_final
959   \ifnum\c_page_mul_balance_tries>\c_page_mul_balance_tries_max\relax
960     \showmessage\m!columns7\empty
961   \else
962     \showmessage\m!columns8{\the\c_page_mul_balance_tries}%
963   \fi
964   \egroup}
965
966\def\page_mul_balance_try_one_attempt
967  {\advance\c_page_mul_balance_tries \plusone
968   \global\setbox\b_page_mul_preceding_rest_of_page\copy\b_page_mul_balance_content\relax
969   \page_mul_split_first_column\b_page_mul_preceding_rest_of_page\d_page_mul_balance_target
970   \dohandlemidcolumns
971     {\page_mul_split_current_column\b_page_mul_preceding_rest_of_page\d_page_mul_balance_target}%
972   \page_mul_split_last_column\b_page_mul_preceding_rest_of_page\d_page_mul_balance_target
973   \setbox\b_page_mul_balance_first_column\vbox{\unvcopy\firstcolumnbox}%
974   \d_page_mul_balance_natural_height\zeropoint
975   \dohandleallcolumns\page_mul_balance_try_one_attempt_step
976   \advance\d_page_mul_balance_natural_height -.0005pt % (33sp) get rid of accurracy problem, pretty new
977   \ifnum\c_page_mul_balance_tries>\c_page_mul_balance_tries_max\relax
978     \exitloop
979   \else\ifdim\d_page_mul_balance_natural_height>\ht\b_page_mul_balance_first_column
980     \advance\d_page_mul_balance_target \d_page_mul_balance_step\relax
981   \else
982     \exitloop
983   \fi\fi}
984
985% \def\page_mul_balance_try_one_attempt_final
986%   {\dohandleallcolumns
987%      {\global\setbox\currentcolumnbox\vbox{\unvcopy\currentcolumnbox}}}
988%
989% \def\page_mul_balance_try_one_attempt_step
990%   {\setbox\b_page_mul_balance_column\vbox
991%      {\unvcopy\currentcolumnbox
992%       \unpenalty
993%       \unskip
994%       \unpenalty
995%       \unskip}% maybe better in main splitter
996%    \ifdim\ht\b_page_mul_balance_column>\d_page_mul_balance_natural_height
997%      \d_page_mul_balance_natural_height\ht\b_page_mul_balance_column
998%    \fi}
999%
1000% In mkiv we juggle node lists and we cannot work on copies that easily but in
1001% practice context hardly uses copies except here. So, the next variant doesn't
1002% use copies but the original in the final result, for which we need an extra
1003% split pass. A simple test case is:
1004%
1005% \starttext
1006%     \startcolumns
1007%         \inleft{!} x \par x
1008%     \stopcolumns
1009% \stoptext
1010
1011\def\page_mul_balance_try_one_attempt_final % we need this because we want to have the non copied content
1012  {\global\setbox\b_page_mul_preceding_rest_of_page\box\b_page_mul_balance_content
1013   \page_mul_split_first_column\b_page_mul_preceding_rest_of_page\d_page_mul_balance_target
1014   \dohandlemidcolumns
1015     {\page_mul_split_current_column\b_page_mul_preceding_rest_of_page\d_page_mul_balance_target}%
1016   \page_mul_split_last_column\b_page_mul_preceding_rest_of_page\d_page_mul_balance_target
1017   \dohandleallcolumns
1018     {\global\setbox\currentcolumnbox\vbox{\unvbox\currentcolumnbox}}}
1019
1020\def\page_mul_balance_try_one_attempt_step
1021  {\setbox\b_page_mul_balance_column\vbox
1022     {\unvbox\currentcolumnbox % was copy but not needed with the above
1023      \unpenalty
1024      \unskip
1025      \unpenalty
1026      \unskip}% maybe better in main splitter
1027   \ifdim\ht\b_page_mul_balance_column>\d_page_mul_balance_natural_height
1028     \d_page_mul_balance_natural_height\ht\b_page_mul_balance_column
1029   \fi}
1030
1031% We cannot assume that the first column is the tallest, if only because we may
1032% have an aborted balance (one line in the first column and a graphic in the
1033% second one).
1034
1035\def\page_mul_balance_try_two
1036  {\d_page_mul_balance_target\zeropoint
1037   \dohandleallcolumns
1038     {\ifdim\ht\currentcolumnbox>\d_page_mul_balance_target
1039        \d_page_mul_balance_target\ht\currentcolumnbox
1040      \fi}%
1041   \d_page_mul_balance_target_less\dimexpr\d_page_mul_balance_target-\openlineheight\relax
1042   \dohandleallcolumnscs\page_mul_balance_try_two_step}
1043
1044\def\page_mul_balance_try_two_step
1045  {\d_page_mul_balance_regular_height\ht\currentcolumnbox
1046   \d_page_mul_balance_threshold\plusten\openlineheight % funny value
1047   \global\setbox\currentcolumnbox\vbox to \d_page_mul_balance_target
1048     {\unvbox\currentcolumnbox
1049      \ifdim\d_page_mul_balance_regular_height>\d_page_mul_balance_threshold
1050        \ifdim\d_page_mul_balance_regular_height<\d_page_mul_balance_target
1051          \ifdim\d_page_mul_balance_regular_height>\d_page_mul_balance_target_less
1052            \vskip\zeropoint  % !!
1053          \else
1054            \vskip\openlineheight
1055            \vfill
1056          \fi
1057        \else
1058          \vskip\zeropoint
1059        \fi
1060      \else
1061        \vskip\openlineheight
1062        \vfill
1063      \fi}}
1064
1065\def\page_mul_balance_try_three
1066  {\bgroup
1067   \ifstretchcolumns
1068     \d_page_mul_balance_target\ht\firstcolumnbox
1069     \d_page_mul_balance_fuzzyness\bottomtolerance\ht\firstcolumnbox
1070     \setbox\b_page_mul_balance_content\vbox{\unvcopy\lastcolumnbox}%
1071     \advance\d_page_mul_balance_target-\htdp\b_page_mul_balance_content\relax
1072     \ifdim\d_page_mul_balance_target>\openlineheight\relax
1073       \ifdim\d_page_mul_balance_target>\d_page_mul_balance_fuzzyness\relax
1074         % \stretchcolumnsfalse % beter good bad than bad good
1075         \showmessage\m!columns9\empty
1076       \fi
1077     \fi
1078   \fi
1079   \dohandleallcolumnscs\page_mul_balance_try_three_step
1080   \egroup}
1081
1082\def\page_mul_balance_try_three_step
1083  {\global\setbox\currentcolumnbox\vbox to \ht\firstcolumnbox
1084     {\ifstretchcolumns
1085        \unvbox\currentcolumnbox
1086      \else
1087        \box\currentcolumnbox
1088        \vfill
1089      \fi}}
1090
1091%D The multicolumn mechanism is incorporated in a \CONTEXT\ interface,
1092%D which acts like:
1093%D
1094%D \starttyping
1095%D \startcolumns[n=4,balance=no]
1096%D   some text
1097%D \stopcolumns
1098%D \stoptyping
1099%D
1100%D The setup is optional. The default behaviour of columns can be set
1101%D up with:
1102%D
1103%D \starttyping
1104%D \setupcolumns
1105%D   [n=2,
1106%D    balance=yes]
1107%D \stoptyping
1108%D
1109%D In this case, stretching is according to the way it's done outside columns
1110%D (\type{\inheritcolumnstrue}). Also we can setup the \type{tolerance} within a
1111%D column, the \type{distance} between columns and the fixed \type{height} of a
1112%D column.
1113%D
1114%D Here come the routines that handle the placement of column floats. Floats that
1115%D are to big migrate to the next column. Floats that are too wide, migrate to the
1116%D top of the next page, where they span as much columns as needed. Floats that are
1117%D left over from outside the multi||column mode are flushed first. In macro
1118%D \type{\page_otr_construct_and_shipout} the topfloats that are left from previous
1119%D text should be set.
1120%D
1121%D When there are some floats in the queue, we inhibit the flushing of floats on top
1122%D of columns. The number of waiting floats is preswent in \type{\savednoftopfloats}
1123%D and is saved. As long as there are floats waiting, the topfloats are places as if
1124%D we are outside multi||column mode. This is neccessary for e.g. multicolumn lists.
1125%D
1126%D When all those floats are flushed, we switch to the local flushing routine.
1127
1128% \newbox  \floatlist
1129% \newbox  \savedfloatlist
1130%
1131% \def\page_floats_column_push_saved
1132%   {\ifconditional\c_page_floats_some_waiting
1133%      \showmessage\m!columns6{\the\savednoffloats}%
1134%      \global\setbox\savedfloatlist\box\floatlist
1135%      \xdef\page_floats_column_pop_saved
1136%        {\global\savednoffloats\the\savednoffloats
1137%         \global\setbox\floatlist\box\savedfloatlist
1138%         \global\noexpand\settrue\c_page_floats_some_waiting}%
1139%      \global\savednoffloats\zerocount
1140%      \global\setfalse\c_page_floats_some_waiting
1141%    \else
1142%      \glet\page_floats_column_pop_saved\relax
1143%    \fi}
1144%
1145% \let\page_floats_column_pop_saved\relax
1146
1147% \def\page_mul_initialize_floats % messy as it adapts everypar
1148%   {\xdef\globalsavednoffloats{\the\savednoffloats}%
1149%    \ifnum\globalsavednoffloats>\zerocount
1150%      \setglobalcolumnfloats % hm, we always push so this never happens
1151%    \else
1152%      \setlocalcolumnfloats
1153%    \fi}
1154
1155    \def\page_mul_initialize_floats % messy as it adapts everypar, we need to adapt this
1156      {\setlocalcolumnfloats}
1157
1158    \newconditional\onlylocalcolumnfloats % temp hack as we will redo floats (grid snapping is also messy now)
1159    \newtoks       \everylocalcolumnfloatspar
1160
1161    \unexpanded\def\page_mul_command_flush_floats
1162      {\ifconditional\onlylocalcolumnfloats
1163         \doflushcolumnfloats
1164       \else
1165         \page_one_command_flush_floats
1166       \fi}
1167
1168    \unexpanded\def\page_mul_command_check_if_float_fits
1169      {\ifconditional\onlylocalcolumnfloats
1170         \docolumnroomfloat
1171       \fi}
1172
1173    \unexpanded\def\page_mul_command_flush_saved_floats
1174      {\ifconditional\onlylocalcolumnfloats\relax
1175       \else
1176         \page_one_command_flush_saved_floats
1177       \fi}
1178
1179    \unexpanded\def\page_mul_command_flush_top_insertions
1180      {\ifconditional\onlylocalcolumnfloats\relax
1181       \else
1182         \page_one_command_flush_top_insertions
1183       \fi}
1184
1185    \appendtoks
1186        \flushnotes
1187        \page_mul_flush_float
1188       %\flushmargincontents
1189        \checkindentation
1190    \to \everylocalcolumnfloatspar
1191
1192    \def\setlocalcolumnfloats
1193      {\settrue\onlylocalcolumnfloats
1194       \everypar\everylocalcolumnfloatspar
1195       \let\page_mul_flush_float \doflushcolumnfloat
1196       \let\page_mul_flush_floats\doflushcolumnfloats}
1197
1198    \def\setglobalcolumnfloats
1199      {\setfalse\onlylocalcolumnfloats
1200       \reseteverypar
1201       \let\page_mul_flush_float \relax
1202       \let\page_mul_flush_floats\noflushcolumnfloats}
1203
1204 % \def\noflushcolumnfloats
1205 %   {\bgroup
1206 %    \xdef\localsavednoffloats{\the\savednoffloats}%
1207 %    \global\savednoffloats\globalsavednoffloats
1208 %    \page_otr_command_flush_top_insertions
1209 %    \xdef\globalsavenoffloats{\the\savednoffloats}%
1210 %    \ifnum\globalsavednoffloats=\zerocount
1211 %      \setlocalcolumnfloats
1212 %    \fi
1213 %    \global\savednoffloats\localsavednoffloats
1214 %    \egroup}
1215 %
1216    \def\noflushcolumnfloats{\doflushcolumnfloats} % not yet redone
1217
1218%D We need to calculate the amount of free space in a columns. When there is not
1219%D enough room, we migrate the float to the next column. These macro's are
1220%D alternatives (and look||alikes) of \type {\doroomfloat}. When a float is to wide,
1221%D for one column, it is moved to the top of the next page. Of course such moved
1222%D floats have to be taken into account when we calculate the available space. It's
1223%D a pitty that such things are no integral part of \TEX.
1224
1225    \def\getcolumnstatus#1#2#3%
1226      {\dimen0=\ifdim\pagegoal<\maxdimen \pagetotal \else \zeropoint \fi
1227       \dimen2=\zeropoint
1228       \count255=\zerocount
1229       \dimen8=\makeupheight
1230       \advance\dimen8 -\d_page_mul_preceding_height
1231       \def\dogetcolumnstatus
1232         {\advance\count255 \plusone
1233          \advance\dimen2 \ht\currenttopcolumnbox
1234          \advance\dimen2 \dp\currenttopcolumnbox
1235          \dimen4\dimen2
1236          \advance\dimen4 \dimen0
1237          \dimen6=\count255\dimen8
1238          \ifdim\dimen4>\dimen6
1239          \else
1240            \let\dogetcolumnstatus\relax
1241          \fi}%
1242       \dohandleallcolumns{\dogetcolumnstatus}%
1243       \ifnum\count255=0 \count255=1 \fi
1244       #1=\count255
1245       #2=\dimen4
1246       #3=\dimen6 }
1247
1248    \def\getinsertionheight
1249      {\ifdim\pagegoal<\maxdimen
1250         \bgroup
1251         \dimen0=\makeupheight
1252         \advance\dimen0 -\pagegoal
1253         \xdef\insertionheight{\the\dimen0}%
1254         \egroup
1255       \else
1256         \glet\insertionheight\zeropoint
1257       \fi}
1258
1259    \def\docolumnroomfloat
1260      {\ifpostponecolumnfloats
1261         \global\setfalse\c_page_floats_room
1262       \else\ifconditional\c_page_floats_not_permitted
1263         \global\setfalse\c_page_floats_room
1264       \else
1265         \bgroup
1266         \getcolumnstatus{\count255}{\dimen0}{\dimen2}%
1267         \page_floats_get_info\s!text
1268         \setbox\scratchbox\vbox % tricky met objecten ?
1269           {\blank[\rootfloatparameter\c!spacebefore]
1270            \snaptogrid\vbox{\vskip\floatheight}}% copy?
1271         \advance\dimen0\dimexpr\ht\scratchbox+2\openlineheight+.5\lineheight\relax\relax % needed because goal a bit higher
1272         \ifdim\dimen0>\dimen2
1273           \global\setfalse\c_page_floats_room
1274         \else
1275           \global\settrue\c_page_floats_room
1276         \fi
1277         \ifdim\floatwidth>\hsize
1278           \showmessage\m!columns{11}\empty
1279           \global\setfalse\c_page_floats_room
1280         \fi
1281         \egroup
1282       \fi\fi}
1283
1284%D Flushing one float is done as soon as possible, i.e. \type {\everypar}.
1285%D This means that (at the moment) sidefloats are not supported (overulled)!
1286
1287    \newif\ifflushingcolumnfloats \flushingcolumnfloatstrue
1288
1289    \def\doflushcolumnfloat
1290      {\ifpostponecolumnfloats\else\ifflushingcolumnfloats\ifconditional\c_page_floats_some_waiting
1291         \doflushcolumnfloatindeed
1292       \fi\fi\fi}
1293
1294    \def\doflushcolumnfloatindeed
1295      {\bgroup
1296       \forgetall
1297       \let\doflushcolumnfloat\relax
1298       \getcolumnstatus{\mofcolumns}{\dimen0}{\dimen2}%
1299       \ifdim\dimen0>\zeropoint
1300         \page_floats_get_info\s!text
1301         \ifdim\floatwidth>\hsize
1302            % dropped ?
1303         \else
1304           \setbox2\vbox
1305             {\blank[\rootfloatparameter\c!spacebefore]
1306              \snaptogrid\vbox{\vskip\floatheight}}%
1307           \advance\dimen0 \ht2
1308           \ifdim\dimen0>\dimen2
1309             \ifnum\mofcolumns<\nofcolumns
1310               \advance\mofcolumns \plusone
1311               \ifdim\ht\currenttopcolumnbox=\zeropoint
1312                 \page_floats_flush\s!text\plusone
1313                 \global\setbox\currenttopcolumnbox\vbox
1314                   {\snaptogrid\vbox{\box\floatbox}
1315                    \whitespace % nodig ?
1316                    \blank[\rootfloatparameter\c!spaceafter]}%
1317                 \dimen4=\htdp\currenttopcolumnbox
1318                 \global\advance\vsize -\dimen4
1319                 \advance\dimen4 -\pagegoal
1320                 \pagegoal-\dimen4
1321                 \showmessage\m!columns{12}a%
1322               \else
1323                 \showmessage\m!columns{12}b%
1324               \fi
1325             \else
1326               \showmessage\m!columns{12}c%
1327             \fi
1328           \else
1329             \ifhmode{\setbox0\lastbox}\fi% waar is die er in geslopen
1330             \par
1331             \ifdim\prevdepth<\zeropoint \else % anders bovenaan kolom witruimte
1332               \nobreak
1333               \blank[\rootfloatparameter\c!spacebefore]
1334               \nobreak
1335             \fi
1336             \page_floats_flush\s!text\plusone
1337             \page_otr_command_flush_float_box
1338             \blank[\rootfloatparameter\c!spaceafter]
1339           \fi
1340         \fi
1341       \fi
1342       \egroup}
1343
1344%D This one looks complicated. Upto \type{\nofcolumns} floats are placed,
1345%D taking the width of a float into account. This routine can be improved
1346%D on different ways:
1347%D
1348%D \startitemize[intro,packed]
1349%D \item taking into account some imaginary baseline, just to get the
1350%D       captions in line
1351%D \item multipass flushing until as many floats are displaced as possible
1352%D \stopitemize
1353%D
1354%D When handling lots of (small) floats spacing can get worse because of
1355%D lining out the columns.
1356
1357    \def\doflushcolumnfloats
1358      {\ifpostponecolumnfloats\else
1359         \bgroup
1360         \forgetall
1361         \ifconditional\c_page_floats_some_waiting
1362           \dimen8\zeropoint
1363           \dimen4\zeropoint
1364           \count0\zerocount   % count0 can be used local
1365           \count2\nofcolumns  % count2 can be used local
1366           \dohandleallcolumns
1367             {\ifnum\count0>\zerocount % the wide one's reserved space
1368                \global\setbox\currenttopcolumnbox\vbox
1369                  {\snaptogrid\vbox
1370                     {\copy\currenttopcolumnbox
1371                      \hpack{\vphantom{\vskip\floatheight}}}% known from previous
1372                      \whitespace % nodig ?
1373                      \blank[\rootfloatparameter\c!spaceafter]}%
1374              \else
1375                \page_floats_get_info\s!text
1376                \ifdim\floatwidth>\hsize
1377                  \dimen0\dimexpr\floatwidth+\d_page_mul_distance+.5pt\relax
1378                  \dimen2\dimexpr\hsize     +\d_page_mul_distance+.5pt\relax
1379                  \divide\dimen0 \dimen2
1380                  \count0\dimen0
1381                  \advance\count0 \plusone
1382                  \ifnum\count0>\count2
1383                    \count0\zerocount
1384                  \else
1385                    \dimen0\dimexpr\count0\hsize+\count0\d_page_mul_distance-\d_page_mul_distance\relax
1386                    \page_floats_flush\s!text\plusone
1387                    \ifdim\floatwidth>\makeupwidth % better somewhere else too
1388                      \global\setbox\floatbox\hbox to \makeupwidth{\hss\box\floatbox\hss}%
1389                    \fi % otherwise the graphic may disappear
1390                    \global\setbox\floatbox\hbox to \dimen0
1391                      {\processaction[\rootfloatparameter\c!location] % how easy to forget
1392                         [   \v!left=>\box\floatbox\hss,
1393                            \v!right=>\hss\box\floatbox,
1394                          \s!default=>\hss\box\floatbox\hss,
1395                          \s!unknown=>\hss\box\floatbox\hss]}%
1396                  \fi
1397                  \showmessage\m!columns{13}\empty
1398                \else
1399                  \page_floats_flush\s!text\plusone
1400                  \ifdim\floatwidth>\makeupwidth % better somewhere else too
1401                    \global\setbox\floatbox\hbox to \makeupwidth{\hss\box\floatbox\hss}%
1402                  \fi % otherwise the graphic may disappear
1403                % \showmessage\m!columns{13}\empty
1404                \fi
1405                \ifdim\ht\floatbox>\zeropoint\relax
1406                  \global\setbox\currenttopcolumnbox\vbox
1407                    {\snaptogrid\vbox
1408                       {\box\currenttopcolumnbox % was copy
1409                        \box\floatbox}
1410                     \whitespace % nodig ?
1411                     \blank[\rootfloatparameter\c!spaceafter]}%
1412                \fi
1413                \dimen6\htdp\currenttopcolumnbox
1414              \fi
1415              \ifdim\dimen4<\ht\currenttopcolumnbox
1416                \dimen4\ht\currenttopcolumnbox
1417              \fi
1418              \advance\dimen8 \dimen6
1419              \advance\count2 \minusone
1420              \advance\count0 \minusone }%
1421           \page_otr_command_set_vsize
1422           \global\advance\vsize -\dimen8
1423           \pagegoal\vsize
1424         \else
1425           % \page_mul_command_flush_floats % does not snap!
1426         \fi
1427         \egroup
1428       \fi}
1429
1430%D The next macro can be used to flush floats in the current stream. No
1431%D width checking is (yet) done.
1432
1433    \def\insertcolumnfloats
1434      {\doloop
1435         {\ifconditional\c_page_floats_some_waiting
1436            \bgroup
1437            \forgetall
1438            % no check for width
1439            \page_floats_get
1440            \blank[\rootfloatparameter\c!spacebefore]
1441            \snaptogrid\vbox{\copy\floatbox}
1442            \blank[\rootfloatparameter\c!spaceafter]
1443            \egroup
1444          \else
1445            \exitloop
1446          \fi}}
1447
1448%D This were the multi||column routines. They can and need to be improved
1449%D but at the moment their behaviour is acceptable.
1450%D
1451%D One inprovement can be to normalize the height of floats to $ n \times $
1452%D \type {\lineheight} with a macro like:
1453%D
1454%D \starttyping
1455%D \normalizevbox{...}
1456%D \stoptyping
1457
1458% border case, should fit on one page
1459%
1460% \startcolumns
1461% 1 \input tufte  \par \placefigure{}{\framed[width=\hsize,height=3cm]{1}}
1462% 2 \input tufte  \par \placefigure{}{\framed[width=\hsize,height=3cm]{2}}
1463% 3 \input tufte  \par \placefigure{}{\framed[width=\hsize,height=3cm]{3}}
1464% \stopcolumns
1465
1466\def\backgroundfinishcolumnbox
1467  {\inheritedcolumnsframed}
1468   % [\c!strut=\v!no,
1469   %  \c!width=\v!fit,
1470   %  \c!height=\v!fit,
1471   %  \c!align=]}
1472
1473% to be reconsidered ... (in any case they need to be unexpandable sinze 2011.12.30)
1474
1475\unexpanded\def\page_columns_align_option_yes {\stretchcolumnstrue \inheritcolumnsfalse}% todo: new key
1476\unexpanded\def\page_columns_align_option_no  {\stretchcolumnsfalse\inheritcolumnsfalse}% todo: new key
1477\unexpanded\def\page_columns_align_option_text{\stretchcolumnsfalse\inheritcolumnstrue }%
1478
1479\newtoks\t_page_mul_initialize
1480
1481\unexpanded\def\startcolumns
1482  {\dosingleempty\page_mul_start}
1483
1484\def\page_mul_start[#1]% %% \startcolumns
1485  {\bgroup
1486   \ifinsidecolumns
1487     \page_mul_start_nop
1488   \else
1489     \iffirstargument
1490       \setupcolumns[#1]%
1491     \fi
1492     \nofcolumns\columnsparameter\c!n\relax
1493     \ifnum\nofcolumns>\plusone
1494       \page_mul_start_yes
1495     \else
1496       \page_mul_start_nop
1497     \fi
1498   \fi}
1499
1500\unexpanded\def\page_mul_start_nop
1501  {\let\stopcolumns\page_mul_stop_nop}
1502
1503\unexpanded\def\page_mul_stop_nop
1504  {\egroup}
1505
1506\unexpanded\def\page_mul_start_yes
1507  {\whitespace
1508   \begingroup
1509   \let\stopcolumns\page_mul_stop_indeed
1510   \global\insidecolumnstrue
1511   \the\t_page_mul_initialize
1512   %
1513   \flushnotes
1514   \begingroup
1515   %
1516   \d_page_mul_leftskip\leftskip
1517   \d_page_mul_rightskip\rightskip
1518   \leftskip\zeropoint
1519   \rightskip\zeropoint
1520   %
1521   \widowpenalty\zerocount % will become option
1522   \clubpenalty \zerocount % will become option
1523   %
1524   \page_floats_column_push_saved
1525   %
1526   \ifdim\dimexpr\pagetotal+\parskip+\openlineheight\relax<\pagegoal
1527     \allowbreak
1528   \else
1529     \break % sometimes fails
1530   \fi
1531   \appendtoks
1532     \topskip1\topskip % best a switch
1533   \to \everybodyfont
1534   \the\everybodyfont  % ugly here
1535   \saveinterlinespace % ugly here
1536   %
1537   \initializecolumns\nofcolumns
1538   %
1539   \hangafter\zerocount
1540   \hangindent\zeropoint
1541   \reseteverypar
1542   \ifdim\pagetotal=\zeropoint \else
1543     \verticalstrut
1544     \vskip-\struttotal
1545   \fi
1546   \global\savedpagetotal\pagetotal
1547   \setupoutputroutine[\s!multicolumn]%
1548   \c_page_mul_routine\c_page_mul_routine_intercept
1549   \page_otr_trigger_output_routine % no \holdinginserts=1, can make footnote disappear !
1550   \global\d_page_mul_preceding_height\ht\b_page_mul_preceding
1551   \c_page_mul_routine\c_page_mul_routine_continue
1552   \page_mul_initialize_floats
1553   \dohandleallcolumns{\global\setbox\currenttopcolumnbox\emptybox}%
1554   \checkbegincolumnfootnotes
1555   \page_otr_command_set_hsize
1556   \page_otr_command_set_vsize}
1557
1558\setnewconstant\multicolumnendsyncmethod\plusone % 1: old sync 2: new sync (cont-loc/project) / may fail ! ! ! !
1559
1560\unexpanded\def\page_mul_stop_indeed
1561  {\relax
1562   \ifnum\multicolumnendsyncmethod=\plustwo
1563     \synchronizeoutput
1564   \else
1565     % don't collapse these
1566     \vskip \lineheight
1567     \vskip-\lineheight % take footnotes into account
1568   \fi
1569   \doflushcolumnfloat  % added recently
1570  %\doflushcolumnfloats % no, since it results in wrong top floats
1571   \flushnotes          % before start of columns
1572   \par
1573   \ifbalancecolumns
1574     \ifnum\multicolumnendsyncmethod=\plusone
1575       \c_page_mul_routine\c_page_mul_routine_continue
1576       \goodbreak
1577     \fi
1578     \c_page_mul_routine\c_page_mul_routine_balance
1579   \else
1580     \goodbreak
1581   \fi
1582   % still the multi column routine
1583   \page_otr_trigger_output_routine % the prevdepth is important, try e.g. toclist in
1584   \prevdepth\zeropoint % columns before some noncolumned text text
1585   %
1586   \c_page_mul_routine\c_page_mul_routine_regular
1587   %
1588   \ifvoid\b_page_mul_preceding\else
1589     \unvbox\b_page_mul_preceding
1590   \fi
1591   \global\d_page_mul_preceding_height\zeropoint
1592   \endgroup % here
1593   \nofcolumns\plusone
1594   \page_otr_command_set_vsize
1595   \checkendcolumnfootnotes
1596   \dosomebreak\allowbreak
1597   \page_floats_column_pop_saved
1598   %
1599   \global\insidecolumnsfalse
1600   \endgroup
1601   \egroup}%
1602
1603\appendtoks
1604    \edef\p_option{\columnsparameter\c!option}%
1605    \ifx\p_option\v!background
1606        \let\finishcolumnbox\backgroundfinishcolumnbox
1607        \doifelseinset{\columnsparameter\c!offset}{\v!none,\v!overlay}
1608          {\d_page_mul_offset\zeropoint}%
1609          {\d_page_mul_offset\dimexpr\columnsparameter\c!offset-\columnsparameter\c!rulethickness\relax}%
1610    \else
1611        \d_page_mul_offset\zeropoint
1612    \fi
1613    \edef\p_command{\columnsparameter\c!command}%
1614    \ifx\p_command\empty \else
1615        \let\postprocesscolumnline\p_command
1616    \fi
1617    \edef\p_height{\columnsparameter\c!height}%
1618    \ifx\p_height\empty
1619        \d_page_mul_forced_height\textheight
1620        \heightencolumnsfalse
1621    \else
1622        \d_page_mul_forced_height\p_height\relax
1623        \heightencolumnstrue
1624    \fi
1625    \edef\p_direction{\columnsparameter\c!direction}%
1626    \ifx\p_direction\v!right
1627        \setfalse\c_page_mul_reverse
1628    \else
1629        \settrue\c_page_mul_reverse
1630    \fi
1631    \edef\p_balance{\columnsparameter\c!balance}%
1632    \ifx\p_balance\v!yes
1633        \balancecolumnstrue
1634    \else
1635        \balancecolumnsfalse
1636    \fi
1637    % % this won't work (blocked by check for overloading; too fuzzy anyway)
1638    % \installalign\v!yes {\page_columns_align_option_yes }% \stretchcolumnstrue \inheritcolumnsfalse
1639    % \installalign\v!no  {\page_columns_align_option_no  }% \stretchcolumnsfalse\inheritcolumnsfalse
1640    % \installalign\v!text{\page_columns_align_option_text}% \stretchcolumnsfalse\inheritcolumnstrue
1641    % %
1642    \stretchcolumnsfalse
1643    \inheritcolumnstrue
1644    \edef\p_align{\columnsparameter\c!align}%
1645    \ifx\p_align\empty \else
1646        \setupalign[\p_align]%
1647    \fi
1648    \edef\p_tolerance{\columnsparameter\c!tolerance}%
1649    \ifx\p_tolerance\empty \else
1650        \setuptolerance[\p_tolerance]%
1651    \fi
1652    \edef\p_blank{\columnsparameter\c!blank}%
1653    \ifx\p_blank\empty \else
1654        \setupblank[\p_blank]%
1655    \fi
1656    \ifdim\s_spac_whitespace_parskip>\zeropoint\relax
1657        \setupwhitespace[\p_blank]%
1658    \fi
1659    \c_page_mul_balance_minimum\columnsparameter\c!ntop\relax
1660    \edef\p_page_mul_rule{\columnsparameter\c!rule}%
1661    \expandnamespacemacro\??columnseparators\p_page_mul_rule\s!unknown
1662\to \t_page_mul_initialize
1663
1664%D Columns breaks
1665
1666\installcolumnbreakmethod \s!multicolumn \v!preference
1667  {\goodbreak}
1668
1669% \installcolumnbreakmethod \s!multicolumn \v!yes
1670%   {\vskip\textheight
1671%    \penalty-200 % we can mark and intercept this
1672%    \vskip-\textheight}
1673
1674\installcolumnbreakmethod \s!multicolumn \v!yes
1675   {\vskip .5\pagegoal
1676    \penalty-200 % we can mark and intercept this
1677    \vskip-.5\pagegoal}
1678
1679%D Next we initialize the lot:
1680
1681\setupcolumns
1682  [\c!n=2,
1683   \c!ntop=1,
1684   \c!command=,
1685   \c!direction=\v!right,
1686   \c!rule=\v!off,
1687   \c!tolerance=\v!tolerant,
1688   \c!distance=1.5\bodyfontsize, % influenced by switching
1689   \c!height=,
1690   \c!balance=\v!yes,
1691   \c!align=\v!text,
1692   \c!blank={\v!line,\v!fixed},
1693   \c!option=,
1694   \c!rulethickness=\linewidth,
1695   \c!offset=.5\bodyfontsize]
1696
1697%D New: only at start of columns; may change ! Rather interwoven and therefore
1698%D to be integrated when the multi column modules are merged.
1699
1700    \unexpanded\def\setupcolumnspan[#1]%
1701      {\getparameters[\??ks][#1]}
1702
1703    \presetlocalframed
1704      [\??ks]
1705
1706    \setupcolumnspan
1707      [\c!n=2,
1708       \c!offset=\v!overlay,
1709       \c!frame=\v!off]
1710
1711    \newbox\b_page_columns_span \let\page_mul_postprocess_spanbox\gobbleoneargument
1712
1713    \unexpanded\def\startcolumnspan
1714      {\dosingleempty\dostartcolumnspan}
1715
1716    \unexpanded\def\stopcolumnspan
1717      {\egroup}
1718
1719    \def\dostartcolumnspan[#1]%
1720      {\bgroup
1721       \setupcolumnspan[#1]%
1722       \forgetall
1723       \ifinsidecolumns
1724         \advance\hsize \d_page_mul_distance
1725         \hsize\@@ksn\hsize
1726         \advance\hsize -\d_page_mul_distance
1727       \fi
1728       \dowithnextboxcs\dofinishcolumnsetspan\vbox\bgroup
1729         %\topskipcorrection % becomes an option !
1730         \EveryPar{\begstrut\EveryPar{}}} % also !
1731
1732    \def\dofinishcolumnsetspan
1733      {\setbox\b_page_columns_span\flushnextbox
1734       \ifinsidecolumns\wd\b_page_columns_span\hsize\fi
1735       \page_mul_postprocess_spanbox\b_page_columns_span
1736       \scratchdimen\ht\b_page_columns_span
1737       \setbox\b_page_columns_span\hbox % depth to be checked, probably option!
1738         {\localframed[\??ks][\c!offset=\v!overlay]{\box\b_page_columns_span}}%
1739       \ht\b_page_columns_span\scratchdimen
1740       \dp\b_page_columns_span\strutdp
1741       \wd\b_page_columns_span\hsize
1742       \ifinsidecolumns
1743         \ifnum\@@ksn>1
1744           \page_otr_command_set_vsize
1745           \dohandleallcolumns
1746             {\ifnum\currentcolumn>\@@ksn\else
1747                \global\setbox\currenttopcolumnbox=\vbox
1748                  {\ifnum\currentcolumn=1
1749                     \snaptogrid\vbox{\copy\b_page_columns_span}
1750                   \else
1751                     \snaptogrid\vbox{\vphantom{\copy\b_page_columns_span}}
1752                   \fi}%
1753                \wd\currenttopcolumnbox\hsize
1754                \global\advance\vsize -\ht\currenttopcolumnbox
1755              \fi}
1756           \pagegoal\vsize
1757         \else
1758           \snaptogrid\vbox{\box\b_page_columns_span}
1759         \fi
1760       \else
1761         \snaptogrid\vbox{\box\b_page_columns_span}
1762       \fi
1763       \endgraf
1764       \ifvmode\prevdepth\strutdp\fi
1765       \egroup}
1766
1767%D Undocumented and still under development.\ifdefined\startsimplecolumns \else
1768
1769\unexpanded\def\startsimplecolumns
1770  {\dosingleempty\page_mul_simple_start}
1771
1772\def\page_mul_simple_start[#1]%
1773  {\bgroup
1774   \setsimplecolumnshsize[#1]%
1775   \nopenalties
1776   \setbox\scratchbox\vbox\bgroup
1777   \forgetall} % \blank[\v!disable]
1778
1779\unexpanded\def\stopsimplecolumns
1780  {\removebottomthings
1781   \egroup
1782   \rigidcolumnbalance\scratchbox
1783   \egroup}
1784
1785\unexpanded\def\setsimplecolumnshsize[#1]%
1786  {\getdummyparameters
1787     [\c!width=\hsize,
1788      \c!distance=1.5\bodyfontsize,
1789      \c!n=2,
1790      \c!lines=0,
1791      #1]%
1792   \edef\rigidcolumnlines
1793     {\directdummyparameter\c!lines}%
1794   \setrigidcolumnhsize
1795     {\directdummyparameter\c!width}%
1796     {\directdummyparameter\c!distance}%
1797     {\directdummyparameter\c!n}}
1798
1799%D Moved here:
1800
1801\unexpanded\def\page_mul_command_test_column
1802  {\dodoubleempty\page_mul_command_test_column_indeed}
1803
1804\unexpanded\def\page_mul_command_test_column_indeed[#1][#2]% works on last column
1805  {\page_otr_command_flush_top_insertions\endgraf
1806   \ifdim\pagegoal<\maxdimen
1807     \ifdim\pagetotal<\pagegoal
1808       \d_page_tests_test\dimexpr
1809         \pagegoal
1810        -\pagetotal
1811         \ifdim\lastskip<\parskip+\parskip\fi
1812         \ifsecondargument+#2\fi
1813       \relax
1814       \getrawnoflines\d_page_tests_test % (raw)
1815       \ifnum#1>\noflines
1816         \column
1817       \fi
1818     \else
1819       \penalty-\plustenthousand % (untested)
1820     \fi
1821   \fi}
1822
1823%D but fragile anyway.
1824
1825\let\page_mul_command_package_contents\page_one_command_package_contents
1826\let\page_mul_command_flush_float_box \page_one_command_flush_float_box
1827\let\page_mul_command_flush_all_floats\page_one_command_flush_all_floats
1828
1829\defineoutputroutine
1830  [\s!multicolumn]
1831  [\s!page_otr_command_routine                =\page_mul_command_routine,
1832   \s!page_otr_command_package_contents       =\page_mul_command_package_contents,
1833   \s!page_otr_command_set_vsize              =\page_mul_command_set_vsize,
1834   \s!page_otr_command_set_hsize              =\page_mul_command_set_hsize,
1835 % \s!page_otr_command_synchronize_hsize      =\page_mul_command_synchronize_hsize,
1836   \s!page_otr_command_next_page              =\page_mul_command_next_page,
1837   \s!page_otr_command_next_page_and_inserts  =\page_mul_command_next_page_and_inserts,
1838 % \s!page_otr_command_set_top_insertions     =\page_mul_command_set_top_insertions,
1839 % \s!page_otr_command_set_bottom_insertions  =\page_mul_command_set_bottom_insertions,
1840   \s!page_otr_command_flush_top_insertions   =\page_mul_command_flush_top_insertions,
1841 % \s!page_otr_command_flush_bottom_insertions=\page_mul_command_flush_bottom_insertions,
1842   \s!page_otr_command_check_if_float_fits    =\page_mul_command_check_if_float_fits,
1843 % \s!page_otr_command_set_float_hsize        =\page_mul_command_set_float_hsize,
1844   \s!page_otr_command_flush_float_box        =\page_mul_command_flush_float_box,
1845   \s!page_otr_command_side_float_output      =\page_mul_command_side_float_output,
1846   \s!page_otr_command_synchronize_side_floats=\page_mul_command_synchronize_side_floats,
1847   \s!page_otr_command_flush_floats           =\page_mul_command_flush_floats,
1848   \s!page_otr_command_flush_side_floats      =\page_mul_command_flush_side_floats,
1849   \s!page_otr_command_flush_saved_floats     =\page_mul_command_flush_saved_floats,
1850   \s!page_otr_command_flush_all_floats       =\page_mul_command_flush_all_floats,
1851 % \s!page_otr_command_flush_margin_blocks    =\page_mul_command_flush_margin_blocks, % not used
1852   \s!page_otr_command_test_column            =\page_mul_command_test_column
1853  ]
1854
1855\installfloatmethod \s!multicolumn  \v!here   \page_mul_place_float_here
1856\installfloatmethod \s!multicolumn  \v!force  \page_mul_place_float_force
1857\installfloatmethod \s!multicolumn  \v!top    \page_mul_place_float_top
1858\installfloatmethod \s!multicolumn  \v!bottom \page_mul_place_float_bottom
1859
1860\appendtoks
1861    \flushingcolumnfloatsfalse
1862\to \everybeforesectionheadhandle
1863
1864\appendtoks
1865   \flushingcolumnfloatstrue
1866\to \everyaftersectionheadhandle
1867
1868\protect \endinput
1869