6

I have a series of text blocks in a 2-column layout, using multicol. Each block of text is wrapped in a minipage environment to ensure that all the contained text appears in the same column. I would like to separate these minipages with a horizontal rule or other decoration, but I don't want the rule to appear if it's at the top of a column.

Clearly, I could add a parameter that would manually suppress the rule, but I'm looking for a way to detect the situation automatically and format appropriately. For the MWE below, that means the line should only appear between the final two paragraphs.

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

\newenvironment{segmented}{%
\begin{minipage}{\linewidth}
\hrule\bigskip
}{\bigskip
\end{minipage}}

\begin{document}
    \begin{multicols}{2}
    \begin{segmented}
        \lipsum[1]  
    \end{segmented}
    \begin{segmented}
        \lipsum[2]  
    \end{segmented}
    \begin{segmented}
        \lipsum[3]  
    \end{segmented}
    \end{multicols}
\end{document}
2
  • Detection might not be easy; see Detecting current column in multicol.
    – Werner
    Commented Dec 9, 2017 at 1:54
  • @Werner, The underlying detection certainly isn't easy, but the basic problem already seems solved in that link and folded into multicol, so thanks for pointing it out. I've put an answer below that builds on \docolaction
    – Karl Hagen
    Commented Dec 9, 2017 at 4:00

4 Answers 4

3

Is it what you'd like to get?

\documentclass{article}
\usepackage[latin]{babel}% for avoiding too many underfull boxes
\usepackage{multicol}
\usepackage{lipsum}

\newenvironment{segmented}
 {%
  \noindent\begin{minipage}{\linewidth}
 }
 {%
  \end{minipage}\par
  \bigskip
  \cleaders\vbox{\hrule width\linewidth}\vskip0.4pt
  \bigskip
 }

\begin{document}

\begin{multicols}{2}
\begin{segmented}
\lipsum[1]  
\end{segmented}
\begin{segmented}
\lipsum[2]  
\end{segmented}
\begin{segmented}
\lipsum[3]  
\end{segmented}
\end{multicols}

\end{document}

enter image description here

3
  • That's exactly the result I'm looking for. Can you confirm or correct my understanding of how this works? \cleaders is a TeX command that repeats the \vbox enough times to fit within \vskip0.4pt, which means that the rule won't appear at the bottom of a column, as the \vskip becomes 0 there.
    – Karl Hagen
    Commented Dec 9, 2017 at 16:29
  • @KarlHagen Since the rule is 0.4pt high, there will be just one; but leaders are like glue, so they disappears at page/column breaks
    – egreg
    Commented Dec 9, 2017 at 16:34
  • ah just see that @egreg did kind of the same while I was collecting screenshots. Commented Dec 9, 2017 at 16:56
2

There's probably a better way to do this, but @Werner's link gave me this idea. It relies on the colaction feature of multicol:

\documentclass{article}
\usepackage[colaction]{multicol}
\usepackage{etoolbox}
\usepackage{lipsum}

