27

If I write

before-text \begin{minipage}[t]{5cm} line1\\line2\\line3\\ \end{minipage} after-text

I get a box, whose top is aligned to the top of "before text". I would like to have the same, when using a tikz node (with line1\\line2\\line3\\) instead of minipage. But the default behaviour is to align the bottom of the node with the bottom of the "before text". How can I do this?

3
  • It is always best to compose a fully compilable MWE that illustrates the problem including the \documentclass and the appropriate packages so that those trying to help don't have to recreate it. Commented Dec 29, 2011 at 19:32
  • 1
    Does this post help: How to vertical align a text and an tikzpicture?
    – Werner
    Commented Dec 29, 2011 at 19:37
  • You can find the relevant discussion in the manual page 117 about baseline key.
    – percusse
    Commented Dec 29, 2011 at 19:37

5 Answers 5

25

To align the contents of the tikzpicture you have some possibilities.

TikZ calculate the bounding box of every picture. If you don't set the bounding box manual the whole picture will be used. TikZ define a node which can be use to reference the calculated bounding box. The node name is current bounding box. However important is the key baseline which is the reference for the vertical alignment of the tikzpicture. This key can be set at the beginning of the environment.

An example with current boundingbox:

\documentclass[a4paper, 11pt]{standalone}
\usepackage{tikz}

\begin{document}
Text before
\begin{tikzpicture}[baseline={([yshift={-\ht\strutbox}]current bounding box.north)},outer sep=0pt,inner sep=0pt]
\draw (0,0)--(1,0);
\draw (2,0)--(3,0);
\node[align=left,] at(1.5,0) {\strut line1\\line2\\line3\\line4};
\end{tikzpicture}
Text between
\begin{tikzpicture}[baseline=(current bounding box.south),outer sep=0pt,inner sep=0pt]
\draw (0,0)--(1,0);
\draw (2,0)--(3,0);
\node[align=left] at (1.5,0) {\strut line1\\line2\\line3\\line4};
\end{tikzpicture}
Text after
\end{document}

An example where the node was named and the node can be used for the vertical alignment. The example was provided by percusse

\documentclass[a4paper, 11pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}

\begin{document}

before text 
\tikz[baseline={([yshift={-\ht\strutbox}]a.north)},outer sep=0pt,inner sep=0pt] \node[align=center] (a) {\strut line1\\line2\\line3\\line4};
text between
\tikz[baseline=(a.south),outer sep=0pt,inner sep=0pt] \node[align=center] (a) {\strut line1\\line2\\line3\\line4};
text after
\end{document}

Every node / tikzpicture have a space around. The spaces are inner sep and outer sep. To align the text you have to set this seps to 0pt. (or calculate the new resulting height). The command \strut for the alignment of the north is required to set a fix height at the first line of the node. In this way you can shift the baseline by a fix height.

enter image description here

8
  • @Werner: Now ;-)???? Commented Dec 29, 2011 at 20:27
  • yes, the baseline option + the yshift={-\ht\strutbox} parameter do the job very well. Thanks! Commented Dec 29, 2011 at 20:28
  • 1
    @MarcoDaniel: Yay for vertical alignment + up-vote-ability. :)
    – Werner
    Commented Dec 29, 2011 at 20:29
  • @MarcoDaniel : could you explain what does actually the \strut before line1 ? Commented Dec 29, 2011 at 20:29
  • @nicolasroy: Do you know the command \strut Commented Dec 29, 2011 at 20:30
11

If you add text width=<width> to the node option the content is wrapped in a top align minipage internally. Together with the baseline option you can achieve the requested alignment. The drawback is that you need to set the width of the node. I tried replacing the internal minipage with a varwidth environment, but without success.

\documentclass{article}

\usepackage{tikz}

\begin{document}

before
\begin{tikzpicture}[baseline={(A.base)}]
    \node [text width=2cm] (A) at (0,0) {first\\second\\third};
\end{tikzpicture}
after

\end{document}

Result

1
  • It's the good method. Commented Dec 29, 2011 at 21:50
6
\documentclass[a4paper, 11pt]{standalone}
\usepackage{tikz}    
\begin{document}

before text 
\raisebox{\dimexpr -\height+\baselineskip-1pt}{% 1pt is \interlineskip
  \tikz \node (a) {\tabular{c} line1\\line2\\line3\\line4\endtabular};}
text between
\raisebox{\dimexpr-0.5\baselineskip-1pt}{%
  \tikz \node (b) {\tabular{c} line1\\line2\\line3\\line4\endtabular};}
text after

\end{document}

enter image description here

1
  • For the interested party, in order to remove the extra tabular padding, using a column alignment of @{}c@{}.
    – Werner
    Commented Dec 29, 2011 at 21:31
6

I'm taking another bite at the same question. With a vastly improved solution, I think --- thus the new answer.

I looked at tikz code: the vertical alignments are hard wired to the bottom line, except the fixed width environment in LaTeX (and only LaTeX). I'm going to prepare a patch for this, soon, until then, an easy hack.

\documentclass{minimal}
\usepackage{tikz}

\makeatletter
\tikzset{
  base/.is choice,
  base/top/.code={\let\vbox\vtop},
  base/bottom/.code={\def\pgfutil@minipage[t]{\minipage[b]}},
  % base/bottom/.code={}, % for plain TeX
}
\makeatother

