Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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}

screen shot

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?

share|improve this question
    
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
1  
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
1  
@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

1 Answer 1

up vote 5 down vote accepted

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.

Applied to your 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
        % 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}

Result

share|improve this answer
    
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

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.