2

So, I am using the tabu package (which I need to use over tabular others for several reasons) to make a long table spanning several pages. This table contains currency amounts in a column, and I'd like to have a subtotal of the column at the start of each page.

After a number of trials using lengths to store values (which I found out is not suitable, as the range of lengths is too limited for the size of the values I need to use), I settled on using the fp package.

Also, after some iterations, I found the simplest approach seems to be using tabu's capabilities to define a different header for the first page and the following ones, putting the variable I'm using as an accumulator inside the header.

However, I'm seeing that the variable is expanded at the end of the total calculation, and not at the moment when the header is used on each page. In the following MWE, the grand total is correctly calculated as 4700, however the header in the second page shows 4700 as the subtotal although the header appears when it should still be 4300 :

\documentclass[11pt,a4paper]{article}
\usepackage{fp}
\usepackage{tabu}
\usepackage{longtable}

\FPset\total{0}

\newcommand{\summa}[1]{#1%
\FPadd\0\total{#1}\global\let\total\0%
}

\newcommand{\resettotal}{%
\FPset\0{0}\global\let\total\0%
}

\begin{document}

\begin{longtabu} to \textwidth {X[l]}
Amount \endfirsthead
Amount \\
subtotal \FPeval{\result}{round(total,2)}\result \endhead
\resettotal \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} \\
 \summa{100.00} 
\end{longtabu}

The global total is \FPeval{\result}{round(total,2)}\result

\end{document}

From my limited understanding of how tabu works internally, this might be because headers are only positioned after the whole table is done (when tabu knows exactly how long the table will be). But is there a way I could tell the interpreter to expand the \total variable at the point where the header is used?

Thanks, cheers.

3

