2

I want utility macros that allow me to change (a) the thickness and/or (b) color of \hlines or \clines in tabulars. I want to do so without redefining \arrayrulewidth but rather by modifying the existing \hline and \cline macros to use my desired thickness in place of \arrayrulewidth.

I first did this for \hline by adding an argument for the thickness of the rule to Martin Scharrer's answer's \thickhline. See \hlineThickness below. I completed the goal by adding a second argument for a color; see \hlineThicknessColor below. That worked as desired.

I hoped to do the same for \cline. I used Masroor's answer to first define \clineThickness with two arguments; the first for the range of columns for the \cline and the second for the width of the rule. \clineThickness invokes a macro \@ClineThickness with three arguments. The first argument of \clineThickness is apparently parsed to become two arguments, one for the initial column and one for the final column. That worked as hoped: I could vary the thickness of a black \cline.

Where I ran into trouble was when I tried to add a new, third argument for a color to define \clineThicknessColor. First, I don't know TeX at all, so I don't follow how \cline seems to parse the first argument (containing a range of columns) into two separate arguments.

Nevertheless, I attempted to add a third argument by simply tacking on #3 (a) to the definition of \clineThickness and (b) to the invocation of \@ClineThickness, and adding #4 to the definition of \@ClineThickness. However, without even trying to make use of the new argument, I get the error "Illegal unit of measure (pt inserted)."

An MWE is below. Two lines are commented out. First, I commented out \clineThicknessColor{2-3}{1.5pt}{blue} so that it will compile.

