[Tex/LaTex] How to make longtable respect \nopagebreak in the beginning

longtablepage-breakingsupertabulartables

For the book I am typesetting now I need a table environment with the following abilities:

  1. Span over multiple pages;
  2. Allow \nopagebreak between two lines
  3. Do not ignore \nopagebreak before the beginning of the environment

Points 1 and 2 can be achieved with longtable environment; but recently I bumped into the situation when the code:

\section{Some section}

\begin{longtable}{...}
...

leaves the section header on one page and starts to typeset longtable on the next page. \nopagebreak between \section and \begin does not help. I found the bug filed on longtable about 9 years ago, but it seems that no-one is interested, and I do not know TeX well enough to fix it myself.

I tried supertabular package — it has the same problem, and stabular which has points 1 and 3, but does not have point 2. Is there some way to make longtable listen to \nopagebreak? Or maybe there is some other package that supports all three points? Or will it be easier to just stack \hboxs and \vboxs in a desired way?

Best Answer

EDIT: as pointed out in a comment below, this solution sometimes produces spurious headers; I do not know how (nor have time) to fix it, sorry.

There are two problems: first, \longtable does the calculations of whether it has enough space on the current page before starting to read the material of the table. Secondly, it inserts a penalty of \z@ (read "zero"), in other words, a place where TeX is allowed to do a page break. We need to inhibit both of those, but in case there is more than one longtable in your book, the change must not be permanent.

Putting the change within a group does not seem possible (because \longtable redefines the output routine locally, I think), so I added a private switch to control whether a penalty and a \break are inserted or not. Put \normalLTfalse before and \normalLTtrue after the longtable (not within) to inhibit the page-break and penalty for that table. (Note that commenting our the line \normalLTfalse will lead to a page break there.)

It may be possible to use the "value" of \if@nobreak instead of explicitly having to use a switch, but I am not sure when \if@nobreak is \iftrue or \iffalse.

\documentclass{article}
\usepackage{longtable}
\usepackage{etoolbox}

\newif\ifnormalLT
\normalLTtrue

\makeatletter
\patchcmd {\LT@start}
          {\vfil \break}
          {\ifnormalLT \vfil \break \fi}
          {\typeout{Patching longtable succeeded!}}
          {\typeout{Patching longtable failed!}\ERROR}
\patchcmd {\LT@start}
          {\penalty \z@}
          {\ifnormalLT \penalty \z@ \fi}
          {\typeout{Patching longtable succeeded!}}
          {\typeout{Patching longtable failed!}\ERROR}
\makeatother

% "lipsum" is just used to have some dummy text.
% The vspace is there so that without patch
% we would have a bad page break. Remove all that.
\usepackage{lipsum}
\begin{document}
\lipsum[1-2]
\vspace{2.5cm}
\lipsum[3-4]

\section{XY}
\normalLTfalse
\begin{longtable}{c}
A \\
C \\
\end{longtable}
\normalLTtrue

\end{document}