Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

Why does the following latex document:

\documentclass{minimal}
\usepackage{tikz}

\begin{document}
    \noindent
    \begin{tikzpicture}
        \draw (0,0) -- (\textwidth,0);
    \end{tikzpicture}
\end{document}

give an overfull \hbox of 0.4pt? The line cap option has no effect. An ultra thick line gives a larger overfull \hbox of 1.6pt.

Is there a better (more TikZ-like) way to achieve the same effect, i.e., draw a line to the edge of the text box? Ideally I'd like to do this without scaling the figure.

share|improve this question
3  
a line the width of the text area can be done with the TeX primitive \hrule if that's all you need? –  David Carlisle Jun 2 '12 at 16:38
    
Yes, I know about \hrulefill and the like, but I can't use those in a TikZ picture. In my actual use case I'm trying to make a picture-based page header that includes a line going to the end of the text. –  Jan Ladislav Dussek Jun 2 '12 at 21:00
1  
In a complex picture it might be easiest to explicitly set the bounding box with [use as bounding box]. –  Caramdir Jun 3 '12 at 3:17
    
Related Question: Drawing a line the width of the text using tikz. –  Peter Grill Dec 28 '12 at 14:29
add comment

4 Answers

up vote 15 down vote accepted

The code can be adapted very easily to not get the warning anymore:

\documentclass{minimal}
\usepackage{tikz}

\begin{document}
    \noindent
    \begin{tikzpicture}
        \draw[line cap=rect] (0,0) -- (\linewidth-\pgflinewidth,0);
    \end{tikzpicture}
\end{document}

EDIT: I think I also have an explanation: Each time tikz draws a line, it uses 2 bounding boxes (cf. PGF manual v 2.10 - page 579):

As a side-effect, the path construction commands keep track of two bounding boxes. One is the bounding box for the current path, the other is a bounding box for all paths in the current picture.

Section 7.13 details these parameters.

What I think is happening, is that regardless of the way how the line ends (line cap), its bounding box protrudes by \pgflinewidth beyond the edge of the text area by this length. This is probably necessary to accommodate the whole line in case of nicely rounded line caps. Since this requires in fact .5\pgflinewidth on each end and the bounding box of the line starts at exactly (0,0) the overhang will be \pgflinewidth at the end of the line.

share|improve this answer
1  
I'm reasonably convinced that this is the correct explanation, i.e., the bounding box for the line leaves extra space for a line cap, even if none is used. Thus, to get a line of the correct length and the correct bounding box, I've used the code \draw[line cap=rect] (0,0) -- (\textwidth-\pgflinewidth,0);. –  Jan Ladislav Dussek Jun 2 '12 at 21:03
1  
Having two bounding boxes in memory (one for the whole tikzpicture and one for the current \draw) has nothing to do with the problem. Also (as Jan noted), your line is too short, but can be made the correct size by adding line caps. –  Caramdir Jun 3 '12 at 3:30
    
@JanLadislavDussek: I modified my code according to your suggestion. –  Count Zero Jun 3 '12 at 8:48
add comment

A possibility to get a line with a length equal to \linewidth or \textwidth without overfull box is to use a clip or, as I wrote in a comment, to modify the bounding box.

\documentclass{minimal}
\usepackage{tikz}

\begin{document}
    \noindent
    \begin{tikzpicture} 
        \draw (0,0.1) -- (\linewidth-\pgflinewidth,0.1); 
        \clip (0,-5\pgflinewidth) rectangle (\textwidth,5\pgflinewidth);
        \draw (0,0) -- (\textwidth,0);
    \end{tikzpicture}
\end{document} 

enter image description here

Remark : It's not another possibility because with \draw (0,0) -- (\linewidth-\pgflinewidth,0); the length is not correct.

Explanations :

I think that TikZ (or PGF with the basic layer) needs to be efficient at each step of the drawing. Firstly, the dimensions must be accurate enough and it's difficult with TeX, but Tikz needs to be fast enough. It's complicated like Caramdir shows in a example to calculate exactly the bounding box in all the cases. Some rules are necessary to avoid being too slow and to get correct results.

1) When you draw a simple line you get a complicated bounding box

enter image description here

This picture shows the problem at each side of the picture. In this first picture I used line join=round but in the next I used the values by default.

enter image description here

2) why add 0.5\pgflinewidth

