5

I have a document where I link to numerous external .pdf and image files. The person who uses this document needs to have a hard copy of all the linked documents and images. So, I would like to include all the linked files in the acutal document to make it easy to print but also to provide a page reference to the documents so that they can easliy be located.

In the MWE, I am manually including the linked documents at the end. However am running into a few problems:

  • can't quite get the page ranges to be displaed via cleverref
  • want to link to the first page and the last page of any included .pdf

enter image description here

Notes:

Code:

\documentclass{article}
\usepackage{hyperref}
\usepackage{xparse}
\usepackage{graphicx}
\usepackage{pdfpages}
\usepackage{xcolor}
\usepackage{lipsum}
\usepackage{xstring}
\usepackage{cleveref}
\usepackage[export]{adjustbox}% http://tex.stackexchange.com/questions/6073/scale-resize-large-images-graphics-that-exceed-page-margins

\NewDocumentCommand{\AddLinkToFile}{% m% #1 = text to display m% #2 = file to open upon clicking }{% \edef\ExpandedFileName{#2}% \IfFileExists{"#2"}{% \hspace{1.0cm}File: \href{run:\ExpandedFileName}{\textcolor{blue}{#1}}% \IfEndWith{#2}{.pdf}{% .pdf files has a page range \hfill\Cpagerefrange{#1 Start}{#1 End}% }{% non .pdf files are only a single page. \hfill\Cpageref{#1}% }% }{% \hspace{1.0cm}File: \href{run:\ExpandedFileName}{\textcolor{orange}{#1}}% \typeout{**** Warning: Failed to link file "\ExpandedFileName".}% }% }%

\begin{document} \AddLinkToFile{Kant 1}{kant.pdf}

\AddLinkToFile{Eiffel 1}{../images/EiffelWide.jpg}

\AddLinkToFile{Lipsum 1}{lipsum.pdf}

\pagenumbering{gobble} %% Now include the links files (eventually need to automate from here to end)

\newpage \phantomsection\label{Kant 1 Start}% \includepdf[pagecommand={\thispagestyle{plain}}, pages=1-last]{kant.pdf} \phantomsection\label{Kant 1 End}%

%% If non .pdf we can have multiple images per page, so no \newpage here \phantomsection\label{Eiffel 1}% \includegraphics[max width=\linewidth,keepaspectratio=true]{../images/EiffelWide.jpg}

\newpage \phantomsection\label{Lipsum 1 Start}% \includepdf[pagecommand={\thispagestyle{plain}}, pages=1-last]{lipsum.pdf} \phantomsection\label{Lipsum 1 End}%

\end{document}

Peter Grill
  • 223,288
  • I would separate the display text and the label name -- you will run into problems if the display text contains commands, umlauts etc. – Ulrike Fischer Sep 17 '16 at 13:09
  • Why don't you put a \label on the first and last page of each included PDF? At least putting one on the first page should be relatively straightforward, shouldn't it? I take it you want the number within the included document, that is, and not the original page number. – cfr Sep 17 '16 at 16:13
  • @UlrikeFischer: Good point. I plan to use an optional parameter to allow for those cases where I can't just use the text itself. Did not include that in the MWE as that is not related to my current issue. – Peter Grill Sep 17 '16 at 23:05
  • @cfr: Yeah, that is exactly what I am trying to do, but am doing somethign wrong. :-( And yes, I want the page number corresponding to the new document, not the page number in the included pdf. – Peter Grill Sep 17 '16 at 23:06
  • @PeterGrill It is horrible code ... does anything help at all? – cfr Sep 18 '16 at 01:23
  • It would be easier if you wanted complete PDFs included, of course, because then you could just count the pages. Then you could do it all with cleveref, though you'd still need a lot of labels. (But maybe hyperref provides an option for this in that case. Or maybe you could use that anyway. I don't know hyperref well enough to know.) – cfr Sep 18 '16 at 01:30
  • @cfr: I do include the complete pdf (via the use of pages=1-last). Won't have any case where I only include certain pages. I had just gotten everything working in my example (except for the reference to the last page of the included pdf) and was about to edit the posting. But, since you posted an answer, I will try to make sense of that first and it might solve my last issue (before the attempt to automte this). – Peter Grill Sep 18 '16 at 01:49
  • In that case, you can avoiding falling back to the pageslts reference for the last page of the document, I think, because you can count the pages and then calculate the appropriate pg:p<number> using the page number of the first page and the number of pages. However, if you've solved it in some other way, I'm not sure how much use my answer is anyway. Do you want me to delete it? – cfr Sep 18 '16 at 03:18
  • @cfr: No, please don't delete it. I haven't yet digested it. I think I understand your comment about calculating the last page link. But, how do I determine how many pagesa are in the pdf. I don't see such a macro in the pdfpages doc. Hmmm, I guess I could use the pagecommand option to increment a counter for each page. – Peter Grill Sep 18 '16 at 04:07
  • @PeterGrill Please see edit below. If you are using pdfTeX, you can eliminate my use of the counter in pagecommand and use \pdfximage. (This may work with e.g. LuaTeX, but I am not sure about this since many of the \pdf... things have disappeared in the current version.) However, I am not sure how to make it all work with cleveref. – cfr Sep 18 '16 at 13:30
  • @PeterGrill I now see you already discovered \pdfximage ... – cfr Sep 18 '16 at 13:32

2 Answers2

2

This is about as inelegant as you can possibly get, but maybe it will provide a useful suggestion somewhere along the line.

It does not use cleveref for the case in which a PDF is included at the very end of the document because I could not figure out how to get that to work. Instead, it uses standard \pageref for that case with a reference provided by pageslts.

For this to work, you assign a tag to each file which is used when adding the link and then when including the PDF. So a wrapper is used for \includepdf. On the positive side, this means you don't have to add the pagecommand explicitly. (You have to add a tag instead.)

\includelinkedpdf{<tag>}[<options>]{<file>}

includes the PDF and adds a great many labels. Note: it adds more labels than it adds pages. It also adds a label on the first page following the PDF, if there is one. (This is why there's a problem if the PDF is the last one in the document.)

\AddLinkToFile{<tag>}{<text>}{<file>}

adds the link. The link for the first page is straightforward. The link for the final page uses a set of labels placed on the pages of the included PDFs. Each one has a label of the form pg:p<no> starting with 1. The command takes the <tag> End reference and gets the standard page number for that reference, if any. If the page number is 0, we assume this is the last thing in the document and use pageslts's label. Otherwise, we subtract the page number for the reference pg:p1. We then set a counter to the result and use it to create a link to pg:p<result>. Which ... er ... seems to work ... possibly.

Note that hyperref wants to be loaded last with certain exceptions, notably cleveref. pageslts says to add plainpages=false,pdfpagelabels=true, so I did that. pageslts will give an error if \pagenumbering{} isn't set, so I've added that to \begin{document}.

If you don't want page numbers to be displayed on the included PDFs, use \pagestyle{empty} in place of \pagestyle{plain}. If you use \pagenumbering{gobble}, compilation will fail with an error.

links and things

\documentclass{article}
\usepackage{xparse,pageslts}
\usepackage{graphicx}
\usepackage{pdfpages}
\usepackage{xcolor}
\usepackage{lipsum}
\usepackage{xstring}
\usepackage[export]{adjustbox}% http://tex.stackexchange.com/questions/6073/scale-resize-large-images-graphics-that-exceed-page-margins
\usepackage[plainpages=false,pdfpagelabels=true]{hyperref}
\usepackage{cleveref}
\AtBeginDocument{\pagenumbering{arabic}}
\makeatletter
\NewDocumentCommand{\AddLinkToFile}{%
  m% #1 = tag
  m% #2 = text to display
  m% #3 = file to open upon clicking
}{%
    \edef\ExpandedFileName{#2}%
    \IfFileExists{"#3"}{%
        \hspace*{1.0cm}File: \href{run:\ExpandedFileName}{\textcolor{blue}{#2}}%
        \IfEndWith{#3}{.pdf}{% .pdf files has a page range
            \hfill
            \edef\tempa{\hypergetpageref{#1 End}}%
            \ifnum\tempa=0 Pages \pageref{#1 Start} to \lastpageref{VeryLastPage}%
            \else
              \edef\tempb{\hypergetpageref{pg:p1}}%
              \setcounter{grill@pg}{\tempa}%
              \addtocounter{grill@pg}{-\tempb}%
              \Cpagerefrange{#1 Start}{pg:p\thegrill@pg}%
            \fi
        }{% non .pdf files are only a single page.
            \hfill\Cpageref{#1}%
        }%
    }{%
        \hspace*{1.0cm}File: \href{run:\ExpandedFileName}{\textcolor{orange}{#2}}%
        \typeout{**** Warning: Failed to link file "\ExpandedFileName".}%
    }%
}
\newcounter{grill@page}
\setcounter{grill@page}{0}
\newcounter{grill@pg}
\setcounter{grill@pg}{0}
\NewDocumentCommand\includelinkedpdf { m O {} m }{%
  \def\makefirststyle{\phantomsection\label{#1 Start}}%
  \def\makedynstyle{\makefirststyle\thispagestyle{plain}\stepcounter{grill@page}\phantomsection\label{pg:p\thegrill@page}\global\let\makefirststyle\relax}%
  \includepdf[
    pagecommand={\makedynstyle},
    #2,
  ]{#3}%
  \phantomsection\label{#1 End}%
  \clearpage
}
\makeatother
\begin{document}
\AddLinkToFile{kant}{Kant 1}{kant.pdf}

\AddLinkToFile{eiffel}{Eiffel 1}{example-image-a.png}

\AddLinkToFile{lipsum}{Lipsum 1}{lipsum.pdf}

\includelinkedpdf{kant}[pages=1-last]{kant}

\includegraphics[max width=\linewidth,keepaspectratio=true]{example-image-a}\label{eiffel}

\includelinkedpdf{lipsum}[pages=1-last]{lipsum}

\end{document}

EDIT

In the above, the tags are simply used to create the labels at Start and End. If you're confident the filename is safe to use, you can obviously use that or make that a default with an optional override.

In the code below, I make specifying the tag optional in this way. For the PDF inclusion the optional argument is delimited by () because that macro already has a regular optional argument. Obviously this can be tweaked to suit.

The main point of this edit is to show how to do without the additional \labels on each page of included PDFs. The code assumes

  1. that pdfTeX is used to compile;
  2. that every page of each included PDF is used.

In this case, we can count the pages using \pdfximage. This avoids the need to create End labels. We can avoid the additional labels on each page by relying on the mechanism hyperref uses to enable indexing. This adds an anchor on each page which index entries access as \hyperpage{}. I'm not sure how to get this working with cleveref, but I don't know that package very well.

\documentclass{article}
\usepackage{xparse}
\usepackage{graphicx}
\usepackage{pdfpages}
\usepackage{xcolor}
\usepackage{lipsum}
\usepackage{xstring}
\usepackage[export]{adjustbox}% http://tex.stackexchange.com/questions/6073/scale-resize-large-images-graphics-that-exceed-page-margins
\usepackage{hyperref}
\usepackage{cleveref}
\makeatletter
\NewDocumentCommand{\AddLinkToFile}{%
  o% #1 = tag
  m% #2 = text to display
  m% #3 = file to open upon clicking
}{%
  \IfValueTF{#1}{%
    \edef\grill@tag{#1}%
  }{%
    \edef\grill@tag{#3}%
  }%
    \edef\ExpandedFileName{#2}%
    \IfFileExists{"#3"}{%
        \hspace*{1.0cm}File: \href{run:\ExpandedFileName}{\textcolor{blue}{#2}}%
        \IfEndWith{#3}{.pdf}{% .pdf files has a page range
          \pdfximage {#3}%
          \setcounter{grill@pg}{\the\pdflastximagepages}%
          \edef\tempa{\hypergetpageref{\grill@tag Start}}%
          \addtocounter{grill@pg}{\tempa-1}%
          \hfill
          Pages \pageref{\grill@tag Start} to \hyperpage{\thegrill@pg}
        }{% non .pdf files are only a single page.
            \hfill\Cpageref{\grill@tag}%
        }%
    }{%
        \hspace*{1.0cm}File: \href{run:\ExpandedFileName}{\textcolor{orange}{#2}}%
        \typeout{**** Warning: Failed to link file "\ExpandedFileName".}%
    }%
}
\newcounter{grill@pg}
\setcounter{grill@pg}{0}
\NewDocumentCommand\includelinkedpdf { d () O {} m }{%
  \IfValueTF{#1}{%
    \edef\grill@tag{#1}%
  }{%
    \edef\grill@tag{#3.pdf}%
  }%
  \def\makefirststyle{\phantomsection\label{\grill@tag Start}}%
  \def\makedynstyle{\makefirststyle\thispagestyle{plain}\global\let\makefirststyle\relax}%
  \includepdf[
    pagecommand={\makedynstyle},
    #2,
  ]{#3}%
}
\makeatother
\begin{document}
\AddLinkToFile{Kant 1}{kant.pdf}

\AddLinkToFile{Eiffel 1}{example-image-a.png}

\AddLinkToFile{Lipsum 1}{lipsum.pdf}

\includelinkedpdf[pages=1-last]{kant}

\includegraphics[max width=\linewidth,keepaspectratio=true]{example-image-a}\label{example-image-a.png}

\includelinkedpdf[pages=1-last]{lipsum}

\end{document}

The output is the same as before.

cfr
  • 198,882
  • The tag could just be the file name (my file names do not have any non-ascii characters in them). Can you add some more explantion as to how the \tag is working? – Peter Grill Sep 18 '16 at 01:56
  • @PeterGrill It is only being used to create the #1 Start and #1 End labels/references. I was just nervous about using the filename since it might include a path. (In my world, it might include a path, though it wouldn't include any strange characters in the filename itself.) – cfr Sep 18 '16 at 03:21
  • AFAIK, a path is fine as a label. – Peter Grill Sep 18 '16 at 04:08
2

Thanks to @cfr for suggesting that I determine the number of pages in the .pdf, I finally came up with a solution. This version labels each page of a <file>.pdf with

\label{<file>.pdf <n>}

where <n> is a counter that goes from 1 thru the number of pages in this .pdf. In the page reference I get the number of pages of an external PDF and link to

\Cpagerefrange{<file>.pdf 1}{<file>.pdf \the\pdflastximagepages}

enter image description here

Code:

\documentclass{article}
\usepackage{xparse}
\usepackage{graphicx}
\usepackage{pdfpages}
\usepackage{xcolor}
\usepackage{lipsum}
\usepackage{xstring}
\usepackage{etoolbox}
%\usepackage{fancyhdr}
\usepackage[export]{adjustbox}% https://tex.stackexchange.com/questions/6073/scale-resize-large-images-graphics-that-exceed-page-margins

\usepackage[all,nodeanchor={south east}, color=magenta, opacity=1]{background} \usepackage{hyperref} \usepackage{cleveref}

%% Use background package to add actual page numbers in bottom right hand of margin \newcommand{\FileName}{} \SetBgContents{\tikz \node at (0,0) {Page \thepage\rotatebox{90}{\hspace*{2.0em}\ttfamily\FileName}};} \SetBgPosition{current page.south east} \SetBgHshift{-0.5em} \SetBgVshift{0.5ex} \SetBgAngle{0.0}% Select rotation \SetBgScale{2.0}% Select scale factor

\NewDocumentCommand{\AddLinkToFile}{% m% #1 = text to display m% #2 = file to open upon clicking }{% \edef\ExpandedFileName{#2}% \par \IfFileExists{"#2"}{% File: \href{run:\ExpandedFileName}{\textcolor{blue}{#1}}% \IfEndWith{#2}{.pdf}{% .pdf files has a page range %% https://tex.stackexchange.com/questions/198091/get-number-of-pages-of-external-pdf \pdfximage{#2}% \edef\LastPage{\the\pdflastximagepages}% \ifnum\LastPage=1 \hfill\Cpageref{#2 1}% Only a single page in this .pdf \else %% Page range is page "1" to \LastPage of this .pdf \hfill\Cpagerefrange{#2 1}{#2 \LastPage}% \fi }{% non .pdf files are only a single page. \hfill\Cpageref{#2}% }% }{% File: \href{run:\ExpandedFileName}{\textcolor{orange}{#1}}% \typeout{**** Warning: Failed to link file "\ExpandedFileName".}% }% }%

\newcounter{CurrentPageNumber} \newcommand*{\LabeThisPage}[1]{% % #1 = label prefix \thispagestyle{empty}% \stepcounter{CurrentPageNumber}% \phantomsection% \label{#1 \arabic{CurrentPageNumber}}% }%

\newcommand{\IncludePdfFile}[1]{% % #1 = .pdf file name (with path) \ifcsdef{#1 Previously Linked}{}{% \newpage% \setcounter{CurrentPageNumber}{0}% \def\FileName{File = #1}% \includepdf[pagecommand={\LabeThisPage{#1}}, pages=1-last]{#1}% \csdef{#1 Previously Linked}{}% No need to re-include this file }% }% \newcommand{\IncludeImageFile}[1]{% % #1 = image file name (with path) non .pdf) %% For non .pdf file we can place multiple images per page, so no \newpage here \phantomsection% \label{#1}% \def\FileName{File = #1}% \includegraphics[max width=\linewidth,keepaspectratio=true]{#1}% }%

\begin{document} \AddLinkToFile{Kant 1}{kant.pdf} \AddLinkToFile{Lipsum Single Page}{lipsum1.pdf} \AddLinkToFile{Eiffel 1}{../images/EiffelWide.jpg} \AddLinkToFile{Lipsum 1}{lipsum.pdf} \AddLinkToFile{Kant 1 Duplicate Link}{kant.pdf} %% -------------------------------------------------------------------------- %% Now include the linked files (eventually need to automate from here to end) %% \pagestyle{empty} \IncludePdfFile{kant.pdf}% \IncludePdfFile{lipsum1.pdf}% \IncludeImageFile{../images/EiffelWide.jpg}% \IncludePdfFile{lipsum.pdf}% \IncludePdfFile{kant.pdf}% \end{document}

Peter Grill
  • 223,288