3

I am trying to design packages and handle package options using pgfkeys through the pgfopts package. However when an option include a formatting command, the whole thing fail and I don't get the expected result.

Here is my test package: pgfoptstest.sty

\NeedsTeXFormat{LaTeX2e}[1994/06/01]
\ProvidesPackage{pgfoptstest}[2020/01/01 pgfopts test]
\RequirePackage{pgfopts}
\pgfkeys{
    /pgfoptstest/.cd,
    format/.store in = \format,
    format = \textit,
    example/.store in = \example,
    example = {This is an \format{example}.},
}
\ProcessPgfPackageOptions{/pgfoptstest}
\newcommand{\makeexample}{\example}
\newcommand{\maketest}[1]{\format{#1}}

And here is my test file: pgfoptstest.tex

\documentclass{article}
\usepackage{pgfoptstest}                                           % OK
%\usepackage[format =]{pgfoptstest}                                % OK
%\usepackage[example = {Another example.}]{pgfoptstest}            % NO SPACES
%\usepackage[example = {{Another example.}}]{pgfoptstest}          % OK
%\usepackage[example = {{Another \textbf{example}.}}]{pgfoptstest} % ERROR
%\usepackage[format = \textbf]{pgfoptstest}                        % ERROR
\begin{document}
\noindent
\makeexample{}\\
\maketest{This is a test.}
\end{document}

How to fix it (ideally on the package side so the user do not have to write their options in a tricky way) so that the lines marked as ERROR work properly?

And as a bonus, is there a way to make the line marked as NO SPACES work properly so that the user does not have to type two pairs of braces?

  • I think that instead of \textit and \textbf you want to use the commands \itshape and \bfseries here. Then you can copy more or less the font key of TikZ/pgf. – user194703 May 9 at 17:05
  • @Schrödinger'scat But I would like to be able to handle options themselves containing formatted text (with macros I don't know in advance). Things like: \usepackage[example = {{Another \textbf{example}.}}]{pgfoptstest} – Vincent May 9 at 19:10
  • The pgfopts documentation says: "An important point to notice is that the key names do not contain a space.This is because the LaTeX kernel removes spaces from options before they are passed to the class or package. Spaces can occur in the path to the key, but not in the key name itself. This restriction only applies to keys used as options". That's possibly why your spaces do not appear unless you enclose your text in brackets. – user193767 May 11 at 14:38
2
+100

Um, I'm afraid I've got some sad news. This is not caused by pgfkeys or pgfopts, but by the way LaTeX itself handles package options. It strips spaces and performs a couple of expansions before passing the options on. There is literally nothing that can be done about it at the package level because it's already too late by the time the package sees them.

:(

Unfortunately, replacing \textbf by \protect\textbf and \noexpand\textbf doesn't help either. What does work is replacing \textbf by \noexpand\noexpand\noexpand\textbf, but that doesn't make for a particularly appealing syntax…

I would expect this question to come up periodically, but a quick search only turned up this one, which is about the same problem with kvoptions. (Maybe I'm not using the right search terms.) The one bit of good news there is that David says that “there may be plans to change this [normalisation during option handling]” in his answer.


Suggestion #1

Before this edit, I sort of jokingly mentioned that writing a package to patch how LaTeX handles options was also an option. I did a little searching and it turns out that, unsurprisingly, someone already did just that. If you load the kvoptions-patch package (which is described in the kvoptions documentation) before loading your own package, options with spaces or macros that you don't want expanded will work!

Here's your MWE with one line added to it:

\begin{filecontents}[overwrite]{pgfoptstest.sty}
\NeedsTeXFormat{LaTeX2e}[1994/06/01]
\ProvidesPackage{pgfoptstest}[2020/01/01 pgfopts test]
\RequirePackage{pgfopts}
\pgfkeys{
    /pgfoptstest/.cd,
    format/.store in = \format,
    format = \textit,
    example/.store in = \example,
    example = {This is an \format{example}.},
}
\ProcessPgfPackageOptions{/pgfoptstest}
\newcommand{\makeexample}{\example}
\newcommand{\maketest}[1]{\format{#1}}
\end{filecontents}


\documentclass{article}
\usepackage{kvoptions-patch} %% <- added
\usepackage[example = Another \textbf{example}.]{pgfoptstest} % OK now
\begin{document}
\noindent
\makeexample{}\\
\maketest{This is a test.}
\end{document}

output


Suggestion #2

Or you could create a setup function that the user has to call to set up your package. It's not as nice as an option, but probably better than asking your users to \noexpand\noexpand\noexpand their unexpandable macros.

\begin{filecontents}{pgfoptstest2.sty}
\NeedsTeXFormat{LaTeX2e}[1994/06/01]
\ProvidesPackage{pgfoptstest2}[2020/01/01 pgfopts test]

\RequirePackage{pgfopts}
\pgfkeys{
    /pgfoptstest/.cd,
    format/.store in = \format,
    format = \textit,
    example/.store in = \example,
    example = {This is an \format{example}.},
}
\ProcessPgfPackageOptions{/pgfoptstest} %% <- optional (badum, tss)

\newcommand*\pgfoptstestsetup[1]{\pgfqkeys{/pgfoptstest}{#1}}

\newcommand{\makeexample}{\example}
\newcommand{\maketest}[1]{\format{#1}}
\end{filecontents}


\documentclass{article}

\usepackage{pgfoptstest2}
\pgfoptstestsetup{
  format=\underline,
  example = \textbf{Another example.}
}

\begin{document}

\noindent
\makeexample{}\\
\maketest{This is a test.}

\end{document}

output


Addendum (experiment)

Here's a little experiment to demonstrate that there's no way to avoid this at the package level.

\begin{filecontents}{stop.sty}
\stop %% <- abort immediately
\end{filecontents}


\documentclass{article}
% \usepackage{stop} %% <- successfully produces nothing
\usepackage[example = {{Another \textbf{example}.}}]{stop} %% <- errors
\begin{document}
\end{document}

  • If no options are passed to stop.sty, everything is fine: TeX stops in the middle of loading the package and produces no output (and no errors).
  • If (for instance) example=\textbf{example} is passed as an option, all hell breaks loose. The error messages are the same as in your MWE.
| improve this answer | |
  • Thanks for your very complete and precise answer, that is exactly what I was looking for. – Vincent May 13 at 17:27
  • You're welcome, and good luck with your packages. – Circumscribe May 13 at 19:06

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.