I boiled down the problem to much more simple MWE. See the update below.
I tried to customize the tabular environment to make it easy to use a coherent style throughout my thesis. I wanted for the tabular environment to be able to
- automatically add
\addlinespace
after every line in the table body (except for the last one) - automatically do alternating row colors - not for individual lines, but for groups of lines (mostly 3, somewhere else on stackexchange this was recommended
I know there is \arraystretch
, but this also affects the header, and thus it looks awkward if the two types of tables are used close each other. I also know that the \rowcolors
command exists, but that does not allow to colorize groups of lines (to the best of my knowledge).
I did all those modifications inside a custom environment, and I can select which one (or both) of the modifications with a command. Both options work, but unfortunatelly the row coloring has a strange effect on the next tabular environment, i.e., not the one that is modified by me.
I prepared a minimal working example:
\documentclass[a4paper,11pt]{book}
\usepackage[T1]{fontenc}
\usepackage[english]{babel}
\usepackage{booktabs}
\usepackage[table]{xcolor}
\usepackage{ifthen}
\usepackage{calc}
\input{tables}
\begin{document}
\begin{stable}
\srowgroupcolors{2}{white}{orange}
\begin{tabular}{ccc}
\toprule
a & b & c\\
\midrule
0 & 0 & 0 \\
0 & 1 & 1 \\
1 & 0 & 1 \\
1 & 1 & 0 \\
\bottomrule
\end{tabular}
\end{stable}
\begin{stable}
\begin{tabular}{ccc}
\toprule
a & b & c\\
\midrule
0 & 0 & 0 \\
0 & 1 & 1 \\
1 & 0 & 1 \\
1 & 1 & 0 \\
\bottomrule
\end{tabular}
\end{stable}
\begin{stable}
\begin{tabular}{ccc}
\toprule
a & b & c\\
\midrule
0 & 0 & 0 \\
0 & 1 & 1 \\
1 & 0 & 1 \\
1 & 1 & 0 \\
\bottomrule
\end{tabular}
\end{stable}
\end{document}
The \srowgroupscolors{2}{white}{orange}
command in the first table activates the row coloring. The second table shows the problem in the first column of the first line (after the \toprule). The third table works as excepted. Note that the problem still persists even if the second tabular environment is used without the surrounding stable environment.
The result looks like this (for the 2nd table inside the stable environment):
The code for stable is defined in the file tables.tex
below. I removed the part that is used for the automatic line spacing, because it causes no problems. It's still rather long (although it consists or more comments than code).
What it does is the following: it counts the table rows, but only for the body, i.e., after \midrule
has been used. While in the header, the counter stays at zero. For each body row, \stable@color@step
is called by the overridden \@arraycr
command. (Note that stable@color@step
is defined in \srowgroupcolors
, otherwise it's empty.) It calls \stable@calc@rowcolor
(in a \noalign
command) that calculates the color based for the next row and stores the corresponding \rowcolor
command in \stable@rowcolor
. The latter is called directly after the \noalign
command. Therefore the \rowcolor
command for the next line is issued immediately after the \\
.
\makeatletter
% counter for the line number (modulo 2 * group@size)
\newcounter{stable@counter}
% size of groups (row coloring)
\newcounter{stable@group@size}
% modulo value, used to get the right color
\newcounter{stable@modulo}
% whether array / tabular have already been patched
\newboolean{stable@patched}
% replacement for table
% arguments:
% #1 - placement - default is !htb
\newenvironment{stable}[1][!htb]{%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% internal commands for auto spacing and coloring of rows
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% set the group size and calculate the modulo
\newcommand*{\stable@set@group@size}[1]{%
\setcounter{stable@group@size}{##1}%
\setcounter{stable@modulo}{2*\value{stable@group@size}}%
}%
%
% calculate the rowcolor based on the row number
% step the rownumber (module 2 * group size) if > 0 (body)
% TODO refactor: extract a step method
\newcommand{\stable@calc@rowcolor}{%
\ifthenelse{\value{stable@counter}=0}%
% we're still in the header
{\gdef\stable@rowcolor{}}%
% we're in the body
{%
% 1 - group size: color 1, above group size: color 2
\ifthenelse{\value{stable@counter}>\value{stable@group@size}}%
{\gdef\stable@rowcolor{\rowcolor{stable@color2}}}%
{\gdef\stable@rowcolor{\rowcolor{stable@color1}}}%
% increase counter -> number of the NEXT row
\stepcounter{stable@counter}%
% modulo operation
\ifthenelse{\value{stable@counter}>\value{stable@modulo}}%
% reset to 1 (sic!), as 0 would be header
{\setcounter{stable@counter}{1}}%
{}%
}%
}%
%
% shortcut: call stable@calc@color in a \noalign, use the calculated color
% the default implementation does nothing, the command has to be changed
\def\stable@color@step{}
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% modify the array / tabular package
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% backup the original array / tabular commands
\newcommand{\stable@arraycr}{}%
\let\stable@arraycr\@arraycr%
\newcommand{\stable@midrule}{}%
\let\stable@midrule\midrule%
%
% patch the array / tabular commands
% lazy patching makes sure this does not break anything
\newcommand*{\stable@patch@array}{%
\ifthenelse{\boolean{stable@patched}}%
% already patched, do nothing
{}%
{%
\setboolean{stable@patched}{true}%
% replace the \\ command (@arraycr): integrate coloring and spacing
\renewcommand{\@arraycr}{%
\stable@arraycr%
\stable@color@step%
}%
%
% replace \midrule: start coloring and spacing
\renewcommand{\midrule}{%
\stable@midrule%
% set counter to 1 (end of heading}
\noalign{%
\setcounter{stable@counter}{1}%
}%
% start coloring (1st row)
\stable@color@step%
}%
}%
}%
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% external commands for tables
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% alternating colored groups of rows
% arguments:
% #1 - group size
% #2 - first color (tables always start with this color)
% #3 - second color
\newcommand{\srowgroupcolors}[3]{%
% shortcut: call stable@calc@color in a \noalign, use the calculated color
\def\stable@color@step{\noalign{\stable@calc@rowcolor}\stable@rowcolor}%
% store the settings
\stable@set@group@size{##1}%
\colorlet{stable@color1}{##2}%
\colorlet{stable@color2}{##3}%
% patch the array commands
\stable@patch@array
}%
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% configuration / defaults
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% prepare row counter
% (count rows modulo (2 * colored group size), value 0 means: header)
\setcounter{stable@counter}{0}%
% initialize with default values
\stable@set@group@size{1}%
% array / tabular commands have not yet been patched
\setboolean{stable@patched}{false}%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% start of the actual table
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% use small sized sans serif font
\begin{table}[#1]%
}{%
\end{table}%
}%
\makeatother
Somehow my code must have managed to break something in tabular although everything is done inside the stable group. The only exception to this is the use of \gdef
in the \stable@calc@rowcolor
that is used to temporarily store the \addlinespace
(if required). I used this workaround (taken from this question) to avoid the !Misplaced \noalign
error.
Has anybody an idea what can be the source of this problems?
Update
The following code triggers the same problem - this is exactly what my tabular extension does:
\documentclass[a4paper,11pt]{book}
\usepackage[T1]{fontenc}
\usepackage[english]{babel}
\usepackage{booktabs}
\usepackage[table]{xcolor}
\begin{document}
\begin{table}
\begin{tabular}{ccc}
\toprule
a & b & c\\
\midrule
\rowcolor{white} 0 & 0 & 0 \\
\rowcolor{white} 0 & 1 & 1 \\
\rowcolor{orange} 1 & 0 & 1 \\
\rowcolor{orange} 1 & 1 & 0 \\
\rowcolor{orange} \bottomrule
\end{tabular}
\end{table}
\begin{table}
\begin{tabular}{ccc}
\toprule
a & b & c\\
\midrule
0 & 0 & 0 \\
0 & 1 & 1 \\
1 & 0 & 1 \\
1 & 1 & 0 \\
\bottomrule
\end{tabular}
\end{table}
\begin{table}
\begin{tabular}{ccc}
\toprule
a & b & c\\
\midrule
0 & 0 & 0 \\
0 & 1 & 1 \\
1 & 0 & 1 \\
1 & 1 & 0 \\
\bottomrule
\end{tabular}
\end{table}
\end{document}
This is triggered by adding the \rowcolor
command directly after the \\
(using either \everycr
or by overwriting \@arraycr
). At that point it is not clear if another row follows. For some reason, the tabular environment does not like a \rowcolor
in the last line, if the last line is either a \bottomrule
or a \hline
.
Is there a way to fix this problem? Does tabular offer a hook at the beginning of a row that I can use instead? Is it possible to check that a row does not contain a \bottomrule
?
Or is there a way to "undo" the \rowcolor
command in an overwritten \bottomrule
? Actually this is what I do to get the spacing part (not shown in the example) working.
Update 2
I further investigated the issue. It boils down to a problem in colortbl. When \rowcolor
is used without being followed by a \\
, the next tabular shows the error. The most minimal example I found is the following:
\documentclass{article}
\usepackage[english]{babel}
\usepackage{colortbl}
\begin{document}
\begin{tabular}{c}
a \\
\rowcolor{orange}
\end{tabular}
\begin{tabular}{c}
a \\
\end{tabular}
\end{document}
The result looks like this:
To me this seems to be a bug in colortbl. Even if this "trailing" \rowcolor
it totally pointless, it should not have an effect on the next tabular.
I tried to look at how \rowcolors
solves the problem - it does not seem to generate such a "trailing" rowcolor
command, but I do not understand the source completely.
My workaround is to create the next tabular in a savebox that is then discarded. The tabular created after the savebox does not show the problem.
\documentclass{article}
\usepackage[english]{babel}
\usepackage{colortbl}
\begin{document}
\begin{tabular}{c}
a \\
\rowcolor{orange}
\end{tabular}
\newsavebox{\bin}
\savebox{\bin}{\tabular{c}\\\endtabular}
\begin{tabular}{c}
a \\
\end{tabular}
\end{document}
Anyway, if there is a better way that fixes my extension I'd happily use that instead of this ugly workaround.
\everycr
to insert your colors instead of redefining\@arraycr
. See e.g. tex.stackexchange.com/questions/89324/…\@arraycr
.