# Convert all all-caps words to small caps

I'd like to turn all all-uppercase words, acronyms etc. into small caps, preferably automatically on the fly and without changing the source files. The ideal solution would also allow me to block some of these substitutions out with a blacklist and/or individual mark-up.

In my understanding this cannot be done with LaTeX alone.

Not knowing Lua, I tried to adapt the solution in Macro: Replace all occurrences of a word with something along the lines of http://lua-users.org/wiki/FrontierPattern which I can get to match the right words. But as the replacement applies to the whole tex-file, including keywords and commands, the typesetting is broken.

Is there a way to apply Lua's string-replacement only to the input-text and nothing else?

As an alternative I had a look at the xesearch-package, which processes the right part of the input only. But its "(very blunt form of) regular expressions" doesn't seem to allow to search for all-caps words, as far as I can see.

• It would be very difficult to automate this, since what qualifies as an all-"caps word"? Any sequence of two consecutive caps? Like AM and PM? Something else? – Steven B. Segletes Jan 12 '15 at 21:03
• Two ore more consecutive caps within word-boundries would be a very good start. AM and PM are good examples; many typographers routinely correct them to small caps to avoid them from sticking out from the main text. – Florian Jan 12 '15 at 21:10
• LaTeX expl3 has an l3regex package that may handle such issue. Or run your source code through an external program like SED. I assume this is something you will do one time, so you may do it semi automatically by repeately search and replace in your editor (Texworks supports regular expression). – Sveinung Jan 12 '15 at 21:56
• @Sveinung: is it possible to apply such a regex code to a complete document, i.e. without having to define an environment? I can't find something like a l3regex tutorial that helps me with that. – Jopie Dec 21 '18 at 12:03
• @Jopie I do not have the necessary skill in regular expression programming, but if you search internet for regular expression, I am sure you will fin a manual descibing the topic, for exeample: gnu.org/software/sed/manual/html_node/Regular-Expressions.html . However, it seems to me that you will have problems to program a good solution that cover all necessary exceptions, ref Florian's comments to the answer below. – Sveinung Dec 28 '18 at 9:47

This uses just pdflatex.

Answer has been EDITED to screen out (catcode 12) "punctuation" in discerning if a word is "all caps".

REEDITED to take multi-paragraph arguments. EDITED to work properly when paragraph ends on a \] environment. EDITED to enhance scope containment features as follows:

1. Use of {...} will provide scope containment, but any CAPS word in the group will not be made into small caps;

2. Use of \bgroup...\egroup{} will provide scope containment, and any CAPS word in the group will be made into small caps.

This fix to scope containment for {...} was accomplished by adding a \junkchar at the beginning of every word, and then stripping it out prior to typesetting.

What is good about the current solution is that macros and inline math do not disturb the algorithm.

However, there is still (at least) one issue: as with all macros, \verb cannot be part of the argument.

\documentclass{article}
\usepackage{stringstrings,ifthen}
\makeatletter\let\Gobble\@gobble\makeatother
\def\junkchar{+}% MUST BE ANY catcode 11 OR 12 character
% TESTS IF WORD IS ALL CAPITAL LETTERS (catcode 12 PUNCTUATION IS SCREENED)
\def\testcaps#1{%
\def\capword{T}\testeleven#1\relax\relax%
\if T\capword\caseupper[q]{#1}%
\ifthenelse{\equal{#1}{\thestring}}{}{\def\capword{F}}%
\fi}
% TESTS IF ALL LETTERS OF WORD ARE \catcode 11 or \catcode 12, THEN \capword STAYS {T}
\def\testeleven#1#2\relax{%
\ifcat\noexpand#1A\ifx\relax#2\relax\else\testeleven#2\relax\fi\else%
\ifcat\noexpand#10\ifx\relax#2\relax\else\testeleven#2\relax\fi\else%
\def\capword{F}%    \fi%
\fi%
\fi}
% CONVERTS CAP WORDS OF ARGUMENT INTO SMALL CAPS (\par ALLOWED)
\long\def\sccaps#1{\testcappars#1\par\relax\relax}
% PARSES \sccaps ARGUMENT INTO PARAGRAPHS AND INVOKES \testcapwords ON EACH PARAGRAPH
\long\def\testcappars#1\par#2\relax{%
\testcapwords\junkchar#1 \relax\relax%
\ifx\relax#2\else\unskip\par\testcappars#2\relax\fi}
% PARSES PARAGRAPH INTO WORDS; TESTS EACH WORD; IF ALL CAPS, MAKES IT SMALL CAPS
\def\testcapwords#1 #2\relax{%
\testcaps{#1}\if T\capword\makesc#1\relax\relax\else\Gobble#1\fi%
\ifx\relax#2\else\ \expandafter\testcapwords\junkchar#2\relax\fi}
% PRODUCES SMALL CAPS WORD FROM KNOWN UPPERCASE WORD.
\def\makesc#1#2#3\relax{\textsc{#2\caselower{#3}}}% #1 IS STRIPPED \junkchar
\begin{document}
\sccaps{% CAN'T START ARGUMENT WITH A SPACE
This is a test of the EMERGENCY BROADCAST SYSTEM.
This is ONLY a test.  The EBS may be consulted for further \textbf{information}.
What about paragraphs that CONTAIN $Ax^2 + Bx + C$ math data?
$y = Ax^2 + Bx + C$
or EVEN displaystyle math?

And NOW for a second paragraph at 9:30 PM.

Testing for \bgroup\tiny group LIMITING behavior\egroup{}, but you
must use bgroup and egroup as the containment if you want CAPS in the group

• This is impressive and I'll study it thoroughly! The solution as it is won't work well for me though, as I would need to modify the source texts with the \sccaps. I am not dependend on verb but it also has a problem with containing the scope of commands, e.g. when putting something like {\LARGE test} in somewhere all the remaining text will be large. – Florian Jan 14 '15 at 11:59
• @Florian Oops. My fix broke the basic behavior, so I rolled back the answer. I am back to suggesting \bgroup...\egroup{} to provide scope containment. – Steven B. Segletes Jan 14 '15 at 13:20
• @Florian OK, I have provided an improvement that allows {...} scoping, but CAP words in the scoping will not be small capped. – Steven B. Segletes Jan 14 '15 at 14:15
• @Jopie To be honest, I am not sure that I would want "CD" to be in small caps (big C, smaller D), but if so the only way using the current algorithm to make that work is CD \unskip s. Of course, if you have to go through that much effort, `\textsc{CD}s would be easier and more informative. I'll give it some extra thought, but I don't think a solution to screen the last letter of each capped word will easily present itself. – Steven B. Segletes Dec 21 '18 at 12:21