Draw Text in different shapes

I just threw in the \outline macro (requires pdflatex) for the fun of it.

The \outline stuff is in the preamble, whereas the necking code is in the main document. I originally stole the code that makes up \outline from Malipivo at TikZ: halo around text?

What I do is create a box (\mytext) with the text (padded a little so that none gets subsequently clipped), and then successively apply a \clipbox that vertically slices the text box \cuts times. On each cut, I apply a \scalebox to shrink the text as a function of the cut number and \dip, which represents the maximum necking fraction. The function I choose here is parabolic, though others can be developed as well.

\documentclass{article}
\usepackage{ifthen,trimclip,calc,fp,graphicx,xcolor}
%%%%% FOR TEXT OUTLINING
\input pdf-trans
\newbox\qbox
\def\usecolor#1{\csname\string\color@#1\endcsname\space}
\newcommand\bordercolor[1]{\colsplit{1}{#1}}
\newcommand\fillcolor[1]{\colsplit{0}{#1}}
\newcommand\outline[1]{\leavevmode%
  \def\maltext{#1}%
  \setbox\qbox=\hbox{\maltext}%
  \boxgs{Q q 2 Tr \thickness\space w \fillcol\space \bordercol\space}{}%
  \copy\qbox%
}
\newcommand\colsplit[2]{\colorlet{tmpcolor}{#2}\edef\tmp{\usecolor{tmpcolor}}%
  \def\tmpB{}\expandafter\colsplithelp\tmp\relax%
  \ifnum0=#1\relax\edef\fillcol{\tmpB}\else\edef\bordercol{\tmpC}\fi}
\def\colsplithelp#1#2 #3\relax{%
  \edef\tmpB{\tmpB#1#2 }%
  \ifnum `#1>`9\relax\def\tmpC{#3}\else\colsplithelp#3\relax\fi
}
\bordercolor{blue}
\fillcolor{yellow}
\def\thickness{.5}
%%%%%
\begin{document}
\Huge
\edef\dip{.5}% percent to depress the amplitude
\def\cuts{200}% Number of cuts
\newsavebox\mytext
\savebox{\mytext}{\kern.3pt\textbf{\textsf{\outline{Valley Text}}}\kern.3pt}% TEXT
\newlength\clipsize
\FPeval{\myprod}{1/cuts}
\clipsize=\myprod\wd\mytext\relax
\newcounter{mycount}
\whiledo{\value{mycount}<\cuts}{%
  \stepcounter{mycount}%
  \edef\NA{\themycount}%
  \edef\NB{\the\numexpr\cuts-\themycount\relax}%
  \FPeval{\myprod}{1 - \dip*\NA*\NB*4/\cuts/\cuts}%
  \clipbox{%
    \value{mycount}\clipsize\relax{} %
    -1pt %
    \wd\mytext-\value{mycount}\clipsize-\clipsize\relax{} %
    -1pt%
  }{\scalebox{1}[\myprod]{\usebox{\mytext}}}%
}
\end{document}

enter image description here

Without the \outline code, the code is more manageable. (EDITED) Here I convert it into a macro \parabtext[<mode>]{<lift>}{<neck>}{<cuts>}{<content>} with several additions from above:

<mode> is 0 for narrow middle, and 1 for narrow ends;

<lift> is the fractional max-lift of the baseline during the transformation

<neck> is the fractional reduction in total height at the neck;

<cuts> are the number of vertical slices to apply to the box (too small, and it will look stair-stepped)

<content> is the stuff to put in a box and transform.

\documentclass{article}
\usepackage{ifthen,trimclip,calc,fp,graphicx,xcolor}
\newsavebox\mytext
\newcounter{mycount}
\newlength\clipsize
\newcommand\parabtext[5][0]{%
  \edef\neck{#3}% percent to depress the amplitude
  \def\cuts{#4}% Number of cuts
  \savebox{\mytext}{\kern.2pt#5\kern.2pt}% TEXT
  \FPeval{\myprod}{1/cuts}%
  \clipsize=\myprod\wd\mytext\relax%
  \setcounter{mycount}{0}%
  \whiledo{\value{mycount}<\cuts}{%
    \stepcounter{mycount}%
    \edef\NA{\themycount}%
    \edef\NB{\the\numexpr\cuts-\themycount\relax}%
    \FPeval{\myprod}{\NA*\NB*4/\cuts/\cuts}%
    \ifnum0#1=0\relax%
      \FPeval{\myprod}{1 - \neck*(\myprod)}%
    \else%
      \FPeval{\myprod}{1 - \neck*(1-\myprod)}%
    \fi%
    \clipbox{%
      \value{mycount}\clipsize\relax{} %
      -1pt %
      \wd\mytext-\value{mycount}\clipsize-\clipsize\relax{} %
      -1pt%
    }{\raisebox{#2\dimexpr\ht\mytext-\myprod\ht\mytext}{%
        \scalebox{1}[\myprod]{\usebox{\mytext}}}}%
  }%
}
\begin{document}
\Huge\centering\def\X{\textbf{XXX}}%
\parabtext{0}{.7}{200}{\textbf{\textcolor{brown}{Valley Text}}}\par
\X\parabtext{0}{.7}{200}{\X}\parabtext[1]{0}{.7}{200}{\X}\X\par
\X\parabtext{1}{.7}{200}{\X}\parabtext[1]{1}{.7}{200}{\X}\X\par
\X\parabtext{.425}{.7}{200}{\X}\parabtext[1]{.425}{.7}{200}{\X}\X
\end{document}

enter image description here

The most recently EDITED option, <mode>, changes the shape function of the transformation. In both modes, a function is evaluated

\FPeval{\myprod}{\NA*\NB*4/\cuts/\cuts}%

That is a parabola that is at a value of 1 at both ends of the box and a value of 0 at the middle of the box. Then, if the mode is 0, the transformation is

\FPeval{\myprod}{1 - \neck*(\myprod)}%

whereas, if not zero, the transformation is

\FPeval{\myprod}{1 - \neck*(1-\myprod)}%

This simple difference will produce the necked and barrelled versions, respectively.


PROPOSED METHODOLOGY

The method is somewhat computationally expensive, but it is intended to be used sparingly for the Wow! effect.

First, I'll show a slightly modified code that takes it, in effect, from a forward difference to a central difference, though it may cost a little more in computation.

But what I think works best is to set the number of slices in a user defined variable to a low number, say \def\slices{5} and develop your document on that basis. On the final compilation, when all is set in the final layout, then re\def\slices to the desired number, say {200}, and recompile one last time.

\documentclass{article}
\usepackage{ifthen,trimclip,calc,fp,graphicx,xcolor}
\newsavebox\mytext
\newcounter{mycount}
\newlength\clipsize
\newcommand\parabtext[5][0]{%
  \edef\neck{#3}% percent to depress the amplitude
  \def\cuts{#4}% Number of cuts
  \savebox{\mytext}{\kern.2pt#5\kern.2pt}% TEXT
  \FPeval{\myprod}{1/cuts}%
  \clipsize=\myprod\wd\mytext\relax%
  \setcounter{mycount}{0}%
  \whiledo{\value{mycount}<\cuts}{%
    \stepcounter{mycount}%
    \edef\NA{\themycount}%
    \edef\NB{\the\numexpr\cuts-\themycount\relax}%
    \FPeval{\myprod}{(\NA-.5)*(\NB+.5)*4/\cuts/\cuts}%
    \ifnum0#1=0\relax%
      \FPeval{\myprod}{1 - \neck*(\myprod)}%
    \else%
      \FPeval{\myprod}{1 - \neck*(1-\myprod)}%
    \fi%
    \clipbox{%
      \value{mycount}\clipsize-\clipsize\relax{} %
      -1pt %
      \wd\mytext-\value{mycount}\clipsize\relax{} %
      -1pt%
    }{\raisebox{#2\dimexpr\ht\mytext-\myprod\ht\mytext}{%
        \scalebox{1}[\myprod]{\usebox{\mytext}}}}%
  }%
}
\begin{document}
\def\slices{200}
\Huge\centering\def\X{\textbf{XXX}}%
\parabtext{0}{.7}{\slices}{\textbf{\textcolor{brown}{Valley Text}}}\par
\X\parabtext{0}{.7}{\slices}{\X}\parabtext[1]{0}{.7}{\slices}{\X}\X\par
\X\parabtext{1}{.7}{\slices}{\X}\parabtext[1]{1}{.7}{\slices}{\X}\X\par
\X\parabtext{.425}{.7}{\slices}{\X}\parabtext[1]{.425}{.7}{\slices}{\X}\X
\end{document}

Here is, for example, the output with \slices set to 5 for the lo-res compilation:

enter image description here

NOTE TO XeLaTeX USERS

The OP noted that this approach failed to work in XeLaTeX. After some study, I boiled the problem down to a bug in the trimclip package (\clipbox of a \scalebox not working properly in xelatex). Joseph Wright was kind enough to diagnose the issue and provide a patch for the trimclip code, when running in XeLaTeX.

\makeatletter
\ifdefined\XeTeXversion
  \def\@cliptoboxdim#1{%
    \setbox #1=\hbox{%
      \Gin@defaultbp\WIDTH{\wd #1}%
      \Gin@defaultbp \DEPTH {\dp #1}%
      \@tempdima \ht #1%
      \advance\@tempdima\dp#1%
      \Gin@defaultbp \TOTALHEIGHT {\@tempdima }%
      \special{pdf:literal q}% 
      \special{pdf:literal 0 -\DEPTH \space \WIDTH \space \TOTALHEIGHT \space re W n }%
      \rlap{\copy #1}%
      \special {pdf:literal Q}%
      \hskip\wd#1%
    }%
  }
\fi
\makeatother

Update

Using triangular mesh, we can approximate any continuous transformation.

\documentclass[border=9,tikz]{standalone}
\begin{document}

\pgfmathdeclarefunction{fx}{2}{\pgfmathparse{#1*(3+cos(#2*20))/3}}
\pgfmathdeclarefunction{fy}{2}{\pgfmathparse{#2*(3-cos(#1*20))/3}}
\pgfmathdeclarefunction{fxx}{2}{\pgfmathparse{fx(#1+1,#2)-fx(#1,#2)}}
\pgfmathdeclarefunction{fxy}{2}{\pgfmathparse{fy(#1+1,#2)-fy(#1,#2)}}
\pgfmathdeclarefunction{fyx}{2}{\pgfmathparse{fx(#1,#2+1)-fx(#1,#2)}}
\pgfmathdeclarefunction{fyy}{2}{\pgfmathparse{fy(#1,#2+1)-fy(#1,#2)}}

\tikz{
    \path(-15,-15)(15,15);
    \foreach\i in{-10,...,9}{
        \foreach\j in{-10,...,9}{
            \pgfmathsetmacro\aa{fxx(\i,\j)}
            \pgfmathsetmacro\ab{fxy(\i,\j)}
            \pgfmathsetmacro\ba{fyx(\i,\j)}
            \pgfmathsetmacro\bb{fyy(\i,\j)}
            \pgfmathsetmacro\xx{fx (\i,\j)}
            \pgfmathsetmacro\yy{fy (\i,\j)}
            \pgflowlevelobj{
                \pgfsettransformentries{\aa}{\ab}{\ba}{\bb}{\xx cm}{\yy cm}
            }{
                \fill[black!10](1,0)--(0,0)--(0,1);
                \clip(1,0)--(0,0)--(0,1)--cycle;
                \tikzset{shift={(-\i,-\j)}}
                \path(0,0)node{\includegraphics[width=20cm]{lena.png}};
            }
            \pgfmathsetmacro\aa{fxx(\i  ,\j+1)}
            \pgfmathsetmacro\ab{fxy(\i  ,\j+1)}
            \pgfmathsetmacro\ba{fyx(\i+1,\j  )}
            \pgfmathsetmacro\bb{fyy(\i+1,\j  )}
            \pgfmathsetmacro\xx{fx (\i+1,\j+1)}
            \pgfmathsetmacro\yy{fy (\i+1,\j+1)}
            \pgflowlevelobj{
                \pgfsettransformentries{\aa}{\ab}{\ba}{\bb}{\xx cm}{\yy cm}
            }{
                \clip(0,0)--(-1,0)--(0,-1)--cycle;
                \tikzset{shift={(-\i-1,-\j-1)}}
                \path(0,0)node{\includegraphics[width=20cm]{lena.png}};
            }
        }
    }
}

\end{document}

Old Answer

Just copying @Steven B. Segletes's idea, only that the grid is 2D, and is done in PGF/TikZ.

% !TEX program = XeLaTeX
% !TEX encoding = UTF-8 Unicode

\documentclass[border=9,tikz]{standalone}
\usepackage{fontspec}\setmainfont{Arial Unicode MS}

\begin{document}

\pgfmathdeclarefunction{fxx}{1}{\pgfmathparse{#1}}
\pgfmathdeclarefunction{fxy}{1}{\pgfmathparse{sin(222+30*#1)}}
\pgfmathdeclarefunction{fyx}{1}{\pgfmathparse{sin(20*#1)}}
\pgfmathdeclarefunction{fyy}{1}{\pgfmathparse{#1}}
\pgfmathdeclarefunction{gxx}{1}{\pgfmathparse{fxx(#1+1)-fxx(#1)}}
\pgfmathdeclarefunction{gxy}{1}{\pgfmathparse{fxy(#1+1)-fxy(#1)}}
\pgfmathdeclarefunction{gyx}{1}{\pgfmathparse{fyx(#1+1)-fyx(#1)}}
\pgfmathdeclarefunction{gyy}{1}{\pgfmathparse{fyy(#1+1)-fyy(#1)}}

\tikz{
    \path(-2,-2)(103,23);
    \foreach\i in{0,...,100}{
        \foreach\j in{0,...,20}{
            {
                \pgfmathsetmacro\aa{gxx(\i)}
                \pgfmathsetmacro\ab{gxy(\i)}
                \pgfmathsetmacro\ba{gyx(\j)}
                \pgfmathsetmacro\bb{gyy(\j)}
                \pgfmathsetmacro\xx{fxx(\i)+fyx(\j)}
                \pgfmathsetmacro\yy{fxy(\i)+fyy(\j)}
                \pgflowlevelobj{
                    \pgfsettransformentries{\aa}{\ab}{\ba}{\bb}{\xx cm}{\yy cm}
                }{
                    \clip(0,0)--(1,0)--(1,1)--(0,1)--cycle;
                    \draw[gray](1,0)--(0,0)--(0,1);
                    \path(-\i,-\j)+(50,10)node{\fontsize{500pt}{0}\selectfont Valley Text};
                }
            }
        }
    }
}

\end{document}

Some Math

In my code a transformation is defined by

(x,y) |--> ( fxx(x)+fyx(y) , fxy(x)+fyy(y) )

where fxx, fyx, fxy, fyy are good functions. Such transformation will send a square to a parallelogram. Coincidently, PDF supports affine transformation, which also send a square to a parallelogram. Thus I can use piecewise affine transformation to approximate the original transformation. The result is that any curve will remain connected, although not differentiable.

Tags:

Text

Tikz Pgf