Nested breakable boxes with line on the left (any package)

boxespage-breaking

I'm trying to create an environment like quotation, but with a line on the left.
I would also like it to support multiple paragraphs, footnotes, math environments and floats (basically you should be able to wrap it around any, or most content and it should just work).

The difficult thing is that I want it to handle pagebreaks, even when nested.
From the TeXFAQ entry about breaking boxes of text, I know it is possible to do this with the boites package, but it breaks floats and footnotes, sadly, so this is not an option for me. There have been similar questions before, but for whole boxes instead of only left lines. I'm not sure if this question might be easier to answer. I am also aware that this is apparently not possible to solve using the framed, mdframed or tcolorbox packages (see the package documentations).

There is a similar question about mdframed specifically, instead of about the problem in general, but the boxes in the answer do not work with arbitrary content. (previous question was marked as duplicate, this is to clarify why I think it isn't)

Can this be done?

Example:
Example

Example code adapted from a similar question:

\documentclass{article}

\usepackage{framed}
\usepackage[parfill]{parskip}

\newenvironment{quotationb}%
{\begin{leftbar}}%
{\end{leftbar}}

\begin{document}

\begin{quotationb}
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
\begin{quotationb}
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
\begin{quotationb}
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
\end{quotationb}
Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
\end{quotationb}
Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
\end{quotationb}
\end{document}

Best Answer

The following is a sufficient option. It uses eso-pic to draw the vertical rules, allows for nesting and adjustment of the indentation and rule width.

enter image description here

\documentclass{article}

\usepackage{lipsum}
\usepackage{changepage,eso-pic}
\usepackage[savepos,abspage,user]{zref}

\newcounter{newleftbar}
\newlength{\leftbarindent}
\newlength{\leftbarwidth}
\setlength{\leftbarwidth}{2pt}

\makeatletter
\NewDocumentEnvironment{leftbar}{ O{2em} }{%
  \setlength{\parindent}{0pt}%
  \def\@leftbarindent{#1}%
  \addtolength{\leftbarindent}{\@leftbarindent}%
  \edef\leftbarnest{\thenewleftbar}%
  \stepcounter{newleftbar}% New leftbar environment
  \edef\theleftbar{\thenewleftbar}%
  \begin{adjustwidth}{#1}{0pt}
    \leavevmode% Start paragraph
    \zsavepos{leftbar-\leftbarnest-\theleftbar-top}% Save position of left bar top
    \zlabel{leftbar-\leftbarnest-\theleftbar-top-page}% Save page of left bar top
    % Store left bar top's text block top/bottom
    \begingroup\edef\x{\endgroup\noexpand\AddToShipoutPictureFG*{%
      \noexpand\AtTextUpperLeft{\noexpand\zsavepos{leftbar-\leftbarnest-\theleftbar-ftt}}%
      \noexpand\AtTextLowerLeft{\noexpand\zsavepos{leftbar-\leftbarnest-\theleftbar-ftb}}%
    }%
    \noexpand\AddToShipoutPictureFG{%
      \noexpand\ifnum\noexpand\zref@extractdefault{leftbar-\leftbarnest-\theleftbar-top-page}{abspage}{0}=\noexpand\zref@extractdefault{leftbar-\leftbarnest-\theleftbar-bottom-page}{abspage}{0}
        % Single page left bar
        \noexpand\ifnum\noexpand\zref@extractdefault{leftbar-\leftbarnest-\theleftbar-top-page}{abspage}{0}=\noexpand\arabic{abspage}
          \noexpand\put(\dimexpr\zposx{leftbar-\leftbarnest-\theleftbar-top}sp-#1,\dimexpr\zposy{leftbar-\leftbarnest-\theleftbar-bottom}sp-.2\baselineskip){%
            \noexpand\rule{\leftbarwidth}{\dimexpr\zposy{leftbar-\leftbarnest-\theleftbar-top}sp-\zposy{leftbar-\leftbarnest-\theleftbar-bottom}sp+\baselineskip}}%
        \noexpand\fi
      \noexpand\fi
      \noexpand\ifnum\noexpand\zref@extractdefault{leftbar-\leftbarnest-\theleftbar-top-page}{abspage}{0}<\noexpand\zref@extractdefault{leftbar-\leftbarnest-\theleftbar-bottom-page}{abspage}{0}
        % Multipage left bar
        \noexpand\ifnum\noexpand\zref@extractdefault{leftbar-\leftbarnest-\theleftbar-top-page}{abspage}{0}=\noexpand\arabic{abspage}
          % First page bar
          \noexpand\put(\dimexpr\zposx{leftbar-\leftbarnest-\theleftbar-top}sp-#1,\zposy{leftbar-\leftbarnest-\theleftbar-ftb}sp){%
            \noexpand\rule{\leftbarwidth}{\dimexpr\zposy{leftbar-\leftbarnest-\theleftbar-top}sp-\zposy{leftbar-\leftbarnest-\theleftbar-ftb}sp+.8\baselineskip}}%
        \noexpand\fi
        \noexpand\ifnum\noexpand\arabic{abspage}>\noexpand\zref@extractdefault{leftbar-\leftbarnest-\theleftbar-top-page}{abspage}{0}
          \noexpand\ifnum\noexpand\arabic{abspage}<\noexpand\zref@extractdefault{leftbar-\leftbarnest-\theleftbar-bottom-page}{abspage}{0}
            % Middle page bar
            \noexpand\put(\dimexpr\zposx{leftbar-\leftbarnest-\theleftbar-top}sp-#1,\dimexpr\zposy{leftbar-\leftbarnest-\theleftbar-ftb}sp-.2\baselineskip){%
              \noexpand\rule{\leftbarwidth}{\dimexpr\zposy{leftbar-\leftbarnest-\theleftbar-ftt}sp-\zposy{leftbar-\leftbarnest-\theleftbar-ftb}sp}}%
          \noexpand\fi
        \noexpand\fi
        \noexpand\ifnum\noexpand\zref@extractdefault{leftbar-\leftbarnest-\theleftbar-bottom-page}{abspage}{0}=\noexpand\arabic{abspage}
          % Last page bar
          \noexpand\put(\dimexpr\zposx{leftbar-\leftbarnest-\theleftbar-top}sp-#1,\dimexpr\zposy{leftbar-\leftbarnest-\theleftbar-bottom}sp-.2\baselineskip){%
            \noexpand\rule{\leftbarwidth}{\dimexpr\zposy{leftbar-\leftbarnest-\theleftbar-ltt}sp-\zposy{leftbar-\leftbarnest-\theleftbar-bottom}sp+.2\baselineskip}}%
        \noexpand\fi
      \noexpand\fi
    }}\x
    \ignorespaces
}{%
    \zlabel{leftbar-\leftbarnest-\theleftbar-bottom-page}% Save page of let bar bottom
    \zsavepos{leftbar-\leftbarnest-\theleftbar-bottom}% Save position of left bar bottom
    % Store left bar bottom's text block top/bottom
    \begingroup\edef\x{\endgroup\noexpand\AddToShipoutPictureFG*{%
      \noexpand\AtTextUpperLeft{\noexpand\zsavepos{leftbar-\leftbarnest-\theleftbar-ltt}}%
      \noexpand\AtTextLowerLeft{\noexpand\zsavepos{leftbar-\leftbarnest-\theleftbar-ltb}}%
    }}\x%
  \end{adjustwidth}
  \addtolength{\leftbarindent}{-\@leftbarindent}%
}
\makeatother

\begin{document}

\lipsum[1]

\begin{leftbar}
  \lipsum[2]
  
  \begin{leftbar}[1em]
    \lipsum[3-12]
  \end{leftbar}
  
  \lipsum[13]
\end{leftbar}

\lipsum[14]

\begin{leftbar}[5pt]
  \lipsum[15]
\end{leftbar}

\lipsum[16]

\begin{leftbar}
  \vspace{-\baselineskip}
  \begin{leftbar}
    \lipsum[17]
  \end{leftbar}
\end{leftbar}

\lipsum[18]

\begin{leftbar}
  \lipsum[19]
  \begin{leftbar}
    \lipsum[20]
  \end{leftbar}
\end{leftbar}

\lipsum[21]

\begin{leftbar}
  \lipsum[22]
  \begin{leftbar}
    \lipsum[23]
    \begin{leftbar}
      \lipsum[24]
      \begin{leftbar}
        \lipsum[25]
      \end{leftbar}
      \lipsum[26]
    \end{leftbar}
    \lipsum[27]
  \end{leftbar}
  \lipsum[28]
\end{leftbar}

\lipsum[29]

\end{document}

zref is used to capture the position of the start and end of the leftbar text block and absolute page number to identify whether the leftbar is on a single page or has to span multiple pages. In the latter case, the bar is split into a "first page bar", "middle page bar" (possibly non-existent) and a "last page bar".

This should work sufficiently for a oneside document, or twoside with similar margins. If the text blocks are offset (like in twoside mode), there may be placement issues, or the code has to be updated to accommodate for that.

Not much is done to check for nesting without content at some level, so manual vertical adjustment is required. Also, multiply consecutive closures (\end{leftbar} \end{leftbar}) may have some misalignment due to spacing from adjustwidth.

Related Question