2

What's wrong with the following package and source file? I get an error at line 14, after [Other], of mypackage.sty, and a "Missing \begin{document}" error resulting.

The source:

\documentclass{article}

\usepackage[what=this]{mypackage}

\usepackage{blindtext}

\begin{document}
\blindtext
\end{document}

The package mypackage.sty:

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{mypackage}

\RequirePackage{kvoptions}
\RequirePackage{xstring}

\DeclareStringOption{what}
\ProcessKeyvalOptions*

\IfStrEqCase{what}{%
    {this}{\dothis}%
    {that}{\dothat}%
    }%
    [Other]%

\newcommand{\dothis}{The magic word is ``this''}
\newcommand{\dothat}{The magic word is ``that''}

\endinput

I strongly suspect there's some simple syntactical error that I can't find. Or else there's something basic that I'm not understanding about either kvoptions or xstring

I think my syntax for \IfStrEqCase is OK, because the docs from texdoc xstring give the form:

\IfStrEqCase⟨[*]⟩{⟨string⟩}{% 
    {⟨string1⟩}{⟨code1⟩}% 
    {⟨string2 ⟩}{⟨code2 ⟩}%
    etc...
    {⟨stringN ⟩}{⟨codeN ⟩}}[⟨other cases code⟩]

My actual purpose

My actual purpose of kvoptions and xstring is for a complex document where:

  • In one custom package read in as part of the preamble, I set a value for a key for one of several possible document text/math font choices. And in that same package, depending on that value, give the requisite commands to use that text/math font combination. (E.g., with cm as the font choice, which will be a default, the requisite command will simply be \usepackage{amsfonts}; with lucida as the font choice, instead, the requisite commands will be \usepackage[lucidasmallscale]{lucidabr} and \linespread{1.04}.)
  • A second custom package read in as part of the preamble gives a large set of math command definitions, some of which depend on the math font being used. So this second package will take that key value as option, use it, inside a \IfStrEqCase expression, to alter the commands as needed. For example, for the cm choice, I specify \newcommand{\nsubset}{\not\subset} and \newcommand{\tendsto}{\rightarrow}; whereas for the lucida choice, since \nsubset is already defined, I don't need to define it, but I use tikz to create, as the definition for \tendsto, an arrow that better harmonizes with arrows appearing in tikz-cd diagrams.

The points of this seemingly complicated setup are:

  1. To allow the font choice to be made by editing a single line in the first package, rather than having to comment and uncomment large blocks of text in source files.
  2. To allow my second package, with all its math definitions, to be used not just in this particular document, but in any document that may need my specialized math definitions.
  • 1
    I admit to not being familiar with either kvoptions or xstring, but I interpret \IfStrEqCase as being a choice. If [Other] is supposed to be within its scope, it should probably be inside the closing brace, but it isn't, and it's being recognized as "text input", so is processed immediately. That's not legit, since text can be accepted only after \begin{document}, hence the cause of the error message. Better check the documentation for the syntax of \IfStrEqCase. – barbara beeton Jun 20 at 1:03
  • @barbarabeeton: I think te \IfStrEqCase syntax is OK, with the [Other] seemingly dangling at its end. – murray Jun 20 at 16:04
3

The first problem, which causes the error is that you are executing the \IfStrEqCase statement inside the package code to produce typeset output, however you can't do that before \begin{document}. I wrapped the test in a definition \mytest which can be used inside the document.

The other problem is that you are comparing what with this or that, which will never match and will go to the other branch. When you set up a key-value option <name> with kvoptions, the value passed to the option <name> will be stored in \<prefix>@<name>. The <prefix> can be changed, but the default is the package name, so mypackage, and <name> is what here, so instead of testing what you should test \mypackage@what. Changing that, the code prints The magic word is “this”.

\documentclass{article}
\usepackage{filecontents}
\begin{filecontents}{mypackage.sty}
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{mypackage}
\RequirePackage{kvoptions}
\RequirePackage{xstring}
\DeclareStringOption{what}
\ProcessKeyvalOptions*
\def\mytest{%
  \IfStrEqCase{\mypackage@what}{%
      {this}{\dothis}%
      {that}{\dothat}%
      }%
      [Other]%
  }
\newcommand{\dothis}{The magic word is ``this''}
\newcommand{\dothat}{The magic word is ``that''}
\endinput
\end{filecontents}
\usepackage[what=this]{mypackage}
\usepackage{blindtext}
\begin{document}
\mytest

\blindtext
\end{document}

For this case in particular you could drop xstring and test if a macro \do<what> exists. If it does, use it, otherwise take the appropriate action. This definition for \mytest would produce the same output as the one with xstring:

\def\mytest{%
  \@ifundefined{do\mypackage@what}%
    {Other}%
    {\@nameuse{do\mypackage@what}}%
  }
  • The docs (texdoc xstring) are quite misleading in this, in that they give, on page 6, examples such as \IfStrEqCase{c}{{a}{AA}{b}{BB}{c}{CC}}[other] but without any discussion that I see of using \IfStrEqCase, etc., in packages. – murray Jun 20 at 14:56
  • @murray Well, they don't, but I would't say it's misleading. It is well known that you can't write text before the \begin{document} and that packages can only be loaded before \begin{document}, so it's sort of implied. What is your actual use case? Do you really want to make your package write something at load time? – Phelype Oleinik Jun 20 at 15:00
  • What is the purpose, and what the necessity, of using \mypackage@what rather than just plain what as the first argument to \IfStrEqCase? Is it possible because the command \DeclareStringOption{what}in effect automatically creates \mypackage@what? And just why must one use a command as that argument, which examples in the docs for xstring don't show at all. It cannot be simply because, within a package, one cannot use a raw string (after all, \RequirePackage{what} does use a raw string). I'm trying to understand how all this works, and not just follow your correction. – murray Jun 20 at 15:02
  • @murray The second paragraph in my answer explains why \mypackage@what instead of what. For TeX what is just the text string "what" with no special meaning. If you compare what with this it will always result false. When you do \DeclareStringOption{what} a macro called \mypackage@what is created and it contains the value passed to the option what (in the example, this). Then, when you compare \mypackage@what (which contains this) with this you get true. If you want examples with commands you need to go further in the manual (Section 4 Advanced macros for programming). – Phelype Oleinik Jun 20 at 15:09
  • 1
    @murray Usually package writing is done by users with a little experience on TeX macro expansion model, so package authors don't bother explaining that to end users (which I have to agree that sometimes makes a barrier for the newcomer which wants to learn it: I've been there, and it's not easy). Perhaps section 3 of the xstring manual can give you a light on how it expands its arguments, but again, you need to be a little familiar with the concept of expansion and other low-level TeX concepts. – Phelype Oleinik Jun 20 at 15:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.