2

To start with, I am only interested in solutions that work with pdfLaTeX, as opposed to, say, those that only work with LuaLaTex.

The 'narrow' problem

First, a preliminary note: some packages redefine the value that \f@series assumes for bold series font: the 'standard value' is bx, but if, for example, we load newtxtext, then it is b.

My narrow question is, how do I programmatically set a macro to the value that \f@series has for bold text. But there is a caveat: the solution should work when plugged into certain other macros, which may or may not complicate things. So let me now describe these other macros.

An MWE in the context of which the solution should work

I have a macro, called \OptBm, that applies \bm to a math expression if the surrounding text is bold, and doesn't apply it if it the surrounding text is not bold.

Well, more precisely, it applies \bm if the surrounding text is not in the default text weight for the document, and doesn't apply it if the surrounding text is in the default text weight for the document. My question is actually about how to make it so that the macro really does apply \bm if and only if the surrounding text is truly bold; more on that below.

Here is an MWE that includes my macro:

\documentclass{memoir}
\usepackage{newtxtext}
\usepackage{bm}

\makeatletter
\newcommand{\Fseries}{\f@series} % for easier access to the content of \f@series

\edef\CharBx{\f@series} 
 % this means that \CharBx will be the string returned by \f@series for 
 % the default weight of the document

