定义 TikZ 样式来缩放节点的内容

定义 TikZ 样式来缩放节点的内容

我正在尝试定义一种scale content=<factor>应用于 TikZ 图片中的节点对象的样式。其目的是缩放节点的内容而不改变节点的外部尺寸。我使用以下定义取得了相对较好的进展:

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

这很有效,除非我指定text width取决于\textwidth如下面的第三个示例(完整的 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}

截屏

显然,TikZ 不会text width直接扩展,这将解决问题:

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

可是,进去之后execute at begin node才发现已经太晚了,似乎\textwidth已经发生了某种变化。

那么如何才能获得节点的扩展值呢text width

或者我应该采取一条完全不同的路线?

答案1

\textwidth通过 a 更改minipage为给定的宽度。现在,如果使用, TikZ 已经使用了 aminipage本身。解决方案是使用 new而不是 进行缩放。否则,相对于 outer 的值将被考虑两次!text width\textwidth\tikz@text@widthtext width\textwidth

您可以从第 3116 行开始找到相关代码tikz.code.tex

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

\pgfutil@minipage是正常的\minipage宏(即 ~ \begin{minipage})并且\tikz@atbegin@node是插入的位置execute at begin node

应用于您的 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}

结果

相关内容