9

A TeX arrow is seemingly vertically centered with the height of an x-character, in this case the final e:

\documentclass{article}
\begin{document}
Source $\rightarrow$ Target
\end{document}

enter image description here

A TikZ arrow, on the other hand, seems to be vertically centered with a cap-height character, in this case the capital S and T:

\documentclass{article}
\usepackage{tikz}
\begin{document}

\begin{tikzpicture}
    \draw [thick, ->] (0,0) -- (1,0);
    \node[left] at (0,0) {Source};
    \node[right] at (1,0) {Target};
\end{tikzpicture}

\end{document} 

enter image description here

Since the word on the left ends in an x-height character and the word on the right begins with a cap-height character, this creates the optical illusion (at least for me) that the word on the right is placed higher up than the word on the left. I'd like to avoid this by centering the TikZ arrow vertically with an x-height character. How can I do this?


EDIT

Actually I just noticed that this isn't an optical illusion. The word on the right is placed higher because it contains the letter g that goes below the baseline. To avoid this problem right now (I guess it should be a separate question), consider the following example. The arrow is not centered with respect to the preceding e, and that looks wrong to me:

enter image description here

Sverre
  • 20,729

3 Answers3

6

TikZ uses hboxes to type the node contents. If there is no explicit text depth is specified the descenders will cause the node height to change. So when you have a text with descenders and no-descenders their height will be necesarily different. That's about it. There is no surprise and also not much to change.

You have mentioned that why would you need a text depth if you don't have a descender and that is again to make sure that whether any node with/out descenders align properly without really parsing every letter and checking if there is any.

But!

If you are not going to draw any borders around the nodes, then you can reset the text depth and live with it.

\documentclass[tikz,border=5mm]{standalone}
\begin{document}
\begin{tikzpicture}[every node/.style={inner sep=0,text depth=0,draw}]
    \node[anchor=east] (s) at (0,0) {Source};
    \node[anchor=west] (t) at (1,0) {Target};
\draw[->] (s) -- (t);
\end{tikzpicture}
\end{document}

enter image description here

Or alternatively if you don't mind drawing the connection with a little more labor, then you can align the nodes with respect to their baseline with descenders respecting it. However, then the nodes won't be horizontally perfectly aligned and you need to draw the connections with, say, ortho identifiers.

\documentclass[tikz,border=5mm]{standalone}
\begin{document}
\begin{tikzpicture}[every node/.style={draw,inner sep=0}]
    \node[anchor=mid] (s) at (0,0) {Source};
    \node[anchor=mid] (t) at (1.5,0) {Target};
\draw[->,ultra thick] (s) -- (t);
\draw[-latex,red] (s) -- (t.west|-s);
\end{tikzpicture}
\end{document}

enter image description here

Keep in mind that, you need to do some preparation if you want to use both left and mid. It is not supported right away as far as I know.

percusse
  • 157,807
  • In your first MWE, I see that with text depth=0, then the box is drawn as if no characters had descenders. That's good to know. But, the question was how to draw the box as if there were no characters of cap-height. Based on your answer, I managed to figure out that I can do that with text height = 1ex. So if you just add that to your first MWE, my question is answered. Feel free to edit it in - or if you don't mind I can do it. – Sverre Sep 30 '14 at 19:50
  • @Sverre text height won't help if one has a descender and the other doesn't. Maybe you meant text depth? – percusse Sep 30 '14 at 20:01
  • I think you misread me now as saying "replace text depth = 0 with text height = 1ex"? If you just add it, you get the desired result. – Sverre Sep 30 '14 at 20:09
  • @Sverre Put Sourceg in the first node. – percusse Sep 30 '14 at 20:40
  • It gives me exactly what I want, so I think we're misunderstanding each other here. I've added an answer to illustrate what I mean. – Sverre Sep 30 '14 at 21:26
4

Following up on percusse's answer, we can use text depth = 0 to indicate that the bottom of the box is drawn as if there were no characters with descenders, and use text height = 1ex to draw the top of the box as if all characters were of x-height:

\documentclass[tikz, border = 1mm]{standalone}
\begin{document}
\begin{tikzpicture}[every node/.style = {inner sep = 0, text height = 1ex, text depth = 0, draw}]
    \node[anchor = east] (s) at (0,0) {Sphinx};
    \node[anchor = west] (t) at (1,0) {Sphinx};
    \draw[->] (s) -- (t);
\end{tikzpicture}
\end{document}

enter image description here

The result is that the arrow is vertically aligned with the center of the x-height characters.

Sverre
  • 20,729
  • Now I get it. I won't do it as such but if it works, it works :) – percusse Sep 30 '14 at 21:29
  • One downside of this solution (as you may have noticed yourself when you had to use border=1mm) is that TikZ doesn't calculate the bounding box correctly so part of the nodes text might spill in adjacent stuff. – Qrrbrbirlbel Apr 10 '15 at 20:56
2

How does TikZ/PGF place text inside a node?

For simple nodes (and I will be using the rectangle shape for this example) with one part TikZ puts all node contents in one TeX box (with a width, a height and a depth as usual) and constructs a path around it with the padding /pgf/inner xsep and /pgf/inner ysep.

The center of the node will be the center of the TeX box. With boxes with different heights and depths this will not be on the same vertical height (relative to the baseline) across boxes.

How does TikZ places nodes?

In your example, you use left and right which is basically the same as anchor=east and anchor=west respectively. These anchors lie on the same vertical height as the center anchor (by definition) and on the respective border of the node.

As you can see from the following image (taken from the PGFmanual, section 66.2 Predefined Shapes) you can see the effects much more clearer thanks to a ridiculous high line:
enter image description here

All shapes also provide anchors that are based on the actual text's baseline: text, base, base west and base east as well as mid, mid west and mid east. This makes it already possible to use

\documentclass[tikz]{standalone}
\begin{document}
\begin{tikzpicture}
  \draw [thick, ->] (0,0) -- node[at start, anchor=mid east] {Source}
                             node[at end,   anchor=mid west] {Target} (1,0);
\end{tikzpicture}
\end{document} 

(Of course, if the border is drawn you will get border on different vertical heights again but then you can use text depth and text height and/or \strut (which equals text depth=+.3\baselineskip, text height=+.7\baselineskip.)

A silly problems with mid anchors

The mid anchors lie 0.5ex above the baseline (and its anchors). But careful! Even though this is a length unit dependent on font size, TikZ implements it a bit silly. Observe what happens when you add [nodes={font=\Huge}] to the tikzpicture environment of the example above:
enter image description here

With [nodes={/utils/exec=\Huge}] (or just using \Huge after the start of the tikzpicture) you get an output you might have expected.

How does TikZ draw a path between nodes?

The mid anchors seem like a good solution, right?

Even if you position the nodes in a manner so that their baselines (or their "midlines") are positioned on the same vertical height, the connection as in

\documentclass[tikz]{standalone}
\usetikzlibrary{positioning}
\begin{document}
\begin{tikzpicture}
  \node (s) {Source}; \node (t) [mid right=of s] {Target};
  \draw [thick, ->] (s) -- (t);
\end{tikzpicture}
\end{document} 

will not be drawn to our wishes.

When you ask TikZ to connect two nodes with a shape (i.e. all but coordinates) it uses points on the border of the nodes' shapes. For a line-to (--) it "shoots" from the center of one node to the other node's center. Where this ray hits the border of the node the actual start of the line is set. This is in fact nothing different then the angular anchors, so the example above might just result in the same as

\draw [thick, ->] (s.359) -- (t.179);

The solution to all this is obvious:

\draw [thick, ->] (s.mid east) -- (t.mid west);

But this requires you to selectively choose the anchors (which can be hidden in a to path but that will also get complex fast if you want to cover all possible combinations of node placement as well as starts and targets).

Why don't we just move the center anchor then?

The tikz-cd provides a shape named asymmetrical rectangle which gives us a center anchor (and east and west) that is similar defined as the usual mid anchors. Instead of a fixed .5ex the value of the /tikz/commutative diagrams/center yshift key is used. Unfortunately, this suffers the same problem regarding font sizes as the mid anchors.

enter image description here

Code

\documentclass[tikz]{standalone}
\usepackage{tikz-cd}\tikzset{nodes={shape=asymmetrical rectangle}}
\usetikzlibrary{positioning}
\begin{document}
\begin{tikzpicture}
  \draw [thick, ->] (0,0) -- node[at start, left] {Source}
                             node[at end,  right] {Target} (1,0);
\end{tikzpicture}
\begin{tikzpicture}
  \node (s) {Source}; \node (t) [right=of s] {Target};
  \draw [thick, ->] (s) -- (t);
\end{tikzpicture}
\end{document} 
Qrrbrbirlbel
  • 119,821