page-mcl.mkxl /size: 17 Kb    last modification: 2025-02-21 11:03
1%D \module
2%D   [       file=page-mcl,
3%D        version=2020.07.26, % stripped down redone page-mul
4%D          title=\CONTEXT\ Page Macros,
5%D       subtitle=Multicolumns Limited,
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\writestatus{loading}{ConTeXt Page Macros / Multicolumns Limited}
15
16\unprotect
17
18%D Columns are kind of hairy in \TEX\ and we would be better if no one needed them.
19%D Anyway, we do need some support and no mechanism can serve all. The original
20%D multicolumn mechanism of \MKII\ was never fully adapted to \MKIV, where mixed
21%D columns and page columns showed up instead. However, for some cases a dumb
22%D mechanism makes sense, so again we introduce multi columns, but without float
23%D hacks. This one will be optimized for mixed usage as for instance itemize needs.
24%D Their main advantage is that they can better deal with notes, which are hairy in
25%D themselves. This will (for now) only happen in \LMTX.
26%D
27%D The code is stripped down ancient \MKII\ code and might become cleaner as it
28%D evolves. Macros keep similar names but take a different namespace. The code is
29%D not yet perfect wrt spacing!
30
31% \enableexperiments[itemize.columns]
32%
33% \starttext
34%
35%     \startmulticolumns
36%         \dorecurse{10}{\samplefile{ward}\footnote{note #1}\par}
37%     \stopmulticolumns
38%
39%     \dorecurse{4}{\samplefile{ward}\par}
40%
41%     \startitemize[packed,columns,two]
42%         \dorecurse{100}{\startitem test #1 \footnote{this is a footnote #1}\stopitem}
43%     \stopitemize
44%
45% \stoptext
46
47\ifdefined \startmulticolumns
48
49    % we're testing and don't want to remake the format
50
51\else
52
53\installcorenamespace {multicolumns}
54
55\installframedcommandhandler \??multicolumns {multicolumns} \??multicolumns
56
57\newdimension   \d_page_mcl_available_width
58\newdimension   \d_page_mcl_distance
59\newdimension   \d_page_mcl_leftskip
60\newdimension   \d_page_mcl_rightskip
61\newdimension   \d_page_mcl_used_width
62\newdimension   \d_page_mcl_temp
63\newdimension   \d_page_mcl_saved_pagetotal  % brrr, still needed ?
64
65\newinteger    \c_page_mcl_balance_minimum
66\newinteger    \c_page_mcl_n_of_lines
67
68\newbox        \b_page_mcl_preceding
69\newdimension  \d_page_mcl_preceding_height
70\newconditional\c_page_mcl_preceding_present
71
72\newbox        \b_page_mcl_rest_of_page
73\newbox        \b_page_mcl_page
74
75\newconditional\c_page_mcl_reverse
76\newconditional\c_page_mcl_balance
77
78\newconstant   \c_page_mcl_routine
79
80\setnewconstant\c_page_mcl_routine_regular   \zerocount
81\setnewconstant\c_page_mcl_routine_intercept \plusone
82\setnewconstant\c_page_mcl_routine_continue  \plustwo
83\setnewconstant\c_page_mcl_routine_balance   \plusthree
84\setnewconstant\c_page_mcl_routine_error     \plusfour
85
86\newbox        \b_page_mcl_balance_content
87\newconstant   \c_page_mcl_balance_tries_max
88\newinteger    \c_page_mcl_balance_tries
89\newdimension  \d_page_mcl_balance_target
90\newdimension  \d_page_mcl_balance_natural_height
91\newdimension  \d_page_mcl_balance_step
92\newconditional\c_page_mcl_balance_possible
93
94\c_page_mcl_balance_tries_max 250 % 100 is too small when floats are involved
95
96%def\m_page_mcl_overshoot_ratio{\ifgridsnapping0\else.5\fi}
97\def\m_page_mcl_overshoot_ratio{.5}
98
99\fi
100
101\protected\def\page_mcl_command_set_hsize
102  {\d_page_mcl_available_width{%
103      \makeupwidth
104     -\d_page_mcl_leftskip
105     -\d_page_mcl_rightskip
106     -\nofcolumns\d_page_mcl_distance
107     +\d_page_mcl_distance
108   }%
109   \d_page_mcl_used_width{%
110      \d_page_mcl_available_width/\nofcolumns
111   }%
112   \textwidth\d_page_mcl_used_width
113   \hsize\d_page_mcl_used_width}
114
115\protected\def\page_mcl_set_n_of_lines#1%
116  {\d_page_mcl_temp{%
117     +\textheight
118      \ifdim\d_page_mcl_preceding_height>\zeropoint -\d_page_mcl_preceding_height \fi
119%       \ifdim\ht\b_page_mcl_preceding>\zeropoint -\ht\b_page_mcl_preceding \fi
120     -#1%
121   }%
122   \getnoflines\d_page_mcl_temp
123   \ifnum\layoutlines>\zerocount \ifnum\noflines>\layoutlines
124     \noflines\layoutlines
125   \fi \fi
126   \c_page_mcl_n_of_lines\noflines}
127
128\protected\def\page_mcl_command_set_vsize
129  {\global\vsize{%
130     \nofcolumns\textheight
131    +\nofcolumns\lineheight
132   }%
133   \pagegoal{%
134     \vsize
135  % -\d_page_floats_inserted_top    % needs checking
136  % -\d_page_floats_inserted_bottom % needs checking
137    -\c_page_mix_n_of_columns\insertheights
138   }}
139
140\protected\def\page_mcl_command_routine
141  {\ifcase\c_page_mcl_routine
142     \page_one_command_routine
143   \or
144     \page_mcl_routine_intercept
145   \or
146     \page_mcl_routine_continue
147   \or
148     \page_mcl_routine_balance
149   \or
150     \page_mcl_routine_error
151   \fi}
152
153\let\page_mcl_command_package_contents\page_one_command_package_contents
154
155\def\page_mcl_routine_intercept
156  {\global\setbox\b_page_mcl_preceding\vbox
157     {\page_otr_command_flush_top_insertions
158      \unvbox\normalpagebox}}
159
160\def\page_mcl_routine_error
161  {\showmessage\m!columns3\empty
162   \page_otr_construct_and_shipout\unvbox\normalpagebox\zerocount} % three arguments
163
164\protected\def\page_mcl_initialize_variables
165  {\reseteverypar % maybe still freeze ....
166   \dontcomplain
167   \settopskip
168   \setmaxdepth
169   \topskip        1\topskip
170   \splittopskip    \topskip
171   \splitmaxdepth   \maxdepth
172   \boxmaxdepth     \maxdepth % dangerous
173   \emergencystretch\zeropoint
174   \relax}
175
176\def\page_mcl_flush_preceding_normal
177  {\unvbox\b_page_mcl_preceding}
178
179\def\page_mcl_flush_preceding_ongrid
180  {\scratchdimen{%
181     \d_page_mcl_saved_pagetotal
182    -\d_page_mcl_preceding_height
183  % -\topskip % no, this is already part of the saved total
184   }%
185   \box\b_page_mcl_preceding
186   \kern\scratchdimen}
187
188\def\page_mcl_flush_packaged_columns_continued
189  {\page_mcl_flush_packaged_columns_indeed
190   \box\b_page_mcl_page}
191
192\def\page_mcl_flush_packaged_columns_balanced
193  {\bgroup
194   \page_mcl_flush_packaged_columns_indeed
195   \getnoflines{\htdp\b_page_mcl_page}%
196   \ht\b_page_mcl_page{%
197     \noflines\openlineheight
198     -\openstrutdepth
199     \ifgridsnapping
200        % quick hack (at least it works with itemize)
201     \else
202       -\openlineheight
203       +\topskip
204     \fi
205   }%
206   \dp\b_page_mcl_page\openstrutdepth
207   \box\b_page_mcl_page
208   \egroup}
209
210\def\page_mcl_synchronize_marks
211  {\dohandleallcolumns{\page_marks_synchronize_column\plusone\nofcolumns\mofcolumns\currentcolumnbox}}
212
213\def\page_mcl_flush_packaged_columns_indeed
214  {\ifvoid\b_page_mcl_preceding
215     \c_page_mcl_preceding_present\conditionalfalse % will be set elsewhere
216   \else
217     \c_page_mcl_preceding_present\conditionaltrue
218     \page_apply_postprocessors_box\b_page_mcl_preceding
219   \fi
220   \forgetall
221   \page_mcl_initialize_variables
222   \page_mcl_synchronize_marks
223   \setbox\b_page_mcl_page\vpack
224     {\ifconditional\c_page_mcl_reverse\reversehpack\else\naturalhpack\fi to \makeupwidth
225        {\hskip\ifconditional\c_page_mcl_reverse\d_page_mcl_rightskip\else\d_page_mcl_leftskip\fi\relax
226         \dohandleallcolumns
227           {\wd\currentcolumnbox\d_page_mcl_used_width
228            \setbox\scratchbox\hpack{\strut\box\currentcolumnbox}% hm, why strut
229            \anch_mark_column_box\scratchbox\currentcolumn
230            \box\scratchbox
231            \hfil}%
232         \unskip
233         \hskip\ifconditional\c_page_mcl_reverse\d_page_mcl_leftskip\else\d_page_mcl_rightskip\fi}}%
234   \ifconditional\c_page_mcl_preceding_present
235     \c_page_mcl_preceding_present\conditionaltrue
236     \ifgridsnapping
237       \page_mcl_flush_preceding_ongrid % obey grid settings, force on grid
238     \else
239       \page_mcl_flush_preceding_normal % ignore grid settings, not on grid
240     \fi
241   \fi
242   \global\d_page_mcl_preceding_height\zeropoint
243   \page_otr_command_set_vsize
244   \dosomebreak\nobreak % hm, only needed when topstuff
245   \ifgridsnapping \else
246     \ifconditional\c_page_mcl_preceding_present
247       \nointerlineskip
248       \vskip{\openstrutheight-\topskip}%
249     \fi
250   \fi
251   \prevdepth\openstrutdepth
252   \nointerlineskip
253   \dp\b_page_mcl_page\zeropoint}
254
255\def\page_mcl_split_column#1#2% copy or box
256  {\global\setbox\currentcolumnbox\vsplit#1 upto #2}
257
258\def\page_mcl_routine_continue
259  {\bgroup
260   \forgetall
261   \page_mcl_initialize_variables
262   \settotalinsertionheight
263   \page_mcl_set_n_of_lines\totalinsertionheight
264   \d_page_mcl_balance_target\c_page_mcl_n_of_lines\openlineheight
265   \dohandleallcolumns{\page_mcl_split_column\normalpagebox\d_page_mcl_balance_target}%
266   \setbox\b_page_mcl_rest_of_page\vpack{\unvbox\normalpagebox}%
267   \dohandleallcolumns
268     {\global\setbox\currentcolumnbox\vpack to \d_page_mcl_balance_target
269        {\unvbox\currentcolumnbox % wel of niet \unvbox ?
270         \vfill}}%
271   \setbox\b_page_mcl_preceding\vpack{\page_mcl_flush_packaged_columns_continued}%
272   \page_otr_construct_and_shipout\box\b_page_mcl_preceding\zerocount % three arguments
273   \page_otr_command_set_hsize
274   \page_otr_command_set_vsize
275   \unvbox\b_page_mcl_rest_of_page
276   \egroup}
277
278\def\page_mcl_routine_balance
279  {\bgroup
280   % why no \forgetall here
281   \page_mcl_initialize_variables
282   \widowpenalty\zerocount
283   \setbox\b_page_mcl_balance_content\vpack{\unvbox\normalpagebox}%
284   \ifdim\ht\b_page_mcl_balance_content>\openlineheight % at least one line
285     \ifnum\c_page_mcl_balance_minimum<\plustwo % balance anyway
286       \c_page_mcl_balance_possible\conditionaltrue
287     \else % check criterium to available lines
288       \getnoflines{\ht\b_page_mcl_balance_content}%
289       \divideby\noflines \nofcolumns \relax
290       \ifnum\noflines<\c_page_mcl_balance_minimum \relax
291         \ifdim{\ht\b_page_mcl_balance_content+\openlineheight}>\makeupheight
292           \c_page_mcl_balance_possible\conditionaltrue % column exceeding text height
293         \else
294           \c_page_mcl_balance_possible\conditionalfalse % it seems to fit
295         \fi
296       \else
297         \c_page_mcl_balance_possible\conditionaltrue % balance indeed
298       \fi
299     \fi
300   \else
301     \c_page_mcl_balance_possible\conditionalfalse % balancing does not make sense
302   \fi
303   \ifconditional\c_page_mcl_balance_possible % start balancing, was: \ifdim\ht\b_page_mcl_balance_content>\openlineheight
304     \page_mcl_balance_try_one
305     \page_mcl_balance_try_two
306   \else
307     % a one liner is not properly handled here, so best rewrite the text then
308     \showmessage\m!columns{10}\empty
309     \global\setbox\firstcolumnbox\vpack{\unvbox\b_page_mcl_balance_content}%
310   \fi
311   \c_page_mcl_routine\c_page_mcl_routine_error
312  %\baselinebottom % forces depth in separation rule
313   \page_mcl_flush_packaged_columns_balanced
314  %\allowbreak
315   \egroup}
316
317% \showmakeup
318
319\def\page_mcl_balance_try_one
320  {\d_page_mcl_balance_target{\ht\b_page_mcl_balance_content+\topskip-\baselineskip}%
321   \divideby\d_page_mcl_balance_target \nofcolumns
322   \vbadness\plustenthousand
323   \c_page_mcl_balance_tries\zerocount
324   \bgroup
325   \ifgridsnapping
326     \d_page_mcl_balance_step\lineheight
327   \else
328     \d_page_mcl_balance_step\spacingfactor\onepoint % rubish
329   \fi
330   \doloop\page_mcl_balance_try_one_attempt
331   \global\setbox\b_page_mcl_rest_of_page\box\voidbox
332   \ifnum\c_page_mcl_balance_tries>\c_page_mcl_balance_tries_max\relax
333     \showmessage\m!columns7\empty
334   \else
335     \showmessage\m!columns8{\the\c_page_mcl_balance_tries}%
336   \fi
337   \egroup}
338
339\def\page_mcl_balance_try_one_attempt
340  {\advanceby\c_page_mcl_balance_tries \plusone
341   \global\setbox\b_page_mcl_rest_of_page\copy\b_page_mcl_balance_content\relax
342   \dohandleallcolumns{\page_mcl_split_column\b_page_mcl_rest_of_page\d_page_mcl_balance_target}%
343   \d_page_mcl_balance_natural_height\zeropoint
344   \dohandleallcolumns\page_mcl_balance_try_one_attempt_step
345\advanceby\d_page_mcl_balance_natural_height-33\scaledpoint % some slack
346   \ifnum\c_page_mcl_balance_tries>\c_page_mcl_balance_tries_max\relax
347     \exitloop
348   \orelse\ifdim\ht\b_page_mcl_rest_of_page>\zeropoint
349     \advanceby\d_page_mcl_balance_target\d_page_mcl_balance_step\relax
350   \orelse\ifdim\d_page_mcl_balance_natural_height>\ht\firstcolumnbox\relax
351     \advanceby\d_page_mcl_balance_target\d_page_mcl_balance_step\relax
352   \else
353     \exitloop
354   \fi}
355
356\def\page_mcl_balance_try_one_attempt_step
357  {\ifcase\currentcolumn\or\else
358     \ifdim\ht\currentcolumnbox>\d_page_mcl_balance_natural_height\relax
359       \d_page_mcl_balance_natural_height\ht\currentcolumnbox
360     \fi
361   \fi}
362
363\def\page_mcl_balance_try_two % hm ... can probably go
364  {\dohandleallcolumnscs\page_mcl_balance_try_two_step}
365
366% \def\page_mcl_balance_try_two_step
367%   {\global\setbox\currentcolumnbox\vbox to \ht\firstcolumnbox
368%      {\box\currentcolumnbox
369%       \vfill}}
370
371\def\page_mcl_balance_try_two_step
372  {%\global\setbox\currentcolumnbox\box\currentcolumnbox
373   \ht\currentcolumnbox\ht\firstcolumnbox}
374
375\permanent\tolerant\protected\def\startmulticolumns[#S#1]%
376  {\bgroup
377   \ifinsidecolumns
378     \page_mcl_start_nop
379   \else
380     \setupmulticolumns[#1]%
381     \nofcolumns\multicolumnsparameter\c!n\relax
382     \ifnum\nofcolumns>\plusone
383       \page_mcl_start_yes
384       \nofmulticolumns\nofcolumns
385     \else
386       \page_mcl_start_nop
387     \fi
388   \fi}
389
390\aliased\let\stopmulticolumns\relax
391
392\def\page_mcl_start_nop
393  {\enforced\let\stopmulticolumns\page_mcl_stop_nop}
394
395\permanent\protected\def\page_mcl_stop_nop
396  {\egroup}
397
398\protected\def\page_mcl_start_yes
399  {\whitespace
400   \begingroup
401   \enforced\let\stopmulticolumns\page_mcl_stop_indeed
402   \global\insidecolumnstrue
403   \global\insidemulticolumnstrue
404   %
405   \d_page_mcl_distance\multicolumnsparameter\c!distance\relax
406   %
407   \ifcstok{\multicolumnsparameter\c!direction}\v!right
408     \c_page_mcl_reverse\conditionalfalse
409   \else
410     \c_page_mcl_reverse\conditionaltrue
411   \fi
412   %
413   \ifcstok{\multicolumnsparameter\c!balance}\v!yes
414     \c_page_mcl_balance\conditionaltrue
415   \else
416     \c_page_mcl_balance\conditionalfalse
417   \fi
418   %
419   \usealignparameter\multicolumnsparameter
420   %
421   \edef\p_blank{\multicolumnsparameter\c!blank}%
422   \ifempty\p_blank \else
423       \setupblank[\p_blank]%
424   \fi
425   %
426   \ifdim\s_spac_whitespace_parskip>\zeropoint\relax
427       \setupwhitespace[\p_blank]%
428   \fi
429   \c_page_mcl_balance_minimum\multicolumnsparameter\c!ntop\relax
430   %
431   \begingroup
432   %
433   \d_page_mcl_leftskip \leftskip
434   \d_page_mcl_rightskip\rightskip
435   \leftskip            \zeroskip
436   \rightskip           \zeroskip
437   \hangafter           \zerocount
438   \hangindent          \zeropoint
439   %
440   \widowpenalty        \zerocount % will become option
441   \clubpenalty         \zerocount % will become option
442   %
443   \ifdim{\pagetotal+\parskip+\openlineheight}<\pagegoal
444     \allowbreak
445   \else
446     \break % sometimes fails
447   \fi
448   \appendtoks
449     \topskip1\topskip % best a switch
450   \to \everybodyfont
451   \expand\everybodyfont  % ugly here
452   \saveinterlinespace % ugly here
453   %
454   \initializecolumns\nofcolumns
455   %
456   \reseteverypar % todo
457   %
458   \ifzeropt\pagetotal\else
459     \verticalstrut
460     \vskip-\struttotal
461   \fi
462   \global\d_page_mcl_saved_pagetotal\pagetotal
463   \setupoutputroutine[\s!multicolumn]%
464   \c_page_mcl_routine\c_page_mcl_routine_intercept
465   \page_otr_trigger_output_routine
466   \global\d_page_mcl_preceding_height\ht\b_page_mcl_preceding
467   \c_page_mcl_routine\c_page_mcl_routine_continue
468   \page_otr_command_set_hsize
469   \page_otr_command_set_vsize}
470
471\permanent\protected\def\page_mcl_stop_indeed
472  {\relax
473   \synchronizeoutput
474   \par
475   \ifconditional\c_page_mcl_balance
476     \c_page_mcl_routine\c_page_mcl_routine_continue
477     \goodbreak
478     \c_page_mcl_routine\c_page_mcl_routine_balance
479   \else
480     \goodbreak
481   \fi
482   % still the multi column routine
483   \page_otr_trigger_output_routine % the prevdepth is important, try e.g. toclist in
484   \prevdepth\zeropoint % columns before some noncolumned text text
485   %
486   \c_page_mcl_routine\c_page_mcl_routine_regular
487   %
488   \ifvoid\b_page_mcl_preceding\else
489     \unvbox\b_page_mcl_preceding
490   \fi
491   \global\d_page_mcl_preceding_height\zeropoint
492   \endgroup % here
493   \nofcolumns\plusone
494   \nofmulticolumns\plusone
495   \page_otr_command_set_vsize
496   \dosomebreak\allowbreak
497   \page_floats_column_pop_saved
498   %
499   \global\insidemulticolumnsfalse
500   \global\insidecolumnsfalse
501   \endgroup
502   \egroup}%
503
504\setupmulticolumns
505  [\c!n=2,
506   \c!ntop=1,
507   \c!direction=\v!right,
508   \c!distance=1.5\bodyfontsize, % influenced by switching
509   \c!balance=\v!yes,
510   \c!align={\v!text,\v!tolerant},
511   \c!blank={\v!line,\v!fixed}]
512
513\defineoutputroutine
514  [\s!multicolumn]
515  [\s!page_otr_command_routine         =\page_mcl_command_routine,
516   \s!page_otr_command_package_contents=\page_mcl_command_package_contents,
517   \s!page_otr_command_set_vsize       =\page_mcl_command_set_vsize,
518   \s!page_otr_command_set_hsize       =\page_mcl_command_set_hsize]
519
520\let\strc_itemgroups_start_columns_old\strc_itemgroups_start_columns
521\let\strc_itemgroups_stop_columns_old \strc_itemgroups_stop_columns
522
523\def\strc_itemgroups_start_columns_new{\startmulticolumns[\c!n=\itemgroupparameter\c!n]}
524\def\strc_itemgroups_stop_columns_new {\stopmulticolumns}
525
526\installtexexperiment
527  {itemize.columns}
528  {\let\strc_itemgroups_start_columns\strc_itemgroups_start_columns_new
529   \let\strc_itemgroups_stop_columns \strc_itemgroups_stop_columns_new}
530  {\let\strc_itemgroups_start_columns\strc_itemgroups_start_columns_old
531   \let\strc_itemgroups_stop_columns \strc_itemgroups_stop_columns_old}
532
533\protect \endinput
534