\begin{document}
text before \rule{1cm}{0.4pt}
\begin{tikzpicture}
  [every node/.style={align=center,anchor=base},baseline=0pt]
  \node at (0,0)[text width=2cm,base=bottom]
        {automatic line breaks with base at the bottom};
  \node at (2,0)[text width=2cm,base=top]
        {automatic line breaks with base at the top};
  \node at (4,0)[text width=2cm,base=bottom]
        {manual\\line breaks\\with\\base at the\\bottom};
  \node at (6,0)[text width=2cm,base=top]
        {manual\\line breaks\\with\\base at the\\top};
\end{tikzpicture}
\rule{1cm}{0.4pt} text after
\end{document}

Extremely simple to use: say base=top or base=bottom. If you don't specify anything, you get the (crazy) default behaviour.

As far as I can see, redefining \vbox (uuuu, scary!:) and \minipage has no ill effects. Looking at the code, all the redefinitions stay in a very local group. ---Ahh well, as you can see from the updated image, there is a side effect... But the alignment works ;-)

top/bottom-base aligned multiline text of fixed/variable width

2
  • This is not needed. Martin's answer is just enough to do this without redefining \vbox. You just need to give a name to the node and if you like for the bottom placement the regular before \tikz[baseline={([yshift={0.5\ht\strutbox}]A.south)}] \node [text width=3em] (A) at (0,0) {first\\second\\third}; after is enough. Also naming the style as base can conflict with the existing base default anchor name.
    – percusse
    Commented Jul 27, 2012 at 14:22
  • I was looking for a more general (and also easy-to-use) solution. General also in the sense that it would work for all shapes! ---About the name, I don't really care, but I think it makes sense to call it base because that's what it does, it determines where the base is. Is anchor base used as a key name at any point? Commented Jul 27, 2012 at 14:40
5

I was bugged by the same problem for quite a while... and I think I have finally found a solution that's usable in everyday life ;-)

I defined a new (generic) anchor top base, which does the job in non-complicated cases.

\documentclass{article}
\usepackage{tikz}

\makeatletter
\pgfdeclaregenericanchor{top base}{%
  \csname pgf@anchor@#1@north\endcsname
  \pgf@anchor@generic@top@base@main
}
\pgfdeclaregenericanchor{top base west}{%
  \csname pgf@anchor@#1@north west\endcsname
  \pgf@anchor@generic@top@base@main
}
\pgfdeclaregenericanchor{top base east}{%
  \csname pgf@anchor@#1@north east\endcsname
  \pgf@anchor@generic@top@base@main
}
\def\pgf@anchor@generic@top@base@main{%
  {%
    \pgfmathsetlength\pgf@ya{\pgfkeysvalueof{/pgf/outer ysep}}%
    \advance\pgf@y-\pgf@ya
    \pgfmathsetlength\pgf@ya{\pgfkeysvalueof{/pgf/inner ysep}}%
    \advance\pgf@y-\pgf@ya
    \pgf@ya=0pt
    \pgfutil@loop
    \ifdim\pgf@y>\baselineskip
      \advance\pgf@y-\baselineskip
      \advance\pgf@ya\baselineskip
    \pgfutil@repeat
    \global\pgf@y=\pgf@ya
  }%
}
\makeatother


\begin{document}

The following node
\begin{tikzpicture}
  [align=center,baseline=(a.base)]
  \node(a){sticks\\up};
\end{tikzpicture}
while the next one
\begin{tikzpicture}
  [align=center,baseline=(a.top base)]
  \node(a){sticks\\down};
\end{tikzpicture}

\end{document}

Result of first example

The anchor (actually, the three anchors, because I also defined the east and the west version) is then of course usable also in within a tikzpicture. An example follows.

\begin{tikzpicture}[align=center,every node/.style={draw,outer sep=2pt}]
  \node(a){first node\\with\\some text};
  \node[anchor=top base west,at=(a.top base east)] (b)
       {this node\\is anchored\\at top base\\and positioned\\at top
         base\\of the node\\on its left};
  \node[anchor=base west,at=(b.top base east)](c)
        {this node\\is anchored\\at base\\and positioned\\at top
         base\\of the node\\on the left};
  \node[anchor=top base west,at=(c.base east)](d)
        {anchored\\at base\\positioned\\at top
         base\\of the left node};
\end{tikzpicture}

Result of second example

How does it work? It first deduces the height (without depth) of the original box where the node text was typeset. Then it estimates the number of newlines by counting how many \baselineskips fit in the height, and sets the top base anchor that much to the north.

The problems: two. First, the procedure fails if a font larger than \baselineskip was used in the box --- ok, I can live with that. Second, much more problematically, it only works for some shapes (rectangle, ellipse, and some others). For other shapes it is impossible to deduce the height of the original box; the information is of course there when saved anchors are computed, but then it gets lost... more precisely, it cannot be recovered from saved anchors and dimensions. I'm thinking the only way to hack this is to define a new shape (say circle'), inheriting everything from circle and adding additional saved dimensions/anchors.

I hope this helps, although it is not as generic as the name suggests :-)

3
  • You are allowed to insert a link to image location. Users with enough reputation are then able to change it into an image link (simply by adding the exclamation mark).
    – Speravir
    Commented Jul 22, 2012 at 0:34
  • Today I had the time to take a look on your code. Luckily meanwhile you had provided links to the images, so I could insert them. (BTW: upvoted, of course!)
    – Speravir
    Commented Jul 23, 2012 at 20:42
  • :-) I don't know why I didn't think of links myself... Thanks for the advice! Commented Jul 23, 2012 at 21:32

You must log in to answer this question.

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