What is the simplest way to extract a URL from a .bib entry for use in \href{}{} (probably using Biblatex)?
Using some of biblatex
's on-board technologies we can make the code a bit shorter. (And I'm really not a fan of constructs such as \citeauthor{#2}, \citetitle{#2}, \citeyear{#2}
, they go horribly wrong when several works are cited and they don't deal with pre- and postnotes that well. Granted, that might not be a problem here, but still ...)
Note first that the starred version of \DeclareCiteCommand
produces a starred macro. So with \DeclareCiteCommand*{\citeimg}
we define \citeimg*
.
We also define a field format that gives us the link, but this time it takes any text and links that to the URL of the entry
\DeclareFieldFormat{imglink}{\href{\thefield{url}}{#1}}
It will be used as \printtext[imglink]{foo}
later.
A bit fancier is
\makeatletter
\DeclareFieldFormat{imglink}{%
\iffieldundef{url}
{{#1}%
\blx@warning@noline{you want to citeimg a work without URL,
please check the entry '\thefield{entrykey}'}}
{\ifhyperref
{\href{\thefield{url}}{#1}}
{#1}}}
\makeatother
that checks for URLs first and doesn't use \href
if hyperref
isn't loaded.
Then the macro that prints the data is
\newbibmacro*{cfr:imgcite}{%
\printnames{labelname}%
\setunit{\addcomma\space}%
\printfield[citetitle]{labeltitle}%
\setunit{\addcomma\space}%
\printfield{year}%
}
Finally, we can define the end-user cite commands
\DeclareCiteCommand*{\citeimg}
{\boolfalse{citetracker}%
\boolfalse{pagetracker}%
\usebibmacro{prenote}}
{\printtext[imglink]{\usebibmacro{cfr:imgcite}}}
{\multicitedelim}
{\usebibmacro{postnote}}
\DeclareCiteCommand{\citeimg}
{\boolfalse{citetracker}%
\boolfalse{pagetracker}%
\usebibmacro{prenote}}
{\usebibmacro{cfr:imgcite}}
{\multicitedelim}
{\usebibmacro{postnote}}
\makeatletter
\newrobustcmd*{\Citeimg}{%
\@ifstar{\bibsentence\citeimg*}{\bibsentence\citeimg}}
\makeatother
MWE
\documentclass{article}
\usepackage[backend=biber]{biblatex}
\usepackage{hyperref}
\addbibresource{biblatex-examples.bib}
\makeatletter
\DeclareFieldFormat{imglink}{%
\iffieldundef{url}
{{#1}%
\blx@warning@noline{you want to citeimg a work without URL,
please check the entry '\thefield{entrykey}'}}
{\ifhyperref
{\href{\thefield{url}}{#1}}
{#1}}}
\newbibmacro*{cfr:imgcite}{%
\printnames{labelname}%
\setunit{\addcomma\space}%
\printfield[citetitle]{labeltitle}%
\setunit{\addcomma\space}%
\printfield{year}%
}
\DeclareCiteCommand*{\citeimg}
{\boolfalse{citetracker}%
\boolfalse{pagetracker}%
\usebibmacro{prenote}}
{\printtext[imglink]{\usebibmacro{cfr:imgcite}}}
{\multicitedelim}
{\usebibmacro{postnote}}
\DeclareCiteCommand{\citeimg}
{\boolfalse{citetracker}%
\boolfalse{pagetracker}%
\usebibmacro{prenote}}
{\usebibmacro{cfr:imgcite}}
{\multicitedelim}
{\usebibmacro{postnote}}
\newrobustcmd*{\Citeimg}{%
\@ifstar{\bibsentence\citeimg*}{\bibsentence\citeimg}}
\makeatother
\begin{document}
\citeimg{markey}
\citeimg*{brandt}
\citeimg*{markey}
\Citeimg{markey}
\Citeimg*{markey}
\end{document}
OK. I came up with something which seems to work - that is, it survives absolutely minimal testing, but will no doubt break as soon as I try to use it in a real document.
I am sure, however, that this cannot possibly be the simplest solution. There must be a more straightforward way of extracting the URL for use as a hyperlink target, mustn't there?
I declared a new 'field format' which consists of a hodgepodge of hodges and podges from biblatex.def
. This is imglink
and it is supposed to create a hyperlink with the text of the link made up of the name-title-year fields from whichever-entry-Biblatex-is-currently-processing.
\DeclareFieldFormat{imglink}{%
\href{#1}{%
\printnames{labelname}, \printfield[citetitle]{labeltitle}, \thefield{year}%
}%
}%
I then hodged and podged some more stuff from biblatex.def
to declare a new citation command, \citeurlimg
.
\DeclareCiteCommand{\citeurlimg}{%
\boolfalse{citetracker}%
\boolfalse{pagetracker}%
\usebibmacro{prenote}%
}{%
\printfield[imglink]{url}%
}{%
\multicitedelim
}{%
\usebibmacro{postnote}%
}%
I don't really have a use for all the bows and whistles here, but I supposed it wouldn't do any harm to leave the possibility of pre- and post- notes for now.
A bit more hodging (or podging) and mangling and tangling of stuff from biblatex.def
suggested this way of defining \Citeurlimg
. (I didn't ask for this in the question because I figured I'd just need to change \citeauthor
to \Citeauthor
. But since I'm not using that directly here, I needed a different approach for the capitalised version.)
\newrobustcmd*{\Citeurlimg}{%
\bibsentence\citeurlimg
}%
This then means I can define \citeimg*{<bibkey>}
and \Citeimg*{<bibkey>}
as simple wrappers around \citeurlimg
and \Citeurlimg
in the starred cases, and as in my original code for the unstarred cases.
\NewDocumentCommand\citeimg{ s m }{%
\IfBooleanTF{#1}{%
\citeurlimg{#2}%
}{%
\citeauthor{#2}, \citetitle{#2}, \citeyear{#2}%
}%
}%
\NewDocumentCommand\Citeimg{ s m }{%
\IfBooleanTF{#1}{%
\Citeurlimg{#2}%
}{%
\Citeauthor{#2}, \citetitle{#2}, \citeyear{#2}%
}%
}%
Complete example:
\documentclass{beamer}
\usepackage{xparse}
\makeatletter
\AtBeginDocument{%
\@ifpackageloaded{biblatex}{%
\DeclareFieldFormat{imglink}{%
\href{#1}{%
\printnames{labelname}, \printfield[citetitle]{labeltitle}, \thefield{year}%
}%
}%
\DeclareCiteCommand{\citeurlimg}{%
\boolfalse{citetracker}%
\boolfalse{pagetracker}%
\usebibmacro{prenote}%
}{%
\printfield[imglink]{url}%
}{%
\multicitedelim
}{%
\usebibmacro{postnote}%
}%
\newrobustcmd*{\Citeurlimg}{%
\bibsentence\citeurlimg
}%
\NewDocumentCommand\citeimg{ s m }{%
\IfBooleanTF{#1}{%
\citeurlimg{#2}%
}{%
\citeauthor{#2}, \citetitle{#2}, \citeyear{#2}%
}%
}%
\NewDocumentCommand\Citeimg{ s m }{%
\IfBooleanTF{#1}{%
\Citeurlimg{#2}%
}{%
\Citeauthor{#2}, \citetitle{#2}, \citeyear{#2}%
}%
}%
}{}%
}
\makeatother
\usepackage[backend=biber]{biblatex}
\bibliography{biblatex-examples}
\begin{document}
\begin{frame}
\citeimg{markey}
\citeimg*{markey}
\Citeimg{markey}
\Citeimg*{markey}
\end{frame}
\end{document}
EDIT
Just for completeness, here's what I've actually got right now. It is pretty much moewe's answer but tries to avoid \def
.
\AtBeginDocument{%
\@ifpackageloaded{biblatex}{%
% moewe's answer at https://tex.stackexchange.com/a/290681/
\@ifpackageloaded{hyperref}{%
\DeclareFieldFormat{imglink}{%
\iffieldundef{url}
{%
{#1}%
\blx@warning@noline{citeimg cannot hyperlink entry '\thefield{entrykey}' as it lacks field url}%
}{%
\href{\thefield{url}}{#1}%
}%
}%
}{%
\DeclareFieldFormat{imglink}{#1}%
}%
\newbibmacro*{cfr:imgcite}{%
\printnames{labelname}%
\setunit{\addcomma\space}%
\printfield[citetitle]{labeltitle}%
\setunit{\addcomma\space}%
\printfield{year}%
}%
\DeclareCiteCommand*{\citeimg}{%
\boolfalse{citetracker}%
\boolfalse{pagetracker}%
\usebibmacro{prenote}%
}{%
\printtext[imglink]{\usebibmacro{cfr:imgcite}}%
}{%
\multicitedelim
}{%
\usebibmacro{postnote}%
}%
\DeclareCiteCommand{\citeimg}{%
\boolfalse{citetracker}%
\boolfalse{pagetracker}%
\usebibmacro{prenote}%
}{%
\usebibmacro{cfr:imgcite}%
}{%
\multicitedelim
}{%
\usebibmacro{postnote}%
}%
\newrobustcmd*{\Citeimg}{%
\bibsentence\@ifstar{\citeimg*}{\citeimg}%
}%
}{}%
}
What I might do is take out the conditional dependency on the load of Biblatex and just add the following to my biblatex.cfg
, which I think will work:
\@ifpackageloaded{hyperref}{%
\DeclareFieldFormat{imglink}{%
\iffieldundef{url}
{%
{#1}%
\blx@warning@noline{citeimg cannot hyperlink entry '\thefield{entrykey}' as it lacks field url}%
}{%
\href{\thefield{url}}{#1}%
}%
}%
}{%
\DeclareFieldFormat{imglink}{#1}%
}%
\newbibmacro*{cfr:imgcite}{%
\printnames{labelname}%
\setunit{\addcomma\space}%
\printfield[citetitle]{labeltitle}%
\setunit{\addcomma\space}%
\printfield{year}%
}%
\DeclareCiteCommand*{\citeimg}{%
\boolfalse{citetracker}%
\boolfalse{pagetracker}%
\usebibmacro{prenote}%
}{%
\printtext[imglink]{\usebibmacro{cfr:imgcite}}%
}{%
\multicitedelim
}{%
\usebibmacro{postnote}%
}%
\DeclareCiteCommand{\citeimg}{%
\boolfalse{citetracker}%
\boolfalse{pagetracker}%
\usebibmacro{prenote}%
}{%
\usebibmacro{cfr:imgcite}%
}{%
\multicitedelim
}{%
\usebibmacro{postnote}%
}%
\newrobustcmd*{\Citeimg}{%
\bibsentence\@ifstar{\citeimg*}{\citeimg}%
}%
This is actually the only reason there's a conditional dependency on hyperref being loaded. As it is now, it only gets used with Beamer, which loads the package anyway. But if I put it in my .cfg
, which might be handy, I need it to work also in the usual case when hyperref is not loaded.
[I tried to use the conditional Biblatex provides, \ifhyperref
, but the sequence didn't seem to be recognised, so maybe I've misunderstood its intended usage.]