0

How do I create a node using TikZ that has sections reserved for other purpose, and text flows around it?

I think I have found an answer, but it does not satisfy me.

In the MWE below,

  • The first 'Test Header' shows where I started: a node containing a header, and a level marker (i.e. what level you can qualify for the thing the header identifies). This takes up more space than I like, so I looked for another approach.
  • The second 'Another Header' shows a recent development, flipping the level marker inside the header box. Looks better, but...
  • The third 'Another Header With...' shows what happens when the text is long enough that it doesn't fit to the left of the level marker.
  • The fourth 'Another Header With...' shows what happens if I create a marker node taller than the header node that nominally contains it.

I'd like to have the text inside the TikZ node respect the level marker node and wrap around it. In this case it would come out looking something like

|Another Header With Lots [ 4 ]|
|of Text                       |

To date, I was able to do this all by setting properties on the header node (that would create marker nodes as needed). The markers and header text almost always were compatible, and I could work around the rest.

I think what I will need to do is create something like a minipage or other box, with a wrapfig or similar containing the markers, and then put that inside the header node. That is, build a complete picture of what I want, and then align it with the containing node.

Am I correct? Or is there an easier way?

If I am correct, how exactly would I do this? I suspect it would be something like:

  • create minipage the same width of the node, with margins/padding;
  • use wrapfig to make room for the markers;
  • create node tall and wide enough to exactly fit minipage, including marker boxes.

I'm hoping I don't need to do this, and I can accept two answers because I actually have two use cases. First is object headings in body text (textwidth boxes that can contain bigger markers), second is a graph showing how the items are connected and will have only a single marker in one corner.

\documentclass{article}

\usepackage{lipsum}
\usepackage[most]{tcolorbox}
\usepackage{tikz}
\usetikzlibrary{intersections,positioning,shapes.misc}

\tikzstyle{test} = [rectangle,draw,thick,fill=brown!30,text width=50mm,inner sep=1mm,font=\sffamily]

\tikzstyle{level} = [test,thin,fill=white,text width=8mm,inner sep=1mm,anchor=south east,font=\footnotesize\sffamily,align=center,yshift=-1\pgflinewidth,]

\tikzstyle{newlevel} = [level,anchor=north east,xshift=-1\pgflinewidth,]

\tikzstyle{gear} = [newlevel,text width=]


\title{TikZ MWE}
\author{Keith Davies}

\begin{document}

\maketitle

\noindent\lipsum[1]

\noindent\begin{tikzpicture}[-,node distance=30mm,auto]
\node[test] (header) {Test Header};
\node[level] at (header.north east) {4};
\end{tikzpicture}

\bigskip

\noindent\begin{tikzpicture}[-,node distance=30mm,thick]
\node[test] (short) {Another Header};
\node[newlevel] at (short.north east) {4};
\end{tikzpicture}

\bigskip

\noindent\begin{tikzpicture}[-,node distance=30mm,thick]
\node[test] (wide) {Another Header With Lots of Text};
\node[newlevel] at (wide.north east) {4};
\end{tikzpicture}

\bigskip

\noindent\begin{tikzpicture}[-,node distance=30mm,thick]
\node[test] (double) {Another Header With two rows in inset};
\node[gear] at (double.north east) {
  \begin{tabular}{@{}lr@{}}
  \bfseries{Cost} & 5 gp \\
  \bfseries{Market} & 15 gp \\
  \bfseries{Weight} & 4 lbs \\
  \end{tabular}};
\end{tikzpicture}

\noindent\lipsum[2-3]

\end{document}

Typically there is only a single line break needed, and I don't truly need to wrap the marker. The solution at TikZ: How to define a node with a custom inner sep on one side (e.g. on the right side of an rectangle)? could actually be pretty close to what I need. I'd like to see if I can get this into a single object (or rather, object-plus-marker) if I can.

1

A partial solution using wrapfig. Add the wrapfig package, change the definition of the gear node, and use a minipage with an empty wrapfigure:

\documentclass{article}

\usepackage{lipsum}
\usepackage[most]{tcolorbox}
\usepackage{tikz}
\usepackage{printlen}
\usetikzlibrary{intersections,positioning,shapes.misc}
\usepackage{wrapfig}

\tikzstyle{header} = [rectangle,draw,text width=40mm,inner sep=1mm,font=\sffamily,anchor=north west]

\tikzstyle{test} = [rectangle,draw,thick,fill=brown!30,text width=50mm,inner sep=1mm,font=\sffamily]    

\tikzstyle{level} = [test,thin,fill=white,text width=8mm,inner sep=1mm,anchor=south east,font=\footnotesize\sffamily,align=center,yshift=-1\pgflinewidth,]

\tikzstyle{newlevel} = [level,anchor=north east,xshift=-1\pgflinewidth,]

\tikzstyle{gear} = [newlevel,text width=,minimum height=3\baselineskip]


\title{TikZ MWE}
\author{Keith Davies}

\setlength{\columnsep}{1mm}

\begin{document}

\maketitle

\noindent\lipsum[1]

\noindent
\begin{tikzpicture}[-,node distance=30mm,thick]
\node[test] (wide) {
  \begin{minipage}{\textwidth}
    \begin{wrapfigure}[1]{r}{0.9cm} \end{wrapfigure}
    Another Header With Lots of Text... and even more than that to force multiple line wraps.
  \end{minipage}
  };
  \node[newlevel] at (wide.north east) {4};
\end{tikzpicture}

\bigskip

\noindent
\begin{tikzpicture}[-,node distance=30mm,thick]
\node[test,minimum height=3\baselineskip] (double) {
  \begin{minipage}{\textwidth}
    \begin{wrapfigure}[3]{r}{2cm} \end{wrapfigure}
    Another Header
  \end{minipage}
  };
\node[gear] at (double.north east) {
  \begin{tabular}{@{}lr@{}}
  \bfseries{Cost} & 5 gp \\
  \bfseries{Market} & 15 gp \\
  \bfseries{Weight} & 4 lbs \\
  \end{tabular}};
\end{tikzpicture}

\noindent\lipsum[2-3]

\end{document}

That is, each header node contains a minipage, the minipage has an empty figure that leaves space for the marker. This appears to meet most of my needs. I have yet to find a satisfactory way of getting the text in the second picture vertically aligned with the top of the node. Setting minimum height centers the minipage, setting text depth requires that I know how many more lines I need. \vfill\mbox{}, whether inside the minipage or not, gets me one more line. I can set the minipage height to 2.5\baselineskip (which for my use is actually probably safe, I don't expect to actually have three lines of text here) but I'd prefer a general solution... eventually I'm going to run into a case where I need more than two lines and it's going to drive me mad because I don't remember doing this.

| improve this answer | |

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.