11

I'd like to define the following style:

test/.style={
    execute at begin node={\raisebox\{-0.2cm\}\{},
    execute at end node={\}}
}

and use it like

\documentclass{article} 
\usepackage{tikz}

\tikzset{
    test/.style={
        execute at begin node={\raisebox\{-0.2cm\}\{},
        execute at end node={\}}
    }
}

\begin{document}

    \begin{tikzpicture}
        \node[test,circle,draw] {g};    
    \end{tikzpicture}

\end{document}

But it seems to be wrong, as I get errors like "! Extra }, or forgotten \endgroup" or "Missing number, treated as zero".

I guess the problem is due to the braces. How can I escape them properly?

EDIT: From the answers it became clear that \raisebox alone (like I tried in my broken solution) would've somehow worked in my case (because the dimension was so small that it didn't change the node size (at least I can't see it), which is what I wanted). So I consider all answers correct that only handled this specific/simple case. Answers using \smash and \phantom solved the problem I had actually in mind (but which I didn't clearly formulate as a question). Thanks again for all the great answers! :)

WARNING: As already said by percusse, if one only wants to align the text baselines, don't use this! Instead, as described in the "Putting a Diagram in Chains" tutorial of the manual, use text height and text depth on all nodes to be aligned, e.g. text height=1.5ex,text depth=.25ex.

3
  • 2
    If this is illustrative of what you are trying to achieve you could just use \tikzset{test/.style={yshift=-0.2cm}}. The problem here is not the escaping of the characters but the fact that what you are trying to execute at the begin and end is not code, but rather a code fragment. Commented Dec 29, 2012 at 15:28
  • @PeterGrill Ok, I'll try. I though it might work as I often see how this is used for activating and deactivating math mode ($ and $).
    – letmaik
    Commented Dec 29, 2012 at 15:34
  • @PeterGrill yshift moves the whole node, I just want to move the text inside the node.
    – letmaik
    Commented Dec 29, 2012 at 15:37

6 Answers 6

13

If I want to do something with the node’s content (which, to my recollection, cannot be accessed by a key), I collect it in a savebox via the lrbox environment.

In the following example, the text is saved in the savebox \qrrTikzNodeContent and then, still in the execute at end node key, used.

Firstly it is typeset in \phantom so that the node gets re-sized appropriately (as if the content would be typeset as usual). The following \usebox command is firstly \llaped to occupy the same horizontal space like its phantom-ed version, and than \smashed so that it doesn’t occupy any vertical space.

I added some hopefully useful keys, that should help how \raisebox may affect the node:

  • phantom re-sizes the node as if the content would be typeset in the usual way;
  • smash deactivates re-sizing of the node as forced by \raisebox.

This does not work with the keys text width and align as \tikz@atbegin@node is executed after the minipage is applied that is used to realize those keys.

For this special use-case I want to point out that there’s also the text depth key. For <content> without a depth setting this to <value> is like a \raisebox{<value>}{<depth>}.

The following code procudes a matrix of nodes:

  normal        raise            raise + phantom
text depth  raise + smash    raise + phantom + smash

Additionally it shows the baseline of the node.

Code