\newcommand{\OptBm}[1]{\ifx\f@series\CharBx#1\else\bm{#1}\fi}
\makeatother

\begin{document}

\tableofcontents

\section{Case $\protect\OptBm{\alpha>1}$}

\noindent This is when the surrounding text is not bold: \Fseries\ $\OptBm{\alpha}$

\noindent
{\bfseries This is when the surrounding text is bold: \Fseries\ $\OptBm{\alpha}$}

Note that $\alpha>1$ is boldfaced in the section title, but not boldfaced in the Table of Contents.

\end{document}

Output: enter image description here

This is not an X-Y problem

You may complain that for the purposes of what I seem to want, it would be enough to use the optional argument to the \section command. But sometimes this is not possible: for example, under RevTex4-2, this optional argument is ignored (see this question).

Thus, my \OptBm macro may be useful in cases where the following four circumstances hold: 1. a math expression appears in a section title, 2. the section title is bold in the body of the document, 3. the section title is not bold in the Table of Contents, and 4. we cannot use the optional argument of the \section command to separately specify the title that goes to the Table of Contents .

Again, all four of these can happen, for example, if we use RevTex4-2.

What I want the macro to do

Back to my macro. I admit that for what I actually need to do at this particular time, the \OptBm macro as it is now is sufficient. Nevertheless, please bear with me.

What I had originally was this: I didn't load the newtxtext package, and my macro was

\edef\CharBx{bx}
\newcommand{\OptBm}[1]{\ifx\f@series\CharBx\bm{#1}\else#1\fi}

But then when I loaded newtxtext, I discovered that the \CharBx definition had to be modified to \edef\CharBx{b}. This is annoying, so I wanted to make it so that \CharBx always holds the appropriate letter or letters. But no matter what I tried, I couldn't make that work. For example, \edef\CharBx{\bfseries \f@series} doesn't work, nor does {\bfseries\edef\CharBx{\f@series}}, nor anything else I tried.

In the end, I came up with the code in the MWE. However, as I said, what the code in the MWE does is apply \bm if and only if the surrounding text is not in the default text weight for the document. But I want it to apply \bm if and only if the surrounding text is bold. And these two are not equivalent if there are additional weights besides bold and medium. Note that, in general, it is not possible to know in advance (i.e. before the document preamble is loaded) which weights might be available; it all depends on what font we use.

Moreover, it is just annoying on its own that I don't know how to programmatically set a macro to the value that \f@series has for bold text.

Summary

  1. Is there a solution to my 'narrow question'? Would it work in the context of my MWE?
  2. If there is no solution to my narrow question such that it still works in the context of my MWE, is there a different way to write the \OptBm macro such that it indeed applies \bm if and only if the surrounding text is bold, even when we don't know in advance what other font weights are available?

3 Answers 3

3

If you only want to cover b versus m then

\def\extracted@font@series@aux#1#2\stop{#1}
\edef\extracted@font@series{\expandafter\extracted@font@series@aux\f@series?\stop}

will do the job: you'll have a single letter which you can then test using for example

\if b\extracted@font@series TRUE\else FALSE\fi

Things can be a lot more complex: you might have a variety of both text and math mode weights in play. For siunitx I need to cover all of this, allowing for the full range of text weights, etc., as listed in the linked question. That's a question of checking for the special cases (where the weight is m but it's not in \f@series), then taking the weight you find and matching it to a math version. That can involve a two-letter weight, of course. You just have to work systematically.

4
  • This does work, +1. However, if someone defines their font using some idiosyncratic labels, i.e. 'tb' ('true bold') instead of something that begins with 'b', this solution will not work. In contrast, the kind of solution I asked for would work even in that case—it would extract whatever label the font uses, no matter how idiosyncratic. For that reason, I can't accept this solution. Jan 27 at 21:57
  • For completeness, here is a MWE with this solution: \documentclass{article} \makeatletter \def\extracted@font@series@aux#1#2\stop{#1} \newcommand{\OptBm}{ \edef\extracted@font@series{\expandafter\extracted@font@series@aux\f@series?\stop} \if b\extracted@font@series TRUE\else FALSE\fi} \makeatother \begin{document} \OptBm\ {\bfseries\OptBm} \end{document} Jan 27 at 22:02
  • @linguisticturn I'm afraid I'd view those cases as bugs in the font definition: (N)FSS defines a clear set of descriptors
    – Joseph Wright
    Jan 27 at 22:03
  • The set of standard descriptors (at least according to LaTeX 2ε font selection form March 2021, p. 5) includes eb (Extra Bold) and ub Ultra Bold). (And also sb, Semi-Bold, which we may or may not want to count as bold.) Still, they all have a b in them, albeit not as the first letter. Perhaps one should check not if b is the first letter, but rather whether it appears in the series descriptor at all, in any position? Jan 28 at 21:43
2

Answer to the narrow question

That question is,

How can we define a macro that holds the value that \f@series assumes when, in the document, we call \bfseries or \textbf?

The solution must work even if we don't use LaTeX's default fonts.

This is actually straightforward: we record the current value of \f@series, globally apply \bfseries, record the value of \f@series`, and then globally set the font series to the original value. In other words,

\edef\DefaultSeries{\f@series}
\bfseries
\edef\DefaultBoldWeight{\f@series} 
\fontseries{\DefaultSeries}\selectfont

The wanted information is stored in \DefaultBoldWeight.

To be certain this procedure will work correctly, this code should be placed after \begin{document}. If it is instead placed in the preamble, then, with some packages (notably newtxtext), \DefaultBoldWeight ends up getting set to a wrong value. In more detail: first note that if we don't load any packages, bx is what LaTeX sets as the weight selected by \bfseries and \textbf.1 On the other hand, both the newtxtext package and the Noto package set b as the weight selected by \bfseries and \textbf. Now, if the above code is placed in the preamble then, with the newtxtext package, \DefaultBoldWeight ends up getting wrongly set to bx. This even though when we use the Noto package, \DefaultBoldWeight ends up getting correctly set to b regardless of whether the code above is placed in the preamble or in the body of the document.

1There is also the value stored in\bfdefault. Surprisingly, if we don't load any packages, then \bfdefault is b, but \bfseries and \textbf select the weight bx. See this question. (On the other hand, \bfseries and \textbf do always select the same weight, because the latter is defined using the former; see Ulrike Fischer's comment to this question.)

Here is an MWE:

\documentclass{article}
% \usepackage[sfdefault]{noto}
\usepackage{newtxtext}
%
\begin{document}
%
% Here is the relevant code:
%
\makeatletter
\edef\DefaultSeries{\f@series}
\bfseries
\edef\DefaultBoldWeight{\f@series} 
\fontseries{\DefaultSeries}\selectfont
\makeatother
%
% let's check that it worked
%
\makeatletter
\noindent Then default text weight of the current document: \f@series\\ 
\textbf{The default bold weight of the current document: \f@series}\\ 
\makeatother
The weight stored in \verb|\DefaultBoldWeight|: \DefaultBoldWeight
%
\end{document}

An answer to the 'weight-transparent \bm' question

The question is,

How can I make a macro that will apply \bm if the surrounding text is bold, but won't apply it if the surrounding text is not bold?

Why we need this

Note that in RevTeX4-2, the section (and subsection) titles are bold in the body of the text, but normal weight in the table of contents. Now assume the section title contains some text in math mode. We may decide that when the surrounding text in the title is bold (as it will be in the body of the text), the math expression should be bold as well. But when the surrounding text is not bold, like in the table of contents, we would want the math to not be bold.

In regular LaTeX, it is possible to add an optional argument to the \section, \subsection, etc. commands; the main argument is what to put into the body of the text, and the optional one is what to put in the table of contents. But RevTex4-2 ignores the optional argument; see here.

One solution would be to use some sort of 'weight-transparent \bm' command.

The relation (and lack of it) to the previous 'narrow question'

I originally thought the answer to this question will use the answer to the narrow question above. However, I no longer think so, as a result of having had some time to think about Joseph Wright's answer as well as about the discussion in the comments to that answer.

My main realization is this: it is completely mainstream to have a package that

  1. defines several font weights that, arguably, should all count as 'bold' for the purposes of the 'weight-transparent \bm' command, and
  2. not all of these weights have font series values/descriptors2 that begin with b.

2For a given font weight, its font series value (or descriptor) is what we should put as the argument of \fontseries to select that font weight.

As far as point 2. above, note that the set of standard descriptors (at least according to LaTeX 2ε font selection form March 2021, p. 5) includes sb (Semi-Bold), eb (Extra Bold) and ub (Ultra Bold), and also bx (Bold extended) and sbx (Semi-bold extended). Indeed, note that

(i) the Noto Sans fonts come in the following varieties of bold: b, sb, eb, ub;
(ii) one of the weights supported by the default LaTeX font is bx; and
(iii) one of the weights supported by kpfonts is sbx.

So we can't even assume the descriptor will consist of no more than two letters.

So, absent any more specific information about which font will be used, the 'weight-transparent \bm' command should probably assume that the font is bold if its descriptor contains the letter 'b'. Whether this is the case can be ascertained, for example, by using the LaTeX kernel commands \in@ and \ifin@. (How to determine whether a string is contained in another string is discussed here, here, and here.)

Three ways to define our macro

This 'weight-transparent \bm' command I will call \WTbm. Here is one possible definition:

\def\WT@in@#1#2{\edef\WT@Aux@in@{\noexpand\in@{#1}{#2}}\WT@Aux@in@}
\DeclareRobustCommand{\WTbm}[1]{\WT@in@{b}{\f@series}\ifin@{\bm{#1}}\else{#1}\fi}

This is based on the first solution of this answer. Of course, we can use literally that answer, like this:

\def\instring#1#2{TT\fi\begingroup
  \edef\x{\endgroup\noexpand\in@{#1}{#2}}\x\ifin@}
\DeclareRobustCommand{\WTbm}[1]{\if\instring{b}{\f@series}{\bm{#1}}\else{#1}\fi}

This definition is very clever and elegant, but the price is that it is much harder to understand.

We could also adapt this answer, like so:

\def\HalfOfIn@{\in@{b}}
\DeclareRobustCommand{\WTbm}[1]{\expandafter\HalfOfIn@\expandafter{\f@series}\ifin@ \bm{#1}\else #1\fi}

Furthermore, we can also exclude certain specific weights from counting as bold for the purposes of our \WTbm. For example, in order for the weight whose descriptor is eb to not count as bold, do the following: in any of the above definitions, replace the appearance of \bm{#1} by {\def\excl{eb}\ifx\f@series\excl#1\else\bm{#1}\fi}. For example,

\def\WT@in@#1#2{\edef\WT@Aux@in@{\noexpand\in@{#1}{#2}}\WT@Aux@in@}
\DeclareRobustCommand{\WTbm}[1]{\WT@in@{b}{\f@series}\ifin@{
{\def\excl{eb}\ifx\f@series\excl#1\else\bm{#1}\fi}
}\else{#1}\fi}

An MWE

\documentclass{report}
\usepackage[T1]{fontenc}
\usepackage[sfdefault]{noto} % because Noto Sans has four different bold weights
\usepackage{bm} 
\usepackage[margin=1.5in]{geometry}
%
% This is the definition of the main command of interest, \WTbm
%
\makeatletter
\def\WT@in@#1#2{\edef\WT@Aux@in@{\noexpand\in@{#1}{#2}}\WT@Aux@in@}
\DeclareRobustCommand{\WTbm}[1]{\WT@in@{b}{\f@series}\ifin@{\bm{#1}}\else{#1}\fi}
\makeatother
%
% what follows is inessential
%
% for easier access to the content of \f@series
\makeatletter
\newcommand{\Fseries}{\f@series} 
\makeatother
%
% for easy tyesetting of the names \Fseries and \WTbm
\newcommand{\FseriesName}{\texttt{\textbackslash{}f@series}}
\newcommand{\WTbmName}{\texttt{\textbackslash{}WTbm}}
%
\newcommand{\sbseries}{\fontseries{sb}\selectfont} 
\newcommand{\ebseries}{\fontseries{eb}\selectfont} 
\newcommand{\ubseries}{\fontseries{ub}\selectfont} 

\setlength{\parindent}{0pt}
%
\begin{document}
%
\tableofcontents
%
\section{\FseriesName\  is `\Fseries' and so \WTbmName\ gives $\WTbm{\alpha>1}$, cf.\ $\alpha>1$}
%
\begin{tabbing}
\FseriesName\ is \hspace{0.2em} \= `\Fseries': \hspace{0.9em} \= The surrounding text is regular weight (medium).\hspace{0.5em}  \= \WTbmName\ gives \hspace{0.5em} \= $\WTbm{\alpha>1}$.\\
%
{\sbseries\FseriesName\ is}  \>  {\sbseries `\Fseries':} \> {\sbseries The surrounding text is semi-bold.}  \> {\sbseries \WTbmName\ gives} \> {\sbseries $\WTbm{\alpha>1}$.}\\
%
{\bfseries\FseriesName\ is}  \>  {\bfseries `\Fseries':} \> {\bfseries The surrounding text is bold.}  \> {\bfseries \WTbmName\ gives} \> {\bfseries $\WTbm{\alpha>1}$.}\\
%
{\ebseries\FseriesName\ is}  \>  {\ebseries `\Fseries':} \> {\ebseries The surrounding text is extra bold.}  \> {\ebseries \WTbmName\ gives} \> {\ebseries $\WTbm{\alpha>1}$.}\\
%
{\ubseries\FseriesName\ is}  \>  {\ubseries `\Fseries':} \> {\ubseries The surrounding text is black (ultra-bold).}  \> {\ubseries \WTbmName\ gives} \> {\ubseries $\WTbm{\alpha>1}$.}
%
\end{tabbing}
%
\end{document}

And here is the output:

enter image description here

0

I solved just this problem for some of my own stuff¹ last year:

\DeclareDocumentCommand{\ForBf}{ mm }
{
  \str_if_eq:eeTF
    \f@series
    \bfdefault
    {#1}
    {#2}
}

This uses Expl3 syntax. There were some hidden challenges because the definition of \bfdefault is not consistent. In some places its created with \newcommand (so it will be defined as a \long macro even though it has no argument), in others it's defined without the \long (this happens with the setting in fontspec).² Using the expl3 \str_if_eq:eeTF³ test allows us to test the values without having to worry about those details. Otherwise, the only effective way to compare \bfdefault and \f@series would be to do something like:⁴

\edef\next{\itdefault}
\ifx\f@shape\next ...

  1. I was adjusting the appearance of the LaTeX logo to get correct spacing in non-CM fonts and in bold and italic.

  2. This weird difference is here to stay, from some discussion on the LaTeX-3 project mailing list from March of last year when I brought it up.

  3. You can drop the T or the F if you only need one branch of the if. Unfortunately, there's no way on stackexchange to emulate the notation used in the interface3 documentation to indicate this (which would be underlined italics in code format).

  4. This was Frank Mittelbach's suggestion for the problem. Apparently, even he forgot about \str_if_eq:eeTF.

5
  • Thank you for your answer. However, this doesn't seem to work with the default font of thearticle class, when \f@series returns bx for bold text, but \bfdefault is b. (It does work if newtxtext is loaded, because the \f@series does return b for bold text.) Jan 28 at 0:15
  • MWE (without newtxtext): \documentclass{article} \usepackage{xparse} \makeatletter\ExplSyntaxOn \DeclareDocumentCommand{\ForBf}{ mm } {\str_if_eq:eeTF \f@series \bfdefault {#1} {#2}} \ExplSyntaxOff\makeatother \begin{document} \noindent Here is \verb|\bfdefault|: \bfdefault\\ {\bfseries \makeatletter Here is \verb|\f@series|: \f@series \makeatother\\ Here is \verb|\ForBf|: \ForBf{true}{false} }\\ Here is \verb|\ForBf| again: \ForBf{true}{false} \end{document} Jan 28 at 0:16
  • The first invocation of \ForBf{true}{false}, inside {\bfseries …}, should return true, and the second one, outside {\bfseries …}, should return false. But in the above MWE, they both return false. On the other hand, if one adds \usepackage{newtxtext} right after \documentclass{article}, then the first \ForBf{true}{false} correctly returns true. Jan 28 at 0:21
  • (Also, what I have in the MWE in the question I posed—the \OptBm macro—seems to be precisely what you say Frank Mittelbach was suggesting. And my question—which, admittedly, is long—explains why that doesn't quite do exactly what I would want.) Jan 28 at 0:39
  • (On why \bfdefault doesn't agree with \bfseries, see this question.) Feb 3 at 20:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.