8

I'm about to produce a series of assessment texts. Some of these assessments have annexes, others don't. I would like to be able to define a specific class, like this :

\documentclass{myexam}
\begin{document}
\frontmatter
\section{Easy steps}
\section{Advanced steps}
\section*{Appendix}
\end{document}

And the \frontmatter macro should print something like :

This text consists of 5 pages plus an appendix of 2 pages.

while, in absence of appendix, it should print like :

This text consists of 5 pages.

I tried to use labels together with a conditional (\newif\ifannex and so on), but it doesn't seem to work. Ideas ?

3
  • More details : I had the idea of putting a \label{append} within the appendix (easy) and to define in the preamble a macro like this : \def\apndx{\ref{append}}. When the appendix is present, \apndx gets a value, fine. Else the compiler sends a warning (normal) and \apndx seems to get the value ?? and not \relax.
    – rcabane
    Oct 14, 2012 at 18:44
  • What did you try to do? \ref and \label need a second compilation run. Oct 14, 2012 at 19:22
  • Of course, twice (or more) !
    – rcabane
    Oct 14, 2012 at 21:01

3 Answers 3

6

You can use the lastpage and refcount packages, but modifying the way you introduce your appendix.

\documentclass{article}
\usepackage{lastpage,refcount}

\newcommand{\frontmatter}{%
  % Whatever you need to put at the beginning
  \computepages
  This text consists of \pageorpages{\textpages}%
  \ifannex
    \ plus an appendix of \pageorpages{\appendixpages}%
  \fi
  .%
}
\newcommand\pageorpages[1]{#1~page\ifnum#1=1 \else s\fi}
% Just in case we need them
\newcommand\textpages{0}
\newcommand\appendixpages{0}

\makeatletter
\newcommand{\computepages}{%
  \ifannex
    \edef\textpages{\number\numexpr\getpagerefnumber{annexstart}-1\relax}%
    \edef\appendixpages{\number\numexpr\getpagerefnumber{LastPage}-\getpagerefnumber{annexstart}+1\relax}%
  \else
    \edef\textpages{\getpagerefnumber{LastPage}}%
  \fi
}

\newif\ifannex
\newcommand{\annex}[1]{%
  \immediate\write\@auxout{\noexpand\global\noexpand\annextrue}%
  \section*{#1}%
  \label{annexstart}%
}
\makeatother

\begin{document}

\frontmatter

\section{Easy steps}

\newpage
\section{Advanced steps}

\newpage
\annex{Appendix}

\end{document}

This correctly shows

This text consists of 2 pages plus an appendix of 1 page.

while, if the \annex line is commented out, the output is

This text consists of 2 pages.

Of course, a couple of compilations are needed for the numbers to synchronize.

4
  • Great ! I'll check this tomorrow. Anyway, I was convinced it was necessary to write and read directly to the aux file, that was the trick you just implemented. Thank you.
    – rcabane
    Oct 14, 2012 at 20:59
  • Is there any drawback in using something like \ifx\r@annexstart\@undefined (as I did in my humble attempt) to check if \annex is called, aside from the fact that the user may use the label without \annex? \label{annexstart} writes to .aux anyway, why not use it? Oct 14, 2012 at 23:20
  • @Qrrbrbirlbel Why did you delete the answer? Your method is good, but I prefer to state explicitly what I want, that's all.
    – egreg
    Oct 14, 2012 at 23:25
  • I did not know of refcount, otherwise I would've tried to use \numexpr, too. (And would have avoided running into problems with hyperref and the like.) Oct 14, 2012 at 23:33
3

This solution uses \ztotpages of package zref's module zref-totpages for the number of total pages. This number does not depend on the page numbering scheme. The question mentioned \frontmatter. Usually this uses \pagenumbering{roman} and a following \mainmatter uses \pagenumbering{arabic} with resetting the page counter. The label LastPage of package lastpage will not see the pages before \mainmatter.

However it is not clear, on which class myexam is based. The use of \section would assume a article based class, but that does not provide \frontmatter. Classes with \frontmatter would use \chapter. Therefore the solution defines \frontmatter if it is not given, otherwise \frontmatter is extended to print the additional text.

The appendix is detected by the presence of zref label StartAppendix. For convenience the label setting and \section*{Appendix} is put into macro \Appendix. If the label is detected, then the number of appendix pages is added to the text.

\documentclass{exam}

\usepackage{zref-totpages}
\makeatletter
\newcommand*{\AppendixStartLabel}{%
  \@bsphack
    \zref@labelbyprops{StartAppendix}{abspage}%
    \zref@refused{StartAppendix}%
  \@esphack
}
\newcommand*{\printpages}[1]{%
  \ifcase#1%
    \textbf{??}~pages%
  \or
    1~page%
  \else
    \number#1~pages%
  \fi
}
\newcommand*{\FrontmatterText}{%
  \noindent
  This text consists of \printpages{\ztotpages}%
  \zref@ifrefundefined{StartAppendix}{%
  }{%
    \newcommand*{\appendix@pages}{}%
    \edef\appendix@pages{%
      \numexpr\zref@extractdefault{LastPage}{abspage}{0}%
      -\zref@extractdefault{StartAppendix}{abspage}{0}%
      +1%
    }%
    \space including an appendix of \printpages{\appendix@pages}%
  }%
  .\par
}
\@ifundefined{frontmatter}{%
  \newcommand*{\frontmatter}{%
    \clearpage
    \FrontmatterText
  }%
}{%
  \g@addto@macro\frontmatter{\FrontmatterText}%
}
\newcommand{\Appendix}{%
  \clearpage
  \section*{\appendixname}%
  \AppendixStartLabel
}
\makeatother

\begin{document}
\frontmatter
\section{Easy steps}
\section{Advanced steps}
\Appendix
\end{document}

Result

3
  • Hello, I think this is the best solution (in my opinion), quite simple and elegant.
    – rcabane
    Oct 15, 2012 at 20:38
  • Works fine; I just think there is a minor error, e.g. the line \space plus an appendix of \printpages{\appendix@pages}% should be \space including an appendix of \printpages{\appendix@pages}% isn't it ?
    – rcabane
    Oct 16, 2012 at 6:59
  • @rcabane You are right, I had used the wording of the question, but with a different meaning of "text". Getting the pages without the appendix is left as exercise for the reader (not too difficult with \numexpr, for example). Oct 16, 2012 at 8:36
2

Better solution

After finding out about refcount we can skip the calculations with counters (and avoid running into problems with hyperref and other packages that redefine \ref so that it is more than just some digits).

I have now borrowed the idea of egreg to outsource the calculations. It makes a much cleaner code.

Code

\documentclass{article}
\usepackage{lastpage}
\usepackage{refcount}
\newcounter{tempcounter}

\newcommand*{\pageorpages}[1]{#1~page\ifnum#1=1 \else s\fi}

\makeatletter
\newcommand*{\textpages}{%Calculation of _text_ pages
  \ifx\r@DontYouDareUseThisLabelApndxStart\@undefined% without appendix
    \number\numexpr\getpagerefnumber{LastPage}+1-\getpagerefnumber{FirstTextPage}\relax
  \else % with appendix
    \number\numexpr\getpagerefnumber{DontYouDareUseThisLabelApndxStart}-\getpagerefnumber{FirstTextPage}\relax
  \fi%
}

\newcommand*{\apndxpages}{% Calculation of _appendix_ pages.
\ifx\r@DontYouDareUseThisLabelApndxStart\@undefined0\else % to prevent errors if this is called accidently
  \number\numexpr\getpagerefnumber{LastPage}-\getpagerefnumber{DontYouDareUseThisLabelApndxStart}+1\relax
\fi
}

\newcommand*{\howmanypages}{% \frontmatter is already defined in some classes
  The following text consists of \pageorpages{\textpages}%
  \ifx\r@DontYouDareUseThisLabelApndxStart\@undefined\else % with appendix
    \space plus an appendix of \pageorpages{\apndxpages}%
  \fi.
  \clearpage % again, no sketchy calculations
  \label{FirstTextPage}
}
\makeatother

\newcommand*{\apndx}{
  \clearpage
  \section*{Appendix}
  \label{DontYouDareUseThisLabelApndxStart} % and I mean it!
}

\usepackage{lipsum}
\begin{document}
\lipsum
\howmanypages
\section{Easy steps}
\lipsum
\section{Advanced steps}
\lipsum
\apndx
\lipsum
\end{document}

Bad Solution (breaks with hyperref and what not)

With the use of one auxiliary counter you can accomplish something like this very easily.

\apndx

The macro apndx uses a \clearpage so that it actually starts on an own page. Otherwise the calculation may be sketchy. The label apndxstart is not only used to get the actual page number, but also to check if there is even an appendix (via the macro \r@apndxstart).

\howmanypages

This macro checks whether a appendix is used:

\ifx\r@apndxstart\@undefined

and then proceed to calculate the actual page numbers.

lastpage package

The lastpage packages is used to get the last page number (with or without appendix).

Code

\documentclass{article}
\usepackage{lastpage}
\newcounter{tempcounter}

\makeatletter
\newcommand*{\howmanypages}{% \frontmatter is already defined in some classes
  \ifx\r@apndxstart\@undefined % without appendix
    \setcounter{tempcounter}{\pageref{LastPage}}
    \addtocounter{tempcounter}{1}
  \else% with appendix
    \setcounter{tempcounter}{\pageref{apndxstart}}%
  \fi%
  \addtocounter{tempcounter}{-\pageref{FirstPage}}
  This text consists of \thetempcounter{} page\ifnum\thetempcounter=1\else s\fi
  \ifx\r@apndxstart\@undefined % without appendix
    .
  \else % with appendix
    \setcounter{tempcounter}{\pageref{LastPage}}%
    \addtocounter{tempcounter}{-\pageref{apndxstart}}%
    \addtocounter{tempcounter}{1}%
    \space plus an appendix of \thetempcounter{} page\ifnum\thetempcounter=1\else s\fi.
  \fi
  \label{FirstPage}
}
\makeatother

\newcommand*{\apndx}{
  \clearpage
  \section*{Appendix}
  \label{apndxstart}
}

\usepackage{lipsum}
\begin{document}
\lipsum
\howmanypages
\section{Easy steps}
\lipsum
\section{Advanced steps}
\lipsum
\apndx
\lipsum
\end{document}
4
  • OK, this works but produces an error at the first compiler pass.
    – rcabane
    Oct 15, 2012 at 20:37
  • @rcabane Which error do you get? I only get a warning encouraging me to rerun to get the references right. (And do not use the “Bad Solution”.) Oct 15, 2012 at 21:53
  • Oh it could be that I mistakenly borrowed my file from the bad solution, not from the good one. I got : No file apndx3.aux. [1{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] ! Missing number, treated as zero. <to be read again> \protect l.36 \howmanypages
    – rcabane
    Oct 16, 2012 at 7:02
  • @rcabane This only happens with the “Bad Solution” (by the way, a second run helps to get it work). But as I said, my other solution won't have that problem. It does get the page number in a much cleaner and safer way. Oct 16, 2012 at 17:29

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .