TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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.

share|improve this question
3  
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
up vote 50 down vote accepted

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:

alt text

\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}
share|improve this answer
    
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}
share|improve this answer
    
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
\usepackage{adjustbox}

% 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}
\end{adjustbox}

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

share|improve this answer
3  
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
2  
@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. – Dmitry 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}

enter image description here

share|improve this answer
    
I tried to use this approach with the tikz matrix environment, but it did not work. Any ideas? – cacamailg Jul 18 '14 at 12:28

A new package tikzscale was created in 2012, originated from this answer. It states that it scales plots most accurately compared to standard pgfplots methods.

In its documentation there is a good overview of means to scale pictures with and without scaling fonts.

share|improve this answer

The solutions so far work fine if the picture scales linarly with its scale factor. However, if there are text nodes that extend the bounding box or excessive line widths, this assumption doesn't hold.

Instead, I'll assume that the width of a picure follows an affine linear equation w = a * x + b, where x is the scaling factor and a and b are constants determined by the picture.

My idea is to measure the width of the picture at two different scaling factors and then compute the correct factor. The macro \affinescale gets four parameters: The desired width, the two scaling factors to sample at and the code for the picture. Inside the code, \affinex contains the scaling factor. The user can decide how the y axis should be scaled and this construct is not limited to tikzpictures.

The two sample points should be chosen such that they're reasonably close to the correct value because there can be nonlinearities in the scaling behavior of a picture, see my examples 3 and 4 below.

Code

\documentclass{article}
\usepackage{tikz}

\newdimen\affinex
\newcommand{\affinescale}[4]{{%
    % #1 = desired width w
    % #2 = \affinex for first measurement x1
    % #3 = \affinex for second measurement x2
    % #4 = code to draw picture
    %
    % take first measurement
    \affinex=#2
    \setbox0=\hbox{{\ignorespaces #4\unskip}}%
    %\copy0
    %\hbox{x1 = \the\affinex, w1 = \the\wd0}%
    %
    % take second measurement
    \affinex=#3
    \setbox1=\hbox{{\ignorespaces #4\unskip}}%
    %\copy1
    %\hbox{x2 = \the\affinex, w2 = \the\wd1}%
    %
    % calculate x from 2, w1, w2, x1, x2; system of equations to solve:
    % w  = a * x  + b ; desired dimensions
    % w1 = a * x1 + b ; measurement 1
    % w2 = a * x2 + b ; measurement 2
    \pgfmathparse{(\wd1 - \wd0) / ((#3) - (#2))}%
    \let\a=\pgfmathresult
    \pgfmathparse{(\wd0 - \a * (#2))}%
    \let\b=\pgfmathresult
    \pgfmathsetlength{\affinex}{((#1) - \b) / \a}%
    %\hbox{a = \a, b = \b, x = \the\affinex}%
    %
    % finally, draw the picture
    \ignorespaces #4\unskip%
}}

\setlength{\parindent}{0pt}

\begin{document}
    Example 1: scale x and y by the same factor

    \vrule width 5cm height 1pt depth 0pt

    \affinescale{5cm}{2cm}{3cm}{
        \begin{tikzpicture}[x=\affinex,y=\affinex]
            \draw (2,0) node[below right] {(2,0)}
               -- (1,2) node[above]       {(1,2)}
               -- (0,0) node[below left]  {(0,0)}
               -- cycle;
        \end{tikzpicture}
    }

    Example 2: only scale x

    \vrule width 10cm height 1pt depth 0pt

    \affinescale{10cm}{0.9cm}{1.1cm}{
        \begin{tikzpicture}[x=\affinex]
            \draw[line width=10pt,black!20!white] (0,0) rectangle (10,1);
            \draw (0,0) node[above right] {(0,0)} rectangle (10,1) node[below left] {(10,1)};
            \node[right=5pt] at (10,.5) {node text};
        \end{tikzpicture}
    }

    Example 3: node sticks out to the right

    \vrule width 5cm height 1pt depth 0pt

    \affinescale{5cm}{2cm}{3cm}{
        \begin{tikzpicture}[x=\affinex]
            \draw[dashed] (0,0) -- (1,0) node[midway,above right] {piecewise linear width};
        \end{tikzpicture}
    }

    Example 4: due to scaling, node doesn't stick out anymore

    \vrule width 10cm height 1pt depth 0pt

    \affinescale{10cm}{8cm}{9cm}{
        \begin{tikzpicture}[x=\affinex]
            \draw[dashed] (0,0) -- (1,0) node[midway,above right] {piecewise linear width};
        \end{tikzpicture}
    }
\end{document}

Result

output

Maybe this could be extended to some kind of iteration scheme to always figure out the correct scaling factor, but I'm scared of the TeX wizardry that would entail. Also, it would be nice to save the scaling factor in the aux file to avoid typesetting the figure three times for every run of LaTeX.

share|improve this answer

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.