Evaluate strings of key-value option in custom package by using a switch-case environment

I'm trying to write a custom package MyUnicodefonts where I'd like to store all my font settings for various math fonts and roman fonts (later also mono and sans).

In my document I intend to choose the font with a key-value option, like:

\documentclass{article}

\usepackage{blindtext}

\begin{document}

\blindmathpaper

\end{document}


For the package file MyUnicodefonts.sty I used the switch from this answer as is appeared handy and sufficient for me and I didn't understood the other answers. I tried to apply it for string comparison.

Using the kvoptions package my file finally looks as follows:

\NeedsTeXFormat{LaTeX2e}

\RequirePackage{fontspec}

\RequirePackage{kvoptions}
\DeclareStringOption[minion]{roman}
\DeclareStringOption[xits]{math}
\ProcessKeyvalOptions*

\RequirePackage{xifthen}
\newcommand{\ifequals}[3]{\ifthenelse{\ifstrequal{#1}{#2}{TRUE}{}}{#3}{}}
\newenvironment{switch}[1]{\renewcommand{\case}{\ifequals{#1}}}{}

\begin{switch}{\MyUnicodefonts@roman}
\case{minion}{\setmainfont[Numbers = OldStyle,
Ligatures = TeX,
]{Minion Pro}}
\end{switch}
\begin{switch}{\MyUnicodefonts@math}
\case{xits}{%
\RequirePackage{unicode-math}
\setmathfont{XITS Math}
\setmathfont[range={"002B,"002D,"003A-"003E} ]{MnSymbol} % + - < = >
}
\case{iwona}{%
\RequirePackage[math]{iwona}
\RequirePackage[mathscr]{eucal}
}
\end{switch}

\endinput


As the kvoptions manual is lacking a comprehensive example I fail to identify the reason for the appearing Undefined control sequence error.

The error would disappear if I'd load unicode-math also before the switch, but I don't want that, as there will be cases, where I don't want to use an unicode-math font. However a new error appears:

! Missing number, treated as zero. \begingroup l.18 \case{myriad}{\setmainfont{Myriad Pro}} A number should have been here; I inserted '0'. (If you can't figure out why I needed to see a number, look up 'weird error' in the index to The TeXbook.)

Which I don't understand, as I substituted the \equal{#1}{#2} from the original switch (see linked answer) to \ifstrequal{#1}{#2}{TRUE}{}.

I hope you can help me find the error or suggest a completely different approach.

• The undefined command is shown in the error message: \ifstrequal. It is defined by package etoolbox, which is not loaded. This and how option strings are processed further are not related to package kvoptions. – Heiko Oberdiek Jun 7 '15 at 19:27

The code in the question has some problems:

• \ifstrequal is not defined, as shown by the error message. It is defined by package etoolbox.

• Environment switch makes a group, all local settings are lost after the end of the environment.

There are lots of ways to implement "switches", this is not covered by package kvoptions, which tries to be simple and easy to use.

A classical way to implement a switch depending on a simple text string is using the hash table of TeX. The following example puts the implementation for an option value in a macro with a name of the form \<prefix>@<option>@<value>. The option setting of kvoptions stores the value in \<prefix>@<option>. Then a simple test using \@ifundefined checks, if the option/value implementation exists and throws an error, if the option value is not supported/unknown.

\NeedsTeXFormat{LaTeX2e}

\RequirePackage{fontspec}

\RequirePackage{kvoptions}
\DeclareStringOption[minion]{roman}
\DeclareStringOption[xits]{math}
\ProcessKeyvalOptions*

% Implementations for option "roman"
\@namedef{MyUnicodefonts@roman@minion}{%
\setmainfont[
Numbers = OldStyle,
Ligatures = TeX,
]{Minion Pro}%
}
\setmainfont[
Numbers={Uppercase,OldStyle},
}

% Implementations for option "math"
\@namedef{MyUnicodefonts@math@xits}{%
\RequirePackage{unicode-math}%
\setmathfont{XITS Math}%
\setmathfont[range={"002B,"002D,"003A-"003E}]{MnSymbol}%
}
\@namedef{MyUnicodefonts@math@iwona}{%
\RequirePackage[math]{iwona}%
\RequirePakcage[mathscr]{eucal}%
}

% Evaluate option settings
\newcommand*{\MyUnicodefonts@evaluate}[1]{%
% Check, if option value in \<prefix>@<option> exists
\@ifundefined{MyUnicodefonts@#1}{%
% Should not happen
\PackageError{MyUnicodefonts}{Evaluating unknown option #1'}\@ehc
}{%
% Check, if there is an implementation for the value of the option
% in macro \<prefix>@<option>@<value>
\@ifundefined{MyUnicodefonts@#1@\csname MyUnicodefonts@#1\endcsname}{%
\PackageError{MyUnicodefonts}{%
Unknown option setting: #1=%
\csname MyUnicodefonts@#1\endcsname
}\@ehc
}{%
% Call the implementation for the value
\csname MyUnicodefonts@#1@%
\csname MyUnicodefonts@#1\endcsname
\endcsname
\relax
}%
}%
}
\MyUnicodefonts@evaluate{roman}
\MyUnicodefonts@evaluate{math}

\endinput


You could use l3keys2e.

File myunicodefonts.sty

\NeedsTeXFormat{LaTeX2e}

\RequirePackage{fontspec}
\RequirePackage{l3keys2e}

\ExplSyntaxOn
\bool_new:N \g_myunicodefonts_mathfont_bool
\tl_new:N \g_myunicodefonts_textfont_tl
\tl_new:N \g_myunicodefonts_mathfont_tl

\keys_define:nn { myunicodefonts }
{
textfont .choice:,
textfont/minion .code:n = \tl_gset:Nn \g_myunicodefonts_textfont_tl {Minion~Pro},
textfont .initial:n = minion,
textoptions .tl_gset:N = \g_myunicodefonts_textfont_options_tl,
mathfont .choice:,
mathfont/xits .code:n =
{
\bool_gset_true:N \g_myunicodefonts_mathfont_bool
\tl_gset:Nn \g_myunicodefonts_mathfont_tl {XITS~Math}
},
mathfont/asana .code:n =
{
\bool_gset_true:N \g_myunicodefonts_mathfont_bool
\tl_gset:Nn \g_myunicodefonts_mathfont_tl {Asana~Math}
},
mathoptions .tl_gset:N = \g_myunicodefonts_mathfont_options_tl,
}

\ProcessKeysPackageOptions{myunicodefonts}

\use:x
{
\setmainfont{\g_myunicodefonts_textfont_tl}[\g_myunicodefonts_textfont_options_tl]
}

\bool_if:NT \g_myunicodefonts_mathfont_bool
{
\RequirePackage{unicode-math}
\use:x
{
\setmathfont[\g_myunicodefonts_mathfont_options_tl]{\g_myunicodefonts_mathfont_tl}
}
}
\ExplSyntaxOff

\endinput


File myunicodefonts.tex for testing

\documentclass{article}

\usepackage[
mathfont=xits,
textoptions={Scale=0.8},
]{myunicodefonts}
\usepackage{blindtext}

\begin{document}

\blindmathpaper

\end{document}


One can supplement this with error checking for wrong options.

Different code

If you want to set yourself the options for the fonts, just do so in the .case:n code.

\NeedsTeXFormat{LaTeX2e}

\RequirePackage{fontspec}
\RequirePackage{l3keys2e}

\ExplSyntaxOn

\keys_define:nn { myunicodefonts }
{
textfont .choice:,
textfont/minion .code:n =
{
\setmainfont{Minion~Pro}[
Numbers = OldStyle,
Ligatures = TeX,
]
},
{
Numbers={Uppercase,OldStyle},
]
},
textfont .initial:n = minion,
mathfont .choice:,
mathfont/xits .code:n =
{
\RequirePackage{unicode-math}
\setmathfont{XITS~Math}
\setmathfont[range={"002B,"002D,"003A-"003E}]{MnSymbol} % + - < = >
},
mathfont/iwona .code:n =
{
\RequirePackage[math]{iwona}
\RequirePackage[mathscr]{eucal}
},
}

\ProcessKeysPackageOptions{myunicodefonts}
\ExplSyntaxOff

\endinput

• Thank you for this exhausting piece of code, but I'm a little struggling to apply it to the full case. First it seems I'm limited to the options I could define within the [] in \setmathfont[<options>]{font} - is that right? But I actually have multiple of these lines for different ranges. Also the options for the roman fonts should be defined within the class and not be editable from outside. Could you please have a look at my edited MWE and insert it in you code? That would help me to understand where all the definitions reappears. Thank you! – thewaywewalk Jun 7 '15 at 17:19
• @thewaywewalk That's a good reason for not having such a package in the first place. – egreg Jun 7 '15 at 17:46
• @thewaywewalk Remove the textoptions key and set the token list variable yourself. Similarly add the code you want to the mathfont` options. – egreg Jun 7 '15 at 17:51
• possibly. It seemed to be a easy thing to do for me in the first place. I will work through your code again tomorrow. – thewaywewalk Jun 7 '15 at 18:13
• @thewaywewalk I added an example of what I meant. – egreg Jun 7 '15 at 18:20