1
2
3\startcomponent metafuntext
4
5\environment metafunenvironment
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 doesnt
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 := w2 ;
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 := "texgyrepagellaregular*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\bodyfontsize10 ;
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\bodyfontsize10).
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 dont 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. Lets 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 dont 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] := extradelta((pos[i].5wid[i])len[n])*(1802delta) ;
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
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]) * (1802delta) ;
783 draw pic[i]
784 rotatedaround (origin,270) shifted (rradius,
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 subgraphic 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 keyvalue 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 \CONTEXTs \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[Tll]
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 dont 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 8points. The part of the line above the baseline
999is 7.2points, while the (maximum) depth is 2.8points. These ratios are the ones
1000we use in \CONTEXT. Because we dont want the text to touch the circle so we
1001define an offset too.
1002
1003\startbuffer
1004\startuseMPgraphic{text vars}
1005 MyOffset := LineHeight2 ;
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 \METAPOSTs \type {intersectionpoint} macro, we use one that
1170comes with \CONTEXT. That way we dont 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 (lllrrrrrr shifted (0,StrutHeight)lll
1201 shifted (0,StrutHeight)cycle) withcolor .5white ;
1202 fill (lllrrrrrr 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 (lllrrr) 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 (StrutHeightStrutDepth<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 {lmtparshape} 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 = BodyFontSize2,
1302 dx = 0,
1303 dy = 0,
1304 lineheight = LineHeight,
1305 strutheight = StrutHeight,
1306 strutdepth = StrutDepth,
1307 topskip = StrutHeight,
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 = BodyFontSize2,
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 = BodyFontSize2,
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 := BodyFontSize2;
1364 vardef shape(expr w, h, o) =
1365 (o,o) -- (wo,o) & (wo,o) .. (.75wo,.5h) ..
1366 (w-2o,ho) & (w-2o,ho) -- (o,ho) -- 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 dont want to show the graphic, you may omit this step.
1386
1387\startbuffer
1388\defineoverlay[shapetest1][\useMPgraphic{shapetest-1}]
1389\defineoverlay[shapetest2][\useMPgraphic{shapetest-2}]
1390\defineoverlay[shapetest3][\useMPgraphic{shapetest-3}]
1391\defineoverlay[shapetest4][\useMPgraphic{shapetest-4}]
1392\stopbuffer
1393
1394\typebuffer \getbuffer
1395
1396As text, we use a quote from Douglas R.Hofstadters 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[shapetest1,shapetest2,shapetest3,shapetest4]
1402 \forgetall
1403 \setupalign[verytolerant,stretch,normal]
1404 \samplefile{douglas}
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=shapetest1]{\getshapetext}} {shapetest1}
1417 {\framed[offset=overlay,frame=off,background=shapetest2]{\getshapetext}} {shapetest2}
1418 {\framed[offset=overlay,frame=off,background=shapetest3]{\getshapetext}} {shapetest3}
1419 {\framed[offset=overlay,frame=off,background=shapetest4]{\getshapetext}} {shapetest4}
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 nonstandard 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 nonstandard 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
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 Hofstadters 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 |