[texhax] Update: Longtable question (partial) solution.

Uwe Lück uwe.lueck at web.de
Wed Mar 10 22:14:00 CET 2010

At 05:20 08.03.10, Beuthe, Thomas wrote:

>No nibbles to my earlier question, but I have kept on digging...
>
>I have found an answer to my first problem thanks to the efforts of Daniel
>He states:
>" Here's an annoying longtable bug for those of you using LaTeX for a
>living. Sometimes, but rarely so,
>  a longtable will break at the oddest places when using horizontal lines
> (hline), including right after the header.
>  The solution is to define a special kind of hline called "nobreakhline"
> and use that instead where you don't want any page breaks."
>
>The code is as follows:
>
>\makeatletter
>\def\nobreakhline{%
>\noalign{\penalty10000}}
>\makeatother
>
>and it does exactly what I want.  Since the document is being mechanically
>generated,
>replacing all \hline occurrences after the header with \nobreakhline is a
>snap.
>I tested it and it works as advertised.  No more orphaned longtable headers.
>
>
>On my second problem I am still clueless however, in spite of lots of
>testing over the weekend. Is there any way of inextricably binding a
>section break with the longtable that occurs immediately after it so that
>the section head can never be orphaned at the bottom of the page?  Doing
>this may cause ugly page breaks (lots of white space at the bottom of a
>page), but that really doesn't matter in this case. Raising the
>widow/orphan penalty doesn't help, and messing with the before or after
>skip parameters didn't seem to help either.
>
>Is there any way of doing this in a general way without manual intervention?
>
>Any thoughts?  I really didn't find anything up to now.
>Section breaks and longtables don't seem to be good bed partners at times.

I once placed another longtable fix on CTAN, http://ctan.org/pkg/ltabptch
(story inside).

It changes \LT at start. I think another change of it solves the problem.
Attached is a new, experimental version v2.00a of ltabptch.sty attempting
at this. Would you like to test it?

I tried to be smart doing something more general. But the essential point
is that

\endgraf\penalty\z@\vskip\LTpre

must be replaced by

\endgraf\if at nobreak\else\penalty\z@\fi\vskip\LTpre

\if at nobreak is exactly the thing for avoiding page breaks after sectioning

Have you thought of making a LaTeX bug report of it? This would be the
normal way, as longtable belongs to the tools bundle of the LaTeX distribution.

HTH -- Uwe.

