% language=us runpath=texruns:manuals/metafun

\startcomponent metafun-text

\environment metafun-environment

\startchapter[reference=sec:typesetting,title={Typesetting in \METAPOST}]

\startintro

It is said that a picture tells more than a thousand words. So you might expect
that text in graphics becomes superfluous. Out of experience we can tell you that
this is not the case. In this chapter we explore the ways to add text to
\METAPOST\ graphics, and let you choose whether or not to have it typeset by
\TEX.

\stopintro

\startsection[title={The process}]

\index{text}

The \METAPOST\ program is about graphics, not about text. Its ancestor was made
for creating fonts and in order to do so, it actually had some limited
capabilities to add text to a graphic, like coordinates to points. Now, when
you really want to add typeset text, we need something more. The traditional way
to add some text to a \METAPOST\ graphic was to use this:

\starttyping
btex Some text to be typeset by \TEX etex
\stoptyping

The machinery was set up in such a way that these snippets got delegated to \TEX,
and the resulting \DVI\ converted back into a a \METAPOST\ picture with objects
that refer to a font and character. Although the \METAPOST\ program could handle
that by calling out to \TEX, we never used that method. In \MKII\ we had two
flows: either direct processing, or collect all texts and process them between
runs. Because the backend (conversion to \PDF) was under \CONTEXT\ control we
could do more than usual. You can find some information in the older manuals.

In \CONTEXT\ \MKIV\ we started using the library variant of \METAPOST. Its
integration in \LUATEX\ made it possible to do all text rendering runtime at high
speed. This change has an enormous speed gain: when this manual had about 425
pages, on my laptop with mobile 3840QM processor, one run of this document takes
18 seconds (14.5 with \LUAJITTEX) and that includes loading a bunch of (outline)
fonts and processing some 2200 \METAPOST\ images. While writing the first version
of this manual runtime was upto 50 times slower for half the number of pages so
compared to \MKII\ we have gained a lot.

In \MKIV\ we still have two internal runs per \METAPOST\ graphic that has text:
the first time the texts snippets are collected and afterwards handed over to
\LUA\ that tells the \TEX\ ending to process it. The dimensions are saved and
used in a second processing of the graphic. Contrary to \MKII\ the user doesn't
see these two runs: they appear as one. In \in {figure} [fig:text:mkiv] this is
illustrated.

\startFLOWchart[mkiv]
    \startFLOWcell
      \name       {metapost 1}
      \location   {1,1}
      \shape      {action}
      \text       {\METAPOST\ \type{textext 1}}
      \connection [rl] {lua 1}
    \stopFLOWcell
    \startFLOWcell
      \name       {lua 1}
      \location   {2,1}
      \shape      {action}
      \text       {\LUA}
      \connection [rl] {context 1}
      \connection [bb] {metapost 2}
    \stopFLOWcell
    \startFLOWcell
      \name       {context 1}
      \location   {3,1}
      \shape      {action}
      \text       {\CONTEXT}
    \stopFLOWcell
    \startFLOWcell
      \name       {metapost 2}
      \location   {4,1}
      \shape      {action}
      \text       {\METAPOST\ \type{textext 2}}
      \connection [bb] {lua 1}
      \connection [rl] {backend 1}
    \stopFLOWcell
    \startFLOWcell
      \name       {backend 1}
      \location   {5,1}
      \shape      {action}
      \text       {backend}
    \stopFLOWcell
\stopFLOWchart

In the later versions of \MKIV\ it was possible to jump out to \TEX\ immediately
and save the second run, although there were cases where this was not possible
(reliable). In \LMTX\ we always have one run. This is summarized in \in {figure}
[fig:text:mkiv]. All this has to do with the fact that \TEX, \METAPOST\ and \LUA\
are integrated but still kind of independent. When you expand a macro (that
triggers processing of a graphic) you end up in a call to the \METAPOST\ library
but \TEX\ is still in that macro. In \LUATEX\ and more advanced in \LUAMETATEX\
you can sort of spawn a subprocess in \TEX\ (called local control) and indeed
process the text immediately and use the calculated dimension after that has been
done. All that \METAPOST\ needs is dimensions as it the backend that eventually
will include the text.

\startplacefigure[title={How \TEX\ and \METAPOST\ work together (\MKIV).},reference=fig:text:mkiv]
    \FLOWchart[mkiv]
\stopplacefigure

\startFLOWchart[lmtx]
    \startFLOWcell
      \name       {context 1}
      \location   {1,1}
      \shape      {action}
      \text       {\CONTEXT}
      \connection [rl] {lua 1}
    \stopFLOWcell
    \startFLOWcell
      \name       {lua 1}
      \location   {2,1}
      \shape      {action}
      \text       {\LUA}
      \connection [lr] {context 1}
      \connection [rl] {metapost 1}
    \stopFLOWcell
    \startFLOWcell
      \name       {metapost 1}
      \location   {3,1}
      \shape      {action}
      \text       {\METAPOST\ \type{textext}}
      \connection [lr] {lua 1}
      \connection [rl] {backend 1}
    \stopFLOWcell
    \startFLOWcell
      \name       {backend 1}
      \location   {5,1}
      \shape      {action}
      \text       {backend}
    \stopFLOWcell
\stopFLOWchart

\startplacefigure[title={How \TEX\ and \METAPOST\ work together (\LMTX).},reference=fig:text:lmtx]
    \FLOWchart[lmtx]
\stopplacefigure

\stopsection

\startsection[title={Environments}]

\index{environments}

The \type {textext} command is the main text handling command and it is used in many places
in this manual. It takes a string.

In \MKII\ text was processed between runs or immediately but still by running an
independent \TEX\ run. That has some consequences when the text was to share its
properties with the main document. This means that in \MKII\ we pass
characteristics of the layout, spacing, fonts, color and optionally an
environment that a user has set up. This is mentioned in the older manual but no
longer relevant in \MKIV\ and \LMTX. Because we handle the text runtime we
automatically use the same style and document properties.

This means that in code like the following, a Palatino font will be used because
that is what we use in this document.

