% language=us runpath=texruns:manuals/metafun \startcomponent metafun-text \environment metafun-environment \startchapter[reference=sec:typesetting,title={Typesetting in \METAPOST}] \startintro It is said that a picture tells more than a thousand words. So you might expect that text in graphics becomes superfluous. Out of experience we can tell you that this is not the case. In this chapter we explore the ways to add text to \METAPOST\ graphics, and let you choose whether or not to have it typeset by \TEX. \stopintro \startsection[title={The process}] \index{text} You can let \METAPOST\ process text that is typeset by \TEX. Such text is first embedded in the \METAPOST\ file in the following way: \starttyping btex Some text to be typeset by \TEX etex \stoptyping This returns a picture, but only after \METAPOST\ has made sure that \TEX\ has converted it into something useful. This process of conversion is slightly system dependent and even a bit obscure. Traditional \METAPOST\ calls a program that filters the \type {btex}|\unknown|\type {etex} commands, next it calls \TEX\ by passing the output routine, in order to make sure that each piece of text ends up on its own page, and afterwards it again calls a program that converts the \DVI\ pages into \METAPOST\ pictures. In \LUATEX's \MPLIB\ a different route is followed. In \CONTEXT\ \MKII, when using \WEBC, you can generate the graphics at run||time. This takes more time than processing the graphics afterwards, but has the advantage that \TEX\ knows immediately what graphic it is dealing with. When enabled, \CONTEXT\ will call either \METAPOST, or, when the graphic contains \type {btex}||\type {etex} commands, call \TEXEXEC, which in turn makes sure that the right auxiliary programs are executed. In \CONTEXT\ \MKIV\ you won't notice this at all because everything is tightly integrated with \LUATEX's \MPLIB. This has an enormous speed gain: when this manual had about 425 pages, on my laptop with mobile 3840QM processor, one run of this document takes 18 seconds (14.5 with \LUAJITTEX) and that includes loading a bunch of (outline) fonts and processing some 2200 \METAPOST\ images. While writing the first version of this manual runtime was upto 50 times slower for half the number of pages so compared to \MKII\ we have gained a lot. \startFLOWchart[metatex] \startFLOWcell \name {script 1} \location {1,1} \shape {action} \text {\type{context}} \connection [rl] {context 1} \stopFLOWcell \startFLOWcell \name {context 1} \location {2,1} \shape {action} \text {\CONTEXT} \connection [bt] {metapost 1} \connection [rl] {script 2} \stopFLOWcell \startFLOWcell \name {metapost 1} \location {2,2} \shape {action} \text {\METAPOST} \stopFLOWcell \startFLOWcell \name {script 2} \location {3,1} \shape {action} \text {\type{context}} \connection [rl] {context 2} \connection [bt] {metapost 2} \stopFLOWcell \startFLOWcell \name {context 2} \location {4,1} \shape {action} \text {\CONTEXT} \stopFLOWcell \startFLOWcell \name {metapost 2} \location {3,2} \shape {action} \text {\METAPOST} \stopFLOWcell \stopFLOWchart \startplacefigure[title={How \TEX\ and \METAPOST\ work together.}] \FLOWchart[metatex] \stopplacefigure \stopsection \startsection[title={Environments}] \index{environments} In case you want to pass code that is shared by all \type {btex}||\type {etex} pictures, \METAPOST\ provides: \starttyping verbatimtex \DefineSomeCommands etex ; \stoptyping However, in \CONTEXT\ one has a better mechanism available. In \CONTEXT\ \MKII\ the advised method is passing environments. The best way to pass them is the following. As an example we switch to the 15 basic \POSTSCRIPT\ fonts. \startbuffer[pos] \startMPenvironment \usetypescript[palatino][texnansi] % mkii has encodings \setupbodyfont[palatino] \stopMPenvironment \stopbuffer \typebuffer[pos] This means that in code like the following, a Palatino font will be used. \starttyping \startMPcode draw btex Meta is a female lion! etex xysized (\the\textwidth,\the\textheight) ; \stopMPcode \stoptyping However, in \CONTEXT\ \MKIV\ this method is no longer recomended as all processing happens in the same run anyway. % beware: extensive scaling can make acrobat crash and okular drop the ! \startbuffer[lioncode] \startMPcode numeric w, h ; w := \the\textwidth ; h := w/2 ; picture p ; p := btex \colored[r=.375,g=.375]{Meta is a female lion!} etex xysized (w,h) ; picture q ; q := btex \colored[r=.625] {Meta is a female lion!} etex xysized (w,h) ; path b ; b := boundingbox p ; draw p ; for i=(.28w,.90h),(.85w,.90h),(w,.05h) : picture r ; r := q ; path s ; s := (fullsquare xscaled .05w yscaled .4h) shifted i ; clip r to s ; draw r ; % draw s ; endfor ; setbounds currentpicture to b ; \stopMPcode \stopbuffer \typebuffer[lioncode] \in {Figure} [lionclip] shows the previous sentence in a slightly different look. You may consider coloring the dots to be an exercise in clipping. \getbuffer[pos] \placefigure [here][lionclip] {An example of clipping.} {\getbuffer[lioncode]} \resetMPenvironment An environment can be reset with \typ {\resetMPenvironment} or by passing \type {reset} as first argument: \starttyping \startMPenvironment[reset] \usetypescript[postscript][texnansi] % mkii \setupbodyfont[postscript] \stopMPenvironment \stoptyping So, to summarize: if you're using \CONTEXT\ \MKIV\ you might as well forgot what you just read. \stopsection \startsection[title={Labels}] \index{labels} In \METAPOST\ you can use the \type {label} macro to position text at certain points. \starttyping label("x", origin) ; \stoptyping The font and scale are determined by two variables, \type {defaultfont} and \type {defaultscale}, the former expecting the name of a font in the form of a string, the latter expecting a numeric to be used in the scaling of the font. Should you choose not to set these yourself, they default to \type {"Mono"} and \type {1.0}, respectively. However, you can change the defaults as follows: \starttyping defaultfont := "texgyrepagella-regular*default" ; defaultscale := 1.2 ; \stoptyping These settings selects Pagella at about 12pt. You can also set these variables to \CONTEXT\ related values. For \CONTEXT\ graphics they are set to: \starttyping defaultfont := "\truefontname{Regular}*default" ; defaultscale := \the\bodyfontsize/10 ; \stoptyping This means that they will adapt themselves to the current body font (in this document we get \truefontname{Regular}) and the current size of the bodyfont (here \the\bodyfontsize/10). \stopsection \startsection[title={\TeX\ text}] \index{text} In the next example we will use a special mechanism for building graphics step by step. The advantage of this method is that we can do intermediate calculations in \TEX. Our objective is to write a macro that draws text along a circular path. While doing so we want to achieve the following: \startitemize[packed] \item the text should be properly kerned, i.e.\ the spacing between characters should be optimal, \item the position on the circle should vary, and \item the radius of the circle should vary. \stopitemize This implementation is not the most straightforward one, but by doing it step by step, at least we see what is involved. Later, we will see a better method. If you run these examples yourself, you must make sure that the \TEX\ environment of your document matches the one used by \METAPOST. We let the bodyfont match the font used in this document, and define \type {RotFont} to be the regular typeface, the one you are reading right now, but bold. \startbuffer \definefont[RotFont][RegularBold*default] \stopbuffer \typebuffer \getbuffer Since \METAPOST\ is unaware of kerning, we have to use \TEX\ to keep track of the positions. We will split the text into tokens (often characters) and store the result in an array of pictures (\type {pic}). We will also store the accumulated width in an array (\type {len}). The number of characters is stored in~\type {n}. In a few paragraphs we will see why the other arrays are needed. While defining the graphic, we need \TEX\ to do some calculations. Therefore, we will use \typ {\startMPdrawing ... \stopMPdrawing} to stepwise construct the definition. The basic pattern we will follow is: \starttyping \resetMPdrawing \startMPdrawing metapost code \stopMPdrawing tex code \startMPdrawing metapost code \stopMPdrawing \MPdrawingdonetrue \getMPdrawing \stoptyping In the process, we will use a few variables. We will store the individual characters of the text in the variable \type {pic}, its width in \type {wid} and the length of the string so far in \type {len}. Later we will use the \type {pos} array to store the position where a character ends up. The variable \type {n} holds the number of tokens. \startbuffer[init] \resetMPdrawing \startMPdrawing picture pic[] ; numeric wid[], len[], pos[], n ; wid[0] := len[0] := pos[0] := n := 0 ; \stopMPdrawing \stopbuffer \typebuffer[init] We also started fresh by resetting the drawing. From now on, each start command will add some more to this graphic. The next macro is responsible for collecting the data. Each element is passed on to \TEX, using the \type {btex} construct. So, \METAPOST\ itself will call \TEX ! \startbuffer[toks] \def\whatever#1% {\appendtoks#1\to\MPtoks \setbox\MPbox=\hbox{\bfd\the\MPtoks}% \startMPdrawing n := n + 1 ; len[n] := \the\wd\MPbox ; \stopMPdrawing \startMPdrawing[-] pic[n] := textext("\bfd\setstrut\strut#1") ; pic[n] := pic[n] shifted - llcorner pic[n] ; \stopMPdrawing} \handletokens MetaPost is Fun!\with\whatever \stopbuffer \typebuffer[toks] We use the low level \CONTEXT\ macro \type {\appendtoks} to extend the token list \type {\MPtoks}. The \type {\handletokens} macro passes each token (character) of \typ {MetaPost is Fun!} to the macro \type {\whatever}. The tokens are appended to the token register \type {\MPtoks} (already defined). Then we typeset the content of \type {\MPtoks} in \type {\MPbox} (also already defined). The width of the box is passed to \METAPOST\ and stored in \type {len}. By default the content of the drawing is expanded, which means that the macro is replaced by its current meaning, so the current width ends up in the \METAPOST\ file. The next part of the drawing, starting with \type {btex}, puts the token in a picture. This time we don't expand the drawing, since we want to pass font information. Here, the \type {[-]} suppresses expansion of \typ {btex \bfd #1 etex}. The process is iterated by \type {\handletokens} for each character of the text \typ {MetaPost is Fun!}. Before we typeset the text, now available in pieces in \type {pic}, in a circle, we will first demonstrate what they look like. You may like to take a look at the file \type {mpgraph.mp} to see what is passed to \METAPOST. \startbuffer[test] \startMPdrawing pair len ; len := origin ; for i=1 upto n : draw pic[i] shifted len ; draw boundingbox pic[i] shifted len withpen pencircle scaled .25pt withcolor red ; len := len+(xpart urcorner pic[i]-xpart llcorner pic[i],0) ; endfor ; \stopMPdrawing \stopbuffer \typebuffer[test] \startbuffer[show] \MPdrawingdonetrue\getMPdrawing \stopbuffer We can call up this drawing with \type {\getMPdrawing}, but first we inform the compiler that our \METAPOST\ drawing is completed. \typebuffer[show] This results in: \startlinecorrection[blank] \getbuffer[init,toks,test,show] \stoplinecorrection Compare this text with the text as typeset by \TEX: \blank \start \bfd MetaPost is Fun!\par \stop \blank and you will see that the text produced by \METAPOST\ is not properly kerned. When putting characters after each other, \TEX\ uses the information available in the font, to optimize the spacing between characters, while \METAPOST\ looks at characters as separate entities. But, since we have stored the optimal spacing in \type {len}, we can let \METAPOST\ do a better job. Let's first calculate the correction needed. \startbuffer[kern] \startMPdrawing for i=1 upto n : wid[i] := abs(xpart urcorner pic[i] - xpart llcorner pic[i]) ; pos[i] := len[i]-wid[i] ; endfor ; \stopMPdrawing \stopbuffer \typebuffer[kern] This compares well to the text as typeset by \TEX: \blank \start \bfd MetaPost is Fun!\par \stop \blank We can now use the values in \type {pos} to position the pictures according to what \TEX\ considered to be the best (relative) position. \startbuffer[test] \startMPdrawing for i=1 upto n : draw pic[i] shifted (pos[i],0) ; draw boundingbox pic[i] shifted (pos[i],0) withpen pencircle scaled .25pt withcolor red ; endfor ; \stopMPdrawing \stopbuffer \typebuffer[test] That this correction is adequate, is demonstrated in the next graphic. If you look closely, you will see that for instance the \quote {o} is moved to the left, under the capital \quote {P}. \startlinecorrection[blank] \getbuffer[init,toks,kern,test,show] \stoplinecorrection When we want to position the pictures along a circle, we need to apply some rotations, especially because we want to go clockwise. Since we don't want to use \quote {complicated} math or more advanced \METAPOST\ code yet, we will do it in steps. \startbuffer[swap] \startMPdrawing for i=1 upto n: pic[i] := pic[i] rotatedaround(origin,-270) ; endfor ; \stopMPdrawing \stopbuffer \typebuffer[swap] \startlinecorrection[blank] \getbuffer[init,toks,kern,swap,test,show] \stoplinecorrection \startbuffer[cent] \startMPdrawing for i=1 upto n : pic[i] := pic[i] shifted (0,ypart -.5[ulcorner pic[i],llcorner pic[i]]) ; endfor ; \stopMPdrawing \stopbuffer We will now center the pictures around the baseline. Centering comes down to shifting over half the height of the picture. This can be expressed by: \starttyping ypart -.5[ulcorner pic[i],llcorner pic[i]] \stoptyping but different ways of calculating the distance are possible too. \typebuffer[cent] So, now we have: \startlinecorrection[blank] \getbuffer[init,toks,kern,swap,cent,test,show] \stoplinecorrection When we typeset on a (half) circle, we should map the actual length onto a partial circle. We denote the radius with an~\type {r} and shift the pictures to the left. \startbuffer[shif] \startMPdrawing numeric r ; r := len[n]/pi ; for i=1 upto n : pic[i] := pic[i] shifted (-r,0) ; endfor ; \stopMPdrawing \stopbuffer \typebuffer[shif] You can now use the following code to test the current state of the pictures. Of course this code should not end up in the final definitions. \startbuffer[test] \startMPdrawing draw origin withpen pencircle scaled 5pt withcolor red ; for i=1 upto n : draw pic[i] ; draw boundingbox pic[i] withpen pencircle scaled .25pt withcolor red ; endfor ; \stopMPdrawing \stopbuffer \typebuffer[test] \startlinecorrection[blank] \getbuffer[init,toks,kern,swap,cent,shif,test,show] \stoplinecorrection Later we will write a compact, efficient macro to take care of rotation. However, for the moment, so as not to overwhelm you with complicated code, we will rotate each individual picture with the following code fragment. \startbuffer[rots] \startMPdrawing numeric delta, extra, radius, rot[] ; delta := extra := radius := 0 ; for i=1 upto n : rot[i] := extra+delta-((pos[i]+.5wid[i])/len[n])*(180+2delta) ; endfor ; \stopMPdrawing \stopbuffer \typebuffer[rots] Here we introduce a few variables that we can use later to tune the result a bit. With \type {delta}, the space between the characters can be increased, while \type {extra} rotates the whole string around the origin. The \type {radius} variable can be used to increase the distance to the origin. Without these variables, the assignment would have been: \starttyping rot[i] := ((pos[i]+.5wid[i])/len[n])*180 ; \stoptyping Placing the pictures is now rather easy: \startbuffer[done] \startMPdrawing for i=1 upto n : draw pic[i] shifted (-radius,0) rotatedaround(origin,rot[i]) ; endfor ; \stopMPdrawing \stopbuffer \typebuffer[done] The pictures are now positioned on half a circle, properly kerned. \startlinecorrection[blank] \getbuffer[init,toks,kern,swap,cent,shif,rots,done,show] \stoplinecorrection A bit more insight is given in the next picture: \startbuffer[test] \startMPdrawing def moved(expr i) = shifted (-radius,0) rotatedaround(origin,rot[i]) enddef ; pickup pencircle scaled .5pt ; for i=1 upto n : draw pic[i] moved(i) ; draw boundingbox pic[i] moved(i) withcolor red ; draw origin -- center pic[i] moved(i) withcolor green ; endfor ; draw tcircle scaled 2r withcolor blue ; \stopMPdrawing \stopbuffer \startlinecorrection[blank] \getbuffer[init,toks,kern,swap,cent,shif,rots,test,show] \stoplinecorrection This was defined as follows. The path variable \type {tcycle} is predefined to the top half of a fullcircle. \typebuffer[test] We will now package all of this into a nice, efficient macro, using, of course, the predefined scratch registers \type {\MPtoks} and \type {\MPbox}. First we define the token processor. Note again the expansion inhibition switch \type {[-]}. \startbuffer \def\processrotationtoken#1% {\appendtoks#1\to\MPtoks \setbox\MPbox=\hbox{\RotFont\the\MPtoks}% \startMPdrawing n := n + 1 ; len[n] := \the\wd\MPbox ; \stopMPdrawing \startMPdrawing[-] pic[n] := textext("\RotFont\setstrut\strut#1") ; pic[n] := pic[n] shifted - llcorner pic[n] ; \stopMPdrawing} \stopbuffer \typebuffer \getbuffer The main macro is a bit more complicated but by using a few scratch numerics, we can keep it readable. \startbuffer \def\rotatetokens#1#2#3#4% delta extra radius tokens {\vbox\bgroup \MPtoks\emptytoks \resetMPdrawing \startMPdrawing picture pic[] ; numeric wid, len[], rot ; numeric delta, extra, radius, n, r ; len[0] := n := 0 ; delta := #1 ; extra := #2 ; radius := #3 ; \stopMPdrawing \handletokens#4\with\processrotationtoken \startMPdrawing r := len[n]/pi ; for i=1 upto n : wid := abs(xpart lrcorner pic[i] - xpart llcorner pic[i]) ; rot := extra + delta - ((len[i]-.5wid)/len[n]) * (180+2delta) ; draw pic[i] rotatedaround (origin,-270) shifted (-r-radius, ypart -.5[ulcorner pic[i], llcorner pic[i]]) rotatedaround (origin,rot) ; endfor ; \stopMPdrawing \MPdrawingdonetrue \getMPdrawing \resetMPdrawing \egroup} \stopbuffer \typebuffer \getbuffer \startbuffer \startcombination[3*1] {\rotatetokens {0} {0}{0}{Does it work ok?}} {A} {\rotatetokens{20} {0}{0}{Does it work ok?}} {B} {\rotatetokens{20}{30}{0}{Does it work ok?}} {C} \stopcombination \stopbuffer We can use this macro as follows: \typebuffer \startlinecorrection[blank] \getbuffer \stoplinecorrection The previous macro is not really an example of generalization, but we used it for demonstrating how to build graphics in a stepwise way. If you put the steps in buffers, you can even combine steps and replace them at will. This is how we made the previous step by step examples: We put each sub||graphic in a buffer and then called the ones we wanted. We now present a more general approach to typesetting along a given path. This method is not only more robust and general, it is also a more compact definition, especially if we omit the tracing and testing code. We use a familiar auxiliary definition. The \type {\setstrut} and \type {\strut} commands ensure that the lines have the proper depth and height. \startbuffer \def\processfollowingtoken#1% {\appendtoks#1\to\MPtoks \setbox\MPbox=\hbox{\RotFont\setstrut\strut\the\MPtoks}% \startMPdrawing n := n + 1 ; len[n] := \the\wd\MPbox ; \stopMPdrawing \startMPdrawing[-] pic[n] := btex \RotFont\setstrut\strut#1 etex ; pic[n] := pic[n] shifted -llcorner pic[n] ; \stopMPdrawing} \stopbuffer \typebuffer \getbuffer In \MKII\ the previous code is collected in the macro \type {\followtokens} but in \MKIV\ we use a different approach. There we use a mix of \TEX, \METAPOST, and \LUA\ to define that macro. The principles remain the same but the code is more robust. \useMPlibrary[txt] So, how does this compare to earlier results? The original, full text as typeset by \TEX, looks like: \blank \start \RotFont We now follow some arbitrary path ... \stop \blank In the examples, the text is typeset along the path with: \startbuffer[toks] \followtokens{We now follow some arbitrary path ...} \stopbuffer \typebuffer[toks] % \startlinecorrection[blank] % \getbuffer[toks] % \stoplinecorrection % Since we did not set a path, a dummy path is used. We can provide a path by % (re)defining the graphic \type {followtokens}. % \startbuffer[trac] % \startMPinclusions % boolean TraceRot ; TraceRot := true ; % \stopMPinclusions % \stopbuffer \startbuffer[draw] \startuseMPgraphic{followtokens} path RotPath ; RotPath := fullcircle ; \stopuseMPgraphic \stopbuffer \startbuffer \typebuffer[draw] \startlinecorrection[blank] \hbox {\getbuffer[draw,toks]\hskip1cm \getbuffer[trac,draw,toks]} \stoplinecorrection \stopbuffer \getbuffer \startbuffer[draw] \startuseMPgraphic{followtokens} path RotPath ; RotPath := reverse fullcircle ; \stopuseMPgraphic \stopbuffer \getbuffer \startbuffer[draw] \startuseMPgraphic{followtokens} path RotPath ; RotPath := (-3cm,-1cm)--(0,1cm)--(3cm,-1cm) ; \stopuseMPgraphic \stopbuffer \getbuffer \startbuffer[draw] \startuseMPgraphic{followtokens} path RotPath ; RotPath := (-3cm,0)--(3cm,1cm) ; \stopuseMPgraphic \stopbuffer \getbuffer \startbuffer[draw] \startuseMPgraphic{followtokens} path RotPath ; RotPath := (-3cm,0)..(-1cm,1cm)..(3cm,0) ; \stopuseMPgraphic \stopbuffer \getbuffer \startbuffer[draw] \startuseMPgraphic{followtokens} path RotPath ; RotPath := (-3cm,0)..(-1cm,1cm)..(0cm,-2cm)..(3cm,0) ; \stopuseMPgraphic \stopbuffer \getbuffer When turned on, tracing will produce bounding boxes as well as draw the path. Tracing can be turned on by saying: \typebuffer[trac] % let's turn it off now \startMPinclusions boolean TraceRot ; TraceRot := false ; \stopMPinclusions The next example is dedicated to Giuseppe Bilotta who wants to handle multiple strings and uses a patched version of \type {\followtokens}. To avoid a complicated explanation, we will present an alternative here that uses overlays. This method also avoids complicated path definitions. \startbuffer \startoverlay {\startuseMPgraphic{followtokens} draw fullcircle scaled 5cm . withpen pencircle scaled 1pt withcolor .625yellow ; draw fullsquare scaled 5.25cm withpen pencircle scaled 1pt withcolor .625red ; drawoptions (withcolor .625red) ; path RotPath ; RotPath := halfcircle scaled 5cm ; setbounds currentpicture to boundingbox fullcircle scaled 5.25cm ; \stopuseMPgraphic \followtokens { Met{\`a} superiore }} {\startuseMPgraphic{followtokens} drawoptions (withcolor .625red) ; path RotPath ; RotPath := halfcircle rotated 90 scaled 5cm ; setbounds currentpicture to boundingbox fullcircle scaled 5.25cm ; \stopuseMPgraphic \followtokens { {$\star$} }} {\startuseMPgraphic{followtokens} drawoptions (withcolor .625red) ; path RotPath ; RotPath := halfcircle rotated 180 scaled 5cm ; setbounds currentpicture to boundingbox fullcircle scaled 5.25cm ; \stopuseMPgraphic \followtokens { Met{\`a} inferiore }} {\startuseMPgraphic{followtokens} drawoptions (withcolor .625red) ; path RotPath ; RotPath := halfcircle rotated 270 scaled 5cm ; setbounds currentpicture to boundingbox fullcircle scaled 5.25cm ; \stopuseMPgraphic \followtokens { {$\star$} }} \stopoverlay \stopbuffer \typebuffer In order to fool the overlay macro that each graphic has the same size, we force a bounding box. \startlinecorrection[blank] \getbuffer \stoplinecorrection \stopsection \startsection[title={Talking to \TEX}] Sometimes, others may say oftentimes, we are in need for some fancy typesetting. If we want to typeset a paragraph of text in a non standard shape, like a circle, we have to fall back on \type {\parshape}. Unfortunately, \TEX\ is not that strong in providing the specifications of more complicated shapes, unless you are willing to do some complicated arithmetic \TEX. Given that \METAPOST\ knows how to deal with shapes, the question is: \quotation {Can \METAPOST\ be of help?} In the process of finding out how to deal with this, we first define a simple path. Because we are going to replace pieces of code, we will compose the graphic from components. First, we create the path. \startbuffer \startuseMPgraphic{text path} path p ; p := ((0,1)..(-1,0)..(1,0)--cycle) scaled 65pt ; \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer This shape is not that beautiful, but it has a few characteristics that will help us to identify bordercases. \startbuffer \startuseMPgraphic{text draw} drawarrow p withpen pencircle scaled 1pt withcolor red ; \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer Now we use \CONTEXT's \type {\includeMPgraphic} command to build our graphic from the previously defined components. \startbuffer \startuseMPgraphic{text} \includeMPgraphic{text path} \includeMPgraphic{text draw} \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer When called with \type {\useMPgraphic{text}}, we get: \startlinecorrection[blank] \useMPgraphic{text} \stoplinecorrection For the moment we start the path at $(x=0,y>0)$, but later using more complicated macros, we will see that we can use arbitrary paths. We are going to split the path in two, and will use the points that make up the bounding box as calcutated by \METAPOST. The next graphic shows one of these points, the lower left corner, available as point \typ {llcorner p}. \startbuffer \startuseMPgraphic{text draw} draw p withpen pencircle scaled 3pt withcolor red ; draw boundingbox p withpen pencircle scaled 1pt ; draw llcorner p withpen pencircle scaled 5pt ; \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer \startlinecorrection[blank] \useMPgraphic{text} \stoplinecorrection The five points that \METAPOST\ can report for each path or picture are: \starttabulate[|Tl|l|] \NC llcorner \NC lower left corner \NC \NR \NC lrcorner \NC lower right corner \NC \NR \NC urcorner \NC upper right corner \NC \NR \NC ulcorner \NC upper left corner \NC \NR \NC center \NC intersection of the diagonals \NC \NR \stoptabulate If we want to typeset text inside this circle, we need to know where a line starts and ends. Given that lines are horizontal and straight, we therefore need to calculate the intersection points of the lines and the path. As a first step, we calculate the top and bottom of the path and after that we split off the left and right path. \startbuffer \startuseMPgraphic{text split} pair t, b ; path l, r ; t := (ulcorner p -- urcorner p) intersectionpoint p ; b := (llcorner p -- lrcorner p) intersectionpoint p ; l := p cutbefore t ; l := l cutafter b ; r := p cutbefore b ; r := r cutafter t ; \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer The \type {intersectionpoint} macro returns the point where two paths cross. If the paths don't cross, an error is reported, when the paths cross more times, just one point is returned. The \type {cutafter} and \type {cutbefore} commands do as their names say and return a path. In the \type {text split} code fragment, \type {t} and \type {b} are the top points of the main path, while \type {l} and \type {r} become the left and right half of path \type {p}. We now draw the original path using a thick pen and both halves with a thinner pen on top of the original. The arrows show the direction. \startbuffer \startuseMPgraphic{text draw} draw p withpen pencircle scaled 3pt withcolor red ; drawarrow l withpen pencircle scaled 1pt withcolor green ; drawarrow r withpen pencircle scaled 1pt withcolor blue ; \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer We use \type {\includeMPgraphic} to assemble the components: \startbuffer \startuseMPgraphic{text} \includeMPgraphic{text path} \includeMPgraphic{text split} \includeMPgraphic{text draw} \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer This graphic is typeset with \type {\useMPgraphic{text}}: \startlinecorrection[blank] \useMPgraphic{text} \stoplinecorrection Before we are going to use them, we define some variables that specify the text. We use a baseline distance of 8~points. The part of the line above the baseline is 7.2~points, while the (maximum) depth is 2.8~points. These ratios are the ones we use in \CONTEXT. Because we don't want the text to touch the circle so we define an offset too. \startbuffer \startuseMPgraphic{text vars} MyOffset := LineHeight/2 ; MyTopSkip := StrutHeight ; \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer We more or less achieve the offset by scaling the path. In doing so, we use the width and height, which we call \type {hsize} and \type {vsize}, thereby conforming to the \TEX\ naming scheme. First we calculate both dimensions from the bounding box of the path. Next we down scale the path to compensate for the offset. When done, we recalculate the dimensions. \startbuffer \startuseMPgraphic{text move} pair t, b ; path q, l, r ; hsize := xpart lrcorner p - xpart llcorner p ; vsize := ypart urcorner p - ypart lrcorner p ; q := p xscaled ((hsize-2MyOffset)/hsize) yscaled ((vsize-2MyOffset)/vsize) ; hsize := xpart lrcorner q - xpart llcorner q ; vsize := ypart urcorner q - ypart lrcorner q ; \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer \startbuffer \startuseMPgraphic{text split} t := (ulcorner q -- urcorner q) intersectionpoint q ; b := (llcorner q -- lrcorner q) intersectionpoint q ; l := q cutbefore t ; l := l cutafter b ; r := q cutbefore b ; r := r cutafter t ; \stopuseMPgraphic \stopbuffer We adapt the \type {text split} code to use the reduced path instead of the original. \typebuffer \getbuffer \startbuffer \startuseMPgraphic{text draw} drawarrow p withpen pencircle scaled 1pt withcolor red ; draw t withpen pencircle scaled 2pt ; draw b withpen pencircle scaled 2pt ; drawarrow l withpen pencircle scaled 1pt withcolor green ; drawarrow r withpen pencircle scaled 1pt withcolor blue ; \stopuseMPgraphic \stopbuffer In order to test what we have reached so far, we draw the original path, the left and right part of the reduced path, and both the top and bottom point. \typebuffer \getbuffer Again we use \type {\includeMPgraphic} to combine the components into a graphic. \startbuffer \startuseMPgraphic{text} \includeMPgraphic{text path} \includeMPgraphic{text vars} \includeMPgraphic{text move} \includeMPgraphic{text split} \includeMPgraphic{text draw} \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer Then we use \type {\useMPgraphic{text}} to call up the picture. \startlinecorrection[blank] \useMPgraphic{text} \stoplinecorrection The offset is not optimal. Note the funny gap at the top. We could try to fix this, but there is a better way to optimize both paths. We lower the top edge of \type {q}'s bounding box by \type {MyTopSkip}, then cut any part of the left and right pieces of \type {q} that lie above it. Similarly, we raise the bottom edge and cut off the pieces that fall below this line. \startbuffer \startuseMPgraphic{text cutoff} path tt, bb ; tt := (ulcorner q -- urcorner q) shifted (0,-MyTopSkip) ; bb := (llcorner q -- lrcorner q) shifted (0,StrutDepth) ; l := l cutbefore (l intersectionpoint tt) ; l := l cutafter (l intersectionpoint bb) ; r := r cutbefore (r intersectionpoint bb) ; r := r cutafter (r intersectionpoint tt) ; \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer Because we use \type {\includeMPgraphic} to construct the graphic, we can redefine \type {text draw} to show the result of this effort. \startbuffer \startuseMPgraphic{text draw} drawarrow p withpen pencircle scaled 1pt withcolor red ; drawarrow l withpen pencircle scaled 1pt withcolor green ; drawarrow r withpen pencircle scaled 1pt withcolor blue ; \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer The \type {text} graphic now becomes: \startbuffer \startuseMPgraphic{text} \includeMPgraphic{text path} \includeMPgraphic{text vars} \includeMPgraphic{text move} \includeMPgraphic{text split} \includeMPgraphic{text cutoff} \includeMPgraphic{text draw} \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer Or, as graphic: \startlinecorrection[blank] \useMPgraphic{text} \stoplinecorrection We are now ready for an attempt to calculate the shape of the text. For each line, we have to calculate the left and right intersection points, and since a line has a height and depth, we have to determine which part touches first. \startbuffer \startuseMPgraphic{text calc} vardef found_point (expr lin, pat, sig) = pair a, b ; a := pat intersection_point (lin shifted (0,StrutHeight)) ; if intersection_found : a := a shifted (0,-StrutHeight) ; else : a := pat intersection_point lin ; fi ; b := pat intersection_point (lin shifted (0,-StrutDepth)) ; if intersection_found : if sig : if xpart b > xpart a : a := b shifted (0,StrutDepth) fi ; else : if xpart b < xpart a : a := b shifted (0,StrutDepth) fi ; fi ; fi ; a enddef ; \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer Instead of using \METAPOST's \type {intersectionpoint} macro, we use one that comes with \CONTEXT. That way we don't get an error message when no point is found, and can use a boolean flag to take further action. Since we use a \type {vardef}, all calculations are hidden and the~\type {a} at the end is returned, so that we can use this macro in an assignment. The \type {sig} variable is used to distinguish between the beginning and end of a line (the left and right subpath). \startbuffer \startuseMPgraphic{text step} path line; pair lll, rrr ; for i=MyTopSkip step LineHeight until vsize : line := (ulcorner q -- urcorner q) shifted (0,-i) ; lll := found_point(line,l,true ) ; rrr := found_point(line,r,false) ; \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer Here we divide the available space in lines. The first line starts at \type {StrutHeight} from the top. We can now finish our graphic by visualizing the lines. Both the height and depth of the lines are shown. \startbuffer \startuseMPgraphic{text line} fill (lll--rrr--rrr shifted (0,StrutHeight)--lll shifted (0,StrutHeight)--cycle) withcolor .5white ; fill (lll--rrr--rrr shifted (0,-StrutDepth)--lll shifted (0,-StrutDepth)--cycle) withcolor .7white ; draw lll withpen pencircle scaled 2pt ; draw rrr withpen pencircle scaled 2pt ; draw (lll--rrr) withpen pencircle scaled .5pt ; \stopuseMPgraphic \startuseMPgraphic{text done} endfor ; \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer The result is still a bit disappointing. \startbuffer \startuseMPgraphic{text} \includeMPgraphic{text path} \includeMPgraphic{text vars} \includeMPgraphic{text move} \includeMPgraphic{text split} \includeMPgraphic{text cutoff} \includeMPgraphic{text draw} \includeMPgraphic{text calc} \includeMPgraphic{text step} \includeMPgraphic{text line} \includeMPgraphic{text done} \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer \startlinecorrection[blank] \useMPgraphic{text} \stoplinecorrection In order to catch the overflow at the bottom, we need to change the \type {for}||loop a bit, so that the number of lines does not exceed the available space. The test that surrounds the assignment of \type {vvsize} makes sure that we get better results when we (on purpose) take a smaller height. \startbuffer \startuseMPgraphic{text step} path line; pair lll, rrr ; numeric vvsize ; if (StrutHeight+StrutDepth 0 : & q fi cutafter t ; \stoptyping As always, when implementing a feature like this, some effort goes into a proper user interface. In doing so, we need some \TEX\ trickery that goes beyond this text, like collecting text and splitting of the part needed. Also, we want to be able to handle multiple shapes at once, like the next example demonstrates. \stopsection \startsection[title={Libraries}] \index{graphics+libraries} In \MKIV\ and \LMTX\ the \METAFUN\ driven text around a curve is a core functionality. In \LMTX\ the specific paragraph shape are available in the core too. Otherwise you need to load a module: \startbuffer \useMPlibrary[txt] \stopbuffer \typebuffer \getbuffer We define four shapes. They are not really beautiful, but they demonstrate what happens in border cases. For instance, too small first lines are ignored. First we define a circle. Watch how the dimensions are set in the graphic. The arguments passed to \type {build_parshape} are: path, an offset, an additional horizontal and vertical displacement, the baseline distance, the height and depth of the line, and the height of the first line (MyTopSkip in \TEX\ terminology). The height and depth of a line are often called strut height and depth, with a strut being an invisible character with maximum dimensions. \startbuffer \startuseMPgraphic{test 1} path p ; p := fullcircle scaled 6cm ; build_parshape(p,6pt,0,0,LineHeight, StrutHeight,StrutDepth,StrutHeight) ; draw p withpen pencircle scaled 1pt ; \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer The second shape is a diamond. This is a rather useless shape, unless the text suits the small lines at the top and bottom. \startbuffer \startuseMPgraphic{test 2} path p ; p := fullsquare rotated 45 scaled 5cm ; build_parshape(p,6pt,0,0,LineHeight, StrutHeight,StrutDepth,StrutHeight) ; draw p withpen pencircle scaled 1pt ; \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer The third and fourth shape demonstrate that providing a suitable offset is not always trivial. \startbuffer \startuseMPgraphic{test 3} numeric w, h ; w := h := 6cm ; path p ; p := (.5w,h) -- (0,h) -- (0,0) -- (w,0) & (w,0) .. (.75w,.5h) .. (w,h) & (w,h) -- cycle ; build_parshape(p,6pt,0,0,LineHeight, StrutHeight,StrutDepth,StrutHeight) ; draw p withpen pencircle scaled 1pt ; \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer Contrary to the first three shapes, here we use a different path for the calculations and the drawing. Watch carefully! If, instead of an offset, we pass a path, \METAPOST\ is able to calculate the right dimensions and offsets. This is needed, since we need these later on. \startbuffer \startuseMPgraphic{test 4} numeric w, h, o ; def shape = (o,o) -- (w-o,o) & (w-o,o) .. (.75w-o,.5h) .. (w-2o,h-o) & (w-2o,h-o) -- (o,h-o) -- cycle enddef ; w := h := 6cm ; o := 6pt ; path p ; p := shape ; w := h := 6cm ; o := 0pt ; path q ; q := shape ; build_parshape(p,q,6pt,6pt,LineHeight, StrutHeight,StrutDepth,StrutHeight) ; draw q withpen pencircle scaled 1pt ; \stopuseMPgraphic \stopbuffer \typebuffer \getbuffer Since we also want these graphics as backgrounds, we define them as overlays. If you don't want to show the graphic, you may omit this step. \startbuffer \defineoverlay[test 1][\useMPgraphic{test 1}] \defineoverlay[test 2][\useMPgraphic{test 2}] \defineoverlay[test 3][\useMPgraphic{test 3}] \defineoverlay[test 4][\useMPgraphic{test 4}] \stopbuffer \typebuffer \getbuffer As text, we use a quote from Douglas R.~Hofstadter's book \quotation {Metamagical Themas, Questing for the Essence of Mind and Pattern}. Watch how we pass a list of shapes. \startbuffer[text] \startshapetext[test 1,test 2,test 3,test 4] \forgetall % as it says \setupalign[verytolerant,stretch,normal]% \input douglas % Douglas R. Hofstadter \stopshapetext \stopbuffer \typebuffer[text] Finally we combine text and shapes. Since we also want a background, we use \type {\framed}. The macros \type {\parwidth} and \type {\parheight} are automatically set to the current shape dimensions. The normal result is shown in \in {figure} [fig:shapes]. \startbuffer[shapes] \startbuffer \startcombination[2*2] {\framed[offset=overlay,frame=off,background=test 1]{\getshapetext}} {test 1} {\framed[offset=overlay,frame=off,background=test 2]{\getshapetext}} {test 2} {\framed[offset=overlay,frame=off,background=test 3]{\getshapetext}} {test 3} {\framed[offset=overlay,frame=off,background=test 4]{\getshapetext}} {test 4} \stopcombination \stopbuffer \stopbuffer \typebuffer[shapes] \getbuffer[shapes] By using a buffer we keep \type {\placefigure} readable. \startbuffer[a] \placefigure [here][fig:shapes] {A continuous text, typeset in a non||standard shape, spread over four areas, and right aligned.} {\getbuffer} \stopbuffer \startbuffer[b] \placefigure [here][fig:shapes] {A continuous text, typeset in a non||standard shape, spread over four areas.} {\scale[factor=max,height=.9\textheight]{\getbuffer}} \stopbuffer \typebuffer[a] \doifmodeelse{screen}{\getbuffer[text,b]}{\getbuffer[text,a]} The traced alternative is shown in \in {figure} [fig:traced shapes]. This one is defined as: \startbuffer[a] \placefigure [here][fig:traced shapes] {A continuous text, typeset in a non||standard shape, spread over four areas (tracing on).} {\startMPinclusions boolean trace_parshape ; trace_parshape := true ; \stopMPinclusions \getbuffer} \stopbuffer \startbuffer[b] \placefigure [here][fig:traced shapes] {A continuous text, typeset in a non||standard shape, spread over four areas (tracing on).} {\startMPinclusions boolean trace_parshape ; trace_parshape := true ; \stopMPinclusions \scale[factor=max,height=.9\textheight]{\getbuffer}} \stopbuffer \typebuffer[a] \doifmodeelse{screen}{\getbuffer[text,b]}{\getbuffer[text,a]} % {\em This mechanism is still somewhat experimental and will be optimized and % extended with name spaces and more.} \blank We can combine all those tricks, although the input is somewhat fuzzy. First we define a quote typeset in a circular paragraph shape. \startbuffer[shape] \startuseMPgraphic{center} build_parshape(fullcircle scaled 8cm,0,0,0,LineHeight, StrutHeight,StrutDepth,StrutHeight) ; \stopuseMPgraphic \startshapetext[center] \input douglas \stopshapetext \defineoverlay[center][\useMPgraphic{center}] \stopbuffer \typebuffer[shape] We will surround this text with a circular line, that we define as follows. By using a buffer we keep things organized. \startbuffer \startbuffer[circle] \startuseMPgraphic{followtokens} path RotPath ; RotPath := reverse fullcircle rotatedaround(origin,90) xscaled \overlaywidth yscaled \overlayheight ; drawoptions (withcolor .625red) ; \stopuseMPgraphic \followtokens {This is just a dummy text, kerned by \TeX\ and typeset in a circle using \MetaPost.\quad} \stopbuffer \defineoverlay[edge][{\getbuffer[circle]}] \stopbuffer \typebuffer \getbuffer The text and graphics come together in a framed text: \startbuffer \startbuffer[quote] \framed [offset=24pt, background=edge, frame=off, backgroundoffset=-18pt] {\getshapetext} \stopbuffer \placefigure {One more time Hofstadter's quotation (normal).} {\getbuffer[shape,quote]} \placefigure {One more time Hofstadter's quotation (traced).} {\startMPinclusions boolean TraceRot ; TraceRot := true ; \stopMPinclusions \getbuffer[shape,quote]} \stopbuffer \typebuffer \getbuffer % {\em Here also, I will rewrite things a bit so that we can avoid \type % {\startMPdrawing} outside the macro, and thereby avoid problems. I can also add % the maps cdrom cover as example.} \stopsection % \startsection[title={Visualizing \TEX}] % % The next example is a bit out of place in this manual, but nevertheless % demonstrates how one can use \METAPOST\ to get insight in what \TEX\ is doing % inside. % % The author of \PDFTEX, \THANH, has extended the paragraph builder with a % provision for protruding characters and glyphs substitution, also known as {\it % hz} (which stands for Hermann Zapf). The {\it hz} optimization involves an % additional pass over the lines and|/|or paragraph, in order to determine how % inconsistency in gaps can be reduced by substituting an \quote {\scale [sx=1.01] % {a}} by an \quote {\scale [sx=5] {a}} or \quote {\scale [sx=.5] {a}}. In \in % {figure} [fig:hz] you can find the visualization in action. By means of colors we % indicate in what way glyphs are substituted by slightly larger or smaller values. % More details on how the {\it hz} optimization works can be found in \THANH's % thesis. % % \placefigure % [page][fig:hz] % {When we feed \TEX\ code into \METAPOST\ and back, we % can visualize {\it hz}||optimization in a colorful way.} % {\doifmodeelse{screen} % {\externalfigure[mfun-hzs.pdf][height=.8\textheight]} % {\externalfigure[mfun-hzp.pdf][height=.8\textheight]}} % % In order to avoid a complicated discussion about how to set up \PDFTEX\ to use % {\it hz} |<|this can best be left over to the macro package that you use|>| we % will illustrate the method behind this kind of visualizations in a more simple % case. % % When you include a \METAPOST\ graphic in \PDFTEX, the output produced by % \METAPOST\ is interpreted by a bunch of macros and converted into raw \PDF\ code. % In the process special extensions, like shading, transparency, graphic inclusion, % are taken care of. When the converter encounters a font inclusion directive, % i.e.\ the \POSTSCRIPT\ \type {fshow} operator, it uses the \TEX\ font handler to % take care of the font. A benefit of this approach is that \TEX\ and \METAPOST\ % share the same font resources and therefore the inclusion is done in the way % expected. % % The low level macro that takes care of the font inclusion provides a couple of so % called hooks, that permit us to do additional manipulations with the character % sequences that are encountered. % % \startbuffer[demo] % draw % btex \definedfont[cmr10]% % Combine the power of \TeX\ and \MetaPost ! % etex scaled 2 ; % \stopbuffer % % \typebuffer[demo] % % When processed, this gives the graphic: % % \startlinecorrection[blank] % \processMPbuffer[demo] % \stoplinecorrection % % The result is not spectacular, and there is no indication that \METAPOST\ has % been in action. The following line of code sets the hook \type {\MPfshowcommand} % |<|this commands takes one argument|>| to produce a ruled horizontal box. % % \startbuffer % \let\MPfshowcommand\ruledhbox % \stopbuffer % % \typebuffer % % \startlinecorrection[blank] % \getbuffer \processMPbuffer[demo] % \stoplinecorrection % % If you watch closely, you will see that the ruled boxes contain one or more % characters (or more precise glyphs). This is a result from \TEX\ explicitely % kerning characters. % % A second hook is provided in the macro that takes care of the font switch. This % command is defined as follows: % % \starttyping % \def\setMPfshowfont#1#2% % {\font\temp=#1\space at #2\relax\temp} % \stoptyping % % The first argument is the raw font name, and the second argument specifies the % desired size. If we want to see what fonts are involved, we can redefine the % hooks as follows. % % \starttyping % \def\setMPfshowfont#1#2% % {\message{[using #1 at #2 in mp graphic]}% % \font\temp=#1\space at #2\relax\temp} % \stoptyping % % It happens that two fonts are used: \type {cmr10} and \type {logo10}. Once we % know this, we can apply some magic: we set the color to the fontname and define a % couple of colors that match the name. % % \startbuffer % \definecolor [cmr10] [darkred] % \definecolor [logo10] [darkyellow] % % \def\setMPfshowfont#1#2% % {\color[#1]\font\temp=#1\space at #2\relax\temp} % \stopbuffer % % \typebuffer % % In the case of the \type {\it hz} examples we had to define a couple of more % colors, but the principle remains. % % \startlinecorrection[blank] % \getbuffer \processMPbuffer[demo] % \stoplinecorrection % % We don't expect the user to use tricks like this on a daily basis, but it % demonstrates that with a bit of knowlegde of the internals of \CONTEXT, you can % produce nice examples of typographic programming. % % \stopsection \stopchapter \stopcomponent