%D \module %D [ file=page-mcl, %D version=2020.07.26, % stripped down redone page-mul %D title=\CONTEXT\ Page Macros, %D subtitle=Multicolumns Limited, %D author=Hans Hagen, %D date=\currentdate, %D copyright={PRAGMA ADE \& \CONTEXT\ Development Team}] %C %C This module is part of the \CONTEXT\ macro||package and is %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. \writestatus{loading}{ConTeXt Page Macros / Multicolumns Limited} \unprotect %D Columns are kind of hairy in \TEX\ and we would be better if no one needed them. %D Anyway, we do need some support and no mechanism can serve all. The original %D multicolumn mechanism of \MKII\ was never fully adapted to \MKIV, where mixed %D columns and page columns showed up instead. However, for some cases a dumb %D mechanism makes sense, so again we introduce multi columns, but without float %D hacks. This one will be optimized for mixed usage as for instance itemize needs. %D Their main advantage is that they can better deal with notes, which are hairy in %D themselves. This will (for now) only happen in \LMTX. %D %D The code is stripped down ancient \MKII\ code and might become cleaner as it %D evolves. Macros keep similar names but take a different namespace. The code is %D not yet perfect wrt spacing! % \enableexperiments[itemize.columns] % % \starttext % % \startmulticolumns % \dorecurse{10}{\samplefile{ward}\footnote{note #1}\par} % \stopmulticolumns % % \dorecurse{4}{\samplefile{ward}\par} % % \startitemize[packed,columns,two] % \dorecurse{100}{\startitem test #1 \footnote{this is a footnote #1}\stopitem} % \stopitemize % % \stoptext \ifdefined \startmulticolumns % we're testing and don't want to remake the format \else \installcorenamespace {multicolumns} \installframedcommandhandler \??multicolumns {multicolumns} \??multicolumns \newdimen \d_page_mcl_available_width \newdimen \d_page_mcl_distance \newdimen \d_page_mcl_leftskip \newdimen \d_page_mcl_rightskip \newdimen \d_page_mcl_used_width \newdimen \d_page_mcl_temp \newdimen \d_page_mcl_saved_pagetotal % brrr, still needed ? \newcount \c_page_mcl_balance_minimum \newcount \c_page_mcl_n_of_lines \newbox \b_page_mcl_preceding \newdimen \d_page_mcl_preceding_height \newconditional\c_page_mcl_preceding_present \newbox \b_page_mcl_rest_of_page \newbox \b_page_mcl_page \newconditional\c_page_mcl_reverse \newconditional\c_page_mcl_balance \newconstant \c_page_mcl_routine \setnewconstant\c_page_mcl_routine_regular \zerocount \setnewconstant\c_page_mcl_routine_intercept \plusone \setnewconstant\c_page_mcl_routine_continue \plustwo \setnewconstant\c_page_mcl_routine_balance \plusthree \setnewconstant\c_page_mcl_routine_error \plusfour \newbox \b_page_mcl_balance_content \newconstant \c_page_mcl_balance_tries_max \newcount \c_page_mcl_balance_tries \newdimen \d_page_mcl_balance_target \newdimen \d_page_mcl_balance_natural_height \newdimen \d_page_mcl_balance_step \newconditional\c_page_mcl_balance_possible \c_page_mcl_balance_tries_max 250 % 100 is too small when floats are involved %def\m_page_mcl_overshoot_ratio{\ifgridsnapping0\else.5\fi} \def\m_page_mcl_overshoot_ratio{.5} \fi \unexpanded\def\page_mcl_command_set_hsize {\d_page_mcl_available_width\dimexpr \makeupwidth -\d_page_mcl_leftskip -\d_page_mcl_rightskip -\nofcolumns\d_page_mcl_distance +\d_page_mcl_distance \relax \d_page_mcl_used_width\dimexpr \d_page_mcl_available_width/\nofcolumns \relax \textwidth\d_page_mcl_used_width \hsize\d_page_mcl_used_width} \unexpanded\def\page_mcl_set_n_of_lines#1% {\d_page_mcl_temp\dimexpr +\textheight \ifdim\d_page_mcl_preceding_height>\zeropoint -\d_page_mcl_preceding_height \fi % \ifdim\ht\b_page_mcl_preceding>\zeropoint -\ht\b_page_mcl_preceding \fi -#1% \relax \getnoflines\d_page_mcl_temp \ifnum\layoutlines>\zerocount \ifnum\noflines>\layoutlines \noflines\layoutlines \fi \fi \c_page_mcl_n_of_lines\noflines} \unexpanded\def\page_mcl_command_set_vsize {%%\page_one_command_set_vsize % indeed? \page_mcl_set_n_of_lines\zeropoint \d_page_mcl_temp\nofcolumns\dimexpr \c_page_mcl_n_of_lines\openlineheight % +\m_page_mcl_overshoot_ratio\openlineheight % collect enough data \relax \global\vsize \d_page_mcl_temp \global\pagegoal\d_page_mcl_temp} % let's do it only here, reports maxdimen anyway \unexpanded\def\page_mcl_command_routine {\ifcase\c_page_mcl_routine \page_one_command_routine \or \page_mcl_routine_intercept \or \page_mcl_routine_continue \or \page_mcl_routine_balance \or \page_mcl_routine_error \fi} \let\page_mcl_command_package_contents\page_one_command_package_contents \def\page_mcl_routine_intercept {\global\setbox\b_page_mcl_preceding\vbox {\page_otr_command_flush_top_insertions \unvbox\normalpagebox}} \def\page_mcl_routine_error {\showmessage\m!columns3\empty \page_otr_construct_and_shipout\unvbox\normalpagebox\zerocount} % three arguments \unexpanded\def\page_mcl_initialize_variables {\reseteverypar % maybe still freeze .... \dontcomplain \settopskip \setmaxdepth \topskip 1\topskip \splittopskip \topskip \splitmaxdepth \maxdepth \boxmaxdepth \maxdepth % dangerous \emergencystretch\zeropoint \relax} \def\page_mcl_flush_preceding_normal {\unvbox\b_page_mcl_preceding} \def\page_mcl_flush_preceding_ongrid {\scratchdimen\dimexpr \d_page_mcl_saved_pagetotal -\d_page_mcl_preceding_height -\topskip \relax \box\b_page_mcl_preceding \kern\scratchdimen} \def\page_mcl_flush_packaged_columns_continued {\page_mcl_flush_packaged_columns_indeed \box\b_page_mcl_page} \def\page_mcl_flush_packaged_columns_balanced {\bgroup \page_mcl_flush_packaged_columns_indeed \getnoflines{\htdp\b_page_mcl_page}% \ht\b_page_mcl_page\dimexpr \noflines\openlineheight -\openstrutdepth \ifgridsnapping % quick hack (at least it works with itemize) \else -\openlineheight +\topskip \fi \relax \dp\b_page_mcl_page\openstrutdepth \box\b_page_mcl_page \egroup} \def\page_mcl_synchronize_marks {\dohandleallcolumns{\page_marks_synchronize_column\plusone\nofcolumns\mofcolumns\currentcolumnbox}} \def\page_mcl_flush_packaged_columns_indeed {\ifvoid\b_page_mcl_preceding \setfalse\c_page_mcl_preceding_present % will be set elsewhere \else \settrue\c_page_mcl_preceding_present \page_apply_postprocessors_box\b_page_mcl_preceding \fi \forgetall \page_mcl_initialize_variables \page_mcl_synchronize_marks \setbox\b_page_mcl_page\vpack {\ifconditional\c_page_mcl_reverse\reversehpack\else\naturalhpack\fi to \makeupwidth {\hskip\ifconditional\c_page_mcl_reverse\d_page_mcl_rightskip\else\d_page_mcl_leftskip\fi\relax \dohandleallcolumns {\wd\currentcolumnbox\d_page_mcl_used_width \setbox\scratchbox\hpack{\strut\box\currentcolumnbox}% hm, why strut \anch_mark_column_box\scratchbox\currentcolumn \box\scratchbox \hfil}% \unskip \hskip\ifconditional\c_page_mcl_reverse\d_page_mcl_leftskip\else\d_page_mcl_rightskip\fi}}% \ifconditional\c_page_mcl_preceding_present \settrue\c_page_mcl_preceding_present \ifgridsnapping \page_mcl_flush_preceding_ongrid % obey grid settings, force on grid \else \page_mcl_flush_preceding_normal % ignore grid settings, not on grid \fi \fi \global\d_page_mcl_preceding_height\zeropoint \page_otr_command_set_vsize \dosomebreak\nobreak % hm, only needed when topstuff \ifgridsnapping \else \ifconditional\c_page_mcl_preceding_present \nointerlineskip \vskip\dimexpr\openstrutheight-\topskip\relax \fi \fi \prevdepth\openstrutdepth \nointerlineskip \dp\b_page_mcl_page\zeropoint} \def\page_mcl_split_column#1#2% copy or box {\global\setbox\currentcolumnbox\vsplit#1 upto #2} \def\page_mcl_routine_continue {\bgroup \forgetall \page_mcl_initialize_variables \settotalinsertionheight \page_mcl_set_n_of_lines\totalinsertionheight \d_page_mcl_balance_target\c_page_mcl_n_of_lines\openlineheight \dohandleallcolumns{\page_mcl_split_column\normalpagebox\d_page_mcl_balance_target}% \setbox\b_page_mcl_rest_of_page\vpack{\unvbox\normalpagebox}% \dohandleallcolumns {\global\setbox\currentcolumnbox\vpack to \d_page_mcl_balance_target {\unvbox\currentcolumnbox % wel of niet \unvbox ? \vfill}}% \setbox\b_page_mcl_preceding\vpack{\page_mcl_flush_packaged_columns_continued}% \page_otr_construct_and_shipout\box\b_page_mcl_preceding\zerocount % three arguments \page_otr_command_set_hsize \page_otr_command_set_vsize \unvbox\b_page_mcl_rest_of_page \egroup} \def\page_mcl_routine_balance {\bgroup % why no \forgetall here \page_mcl_initialize_variables \widowpenalty\zerocount \setbox\b_page_mcl_balance_content\vpack{\unvbox\normalpagebox}% \ifdim\ht\b_page_mcl_balance_content>\openlineheight % at least one line \ifnum\c_page_mcl_balance_minimum<\plustwo % balance anyway \settrue\c_page_mcl_balance_possible \else % check criterium to available lines \getnoflines{\ht\b_page_mcl_balance_content}% \divide\noflines \nofcolumns \relax \ifnum\noflines<\c_page_mcl_balance_minimum \relax \ifdim\dimexpr\ht\b_page_mcl_balance_content+\openlineheight\relax>\makeupheight \settrue\c_page_mcl_balance_possible % column exceeding text height \else \setfalse\c_page_mcl_balance_possible % it seems to fit \fi \else \settrue\c_page_mcl_balance_possible % balance indeed \fi \fi \else \setfalse\c_page_mcl_balance_possible % balancing does not make sense \fi \ifconditional\c_page_mcl_balance_possible % start balancing, was: \ifdim\ht\b_page_mcl_balance_content>\openlineheight \page_mcl_balance_try_one \page_mcl_balance_try_two \else % a one liner is not properly handled here, so best rewrite the text then \showmessage\m!columns{10}\empty \global\setbox\firstcolumnbox\vpack{\unvbox\b_page_mcl_balance_content}% \fi \c_page_mcl_routine\c_page_mcl_routine_error %\baselinebottom % forces depth in separation rule \page_mcl_flush_packaged_columns_balanced %\allowbreak \egroup} % \showmakeup \def\page_mcl_balance_try_one {\d_page_mcl_balance_target\dimexpr\ht\b_page_mcl_balance_content+\topskip-\baselineskip\relax \divide\d_page_mcl_balance_target \nofcolumns \vbadness\plustenthousand \c_page_mcl_balance_tries\zerocount \bgroup \ifgridsnapping \d_page_mcl_balance_step\lineheight \else \d_page_mcl_balance_step\spacingfactor\onepoint % rubish \fi \doloop\page_mcl_balance_try_one_attempt \global\setbox\b_page_mcl_rest_of_page\box\voidbox \ifnum\c_page_mcl_balance_tries>\c_page_mcl_balance_tries_max\relax \showmessage\m!columns7\empty \else \showmessage\m!columns8{\the\c_page_mcl_balance_tries}% \fi \egroup} \def\page_mcl_balance_try_one_attempt {\advance\c_page_mcl_balance_tries \plusone \global\setbox\b_page_mcl_rest_of_page\copy\b_page_mcl_balance_content\relax \dohandleallcolumns{\page_mcl_split_column\b_page_mcl_rest_of_page\d_page_mcl_balance_target}% \d_page_mcl_balance_natural_height\zeropoint \dohandleallcolumns\page_mcl_balance_try_one_attempt_step \advance\d_page_mcl_balance_natural_height-33\scaledpoint % some slack \ifnum\c_page_mcl_balance_tries>\c_page_mcl_balance_tries_max\relax \exitloop \orelse\ifdim\ht\b_page_mcl_rest_of_page>\zeropoint \advance\d_page_mcl_balance_target\d_page_mcl_balance_step\relax \orelse\ifdim\d_page_mcl_balance_natural_height>\ht\firstcolumnbox\relax \advance\d_page_mcl_balance_target\d_page_mcl_balance_step\relax \else \exitloop \fi} \def\page_mcl_balance_try_one_attempt_step {\ifcase\currentcolumn\or\else \ifdim\ht\currentcolumnbox>\d_page_mcl_balance_natural_height\relax \d_page_mcl_balance_natural_height\ht\currentcolumnbox \fi \fi} \def\page_mcl_balance_try_two % hm ... can probably go {\dohandleallcolumnscs\page_mcl_balance_try_two_step} % \def\page_mcl_balance_try_two_step % {\global\setbox\currentcolumnbox\vbox to \ht\firstcolumnbox % {\box\currentcolumnbox % \vfill}} \def\page_mcl_balance_try_two_step {%\global\setbox\currentcolumnbox\box\currentcolumnbox \ht\currentcolumnbox\ht\firstcolumnbox} \unexpanded\def\startmulticolumns {\dosingleempty\page_mcl_start} \def\page_mcl_start[#1]% {\bgroup \ifinsidecolumns \page_mcl_start_nop \else \iffirstargument \setupmulticolumns[#1]% \fi \nofcolumns\multicolumnsparameter\c!n\relax \ifnum\nofcolumns>\plusone \page_mcl_start_yes \nofmulticolumns\nofcolumns \else \page_mcl_start_nop \fi \fi} \unexpanded\def\page_mcl_start_nop {\let\stopmulticolumns\page_mcl_stop_nop} \unexpanded\def\page_mcl_stop_nop {\egroup} \unexpanded\def\page_mcl_start_yes {\whitespace \begingroup \let\stopmulticolumns\page_mcl_stop_indeed \global\insidecolumnstrue \global\insidemulticolumnstrue % \d_page_mcl_distance\multicolumnsparameter\c!distance\relax % \edef\p_direction{\multicolumnsparameter\c!direction}% \ifx\p_direction\v!right \setfalse\c_page_mcl_reverse \else \settrue\c_page_mcl_reverse \fi % \edef\p_balance{\multicolumnsparameter\c!balance}% \ifx\p_balance\v!yes \settrue\c_page_mcl_balance \else \setfalse\c_page_mcl_balance \fi % \usealignparameter\multicolumnsparameter % \edef\p_blank{\multicolumnsparameter\c!blank}% \ifx\p_blank\empty \else \setupblank[\p_blank]% \fi % \ifdim\s_spac_whitespace_parskip>\zeropoint\relax \setupwhitespace[\p_blank]% \fi \c_page_mcl_balance_minimum\multicolumnsparameter\c!ntop\relax % \begingroup % \d_page_mcl_leftskip \leftskip \d_page_mcl_rightskip\rightskip \leftskip \zeropoint \rightskip \zeropoint \hangafter \zerocount \hangindent \zeropoint % \widowpenalty \zerocount % will become option \clubpenalty \zerocount % will become option % \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 % \reseteverypar % todo % \ifdim\pagetotal=\zeropoint \else \verticalstrut \vskip-\struttotal \fi \global\d_page_mcl_saved_pagetotal\pagetotal \setupoutputroutine[\s!multicolumn]% \c_page_mcl_routine\c_page_mcl_routine_intercept \page_otr_trigger_output_routine \global\d_page_mcl_preceding_height\ht\b_page_mcl_preceding \c_page_mcl_routine\c_page_mcl_routine_continue \page_otr_command_set_hsize \page_otr_command_set_vsize} \unexpanded\def\page_mcl_stop_indeed {\relax \synchronizeoutput \par \ifconditional\c_page_mcl_balance \c_page_mcl_routine\c_page_mcl_routine_continue \goodbreak \c_page_mcl_routine\c_page_mcl_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_mcl_routine\c_page_mcl_routine_regular % \ifvoid\b_page_mcl_preceding\else \unvbox\b_page_mcl_preceding \fi \global\d_page_mcl_preceding_height\zeropoint \endgroup % here \nofcolumns\plusone \nofmulticolumns\plusone \page_otr_command_set_vsize \dosomebreak\allowbreak \page_floats_column_pop_saved % \global\insidemulticolumnsfalse \global\insidecolumnsfalse \endgroup \egroup}% \setupmulticolumns [\c!n=2, \c!ntop=1, \c!direction=\v!right, \c!distance=1.5\bodyfontsize, % influenced by switching \c!balance=\v!yes, \c!align={\v!text,\v!tolerant}, \c!blank={\v!line,\v!fixed}] \defineoutputroutine [\s!multicolumn] [\s!page_otr_command_routine =\page_mcl_command_routine, \s!page_otr_command_package_contents=\page_mcl_command_package_contents, \s!page_otr_command_set_vsize =\page_mcl_command_set_vsize, \s!page_otr_command_set_hsize =\page_mcl_command_set_hsize] \let\strc_itemgroups_start_columns_old\strc_itemgroups_start_columns \let\strc_itemgroups_stop_columns_old \strc_itemgroups_stop_columns \def\strc_itemgroups_start_columns_new{\startmulticolumns\relax} \def\strc_itemgroups_stop_columns_new {\stopmulticolumns} \installtexexperiment {itemize.columns} {\let\strc_itemgroups_start_columns\strc_itemgroups_start_columns_new \let\strc_itemgroups_stop_columns \strc_itemgroups_stop_columns_new} {\let\strc_itemgroups_start_columns\strc_itemgroups_start_columns_old \let\strc_itemgroups_stop_columns \strc_itemgroups_stop_columns_old} \protect \endinput