\starttyping
\startMPcode
draw btex Meta is a female lion! etex xysized (TextWidth,TextHeight) ;
\stopMPcode
\stoptyping

But even when we still support \type {btex} we strongly advise users to use this:

\starttyping
\startMPcode
draw textext("Meta is a female lion!") xysized (TextWidth,TextHeight) ;
\stopMPcode
\stoptyping

In this example as well in the next one you see that we access \type {TextWidth}
directly. In older examples in the \CONTEXT\ distribution you will notice that we
serialize \TEX\ registers like \type {\textwidth}. This is still valid but less
preferable. Here \type {TextWidth} is actually a macro that calls out to \LUA\
from where we fetch the value of \type {\textwidth} and pipe it back to
\METAPOST\ in a way that makes it look like we have a variable.

\startbuffer[lioncode]
\startMPcode
numeric w, h ; w := TextWidth ; h := w/2 ;

picture p ; p := textext.urt("Meta is a female lion!") xysized (w,h) ;
picture q ; q := textext.urt("Meta is a female lion!") xysized (w,h) ;

path b ; b := boundingbox p ; draw p withcolor "darkyellow" ;

draw textextanchor(p) withpen pencircle scaled 5mm withcolor "darkblue" ;

for i = (.28w,.90h), (.85w,.90h), (w,.05h) :
  picture r ; r := q ;
  path s ; s := (fullsquare xscaled .05w yscaled .4h) shifted i ;
  clip r to s ;
  draw r  withcolor "darkred";
endfor ;

setbounds currentpicture to b ;
\stopMPcode
\stopbuffer

\typebuffer[lioncode]

\in {Figure} [lionclip] shows the previous sentence in a slightly different look.
You may consider coloring the dots to be an exercise in clipping.

\startplacefigure[title={An example of clipping.},reference=lionclip]
  \getbuffer[lioncode]
\stopplacefigure

In this example we show that the origin of the text. By default it gets centered
but the \type {urt} suffix moves it. Here is another example of showing the
origin:

\startbuffer
\startMPcode
picture p ; p := thetextext.top("foo",(10,1));
picture q ; q := thetextext.lft("foo",(1,10));

draw textextanchor(p) withpen pencircle scaled 2mm withcolor "darkred" ;
draw textextanchor(q) withpen pencircle scaled 1mm withcolor "white" ;

draw p;
draw q;
\stopMPcode
\stopbuffer

\typebuffer

It gives the following:

\startlinecorrection[blank]
\getbuffer
\stoplinecorrection

\stopsection

\startsection[title={Labels}]

\index{labels}

In \METAPOST\ you can use the \type {label} macro to position text at certain
points.

\starttyping
label("x", origin) ;
\stoptyping

The font and scale are determined by two variables, \type {defaultfont} and \type
{defaultscale}, the former expecting the name of a font in the form of a string,
the latter expecting a numeric to be used in the scaling of the font. Should you
choose not to set these yourself, they default to \type {"Mono"} and \type
{1.0}, respectively. However, you can change the defaults as follows:

\starttyping
defaultfont  := "texgyrepagella-regular*default" ;
defaultscale := 1.2 ;
\stoptyping

These settings selects Pagella at about 12pt. You can also set these variables
to \CONTEXT\ related values. For \CONTEXT\ graphics they are set to:

\starttyping
defaultfont  := "\truefontname{Regular}*default" ;
defaultscale := \the\bodyfontsize/10 ;
\stoptyping

This means that they will adapt themselves to the current body font (in this
document we get \truefontname {Regular}) and the current size of the bodyfont
(here \the\bodyfontsize/10).

Normally you can stick to the default and forget about these details. More important
is how you anchor labels. This is best shown with some examples:

\startbuffer
\startMPcode
  path p ; p := fullcircle scaled 3cm ;

  drawarrow p ;

  dotlabel     ("test",point 0 of p) withcolor red ;
  dotlabel.rt  ("rt",  point 1 of p) ;
  dotlabel.urt ("urt", point 2 of p) ;
  dotlabel.top ("top", point 3 of p) ;
  dotlabel.ulft("ulft",point 4 of p) ;
  dotlabel.lft ("lft", point 5 of p) ;
  dotlabel.llft("llft",point 6 of p) ;
  dotlabel.bot ("bot", point 7 of p) ;
  dotlabel.lrt ("lrt", point 8 of p) ;
\stopMPcode
\stopbuffer

\typebuffer \startlinecorrection[blank] \getbuffer \stoplinecorrection

\startbuffer
\startMPcode
  path p ; p := fullcircle scaled 3cm ;

  drawarrow p ;

  dotlabel     ("test",p) ;
  dotlabel.rt  ("rt",  p) ;
  dotlabel.urt ("urt", p) ;
  dotlabel.top ("top", p) ;
  dotlabel.ulft("ulft",p) ;
  dotlabel.lft ("lft", p) ;
  dotlabel.llft("llft",p) ;
  dotlabel.bot ("bot", p) ;
  dotlabel.lrt ("lrt", p) ;
\stopMPcode
\stopbuffer

\typebuffer \startlinecorrection[blank] \getbuffer \stoplinecorrection

\startbuffer
\startMPcode
  p := (origin..right..up..left) scaled 3cm ;

  drawarrow p ;

  label     ("test",p) ;
  label.rt  ("rt",  p) ;
  label.urt ("urt", p) ;
  label.top ("top", p) ;
  label.ulft("ulft",p) ;
  label.lft ("lft", p) ;
  label.llft("llft",p) ;
  label.bot ("bot", p) ;
  label.lrt ("lrt", p) ;
\stopMPcode
\stopbuffer

\typebuffer \startlinecorrection[blank] \getbuffer \stoplinecorrection

\startbuffer
\startMPcode
  picture p ; p := image(drawarrow (origin..right..up..left) scaled 3cm ;) ;

  draw p ;
  draw bbox p dashed evenly ;

  label     ("test",p) ;
  label.rt  ("rt",  p) ;
  label.urt ("urt", p) ;
  label.top ("top", p) ;
  label.ulft("ulft",p) ;
  label.lft ("lft", p) ;
  label.llft("llft",p) ;
  label.bot ("bot", p) ;
  label.lrt ("lrt", p) ;
\stopMPcode
\stopbuffer

\typebuffer \startlinecorrection[blank] \getbuffer \stoplinecorrection

\startbuffer
\startMPcode
  picture p ; p := image(drawarrow (origin..right..up..left) scaled 3cm ;) ;

  draw p ;
  draw bbox p dashed evenly ;

  draw textext     ("test") shifted theoffset     (p) ;
  draw textext.lft ("rt")   shifted theoffset.rt  (p) ;
  draw textext.llft("urt")  shifted theoffset.urt (p) ;
  draw textext.bot ("top")  shifted theoffset.top (p) ;
  draw textext.lrt ("ulft") shifted theoffset.ulft(p) ;
  draw textext.rt  ("lft")  shifted theoffset.lft (p) ;
  draw textext.urt ("llft") shifted theoffset.llft(p) ;
  draw textext.top ("bot")  shifted theoffset.bot (p) ;
  draw textext.ulft("lrt")  shifted theoffset.lrt (p) ;
\stopMPcode
\stopbuffer

\typebuffer \startlinecorrection[blank] \getbuffer \stoplinecorrection

\stopsection

\startsection[title={Text along a path}]

\index{text}

In the next example we will use a special mechanism for building graphics step by
step. The advantage of this method is that we can do intermediate calculations in
\TEX. Our objective is to write a macro that draws text along a circular path.
While doing so we want to achieve the following:

\startitemize[packed]
    \startitem
        the text should be properly kerned, i.e.\ the spacing between characters
        should be optimal,
    \stopitem
    \startitem
        the position on the circle should vary, and
    \stopitem
    \startitem
        the radius of the circle should vary.
    \stopitem
\stopitemize

This code below is a bit rusted but it illustrates the principles. In \MKIV\ and
\LMTX\ we have typesetting along a curve built in so we no longer show the \TEX\
code that integrates it. This implementation is not the most straightforward one,
but by doing it step by step, at least we see what is involved.

We let the bodyfont match the font used in this document, and define \type
{RotFont} to be the regular typeface, the one you are reading right now, but
bold.

\startbuffer
\definefont[RotFont][RegularBold*default]
\stopbuffer

\typebuffer \getbuffer

Since \METAPOST\ is unaware of kerning, we have to use \TEX\ to keep track of the
positions. We will split the text into tokens (often characters) and store the
result in an array of pictures (\type {pic}). We will also store the accumulated
width in an array (\type {len}). The number of characters is stored in~\type {n}.
In a few paragraphs we will see why the other arrays are needed.

While defining the graphic, we need \TEX\ to do some calculations. Therefore, we
will use \type {\startMPdrawing} to stepwise construct the definition. The basic
pattern we will follow is:

\starttyping
\resetMPdrawing
\startMPdrawing
  metapost code
\stopMPdrawing
tex code
\startMPdrawing
  metapost code
\stopMPdrawing
\MPdrawingdonetrue
\getMPdrawing
\stoptyping

In the process, we will use a few variables. We will store the individual
characters of the text in the variable \type {pic}, its width in \type {wid} and
the length of the string so far in \type {len}. Later we will use the \type {pos}
array to store the position where a character ends up. The variable \type {n}
holds the number of tokens.

\startbuffer[init]
\resetMPdrawing
\startMPdrawing
  picture pic[] ;
  numeric wid[], len[], pos[], n ;
  wid[0] := len[0] := pos[0] := n := 0 ;
\stopMPdrawing
\stopbuffer

\typebuffer[init]

We also started fresh by resetting the drawing. From now on, each start command
will add some more to this graphic. The next macro is responsible for collecting
the data. Each element is passed on to \TEX, using the \type {btex} construct.
So, \METAPOST\ itself will call \TEX !

\startbuffer[toks]
\def\whatever#1%
  {\appendtoks#1\to\MPtoks
   \setbox\MPbox=\hbox{\bfd\the\MPtoks}%
   \startMPdrawing
     n := n + 1 ; len[n] := \the\wd\MPbox ;
   \stopMPdrawing
   \startMPdrawing[-]
     pic[n] := textext("\bfd\setstrut\strut#1") ;
     pic[n] := pic[n] shifted - llcorner pic[n] ;
   \stopMPdrawing}

\handletokens MetaPost is Fun!\with\whatever
\stopbuffer

\typebuffer[toks]

We use the low level \CONTEXT\ macro \type {\appendtoks} to extend the token list
\type {\MPtoks}. The \type {\handletokens} macro passes each token (character) of
\typ {MetaPost is Fun!} to the macro \type {\whatever}. The tokens are appended
to the token register \type {\MPtoks} (already defined). Then we typeset the
content of \type {\MPtoks} in \type {\MPbox} (also already defined). The width of
the box is passed to \METAPOST\ and stored in \type {len}.

By default the content of the drawing is expanded, which means that the macro is
replaced by its current meaning, so the current width ends up in the \METAPOST\
file. The next part of the drawing, starting with \type {btex}, puts the token in
a picture. This time we don't expand the drawing, since we want to pass font
information. Here, the \type {[-]} suppresses expansion of \typ {btex \bfd #1
etex}. The process is iterated by \type {\handletokens} for each character of the
text \typ {MetaPost is Fun!}.

Before we typeset the text, now available in pieces in \type {pic}, in a circle,
we will first demonstrate what they look like. You may like to take a look at the
file \type {mpgraph.mp} to see what is passed to \METAPOST.

\startbuffer[test]
\startMPdrawing
  pair len ; len := origin ;
  for i=1 upto n :
    draw pic[i] shifted len ;
    draw boundingbox pic[i] shifted len
      withpen pencircle scaled .25pt withcolor red ;
    len := len+(xpart urcorner pic[i]-xpart llcorner pic[i],0) ;
  endfor ;
\stopMPdrawing
\stopbuffer

\typebuffer[test]

\startbuffer[show]
\MPdrawingdonetrue\getMPdrawing
\stopbuffer

