How to scale a tikzpicture to \textwidth

I would like to scale my tikzpicture to exactly (or a proportion of) the \textwidth

Of course I could play with the [scale=0.5] option until I found the right value, but I assume there must be an easier way.

How to get a properly scaled (the fonts still must be correct) tikzpicture with an exact width.

-
I was surprised this wasn't possible out of the box, but then I found in the tikz manual: « Do not scale graphics. This means that when generating graphics using an external program, create them “at the right size.” » –  Will Robertson Dec 2 '10 at 17:16
@Will: I don't see the point of that advice for graphics generated with TikZ as the scaling is quite intelligent (e.g. it keeps line widths and font sizes). –  Caramdir Dec 2 '10 at 19:17
@Caramdir all lengths in tikz are absolute aren't they? I haven't used it much, but I didn't think you could set a figure size and then work in relative coordinates to that. –  Will Robertson Dec 3 '10 at 0:40

This question was asked on comp.text.tex and received a good answer by Ulrike Fischer. It works by typesetting the {tikzpicture} once, measure its width and then retypeset it to the correct width by automatically computing the required scale.

Here's a more user-friendly interface for this solution using the environ package. It works by using a {scaletikzpicturetowidth} environment with the desired width as first argument in combination with specifying the [scale=\tikzscale] option to the tikzpicture. For example, to scale a tikzpicture to {textwidth}, you would use:

\begin{center}
\begin{scaletikzpicturetowidth}{\textwidth}
\begin{tikzpicture}[scale=\tikzscale]
\draw (0,0) rectangle (1,1) node[below left] {$A$};
\draw (2,1) circle (1cm) node [below] {$B$};
\end{tikzpicture}
\end{scaletikzpicturetowidth}
\end{center}


Here's a complete compilable code which shows both the unscaled tikzpicture and the scaled one:

\documentclass{article}
\usepackage{tikz}
\usepackage{environ}
\makeatletter
\newsavebox{\measure@tikzpicture}
\NewEnviron{scaletikzpicturetowidth}[1]{%
\def\tikz@width{#1}%
\def\tikzscale{1}\begin{lrbox}{\measure@tikzpicture}%
\BODY
\end{lrbox}%
\pgfmathparse{#1/\wd\measure@tikzpicture}%
\edef\tikzscale{\pgfmathresult}%
\BODY
}
\makeatother
\begin{document}

Bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla.
\begin{center}
\begin{tikzpicture}
\draw (0,0) rectangle (1,1) node[below left] {$A$};
\draw (2,1) circle (1cm) node [below] {$B$};
\end{tikzpicture}
\end{center}
Bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla.
\begin{center}
\begin{scaletikzpicturetowidth}{\textwidth}
\begin{tikzpicture}[scale=\tikzscale]
\draw (0,0) rectangle (1,1) node[below left] {$A$};
\draw (2,1) circle (1cm) node [below] {$B$};
\end{tikzpicture}
\end{scaletikzpicturetowidth}
\end{center}
Bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla.

\end{document}

-
is there a ConTeXt equivalent? –  flying sheep Sep 10 '11 at 10:48
@flying sheep: I don't see why you couldn't do that in ConTeXt, but as I don't know it at all, I can't help. Try asking a new question on the subject or go on a ConTeXt specific forum. –  Philippe Goutet Sep 10 '11 at 20:03

Maybe the most simple way to use relative coordinates is using scale=\textwidth/1cm and then only use values between 0 and 1 for your coordinates (assuming your basic scale size is 1cm). You could also estimate the size of your figure and use the same trick. I had that problem since the figure was already done and I wanted to scale it afterwards.

\begin{tikzpicture}[scale=\textwidth/15.2cm,samples=200]
%x axis
\draw[->] (-0.1,0) -- (15.1,0) node[below] {$x$};

%y axis
\draw[->] (0,-0.1) -- (0,6.1) node[left] {$y$};

%exponential function
\draw[color=red, thick, domain=0.0:9.5] plot[id=efunc] function{1.5**(x-5) - 1.5**(-5) + 0.1} node[color=red, anchor=west] {$y = e^x$};

%logistic function
\draw[color=blue, thick, domain=0.0:15.0] plot[id=logfunc] function{5/(1 + 2**(-x+7.5)) - 5/(1 + 2**(7.5)) + 0.1} node[color=blue, anchor=north east] {$y = \cfrac{1}{1 + e^{-x}}$};
\end{tikzpicture}

-
That's a nifty way to do it! –  Jake Oct 25 '11 at 8:08

1)

For smaller tikzpictures you can simply use the \resizebox macro from the graphics (or graphicx) package:

\resizebox{\textwidth}{!}{%
\begin{tikzpicture}
\draw (0,0) .... ;
\end{tikzpicture}
}%


However, this makes the picture part of a macro argument which e.g. doesn't allow verbatim text inside nodes. TikZ itself goes through some effort to process the node content as box not as macro argument to allow any form of code inside it, including verbatim.

The use of the environ package should be also avoided in the general case because it also makes the environment body a macro argument.

2)

