157

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.

5
  • 8
    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.” » Dec 2, 2010 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, 2010 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. Dec 3, 2010 at 0:40
  • Weirdly, scaling also keep arrow tip sizes and rounded corners, but does scale plot marker size. I believe in the end, "do not scale graphics" is sound advice.
    – A. Donda
    Jul 3, 2020 at 4:10
  • @WillRobertson, thanks for pointing that out. To me that makes no sense. Please note that I'm not attacking you, I'm just confused by tikz documentation... I mean, I could also do this anywhere else "at the right size", for instance in inkscape...
    – Pouya
    Oct 29, 2020 at 15:50

7 Answers 7

91

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:

unscaled and scaled tikzpicture in comparison

\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}

If you need to use the external tikzlibrary, here is a workaround:

\documentclass{article}
\usepackage{tikz}
\usepackage{environ}

\usetikzlibrary{external}\tikzexternalize%\tikzset{external/force remake}

\makeatletter
\newcounter{tikz@scale@num}
\newsavebox{\measure@tikzpicture}
\NewEnviron{scaletikzpicturetowidth}[2][]{%
  % optional argument #1 is passed to \tikzsetnextfilename if not empty
  \stepcounter{tikz@scale@num}%
  \def\tikz@width{#2}%
  \def\tikzscale{1}\begin{lrbox}{\measure@tikzpicture}%
  \BODY
  \end{lrbox}%
  \pgfmathparse{#2/\wd\measure@tikzpicture}%
  \ifcsname tikzscale\number\value{tikz@scale@num}\endcsname\else
      \expandafter\xdef\csname tikzscale\number\value{tikz@scale@num}\endcsname{\pgfmathresult}%
  \fi
  \tikzset{external/system call={pdflatex \tikzexternalcheckshellescape -halt-on-error -interaction=batchmode -jobname "\image" "\string\expandafter\string\edef\string\csname\space tikzscale\number\value{tikz@scale@num}\string\endcsname{\csname tikzscale\number\value{tikz@scale@num}\endcsname}\texsource"}}%
  \edef\tikzscale{\csname tikzscale\number\value{tikz@scale@num}\endcsname}%
  \ifcat$#1$\else\tikzsetnextfilename{#1}\fi
  \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}

If you need to use \tikzsetnextfilename{name} for an automatically scaled picture, use instead the syntax \begin{scaletikzpicturetowidth}[name]{\textwidth}.

20
  • is there a ConTeXt equivalent? Sep 10, 2011 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. Sep 10, 2011 at 20:03
  • 2
    This solution seemed good but just has no effect in my tex document.
    – Spenhouet
    Nov 28, 2017 at 22:23
  • @Spen: There's no reason it shouldn't work. Does it work in the test document above but not in another of your documents? If so, you should try to add your packages one by one to the test document until the problem occurs. If that doesn't help, please ask a new question so you can provide more details. Dec 2, 2017 at 7:43
  • When I use this solution, the image does scale, but only to something like 75 % of the \textwidth. Why? When I use Martin Scharrer's adjustbox, the image successfully scales to the whole \textwidth, but on the other hand has the side effect that text and other details that shouldn't be scaled are scaled. Jul 4, 2020 at 0:02
72

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.

4
  • 10
    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. Feb 20, 2011 at 0:07
  • 7
    @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. Feb 20, 2011 at 0:13
  • method 1 here worked well for me Jan 26, 2017 at 0:43
  • adjustbox worked in my case (I was using pgfplots with tikz) Jan 19, 2020 at 1:06
50

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}
2
  • 1
    I had a tikz picture where I used cm inside. I measured it's width in cm (open pdf in inkscape, select the image, change unit to cm) and then used \begin{tikzpicture}[scale=\linewidth/12cm, transform shape]... where 12cm was the width of my picture. It worked pretty nicely.
    – orestisf
    May 15, 2017 at 22:34
  • Is there a way to combine tikzscale with standalone since I would like to scale standalone tikzpictures to linewidth keeping the fontsize as define within the standalone file.
    – Nivek
    Jul 5, 2021 at 22:02
9

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

2
  • I tried to use this approach with the tikz matrix environment, but it did not work. Any ideas?
    – cacamailg
    Jul 18, 2014 at 12:28
  • 1
    @cacamailg Many of these solutions work by grabbing the whole TikZ picture as an argument. This means you can't use an active & in a matrix but you need to use the ampersand replacement option (or use \pgfmatrixnextcell). Oct 26, 2022 at 12:28
5

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.

4

You may simply set \pgfplotsset{compat=x.xx, width=\textwidth} as the following example if you use pgfplots.

\documentclass[twocolumn]{article}

\usepackage{pgfplots}
\usepackage{lipsum}

\begin{document}

\lipsum[1]

\begin{figure}[h]
    \pgfplotsset{compat=1.16, width=\columnwidth}
    \begin{tikzpicture}
        \begin{axis}
            \addplot[
                red,
                domain=-3e-3:3e-3,
                samples=201,
            ]
                {exp(-x^2 / (2e-3^2)) / (1e-3 * sqrt(2*pi))};
        \end{axis}
    \end{tikzpicture}
\end{figure}

\lipsum[2-5]

\end{document}
3
  • 1
    Welcome to TeX.SE. Can you explain better with a complete code your answer?
    – Sebastiano
    Sep 1, 2020 at 8:29
  • 1
    @Sebastiano I've added a full example.
    – Xintong Li
    Sep 2, 2020 at 15:36
  • Very good :-)...
    – Sebastiano
    Sep 2, 2020 at 15:41
0

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.

3
  • Examples 3 and 4 don't show in the preview image. Nov 17, 2017 at 9:59
  • @corwin.amber I don't undestand. For me, the image contains all four examples (always “Example n:” followed by a rule showing the desired width and finally the scaled image). Can you see all examples by clicking/touching the image?
    – wrtlprnft
    Nov 18, 2017 at 10:56
  • Sry, I did not understand that "piecewise linear width" over a dashed line was the image. I guess I was expecting some shape like in the first two... Nov 18, 2017 at 19:00

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .