[Tex/LaTex] going on with pgfpages and page labels

beamerpage-numberingpgfpages

I’m using a feature of beamer that shows notes beside slides, but the page numbers that are coming out are crazy, making it impossible to extract specific slides into other documents with \includegraphics[page=n]{presentation.pdf}.

For example, in this MWE, there are two frames that are each 1 slide, and then one frame that is four slides. The PDF page labels that show up in Acrobat are, as indicated, 2, 3, 3, 3, 3, and 7. It starts at page 2 instead of 1, and then there are four page 3s, but they don’t even all belong to the same frame!

\documentclass{beamer}
\usepackage{pgfpages}

\setbeameroption{show notes on second screen=left}
\setbeamertemplate{note page}{frame \insertframenumber \\ \insertnote}
\def\pg#1{pgfpages says this is page #1 but it is really page \arabic{page}}

\begin{document}

\setcounter{page}{0}

\frame{hi \note{\pg{2}}}

\frame{there \note{\pg{3}}}

\frame{how \pause are \pause you \pause today
\note<1-3>{\pg{3}}
\note<4>{\pg{7}}}

\end{document}

Here’s what the output looks like:

enter image description here

I know that the manual says, “pgfpages will produce wrong page numbers in the .aux file” and gives a manual workaround. But the page counter still works, so shouldn’t there be some way to pass that through to the PDF? I’d like to fix this, but don’t know where to start. Specifically, when the manual says that “TeX instantiates the page numbers when writing an .aux file only when the physical page is shipped out,” what exactly is it talking about? Where is the source code for that?

Best Answer

The page labels in the .pdf file are actually independent of the page numbers in the .aux file. The page labels are also set during shipout but by the hyperref package.

The pgfpages package however ships out a physical page after the next/following logical page is finished. This is necessary for the 'two screens with optional second' layout to allow setting the second screen after the main screen. However, as a side effect, the page counter and some page states of the hyperref package (like page transition) are already set for the next logical page when the (previous) physical page is shipped, which results in the wrong pdfpagelabels and page transitions occurring too early.

To solve this problem, the relevant page states can be saved when the first logical page of a physical page is to be shipped and restored before the corresponding physical page is shipped.

It should be noted that this all only works if pgfpages is loaded after hyperref since hyperref's shipout hook may only be called for physical pages.

% Save and restore pdfpage related information for hyperref.
\@ifpackageloaded{hyperref}{

  % Create new counter to save the page counter.
  \newcount\pgfpages@state@c@page

  % The special per-page label 'thispdfpagelabel' is set/cleared
  % globally during hyperref's shipout hook for physical pages.
  % Therefore it must be saved temporarily before physical shipout.
  \def\pgfpages@state@save@temp{%
    \ifHy@pdfpagelabels%
      % \HyPL@thisLabel is only defined if pdfpagelabels are activated.
      \let\pgfpages@state@thisLabel@temp\HyPL@thisLabel%
    \fi%
  }

  % Save all relevant page states if the first logical page is shipped.
  \def\pgfpages@state@save#1{%
    % Probably it should be configurable which logical page is
    % considered, but the first page seems reasonable for now.
    \ifnum#1=\pgf@firstshipout\relax%
      % Save the special per-page label 'thispdfpagelabel', which is
      % temporarily saved before.
      \ifHy@pdfpagelabels%
        \ifx\pgfpages@state@thisLabel@temp\relax%
          \global\let\pgfpages@state@thisLabel\relax%
        \else%
          \xdef\pgfpages@state@thisLabel{\pgfpages@state@thisLabel@temp}%
        \fi%
      \fi%
      % Save the page counter and its formatting.
      \global\pgfpages@state@c@page=\c@page%
      \global\let\pgfpages@state@thepage\thepage%
      % Save the page transition and duration.
      \global\let\pgfpages@state@pdfpagetransition\@pdfpagetransition%
      \global\let\pgfpages@state@pdfpageduration\@pdfpageduration%
    \fi%
  }

  % Restore all relevant page states.
  \def\pgfpages@state@restore{%
    % Restore the special per-page label 'thispdfpagelabel'.
    \ifHy@pdfpagelabels%
      % \HyPL@thisLabel is only defined if pdfpagelabels are activated.
      \let\HyPL@thisLabel\pgfpages@state@thisLabel%
    \fi%
    % Restore the page counter and its formatting.
    \c@page=\pgfpages@state@c@page%
    \let\thepage\pgfpages@state@thepage%
    % Restore the page transition and duration.
    \let\@pdfpagetransition\pgfpages@state@pdfpagetransition%
    \let\@pdfpageduration\pgfpages@state@pdfpageduration%
  }

}{
  % Do nothing if hyperref is not loaded.
  \def\pgfpages@state@save@temp{}
  \def\pgfpages@state@save#1{}
  \def\pgfpages@state@restore{}
}


% "Patch" macros of pgfpages.

\def\pgfpages@shipoutshipoutbox{%
  \begingroup
    \let \protect \noexpand
    \@resetactivechars
    \global\let\@@if@newlist\if@newlist
    \global\@newlistfalse
    \@parboxrestore%
    % Restore page states before physical shipout:
    \pgfpages@state@restore%
    \pgfpages@originalshipout%
    \vbox{\hbox{%
      \hskip-1in%
      \vbox to \pgfphysicalheight{%
        \vss\box\pgfpages@shipoutbox%
        \vskip1in%
      }}}%
  \endgroup%
}

\def\pgfpages@interceptshipout{%
  % Temporarily save some page states which are globally set during
  % physical shipout:
  \pgfpages@state@save@temp%
  \ifnum\pgf@shipoutnextto>0\relax
    \def\pgf@next{%
      \expandafter\global\expandafter\setbox\csname pgfpages@box@\the\pgf@shipoutnextto\endcsname=\box\voidb@x%
      \afterassignment\pgfpages@shipouttestnext%
      \pgfpagesshipoutlogicalpage{\the\pgf@shipoutnextto}%
    }%
  \else%
    \ifpgf@holdingphysicalpage% shipout physical page now
      {\pgfshipoutphysicalpage}%
    \fi%    
    \ifnum\pgf@logicalpages=0\relax
      \def\pgf@next{\pgfpages@originalshipout}%
    \else%
      \def\pgf@next{%
        \expandafter\global\expandafter\setbox\csname pgfpages@box@\the\pgf@currentshipout\endcsname=\box\voidb@x%
        \afterassignment\pgfpages@shipouttest%
        \pgfpagesshipoutlogicalpage{\the\pgf@currentshipout}%
      }%
    \fi%
  \fi%
  \pgf@next%  
}

\renewcommand\pgfpagesshipoutlogicalpage[1]{%
  \global\pgfphysicalpageemptyfalse%
  % Save page states before logical shipout:
  \pgfpages@state@save{#1}%
  \expandafter\global\expandafter\setbox\csname pgfpages@box@#1\endcsname=}