In can simply define your own environment using lrbox like in Philippe's answer, but as normal environment and with \resizebox:

\newsavebox\mybox
\newenvironment{resizedtikzpicture}[1]{%
\def\mywidth{#1}%
\begin{lrbox}{\mybox}%
\begin{tikzpicture}
}{%
\end{tikzpicture}%
\end{lrbox}%
\resizebox{\mywidth}{!}{\usebox\mybox}%
}
%
% Usage example:
\begin{resizedtikzpicture}{\textwidth}[<tikz options>]
\draw .... ;
\end{resizedtikzpicture}


3)

I recently created the package adjustbox to give users the power of \includegraphics options for text or other contents. This can be used here nicely:

% Preamble

% Document
\begin{adjustbox}{width=\textwidth}% there is also 'max width' to only scale it down if it is larger
\begin{tikzpicture}[<options>]
\draw .... ;
\end{tikzpicture}


It supports verbatim and other special content and will work for normal text as well as other picture environments.

-
the problem with all these methods is that they will also change the size of text inside the labels, which is not wanted. If you really need verbatim inside the {tikzpicture}, one way would be to write to an external file the environment's content instead of using the environ package. –  Philippe Goutet Feb 20 '11 at 0:07
@Philippe: Ok, that's what he meant with "the fonts still must be correct". But then it could be done using x=\textwidth, i.e. setting the x-unit to the text width and drawing the picture using relative coordinates. The question isn't very clear. The OP doesn't mentions what kind of picture he has etc. –  Martin Scharrer Feb 20 '11 at 0:13
Your second solution doesn't work due to arguments of newenvironment seem to be not propogated to the definition of the closing statement. –  Dmitrii F. Volosnykh Apr 22 '11 at 5:17
@Dmitry: Thanks for pointing that out. I corrected it. –  Martin Scharrer Apr 22 '11 at 7:01

Here a solution based on Philippe Goutet's answer from an idea of Ulrike Fischer. I created a new environment tikzpicture*. The main idea now it's to avoid to use multiple environments. Only one is necessary tikzpicture*. Normally it's possible to use options like with tikzpicture. I made some tests but It would be interesting to make more tests.

Firstly, I suppose that the user wants to adjust all the figures to the same width. I chose \linewidth by default. The user needs to set up the parameters with \setupscalewithtikz. This macro requieres one argument (a length) to apply to newwidth . It's possible to change the value of \newwidth with \setlength{\newwidth}{length}.

Here an example :

\documentclass[landscape]{article}
\usepackage{fullpage,tikz}
\usepackage{environ,amsmath,multicol}

\makeatletter
\newcommand*\setupscalewithtikz[1][\linewidth]{%
\newsavebox{\box@tikzpicture}
\newlength{\newwidth}
\setlength{\newwidth}{#1}
}
\NewEnviron{tikzpicture*}[1][]{%
\begin{lrbox}{\box@tikzpicture}%
\begin{tikzpicture}[#1]
\BODY
\end{tikzpicture}%
\end{lrbox}%
\pgfmathsetmacro\width@scale@picture{\newwidth/\wd\box@tikzpicture}%
\begin{tikzpicture}[#1,scale=\width@scale@picture]
\BODY
\end{tikzpicture}%
}%
\makeatother

\begin{document}
\parindent=0pt

\def\somecode{%
\draw[fill=red!20] (0,0) rectangle (1.2,1.2) coordinate (A);
\draw[fill=blue!20][rotate around ={-atan(4/3):(A)}](A) rectangle ++(1.5,1.5);
\draw[fill=green!20] (1.2,0) rectangle +(0.9,-0.9);
}

\setlength\columnsep{1ex}
\setlength\columnseprule{0.5 pt}
\begin{multicols}{2}[Some squares with the environment \emph{tikzpicture*}]
\setupscalewithtikz % adapt automatically to \linewidth

\begin{tikzpicture}
\somecode
\end{tikzpicture}

\begin{tikzpicture*}
\somecode
\end{tikzpicture*}

\setlength{\newwidth}{1cm}
\begin{tikzpicture*}
\somecode
\end{tikzpicture*}

\setlength{\newwidth}{6cm}
\begin{tikzpicture*}
\somecode
\end{tikzpicture*}
\end{multicols}
\end{document}


-