How to style a table without messing up tables source code?

It is easy to style table separator lines/rules with a command outside of the table environment.

Minimum Working Example:

\documentclass{article}
\usepackage{booktabs}
\usepackage{colortbl}

\begin{document}

\arrayrulecolor{blue}

\begin{tabular}{l}
\toprule
\midrule
abc\\
def\\
\bottomrule
\end{tabular}

\end{document}


However, would it be possible to style rows or grids of the table outside of the table environment as well? For example: Place specifications with styles for grids and row background colors before the table environment to keep the table source code clean?

In fact I want to build some nice looking invoice table like this:

The reason why I want to keep the source code clean is simply the fact that I copy the table content out of an external calculation program (LibreOffice Calc) into LaTeX, so I do not want to adjust all rows manually by hand afterwards. In best case the table source code does not contain any style specifications, only mandatory elements like & and \\ at the end of the row (what I can insert in my calculation program already).

The mentioned output of my calculation program is preformatted for LaTeX and looks like that:

\toprule
\textbf{Position}   &   \textbf{Date}   &   \textbf{Work type}  &   \textbf{Duration}   &   \textbf{Hourly rate}    &   \textbf{TOTAL}    \\
\midrule
1   &   05/04/2018  &   Cleaning the balcony    &   \SI{4}{\hour}   &   \SI{10}{\euro\per\hour} &   \SI{40}{\euro}  \\
2   &   05/04/2019  &   Mowing the meadow   &   \SI{7.5}{\hour} &   \SI{10}{\euro\per\hour} &   \SI{75}{\euro}  \\
3   &   05/04/2020  &   Planting some flowers   &   \SI{5.5}{\hour} &   \SI{10}{\euro\per\hour} &   \SI{55}{\euro}  \\
4   &   05/04/2021  &   Producing ice cream &   \SI{10.5}{\hour}    &   \SI{10}{\euro\per\hour} &   \SI{105}{\euro} \\
5   &   05/04/2022  &   Cleaning the television &   \SI{5.5}{\hour} &   \SI{10}{\euro\per\hour} &   \SI{55}{\euro}  \\
6   &   05/04/2023  &   Building a pond &   \SI{9}{\hour}   &   \SI{10}{\euro\per\hour} &   \SI{90}{\euro}  \\
7   &   05/04/2024  &   Feeding the fishes  &   \SI{8.5}{\hour} &   \SI{10}{\euro\per\hour} &   \SI{85}{\euro}  \\
8   &   05/04/2025  &   Cutting the trees   &   \SI{7}{\hour}   &   \SI{10}{\euro\per\hour} &   \SI{70}{\euro}  \\
9   &   05/04/2026  &   Watering the flowers    &   \SI{3}{\hour}   &   \SI{10}{\euro\per\hour} &   \SI{30}{\euro}  \\
\midrule
&       &       &   Summary:            &   \SI{1000}{\euro}  \\
\bottomrule


As you can see it would be nice to just copy-and-paste this block into LaTeX without inserting additional style specifications manually by hand.

Complete Minimum Working Example (MWE):

\documentclass{article}
\usepackage[english, ngerman]{babel}
\usepackage{tabularx}
\usepackage{booktabs}
\usepackage[per=slash,locale=DE]{siunitx}

\sisetup{round-mode = places, round-precision = 2, round-integer-to-decimal}
\newunit{\euro}{EUR}

\begin{document}

\begin{tabularx}{1\textwidth}{c c p{5cm} r r r}

\toprule
\textbf{Position}   &   \textbf{Date}   &   \textbf{Work type}  &   \textbf{Duration}   &   \textbf{Hourly rate}    &   \textbf{TOTAL}    \\
\midrule
1   &   05/04/2018  &   Cleaning the balcony    &   \SI{4}{\hour}   &   \SI{10}{\euro\per\hour} &   \SI{40}{\euro}  \\
2   &   05/04/2019  &   Mowing the meadow   &   \SI{7.5}{\hour} &   \SI{10}{\euro\per\hour} &   \SI{75}{\euro}  \\
3   &   05/04/2020  &   Planting some flowers   &   \SI{5.5}{\hour} &   \SI{10}{\euro\per\hour} &   \SI{55}{\euro}  \\
4   &   05/04/2021  &   Producing ice cream &   \SI{10.5}{\hour}    &   \SI{10}{\euro\per\hour} &   \SI{105}{\euro} \\
5   &   05/04/2022  &   Cleaning the television &   \SI{5.5}{\hour} &   \SI{10}{\euro\per\hour} &   \SI{55}{\euro}  \\
6   &   05/04/2023  &   Building a pond &   \SI{9}{\hour}   &   \SI{10}{\euro\per\hour} &   \SI{90}{\euro}  \\
7   &   05/04/2024  &   Feeding the fishes  &   \SI{8.5}{\hour} &   \SI{10}{\euro\per\hour} &   \SI{85}{\euro}  \\
8   &   05/04/2025  &   Cutting the trees   &   \SI{7}{\hour}   &   \SI{10}{\euro\per\hour} &   \SI{70}{\euro}  \\
9   &   05/04/2026  &   Watering the flowers    &   \SI{3}{\hour}   &   \SI{10}{\euro\per\hour} &   \SI{30}{\euro}  \\
\midrule
&       &       &   Summary:            &   \SI{1000}{\euro}  \\
\bottomrule

