背景
正如已经表明的那样,我正在努力克服声明的数学字体的限制。我怀疑部分原因在于我的软件包。我的软件包在过去一年半中不断发展,现在非常大,有两个辅助软件包。其中一个执行了以下代码:
\DeclareSymbolFont{pletters} {OML}{ztmcm}{m}{it}
\DeclareSymbolFont{cletters}{OT1}{cmm}{m}{it}
\DeclareSymbolFont{upletters}{U}{ntxmia}{m}{it}
\SetSymbolFont{upletters}{bold}{U}{ntxmia}{b}{it}
\DeclareFontSubstitution{U}{ntxmia}{m}{it}
\DeclareSymbolFont{coperators} {OT1}{cmr} {m}{n}
这是因为同一个子包随后继续提供从这些字体中选择希腊字形的选项,并且逐个选择字形,每个字母都有一个选项。现在,显然我不会混合mathptmx
和Computer Modern
。这种混合可能性的原因是:
- 这些
mathptmx
字形的比例不合适,也就是说,它们比周围的拉丁语大,所以我设计了命令来缩放它们,但我厌倦了频繁调整它们的大小; newtx
字形解决了缩放问题,但它们看起来比同类的字形更糟糕(在我看来)mathptmx
,并且有些我真的看不到。
后来我发现了直立newtx
希腊文,现在我只用它。但是,我不想删除这些选项,因为编写代码花了我很长时间。现在,我正在考虑将这些声明放入宏中,以便在选择其中一种字体的字形时执行,以避免额外的字体声明。但是,我认为为每个从中选择的字符声明字体并不明智——即我不喜欢这个想法。对我来说,这意味着声明upletters
多达 24 次,不,实际上是 30 次,因为有 24 个字母和 6 种\var
形式。
问题
所以我的问题是:有没有办法检查某个具有特定名称的数学字体是否已经声明?如果它是正确的字体——即,false
如果pletters
声明为不同于,它会给出什么{OML}{ztmcm}{m}{it}
?如果是这样,我该如何实现呢?
笔记:
这{coperators}{OT1}{cmr}{m}{n}
部分是因为\omicron
从操作员字体中获取了,所以为了有选项,\omicron
我需要该字体。以防有人想知道那是做什么用的。
答案1
警告:这个答案已经完全重写了。虽然总体思路与第一个版本相同,但新代码在许多方面有所不同。我也将添加一些解释。
代码
下面的代码定义了两个命令:
\GrabFontIdentifier
让其最后一个强制参数中传递的控制序列等于其第一个强制参数中传递的 NFSS 符号字体名称所绑定的字体选择器。默认情况下,它假定为数学版本所做的绑定normal
,但不同的数学版本(例如,bold
) 可以在可选参数中指定。由于它执行赋值,因此该命令显然不是纯粹可扩展的。\SameFontIdentifier
可以用作伪参数来\if
测试用初始化的给定控制序列是否\GrabFontIdentifier
是与给定 NFSS 规范(编码/系列/重量/形状)绑定的字体选择器。此命令是纯粹可扩展。
我认为最好看一下代码,除了定义这两个命令之外,还展示了如何使用它们(这是一个完整的可编译示例):
\documentclass[12pt]{article}
\makeatletter
\newcommand*\GrabFontIdentifier[3][normal]{%
% #1 <- a NFSS math version name, defaults to "normal"
% #2 <- a NFSS symbol font name, e.g. "operators"
% #3 <- a control sequence, e.g. "\next"
\@ifundefined{sym#2}{%
\global\let#3\@undefined
}{%
\sbox\z@{%
\mathversion{#1}%
$%
\expandafter\global\expandafter\let\expandafter#3%
\the\textfont\csname sym#2\endcsname
$%
}%
}%
}
\newcommand*\SameFontIdentifier[5]{%
% #1 <- control sequence to test, e.g. "\next"
% #2 <- encoding, e.g. "OT1"
% #3 <- family name, e.g. "cmr"
% #4 <- weight, e.g. "m"
% #5 <- shape, e.g. "n"
TT\fi
\expandafter\ifx\csname #2/#3/#4/#5/\tf@size\endcsname #1%
}
% Needed in order to make sure "\tf@size" is defined:
\sbox\z@{$$}
\makeatother
\begin{document}
\GrabFontIdentifier[bold]{operators}\next
% Test these ones too:
% \GrabFontIdentifier[bold]{letters}\next
% \GrabFontIdentifier{operators}\next
% \GrabFontIdentifier{letters}\next
% \GrabFontIdentifier{nonExistentName}\next
First test:
\if\SameFontIdentifier\next{OT1}{cmr}{bx}{n}%
Coincides.
\else
Does not coincide.
\fi
Second test:
\if\SameFontIdentifier\next{OML}{cmm}{b}{it}%
Coincides.
\else
Does not coincide.
\fi
\end{document}
与以前版本的代码相比,引入了以下增强功能:
\SameFontIdentifier
(以前称为 )专门用于字体大小的参数\TestFontIdentifier
已被删除:实际上,大小的指定与当前问题无关。现在会自动提供“文本”大小。如上所述,
\SameFontIdentifier
它已完全可扩展。之前并非如此(我的错误)。正确处理了不存在的符号字体名称的情况。以前也是这样(又是我的错)。
这个习惯用法\if\SameFontIdentifier … \else … \fi
很典型,我在这里就不讨论了;我只想说,这种结构允许你有一个显式的与和\if
一起使用,用于由于外部错误条件而跳过整个代码段的情况。如果你更喜欢 LaTeX 样式的条件,你可以改用这种方式:\else
\fi
\documentclass[12pt]{article}
\makeatletter
\newcommand*\GrabFontIdentifier[3][normal]{%
% #1 <- a NFSS math version name, defaults to "normal"
% #2 <- a NFSS symbol font name, e.g. "operators"
% #3 <- a control sequence, e.g. "\next"
\@ifundefined{sym#2}{%
\global\let#3\@undefined
}{%
\sbox\z@{%
\mathversion{#1}%
$%
\expandafter\global\expandafter\let\expandafter#3%
\the\textfont\csname sym#2\endcsname
$%
}%
}%
}
\newcommand*\IfSameFontIdentifier[5]{% plus two "hidden" arguments
% #1 <- control sequence to test, e.g. "\next"
% #2 <- encoding, e.g. "OT1"
% #3 <- family name, e.g. "cmr"
% #4 <- weight, e.g. "m"
% #5 <- shape, e.g. "n"
% #6 <- TRUE text
% #7 <- FALSE text
\expandafter\ifx\csname #2/#3/#4/#5/\tf@size\endcsname #1%
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
}
% Needed in order to make sure "\tf@size" is defined:
\sbox\z@{$$}
\makeatother
\begin{document}
\GrabFontIdentifier[bold]{operators}\next
% Test these ones too:
% \GrabFontIdentifier[bold]{letters}\next
% \GrabFontIdentifier{operators}\next
% \GrabFontIdentifier{letters}\next
% \GrabFontIdentifier{nonExistentName}\next
First test:
\IfSameFontIdentifier\next{OT1}{cmr}{bx}{n}
{Coincides.}
{Does not coincide.}
Second test:
\IfSameFontIdentifier\next{OML}{cmm}{b}{it}
{Coincides.}
{Does not coincide.}
\end{document}
一些解释
思路很简单:在 中\GrabFontIdentifier
,我们首先处理不存在符号字体名称的情况:在这种情况下,我们将\let
目标控制序列设为未定义。否则,我们排版一个临时框(我们不会再使用它),并使用合适的 s 链\expandafter
让目标控制序列等于 的扩展\the\textfont
n(例如,\the\textfont1
在 的情况#2
下letters
),其中数字n由 提供\csname sym#2\endcsname
。
扩展是什么\the\textfont
n答案在_The TeXbook第214页的最后两行:
\the<font>
生成选择指定字体的字体标识符。
此外,第 271 页上的语法图确认\textfont<4-bit number>
构成了的正确实例<font>
。
另一方面,NFSS 将控制序列定义\csname<enc>/<family>/<weight>/<shape>/<size>\endcsname
为具有编码的字体的字体标识符<enc>
等;因此,在中\SameFontIdentifier
,我们只需测试以这种方式从提供的参数构造的控制序列是否\ifx
等于在第一个参数中传递的控制序列。事实上,如果你看看第 219 页的顶部,其中\ifx
指定了测试成功的条件,你会发现:
如果 (a) 两个标记不是宏,并且它们都代表相同的 (字符代码,类别代码) 对或相同的 TeX 基元,则条件为真,或者相同
\font
或\chardef
或\countdef
等;…
我想,这应该足以原谅我之前的仓促回答了……
答案2
需要说明的是,以下是我最终做的事情,使用了 Gustavo 答案的旧版本中的想法。
\newcommand*\GrabFontIdentifier[2]{%
% #1 <- a control sequence, e.g. "\next"
% #2 <- a NFSS font name, e.g. "operators"
\sbox\z@{$%
\expandafter\global\expandafter\let\expandafter#1%
\the\textfont\csname sym#2\endcsname
$}%
}
\newcommand*\TestFontIdentifier[5]{%
% #1 <- control sequence to test, e.g. "\next"
% #2 <- encoding, e.g. "OT1"
% #3 <- family name, e.g. "cmr"
% #4 <- weight, e.g. "m"
% #5 <- shape, e.g. "r"
TT\fi
\expandafter\let\expandafter\@MyTemporary\csname #2/#3/#4/#5\endcsname
\ifx\@MyTemporary #1%
}
\newcommand{\declare@ptm}{
\@ifundefined{sympletters}
{\DeclareSymbolFont{pletters} {OML}{ztmcm}{m}{it}}
{\GrabFontIdentifier{\ptm@}{pletters}
\if\TestFontIdentifier{\ptm@}{OML}{ztmcm}{m}{it}
\else
\DeclareSymbolFont{pletters} {OML}{ztmcm}{m}{it}
\fi}
}
\newcommand{\declare@cmr}{
\@ifundefined{symcletters}
{\DeclareSymbolFont{cletters}{OT1}{cmm}{m}{it}}
{\GrabFontIdentifier{\cmr@}{cletters}
\if\TestFontIdentifier{\cmr@}{OT1}{cmm}{m}{it}
\else
\DeclareSymbolFont{cletters}{OT1}{cmm}{m}{it}
\fi}
}
\newcommand{\declare@upnewtx}{
\@ifundefined{symupletters}
{\DeclareSymbolFont{upletters}{U}{ntxmia}{m}{it}
\SetSymbolFont{upletters}{bold}{U}{ntxmia}{b}{it}
\DeclareFontSubstitution{U}{ntxmia}{m}{it}}
{\GrabFontIdentifier{\upnewtx@}{upletters}
\if\TestFontIdentifier{\upnewtx@}{U}{ntxmia}{m}{it}
\else
\DeclareSymbolFont{upletters}{U}{ntxmia}{m}{it}
\SetSymbolFont{upletters}{bold}{U}{ntxmia}{b}{it}
\DeclareFontSubstitution{U}{ntxmia}{m}{it}
\fi}
}
\newcommand{\declare@cop}{
\@ifundefined{symcoperators}
{\DeclareSymbolFont{coperators} {OT1}{cmr} {m}{n}}
{\GrabFontIdentifier{\cop@}{cletters}
\if\TestFontIdentifier{\cop@}{OT1}{cmr} {m}{n}
\else
\DeclareSymbolFont{coperators} {OT1}{cmr} {m}{n}
\fi}
}
如果您对没有 的 @s 感到困惑\makeatletter
,请记住我在一个包中。这似乎有效,至少当唯一声明的字体是 时upletters
。它似乎还解决了让我提出这个问题的问题:Too many math fonts
。现在,我可以声明bbold
为字母表而无需删除dutchcal
,因为我被迫这样做在有人向我提供此解决方案之前。谢谢 Gustavo。现在让我读一下我刚刚才发现的新答案。
更新: 阅读新答案后,特别是他说的部分:
另一方面,NFSS 将形式为的控制序列定义
\csname<enc>/<family>/<weight>/<shape>/<size>\endcsname
为具有编码的字体的字体标识符<enc>
等;因此,在中\SameFontIdentifier
,我们只需测试以这种方式从提供的参数构造的控制序列是否\ifx
等于在第一个参数中传递的控制序列。
我开始怀疑上面的命令没有按照我的意愿执行。更准确地说,我怀疑它在每次出现命令时都会重新声明字体\declare@<blab>
。我通过在 s的分支\message{defined}
中添加 a和在 分支中添加 a 进行了一些测试,得到了一堆。所以我按照答案的建议添加了,得到了一堆。所以看起来我想要这个测试的原因之一,避免每次都重新声明字体,并不是那么重要,因为我的问题——字体太多——即使重新声明也得到了解决。但是,我仍然会保留条件,因为我不想删除所有代码。true
\TestFontIdentifier
\message{undefined or differently defined}
false
undefinedordifferentlydefined
/\tf@size
defined