There are essentially three parts to the patch.
- Adding a
\phantomsection
before every \cite
does most of the work. These phantom sections are what the hyperrefs point back to.
- Convince
backref
to point the hyperlinks back to the phantom sections even if using page numbers as the labels.
- Don't let
backref
over-rule our hyper-target (and use the start of the document instead) if it thinks there's no section to point back to.
The patch has now been updated to hopefully work as expected in all situations, either when using natbib
or not. See the post history if not interested in natbib
support. Note that biblatex
provides its own backref
mode, but does not provide direct links, and this patch does not work there (and should not be used!)
Patch code (use in document preamble after loading the hyperref
package with a backref
option; note this will fail ungracefully without hyperref
!):
%%%% these patches ensure that the backrefs point to the actual occurrences of the citations in the text, not just the page or section in which they appeared
%%%% https://tex.stackexchange.com/questions/54541/precise-back-reference-target-with-hyperref-and-backref
%%%% BEGIN BACKREF DIRECT PATCH, apply these AFTER loading hyperref package with appropriate backref option
% The following options are provided for the patch, currently with a poor interface!
% * If there are multiple cites on the same (page|section) (depending on backref mode),
% should we show only the first one or should we show them all?
\newif\ifbackrefshowonlyfirst
\backrefshowonlyfirstfalse
%\backrefshowonlyfirsttrue
%%%% end of options
%
% hyperref is essential for this patch to make any sense, so it is not unreasonable to request it be loaded before applying the patch
\makeatletter
% 1. insert a phantomsection before every cite, so hyperref has something to target
% * in case natbib is loaded. hyperref provides an appropriate hook so this should be safe, and we don't even need to check if natbib is loaded!
\let\BR@direct@old@hyper@natlinkstart\hyper@natlinkstart
\renewcommand*{\hyper@natlinkstart}{\phantomsection\BR@direct@old@hyper@natlinkstart}% note that the anchor will appear after any brackets at the start of the citation, but that's not really a big issue?
% * if natbib isn't used, backref lets \@citex to \BR@citex during \AtBeginDocument
% so just patch \BR@citex
\let\BR@direct@oldBR@citex\BR@citex
\renewcommand*{\BR@citex}{\phantomsection\BR@direct@oldBR@citex}%
% 2. if using page numbers, show the page number but still hyperlink to the phantomsection instead of just the page!
\long\def\hyper@page@BR@direct@ref#1#2#3{\hyperlink{#3}{#1}}
% check which package option the user loaded (pages (hyperpageref) or sections (hyperref)?)
\ifx\backrefxxx\hyper@page@backref
% they wanted pages! make sure they get our re-definition
\let\backrefxxx\hyper@page@BR@direct@ref
\ifbackrefshowonlyfirst
%\let\backrefxxxdupe\hyper@page@backref% test only the page number
\newcommand*{\backrefxxxdupe}[3]{#1}% test only the page number
\fi
\else
\ifbackrefshowonlyfirst
\newcommand*{\backrefxxxdupe}[3]{#2}% test only the section name
\fi
\fi
% 3. now make sure that even if there is no numbered section, the hyperref's still work instead of going to the start of the document!
\RequirePackage{etoolbox}
\patchcmd{\Hy@backout}{Doc-Start}{\@currentHref}{}{\errmessage{I can't seem to patch backref}}
\makeatother
%%%% END BACKREF PATCHES
And all together in the context of an MWE (based on the provided one, but extended), and now using the biblio.bib
example file which should hopefully get found automatically (otherwise use the link to get it from CTAN):
\documentclass[10pt,a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{natbib}
\bibliographystyle{plainnat}
\usepackage[backref=page]{hyperref}
%%%% these patches ensure that the backrefs point to the actual occurrences of the citations in the text, not just the page or section in which they appeared
%%%% https://tex.stackexchange.com/questions/54541/precise-back-reference-target-with-hyperref-and-backref
%%%% BEGIN BACKREF DIRECT PATCH, apply these AFTER loading hyperref package with appropriate backref option
% The following options are provided for the patch, currently with a poor interface!
% * If there are multiple cites on the same (page|section) (depending on backref mode),
% should we show only the first one or should we show them all?
\newif\ifbackrefshowonlyfirst
\backrefshowonlyfirstfalse
%\backrefshowonlyfirsttrue
%%%% end of options
%
% hyperref is essential for this patch to make any sense, so it is not unreasonable to request it be loaded before applying the patch
\makeatletter
% 1. insert a phantomsection before every cite, so hyperref has something to target
% * in case natbib is loaded. hyperref provides an appropriate hook so this should be safe, and we don't even need to check if natbib is loaded!
\let\BR@direct@old@hyper@natlinkstart\hyper@natlinkstart
\renewcommand*{\hyper@natlinkstart}{\phantomsection\BR@direct@old@hyper@natlinkstart}% note that the anchor will appear after any brackets at the start of the citation, but that's not really a big issue?
% * if natbib isn't used, backref lets \@citex to \BR@citex during \AtBeginDocument
% so just patch \BR@citex
\let\BR@direct@oldBR@citex\BR@citex
\renewcommand*{\BR@citex}{\phantomsection\BR@direct@oldBR@citex}%
% 2. if using page numbers, show the page number but still hyperlink to the phantomsection instead of just the page!
\long\def\hyper@page@BR@direct@ref#1#2#3{\hyperlink{#3}{#1}}
% check which package option the user loaded (pages (hyperpageref) or sections (hyperref)?)
\ifx\backrefxxx\hyper@page@backref
% they wanted pages! make sure they get our re-definition
\let\backrefxxx\hyper@page@BR@direct@ref
\ifbackrefshowonlyfirst
%\let\backrefxxxdupe\hyper@page@backref% test only the page number
\newcommand*{\backrefxxxdupe}[3]{#1}% test only the page number
\fi
\else
\ifbackrefshowonlyfirst
\newcommand*{\backrefxxxdupe}[3]{#2}% test only the section name
\fi
\fi
% 3. now make sure that even if there is no numbered section, the hyperref's still work instead of going to the start of the document!
\RequirePackage{etoolbox}
\patchcmd{\Hy@backout}{Doc-Start}{\@currentHref}{}{\errmessage{I can't seem to patch backref}}
\makeatother
%%%% END BACKREF PATCHES
\begin{document}
Top of page. No sections at all here yet.
\vfill
\cite{GSM97}
\vfill
\cite{Lam94}
\section*{Unnumbered}
Check that things work in unnumbered sections.
\cite{Lam94}
\newpage
\section{Numbered}
And in numbered sections!
This is just a test \citep{GSM97} on another page and not at the start of the line to see how well things work.
And other citation to something already cited \cite{GSM97} on the same page in the same section.
\section{Another numbered one}
And another dupe test \citet{GSM97}.
\newpage
\bibliography{biblio}
\end{document}
Note that this example uses \citet
and \citep
from natbib
just to check that they work as expected.
Note from the hyperref
manual:
Some options can be given at any time, but many are restricted: before \begin{document}
, only in \usepackage[...]{hyperref}
, before first use, etc.
hyperref
disables the backref
option when the package is loaded. The reason for this restriction with backref
is simply to make the implementation easier. (Note that it is supported to put the \hypersetup{backref}
in hyperref.cfg
.)
Note the warning you get when attempting to set the backref
option after it has been disabled:
Package hyperref Warning: Option `backref' has already been used,
(hyperref) setting the option has no effect on input line 10.
This is the standard warning text for disabled hyperref
options, and is perhaps a bit misleading. It should say that the option has to be specified sooner.
Best Answer
As it is another approach on a lower level, I'll write it as another answer.
You also can patch the
\citefield
-command using thexpatch
-package. Therefore you just have find its underlying macro which will be invoked by it:\blx@cite@citefield
You won't be able to use
\citefield{author}
as you wanted, because its internally not handled as afield
but as aname
, so you also had to patch\citename
, if you want to use\citename{key}{author}
. The third command in this row is\citelist
which you also may have to patch. Seebiblatex-manual, 3.7.7 Low-level Commands
.My example contains patches for all three, but
\citename
and\citelist
only in comments and untested. (It doesn't make much sense to me to use them in this way.)If you really want to have ONE command like
\citeany{key}{field/name/list}
, I guess, you had to implement a new macro with an internal switch which leads to the macros\citefield
,\citekey
,\citelist
depending on the givenfield/name/list
. But this is rather more difficult, as you have to deal with the 5 optional parameters from the cite-commands. I don't know how to do this.