4

The following code tries to extract plain text from formatted argument(here is \bfseries) and fails.

\x is used to store the expansion result of \pgfkeysvalueof.

\edef is for fully expansion of \pgfkeysvalueof to get the value of the key which may be used latter.

Why is the second try wrong?

How to extract plain text(ABC without bolded in this example) from the value of a key by \pgfkeys in the second try?

Code:

\documentclass{article}
\usepackage{tikz,etoolbox}%
\begin{document}
%(1)
\pgfkeys{title/.initial,title=ABC}
\edef\x{\pgfkeysvalueof{/title}}\x %This works all right
%(2)
\pgfkeys{title=\bfseries ABC}
\edef\x{\pgfkeysvalueof{/title}}\x %This can not pass compile
\end{document}
lyl
  • 2,727
  • As far as I know, you can't without making some assumptions and/or modifying the TeX engine. Are you okay with a solution that simply removes any control sequences? – user202729 Jun 29 '22 at 03:30
  • (so for example \alpha will results in nothing, \"a will results in a, \char`a will results in ​`a, \verb+123+ will results in +123+) – user202729 Jun 29 '22 at 03:31
  • I'm curious: why did you tihnk \edef\x{\pgfkeysvalueof{/title}} was going to remove \bfseries from the key? (That's what I understand what you said you wanted to do.) – A.Ellett Jun 29 '22 at 03:36
  • 1
    If this is something that you want to do on a regular basis (ie, more than once or twice), perhaps you should consider separating the markup from the plain text itself. Then you can set up a situation that will turn the markup off when you don't want it. – A.Ellett Jun 29 '22 at 03:37
  • @A.Ellett @user202729 So the text and its text have to be seperated? If format in the middle of text is needed, for example `ab{\color{red}cd}ef", how to seperate them? – lyl Jun 29 '22 at 03:46
  • @A.Ellett The original idea of \edef here is to expand \pgfkeysvalueof to see if its value is blank. But when control sequences are in the value, this causes wrong. Any method to make it work? – lyl Jun 29 '22 at 03:49
  • @lyl No with this sort of markup. The expansion of \bfseries doesn't behave nicely in an \[xe]def sort of situation. – A.Ellett Jun 29 '22 at 03:50
  • @ Then, in such cases, how to fully expand \pgfkeysvalueof whose value has control sequences like bfseries? – lyl Jun 29 '22 at 03:54
  • This is an XY problem! If you want to check if the (typeset) content of a key is blank, you can set it into a box and then check if the size is zero, after stripping blanks. And even that is not so easy, it will probably fail with control spaces. – Rmano Jun 29 '22 at 06:27
  • Ah, you already asked that.... https://tex.stackexchange.com/questions/644492/ifblank-doe-not-work-correctly-with-pgfkeys – Rmano Jun 29 '22 at 06:28
  • @Rmano Yes, I already asked that. But in that question, control sequence in value is not thought about. As for the box solution you mentioned, would you provide with more? – lyl Jun 29 '22 at 06:42
  • @lyl https://tex.stackexchange.com/a/649289/38080 (that would be OT here, given the question...) – Rmano Jun 29 '22 at 07:48
  • 1
    Use \pgfkeysgetvalue{/title}\x instead of your \edef. That will not fully expand stuff in the key, instead your \x would then be \bfseries ABC, but at least it wouldn't break like your \edef does. – Skillmon Jun 29 '22 at 10:46
  • Thank you @Skillmon !! Then how to extract plain text from \x? – lyl Jun 30 '22 at 03:52

3 Answers3

4

I can think of three approaches here. The first two ignore using any keys. Instead, I just use command structures.

This first example illustrates the basic idea. Here this is similar to what your example with the keys.

\documentclass{article}

\newcommand\lyimarkup{\bfseries} \makeatletter \newcommand\lyititle{@ifstar{\lyititle@starred}{\lyititle@nostar}} \newcommand\lyititle@starred[2]{\providecommand#1{}\renewcommand#1{#2}} \newcommand\lyititle@nostar[2]{\providecommand#1{}\renewcommand#1{{\bfseries#2}}} \makeatother

\begin{document}

\lyititle\x{Hello World}\x

\lyititle*\x{Hello World}\x

\end{document}

This second example illustrates how you could work with something more dynamic as per what you suggested in the comments.

\documentclass{article}
\usepackage{xcolor}
\newcommand\lyicolor[1]{\color{#1}}
\makeatletter
\newcommand\lyitext{\@ifstar{\lyitext@starred}{\lyitext@nostar}}
\newcommand\lyitext@starred[2]{%%
  \providecommand#1{}%%
  \renewcommand#1{{\renewcommand\lyicolor[1]{}#2}}}
\newcommand\lyitext@nostar[2]{\providecommand#1{}\renewcommand#1{#2}}
\makeatother

\begin{document}

\lyitext\x{ab{\lyicolor{red}cd}ef}\x

\lyitext*\x{ab{\lyicolor{red}cd}ef}\x%% no mark up

\lyitext\x{ab{\lyicolor{red}cd}ef}\x%% mark up restored

\end{document}

I'll leave it for you to work out how to do this with pgfkeys.


The examples given above where written as they are because I wanted to stick closely to what the OP was doing in their MWE. Perhaps the OP wants to use \x in different places throughout the document. But, if that's not the case and if the OP only wants the option of rendering unmarked up text, then the answers above are far more clunky than necessary.

I don't want to assume that the OP (or anyone else stumbling across this answer) understands LaTeX enough to write these examples a bit more simply. So, I provide simplified versions here.

The first example can be rewritten as:

\documentclass{article}

\newcommand\lyimarkup{\bfseries} \makeatletter \newcommand\lyititle{@ifstar{\lyititle@starred}{\lyititle@nostar}} \newcommand\lyititle@starred[1]{#1} \newcommand\lyititle@nostar[1]{\textbf{#1}} \makeatother

\begin{document}

\lyititle{Hello World}

\lyititle*{Hello World}

\end{document}

The second example can be rewritten as:

\documentclass{article}
\usepackage{xcolor}
\newcommand\lyicolor[1]{\color{#1}}
\makeatletter
\newcommand\lyitext{\@ifstar{\lyitext@starred}{\lyitext@nostar}}
\newcommand\lyitext@starred[1]{%%
  {\renewcommand\lyicolor[1]{}%%
  #1}}
\newcommand\lyitext@nostar[1]{#1}
\makeatother

\begin{document}

\lyitext{ab{\lyicolor{red}cd}ef}

\lyitext*{ab{\lyicolor{red}cd}ef}

\lyitext{ab{\lyicolor{red}cd}ef}

\end{document}


If you try to abide by the principle that content and markup should be clearly separated, then this sort of situation is relatively easy to deal with. If you're not used to doing this and you're only thinking about how you want the end product to look, this will take some effort to learn. So, for example, in a math textbook you might bold a word in the text when you first introduce it.

Instead of writing

\textbf{new fangled word}

you could write something like

\vocab{new fangled word}

where you define

\newcommand\vocab[1]{\textbf{#1}}

It takes some thought about how you're structuring the document. But once you start thinking this way, LaTeX's power really shines through.

A.Ellett
  • 50,533
4

You can use the \text_purify:n function from expl3. This is not a fool proof way to get everything right, because parsing TeX requires TeX, but at least it'll work in most simple cases like your MWE.

\documentclass{article}
\usepackage{tikz,etoolbox}%

\ExplSyntaxOn \NewExpandableDocumentCommand \PurifiedPgfkeysvalueof { m } { \text_purify:n { \pgfkeysvalueof {#1} } } \NewDocumentCommand \GetPurifiedPgfkey { m m } { \tl_set:Nx #1 { \text_purify:n { \pgfkeysvalueof {#2} } } } \ExplSyntaxOff

\begin{document} %(1) \pgfkeys{title/.initial,title=ABC} \edef\x{\pgfkeysvalueof{/title}}\x %This works all right %(2) \pgfkeys{title=\bfseries ABC} \GetPurifiedPgfkey\mytitle{/title}\mytitle %This can not pass compile

or without an assignment: \PurifiedPgfkeysvalueof{/title} \end{document}

Skillmon
  • 60,462
  • \text_purify:n calls \text_expand:n, so we don't need V-type expansion here - otherwise a good plan – Joseph Wright Jun 30 '22 at 06:15
  • 1
    @JosephWright right, I think I could even get away without the temporary setting via \pgfkeysgetvalue, by using \text_purify:n { \pgfkeysvalueof {#2} }. – Skillmon Jun 30 '22 at 06:23
  • It seems that the input of {\color{red} text} would output plain text of red text, not text – lyl Jul 01 '22 at 01:11
  • 1
    @lyl then separate formatting from content as A.Ellet suggests (but maybe do it with a different implementation), or locally redefine \color to gobble the optional and mandatory argument (\makeatletter\begingroup\def\color#1#{\@gobble}\GetPurifiedPgfkey\mytitle{/title}\mytitle\endgroup). – Skillmon Jul 01 '22 at 06:36
2

If you know what macros are used in the extracted text then you can \let them to \relax temporary in a group:

\pgfkeys{title=\bfseries ABC}
{\let\bfseries=\relax \xdef\x{\pgfkeysvalueof{/title}}}\meaning\x

If you don't know the macros used in the text, you must to use right number of \expandafter in connection with \unexpanded in order to expand the right level of the \pgfkeysvalueof macro:

\def\threeexa{\expandafter\expandafter\expandafter}
\def\pgfkeyprotect#1#2{\unexpanded\expandafter\threeexa\expandafter{#1{#2}}}
\pgfkeys{title=\bfseries ABC}
\edef\x{\pgfkeyprotect\pgfkeysvalueof{/title}}\meaning\x
wipet
  • 74,238