24

General description of the intrinsic behavior

The labels in a tikz picture are shifted (e.g. by above) exactly to the extreme of the letters in the labels, i.e. it takes the descender (see Wikipedia's entry for descender) of the letters into account like at the letter "g" (or the "p" of Sphinx).

enter image description here

Different aspect (why that question)

But then the following examples do not look symmetric anymore.

\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
\coordinate (leadl) at (1,0);
\coordinate (leadr) at (2,0);
\fill (leadl)  circle[radius=2pt];
\fill (leadr)  circle[radius=2pt];
\node[above] at (leadl) {left};
\node[above] at (leadr) {right};
\end{tikzpicture}
\end{document}

Example with labels above

\documentclass[tikz]{standalone}
\begin{document}
\begin{tikzpicture}
\coordinate (leadl) at (1,0);
\coordinate (leadr) at (2,0);
\fill (leadl)  circle[radius=2pt];
\fill (leadr)  circle[radius=2pt];
\node[below] at (leadl) {lelele};
\node[below] at (leadr) {rerere};
\end{tikzpicture}
\end{document}

enter image description here

Issue to be solved

What is the easiest way to work around this (in some cases wished) feature? I do not want to analyze every character in all nodes' labels that I am typing.

strpeter
  • 5,215
  • Does there also exist something like anchor=ascent or anchor=descent? I have not found these in the manual. For the case [below] it would be necessary to anchor the ascent or add the font size to the base. – strpeter Dec 28 '13 at 18:00

4 Answers4

24

TikZ understands this problem already and offers text depth length. You can either zero it out or add the fixed amount of depth to all nodes. Example (I somehow like cramped design)

\documentclass[tikz]{standalone}
\begin{document}
\begin{tikzpicture}[scale=1.5]
\coordinate (leadl) at (3,0);
\coordinate (leadr) at (5,0);
\fill (leadr)  circle[radius=2pt];
\fill (leadl)  circle[radius=2pt];
\node[above] at (leadl) {left circle};
\node[above,text depth=0pt] at (leadr) {right circle};
\end{tikzpicture}
\end{document}

enter image description here

percusse
  • 157,807
9

Here is a general solution with two variants : \strut or \vphantom. This solution may be used with multi-lines nodes or different font sizes.

enter image description here

\documentclass{standalone}
\usepackage{tikz}
\tikzset{
  fix node via strut/.style={
    execute at begin node={\strut},
    execute at end node={\strut},
  },
  fix node via vphantom/.style={
    execute at begin node={\vphantom{Ag}},
    execute at end node={\vphantom{Ag}},
  },
}
\begin{document}
\begin{tikzpicture}
  \coordinate (leadl) at (1,0);
  \coordinate (leadr) at (2,0);
  \fill (leadl)  circle[radius=2pt];
  \fill (leadr)  circle[radius=2pt];
  \node[fix node via strut,above,align=center] at (leadl) {left\\left};
  \node[fix node via strut,above,align=center] at (leadr) {right\\right};
  \node[fix node via strut,below,font=\large] at (leadl) {lelele};
  \node[fix node via strut,below,font=\large] at (leadr) {rerere};
  \node[blue,above] at (current bounding box.north){via strut};
\end{tikzpicture}
\begin{tikzpicture}
  \coordinate (leadl) at (1,0);
  \coordinate (leadr) at (2,0);
  \fill (leadl)  circle[radius=2pt];
  \fill (leadr)  circle[radius=2pt];
  \node[fix node via vphantom,above,align=center] at (leadl) {left\\left};
  \node[fix node via vphantom,above,align=center] at (leadr) {right\\right};
  \node[fix node via vphantom,below,font=\large] at (leadl) {lelele};
  \node[fix node via vphantom,below,font=\large] at (leadr) {rerere};
  \node[red,above] at (current bounding box.north){via vphantom};
\end{tikzpicture}
\end{document}
Paul Gaborit
  • 70,770
  • 10
  • 176
  • 283
  • 1
    It would be nice to define a key like tall text/.style={every node/.append style={ execute at begin node={\strut}, execute at end node={\strut}, }} so you can use it in a scope to modify the behaviour of text in every node. – Bordaigorl Dec 31 '13 at 00:41
  • @Paul Gaborit: Why did you set font=\large at the labels below the nodes? – strpeter Dec 31 '13 at 07:59
  • 1
    @strpeter To show that my solution takes into account the current font size of the node. – Paul Gaborit Dec 31 '13 at 08:10
  • Maybe \vphantom{Ag} is not always enough. I have defined a font dependent strut \fstrut as follows \newcommand{\fstrut}[0]{\vphantom{ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜẞabcdefghijklmnopqrstuvwxyzäöüß}} this should also catch the ascender and descender height dependent on the definition of the font. (There might be more relevant letters dependent on your language and I don't know if strut is the correct term for what I do.) – white_gecko Jun 28 '20 at 13:57
  • 1
    @white_gecko Good suggestion. In fact, the correct list to use depends of the current language, of the current font, of some other aspects... and of your usage! ;-) – Paul Gaborit Jun 28 '20 at 15:51
4

What you need to do is add anchor=base to the options of the two nodes. It will align the two labels by their base lines. Presumably you meant to write:

\node[above of=leadl, anchor=base] {left circle};

In order to anchor to the ascent or descent, you would use e.g. anchor=north. This will anchor the node at the middle of the upper edge of the node. However, there is some invisible padding inside the node (try adding e.g. fill=blue to see the padding). To remove the padding, use inner sep=0pt. Other choices for anchor position include north east, north west, south east, etc.

yori
  • 5,681
  • 29
  • 59
1

I wrote something like this for a similar purpose. Arguably it is a bit bulky for what it accomplishes, but perhaps you will find it useful.

\usepackage{xparse}
\makeatletter
\newcommand{\standardisebox}[1]{%
    \smash{#1}\vphantom{\strpeters@box@determining@symbol}%
}
\let\standardizebox\standardisebox
\NewDocumentCommand{\setboxdeterminingsymbol}{sm}{%
    \IfBooleanTF {#1}%
                 {\gdef\strpeters@box@determining@symbol{#2}}%
                 {\def\strpeters@box@determining@symbol{#2}}%
}
\NewDocumentCommand{\resetboxdeterminingsymbol}{s}{%
    \IfBooleanTF {#1}%
                 {\setboxdeterminingsymbol*{0}}%
                 {\setboxdeterminingsymbol{0}}%
}
\resetboxdeterminingsymbol
\makeatother

The way this works is, you wrap the node text in \standardisebox and it is typeset as though it had the height and depth of a digit 0 instead of its true height and depth. You can change the character used to determine the height and depth using the macro \setboxdeterminingsymbol, and reset it to normal using \resetboxdeterminingsymbol. Use the starred forms for global changes.

\documentclass{standalone}
\usepackage{tikz}
\usepackage{xparse}
\makeatletter
\newcommand{\standardisebox}[1]{%
    \smash{#1}\vphantom{\strpeters@box@determining@symbol}%
}
\let\standardizebox\standardisebox
\NewDocumentCommand{\setboxdeterminingsymbol}{sm}{%
    \IfBooleanTF {#1}%
                 {\gdef\strpeters@box@determining@symbol{#2}}%
                 {\def\strpeters@box@determining@symbol{#2}}%
}
\NewDocumentCommand{\resetboxdeterminingsymbol}{s}{%
    \IfBooleanTF {#1}%
                 {\setboxdeterminingsymbol*{0}}%
                 {\setboxdeterminingsymbol{0}}%
}
\resetboxdeterminingsymbol
\makeatother
\begin{document}
\begin{tikzpicture}
\coordinate (leadl) at (1,0);
\coordinate (leadr) at (2,0);
\fill (leadr)  circle[radius=2pt];
\fill (leadl)  circle[radius=2pt];
\node[above] at (leadl) {\standardisebox{left}};
\node[above] at (leadr) {\standardisebox{right}};
\end{tikzpicture}
\end{document}

In my case I was placing nodes (actually node labels) systematically inside of another macro, so using the macro to reset all of their boxes was not hard work. If you're setting many nodes manually, then wrapping each of them in the macro may prove to be arduous.

strpeter
  • 5,215
Hammerite
  • 3,596
  • Sorry, I don't understand what's the use case of this: isn't my answer delegating all this hard word to TiKZ which already knows how to deal with ascents and descents? – yori Dec 28 '13 at 18:40
  • @Yori, your answer works well when the nodes are positioned above other picture elements. Your prescription for when the nodes are to be positioned below other picture elements is to use e.g. the "north" anchor. This as far as I can see doesn't work so well when one node text has ascenders and the other doesn't (e.g. one node text is "lalala" and the other is "woowoowoo") – Hammerite Dec 28 '13 at 18:45
  • I see. But wouldn't anchor=base, yshift=-1ex do the trick in that case? This would "anchor" the node at 1ex (the height of an x) above the base line, which is at the median line (see figure above). – yori Dec 28 '13 at 18:52
  • Yes, that seems like it would work. – Hammerite Dec 28 '13 at 19:13