[comp.text.tex] improved splitable array/tabular environment

duchier@cs.yale.edu (Denys Duchier) (08/08/90)

There was some interest in my implementation of \Tabular which
extended LaTeX's \tabular environment to allow it to be automatically
split across pages.  Here is a new implementation which folds this
code back into LaTeX, supports the Mittelbach's extensions, and fixes
bugs both in his code and in my earlier version.  I wrote this new
code over the weekend, and I expect there must still be a few
interesting bugs lurking in it.  Be kind... Be specific!

In the new regime the array / tabular environments recognize `x' as an
additional positioning option (e.g. \begin{tabular}[x]...); it means
that once the table has been typeset, it should be unboxed to allow
the rows to be split across pages. \hline attempts to do the right
thing so that if the table is split at a \hline, the line appears both
at the bottom of the page and at the top of the next page.

The array environment is no longer constrained to appear only in math
mode.  The `x' option is incompatible with the `*' variants.  Also, it
cannot appear in math mode; however, since the array environment no
longer has to appear in math mode, it too can be split (for example,
just put it in a \center environment and use the `x' option).

--Denys

%%  array.sty
%%  Denys Duchier, Yale University, August 1990

\newif\if@arraystart     \@arraystarttrue                   % true at beginning of preamble
\newif\if@arrayleftskip  \@arrayleftskiptrue                % cancelled by @ option
\newif\if@arrayrightskip \@arrayrightskiptrue               % cancelled by @ option
\newif\if@arrayleftfil   \@arrayleftfilfalse                % column justification
\newif\if@arrayrightfil  \@arrayrightfilfalse               % column justification
\newif\if@arraymath      \@arraymathfalse                   % entries in math mode? (local)

\let\@preambletoks\toks@
\let\@preamblecontext\count@

\newcount\@arraycolnum \@arraycolnum\z@
\newdimen\extrarowheight \extrarowheight\z@

\def\@arrayleftinsert{}
\def\@arrayrightinsert{}
\def\@arrayleftbracket{}
\def\@arrayrightbracket{}

%%  \@preamblecontext
%%      0  ->  before > option
%%      1  ->  after  > option
%%      2  ->  after column specifier
%%      3  ->  after  < option

\def\@defarrayoption#1{\@namedef{@array@option@#1}}

\def\@arrayerror#1{\@latexerr
  {misplaced #1 option (missing column specifier?)}\@ehd}

\def\@addtopreamble#1{\expandafter\global
  \expandafter\@preambletoks\expandafter{\the\@preambletoks#1}}

\def\@xaddtopreamble#1{\expandafter\@addtopreamble\expandafter{#1}} % one level expansion
\def\@eaddtopreamble#1{\edef\@tempa{#1}\@xaddtopreamble{\@tempa}}   % full expansion

\def\@arraydollar{\futurelet\@tempc\@@arraydollar}
\def\@@arraydollar{\ifx\@tempc\@arraydollar\else\if@arraymath$\fi\fi}

\@defarrayoption{@}#1{%
  \ifcase\@preamblecontext
    \global\@arrayleftskipfalse
  \or \@arrayerror{@}%
  \or \global\@preamblecontext\thr@@
      \global\@arrayrightskipfalse
  \else \global\@arrayrightskipfalse\fi
  \@addtopreamble{\@arraydollar#1\@arraydollar}\@arraycontinue}

\@defarrayoption{>}#1{%
  \ifcase\@preamblecontext
  \or \@arrayerror{#1}%
  \else \@arrayflushcolumn\fi
  \global\@preamblecontext\@ne
  \gdef\@arrayleftinsert{#1}\@arraycontinue}

\@defarrayoption{<}#1{%
  \ifnum\@preamblecontext=\tw@\else\@arrayerror{<}\fi
  \global\@preamblecontext\thr@@
  \gdef\@arrayrightinsert{#1}\@arraycontinue}

\def\@arrayintercol#1#2{%
  \ifcase\@preamblecontext
    \if@arraystart\else\if@arrayleftskip
      \@addtopreamble{\hskip\doublerulesep}\fi\fi
  \or \@arrayerror{#1}%
  \else \@arrayflushcolumn\global\@preamblecontext\z@\fi
  \@addtopreamble{#2}\@arraycontinue}

\@defarrayoption{!}{\@arrayintercol{!}}
\@defarrayoption{|}{\@arrayintercol{|}{\vline}}

\def\@arraycolspec#1#2#3#4{%
  \ifnum\@preamblecontext>\@ne\@arrayflushcolumn\fi
  \global\@preamblecontext\tw@
  \global\let\if@arrayleftfil#1%
  \global\let\if@arrayrightfil#2%
  \gdef\@arrayleftbracket{#3}%
  \gdef\@arrayrightbracket{#4}\@arraycontinue}

\def\@arraystartpbox#1{\bgroup\hsize#1\@arrayparboxrestore
  \vrule\@height\ht\@arstrutbox\@width\z@}

\def\@arrayendpbox{\vrule\@width\z@\@depth\dp\@arstrutbox\egroup}

\@defarrayoption{c}{\@arraycolspec{\iftrue}{\iftrue}{}{}}
\@defarrayoption{l}{\@arraycolspec{\iffalse}{\iftrue}{}{}}
\@defarrayoption{r}{\@arraycolspec{\iftrue}{\iffalse}{}{}}

\@defarrayoption{p}#1{\@arraycolspec{\iffalse}{\iffalse}
  {\vtop\@arraystartpbox{#1}}{\@arrayendpbox}}
\@defarrayoption{m}#1{\@arraycolspec{\iffalse}{\iffalse}
  {\@arraydollar\vcenter\@arraystartpbox{#1}}
  {\@arrayendpbox\@arraydollar}}
\@defarrayoption{b}#1{\@arraycolspec{\iffalse}{\iffalse}
  {\vbox\@arraystartpbox{#1}}{\@arrayendpbox}}

\@defarrayoption{*}#1#2{\@tempcnta#1
  \ifnum\@tempcnta>\z@
    \advance\@tempcnta\m@ne
    \def\@tempa##1{\def\@tempa{\@arraycontinue#2*{##1}{##2}}}%
    \expandafter\@tempa\expandafter{\the\@tempcnta}%
  \else \let\@tempa\@arraycontinue\fi
  \@tempa}

\@defarrayoption{.}{%
  \ifcase\@preamblecontext
  \or \@latexerr{unexpected end of preamble (missing column specifier?)}\@ehd
  \else \@arrayflushcolumn\fi}

\def\@@arraycontinue#1{\@ifundefined{@array@option@#1}
    {\let\@tempa\@arraycontinue\@latexerr{unrecognized option `#1'}\@ehd}
    {\expandafter\let\expandafter\@tempa
     \csname @array@option@#1\endcsname}%
  \@tempa}

\def\@arraycontinue{\global\@arraystartfalse\@@arraycontinue}

\def\@arraysharp{##}

\def\@arrayflushcolumn{%
  \@eaddtopreamble{%
    \ifnum\@arraycolnum>\z@&\fi
    \if@arrayleftskip\hskip\@arraycolsep\fi
    \if@arrayleftfil\hfil\fi
    \kern\z@}%
  \@addtopreamble{\@arraydollar}%
  \@xaddtopreamble{\@arrayleftbracket}%
  \@xaddtopreamble{\@arrayleftinsert}%
  \@addtopreamble{\ignorespaces}%
  \@xaddtopreamble{\@arraysharp}%
  \@addtopreamble{\unskip}%
  \@xaddtopreamble{\@arrayrightinsert}%
  \@xaddtopreamble{\@arrayrightbracket}%
  \@addtopreamble{\@arraydollar}%
  \@eaddtopreamble{%
    \if@arrayrightfil\hfil\fi
    \if@arrayrightskip\hskip\@arraycolsep\fi}%
  \global\@arrayleftskiptrue
  \global\@arrayrightskiptrue
  \gdef\@arrayleftinsert{}%
  \gdef\@arrayrightinsert{}%
  \gdef\@arrayleftbracket{}%
  \gdef\@arrayrightbracket{}%
  \advance\@arraycolnum\@ne}

\def\@arraymakepreamble#1{%
  \global\@preambletoks{}%
  \global\@preamblecontext\z@
  \global\@arraystarttrue
  \global\@arrayleftskiptrue
  \global\@arrayrightskiptrue
  \global\@arrayleftfiltrue
  \global\@arrayrightfiltrue
  \gdef\@arrayleftinsert{}%
  \gdef\@arrayrightinsert{}%
  \gdef\@arrayleftbracket{}%
  \gdef\@arrayrightbracket{}%
  \@arraycolnum\z@
  \@@arraycontinue#1.}

\newif\if@arrayunbox  \@arrayunboxfalse  % (local)
\newif\if@arrayunmath \@arrayunmathfalse % (local)

\def\hline{\noalign{\ifnum0=`}\fi
  \if@arrayunbox\let\@tempa\@unboxhline\else\let\@tempa\@latexhline\fi\@tempa}
\def\@latexhline{\hrule\@height\arrayrulewidth\futurelet\@tempa\@latexxhline}
\def\@latexxhline{\ifx\@tempa\hline\vskip\doublerulesep\fi\ifnum0=`{\fi}}
\def\@@unboxhline{\multispan{\@arraycolnum}%
  \unskip\leaders\hrule\@height\arrayrulewidth\hfill\cr}
\def\@unboxhline{\nopagebreak\ifnum0=`{\fi}%
  \@@unboxhline
  \noalign{\vskip-\arrayrulewidth\pagebreak[2]}%
  \@@unboxhline
  \noalign{\ifnum0=`}\fi\nopagebreak\futurelet\@tempa\@latexxhline}

\def\@array[#1]#2{%
  \@tempdima\ht\strutbox
  \advance\@tempdima\extrarowheight
  \setbox\@arstrutbox\hbox{\vrule
    \@height\arraystretch\@tempdima
    \@depth\arraystretch\dp\strutbox
    \@width\z@}%
  \@arraymakepreamble{#2}%
  \@arrayunmathfalse
  \if #1x
    \ifmmode\@latexerr{cannot use tabular or array with x option in math mode}\@ehd\fi
    \@tempskipa\leftskip
    \@tempskipb\rightskip
    \skip@\parfillskip
    \trivlist \leftskip\@tempskipa
              \rightskip\@tempskipb
              \parfillskip\skip@
              \lineskip\z@\baselineskip\z@
              \if@arraymath\mathsurround\z@\fi
              \item[]\noindent
    \@arrayunboxtrue
    \ifx\@halignto\@empty\else
      \@latexerr{cannot use tabular* or array* with x option}\@ehd\fi
    \gdef\@halignto{to\linewidth}%
    \setbox\@tempboxa\vbox
  \else\if #1c
    \ifmmode\else\@arrayunmathtrue$\fi \vcenter
  \else\if #1t                         \vtop
  \else                                \vbox\fi\fi\fi  \bgroup
  \edef\@tempa{\everycr{}\tabskip\if@arrayunbox\leftskip\else\z@\fi
    \halign\@halignto\bgroup\tabskip\z@\@arstrut\the\@preambletoks
    \tabskip\if@arrayunbox\rightskip\else\z@\fi\cr}%
  \lineskip\z@\baselineskip\z@
  \if@arraymath\mathsurround\z@\fi
  \let\\\@arraycr \let\par\@empty
  \@tempa}

\def\@endarray{\crcr\egroup\egroup
  \if@arrayunmath$\fi
  \if@arrayunbox\unvbox\@tempboxa\endtrivlist\fi}

\newif\if@arraybreak \@arraybreakfalse

\def\@arraybreak{\if@arraybreak\pagebreak[1]\else\nopagebreak\fi}
\def\@arraycrbreak{\if@arrayunbox
  \def\@tempa{\cr\noalign{\@arraybreak}}\else
  \def\@tempa{\cr}\fi\@tempa}

\def\@arraycr{{\ifnum0=`}\fi\@ifnextchar*
  {\global\@arraybreakfalse\@arraycrarg}
  {\global\@arraybreaktrue\@arraycrarg}}

\def\@arraycrarg{\@ifnextchar[
  {\ifnum0=`{\fi}\@xarraycrarg}
  {\ifnum0=`{\fi}\@arraycrbreak}}

\def\@xarraycrarg[#1]{\ifdim#1>\z@\@arraycrpos{#1}\else\@arraycrneg{#1}\fi}

\def\@arraycrpos#1{\unskip \@tempdima#1
  \advance\@tempdima \dp\@arstrutbox
  \vrule\@depth\@tempdima\@width\z@\@arraycrbreak}

\def\@arraycrneg#1{\cr\noalign{\if@arrayunbox\@arraybreak\fi\vskip#1}}

\def\multicolumn#1#2#3{\multispan{#1}\begingroup
  \def\@arraysharp{#3}\@arraymakepreamble{#2}%
  \ifnum\@arraycolnum>\@ne
    \@latexerr{only one column specifier allowed in \noexpand\multicolumn}\@ehd\fi
  \endgroup
  \@arstrut\the\@preambletoks\ignorespaces}

\def\@tabarray{\@ifnextchar[{\@array}{\@array[c]}}

\def\array{\let\@arraycolsep\arraycolsep\@arraymathtrue\gdef\@halignto{}\@tabarray}
\def\tabular{\let\@arraycolsep\tabcolsep\@arraymathfalse\gdef\@halignto{}\@tabarray}
\@namedef{array*}#1{\let\@arraycolsep\arraycolsep\@arraymathtrue\gdef\@halignto{to#1}\@tabarray}
\@namedef{tabular*}#1{\let\@arraycolsep\tabcolsep\@arraymathfalse\gdef\@halignto{to#1}\@tabarray}

\let\endarray\@endarray
\let\endtabular\@endarray
\expandafter\let\csname endarray*\endcsname\@endarray
\expandafter\let\csname endtabular*\endcsname\@endarray