I could not find a way to put dynamic content in the header: I made a few attempts but each time it appeared the header was evaluated only once; I did not investigate in the source code (not only tabu but also longtable's code is used).

Thus, I developed an alternative approach where the header is only used to reserve some space, and the partial sums are inscribed there by the first table row on each new page. To detect if a table row is first on a page, I use the standard system of \label's and \ref's. I had tried before some other approaches with \pagetotal or \pdfsavepos, to no avail.

The update makes the page number extraction hyperref compatible.

A novel update adds a second version of the code. A complete revision was needed to cope with the following problem: the page number with the \label/\ref mechanism is printed to the aux file via \thepage. Thus, with \pagenumbering{roman} for example this will a number in roman notation, unusable in \ifnum tests. Renewing temporarily \thepage does not help, because \thepage is let to \relax in the \write done by \label, in order for its expansion to occur at shipout time.

Hence, I needed to develop my own \label/\ref mechanism (there is then no special consideration for hyperref, there is zero interaction). Furthermore, the new code will handle more than one computation in the document: \resettotal is now a macro with one argument which a user chosen ID for the computation. This ID is needed to get the labels unique. This custom \label/\ref only puts in the aux file the data of the page number.

This extended version of the code is at the bottom of this answer.

partial1

partial2

partial 3

partial 4 ... partial 8

partial 9

partial 10

\documentclass[11pt]{article}
\usepackage[paperheight=5cm]{geometry}
\usepackage{fp}
\usepackage{tabu}
\usepackage{longtable}

\FPset\total{0}

\newcounter{myindex}

\makeatletter

%% not compatible with hyperref:
%% \def\SANEpageref #1{\expandafter\expandafter\expandafter
%%                   \@secondoftwo\csname r@#1\endcsname\empty}

%% update, can be used with or without hyperref:
\def\SANEpageref@ #1#2#3\SANEpageref@{#2}
\def\SANEpageref #1{\expandafter\expandafter\expandafter
                \SANEpageref@\csname r@#1\endcsname{}\SANEpageref@ }


\newcommand{\summa}[1]{\relax
  \refstepcounter{myindex}\label{sum@\arabic{myindex}}%
  \ifcsname r@sum@\arabic{myindex}\endcsname
    \ifnum 0\summa@currentpage<0\SANEpageref{sum@\arabic{myindex}}
      \xdef\summa@currentpage{\SANEpageref{sum@\arabic{myindex}}}%
      \smash{\rlap{\raisebox{.5cm}{Current total: \FPround\result\total2\result}}}%
    \fi
  \fi
  #1\FPadd\0\total{#1}\global\let\total\0%
}

\newcommand{\resettotal}{%
  \FPset\0{0}\global\let\total\0%
  \setcounter{myindex}{-1}%
  \refstepcounter{myindex}\label{sum@0}%
  \xdef\summa@currentpage{\SANEpageref{sum@0}}%
}
\makeatother

\begin{document}\pagestyle{empty}

\begin{longtabu} to \textwidth {X[l]}
Amount \endfirsthead
Amount \\
\rule [-3pt]{5cm}{1pt}
\endhead
 1:  \resettotal \summa{100.00} \\
 2:  \summa{100.00} \\
 3:  \summa{100.00} \\
 4:  \summa{100.00} \\
 5:  \summa{100.00} \\
 6:  \summa{100.00} \\
 7:  \summa{100.00} \\
 8:  \summa{100.00} \\
 9:  \summa{100.00} \\
10:  \summa{100.00} \\
11:  \summa{100.00} \\
12:  \summa{100.00} \\
13:  \summa{100.00} \\
14:  \summa{100.00} \\
15:  \summa{100.00} \\
16:  \summa{100.00} \\
17:  \summa{100.00} \\
18:  \summa{100.00} \\
19:  \summa{100.00} \\
20:  \summa{100.00} \\
21:  \summa{100.00} \\
22:  \summa{100.00} \\
23:  \summa{100.00} \\
24:  \summa{100.00} \\
25:  \summa{100.00} \\
26:  \summa{100.00} \\
27:  \summa{100.00} \\
28:  \summa{100.00} \\
29:  \summa{100.00} \\
30:  \summa{100.00} \\
31:  \summa{100.00} \\
32:  \summa{100.00} \\
33:  \summa{100.00} \\
34:  \summa{100.00} \\
35:  \summa{100.00} \\
36:  \summa{100.00} \\
37:  \summa{100.00} \\
38:  \summa{100.00} \\
39:  \summa{100.00} \\
40:  \summa{100.00} \\
41:  \summa{100.00} \\
42:  \summa{100.00} \\
43:  \summa{100.00} \\
44:  \summa{100.00} \\
45:  \summa{100.00} \\
46:  \summa{100.00} \\
47:  \summa{100.00} 
\end{longtabu}

The global total is \FPeval{\result}{round(total,2)}\result

\end{document}

Extended version of the code

\documentclass[11pt]{article}
\usepackage[paperheight=7cm]{geometry}
\usepackage{fp}
\usepackage{tabu}
\usepackage{longtable}

\makeatletter
\newcounter{computindex}

% THIS EXPANDS #1 IMMEDIATELY, BUT DELAYS EXPANSION OF DECIMAL PAGE NUMBER TO
% SHIPOUT TIME
\newcommand*\computpagelabel [1]
   {\edef\computpagelabel@tmp
        {\noexpand\write\@auxout {\newcomputpagelabel{#1}{\noexpand\the\c@page}}}%
    \computpagelabel@tmp }

\providecommand*\newcomputpagelabel {}
\protected\def\newcomputpagelabel #1#2{\global\@namedef{computpage@#1}{#2}}

\newcommand*\computpage [1]{\csname computpage@#1\endcsname }

\newcommand{\StorePageAndPossiblyUpdateHeader}{%
   \stepcounter{computindex}%
   \computpagelabel{\computID.\arabic{computindex}}%
   \ifcsname computpage@\computID.\arabic{computindex}\endcsname
    \ifnum 0\comput@currentpage<0\computpage{\computID.\arabic{computindex}}
     \xdef\comput@currentpage{\computpage{\computID.\arabic{computindex}}}%
     \UpdatePartialHeader
    \fi
   \fi
}

\newcommand{\UpdatePartialHeader}
   {\smash{\rlap{\raisebox{.5cm}{Current total: \FPround\result\total2\result}}}}%

\newcommand{\summa}[1]{\relax\StorePageAndPossiblyUpdateHeader
  #1\FPadd\0\total{#1}\global\let\total\0%
}

% NEW VERSION OF \resettotal HAS AN ARGUMENT TO IDENTIFY THE COMPUTATION 

\newcommand*{\resettotal}[1]{%
  \FPset\0{0}\global\let\total\0%
% attention au cas de plusieurs tables, on a besoin d'un identifiant:
  \gdef\computID{#1}%
  \setcounter{computindex}{0}%
  \computpagelabel{\computID.0}%
  \xdef\comput@currentpage{\computpage{\computID.0}}%
}
\makeatother

\begin{document}
\pagenumbering {roman}% this would have invalidated earlier versions

\begin{longtabu} to \textwidth {X[l]}
Amount \endfirsthead
Amount \\
\rule [-3pt]{5cm}{1pt}
\endhead
 1:  \resettotal {table1}\summa{100.00} \\
 2:  \summa{100.00} \\
 3:  \summa{100.00} \\
 4:  \summa{100.00} \\
 5:  \summa{100.00} \\
 6:  \summa{100.00} \\
 7:  \summa{100.00} \\
 8:  \summa{100.00} \\
 9:  \summa{100.00} \\
10:  \summa{100.00} \\
11:  \summa{100.00} \\
12:  \summa{100.00} \\
13:  \summa{100.00} \\
14:  \summa{100.00} \\
15:  \summa{100.00} \\
16:  \summa{100.00} \\
17:  \summa{100.00} \\
18:  \summa{100.00} \\
19:  \summa{100.00} \\
20:  \summa{100.00} \\
21:  \summa{100.00} \\
22:  \summa{100.00} \\
23:  \summa{100.00} \\
24:  \summa{100.00} \\
25:  \summa{100.00} \\
26:  \summa{100.00} \\
27:  \summa{100.00} \\
28:  \summa{100.00} \\
29:  \summa{100.00} \\
30:  \summa{100.00} \\
31:  \summa{100.00} \\
32:  \summa{100.00} \\
33:  \summa{100.00} \\
34:  \summa{100.00} \\
35:  \summa{100.00} \\
36:  \summa{100.00} \\
37:  \summa{100.00} \\
38:  \summa{100.00} \\
39:  \summa{100.00} \\
40:  \summa{100.00} \\
41:  \summa{100.00} \\
42:  \summa{100.00} \\
43:  \summa{100.00} \\
44:  \summa{100.00} \\
45:  \summa{100.00} \\
46:  \summa{100.00} \\
47:  \summa{100.00} 
\end{longtabu}

The global total is \FPeval{\result}{round(total,2)}\result

\begin{longtabu} to \textwidth {X[l]}
Amount \endfirsthead
Amount \\
\rule [-3pt]{5cm}{1pt}
\endhead
 1:  \resettotal {table2}\summa{123.45} \\
 2:  \summa{100.00} \\
 3:  \summa{100.00} \\
 4:  \summa{100.00} \\
 5:  \summa{100.00} \\
 6:  \summa{100.00} \\
 7:  \summa{100.00} \\
 8:  \summa{100.00} \\
 9:  \summa{100.00} \\
10:  \summa{100.00} \\
11:  \summa{100.00} \\
12:  \summa{100.00} \\
13:  \summa{100.00} \\
14:  \summa{100.00} \\
15:  \summa{100.00} \\
16:  \summa{100.00} \\
17:  \summa{100.00} \\
18:  \summa{100.00} \\
19:  \summa{100.00} \\
20:  \summa{100.00} \\
21:  \summa{100.00} \\
22:  \summa{100.00} \\
23:  \summa{999.00} \\
24:  \summa{100.00} \\
25:  \summa{100.00} \\
26:  \summa{100.00} \\
27:  \summa{100.00} \\
28:  \summa{100.00} \\
29:  \summa{100.00} \\
30:  \summa{100.00} \\
31:  \summa{100.00} \\
32:  \summa{100.00} \\
33:  \summa{100.00} \\
34:  \summa{100.00} \\
35:  \summa{100.00} \\
36:  \summa{100.00} \\
37:  \summa{100.00} \\
38:  \summa{100.00} \\
39:  \summa{100.00} \\
40:  \summa{100.00} \\
41:  \summa{100.00} \\
42:  \summa{100.00} \\
43:  \summa{100.00} \\
44:  \summa{100.00} \\
45:  \summa{100.00} \\
46:  \summa{100.00} \\
47:  \summa{100.00} 
\end{longtabu}

The global total is \FPround\result\total2\result.

\end{document}

two pages from the output of the above code

partial 1

partial 2

  • Thanks for this, you really put effort in this. It works perfectly, thanks again. – risoldi May 27 '14 at 6:58
  • @timber The code could work only with standard decimal page numbering. I have added a revised version which copes with arbitrary page numberings. – user4686 May 27 '14 at 8:04
  • actually, the \ifnum test which requires decimal page numbers could be replaced by an \ifx test because all that matters is to detect that the page numbers has been modified (and it is ok to make \def's at this location, expandability not required). Thus, the first code version could have been modified in this minimal way. However it still needs to be extended as in the second code version with a \computID to identify distinct computations in the same document. – user4686 May 27 '14 at 8:39
  • Merci, ce deuxième update me sera aussi très utile pour avoir des compteurs multiples. :) – risoldi May 27 '14 at 9:13

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.