15

I am trying to define a new macro with the following syntax:

\citeimg*{<bibkey>}

The unstarred form should simply produce

\citeauthor{<bibkey>}, \citetitle{<bibkey>}, \citeyear{<bibkey>}

which is straightforward (when I'm not being an idiot).

The starred form should produce

\citeauthor{<bibkey>}, \citetitle{<bibkey>}, \citeyear{<bibkey>}

and link this text to the URL for <bibkey> i.e. schematically

\href{\citefield{<bibkey>}{url}}{\citeauthor{<bibkey>}, \citetitle{<bibkey>}, \citeyear{<bibkey>}}

which obviously doesn't work because life is never that easy.

I have one problem:

  1. making the starred form link to the relevant URL (rather than a dummy one as in the example below).

MWE:

\documentclass{beamer}
\usepackage{hyperref}
\usepackage{xparse}
\makeatletter
\AtBeginDocument{%
  \@ifpackageloaded{biblatex}{%
    \NewDocumentCommand\citeimg{ s m }{%
      \IfBooleanTF{#1}{%
        Starred: \href{www.example.com}{\citeauthor{#2}, \citetitle{#2}, \citeyear{#2}}%
      }{%
        Unstarred: \citeauthor{#2}, \citetitle{#2}, \citeyear{#2}%
      }%
    }%
  }{}%
}
\makeatother
\usepackage[backend=biber]{biblatex}
\bibliography{biblatex-examples}
\begin{document}
\begin{frame}
  Desired output from unstarred version:

  \citeimg{markey}

  I want the starred version to output the same text linked to URL for the relevant .bib entry.

  \citeimg*{markey}

  I understand why this links to the wrong place, but I'm not sure how best to fix it.

  I would like the starred form to be equivalent to the following:

  \href{http://tug.ctan.org/tex-archive/info/bibtex/tamethebeast/ttb_en.pdf}{\citeauthor{markey}, \citetitle{markey}, \citeyear{markey}}
\end{frame}
\end{document}

possibly explanatory slide

I wondered if something like the following would help or a new bib macro. But that seems overly complex for what appears to me (perhaps naïvely) to be a relatively simple requirement.

\DeclareFieldFormat{linked}{% simplified from Audrey's answer at http://tex.stackexchange.com/a/49578/
  \iffieldundef{url}
  {#1}
  {\href{\thefield{url}}{#1}}}
  • Sorry, i just have to ask ... It is not enough to have the url clickable in the final bibliography? – Johannes_B Feb 1 '16 at 19:33
  • @Johannes_B No. It is for use in slides where I don't typically include a bibliography. Licences often require a link and attribution. I want something compact which satisfies that and, often, the neatest way is to make the author/title/year a clickable link. – cfr Feb 1 '16 at 21:14
7

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}

example output

  • Thank you! (I knew if I posted bad code, you'd post good code ;).) I've tweaked it a bit and included my current version below in case it is of any use to anybody. [But if you object, I can delete it or delete my answer altogether. I'm not sure it serves much purpose other than to demonstrate my ignorance of Biblatex.] – cfr Feb 2 '16 at 22:37
  • 1
    @cfr I have removed the \def (indeed I should have probably just looked a bit harder before, now it fits the definition of \Cietauthor in biblatex.def). I have also implemented \ifhyperref (maybe you want to change the logic there, not sure?). (I think \ifhyperref does what you want, but it can only be used in a biblatex context, i.e. in \DeclareFieldFormat, \newbibmacro, \DeclareCiteCommand etc.) I would probably go as far as saying that you could merge the two commands into one that automatically sets the link if possible and doesn't if hyperref isn't loaded. – moewe Feb 3 '16 at 8:44
  • 1
    @cfr Needless to say that I have absolutely no objection to your edited answer. – moewe Feb 3 '16 at 8:45
  • Thanks. That makes sense. I was going to put the condition inside the command. However, that seemed inefficient: if I check for hyperref at the start of the document and define the commands accordingly, I'm only checking whether the package is loaded once, whereas putting the condition inside the definitions mean it has to be applied every time the commands are used, I think. Probably the inefficiency is of absolutely no significance, though. – cfr Feb 3 '16 at 21:37
  • 1
    @cfr Mhhh, you might have a point there (I'm not sure about the real impact on performance), \ifhyperref is peppered throughout biblatex.def though, so it can't be too bad. (The definition in biblatex2.sty is \let\blx@imc@ifhyperref\@firstoftwo or \let\blx@imc@ifhyperref\@secondoftwo so it "only" seems to introduce additional overhead and doesn't do fancy stuff when executed). – moewe Feb 4 '16 at 7:36
4

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.]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.