\end{tabularx}

\end{document}

• Can you post a complete minimal working example that shows a (very simple example) of the "table code from the external calculation program". It's difficult to put scaffolding around code when you don't know what it looks like -- your current MWE does not contain enough columns to match the invoice. I would think that a better approach would be to have the "external program" write the complete latex file, without the need to cut-and-paste anything but, presumably, you cannot easily modify the output from the "external program"?
– user30471
Aug 21 '18 at 23:34
• @Andrew: Thanks a lot for your request! I've updated my question...
– Dave
Aug 21 '18 at 23:44

Here's a quick mock-up that shows how you might do this to produce:

and here is the code:

\documentclass{article}
\usepackage[svgnames,table]{xcolor}
\usepackage[margin=10mm,a4paper]{geometry}
\usepackage[english, ngerman]{babel}
\usepackage{tabularx}
%\usepackage{booktabs}
\usepackage[per=slash,locale=DE]{siunitx}
\usepackage{colortbl}
\usepackage{expl3}
\usepackage{environ}

\sisetup{round-mode = places, round-precision = 2, round-integer-to-decimal}
\newunit{\euro}{EUR}

\newcolumntype{A}{>{\columncolor{Gainsboro}\raggedright}p{22mm}}
\newcolumntype{B}{>{\columncolor{Gainsboro}\raggedright}p{50mm}}
\newcolumntype{C}{>{\columncolor{Gainsboro}}p{30mm}}

\newcommand\invoicetop{\textcolor{blue}{Nice stuff at the top of the invoice}}
\newcommand\invoicebot{\textcolor{blue}{Nice stuff at the bottom of the invoice}}

\ExplSyntaxOn
\seq_new:N \l_invoice_seq
\tl_new:N \l_table_tl
\NewEnviron{invoice}{%
% break the table up into its pieces
\regex_split:noN { (?:\c{toprule}|\c{midrule}|\c{bottomrule}) } {\BODY} \l_invoice_seq
% initialise the table contents using the row colour
\tl_set:Nn \l_table_tl { \rowcolor{LimeGreen} }
\seq_pop_left:NN \l_invoice_seq \l_tmpa_tl
% if \l_tmpa_tl is empty then pop again
\tl_if_blank:VT \l_tmpa_tl { \seq_pop_left:NN \l_invoice_seq \l_tmpa_tl }
\tl_put_right:No \l_table_tl {\l_tmpa_tl}
% now pop and print the table body
\seq_pop_left:NN \l_invoice_seq \l_tmpa_tl
\tl_put_right:No \l_table_tl {\l_tmpa_tl}
% finally add the table footer in green
\tl_put_right:Nn \l_table_tl { \rowcolor{LimeGreen} }
\seq_pop_left:NN \l_invoice_seq \l_tmpa_tl
\tl_put_right:No \l_table_tl {\l_tmpa_tl}
% and now print the invoice
\noindent\invoicetop
\newline
\begin{tabularx}{\textwidth}{AABAAC}%\toprule
\tl_use:N \l_table_tl
\end{tabularx}
\newline
\noindent\invoicebot
}
\cs_generate_variant:Nn \regex_split:nnN {noN}
\ExplSyntaxOff

\arrayrulecolor{blue}

\begin{document}

