Rewrite \url{https://example.com/path} to \href{https://example.com/path}{path} from macro

This document is cut down from a BibTeX-generated .bbl file:

\documentclass{article}
\usepackage{url}
\usepackage{hyperref}
\newcommand{\showDOI}[1]{\textsc{doi:}#1}
\begin{document}
\urldef\tempurl\url{https://doi.org/10.1016/j.comnet.2004.08.013}
\showDOI{\tempurl}
\end{document}


It renders like this:

\documentclass{article}
\usepackage{url}
\usepackage{hyperref}
\newcommand{\showDOI}[1]{\textsc{doi:}#1}
\begin{document}
\showDOI{\href{https://doi.org/10.1016/j.comnet.2004.08.013}%
{\UrlFont 10.1016/j.comnet.2004.08.013}
\end{document}


i.e.

with the PDF hyperlink still pointing to the full URL.

But, because I don't want to have to mess with the .bst file, and because I need the \verb-like behavior of \url applied before \showDOI is expanded — some of the DOIs in my actual bibliography have underscores and/or percent signs in them — I want to accomplish this by changing only the definition of \showDOI, not the way it is used.

Is this possible? If so, how?

• Which bib style are you using? Some have options to automatically display the doi like this. – user36296 Jun 28 '17 at 21:36
• @samcarter "ACM-Reference-Format.bst" (see acm.org/publications/proceedings-template) which is, quite frankly, terrible, although not the worst journal template I've ever had to work with (it's going to be hard to beat the one that thought it was a good idea to redefine \p@!) It does not have any convenient knobs. – zwol Jun 28 '17 at 21:52

The plain text of an url created by \urldef can be modified by defining \UrlLeft and \UrlRight. Adapted from the url package manual, using xstring for string processing:

\documentclass{article}
\usepackage{url}
\usepackage{hyperref}
\usepackage{xstring}
\setlength{\parskip}{1em}   % for presentation only

\newcommand{\showDOI}[1]{%
\let\oldleft\UrlLeft%
\let\oldright\UrlRight%
\def\UrlLeft##1\UrlRight{\StrGobbleLeft{##1}{16}}%
\textsc{doi:} #1%
\let\UrlLeft\oldleft%
\let\UrlRight\oldright%
}

\begin{document}
\url{http://www.regular-url.com/}

\urldef\tempurl\url{https://doi.org/10.1016/j.comnet.2004.08.013}
\showDOI{\tempurl}

\url{http://www.other-url.com/}
\end{document}


This works because hyperref creates the link first with the original argument and then formats the link using the url package, which in turn uses the provided definition. Note that the definitions need to be saved and restored around the DOI display to avoid truncating of normal urls.

Result (link target is shown as tooltip):

• This works nicely, but just to make sure I understand, \StrGobbleLeft{##1}{16} is chopping the first 16 characters off the URL, which is exactly how long https://doi.org/ happens to be? – zwol Jun 28 '17 at 21:57
• @zwol that's correct :) In case you want to make it a bit more flexible (for example also allowing for http://doi.org) you could look into the substring matching functions provided by xstring (or stringstrings, or coolstr), there are several commands that can be useful for this. – Marijn Jun 29 '17 at 7:46
• or incorporate @Herbert 's approach with #1//#2/#3, of course. – Marijn Jun 29 '17 at 7:48
• I am going with this answer but with \def\UrlLeft##1//##2/##3\UrlRight{##3} as in Herbert's answer -- as well as not hardwiring the length of https://doi.org/ this has the advantage of not requiring any additional packages. – zwol Jun 29 '17 at 14:41
\documentclass{article}
\usepackage{hyperref}
\makeatletter
\def\showDOI#1{\expandafter\showDOI@i#1\@nil}
\def\showDOI@i#1//#2/#3\@nil{\textsc{DOI:} \href{#1//#2/#3}{\UrlFont#3}}
\makeatother
\begin{document}
\def\tempurl{https://doi.org/10.1016/j.comnet.2004.08.013}
\showDOI{\tempurl}%

\showDOI{https://doi.org/10.1016/j.comnet.2004.08.013}
\end{document}


• this seems to result in an error if \showDOI{\tempurl} (after \urldef) from the OP is used, possibly because of the internals of \urldef? – Marijn Jun 28 '17 at 20:12
• see edited answer. When using \urldef it is not so easy to handle. – user2478 Jun 28 '17 at 20:17
• Runaway argument? \protect \tempurl \@nil ! Paragraph ended before \showDOI@i was complete. No, use of \urldef is not optional, sorry. – zwol Jun 28 '17 at 21:55

Let's remove anything coming before doi.org/ (so even if you happen to have http instead of https, the macro will work the same).

\documentclass{article}
\usepackage{xparse}
\usepackage{hyperref}

\ExplSyntaxOn
\NewDocumentCommand{\showDOI}{m}
{
\textsc{doi:}
%\nobreakspace % if you want a space, uncomment this
\zwol_showdoi:n { #1 }
}

\tl_new:N \l__zwol_showdoi_tl
\tl_new:N \l__zwol_showdoi_full_tl

\cs_new_protected:Nn \zwol_showdoi:n
{
\tl_if_single:nTF { #1 }
{ % single token
\cs_if_exist:cTF { \cs_to_str:N #1 ~ }
{% we assume it is \urldef
\__zwol_showdoi_urldef:c { \cs_to_str:N #1 ~ }
}
{
\tl_set:Nx \l__zwol_showdoi_tl { #1 }
}
}
{
\tl_set:Nn \l__zwol_showdoi_tl { #1 }
}
\tl_set_eq:NN \l__zwol_showdoi_full_tl \l__zwol_showdoi_tl
\regex_replace_once:nnN { \A .*doi\.org/ } { } \l__zwol_showdoi_tl
\href{\l__zwol_showdoi_full_tl}{\UrlFont\l__zwol_showdoi_tl}
}

\cs_new_protected:Nn \__zwol_showdoi_urldef:N
{
\tl_set_eq:NN \l__zwol_showdoi_tl #1
\regex_replace_once:nnN { \c{url} \cB. (.*) \cE. } { \1 } \l__zwol_showdoi_tl
}
\cs_generate_variant:Nn \__zwol_showdoi_urldef:N { c }
\ExplSyntaxOff

\begin{document}

\urldef\tempurl\url{https://doi.org/10.1016/j.comnet.2004.08.013}

\showDOI{\tempurl}

\showDOI{https://doi.org/10.1016/j.comnet.2004.08.013}

\end{document}


You may need to update your TeX distribution, because regexes entered the expl3 kernel quite recently. If you can't update, add \usepackage{l3regex}.

• Like Herbert's answer, this does not work when \tempurl is defined with \urldef as shown in the original code sample. Use of \urldef is one of the things I cannot change. – zwol Jun 29 '17 at 14:38
• @zwol Now it accepts also macros defined with \urldef. – egreg Jun 29 '17 at 15:59