# Bounding box is larger than expected when drawing a curved path

I have been working on logo for my department and I have the following code. The problem is that whenever I compile the diagram seems to be larger than the actual logo. I do not know what is the problem. I know the numbers I have selected are not the best but any insights into the matter will be highly appreciated.

 \documentclass[letterpaper]{article}
\usepackage[top=2cm,bottom=2cm,left=2cm,right=2cm]{geometry}
%\usepackage{amsmath,amssymb,units}
%\usepackage{enumitem,multicol}
\usepackage{tikz}
%\usetikzlibrary{arrows}
\usepackage{lipsum}

\begin{document}

\lipsum[1-2]
\begin{tikzpicture}[y=0.80pt, x=0.8pt,yscale=-1]
\path[draw=black,fill=white]
(258.9527,290.5199) .. controls (173.9885,538.4766) and (568.5860,261.2969) ..
(306.5098,257.1141) .. controls (44.4337,252.9312) and (429.9845,542.5624) ..
(352.9767,292.0206) .. controls (275.9689,41.4788) and (119.6549,497.6604) ..
(334.1376,346.9999) .. controls (548.6203,196.3394) and (66.4622,188.6439) ..
(276.0276,346.0724) .. controls (485.5930,503.5010) and (343.9169,42.5633) ..
(258.9527,290.5199) -- cycle;
\end{tikzpicture}
\lipsum[1-2]
\end{document}


See image below:

• Do you mean that the bounding box of the diagram is much larger than the diagram itself? Feb 6, 2012 at 20:24
• Yes, my word choice was not the most appropriate but yes that is what I am asking Feb 6, 2012 at 20:29
• I took the liberty to edit the title and to add a tag. Feb 6, 2012 at 20:39
• @user946850 Thanks for the edit. That seems like a better title for the question. Feb 6, 2012 at 20:43
• @azetina: Cool logo - How did you draw it? Feb 6, 2012 at 23:06

Update : before to use a grid it's possible to use pdfcrop to get the dimensions of the "real" picture. You need to use \thispagestyle{empty} and you need to compile only the picture. You get a pdf file then with pdfcrop you get a new pdf file. Inside this pdf, you can read /BBox [0 0 146.908 142.991] (be careful with the units). We don't have the origin but we get the dimensions. pdfcrop can also give a pdf file that you can include with a correct bounding box.

Manually : With a grid

\documentclass[letterpaper]{article}
\usepackage{tikz}

\begin{document}

\begin{tikzpicture}[y=0.80pt, x=0.8pt,yscale=-1]
\draw[help lines,step=8pt] (208,208) grid (400, 400);
\clip (208,208) rectangle (400, 400);
\path[draw=black]
(258.9527,290.5199) .. controls (173.9885,538.4766) and (568.5860,261.2969) ..
(306.5098,257.1141) .. controls (44.4337,252.9312) and (429.9845,542.5624) ..
(352.9767,292.0206) .. controls (275.9689,41.4788) and (119.6549,497.6604) ..
(334.1376,346.9999) .. controls (548.6203,196.3394) and (66.4622,188.6439) ..
(276.0276,346.0724) .. controls (485.5930,503.5010) and (343.9169,42.5633) ..
(258.9527,290.5199) -- cycle;
\end{tikzpicture}
\begin{tikzpicture}[y=0.80pt, x=0.8pt,yscale=-1]
\path[draw=black] (213,215) rectangle (398, 395);
\clip (213,215) rectangle (398, 395);
\path[draw=black]
(258.9527,290.5199) .. controls (173.9885,538.4766) and (568.5860,261.2969) ..
(306.5098,257.1141) .. controls (44.4337,252.9312)  and (429.9845,542.5624) ..
(352.9767,292.0206) .. controls (275.9689,41.4788)  and (119.6549,497.6604) ..
(334.1376,346.9999) .. controls (548.6203,196.3394) and (66.4622,188.6439) ..
(276.0276,346.0724) .. controls (485.5930,503.5010) and (343.9169,42.5633) ..
(258.9527,290.5199) -- cycle;
\end{tikzpicture}
\end{document}


• Nice use of a grid to work out what the size should be. Definitely the most practical solution. Feb 7, 2012 at 9:46
• I like the pdfcrop idea, I actually do that quite a lot. And +1 for the grid supports as well!
– yo'
Feb 7, 2012 at 10:31
• @altermundus Thanks for providing a substantial solution. Feb 7, 2012 at 15:30

As seen in the answer by Peter Grill, the size of the bounding box is determined not only by the path points, but also by the control points. In order to reduce the size of the bounding box, we have to specify it explicitly.

The manual states:

PGF is reasonably good at keeping track of the size of your picture and reserving just the right amount of space for it in the main document. However, in some cases you may want to say things like “do not count this for the picture size” or “the picture is actually a little large.” For this you can use the option use as bounding box or the command \useasboundingbox, which is just a shorthand for \path[use as bounding box].

And especially with respect to curved lines:

... Controls points of a curve often lie far “outside” the curve and make the bounding box too large. In this case, you should use the [use as bounding box] option.

As a quick fix, you could add something like the following into your tikzpicture environment before drawing:

 \path[use as bounding box] (220, 200) rectangle (400, 400);


For more precise calculation, find points that will define the convex hull of your logo with sufficient precision and use a polygon as bounding box -- this works equally well.

Here is the result of the quick fix:

• The bounding box is good but is it possible to edit the logo to get the same effect? Just wondering. Feb 6, 2012 at 20:50
• If you used only the path points for computing the bounding box, the result would be equally wrong. (This is what I tried first.) TikZ would have to do rather difficult computations in order to get it right. Feb 6, 2012 at 21:01

