I'd like to typeset two chunks of text (a problem statement and solution) next to each other, but I don't want the problem to be the last thing on a page and the solution to be the first thing on the next page. Is there a way to get LaTeX to try to keep them together if they occur at a page boundary — for example, by pushing a few lines of the problem onto the solution page, or vice versa? (Each paragraph/section can be broken individually, but I would like the boundary to be on one page.)
[Tex/LaTex] How to tie two pieces of text together so LaTeX tries not to break exactly between them
page-breakingparagraphs
Related Solutions
You can use zref
with the abspos
and user
modules (e.g. load the zref-abspos
and zref-user
packages) and add a \zlabel{labelname}
to the \tikzmark
at the beginning of the paragraph as well one at the end of the paragraph. If you using an environment simply place one at begin
and end
. Then you can check at the end of the environment if the absolute page numbers of both are identical or not. For this you can use:
\ifnum\zref@extract{<begin-label>}{abspage}=\zref@extract{<end-label>}{abspage}\relax
<same page>
\else
<different page>
\fi
(Note: Maybe \zref@extractdefault
is better because you can provide a default value used for the first runs when the label is not set yet.)
See also my answer to How to draw text-anchored tikz line below text instead of above? where I do something very similar!
Here a solution I now come up with. It draws the first border half in the begin-macro if the end-macro is not at the same page. Also the case where the environment spans more than two pages, e.g. starts at page 1 and ends at page 3, is covered by extra code.
\documentclass[twoside]{book}
\usepackage{zref-abspage}
\usepackage{zref-user}
\usepackage{tikz}
\usepackage{atbegshi}
\usetikzlibrary{calc}
\makeatletter
\newcommand{\currentsidemargin}{%
\ifodd\zref@extract{textarea-\thetextarea}{abspage}%
\oddsidemargin%
\else%
\evensidemargin%
\fi%
}
\newcounter{textarea}
\newcommand{\settextarea}{%
\stepcounter{textarea}%
\zlabel{textarea-\thetextarea}%
\begin{tikzpicture}[overlay,remember picture]
% Helper nodes
\path (current page.north west) ++(\hoffset, -\voffset)
node[anchor=north west, shape=rectangle, inner sep=0, minimum width=\paperwidth, minimum height=\paperheight]
(pagearea) {};
\path (pagearea.north west) ++(1in+\currentsidemargin,-1in-\topmargin-\headheight-\headsep)
node[anchor=north west, shape=rectangle, inner sep=0, minimum width=\textwidth, minimum height=\textheight]
(textarea) {};
\end{tikzpicture}%
}
\tikzset{tikzborder/.style={line width=1mm,red,double=blue}}
\newcounter{tikzborder}
\newcounter{tikzborderpages}
\newenvironment{tikzborder}[1][]{%
\medskip\par
% Allow user to overwrite the used style locally
\ifx&\else
\tikzset{tikzborder/.style={#1}}%
\fi
\settextarea
\stepcounter{tikzborder}%
\tikz[overlay,remember picture] \coordinate (tikzborder-\thetikzborder);% Modified \tikzmark macro
\zlabel{tikzborder-begin-\thetikzborder}%
% Test if end-label is at the same page and draw first half of border if not
\ifnum\zref@extract{tikzborder-begin-\thetikzborder}{abspage}=\zref@extract{tikzborder-end-\thetikzborder}{abspage} \else
\begin{tikzpicture}[overlay,remember picture]
\draw [tikzborder]
let \p0 = (textarea.north west), \p1 = (tikzborder-\thetikzborder), \p2 = (textarea.south east) in
(\x0-\fboxsep-.5\pgflinewidth,\y2-\fboxsep-.5\pgflinewidth)
|-
(\x2+\fboxsep+.5\pgflinewidth,\ht\strutbox+\fboxsep+.5\pgflinewidth)
--
(\x2+\fboxsep+.5\pgflinewidth,\y2-\fboxsep-.5\pgflinewidth)
;
\end{tikzpicture}%
% If it spreads over more than two pages:
\setcounter{tikzborderpages}{\numexpr-\zref@extract{tikzborder-begin-\thetikzborder}{abspage}+\zref@extract{tikzborder-end-\thetikzborder}{abspage}}
\ifnum\value{tikzborderpages}>1
\AtBeginShipoutNext{\tikzborderpage}%
\fi
\fi
}{%
\zlabel{tikzborder-end-\thetikzborder}%
% Test if begin-label is at the same page and draw while border if so
\ifnum\zref@extract{tikzborder-begin-\thetikzborder}{abspage}=\zref@extract{tikzborder-end-\thetikzborder}{abspage}
\begin{tikzpicture}[overlay,remember picture]
\draw [tikzborder]
let \p0 = (textarea.north west), \p1 = (tikzborder-\thetikzborder), \p2 = (textarea.south east) in
(\x0-\fboxsep-.5\pgflinewidth,\y1+\ht\strutbox+\fboxsep+.5\pgflinewidth)
|-
(\x2+\fboxsep+.5\pgflinewidth,-\dp\strutbox-\fboxsep-.5\pgflinewidth)
|-
(\x0-\fboxsep-.5\pgflinewidth,\y1+\ht\strutbox+\fboxsep+.5\pgflinewidth)
-- cycle
;
\end{tikzpicture}%
% Otherwise draw second half of border
\else
\settextarea
\begin{tikzpicture}[overlay,remember picture]
\draw [tikzborder]
let \p0 = (textarea.north west), \p1 = (tikzborder-\thetikzborder), \p2 = (textarea.south east) in
(\x0-\fboxsep-.5\pgflinewidth,\y0+\fboxsep+.5\pgflinewidth)
|-
(\x2+\fboxsep+.5\pgflinewidth,-\dp\strutbox-\fboxsep-.5\pgflinewidth)
--
(\x2+\fboxsep+.5\pgflinewidth,\y0+\fboxsep+.5\pgflinewidth)
;
\end{tikzpicture}%
\fi
\par\medskip
}
\newcommand{\tikzborderpage}{%
\settextarea
\begin{tikzpicture}[overlay,remember picture]
\draw [tikzborder]
([shift={(-\fboxsep-.5\pgflinewidth, \fboxsep+.5\pgflinewidth)}]textarea.north west)
--
([shift={(-\fboxsep-.5\pgflinewidth,-\fboxsep-.5\pgflinewidth)}]textarea.south west)
;
\draw [tikzborder]
([shift={( \fboxsep+.5\pgflinewidth, \fboxsep+.5\pgflinewidth)}]textarea.north east)
--
([shift={( \fboxsep+.5\pgflinewidth,-\fboxsep-.5\pgflinewidth)}]textarea.south east)
;
\end{tikzpicture}%
\addtocounter{tikzborderpages}{-1}%
\ifnum\value{tikzborderpages}>1
\AtBeginShipoutNext{\tikzborderpage}%
\fi
\vspace{-\baselineskip}% Compensate for the generated extra line at begin of the page. No idea why exactly this happens. Also \baselineskip seems not to be 100% right.
}
\makeatother
\usepackage{lipsum}
\newcommand\xlipsum[1][]{{\let\par\relax\lipsum*[#1]}}
\begin{document}
\begin{tikzborder}
\xlipsum[1]
\end{tikzborder}
\lipsum[2-4]
\begin{tikzborder}
\xlipsum[1]
\end{tikzborder}
\lipsum[2-4]
\begin{tikzborder}
\xlipsum[1-9]
\end{tikzborder}
\lipsum[2-4]
\begin{tikzborder}
\xlipsum[1-20]
\end{tikzborder}
\end{document}
(The frame is not displayed fully right in the picture because of the low resolution etc.
Click to enlarge)
Some filler text. Some filler text. Some filler text. Some filler text. Some filler text. Some filler text:
\widowpenalty=-5000 \nopagebreak
\begin{itemize}
\item Some text.
\item Some text.
\item Some text.
\end{itemize}
pushes
Some filler text:
To the top of page 4. The negative widow penalty encourages rather than discourages a break before the last line of the partial paragraph and the \nopagebreak
stops it breaking before the list.
Best Answer
There also is the
samepage
environment. I would try the following:Maybe that's good enough for you.