背景

背景

背景

使用 LaTeX 转义的用户生成内容自动创建书籍。

问题

本书的版式采用多栏布局,将书的内容分成几部分。如果第二部分太长,则无论第一部分之后还有多少空间,都会将其全部写在下一页上。

问题

如何强制multicols包仅在第一列中跳转到下一列,但不是如果在第二列内则中断?

输出

下图说明了问题的目的;第二部分应该尽可能填满第一页第二列的空间;第二部分中不适合的部分将溢出到第二页:

填充空间

代码

重现该问题的一个最小示例:

\documentclass{article}
\usepackage{lipsum}
\usepackage{multicol}

\raggedcolumns

\begin{document}

\begin{multicols*}{2}
\section{First}
\lipsum[1-3]

% Insert column break ONLY IF still within 1st column (e.g. if \lipsum[1-2] above).
% DO NOT insert column break if already in 2nd column (e.g. if \lipsum[1-3] above).
\columnbreak

\section{Second}
\lipsum[4-5]
\end{multicols*}

\end{document}

尝试

我尝试使用multicols*,但\raggedcolumns无济于事。我以为这\maxbalancingoverflow会有所帮助,但却无法解析它。

有关的

我发现一些问题很有用,但没有提供任何简单的解决方案:

答案1

相关问题的答案检测多列中的当前列应该已经解决了这个问题,因为它被编写来检测你位于哪一列。所以我的第一个猜测是

\usepackage{mccolaction}
...
\docolaction{\columnbreak}{}{}

应该可以解决问题。

不幸的是,事实并非如此,因为我写\docolaction决定我们在哪一列的点的方式是直接下列的执行的参数。因此,\columnbreak上面的执行会将其从第一列移到第二列 :-( 因为在某些情况下,这种顺序是正确的,所以不能说它在实施时是错误的。

因此,要么需要重新定义\docolaction,要么做一点重新编程,如下所示:

\newif\ifinlast
\def\columnbreakunlesslast{%
    \docolaction{\typeout{in first column}\inlastfalse}%
                           {\typeout{in middle column}\inlastfalse}%
                           {\typeout{in last column}\inlasttrue}%
    \unless \ifinlast \columnbreak \fi
 }

像这样实现,它也适用于 3 列或更多列。例如使用\lipsum[1-3]我们得到

在此处输入图片描述

并且\lipsum[1-2] 发生列中断:

在此处输入图片描述

当然,此代码也适用于不同数量的列,包括 OP 问题中的 2 个列。我仅给出 3 列的示例,以表明它不限于两列的情况,并且可以利用multicols 的能力来生成更多列。

作为参考,这里是使用该包的完整代码mccolaction(该版本可与 SVN 中的开发版本和 multicol 的 CTAN 版本兼容):

\begin{filecontents}{mccolaction.sty}
%    \begin{macrocode}
\ProvidesPackage{mccolaction}
          [2013/07/07 v0.9c  column actions for multicolumn formatting (FMi)]
%    \end{macrocode}
%
%    \begin{macrocode}
\RequirePackage{etoolbox}
\RequirePackage{multicol}[2011/06/27]
%    \end{macrocode}
%
%   Determining the current column in multicols is difficult because
%   (in contrast to the twocolumn mode of standard LaTeX) the
%   multicols columns are determined very late in the game and due to
%   the balancing routine it is not known where a piece of text is
%   going to end up at the time the text is typeset. Only afterwards,
%   when everything has be typeset into a single long galley, that
%   galley is split into individual columns (at the very end in a
%   possibly huge set of trials to balance the column material.
%
%   Therefore the approach taken here is to write out a single line
%   into the .aux file whenever a column is finally typeset:
%\begin{verbatim}
% \mc@col@status{<number>}
%\end{verbatim}
%   The number in the argument denotes the different kind of column: 1
%   for left column 2 for any middle column and 3 for the final column.
%
%   We only set this up for the LR typesetting case here, something
%   similar could be done for the RL version:
%    \begin{macrocode}
%
% there has been a code change after 2011/06 that never made it to CTAN
% but only into the SVN repository
% so we need to provide two different versions:
\@ifpackagelater{multicol}{2011/06/28}
{%
\patchcmd{\LR@column@boxes}{\box\count@}%
         {\protected@write\@auxout{}{\string\mc@col@status
              {\ifmc@firstcol 1\else 2\fi}}%
          \mc@firstcolfalse
          \box\count@}%
         {\typeout{juhu!}}{\typeout{oje!}}%
\patchcmd{\LR@column@boxes}{\box\mult@rightbox}%
         {\protected@write\@auxout{}{\string\mc@col@status{3}}%
          \box\mult@rightbox}%
         {\typeout{juhu!}}{\typeout{oje!}}%
}
{%
\patchcmd{\page@sofar}{\box\count@}%
         {\protected@write\@auxout{}{\string\mc@col@status
              {\ifmc@firstcol 1\else 2\fi}}%
          \mc@firstcolfalse
          \box\count@}%
         {\typeout{juhu!}}{\typeout{oje!}}%
\patchcmd{\page@sofar}{\box\mult@rightbox}%
         {\protected@write\@auxout{}{\string\mc@col@status{3}}%
          \box\mult@rightbox}%
         {\typeout{juhu!}}{\typeout{oje!}}%
}
\newif\ifmc@firstcol
\mc@firstcoltrue
%    \end{macrocode}
%
%   Need to reinitiate \verb=\mc@align@columns= as this was let to
%   the old definition of \verb=\LR@column@boxes=.
%
%    \begin{macrocode}
\LRmulticolcolumns

%   Whenever we want to do something that depends on the current
%   column we execute \verb=\docolaction=. This command takes one
%   optional and three mandatory arguments. The mandatory ones denote
%   what to do if this is a ``left'', ``middle'', or ``right'' column
%   and the optional one is simply there to say what to do if we don't
%   know (default is to use the ``left'' column action in that case).
%
%   We use one counter \verb=\mc@col@check@num= to generate us unique
%   label names. Each time we execute \verb=\docolaction= we increment
%   this counter to get a new name.
%    \begin{macrocode}
\newcount\mc@col@check@num
%    \end{macrocode}

%   The generated ``labels'' are named
%   \verb=\mc@col-\the\mc@col@check@num= and they hold as values the
%   numbers 1, 2, or 3 denoting the current column type.

%    \begin{macrocode}
\newcommand\docolaction[4][1]{%
 \global\advance\mc@col@check@num\@ne
 \edef\mc@col@type{\expandafter\ifx
               \csname mc@col-\the\mc@col@check@num\endcsname\relax
                  0\else
                  \csname mc@col-\the\mc@col@check@num\endcsname
               \fi}%
%    \end{macrocode}
%    We prefix with 0 so that an unknown label (that returns
%   \verb=\relax=) will result in case 0
%    \begin{macrocode}
 \ifcase \mc@col@type\relax
%    \end{macrocode}
%    If column is unknown we use the default action or the action
%   denoted by the optional argument (so that arg can take the value
%   1, 2, 3)
%    \begin{macrocode}
     \ifcase #1\or #2\or#3\or#4\fi   % 0 not known use first col as default
  \or
%    \end{macrocode}
%    Otherwise we know (or think we know) that this is a first, middle,
%   or last column:
%    \begin{macrocode}
     #2%  % 1 First col
  \or
     #3%  % 2 any middle col
  \or
     #4%  % 3 last col
  \else
    \ERROR
  \fi
%    \end{macrocode}
%    But how does the column number get associated with our label? We
%   do do this by writing another line into the aux file at this point:
%    \begin{macrocode}
  \edef\next{\write\@auxout
     {\string\mc@set@col@status{mc@col-\the\mc@col@check@num}%
                               {\mc@col@type}}}%
  \next
}
%    \end{macrocode}
%
%   Because of extra data writing to the aux file the aux file will
%   now contain something like the following after the document is
%   processed the first time:
%\begin{verbatim}
%\relax
%\mc@col@status{1}
%\mc@set@col@status{lcol-1}{0}
%\mc@col@status{2}
%\mc@set@col@status{lcol-2}{0}
%\mc@col@status{3}
%\mc@set@col@status{lcol-3}{0}
%\mc@col@status{1}
%\mc@col@status{2}
%\mc@col@status{3}
%\mc@set@col@status{lcol-4}{0}
%\end{verbatim}
%   The \verb=\mc@col@status= line denotes the column type and has been
%   writting out just before corresponding the column box was placed
%   onto the page.
%   The\verb=\mc@set@col@status= lines have been written out as part
%   of shipping the column boxes out, e.g.,
%   \verb=\mc@set@col@status{lcol-1}{0}= was therefore somewhere within
%   the first column as it appears between \verb=\mc@col@status{1}=
%   and  \verb=\mc@col@status{2}=
%   The second argument in that line is the value used in the previous
%   run (or zero if there was no previous run. We can use this to
%   determine if a rerun is necessary.
%
%   Thus with this knowledge we can set things up to get the labels
%   working.
%
%   When the aux file is read in \verb=\mc@col@status= is used to set
%   \verb=\mc@curr@col@status=:
%
%    \begin{macrocode}
\def\mc@col@status#1{\gdef\mc@curr@col@status{#1}}
%    \end{macrocode}

%   And when \verb=\mc@set@col@status= is executed we can simply set
%   up the label by associating it with the \verb=\mc@curr@col@status=
%   and ignore the second argument:
%    \begin{macrocode}
\def\mc@set@col@status#1#2{%
   \global\expandafter\let\csname #1\endcsname\mc@curr@col@status}
%    \end{macrocode}
%
%   The above definition is being used when the \texttt{.aux} file is
%   read in at the beginning. At the end we need a different
%   definition to test if another typesetting run is needed. There we
%   compare the value used in the current run (stored in the second
%   argument) with the value used on the next run. If those two values
%   differ we set \verb=@tempswa= to false which will trigger the
%   ``Label(s) may have changed'' warning.
%    \begin{macrocode}
\AtEndDocument{\def\mc@set@col@status#1#2{%
     \ifnum #2=\mc@curr@col@status\else
       \@tempswatrue
     \fi}%
}
%    \end{macrocode
\end{filecontents}


\documentclass{article}
\usepackage{lipsum}
%\usepackage{multicol}
\usepackage{mccolaction}

\newif\ifinlast
\def\columnbreakunlesslast{%
    \docolaction{\typeout{in first column}\inlastfalse}%
                           {\typeout{in middle column}\inlastfalse}%
                           {\typeout{in last  column}\inlasttrue}%
    \unless \ifinlast \columnbreak \fi
 }

\raggedcolumns

\begin{document}

\begin{multicols*}{3}
\section{First}
\lipsum[1-2]

% Insert column break ONLY IF still within 1st column (e.g. if \lipsum[1-2] above).
% DO NOT insert column break if already in 2nd column (e.g. if \lipsum[1-3] above).
\columnbreakunlesslast

\section{Second}
\lipsum[4-5]
\end{multicols*}

\end{document}

相关内容