10

I am looking for an option to will an arbitrary shape (box, circle or something defined via fit library) with custom text.

For example instead of filling it with dots as here

\documentclass[10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{patterns}
\begin{document}
\begin{tikzpicture}
\draw[pattern=dots, pattern color=green] (0, 0) rectangle (4, 4);
\end{tikzpicture}
\end{document}

I would like to fill it with custom text (e.g. every dot is replaced by the text and the spacing of the text is adjusted accordingly such that it does not overlap).

  • Is this not suitable for you? : tex.stackexchange.com/questions/67032/… – ferahfeza Nov 19 '18 at 8:21
  • @ferahfeza That's indeed close, but I would like to fill it repeatedly with the same text like "bla bla bla bla" all over the shape. I don't have a text or paragraph which I want to fit into the shape. I could of course copy "bla"/the text as often as needed (try and error process) but that would be less nice ;-). – bonanza Nov 19 '18 at 11:01
12
+50

I think it might not be possible to use text in actual patterns, but I would be happy to be proven wrong. Here are two ways to accomplish what you want without using patterns.

  • The first one uses a node to print the text and then \clips it. For more information on clipping, see section 2.11 (p36) of the pgf manual (for version 3.1.1).
  • The second one uses the path picture key to fill the area bounded by a path with this text. This is explained in section 15.6 of the pgf manual (for version 3.1.1).

What \TeXture[<text>]{<width>}{<height>} does is fill an area slightly less wide than <width> and slightly less high than <height> with copies of <text>. (Less because <text> that would need to be cut off simply isn't shown.) Both the name and the contents of this command were taken from an exercise in the TeXbook. The \leaders primitive, on which \TeXture relies, is e.g. explained in section 9.2 (p99) of TeX by topic.

\documentclass[tikz]{standalone}

\newcommand*\TeXture[3][\TeX]{\leavevmode\hbox to #2{\leaders\vbox to #3{\leaders\hbox{#1}\vfil}\hfil}}

\begin{document}

\begin{tikzpicture}
    \begin{scope}
        \clip (0,0) circle[radius=2];
        \node at (0,0) {\TeXture{5cm}{5cm}};
    \end{scope}
\end{tikzpicture}

\begin{tikzpicture}
    \path[path picture={\node at (path picture bounding box.center) {\TeXture{5cm}{5cm}};}] 
         (0,0) circle[radius=2];
\end{tikzpicture}

\end{document}

This produces two pages that are (as far as I can tell) identical:

output

Remark: if you want correct line spacing you can use \TeXture[bla\strut]{5cm}{5cm}.


Addendum

Here's a version with a more TikZ-friendly syntax that allows for some customisation! The preamble is a little complicated, but it's easy to use.

I've defined four keys:

  • TeXture=<text> turns TeXturing on and sets the text that is to be used (default: \TeX);
  • TeXture x sep=<length> and TeXture y sep=<length> determine the horizontal and vertical separation repsectively (default: 0pt and 0pt);
  • TeXture sep=<length> sets both TeXture x sep and TeXture y sep.

An earlier version also required you to specify the size of the area that needs to be filled, but this is no longer necessary. The new version extracs this information and repeats the text an appropriate number of times

\documentclass[tikz,margin=1mm]{standalone}

\newcommand*\tikzTeXture[1]{% %% <- Does the actual filling
  \begingroup
    \setbox0=\vbox spread \pgfkeysvalueof{/tikz/TeXture y sep}{\vfil%
               \hbox spread \pgfkeysvalueof{/tikz/TeXture x sep}{\hfil#1\hfil}\vfil}%
    \def\fillareasize{\pgfpointdiff %% <- size of area to be filled
      {\pgfpointanchor{path picture bounding box}{south west}}%
      {\pgfpointanchor{path picture bounding box}{north east}}}%
    \pgfextractx{\dimen0}{\fillareasize}% %% <- width of area to be filled
    \pgfextracty{\dimen2}{\fillareasize}% %% <- height of area to be filled
    \leavevmode\hbox to \dimexpr\dimen0+\wd0{%
      \cleaders\vbox to \dimexpr\dimen2+\ht0+\dp0{
        \cleaders\box0\vfil
      }\hfil
    }%
  \endgroup
}

\tikzset{TeXture/.style={path picture={
  \node[anchor=center,text width=,text height=]
        at (path picture bounding box.center) {\tikzTeXture{#1}};}
}}
\tikzset{TeXture/.default=\TeX}
\tikzset{TeXture x sep/.initial=0pt}
\tikzset{TeXture y sep/.initial=0pt}
\tikzset{TeXture sep/.style={/tikz/TeXture x sep=#1,/tikz/TeXture y sep=#1}}

\begin{document}

\begin{tikzpicture}
    \draw[domain=0:360,samples=128,fill=red!80!black,line join=round,
          TeXture=\LaTeX,TeXture sep=2pt]
         plot ({2*sin(\x)^3},{(13*cos(\x)-5*cos(2*\x)-2*cos(3*\x)-cos(4*\x))/8}) -- cycle;
\end{tikzpicture}

\end{document}

I ♥️ LaTeX

This version should be compatible with the positioning library btw.

  • 1
    truly amazing, is there a way I can control the horizonal and vertical space between the different repetitions? – bonanza Feb 11 at 20:11
  • 1
    A simple way to effectively add space is by making the contents of the optional argument taller or wider. To make it wider you can just add some extra horizontal space with e.g. \TeXture[bla ]{…}{…} or \TeXture[\bla\hspace{…}]{…}{…}. If you want normal baseline separation you can use \TeXture[bla\strut]{…}{…}, but you could also set the distance between consecutive baselines to e.g. 12pt with \TeXture[{\raisebox{0pt}[12pt][0pt]{bla}}]{…}{…} or \TeXture[{\rule[-6pt]{0pt}{12pt}}bla]{…}{…}. (The extra {} are there because you're not allowed to use [] inside an optional argument.) – Circumscribe Feb 11 at 21:01
  • 1
    @bonanza The problem is that the options you give to the outer node are inherited by the node inside the path picture because of how the TikZ' key-value system works. There are actually two problematic keys text width=3cm and below right=…. The first you can undo by providing text width= (no value) to the inner key, and the second one is, well, less easy. – Circumscribe Feb 11 at 23:12
  • 1
    @bonanza: I found a question about the path picture-positioning incompatibility here! You should add anchor=center to the inner node (in addition to text width=). So \node[circle,draw,minimum width=3.5cm, minimum height=2cm,below right=1cm and 2cm of a,text width=3cm,path picture={\node[text width=,anchor=center] at (path picture bounding box.center) {\TeXture{5cm}{5cm}};}] (test) {test}; should work. – Circumscribe Feb 11 at 23:50
  • 1
    @bonanza: I've created a more TikZy syntax for customisation in the meantime (see ↑↑). The last version should be compatible with the positioning library. – Circumscribe Feb 12 at 0:00

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.