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