[Tex/LaTex] How to scale a tikzpicture to \textwidth

marginsscalingtikz-pgf

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.

Best Answer

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

Related Question