Second, I commented out \leaders\color{#4}\hrule\@height#3\hfill just to indicate how I'm planning to use the new color argument if I can first successfully add that argument.

What am I doing wrong in my attempt to add a color argument to \clineThicknessColor?

\documentclass{article}
\usepackage{xcolor}

\makeatletter
\newcommand{\hlineThickness}[1]{%
%   Example usage: \hlineThickness{2pt}
%   Inspired by Martin Scharrer's answer https://tex.stackexchange.com/a/41761/7922
    \noalign {\ifnum 0=`}\fi \hrule height #1
    \futurelet \reserved@a \@xhline
}

\newcommand{\hlineThicknessColor}[2]{%
%   Example usage: \hlineThicknessColor{0.6pt}{red}
    \noalign {\ifnum 0=`}\fi \color{#2}\hrule height #1
    \futurelet \reserved@a \@xhline
}

\def\clineThickness#1#2{\@ClineThickness#1#2\@nil}
%   Example usage: \ClineThickness{2-5}{2.0pt}
%   Source: Masroor's answer https://tex.stackexchange.com/a/173535/7922
\def\@ClineThickness#1-#2#3\@nil{%
  \omit
  \@multicnt#1%
  \advance\@multispan\m@ne
  \ifnum\@multicnt=\@ne\@firstofone{&\omit}\fi
  \@multicnt#2%
  \advance\@multicnt-#1%
  \advance\@multispan\@ne
  \leaders\hrule\@height#3\hfill
  \cr}

\def\clineThicknessColor#1#2#3{\@ClineThicknessColor#1#2#3\@nil}
%   Example usage: \clineThicknessColor{2-3}{1.5pt}{blue}
\def\@ClineThicknessColor#1-#2#3#4\@nil{%
    \omit
    \@multicnt#1%
    \advance\@multispan\m@ne
    \ifnum\@multicnt=\@ne\@firstofone{&\omit}\fi
    \@multicnt#2%
    \advance\@multicnt-#1%
    \advance\@multispan\@ne
    \leaders\hrule\@height#3\hfill
%   \leaders\color{#4}\hrule\@height#3\hfill
    \cr}
\makeatother

\begin{document}
\begin{tabular}{c | c | c | c}
A & B & C & D\\
\hlineThickness{2pt}
E & F & G & H\\
\hlineThicknessColor{0.6pt}{red}
I & J & K & L\\
\clineThickness{2-3}{1.5pt}
M & N & O & P\\
%\clineThicknessColor{2-3}{1.5pt}{blue}
\end{tabular}
\end{document}

enter image description here

2

You almost had it, you just have to rearrange the argument specification for \@ClineThicknessColor a little bit and put extra braces around the arguments.

Let me explain how parsing the dash in a macro works. When you define a command with \def, you give an argument specification which is a pattern that tex expects to see after you use the command. For example if I say \def\test#1-#2{...} then when I use the command \test, latex expects to see some number of (brace balanced) arbitrary tokens followed by a dash, followed by a dash, followed by one token or braced argument. For instance, if I then say \test abc-def then argument #1 will be abc, argument #2 will be d and ef will be left on the input stream. If I say \test {abc}-{def} then argument #1 is abc and argument #2 is def (note that the braces have been removed from the arguments). If I say \test{abc-def} then tex doesn't see the dash in the braces and keeps looking for a dash. If it doesn't find a dash it throws an error.

Thus, there is a standard trick to parse an argument like {abc-def}: we use the fact that braces are removed from around an argument, so I say:

\def\mycommand#1{\myhelpercommand#1\@nil}
\def\myhelpercommand#1-#2\@nil{...} % do stuff here

The point of the token \@nil is to serve as a delimiter for the second argument -- with the braces removed, tex can no longer tell where the argument ends without some token to serve as the endpoint. Now if I want to use this pattern with a multiargument command, I need to remember to replace the braces tex removes from the later arguments, and I should put them after the \@nil:

\def\mycommand#1#2#3{\myhelpercommand#1\@nil{#2}{#3}}
\def\myhelpercommand#1-#2\@nil#3#4{...} % do stuff here

Here's the code with this fix added:

\documentclass{article}
\usepackage{xcolor}

\makeatletter
\newcommand{\hlineThickness}[1]{%
%   Example usage: \hlineThickness{2pt}
%   Inspired by Martin Scharrer's answer https://tex.stackexchange.com/a/41761/7922
    \noalign {\ifnum 0=`}\fi \hrule height #1
    \futurelet \reserved@a \@xhline
}

\newcommand{\hlineThicknessColor}[2]{%
%   Example usage: \hlineThicknessColor{0.6pt}{red}
    \noalign {\ifnum 0=`}\fi \color{#2}\hrule height #1
    \futurelet \reserved@a \@xhline
}

\def\clineThickness#1#2{\@ClineThickness#1#2\@nil}
%   Example usage: \ClineThickness{2-5}{2.0pt}
%   Source: Masroor's answer https://tex.stackexchange.com/a/173535/7922
\def\@ClineThickness#1-#2#3\@nil{%
  \omit
  \@multicnt#1%
  \advance\@multispan\m@ne
  \ifnum\@multicnt=\@ne\@firstofone{&\omit}\fi
  \@multicnt#2%
  \advance\@multicnt-#1%
  \advance\@multispan\@ne
  \leaders\hrule\@height#3\hfill
  \cr}

\def\clineThicknessColor#1#2#3{\@ClineThicknessColor#1\@nil{#2}{#3}}
%   Example usage: \clineThicknessColor{2-3}{1.5pt}{blue}
\def\@ClineThicknessColor#1-#2\@nil#3#4{%
    \omit
    \@multicnt#1%
    \advance\@multispan\m@ne
    \ifnum\@multicnt=\@ne\@firstofone{&\omit}\fi
    \@multicnt#2%
    \advance\@multicnt-#1%
    \advance\@multispan\@ne
    \color{#4}
    \leaders\hrule\@height#3\hfill
    \leaders\hrule\@height#3\hfill
    \cr}
\makeatother

\begin{document}
\begin{tabular}{c | c | c | c}
A & B & C & D\\
\hlineThickness{2pt}
E & F & G & H\\
\hlineThicknessColor{0.6pt}{red}
I & J & K & L\\
\clineThickness{2-3}{1.5pt}
M & N & O & P\\
\clineThicknessColor{2-4}{1.5pt}{blue}
\end{tabular}
\end{document} 
  • Thanks! I have no idea what \@nil accomplishes or how you knew to move it around, but I appreciate your providing the correct magical incantation! (My next commented line doesn't work, but that'll be the subject of a separate question.) – Jim Ratliff Aug 25 '18 at 15:08
  • I'm working on an explanation which should clarify. – Hood Chatham Aug 25 '18 at 15:09
  • Correction: You also fixed my incorrect use of \color, so I don't need a new question either. Double thanks! – Jim Ratliff Aug 25 '18 at 15:13
  • You could upvote my answer =P – Hood Chatham Aug 25 '18 at 15:14
  • Your additional commentary/explanation is very helpful. (But it's still seems like magic!) – Jim Ratliff Aug 25 '18 at 15:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.