1

I am trying to define a pgf key handler name/.unquote and store in=\command so that {name="quoted string"} or {name=unquotedstring} both work. That is, the net effect of .unquote and store in is to remove an optional outer string marks from the argument.

So far, I have been unable. From what I tried, everything breaks apart when using \@ifnextcharin the MWE below [I also tried to \expandafter at several stages, to no avail].

Can you help?

\documentclass{article}
\usepackage{pgfkeys}
\usepackage{fontawesome5}
\begin{document}
\makeatletter
\def\remove@quotes@eov"#1"#2\pgfeov{#1}
\def\remove@eov#1\pgfeov{#1}
\def\maybe@remove@quotes#1\pgfeov{\@ifnextchar"%
    {\let\@next=\remove@quotes@eov}{\let\@next=\remove@eov}%
    \@next#1\pgfeov}

\pgfkeys{/handlers/.unquote and store in/.code=%
    {%
        \pgfkeysalso{%
            \pgfkeyscurrentpath/.code=%
                {%
                    \edef#1{\maybe@remove@quotes##1\pgfeov}%
                }%
        }%
    }%
}%


\pgfkeys{/faicon/.is family, /faicon/.cd,
    name/.unquote and store in=\shtcIconName,
    style/.unquote and store in=\shtcIconStyle,
}
\newcommand\shortcodefaicon[1][]{\pgfkeys{/faicon/.cd,#1}\faIcon[\shtcIconStyle]{\shtcIconName}}


\shortcodefaicon[name="apple", style=regular]

\end{document}
4
  • Side note, read the TeXbook for the reference to TeX programming especially on the topic of expandable/unexpandable commands.
    – user202729
    Nov 4, 2022 at 20:18
  • Your ifnextchar will always see \@next and never the first character of #1. Nov 4, 2022 at 20:47
  • @Qrrbrbirlbel Damn! you're absolutely right, of course.
    – Koji
    Nov 4, 2022 at 22:34
  • @user202729 I am confess that I have, although obviously not much sunk in...
    – Koji
    Nov 4, 2022 at 22:35

2 Answers 2

2

Use expl3 to make it a bit simpler. Assuming it is not a problem to convert the input to a string, and that if the first character is " then so is the last one.

%! TEX program = lualatex
\documentclass{article}
\usepackage{pgfkeys}
\usepackage{fontawesome5}
\begin{document}
\makeatletter

\ExplSyntaxOn
\cs_generate_variant:Nn \str_if_eq:nnTF {x}
\cs_new_protected:Npn \assignRemoveQuotes #1 #2 {
    \str_if_eq:xnTF {\str_head:n {#2}} {"} {
        \str_set:Nx #1 {\str_range:nnn {#2} {2} {-2}}
    } {
        \str_set:Nn #1 {#2}
    }
}
\ExplSyntaxOff

\pgfkeys{/handlers/.unquote and store in/.code=%
    {%
        \pgfkeysalso{%
            \pgfkeyscurrentpath/.code=%
                {%
                    \assignRemoveQuotes{#1}{##1}%
                }%
        }%
    }%
}%


\pgfkeys{/faicon/.is family, /faicon/.cd,
    name/.unquote and store in=\shtcIconName,
    style/.unquote and store in=\shtcIconStyle,
}
\newcommand\shortcodefaicon[1][]{\pgfkeys{/faicon/.cd,#1}\faIcon[\shtcIconStyle]{\shtcIconName}}


\shortcodefaicon[name="apple", style=regular]

\end{document}

For educational purpose, a solution using LaTeX2e-programming style.

%! TEX program = lualatex
\documentclass{article}
\usepackage{pgfkeys}
\usepackage{fontawesome5}
\begin{document}
\makeatletter
\errorcontextlines=5
\def\remove@quotes@eov"#1"\myeov#2{\def#2{#1}}
\def\remove@eov#1\myeov#2{\def#2{#1}}
\def\maybe@remove@quotes{\@ifnextchar"%
    \remove@quotes@eov \remove@eov%
}

\pgfkeys{/handlers/.unquote and store in/.code=%
    {%
        \pgfkeysalso{%
            \pgfkeyscurrentpath/.code=%
                {%
                    \maybe@remove@quotes##1\myeov#1%
                }%
        }%
    }%
}%


\pgfkeys{/faicon/.is family, /faicon/.cd,
    name/.unquote and store in=\shtcIconName,
    style/.unquote and store in=\shtcIconStyle,
}
\newcommand\shortcodefaicon[1][]{\pgfkeys{/faicon/.cd,#1}\faIcon[\shtcIconStyle]{\shtcIconName}}


\shortcodefaicon[name="apple", style=regular]

\end{document}

Although it only really have "educational purpose" if you study the macro expansion rules properly and trace through the execution by hand. (most people don't really want to learn this.)

By the way, reusing \pgfeov proved to be an extraordinarily bad idea. (I should have guessed when the "mysterious error" came up.) So renamed to \myeov in the code.

2
  • Thanks a lot, especially for the educationally enhanced answer. I plan to go through it, but your comment about reusing \pgfeov is confusing...
    – Koji
    Nov 4, 2022 at 22:38
  • @Koji It simply mean that the code of pgf relies on the user code does not contain \pgfeov to work correctly. Just try to rename \myeov to \pgfeov and incomprehensible errors pop up.
    – user202729
    Nov 5, 2022 at 1:55
1

Here's a PGFkeys solution that uses a few helper macros (stored as other keys) that strip normal " or active " from their argument as well as a trivial macro that just grabs the whole argument.

Instead of needing to protect \pgfeov then with multiple braces, the original \pgfkeysdef macro is used (which would have been used by .code) to hide that one \pgfeov from the PGFKeys parser.

Otherwise, we'll need three braces (or another helper macro like \@firstofone):

\pgfkeys{
  /handlers/.unquote and store in/.style={
    \pgfkeyscurrentpath/.code={{{% Eugh!
      \@ifnextchar"{\pgfkeysvalueof{/handlers/helper/unquote/.@cmd}}{%
        \expandafter\@ifnextchar\@activedblquote
                   {\pgfkeysvalueof{/handlers/helper/unquote active/.@cmd}}
                   {\pgfkeysvalueof{/handlers/helper/store in/.@cmd}}}##1\pgfeov
    \let#1\pgfkeys@temp}}}}}

Of course, if you use your own unpacker, there's no need to use \pgfeov here in the first place:

\def\removeNormalDblQuote"#1"\myeov{…}
\def\removeActiveDblQuote"#1"\myeof{…} % with active " of course
\def\donotremoveaDblQuote#1\myeov{…}

You just need to make sure that \@ifnextchar actually “sees” the first character of #1/##1 … so don't use that \@next macro there, that won't work.


By the way, your definition would not store an unquoted argument in #1 but the whole \maybe@remove@quotes spiel, too. That couldn't have worked either since \faIcon will need a fully expandable argument there.

Code

\documentclass{article}
\usepackage{pgfkeys}
\usepackage[ngerman]{babel}
\makeatletter
\pgfkeys{/handlers/helper/.cd,
  unquote/.code args={"#1"}{\def\pgfkeys@temp{#1}},
  store in/.code={\def\pgfkeys@temp{#1}}}
\begingroup % active " for babel user
  \catcode`\"\active
  \gdef\@activedblquote{"}%
  \expandafter
\endgroup\@firstofone{%
  \pgfkeys{/handlers/helper/unquote active/.code args=%
    {"#1"}{\def\pgfkeys@temp{#1}}}}
\pgfkeys{
  /handlers/.unquote and store in/.code=%
    \pgfkeysdef{\pgfkeyscurrentpath}{% \pgfkeysdef is what .code actually does
      \@ifnextchar"{\pgfkeysvalueof{/handlers/helper/unquote/.@cmd}}{%
        \expandafter\@ifnextchar\@activedblquote
                   {\pgfkeysvalueof{/handlers/helper/unquote active/.@cmd}}
                   {\pgfkeysvalueof{/handlers/helper/store in/.@cmd}}}##1\pgfeov
      \let#1\pgfkeys@temp}}
\makeatother
\usepackage{fontawesome5}
\begin{document}
\pgfkeys{/faicon/.is family, /faicon/.cd,
    name/.unquote and store in=\shtcIconName,
    style/.unquote and store in=\shtcIconStyle}
\newcommand\shortcodefaicon[1][]{%
  \pgfkeys{/faicon/.cd,#1}\faIcon[\shtcIconStyle]{\shtcIconName}}
\shortcodefaicon[name="apple", style=regular]
\end{document}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .