summaryrefslogtreecommitdiff
path: root/tex/context/base/page-mix.mkiv
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/page-mix.mkiv')
-rw-r--r--tex/context/base/page-mix.mkiv771
1 files changed, 771 insertions, 0 deletions
diff --git a/tex/context/base/page-mix.mkiv b/tex/context/base/page-mix.mkiv
new file mode 100644
index 000000000..a4ef619bb
--- /dev/null
+++ b/tex/context/base/page-mix.mkiv
@@ -0,0 +1,771 @@
+%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
+
+% luatex buglet:
+%
+% \ctxlua{tex.setbox("global",0,node.hpack(nodes.pool.glyph("a",font.current())))}\box0
+
+\registerctxluafile{page-mix}{1.001}
+
+\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 grid. 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}
+
+\def\s!mixedcolumn{mixedcolumn}
+
+\installframedcommandhandler \??mixedcolumns {mixedcolumns} \??mixedcolumns
+
+\setupmixedcolumns
+ [\c!distance=1.5\bodyfontsize,
+ \c!n=\plustwo,
+ %\c!rule=\v!none,
+ \c!frame=\v!off,
+ \c!strut=\v!no,
+ \c!offset=\v!overlay,
+ \c!alternative=\v!local,
+ \c!maxheight=\textheight,
+ \c!maxwidth=\makeupwidth,
+ \c!step=.25\lineheight, % needs some experimenting
+ \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
+ \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
+
+%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!rule=\v!off,
+ \c!balance=\v!yes]
+
+\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
+ {\global\setbox\b_page_mix_preceding\vbox
+ {\page_otr_command_flush_top_insertions
+ \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}
+
+%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}
+
+%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
+ {\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
+ {\gridsnappingtrue
+ \setsystemmode\v!grid
+ \spac_grids_snap_value_set\v!yes}
+
+%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}
+
+\setvalue{\??mixedcolumnsseparator\v!rule}%
+ {\starttextproperties
+ \usemixedcolumnscolorparameter\c!rulecolor
+ \vrule\s!width\mixedcolumnsparameter\c!rulethickness
+ \stoptextproperties}
+
+\unexpanded\def\page_mix_command_inject_separator
+ {\bgroup
+ \hss
+ \csname\??mixedcolumnsseparator\mixedcolumnsparameter\c!separator\endcsname
+ \hss
+ \egroup}
+
+%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
+
+\unexpanded\def\startmixedcolumns
+ {\dodoubleempty\page_mix_start_columns}
+
+\unexpanded\def\page_mix_start_columns
+ {\pushmacro\currentmixedcolumns
+ \pushmacro\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}%
+ \edef\currentmixedcolumnsmethod{\mixedcolumnsparameter\c!method}%
+ \mixedcolumnsparameter\c!before\relax
+ \csname\??mixedcolumnsbefore\currentmixedcolumnsmethod\endcsname\relax
+ \begingroup
+ \setupcurrentmixedcolumns[#2]%
+ \page_mix_initialize_columns
+ \csname\??mixedcolumnsstart\currentmixedcolumnsmethod\endcsname}
+
+\def\page_mix_start_columns_b[#1][#2]%
+ {\doifassignmentelse{#1}%
+ {\let\currentmixedcolumns\empty
+ \page_mix_error_b}
+ {\edef\currentmixedcolumns{#1}%
+ \firstargumentfalse}%
+ \edef\currentmixedcolumnsmethod{\mixedcolumnsparameter\c!method}%
+ \mixedcolumnsparameter\c!before\relax % so, it doesn't list to local settings !
+ \csname\??mixedcolumnsbefore\currentmixedcolumnsmethod\endcsname\relax
+ \begingroup
+ \iffirstargument
+ \setupcurrentmixedcolumns[#1]%
+ \fi
+ \page_mix_initialize_columns
+ \csname\??mixedcolumnsstart\currentmixedcolumnsmethod\endcsname} % no \relax
+
+\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
+ \edef\currentmixedcolumnsmethod{\mixedcolumnsparameter\c!method}%
+ \mixedcolumnsparameter\c!before\relax
+ \csname\??mixedcolumnsbefore\currentmixedcolumnsmethod\endcsname\relax
+ \begingroup
+ \page_mix_initialize_columns
+ \csname\??mixedcolumnsstart\currentmixedcolumnsmethod\endcsname}
+
+\unexpanded\def\page_mix_fast_columns_start#1%
+ {\pushmacro\currentmixedcolumns
+ \pushmacro\currentmixedcolumnsmethod
+ \edef\currentmixedcolumns{#1}%
+ \edef\currentmixedcolumnsmethod{\mixedcolumnsparameter\c!method}%
+ \mixedcolumnsparameter\c!before\relax % so, it doesn't list to local settings !
+ \csname\??mixedcolumnsbefore\currentmixedcolumnsmethod\endcsname\relax
+ \begingroup
+ \page_mix_initialize_columns
+ \csname\??mixedcolumnsstart\currentmixedcolumnsmethod\endcsname} % no \relax
+
+%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.
+
+\unexpanded\def\stopmixedcolumns
+ {\csname\??mixedcolumnsstop \currentmixedcolumnsmethod\endcsname % no \relax
+ \endgroup
+ \csname\??mixedcolumnsafter\currentmixedcolumnsmethod\endcsname\relax
+ \mixedcolumnsparameter\c!after\relax
+ \popmacro\currentmixedcolumnsmethod
+ \popmacro\currentmixedcolumns}
+
+% \unexpanded\def\stopmixedcolumns
+% {\csname\??mixedcolumnsstop \currentmixedcolumnsmethod\endcsname % no \relax
+% \endgroup
+% \csname\??mixedcolumnsafter\currentmixedcolumnsmethod\endcsname\relax
+% \mixedcolumnsparameter\c!after\relax
+% \ifx\currentmixedcolumnsmethod\s!otr
+% \popmacro\currentmixedcolumnsmethod
+% \popmacro\currentmixedcolumns
+% \synchronizeoutput % brrr, otherwise sometimes issues in itemize
+% \else
+% \popmacro\currentmixedcolumnsmethod
+% \popmacro\currentmixedcolumns
+% \fi
+% }
+
+\let\page_mix_fast_columns_stop\stopmixedcolumns
+
+%D This is how the fast one is used:
+
+\unexpanded\def\strc_itemgroups_start_columns
+ {\page_mix_fast_columns_start\s!itemgroupcolumns}
+
+\let\strc_itemgroups_stop_columns\page_mix_fast_columns_stop
+
+%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
+ %
+ \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
+ %
+ \usemixedcolumnscolorparameter\c!color
+ %
+ \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{\??mixedcolumnsstart\s!otr}%
+ {\ifcase\c_page_mix_otr_nesting\or
+ \setupoutputroutine[\s!mixedcolumn]%
+ \c_page_mix_routine\c_page_mix_routine_intercept
+ \page_otr_trigger_output_routine
+ %
+ \holdinginserts\maxdimen
+ %
+ \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}
+
+\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{\??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%
+ {\ctxcommand{mixsetsplit {
+ box = \number\b_page_mix_collected,
+ nofcolumns = \number\c_page_mix_n_of_columns,
+ maxheight = \number\d_page_mix_max_height,
+ step = \number\d_page_mix_balance_step,
+ cycles = \number\c_page_mix_balance_cycles,
+ preheight = \number\d_page_mix_preceding_height,
+ prebox = \number\b_page_mix_preceding,
+ strutht = \number\strutht,
+ strutdp = \number\strutdp,
+ threshold = \number\d_page_mix_threshold,
+ balance = "#1",
+ alternative = "\mixedcolumnsparameter\c!alternative",
+ }}%
+ \deadcycles\zerocount}
+
+\unexpanded\def\page_mix_routine_package
+ {\ctxcommand{mixfinalize()}%
+ \setbox\b_page_mix_collected\vbox \bgroup
+ \ifvoid\b_page_mix_preceding \else
+ \box\b_page_mix_preceding
+ \global\d_page_mix_preceding_height\zeropoint
+ \nointerlineskip
+ \fi
+ \hskip\d_page_mix_leftskip
+ \page_mix_hbox to \d_page_mix_max_width \bgroup
+ \letmixedcolumnsparameter\c!strut\v!no
+ % maybe use \c_page_mix_used_of_columns
+ \dorecurse\c_page_mix_n_of_columns {%
+ \inheritedmixedcolumnsframed{\page_mix_command_package_column}%
+ \ifnum\recurselevel<\c_page_mix_n_of_columns
+ \page_mix_command_inject_separator
+ \fi
+ }%
+ \egroup
+ \egroup}
+
+\unexpanded\def\page_mix_command_package_column
+ {\page_mix_hbox to \d_page_mix_column_width \bgroup
+ % maybe intercept empty
+ \ctxcommand{mixgetsplit(\recurselevel)}%
+ \hskip-\d_page_mix_column_width
+ \page_mix_hbox to \d_page_mix_column_width \bgroup
+ \placenoteinserts
+ \hss
+ \egroup
+ \egroup}
+
+\unexpanded\def\page_mix_routine_continue
+ {\bgroup
+ \forgetall
+ \dontcomplain
+ \setbox\b_page_mix_collected\vbox{\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
+ \ctxcommand{mixflushrest()}%
+ \ctxcommand{mixcleanup()}%
+ \egroup}
+
+\unexpanded\def\page_mix_routine_balance
+ {\bgroup
+ \forgetall
+ \dontcomplain
+ \setbox\b_page_mix_collected\vbox{\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\ctxcommand{mixstate()}\relax
+ % 0 = okay, we can balance
+ \setbox\b_page_mix_collected\vbox{\ctxcommand{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}%
+ \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
+ \ctxcommand{mixflushrest()}% rubish
+ \ctxcommand{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
+ \setbox\b_page_mix_collected\vbox{\ctxcommand{mixflushrest()}}% we could avoid this
+ \ctxcommand{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
+ \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
+ \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
+ \ctxcommand{mixflushrest()}%
+ \ctxcommand{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
+ \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 % hm
+ {\page_otr_construct_and_shipout\unvbox\normalpagebox}
+
+\unexpanded\def\page_mix_command_synchronize_side_floats % hm
+ {\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 We need to hook some handlers into the output routine and we define
+%D a dedicated one:
+
+\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_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_synchronize_hsize =\page_mix_command_synchronize_hsize,
+ % \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_set_float_hsize =\page_mix_command_set_float_hsize,
+ \s!page_otr_command_check_if_float_fits =\page_mix_command_check_if_float_fits,
+ \s!page_otr_command_flush_float_box =\page_mix_command_flush_float_box,
+ \s!page_otr_command_synchronize_side_floats=\page_mix_command_synchronize_side_floats,
+ \s!page_otr_command_side_float_output =\page_mix_command_side_float_output,
+ \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_margin_blocks =\page_mix_command_flush_margin_blocks, % not used
+ ]
+
+%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