在这个问题,sfranky 要求在 上添加\bm{\phi}
由 包生成的双重重音符号。和包bm
中都实现了双重重音符号,但这些包与 配合得不好。 我自己发现:可以使用 来使其工作。amsmath
accents
bm
\use@mathgroup
现在,在回答 sfranky 的问题时,我建议使用
\use@mathgroup{\M@OMS}{5}{\bm{\phi}}
\phi
得到包可以处理的粗体accents
。但是,其中一半只是有根据的猜测。OMS
我认为是编码(在本例中可能应该是OML
),5
是的字体系列\bm{phi}
。虽然它似乎有效,但这肯定不是正确的做法。
因此我的问题是:有没有办法自动识别我要使用的符号的编码和系列?
答案1
可以通过查看符号的系列\mathcode
(这适用于明确的字符,如1
、a
等;例如,\the\mathcode`1
给出十六28721
进制的符号"7031
)或查看\meaning
以 开头的符号\mathchar
(例如\meaning\phi
给出\mathchar"11E
)来提取符号的系列。四个十六进制数字中的第二个是数学系列(如果少于这个数字,则前导数字为零)。例如,1
具有数学代码"7031
,因此它来自系列0
(运算符),并且\phi
具有数学代码"011E
,因此它来自系列1
(字母)。
要从符号中获取家族,您可以按照以下方式进行:查看\meaning
符号的。通常带有重音符号(1
、\phi
等)的符号通常具有 ,\meaning
其为\mathchar"....
或the character 1
或。对于第一种类型,您必须从 中the letter a
删除并仅保留数字。对于第二种类型,您必须检查 是否以 开头,查看,然后将其转换为十六进制(例如,通过使用\mathchar
\meaning
\meaning
the
\mathcode
binhex.tex
)。在每种情况下,您可能需要用前导零填充数字以可靠地提取第二位数字。
要说出字符的含义是以\mathchar
还是以开头the
,有多种方法。在这里,我使用了与 相同的方法,amsmath.sty
以便自动\dots
。由于\meaning
生成 catcode 12 个字符,因此必须生成一个\
、一个m
、一个t
等 catcode 为 12 的字符来进行测试。这样做的一种方法是使用大写/小写技巧(原理与我对问题的回答相同如何制作真正的反斜杠(转义)字符?):取 12 个字符的 catcode(例如!
、?
等),并将其大写版本设为\
、m
等,然后将整个定义大写。
以下代码展示了如何实现这一切。我已经对其进行了注释,希望您能看懂。
\documentclass{article}
% the code works with all font packages
%\usepackage{lmodern}
%\usepackage{fourier}
%\usepackage[utopia]{mathdesign}
%\usepackage[charter]{mathdesign}
%\usepackage[garamond]{mathdesign}
%\usepackage{txfonts}
%\usepackage{pxfonts}
%\usepackage{mathpazo}
\usepackage{amsmath}
\usepackage{bm,accents}
\input{binhex}% for hex conversion
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%% CODE TO DETECT MATH FAMILY %%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
\makeatletter
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Utility macros to pad zeros (e.g. transform 34 into 0034)
% The delimiter is \empty so that if it is expanded, it does nothing
\def\@pad@four@zeros#1{\@pad@four@zeros@aux#1\empty\empty\empty\empty}%
\def\@pad@four@zeros@aux#1#2#3#4\empty{%
\ifx#1\empty 0\fi\ifx#2\empty 0\fi\ifx#3\empty 0\fi\ifx\\#4\\0\fi#1#2#3#4%
}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Macro extracting the second digit (e.g. 7163 -> 1)
\def\@extract@second@of@four#1#2#3#4\@nil{#2}%
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% new boolean which will be used to test if a \meaning begins with \mathcha (e.g. \mahchar"11E)
\newif\if@begins@by@mathchar@
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% uppercase trick to obtain characters with catcode 12
\begingroup
\uccode`!=`\\ % ! is \ with catcode 12
\uccode`?=`m % ? is m with catcode 12
\uccode`,=`a % , is a with catcode 12
\uccode`.=`t % . is t with catcode 12
\uccode`;=`h % ; is h with catcode 12
\uccode`/=`c % / is c with catcode 12
\uccode`|=`r % | is r with catcode 12
\uppercase{\endgroup
\def\@if@begins@by@mathchar@#1#2#3#4#5#6#7#8#9\@nil{%
\@begins@by@mathchar@false % initiate boolean to false
% test if #1#2#3#4#5#6#7#8 is !?,.;/;, i.e. \mathcha
\ifx !#1\ifx ?#2\ifx ,#3\ifx .#4\ifx ;#5\ifx /#6\ifx ;#7\ifx ,#8%
\@begins@by@mathchar@true % switch boolean to true
\fi\fi\fi\fi\fi\fi\fi\fi
\if@begins@by@mathchar@ % if boolean true
\expandafter\@firstoftwo
\else % if boolean false
\expandafter\@secondoftwo
\fi
}
% macro which strips prefix \mathchar
\gdef\@strip@mathchar@prefix!?,.;/;,|#1\@nil{#1}%
% end of uppercase trick
}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% new boolean which will be used to test if a \meaning begins with the (e.g. the character 1)
\newif\if@begins@by@the@
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% uppercase trick to obtain characters with catcode 12
\begingroup
\uccode`!=`t % ! is t with catcode 12
\uccode`?=`h % ? is h with catcode 12
\uccode`,=`e % , is e with catcode 12
\uppercase{\endgroup
\def\@if@begins@by@the@#1#2#3#4\@nil{%
\@begins@by@the@false % initiate boolean to false
% test if #1#2#3 is !?, i.e. the
\ifx !#1\ifx ?#2\ifx ,#3%
\@begins@by@the@true % switch boolean to true
\fi\fi\fi
\if@begins@by@the@ % if boolean true
\expandafter\@firstoftwo
\else % if boolean false
\expandafter\@secondoftwo
\fi
}
}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% macro which finds the font family of a math glyph
% does not work for characters modified by amsmath (because of the
% \DOTSB before them) but as they are not normally accented, it
% shouldn't be a problem
\def\find@font@family#1{%
% #1 = glyph from which to extract font family
\edef\@the@mathcode@{-1}% dummy definition (-1 will give a 0 family)
% test if #1 begins with \mathchar
\expandafter\@if@begins@by@mathchar@\meaning#1\@nil
% if so, store the value of \mathchar inside \@the@mathcode@
{\edef\@the@mathcode@{%
\number\expandafter\@strip@mathchar@prefix\meaning#1\@nil}}%
% otherwise, do nothing
{}%
% test if #1 begins with the
\expandafter\@if@begins@by@the@\meaning#1\@nil
% if so, store the mathcode inside \@the@mathcode@
{\edef\@the@mathcode@{\the\mathcode`#1}}%
% otherwise, do nothing
{}%
% convert the mathcode into hex with package binhex.tex
\edef\@the@mathcode@hex{\hex{\@the@mathcode@}}%
% convert the hex number to a 4-digit one
\edef\@the@mathcode@hex{\expandafter\@pad@four@zeros
\expandafter{\@the@mathcode@hex}}%
% store the second digit (which is the math family) in \@the@math@family
\edef\@the@math@family{%
\expandafter\@extract@second@of@four\@the@mathcode@hex\@nil}%
}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%% NEW \accbm MACRO %%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
\newcommand{\accbm}[1]{%
% store the font family inside \@the@math@family
\find@font@family{#1}%
% assign counter \@tempcnta to \@the@math@family so that \bm@boldtable
% gives the right corresponding bold family offset
\@tempcnta=\@the@math@family
% select the right \fam and apply it to \bm
% \@the@math@family+\bm@boldtable is the number of the bold family
\use@mathgroup{}% not currently used by LaTeX so can be empty
{\number\numexpr\@the@math@family+\bm@boldtable}%
{\bm{#1}}%
}
%
\makeatother
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%% END OF CODE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{document}
\newcommand{\test}[1]{$\hat{\dot{\bm{#1}}} \hat{\dot{\accbm{#1}}} \bm{{\hat{\dot{#1}}}}$\par}
\test{\phi}
\test{A}
\test{1}
\test{\Gamma}
\test{+}
% should give 3 but gives 0 if amsmath is loaded
\makeatletter$\find@font@family{\sum}\@the@math@family$\makeatother
\end{document}
在测试中,我比较了\hat{\dot{\bm{...}}}
、\hat{\dot{\accbm{...}}}
和\bm{{\hat{\dot{...}}}}
(最后一个将重音符号加粗,但可以立即使用)。以下是使用 mathdesign/utopia 字体时的结果(从左到右):
对代码的一些解释。
1.关于编码。编码信息(例如\M@OMS
、\M@OML
等)实际上并非由 LaTeX 使用。\meaning
这些宏的 通常是\default@M .
或\default@M \noaccents@ .
其中\default@M
为空,并由\noaccents@
amsmath 的数学重音机制用于在系列之间切换0
,并7
避免在数学字母表内部使用重音时出现奇怪的结果(尝试$\mathcal{\hat{A}}$
使用和不使用 amsmath 来查看效果)。
一旦获得了数字,就可以通过查看 \the\textfont2 (通常为 \OMS/cmsy/m/n/10)并提取和 第一个\fam
之间的标记来提取编码,这可以通过分隔宏来完成:\
/
\makeatletter
\def\@extract@encoding@aux#1#2/#3\@nil{#2}
\newcommand{\extractencoding}[1]{%
\expandafter\@extract@encoding@aux\detokenize\expandafter{\the\textfont#1}/\@nil
}
\makeatother
\extractencoding{0} \extractencoding{1} \extractencoding{2} \extractencoding{3} \extractencoding{4}
如果当前未使用该字体系列,则通过这种方式提取的编码将为“nullfont”。
2.关于boldtable宏。该\bm@boldtable
宏保存了系列和它们的粗体版本之间的偏移量,并\ifcase
以以下方式存储在结构中:
\ifcase \@tempcnta 9\relax \or 9\relax \or 9\relax \or 9\relax \or \z@ \or \z@ \or \z@ \or \z@ \or \z@ \else \z@ \fi .
这意味着偏移量9
对于\fam0
到 是\fam3
,对于所有其他的偏移量为零。要访问这些数字,必须将当前\fam
数字存储在计数寄存器中\@tempcnta
,您可以使用
\@tempcnta=\@the@math@family
然后你得到相应的粗体\fam
数字
\number\numexpr\@the@math@family+\bm@boldtable
答案2
我不知道有任何预先存在的机制可以识别符号的编码和系列。
这encguide 包可能会提供一些启发。它使用如下命令来打印每个系列中的编码集:
\ftable{cmr10}{OT1}
具体来说,它会打印属于某个家族和编码的每个字符,如下所示:
\font\X=cmr10
\X{\char86} % "V"
系列和编码列表位于指南导览(从第 1298 行开始)。
话虽如此,我不确定您是否可以从给定的符号名称或字符中提取家族和编码。我尝试使用该ifthen
包进行比较(即\X\ifthenelse{\equal{V}{\char86}}{Same}{Different}
),但比较没有按预期进行。如果可以进行比较,则可以将符号与每个家族的每个字符进行比较。
与构建这个反向映射相比,对于 sfranky(以及有类似问题的其他用户)来说,通过 X Ǝ T E X 或 LuaTeX使用 unicode 可能是一个明智的选择。
虽然这不是一个解决方案,但我希望这能提供一些有用的思考。
答案3
这实际上不是对我问题的回答,而是解释了为什么包bm
无法识别编码和系列是错误的原因。举个例子,在via\mathcal
中定义fontmath.ltx
\DeclareSymbolFontAlphabet{\mathcal}{symbols}
实际上,这使得\mathcal
扩展为
\use@mathgroup\M@OMS\symsymbols
在数学模式下使用时,即同时提供编码( \M@OMS
)和系列( )。这使和包能够正确放置多个重音。因此,对所讨论问题的正确修复应该是最终扩展到正确的。\symsymbols
amsmath
accents
\bm
\use@mathgroup<encoding><family>