>Thomas
>
>
>
>=======================================
>Hello all,
>
>I'm overseeing the production of some large documents that are almost
>entirely mechanically generated.
>Large portions consist of sections that contain only longtables and no
>other text or features.
>Example:
>
>\subsection{A}
>longtable here
>\subsection{B}
>another longtable here
>and so on
>
>This can cause funny pagebreaks sometimes where a section heading is orphaned
>at the bottom of the page because the only thing following it is a
>longtable before the next
>section break occurs.
>
>It can also cause situations where the section break occurs at the bottom
>of the page
>and the only thing that follows it is the header of the longtable and an
>\hline below it
>at the very bottom of the page, and the table continues on next page.  For
>example:
>
>2.0 Section Heading for a Section
>
>------------------
>----------------------------------------------------------------------------------
>
>[page breaks here]
>
>------------------
>----------------------------------------------------------------------------------
>| First table
>entry...                                                           |
>----------------------------------------------------------------------------------
>| Second table
>entry...                                                          |
>----------------------------------------------------------------------------------
>...and so on
>
>So my question is:
>
>...aside from manually entering a \newpage before the section break, which
>is not a good option
>because the document source is being produced by a large perl script, and
>would mean I would
>have to do the manual correction every time I re-produce the document
>source via the perl script...
>(which happens far too often...)
>
>1) Is there any way of glueing the \subsection to the top of the longtable
>somehow?
>
>2) Is there any way to force longtable NOT to allow the orphaning of one
>of its heads at the end of a page?
>    (From what I read in the documentation, this isn't really supposed to
> happen.)
>
>I've tried readin through all of the documentation again and done a search
>to see if anyone
>else had this problem before me, but no luck?
>
>Does David Carlisle or David Kastrup read this list?
>I tried contacting them at some of their old email addresses, but got only
>a bounce.
>Does anyone happen to have their up to date email addresses handy?
>
>Even a pointer to a previous discussion or FAQ would be great!
>
>Thomas
-------------- next part --------------
%% LTabPtch.sty---Uwe L"uck---patch for Longtable.
\def\filedate{2010/03/10} \def\fileversion{2.00a}
%% For David Carlisle's longtable.sty, version 4.11
%% Fixes tools/3180 (Sebastian Rahtz) and tools/3485:
%% missing/wrong interline glues above/below table.
%%
%% Copyright (C) 2002--2005, 2010 Uwe L"uck,
%% http://www.contact-ednotes.sty.de.vu
%% --author-maintained.
%%
%% Usage, no warranty, distribution as in lppl.txt on CTAN
%% (macros/latex/base/lppl.txt).
%%
%%
\NeedsTeXFormat{LaTeX2e}[1994/05/20] % \CheckCommand
\ProvidesPackage{ltabptch}[\filedate\space v\fileversion\space
Longtable patch for tools/3180, 3485 (ul)]
%
% The problem(s): [Thomas Beuthe 2010 not recognized]
% Sebastian Rahtz (tools/3180) observed 2000/03/05 that there be
% a "spurios blank line" below the longtable. This impression
% may have been due to two spacing problems:
% (1) There is \parskip and some interline glue below longtable,
% while there is no \parskip and no interline glue above
% (2) After longtable, \prevdepth is the depth of the last line
% before the longtable (if there is no head) or the depth of the
% head (otherwise). So \prevdepth may be 0pt. By contrast, the
% last table row has depth of at least \dp\@arstrutbox--which
% should rather matter, and this is 3.6pt or more.
% --I have reported something like this as tools/3485, not
% entirely correctly. Details are presented below \endinput.
%
% Solution presented:
% (1) I think there should be \parskip and usual interline glue
% above, since the same would happen with a table in a center
% environment (e.g.).
% \noindent seems to work fatally; moreover, \LT at start's decision
% whether to enter a new page would ignore \parskip and interline
% glue still needed.
% So emulate interline glue calculation according to TeXbook p. 80
% and add it by \vskip, along with \parskip.
% (This is rather a hack---a proper correction should perhaps not
% "emulate" but place the interlineglue changes of \LT at array inside
% the \vbox \LT at bchunk starts and treat glue between chunks in a
% new way.)
% (2) Set \prevdepth to depth of box 0 before the latter is
% \unvbox'ed (which leaves \prevdepth untouched, TeXbook p. 282)
% in \endlongtable.
%
% I thought David Carlisle would change Longtable according to my
% suggestions. By e-mail from 2003/04/07 however, he finds it
% better not to change Longtable, in order not to change layouts
% of works that have been made using Longtable so far
% (e-Mail 2003/04/08).
%
%% <- TODO: change this when David has changed his mind!
% By the way:
% 1.) There are two successive lines in the Longtable code (v4.11)
%     together saying:
%         \prevdepth\z@\global
%         \global\let\LT at setprevdepth\relax
%     Isn't the first \global just a typo?
%     --Yes--this is David Carlisle's answer in his e-mail
%     dating from 2004/05/14.
% 2.) Couldn't the one-column restriction easily be removed
%     by using \columnwidth instead of \hsize?
%
% Patching is to override Longtable.sty:
\RequirePackage{longtable}[2004/02/01]
%% TODO: Cf. \CheckCommand below.
%
% For (1), we are patching \LT at start, assumed to have been defined
% as follows (Longtable v4.11; sorry we can no longer handle v3.16
% through 4.10):

