2

I am trying to use the ifthen package to automatically rescale some custom vertical spaces so that if the document has an odd number of pages on the first compilation, a second compilation will reduce the vertical space, so that I'll finish with an even number of pages. However, I'm running into issues with \pageref not returning the correct number of pages.

e.g.

  • First compilation yields 3 pages.

  • Second compilation yields 2 pages; however, footer reads Pg. 2 of 3.

  • Successive compilations do not resolve the issue; it remains as "Pg. 2 of 3" (in fact, in my actual document, a third compilation returns 3 pages again, with the last page reading "Pg. 3 of 2." A fourth compilation then returns a 2-page document with the last page reading "Pg. 2 of 3" again, and this loops forever. I could not recreate this in my MWE, though).

MWE:

\documentclass[9pt,leqno]{extarticle}
\usepackage{lastpage}
\usepackage{fancyhdr}
    \pagestyle{fancy}
    \lhead{Detail}
    \chead{About}
    \rhead{Doc}
    \lfoot{Name}
    \cfoot{Title}
    \rfoot{Pg. \thepage\ of \pageref{LastPage}}
\usepackage{lipsum}


 %1. Define custom spaces, with glue
    % Full space
    \newlength{\myqSp} % Define the length variable
    \setlength{\myqSp}{1.3in plus .4in minus .4in} % Initialize value 

 % Define the different vertical spacing commands
    \def\qSp{\vspace{\myqSp}} 
    \def\sqSp{\vspace{0.25\myqSp}} 
    \def\mqSp{\vspace{.6\myqSp}}

\usepackage{ifthen}


\begin{document}

% if-then statement for automatically reducing the definition of vertical space if the number of pages is odd. N.B. I have only included the command for the first spacing command (\qSp), to illustrate the MWE.
\ifthenelse{\isodd{\pageref{LastPage}}}{\def\qSp{\vspace{0.3\myqSp}}}{\null}

\lipsum[1] \qSp

\lipsum[1] \qSp

\lipsum[1] \qSp

\lipsum[1] \qSp

\lipsum[1] \qSp

\lipsum[1] \qSp

\lipsum[1] \qSp



\end{document}

Motivation and Reasoning

First off, I am also looking at alternative solutions for ending up with an even-page document and am aware this can be done in different - even simpler (albeit manual) - ways. While I greatly appreciate suggestions on that front, I really am looking to understand this issue, so I can learn :)

The need for this functionality is related to the printer I am using, which doesn't duplex, coupled with the fact that I am printing many copies of the document: so having documents automatically rescale vertical spaces to ensure even numbers of pages greatly facilitates quick and efficient assembling of the docs. The spaces left in the doc are for students to do work, and so are quite flexible.

  • 3
    i've rephrased the title slightly to make better sense. (you don't want a document with only even page numbers -- no odd ones -- which is how i first read it.) i haven't tested your example, but think that you might try adding a flag to the .aux file to say that "tightening" was needed, and in the next run, if that flag is "true", repeat the space compaction. it sounds to me that if the job detects that the number of pages is even, it doesn't perform the compaction. but the number of the last page is taken from the .aux file, and thus switches back and forth ... infinitely. – barbara beeton Mar 23 '18 at 16:24
  • 1
    Fixed the code AGAIN. Now it should be working properly. Don't forget to mark as answer if it satisfies your question :) – Andreas Storvik Strauman Mar 23 '18 at 18:04
  • 1
    @AndreasStorvikStrauman thank you kindly for the detailed reply - there's a lot for me to digest, so please forgive me the delay in marking the question as "answered," until I can work through and test it out. It may take some time, but I will be sure to mark it! – Rax Adaam Mar 29 '18 at 14:45
  • @RaxAdaam Did you ever figure this out? :) – Andreas Storvik Strauman May 15 '18 at 17:47
6

According to the LastPage docs, it writes the number of pages out to the .aux-file, and gives the correct number of pages on the very next compilation. However at the next compilation, there will be a different number of pages caused by your code. So if you're using LatexMK, then it's going to run back and forth until it hits the maximum number of runs, which is why \pageref{LastPage} and the number of pages disagree.

So \pageref{LastPage} contains the number of pages on the previous run. One solution would be to also write to the .aux-file, using the \write directive, that you already have done the fixing, and then won't do it again. However, this exact approach could be done only once, and the file had to be deleted.

Instead, we need to check if the odd/even actually changed, and keep our changes if needed. Let's look at how we want it to behave on the different runs:

Assume that we first run with an odd number of pages. Let's say 3 pages.

  1. run: we don't know last page or what the previous number of pages were. Do nothing.
  2. run: We now know what the last page of the document is (3), and it is odd. Then we write to the file that we have an odd number of pages, and the number of even pages we expect on the next run (3-1=2). Now we fix it by tightening.
  3. run: LastPage tells us now that it is an even number of pages (2), but we still want to do the tightening. We wrote that we took care of it, and that that on the next run there should be a given number of pages (2). If the number of expected pages (2) are the number of pages we have, then do the tightening and again write that it's actually an odd number before tightening, and still how many pages we expect.
  4. run: We now have added a page. We have to pick up on the fact that there are now a different number of pages (3), but LastPage still gives us 2 pages. This will be updated in the next run:
  5. Different number of pages than expected (expected 2, but got 3). Remove everything from the .aux-file and go to step 2!

Edit: If you used the following code before this edit, it probably would do some weird stuff. Here ya go:

This does actually work, but fair warning: there might be special cases.

\makeatletter
  % 'Function' for writing to .aux if we have odd pages, and how many pages to expect.
  \def\writeOdd#1{
    \edef\toExpect{\number\numexpr\lastpage@lastpage-1}
    \write\@auxout{\string\xdef\string\wasodd{#1}}
    \write\@auxout{\string\xdef\string\expectPages{\toExpect}}
  }
  % 'Function' for keeping our values in the aux file
  \def\keepOdd{
    \write\@auxout{\string\xdef\string\wasodd{\wasodd}}
    \write\@auxout{\string\xdef\string\expectPages{\expectPages}}
  }
\def\tighten{
  \def\qSp{\vspace{0.3\myqSp}}
}
%Do we know whether the last run had an odd number of pages?
\ifcsname wasodd\endcsname
  % If it was odd and we got the expected number of pages, then keep our values and do the tightening
  \if\wasodd1
    \if\expectPages\lastpage@lastpage
      \keepOdd
      \tighten
    \fi
  \fi
\else
    \ifcsname lastpage@lastpage\endcsname
      %We do not know how many pages to expect or whether the last ones were odd, so let's check and do tightening
      \ifthenelse{\isodd{\pageref{LastPage}}}{\writeOdd{1}\tighten}{\null}
    \fi
\fi
\makeatother
| improve this answer | |
1

The problem with your references comes from the repeated compilations:
On the first pass, the locations of the labels are stored in the .aux file. In addition, at the end of the compilation (so after the label positions are stored) you change some spacings to get to an even number of pages. This can cause labels to end up on a different (earlier page).
On the next pass, the .aux file is read with the old label positions (which can be wrong!). These positions are used to resolve the references. Then the aux file is rewritten with the new label positions...

And as you always use the pageref to the last page to adjust the next compilation, you run into problems: 1st run: LastPage = 3 written to .aux file
2nd run: LastPage read from aux file, val: 3 => used to correct your lengths; at end, (as you adjusted your lengths sufficiently) new value written LastPage = 2
3rd run: LastPage read from aux file, val: 2 => no correction so you end up with 3 pages...
etc. etc.

Unless this has to be done automatically, I think the best way is to print a warning to console/log file and do the correction manually.

| improve this answer | |

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.