\documentclass[tikz]{standalone}
\newsavebox\qrrTikzNodeContent
\newif\ifphantom% remove for 'clean' version
\newif\ifsmash  % remove for 'clean' version
\tikzset{
  nodes={draw,circle},
  bl/.style={append after command={\pgfextra{\draw[very thin] (\tikzlastnode.base west) -- (\tikzlastnode.base east);}}},% debug, remove for 'clean' version
  phantom/.is if=phantom,% remove for 'clean' version
  smash/.is if=smash,    % remove for 'clean' version
  raise/.style={
    execute at begin node={\begingroup\begin{lrbox}{\qrrTikzNodeContent}},
    execute at end node={\end{lrbox}% remove for 'clean' version
      \ifphantom
          \phantom{\usebox\qrrTikzNodeContent}%
      \fi
      \ifsmash\smash{\fi
        \ifphantom\llap{\fi
          \raisebox{#1}{\usebox\qrrTikzNodeContent}%
        \ifphantom}\fi
      \ifsmash}\fi
      \endgroup
    },
%    execute at end node={\end{lrbox}% 'clean' version
%      \phantom{\usebox\qrrTikzNodeContent}%
%      \smash{%
%        \llap{%
%          \raisebox{#1}{\usebox\qrrTikzNodeContent}%
%        }%
%      }%
%      \endgroup
%    }
  },
}
\begin{document}
  \begin{tikzpicture}
    \matrix[draw=none, append after command={}, row sep=1ex, column sep=1ex] {
      \node[bl,                            ] {g}; &
      \node[bl, raise=-.2cm,               ] {g}; &  % only raisebox
      \node[bl, raise=-.2cm,        phantom] {g}; \\ % phantom
      \node[bl, text depth=-.2cm           ] {g}; &  % text depth
      \node[bl, raise=-.2cm, smash,        ] {g}; &  % smash
      \node[bl, raise=-.2cm, smash, phantom] {g}; \\ % smash + phantom
    };
  \end{tikzpicture}

  \begin{tikzpicture}
    \matrix[draw=none, append after command={}, row sep=1ex, column sep=1ex] {
      \node[bl,                            ] {h}; &
      \node[bl, raise=-.2cm,               ] {h}; &  % only raisebox
      \node[bl, raise=-.2cm,        phantom] {h}; \\ % phantom
      \node[bl, text depth=-.2cm           ] {h}; &  % text depth
      \node[bl, raise=-.2cm, smash,        ] {h}; &  % smash
      \node[bl, raise=-.2cm, smash, phantom] {h}; \\ % smash + phantom
    };
  \end{tikzpicture}
\end{document}

Output

enter image description here enter image description here

4
  • Interesting! So many options one has now. So in my case I would need the raise=-.2cm, smash, phantom combination as I don't want the node size to be changed. I guess this particular combination is similar to mwibrow's solution.
    – letmaik
    Commented Dec 29, 2012 at 19:56
  • @neo I have added the phantom and smash switch so that you can see that a simple \raisebox might not necessarily be everything you need to do. You can either remove the switches (.as if keys, \if<switch> and \fi) or create an extra style: <name>/.style={raise=#1, smash, phantom} Commented Dec 29, 2012 at 22:20
  • Ah! In my case, 0.2cm was so small that it didn't change the size of the node! That's why I thought that it would work for all dimensions. Now I really understand why your solution offers phantom and smash.
    – letmaik
    Commented Dec 30, 2012 at 13:11
  • (or at least I didn't see that the size was changed)
    – letmaik
    Commented Dec 30, 2012 at 13:31
8

How about:

\documentclass{standalone} 
\usepackage{tikz}

\newbox\collectbox
\tikzset{
    raise text/.style={
        execute at begin node={\setbox\collectbox=\hbox\bgroup},
        execute at end node={%
            \egroup%
            \phantom{\copy\collectbox}%
            \kern-\wd\collectbox%
            \smash{\raisebox{#1pt}{\box\collectbox}}}
    }
}

\begin{document}

\foreach \y in {-5,-4,...,5}
    \tikz\node [raise text=\y, circle, draw] {g}; 

\end{document}

Obviously the use of \smash means the text isn't taken in to account when calculating the bounding box (which isn't a problem in this case as the text is inside the node path).

enter image description here

EDIT: Actually, I see this is, in essence, identical to the answer given by Qrrbrbirlbel above, only mine is a bit less versatile.

1
  • If I see it correctly, your code handles Qrrbrbirlbel's raise=-.2cm, smash, phantom case, right?
    – letmaik
    Commented Dec 29, 2012 at 19:57
7

I would strongly recommend not doing this since what you have in mind is valid for matrix cells but not for regular nodes. In fact, for the regular node handling, it's not just grab whatever is provided between{} but more complicated text color,font etc. adjustments take place. You can instead do your adjustments by text height,text depth etc. tools provided or put a label style externally.

But if you want to get a little adventurous then you need to give a little care for your group not to be closed (in this context delimiters matched) too quickly.

\documentclass{article}
\usepackage{tikz}

\tikzset{
    test/.style={
        execute at begin node=\raisebox{#1}\protect\begingroup,
        execute at end node=\endgroup
    }
}

\begin{document}

    \begin{tikzpicture}
        \draw (-5,0) rectangle (5,3);
                \foreach\x in{-10,-9,...,10}{
        \node[circle,draw,test=\x mm] at (4*\x mm,1.5){g};
                }
    \end{tikzpicture}


\end{document}

enter image description here

See how box raise amount is pushing the box in the reverse direction (which is another evidence for why you should not do this.

2
  • Why is \raisebox working in the opposite direction here?
    – letmaik
    Commented Dec 29, 2012 at 20:02
  • @neo The box is first placed without knowing that the contents are going to be stretched so the box is placed in the base anchor as it is (protect is hiding the information) . It's kind of a hack but avoids extra register allocation. I have updated the example.
    – percusse
    Commented Dec 30, 2012 at 1:49
5

Perhaps something like this?

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

\tikzset{test/.style={yshift=-0.15cm}}
\newcommand{\MyNode}[2]{
        \node (#1) at (#2) [test,circle,draw] {\phantom{#1}};    
        \node [test] at (#1) {#1};
}
\begin{document}
    \begin{tikzpicture}
        \MyNode{g}{0,0}
        \MyNode{h}{1,0}
    \end{tikzpicture}
\end{document}

enter image description here

1
  • I like the simplicity, but I don't like the newcommand -- having such thing as a style (as in mwibrow's answer) has more flexibility I guess.
    – letmaik
    Commented Dec 29, 2012 at 20:01
3

If you simply want to raise the node content you can use

\tikzset{test/.style={yshift=-0.2cm}}

enter image description here

As mentioned in the comments, the problem is not with escaping the curly braces, but rather with code fragments.

1
  • Well, this also raises the node itself and not only the node content. What I want to do is change the text baseline (if it's called like that) in some situations, and most people seem to use \raisebox even in tikz for that.
    – letmaik
    Commented Dec 29, 2012 at 15:50
1

OK, first of all I don't think that a key execute at begin node actually exists in TikZ. There is however execute at begin scope, maybe you want to use that.

Now about the escaping of braces: You don't have to use braces around the code for execute at.... Instead just put your code on one line and use \bgroup and \egroup:

test./style={
    execute at begin scope=\bgroup\raisebox{-0.2cm}
    execute at end scope=\egroup
}

I'm not sure what exactly you are trying to achieve with this code, so my answer may not be exactly what you were looking for.

4
  • 2
    What a shame, it compiles but doesn't cause any effect. execute at begin node does indeed exist, have a look at the pgf manual. Although I can't use it with your proposed way of using \bgroup and \egroup.
    – letmaik
    Commented Dec 29, 2012 at 15:48
  • 5
    The key execute at begin node exists but it is not documented... Commented Dec 29, 2012 at 15:51
  • Ah, didn't know that. Thanks! :)
    – alexkelbo
    Commented Dec 29, 2012 at 17:34
  • 1
    @alexraasch: Actually there are execute at begin picture, execute at begin scope, execute at begin to, execute at begin node and execute at begin cell (plus the respective execute at end...) and also execute at empty cell. Commented Dec 30, 2012 at 10:58

You must log in to answer this question.

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