# Defining a TikZ style to scale the content of a node

I am trying to define a scale content=<factor> style to be applied to node objects in TikZ pictures. It's purpose is to scale the content of the node without changing the node's outer dimensions. I got relatively far with the following definition:

% Key to scale the content of a node by a factor #1
\tikzset{scale content/.style={
execute at begin node={
\numdef{\scale@content@nest}{\scale@content@nest+1} % increase nesting counter
\ifnumequal{\scale@content@nest}{1}{                % do not nest
\pgfmathsetmacro{\scale@content}{#1}
\begin{lrbox}{\@tempboxa}
\ifx\tikz@text@width\pgfutil@empty\else
% if 'text width' has been specified it must been scaled reciprocally
% we achieve this by typesetting in a accordingly dimensioned minipage
\pgfmathsetlength{\@tempdima}{\tikz@text@width/#1}
\begin{minipage}{\@tempdima}
\fi
}{}
},
execute at end node={
\ifnumequal{\scale@content@nest}{1}{
\ifx\tikz@text@width\pgfutil@empty\else
\end{minipage}
\fi
\end{lrbox}
\scalebox{\scale@content}{\usebox\@tempboxa}
}{}
\numdef{\scale@content@nest}{\scale@content@nest-1} % decrease nesting counter
}
}}


This works well, unless I specify text width depending on \textwidth as shown in the third example below (complete MWE):

\documentclass{article}
\usepackage{tikz,etoolbox}
\makeatletter

% Key to scale the content of a node by a factor #1
\tikzset{scale content/.style={
execute at begin node={
\numdef{\scale@content@nest}{\scale@content@nest+1} % increase nesting counter
\ifnumequal{\scale@content@nest}{1}{                % do not nest
\pgfmathsetmacro{\scale@content}{#1}
\begin{lrbox}{\@tempboxa}
\ifx\tikz@text@width\pgfutil@empty\else
% if 'text width' has been specified, scale it accordingly by using a minipage
\pgfmathsetlength{\@tempdima}{\tikz@text@width/#1}
\begin{minipage}{\@tempdima}
\fi
}{}
},
execute at end node={
\ifnumequal{\scale@content@nest}{1}{
\ifx\tikz@text@width\pgfutil@empty\else
\end{minipage}
\fi
\end{lrbox}
\scalebox{\scale@content}{\usebox\@tempboxa}
}{}
\numdef{\scale@content@nest}{\scale@content@nest-1} % decrease nesting counter
}
}}
\makeatother

\begin{document}
\tikzset{every node/.style={draw, fill=yellow!40}}

\tikz\node[text width=3cm]{Not scaled, text width is 3cm};
\par
\tikz\node[scale content=0.7, text width=3cm]{scaled by 0.7, text width is 3cm, everything is fine};
\par
\tikz\node[scale content=0.7, text width=0.5\textwidth, text=red]{scaled by 0.7, text width is 0.5\textbackslash textwidth, so why aren't we using it?};
\par
\pgfmathsetlengthmacro{\mywidth}{0.5\textwidth}
\tikz\node[scale content=0.7, text width=\mywidth]{scaled by 0.7, text width is 0.5\textbackslash textwidth, manually expanded before, so this is how it should look like};
\end{document}


Apparently, TikZ does not expand text width directly, which would solve the problem:

% original definition from tikz.code.tex:779
\tikzoption{text width}{\def\tikz@text@width{#1}}

% Alternative definition that would work
\tikzoption{text width}{\pgfmathsetlengthmacro{\tikz@text@width}{#1}}


However, once I am inside execute at begin node it seems already to be to late, apparently \textwidth has already be transformed somehow.

So how can I get the expanded value of a node's text width?

Or should I take a completely different route?

-
You have a couple of spurious spaces in your code. You should add % directly after each { or } appearing at the end of the line. –  Martin Scharrer Jun 30 '11 at 9:49
Also note the existance of the adjustbox package which allows for \begin{adjustbox}{scale=<factor>} ... \end{adjustbox} which could replace the lrbox/\scalebox combination. It effectively does the same internally. However your current code is more efficient. –  Martin Scharrer Jun 30 '11 at 9:52
@Martin: Actually, I do use adjustboxin my real implementation and I like it pretty much :-) I only substituted it for this MWE, so that others do not have to download an extra package that is not yet part of standard latex installations. (PS: Thanks for the spurious spaces hint!) –  Daniel Jun 30 '11 at 14:03
I see :-) The adjustbox package is part of the current TeXLive and MikTeX. However, "standard LaTeX installations" like the one of Ubuntu unfortunately use an older version :-( –  Martin Scharrer Jun 30 '11 at 14:40

The \textwidth macro is changed by a minipage to the given width. Now, TikZ already uses a minipage itself if text width is used. The solution would be to use the new \textwidth for scaling instead of \tikz@text@width. Otherwise a text width value relative to the outer \textwidth would be taken into account twice!

You can find the relevant code in tikz.code.tex starting at line 3116:

\ifx\tikz@text@width\pgfutil@empty%
\else%
\begingroup%
\pgfmathsetlength{\pgf@x}{\tikz@text@width}%
\pgfutil@minipage[t]{\pgf@x}\leavevmode\hbox{}%
\tikz@text@action%
\fi%
\tikz@atbegin@node%


Here \pgfutil@minipage is the normal \minipage macro (i.e. ~\begin{minipage}) and \tikz@atbegin@node is where the execute at begin node is inserted.

\documentclass{article}
\usepackage{tikz,etoolbox}
\makeatletter

% Key to scale the content of a node by a factor #1
\tikzset{scale content/.style={
execute at begin node={
\numdef{\scale@content@nest}{\scale@content@nest+1} % increase nesting counter
\ifnumequal{\scale@content@nest}{1}{                % do not nest
\pgfmathsetmacro{\scale@content}{#1}
\begin{lrbox}{\@tempboxa}
\ifx\tikz@text@width\pgfutil@empty\else
% if 'text width' has been specified, scale it accordingly by using a minipage
% Here \textwidth now represents the original text width of the node.
\pgfmathsetlength{\@tempdima}{\textwidth/\scale@content}
\begin{minipage}{\@tempdima}
\fi
}{}
},
execute at end node={
\ifnumequal{\scale@content@nest}{1}{
\ifx\tikz@text@width\pgfutil@empty\else
\end{minipage}
\fi
\end{lrbox}
\scalebox{\scale@content}{\usebox\@tempboxa}
}{}
\numdef{\scale@content@nest}{\scale@content@nest-1} % decrease nesting counter
}
}}
\makeatother

\begin{document}
\tikzset{every node/.style={draw, fill=yellow!40}}

\tikz\node[text width=3cm]{Not scaled, text width is 3cm};
\par
\tikz\node[scale content=0.7, text width=3cm]{scaled by 0.7, text width is 3cm, everything is fine};
\par
\tracingassigns=1
\tikz\node[scale content=0.7, text width=0.5\textwidth, text=red]{scaled by 0.7, text width is 0.5\textbackslash textwidth, so why aren't we using it?};
\par
\pgfmathsetlengthmacro{\mywidth}{0.5\textwidth}
\tikz\node[scale content=0.7, text width=\mywidth]{scaled by 0.7, text width is 0.5\textbackslash textwidth, manually expanded before, so this is how it should look like};
\end{document}


-
Thanks for the answer and good explanation! –  Daniel Jun 30 '11 at 14:43
@Daniel: You are welcome. BTW: I didn't fixed the spaces in this code example but simply copied your original code. –  Martin Scharrer Jun 30 '11 at 14:48