[Tex/LaTex] Title case an acronym

acronymsglossariesstringstringstitlecaps

Sometimes in my document I wish to use an acronym. Sometimes I want the acronym to appear in title case (for example if it appears in a title).*

*I need this to work in all environments, not specifically titles.

Variants on this question have been asked all over the site, however none of the proposed solutions actually work. The closest example is one that would require me to rewrite my entire glossary file, something I am unwilling to do, as it is shared by other documents and other people.

Three solutions proposed use titlecaps, mfirstuc, and stringstrings, however none of them work on acronyms.

\documentclass{article}
\usepackage{glossaries}
\usepackage{titlecaps}
\usepackage{mfirstuc}
\usepackage{stringstrings}

\newacronym{srs}{SRS}{spaced repetition system}

\begin{document}
  \titlecap{\gls{srs}}
  \capitalisewords{\gls{srs}}
  \capitalizetitle{\gls{srs}}
\end{document} 

Without having to modify my glossary, and preferably without having to include a huge wodge of impenetrable code in my tex file, how can I capitalise the first letter of an acronym sometimes?

EDIT: For clarity I am looking for a way to get
Spaced Repetition System and not Spaced repetition system, or SPACED REPETITION SYSTEM.

Best Answer

\capitialisewords works by applying \makefirstuc to each space-separated element of its argument. In the case of \capitalisewords{\gls{src}}, there are no spaces in the argument, so it simply does \makefirstuc{\gls{src}}.

\makefirstuc applies a set of rules when deciding how to change the case:

  1. If the argument starts with a command and that command is followed by an argument, then the uppercasing is applied to the first object of the inner argument.
  2. If the argument starts with a command and that command isn't followed by an argument, then the uppercasing is applied to the command (unless the command is \protect, in which case it's discarded and the rules are reapplied).
  3. If the argument doesn't start with a command, then the uppercasing is applied to the first object in the command.

Examples:

  • \makefirstuc{\emph{word}} becomes \emph{\uppercase word} (first case)
  • \makefirstuc{\oe-ligature} becomes \uppercase\oe-ligature (second case)
  • \makefirstuc{word} becomes \uppercase word

(The uppercasing command used is actually \mfirstucMakeUppercase, which may be defined as \MakeUppercase or \MakeTextUppercase.)

So \makefirstuc{\gls{src}} comes under the first case. This means that it tries to do \gls{\uppercase src}. That is, it tries to change the case of the label.

If the actually text that requires case-changing is hidden within the argument of \makefirstuc (or \capitialisewords) the first letter can't be accessed. Since \gls is robust, it can't even be expanded before being passed as the argument. This is why the glossaries package provides \Gls, because the case-changing has to be performed deep inside the internals of the command.

I don't know how similar packages deal with the presence of formatting commands (such as \emph) within the text that needs to have its case-changed, but none of them will be able to access the text if it's embedded within a robust command.

The only solution here is to use one of the expandable commands like this:

\documentclass{article}

\usepackage{glossaries}

\newacronym{srs}{SRS}{spaced repetition system}

\begin{document}
  \ecapitalisewords{\glsentrylong{srs}}
\end{document}

This actually works better in page headings, as you're unlikely to want each page header added to the location list for the entry, which is what can happen if you use \gls in a section or chapter heading. (See Why shouldn't I use commands like \gls in \section, \chapter, \caption etc?)

You can, of course, define a custom command, such as:

\newcommand*{\TCac}[1]{\ecapitalisewords{\glsentrylong{#1}}}

or (index and hyperlink)

\newcommand*{\iTCac}[2][]{\glsdisp[#1]{#2}{\ecapitalisewords{\glsentrylong{#2}}}}

Edit:

Take care using \ecapitialisewords as it fully expands the argument, which can have unexpected results if the text contains commands. For example:

\documentclass{article}

\usepackage{xcolor}
\usepackage{glossaries}

\newcommand*{\strong}[1]{\textcolor{red}{\textbf{#1}}}

\newacronym{srs}{SRS}{\strong{spaced} repetition system}

\newcommand*{\TCac}[1]{\ecapitalisewords{\glsentrylong{#1}}}

\begin{document}

\TCac{srs}.

\end{document}

This superficially looks like case 1 with \strong{spaced}, but full expansion means that it's now attempting to do

\capitialisewords{\protect\leavevmode{\protect\color{red}\protect\textbf{spaced}}  repetition system}

This now follows the rules: discard initial \protect, then apply case 1, which ends up as

\leavevmode{\uppercase\protect\color{red}\protect\textbf{spaced}}

which is incorrect. There are two solutions. Either only use robust commands:

\newrobustcmd*{\strong}[1]{\textcolor{red}{\textbf{#1}}}

or first fetch the value of the field and store it in a command using \glsfetchfield and then apply \capitialisewords:

\documentclass{article}

\usepackage{xcolor}
\usepackage{glossaries}

\newcommand*{\strong}[1]{\textcolor{red}{\textbf{#1}}}

\newacronym{srs}{SRS}{\strong{spaced} repetition system}

\newrobustcmd*{\TCac}[1]{%
 \glsfieldfetch{#1}{long}{\phrase}%
 \expandafter\capitalisewords\expandafter{\phrase}%
}

\begin{document}

\TCac{srs}.

\end{document}

There is still a problem here. The value of the long key is still fully expanded. (You can show the value using \showglslong{srs} after you define the entry. This uses \show on the internal command used to store the value of the long field.) This happens because the field expansion is on by default for the long key. You can switch this off using \glsnoexpandfields:

\documentclass{article}

\usepackage{xcolor}
\usepackage{glossaries}

\glsnoexpandfields

\newcommand*{\strong}[1]{\textcolor{red}{\textbf{#1}}}

\newacronym{srs}{SRS}{\strong{spaced} repetition system}

\newrobustcmd*{\TCac}[1]{%
 \glsfieldfetch{#1}{long}{\phrase}%
 \expandafter\capitalisewords\expandafter{\phrase}%
}

\begin{document}

\TCac{srs}.

\end{document}

This now works correctly.

Note that \xcapitalisewords is a shortcut for \expandafter\capitalisewords\expandafter so you can do

\newrobustcmd*{\TCac}[1]{%
 \glsfieldfetch{#1}{long}{\phrase}%
 \xcapitalisewords{\phrase}%
}

As from glossaries v4.22, you can use

\glsentrytitlecase{src}{long}

which effectively does the same thing as the \TCac example above, but the second argument is the field name (e.g. first or text or desc).