# Siunitx font weight detection with Roboto

When loading the roboto pakage with light font as default, temporarily switching to a different font weight causes siunitx to fail to detect the correct font.

\documentclass{article}
\usepackage[light,medium]{roboto}
\providecommand*\lseries{\fontseries{l}\selectfont}
\usepackage{siunitx}
\sisetup{detect-all=true}
\begin{document}
\sffamily
1\,mV \SI{1}{\milli\volt}

\fontseries{m}\selectfont%
1\,mV \SI{1}{\milli\volt}
\end{document}


There seems to be some trouble of siunitx detecting font weights, when additional weights come into play, like here or here. In this case, however, I cannot understand the issue, since I change back to the default m weight that siunitx should understand. What's the issue here?

• Do these possibilities help: tex.stackexchange.com/q/498612/140850 – Ralf Stubner Feb 27 at 11:35
• @RalfStubner Unfortunately not. Answer one is already contained in my answer. Answers two and three suggest varying settings of detect-* together with either mode=text or mode=math. All of them give the same result as in my question. – ranguwud Feb 27 at 13:34

The problem is that the code of siunitx tries to work in math and text mode in a similar way. In math there is basically only normal and bold and so also in text mode siunitx more or less only tries to detect if bold is active and if not it uses the normal series, which means here the light font. One way to get around is, is to locally reset the default series. The following examples needs a current LaTeX 2020-02-02:

\documentclass{article}
\usepackage[light,medium]{roboto}
\providecommand*\lseries{\mdseries}
\usepackage{siunitx}
\sisetup{detect-all=true}
\begin{document}
\sffamily
1\,mV \SI{1}{\milli\volt} \textbf{1\,mV}

{\DeclareFontSeriesDefault[sf]{md}{m}\mdseries
1\,mV \SI{1}{\milli\volt}}

1\,mV \SI{1}{\milli\volt}
\end{document}


There is another problem hidden here: siunitx detects bold by looking if the current series is b or bx. But with your setup it is bx for the roman font, but sb for the sans serif, and so detecting the weight in sans serif fails:

\documentclass{article}
\usepackage[light,medium]{roboto}
\providecommand*\lseries{\mdseries}
\usepackage{siunitx}
\sisetup{detect-all=true}

\begin{document}
1\,mV \SI{1}{\milli\volt}

{\bfseries \makeatletter f@series is: \f@series. \quad
1\,mV \SI{1}{\milli\volt}}

\sffamily
1\,mV \SI{1}{\milli\volt}

{\bfseries \makeatletter f@series is: \f@series. \quad
1\,mV \SI{1}{\milli\volt}}
\end{document}


• Your answer points into the correct direction. There are a number of additional subtleties that I added in my own answer. – ranguwud Mar 1 at 18:26

You might try to use something like this for proper detection (in a more general way):

\documentclass{article}

\makeatletter

\newif\if@series@context

% test if current typesetting context is "\mdseries" or "\bfseries"
% in the current font family is one of the document meta families rm/sf/tt
% if not it works if it fits \mddefault or \bfdefault

% \IfSeriesContextTF {<either md or bf>}{<true code>}{<false code>}

\def\IfSeriesContextTF#1{%
\expand@font@defaults
\@series@contextfalse
\def\@test@context{#1}%
\expandafter\edef\csname ??def@ult\endcsname{\f@family}%
\let\@elt\test@series@context
\@meta@family@list
\@elt{??}%
\let\@elt\relax
\if@series@context
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
}

\def\test@series@context#1{%
\edef\reserved@a{\csname #1def@ult\endcsname}%
\ifx\f@family\reserved@a
\let\@elt\@gobble
\typeout{Internal test 1: \csname\@test@context series@#1\endcsname=\f@series}%
\expandafter\ifx
\csname\@test@context series@#1\endcsname\f@series
\@series@contexttrue
\else
\typeout{Internal test 2: \csname\@test@context def@ult\endcsname=\f@series}%
\expandafter\ifx
\csname\@test@context def@ult\endcsname\f@series
\@series@contexttrue
\fi\fi\fi
}

% update to the current kernel
\def\expand@font@defaults{%
\edef\rmdef@ult{\rmdefault}%
\edef\sfdef@ult{\sfdefault}%
\edef\ttdef@ult{\ttdefault}%
\expandafter\series@maybe@drop@one@m\expandafter{\bfdefault}\bfdef@ult
\expandafter\series@maybe@drop@one@m\expandafter{\mddefault}\mddef@ult
\edef\famdef@ult{\familydefault}%
}

\DeclareFontSeriesDefault{bf}{bm}  % this one may need adding to LaTeX as default

\makeatother

% test setup

\DeclareFontShape{OT1}{cmss}{l}{n}{<->alias * cmtt/m/n}{}
\DeclareFontShape{OT1}{cmss}{eb}{n}{<->alias * cmr/bx/n}{}

\DeclareFontSeriesDefault[rm]{bf}{b}

\DeclareFontSeriesDefault[sf]{md}{l}
\DeclareFontSeriesDefault[sf]{bf}{eb}

\newcommand\test[1]{\IfSeriesContextTF{#1}{\typeout{==> #1: T}}{\typeout{==> #1: F}}}

\begin{document}

\typeout{rm uses m/b in this doc}
\typeout{series = \csname f@series\endcsname}

\test{md}
\test{bf}

\bfseries
\typeout{series = \csname f@series\endcsname}

\test{md}
\test{bf}

\sffamily
\typeout{sf has l/eb}
\typeout{series = \csname f@series\endcsname}

\test{md}
\test{bf}

\mdseries
\typeout{series = \csname f@series\endcsname}

\test{md}
\test{bf}

\typeout{sf has "bx" but it is neither the bold nor the medium face per spec above}
\fontseries{bx}\selectfont
\typeout{series = \csname f@series\endcsname}

\test{md}
\test{bf}

\typeout{this is ptm not one of the meta families}
\fontfamily{ptm}\selectfont  % neither "rm" "sf" or "tt"

\typeout{request bx is not recognized for ptm as bold}
\typeout{series = \csname f@series\endcsname}

\test{md}
\test{bf}

\bfseries
\typeout{but explicitly reasking for bfseries (which produces "b" now) does}
\typeout{series = \csname f@series\endcsname}

\test{md}
\test{bf}

\end{document}


which I might add in one way or the other in the next release.

This will produce:

This is pdfTeX, Version 3.14159265-2.6-1.40.20 (TeX Live 2019) (preloaded format=pdflatex)
restricted \write18 enabled.
entering extended mode
(./Untitled-2.tex
LaTeX2e <2020-02-02> patch level 5
L3 programming layer <2020-02-25>
(/usr/local/texlive/2019/texmf-dist/tex/latex/base/article.cls
Document Class: article 2019/12/20 v1.4l Standard LaTeX document class
(/usr/local/texlive/2019/texmf-dist/tex/latex/base/size10.clo))
(/usr/local/texlive/2019/texmf-dist/tex/latex/l3backend/l3backend-pdfmode.def)
(./Untitled-2.aux)
rm uses m/b in this doc
series = m
Internal test 1: m=m
==> md: T
Internal test 1: b=m
Internal test 2: b=m
==> bf: F
series = b
Internal test 1: m=b
Internal test 2: m=b
==> md: F
Internal test 1: b=b
==> bf: T
sf has l/eb
series = eb
Internal test 1: l=eb
Internal test 2: m=eb
==> md: F
Internal test 1: eb=eb
==> bf: T
series = l
Internal test 1: l=l
==> md: T
Internal test 1: eb=l
Internal test 2: b=l
==> bf: F
sf has "bx" but it is neither the bold nor the medium face per spec above
series = bx
Internal test 1: l=bx
Internal test 2: m=bx
==> md: F
Internal test 1: eb=bx
Internal test 2: b=bx
==> bf: F
this is ptm not one of the meta families
(/usr/local/texlive/2019/texmf-dist/tex/latex/psnfss/ot1ptm.fd)
request bx is not recognized for ptm as bold
series = bx
Internal test 1: \mdseries@?? =bx
Internal test 2: m=bx
==> md: F
Internal test 1: \bfseries@?? =bx
Internal test 2: b=bx
==> bf: F
but explicitly reasking for bfseries (which produces "b" now) does
series = b
Internal test 1: \mdseries@?? =b
Internal test 2: m=b
==> md: F
Internal test 1: \bfseries@?? =b
Internal test 2: b=b
==> bf: T
(./Untitled-2.aux) )
No pages of output.
Transcript written on Untitled-2.log.

• I must say that I'm having a hard time understanding what's going on in detail in your code with all the redefinitions of \@elt and so on. Regardless, I understand that you define a command that is smarter in the detection of the font weight. But that comes at the cost of touching an internal command of the LaTeX kernel. Is that preferrable over fiddling with internals of siunitx that Ulrike (righteously) frowns upon? – ranguwud Mar 4 at 20:35
• @ranguwud As I said I'm thinking of adding that or a similar command (perhaps named differently) to the LaTeX kernel, so consider it a black box to be used for testing and feedback rather than a final version. If you assume it is being offered by the LaTeX kernel then what you do is using the noninternal interface it offers (or siunitx in the end). And yes I can touch the LaTeX internals given that I wrote NFSS :-). Of course that this isn't the case yet you need to load that black box upfront for testing. – Frank Mittelbach Mar 5 at 14:06

Building on Ulrikes answer I want to expand her solution and also give a bit more background, what's going on here, since a number of different things are involved that complicate the situation.

First, it's worth mentioning that siunitx can operate in two different modes, nameley mode=text and mode=math. The siunitx manual says the following about this option.

The modeoption determines whether siunitx uses math or text mode when printing output. The choices are math and text. When using math mode, text is printed using a math font whereas in text mode a text font is used. The extent to which this is visually obvious depends on the fonts in use in the document. [...] This option has no effect if the detect-mode switch is true.

Here, I only discuss the mode=text case, since Roboto is a text font and no math font anyway. For mode=math a similar approach should be possible.

Since the font weight needs to be detected, we need to know what the current weight is that is stored in \f@series. For the Roboto font, this mechanism was recently changed. For example, in the package version of 2019/04/19, bold font with the medium option is defined as

\ifroboto@medium\def\bfseries@sf{medium}\fi


whereas for the newer version of 2019/12/11 this was changed to

\ifroboto@medium\def\bfseries@sf{sb}\fi


which lead Ulrike to state that a current LaTeX version is needed. In a similar way, a number of explicit words like light or thin were changed to shorthands like l or el. This does not really cause problems for the solution to follow, but it means that it needs to be adapted to the package version.

I first address the issue of regular weight not being detected, then I turn to the problem with bold text. Ulrike stated that siunitx only tries to find out, whether bold is active or not, but doesn't check for additional weights that a font may provide. So let's have a look at the check that siuntix performs in text mode.

\cs_new_protected:Npn \__siunitx_detect_font_weight_text: {
\tl_set:Nx \l__siunitx_tmpa_tl { \tl_head:N \f@series }
\str_if_eq:VnT \l__siunitx_tmpa_tl { b }
{
\cs_set:Npn \__siunitx_font_weight:
{
\boldmath
\bfseries
}
}
\str_if_eq:VnT \l__siunitx_tmpa_tl { l }
{ \cs_set:Npn \__siunitx_font_weight: { \lseries } }
}


So siunitx looks whether \f@series starts with letter b and if yes, sets up things for bold printing. But it also checks, whether \f@series starts with letter l and if yes issues \lseries. So it looks like this should work as-is, since it would issue \lseries for light Roboto, do nothing for medium Roboto and setup bold for \f@series starting with b.

So why is the output wrong after switching to medium Roboto? It turns out that in order to set number and units in sans, siunitx invokes \sffamily. Now when medium weight was selected by \fontseries{m}\selectfont or \mdseries, then \sffamily evaluates to \mdseries@sf, which gets defined as

\ifroboto@light\def\mdseries@sf{l}


when the roboto package is loaded with the light option. So that means that \sffamily actually explicitly switches back to light weight and produces the wrong output. I'm not quite sure, which package to blame here, since for both siunitx and roboto individually, their approaches appear plausible, but in combination it's causing trouble.

One possible solution is given by Ulrike by (locally) redefining \sffamily, e.g. by using the convenient command that she gives.

\DeclareFontSeriesDefault[sf]{md}{m}


Another option is to (locally) tell siunitx to use a different command to setup sans fonts using the siunitx option text-sf (or number-text-sf and unit-text-sf) that is undocumented as far as I can tell.

\sisetup{text-sf=\sffamily\fontseries{m}\selectfont}


The problem with bold is that the medium option of the roboto package sets up semibold and that the corresponding sb, well, just doesn't start with letter b. The only way I see here, is to patch the test \__siunitx_detect_font_weight_text:.

This approach is similar to this answer, but evidently is not very reliable. As stated in that answer and by Ulrike in her comment, it's generally not a good idea to fumble around in internal commands of packages. The implementation or even the name of \__siunitx_detect_font_weight_text: may change with future versions of siunitx and then the patch has to be corrected accordingly.

\apptocmd \__siunitx_detect_font_weight_text: {
\str_if_eq:VnT \f@series { sb }
{
\cs_set:Npn \__siunitx_font_weight:
{
\boldmath
\bfseries
}
}
} { } { }


This patch uses the \apptocmd functionality from etoolbox. Note that there is an issue on GitHub that asks to add this very functionality, so hopefully, this patch will be obsolete in the future.

Finally a complete solution that combines all of the above may look as follows.

\documentclass{article}
\usepackage{etoolbox}
\usepackage[light,medium]{roboto}
\providecommand*\lseries{\fontseries{l}\selectfont}
\usepackage{siunitx}
\sisetup{detect-weight=true,detect-family=true,mode=text}

\makeatletter
\ExplSyntaxOn
\apptocmd \__siunitx_detect_font_weight_text: {
\str_if_eq:VnT \f@series { sb }
{
\cs_set:Npn \__siunitx_font_weight:
{
\boldmath
\bfseries
}
}
} { } { }
\ExplSyntaxOff
\makeatother

\newenvironment{RobotoRegular}{%
\begingroup%
\sisetup{text-sf=\sffamily\fontseries{m}\selectfont}%
\fontseries{m}\selectfont%
}{%
\endgroup%
}

\begin{document}
\sffamily

1\,mV \SI{1}{\milli\volt}

\begin{RobotoRegular}
1\,mV \SI{1}{\milli\volt}
\end{RobotoRegular}

\begingroup
\bfseries \makeatletter f@series is: \f@series. \quad
1\,mV \SI{1}{\milli\volt}
\endgroup

\end{document}


This solution now respects the weight for all three cases. If more of the weights that the roboto package offers are needed, of course more changes along these lines may be necessary.

• Sorry but you should not hack an internal command of siunitx. Internal is every command that has two underscores at the begin. \__siunitx_detect_font_weight_text: should be tabu for you. Make a feature request to siunitx if you think something could be improved (but imho Joseph is already working on it). – Ulrike Fischer Mar 1 at 18:29
• Well, I agree that I shouldn't, but given that it doesn't work, what should I do? ;-) The corresponding issue on Github is here, but it's out there for 2.5 years now, so waiting for it seems not to be a proper solution for the moment. – ranguwud Mar 1 at 18:32
• @UlrikeFischer I have made an edit to my answer, to highlight the danger more explicitly. Any suggestions on how to avoid fumbling with the internals is welcome. – ranguwud Mar 1 at 18:38