9

In XeLaTeX there is an UppercaseSmallCaps font option (as mentioned in this question for example) that will cause upper-case letters to be rendered in small-caps and lower-case letters to remain.

Question: How can UppercaseSmallCaps be implemented in plain LaTeX (or pdfLaTeX) so that text like \upsc{ABCdef} will be rendered as \textsc{abc}def?

mforbes
  • 5,571

2 Answers2

8

Maybe a bit over the top, but you could use l3regex to achieve this

\documentclass{article}
\usepackage{l3regex,xparse}
\ExplSyntaxOn
\cs_generate_variant:Nn \tl_rescan:nn { nV } 
\NewDocumentCommand \upsc { m }
  {
    \tl_set:Nn \l_tmpa_tl {#1}
    \regex_replace_all:nnN { ([A-Z]+) }
      { \c{textsc} \cB\{ \c{lowercase} \cB\{ \1 \cE\} \cE\} } \l_tmpa_tl
    \tl_use:N \l_tmpa_tl
  }
\ExplSyntaxOff
\begin{document}
\upsc{ABCabcDEF}
\end{document}

This uses the latest experimental version of l3regex, which is on CTAN and which has an 'extended regex' system to allow the inclusion of category-code information in the search and replace text.

You could set up a search for upper-case letters in the same way using alternative methods (such as xstring).


Explanation of the code

The basic idea here is to store the text, do a replacement then print it again. I've chosen to replace each upper case <Letter> by

\textsc{\lowercase{<Letter>}}

The search part is a simple regex ([A-Z]+), which will capture the letter as \1 (in the replace part). There, \c{textsc} will create the control sequence \textsc in the updated text and \c{lowercase} creates \lowercase. \1 is the captured letter from the search part, while \cB\{ and \cE\} possibly need a bit of explanation. In the replacement text, \cB\{ creates the character { (which needs to be escaped) with category code 'Begin group'. Similarly, \cE\} creates the character } with category code 'End group'.

The final stage is to insert the result into the input stream. If you want to see what is happening, try adding \tl_show:N \l_tmpa_tl after the \tl_use:N line.

Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
  • You can't use fontspec with pdfTeX, so UppercaseSmallCaps is not directly relevant. – Joseph Wright Jan 02 '12 at 14:25
  • Support for accents would be more challenging (they'll work in the lower case part but not the upper case part). – Joseph Wright Jan 02 '12 at 14:38
  • Nice demonstration of LaTeX3! Better than egreg's in that it works with spaces and paragraphs (with a small modification), but an order of magnitude slower, and disables kerning... – mforbes Jan 04 '12 at 05:48
  • One comment: if you want this to work with macros, then you should replace \tl_set:Nn with \tl_set:Nx so that those macros get expanded. This is needed, for example, if trying to use this with the glossaries package: \renewcommand*{\acronymfont}[1]{\upsc{#1}}. – mforbes Jul 12 '19 at 09:16
  • @mforbes You'd want \protected@edef with any user input: not all LaTeX2e commands will be happy inside a raw x-type expansion – Joseph Wright Jul 12 '19 at 09:26
  • So I would \protected@edef\tmp{#1} then \tl_set:No \l_tmpa_tl \tmp to be safe? – mforbes Jul 12 '19 at 09:38
  • 1
    @mforbes No, I just meant \protected@edef \l_tmpa_tl {#1}: there are some 2e concepts that really can't be avoided. – Joseph Wright Jul 12 '19 at 10:21
6
\documentclass[convert,border=1]{standalone}

\makeatletter
\newif\ifsc@active
\newif\ifnf@active
\def\upsc#1{{\sc@activefalse\nf@activetrue\@upsc#1\@nil}}
\def\@upsc#1{\ifx#1\@nil\else\@@upsc{#1}\expandafter\@upsc\fi}
\def\@@upsc#1{%
  \ifnum\uccode`#1=`#1\relax
     \ifsc@active\else\sc@activetrue\nf@activefalse\scshape\fi
     \expandafter\@firstoftwo
   \else
     \ifsc@active\sc@activefalse\fi
     \ifnf@active\else\nf@activetrue\normalfont\fi
     \expandafter\@secondoftwo
   \fi
     {\lowercase{#1}}%
     {#1}}
\makeatother

\begin{document}
\upsc{xABCdovff}
\end{document}

Doesn't work with accented letters, of course.

enter image description here

egreg
  • 1,121,712