\numdef{\columnnumber}{0}
\newcommand{\conditionalrule}[1]{
    \ifnumequal{#1}{\columnnumber}{\hrule}{}
    \global\numdef{\columnnumber}{#1}
    }

\newenvironment{segmented}{%
\noindent\begin{minipage}{\linewidth}
    \docolaction{\conditionalrule{1}}{}{\conditionalrule{2}}\bigskip
}{\bigskip
\end{minipage}}

\begin{document}
    \begin{multicols}{2}
        \begin{segmented}
            \lipsum[1]  
        \end{segmented}
        \begin{segmented}
            \lipsum[2]  
        \end{segmented}
        \begin{segmented}
            \lipsum[3]  
        \end{segmented}
    \end{multicols}
\end{document}

multicol line

If anyone has a more elegant/efficient solution, I'd be happy to accept their answer, so I'm holding off on accepting my own answer for a little while.

1
  • I hope you don't mind, I added an example output image to better demonstrate what your code does. Feel free to roll back the edit if you so wish.
    – Troy
    Commented Dec 9, 2017 at 8:19
2

You can let TeX determine the position (which works within or outside multicol):

\documentclass[]{article}
\usepackage{multicol,lipsum}

\usepackage{xparse}

\ExplSyntaxOn

\dim_new:N \l__condline_dim
\box_new:N \l__condline_box

% #1 skip produced
% #2 vertical correction of material
% #3 material 
% #4 break penalty after (if forbidden the only place to break is before

\cs_new:Npn \condline:nn #1#2#3#4 
  {
    \par
    \hbox_set_to_wd:Nnn \l__condline_box \columnwidth {\hfil #3 \hfil}
%
    \dim_set:Nn \l__condline_dim
        { \box_ht:N \l__condline_box +
          \box_dp:N \l__condline_box }
%
    \skip_vertical:n { #1 / 2 - \prevdepth + #2 - \l__condline_dim / 2 }
    \nobreak    
%
    \cleaders \copy \l__condline_box \vskip \l__condline_dim
    \nobreak    
%
    \skip_vertical:n { #1 / 2 - \parskip  - #2 - \l__condline_dim / 2 }
    \penalty #4 \relax 
%
    \dim_set:Nn \prevdepth { 0pt }
  }

% document level command
%
% \condline * < correction > [ skip ] { material }
%
% all arguments optional

\DeclareDocumentCommand\condline 
  { s  D<>{3pt} O{\baselineskip} G{$*\quad*\quad*$} }
  { \IfBooleanTF #1
      { \condline:nn {#3} {#2} {#4} {10000} }
      { \condline:nn {#3} {#2} {#4} {0} }
  }

\ExplSyntaxOff

\begin{document}
\begin{multicols}{3}
  \lipsum[1]
  \condline{...........}
  line1\\  line2\\  line3\\  line4\\ line5\\  
  add two extra lines
  and the stars below will vanish!\\
 % line9\\  line10\\
  LAST LAST LAST
  \condline{$* \quad * \quad * $}
  \lipsum[2]
\end{multicols}

  \condline<-5pt>{\hrulefill}

\begin{multicols}{2}
  \lipsum[3]
  \condline{\hrulefill}
  \lipsum[4]
  \condline[2\baselineskip]{---With star this line would vanish }
  \lipsum[5]
  \condline{\dotfill}
  \lipsum[6]
\end{multicols}

\end{document} 

(the code is a bit of a mixture of expl3 + ordinary LaTeX as expl3 doesn't yet have all tools for text processing (like handling penalties))

The result is:

enter image description here

And if we add 2 lines in the first multicol and use the star form of \condline in the second one we will get:

enter image description here

The right bottom column now appears to be too short but that is understandable as now the only break is before the \condline and the line vanishes if the break is taken there (just like the line with asterisks in the first multicol vanished at the break.

1

A proposal:

\documentclass{article}
\usepackage{multicol}
\usepackage{lipsum}
\usepackage{zref-savepos}%

\newcount\myy\newcount\ymax
\newcounter{myx}\setcounter{myx}{1}

\typeout{\themyx}
\newcommand\donotruleattop{%
    \stepcounter{myx}
    \zsaveposy{\themyx}%
    \typeout{y position of \themyx: \zposy{\themyx}}
    \myy=\numexpr\zposy{\themyx}\relax
    \ifnum\myy>\ymax\relax\else\hrule\bigskip\fi
}%
\newcommand{\gaugeymax}{%
\stepcounter{myx}
\zsaveposy{\themyx}
\ymax=\numexpr\zposy{\themyx}-256\relax
}

\newenvironment{segmented}{%
\noindent\begin{minipage}{\linewidth}\donotruleattop
}{\bigskip
\end{minipage}}

\begin{document}\gaugeymax 
    \begin{multicols}{2}
    \begin{segmented}
        \lipsum[1] 
    \end{segmented}
    \begin{segmented}
        \lipsum[2] 
    \end{segmented}
    \begin{segmented}
        \lipsum[3] 
    \end{segmented}
    \end{multicols}
\end{document}

enter image description here

There is a shortcoming: The number 256 appearing in the definition is hard-coded, and might have to be adjusted if you change the layout of the document.

Nevertheless, this might be a starting point for a smoother solution. And it is both a counter-example and confirmation of @Werner's statement, depending on what you mean by easy.

Obviously, this construction is not specific to multicolumn.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .