summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/page-mix.mkxl
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/mkiv/page-mix.mkxl')
-rw-r--r--tex/context/base/mkiv/page-mix.mkxl1092
1 files changed, 1092 insertions, 0 deletions
diff --git a/tex/context/base/mkiv/page-mix.mkxl b/tex/context/base/mkiv/page-mix.mkxl
new file mode 100644
index 000000000..20f4ff3ca
--- /dev/null
+++ b/tex/context/base/mkiv/page-mix.mkxl
@@ -0,0 +1,1092 @@
+%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
+\newconditional\c_page_mix_grid_snapping
+
+%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]
+%
+% \protected\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:
+
+\protected\def\strc_itemgroups_start_columns
+ {\startmixedcolumns[\s!itemgroupcolumns]} % we could have a fast one
+
+\protected\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.
+
+\protected\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_construct_and_shipout#1#2#3%
+ {\ifconditional\c_page_mix_grid_snapping\else\gridsnappingfalse\fi % maybe only for notes (bottom alignment)
+ \page_otr_construct_and_shipout#1#2#3%
+ \ifconditional\c_page_mix_grid_snapping \gridsnappingtrue \fi}
+
+
+\def\page_mix_routine_error
+ {\showmessage\m!columns3\empty
+ \page_mix_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.
+
+\protected\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.
+
+\protected\def\page_mix_command_set_vsize
+ {\vsize\dimexpr\c_page_mix_n_of_columns\textheight+\c_page_mix_n_of_columns\lineheight\relax
+ \pagegoal\dimexpr
+ \vsize
+% -\d_page_floats_inserted_top % needs checking
+% -\d_page_floats_inserted_bottom % needs checking
+ -\c_page_mix_n_of_columns\insertheights
+ \relax}
+
+%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}%
+ \setfalse\c_page_mix_grid_snapping
+ \ifempty\p_grid
+ % just follow the default grid settings
+ \else
+ \ifgridsnapping\settrue\c_page_mix_grid_snapping\fi
+ \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}
+
+\protected\def\installmixedcolumnseparator#1#2%
+ {\setvalue{\??mixedcolumnsseparator#1}{#2}}
+
+\installmixedcolumnseparator\v!rule
+ {\vrule
+ \s!width \mixedcolumnsparameter\c!rulethickness
+ \s!height\mixedcolumnseparatorheight
+ \s!depth \mixedcolumnseparatordepth
+ \relax}
+
+\protected\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
+
+\protected\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%
+ \orelse\ifinsidecolumns
+ \doubleexpandafter#2%
+ \else
+ \doubleexpandafter#1%
+ \fi}
+
+\protected\def\page_mix_start_columns
+ {\push_macro_currentmixedcolumns
+ \push_macro_currentmixedcolumnsmethod
+ \ifsecondargument
+ \singleexpandafter\page_mix_start_columns_a
+ \orelse\iffirstargument
+ \doubleexpandafter\page_mix_start_columns_b
+ \else
+ \doubleexpandafter\page_mix_start_columns_c
+ \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}
+
+\protected\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}
+
+\protected\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}
+
+\protected\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}
+
+% \protected\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:
+
+\protected\def\strc_itemgroups_start_columns
+ {\page_mix_fast_columns_start\s!itemgroupcolumns}
+
+\protected\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.
+
+\protected\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\recurselevel
+ % 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}
+
+\protected\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 }
+
+\protected\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}
+
+% \protected\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}
+
+\protected\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_mix_construct_and_shipout\box\b_page_mix_collected\zerocount % three arguments
+ \clf_mixflushrest
+ \clf_mixcleanup
+ \egroup}
+
+\protected\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_mix_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}%
+ \ifempty\p_profile \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:
+
+\protected\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
+
+\protected\def\page_mix_command_check_if_float_fits
+ {\ifpostponecolumnfloats
+ \global\setfalse\c_page_floats_room
+ \orelse\ifconditional\c_page_floats_not_permitted
+ \global\setfalse\c_page_floats_room
+ \else
+% \bgroup
+% \getcolumnstatus{\count255}{\dimen0}{\dimen2}%
+% \page_floats_get_info\s!text
+% \setbox\scratchbox\vbox % tricky met objecten ?
+% {\blank[\rootfloatparameter\c!spacebefore]
+% \snaptogrid\vbox{\vskip\floatheight}}% copy?
+% \advance\dimen0\dimexpr\ht\scratchbox+2\openlineheight+.5\lineheight\relax\relax % needed because goal a bit higher
+% \ifdim\dimen0>\dimen2
+% \global\setfalse\c_page_floats_room
+% \else
+ \global\settrue\c_page_floats_room
+ \fi
+ \ifdim\floatwidth>\hsize
+ \showmessage\m!columns{11}\empty
+ \global\setfalse\c_page_floats_room
+ \fi}
+
+\protected\def\page_mix_command_flush_floats
+ {\page_one_command_flush_floats}
+
+\protected\def\page_mix_command_flush_saved_floats
+ {\page_one_command_flush_saved_floats}
+
+% \protected\def\page_mix_command_flush_top_insertions
+% {\page_one_command_flush_top_insertions}
+
+\protected\def\page_mix_place_float_top
+ {\showmessage\m!columns4\empty\page_one_place_float_here}
+
+\protected\def\page_mix_place_float_bottom
+ {\showmessage\m!columns5\empty\page_one_place_float_here}
+
+\protected\def\page_mix_place_float_here
+ {\page_one_place_float_here}
+
+\protected\def\page_mix_place_float_force
+ {\page_one_place_float_force}
+
+\protected\def\page_mix_command_side_float_output
+ {\page_mix_construct_and_shipout\unvbox\normalpagebox\zerocount} % three arguments
+
+\protected\def\page_mix_command_synchronize_side_floats
+ {\page_sides_forget_floats}
+
+\protected\def\page_mix_command_flush_side_floats
+ {\page_sides_forget_floats}
+
+\protected\def\page_mix_command_next_page
+ {\page_otr_eject_page}
+
+\protected\def\page_mix_command_next_page_and_inserts
+ {\page_otr_eject_page_and_flush_inserts}
+
+%D Moved here and dedicated:
+
+\protected\def\page_mix_command_test_column
+ {\dodoubleempty\page_mix_command_test_column_indeed}
+
+\protected\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
+ \c_attr_checkedbreak\number\scratchdimen % why \number
+ \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