Saturday, January 1, 2011

Optional Arguments in Latex Functions, Style and Class Files

I just wrote my first own latex package and as usual i was rather aiming for the stars than KISS.
The last big issue was finding out how to have optional package arguments that take a value (think geometry's left).

First: 
How to have functions with more than one optional argument or named optional arguments: Turns out not to be too complicated in the end. First you need the package keyval which enables you to parse key=value style arguments and then you need to understand how it works (the doc is aweful imho). Set up your parsing functions with something like

\define@key{keygroup}{keyname}{\dostuff{#1}}

this defines an option keyname that takes an argument, that has to be provided if the key is used.
Example: 
\define@key{keygroup}{color}{\domorestuff{#1}}

You can also define an optional argument that has an optional value that gets used when none is provided with the following syntax

\define@key{keygroup}{keyname}[default]{\dostuff{#1}}

 This lets you for example define a length type argument like follows:

\define@key{head}{width}[\textwidth]{\def\@bannerwidth{#1}}

This would set \@bannerwidth to 5cm for width=5cm and to the width of the text otherwise.
 Now to parse an optionstring you need to pass it to \setkeys like this:

\setkeys{keygroup}{optstring}

So now in order to have a command with more than one optional argument (beware syntax is different though!)

\newcommand{\printbanner}[2][]{
    \setkeys{keygroup}{#1} 
    ....
}
and call it like so: 

\printbanner[width=18cm,color = red]{Other Arg}
 
Second:
I think keyval was written with functions in mind but that shan't stop us from abusing it for package options. Declare all your options that don't need keyval syntax first (it's easier than going through keyval). Afterwards set up the catchall handler with 

\DeclareOption*{\edef\@act{\noexpand\setkeys{keygroup}{\CurrentOption}}\@act}

The reason you need this is so you can delay the expansion of the arguments. This should be enough so you can 

\includepackage[width=18cm]{package}

Further Notes:
If you want a switch case like option have a look at the package ifthen.
A complete, minimal, working example is available here.

No comments:

Post a Comment