When you draw a rectangle for example, the width is determined from the middle of line (with rounded corners or not). So I think that the main rule is to add 0.5\pgflinewidth in all the cases.

enter image description here enter image description here

3) Now why clip is fine to get exactly what you want

When a clip is used, the line width is not used

enter image description here

4) Conclusion. I think it's preferable to avoid very large width for the line and in some cases it's necessary to have this question in memory to get exact dimensions.

Here is the code for experimentation:

\documentclass{minimal}
\usepackage{tikz}

\begin{document}

\begin{tikzpicture} 
    \draw[help lines] (0,0) grid (1,1);  
    \draw[line width=.4cm,opacity=.2,line join=round] (0,0) -- (1,0) -- (0,1);
    \draw[red] (current bounding box.south west) rectangle (current bounding box.north east);
\end{tikzpicture}

\vspace*{12pt}   
\begin{tikzpicture} 
    \draw[help lines] (0,0) grid (1,1);  
    \draw[line width=.4cm,opacity=.2] (0,0) -- (1,0) -- (0,1);
    \draw[red] (current bounding box.south west) rectangle (current bounding box.north east);
\end{tikzpicture}   

\vspace*{12pt}
 \begin{tikzpicture}
  \draw[help lines] (0,0) grid (2,1);
    \draw[line width=.4cm,rounded corners,opacity=.2] (0,0) rectangle (2,1);
    \draw[red] (current bounding box.south west) rectangle (current bounding box.north east);
\end{tikzpicture}

\vspace*{12pt}
 \begin{tikzpicture}
  \draw[help lines] (0,0) grid (2,1);
    \draw[line width=.4cm,opacity=.2] (0,0) rectangle (2,1);
    \draw[red] (current bounding box.south west) rectangle (current bounding box.north east);
\end{tikzpicture} 

\vspace*{12pt}    
\begin{tikzpicture}
  \draw[help lines] (0,0) grid (2,1);
    \clip (0,0) rectangle (2,1);  
    \draw[line width=.4cm,opacity=.2] (0,0) rectangle (2,1);
    \draw[red] (current bounding box.south west) rectangle (current bounding box.north east);
\end{tikzpicture}  

\end{document} 
share|improve this answer
    
Which length are you referring to in your remark? Could you elaborate please? –  Count Zero Jun 2 '12 at 17:20
    
I want only to say that it's possible to have a length = \textwidth > \textwidth-\pgflinewidth without an overfull box –  Alain Matthes Jun 2 '12 at 18:02
    
^ Indeed you can avoid overfull, even in much easier way: \hbox to \textwidth {\begin{tikzpicture} \draw (0,0) -- (\textwidth,0); \end{tikzpicture}\hss} –  tohecz Jun 2 '12 at 18:09
    
much easier ? I'm not sure. If the problem is to create a picture inside a page with a normal width, I prefer to control the bounding box inside the tikzpicture environment and to adapt the size of the box created by tikz. Another possibility is to fix the bounding box directly ! –  Alain Matthes Jun 2 '12 at 18:14
    
So basically my suspicion that the bounding box is the culprit, proves to be correct? And my code generates a line with actual length \textwidth-\pgflinewidth, but the bounding box has width \textwidth? –  Count Zero Jun 2 '12 at 18:48
show 3 more comments

The first two tikzpictures show the bug calculation of the bounding box.

The third tikzpicture answers the question via a filled rectangle. ;-)

\documentclass{minimal}
\usepackage{tikz}

\begin{document}
\begin{tikzpicture}[outer sep=0pt,inner sep=0pt]
  \draw[line cap=butt,line width=10pt] (0,0) -- (1,0);
  \draw[line width=1pt,red]
  (current bounding box.south east)
  rectangle
  (current bounding box.north west);
\end{tikzpicture}

\begin{tikzpicture}[outer sep=0pt,inner sep=0pt]
  \draw[line cap=round,line width=10pt] (0,0) -- (1,0);
  \draw[line width=1pt,red]
  (current bounding box.south east)
  rectangle
  (current bounding box.north west);
\end{tikzpicture}

\noindent
\begin{tikzpicture}
  \fill (0,0) rectangle (\linewidth,1pt);
\end{tikzpicture}
\end{document}

result