\begin{invoice}
\toprule
\textbf{Position} & \textbf{Date}        & \textbf{Work~type}  &
\textbf{Duration} & \textbf{Hourly~rate} & \textbf{Summarized~rate}
\\
\midrule
1 & 05/04/2018 & Cleaning the balcony    & \SI{4}{\hour}   & \SI{10}{\euro\per\hour} & \SI{40}{\euro}  \\
2 & 05/04/2019 & Mowing the meadow       & \SI{7.5}{\hour} & \SI{10}{\euro\per\hour} & \SI{75}{\euro}  \\
3 & 05/04/2020 & Planting some flowers   & \SI{5.5}{\hour} & \SI{10}{\euro\per\hour} & \SI{55}{\euro}  \\
4 & 05/04/2021 & Producing ice cream     & \SI{10.5}{\hour}& \SI{10}{\euro\per\hour} & \SI{105}{\euro} \\
5 & 05/04/2022 & Cleaning the television & \SI{5.5}{\hour} & \SI{10}{\euro\per\hour} & \SI{55}{\euro}  \\
6 & 05/04/2023 & Building a pond         & \SI{9}{\hour}   & \SI{10}{\euro\per\hour} & \SI{90}{\euro}  \\
7 & 05/04/2024 & Feeding the fishes      & \SI{8.5}{\hour} & \SI{10}{\euro\per\hour} & \SI{85}{\euro}  \\
8 & 05/04/2025 & Cutting the trees       & \SI{7}{\hour}   & \SI{10}{\euro\per\hour} & \SI{70}{\euro}  \\
9 & 05/04/2026 & Watering the flowers    & \SI{3}{\hour}   & \SI{10}{\euro\per\hour} & \SI{30}{\euro}  \\
\midrule
&            &                         &                 &  Summary:               &   \SI{1000}{\euro}  \\
\bottomrule
\end{invoice}

\end{document}


All that I have done is to replace your table with an invoice environment. This environment uses \regex_split from LaTeX3 to split the environment contents around the \toprule, \midrule and \bottomrule commands to access the table header, footer and table content. The header and footer are then given a green background and the table contents a gray background. The code contains macros \invoicetop and \invoicebot for the material at the top and bottom of the invoice that you should replace with whatever you want to put there.

The cell background are controlled by defining some new column types:

\newcolumntype{A}{>{\columncolor{Gainsboro}\raggedright}p{22mm}}
\newcolumntype{B}{>{\columncolor{Gainsboro}\raggedright}p{50mm}}
\newcolumntype{C}{>{\columncolor{Gainsboro}}p{30mm}}


I am a big fan of the booktabs package but I have not used it because it does not play well with coloured cells. Also, the code is a bit of a hack, and is harder than it should be, because I have assumed that you have no control over the format of the table that is produced by your "mysterious external program". If you are able to customise the output of this program then the macro design becomes much easier.

Finally, if you end up using something like this then I would put all of the macros and \usepakage{...} commands into a separate style file, for example invoice.sty, after which your invoice becomes the more tractable

\documentclass{article}
\usepackage{invoice}

\begin{document}

\begin{invoice}
...
\end{invoice}

\end{invoice}


Of course, if you can control the output of the "external program" then you can make it print exactly this into a file (and automatically run pdflatex for you and ...).

It's easy with {NiceTabular} of nicematrix.

\documentclass[landscape]{article}
\usepackage{geometry}
\geometry{left=1cm,right=1cm}
\usepackage{xcolor}
\usepackage{nicematrix,tikz}

\begin{document}

\begin{center}
\begin{NiceTabular}{*{24}{c}}[first-row,first-col,hvlines,columns-width=auto,name=A]
\CodeBefore
\arraycolor{lightgray}
\Body
edge &
0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 & 13 & 14 & 15 & 16 & 17 & 18 & 19 & 21 & 21 & 22 & 23\\
destination &
\RowStyle{\itshape}
b & d & e & a & c & e & b & e & g & a & e & f & a & b & c & d & f & g & d & e & g & c & e & f \\
weight &
4 & 12 & 9 &  4 & 8 & 7 & 8 & 5 & 2 & 12 & 3 & 6 & 9 & 7 & 5 & 3 & 1 & 13 & 6 & 1 & 11 & 2 & 13 & 11
\end{NiceTabular}
\end{center}

\vspace{3cm}

\begin{center}
\begin{NiceTabular}{*{7}{c}}[first-col,last-row,hvlines,columns-width=auto,name=B]
\CodeBefore
\arraycolor{lightgray}
\Body
first edge &
0 & 3 & 6 & 9 & 12 & 18 & 21\\
outdegree &
3 & 3 & 3 & 3 & 6 & 3 & 3\\
vertex &
\RowStyle{\itshape}
a & b & c & d & e & f & g\\
\end{NiceTabular}
\end{center}

\tikzset{A/.style = {name prefix = A-} , B/.style = {name prefix = B-}}

\begin{tikzpicture}[remember picture, overlay]
\draw [A]  (3-|1)  { [B] -- (1-|1) }
(3-|4)  { [B] -- (1-|2) }
(3-|7)  { [B] -- (1-|3) }
(3-|10) { [B] -- (1-|4) }
(3-|12) { [B] -- (1-|5) }
(3-|18) { [B] -- (1-|6) }
(3-|21) { [B] -- (1-|7) }
(3-|24) { [B] -- (1-|8) } ;
\end{tikzpicture}

\end{document}


You need several compilations (because nicematrix uses PGF/Tikz nodes under the hood).