\CheckCommand*{\LT at start}{%
\let\LT at start\endgraf
\endgraf\penalty\z@\vskip\LTpre
\dimen@\pagetotal
\dimen at ii\vfuzz
\vfuzz\maxdimen
\setbox\tw@\copy\z@
\setbox\tw@\vsplit\tw@ to \ht\@arstrutbox
\setbox\tw@\vbox{\unvbox\tw@}%
\vfuzz\dimen at ii
\ifdim\ht\@arstrutbox>\ht\tw@\@arstrutbox\else\tw@\fi
\ifdim\dp\@arstrutbox>\dp\tw@\@arstrutbox\else\tw@\fi
\ifdim \dimen@>\z@\vfil\break\fi
\global\@colroom\@colht
\ifvoid\LT at foot\else
\maxdepth\z@
\fi
%% \nobreak new with Longtable v4.11.
\output{\LT at output}}
%
% Change it:
\def\LT at start{%
\let\LT at start\endgraf
%<v2.00> for Thomas Beuthe texhax March 2010
% most simply surround \penalty\z@' with \if at nobreak\else...\fi'
%   \endgraf\penalty\z@\vskip\LTpre
\endgraf
\@tempskipa\LTpre
%  \vskip\parskip %% U.L. v1.73 or earlier
\if at nobreak \else
\fi
%</v2.00>
% I want to add the interline glue before \pagetotal goes to \dimen at .
% So I move \LT at start's \vsplit here.
%% U.L. start:
\dimen at ii\vfuzz
\vfuzz\maxdimen
\setbox\tw@\copy\z@
\setbox\tw@\vsplit\tw@ to \ht\@arstrutbox
\setbox\tw@\vbox{\unvbox\tw@}%
\vfuzz\dimen at ii
\ifdim\prevdepth>-\@m\p@
% First we need the maximum of the heights of \tw@ and head row.
% A little flaw might be here: I am ignoring \Lt at start's
% \@arstrutbox concern below, cf. remark there.
\@tempskipa % \normalbaselineskip going to enter.
%  \typeout{\@tempskipa=\the\@tempskipa}% TEST
\@tempskipa-\ifdim\@tempskipa<\ht\tw@\ht\tw@\else\@tempskipa\fi
%  \typeout{\@tempskipa=\the\@tempskipa}% TEST
%  \typeout{\string\prevdepth=\prevdepth}% TEST
%  \typeout{\@tempskipa=\the\@tempskipa}% TEST
%  \typeout{\normallineskiplimit=\the\normallineskiplimit}% TEST
%  \typeout{\@tempskipa=\the\@tempskipa}% TEST
\vskip \ifdim\@tempskipa<\normallineskiplimit
\normallineskip
\else
\@tempskipa
\fi
\fi
%                 % (if only negative) glue.
%% U.L. end.
\dimen@\pagetotal
%   \dimen at ii\vfuzz
%   \vfuzz\maxdimen
%     \setbox\tw@\copy\z@
%     \setbox\tw@\vsplit\tw@ to \ht\@arstrutbox
%     \setbox\tw@\vbox{\unvbox\tw@}%
%   \vfuzz\dimen at ii
%
% Remark U.L.: The following four code lines seem strange to me:
% after all, \tw@ contains \@arstrutbox, doesn't it?
% And even if not: why should we need room for another \@arstrutbox?
\ifdim\ht\@arstrutbox>\ht\tw@\@arstrutbox\else\tw@\fi
\ifdim\dp\@arstrutbox>\dp\tw@\@arstrutbox\else\tw@\fi
\ifdim \dimen@>\z@\vfil\break\fi
\global\@colroom\@colht
\ifvoid\LT at foot\else
\maxdepth\z@
\fi
%% \nobreak new with Longtable 4.11.
\output{\LT at output}}
%
%
% For (2), we are patching \endlongtable:
% The patch should not change essentials of \endlongtable
% which may have changed when this patch meets longtable.
% We just assume that the final contribution to the main vertical
% list is done by \unvbox0 which is preceded by \LT at start.
% \prevdepth\dp0 can be put in between, it will then
% last till the end (TeXbook pp. 282, 277f.).
\def\@tempa#1\LT at start{#1\LT at start \prevdepth\dp\z@}
\expandafter\expandafter\expandafter \def
\expandafter\expandafter\expandafter \endlongtable
\expandafter\expandafter\expandafter
{\expandafter\@tempa\endlongtable}
%
\endinput

