使用 Roboto 进行 Siunitx 字体粗细检测

使用 Roboto 进行 Siunitx 字体粗细检测

当加载roboto默认使用浅色字体的包时,暂时切换到不同的字体粗细会导致siunitx无法检测正确的字体。

\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}

字体粗细错误。

siunitx当使用额外的字体粗细时,检测字体粗细似乎会有些麻烦,例如这里或者这里。但是,在这种情况下,我无法理解这个问题,因为我改回了应该理解的默认权msiunitx。这里的问题是什么?

答案1

问题在于 siunitx 的代码试图以类似的方式在数学和文本模式下工作。在数学中基本上只有普通和粗体,因此在文本模式下,siunitx 或多或少只尝试检测粗体是否处于活动状态,如果没有,则使用普通系列,这意味着这里是浅色字体。一种解决方法是本地重置默认系列。以下示例需要当前的 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}

在此处输入图片描述

这里还隐藏着另一个问题:siunitx通过查看当前系列是 b 还是 bx 来检测粗体。但是,您的设置是bx针对罗马字体的,而不是sb针对无衬线字体的,因此检测无衬线字体的粗细会失败:

\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}

在此处输入图片描述

答案2

您可以尝试使用类似这样的方法进行正确的检测(以更通用的方式):

\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} 

我可能会在下一个版本中以某种方式添加它。

这将产生:

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.

答案3

以 Ulrikes 为基础回答我想扩展她的解决方案,并提供更多背景信息,了解这里发生的事情,因为其中涉及许多不同的因素,使情况变得复杂。

首先,值得一提的是,它siunitx可以在两种不同的模式,即mode=textmode=mathsiunitx 手动的关于此选项有如下说明。

mode选项决定siunitx打印输出时使用数学模式还是文本模式。选项包括math和。使用数学模式时,使用数学字体打印文本,而在文本模式下则使用文本字体。这在视觉上有多明显取决于文档中使用的字体。[...] 如果开关为 ,text则此选项无效。detect-modetrue

这里我只讨论这种mode=text情况,因为 Roboto 是文本字体,而不是数学字体。因为mode=math应该可以采用类似的方法。


由于需要检测字体粗细,我们需要知道存储在中的当前粗细是多少\f@series。对于 Roboto 字体,此机制最近已更改。例如,在 2019/04/19 的软件包版本中,带有medium选项的粗体字体定义为

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

而对于 2019/12/11 的新版本,此值更改为

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

这导致 Ulrike 指出需要当前版本的 LaTeX。类似地,许多显式单词(如light或 )thin被更改为简写形式(如l或 )el。这实际上不会给后续解决方案带来问题,但这意味着它需要适应软件包版本。


我首先解决了无法检测到常规粗细的问题,然后转向粗体文本的问题。Ulrike 表示,它siunitx只会尝试找出粗体是否处于活动状态,而不会检查字体可能提供的额外粗细。因此,让我们看看siuntix文本模式

\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 } }
}

因此,siunitx查看是否\f@series以字母开头b,如果是,则设置粗体打印。但是它检查是否\f@series以字母开头l,如果是,则发出\lseries。因此,这看起来应该按原样工作,因为它会\lseries为轻型 Roboto 发出,为中型 Roboto 不执行任何操作,并为以\f@series开头设置粗体b

那么,为什么切换到中等 Roboto 后输出会出错?原来,为了在 sans 中设置数字和单位,siunitx需要调用。现在,当通过或\sffamily选择中等重量时,则计算为,其定义为\fontseries{m}\selectfont\mdseries\sffamily\mdseries@sf

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

roboto包加载选项时light。这意味着\sffamily实际上明确切换回轻量级并产生错误的输出。我不太确定这应该归咎于哪个包,因为对于siunitxroboto单独来说,它们的方法似乎合理,但结合起来却带来了麻烦。

Ulrike 给出了一个可能的解决方案,即(本地)重新定义\sffamily,例如使用她提供的便捷命令。

\DeclareFontSeriesDefault[sf]{md}{m}

另一个选项是(本地)告诉siunitx使用不同的命令来设置 sans 字体,使用据我所知未记录的siunitx选项text-sf(或number-text-sf和)。unit-text-sf

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

粗体的问题在于,软件包medium的选项roboto设置了半粗体,而相应的sb,嗯,就是不以字母开头b。我在这里看到的唯一方法是修补测试\__siunitx_detect_font_weight_text:

这种方法类似于这个答案,但显然不太可靠。正如该回答和 Ulrike 在其评论,在软件包的内部命令中摸索通常不是一个好主意。 的实现甚至名称\__siunitx_detect_font_weight_text:可能会随着 的未来版本而改变siunitx,然后必须相应地更正补丁。

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

此补丁使用的\apptocmd功能etoolbox。请注意,问题在 GitHub 上要求添加此功能,因此希望这个补丁将来会过时。


最后,结合以上所有内容的完整解决方案可能如下所示。

\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

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

\begin{RobotoRegular}
\makeatletter f@series is: \f@series. \quad 
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}

细体、中等体和半粗体。

此解决方案现在考虑了所有三种情况的权重。如果需要该软件包提供的更多权重roboto,当然可能需要在这方面进行更多更改。

相关内容