%% This is part of the OpTeX project, see http://petr.olsak.net/optex

\_codedecl \begmulti {Balanced columns <2024-10-31>} % preloaded in format

   \_doc -----------------------------
   \`\_betweencolumns` or \`\_leftofcolumns` or \`\_rightofcolumns` include
   a material printed between columns or left of all columns or right of all
   columns respectively. The \^`\_betweencolumns` must include a
   stretchability or a material with exactly \^`\colsep` width. You can
   redefine these macros. For example the rule between columns can be reached
   by `\_def\_betweencolumns{\hss\vrule\hss}`.\nl
   \`\_multiskip` puts its material at the start and at the end of
   \^`\begmulti`...\^`\endmulti`.
   \_cod -----------------------------

\_def\_betweencolumns{\_hss} \_def\_leftofcolumns{} \_def\_rightofcolumns{}
\_def\_multiskip{\_medskip}   % space above and below \begmulti...\endmulti

   \_doc -----------------------------
   The code used here is documented in detail in the \"\TeX/book naruby", pages 244--246,
   free available, \url{http://petr.olsak.net/tbn.html}, but in Czech.
   Roughly speaking, macros complete all material between
   \`\begmulti``<num-columns>` and \`\endmulti`
   into one `\vbox 6`. Then the macro measures the amount of free space at the current
   page using `\pagegoal` and `\pagtotal` and does `\vsplit` of `\vbox 6` to
   columns with a height of such free space. This is done only if we have
   enough amount of material in `\vbox 6` to fill the full page by columns.
   This is repeated in a loop until we have less amount of material in `\vbox 6`.
   Then we run \`\_balancecolumns` which balances the last part of the columns.
   Each part of printed material is distributed to the main vertical list as
   `\hbox{<columns>}` and we need not do any change in the output routine.

   If you have paragraphs in \^`\begmulti`... \^`\endmulti` environment then
   you may say `\raggedright` inside this environment and you can re-assign
   `\widowpenalty` and `\clubppenalty` (they are set to 10000 in \OpTeX/).
   \_cod -----------------------------

\_def\_begmulti #1 {\_par\_bgroup\_wipeepar
   \_ifnum\_lastpenalty>10000 \_vskip4.5\_baselineskip\_penalty9999 \_vskip-4.5\_baselineskip \_fi
   \_multiskip \_def\_Ncols{#1}
   \_setbox6=\_vbox\_bgroup\_bgroup \_let\_setxhsize=\_relax \_penalty-99
   %% \hsize := column width = (\hsize+\colsep) / n - \colsep
   \_setbox0=\_hbox{\_leftofcolumns\_rightofcolumns}%
   \_advance\_hsize by-\_wd0 \_advance\_hsize by\_colsep
   \_divide\_hsize by\_Ncols  \_advance\_hsize by-\_colsep
}
\_def\_endmulti{\_vskip-\_prevdepth\_vfil
   \_ea\_egroup\_ea\_egroup\_ea\_baselineskip\_the\_baselineskip\_relax
   \_dimen0=.8\_maxdimen \_tmpnum=\_dimen0 \_divide\_tmpnum by\_baselineskip
   \_splittopskip=\_baselineskip
   \_setbox1=\_vsplit6 to0pt % initialize first \splittopskip in \box6
   %% \dimen1 := the free space on the page
   \_penalty0 % initialize \_pageoal
   \_ifdim\_pagegoal=\_maxdimen \_setcolsize\_vsize
   \_else \_setcolsize{\_dimexpr\_pagegoal-\_pagetotal}\_fi
   \_ifdim \_dimen1<2\_baselineskip \_vfil\_break \_setcolsize\_vsize \_fi
   \_setsplitsize % sets \_dimen0 = \_ht6
   \_divide\_dimen0 by\_Ncols \_relax
   %% split the material to more pages?
   \_ifdim \_dimen0>\_dimen1 \_splitpart
   \_else \_balancecolumns \_fi  % only balancing
   \_multiskip \_egroup
}

   \_doc -----------------------------
   Splitting columns...
   \_cod -----------------------------

\_def\_makecolumns{\_bgroup % full page, destination height: \dimen1
   \_vbadness=20000 \_splitmaxdepth=\_maxdepth \_dimen6=\_wd6
   \_createcolumns
   \_printcolumns
   \_egroup
}
\_def\_splitpart{%
   \_makecolumns % full page
   \_vskip 0pt plus 1fil minus\_baselineskip \_break
   \_setsplitsize % sets \_dimen0 = \_ht6
   \_divide\_dimen0 by\_Ncols \_relax
   \_ifx\_balancecolumns\_flushcolumns \_advance\_dimen0 by-.5\_vsize \_fi
   \_setcolsize\_vsize \_dimen2=\_dimen1
   \_advance\_dimen2 by-\_baselineskip
   %% split the material to more pages?
   \_ifvoid6 \_else
      \_ifdim \_dimen0>\_dimen2 \_ea\_ea\_ea \_splitpart
      \_else \_balancecolumns % last balancing
   \_fi \_fi
}

   \_doc -----------------------------
   Final balancing of the columns.
   \_cod -----------------------------

\_def\_balancecolumns{\_bgroup \_setbox7=\_copy6 % destination height: \dimen0
   \_ifdim\_dimen0>\_baselineskip \_else \_dimen0=\_baselineskip \_fi
   \_vbadness=20000 \_dimen6=\_wd6 \_dimen1=\_dimen0
   \_def\_trybalance{\_createcolumns
      \_ifvoid6 \_else
         \_advance \_dimen1 by.2\_baselineskip
         \_setbox6=\_copy7
         \_ea \_trybalance \_fi}\_trybalance
   \_printcolumns
   \_egroup
}

   \_doc -----------------------------
   \`\_setcolsize``<dimen>` sets initial value `\dimen1=<size>` which is
   used as height of columns at given page.
   The correction `\splittopskip`$-$`\topskip` is done if the columns start
   at the top of the page.\nl
   \`\_createcolumns` prepares columns with given height `\dimen1` side
   by side to the `\box1`.\nl
   \`\_printcolumns` prints the columns prepared in `\box1`.
   The first `\hbox{}` moves typesetting point to the next baseline.
   Next negative skip ensures that the first line from
   split columns is at this position.
   \_cod -----------------------------

\_def\_setcolsize #1{\_dimen1=#1\_relax
   \_ifdim\_dimen1=\_vsize
      \_advance \_dimen1 by \_splittopskip \_advance \_dimen1 by-\_topskip \_fi
}
\_def\_createcolumns{%
   \_setbox1=\_hbox{\_leftofcolumns}\_tmpnum=0
   \_loop \_ifnum\_Ncols>\_tmpnum
      \_advance\_tmpnum by1
      \_setbox1=\_hbox{\_unhbox1
         \_ifvoid6 \_hbox to\_dimen6{\_hss}\_else \_vsplit6 to\_dimen1 \_fi
         \_ifnum\_Ncols=\_tmpnum \_rightofcolumns \_else \_betweencolumns \_fi}%
   \_repeat
}
\_def\_printcolumns{%
   \_hbox{}\_nobreak\_vskip-\_splittopskip \_nointerlineskip
   \_hbox to\_hsize{\_unhbox1}%
}
\_public \begmulti \endmulti ;

   \_doc -----------------------------
   The accumulated `\box6` (before its splitting to pages and columns) can
   be very large and we cannot measure it with classical \TeX/ methods because
   we can get \"dimension too lagre" error or arithmetic overflow.
   The \TeX/ limit for dimensions is about 5.75\,m. For example,
   the `\box6` of the index in this document has about 6.5\,m height.

   The \`\_rawht``<box-num>` macro solves this problem.
   It expands to the height plus depth of
   given `\vbox` as an integer in points (i.e. the the fraction part of points is
   stripped and unit isn't appended). The limit $2^{31}$ of such an integer
   allows us to measure boxes with many kilometers height.

   The trick used in the \`\_setsplitsize` macro sets `\dimen0` to the maximum of
   `.8\maxdimen` and actual `\ht6`, because 13100 is `.8\maxdimen` in points.
   This is sufficient because if the `\dimen0` is big then we get $n$
   columns for full page. This is not the last page where exact calculations
   for balancing is needed.
   \_cod -----------------------------

\_def\_setsplitsize{%
   \_ifnum \_rawht6 > 13100 \_dimen0 = .8\_maxdimen
   \_else \_dimen0=\_ht6 \_def\_rawht6{0}\_fi
}
\_def\_rawht{\_directlua{optex.raw_ht()}}

\_endcode % -------------------------------------

2024-10-31 \splitmaxdepth set, bug fixed
2024-09-06 \_rawht implemented, more robust measurement of big boxes
2022-11-26 \at least three lines in the beginning of \begmulti at the page
2022-05-05 `\_betweencolumns` etc. introduced.
2021-05-20 Colors inside \begmulti...\endmuti, bug fixed
2020-03-26 Introduced
