9

I would like to create some split rectangles whose widths are defined by the length of a designated line. I would like to do this through a key such as in:

\documentclass[border=4pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes.multipart}
\newlength\aetmplength

\begin{document}

\begin{tikzpicture}[
  set text to width of/.code={%%
    \settowidth\aetmplength{#1}%%
    \typeout{--->`#1`::\the\aetmplength}%%
    \pgfkeysalso{text width=\the\aetmplength}%%
  },
  ]
  \node[draw,
        shape=rectangle split,
        rectangle split parts=2,
        set text to width of=This is the top of the node,
        %text width=0.95in
        ] () 
        {
         This is the top of the node
         \nodepart{two}
           first item\\
           second item\\
           \ldots};
\end{tikzpicture}

\end{document}

But this doesn't work. I could easily spend sometime working around this. So this isn't a problem of how to set the length I want. What I want to understand is why this definition for the key isn't doing what I want it to do.

  • 1
    It is \nullfont inside the tikz picture. – percusse May 3 '15 at 17:42
  • @percusse I'm not sure I understand what you mean. What's \nullfont? And whatever that is, is there a way to work around it? – A.Ellett May 3 '15 at 17:44
  • Sorry my mistaken presumption. I meant: \nullfont is the primitive, roughly speaking, that maps the font definition to zero. Hence in the code, your text input is expanded to zero width as text is mapped to nullfont. You can place things into a temporary box and measure its width instead. TikZ does that to ignore any dangling text commands etc. within the picture. – percusse May 3 '15 at 17:50
  • @percusse I don't think I'm getting what you're talking about. I tried something along the lines of \settowidth\aetmplength{\mbox{#1}} and I tried \sbox\aetmpbox{#1}\aetmplength\wd\aetmpbox, but neither approach is working. – A.Ellett May 3 '15 at 18:50
  • Try this as the code of the key : \pgfutil@selectfont\settowidth\aetmplength{#1}\pgfkeysalso{text width=\the\aetmplength} – percusse May 3 '15 at 23:29
9

The explanation, that TikZ is using \nullfont is already given in percusse's answer. Therefore \settowidth will not work as expected.

\pgfmathsetlength{width("#1")}

Instead of \settowidth the pgfmath function width can be used instead:

\pgfmathsetlength\aetmplength{width("#1")}

Full example:

\documentclass[border=4pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes.multipart}
\newlength\aetmplength

\begin{document}

\begin{tikzpicture}[
  set text to width of/.code={%%
    % \settowidth\aetmplength{#1}%%
    \pgfmathsetlength\aetmplength{width("#1")}%
    \typeout{--->`#1`::\the\aetmplength}%%
    \pgfkeysalso{text width=\the\aetmplength}%%
  },
  ] 
  \node[draw,
        shape=rectangle split,
        rectangle split parts=2,
        set text to width of=This is the top of the node,
        %text width=0.95in
        ] ()
        {   
         This is the top of the node
         \nodepart{two}
           first item\\
           second item\\
           \ldots};
\end{tikzpicture}

\end{document}

Result

\pgftext{...}

Another way is \pgftext, which escapes back to TeX, where \settowidth will work again. Since the argument of \pgftext is processed inside a group, \aetmplength is assigned globally and \setwidth act on a local temporary dimen register. Local and global assignments should not be mixed on the same control sequence for memory reasons.

\pgftext{%
  \settowidth{\dimen0}{#1}%%
  \global\aetmplength=\dimen0\relax
}%

node font

The latest version can also be used to respect the value of option node font. The value is stored in macro \tikz@node@textfont. Without \makeatletter and \makeatother the control sequence can also be specified by \csname and \endcsname:

\documentclass[border=4pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes.multipart}
\newlength\aetmplength

\begin{document}

\begin{tikzpicture}[node font=\tiny,
  set text to width of/.code={%%
    \pgftext{%
      \csname tikz@node@textfont\endcsname
      \settowidth{\dimen0}{#1}%%
      \global\aetmplength=\dimen0\relax
    }%
    \typeout{--->`#1`::\the\aetmplength}%%
    \pgfkeysalso{text width=\the\aetmplength}%%
  },
  ] 
  \node[draw,
        shape=rectangle split,
        rectangle split parts=2,
        set text to width of=This is the top of the node,
        %text width=0.95in
        ] ()
        {   
         This is the top of the node
         \nodepart{two}
           first item\\
           second item\\
           \ldots};
\end{tikzpicture}

\end{document}

Result with node font setting

8

TikZ/PGF design is done in such a way that its regular syntax doesn't have any spurious whitespace leaks or unintended characters are printed out inside the code etc. One way of doing it is to map the current font to the special primitive \nullfont.

This causes that you cannot simply handle text width without recreating the original non-\nullfont state. Just to give some context, below is taken from the pgf@picture environment begin definition:

    \begingroup%
      \let\pgf@setlengthorig=\setlength%
      \let\pgf@addtolengthorig=\addtolength%
      \let\pgf@selectfontorig=\selectfont%  <== Records the original
      \let\setlength=\pgf@setlength%
      \let\addtolength=\pgf@addtolength%
      \let\selectfont=\pgf@selectfont% <== Overwrites
      \nullfont\spaceskip0pt\xspaceskip0pt% <== Resets font
      \setbox\pgf@layerbox@main\hbox to0pt\bgroup%
        \begingroup%

Once we know this then you can temporarily go back to original definition inside a group get the width and survive the group.

\documentclass[border=4pt,tikz]{standalone}
\usetikzlibrary{shapes.multipart}
\makeatletter
\tikzset{set text to width of/.code={%%
\begingroup%
\pgfutil@selectfont%
\settowidth\pgf@xc{#1}% <== PGF internal scratch registers xa,ya,xb,yb,xc,yc
\edef\temp{\noexpand\pgfkeysalso{text width=\the\pgf@xc}}%<- Nonzero
\expandafter\endgroup\temp%%
\typeout{--->`#1`::\the\pgf@xc}%<-Still zero!
  }
}
\makeatother

\begin{document}
\begin{tikzpicture}
  \node[draw,
        align=center,%<- for line breaks
        shape=rectangle split,
        rectangle split parts=2,
        set text to width of=This is looooooooonnnnnnnnger than the top,
        ] () 
        {
         This is the top of the node
         \nodepart{two}
           first item\\
           second item\\
           \ldots};
\end{tikzpicture}
\end{document}

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.