We can call up this drawing with \type {\getMPdrawing}, but first we inform the
compiler that our \METAPOST\ drawing is completed.

\typebuffer[show]

This results in:

\startlinecorrection[blank]
\getbuffer[init,toks,test,show]
\stoplinecorrection

Compare this text with the text as typeset by \TEX:

\blank \start \bfd MetaPost is Fun!\par \stop \blank

and you will see that the text produced by \METAPOST\ is not properly kerned.
When putting characters after each other, \TEX\ uses the information available in
the font, to optimize the spacing between characters, while \METAPOST\ looks at
characters as separate entities. But, since we have stored the optimal spacing in
\type {len}, we can let \METAPOST\ do a better job. Let's first calculate the
correction needed.

\startbuffer[kern]
\startMPdrawing
  for i=1 upto n :
    wid[i] := abs(xpart urcorner pic[i] - xpart llcorner pic[i]) ;
    pos[i] := len[i]-wid[i] ;
  endfor ;
\stopMPdrawing
\stopbuffer

\typebuffer[kern]

This compares well to the text as typeset by \TEX:

\blank \start \bfd MetaPost is Fun!\par \stop \blank

We can now use the values in \type {pos} to position the pictures according to
what \TEX\ considered to be the best (relative) position.

\startbuffer[test]
\startMPdrawing
  for i=1 upto n :
    draw pic[i] shifted (pos[i],0) ;
    draw boundingbox pic[i] shifted (pos[i],0)
      withpen pencircle scaled .25pt withcolor red ;
  endfor ;
\stopMPdrawing
\stopbuffer

\typebuffer[test]

That this correction is adequate, is demonstrated in the next graphic. If you
look closely, you will see that for instance the \quote {o} is moved to the left,
under the capital \quote {P}.

\startlinecorrection[blank]
\getbuffer[init,toks,kern,test,show]
\stoplinecorrection

When we want to position the pictures along a circle, we need to apply some
rotations, especially because we want to go clockwise. Since we don't want to use
\quote {complicated} math or more advanced \METAPOST\ code yet, we will do it in
steps.

\startbuffer[swap]
\startMPdrawing
  for i=1 upto n:
    pic[i] := pic[i] rotatedaround(origin,-270) ;
  endfor ;
\stopMPdrawing
\stopbuffer

\typebuffer[swap]

\startlinecorrection[blank]
\getbuffer[init,toks,kern,swap,test,show]
\stoplinecorrection

\startbuffer[cent]
\startMPdrawing
  for i=1 upto n :
    pic[i] := pic[i]
      shifted (0,ypart -.5[ulcorner pic[i],llcorner pic[i]]) ;
  endfor ;
\stopMPdrawing
\stopbuffer

We will now center the pictures around the baseline. Centering comes down to
shifting over half the height of the picture. This can be expressed by:

\starttyping
ypart -.5[ulcorner pic[i],llcorner pic[i]]
\stoptyping

but different ways of calculating the distance are possible
too.

\typebuffer[cent]

So, now we have:

\startlinecorrection[blank]
\getbuffer[init,toks,kern,swap,cent,test,show]
\stoplinecorrection

When we typeset on a (half) circle, we should map the actual length onto a
partial circle. We denote the radius with an~\type {r} and shift the pictures to
the left.

\startbuffer[shif]
\startMPdrawing
  numeric r ; r := len[n]/pi ;
  for i=1 upto n :
    pic[i] := pic[i] shifted (-r,0) ;
  endfor ;
\stopMPdrawing
\stopbuffer

\typebuffer[shif]

You can now use the following code to test the current state of the pictures. Of
course this code should not end up in the final definitions.

\startbuffer[test]
\startMPdrawing
  draw origin
    withpen pencircle scaled 5pt withcolor red ;
  for i=1 upto n :
    draw pic[i] ;
    draw boundingbox pic[i]
      withpen pencircle scaled .25pt withcolor red ;
  endfor ;
\stopMPdrawing
\stopbuffer

\typebuffer[test]

\startlinecorrection[blank]
\getbuffer[init,toks,kern,swap,cent,shif,test,show]
\stoplinecorrection

Later we will write a compact, efficient macro to take care of rotation. However,
for the moment, so as not to overwhelm you with complicated code, we will rotate
each individual picture with the following code fragment.

\startbuffer[rots]
\startMPdrawing
  numeric delta, extra, radius, rot[] ;

  delta  := extra := radius := 0 ;

  for i=1 upto n :
    rot[i] := extra+delta-((pos[i]+.5wid[i])/len[n])*(180+2delta) ;
  endfor ;
\stopMPdrawing
\stopbuffer

\typebuffer[rots]

Here we introduce a few variables that we can use later to tune the result a bit.
With \type {delta}, the space between the characters can be increased, while
\type {extra} rotates the whole string around the origin. The \type {radius}
variable can be used to increase the distance to the origin. Without these
variables, the assignment would have been:

\starttyping
rot[i] := ((pos[i]+.5wid[i])/len[n])*180 ;
\stoptyping

Placing the pictures is now rather easy:

\startbuffer[done]
\startMPdrawing
  for i=1 upto n :
    draw pic[i] shifted (-radius,0) rotatedaround(origin,rot[i]) ;
  endfor ;
\stopMPdrawing
\stopbuffer

\typebuffer[done]

The pictures are now positioned on half a circle, properly kerned.

\startlinecorrection[blank]
\getbuffer[init,toks,kern,swap,cent,shif,rots,done,show]
\stoplinecorrection

A bit more insight is given in the next picture:

\startbuffer[test]
\startMPdrawing
  def moved(expr i) =
    shifted (-radius,0) rotatedaround(origin,rot[i])
  enddef ;
  pickup pencircle scaled .5pt ;
  for i=1 upto n :
    draw pic[i] moved(i) ;
    draw boundingbox pic[i] moved(i) withcolor red ;
    draw origin -- center pic[i] moved(i) withcolor green ;
  endfor ;
  draw tcircle scaled 2r withcolor blue ;
\stopMPdrawing
\stopbuffer

