我非常确定在类级别传递的选项(包括未知选项)也传递给了已加载的包。因此我不明白为什么以下 MCE 如此有效:
\documentclass{article}
\usepackage[acronym,nomain]{glossaries}
\makeglossaries
\newacronym{at}{at}{Acronym test}
\begin{document}
\glsaddall
\printglossary[type=\acronymtype]
\end{document}
但不是以下将acronym
选项传递给类而不是glossaries
包的情况:
\documentclass[acronym]{article}
\usepackage[nomain]{glossaries}
\makeglossaries
\newacronym{at}{at}{Acronym test}
\begin{document}
\glsaddall
\printglossary[type=\acronymtype]
\end{document}
这确实会导致以下错误消息:
! Missing \endcsname inserted.
<to be read again>
\glsdefaulttype
l.6 \newacronym{at}{at}{Acronym test}
? s
OK, entering \scrollmode...
! Missing \endcsname inserted.
<to be read again>
\glsdefaulttype
l.6 \newacronym{at}{at}{Acronym test}
! Missing \endcsname inserted.
<to be read again>
\glsdefaulttype
l.6 \newacronym{at}{at}{Acronym test}
! Package glossaries Error: Glossary type '\glsdefaulttype ' has not
been defined.
[...]
请注意以下 MCE(acronym
选项传递给类,但没有nomain
glossaries
选项):
\documentclass[acronym]{myclass}
\usepackage{glossaries}
\makeglossaries
\newacronym{at}{at}{Acronym test}
\begin{document}
\glsaddall
%
\printglossary[type=\acronymtype]
\end{document}
确实有效(嗯,主要是:词汇表的名称是“词汇表”,而如果acronym
选项不是传递给类而是glossaries
直接传递给类,则它是“首字母缩略词”)。
顺便说一句,在个人类中明确传递(感谢\PassOptionsToPackage
)acronym
类选项会产生同样的错误:glossaries
\begin{filecontents*}{myclass.cls}
\NeedsTeXFormat{LaTeX2e}
\newif\if@acronym\@acronymfalse
\DeclareOption{acronym}{\@acronymtrue}
\ProcessOptions\relax
\LoadClass{article}
%
\RequirePackage{filehook}
\RequirePackage{hopatch}
%
\hopatch@AfterPackage{glossaries}{%
\if@acronym
\PassOptionsToPackage{acronym}{glossaries}%
\fi
}
\endinput
\end{filecontents*}
\documentclass[acronym]{myclass}
\usepackage[nomain]{glossaries}
\makeglossaries
\newacronym{at}{at}{Acronym test}
\begin{document}
\glsaddall
%
\printglossary[type=\acronymtype]
\end{document}
答案1
下面是一个可以说明这个问题的一般示例。假设我编写了一个名为的包,如下所示test
:
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{test}
\RequirePackage{xkeyval}
\RequirePackage{etoolbox}
\define@boolkey{test.sty}[test]{bold}[true]{}
\newcommand*{\test@animal}{parrot}
\newcommand*{\test@parrot@sound}{squawk}
\newcommand*{\test@dog@sound}{woof}
\newcommand*{\test@duck@sound}{quack}
\newcommand*{\test@cow@sound}{moo}
\define@choicekey{test.sty}{animal}{dog,duck,cow}{%
\renewcommand*{\test@animal}{#1}%
}
\newcommand*{\test@adj}{silly}
\define@key{test.sty}{adj}{\renewcommand*{\test@adj}{#1}}
\ProcessOptionsX
\newcommand*{\test}{{\iftestbold \bfseries \fi The \test@adj\
\test@animal\ said ``\csuse{test@\test@animal @sound}!''}}
\endinput
以下是使用此类的示例文档:
\documentclass{article}
\usepackage[bold,adj={giant killer},animal=duck]{test}
\begin{document}
\test
\end{document}
得出的结果为:
但是,正如评论中提到的,这些选项不能通过文档类选项传递。如果我尝试:
\documentclass[animal=duck]{article}
我收到警告:
LaTeX Warning: Unused global option(s):
[animal=duck].
更糟糕的是,如果我尝试:
\documentclass[animal={duck}]{article}
我收到错误:
! LaTeX Error: Missing \begin{document}.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.123 \input
{size1\@ptsize.clo}
总的来说,我不太喜欢指定包选项的想法,因为\documentclass
可能有多个包具有相同的选项。如果用户没有意识到这一点,可能会出现意外结果。(这从这里或其他网站或新闻组中用户提出的问题次数就可以看出,他们不明白为什么他们包含的图像都处于草稿模式。)事实上,babel
语言选项是我建议与文档类选项一起传递的唯一选项(但仅仅是因为babel
没有提供方便的加载语言列表)。
那么还有哪些替代方案呢?
软件包通常会提供可以代替软件包选项使用的命令。以下是对软件包设计的改进:
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{test}
\RequirePackage{xkeyval}
\RequirePackage{etoolbox}
\define@boolkey{test.sty}[test]{bold}[true]{}
\newcommand*{\test@animal}{parrot}
\newcommand*{\test@parrot@sound}{squawk}
\newcommand*{\test@dog@sound}{woof}
\newcommand*{\test@duck@sound}{quack}
\newcommand*{\test@cow@sound}{moo}
\newcommand*{\setanimal}[1]{%
\ifcsdef{test@#1@sound}%
{%
\renewcommand*{\test@animal}{#1}%
}%
{%
\PackageError{test}{Unknown animal `#1'}{}%
}%
}
\define@choicekey{test.sty}{animal}{dog,duck,cow}{%
\setanimal{#1}%
}
\newcommand*{\test@adj}{silly}
\newcommand*{\setadj}[1]{\renewcommand*{\test@adj}{#1}}
\define@key{test.sty}{adj}{\setadj{#1}}
\ProcessOptionsX
\newcommand*{\setuptest}[1]{\setkeys{test.sty}{#1}}
\newcommand*{\test}{{\iftestbold \bfseries \fi The \test@adj\
\test@animal\ said ``\csuse{test@\test@animal @sound}!''}}
\endinput
现在,如果我无法在加载包时设置选项(例如,该包由另一个包自动加载),我可以通过这些新命令指定我的选项。例如:
\documentclass{article}
\usepackage{test}
\setuptest{bold,adj={giant killer},animal=duck}
\begin{document}
\test
\end{document}
不幸的是,有些时候必须在加载包时设置选项,这又回到了glossaries
。大多数包选项都有一个等效的命令或一组可实现相同效果的命令。例如,shortcuts
可以通过命令 实现 选项\DefineAcronymSynonyms
。该acronym
选项稍微复杂一些:
\documentclass{article}
\usepackage{glossaries}
\newglossary[alg]{acronym}{acr}{acn}{\acronymname}%
\renewcommand*{\acronymtype}{acronym}%
\DeclareAcronymList{acronym}
\newglossaryentry{sample}{name=sample,description={an example}}
\newacronym{abc}{ABC}{Sample}
\makeglossaries
\begin{document}
\gls{sample}
\gls{abc}
\printglossaries
\end{document}
在加载包时,实际上只有两个选项必须设置:makeindex
和xindy
。在 4.01 版本中,您还可以使用makeindex
或xindygloss
(相当于xindy
没有值的选项)作为类选项。
编辑:
请注意,使用定义的包选项\DeclareOptionX
也不能通过文档类传递。例如,假设我的测试包现在是:
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{test}
\RequirePackage{xkeyval}
\RequirePackage{etoolbox}
\newcommand*{\test@shape}{}
\DeclareOptionX{em}{\renewcommand*{\test@shape}{\em}}
\define@boolkey{test.sty}[test]{bold}[true]{}
\newcommand*{\test@animal}{parrot}
\newcommand*{\test@parrot@sound}{squawk}
\newcommand*{\test@dog@sound}{woof}
\newcommand*{\test@duck@sound}{quack}
\newcommand*{\test@cow@sound}{moo}
\newcommand*{\setanimal}[1]{%
\ifcsdef{test@#1@sound}%
{%
\renewcommand*{\test@animal}{#1}%
}%
{%
\PackageError{test}{Unknown animal `#1'}{}%
}%
}
\define@choicekey{test.sty}{animal}{dog,duck,cow}{%
\setanimal{#1}%
}
\newcommand*{\test@adj}{silly}
\newcommand*{\setadj}[1]{\renewcommand*{\test@adj}{#1}}
\define@key{test.sty}{adj}{\setadj{#1}}
\ProcessOptionsX
\newcommand*{\setuptest}[1]{\setkeys{test.sty}{#1}}
\newcommand*{\test}{{\test@shape \iftestbold \bfseries \fi The \test@adj\
\test@animal\ said ``\csuse{test@\test@animal @sound}!''}}
\endinput
测试文档为:
\documentclass[em]{article}
\usepackage{test}
\begin{document}
\test
\end{document}
该em
选项将被忽略并发出警告:
LaTeX Warning: Unused global option(s):
[em].
\documentclass
我能想到的唯一允许通过或传递此选项的方法\usepackage
是使用两者定义它\DeclareOptionX
,\DeclareOption
然后\ProcessOptionsX
像这样处理类选项:
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{test}
\RequirePackage{xkeyval}
\RequirePackage{etoolbox}
\newcommand*{\test@shape}{}
\DeclareOptionX{em}{\renewcommand*{\test@shape}{\em}}
\DeclareOption{em}{\renewcommand*{\test@shape}{\em}}
\define@boolkey{test.sty}[test]{bold}[true]{}
\newcommand*{\test@animal}{parrot}
\newcommand*{\test@parrot@sound}{squawk}
\newcommand*{\test@dog@sound}{woof}
\newcommand*{\test@duck@sound}{quack}
\newcommand*{\test@cow@sound}{moo}
\newcommand*{\setanimal}[1]{%
\ifcsdef{test@#1@sound}%
{%
\renewcommand*{\test@animal}{#1}%
}%
{%
\PackageError{test}{Unknown animal `#1'}{}%
}%
}
\define@choicekey{test.sty}{animal}{dog,duck,cow}{%
\setanimal{#1}%
}
\newcommand*{\test@adj}{silly}
\newcommand*{\setadj}[1]{\renewcommand*{\test@adj}{#1}}
\define@key{test.sty}{adj}{\setadj{#1}}
\@for\CurrentOption :=\@declaredoptions\do{%
\ifx\CurrentOption\@empty
\else
\@expandtwoargs
\in@ {,\CurrentOption ,}{,\@classoptionslist,\@curroptions,}%
\ifin@ \@use@ption
\expandafter \let\csname ds@\CurrentOption\endcsname\@empty
\fi
\fi
}
\ProcessOptionsX
\newcommand*{\setuptest}[1]{\setkeys{test.sty}{#1}}
\newcommand*{\test}{{\test@shape \iftestbold \bfseries \fi The \test@adj\
\test@animal\ said ``\csuse{test@\test@animal @sound}!''}}
\endinput
现在以下两个文档产生相同的输出:
示例 1:
\documentclass[em]{article}
\usepackage[bold,adj={giant killer},animal=duck]{test}
\begin{document}
\test
\end{document}
示例 2:
\documentclass{article}
\usepackage[em,bold,adj={giant killer},animal=duck]{test}
\begin{document}
\test
\end{document}
两种情况下的结果都是: