# Text baseline alignment in filled boxes using tikz

How do I align text in individual boxes to avoid getting results like the following where the second word is shifted up due to parts of the characters stretching below the baseline?

Here's my MWE:

\documentclass{article}
\usepackage{tikz}
\pagestyle{empty}
\usepackage{geometry}
\usepackage{xcolor}
\usetikzlibrary{positioning,calc}

\geometry{papersize={85mm, 15mm}}

\begin{document}

\newcommand\textbox[5]{
\begin{tikzpicture}[remember picture,overlay]
\filldraw [fill=black, draw=black, line width=0.3mm]
($(current page.south west)+(#1,#2)$) rectangle
($(current page.south west)+(#3,#4)$)
node[pos=0.5] {\color{white}#5};
\end{tikzpicture}}

\textbox{10.0mm}{5mm}{40.0mm}{10.0mm}{\bf{Levels}}
\textbox{45.0mm}{5mm}{75.0mm}{10.0mm}{\bf{Gauging}}

\end{document}


I know there is an argument yshift but I don't know how to make this work dependent on the text's baseline. Maybe that argument isn't even the right option.

Edited to say: the placement of the word "Levels" looks natural and "Gauging" should be at the same level, meaning the g's can stick out towards the bottom.

• unrelated but \bf is not defined by default in latex, and when it is defined (for compatibility with documents written in the 1980s) the syntax is {\bf Levels} not \bf{Levels} use \textbf{levels} Jan 25, 2020 at 10:11
• OK, thanks for the hint. Jan 25, 2020 at 10:15
• you could use \strut#5 so they always aligned as if they had a descender Jan 25, 2020 at 10:24
• If I use \strut%5 then they align. However, both of the words are shifted up. I think it looks more natural if it is placed like the word "Levels". So in case of "Gauging", the g's should be sticking out to the bottom. Jan 25, 2020 at 10:32
• In terms of the \strut approach, here is the related question: tex.stackexchange.com/questions/133227/… Jan 27, 2020 at 13:59

First, as David Carlisle said, you should not use \bf in LaTeX (plus, it doesn't take an argument, it is what Leslie Lamport would call a declaration: it acts on whatever follows in the current TeX group). I replaced it with font=\normalfont\bfseries in the node options.

You have texts of different depths, but you presumably want the black rectangles to have exactly the same height. Since you said in a comment that you would like vertical placement to be the same as for Levels, which has no descender, I propose to ignore the depth of the texts passed to your \textbox macro. This can be done either with TikZ options or with box manipulation commands provided by (La)TeX.

# TikZy ways

## Using TikZ nodes for the black rectangles

Here, we use a TikZ node with rectangle shape to draw each text as well as the black rectangle behind it (in your solution, the black rectangles were drawn with a rectangle operation, not a node). For cleanliness and readability of the code, we define a TikZ style named my special node that contains all options needed for these special nodes. The most important options in this style in relation to your question are:

• text depth=0pt in order to ignore the depth of the node contents when computing its dimensions;

• anchor=south west to place the node by giving the coordinates of its lower left corner;

• font=\normalfont\bfseries to select the font;

• minimum width and minimum height so that the rectangles are at least as wide and high as mandated by the arguments of your \textbox macro;

• inner sep=0pt to ensure that no more space is automatically added inside the node, around its contents (with a larger value, the node contents could “push” its border even when not touching it; this can be desirable in some way, but may also easily result in nodes with different heights if the contents of a node is higher than that of other nodes) .

You don't really need line width=0.3mm (you could use 0pt) nor the -0.5\pgflinewidth in the xshift and yshift amounts used for the node. The only reason I include them is to provide an exact match with your example (for the line width) and with the TeXnical method given below. With the following code, the TikZ calc and positioning libraries are not needed at all.

\documentclass{article}
\usepackage[papersize={85mm, 15mm}]{geometry}
\usepackage{xcolor}
\usepackage{tikz}

\pagestyle{empty}

\tikzset{
my special node/.style n args={4}{
line width=0.3mm, draw=black, fill=black, text=white,
inner sep=0pt, text depth=0pt, anchor=south west, font=\normalfont\bfseries,
minimum width={(#3)-(#1)}, minimum height={(#4)-(#2)},
}
}

\begin{document}

\newcommand{\textbox}[5]{%
\begin{tikzpicture}[remember picture, overlay]
\node[my special node={#1}{#2}{#3}{#4}]
at ([xshift={(#1)-0.5\pgflinewidth}, yshift={(#2)-0.5\pgflinewidth}]%
current page.south west) {#5};
\end{tikzpicture}%
}

\textbox{10.0mm}{5mm}{40.0mm}{10.0mm}{Levels}
\textbox{45.0mm}{5mm}{75.0mm}{10.0mm}{Gauging}

\end{document}


Cropped screenshot:

Proof that both texts have the same baseline:

One nice thing when your rectangles are TikZ nodes is that you can use many TikZ tools to work with them: name nodes, place nodes relatively to other ones, connect nodes, drop shadows, etc. For instance, with the above code, you only need to add \usetikzlibrary{shadows} and the drop shadow node option to obtain this:

## Using the rectangle operation

@Schrödinger'scat proposed another nice TikZy solution that is very close to your code and produces the exact same output as my two methods given above and below. This method uses the rectangle operation to draw the black rectangles, as opposed to filling the rectangle nodes containing the texts from the fifth argument of \textbox.

\documentclass{article}
\usepackage[papersize={85mm, 15mm}]{geometry}
\usepackage{xcolor}
\usepackage{tikz}

\pagestyle{empty}

\begin{document}

\newcommand*{\textbox}[5]{%
\begin{tikzpicture}[remember picture, overlay]
\filldraw [fill=black, draw=black, line width=0.3mm]
(current page.south west)+(#1,#2) rectangle +(#3,#4)
node[pos=0.5, font=\normalfont\bfseries, text depth=0pt, text=white] {#5};
\end{tikzpicture}%
}

\textbox{10.0mm}{5mm}{40.0mm}{10.0mm}{Levels}
\textbox{45.0mm}{5mm}{75.0mm}{10.0mm}{Gauging}

\end{document}


The output is exactly the same as above.

# TeXnical way

Here, instead of using text depth=0pt for the nodes containing the boxed texts, we use a short macro that puts its argument in a box, sets the box depth to zero and outputs the box. This implies that again, TeX will ignore the depth of the argument when determining the vertical extent of the TikZ node contents.

\documentclass{article}
\usepackage[papersize={85mm, 15mm}]{geometry}
\usepackage{xcolor}
\usepackage{tikz}
\usetikzlibrary{calc}

\pagestyle{empty}

\newsavebox{\mybox}

\newcommand*{\killboxdepth}[1]{%
\sbox{\mybox}{#1}%
\dp\mybox=0pt \box\mybox
}

\begin{document}

\newcommand*{\textbox}[5]{%
\begin{tikzpicture}[remember picture, overlay]
\filldraw [fill=black, draw=black, line width=0.3mm]
($(current page.south west)+(#1,#2)$) rectangle
($(current page.south west)+(#3,#4)$)
node[pos=0.5, font=\normalfont\bfseries] {\color{white}\killboxdepth{#5}};
\end{tikzpicture}%
}

\textbox{10.0mm}{5mm}{40.0mm}{10.0mm}{Levels}
\textbox{45.0mm}{5mm}{75.0mm}{10.0mm}{Gauging}

\end{document}


The output is exactly the same as in the TikZy ways.

Note: another way to define \killboxdepth is the following:

\newcommand{\killboxdepth}{%
\raisebox{0pt}[\height][0pt]%
}


This is a bit more LaTeXish than using the \dp and \box commands, which are TeX primitives. As @barbarabeeton pointed out, yet another way—assuming amsmath is loaded—would be to use \smash[b] instead of \killboxdepth (then this macro isn't necessary). There is definitely more than one way to do it!

• This is it and it's perfect! Many thanks for the solution, frougon. I don't think a different strategy is needed. Jan 25, 2020 at 11:42
• @Hansel Glad to hear that. :-) I added a method that is more TikZy and gives exactly the same output. Jan 25, 2020 at 16:11
• Maybe also add \newcommand*{\textbox}[5]{% \begin{tikzpicture}[remember picture, overlay] \filldraw [fill=black, draw=black, line width=0.3mm] (current page.south west)+(#1,#2) rectangle +(#3,#4) node[pos=0.5, font=\normalfont\bfseries,text depth=0pt,text=white] {#5}; \end{tikzpicture}% } .
– user194703
Jan 25, 2020 at 17:39
• @Schrödinger'scat Thanks for your suggestion! I've incorporated your solution too. I had tried with text=white when working on the chronologically first version—the TeXnical one—and it didn't work (I guess the \sbox doesn't see the text=white?). But it does work with your code and also with my TikZy method, which doesn't use \sbox. Jan 25, 2020 at 18:13
• Thanks a lot for the other options and especially for the addition of a detailed description. I am sure this will help others facing the same issue in the future. Jan 25, 2020 at 18:26