9

How can I store a URL in a macro if the URL has a # and/or a % in it? The MWE below works fine for the first two, but not the third one.

enter image description here

Notes:

  • One solution is to replace the # with \string# and the % with \%, but wodering if there is an easier way.

  • I realize that this may mess up the syntax highliting, but the \csdef{} section shown here will be in a separte .def file that will be be \input{}, so not an issue.

Reference:

Code

\documentclass{article}
\usepackage{etoolbox}
\usepackage{hyperref}
\usepackage{tikz}

\csdef{url Title 1}{The Google} \csdef{url Link 1}{http://www.google.com}

\csdef{url Title 2}{TeX.SE} \csdef{url Link 2}{http://tex.stackexchange.com}

%\csdef{url Title 3}{Algebra and Trigonometry:~History of Trigonometry} %\csdef{url Link 3}{https://books.google.com/books?id=4tFFDwAAQBAJ&pg=PT233&dq=history+of+trigonometry&hl=en&newbks=1&newbks_redir=0&sa=X&ved=2ahUKEwjIzcj0-tnqAhXwFjQIHZ6XDLUQ6AEwAnoECAQQAg#v=onepage&q&f=false}

\def\MaxNUmberOfURLs{5} \newcommand{\DisplayURLs}{% \textbf{List of URLs} \foreach \xCount in {1,...,\MaxNUmberOfURLs} {% \ifcsdef{url Title \xCount}{% \par\xCount: \href{\csuse{url Link \xCount}}{\csuse{url Title \xCount}} }{}% }% }

\begin{document} \DisplayURLs \end{document}

Peter Grill
  • 223,288
  • \csedef{url Link 3}{\detokenize{https://books.google.com/books?id=4tFFDwAAQBAJ&pg=PT233&dq=history+of+trigonometry&hl=en&newbks=1&newbks_redir=0&sa=X&ved=2ahUKEwjIzcj0-tnqAhXwFjQIHZ6XDLUQ6AEwAnoECAQQAg#v=onepage&q&f=false}} should work. edit Hrmpf: It works in the MWE, but breaks with %. – moewe Jul 19 '20 at 18:49
  • 1
    You asked, "How can I store a URL in a macro if the URL has a #and/or a % in it?" Isn't this precisely what the \urldef macro of the url package is supposed to do? E.g., \urldef{\myURL}\url{https://books.google.com/books?id=4tFFDwAAQBAJ&pg=PT233&dq=history+of+trigonometry&hl=en&newbks=1&newbks_redir=0&sa=X&ved=2ahUKEwjIzcj0-tnqAhXwFjQIHZ6XDLUQ6AEwAnoECAQQAg#v=onepage&q&f=false} – Mico Jul 19 '20 at 19:54
  • 1
    @Mico: Did not know about that. You should post an asnwer as that might be a better alternative for some. For me, the \csdefurl defined in moewe's answer works great. – Peter Grill Jul 19 '20 at 20:53

2 Answers2

7

You asked,

"How can I store a URL in a macro if the URL has a # and/or a % in it?"

The \urldef directive of the url package lets you create precisely such macros. E.g.,

\urldef{\myURL}\url{https://books.google.com/books?id=4tFFDwAAQBAJ&pg=PT233&dq=history+of+trigonometry&hl=en&newbks=1&newbks_redir=0&sa=X&ved=2ahUKEwjIzcj0-tnqAhXwFjQIHZ6XDLUQ6AEwAnoECAQQAg#v=onepage&q&f=false}

Note the syntax: \urldef{<macroname>}\url{<URLstring>}. Observe taht \url{<URLstring>} must not be enclosed in curly braces.

\myUrl may be used in the argument of \footnote directives.

Mico
  • 506,678
5

hyperref's \hyper@normalise comes in extremely handy here. It can be used to sanitise the argument of a macro and allow it to accept special characters without escaping. (I think I first learned about \hyper@normalise from Michael Ummels' answer to Getting those %#!^& signs in the footnote!)

Just use it to define a new macro like \newcommand*{\csdefurl}[1]{\hyper@normalise{\csdef{#1}}}. (Note that the argument that is going to be "normalised" is not explicitly part of the definition. \newcommand*{\csdefurl}[2]{\hyper@normalise{\csdef{#1}}{#2}} would not work because then the argument is read with the unnormalised catcode setup.)

\documentclass{article}
\usepackage{etoolbox}
\usepackage{hyperref}
\usepackage{tikz}

\makeatletter \newcommand*{\csdefurl}[1]{\hyper@normalise{\csdef{#1}}} \makeatother

\csdef{url Title 1}{The Google} \csdefurl{url Link 1}{http://www.google.com}

\csdef{url Title 2}{TeX.SE} \csdefurl{url Link 2}{http://tex.stackexchange.com}

\csdef{url Title 3}{Algebra and Trigonometry:~History of Trigonometry} \csdefurl{url Link 3}{https://example.com/~test/a%20and%20b.html#anchor}

\def\MaxNUmberOfURLs{5} \newcommand{\DisplayURLs}{% \textbf{List of URLs} \foreach \xCount in {1,...,\MaxNUmberOfURLs} {% \ifcsdef{url Title \xCount}{% \par\xCount: \href{\csuse{url Link \xCount}}{\csuse{url Title \xCount}} }{}% }% }

\begin{document} \DisplayURLs \end{document}

Screenshot of the output. The third item reads "Algebra and Trigonometry: History of Trigonometry" and the little pop-up the PDF viewer shows on hover displays "https://example.com/~test/a%20and%20b.html#anchor"


Some more testing reveals that

\makeatletter
\newcommand*{\csdefurl@i}{}
\newrobustcmd*{\csdefurl}[1]{%
  \def\csdefurl@i{\csdef{#1}}%
  \hyper@normalise\csdefurl@i}
\makeatother

might be safer.

Compare this definition to the one above in

\documentclass{article}
\usepackage{etoolbox}
\usepackage{hyperref}
\usepackage{tikz}

\makeatletter \newcommand{\csdefurl@i}{} \newrobustcmd{\csdefurl}[1]{% \def\csdefurl@i{\csdef{#1}}% \hyper@normalise\csdefurl@i} \makeatother

\csdef{ürl Title 1}{The Google} \csdefurl{ürl Link 1}{http://www.google.com}

\csdef{ürl Title 2}{TeX.SE} \csdefurl{ürl Link 2}{http://tex.stackexchange.com}

\csdef{ürl Title 3}{Algebra and Trigonometry:~History of Trigonometry} \csdefurl{ürl Link 3}{https://example.com/~test/a%20and%20b.html#anchor}

\def\MaxNUmberOfURLs{5} \newcommand{\DisplayURLs}{% \textbf{List of URLs} \foreach \xCount in {1,...,\MaxNUmberOfURLs} {% \ifcsdef{ürl Title \xCount}{% \par\xCount: \href{\csuse{ürl Link \xCount}}{\csuse{ürl Title \xCount}} }{}% }% }

\begin{document} \DisplayURLs \end{document}

where we have the non-ASCII ürl instead of url.

moewe
  • 175,683