5

Is there a way to have tikz ignore the white space produced by a node's inner sep when calculating the picture's bounding box?

Consider the following MWE:

\documentclass{article}

\usepackage[showframe,pass]{geometry}
\usepackage{tikz}

\begin{document}

\noindent \begin{tikzpicture}
\node[inner sep=0pt] (X) {X};
\draw (X) -- ++ (0.5, 0);
\draw[gray] (current bounding box.south west) rectangle (current bounding box.north east);
\end{tikzpicture}

\noindent X

\noindent \begin{tikzpicture}
\node[inner sep=2pt] (X) {X};
\draw (X) -- ++ (0.5, 0);
\draw[gray] (current bounding box.south west) rectangle (current bounding box.north east);
\end{tikzpicture}

\end{document}

Here's the output:

enter image description here

In this example, the inner sep is useful when drawing lines from/to the node (see the horizontal line in the example), but on the other side, it indents the second X with regard to the normal text. Some way of specifying the bounding box as the smallest rectangle containing all "ink" (and thereby clipping all inner seps and possibly other white spaces that needlessly extend the bounding box) would be great here...

rainer
  • 2,077

2 Answers2

5

This solution does not really ignore inner sep for bounding box calculation, but might give the desired result in many cases.

It seems that the value of outer sep does not influence the bounding box. So using outer sep instead and setting inner sep = 0 does the trick.

This approach does even work partially for other shapes. In this case a negative inner sep (or inner xsep or inner ysep) may help with quick and dirty fine tuning.

\documentclass[border = 1, varwidth]{standalone}
\usepackage{tikz}
\begin{document}
    \begin{tikzpicture}
        \node[inner sep = 0, outer sep = 0.3333em] (X) {X};
        \foreach \a in {0, 5, ..., 90} \draw (X) -- ++(\a:2em);
        \fill[black, opacity = 0.25] (current bounding box.south west)
                           rectangle (current bounding box.north east);
    \end{tikzpicture}
    \begin{tikzpicture}
        \node[inner sep = 0em, outer sep = 0.3333em, circle] (X) {X};
        \foreach \a in {0, 5, ..., 90} \draw (X) -- ++(\a:2em);
        \fill[black, opacity = 0.25] (current bounding box.south west)
                           rectangle (current bounding box.north east);
    \end{tikzpicture}

    \pgfkeysvalueof{/pgf/inner xsep}
\end{document}

enter image description here

I have printed out the default value for inner sep for reference.

3

I like krnk's answer, which was exactly what I was looking for.

The amount of code copied from TikZ can be reduced by using the shape inheritance mechanism (which may not have been available at the time of the original answer, I don't know).

I also changed the background path so that the original rectangle is created without updating the bounding box. This makes it possible to fill the rectangle white, for instance.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{backgrounds}

\makeatletter
\pgfdeclareshape{tight}{
    % inherit as much stuff as possible from the rectangle shape
    \inheritsavedanchors[from=rectangle]
    \foreach \x in {east,west} \foreach \y in {north,mid,base,south} {
        \inheritanchor[from=rectangle]{\y\space\x}
    }
    \foreach \x in {east,west,north,mid,base,south,center,text} {
        \inheritanchor[from=rectangle]{\x}
    }
    \inheritanchorborder[from=rectangle]
    \inheritbackgroundpath[from=rectangle]

    \savedanchor\tightnortheast{%
        \pgfpoint{\wd\pgfnodeparttextbox}
                 {\ht\pgfnodeparttextbox}%
    }

    \savedanchor\tightsouthwest{%
        \pgfpoint{0pt}
                 {-\dp\pgfnodeparttextbox}%
    }

    \backgroundpath{%
        % move to each of the tight corners, updating the bounding box
        \pgfpathmoveto\tightsouthwest
        \pgfpathmoveto\tightnortheast
        \pgfpathmoveto{\tightsouthwest\pgf@xa=\pgf@x\tightnortheast\pgf@x=\pgf@xa}%
        \pgfpathmoveto{\tightsouthwest\pgf@ya=\pgf@y\tightnortheast\pgf@y=\pgf@ya}%

        % draw the usual recangle border without affecting the bounding box
        \pgf@relevantforpicturesizefalse
        \pgfpathrectanglecorners\southwest
                                \northeast
        % this is executed in a grop, so bounding box tracking automatically
        % gets re-enabled if it was enabled to begin with
    }
}
\makeatother

\begin{document}
    \foreach \shape in {rectangle, tight} {
        \par shape = \shape:\par
        \begin{tikzpicture}[framed,tight background]
            \node[\shape] at (0,0) (x) {x};
            \node[\shape] at (1,0) (y) {y};

            \draw (x) -- (y);
        \end{tikzpicture}
    }
\end{document}

output

wrtlprnft
  • 3,859