summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/cals/cals/table.dtx
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/latex/contrib/cals/cals/table.dtx
Initial commit
Diffstat (limited to 'macros/latex/contrib/cals/cals/table.dtx')
-rw-r--r--macros/latex/contrib/cals/cals/table.dtx481
1 files changed, 481 insertions, 0 deletions
diff --git a/macros/latex/contrib/cals/cals/table.dtx b/macros/latex/contrib/cals/cals/table.dtx
new file mode 100644
index 0000000000..9467020002
--- /dev/null
+++ b/macros/latex/contrib/cals/cals/table.dtx
@@ -0,0 +1,481 @@
+% \subsection{Row dispatcher}
+
+% \begin{macro}{\cals@row@dispatch}
+% Depending if the current row has a rowspan cell or not,
+% the execution is different.
+% \begin{macrocode}
+\newcommand\cals@row@dispatch{%
+\ifx b\cals@current@context
+ \cals@ifInRspan\iftrue
+ \cals@row@dispatch@span
+ \else
+ \cals@row@dispatch@nospan
+ \fi
+\else
+ \cals@row@dispatch@nospan
+\fi}
+% \end{macrocode}
+% \end{macro}
+
+% \begin{macro}{\cals@row@dispatch@nospan}
+% After a row is typeset in a box, this macro decides what to do next.
+% Usually, it should just add decorations and output the row.
+% But if a table break is required, it should put the current row
+% to backup, typeset the footer, the break, the header and only then
+% the row from the backup. Summary of main parameters:
+% \begin{itemize}
+% \item rowsep from the last row (|\cals@last@rs|) and the
+% last context (|\cals@last@context|)
+% \item current row (|\cals@current@row|), its decorations
+% (|\cals@current@cs|, |\cals@current@rs@above|,
+% |\cals@current@rs@below|) and context (|\cals@current@context|)
+% \end{itemize}
+% \begin{macrocode}
+\newcommand\cals@row@dispatch@nospan{%
+% \end{macrocode}
+% The header and footer rows are always typeset without further
+% considerations.
+% \begin{macrocode}
+\let\cals@last@context@bak=\cals@last@context
+\ifx h\cals@current@context \else
+\ifx f\cals@current@context \else
+% \end{macrocode}
+% In the body, if a break is required: do it.
+% \begin{macrocode}
+\cals@ifbreak\iftrue
+ \setbox\cals@backup@row=\box\cals@current@row
+ \setbox\cals@backup@cs=\box\cals@current@cs
+ \let\cals@backup@rs@above=\cals@current@rs@above
+ \let\cals@backup@rs@below=\cals@current@rs@below
+ \let\cals@backup@context=\cals@current@context
+ \cals@tfoot@tokens
+ \lastrule
+ \cals@issue@break
+ \cals@thead@tokens
+ \setbox\cals@current@row=\box\cals@backup@row
+ \setbox\cals@current@cs=\box\cals@backup@cs
+ \let\cals@current@rs@above=\cals@backup@rs@above
+ \let\cals@current@rs@below=\cals@backup@rs@below
+ \let\cals@current@context=\cals@backup@context
+\fi\fi\fi
+% \end{macrocode}
+% Typeset the row. If the width of the row is more than hsize, then
+% the issue-code should not fit the row to hsize.
+% \begin{macrocode}
+\ifdim\wd\cals@current@row>\hsize\relax
+\def\cals@tohsize{}%
+\fi
+\cals@issue@row
+% \end{macrocode}
+% Consider a table such that thead+row1 do not fit to a page
+% (see the unit test |regression/test_010_wrongbreak|).
+% Without the next code, the following happens:
+% thead and row1 are typeset, but the output procedure is not
+% executed yet. Therefore, when row2 is ready, we detect that
+% a table break is required and create it. Then the output procedure
+% moves thead+row1 on the next page. The result:
+% thead and row1 on one page, row2 and the rest on the next page
+% instead of the whole table on one page.
+% Solution: force a run of the output procedure after
+% the first row of a table chunk.
+% \begin{macrocode}
+\ifx b\cals@last@context
+ {\dimen0=\pagetotal\relax
+ \advance\dimen0 by \cals@tfoot@height\relax
+ \advance\dimen0 by -\pagegoal
+ \ifdim\dimen0>0pt\relax
+ \vskip\dimen0
+ \penalty9999 % with 10000, the output page builder is not called
+ \vskip-\dimen0\relax
+ \fi
+ }%
+\fi
+}
+% \end{macrocode}
+% \end{macro}
+
+% \begin{macro}{\cals@row@dispatch@span}
+% The only specific thing to rowspanned rows is that we should not
+% allow breaks between the rows in one group. We put these rows
+% to one box, and process this big box as a big row.
+% \begin{macrocode}
+\newcommand\cals@row@dispatch@span{%
+% \end{macrocode}
+% Output the row to the backup box. If the row is the first
+% row in the span, let its decorations will be the decorations
+% for the future big row. Also, reset the values of leftskip
+% and rightskip to avoid adding them twice, once in a individual
+% row, and once to the common span box.
+% \begin{macrocode}
+\ifvoid\cals@backup@row
+ \setbox\cals@backup@row=\vbox{\box\cals@current@row}%
+ \setbox\cals@backup@cs=\box\cals@current@cs
+ \let\cals@backup@rs@above=\cals@current@rs@above
+ \let\cals@backup@last@rs@below=\cals@last@rs@below
+ \let\cals@backup@context=\cals@last@context
+ \cals@backup@leftskip=\leftskip\relax
+ \cals@backup@rightskip=\rightskip\relax
+ \let\cals@backup@tohsize=\cals@tohsize
+ \leftskip=0pt\relax \rightskip=0pt\relax \def\cals@tohsize{}%
+\else
+ \setbox\cals@backup@row=\vbox{\unvbox\cals@backup@row
+ \cals@issue@row}%
+\fi
+\let\cals@last@rs@below=\cals@current@rs@below
+\let\cals@last@context=\cals@current@context
+% \end{macrocode}
+% If this is the last row of the span, create the fake big row
+% and use the normal dispatcher.
+%
+% \begin{macrocode}
+\cals@ifLastRspanRow\iftrue
+ \setbox\cals@current@row=\box\cals@backup@row
+ \setbox\cals@current@cs=\box\cals@backup@cs
+ \let\cals@current@rs@above=\cals@backup@rs@above
+ \let\cals@last@rs@below=\cals@backup@last@rs@below
+ \let\cals@last@context=\cals@backup@context
+ \leftskip=\cals@backup@leftskip
+ \rightskip=\cals@backup@rightskip
+ \let\cals@tohsize=\cals@backup@tohsize
+ \cals@row@dispatch@nospan
+\fi
+}
+% \end{macrocode}
+% \end{macro}
+
+% \begin{macro}{\cals@backup@row}
+% \begin{macro}{\cals@backup@cs}
+% Boxes and skips for backup.
+% \begin{macrocode}
+\newbox\cals@backup@row
+\newbox\cals@backup@cs
+\newskip\cals@backup@leftskip
+\newskip\cals@backup@rightskip
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+
+% To decide on table breaks and row separation decorations,
+% we need to trace context.
+
+% \begin{macro}{\cals@current@context}
+% The context of the current row. Possible values,
+% set as a "|\let|" to a character:
+% \begin{itemize}
+% \item n: no context, should not happen when the value is required
+% \item h: table header
+% \item f: table footer
+% \item b: table body
+% \end{itemize}
+% \end{macro}
+
+% \begin{macro}{\cals@last@context}
+% The context of the previous row. Possible values, set as a
+% "|\let|" to a character:
+% \begin{itemize}
+% \item n: there is no previous row (not only the start of a table,
+% but also the start of a table chunk)
+% \item h, f, b: table header, footer, body
+% \item r: a last rule of the table (or its chunk) is just output.
+% This status is used to allow multiple calls to |\lastrule|.
+% Probably the use of |current| instead of |last| is more logical,
+% but using |last| is more safe. Who knows if an user decides to use
+% |\lastrule| somewhere in a middle of a table.
+% \end{itemize}
+% \end{macro}
+
+% \begin{macro}{\cals@ifbreak}
+% Table breaks can be manual or automatic. The first is easy,
+% the second is near to impossible if we take into account
+% table headers and footer. The following heuristic seems good.
+%
+% Check if the current row plus the footer fits to the
+% rest of the page. If not, a break is required. This approach
+% is based on two assumptions:
+% \begin{itemize}
+% \item the height of the footer is always the same, and
+% \item any body row is larger than the footer.
+% \end{itemize}
+%
+% More precise and technical description: |\cals@ifbreak| decides
+% if an automatic table break is required and leaves the
+% macro |\cals@iftrue| (yes) or |\cals@iffalse| (no) in the input stream.
+% If the user sets |\cals@tbreak@tokens| (using |\tbreak|),
+% break is forced. Otherwise, no break is allowed:
+% \begin{itemize}
+% \item inside a box (= inside a float)
+% \item In the header
+% \item In the footer
+% \item Immediately after the header
+% \item At the beginning of a chunk of a table.
+% \end{itemize}
+% Otherwise break is recommended when
+% the sum of the height of the current row and of the footer part
+% is greater as the rest height of the page.
+% The implicit first parameter is used for if-fi balancing,
+% see |\cals@iftrue|.
+% \begin{macrocode}
+\newcommand\cals@ifbreak[1]{}
+\def\cals@ifbreak{%
+\let\cals@tmp=\cals@iffalse
+\let\cals@tmpII=\cals@iftrue
+\ifx\relax\cals@tbreak@tokens
+ \ifinner\else
+ \ifx h\cals@current@context \else
+ \ifx f\cals@current@context \else
+ \ifx h\cals@last@context \else
+ \ifx n\cals@last@context \else
+ \dimen0=\pagetotal\relax
+ \advance\dimen0 by \ht\cals@current@row\relax
+ %\showthe\ht\cals@current@row\relax
+ \ifx \cals@tfoot@tokens\relax \else
+ %\show\cals@tfoot@height\relax
+ \advance\dimen0 by \cals@tfoot@height\relax
+ \fi
+ %\showthe\dimen0\relax
+ \ifdim \dimen0>\pagegoal\relax
+ \let\cals@tmp=\cals@tmpII
+ \fi
+ \fi\fi\fi\fi\fi
+\else \let\cals@tmp=\cals@tmpII % tbreak@tokens
+\fi
+\cals@tmp}
+% \end{macrocode}
+% \end{macro}
+
+% \begin{macro}{\cals@issue@break}
+% By default, force a page break, otherwise use user's tokens
+% set by |\tbreak|.
+% \begin{macrocode}
+\newcommand\cals@issue@break{\ifx \relax\cals@tbreak@tokens \penalty-10000 %
+\else \cals@tbreak@tokens \fi
+\let\cals@tbreak@tokens=\relax
+\let\cals@last@context=n}
+% \end{macrocode}
+% \end{macro}
+
+% \begin{macro}{\cals@set@tohsize}
+% \begin{macro}{\cals@tohsize}
+% Table row contains not only the row itself, but also |\leftskip|
+% and |\rightskip|. Now the dilemma. If the row is just |\hbox|,
+% than the glue component is ignored, and the table always aligned
+% left. On the other side, if the row is |\hbox to \hsize|, then
+% the user gets underfulled boxes. A simple solution is to
+% switch on and off the |hsize|-part depending on the skips.
+% \begin{macrocode}
+\newcommand\cals@tohsize{}
+\newcommand\cals@set@tohsize{\def\cals@tohsize{}%
+\ifnum\gluestretchorder\leftskip>0\relax \def\cals@tohsize{to \hsize}\fi
+\ifnum\gluestretchorder\rightskip>0\relax \def\cals@tohsize{to \hsize}\fi
+}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+
+% \begin{macro}{\cals@activate@rtl}
+% \begin{macro}{\cals@deactivate@rtl}
+% \begin{macro}{\cals@hbox}
+% For bidi support, use |\hboxR| instead of |\hbox|.
+% Actually, more is required for bidi support, and
+% these macros are retained only as "legacy".
+% Otherwise I'd move the code into the beginning of `calstable`.
+% \begin{macrocode}
+\newcommand\cals@hbox{}
+\newcommand\cals@activate@rtl{\let\cals@hbox=\hboxR}
+\newcommand\cals@deactivate@rtl{\let\cals@hbox=\hbox}
+\cals@deactivate@rtl
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+
+% \begin{macro}{\cals@issue@rowsep@alone}
+% Typesets the top (or bottom) frame of a table:
+% combines |\cals@current@rs@above| and |\cals@framers@width|
+% and outputs the row separator.
+% \begin{macrocode}
+\newcommand\cals@issue@rowsep@alone{%
+\setbox0=\cals@hbox\cals@tohsize{%
+ \cals@hskip@lr\leftskip\rightskip
+ \cals@rs@sofar@reset
+ \cals@rs@joinOne\cals@framers@width\cals@current@rs@above
+ \cals@rs@sofar@end
+ \cals@hskip@lr\rightskip\leftskip}%
+\ht0=0pt \dp0=0pt \box0 }
+% \end{macrocode}
+% \end{macro}
+
+% \begin{macro}{\cals@issue@rowsep}
+% Combine row separations |\cals@last@rs@below| and |\cals@current@rs@above|,
+% taking into considiration the width of the rule:
+% \begin{itemize}
+% \item n to h, f, b (the top frame): use |\cals@framers@width|
+% and ignore |last@rs@below| because we don't have it
+% \item h to h, b to b, f to f (the usual separator): use |\cals@rs@width|
+% \item for all other combinations (header to body, body to footer),
+% including impossible: use |\cals@bodyrs@width|
+% \end{itemize}
+% \begin{macrocode}
+\newcommand\cals@issue@rowsep{%
+\ifx n\cals@last@context \cals@issue@rowsep@alone \else
+ \ifx \cals@last@context\cals@current@context
+ \let\cals@tmpIII=\cals@rs@width \else
+ \let\cals@tmpIII=\cals@bodyrs@width \fi
+ \setbox0=\cals@hbox\cals@tohsize{%
+ \cals@hskip@lr\leftskip\rightskip
+ \cals@rs@sofar@reset
+ \cals@rs@joinTwo\cals@tmpIII\cals@last@rs@below\cals@current@rs@above
+ \cals@rs@sofar@end
+ \cals@hskip@lr\rightskip\leftskip}%
+ \ht0=0pt \dp0=0pt \box0 %
+\fi}
+% \end{macrocode}
+% \end{macro}
+
+
+% \begin{macro}{\cals@last@row@height}
+% For spanning support, we need to remember the height of the last row
+% \begin{macrocode}
+\newdimen\cals@last@row@height
+% \end{macrocode}
+% \end{macro}
+
+% \begin{macro}{\cals@issue@row}
+% Typesets the current row and its decorations, then updates
+% the last context. Regards |\leftskip| and |\rightskip|
+% by putting them inside the row.
+% \begin{macrocode}
+\newcommand\cals@issue@row{%
+% \end{macrocode}
+% Decorations: first the column separation, then the row separation.
+% \begin{macrocode}
+\nointerlineskip
+\setbox0=\vtop{\cals@hbox\cals@tohsize{\cals@hskip@lr\leftskip\rightskip
+\box\cals@current@cs \cals@hskip@lr\rightskip\leftskip}}%
+\ht0=0pt\relax\box0
+\nointerlineskip
+\cals@issue@rowsep
+\nointerlineskip
+% \end{macrocode}
+% Output the row, update the last context.
+% \begin{macrocode}
+\cals@hbox\cals@tohsize{\cals@hskip@lr\leftskip\rightskip
+\box\cals@current@row \cals@hskip@lr\rightskip\leftskip}%
+\let\cals@last@rs@below=\cals@current@rs@below
+\let\cals@last@context=\cals@current@context
+\nobreak}
+% \end{macrocode}
+% \end{macro}
+
+% \subsection{Table elements}
+
+% \begin{environment}{\calstable}
+% Setup the parameters and let the row dispatcher to do all the work.
+% \begin{macrocode}
+\newenvironment{calstable}[1][\cals@table@alignment]{%
+\if@RTL\@RTLtabtrue\cals@activate@rtl\fi
+\let\cals@thead@tokens=\relax
+\let\cals@tfoot@tokens=\relax
+\let\cals@tbreak@tokens=\relax
+\cals@tfoot@height=0pt \relax
+\let\cals@last@context=n%
+\let\cals@current@context=b%
+\parindent=0pt \relax%
+\cals@setup@alignment{#1}%
+\cals@setpadding{Ag}\cals@setcellprevdepth{Al}\cals@set@tohsize%
+%% Alignment inside is independent on center/flushright outside
+\parfillskip=0pt plus1fil\relax
+\let\cals@borderL=\relax
+\let\cals@borderR=\relax
+\let\cals@borderT=\relax
+\let\cals@borderB=\relax
+% \end{macrocode}
+% Bug fix for \url{http://tex.stackexchange.com/questions/167400/fancyhdr-and-cals-vertical-merge-problem}.
+% Table inside a table is ok, but when there are 1) page break inside
+% a table in the text flow, and 2) a table with a vertically straddled
+% cell is created in the output procedure, then this table inside table
+% needs additional cleaning.
+% \begin{macrocode}
+\setbox\cals@backup@row=\box\voidb@x\relax
+\cals@AtBeginTable
+}{% End of the table
+\cals@tfoot@tokens\lastrule\cals@AtEndTable}
+% \end{macrocode}
+% \end{environment}
+
+% \begin{macro}{\cals@AtBeginTable}
+% \begin{macro}{\cals@AtEndTable}
+% Callbacks for more initialization possibilities.
+% \begin{macrocode}
+\newcommand\cals@AtBeginTable{}%
+\newcommand\cals@AtEndTable{}%
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+
+% \begin{macro}{\lastrule}
+% Typesets the last rule (bottom frame) of a table chunk.
+% Repeatable calls are ignored.
+% Useful in the macro |\tfoot|.
+% \begin{macrocode}
+\newcommand\lastrule{%
+\ifx r\cals@last@context \relax \else
+ \let\cals@last@context=r%
+ \nointerlineskip
+ \let\cals@current@rs@above=\cals@last@rs@below\cals@issue@rowsep@alone%
+\fi}
+% \end{macrocode}
+% \end{macro}
+
+% \begin{macro}{\thead}
+% Table: the header. Remember for later use, typeset right now.
+% \begin{macrocode}
+\newcommand\thead[1]{%
+\def\cals@thead@tokens{\let\cals@current@context=h%
+#1\let\cals@current@context=b}%
+\cals@thead@tokens}
+% \end{macrocode}
+% \end{macro}
+
+% \begin{macro}{\tfoot}
+% Table: the footer. Remember for later use. Right now, typeset
+% to a box to calculate an expected height for the table breaker
+% |\cals@ifbreak|.
+% \begin{macrocode}
+\newcommand\tfoot[1]{%
+\def\cals@tfoot@tokens{\let\cals@current@context=f#1}%
+\setbox0=\vbox{\cals@tfoot@tokens}%
+\cals@tfoot@height=\ht0 \relax}
+% \end{macrocode}
+% \end{macro}
+
+% \begin{macro}{\cals@tfoot@height}
+% The height of the footer.
+% \begin{macrocode}
+\newdimen\cals@tfoot@height
+% \end{macrocode}
+% \end{macro}
+
+% \begin{macro}{\tbreak}
+% Table: force a table break. Argument should contain something
+% like |\penalty-10000 |.
+% \begin{macrocode}
+\newcommand\tbreak[1]{\def\cals@tbreak@tokens{#1}}
+% \end{macrocode}
+% \end{macro}
+
+% \begin{macro}{\cals@table@alignment}
+% The default alignment of tables in the text flow.
+% Doesn't affect the text alignment inside cells.
+% \begin{itemize}
+% \item |n|: no settings, the default |\leftskip| and |\rightskip| are used
+% \item |l|: align left
+% \item |c|: align center
+% \item |r|: align right
+% \end{itemize}
+% This setting is appeared in the version 2.3. Earlier versions
+% worked as it were |n|.
+% \begin{macrocode}
+\newcommand\cals@table@alignment{l}
+% \end{macrocode}
+% \end{macro}