\startlinecorrection[blank]
\getbuffer[init,toks,kern,swap,cent,shif,rots,test,show]
\stoplinecorrection

This was defined as follows. The path variable \type {tcycle} is predefined to
the top half of a fullcircle.

\typebuffer[test]

We will now package all of this into a nice, efficient macro, using, of course,
the predefined scratch registers \type {\MPtoks} and \type {\MPbox}. First we
define the token processor. Note again the expansion inhibition switch \type
{[-]}. This code is presented as an example of how these mechanissm evolved in
\CONTEXT.

\startbuffer
\def\processrotationtoken#1%
  {\appendtoks#1\to\MPtoks
   \setbox\MPbox=\hbox{\RotFont\the\MPtoks}%
   \startMPdrawing
     n := n + 1 ; len[n] := \the\wd\MPbox ;
   \stopMPdrawing
   \startMPdrawing[-]
     pic[n] := textext("\RotFont\setstrut\strut#1") ;
     pic[n] := pic[n] shifted - llcorner pic[n] ;
   \stopMPdrawing}
\stopbuffer

\typebuffer

\getbuffer

The main macro is a bit more complicated but by using a few scratch numerics, we
can keep it readable.

\startbuffer
\def\rotatetokens#1#2#3#4% delta extra radius tokens
  {\vbox\bgroup
   \MPtoks\emptytoks
   \resetMPdrawing
   \startMPdrawing
     picture pic[] ;
     numeric wid, len[], rot ;
     numeric delta, extra, radius, n, r ;
     len[0] := n := 0 ;
     delta  := #1 ; extra := #2 ; radius := #3 ;
   \stopMPdrawing
   \handletokens#4\with\processrotationtoken
   \startMPdrawing
     r := len[n]/pi ;
     for i=1 upto n :
       wid := abs(xpart lrcorner pic[i] -
                  xpart llcorner pic[i]) ;
       rot := extra + delta -
              ((len[i]-.5wid)/len[n]) * (180+2delta) ;
       draw pic[i]
         rotatedaround (origin,-270) shifted (-r-radius,
         ypart -.5[ulcorner pic[i], llcorner pic[i]])
         rotatedaround (origin,rot) ;
     endfor ;
   \stopMPdrawing
   \MPdrawingdonetrue
   \getMPdrawing
   \resetMPdrawing
   \egroup}
\stopbuffer

\typebuffer

\getbuffer

\startbuffer
\startcombination[3*1]
  {\rotatetokens {0} {0}{0}{Does it work ok?}} {A}
  {\rotatetokens{20} {0}{0}{Does it work ok?}} {B}
  {\rotatetokens{20}{30}{0}{Does it work ok?}} {C}
\stopcombination
\stopbuffer

We can use this macro as follows:

\typebuffer

\startlinecorrection[blank]
\getbuffer
\stoplinecorrection

The previous macro is not really an example of generalization, but we used it for
demonstrating how to build graphics in a stepwise way. If you put the steps in
buffers, you can even combine steps and replace them at will. This is how we made
the previous step by step examples: We put each sub||graphic in a buffer and then
called the ones we wanted.

Next we show how it is done today in \LMTX. Instead of wrapping it in a macro we
just do all in \METAPOST. Watch how we use the key|/|value interface:

\startbuffer
\startMPcode
  draw lmt_followtext [
    path   = halfcircle rotated 45 scaled 4cm,
    text   = "Does it work ok?",
    spread = true,
    trace  = true,
  ] ;
  draw lmt_followtext [
    path   = halfcircle rotated -30 scaled 3cm,
    text   = "Does it work ok?",
    spread = false,
    trace  = true,
  ] shifted (4cm,0) ;
  draw lmt_followtext [
    path   = reverse halfcircle scaled 2cm,
    text   = "Does it work ok?",
    spread = true,
    trace  = true,
  ] shifted (8cm,0) ;
\stopMPcode
\stopMPcode
\stopbuffer

\typebuffer

\startlinecorrection[blank]
\getbuffer
\stoplinecorrection

\stopsection

\startsection[title={Using shapes}]

Sometimes, others may say oftentimes, we are in need for some fancy typesetting.
If we want to typeset a paragraph of text in a non standard shape, like a circle,
we have to fall back on \type {\parshape}. Unfortunately, \TEX\ is not that
strong in providing the specifications of more complicated shapes, unless you are
willing to do some complicated arithmetic \TEX. Given that \METAPOST\ knows how
to deal with shapes, the question is: \quotation {Can \METAPOST\ be of help?}

In the process of finding out how to deal with this, we first define a simple
path. Because we are going to replace pieces of code, we will compose the graphic
from components. First, we create the path.

\startbuffer
\startuseMPgraphic{text path}
  path p ; p := ((0,1)..(-1,0)..(1,0)--cycle) scaled 65pt ;
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

This shape is not that beautiful, but it has a few characteristics that will help
us to identify bordercases.

\startbuffer
\startuseMPgraphic{text draw}
  drawarrow p withpen pencircle scaled 1pt withcolor red ;
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

Now we use \CONTEXT's \type {\includeMPgraphic} command to build our graphic from
the previously defined components.

\startbuffer
\startuseMPgraphic{text}
  \includeMPgraphic{text path}
  \includeMPgraphic{text draw}
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

When called with \type {\useMPgraphic{text}}, we get:

\startlinecorrection[blank]
\useMPgraphic{text}
\stoplinecorrection

For the moment we start the path at $(x=0,y>0)$, but later using more complicated
macros, we will see that we can use arbitrary paths.

We are going to split the path in two, and will use the points that make up the
bounding box as calcutated by \METAPOST. The next graphic shows one of these
points, the lower left corner, available as point \typ {llcorner p}.

\startbuffer
\startuseMPgraphic{text draw}
  draw             p withpen pencircle scaled 3pt withcolor red ;
  draw boundingbox p withpen pencircle scaled 1pt ;
  draw llcorner    p withpen pencircle scaled 5pt ;
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

\startlinecorrection[blank]
\useMPgraphic{text}
\stoplinecorrection

The five points that \METAPOST\ can report for each path or picture are:

