In myclass.cls, I want to create an option with xkeyval so that, for
example,
\documentclass[xcolor={monochrome,dvipsnames}]{myclass}
will pass the xcolor options to xcolor package which is loaded in
myclass.cls.
I'm new to xkeyval and I tried the following with no luck:
\define@key[cu]{kkk}{xcolor}[{dvipsnames}]{\PassOptionsToPackage{#1}{xcolor}}
Thanks.
Leo
What exactly fails? Can you post a minimal example?
--
Joseph Wright
xkv.tex:
\documentclass[xcolor={monochrome,dvipsnames}]{myclass}
\begin{document}
\textcolor{red}{demo1}
\end{document}
myclass.cls:
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{myclass}[2009/11/23 a class for me]
\RequirePackage{xkeyval}
\define@key[cu]{kkk}{xcolor}[{dvipsnames}]{\PassOptionsToPackage{#1}{xcolor}}
\ProcessOptionsX[cu]<kkk>
\LoadClass{article}
\RequirePackage{xcolor}
I'm getting this:
......
l.7
! You can't use `macro parameter character #' in horizontal mode.
\@removeelement #1#2#3->\def \reserved@a ##1,#1,##
2\reserved@a
{##1,##2\rese...
l.7
Runaway argument?
{##1,##2\reserved@b }\def \reserved@b ##1,\reserved@b ##2\reserved@b
\ETC.
! Paragraph ended before \reserved@a was complete.
<to be read again>
\par
l.7
......
> -- Joseph Wright
Thanks.
Leo
Something seems to be up here: what you've done looks fine to me.
Everything is okay when using xkyeval for a package using a modified
version of the same (miss out the class lines and save as a .sty file
to see this). Perhaps report the issue to the xkeyval author?
--
Joseph Wright
I've reported this to the package author. Hopefully we'll here what is
up soon enough!
--
Joseph Wright
What about
> \documentclass[xcolor={monochrome,dvipsnames}]{myclass}
--> \documentclass[{xcolor=monochrome,dvipsnames}]{myclass}
I did not dive into the option list of xcolor deeply, but I think, the
result is the intended. And no error message is thrown.
Best, Claas
Thanks. But I think {xcolor=monochrome,dvipsnames} is less intuitive
than xcolor={monochrome,dvipsnames}, which is common in a few packages.
Best,
Leo
--
Emacs uptime: 62 days, 5 hours, 44 minutes, 32 seconds
Thank you for this.
> -- Joseph Wright
>
--
Emacs uptime: 62 days, 5 hours, 46 minutes, 59 seconds
> Thanks. But I think {xcolor=monochrome,dvipsnames} is less intuitive
> than xcolor={monochrome,dvipsnames}, which is common in a few packages.
Full ack, I came across this more or less accidently :-)
Best, Claas
There is obviously a difference in how keyval packages handle class
options. A similar idea to what you want to achieve is pretty easy
using the keys system in expl3 (with the accompanying l3keys2e from
the xpackages):
\begin{filecontents}{test.cls}
\RequirePackage{expl3}
\ProvidesExplClass{test}{2009/11/23}{0.0}{test class}
\LoadClass{article}
\RequirePackage{l3keys2e}
\keys_define:nn{test}
{ xcolor .code:n = \PassOptionsToPackage {#1} { xcolor } }
\ProcessKeysOptions{test}
\RequirePackage{xcolor}
\end{filecontents}
\documentclass[xcolor={monochrome,dvipsnames}]{test}
\begin{document}
\textcolor{red}{demo1}
\end{document}
I suspect the same will work using kvoptions, as the code in l3keys2e
for handling options is based on that in kvoptions.
--
Joseph Wright
Please try the following, if you like it. It worked on my system.
\documentclass{myclass}
\myclassoptions{xcolor={monochrome,dvipsnames}}
\begin{document}
\textcolor{red!75}{Demo}
\end{document}
% myclass.cls:
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{myclass}[2009/11/23 v1.0 A minimal class]
\RequirePackage{xkeyval}
\define@key[KV]{myclass}{xcolor}[dvipsnames]{\PassOptionsToPackage{#1}
{xcolor}}
\ProcessOptionsX[KV]<myclass>\relax
\long\def\myclassoptions#1{\setkeys[KV]{myclass}{#1}}
\LoadClass{article}
\RequirePackage{xcolor}
% You also need a way of passing unknown options.
xcolor={monochrome,dvipsnames}
is first parsed just using the key name. If the key is defined for the
class, then the option is added to the "to do" list, otherwise it's
added to the "not used" list directly. So at no point is the option
removed from any lists: it's only added. To do this, kvoptions
overwrites the kernels \@unusedoptions with its own.
In contrast, xkeyval uses a modified parser to both check the validity
of keys and processes them at the same time. It does this by first
processing the option, then removing it from \@unusedoptions using
\@removeelement. However, the way \@removeelement is defined only works
for arguments not containing braces. So this is where the odd errors are
appearing.
Now, who's bug this is is a good question. Clearly, xkeyval could use
the same approach as kvoptions to bypass the issue, but at the same time
\@removeelement should behave better. I'll report this as a LaTeX bug, I
think, and leave it to the author of xkeyval to decide how to handle the
issue at his end (LaTeX bugs don't necessarily get resolved very quickly).
--
Joseph Wright
Thank you for looking into this.
I just learnt that \RequirePackage{xkvltxp} also fixes the problem.
Leo
Thank you for this. I think this is the best solution without loading
xkvltxp.
Leo
I tried using:
\DeclareOptionX*{%
\PassOptionsToClass{\CurrentOption}{memoir}%
}
But it didn't work. Ideas?
Leo
I just realised there's a flaw in this as (it seems)
PassOptionsToPackage is only for future RequirePackage and thus it won't
work after the package xolor has been loaded, which is the case in the
code above.
Leo
--
Emacs uptime: 65 days, 2 hours, 51 minutes, 51 seconds
The solution using \myclassoptions can't be used to pass options to
packages, because the packages are loaded by the class file before
\myclassoptions is invoked. If you must really pass options to
packages via \documentclass, you need to use \RequirePackage{xkvltxp}
on the top of \documentclass statement or use another scheme, which
submits the options to packages individually (option by option) via
\documentclass. The latter scheme requires that you load the packages
(eg, xcolor) in the class file after assemblying the options within
the class file. If you're interested in the latter approach, I can
provide a sample.
> \DeclareOptionX*{%
> \PassOptionsToClass{\CurrentOption}{memoir}}
> But it didn't work. Ideas?
Which errors did you get?
Or use kvoptions to do the processing, where it works.
--
Joseph Wright
It was a mistake. It works as expected.
Leo
I re-read your keyval.pdf;)
I stick with xkeyval as it seems it is the most popular package widely
used in many packages.
I haven't installed any LaTeX3 packages yet as I am not sure it will
happen (sorry to sound pessimistic).
Leo
I have to say that I wouldn't choose xkeyval if I was writing any of my
packages "from scratch". It's caused me no end of trouble in achemso. I
think of the LaTeX2e solutions, I'd go with kvoptions + kvsetkeys (both
in Heiko Oberdiek's bundle, and therefore widely available). While I
like pgfkeys, there are some bugs (such as not being babel safe) that
put me off.
> I haven't installed any LaTeX3 packages yet as I am not sure it will
> happen (sorry to sound pessimistic).
No problem :-) At least as far as expl3 goes, things do work, and the
code is in TeX Live 2009. So that at least will be available. (I'm using
expl3 for version 2 of siunitx: one reason is that this means I do get a
keyval system that works and is pretty flexible. But then I wrote most
of the keyval system for expl3, so I would say that.)
--
Joseph Wright
I will migrate my .cls to kvoptions. Thanks for the info.
Leo
--
Emacs uptime: 66 days, 8 hours, 17 minutes, 38 seconds
I have found a way to use \myclassoptions to pass options to packages
(without deploying 'xkvltxp' and 'kvoptions-patch' packages). See
below. The trick is to delay the loading of the packages (to which you
want to pass options) until \myclassoptions is fired. For obvious
reasons, instead of using the \AtBeginDocument hook from the LaTeX
kernel, I have used the \AtEndPreamble hook from etoolbox.sty.
Disclaimer: I have conducted only limited tests on some larger in-
house class files.
What are the risks in using this approach? I don't know. But one
limitation is that you can't use the resources from the deferred
packages in your class file.
To avoid deferring package loading, I tried another approach which
also worked. I wrote all the \PassOptionsToPackage statements to a
special auxiliary file (call it .pop file, for 'Pass Options To
Package'), which is loaded early enough, unlike the .aux file). This
approach requires that the document is compiled at least twice, a pre-
requisite to get references right, anyway. On the first run, when
\PassOptionsToPackage statements haven't yet been written in the .pop
file, the run is neatly aborted as soon as all of them have been
written. I prefer this latter method, but it is slightly more
complicated.
You can restrict within the class file the options that can appear in
\myclassoptions. For example, you can instruct it not to accept
a4paper, 10pt, twocolumn, etc, which should instead appear in
\@classoptionslist.
By using toggles below, the packages are loaded only if indicated/
needed by the calling document. This makes sense only because the
loading of the packages is delayed.
%%%%%% Class file %%%%%%%
\ProvidesClass{myclass}[2009/12/10 v1.0 A minimal class]
\NeedsTeXFormat{LaTeX2e}
\RequirePackage{etoolbox}
% You can use kvoptions.sty & kvsetkeys.sty instead
% of xkeyval (as you've agreed to follow the suggestion of Joseph).
\RequirePackage{xkeyval}
\newtoggle{xcolor}
\newtoggle{natbib}
\AtEndPreamble{%
\iftoggle{xcolor}{\RequirePackage{xcolor}}{}%
\iftoggle{natbib}{\RequirePackage{natbib}}{}%
}
\define@key[KV]{myclass}{xcolor}[dvipsnames]{%
\toggletrue{xcolor}%
\PassOptionsToPackage{#1}{xcolor}%
}
\define@key[KV]{myclass}{natbib}[numbers]{%
\toggletrue{natbib}%
\PassOptionsToPackage{#1}{natbib}%
}
\DeclareOptionX*{\PassOptionsToClass{\CurrentOption}{memoir}}
\ExecuteOptionsX[KV]<myclass>{}
\ProcessOptionsX*[KV]<myclass>\relax
\def\myclassoptions#1{\setkeys[KV]{myclass}{#1}}
\LoadClass{memoir}
\endinput
%%%%%%%%% Example document %%%%%%%%%
\documentclass[a4paper]{myclass}
\myclassoptions{xcolor={table,dvipsnames},natbib=
{numbers,sort&compress}}
\begin{document}
% Without the 'table' option to 'xcolor' package above,
% LaTeX will complain about the commands below:
\rowcolors[\hline]{3}{green!25}{yellow!50}
\arrayrulecolor{red!75!gray}
\begin{tabular}{ll}
test & row \number\rownum\\
test & row \number\rownum\\
test & row \number\rownum\\
test & row \number\rownum\\
\arrayrulecolor{black}
test & row \number\rownum\\
test & row \number\rownum\\
\rowcolor{blue!25}
test & row \number\rownum\\
test & row \number\rownum\\
\hiderowcolors
test & row \number\rownum\\
test & row \number\rownum\\
\showrowcolors
test & row \number\rownum\\
test & row \number\rownum\\
\multicolumn{1}%
{>{\columncolor{red!12}}l}{test} & row \number\rownum\\
\end{tabular}
\end{document}