metafun-text-lmtx.tex /size: 46 Kb    last modification: 2023-12-21 09:43
1% language=us runpath=texruns:manuals/metafun
2
3\startcomponent metafun-text
4
5\environment metafun-environment
6
7\startchapter[reference=sec:typesetting,title={Typesetting in \METAPOST}]
8
9\startintro
10
11It is said that a picture tells more than a thousand words. So you might expect
12that text in graphics becomes superfluous. Out of experience we can tell you that
13this is not the case. In this chapter we explore the ways to add text to
14\METAPOST\ graphics, and let you choose whether or not to have it typeset by
15\TEX.
16
17\stopintro
18
19\startsection[title={The process}]
20
21\index{text}
22
23The \METAPOST\ program is about graphics, not about text. Its ancestor was made
24for creating fonts and in order to do so, it actually had some limited
25capabilities to add text to a graphic, like coordinates to points. Now, when
26you really want to add typeset text, we need something more. The traditional way
27to add some text to a \METAPOST\ graphic was to use this:
28
29\starttyping
30btex Some text to be typeset by \TEX etex
31\stoptyping
32
33The machinery was set up in such a way that these snippets got delegated to \TEX,
34and the resulting \DVI\ converted back into a a \METAPOST\ picture with objects
35that refer to a font and character. Although the \METAPOST\ program could handle
36that by calling out to \TEX, we never used that method. In \MKII\ we had two
37flows: either direct processing, or collect all texts and process them between
38runs. Because the backend (conversion to \PDF) was under \CONTEXT\ control we
39could do more than usual. You can find some information in the older manuals.
40
41In \CONTEXT\ \MKIV\ we started using the library variant of \METAPOST. Its
42integration in \LUATEX\ made it possible to do all text rendering runtime at high
43speed. This change has an enormous speed gain: when this manual had about 425
44pages, on my laptop with mobile 3840QM processor, one run of this document takes
4518 seconds (14.5 with \LUAJITTEX) and that includes loading a bunch of (outline)
46fonts and processing some 2200 \METAPOST\ images. While writing the first version
47of this manual runtime was upto 50 times slower for half the number of pages so
48compared to \MKII\ we have gained a lot.
49
50In \MKIV\ we still have two internal runs per \METAPOST\ graphic that has text:
51the first time the texts snippets are collected and afterwards handed over to
52\LUA\ that tells the \TEX\ ending to process it. The dimensions are saved and
53used in a second processing of the graphic. Contrary to \MKII\ the user doesn't
54see these two runs: they appear as one. In \in {figure} [fig:text:mkiv] this is
55illustrated.
56
57\startFLOWchart[mkiv]
58    \startFLOWcell
59      \name       {metapost 1}
60      \location   {1,1}
61      \shape      {action}
62      \text       {\METAPOST\ \type{textext 1}}
63      \connection [rl] {lua 1}
64    \stopFLOWcell
65    \startFLOWcell
66      \name       {lua 1}
67      \location   {2,1}
68      \shape      {action}
69      \text       {\LUA}
70      \connection [rl] {context 1}
71      \connection [bb] {metapost 2}
72    \stopFLOWcell
73    \startFLOWcell
74      \name       {context 1}
75      \location   {3,1}
76      \shape      {action}
77      \text       {\CONTEXT}
78    \stopFLOWcell
79    \startFLOWcell
80      \name       {metapost 2}
81      \location   {4,1}
82      \shape      {action}
83      \text       {\METAPOST\ \type{textext 2}}
84      \connection [bb] {lua 1}
85      \connection [rl] {backend 1}
86    \stopFLOWcell
87    \startFLOWcell
88      \name       {backend 1}
89      \location   {5,1}
90      \shape      {action}
91      \text       {backend}
92    \stopFLOWcell
93\stopFLOWchart
94
95In the later versions of \MKIV\ it was possible to jump out to \TEX\ immediately
96and save the second run, although there were cases where this was not possible
97(reliable). In \LMTX\ we always have one run. This is summarized in \in {figure}
98[fig:text:mkiv]. All this has to do with the fact that \TEX, \METAPOST\ and \LUA\
99are integrated but still kind of independent. When you expand a macro (that
100triggers processing of a graphic) you end up in a call to the \METAPOST\ library
101but \TEX\ is still in that macro. In \LUATEX\ and more advanced in \LUAMETATEX\
102you can sort of spawn a subprocess in \TEX\ (called local control) and indeed
103process the text immediately and use the calculated dimension after that has been
104done. All that \METAPOST\ needs is dimensions as it the backend that eventually
105will include the text.
106
107\startplacefigure[title={How \TEX\ and \METAPOST\ work together (\MKIV).},reference=fig:text:mkiv]
108    \FLOWchart[mkiv]
109\stopplacefigure
110
111\startFLOWchart[lmtx]
112    \startFLOWcell
113      \name       {context 1}
114      \location   {1,1}
115      \shape      {action}
116      \text       {\CONTEXT}
117      \connection [rl] {lua 1}
118    \stopFLOWcell
119    \startFLOWcell
120      \name       {lua 1}
121      \location   {2,1}
122      \shape      {action}
123      \text       {\LUA}
124      \connection [lr] {context 1}
125      \connection [rl] {metapost 1}
126    \stopFLOWcell
127    \startFLOWcell
128      \name       {metapost 1}
129      \location   {3,1}
130      \shape      {action}
131      \text       {\METAPOST\ \type{textext}}
132      \connection [lr] {lua 1}
133      \connection [rl] {backend 1}
134    \stopFLOWcell
135    \startFLOWcell
136      \name       {backend 1}
137      \location   {5,1}
138      \shape      {action}
139      \text       {backend}
140    \stopFLOWcell
141\stopFLOWchart
142
143\startplacefigure[title={How \TEX\ and \METAPOST\ work together (\LMTX).},reference=fig:text:lmtx]
144    \FLOWchart[lmtx]
145\stopplacefigure
146
147\stopsection
148
149\startsection[title={Environments}]
150
151\index{environments}
152
153The \type {textext} command is the main text handling command and it is used in many places
154in this manual. It takes a string.
155
156In \MKII\ text was processed between runs or immediately but still by running an
157independent \TEX\ run. That has some consequences when the text was to share its
158properties with the main document. This means that in \MKII\ we pass
159characteristics of the layout, spacing, fonts, color and optionally an
160environment that a user has set up. This is mentioned in the older manual but no
161longer relevant in \MKIV\ and \LMTX. Because we handle the text runtime we
162automatically use the same style and document properties.
163
164This means that in code like the following, a Palatino font will be used because
165that is what we use in this document.
166
167\starttyping
168\startMPcode
169draw btex Meta is a female lion! etex xysized (TextWidth,TextHeight) ;
170\stopMPcode
171\stoptyping
172
173But even when we still support \type {btex} we strongly advise users to use this:
174
175\starttyping
176\startMPcode
177draw textext("Meta is a female lion!") xysized (TextWidth,TextHeight) ;
178\stopMPcode
179\stoptyping
180
181In this example as well in the next one you see that we access \type {TextWidth}
182directly. In older examples in the \CONTEXT\ distribution you will notice that we
183serialize \TEX\ registers like \type {\textwidth}. This is still valid but less
184preferable. Here \type {TextWidth} is actually a macro that calls out to \LUA\
185from where we fetch the value of \type {\textwidth} and pipe it back to
186\METAPOST\ in a way that makes it look like we have a variable.
187
188\startbuffer[lioncode]
189\startMPcode
190numeric w, h ; w := TextWidth ; h := w/2 ;
191
192picture p ; p := textext.urt("Meta is a female lion!") xysized (w,h) ;
193picture q ; q := textext.urt("Meta is a female lion!") xysized (w,h) ;
194
195path b ; b := boundingbox p ; draw p withcolor "darkyellow" ;
196
197draw textextanchor(p) withpen pencircle scaled 5mm withcolor "darkblue" ;
198
199for i = (.28w,.90h), (.85w,.90h), (w,.05h) :
200  picture r ; r := q ;
201  path s ; s := (fullsquare xscaled .05w yscaled .4h) shifted i ;
202  clip r to s ;
203  draw r  withcolor "darkred";
204endfor ;
205
206setbounds currentpicture to b ;
207\stopMPcode
208\stopbuffer
209
210\typebuffer[lioncode]
211
212\in {Figure} [lionclip] shows the previous sentence in a slightly different look.
213You may consider coloring the dots to be an exercise in clipping.
214
215\startplacefigure[title={An example of clipping.},reference=lionclip]
216  \getbuffer[lioncode]
217\stopplacefigure
218
219In this example we show that the origin of the text. By default it gets centered
220but the \type {urt} suffix moves it. Here is another example of showing the
221origin:
222
223\startbuffer
224\startMPcode
225picture p ; p := thetextext.top("foo",(10,1));
226picture q ; q := thetextext.lft("foo",(1,10));
227
228draw textextanchor(p) withpen pencircle scaled 2mm withcolor "darkred" ;
229draw textextanchor(q) withpen pencircle scaled 1mm withcolor "white" ;
230
231draw p;
232draw q;
233\stopMPcode
234\stopbuffer
235
236\typebuffer
237
238It gives the following:
239
240\startlinecorrection[blank]
241\getbuffer
242\stoplinecorrection
243
244\stopsection
245
246\startsection[title={Labels}]
247
248\index{labels}
249
250In \METAPOST\ you can use the \type {label} macro to position text at certain
251points.
252
253\starttyping
254label("x", origin) ;
255\stoptyping
256
257The font and scale are determined by two variables, \type {defaultfont} and \type
258{defaultscale}, the former expecting the name of a font in the form of a string,
259the latter expecting a numeric to be used in the scaling of the font. Should you
260choose not to set these yourself, they default to \type {"Mono"} and \type
261{1.0}, respectively. However, you can change the defaults as follows:
262
263\starttyping
264defaultfont  := "texgyrepagella-regular*default" ;
265defaultscale := 1.2 ;
266\stoptyping
267
268These settings selects Pagella at about 12pt. You can also set these variables
269to \CONTEXT\ related values. For \CONTEXT\ graphics they are set to:
270
271\starttyping
272defaultfont  := "\truefontname{Regular}*default" ;
273defaultscale := \the\bodyfontsize/10 ;
274\stoptyping
275
276This means that they will adapt themselves to the current body font (in this
277document we get \truefontname {Regular}) and the current size of the bodyfont
278(here \the\bodyfontsize/10).
279
280Normally you can stick to the default and forget about these details. More important
281is how you anchor labels. This is best shown with some examples:
282
283\startbuffer
284\startMPcode
285  path p ; p := fullcircle scaled 3cm ;
286
287  drawarrow p ;
288
289  dotlabel     ("test",point 0 of p) withcolor red ;
290  dotlabel.rt  ("rt",  point 1 of p) ;
291  dotlabel.urt ("urt", point 2 of p) ;
292  dotlabel.top ("top", point 3 of p) ;
293  dotlabel.ulft("ulft",point 4 of p) ;
294  dotlabel.lft ("lft", point 5 of p) ;
295  dotlabel.llft("llft",point 6 of p) ;
296  dotlabel.bot ("bot", point 7 of p) ;
297  dotlabel.lrt ("lrt", point 8 of p) ;
298\stopMPcode
299\stopbuffer
300
301\typebuffer \startlinecorrection[blank] \getbuffer \stoplinecorrection
302
303\startbuffer
304\startMPcode
305  path p ; p := fullcircle scaled 3cm ;
306
307  drawarrow p ;
308
309  dotlabel     ("test",p) ;
310  dotlabel.rt  ("rt",  p) ;
311  dotlabel.urt ("urt", p) ;
312  dotlabel.top ("top", p) ;
313  dotlabel.ulft("ulft",p) ;
314  dotlabel.lft ("lft", p) ;
315  dotlabel.llft("llft",p) ;
316  dotlabel.bot ("bot", p) ;
317  dotlabel.lrt ("lrt", p) ;
318\stopMPcode
319\stopbuffer
320
321\typebuffer \startlinecorrection[blank] \getbuffer \stoplinecorrection
322
323\startbuffer
324\startMPcode
325  p := (origin..right..up..left) scaled 3cm ;
326
327  drawarrow p ;
328
329  label     ("test",p) ;
330  label.rt  ("rt",  p) ;
331  label.urt ("urt", p) ;
332  label.top ("top", p) ;
333  label.ulft("ulft",p) ;
334  label.lft ("lft", p) ;
335  label.llft("llft",p) ;
336  label.bot ("bot", p) ;
337  label.lrt ("lrt", p) ;
338\stopMPcode
339\stopbuffer
340
341\typebuffer \startlinecorrection[blank] \getbuffer \stoplinecorrection
342
343\startbuffer
344\startMPcode
345  picture p ; p := image(drawarrow (origin..right..up..left) scaled 3cm ;) ;
346
347  draw p ;
348  draw bbox p dashed evenly ;
349
350  label     ("test",p) ;
351  label.rt  ("rt",  p) ;
352  label.urt ("urt", p) ;
353  label.top ("top", p) ;
354  label.ulft("ulft",p) ;
355  label.lft ("lft", p) ;
356  label.llft("llft",p) ;
357  label.bot ("bot", p) ;
358  label.lrt ("lrt", p) ;
359\stopMPcode
360\stopbuffer
361
362\typebuffer \startlinecorrection[blank] \getbuffer \stoplinecorrection
363
364\startbuffer
365\startMPcode
366  picture p ; p := image(drawarrow (origin..right..up..left) scaled 3cm ;) ;
367
368  draw p ;
369  draw bbox p dashed evenly ;
370
371  draw textext     ("test") shifted theoffset     (p) ;
372  draw textext.lft ("rt")   shifted theoffset.rt  (p) ;
373  draw textext.llft("urt")  shifted theoffset.urt (p) ;
374  draw textext.bot ("top")  shifted theoffset.top (p) ;
375  draw textext.lrt ("ulft") shifted theoffset.ulft(p) ;
376  draw textext.rt  ("lft")  shifted theoffset.lft (p) ;
377  draw textext.urt ("llft") shifted theoffset.llft(p) ;
378  draw textext.top ("bot")  shifted theoffset.bot (p) ;
379  draw textext.ulft("lrt")  shifted theoffset.lrt (p) ;
380\stopMPcode
381\stopbuffer
382
383\typebuffer \startlinecorrection[blank] \getbuffer \stoplinecorrection
384
385\stopsection
386
387\startsection[title={Text along a path}]
388
389\index{text}
390
391In the next example we will use a special mechanism for building graphics step by
392step. The advantage of this method is that we can do intermediate calculations in
393\TEX. Our objective is to write a macro that draws text along a circular path.
394While doing so we want to achieve the following:
395
396\startitemize[packed]
397    \startitem
398        the text should be properly kerned, i.e.\ the spacing between characters
399        should be optimal,
400    \stopitem
401    \startitem
402        the position on the circle should vary, and
403    \stopitem
404    \startitem
405        the radius of the circle should vary.
406    \stopitem
407\stopitemize
408
409This code below is a bit rusted but it illustrates the principles. In \MKIV\ and
410\LMTX\ we have typesetting along a curve built in so we no longer show the \TEX\
411code that integrates it. This implementation is not the most straightforward one,
412but by doing it step by step, at least we see what is involved.
413
414We let the bodyfont match the font used in this document, and define \type
415{RotFont} to be the regular typeface, the one you are reading right now, but
416bold.
417
418\startbuffer
419\definefont[RotFont][RegularBold*default]
420\stopbuffer
421
422\typebuffer \getbuffer
423
424Since \METAPOST\ is unaware of kerning, we have to use \TEX\ to keep track of the
425positions. We will split the text into tokens (often characters) and store the
426result in an array of pictures (\type {pic}). We will also store the accumulated
427width in an array (\type {len}). The number of characters is stored in~\type {n}.
428In a few paragraphs we will see why the other arrays are needed.
429
430While defining the graphic, we need \TEX\ to do some calculations. Therefore, we
431will use \type {\startMPdrawing} to stepwise construct the definition. The basic
432pattern we will follow is:
433
434\starttyping
435\resetMPdrawing
436\startMPdrawing
437  metapost code
438\stopMPdrawing
439tex code
440\startMPdrawing
441  metapost code
442\stopMPdrawing
443\MPdrawingdonetrue
444\getMPdrawing
445\stoptyping
446
447In the process, we will use a few variables. We will store the individual
448characters of the text in the variable \type {pic}, its width in \type {wid} and
449the length of the string so far in \type {len}. Later we will use the \type {pos}
450array to store the position where a character ends up. The variable \type {n}
451holds the number of tokens.
452
453\startbuffer[init]
454\resetMPdrawing
455\startMPdrawing
456  picture pic[] ;
457  numeric wid[], len[], pos[], n ;
458  wid[0] := len[0] := pos[0] := n := 0 ;
459\stopMPdrawing
460\stopbuffer
461
462\typebuffer[init]
463
464We also started fresh by resetting the drawing. From now on, each start command
465will add some more to this graphic. The next macro is responsible for collecting
466the data. Each element is passed on to \TEX, using the \type {btex} construct.
467So, \METAPOST\ itself will call \TEX !
468
469\startbuffer[toks]
470\def\whatever#1%
471  {\appendtoks#1\to\MPtoks
472   \setbox\MPbox=\hbox{\bfd\the\MPtoks}%
473   \startMPdrawing
474     n := n + 1 ; len[n] := \the\wd\MPbox ;
475   \stopMPdrawing
476   \startMPdrawing[-]
477     pic[n] := textext("\bfd\setstrut\strut#1") ;
478     pic[n] := pic[n] shifted - llcorner pic[n] ;
479   \stopMPdrawing}
480
481\handletokens MetaPost is Fun!\with\whatever
482\stopbuffer
483
484\typebuffer[toks]
485
486We use the low level \CONTEXT\ macro \type {\appendtoks} to extend the token list
487\type {\MPtoks}. The \type {\handletokens} macro passes each token (character) of
488\typ {MetaPost is Fun!} to the macro \type {\whatever}. The tokens are appended
489to the token register \type {\MPtoks} (already defined). Then we typeset the
490content of \type {\MPtoks} in \type {\MPbox} (also already defined). The width of
491the box is passed to \METAPOST\ and stored in \type {len}.
492
493By default the content of the drawing is expanded, which means that the macro is
494replaced by its current meaning, so the current width ends up in the \METAPOST\
495file. The next part of the drawing, starting with \type {btex}, puts the token in
496a picture. This time we don't expand the drawing, since we want to pass font
497information. Here, the \type {[-]} suppresses expansion of \typ {btex \bfd #1
498etex}. The process is iterated by \type {\handletokens} for each character of the
499text \typ {MetaPost is Fun!}.
500
501Before we typeset the text, now available in pieces in \type {pic}, in a circle,
502we will first demonstrate what they look like. You may like to take a look at the
503file \type {mpgraph.mp} to see what is passed to \METAPOST.
504
505\startbuffer[test]
506\startMPdrawing
507  pair len ; len := origin ;
508  for i=1 upto n :
509    draw pic[i] shifted len ;
510    draw boundingbox pic[i] shifted len
511      withpen pencircle scaled .25pt withcolor red ;
512    len := len+(xpart urcorner pic[i]-xpart llcorner pic[i],0) ;
513  endfor ;
514\stopMPdrawing
515\stopbuffer
516
517\typebuffer[test]
518
519\startbuffer[show]
520\MPdrawingdonetrue\getMPdrawing
521\stopbuffer
522
523We can call up this drawing with \type {\getMPdrawing}, but first we inform the
524compiler that our \METAPOST\ drawing is completed.
525
526\typebuffer[show]
527
528This results in:
529
530\startlinecorrection[blank]
531\getbuffer[init,toks,test,show]
532\stoplinecorrection
533
534Compare this text with the text as typeset by \TEX:
535
536\blank \start \bfd MetaPost is Fun!\par \stop \blank
537
538and you will see that the text produced by \METAPOST\ is not properly kerned.
539When putting characters after each other, \TEX\ uses the information available in
540the font, to optimize the spacing between characters, while \METAPOST\ looks at
541characters as separate entities. But, since we have stored the optimal spacing in
542\type {len}, we can let \METAPOST\ do a better job. Let's first calculate the
543correction needed.
544
545\startbuffer[kern]
546\startMPdrawing
547  for i=1 upto n :
548    wid[i] := abs(xpart urcorner pic[i] - xpart llcorner pic[i]) ;
549    pos[i] := len[i]-wid[i] ;
550  endfor ;
551\stopMPdrawing
552\stopbuffer
553
554\typebuffer[kern]
555
556This compares well to the text as typeset by \TEX:
557
558\blank \start \bfd MetaPost is Fun!\par \stop \blank
559
560We can now use the values in \type {pos} to position the pictures according to
561what \TEX\ considered to be the best (relative) position.
562
563\startbuffer[test]
564\startMPdrawing
565  for i=1 upto n :
566    draw pic[i] shifted (pos[i],0) ;
567    draw boundingbox pic[i] shifted (pos[i],0)
568      withpen pencircle scaled .25pt withcolor red ;
569  endfor ;
570\stopMPdrawing
571\stopbuffer
572
573\typebuffer[test]
574
575That this correction is adequate, is demonstrated in the next graphic. If you
576look closely, you will see that for instance the \quote {o} is moved to the left,
577under the capital \quote {P}.
578
579\startlinecorrection[blank]
580\getbuffer[init,toks,kern,test,show]
581\stoplinecorrection
582
583When we want to position the pictures along a circle, we need to apply some
584rotations, especially because we want to go clockwise. Since we don't want to use
585\quote {complicated} math or more advanced \METAPOST\ code yet, we will do it in
586steps.
587
588\startbuffer[swap]
589\startMPdrawing
590  for i=1 upto n:
591    pic[i] := pic[i] rotatedaround(origin,-270) ;
592  endfor ;
593\stopMPdrawing
594\stopbuffer
595
596\typebuffer[swap]
597
598\startlinecorrection[blank]
599\getbuffer[init,toks,kern,swap,test,show]
600\stoplinecorrection
601
602\startbuffer[cent]
603\startMPdrawing
604  for i=1 upto n :
605    pic[i] := pic[i]
606      shifted (0,ypart -.5[ulcorner pic[i],llcorner pic[i]]) ;
607  endfor ;
608\stopMPdrawing
609\stopbuffer
610
611We will now center the pictures around the baseline. Centering comes down to
612shifting over half the height of the picture. This can be expressed by:
613
614\starttyping
615ypart -.5[ulcorner pic[i],llcorner pic[i]]
616\stoptyping
617
618but different ways of calculating the distance are possible
619too.
620
621\typebuffer[cent]
622
623So, now we have:
624
625\startlinecorrection[blank]
626\getbuffer[init,toks,kern,swap,cent,test,show]
627\stoplinecorrection
628
629When we typeset on a (half) circle, we should map the actual length onto a
630partial circle. We denote the radius with an~\type {r} and shift the pictures to
631the left.
632
633\startbuffer[shif]
634\startMPdrawing
635  numeric r ; r := len[n]/pi ;
636  for i=1 upto n :
637    pic[i] := pic[i] shifted (-r,0) ;
638  endfor ;
639\stopMPdrawing
640\stopbuffer
641
642\typebuffer[shif]
643
644You can now use the following code to test the current state of the pictures. Of
645course this code should not end up in the final definitions.
646
647\startbuffer[test]
648\startMPdrawing
649  draw origin
650    withpen pencircle scaled 5pt withcolor red ;
651  for i=1 upto n :
652    draw pic[i] ;
653    draw boundingbox pic[i]
654      withpen pencircle scaled .25pt withcolor red ;
655  endfor ;
656\stopMPdrawing
657\stopbuffer
658
659\typebuffer[test]
660
661\startlinecorrection[blank]
662\getbuffer[init,toks,kern,swap,cent,shif,test,show]
663\stoplinecorrection
664
665Later we will write a compact, efficient macro to take care of rotation. However,
666for the moment, so as not to overwhelm you with complicated code, we will rotate
667each individual picture with the following code fragment.
668
669\startbuffer[rots]
670\startMPdrawing
671  numeric delta, extra, radius, rot[] ;
672
673  delta  := extra := radius := 0 ;
674
675  for i=1 upto n :
676    rot[i] := extra+delta-((pos[i]+.5wid[i])/len[n])*(180+2delta) ;
677  endfor ;
678\stopMPdrawing
679\stopbuffer
680
681\typebuffer[rots]
682
683Here we introduce a few variables that we can use later to tune the result a bit.
684With \type {delta}, the space between the characters can be increased, while
685\type {extra} rotates the whole string around the origin. The \type {radius}
686variable can be used to increase the distance to the origin. Without these
687variables, the assignment would have been:
688
689\starttyping
690rot[i] := ((pos[i]+.5wid[i])/len[n])*180 ;
691\stoptyping
692
693Placing the pictures is now rather easy:
694
695\startbuffer[done]
696\startMPdrawing
697  for i=1 upto n :
698    draw pic[i] shifted (-radius,0) rotatedaround(origin,rot[i]) ;
699  endfor ;
700\stopMPdrawing
701\stopbuffer
702
703\typebuffer[done]
704
705The pictures are now positioned on half a circle, properly kerned.
706
707\startlinecorrection[blank]
708\getbuffer[init,toks,kern,swap,cent,shif,rots,done,show]
709\stoplinecorrection
710
711A bit more insight is given in the next picture:
712
713\startbuffer[test]
714\startMPdrawing
715  def moved(expr i) =
716    shifted (-radius,0) rotatedaround(origin,rot[i])
717  enddef ;
718  pickup pencircle scaled .5pt ;
719  for i=1 upto n :
720    draw pic[i] moved(i) ;
721    draw boundingbox pic[i] moved(i) withcolor red ;
722    draw origin -- center pic[i] moved(i) withcolor green ;
723  endfor ;
724  draw tcircle scaled 2r withcolor blue ;
725\stopMPdrawing
726\stopbuffer
727
728\startlinecorrection[blank]
729\getbuffer[init,toks,kern,swap,cent,shif,rots,test,show]
730\stoplinecorrection
731
732This was defined as follows. The path variable \type {tcycle} is predefined to
733the top half of a fullcircle.
734
735\typebuffer[test]
736
737We will now package all of this into a nice, efficient macro, using, of course,
738the predefined scratch registers \type {\MPtoks} and \type {\MPbox}. First we
739define the token processor. Note again the expansion inhibition switch \type
740{[-]}. This code is presented as an example of how these mechanissm evolved in
741\CONTEXT.
742
743\startbuffer
744\def\processrotationtoken#1%
745  {\appendtoks#1\to\MPtoks
746   \setbox\MPbox=\hbox{\RotFont\the\MPtoks}%
747   \startMPdrawing
748     n := n + 1 ; len[n] := \the\wd\MPbox ;
749   \stopMPdrawing
750   \startMPdrawing[-]
751     pic[n] := textext("\RotFont\setstrut\strut#1") ;
752     pic[n] := pic[n] shifted - llcorner pic[n] ;
753   \stopMPdrawing}
754\stopbuffer
755
756\typebuffer
757
758\getbuffer
759
760The main macro is a bit more complicated but by using a few scratch numerics, we
761can keep it readable.
762
763\startbuffer
764\def\rotatetokens#1#2#3#4% delta extra radius tokens
765  {\vbox\bgroup
766   \MPtoks\emptytoks
767   \resetMPdrawing
768   \startMPdrawing
769     picture pic[] ;
770     numeric wid, len[], rot ;
771     numeric delta, extra, radius, n, r ;
772     len[0] := n := 0 ;
773     delta  := #1 ; extra := #2 ; radius := #3 ;
774   \stopMPdrawing
775   \handletokens#4\with\processrotationtoken
776   \startMPdrawing
777     r := len[n]/pi ;
778     for i=1 upto n :
779       wid := abs(xpart lrcorner pic[i] -
780                  xpart llcorner pic[i]) ;
781       rot := extra + delta -
782              ((len[i]-.5wid)/len[n]) * (180+2delta) ;
783       draw pic[i]
784         rotatedaround (origin,-270) shifted (-r-radius,
785         ypart -.5[ulcorner pic[i], llcorner pic[i]])
786         rotatedaround (origin,rot) ;
787     endfor ;
788   \stopMPdrawing
789   \MPdrawingdonetrue
790   \getMPdrawing
791   \resetMPdrawing
792   \egroup}
793\stopbuffer
794
795\typebuffer
796
797\getbuffer
798
799\startbuffer
800\startcombination[3*1]
801  {\rotatetokens {0} {0}{0}{Does it work ok?}} {A}
802  {\rotatetokens{20} {0}{0}{Does it work ok?}} {B}
803  {\rotatetokens{20}{30}{0}{Does it work ok?}} {C}
804\stopcombination
805\stopbuffer
806
807We can use this macro as follows:
808
809\typebuffer
810
811\startlinecorrection[blank]
812\getbuffer
813\stoplinecorrection
814
815The previous macro is not really an example of generalization, but we used it for
816demonstrating how to build graphics in a stepwise way. If you put the steps in
817buffers, you can even combine steps and replace them at will. This is how we made
818the previous step by step examples: We put each sub||graphic in a buffer and then
819called the ones we wanted.
820
821Next we show how it is done today in \LMTX. Instead of wrapping it in a macro we
822just do all in \METAPOST. Watch how we use the key|/|value interface:
823
824\startbuffer
825\startMPcode
826  draw lmt_followtext [
827    path   = halfcircle rotated 45 scaled 4cm,
828    text   = "Does it work ok?",
829    spread = true,
830    trace  = true,
831  ] ;
832  draw lmt_followtext [
833    path   = halfcircle rotated -30 scaled 3cm,
834    text   = "Does it work ok?",
835    spread = false,
836    trace  = true,
837  ] shifted (4cm,0) ;
838  draw lmt_followtext [
839    path   = reverse halfcircle scaled 2cm,
840    text   = "Does it work ok?",
841    spread = true,
842    trace  = true,
843  ] shifted (8cm,0) ;
844\stopMPcode
845\stopMPcode
846\stopbuffer
847
848\typebuffer
849
850\startlinecorrection[blank]
851\getbuffer
852\stoplinecorrection
853
854\stopsection
855
856\startsection[title={Using shapes}]
857
858Sometimes, others may say oftentimes, we are in need for some fancy typesetting.
859If we want to typeset a paragraph of text in a non standard shape, like a circle,
860we have to fall back on \type {\parshape}. Unfortunately, \TEX\ is not that
861strong in providing the specifications of more complicated shapes, unless you are
862willing to do some complicated arithmetic \TEX. Given that \METAPOST\ knows how
863to deal with shapes, the question is: \quotation {Can \METAPOST\ be of help?}
864
865In the process of finding out how to deal with this, we first define a simple
866path. Because we are going to replace pieces of code, we will compose the graphic
867from components. First, we create the path.
868
869\startbuffer
870\startuseMPgraphic{text path}
871  path p ; p := ((0,1)..(-1,0)..(1,0)--cycle) scaled 65pt ;
872\stopuseMPgraphic
873\stopbuffer
874
875\typebuffer \getbuffer
876
877This shape is not that beautiful, but it has a few characteristics that will help
878us to identify bordercases.
879
880\startbuffer
881\startuseMPgraphic{text draw}
882  drawarrow p withpen pencircle scaled 1pt withcolor red ;
883\stopuseMPgraphic
884\stopbuffer
885
886\typebuffer \getbuffer
887
888Now we use \CONTEXT's \type {\includeMPgraphic} command to build our graphic from
889the previously defined components.
890
891\startbuffer
892\startuseMPgraphic{text}
893  \includeMPgraphic{text path}
894  \includeMPgraphic{text draw}
895\stopuseMPgraphic
896\stopbuffer
897
898\typebuffer \getbuffer
899
900When called with \type {\useMPgraphic{text}}, we get:
901
902\startlinecorrection[blank]
903\useMPgraphic{text}
904\stoplinecorrection
905
906For the moment we start the path at $(x=0,y>0)$, but later using more complicated
907macros, we will see that we can use arbitrary paths.
908
909We are going to split the path in two, and will use the points that make up the
910bounding box as calcutated by \METAPOST. The next graphic shows one of these
911points, the lower left corner, available as point \typ {llcorner p}.
912
913\startbuffer
914\startuseMPgraphic{text draw}
915  draw             p withpen pencircle scaled 3pt withcolor red ;
916  draw boundingbox p withpen pencircle scaled 1pt ;
917  draw llcorner    p withpen pencircle scaled 5pt ;
918\stopuseMPgraphic
919\stopbuffer
920
921\typebuffer \getbuffer
922
923\startlinecorrection[blank]
924\useMPgraphic{text}
925\stoplinecorrection
926
927The five points that \METAPOST\ can report for each path or picture are:
928
929\starttabulate[|Tl|l|]
930\NC llcorner \NC lower left  corner            \NC \NR
931\NC lrcorner \NC lower right corner            \NC \NR
932\NC urcorner \NC upper right corner            \NC \NR
933\NC ulcorner \NC upper left  corner            \NC \NR
934\NC center   \NC intersection of the diagonals \NC \NR
935\stoptabulate
936
937If we want to typeset text inside this circle, we need to know where a line
938starts and ends. Given that lines are horizontal and straight, we therefore need
939to calculate the intersection points of the lines and the path. As a first step,
940we calculate the top and bottom of the path and after that we split off the left
941and right path.
942
943\startbuffer
944\startuseMPgraphic{text split}
945  pair t, b ; path l, r ;
946
947  t := (ulcorner p -- urcorner p) intersectionpoint p ;
948  b := (llcorner p -- lrcorner p) intersectionpoint p ;
949
950  l := p cutbefore t ; l := l cutafter b ;
951  r := p cutbefore b ; r := r cutafter t ;
952\stopuseMPgraphic
953\stopbuffer
954
955\typebuffer \getbuffer
956
957The \type {intersectionpoint} macro returns the point where two paths cross. If
958the paths don't cross, an error is reported, when the paths cross more times,
959just one point is returned. The \type {cutafter} and \type {cutbefore} commands
960do as their names say and return a path.
961
962In the \type {text split} code fragment, \type {t} and \type {b} are the top
963points of the main path, while \type {l} and \type {r} become the left and right
964half of path \type {p}.
965
966We now draw the original path using a thick pen and both halves with a thinner
967pen on top of the original. The arrows show the direction.
968
969\startbuffer
970\startuseMPgraphic{text draw}
971  draw      p withpen pencircle scaled 3pt withcolor red ;
972  drawarrow l withpen pencircle scaled 1pt withcolor green ;
973  drawarrow r withpen pencircle scaled 1pt withcolor blue ;
974\stopuseMPgraphic
975\stopbuffer
976
977\typebuffer \getbuffer
978
979We use \type {\includeMPgraphic} to assemble the components:
980
981\startbuffer
982\startuseMPgraphic{text}
983  \includeMPgraphic{text path}
984  \includeMPgraphic{text split}
985  \includeMPgraphic{text draw}
986\stopuseMPgraphic
987\stopbuffer
988
989\typebuffer \getbuffer
990
991This graphic is typeset with \type {\useMPgraphic{text}}:
992
993\startlinecorrection[blank]
994\useMPgraphic{text}
995\stoplinecorrection
996
997Before we are going to use them, we define some variables that specify the text.
998We use a baseline distance of 8~points. The part of the line above the baseline
999is 7.2~points, while the (maximum) depth is 2.8~points. These ratios are the ones
1000we use in \CONTEXT. Because we don't want the text to touch the circle so we
1001define an offset too.
1002
1003\startbuffer
1004\startuseMPgraphic{text vars}
1005  MyOffset  := LineHeight/2 ;
1006  MyTopSkip := StrutHeight ;
1007\stopuseMPgraphic
1008\stopbuffer
1009
1010\typebuffer \getbuffer
1011
1012We more or less achieve the offset by scaling the path. In doing so, we use the
1013width and height, which we call \type {hsize} and \type {vsize}, thereby
1014conforming to the \TEX\ naming scheme.
1015
1016First we calculate both dimensions from the bounding box of the path. Next we
1017down scale the path to compensate for the offset. When done, we recalculate the
1018dimensions.
1019
1020\startbuffer
1021\startuseMPgraphic{text move}
1022  pair t, b ; path q, l, r ;
1023
1024  hsize := xpart lrcorner p - xpart llcorner p ;
1025  vsize := ypart urcorner p - ypart lrcorner p ;
1026
1027  q := p xscaled ((hsize-2MyOffset)/hsize)
1028         yscaled ((vsize-2MyOffset)/vsize) ;
1029
1030  hsize := xpart lrcorner q - xpart llcorner q ;
1031  vsize := ypart urcorner q - ypart lrcorner q ;
1032\stopuseMPgraphic
1033\stopbuffer
1034
1035\typebuffer \getbuffer
1036
1037\startbuffer
1038\startuseMPgraphic{text split}
1039  t := (ulcorner q -- urcorner q) intersectionpoint q ;
1040  b := (llcorner q -- lrcorner q) intersectionpoint q ;
1041
1042  l := q cutbefore t ; l := l cutafter b ;
1043  r := q cutbefore b ; r := r cutafter t ;
1044\stopuseMPgraphic
1045\stopbuffer
1046
1047We adapt the \type {text split} code to use the reduced path
1048instead of the original.
1049
1050\typebuffer \getbuffer
1051
1052\startbuffer
1053\startuseMPgraphic{text draw}
1054  drawarrow p withpen pencircle scaled 1pt withcolor red ;
1055  draw      t withpen pencircle scaled 2pt ;
1056  draw      b withpen pencircle scaled 2pt ;
1057  drawarrow l withpen pencircle scaled 1pt withcolor green ;
1058  drawarrow r withpen pencircle scaled 1pt withcolor blue ;
1059\stopuseMPgraphic
1060\stopbuffer
1061
1062In order to test what we have reached so far, we draw the original path, the left
1063and right part of the reduced path, and both the top and bottom point.
1064
1065\typebuffer \getbuffer
1066
1067Again we use \type {\includeMPgraphic} to combine the
1068components into a graphic.
1069
1070\startbuffer
1071\startuseMPgraphic{text}
1072  \includeMPgraphic{text path} \includeMPgraphic{text vars}
1073  \includeMPgraphic{text move} \includeMPgraphic{text split}
1074  \includeMPgraphic{text draw}
1075\stopuseMPgraphic
1076\stopbuffer
1077
1078\typebuffer \getbuffer
1079
1080Then we use \type {\useMPgraphic{text}} to call up the picture.
1081
1082\startlinecorrection[blank]
1083\useMPgraphic{text}
1084\stoplinecorrection
1085
1086The offset is not optimal. Note the funny gap at the top. We could try to fix
1087this, but there is a better way to optimize both paths.
1088
1089We lower the top edge of \type {q}'s bounding box by \type {MyTopSkip}, then cut
1090any part of the left and right pieces of \type {q} that lie above it. Similarly,
1091we raise the bottom edge and cut off the pieces that fall below this line.
1092
1093\startbuffer
1094\startuseMPgraphic{text cutoff}
1095  path tt, bb ;
1096
1097  tt := (ulcorner q -- urcorner q) shifted (0,-MyTopSkip) ;
1098  bb := (llcorner q -- lrcorner q) shifted (0,StrutDepth) ;
1099
1100  l := l cutbefore (l intersectionpoint tt) ;
1101  l := l cutafter  (l intersectionpoint bb) ;
1102  r := r cutbefore (r intersectionpoint bb) ;
1103  r := r cutafter  (r intersectionpoint tt) ;
1104\stopuseMPgraphic
1105\stopbuffer
1106
1107\typebuffer \getbuffer
1108
1109Because we use \type {\includeMPgraphic} to construct the graphic, we can
1110redefine \type {text draw} to show the result of this effort.
1111
1112\startbuffer
1113\startuseMPgraphic{text draw}
1114  drawarrow p withpen pencircle scaled 1pt withcolor red ;
1115  drawarrow l withpen pencircle scaled 1pt withcolor green ;
1116  drawarrow r withpen pencircle scaled 1pt withcolor blue ;
1117\stopuseMPgraphic
1118\stopbuffer
1119
1120\typebuffer \getbuffer
1121
1122The \type {text} graphic now becomes:
1123
1124\startbuffer
1125\startuseMPgraphic{text}
1126  \includeMPgraphic{text path}   \includeMPgraphic{text vars}
1127  \includeMPgraphic{text move}   \includeMPgraphic{text split}
1128  \includeMPgraphic{text cutoff} \includeMPgraphic{text draw}
1129\stopuseMPgraphic
1130\stopbuffer
1131
1132\typebuffer \getbuffer
1133
1134Or, as graphic:
1135
1136\startlinecorrection[blank]
1137\useMPgraphic{text}
1138\stoplinecorrection
1139
1140We are now ready for an attempt to calculate the shape of the text. For each
1141line, we have to calculate the left and right intersection points, and since a
1142line has a height and depth, we have to determine which part touches first.
1143
1144\startbuffer
1145\startuseMPgraphic{text calc}
1146  vardef found_point (expr lin, pat, sig) =
1147    pair a, b ;
1148    a := pat intersection_point (lin shifted (0,StrutHeight)) ;
1149    if intersection_found :
1150      a := a shifted (0,-StrutHeight) ;
1151    else :
1152      a := pat intersection_point lin ;
1153    fi ;
1154    b := pat intersection_point (lin shifted (0,-StrutDepth)) ;
1155    if intersection_found :
1156      if sig :
1157        if xpart b > xpart a : a := b shifted (0,StrutDepth) fi ;
1158      else :
1159        if xpart b < xpart a : a := b shifted (0,StrutDepth) fi ;
1160      fi ;
1161    fi ;
1162    a
1163  enddef ;
1164\stopuseMPgraphic
1165\stopbuffer
1166
1167\typebuffer \getbuffer
1168
1169Instead of using \METAPOST's \type {intersectionpoint} macro, we use one that
1170comes with \CONTEXT. That way we don't get an error message when no point is
1171found, and can use a boolean flag to take further action. Since we use a \type
1172{vardef}, all calculations are hidden and the~\type {a} at the end is returned,
1173so that we can use this macro in an assignment. The \type {sig} variable is used
1174to distinguish between the beginning and end of a line (the left and right
1175subpath).
1176
1177\startbuffer
1178\startuseMPgraphic{text step}
1179  path line; pair lll, rrr ;
1180
1181  for i=MyTopSkip step LineHeight until vsize :
1182
1183    line := (ulcorner q -- urcorner q) shifted (0,-i) ;
1184
1185    lll := found_point(line,l,true ) ;
1186    rrr := found_point(line,r,false) ;
1187\stopuseMPgraphic
1188\stopbuffer
1189
1190\typebuffer \getbuffer
1191
1192Here we divide the available space in lines. The first line starts at \type
1193{StrutHeight} from the top.
1194
1195We can now finish our graphic by visualizing the lines. Both the height and depth
1196of the lines are shown.
1197
1198\startbuffer
1199\startuseMPgraphic{text line}
1200    fill (lll--rrr--rrr shifted (0,StrutHeight)--lll
1201      shifted (0,StrutHeight)--cycle) withcolor .5white ;
1202    fill (lll--rrr--rrr shifted (0,-StrutDepth)--lll
1203      shifted (0,-StrutDepth)--cycle) withcolor .7white ;
1204    draw lll withpen pencircle scaled 2pt ;
1205    draw rrr withpen pencircle scaled 2pt ;
1206    draw (lll--rrr) withpen pencircle scaled .5pt ;
1207\stopuseMPgraphic
1208
1209\startuseMPgraphic{text done}
1210  endfor ;
1211\stopuseMPgraphic
1212\stopbuffer
1213
1214\typebuffer \getbuffer
1215
1216The result is still a bit disappointing.
1217
1218\startbuffer
1219\startuseMPgraphic{text}
1220  \includeMPgraphic{text path}   \includeMPgraphic{text vars}
1221  \includeMPgraphic{text move}   \includeMPgraphic{text split}
1222  \includeMPgraphic{text cutoff} \includeMPgraphic{text draw}
1223  \includeMPgraphic{text calc}   \includeMPgraphic{text step}
1224  \includeMPgraphic{text line}   \includeMPgraphic{text done}
1225\stopuseMPgraphic
1226\stopbuffer
1227
1228\typebuffer \getbuffer
1229\startlinecorrection[blank]
1230\useMPgraphic{text}
1231\stoplinecorrection
1232
1233In order to catch the overflow at the bottom, we need to change the \type
1234{for}||loop a bit, so that the number of lines does not exceed the available
1235space. The test that surrounds the assignment of \type {vvsize} makes sure that
1236we get better results when we (on purpose) take a smaller height.
1237
1238\startbuffer
1239\startuseMPgraphic{text step}
1240  path line; pair lll, rrr ; numeric vvsize ;
1241
1242  if (StrutHeight+StrutDepth<LineHeight) :
1243    vvsize := vsize ;
1244  else :
1245    vvsize := (vsize div LineHeight) * LineHeight ;
1246  fi ;
1247
1248  for i=MyTopSkip step LineHeight until vvsize :
1249
1250    line := (ulcorner q -- urcorner q) shifted (0,-i) ;
1251
1252    lll := found_point(line,l,true ) ;
1253    rrr := found_point(line,r,false) ;
1254\stopuseMPgraphic
1255\stopbuffer
1256
1257\typebuffer \getbuffer
1258
1259\startlinecorrection[blank]
1260\useMPgraphic{text}
1261\stoplinecorrection
1262
1263We can manipulate the heigth and depth of the lines to give different (and maybe
1264better) results.
1265
1266\startbuffer
1267\startuseMPgraphic{text vars}
1268MyOffset  := .5LineHeight ;
1269MyTopSkip := StrutHeight ;
1270\stopuseMPgraphic
1271\stopbuffer
1272
1273\typebuffer \getbuffer
1274
1275\startlinecorrection[blank]
1276\useMPgraphic{text}
1277\stoplinecorrection
1278
1279Now that we know a bit how this works we demonstrate the implementation in \LMTX.
1280Where in \MKIV\ and \MKII\ we let \METAPOST\ pass the dimensions to \TEX\ via a
1281temporary file in \LMTX\ we communicate more directly. You can take a look at the
1282source code if you want to know how that works.
1283
1284We start by defining four shapes. They are not really beautiful, but they
1285demonstrate what happens in border cases. For instance, too small first lines are
1286ignored. First we define a circle. Watch how the dimensions are set in the
1287graphic. The arguments passed to \type {lmt_parshape} are: path, an offset, an
1288additional horizontal and vertical displacement, the baseline distance, the
1289height and depth of the line, and the height of the first line (aka topskip). The
1290height and depth of a line are often called strut height and depth, with a strut
1291being an invisible character with maximum dimensions. Variables like \type
1292{StrutHeight} refer to values at the \TEX\ end. We play safe and group so that we
1293can save variables.
1294
1295\startbuffer
1296\startuseMPgraphic{shapetest-1}
1297  begingroup ;
1298    save p ; path p ; p := fullcircle scaled 6cm ;
1299    lmt_parshape [
1300        path        = p,
1301        offset      = BodyFontSize/2,
1302        dx          = 0,           % default
1303        dy          = 0,           % default
1304        lineheight  = LineHeight,  % default
1305        strutheight = StrutHeight, % default
1306        strutdepth  = StrutDepth,  % default
1307        topskip     = StrutHeight, % default
1308    ] ;
1309    draw p withpen pencircle scaled 1pt ;
1310  endgroup ;
1311\stopuseMPgraphic
1312\stopbuffer
1313
1314\typebuffer \getbuffer
1315
1316The second shape is a diamond. This is a rather useless shape, unless the text
1317suits the small lines at the top and bottom.
1318
1319\startbuffer
1320\startuseMPgraphic{shapetest-2}
1321  begingroup ;
1322    save p ; path p ; p := fullsquare rotated 45 scaled 5cm ;
1323    lmt_parshape [
1324        path   = p,
1325        offset = BodyFontSize/2,
1326        trace  = true,
1327    ] ;
1328    draw p withpen pencircle scaled 1pt ;
1329  endgroup ;
1330\stopuseMPgraphic
1331\stopbuffer
1332
1333\typebuffer \getbuffer
1334
1335The third and fourth shape demonstrate that providing a suitable offset is not
1336always trivial.
1337
1338\startbuffer
1339\startuseMPgraphic{shapetest-3}
1340  begingroup ;
1341    save w, h, p ; path p ; w := h := 6cm ;
1342    p := (.5w,h) -- (   0,  h) -- (0,0) -- (w,0) &
1343         (  w,0) .. (.75w,.5h) .. (w,h) &  (w,h) -- cycle ;
1344    lmt_parshape [
1345        path   = p,
1346        offset = BodyFontSize/2,
1347    ] ;
1348    draw p withpen pencircle scaled 1pt ;
1349  endgroup ;
1350\stopuseMPgraphic
1351\stopbuffer
1352
1353\typebuffer \getbuffer
1354
1355Contrary to the first three shapes, here we use a different path for the
1356calculations and the drawing. Watch carefully! If, instead of an offset, we pass
1357a path, \METAPOST\ is able to calculate the right dimensions and offsets. This is
1358needed, since we need these later on.
1359
1360\startbuffer
1361\startuseMPgraphic{shapetest-4}
1362  begingroup ;
1363    save d, p, q, shape ; path p, q ; d := BodyFontSize/2;
1364    vardef shape(expr w, h, o) =
1365        (o,o) -- (w-o,o) & (w-o,o) .. (.75w-o,.5h) ..
1366        (w-2o,h-o) & (w-2o,h-o) -- (o,h-o) -- cycle
1367    enddef ;
1368    p := shape(6cm, 6cm, d) ;
1369    q := shape(6cm, 6cm, 0) ;
1370    lmt_parshape [
1371        path       = p,
1372        offsetpath = q,
1373        dx         = d,
1374        dy         = d,
1375        trace      = true,
1376    ] ;
1377    draw q withpen pencircle scaled 1pt ;
1378  endgroup ;
1379\stopuseMPgraphic
1380\stopbuffer
1381
1382\typebuffer \getbuffer
1383
1384Since we also want these graphics as backgrounds, we define them as overlays. If
1385you don't want to show the graphic, you may omit this step.
1386
1387\startbuffer
1388\defineoverlay[shapetest-1][\useMPgraphic{shapetest-1}]
1389\defineoverlay[shapetest-2][\useMPgraphic{shapetest-2}]
1390\defineoverlay[shapetest-3][\useMPgraphic{shapetest-3}]
1391\defineoverlay[shapetest-4][\useMPgraphic{shapetest-4}]
1392\stopbuffer
1393
1394\typebuffer \getbuffer
1395
1396As text, we use a quote from Douglas R.~Hofstadter's book \quotation {Metamagical
1397Themas, Questing for the Essence of Mind and Pattern}. Watch how we pass a list
1398of shapes.
1399
1400\startbuffer[text]
1401\startshapetext[shapetest-1,shapetest-2,shapetest-3,shapetest-4]
1402  \forgetall % as it says
1403  \setupalign[verytolerant,stretch,normal]%
1404  \samplefile{douglas}% Douglas R. Hofstadter
1405\stopshapetext
1406\stopbuffer
1407
1408\typebuffer[text]
1409
1410Finally we combine text and shapes. Since we also want a background, we use \type
1411{\framed}. The normal result is shown in \in {figure} [fig:shapes].
1412
1413\startbuffer[shapes]
1414\startbuffer
1415\startcombination[2*2]
1416  {\framed[offset=overlay,frame=off,background=shapetest-1]{\getshapetext}} {shapetest-1}
1417  {\framed[offset=overlay,frame=off,background=shapetest-2]{\getshapetext}} {shapetest-2}
1418  {\framed[offset=overlay,frame=off,background=shapetest-3]{\getshapetext}} {shapetest-3}
1419  {\framed[offset=overlay,frame=off,background=shapetest-4]{\getshapetext}} {shapetest-4}
1420\stopcombination
1421\stopbuffer
1422\stopbuffer
1423
1424\typebuffer[shapes]
1425
1426\getbuffer[shapes]
1427
1428By using a buffer we keep \type {\placefigure} readable. You might have noticed
1429that for two shapes we turned on tracing.
1430
1431\startbuffer[a]
1432\placefigure
1433  [here][fig:shapes]
1434  {A continuous text, typeset in a non||standard shape,
1435   spread over four areas, and right aligned.}
1436  {\getbuffer}
1437\stopbuffer
1438
1439\startbuffer[b]
1440\placefigure
1441  [here][fig:shapes]
1442  {A continuous text, typeset in a non||standard shape,
1443   spread over four areas.}
1444  {\scale[factor=max,height=.9\textheight]{\getbuffer}}
1445\stopbuffer
1446
1447\typebuffer[a]
1448
1449\doifmodeelse{screen}{\getbuffer[text,b]}{\getbuffer[text,a]}
1450
1451\blank
1452
1453We can combine all those tricks, although the input is somewhat fuzzy. First we
1454define a quote typeset in a circular paragraph shape where we use the defaults.
1455
1456\startbuffer[shape]
1457\startuseMPgraphic{center}
1458  lmt_parshape [
1459    path = fullcircle scaled 8cm,
1460  ]
1461\stopuseMPgraphic
1462
1463\startshapetext[center]
1464  \samplefile{douglas}%
1465\stopshapetext
1466
1467\defineoverlay[center][\useMPgraphic{center}]
1468\stopbuffer
1469
1470\typebuffer[shape]
1471
1472We will surround this text with a circular line, that we define as follows. By
1473using a buffer we keep things organized.
1474
1475\startbuffer
1476\startuseMPgraphic{circle}
1477  save p ; path p ;
1478  p := reverse fullcircle rotatedaround(origin,90)
1479    xscaled OverlayWidth yscaled OverlayHeight ;
1480
1481  draw lmt_followtext [
1482    path   = p,
1483    text   = "This is just a dummy text, kerned by \TeX\ and typeset
1484              in a circle using \MetaPost.\quad",
1485    spread = true,
1486    trace  = true,
1487  ] ;
1488
1489% draw p ;
1490\stopuseMPgraphic
1491
1492\defineoverlay[edge][\useMPgraphic{circle}]
1493\stopbuffer
1494
1495\typebuffer \getbuffer
1496
1497The text and graphics come together in a framed text:
1498
1499\startbuffer
1500\startplacefigure[title={One more time Hofstadter's quotation (normal).}]
1501  \getbuffer[shape,quote]%
1502  \framed
1503    [offset=24pt,
1504     background=edge,
1505     frame=off,
1506     backgroundoffset=-18pt]
1507    {\getshapetext}
1508\stopplacefigure
1509\stopbuffer
1510
1511\typebuffer \getbuffer
1512
1513\stopsection
1514
1515\startsection[title=Hashes]
1516
1517\index {hashes}
1518
1519We can store data in a hash and access it later. Storage happens efficiently at the
1520\LUA\ end.
1521
1522\startbuffer
1523\startMPcode
1524  tohash("foo","bar","gnu") ;
1525  tohash("foo","rab","ung") ;
1526  fill fullcircle scaled 1cm withcolor "lightgray" ;
1527  draw textext(fromhash("foo","bar")) ;
1528  draw textext(fromhash("foo","rab")) rotated 90 ;
1529\stopMPcode
1530\stopbuffer
1531
1532\typebuffer \startlinecorrection \getbuffer \stoplinecorrection
1533
1534With \type {newhash("foo")} we get back an index but this macro is only needed in
1535\MKIV\ where. With \type {disposehash("foo")} a hash get wiped.
1536
1537\startbuffer
1538\startMPcode
1539  tohash(4,"bar","gnu") ;
1540  tohash(4,"rab","ung") ;
1541  fill fullcircle scaled 1cm withcolor "lightgray" ;
1542  draw textext(fromhash(4,"bar")) ;
1543  draw textext(fromhash(4,"rab")) rotated 90 ;
1544\stopMPcode
1545\stopbuffer
1546
1547\typebuffer \startlinecorrection \getbuffer \stoplinecorrection
1548
1549You can also used an indexed hash:
1550
1551\startbuffer
1552\startMPcode
1553  tohash("foo",1,"gnu") ;
1554  tohash("foo",2,"ung") ;
1555  fill fullcircle scaled 1cm withcolor "lightgray" ;
1556  for i=1 upto 3 :
1557    if inhash("foo",i) :
1558      draw textext(fromhash("foo",i))
1559        rotated ((i-1) * 90) ;
1560    fi ;
1561  endfor ;
1562\stopMPcode
1563\stopbuffer
1564
1565\typebuffer \startlinecorrection \getbuffer \stoplinecorrection
1566
1567And even booleans can be used as index:
1568
1569\startbuffer
1570\startMPcode
1571  tohash("foo",false,"gnu") ;
1572  tohash("foo",true,"ung") ;
1573  fill fullcircle scaled 1cm withcolor "lightgray" ;
1574  draw textext(fromhash("foo",false)) ;
1575  draw textext(fromhash("foo",true)) rotated 90 ;
1576\stopMPcode
1577\stopbuffer
1578
1579\typebuffer \startlinecorrection \getbuffer \stoplinecorrection
1580
1581
1582\stopsection
1583
1584\stopchapter
1585
1586\stopcomponent
1587