\starttabulate[|Tl|l|]
\NC llcorner \NC lower left  corner            \NC \NR
\NC lrcorner \NC lower right corner            \NC \NR
\NC urcorner \NC upper right corner            \NC \NR
\NC ulcorner \NC upper left  corner            \NC \NR
\NC center   \NC intersection of the diagonals \NC \NR
\stoptabulate

If we want to typeset text inside this circle, we need to know where a line
starts and ends. Given that lines are horizontal and straight, we therefore need
to calculate the intersection points of the lines and the path. As a first step,
we calculate the top and bottom of the path and after that we split off the left
and right path.

\startbuffer
\startuseMPgraphic{text split}
  pair t, b ; path l, r ;

  t := (ulcorner p -- urcorner p) intersectionpoint p ;
  b := (llcorner p -- lrcorner p) intersectionpoint p ;

  l := p cutbefore t ; l := l cutafter b ;
  r := p cutbefore b ; r := r cutafter t ;
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

The \type {intersectionpoint} macro returns the point where two paths cross. If
the paths don't cross, an error is reported, when the paths cross more times,
just one point is returned. The \type {cutafter} and \type {cutbefore} commands
do as their names say and return a path.

In the \type {text split} code fragment, \type {t} and \type {b} are the top
points of the main path, while \type {l} and \type {r} become the left and right
half of path \type {p}.

We now draw the original path using a thick pen and both halves with a thinner
pen on top of the original. The arrows show the direction.

\startbuffer
\startuseMPgraphic{text draw}
  draw      p withpen pencircle scaled 3pt withcolor red ;
  drawarrow l withpen pencircle scaled 1pt withcolor green ;
  drawarrow r withpen pencircle scaled 1pt withcolor blue ;
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

We use \type {\includeMPgraphic} to assemble the components:

\startbuffer
\startuseMPgraphic{text}
  \includeMPgraphic{text path}
  \includeMPgraphic{text split}
  \includeMPgraphic{text draw}
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

This graphic is typeset with \type {\useMPgraphic{text}}:

\startlinecorrection[blank]
\useMPgraphic{text}
\stoplinecorrection

Before we are going to use them, we define some variables that specify the text.
We use a baseline distance of 8~points. The part of the line above the baseline
is 7.2~points, while the (maximum) depth is 2.8~points. These ratios are the ones
we use in \CONTEXT. Because we don't want the text to touch the circle so we
define an offset too.

\startbuffer
\startuseMPgraphic{text vars}
  MyOffset  := LineHeight/2 ;
  MyTopSkip := StrutHeight ;
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

We more or less achieve the offset by scaling the path. In doing so, we use the
width and height, which we call \type {hsize} and \type {vsize}, thereby
conforming to the \TEX\ naming scheme.

First we calculate both dimensions from the bounding box of the path. Next we
down scale the path to compensate for the offset. When done, we recalculate the
dimensions.

\startbuffer
\startuseMPgraphic{text move}
  pair t, b ; path q, l, r ;

  hsize := xpart lrcorner p - xpart llcorner p ;
  vsize := ypart urcorner p - ypart lrcorner p ;

  q := p xscaled ((hsize-2MyOffset)/hsize)
         yscaled ((vsize-2MyOffset)/vsize) ;

  hsize := xpart lrcorner q - xpart llcorner q ;
  vsize := ypart urcorner q - ypart lrcorner q ;
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

\startbuffer
\startuseMPgraphic{text split}
  t := (ulcorner q -- urcorner q) intersectionpoint q ;
  b := (llcorner q -- lrcorner q) intersectionpoint q ;

  l := q cutbefore t ; l := l cutafter b ;
  r := q cutbefore b ; r := r cutafter t ;
\stopuseMPgraphic
\stopbuffer

We adapt the \type {text split} code to use the reduced path
instead of the original.

\typebuffer \getbuffer

\startbuffer
\startuseMPgraphic{text draw}
  drawarrow p withpen pencircle scaled 1pt withcolor red ;
  draw      t withpen pencircle scaled 2pt ;
  draw      b withpen pencircle scaled 2pt ;
  drawarrow l withpen pencircle scaled 1pt withcolor green ;
  drawarrow r withpen pencircle scaled 1pt withcolor blue ;
\stopuseMPgraphic
\stopbuffer

In order to test what we have reached so far, we draw the original path, the left
and right part of the reduced path, and both the top and bottom point.

\typebuffer \getbuffer

Again we use \type {\includeMPgraphic} to combine the
components into a graphic.

\startbuffer
\startuseMPgraphic{text}
  \includeMPgraphic{text path} \includeMPgraphic{text vars}
  \includeMPgraphic{text move} \includeMPgraphic{text split}
  \includeMPgraphic{text draw}
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

Then we use \type {\useMPgraphic{text}} to call up the picture.

\startlinecorrection[blank]
\useMPgraphic{text}
\stoplinecorrection

The offset is not optimal. Note the funny gap at the top. We could try to fix
this, but there is a better way to optimize both paths.

We lower the top edge of \type {q}'s bounding box by \type {MyTopSkip}, then cut
any part of the left and right pieces of \type {q} that lie above it. Similarly,
we raise the bottom edge and cut off the pieces that fall below this line.

\startbuffer
\startuseMPgraphic{text cutoff}
  path tt, bb ;

  tt := (ulcorner q -- urcorner q) shifted (0,-MyTopSkip) ;
  bb := (llcorner q -- lrcorner q) shifted (0,StrutDepth) ;

  l := l cutbefore (l intersectionpoint tt) ;
  l := l cutafter  (l intersectionpoint bb) ;
  r := r cutbefore (r intersectionpoint bb) ;
  r := r cutafter  (r intersectionpoint tt) ;
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

Because we use \type {\includeMPgraphic} to construct the graphic, we can
redefine \type {text draw} to show the result of this effort.

\startbuffer
\startuseMPgraphic{text draw}
  drawarrow p withpen pencircle scaled 1pt withcolor red ;
  drawarrow l withpen pencircle scaled 1pt withcolor green ;
  drawarrow r withpen pencircle scaled 1pt withcolor blue ;
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