This is not an answer, but thought it might be useful to see why this is happening as @user946850 points out. I added the following to the code to see where the control points are:

\foreach \x in {{(173.9885,538.4766)}, {(568.5860,261.2969)}, {(44.4337,252.9312)},
{(429.9845,542.5624)}, {(275.9689,41.4788)}, {(119.6549,497.6604)}, {(548.6203,196.3394)}, {(66.4622,188.6439)}, {(485.5930,503.5010)}, {(343.9169,42.5633)}} {
\node [fill=red,shape=circle] at \x {};
};


Another way to see the bounding box is to apply the following at the end of the picture:

\draw [blue] (current bounding box.south west) rectangle (current bounding box.north east);


• I suppose that is what is causing the bounding box to be so large. Is this customizable other than adding the bounding box as user946850 suggests. Feb 6, 2012 at 20:44
• Thanks for the effort. I was just too lazy to figure out how to iterate over coordinates :-) Feb 6, 2012 at 20:47
• Is there a simple general way to exclude control points from a bounding box before a picture is drawn? May 27, 2012 at 12:57

I added an experimental library bbox which computes the bounding box for curves. I tested it and it seems to work unless the curve has very steep angles, in which case there might be dimension too large errors. However, in this example and all reasonable'' cases it seems to work.

\documentclass[letterpaper]{article}
\usepackage[top=2cm,bottom=2cm,left=2cm,right=2cm]{geometry}
\usepackage{tikz}
\usetikzlibrary{bbox}

\begin{document}
\begin{figure}
\centering
\begin{tikzpicture}[y=0.80pt, x=0.8pt,yscale=-1]
\path[draw=black,fill=white]
(258.9527,290.5199) .. controls (173.9885,538.4766) and (568.5860,261.2969) ..
(306.5098,257.1141) .. controls (44.4337,252.9312) and (429.9845,542.5624) ..
(352.9767,292.0206) .. controls (275.9689,41.4788) and (119.6549,497.6604) ..
(334.1376,346.9999) .. controls (548.6203,196.3394) and (66.4622,188.6439) ..
(276.0276,346.0724) .. controls (485.5930,503.5010) and (343.9169,42.5633) ..
(258.9527,290.5199) -- cycle;
\draw (current bounding box.south west) rectangle  (current bounding box.north
east);
\end{tikzpicture}
\caption{Default.}
\end{figure}
\begin{figure}
\centering
\begin{tikzpicture}[y=0.80pt, x=0.8pt,yscale=-1,bezier bounding box]
\path[draw=black,fill=white]
(258.9527,290.5199) .. controls (173.9885,538.4766) and (568.5860,261.2969) ..
(306.5098,257.1141) .. controls (44.4337,252.9312) and (429.9845,542.5624) ..
(352.9767,292.0206) .. controls (275.9689,41.4788) and (119.6549,497.6604) ..
(334.1376,346.9999) .. controls (548.6203,196.3394) and (66.4622,188.6439) ..
(276.0276,346.0724) .. controls (485.5930,503.5010) and (343.9169,42.5633) ..
(258.9527,290.5199) -- cycle;
\draw (current bounding box.south west) rectangle  (current bounding box.north
east);
\end{tikzpicture}
\caption{With \texttt{bezier bounding box} from the \texttt{bbox} library
switched on.}
\end{figure}
\end{document}


The theory behind this is very simple. The TeX code for the following can be found under this link.

For those who do not want to follow external links: this is the code of the library:

\tikzset{%
bezier bounding box/.is choice,%
bezier bounding box/.default=true,%
bezier bounding box/true/.code=\tikzset{switch on bezier bounding box},%
bezier bounding box/false/.code=\tikzset{switch off bezier bounding box}}%
\tikzset{switch off bezier bounding box/.code={%
\def\pgf@lt@curveto##1##2##3##4##5##6{%
\pgf@protocolsizes{##1}{##2}%
\pgf@protocolsizes{##3}{##4}%
\pgf@protocolsizes{##5}{##6}%
\pgfsyssoftpath@curveto{\the##1}{\the##2}{\the##3}{\the##4}{\the##5}{\the##6}%
}%
\let\pgf@nlt@curveto\pgf@lt@curveto}}
%
% it might just be me but according to what I believe to find
% \pgfmathsetlengthmacro appears to generate spaces
%
\tikzset{switch on bezier bounding box/.code={%
\def\pgf@lt@curveto##1##2##3##4##5##6{%
% extrema in x
\pgfmathsetmacro{\pgf@temp@b}{abs(\pgf@path@lastx-##5-3*##1+3*##3)}%
% ^^^ this is used for the denominator below, cannot become too small
\pgfmathsetmacro{\pgf@temp@c}{max(1+\pgf@path@lastx,max(##1,max(##3,##5)))}%
% ^^^ in order to avoid dimension too large errors from squaring lengths in pt
\pgfmathparse{((##1/\pgf@temp@c)*(##1/\pgf@temp@c)-1*((##1/\pgf@temp@c)*(##3/\pgf@temp@c))+(##3/\pgf@temp@c)*(##3/\pgf@temp@c)-1*((##1/\pgf@temp@c)*(##5/\pgf@temp@c))+(-(##3/\pgf@temp@c)+(##5/\pgf@temp@c))*(\pgf@path@lastx/\pgf@temp@c))}%
\pgfutil@tempdima=\pgfmathresult pt\relax%
% ^^^ discriminant
\ifdim\pgf@temp@b pt<0.01pt\relax%
% approximately linear
\pgfmathparse{abs(2*(##1)-2*(##3)+(##5))}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\ifdim\pgfutil@tempdimb<0.1pt\relax%
% if the denominator is very small, it is *likely* large but could be 0/0
\else
\pgfmathsetmacro{\pgf@temp@a}{(2*(##1)-3*(##3)+(##5))/(2*(##1)-2*(##3)+(##5))}%
\pgfmathparse{\pgf@path@lastx*pow(1-\pgf@temp@a,3)+3*##1*pow(1-\pgf@temp@a,2)*\pgf@temp@a+3*##3*(1-\pgf@temp@a)*\pgf@temp@a*\pgf@temp@a+##5*pow(\pgf@temp@a,3)}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\pgf@protocolsizes{\pgfutil@tempdimb}{##6}%
\fi%
\else
\ifdim\pgfutil@tempdima<0pt\relax% negative discriminant -> no turning point
\else
\pgfmathsetmacro{\pgf@temp@a}{min(1,max(0,(\pgf@path@lastx-2*##1+##3-\pgf@temp@c*sqrt(\pgfutil@tempdima))/(\pgf@path@lastx-##5-3*##1+3*##3)))}%
\pgfmathparse{\pgf@path@lastx*pow(1-\pgf@temp@a,3)+3*##1*pow(1-\pgf@temp@a,2)*\pgf@temp@a+3*##3*(1-\pgf@temp@a)*\pgf@temp@a*\pgf@temp@a+##5*pow(\pgf@temp@a,3)}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\pgf@protocolsizes{\pgfutil@tempdimb}{##6}%
\pgfmathsetmacro{\pgf@temp@a}{min(1,max(0,(\pgf@path@lastx-2*##1+##3+\pgf@temp@c*sqrt(\pgfutil@tempdima))/(\pgf@path@lastx-##5-3*##1+3*##3)))}%
\pgfmathparse{\pgf@path@lastx*pow(1-\pgf@temp@a,3)+3*##1*pow(1-\pgf@temp@a,2)*\pgf@temp@a+3*##3*(1-\pgf@temp@a)*\pgf@temp@a*\pgf@temp@a+##5*pow(\pgf@temp@a,3)}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\pgf@protocolsizes{\pgfutil@tempdimb}{##6}%
\fi%
\fi
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% extrema in y (completely analogous to the above)
\pgfmathsetmacro{\pgf@temp@b}{abs(\pgf@path@lasty-##6-3*##2+3*##4)}%
\pgfmathsetmacro{\pgf@temp@c}{max(1+\pgf@path@lasty,max(##2,max(##4,##6)))}%
\pgfmathparse{((##2/\pgf@temp@c)*(##2/\pgf@temp@c)-1*((##2/\pgf@temp@c)*(##4/\pgf@temp@c))+(##4/\pgf@temp@c)*(##4/\pgf@temp@c)-1*((##2/\pgf@temp@c)*(##6/\pgf@temp@c))+(-(##4/\pgf@temp@c)+(##6/\pgf@temp@c))*(\pgf@path@lasty/\pgf@temp@c))}%
\pgfutil@tempdima=\pgfmathresult pt\relax%
% ^^^ discriminant
\ifdim\pgf@temp@b pt<0.01pt\relax%
% approximately linear
\pgfmathparse{abs(2*(##2)-2*(##4)+(##6))}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\ifdim\pgfutil@tempdimb<0.1pt\relax%
% if the denominator is very small, it is *likely* large but could be 0/0
\else
\pgfmathsetmacro{\pgf@temp@a}{(2*(##2)-3*(##4)+(##6))/(2*(##2)-2*(##4)+(##6))}%
\pgfmathparse{\pgf@path@lasty*pow(1-\pgf@temp@a,3)+3*##2*pow(1-\pgf@temp@a,2)*\pgf@temp@a+3*##4*(1-\pgf@temp@a)*\pgf@temp@a*\pgf@temp@a+##6*pow(\pgf@temp@a,3)}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\pgf@protocolsizes{##5}{\pgfutil@tempdimb}%
\fi%
\else
\ifdim\pgfutil@tempdima<0pt\relax% negative discriminant -> no turning point
\else
\pgfmathsetmacro{\pgf@temp@a}{min(1,max(0,(\pgf@path@lasty-2*##2+##4-\pgf@temp@c*sqrt(\pgfutil@tempdima))/(\pgf@path@lasty-##6-3*##2+3*##4)))}%
\pgfmathparse{\pgf@path@lasty*pow(1-\pgf@temp@a,3)+3*##2*pow(1-\pgf@temp@a,2)*\pgf@temp@a+3*##4*(1-\pgf@temp@a)*\pgf@temp@a*\pgf@temp@a+##6*pow(\pgf@temp@a,3)}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\pgf@protocolsizes{##5}{\pgfutil@tempdimb}%
\pgfmathsetmacro{\pgf@temp@a}{min(1,max(0,(\pgf@path@lasty-2*##2+##4+\pgf@temp@c*sqrt(\pgfutil@tempdima))/(\pgf@path@lasty-##6-3*##2+3*##4)))}%
\pgfmathparse{\pgf@path@lasty*pow(1-\pgf@temp@a,3)+3*##2*pow(1-\pgf@temp@a,2)*\pgf@temp@a+3*##4*(1-\pgf@temp@a)*\pgf@temp@a*\pgf@temp@a+##6*pow(\pgf@temp@a,3)}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\pgf@protocolsizes{##5}{\pgfutil@tempdimb}%
\fi%
\fi
\pgf@protocolsizes{\pgf@path@lastx}{\pgf@path@lasty}%
\pgf@protocolsizes{##5}{##6}%
\pgfsyssoftpath@curveto{\the##1}{\the##2}{\the##3}{\the##4}{\the##5}{\the##6}%
}
\let\pgf@nlt@curveto\pgf@lt@curveto}}% fix me: 0/0 cases and occasional
% dimension too large errors (what's the cause?)


If you do not want do load the library, you may just copy the code and sandwich it between \makeatletter and \makeatother.

If you encounter dimension too large errors, you may want to use

\usetikzlibrary{fpu}
\newcommand{\pgfmathsetmacroFPU}[2]{\begingroup%
\pgfkeys{/pgf/fpu,/pgf/fpu/output format=fixed}%
\pgfmathsetmacro{#1}{#2}%
\pgfmathsmuggle#1\endgroup}
\newcommand{\pgfmathparseFPU}[1]{\begingroup%
\pgfkeys{/pgf/fpu,/pgf/fpu/output format=fixed}%
\pgfmathparse{#1}%
\pgfmathsmuggle\pgfmathresult\endgroup}
\tikzset{%
bezier bounding box/.is choice,%
bezier bounding box/.default=true,%
bezier bounding box/true/.code=\tikzset{switch on bezier bounding box},%
bezier bounding box/false/.code=\tikzset{switch off bezier bounding box}}%
\tikzset{switch off bezier bounding box/.code={%
\def\pgf@lt@curveto##1##2##3##4##5##6{%
\pgf@protocolsizes{##1}{##2}%
\pgf@protocolsizes{##3}{##4}%
\pgf@protocolsizes{##5}{##6}%
\pgfsyssoftpath@curveto{\the##1}{\the##2}{\the##3}{\the##4}{\the##5}{\the##6}%
}%
\let\pgf@nlt@curveto\pgf@lt@curveto}}
%
% it might just be me but according to what I believe to find
% \pgfmathsetlengthmacro appears to generate spaces
%
\tikzset{switch on bezier bounding box/.code={%
\def\pgf@lt@curveto##1##2##3##4##5##6{%
% extrema in x
\pgfmathsetmacroFPU{\pgf@temp@b}{abs(\pgf@path@lastx-##5-3*##1+3*##3)}%
% ^^^ this is used for the denominator below, cannot become too small
\pgfmathsetmacroFPU{\pgf@temp@c}{max(1+\pgf@path@lastx,max(##1,max(##3,##5)))}%
% ^^^ in order to avoid dimension too large errors from squaring lengths in pt
\pgfmathparseFPU{((##1/\pgf@temp@c)*(##1/\pgf@temp@c)-1*((##1/\pgf@temp@c)*(##3/\pgf@temp@c))+(##3/\pgf@temp@c)*(##3/\pgf@temp@c)-1*((##1/\pgf@temp@c)*(##5/\pgf@temp@c))+(-(##3/\pgf@temp@c)+(##5/\pgf@temp@c))*(\pgf@path@lastx/\pgf@temp@c))}%
\pgfutil@tempdima=\pgfmathresult pt\relax%
% ^^^ discriminant
\ifdim\pgf@temp@b pt<0.01pt\relax%
% approximately linear
\pgfmathparseFPU{abs(2*(##1)-2*(##3)+(##5))}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\ifdim\pgfutil@tempdimb<0.1pt\relax%
% if the denominator is very small, t is *likely* large but could be 0/0
\else
\pgfmathsetmacroFPU{\pgf@temp@a}{(2*(##1)-3*(##3)+(##5))/(2*(##1)-2*(##3)+(##5))}%
\pgfmathparseFPU{\pgf@path@lastx*pow(1-\pgf@temp@a,3)+3*##1*pow(1-\pgf@temp@a,2)*\pgf@temp@a+3*##3*(1-\pgf@temp@a)*\pgf@temp@a*\pgf@temp@a+##5*pow(\pgf@temp@a,3)}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\pgf@protocolsizes{\pgfutil@tempdimb}{##6}%
\fi%
\else
\ifdim\pgfutil@tempdima<0pt\relax% negative discriminant -> no turning point
\else
\pgfmathsetmacroFPU{\pgf@temp@a}{min(1,max(0,(\pgf@path@lastx-2*##1+##3-\pgf@temp@c*sqrt(\pgfutil@tempdima))/(\pgf@path@lastx-##5-3*##1+3*##3)))}%
\pgfmathparseFPU{\pgf@path@lastx*pow(1-\pgf@temp@a,3)+3*##1*pow(1-\pgf@temp@a,2)*\pgf@temp@a+3*##3*(1-\pgf@temp@a)*\pgf@temp@a*\pgf@temp@a+##5*pow(\pgf@temp@a,3)}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\pgf@protocolsizes{\pgfutil@tempdimb}{##6}%
\pgfmathsetmacroFPU{\pgf@temp@a}{min(1,max(0,(\pgf@path@lastx-2*##1+##3+\pgf@temp@c*sqrt(\pgfutil@tempdima))/(\pgf@path@lastx-##5-3*##1+3*##3)))}%
\pgfmathparseFPU{\pgf@path@lastx*pow(1-\pgf@temp@a,3)+3*##1*pow(1-\pgf@temp@a,2)*\pgf@temp@a+3*##3*(1-\pgf@temp@a)*\pgf@temp@a*\pgf@temp@a+##5*pow(\pgf@temp@a,3)}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\pgf@protocolsizes{\pgfutil@tempdimb}{##6}%
\fi%
\fi
%%%%%%%%%%%%%%%%%%%%%%%%%%%
% extrema in y (completely analogous to the above)
\pgfmathsetmacroFPU{\pgf@temp@b}{abs(\pgf@path@lasty-##6-3*##2+3*##4)}%
\pgfmathsetmacroFPU{\pgf@temp@c}{max(1+\pgf@path@lasty,max(##2,max(##4,##6)))}%
\pgfmathparseFPU{((##2/\pgf@temp@c)*(##2/\pgf@temp@c)-1*((##2/\pgf@temp@c)*(##4/\pgf@temp@c))+(##4/\pgf@temp@c)*(##4/\pgf@temp@c)-1*((##2/\pgf@temp@c)*(##6/\pgf@temp@c))+(-(##4/\pgf@temp@c)+(##6/\pgf@temp@c))*(\pgf@path@lasty/\pgf@temp@c))}%
\pgfutil@tempdima=\pgfmathresult pt\relax%
% ^^^ discriminant
\ifdim\pgf@temp@b pt<0.01pt\relax%
% approximately linear
\pgfmathparseFPU{abs(2*(##2)-2*(##4)+(##6))}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\ifdim\pgfutil@tempdimb<0.1pt\relax%
% if the denominator is very small, t is *likely* large but could be 0/0
\else
\pgfmathsetmacroFPU{\pgf@temp@a}{(2*(##2)-3*(##4)+(##6))/(2*(##2)-2*(##4)+(##6))}%
\pgfmathparseFPU{\pgf@path@lasty*pow(1-\pgf@temp@a,3)+3*##2*pow(1-\pgf@temp@a,2)*\pgf@temp@a+3*##4*(1-\pgf@temp@a)*\pgf@temp@a*\pgf@temp@a+##6*pow(\pgf@temp@a,3)}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\pgf@protocolsizes{##5}{\pgfutil@tempdimb}%
\fi%
\else
\ifdim\pgfutil@tempdima<0pt\relax% negative discriminant -> no turning point
\else
\pgfmathsetmacroFPU{\pgf@temp@a}{min(1,max(0,(\pgf@path@lasty-2*##2+##4-\pgf@temp@c*sqrt(\pgfutil@tempdima))/(\pgf@path@lasty-##6-3*##2+3*##4)))}%
\pgfmathparseFPU{\pgf@path@lasty*pow(1-\pgf@temp@a,3)+3*##2*pow(1-\pgf@temp@a,2)*\pgf@temp@a+3*##4*(1-\pgf@temp@a)*\pgf@temp@a*\pgf@temp@a+##6*pow(\pgf@temp@a,3)}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\pgf@protocolsizes{##5}{\pgfutil@tempdimb}%
\pgfmathsetmacroFPU{\pgf@temp@a}{min(1,max(0,(\pgf@path@lasty-2*##2+##4+\pgf@temp@c*sqrt(\pgfutil@tempdima))/(\pgf@path@lasty-##6-3*##2+3*##4)))}%
\pgfmathparseFPU{\pgf@path@lasty*pow(1-\pgf@temp@a,3)+3*##2*pow(1-\pgf@temp@a,2)*\pgf@temp@a+3*##4*(1-\pgf@temp@a)*\pgf@temp@a*\pgf@temp@a+##6*pow(\pgf@temp@a,3)}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\pgf@protocolsizes{##5}{\pgfutil@tempdimb}%
\fi%
\fi
\pgf@protocolsizes{\pgf@path@lastx}{\pgf@path@lasty}%
\pgf@protocolsizes{##5}{##6}%
\pgfsyssoftpath@curveto{\the##1}{\the##2}{\the##3}{\the##4}{\the##5}{\the##6}%
}
\let\pgf@nlt@curveto\pgf@lt@curveto}}
\endinput


instead. This is even slower than the above but has less problems with the dimension too large errors.

• Wow! Good job! Nice piece work. Jul 24, 2019 at 15:53
• Such a nice contribution! in my opinion, the next version of TikZ/PGF should improve right-bounding-box problem when drawing Bezier curves Sep 27, 2019 at 3:49
• @BlackMild It does.
– user194703
Dec 31, 2019 at 7:58

clip the figure before drawing

\documentclass[letterpaper]{article}
\usepackage[top=2cm,bottom=2cm,left=2cm,right=2cm]{geometry}
%\usepackage{amsmath,amssymb,units}
%\usepackage{enumitem,multicol}
\usepackage{tikz}
%\usetikzlibrary{arrows}
\usepackage{lipsum}

\begin{document}

\lipsum[1-2]
\begin{tikzpicture}[y=0.80pt, x=0.8pt,yscale=-1]
\clip[draw](305,305) circle (100);
\path[draw=red,fill=white]
(258.9527,290.5199) .. controls (173.9885,538.4766) and (568.5860,261.2969) ..
(306.5098,257.1141) .. controls (44.4337,252.9312) and (429.9845,542.5624) ..
(352.9767,292.0206) .. controls (275.9689,41.4788) and (119.6549,497.6604) ..
(334.1376,346.9999) .. controls (548.6203,196.3394) and (66.4622,188.6439) ..
(276.0276,346.0724) .. controls (485.5930,503.5010) and (343.9169,42.5633) ..
(258.9527,290.5199) -- cycle;
\end{tikzpicture}
\lipsum[1-2]
\end{document}


There is/was a library taking care of this. The first version of the library was incorporated in pgf without my approval, and had bugs. Some bugs were pointed out on the GitHub site, and fixed. In particular this issue was a very nice issue report which pointed out some nontrivial problems. I hadn't appreciated that arrow heads deform a path after it got reported to the bounding box protocol (unless one loads bending). Henri Menke has decided to take the library out of pgf.1 Because of the use of strong language and inappropriate mocking in a chat room I have decided to leave the chat and to delete my GitHub account, the more so since I never really got used to that repository.

However, the good news is that you can still use the library. Save the following under pgflibrarybbox.code.tex:

% Copyright 2020 by an anonymous contributor
%
% This file may be distributed and/or modified
%
% 1. under the LaTeX Project Public License and/or
% 2. under the GNU General Public License.
%

\usepgflibrary{fpu}
\global\let\pgf@bbox@lt@curveto@normal\pgf@lt@curveto
\global\let\pgf@bbox@nlt@curveto@normal\pgf@nlt@curveto

\pgfqkeys{/pgf}{bezier bounding box/.is if=pgf@bbox@switch@}

\pgfqkeys{/pgf}{use fpu reciprocal/.code={%
\def\pgfmathreciprocal@##1{%
\begingroup
\pgfkeys{/pgf/fpu=true,/pgf/fpu/output format=fixed}%
\pgfmathparse{1/##1}%
\pgfmath@smuggleone\pgfmathresult
\endgroup
}}}%

\def\pgf@bbox@switch@false{%
\let\pgf@lt@curveto \pgf@bbox@lt@curveto@normal
\let\pgf@nlt@curveto\pgf@bbox@nlt@curveto@normal
}

\def\pgf@bbox@switch@true{%
\let\pgf@lt@curveto \pgf@bbox@curveto
\let\pgf@nlt@curveto\pgf@bbox@curveto
}
\def\pgf@bbox@curveto#1#2#3#4#5#6{%
\begingroup
\pgfkeys{/pgf/fpu,/pgf/fpu/output format=fixed}%
% extrema in x
% first discriminant d1, must be \ne 0
\pgfmathsetmacro{\pgf@temp@a}{(\pgf@path@lastx)-(#5)-3*(#1)+3*(#3)}%
\pgfmathtruncatemacro{\pgf@temp@c}{(abs(\pgf@temp@a)>0.1?1:0)}%
\ifnum\pgf@temp@c=1\relax
% second discriminant d2, must be \ge 0
\pgfmathsetmacro{\pgf@temp@b}{(\pgf@path@lastx)*(#5)-(#5)*(#1)+(#1)*(#1)-(\pgf@path@lastx)*(#3)-(#1)*(#3)+(#3)*(#3)}%
\pgfmathtruncatemacro{\pgf@temp@c}{sign(\pgf@temp@b)}%
\ifnum\pgf@temp@c<0
\else
\pgfmathsetmacro{\pgf@temp@b}{sqrt(abs(\pgf@temp@b))}%
\pgfmathsetmacro{\pgf@temp@c}{max(0,min(1,((\pgf@path@lastx)-2*(#1)+(#3)-\pgf@temp@b)/\pgf@temp@a))}%
\pgfmathparse{(\pgf@path@lastx)*pow((1-\pgf@temp@c),3)+3*(#1)*pow((1-\pgf@temp@c),2)*\pgf@temp@c+3*(#3)*(1-\pgf@temp@c)*\pgf@temp@c*\pgf@temp@c+(#5)*\pgf@temp@c*\pgf@temp@c*\pgf@temp@c}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\pgf@protocolsizes{\pgfutil@tempdimb}{\pgf@path@lasty}%
\pgfmathsetmacro{\pgf@temp@c}{max(0,min(1,((\pgf@path@lastx)-2*(#1)+(#3)+\pgf@temp@b)/\pgf@temp@a))}%
\pgfmathparse{(\pgf@path@lastx)*pow((1-\pgf@temp@c),3)+3*(#1)*pow((1-\pgf@temp@c),2)*\pgf@temp@c+3*(#3)*(1-\pgf@temp@c)*\pgf@temp@c*\pgf@temp@c+(#5)*\pgf@temp@c*\pgf@temp@c*\pgf@temp@c}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\pgf@protocolsizes{\pgfutil@tempdimb}{\pgf@path@lasty}%
\fi
\else
% third discriminant d3, must be \ne 0
\pgfmathsetmacro{\pgf@temp@b}{abs((#5)+(#1)-2*(#3))}%
\pgfmathtruncatemacro{\pgf@temp@c}{(abs(\pgf@temp@b)>0.1?1:0)}%
\ifnum\pgf@temp@c=1\relax
\pgfmathsetmacro{\pgf@temp@c}{((#5)+2*(#1)-3*(#3))/((#5)+(#1)-2*(#3))}%
\pgfmathparse{(\pgf@path@lastx)*pow((1-\pgf@temp@c),3)+3*(#1)*pow((1-\pgf@temp@c),2)*\pgf@temp@c+3*(#3)*(1-\pgf@temp@c)*\pgf@temp@c*\pgf@temp@c+(#5)*\pgf@temp@c*\pgf@temp@c*\pgf@temp@c}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\pgf@protocolsizes{\pgfutil@tempdimb}{\pgf@path@lasty}%
\fi
\fi
% 0/0
\pgfmathsetmacro{\pgf@temp@a}{(#5)+(#1)-2*(#3)}%
\pgfmathtruncatemacro{\pgf@temp@b}{(abs(\pgf@temp@a)>0.1?1:0)}%
\ifnum\pgf@temp@b=1\relax
\pgfmathsetmacro{\pgf@temp@c}{max(0,min(1,((#5)+2*(#1)-3*(#3))/(2*\pgf@temp@a)))}%
\else
\pgfmathsetmacro{\pgf@temp@c}{0.5}%
\fi
\pgfmathparse{(\pgf@path@lastx)*pow((1-\pgf@temp@c),3)+3*(#1)*pow((1-\pgf@temp@c),2)*\pgf@temp@c+3*(#3)*(1-\pgf@temp@c)*\pgf@temp@c*\pgf@temp@c+(#5)*\pgf@temp@c*\pgf@temp@c*\pgf@temp@c}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\pgf@protocolsizes{\pgfutil@tempdimb}{\pgf@path@lasty}%
%
% y code
% first discriminant d1, must be \ne 0
\pgfmathsetmacro{\pgf@temp@a}{(\pgf@path@lasty)-(#6)-3*(#2)+3*(#4)}%
\pgfmathtruncatemacro{\pgf@temp@c}{(abs(\pgf@temp@a)>0.1?1:0)}%
\ifnum\pgf@temp@c=1\relax
% second discriminant d2, must be \ge 0
\pgfmathsetmacro{\pgf@temp@b}{(\pgf@path@lasty)*(#6)-(#6)*(#2)+(#2)*(#2)-(\pgf@path@lasty)*(#4)-(#2)*(#4)+(#4)*(#4)}%
\pgfmathtruncatemacro{\pgf@temp@c}{sign(\pgf@temp@b)}%
\ifnum\pgf@temp@c<0
\else
\pgfmathsetmacro{\pgf@temp@b}{sqrt(abs(\pgf@temp@b))}%
\pgfmathsetmacro{\pgf@temp@c}{max(0,min(1,((\pgf@path@lasty)-2*(#2)+(#4)-\pgf@temp@b)/\pgf@temp@a))}%
\pgfmathparse{(\pgf@path@lasty)*pow((1-\pgf@temp@c),3)+3*(#2)*pow((1-\pgf@temp@c),2)*\pgf@temp@c+3*(#4)*(1-\pgf@temp@c)*\pgf@temp@c*\pgf@temp@c+(#6)*\pgf@temp@c*\pgf@temp@c*\pgf@temp@c}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\pgf@protocolsizes{\pgf@path@lastx}{\pgfutil@tempdimb}%
\pgfmathsetmacro{\pgf@temp@c}{max(0,min(1,((\pgf@path@lasty)-2*(#2)+(#4)+\pgf@temp@b)/\pgf@temp@a))}%
\pgfmathparse{(\pgf@path@lasty)*pow((1-\pgf@temp@c),3)+3*(#2)*pow((1-\pgf@temp@c),2)*\pgf@temp@c+3*(#4)*(1-\pgf@temp@c)*\pgf@temp@c*\pgf@temp@c+(#6)*\pgf@temp@c*\pgf@temp@c*\pgf@temp@c}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\pgf@protocolsizes{\pgf@path@lastx}{\pgfutil@tempdimb}%
\fi
\else
% third discriminant d3, must be \ne 0
\pgfmathsetmacro{\pgf@temp@b}{abs((#6)+(#2)-2*(#4))}%
\pgfmathtruncatemacro{\pgf@temp@c}{(abs(\pgf@temp@b)>0.1?1:0)}%
\ifnum\pgf@temp@c=1\relax
\pgfmathsetmacro{\pgf@temp@c}{((#6)+2*(#2)-3*(#4))/((#6)+(#2)-2*(#4))}%
\pgfmathparse{(\pgf@path@lasty)*pow((1-\pgf@temp@c),3)+3*(#2)*pow((1-\pgf@temp@c),2)*\pgf@temp@c+3*(#4)*(1-\pgf@temp@c)*\pgf@temp@c*\pgf@temp@c+(#6)*\pgf@temp@c*\pgf@temp@c*\pgf@temp@c}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\pgf@protocolsizes{\pgf@path@lastx}{\pgfutil@tempdimb}%
\fi
\fi
% 0/0
\pgfmathsetmacro{\pgf@temp@a}{(#6)+(#2)-2*(#4)}%
\pgfmathtruncatemacro{\pgf@temp@b}{(abs(\pgf@temp@a)>0.1?1:0)}%
\ifnum\pgf@temp@b=1\relax
\pgfmathsetmacro{\pgf@temp@c}{max(0,min(1,((#6)+2*(#2)-3*(#4))/(2*\pgf@temp@a)))}%
\else
\pgfmathsetmacro{\pgf@temp@c}{0.5}%
\fi
\pgfmathparse{(\pgf@path@lasty)*pow((1-\pgf@temp@c),3)+3*(#2)*pow((1-\pgf@temp@c),2)*\pgf@temp@c+3*(#4)*(1-\pgf@temp@c)*\pgf@temp@c*\pgf@temp@c+(#6)*\pgf@temp@c*\pgf@temp@c*\pgf@temp@c}%
\pgfutil@tempdimb=\pgfmathresult pt\relax%
\pgf@protocolsizes{\pgf@path@lastx}{\pgfutil@tempdimb}%
%
\pgf@protocolsizes{\pgf@path@lastx}{\pgf@path@lasty}%
\pgf@protocolsizes{#5}{#6}%
\endgroup
\pgfsyssoftpath@curveto{\the#1}{\the#2}{\the#3}{\the#4}{\the#5}{\the#6}%
}
\endinput


This is a sample document.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{bbox}
\usetikzlibrary{positioning,calc,bending}
\begin{document}
\message{For one hundred curves the compilation takes a few seconds.}

\subsection*{Code example from issue \#856}

\begin{tikzpicture}[bezier bounding box=true,
atomicNode/.style={rectangle,draw,minimum width=4em,minimum height=4em}]
\node[atomicNode] (upper) {};
\node[atomicNode,below=of upper] (lower) {};
\coordinate (zUpper) at ($(upper.north west)!.75!(upper.south west)$);
\coordinate (yUpper) at (upper.east);
\coordinate (zLower) at ($(lower.north east)!.25!(lower.south east)$);
\coordinate (yLower) at (lower.west);
\draw[-latex] (yUpper) .. controls ($(yUpper)+(13em,0)$) and ($(yLower)-(13em,0)$) .. (yLower);
\draw[-latex] (zLower) .. controls ($(zLower)+(13em,0)$) and ($(zUpper)-(13em,0)$) .. (zUpper);
\draw  (current bounding box.north west) rectangle (current bounding box.south east);
\end{tikzpicture}

\subsection*{Code example from issue \#838}

\begin{tikzpicture}[bezier bounding box=true]
\draw (1,0) .. controls (5,1) and (5,2) .. (1,1);
\draw (current bounding box.south west) rectangle
(current bounding box.north east);
\end{tikzpicture}

\subsection*{Cubic B\'ezier curves}

\foreach \X in {1,...,100}
{\begin{tikzpicture}
\pgfmathsetseed{\X}
\pgfmathsetmacro{\rnda}{360*rnd}
\pgfmathsetmacro{\rndb}{360*rnd}
\pgfmathsetmacro{\rndc}{360*rnd}
\pgfmathsetmacro{\rndd}{3*rnd}
\pgfmathsetmacro{\rnde}{3*rnd}
\pgfmathsetmacro{\rndf}{3*rnd}
\begin{scope}[local bounding box=without,red]
\draw (0,0)  .. controls (\rnda:\rndd) and (\rndb:\rnde) .. (\rndc:\rndf);
\end{scope}
\draw[red] (without.south west) rectangle (without.north east);
%
\begin{scope}[xshift=3.5cm]
\begin{scope}[local bounding box=with,use fpu reciprocal,bezier bounding box=true]
\draw (0,0)  .. controls (\rnda:\rndd) and (\rndb:\rnde) .. (\rndc:\rndf);
\end{scope}
\end{scope}
\draw (with.south west) rectangle (with.north east);
\end{tikzpicture}\par}
\clearpage

\foreach \X in {1,...,100}
{\begin{tikzpicture}
\pgfmathsetseed{\X}
\pgfmathsetmacro{\rnda}{360*rnd}
\pgfmathsetmacro{\rndc}{360*rnd}
\pgfmathsetmacro{\rndd}{3*rnd}
\pgfmathsetmacro{\rnde}{3*rnd}
\pgfmathsetmacro{\rndf}{3*rnd}
\begin{scope}[local bounding box=without,red]
\draw (0,0)  .. controls (\rnda:\rndd) .. (\rndc:\rndf);
\end{scope}
\draw[red] (without.south west) rectangle (without.north east);
%
\begin{scope}[xshift=3.5cm]
\begin{scope}[local bounding box=with,bezier bounding box=true]
\draw (0,0)  .. controls (\rnda:\rndd) .. (\rndc:\rndf);
\end{scope}
\end{scope}
\draw (with.south west) rectangle (with.north east);
\end{tikzpicture}\par}
\end{document}


I have also written a manual for that, but there is no space to upload it.2 The pgf maintainers have a copy. Let me mention two potentially important points:

1. Adding arrow heads deforms a path after it has been reported to the bounding box. Loading the bending library, which switches on the key /pgf/arrow keys/flex, is sufficient to avoid the deformation, and thus to obtain the correct bounding box.
2. Another potentially important point concerns Dimension too large errors. Empirically I find that they occur very rarely, if at all. The culprit is the reciprocal, see this post and this issue. Installing an fpu reciprocal solved the problem in all cases I have encountered. Apart from that, installing an fpu reciprocal fixes a large subset of the Dimension too large errors that occur in decorations, but not all of them.3

1The reason is not that the library would not work, it does work, in particular after the upgrade. The dispute was on how one writes Bézier, apparently using B\'ezier in the source code is a crime, and reason for others to step on me. I do not support this view, nor behavior, and quit.

2I'd like to thank Stefan Pinnow for improvements.

3The use fpu reciprocal key will probably appear under the name /pgf/fpu/install only={reciprocal} in pgf. Of course, I got first shouted at for suggesting to add such a key/functionality to pgf. After it turned out useful, it got renamed, and the credit goes to others. Anyway, the upshot is that you may use use /pgf/fpu/install only={reciprocal} instead of use fpu reciprocal. Apart from explaining why the library will disappear from the distributions, these discussions are irrelevant. This post is an attempt to pass some useful information on, hoping it will be useful for others. To reiterate, to the best of my knowledge the library does work, and this version is strictly better than the one that is/was part of pgf v3.1.5.

• @JeT I stress that in the pdf version it showed Bézier, the dispute was on the source code, I dared to use B\'ezier because my editor, which has all the features that allowed me to conveniently write the library, does not like unicode chars. There are at least two users who publicly stepped on be because of this, which is the reason for me to decide to wrap up. Maybe they will find some who can write a similar library with an editor they like. I dare to predict that they will find another reason to step on such a person, I have not seen a single occasion in which they admit mistakes.
– user194703
Jun 16, 2020 at 19:10
• @JeT I do not think it has to do with rivalry on this site, just with personalities. Users who have no experience in international collaborations have nothing better to do than to publicly step on others and tell them what they need to do in international collaborations. Other users do everything to insert their names in the LaTeX history, regardless of how much they hurt LaTeX users with this. However, I am not ready to spend more energy in such a scheme. Everybody can have a bad day and make inappropriate comments, but not everyone seems to be able to admit mistakes and take them back.
– user194703
Jun 16, 2020 at 19:21
• regarding the wrap up... I must say it saddens me. I followed you pretty much daily (to the point my wife asked me "who's this schrodinger s cat? :).
– JeT
Jun 16, 2020 at 19:22
• @JeT There are other users who will write answers. However, I never thought this site was sustainable. It may, however, be an important experiment which allows us to see what went wrong when setting up a better scheme in the future. Things that went wrong here include that copying codes without indicating the source seems to be tolerated, the fact that moderator decisions cannot be challenged, and that those participating in the chat have special rights. Maybe a future, better site can try to work on these problems.
– user194703
Jun 16, 2020 at 19:29
• @JeT I agree, of course, on the statements of universities, and I have attended talks by and discussed with Jocelyn Bell. That's all bad, but what might be worse is that users here take it to another level. I have not heard of an instance in which a journal would reject an article because the author used B\'ezier to typeset Bézier, let alone that the editor calls the author names because of that, or an unrelated person would look at the submission and mock the author for this in a public chat. In this regard academia is way better than this site.
– user194703
Jun 16, 2020 at 19:33