42

What I want to do is to show a "progress bar" in the header to show how far you are in the document, by making a rule with width pagenumber divided by total pages times the paper width.

This is what I have:

\usepackage{fancyhdr,lastpage}
\pagestyle{fancy}
\lhead{\rule{\value{page}/\value{\pageref{LastPage}}*120mm}{2mm}}

but obviously it doesn't work.

Sveinung
  • 20,355
  • 5
    I hope you don't mind that I changed your title to better reflect the real problem, for the benefit of people of the future having similar problems and searching the archives. – Lev Bishop Oct 17 '10 at 15:31
  • Side note, it may be obvious, but the aux file is only sourced when \begin{document} is executed so this cannot be used in the preamble – user202729 Aug 07 '22 at 16:36

7 Answers7

33

Solving this kind of problem is the raison d'être of the refcount package. Here's one way to use it:

\documentclass[oneside]{book}
\usepackage{lipsum,fancyhdr,lastpage,refcount}
\pagestyle{fancy}
\setrefcountdefault{-1}
\lhead{\rule{\dimexpr \textwidth * \thepage/\getpagerefnumber{LastPage}}{2mm}}
\begin{document}
\lipsum[1-60] %Insert dummy text for demonstration
\end{document}
Lev Bishop
  • 45,462
  • 1
    As much as I like TH's answer, I think this is a perfect example of why it is good to wait a bit before accepting an answer. – Hendrik Vogt Oct 17 '10 at 07:01
  • @Hendrik Vogt: I believe he can (and should) change his answer. Mine is clearly inferior to Lev's. – TH. Oct 17 '10 at 08:16
  • You're right. They both work, but this looks much so cleaner. Thanks! – Edo Mangelaars Oct 17 '10 at 13:06
  • I have a small problem with your code. Everytime a document containing this fails to typeset, the next time I try it says:
    ! Arithmetic overflow.
    <recently read> \relax
    
    

    at the \lhead line, and I have to comment it out, typeset, and then uncomment it for the document to typeset again.

    – Edo Mangelaars Nov 01 '10 at 14:53
  • 1
    @Edootjuh, did you forget to include the \setrefcountdefault{-1} line? It's purpose is to deal with this issue, by avoiding the divide-by-zero that would otherwise occur when the LastPage label is undefined. – Lev Bishop Nov 02 '10 at 05:08
  • 1
    @Lev Bishop: No, it's there. The strange thing about it is that the error occurs on page 63. – Edo Mangelaars Nov 03 '10 at 14:41
  • 1
    @Edootjuh, OK I see. The problem is that TeX's maximum allowed dimension is 2^30 sp, or about 19ft. Try \setrefcountdefault{1000} to avoid it – Lev Bishop Nov 04 '10 at 01:48
  • @Lev Bishop: Thanks. Now it doesn't error anymore, but generates a lot of Overfull \hbox errors when LastPage isn't defined yet, which slows down the build process a lot. I can live with it, but is there a way to skip the calculation when there is no LastPage label? – Edo Mangelaars Nov 08 '10 at 15:35
16

The code of Lev will break if the page number is not a number, e.g. with \pagenumbering{Roman}. I would use the zref package:

\documentclass[oneside]{book}
\usepackage{lipsum,fancyhdr}
\pagestyle{fancy}
\usepackage[lastpage]{zref}
\pagenumbering{Roman}
\makeatletter
\zref@newprop*{numpage}{\the\value{page}}
\zref@addprop{main}{numpage}
\lhead{\rule{\dimexpr \textwidth * \the\value{page}/\zref@extractdefault{LastPage}{numpage}{1}}{2mm}}
\makeatother
\begin{document}
\lipsum[1-60] %Insert dummy text for demonstration
\end{document}

(Instead of the new property "numpage" one could also use "abspage" if the absolute page number is wanted.)

lockstep
  • 250,273
Ulrike Fischer
  • 327,261
  • Couldn't I use \value{\thepage} and \value{\pageref{LastPage}} then? – Edo Mangelaars Oct 18 '10 at 15:55
  • Well why don't you try it? (But no, it will not work, the argument of \value must be the name of counter.) – Ulrike Fischer Oct 18 '10 at 17:53
  • @UlrikeFischer Brilliant! I have adapted the code to test if a document has one or more pages, and remove the page number if it is only one page (\thispagestyle{empty}), but keep page number on the first page if the document has two or more pages. Thank you! – Sveinung May 21 '12 at 13:44
8

OK, you got already 4 answers, but to give still more options to other people looking up the question:

As you already are useing the lastpage package, you can also use \lastpage@lastpage (at least with the recent version 1.2k):

\documentclass[oneside]{book}
\usepackage{fancyhdr,lastpage}
\pagestyle{fancy}
\makeatletter
\def\lastpage@lastpage{1000}% because \lastpage@lastpage is undefined before
% the .aux file has been loaded at the begin of the document
% (and during the first compilation run).
\lhead{\rule{\dimexpr \textwidth*\thepage/\lastpage@lastpage \relax}{2mm}}%
\makeatother
\usepackage{lipsum}
\begin{document}
\lipsum[1-60] % inserts dummy text for demonstration.
\end{document}

BUT when another pagenumbering scheme (e.g. Roman) is used, or the pagenumbers are reset, this will not work, e.g. \lastpage@lastpage could be X (Roman for 10), and pages put out with \AtEndDocument might not be counted (depending on loading order of the packages). In those cases the pageslts package (v1.2a) could be used:

\documentclass[oneside]{book}
\usepackage{fancyhdr,pageslts}
\setcounter{pagesLTS.pagenr}{1000}
% because pagesLTS.pagenr is zero before
% the .aux file has been loaded at the begin of the document
% (and during the first compilation run).
\pagestyle{fancy}
\lhead{\rule{\dimexpr \textwidth*\theCurrentPage/\the\value{pagesLTS.pagenr}\relax}{2mm}}%
\usepackage{lipsum}
\begin{document}
\pagenumbering{arabic}
\lipsum[1-30] % inserts dummy text for demonstration.
\clearpage
\pagenumbering{Roman}
\lipsum[31-60] % inserts dummy text for demonstration.
\end{document}

(I also replace the fixed 120mm by \textwidth.)

Stephen
  • 14,890
7

This one is tricky, unfortunately catching me and I'm guessing a few others out by the way it was originally framed. Edootjuh's problem has nothing to do with whether calc (or in fact whether \dimexpr) can execute a divide. It (they) can. Rather, it has to do with whether \pageref{LastPage} (or as far as I can work out, any expression based on \pageref{LastPage}) can stand as an operand in the divide.

The problem is trivially solved if (as in Aditya's contribution) the last page number is hard-coded as a literal. It is much less easily solved if this value is pulled in via a \pageref to a label. Ultimately, the problem that needs to be solved is this:

\documentclass{article}
\usepackage{fancyhdr}
\usepackage{calc}
\usepackage{lipsum}

\pagestyle{fancy}
\lhead{\rule{\textwidth * \thepage / 6}{2mm}}                     % <-- works fine, trivial solution
%\lhead{\rule{\textwidth * \thepage / \pageref{LastPage}}{2mm}}   % <-- any form of reference to a label fails

\begin{document}
\pageref{LastPage}

\rule{\dimexpr 120mm*1/6 \relax}{2mm}   % solution requires reference to LastPage label rather than a hard-coded literal here 

\lipsum[1-30]
\label{LastPage}
\end{document}
  • 1
    You're absolutely right. That's where I got hung up last night. Fortunately, the solution isn't too complicated but it does depend on how references are implemented. – TH. Oct 16 '10 at 12:56
7

It's slightly tricky to do this since you need to unroll what \pageref does, but what you need to do basically boils down to this.

\newcount\lastpagecount
\makeatletter
\AtBeginDocument{%
        \expandafter\ifx\csname r@LastPage\endcsname\relax
                \lastpagecount\m@ne
        \else
                \lastpagecount\expandafter\@secondoftwo\r@LastPage\relax
        \fi
}
\lhead{%
        \begingroup
                \ifnum\c@page>\lastpagecount
                        \count@\c@page
                \else
                        \count@\lastpagecount
                \fi
                \dimen@\dimexpr 120mm * \c@page/\count@\relax
                \rule{\dimen@}{2mm}%
        \endgroup
}
\makeatother

The code being passed to \AtBeginDocument checks to see if the LastPage reference has been set. If it hasn't, it sets the \lastpagecount to -1. If it has, then it sets \lastpagecount to be the value of the last page.

Then, in \lhead, \count@ is set to the maximum of the current page and the last page. Note that it's possible for the current page to be greater than the last page because you need to compile at least twice to find the real last page number. Then it uses \dimexpr ... \relax to compute the length of the rule.

This didn't get extensive testing, but it seems to work.

TH.
  • 62,639
3

As to the problem of "using pageref as a number", but not the "progress bar problem": there's a fine solution here. And for the record, here's a way to increase the reference of pageref by 1:

\usepackage{refcount}

\newcommand{\incpageref}[1]{%
  \number\numexpr\getpagerefnumber{#1} + 1}

\incpagere takes label as an argument.

Adobe
  • 3,037
1

Grab Value of Page Counter

After being disappointed that this similar question was marked as a duplicate, I sought out and found a solution that fits my situation. My answer is more fitting to the other question, but I will leave it here, because I cannot add an answer to a question marked as "duplicate".

Another route that does not involve references is to use counters themselves.

I recently needed to solve an issue with a multilingual document having multiple cover pages

  • One true physical cover
  • Internal cover pages per language

Because all cover pages are created with the \maketitle command, I needed only the first \maketitle (the true cover page) to reset the page count to 1 (such that page 1 is following the cover page--the actual contents of the text). The solution was to:

Create a custom counter

 \newcounter{runningpagecounter}

Set this counter within the \maketitle command just before resetting the page count to 1

\newcommand{\maketitle}[1]{%
% WHATEVER CODE
{\Huge #1}
\setcounter{runningpagecounter}{\value{page}
\setcounter{page}{1}
}%

For internal documents (units within the document that call \maketitle), you can do the following: After calling \maketitle, which will reset the page counter to 1, you just reset the page counter again to your custom counter that grabbed the number before it was reset:

\setcounter{page}{\value{runningpagecounter}}
  • This is a nice explanation and really a simplification to resort to the counter itself. Nevertheless, there is an important point in using \pageref{} to get the page: in floats the page counter does not reflect the real page of where the float is placed. Anyway +1 for the time you invested in your answer. – loved.by.Jesus Nov 11 '22 at 14:03