# Adding words centered above other words with TikZ

I'm writing a sentence, and I'd like to add labels to the elements of the sentence above those elements. In the example below, I'd like the label Verb to be centered above the verb constitutes and the label Noun to be centered above the noun phrase a sentence.

I think I'd rather do this with TikZ, because I will later add curly braces and arrows and such (but let's do one thing at a time).

In the example below, I've more or less centered the labels manually by eye, which is clearly not the way to do it. How can I center the labels automatically?

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

\begin{tikzpicture}
\node at (0,0) (Sentence) {This constitutes a sentence};
\node[above] at (-0.5,0.1) {\footnotesize{Verb}};
\node[above] at (1.2,0.1) {\footnotesize{Noun}};
\end{tikzpicture}

\end{document}


Here's an option that allows you to place tikz environments inline with your normal text.

\documentclass{article}
\usepackage{tikz}

\newcommand{\inline}[2]{%
\begin{tikzpicture}[baseline=(word.base), txt/.style={shape=rectangle, inner sep=0pt}]% the baseline key ensures that nodes won't shift up if there's text with descenders, and the txt style removes extra spacing so you can use this inline
\node[txt] (word) {#1};% the first argument is the contents of the main node
\node[above] at (word.north) {\footnotesize{#2}};% the second argument is the tag; you can play with the positioning as necessary
\end{tikzpicture}%
}

\begin{document}

This \inline{constitutes}{Verb} \inline{a sentence}{Noun}.

\end{document}


If you want to add arrows connecting these nodes, you could use the remember picture/overlay option to access nodes in a previous tikzpicture (see for example this answer to How can I draw arrows between words in linguistic examples?).

Depending on what else you want to add on, it might make sense to use a matrix, which is similar to a tabular within a tikzpicture. Then you can add all the frills you want to the nodes in the matrix. I've done this when I want to align horizontally align phrases across multiple similar sentences while still having arrows and such internal to each sentence.

(edited to reflect suggestions by Qrrbrbirlbel)

• I wouldn't use #1 as a node name. It might just contain stuff that isn't allowed (fully expandable) like \textbf or \" or UTF-8 text. You can use any name you want as you won't (and can't in this instance) reference the node later anyway. Or none (i.e. let TikZ use one on its own) and use the append after command key where you can reference the node with \tikzlastnode. Also: Should \tikzset or \tikzstyle be used to define TikZ styles? Apr 10 '15 at 15:35
• @Qrrbrbirlbel, good points, thanks. I've updated the answer accordingly. Apr 10 '15 at 16:09
• This is a minor thing, but I've noticed that if my label has text with a descender, its position will not be horizontally aligned with a label without a descender (replace the label Noun with Subject, and you'll see what I mean). Apr 10 '15 at 16:53
• In other words, the label should somehow include text depth = 0, I believe, cf. my answer here. Apr 10 '15 at 16:56
• I've decided to open up a new question with the new issues I've identified: tex.stackexchange.com/questions/237963/… Apr 10 '15 at 19:35

Not sure if this solution will serve to your further proposes but in any case here it is:

\documentclass{article}
\usepackage{tikz}
\begin{document}
This \tikz[baseline]{\node[anchor=text,label={[font=\footnotesize]above:Verb}]{constitutes};}%
a \tikz[baseline]{\node[anchor=text, label={[font=\footnotesize]above:Noun}]{sentence};}
\end{document}


• Any way to do this with normal spacing between the words of the sentence? Apr 10 '15 at 14:05
• @Sverre Yes, you can add inner sep=0pt and outer sep=0pt to each node options. Apr 10 '15 at 14:22
• Thanks. Also, how can I adjust the vertical spacing of the labels with this code? Now it's set automatically with above:, and I haven't found a way of specifying it further. Apr 10 '15 at 16:43

The text effects along path decoration may be useful if you want to go further with braces and arrows or drawing around the text in general. The decoration takes a bit of setting up, but it can be customised in quite useful ways, and ultimately a lot of the work can be tidied up in styles.

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{decorations.text, decorations.pathreplacing}
\begin{document}
\begin{tikzpicture}
\path [decoration={text effects along path,
text=The quick brown fox jumps over the lazy dog,
text effects/.cd,
path from text, text along path,
group letters, word count=\w,
every word/.style={name=word-\w, execute at begin node=\strut}},
decorate] (0,0);

\node [above=1ex, font=\footnotesize\strut] at (word-\i) {\pos};

\draw [decoration={brace, mirror}, decorate]
(word-1.south west) -- (word-4.south east)
node [midway, below] {Noun Phrase};

\draw [decoration={brace, mirror}, decorate]
(word-7.south west) -- (word-9.south east)
node [midway, below] {Noun Phrase};
\end{tikzpicture}
\end{document}


And here is one way of tidying things up in styles...

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{decorations.text, decorations.pathreplacing}
\tikzset{%
mark text/.style args={"#1" using #2}{
decoration={text effects along path,
text={#1},
text effects/.cd,
path from text, text along path,
group letters, word count=\w,
every word/.style={name=word-\w, execute at begin node=\strut},
above word/.style args={##1:##2}{word ##1/.style={label=above:{##2}}},
%
#2
},
decorate,
insert path={(0,0)}
}
}
\begin{document}
\tikz\path [mark text={"The quick brown fox jumps over the lazy dog" using
above word={1:Det},
above word={5:Verb},
above word={9:Noun}
}];
\end{document}


• Would this work if I didn't want to label every word in the sentence? Apr 10 '15 at 17:01
• @Sverre yes it would still work. The labels can be added as nodes individually rather than in a \foreach loop as I did initially above. Also it is possible to label the words inside the decoration as I demonstrate with my updated answer. Apr 10 '15 at 18:03
• This might be the kind of solution I need to adopt in the end, but I must admit that I have great difficulties understanding exactly how the code works. I'll see how my follow-up question develops first, then I might return to this. Apr 10 '15 at 19:55

Based on your follow-up questions, I'm starting to think you'd be better off using a glossing package to build your sentences with annotations, and then you can use tikz to add braces, arrows, etc.

Here's an example using expex in combination with tikz. The main idea is that we're placing tikz nodes inside the glosses, and then because we're using remember picture and overlay, we can refer to those nodes outside their original tikzpicture. I've defined an expex style to take care of the horizontal and vertical spacing issues you've described in your follow-up questions. The arrows I put in here could be replaced by braces following Mark Wilbrow's answer or whatever else you want to add. I haven't created an abstracted command for the arrows, but you could do that if you want.

\documentclass{article}

\newlength{\Aheight}
\setlength{\Aheight}{\fontcharht\fontA}

\newlength{\gdepth}
\setlength{\gdepth}{\fontchardp\fontg}

\usepackage{tikz}
\usepackage{expex}

\lingset{% these are my default settings for using expex for interlinear glosses
Everyex=,
glstyle=wrap, %
everyglpreamble={\itshape}, %
belowglpreambleskip=-0.2ex, %
everygla=, %
everyglft=, %
aboveglftskip=-0.2ex, %
interpartskip=1ex, %
glspace=!0pt plus .2em, %
glrightskip=0pt plus .5\hsize, %
aboveexskip=1ex plus .4ex minus .4ex, %
belowexskip=1.5ex plus .4ex minus .4ex %
}

\definelingstyle{tags}{% this is a new style that allows you to use tags and arrows or braces
glwordalign=center,% the words are centered beneath the tags
everygla=\footnotesize,% the top line is footnotesize
glspace=0.3em,% horizontal spacing between glwords
aboveglbskip=0.1ex,% vertical spacing between the tag line and word line
interpartskip=3.5ex% vertical spacing between parts of a multipart example (increased to allow space for overlaid arrow)
}

\newcommand\txt[2]{%
\begin{tikzpicture}[remember picture, baseline=(#1.base)]%
\node[inner xsep=0pt, inner ysep=1pt, text height = \Aheight, text depth = \gdepth] (#1) {#2};% I want to keep some inner ysep so that the arrows don't start and end right at the bottom of the letter
\end{tikzpicture}%
}

\begin{document}

\pex[lingstyle=tags]
\a \begingl
\gla xx jj xx jj //
\glb gggggg \txt{a}{aaaaa} ttttttttt \txt{b}{bbbbb\rlap{.}}//
\endgl
\a \begingl
\gla xxxx jjjjjjjj xxxxx jjjjjjjj //
\glb \txt{g}{gg} aa \txt{t}{tt} bb\rlap{.} //
\endgl
\xe

\begin{tikzpicture}[remember picture, overlay, >=stealth]
\draw[->] (a.south) |- +(0ex,-1.5ex)  -| node[near start,anchor=base,yshift={-1.5ex}] {\footnotesize agreement} (b.south);
\draw[->] (t.south) |- +(0ex,-1.5ex)  -| node[near start,anchor=base,yshift={-1.5ex}] {\footnotesize movement} (g.south);
\end{tikzpicture}

\ex
\begingl
\glpreamble You can still do normal glossing if you leave out the tags lingstyle//
\gla Ndi-\O-ani wa-v-aka-teng-er-a  \O-rokwe ku-chi-toro nezuro? //
\glb \textsc{cop}-1a-who 1a.\textsc{nse}-2.\textsc{sm}-\textsc{pst}-buy-\textsc{appl}-\textsc{fv} 5-dress 17-7-store yesterday //
\glft Who(m) did they buy a dress (for) at the store yesterday?'\trailingcitation{(2014-09-09-01-TD)} //
\endgl
\xe

\end{document}


• As a fellow linguist, I can see why you'd think I eventually wanted to end up with something along these lines. For my present purposes, however, I actually don't (I'm not going to have any glossing, numbered examples, or anything like that). But I'm definitely gonna save this code here for later use! :) Apr 11 '15 at 13:12
• @Sverre, you can use expex without the numbering (see tex.stackexchange.com/a/213038/42880). If you were only going to be annotating a few words here and there, then I think my initial solution with the inline tikz environments is good. But if you really want to be tagging each word, then expex takes care of all the alignment issues you bring up in your follow-up questions and you don't have to surround each word-tag pair with a command. Maybe if you post a picture of what your desired output actually is, we'd have a better sense of what is really the best way to achieve it. Apr 11 '15 at 15:19
• I don't intend to tag every word - just a word here and there. The picture in my question above is pretty much exactly what I was looking for. I just noticed the space issue when I just happened to have two tagged words right next to each other, and the comma/period issue when I happened to tag a word at the end of the sentence. I am indeed using your tikz solution for the time being. Apr 11 '15 at 15:27

Here, I use stackengine methods to stack the text. It provides a variety of formats for this, relative to alignments, gaps, over/under stacks, top/bottom lapping, etc.

I added \def\useanchorwidth{T} to the definition, which allows a wider overset not to affect the spacing of the primary sentence. This, of course, can be removed, as it could result in overlap of overset material.

\documentclass{article}
\usepackage{tikz,stackengine}
\newcommand\notate[2]{\def\useanchorwidth{T}\def\stacktype{L}\stackon{#1}{\footnotesize#2}}
\begin{document}

\begin{tikzpicture}
\node at (0,0) (Sentence) {%
This \notate{constitutes}{Verb} a \notate{sentence}{Noun}};
\end{tikzpicture}
\end{document}


• This is beautifully simple! It does, however, also have the comma/period problem addressed here. The sentence is also pushed away from the left margin. I tried to add [node/.style = {inner sep = 0pt}] to your code to fix that, but that didn't help. Any ideas? Apr 11 '15 at 13:21
• @Sverre The "comma problem", by which I think you refer to the problem if the overset is wider than the anchor word, is resolved by setting \def\useanchorwidth{T} in the preamble. Apr 11 '15 at 13:24
• @Sverre I am not sure what the "left margin" problem is. Apr 11 '15 at 13:26
• But then labels above the words collide if their boxes are wider than the boxes of the words below. Apr 11 '15 at 13:26
• So, what is the desired behavior, then? You could always say \stack{word,}{overset}` to get the comma next to the word. Apr 11 '15 at 13:27