# Replacing Small Caps When Undefined or Not Available

According to the brilliant answer to this question it is possible to check whether small caps are available or not. However, trying to actually make a use of this code I get a strange result: When the font shape is undefined the replacement works as expected. When the font shape is not available the test says the font shape is available but Latex uses a replacement, instead.

Is there a way to use \fakedsc rather than the roman version for the normal sans serif small caps?

In the following code you can see that small caps are replaced when T1/lmr/bx/sc and T1/lmss/bx/sc are used (both undefined) but not when T1/lmss/m/sc is used (not available):

\documentclass{scrartcl}

\usepackage[utf8]{inputenc}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\usepackage{relsize}
\usepackage{etoolbox}

\newcommand{\fakedsc}[3][1]{#2\begingroup\smaller[#1]{\MakeUppercase{#3}}\endgroup}

\makeatletter
\def\define@newfont{%
\begingroup
\let\typeout\@font@info
\escapechar\m@ne
\expandafter\expandafter\expandafter
\split@name\expandafter\string\font@name\@nil
\expandafter\ifx
\csname\curr@fontshape\endcsname \relax
\expandafter\gdef\csname \curr@fontshape/sub\endcsname{}% new
\wrong@fontshape\else
\extract@font\fi
\endgroup}

\newcommand\testsc[3][1]{%
\ifcsname \f@encoding/\f@family/\f@series/sc/sub\endcsname
no \fakedsc[#1]{#2}{#3}
\else
\ifcsname \f@encoding/\f@family/\f@series/sc\endcsname  yes \textsc{#2#3} \else no \fakedsc[#1]{#2}{#3} \fi
\fi
}
\makeatother

\begin{document}

\detokenize{\rmfamily} \textsc{Test} -- \testsc{T}{ested} -- without \fakedsc{T}{est} => test successful, no replacement

{\bfseries
\detokenize{\rmfamily} \textsc{Test} -- \testsc{T}{ested} -- without \fakedsc{T}{est}} => test successful, replaced

\sffamily
\detokenize{\sffamily} \textsc{Test} -- \testsc{T}{ested} -- without \fakedsc{T}{est} => test \textbf{not successful}, auto replacement

{\bfseries
\detokenize{\sffamily} \textsc{Test} -- \testsc{T}{ested} -- without \fakedsc{T}{est}} => test successful, replaced

\end{document}


The results are shown here:

EDIT: I found something helpful in the source2e documentation. On top of page 166 it says:

If the \curr@fontshape combination is not available, (i.e. undeﬁned) we call the macro \wrong@fontshape to take care of this case. Otherwise \extract@font will load the external font for us.

341 % \expandafter\ifx

342 % \csname\curr@fontshape\endcsname \relax

344 % \fi

345 \expandafter\ifx

346 \csname\curr@fontshape\endcsname \relax

347 \wrong@fontshape\else

To allow substitution we call the curr@fontshape macro which usually will expand to \relax but may hold code for substitution (see \subst@fontshape deﬁnition).

348 % \csname\curr@fontshape\endcsname

349 \extract@font\fi

We are nearly ﬁnished and must only restore the \escapechar by closing the group.

350 \endgroup}

... so the magic word is undefined, meaning a solution for a font shape that is not available (according to the LaTeX warnings is still needed).

EDIT2:

As far as I understand, \testsc tests for a substitution (which leads to the warning "Font shape T1/lmr/bx/sc undefined(Font) using T1/lmr/bx/n instead" if \textsc is used). Then, it tests if the font shape exists. For some reasons, this test is positive for T1/lmss/m/sc although, with \textsc it gives the warning "Font shape T1/lmss/m/sc in size <10.95> not available(Font) Font shape T1/lmr/m/sc tried instead". Is en extra test for this case needed? In contrast to the first warning, I was not able to find out how this warning is produced. I found a definition of this warning on page 64 of the font installation guide. It looks like the family is changed before testing is done, in this case only.

EDIT3:

I had a closer look at the "not available" warning. It turns out this is produced by t1lmss.fd (located in MiKTeX 2.9/tex/latex/lm). Besides other substitutions it declares:

\DeclareFontShape{T1}{lmss}{m}{sc}
{<->sub*lmr/m/sc}{}


By adding the following code to the preamble I was able to change the substitution to the normal shape of the sans serif font.

\documentclass{scrartcl}

\usepackage[utf8]{inputenc}
\usepackage{lmodern} \sffamily\selectfont
\usepackage[T1]{fontenc}
\usepackage{relsize}
\usepackage{etoolbox}

\DeclareFontShape{T1}{lmss}{m}{sc}
{<->sub*lmss/m/n}{}
\normalfont
...


This is the new output:

Is it possible to make a test for this kind of substitution, as well, or to "undeclare" it without messing around with the .fd file?

Disclaimer: I am aware of the problems that arise on faking small caps but using a different font is no solution for me.

The last step after EDIT3 was actually pretty simple: copy & paste the code from the t1lmss.fd and delete the unwanted code in the tex file itself. Thus, the declaration of the font shape gets overridden while leaving the original fd file intact. The complete tex file looks as follows:

\documentclass{scrartcl}

\usepackage[utf8]{inputenc}
\usepackage{lmodern} %\sffamily\selectfont % (not needed if the whole fd file is inserted, which uses \DeclareFontFamily{T1}{lmss}{})
\usepackage[T1]{fontenc}
\usepackage{relsize}
\usepackage{etoolbox}

%%% taken from the .fd file (MiKTeX 2.9\tex\latex\lm\t1lmss.fd) -- this overrides the definitions in the fd file while leaving the original file untouched
\DeclareFontFamily{T1}{lmss}{}
\DeclareFontShape{T1}{lmss}{m}{n}
{<-8.5> ec-lmss8
<8.5-9.5> ec-lmss9      <9.5-11>  ec-lmss10
<11-15.5> ec-lmss12     <15.5-> ec-lmss17
}{}
\DeclareFontShape{T1}{lmss}{m}{it}
{<->ssub*lmss/m/sl}{}
\DeclareFontShape{T1}{lmss}{m}{sl}
{<-8.5> ec-lmsso8
<8.5-9.5> ec-lmsso9      <9.5-11>  ec-lmsso10
<11-15.5> ec-lmsso12     <15.5-> ec-lmsso17
}{}
%%%%%%% Font/shape undefined, therefore substituted
%\DeclareFontShape{T1}{lmss}{m}{sc}     % <-- deleted
%{<->sub*lmr/m/sc}{}                    % <-- deleted
\DeclareFontShape{T1}{lmss}{b}{n}
{<->ssub * lmss/bx/n}{}
\DeclareFontShape{T1}{lmss}{b}{sl}
{<->ssub * lmss/bx/sl}{}
\DeclareFontShape{T1}{lmss}{b}{it}
{<->ssub * lmss/bx/it}{}
%%%%%%%% semibold condensed series
\DeclareFontShape{T1}{lmss}{sbc}{n}
{<-> ec-lmssdc10}{}
\DeclareFontShape{T1}{lmss}{sbc}{sl}
{<-> ec-lmssdo10}{}
\DeclareFontShape{T1}{lmss}{sbc}{it}
{<->ssub*lmss/sbc/sl}{}
%%%%%%%%% bold extended series
\DeclareFontShape{T1}{lmss}{bx}{n}
{<-> ec-lmssbx10}{}
\DeclareFontShape{T1}{lmss}{bx}{sl}
{<-> ec-lmssbo10}{}
\DeclareFontShape{T1}{lmss}{bx}{it}
{<->ssub*lmss/bx/sl}{}
%%% end of fd file
\normalfont

\newcommand{\fakedsc}[3][1]{#2\begingroup\smaller[#1]{\MakeUppercase{#3}}\endgroup}

\makeatletter
\def\define@newfont{%
\begingroup
\let\typeout\@font@info
\escapechar\m@ne
\expandafter\expandafter\expandafter
\split@name\expandafter\string\font@name\@nil
\expandafter\ifx
\csname\curr@fontshape\endcsname \relax
\expandafter\gdef\csname \curr@fontshape/sub\endcsname{}% new
\wrong@fontshape\else
\extract@font\fi
\endgroup}

\newcommand\testsc[3][1]{%
\ifcsname \f@encoding/\f@family/\f@series/sc/sub\endcsname
no \fakedsc[#1]{#2}{#3}
\else
\ifcsname \f@encoding/\f@family/\f@series/sc\endcsname  yes \textsc{#2#3} \else no \fakedsc[#1]{#2}{#3} \fi
\fi
}
\makeatother

\begin{document}

\detokenize{\rmfamily} \textsc{Test} -- \testsc{T}{ested} -- without \fakedsc{T}{est} => test successful, no replacement

{\bfseries
\detokenize{\rmfamily} \textsc{Test} -- \testsc{T}{ested} -- without \fakedsc{T}{est}} => test successful, replaced

\sffamily
\detokenize{\sffamily} \textsc{Test} -- \testsc{T}{ested} -- without \fakedsc{T}{est} => test finally successful, \detokenize{\fakedsc} used!!!

{\bfseries
\detokenize{\sffamily} \textsc{Test} -- \testsc{T}{ested} -- without \fakedsc{T}{est}} => test successful, replaced

\end{document}


As you can see, \sffamily \textsc{Test} now gives a warning that the font shape is undefinded:

However, if all \textsc{Test} parts are deleted, no warnings are shown. Maybe one could even warn that faked small caps are used, if that is desired (for me, it is not).

Even better: \sffamily \testsc{Tested} no gives no as an answer (meaning the font shape is not defined) and uses \fakedsc as intended:

That was a hard piece of work, but I'm happy I finally figured it out. If there is a cleaner solution, I'd be happy to learn about it.

Disclaimer:

Use this solution on your own responsibility and only if you can live with the horrible results.