%D \module %D [ file=page-mix, %D version=2012.07.12, %D title=\CONTEXT\ Page Macros, %D subtitle=Mixed Columns, %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 / Mixed Columns} %D This is a very experimental module. Eventually it will replace the current %D multi column mechanism (that then will be an instance). The \LUA\ part of %D the interface will quite probably change so don't use that one directly %D (yet). % todo: % % consult note class % notes per page % notes in each column % notes in last column % notes local/global % top and bottom inserts % wide floats % move floats % offsets (inner ones, so we change the hsize ... needed with backgrounds % when no content we currently loose the page % luatex buglet: % % \ctxlua{tex.setbox("global",0,node.hpack(nodes.pool.glyph("a",font.current())))}\box0 \registerctxluafile{page-mix}{} \unprotect %D The mixed output routine replaces the traditional multi column handler that %D started out in \MKII. One of the complications of a routine is that it needs %D to align nicely when mixed in a single column layout. Instead of using all %D kind of shift juggling in this mechanism we simply switch to grid mode %D locally. After all, columns don't look nice when not on a. As the grid %D snapper in \MKIV\ is more advanced not that much extra code is needed. %D We use the command handler but the parent settings are not to be changed. %D Instead we could have used a dedicated root setup, but it's not worth the %D trouble. \installcorenamespace{mixedcolumns} \installframedcommandhandler \??mixedcolumns {mixedcolumns} \??mixedcolumns % old multicolumns mechanism % % \c!ntop=1, % \c!rule=\v!off, : now separator=rule % \c!height=, % \c!blank={\v!line,\v!fixed}, % \c!rulethickness=\linewidth, % \c!offset=.5\bodyfontsize, \setupmixedcolumns [\c!distance=1.5\bodyfontsize, \c!n=\plustwo, %\c!align=, % inherit (also replaces tolerance) %\c!before=, %\c!after=, %\c!separator=\v!none, %\c!setups=, %\c!balance=\v!no, %\c!blank={\v!line,\v!fixed}, yes or no \c!frame=\v!off, \c!strut=\v!no, \c!offset=\v!overlay, \c!alternative=\v!local, \c!maxheight=\textheight, \c!maxwidth=\makeupwidth, \c!grid=\v!tolerant, \c!internalgrid=\v!line, \c!step=.25\lineheight, % needs some experimenting %\c!splitmethod=\v!fixed, % will be default \c!direction=\v!normal, % new (also todo in the new columnsets) \c!notes=\v!yes, \c!method=\ifinner\s!box\else\s!otr\fi] % automatic as suggested by WS \let\startmixedcolumns\relax % defined later \let\stopmixedcolumns \relax % defined later \appendtoks % could become an option \setuevalue{\e!start\currentmixedcolumns}{\startmixedcolumns[\currentmixedcolumns]}% \setuevalue{\e!stop \currentmixedcolumns}{\stopmixedcolumns}% \to \everydefinemixedcolumns %D In order to avoid a mixup we use quite some local registers. \newdimen \d_page_mix_column_width \newdimen \d_page_mix_max_height \newdimen \d_page_mix_max_width \newdimen \d_page_mix_distance \newcount \c_page_mix_n_of_columns \newdimen \d_page_mix_threshold \newdimen \d_page_mix_leftskip \newdimen \d_page_mix_rightskip \newdimen \d_page_mix_balance_step \setnewconstant\c_page_mix_balance_cycles 500 \setnewconstant\c_page_mix_break_forced -123 \newbox \b_page_mix_preceding \newdimen \d_page_mix_preceding_height \newbox \b_page_mix_collected \newconstant \c_page_mix_routine \setnewconstant\c_page_mix_routine_regular \zerocount \setnewconstant\c_page_mix_routine_intercept\plusone \setnewconstant\c_page_mix_routine_continue \plustwo \setnewconstant\c_page_mix_routine_balance \plusthree \setnewconstant\c_page_mix_routine_error \plusfour \newconditional\c_page_mix_process_notes %D The main environment is called as follows: %D %D \starttyping %D \startmixedcolumns[instance][settings] %D \startmixedcolumns[instance] %D \startmixedcolumns[settings] %D \stoptyping %D %D However, best is not to use this one directly but define an instance and %D use that one. % % For the moment only on my machine: % % \definemixedcolumns % [\v!columns] % % \unexpanded\def\setupcolumns % {\setupmixedcolumns[\v!columns]} %D In itemizations we also need columns, so let's define a apecial instance %D for them. These need to work well in situations like this: %D %D \starttyping %D \input zapf %D %D \startnarrower %D \startitemize[columns,two,packed][before=,after=] %D \dorecurse{10}{\startitem item #1 \stopitem} %D \stopitemize %D \stopnarrower %D %D \input zapf %D %D \startnarrower %D \startitemize[columns,two][before=,after=] %D \dorecurse{10}{\startitem item #1 \stopitem} %D \stopitemize %D \stopnarrower %D %D \input zapf %D %D \startnarrower %D \startitemize[columns,two] %D \dorecurse{10}{\startitem item #1 \stopitem} %D \stopitemize %D \stopnarrower %D %D \input zapf %D \stoptyping \ifdefined\s!itemgroupcolumns \else \def\s!itemgroupcolumns{itemgroupcolumns} \fi \definemixedcolumns [\s!itemgroupcolumns] [\c!n=\itemgroupparameter\c!n, \c!direction=\itemgroupparameter\c!direction, \c!separator=\v!none, \c!splitmethod=\v!none, \c!grid=\v!tolerant, \c!internalgrid=\v!halfline, % new, we may still revert to \v!line \c!balance=\v!yes, \c!notes=\v!no] % kind of hidden % better \setupmixedcolumns [\s!itemgroupcolumns] [\c!splitmethod=\v!fixed, \c!grid=\v!yes, \c!internalgrid=\v!line] % even better: \setupitemgroup [\c!grid=\v!tolerant:10] % 10 pct tolerance in columns snapping \setupmixedcolumns [\s!itemgroupcolumns] [\c!grid=\itemgroupparameter\c!grid] % the fast hooks: \unexpanded\def\strc_itemgroups_start_columns {\startmixedcolumns[\s!itemgroupcolumns]} % we could have a fast one \unexpanded\def\strc_itemgroups_stop_columns {\stopmixedcolumns} %D The mixed output routine can be in different states. First we need to intercept %D the already present content. This permits mixed single and multi column usage. %D Then we have the continuous routine, one that intercepts pages in sequence. %D Finally, when we finish the mixed columns mode, we can (optionally) balance the %D last page. \unexpanded\def\page_mix_command_routine {\ifcase\c_page_mix_routine \page_one_command_routine \or \page_mix_routine_intercept \or \page_mix_routine_continue \or \page_mix_routine_balance \or \page_mix_routine_error \fi} %D The interceptor is quite simple, at least for the moment. \def\page_mix_routine_intercept {\ifdim\pagetotal>\pagegoal % testcase: preceding-001 ... if we don't do this, text can disappear as % preceding is overwritten ... needs to be figured out some day \page_one_command_routine \fi \global\setbox\b_page_mix_preceding\vbox % pack ? {\forgetall \page_otr_command_flush_top_insertions \ifdim\htdp\b_page_mix_preceding=\zeropoint \else \writestatus\m!columns{preceding error}% \unvbox\b_page_mix_preceding \fi \unvbox\normalpagebox}} %D The error routine is there but unlikely to be called. It is a left-over from %D the traditional routine that might come in handy some day. \def\page_mix_routine_error {\showmessage\m!columns3\empty \page_otr_construct_and_shipout\unvbox\normalpagebox\zerocount} % three arguments %D Some settings (and actions) depend on the current output routine and setting the %D hsize and vsize is among them. The calculation of the hsize is done elsewhere. \unexpanded\def\page_mix_command_set_hsize {\hsize\d_page_mix_column_width \columnwidth\d_page_mix_column_width} %D When setting the vsize we make sure that we collect a few more lines than needed %D so that we have enough to split over the columns. Collecting too much is somewhat %D tricky as they will spill over to the next page. \unexpanded\def\page_mix_command_set_vsize {\vsize\dimexpr\c_page_mix_n_of_columns\textheight+\c_page_mix_n_of_columns\lineheight\relax \pagegoal\vsize} %D As we use \LUA\ there is the usual amount of tracing at that end. At the tex end %D we only visualize boxes. \let\page_mix_hbox\hbox \let\page_mix_vbox\vbox \installtextracker {mixedcolumns.boxes} {\let\page_mix_hbox\ruledhbox \let\page_mix_vbox\ruledvbox} {\let\page_mix_hbox\hbox \let\page_mix_vbox\vbox} %D We provide a few column break options. Interesting is that while forcing a new %D column in the traditional mechanism was a pain, here it works quite well. \installcolumnbreakmethod \s!mixedcolumn \v!preference {\goodbreak} \installcolumnbreakmethod \s!mixedcolumn \v!yes {\par \penalty\c_page_mix_break_forced\relax} %D As we operate in grid snapping mode, we use a dedicated macro to enable this %D mechamism. \def\page_mix_enable_grid_snapping {\edef\p_grid{\mixedcolumnsparameter\c!grid}% \ifx\p_grid\empty % just follow the default grid settings \else \gridsnappingtrue \setsystemmode\v!grid \spac_grids_snap_value_set\p_grid \fi} %D Between columns there is normally just spacing unless one enforces a rule. %D %D \starttyping %D \input zapf %D %D \startnarrower %D \startmixedcolumns[n=2,background=color,backgroundcolor=red,rulethickness=1mm,rulecolor=green,separator=rule] %D \input zapf %D \stopmixedcolumns %D \stopnarrower %D %D \input zapf %D \stoptyping \installcorenamespace{mixedcolumnsseparator} \unexpanded\def\installmixedcolumnseparator#1#2% {\setvalue{\??mixedcolumnsseparator#1}{#2}} \installmixedcolumnseparator\v!rule {\vrule \s!width \mixedcolumnsparameter\c!rulethickness \s!height\mixedcolumnseparatorheight \s!depth \mixedcolumnseparatordepth \relax} \unexpanded\def\page_mix_command_inject_separator {\begingroup \setbox\scratchbox\hbox to \zeropoint \bgroup \hss \starttextproperties \usemixedcolumnscolorparameter\c!rulecolor \begincsname\??mixedcolumnsseparator\p_separator\endcsname % was \c!rule \stoptextproperties \hss \egroup \ht\scratchbox\zeropoint \dp\scratchbox\zeropoint \hss \box\scratchbox \hss \endgroup} %D We've now arrived at the real code. The start command mostly sets up the %D environment and variables that are used in the splitter. One of the last %D things happening at the start is switching over to the mixed continuous %D routine. \installcorenamespace{mixedcolumnsbefore} \installcorenamespace{mixedcolumnsstart} \installcorenamespace{mixedcolumnsstop} \installcorenamespace{mixedcolumnsafter} %D For practical reasons there is always a first argument needed that %D indicates the class. %D %D \starttyping %D \startmixedcolumns[n=3,alternative=global] %D \dorecurse{200}{Zomaar wat #1 met een footnote\footnote{note #1}. } %D \stopmixedcolumns %D \stoptyping \let\currentmixedcolumnsmethod\empty \installmacrostack\currentmixedcolumns \installmacrostack\currentmixedcolumnsmethod \unexpanded\def\startmixedcolumns {\dodoubleempty\page_mix_start_columns} \def\page_mix_start_columns_checked#1#2% {\edef\currentmixedcolumnsmethod{\mixedcolumnsparameter\c!method}% \ifx\currentmixedcolumnsmethod\v!box \singleexpandafter#1% \else\ifinsidecolumns \doubleexpandafter#2% \else \doubleexpandafter#1% \fi\fi} \unexpanded\def\page_mix_start_columns {\push_macro_currentmixedcolumns \push_macro_currentmixedcolumnsmethod \ifsecondargument \singleexpandafter\page_mix_start_columns_a \else\iffirstargument \doubleexpandafter\page_mix_start_columns_b \else \doubleexpandafter\page_mix_start_columns_c \fi\fi} \def\page_mix_start_columns_a[#1]% [#2]% {\edef\currentmixedcolumns{#1}% \page_mix_start_columns_checked \page_mix_start_columns_a_yes \page_mix_start_columns_a_nop} \def\page_mix_start_columns_a_yes[#1]% {\mixedcolumnsparameter\c!before\relax \begincsname\??mixedcolumnsbefore\currentmixedcolumnsmethod\endcsname\relax \begingroup \setupcurrentmixedcolumns[#1]% \page_mix_initialize_columns \begincsname\??mixedcolumnsstart\currentmixedcolumnsmethod\endcsname \let\stopmixedcolumns\page_mix_columns_stop_yes} \def\page_mix_start_columns_a_nop[#1]% {\begingroup \let\stopmixedcolumns\page_mix_columns_stop_nop} \def\page_mix_start_columns_b[#1][#2]% {\doifelseassignment{#1}% {\let\currentmixedcolumns\empty \page_mix_error_b} {\edef\currentmixedcolumns{#1}% \firstargumentfalse}% \page_mix_start_columns_checked \page_mix_start_columns_b_yes \page_mix_start_columns_b_nop [#1]} \def\page_mix_start_columns_b_yes[#1]% {\mixedcolumnsparameter\c!before\relax % so, it doesn't listen to local settings ! \begincsname\??mixedcolumnsbefore\currentmixedcolumnsmethod\endcsname\relax \begingroup \iffirstargument \setupcurrentmixedcolumns[#1]% \fi \page_mix_initialize_columns \begincsname\??mixedcolumnsstart\currentmixedcolumnsmethod\endcsname % no \relax \let\stopmixedcolumns\page_mix_columns_stop_yes} \def\page_mix_start_columns_b_nop[#1]% {\begingroup \let\stopmixedcolumns\page_mix_columns_stop_nop} \def\page_mix_error_b {\writestatus\m!columns{best use an instance of mixed columns}} \def\page_mix_start_columns_c[#1][#2]% {\let\currentmixedcolumns\empty \page_mix_start_columns_checked \page_mix_start_columns_c_yes \page_mix_start_columns_c_nop} \def\page_mix_start_columns_c_yes {\mixedcolumnsparameter\c!before\relax \begincsname\??mixedcolumnsbefore\currentmixedcolumnsmethod\endcsname\relax \begingroup \page_mix_initialize_columns \begincsname\??mixedcolumnsstart\currentmixedcolumnsmethod\endcsname \let\stopmixedcolumns\page_mix_columns_stop_yes} \def\page_mix_start_columns_c_nop {\begingroup \let\stopmixedcolumns\page_mix_columns_stop_nop} \unexpanded\def\page_mix_fast_columns_start#1% {\push_macro_currentmixedcolumns \push_macro_currentmixedcolumnsmethod \edef\currentmixedcolumns{#1}% \edef\currentmixedcolumnsmethod{\mixedcolumnsparameter\c!method}% \mixedcolumnsparameter\c!before\relax % so, it doesn't listen to local settings ! \begincsname\??mixedcolumnsbefore\currentmixedcolumnsmethod\endcsname\relax \begingroup \page_mix_initialize_columns \begincsname\??mixedcolumnsstart\currentmixedcolumnsmethod\endcsname % no \relax \let\page_mix_fast_columns_stop\page_mix_columns_stop_yes} %D When we stop, we switch over to the balancing routine. After we're done we %D make sure to set the sizes are set, a somewhat redundant action when we %D already have flushed but better be safe. \let\page_mix_fast_columns_stop\relax \newtoks\t_page_mix_at_the_end \def\page_mix_finalize_columns {\ifconditional\c_page_mix_process_notes \else \global\t_page_mix_at_the_end{\stoppostponingnotes}% \fi} \unexpanded\def\page_mix_columns_stop_yes {\begincsname\??mixedcolumnsstop\currentmixedcolumnsmethod\endcsname % no \relax \page_mix_finalize_columns \endgroup \begincsname\??mixedcolumnsafter\currentmixedcolumnsmethod\endcsname\relax \mixedcolumnsparameter\c!after\relax \pop_macro_currentmixedcolumnsmethod \pop_macro_currentmixedcolumns \the\t_page_mix_at_the_end\global\t_page_mix_at_the_end\emptytoks} \unexpanded\def\page_mix_columns_stop_nop {\page_mix_finalize_columns \endgroup \pop_macro_currentmixedcolumnsmethod \pop_macro_currentmixedcolumns \the\t_page_mix_at_the_end\global\t_page_mix_at_the_end\emptytoks} % \unexpanded\def\page_mix_columns_stop_yes % {\begincsname\??mixedcolumnsstop \currentmixedcolumnsmethod\endcsname % no \relax % \endgroup % \begincsname\??mixedcolumnsafter\currentmixedcolumnsmethod\endcsname\relax % \mixedcolumnsparameter\c!after\relax % \ifx\currentmixedcolumnsmethod\s!otr % \pop_macro_currentmixedcolumnsmethod % \pop_macro_currentmixedcolumns % \synchronizeoutput % brrr, otherwise sometimes issues in itemize % \else % \pop_macro_currentmixedcolumnsmethod % \pop_macro_currentmixedcolumns % \fi % } %D This is how the fast one is used: \unexpanded\def\strc_itemgroups_start_columns {\page_mix_fast_columns_start\s!itemgroupcolumns} \unexpanded\def\strc_itemgroups_stop_columns {\page_mix_fast_columns_stop} % set by start % not used nor documented so commented: % % \setupmixedcolumns % [\s!itemgroupcolumns] % [\c!grid=\itemgroupparameter\c!grid] % % \setupitemgroup % [\c!grid=\v!yes] % we need a value % better %D The common initialization: \def\page_mix_initialize_columns {\page_mix_enable_grid_snapping % \d_page_mix_distance \mixedcolumnsparameter\c!distance \c_page_mix_n_of_columns\mixedcolumnsparameter\c!n \d_page_mix_max_height \mixedcolumnsparameter\c!maxheight \d_page_mix_max_width \mixedcolumnsparameter\c!maxwidth \d_page_mix_balance_step\mixedcolumnsparameter\c!step % \d_page_mix_max_width\dimexpr\d_page_mix_max_width-\leftskip-\rightskip\relax \d_page_mix_leftskip \leftskip \d_page_mix_rightskip\rightskip \leftskip \zeropoint \rightskip\zeropoint % \doifelse{\mixedcolumnsparameter\c!notes}\v!yes\settrue\setfalse\c_page_mix_process_notes \ifconditional\c_page_mix_process_notes \else \startpostponingnotes \fi % \d_page_mix_threshold\zeropoint % \d_page_mix_column_width\dimexpr(\d_page_mix_max_width-\d_page_mix_distance*\numexpr(\c_page_mix_n_of_columns-\plusone)\relax)/\c_page_mix_n_of_columns\relax % \columnwidth \d_page_mix_column_width \columndistance\d_page_mix_distance \nofcolumns \c_page_mix_n_of_columns \textwidth \d_page_mix_column_width % kind of redundant but we had it so ... % \usemixedcolumnscolorparameter\c!color % \insidecolumnstrue % new % \usealignparameter \mixedcolumnsparameter \useblankparameter \mixedcolumnsparameter \useprofileparameter\mixedcolumnsparameter % new % \nofcolumns\c_page_mix_n_of_columns} % public %D The otr method related hooks are defined next: % \setvalue{\??mixedcolumnsbefore\s!otr}% % {\par % \ifdim\pagetotal=\zeropoint \else % \verticalstrut % probably no longer needed % \vskip-\struttotal % probably no longer needed % \fi} \newcount\c_page_mix_otr_nesting % \setvalue{\??mixedcolumnsbefore\s!otr}% % {\par % \global\advance\c_page_mix_otr_nesting\plusone % \ifcase\c_page_mix_otr_nesting\or % \ifdim\pagetotal=\zeropoint \else % \obeydepth % we could handle this in pre material % \fi % \fi} \setvalue{\??mixedcolumnsbefore\s!otr}% {\par \global\advance\c_page_mix_otr_nesting\plusone \ifcase\c_page_mix_otr_nesting\or \ifdim\pagetotal=\zeropoint \else % make sure that whitespace an dblanks are done \strut \vskip-\lineheight % no, bad spacing: \obeydepth % we could handle this in pre material \fi \fi} \setvalue{\??mixedcolumnsstart\s!otr}% {\ifcase\c_page_mix_otr_nesting\or \scratchwidth\textwidth \setupoutputroutine[\s!mixedcolumn]% \c_page_mix_routine\c_page_mix_routine_intercept \page_otr_trigger_output_routine % \holdinginserts\maxdimen % \ifvoid\b_page_mix_preceding \else % moved here, before the packaging \page_postprocessors_linenumbers_deepbox\b_page_mix_preceding % we need to avoid unvboxing with successive balanced on one page \global\setbox\b_page_mix_preceding\vpack{\box\b_page_mix_preceding}% \wd\b_page_mix_preceding\scratchwidth % \makeupwidth \page_grids_add_to_one\b_page_mix_preceding \fi \global\d_page_mix_preceding_height\ht\b_page_mix_preceding \c_page_mix_routine\c_page_mix_routine_continue % \page_mix_command_set_vsize \page_mix_command_set_hsize \fi \usealignparameter\mixedcolumnsparameter \usesetupsparameter\mixedcolumnsparameter} % \setvalue{\??mixedcolumnsstop\s!otr}% % {\par % \ifcase\c_page_mix_otr_nesting\or % \c_page_mix_routine\c_page_mix_routine_balance % \page_otr_trigger_output_routine % \fi} \setvalue{\??mixedcolumnsstop\s!otr}% {\par \ifcase\c_page_mix_otr_nesting\or \doifelse{\mixedcolumnsparameter\c!balance}\v!yes {\c_page_mix_routine\c_page_mix_routine_balance}% {\penalty-\plustenthousand}% weird hack, we need to trigger the otr sometimes (new per 20140306, see balancing-001.tex) \page_otr_trigger_output_routine \ifvoid\b_page_mix_preceding \else % empty columns so we need to make sure pending content is flushed \unvbox\b_page_mix_preceding % new per 2014.10.25 \fi \fi} \setvalue{\??mixedcolumnsafter\s!otr}% {\ifcase\c_page_mix_otr_nesting\or \prevdepth\strutdp \page_otr_command_set_vsize \page_otr_command_set_hsize \fi \global\advance\c_page_mix_otr_nesting\minusone} %D The splitting and therefore balancing is done at the \LUA\ end. This gives %D more readable code and also makes it easier to deal with insertions like %D footnotes. Eventually we will have multiple strategies available. \unexpanded\def\page_mix_routine_construct#1% {\d_page_mix_max_height\mixedcolumnsparameter\c!maxheight % can have changed due to header=high \ifconditional\c_page_mix_process_notes \totalnoteheight\zeropoint \else \settotalinsertionheight \fi \clf_mixsetsplit box \b_page_mix_collected nofcolumns \c_page_mix_n_of_columns maxheight \d_page_mix_max_height noteheight \totalnoteheight step \d_page_mix_balance_step cycles \c_page_mix_balance_cycles preheight \d_page_mix_preceding_height prebox \b_page_mix_preceding strutht \strutht strutdp \strutdp threshold \d_page_mix_threshold splitmethod {\mixedcolumnsparameter\c!splitmethod}% balance {#1}% alternative {\mixedcolumnsparameter\c!alternative}% internalgrid {\mixedcolumnsparameter\c!internalgrid}% grid \ifgridsnapping tru\else fals\fi e % notes \ifconditional\c_page_mix_process_notes tru\else fals\fi e % \relax \deadcycles\zerocount} \newdimen\mixedcolumnseparatorheight \newdimen\mixedcolumnseparatordepth \newdimen\mixedcolumnseparatorwidth \def\page_mix_routine_package_step {% needs packaging anyway \setbox\scratchbox\page_mix_command_package_column \page_lines_add_numbers_to_box\scratchbox\recurselevel\c_page_mix_n_of_columns\plusone % new \page_marks_synchronize_column\plusone\c_page_mix_n_of_columns\recurselevel\scratchbox % backgrounds \anch_mark_column_box\scratchbox % for the moment a quick and dirty patch .. we need to go into the box (hence the \plusone) .. a slowdowner % moved to start: \page_lines_add_numbers_to_box\scratchbox\recurselevel\c_page_mix_n_of_columns\plusone % the framed needs a reset of strut, align, setups etc \mixedcolumnseparatorheight\ht\scratchbox \mixedcolumnseparatordepth \dp\scratchbox \inheritedmixedcolumnsframedbox\currentmixedcolumns\scratchbox} \def\page_mix_routine_package_separate {\ifcsname\??mixedcolumnsseparator\p_separator\endcsname \page_mix_command_inject_separator \else \hss \fi} \unexpanded\def\page_mix_routine_package {\clf_mixfinalize \setbox\b_page_mix_collected\vbox \bgroup \ifvoid\b_page_mix_preceding \else % \page_postprocessors_linenumbers_deepbox\b_page_mix_preceding % already done \vpack\bgroup \box\b_page_mix_preceding \egroup \global\d_page_mix_preceding_height\zeropoint \nointerlineskip % no no: % \prevdepth\strutdepth \fi \hskip\d_page_mix_leftskip \page_mix_hbox to \d_page_mix_max_width \bgroup \edef\p_separator{\mixedcolumnsparameter\c!separator}% \mixedcolumnseparatorwidth\d_page_mix_distance % \mixedcolumnsparameter\c!rulethickness\relax \edef\p_direction{\mixedcolumnsparameter\c!direction}% \ifx\p_direction\v!reverse \dostepwiserecurse\c_page_mix_n_of_columns\plusone\minusone {\page_mix_routine_package_step \ifnum\recurselevel>\plusone \page_mix_routine_package_separate \fi}% \else \dorecurse\c_page_mix_n_of_columns {\page_mix_routine_package_step \ifnum\recurselevel<\c_page_mix_n_of_columns \page_mix_routine_package_separate \fi}% \fi \egroup \hskip\d_page_mix_rightskip \egroup \wd\b_page_mix_collected\dimexpr \d_page_mix_max_width +\d_page_mix_rightskip +\d_page_mix_leftskip \relax } \unexpanded\def\page_mix_command_package_column {\page_mix_hbox to \d_page_mix_column_width \bgroup % maybe intercept empty \clf_mixgetsplit\recurselevel\relax \hskip-\d_page_mix_column_width \vbox \bgroup \hsize\d_page_mix_column_width \ifconditional\c_page_mix_process_notes \placenoteinserts \fi \egroup \hss \egroup} % \unexpanded\def\page_mix_command_package_column % {\page_mix_hbox to \d_page_mix_column_width \bgroup % % maybe intercept empty % \ruledhpack\bgroup % \clf_mixgetsplit\recurselevel\relax % \egroup % \hskip-\d_page_mix_column_width % \ruledhpack \bgroup % \hsize\d_page_mix_column_width % \ifconditional\c_page_mix_process_notes % \placenoteinserts % \fi % \egroup % \hss % \egroup} \unexpanded\def\page_mix_routine_continue {\bgroup \forgetall \dontcomplain \setbox\b_page_mix_collected\vpack{\unvbox\normalpagebox}% brrr we need to make a tight box (combine this in lua) \page_mix_routine_construct\v!no \page_mix_routine_package \page_otr_construct_and_shipout\box\b_page_mix_collected\zerocount % three arguments \clf_mixflushrest \clf_mixcleanup \egroup} \unexpanded\def\page_mix_routine_balance {\bgroup \forgetall \dontcomplain \setbox\b_page_mix_collected\vpack{\unvbox\normalpagebox}% brrr we need to make a tight box (combine this in lua) \doloop {%writestatus\m!columns{construct continue (\the\htdp\b_page_mix_collected)}% \page_mix_routine_construct\v!no \ifcase\clf_mixstate\relax % 0 = okay, we can balance \setbox\b_page_mix_collected\vpack{\clf_mixflushlist}% we could avoid this %writestatus\m!columns{construct balance}% \page_mix_routine_construct\v!yes \page_mix_routine_package \c_page_mix_routine\c_page_mix_routine_regular % \setupoutputroutine[\s!singlecolumn]% \page_otr_command_set_vsize \page_otr_command_set_hsize \par %writestatus\m!columns{flush balance}% \page_grids_add_to_mix\b_page_mix_collected % no linenumbers here \box\b_page_mix_collected \vskip\zeropoint % triggers recalculation of page stuff (weird that this is needed but it *is* needed, see mixed-001.tex) \par \nointerlineskip \prevdepth\strutdp \clf_mixflushrest% rubish \clf_mixcleanup % rubish \exitloop \or % 1 = we have stuff left, so flush and rebalance %writestatus\m!columns{flush continue}% \page_mix_routine_package \page_otr_construct_and_shipout\box\b_page_mix_collected\zerocount % three arguments \setbox\b_page_mix_collected\vpack{\clf_mixflushrest}% we could avoid this \clf_mixcleanup \ifdim\ht\b_page_mix_collected=\zeropoint \exitloop \fi \fi}% \egroup} %D We also implement a variant compatible with the so called simple columns %D mechanism: %D %D \starttyping %D \startboxedcolumns %D \input zapf %D \stopboxedcolumns %D \stoptyping %D %D This is a rather mininimalistic variant. % Maybe we also need a variant with obeydepth before and prevdepth after so % that we get a nice spacing. \definemixedcolumns [boxedcolumns] [\c!balance=\v!yes, \c!n=2, \c!method=\s!box, \c!strut=\v!yes, \c!maxwidth=\availablehsize] %D Boxed columns can be used nested: %D %D \starttyping %D \setupmixedcolumns %D [boxedcolumns] %D [n=2, %D background=color, %D backgroundcolor=darkred, %D color=white, %D backgroundoffset=1mm] %D %D \definemixedcolumns %D [nestedboxedcolumns] %D [boxedcolumns] %D [n=2, %D background=color, %D backgroundcolor=white, %D color=darkred, %D strut=yes, %D backgroundoffset=0mm] %D %D \startboxedcolumns %D \input zapf \par \input ward \par \obeydepth %D \startnestedboxedcolumns %D \input zapf %D \stopnestedboxedcolumns %D \par \input zapf \par \obeydepth %D \startnestedboxedcolumns %D \input zapf %D \stopnestedboxedcolumns %D \par \input zapf %D \stopboxedcolumns %D \stoptyping %D Next we define the hooks: \letvalue{\??mixedcolumnsbefore\s!box}\donothing \letvalue{\??mixedcolumnsafter \s!box}\donothing \setvalue{\??mixedcolumnsstart\s!box}% {\edef\p_page_mix_strut{\mixedcolumnsparameter\c!strut}% \setbox\b_page_mix_collected\vbox \bgroup \let\currentoutputroutine\s!mixedcolumn % makes \column work \forgetall \usegridparameter\mixedcolumnsparameter % \useprofileparameter\mixedcolumnsparameter \page_mix_command_set_hsize \ifx\p_page_mix_strut\v!yes \begstrut \ignorespaces \fi} \setvalue{\??mixedcolumnsstop\s!box}% {\ifx\p_page_mix_strut\v!yes \removeunwantedspaces \endstrut \fi \egroup \edef\p_profile{\mixedcolumnsparameter\c!profile}% \ifx\p_profile\empty \else % this can never be ok because we cheat with depth and height % and glue in between and when we're too large we run into issues % so mayb best limit correction to one line \profilegivenbox\p_profile\b_page_mix_collected \setbox\b_page_mix_collected\vpack{\unvbox\b_page_mix_collected}% % tracing % \addprofiletobox\b_page_mix_collected \fi \page_mix_box_balance} %D The related balancer is only a few lines: \unexpanded\def\page_mix_box_balance {\bgroup \dontcomplain \page_mix_routine_construct\v!yes \page_mix_routine_package \dontleavehmode\box\b_page_mix_collected \clf_mixflushrest \clf_mixcleanup \egroup} %D As usual, floats complicates matters and this is where experimental code %D starts. \let\page_mix_command_package_contents\page_one_command_package_contents \let\page_mix_command_flush_float_box \page_one_command_flush_float_box \unexpanded\def\page_mix_command_check_if_float_fits {\ifpostponecolumnfloats \global\setfalse\c_page_floats_room \else\ifconditional\c_page_floats_not_permitted \global\setfalse\c_page_floats_room \else % \bgroup % \getcolumnstatus{\count255}{\dimen0}{\dimen2}% % \page_floats_get_info\s!text % \setbox\scratchbox\vbox % tricky met objecten ? % {\blank[\rootfloatparameter\c!spacebefore] % \snaptogrid\vbox{\vskip\floatheight}}% copy? % \advance\dimen0\dimexpr\ht\scratchbox+2\openlineheight+.5\lineheight\relax\relax % needed because goal a bit higher % \ifdim\dimen0>\dimen2 % \global\setfalse\c_page_floats_room % \else \global\settrue\c_page_floats_room \fi\fi \ifdim\floatwidth>\hsize \showmessage\m!columns{11}\empty \global\setfalse\c_page_floats_room \fi} \unexpanded\def\page_mix_command_flush_floats {\page_one_command_flush_floats} \unexpanded\def\page_mix_command_flush_saved_floats {\page_one_command_flush_saved_floats} % \unexpanded\def\page_mix_command_flush_top_insertions % {\page_one_command_flush_top_insertions} \unexpanded\def\page_mix_place_float_top {\showmessage\m!columns4\empty\page_one_place_float_here} \unexpanded\def\page_mix_place_float_bottom {\showmessage\m!columns5\empty\page_one_place_float_here} \unexpanded\def\page_mix_place_float_here {\page_one_place_float_here} \unexpanded\def\page_mix_place_float_force {\page_one_place_float_force} \unexpanded\def\page_mix_command_side_float_output {\page_otr_construct_and_shipout\unvbox\normalpagebox\zerocount} % three arguments \unexpanded\def\page_mix_command_synchronize_side_floats {\page_sides_forget_floats} \unexpanded\def\page_mix_command_flush_side_floats {\page_sides_forget_floats} \unexpanded\def\page_mix_command_next_page {\page_otr_eject_page} \unexpanded\def\page_mix_command_next_page_and_inserts {\page_otr_eject_page_and_flush_inserts} %D Moved here and dedicated: \unexpanded\def\page_mix_command_test_column {\dodoubleempty\page_mix_command_test_column_indeed} \unexpanded\def\page_mix_command_test_column_indeed[#1][#2]% works on last column {\par \begingroup \scratchdimen\dimexpr#1\lineheight\ifsecondargument+#2\fi\relax \ifdim\scratchdimen>\zeropoint \attribute\checkedbreakattribute\number\scratchdimen \penalty\c_page_mix_break_forced\relax \fi \endgroup} %D We need to hook some handlers into the output routine and we define %D a dedicated one: \let\page_mix_command_flush_all_floats\page_one_command_flush_all_floats \defineoutputroutine [\s!mixedcolumn] [\s!page_otr_command_routine =\page_mix_command_routine, \s!page_otr_command_package_contents =\page_mix_command_package_contents, \s!page_otr_command_set_vsize =\page_mix_command_set_vsize, \s!page_otr_command_set_hsize =\page_mix_command_set_hsize, % \s!page_otr_command_synchronize_hsize =\page_mix_command_synchronize_hsize, \s!page_otr_command_next_page =\page_mix_command_next_page, \s!page_otr_command_next_page_and_inserts =\page_mix_command_next_page_and_inserts, % \s!page_otr_command_set_top_insertions =\page_mix_command_set_top_insertions, % \s!page_otr_command_set_bottom_insertions =\page_mix_command_set_bottom_insertions, % \s!page_otr_command_flush_top_insertions =\page_mix_command_flush_top_insertions, % \s!page_otr_command_flush_bottom_insertions=\page_mix_command_flush_bottom_insertions, \s!page_otr_command_check_if_float_fits =\page_mix_command_check_if_float_fits, % \s!page_otr_command_set_float_hsize =\page_mix_command_set_float_hsize, \s!page_otr_command_flush_float_box =\page_mix_command_flush_float_box, \s!page_otr_command_side_float_output =\page_mix_command_side_float_output, \s!page_otr_command_synchronize_side_floats=\page_mix_command_synchronize_side_floats, \s!page_otr_command_flush_floats =\page_mix_command_flush_floats, \s!page_otr_command_flush_side_floats =\page_mix_command_flush_side_floats, \s!page_otr_command_flush_saved_floats =\page_mix_command_flush_saved_floats, \s!page_otr_command_flush_all_floats =\page_mix_command_flush_all_floats, % \s!page_otr_command_flush_margin_blocks =\page_mix_command_flush_margin_blocks, % not used \s!page_otr_command_test_column =\page_mix_command_test_column ] %D Only a few float placement options are supported: \installfloatmethod \s!mixedcolumn \v!here \page_mix_place_float_here \installfloatmethod \s!mixedcolumn \v!force \page_mix_place_float_force \installfloatmethod \s!mixedcolumn \v!top \page_mix_place_float_top \installfloatmethod \s!mixedcolumn \v!bottom \page_mix_place_float_bottom \installfloatmethod \s!mixedcolumn \v!local \somelocalfloat %D It ends here. \protect \endinput