Another solution uses trim left and trim right to compute good (horizontal) bounding box without clipping (as pgf manual says, "[...]baseline, trim left and trim right are currently the only supported way of truncated bounding boxes which are compatible with image externalization[...]").

\documentclass{minimal}
\usepackage{tikz}

\begin{document}
\noindent
\begin{tikzpicture}[trim left=0cm,trim right=\linewidth]
  \draw (0,0) -- (\linewidth,0);
\end{tikzpicture}

\end{document}
share|improve this answer
    
You're right: the best (cleanest) solution seems to be to use a rectangle instead of a line. –  Jan Ladislav Dussek Jun 3 '12 at 0:16
    
but in this case you get a problem with colors between drawing and filling. –  Alain Matthes Jun 3 '12 at 7:01
    
@Altermundus You are right so I propose a new solution... –  Paul Gaborit Jun 3 '12 at 8:57
    
Finally all the solutions consist to adapt the bounding box of the picture. –  Alain Matthes Jun 3 '12 at 9:17
add comment

This is an answer to the first question: Why does TikZ do that?

To look at this we need to understand how TikZ works internally. The following is a very simplified (but I hope correct) account of how TikZ code gets processed:

  1. Frontend code (i.e. TikZ code) is translated to “basic layer” code (i.e. pgf code). For example,

     \draw (0,0) -- (\textwidth,0);
    

    (basically) becomes

     \pgfmoveto{\pgfpoint{0pt}{0pt}}
     \pgflineto{\pgfpoint{\textwidth}{0pt}}
     \pgfusepath{stroke}
    
  2. The pgf code constructs a path. A path is a mathematical object and so everything is represented as having infinitely small line width. Thus the first two \pgf... lines above will result in a path that consists just of one line segment. The bounding box is calculated in this step by simply looking at the extremal values of each part of the path. So in this example, the bounding box of the path will be rectangle of width \textwidth and height 0.

  3. The path is then “used” with \pgfusepath and similar commands. This translates it into drawing commands of the output format (e.g. PostScript or PDF).

The problem with this approach is that the bounding box might change between steps 2 and 3 as stroking a path with a positive line width will change its size. However pgf never gets the information of the final bounding box, because the stroking is only done at the level of the viewer (the PDF contains the information of the path and the line width and then the PDF viewer has to figure out how to display that correctly).

Considering the line width while calculating the bounding box in step 2 is I think significantly harder than just calculating the bounding box of the path itself. Thus pgf takes the easy way out: after finishing the second step, it just adds half the line width in every direction to the bounding box. This is done in pgfcorepathusage.code.tex:

  %
  % Check whether the path is stroked. If so, add half the line width
  % to the bounding box.
  %
  \ifpgf@relevantforpicturesize%
    \ifx\pgf@up@stroke\pgfutil@empty%
    \else%
      \ifdim\pgf@picmaxx=-16000pt\relax%
      \else%
        \pgf@x=\pgf@pathminx\advance\pgf@x by-.5\pgflinewidth%
        \ifdim\pgf@x<\pgf@picminx\global\pgf@picminx\pgf@x\fi%
        \pgf@y=\pgf@pathminy\advance\pgf@y by-.5\pgflinewidth%
        \ifdim\pgf@y<\pgf@picminy\global\pgf@picminy\pgf@y\fi%
        \pgf@x=\pgf@pathmaxx\advance\pgf@x by.5\pgflinewidth%
        \ifdim\pgf@x>\pgf@picmaxx\global\pgf@picmaxx\pgf@x\fi%
        \pgf@y=\pgf@pathmaxy\advance\pgf@y by.5\pgflinewidth%
        \ifdim\pgf@y>\pgf@picmaxy\global\pgf@picmaxy\pgf@y\fi%
      \fi%
    \fi%
  \fi%

Of course this approximation might also be wrong in the other direction, as the following example shows:

\documentclass{article}
\usepackage{tikz}

\begin{document}
\begin{tikzpicture}
    \draw[line width=1cm] (0,0) -- (5,0) -- (0,2);
    \draw[red] (current bounding box.south west) rectangle (current bounding box.north east);
\end{tikzpicture}
\end{document}

example

Exercise: Compute the correct bounding box for this example.

share|improve this answer
    
You're right: pgf computes an approximation of the bounding box. This approximation is exact only with line cap=round and line join=round. –  Paul Gaborit Jun 3 '12 at 8:39
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.