[Tex/LaTex] Automagically scale Tikz-Picture AND use Externalization

scalingtikz-pgf

I need to scale Tikz pictures automatically, and I do this using the following custom environment:

% Automagically scale a Tikz picture, so it has the desired (given) width.
% Does NOT scale line width/text width! Needs the package "environ"!
%
% Usage:
%
% \begin{myscaletikzwidth}{\textwidth}
%   \begin{tikzpicture}[scale=\tikzscale]
%     ..
%   \end{tikzpicture}
% \end{myscaletikzwidth}
%
\makeatletter
\newsavebox{\measure@tikzpicture}
\NewEnviron{myscaletikzwidth}[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

(I got that code from another topic on Stackexchange.)

This code works perfectly fine, however it doesn't work anymore, when I turn on externalization (graphics will then be created in their original size).

How can I make auto-scaling work in conjunction with externalization?

I am using the modern externalization approach that has been introduced with newer Tikz versions ("\tikzexternalize").

Maybe some hack with writing "\def\tikzscale{MyDeterminedScaleFactor}" to an external file and inputting that file afterwards?

Could you please help me with that?

Best Answer

The problem is that the external library will automatically assign names for each encountered tikzpicture - but in your case, you effectively have one name and two pictures.

One could think about a solution which assigns individual file names for each of the two involved pictures - but that is a waste of time because you are never interested in the first one.

So, I propose the following solution: we check if we are currently generating an external picture. If so, we will always typeset the first (which is necessary to determine the picture's size) and we will externalize the second one. If we are currently NOT exporting the (current) picture, we can safely assume that there is an external picture ready at hand - and use it.

Here is the modification. It requires that \usetikzlibrary{external} has been loaded (although it does not necessarily need to be active).

% Automagically scale a Tikz picture, so it has the desired (given) width.
% Does NOT scale line width/text width! Needs the package "environ"!
%
% Usage:
%
% \begin{myscaletikzwidth}{\textwidth}
%   \begin{tikzpicture}[scale=\tikzscale]
%     ..
%   \end{tikzpicture}
% \end{myscaletikzwidth}
%
\makeatletter
\newsavebox{\measure@tikzpicture}
\NewEnviron{myscaletikzwidth}[1]{%
  \tikzifexternalizingnext{%
      \def\tikz@width{#1}%
      \def\tikzscale{1}\begin{lrbox}{\measure@tikzpicture}%
      \tikzset{external/export next=false,external/optimize=false}% force translation of this BODY (and do not optimize it away as it would usually do):
      \BODY
      \end{lrbox}%
      \pgfmathparse{#1/\wd\measure@tikzpicture}%
      \edef\tikzscale{\pgfmathresult}%
      \BODY
  }{% this will re-use an existing external graphics:
    \BODY
  }%
}
\makeatother

EDIT: You are right, there are problems with the todonotes package. It is (unfortunately) incompatible with the tikz external image. Apparently,

\usepackage{todonotes}
\makeatletter
\renewcommand{\todo}[2][]{%
   \tikzexternaldisable\@todo[#1]{#2}\tikzexternalenable%
}
\makeatother

helps here.

Related Question