The \type {text} graphic now becomes:

\startbuffer
\startuseMPgraphic{text}
  \includeMPgraphic{text path}   \includeMPgraphic{text vars}
  \includeMPgraphic{text move}   \includeMPgraphic{text split}
  \includeMPgraphic{text cutoff} \includeMPgraphic{text draw}
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

Or, as graphic:

\startlinecorrection[blank]
\useMPgraphic{text}
\stoplinecorrection

We are now ready for an attempt to calculate the shape of the text. For each
line, we have to calculate the left and right intersection points, and since a
line has a height and depth, we have to determine which part touches first.

\startbuffer
\startuseMPgraphic{text calc}
  vardef found_point (expr lin, pat, sig) =
    pair a, b ;
    a := pat intersection_point (lin shifted (0,StrutHeight)) ;
    if intersection_found :
      a := a shifted (0,-StrutHeight) ;
    else :
      a := pat intersection_point lin ;
    fi ;
    b := pat intersection_point (lin shifted (0,-StrutDepth)) ;
    if intersection_found :
      if sig :
        if xpart b > xpart a : a := b shifted (0,StrutDepth) fi ;
      else :
        if xpart b < xpart a : a := b shifted (0,StrutDepth) fi ;
      fi ;
    fi ;
    a
  enddef ;
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

Instead of using \METAPOST's \type {intersectionpoint} macro, we use one that
comes with \CONTEXT. That way we don't get an error message when no point is
found, and can use a boolean flag to take further action. Since we use a \type
{vardef}, all calculations are hidden and the~\type {a} at the end is returned,
so that we can use this macro in an assignment. The \type {sig} variable is used
to distinguish between the beginning and end of a line (the left and right
subpath).

\startbuffer
\startuseMPgraphic{text step}
  path line; pair lll, rrr ;

  for i=MyTopSkip step LineHeight until vsize :

    line := (ulcorner q -- urcorner q) shifted (0,-i) ;

    lll := found_point(line,l,true ) ;
    rrr := found_point(line,r,false) ;
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

Here we divide the available space in lines. The first line starts at \type
{StrutHeight} from the top.

We can now finish our graphic by visualizing the lines. Both the height and depth
of the lines are shown.

\startbuffer
\startuseMPgraphic{text line}
    fill (lll--rrr--rrr shifted (0,StrutHeight)--lll
      shifted (0,StrutHeight)--cycle) withcolor .5white ;
    fill (lll--rrr--rrr shifted (0,-StrutDepth)--lll
      shifted (0,-StrutDepth)--cycle) withcolor .7white ;
    draw lll withpen pencircle scaled 2pt ;
    draw rrr withpen pencircle scaled 2pt ;
    draw (lll--rrr) withpen pencircle scaled .5pt ;
\stopuseMPgraphic

\startuseMPgraphic{text done}
  endfor ;
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

The result is still a bit disappointing.

\startbuffer
\startuseMPgraphic{text}
  \includeMPgraphic{text path}   \includeMPgraphic{text vars}
  \includeMPgraphic{text move}   \includeMPgraphic{text split}
  \includeMPgraphic{text cutoff} \includeMPgraphic{text draw}
  \includeMPgraphic{text calc}   \includeMPgraphic{text step}
  \includeMPgraphic{text line}   \includeMPgraphic{text done}
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer
\startlinecorrection[blank]
\useMPgraphic{text}
\stoplinecorrection

In order to catch the overflow at the bottom, we need to change the \type
{for}||loop a bit, so that the number of lines does not exceed the available
space. The test that surrounds the assignment of \type {vvsize} makes sure that
we get better results when we (on purpose) take a smaller height.

\startbuffer
\startuseMPgraphic{text step}
  path line; pair lll, rrr ; numeric vvsize ;

  if (StrutHeight+StrutDepth<LineHeight) :
    vvsize := vsize ;
  else :
    vvsize := (vsize div LineHeight) * LineHeight ;
  fi ;

  for i=MyTopSkip step LineHeight until vvsize :

    line := (ulcorner q -- urcorner q) shifted (0,-i) ;

    lll := found_point(line,l,true ) ;
    rrr := found_point(line,r,false) ;
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

\startlinecorrection[blank]
\useMPgraphic{text}
\stoplinecorrection

We can manipulate the heigth and depth of the lines to give different (and maybe
better) results.

\startbuffer
\startuseMPgraphic{text vars}
MyOffset  := .5LineHeight ;
MyTopSkip := StrutHeight ;
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

\startlinecorrection[blank]
\useMPgraphic{text}
\stoplinecorrection

Now that we know a bit how this works we demonstrate the implementation in \LMTX.
Where in \MKIV\ and \MKII\ we let \METAPOST\ pass the dimensions to \TEX\ via a
temporary file in \LMTX\ we communicate more directly. You can take a look at the
source code if you want to know how that works.

We start by defining four shapes. They are not really beautiful, but they
demonstrate what happens in border cases. For instance, too small first lines are
ignored. First we define a circle. Watch how the dimensions are set in the
graphic. The arguments passed to \type {lmt_parshape} are: path, an offset, an
additional horizontal and vertical displacement, the baseline distance, the
height and depth of the line, and the height of the first line (aka topskip). The
height and depth of a line are often called strut height and depth, with a strut
being an invisible character with maximum dimensions. Variables like \type
{StrutHeight} refer to values at the \TEX\ end. We play safe and group so that we
can save variables.

\startbuffer
\startuseMPgraphic{shapetest-1}
  begingroup ;
    save p ; path p ; p := fullcircle scaled 6cm ;
    lmt_parshape [
        path        = p,
        offset      = BodyFontSize/2,
        dx          = 0,           % default
        dy          = 0,           % default
        lineheight  = LineHeight,  % default
        strutheight = StrutHeight, % default
        strutdepth  = StrutDepth,  % default
        topskip     = StrutHeight, % default
    ] ;
    draw p withpen pencircle scaled 1pt ;
  endgroup ;
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

The second shape is a diamond. This is a rather useless shape, unless the text
suits the small lines at the top and bottom.

\startbuffer
\startuseMPgraphic{shapetest-2}
  begingroup ;
    save p ; path p ; p := fullsquare rotated 45 scaled 5cm ;
    lmt_parshape [
        path   = p,
        offset = BodyFontSize/2,
        trace  = true,
    ] ;
    draw p withpen pencircle scaled 1pt ;
  endgroup ;
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

The third and fourth shape demonstrate that providing a suitable offset is not
always trivial.

\startbuffer
\startuseMPgraphic{shapetest-3}
  begingroup ;
    save w, h, p ; path p ; w := h := 6cm ;
    p := (.5w,h) -- (   0,  h) -- (0,0) -- (w,0) &
         (  w,0) .. (.75w,.5h) .. (w,h) &  (w,h) -- cycle ;
    lmt_parshape [
        path   = p,
        offset = BodyFontSize/2,
    ] ;
    draw p withpen pencircle scaled 1pt ;
  endgroup ;
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

Contrary to the first three shapes, here we use a different path for the
calculations and the drawing. Watch carefully! If, instead of an offset, we pass
a path, \METAPOST\ is able to calculate the right dimensions and offsets. This is
needed, since we need these later on.

\startbuffer
\startuseMPgraphic{shapetest-4}
  begingroup ;
    save d, p, q, shape ; path p, q ; d := BodyFontSize/2;
    vardef shape(expr w, h, o) =
        (o,o) -- (w-o,o) & (w-o,o) .. (.75w-o,.5h) ..
        (w-2o,h-o) & (w-2o,h-o) -- (o,h-o) -- cycle
    enddef ;
    p := shape(6cm, 6cm, d) ;
    q := shape(6cm, 6cm, 0) ;
    lmt_parshape [
        path       = p,
        offsetpath = q,
        dx         = d,
        dy         = d,
        trace      = true,
    ] ;
    draw q withpen pencircle scaled 1pt ;
  endgroup ;
\stopuseMPgraphic
\stopbuffer

\typebuffer \getbuffer

Since we also want these graphics as backgrounds, we define them as overlays. If
you don't want to show the graphic, you may omit this step.

\startbuffer
\defineoverlay[shapetest-1][\useMPgraphic{shapetest-1}]
\defineoverlay[shapetest-2][\useMPgraphic{shapetest-2}]
\defineoverlay[shapetest-3][\useMPgraphic{shapetest-3}]
\defineoverlay[shapetest-4][\useMPgraphic{shapetest-4}]
\stopbuffer

\typebuffer \getbuffer

As text, we use a quote from Douglas R.~Hofstadter's book \quotation {Metamagical
Themas, Questing for the Essence of Mind and Pattern}. Watch how we pass a list
of shapes.

\startbuffer[text]
\startshapetext[shapetest-1,shapetest-2,shapetest-3,shapetest-4]
  \forgetall % as it says
  \setupalign[verytolerant,stretch,normal]%
  \samplefile{douglas}% Douglas R. Hofstadter
\stopshapetext
\stopbuffer

\typebuffer[text]

Finally we combine text and shapes. Since we also want a background, we use \type
{\framed}. The normal result is shown in \in {figure} [fig:shapes].

\startbuffer[shapes]
\startbuffer
\startcombination[2*2]
  {\framed[offset=overlay,frame=off,background=shapetest-1]{\getshapetext}} {shapetest-1}
  {\framed[offset=overlay,frame=off,background=shapetest-2]{\getshapetext}} {shapetest-2}
  {\framed[offset=overlay,frame=off,background=shapetest-3]{\getshapetext}} {shapetest-3}
  {\framed[offset=overlay,frame=off,background=shapetest-4]{\getshapetext}} {shapetest-4}
\stopcombination
\stopbuffer
\stopbuffer

\typebuffer[shapes]

\getbuffer[shapes]

By using a buffer we keep \type {\placefigure} readable. You might have noticed
that for two shapes we turned on tracing.

\startbuffer[a]
\placefigure
  [here][fig:shapes]
  {A continuous text, typeset in a non||standard shape,
   spread over four areas, and right aligned.}
  {\getbuffer}
\stopbuffer

\startbuffer[b]
\placefigure
  [here][fig:shapes]
  {A continuous text, typeset in a non||standard shape,
   spread over four areas.}
  {\scale[factor=max,height=.9\textheight]{\getbuffer}}
\stopbuffer

\typebuffer[a]

\doifmodeelse{screen}{\getbuffer[text,b]}{\getbuffer[text,a]}

\blank

We can combine all those tricks, although the input is somewhat fuzzy. First we
define a quote typeset in a circular paragraph shape where we use the defaults.

\startbuffer[shape]
\startuseMPgraphic{center}
  lmt_parshape [
    path = fullcircle scaled 8cm,
  ]
\stopuseMPgraphic

\startshapetext[center]
  \samplefile{douglas}%
\stopshapetext

\defineoverlay[center][\useMPgraphic{center}]
\stopbuffer

\typebuffer[shape]

We will surround this text with a circular line, that we define as follows. By
using a buffer we keep things organized.

\startbuffer
\startuseMPgraphic{circle}
  save p ; path p ;
  p := reverse fullcircle rotatedaround(origin,90)
    xscaled OverlayWidth yscaled OverlayHeight ;

  draw lmt_followtext [
    path   = p,
    text   = "This is just a dummy text, kerned by \TeX\ and typeset
              in a circle using \MetaPost.\quad",
    spread = true,
    trace  = true,
  ] ;

% draw p ;
\stopuseMPgraphic

\defineoverlay[edge][\useMPgraphic{circle}]
\stopbuffer

\typebuffer \getbuffer

The text and graphics come together in a framed text:

\startbuffer
\startplacefigure[title={One more time Hofstadter's quotation (normal).}]
  \getbuffer[shape,quote]%
  \framed
    [offset=24pt,
     background=edge,
     frame=off,
     backgroundoffset=-18pt]
    {\getshapetext}
\stopplacefigure
\stopbuffer

\typebuffer \getbuffer

\stopsection

\stopchapter

\stopcomponent