DETAILED DIAGNOSIS:

Concerning the question of "bugs" vs. "features", you might
reason what features should be aimed at.

%% TODO: This can be shortened when David agrees.

(A) If there is \parskip below a longtable, there should
be \parskip above as well--shouldn't it?
(B) Interline glue above should (in general) depend on the
same values of \baselineskip and \lineskip as below---OK?
(C) Interline glue below a longtable should depend on the
depth of the last row, not on the depth of the head or of the
last line above the longtable--OK?

of these three demands.
Concerning (A), \parskip appears below the longtable, but not
above.
Concerning (B), (i) no interline glue appears above if there
is no head, (ii) otherwise, only zero interline glue appears.
Concerning (C), the interline glue below the longtable is
calculated using the depth of (i) the last line above the
longtable as \prevdepth when there is no head or of (ii) the
head otherwise. (Thus I was wrong earlier when I claimed that
\prevdepth were 0pt after longtable. This only holds in the
previous case (i) when the last line above the longtable has
zero depth.)

You can observe this by playing with the demonstration file
ltabptch.tex in CTAN folder macros/latex/contrib/ltabptch.

Reasons: For contributing a head to the main vertical list,
longtable uses \copy or \box, which yields a <box> according
to TeXbook p. 278. Rows are contributed by \unvbox. Now,
violation of (A) is due to the fact that neither \copy or
\box of the head nor the \unvbox for table rows leads to
execution of an \indent or \noindent (TeXbook pp. 282f., see
also <horizontal command>).
Violation of (B) is due either to (i) the fact that \unvbox
is no <box> (TeXbook p. 282/278) or to (ii) the fact that at
\copy or \box \baselineskip and \lineskip are 0pt.
Violation of (C) is due to the fact that \unvbox does not
change \prevdepth (TeXbook p. 282).

As to recognizable effects:
Violation of (A), of course, has very little effect in
general since \parskip is 0pt plus 1pt usually. This is hardly
recognizable in the presence of the \bigskipamount above and
below a longtable.
Violation of (B) results in missing appropriate interline
glue above, which often should be 3.6pt or more. This may
rather be recognizable and may contribute to the impression
that there even is a spurious blank line below the longtable
(this is what Sebastion Rahtz reported in tools/3180).
For the previous as well as for the details and the
effects of violation of (C), consider the algorithm for
calculating interline glue on p. 80 of the TeXbook. If there
is no head, and the depth of the last line before the
longtable is 0pt, the interline glue below the longtable is
\baselineskip minus the height of the first line after the
longtable. The interline glue *should be* \baselineskip minus
height of following line *minus* depth of last table row--if
this depth is not too large (depending on \lineskiplimit).
This depth is at least 3.6pt. So the interline glue is often
by 3.6pt or more greater than is appropriate. Considering the
missing interline glue above the longtable and the \parskip
below, the glue after the longtable is by 6.2pt (or more) plus
1pt larger than the glue before. Due to the shrink component
of the surrounding \bigskipamount, the glue following the
longtable may be nearly twice as large as the glue before.
---Usually, violation of (C) has no effect at all. Namely,
there is a head usually, and its depth is usually the same as
the depth of the last row, namely \dp\@arstrutbox.

%% VERSION HISTORY:
v1.7   2003/01/07 Sent around 2003/01/13 with first release of
ednotes.sty. Sent to David Carlisle. Earlier versions
were sent to Frank Mittelbach and the team.
longtable -> Longtable (selectively).
v1.74  2004/08/05 Added ---Yes---...'.
2004/08/21 Typo fix for problem'; added report on 2003/04/08.
2004/08/23 Corrected and extended diagnosis, sent to CTAN.
v1.74b  .../08/23 ---' -> --'; added that (C) usually doesn't
matter.
v1.74c  .../08/31 author-maintained'.
v1.74d 2005/01/10 Contact via http.
v2.00  2010/03/10 Attempt for Thomas Beuthe