7

I've written a macro that scans its content for illegal characters and then changes them to something more appropriate. In the following example, the macro scans for _ and replaces them with \rule[-1pt]{0.75em}{1.0pt}.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}

\makeatletter

\newcommand\aescandash[1]{%%
  \let\ae@scan@dash@result\relax
  \ae@scan@dash@parse#1_\@nil
  \ae@scan@dash@result
}

\def\ae@add@to@result#1#2{%%
  \ifx#1\relax
    \def#1{#2}%%
  \else
    \expandafter\def\expandafter#1\expandafter{#1#2}%%
  \fi}

\def\ae@scan@dash@parse#1_#2\@nil{%%
  \def\ae@reevaluate{}%%
  \expandafter\ifx\expandafter\relax\detokenize\expandafter{#2}\relax
    \ae@add@to@result\ae@scan@dash@result{#1}%%
  \else
    \ae@add@to@result\ae@scan@dash@result{#1\rule[-1pt]{0.75em}{1.0pt}}%%
    \def\ae@reevaluate{\ae@scan@dash@parse#2\@nil}%%
  \fi
  \ae@reevaluate
  }

\makeatother

\begin{document}

\aescandash{this_is_a_dash_filled_sentence}

\end{document}

I would like to be able to apply such a pre-processor to the content of nodes in a TikZ picture:

\begin{tikzpicture}[%%
  my node/.style={red,
                  preprocessor=\aescandash},
  ]

  \foreach \myn [count=\myc from 1] in {this,that,another_text}
  {
    \node[my node] at (0,-\myc) {\myn};
  }

\end{tikzpicture}

But there is no such preprocessor key. Ideally I would like the preprocessor to effectly pass to the node contents something along the lines of:

\expandafter\aescandash\expandafter{\myn}

In other words, the preprocessor would accomplish what the following code does:

\begin{tikzpicture}[%%
  my node/.style={red},
  ]

  \foreach \myn [count=\myc from 1] in {this,that,another_text}
  {
    \node[my node] at (0,-\myc) {\expandafter\aescandash\expandafter{\myn}};
  }

\end{tikzpicture}
  • You're looking for disgrace. ;-) – egreg Nov 22 '14 at 22:01
  • @egreg I'm not sure I understand. Am I doing something foolish here? Or asking for something so obvious? – A.Ellett Nov 22 '14 at 22:03
  • If you don't want “dangerous input”, don't allow it to begin with. But printing an underscore as such is not really difficult. – egreg Nov 22 '14 at 22:06
  • @egreg Ah well that I get. But I'm trying to document some code for in which such illegal characters are abundant. But I've had other occasions where I wanted to preformat the content passed to a node (not necessarily to strip and replace characters). Here it just seemed simple enough to create an understandable MWE. – A.Ellett Nov 22 '14 at 22:13
5

Why not just this?

\documentclass[tikz,border=5pt]{standalone}
\usetikzlibrary{calc}

\makeatletter

\newcommand\aescandash[1]{%%
  \let\ae@scan@dash@result\relax
  \ae@scan@dash@parse#1_\@nil
  \ae@scan@dash@result
}

\def\ae@add@to@result#1#2{%%
  \ifx#1\relax
  \def#1{#2}%%
  \else
  \expandafter\def\expandafter#1\expandafter{#1#2}%%
  \fi}

\def\ae@scan@dash@parse#1_#2\@nil{%%
  \def\ae@reevaluate{}%%
  \expandafter\ifx\expandafter\relax\detokenize\expandafter{#2}\relax
  \ae@add@to@result\ae@scan@dash@result{#1}%%
  \else
  \ae@add@to@result\ae@scan@dash@result{#1\rule[-1pt]{0.75em}{1.0pt}}%%
  \def\ae@reevaluate{\ae@scan@dash@parse#2\@nil}%%
  \fi
  \ae@reevaluate
}

\makeatother

\begin{document}

  \begin{tikzpicture}
    [
      my node/.code={
        \let\oldmyn\myn
        \gdef\myn{\expandafter\aescandash\expandafter{\oldmyn}}%
      },
    ]

    \foreach \myn [count=\myc from 1] in {this,that,another_text}
    {
      \node[my node] at (0,-\myc) {\myn};
    }

  \end{tikzpicture}

\end{document}

my node code

| improve this answer | |
  • This is not quite what I want, but it does suggest a solution that will work for my immediate purposes. – A.Ellett Nov 22 '14 at 22:20
4

It is possible to insert a preprocessor, but obviously this involves a teensy hack of the node parser.

In the code below I define a preprocess node content key which takes a macro as an argument. This macro should be defined to take one argument, which will be the text to process. Obviously if there is something more complex than simple text then the whole thing may fail miserably.

I also show an alternative (although not necessarily better) way of replacing characters using extended latex. I use asterisks instead of underscores so no errors result when there is no preprocessor.

\documentclass[tikz, border=5]{standalone}

\makeatletter    
\let\tikz@do@fig@original=\tikz@do@fig
\newtoks\tikz@fig@toks%
\def\tikz@do@fig@preprocessed{%
  \tikz@do@fig@original%
  \afterassignment\tikz@do@fig@@preprocessed%
  \tikz@fig@toks=\bgroup}

\def\tikz@do@fig@@preprocessed{%
  \expandafter\tikz@fig@preprocess\expandafter{\the\tikz@fig@toks}%
  \egroup}

\def\tikz@fig@preprocess#1{#1}
\tikzset{%
  preprocess node content/.code={%
    \let\tikz@fig@preprocess=#1\relax%
    \let\tikz@do@fig=\tikz@do@fig@preprocessed%
  }
}
\makeatother

{\catcode`\*=13 \gdef*{\rule[-1pt]{0.75em}{1.0pt}}}
\def\pp#1{{\catcode`\*=13 \scantokens{#1\ignorespaces}}}

\begin{document}
\begin{tikzpicture}
\node [draw] at (0,0) {*foo*bar*};
\node [draw, preprocess node content=\pp] at (0,-1) {*foo*bar*};
\end{tikzpicture}
\end{document}

enter image description here

| improve this answer | |
  • So is \tikz@do@fig the macro that TikZ uses to store the node contents? – A.Ellett Nov 24 '14 at 18:19
  • I like your approach for handling characters better than my recursive approach: it seems more natural. – A.Ellett Nov 24 '14 at 18:24
  • @A.Ellett essentially yes, but it is a bit more complex. \tikz@do@fig (in older versions it was \tikz@@fig@main) is executed after the open { of the node contents is stripped away. All the options for node contents are executed and then an \hbox is started which collects the node contents up the closing }. So in the normal course of events TikZ only knows about the box rather than its contents. – Mark Wibrow Nov 25 '14 at 6:50
3

There is actually a preprocessor in disguise. You can use the node contents key started with TikZ 3. And via style nesting it becomes a preprocessor. But the downside is that you have to give all the specs within the brackets because closing bracket finishes the node parsing on a path.

Edit: After cfr's correction, a little better with the option to choose a preprocessor or turn it off. scanned node contents is admittedly a bloated name so you can choose something more catchy.

\documentclass[]{article}
\usepackage{tikz}
\makeatletter
\newcommand\aescandash[1]{\let\ae@scan@dash@result\relax\ae@scan@dash@parse#1_\@nil\ae@scan@dash@result}
\def\ae@add@to@result#1#2{\ifx#1\relax\def#1{#2}\else\expandafter\def\expandafter#1\expandafter{#1#2}\fi}

\def\ae@scan@dash@parse#1_#2\@nil{\def\ae@reevaluate{}%
\expandafter\ifx\expandafter\relax\detokenize\expandafter{#2}\relax%
    \ae@add@to@result\ae@scan@dash@result{#1}%%
  \else\ae@add@to@result\ae@scan@dash@result{#1\rule[-1pt]{0.75em}{1.0pt}}%
\def\ae@reevaluate{\ae@scan@dash@parse#2\@nil}%%
  \fi%
  \ae@reevaluate%
  }
\makeatother

\tikzset{
  preprocessor/.store in=\mypreproc,
  preprocessor=aescandash,
  %preprocessor=, % Turn it off
  scanned node contents/.style={
    node contents={\csname\mypreproc\endcsname{#1}},
  }
}
\begin{document}
\begin{tikzpicture}
\node[scanned node contents={this_is_a_dash_filled_sentence},fill=red!10,at={(1,0)},anchor=west];
\node[scanned node contents={another_dashed_one},fill=blue!10,at={(0,1)}];
\end{tikzpicture}
\end{document}

enter image description here

| improve this answer | |
  • (+1) but it doesn't use the requested syntax... – cfr Nov 22 '14 at 23:24
  • @cfr Uh oh, completely missed that spec. – percusse Nov 22 '14 at 23:26
  • It is more preprocessor like, though. – cfr Nov 22 '14 at 23:27
  • 1
    @A.Ellett True but if you want to change the preprocessor then you need to type the whole key. So you can change along the way – percusse Nov 23 '14 at 0:14
  • 2
    @A.Ellett when used, node contents finishes the node parsing after closing bracket and doesn't understand the rest – percusse Nov 23 '14 at 0:15
2

Thanks to @cfr , this is the solution I came up with. I didn't fully go with his solution because I need \myn unaltered for later purposes in my main document.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}

\makeatletter

\newcommand\aescandash[1]{%%
  \let\ae@scan@dash@result\relax
  \ae@scan@dash@parse#1_\@nil
  \ae@scan@dash@result
}

\def\ae@add@to@result#1#2{%%
  \ifx#1\relax
    \def#1{#2}%%
  \else
    \expandafter\def\expandafter#1\expandafter{#1#2}%%
  \fi}

\def\ae@scan@dash@parse#1_#2\@nil{%%
  \def\ae@reevaluate{}%%
  \expandafter\ifx\expandafter\relax\detokenize\expandafter{#2}\relax
    \ae@add@to@result\ae@scan@dash@result{#1}%%
  \else
    \ae@add@to@result\ae@scan@dash@result{#1\rule[-1pt]{0.75em}{1.0pt}}%%
    \def\ae@reevaluate{\ae@scan@dash@parse#2\@nil}%%
  \fi
  \ae@reevaluate
  }

\makeatother

\begin{document}

\aescandash{this_is_a_dash_filled_sentence}

\begin{tikzpicture}[%%
  my node/.style={red},
  my node content/.code={\def\aenodecontent{\expandafter\aescandash\expandafter{#1}}},
  ]

  \foreach \myn [count=\myc from 1] in {this,that,another_text}
  {
    \node[my node,my node content=\myn] at (0,-\myc) {\aenodecontent};
  }

\end{tikzpicture}

\end{document}

Instead of upvoting this solution, please upvote cfr's solution (hence the reason for making this community wiki). But I felt what I wound up doing was just different enough to warrant posting the approach I took.

| improve this answer | |
  • (+1) I would change node content to my node content to avoid confusion with node contents. (Obviously not a technical objection since a computer wouldn't confuse them. But humans would. At least, this human would.) – cfr Nov 22 '14 at 22:27

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.