Search code examples
rlatexknitrpdflatexhyperref

How to systematically change knitr \label{} behavior to add hyperlink anchors


I want to change knitr's behavior when it creates a figure environment in LaTeX to call a different LaTeX command than \label{}, e.g, \alabel{} where I define \alabel to run \label{foo} as well as \hypertarget{foo}{} using the hyperref LaTeX package. I'm doing this so that I can construct a URL in a web browser to get to a specific place in the .pdf document built with pdflatex, e.g. http://.../my.pdf#nameddest=foo.

How can I either override \label{} or emit an additional \hypertarget{same label used by \label{} in the figures?

This is in the context of a .Rnw file. I'd like the anchor to appear inside the figure environmentfor optimal positioning of the cursor when jumping into the.pdf` document.

UPDATE

In rethinking this I think it's best not to generate hypertarget anchors but to write an R function that parses the LaTeX aux file to retrieve the page number of the reference (\newlabel lines) to generate the needed URL to the pdf file. In the .Rnw or .Rmd file I can call this function from within a sentence to insert the computed URL.

UPDATE

I've decided after all to go with @werner's excellent method, which works flawlessly. For anyone interested in the R-based approach that doesn't require the use of hypertarget, here is the LaTeX code needed to set up for it - this handles the case where physical page numbers do not match logical page numbers (e.g., using logical numbers such as chapter number - page within chapter.

% Creates .pag file mapping absolute page numbers to logical page
% numbers; works with R function latexRef

\newwrite\pgfile
\immediate\openout\pgfile=\jobname .pag
\newcounter{abspage}
\setcounter{abspage}{0}

\useackage{everypage}
\AddEverypageHook{%
  \addtocounter{abspage}{1}
  \immediate\write\pgfile{\thepage, \theabspage}%
}
\AtEndDocument{\clearpage\immediate\closeout\pgfile}

Here's the R function that does the lookups in the .aux, .pag files:

## Create hyperlink to appropriate physical page in a pdf document
## created by pdflatex given the .aux and .pag file.  Absolute and
## named page numbers are store in the .pag file created by hslide.sty

latexRef <- function(label, base, name, path='doc/',
                     blogpath='/home/harrelfe/R/blog/blogdown/static/doc/',
                     lang=c('markdown', 'latex')) {
  lang <- match.arg(lang)
  aux <- paste0(blogpath, base, '.aux')
  if(! file.exists(aux))
    stop(paste('no file named', aux))
  path <- paste0(path, base, '.pdf')
  pag  <- paste0(blogpath, base, '.pag')
  pagemap <- NULL
  if(file.exists(pag)) {
    p <- read.table(pag, sep=',')
    pagemap        <- trimws(p[[2]])
    names(pagemap) <- trimws(p[[1]])
  }

  r <- readLines(aux)
  w <- paste0('\\\\newlabel\\{', label, '\\}')
  i <- grepl(w, r)
  if(! any(i)) stop(paste('no label =', label))
  r <- r[i][1]
  r <- gsub('\\{', ',', r)
  r <- gsub('\\}', ',', r)
  x <- scan(text=r, sep=',', what=character(0), quiet=TRUE)
  section <- x[5]
  if(section != '') section <- paste0(' Section ', section)
  page    <- trimws(x[7])
  if(length(pagemap)) page <- pagemap[page]
  url     <- paste0('http://fharrell.com/', path, '#page=', page)
  switch(lang,
         markdown = paste0('[', name, section, '](', url, ')'),
         latex    = paste0('\\href{', url, '}{', name, section, '}')
         )
  }

Solution

  • You could add the following to your LaTeX preamble a global replacement of \label with a combination of \label-and-\hypertarget in the following way:

    ---
    title: 'A title'
    author: 'An author'
    date: 'A date'
    output: 
      pdf_document:
        keep_tex: true
    header-includes:
      - \AtBeginDocument{
          \let\oldlabel\label
          \renewcommand{\label}[1]{\oldlabel{#1}\hypertarget{#1}{}}
        }
    ---
    
    See Figure \ref{fig:figure}.
    
    \begin{figure}
      \caption{A figure caption}
      \label{fig:figure}
    \end{figure}