% language=us % author : Hans Hagen % copyright : ConTeXt Development Team % license : Creative Commons Attribution ShareAlike 4.0 International % reference : pragma-ade.nl | contextgarden.net | texlive (related) distributions % origin : the ConTeXt distribution % % comment : Because this manual is distributed with TeX distributions it comes with a rather % liberal license. We try to adapt these documents to upgrades in the (sub)systems % that they describe. Using parts of the content otherwise can therefore conflict % with existing functionality and we cannot be held responsible for that. Many of % the manuals contain characteristic graphics and personal notes or examples that % make no sense when used out-of-context. % % comment : Some chapters might have been published in TugBoat, the NTG Maps, the ConTeXt % Group journal or otherwise. Thanks to the editors for corrections. Also thanks % to users for testing, feedback and corrections. \usemodule[mag-01,abr-02] \startbuffer[abstract] The content of tenth magazine was written while listening to Tori Amos' latest album, The Beekeeper. In the (nice) booklet the text flows in shapes and here I will demonstrate that \TEX\ can do something similar. It's also a nice example of applying \HZ\ optimization. \stopbuffer \startdocument [title={Good looking shapes}, author=Hans Hagen, affiliation=PRAGMA ADE, date=March 2005, number=10 \MKIV] Just as it takes while to get an understanding what \TEX\ is about, it takes a couple of listening loops to get a general picture about Tori Amos' Beekeeper. While browsing the rather nicely designed booklet I got puzzled |<|as usual when seeing such nice book(let)s|>| why everything looked okay except the text. High end design combined with rather low end typography. Don't get me wrong, apart from the typesetting it's a pretty good product! Tori being one of my favourite artists, you can imagine that I wrote quite some \CONTEXT\ code listening to her music. Now I will not argue that \TEX\ (or \CONTEXT) is the proper system for making \CD\ covers, but since most of such a booklet is a matter of pasting graphics components together, I can imagine that one should ask someone to typeset the text snippets using a proper engine. Anyway, most buyers (fans) won't notice it, but anyone familiar with \TEX\ will immediate get distracted by the strange intercharacter and interline spacing. Typesetting in a fixed shape is non||trivial. First of all lines should break in a pleasing way. If possible, hyphenation should be avoided. The gaps between characters must not become to large and the last line should not be too short. Doing this in \TEX\ is non trivial either, not so much because \TEX\ cannot do such things, but because one needs to control several mechanisms at once. On the other hand, one should know what one's dealing with anyway. Because the size of the shape is fixed, we can manipulate the number of lines and/or the line length and scale afterwards to the desired size. The font size is not fixed. This permits us to implement a semi||automated solution. The difference between the first version of the solution and current one is that we take into account an odd|/|even number of lines. Also, finding the best exit condition took some experiments. The final solution is not that complex and also shows a couple of tricks. \startbuffer \definecolor[BeeColorA][r=.4,g=.5,b=.6] \definecolor[BeeColorB][r=.5,g=.6,b=.4] \definecolor[BeeColorC][r=.6,g=.4,b=.5] \definecolor[BeeColor] [BeeColorA] \defineoverlay [beecell] [\uniqueMPgraphic{beecell}{offset=3mm,color=BeeColor}] \startuniqueMPgraphic{beecell}{offset,color} fill for i = 1 upto 6 : (0,OverlayHeight/2) rotatedaround (center OverlayBox,i*60) -- endfor cycle withpen pencircle scaled \MPvar{offset} withcolor \MPvar{color} ; \stopuniqueMPgraphic \stopbuffer \getbuffer The shape we are dealing with looks as follows: \startlinecorrection \startMPcode fill for i = 1 upto 6 : (5cm,0) rotatedaround(origin,i*60) -- endfor cycle withpen pencircle scaled 2mm withcolor \MPcolor{BeeColorC} ; currentpicture := currentpicture xsized(5cm) ; \stopMPcode \stoplinecorrection We will will later put such a shape behind the text for which we define an overlay: \typebuffer Normally one will not put a shape behind the text, but in our case it illustrates the idea. We use an offset in order to get a more pleasing look. We will use the following two sample texts. The original linebreaks are visible in the source: \startbuffer \startbuffer[parasol] \title {PARASOL} when I come to terms to terms with this when I come to terms with this when I come to terms to terms with this my world will change for me I haven't moved since the call came since the call came I haven't moved I stare at the wall knowing on the other side the storm that waits for me then the Seated Woman with a Parasol may be the only one you can't Betray if I'm the Seated Women with a Parasol I will be safe in my frame I have no need for a sea view for a sea view I have no need I have my little pleasures this wall being one of these when I come to terms to terms with this when I come to terms with this when I come to terms with this whip lash of Silk on wool embroidery then the Seated Woman with a Parasol may be the only one you can't betray if I'm the Seated Woman with a Parasol I will be safe in my frame I will be safe in my frame in your House in your frame \stopbuffer \startbuffer[beekeeper] \title {THE BEEKEEPER} Flaxen hair blowing in the breeze It is time for the geese to head south I have come with my mustard seed I cannot accept that she will be taken from me ``Do you know who I am'' she said ``I'm the one who taps you on the shoulder when it's your time Don't be afraid I promise that she will awake Tomorrow Somewhere Tomorrow Somewhere'' --- wrap yourself around the Tree of Life and the Dance of the Infinity of the Hive --- take this message to Michael I will comb myself into chains In between the tap dance clan and your ballerina gang I have come for the Beekeeper I know you want my You want my Queen --- Anything but this Can you use me instead? In your gown with your breathing mask Plugged into a heart machine As if you ever needed one I must see the Beekeeper I must see if she'll keep her alive Call Engine 49 I have come with my mustard seed Maybe I'm passing you by On my way On my way I'm just passing you by But don't be confused One day I'll be coming for you \unknown\space I must see the Beekeeper I must see the Beekeeper \stopbuffer \stopbuffer \typebuffer \getbuffer We will call these buffers indirectly (using setups is a convenient way to collect commands and definitions). \startbuffer \startsetups [beetext] \getbuffer[parasol] \stopsetups \stopbuffer \typebuffer \getbuffer Now comes the dirty code. We assume that you know a bit of \CONTEXT. First of all we choose a font, in our case a Termes for the running text. We will use Hermann Zapf optimization, which is way more acceptable that intercharacter spacing and gives quite good results here. \startbuffer \definefontfeature[hzdefault][default][hz=quality] \definefont[BeeFont][file:texgyre-termes*hzdefault] \stopbuffer \typebuffer \getbuffer The core of the code is a loop wherein we try to figure out what the best width is. In principle this method can be used for similar shapes. Beforehand we define a few variables. \startbuffer \cldcontext{math.cosd(60)} \cldcontext{math.sind(60)} \newdimen\BeeEdge \newdimen\BeeLine \newdimen\BeeSize \newbox \BeeBox \def\BeeLines{17} % choose optimum odd/even \def\BeeStart{2cm} % set automatically \def\BeeStep {.5mm} % accurate enough \stopbuffer \typebuffer \getbuffer The loop starts with a rather small width and with increasing steps tries to find the solution where the number of used lines equals the asked number of lines. We could have used low level \TEX\ primitives, but using a few \CONTEXT\ wrappers makes more sense because that way struts and alike are set as well. In the end we stretch the interline spacing to match the height of the cell. \startbuffer \startsetups beeloop \def\title##1% {{\ss\bf\kerncharacters[0.25]##1}% \hskip.5em plus .5em minus .25em\relax \ignorespaces} \setbox\scratchbox=\hbox{\setups[beetext]} \edef\BeeStart {\the\dimexpr.5\wd\scratchbox/\BeeLines\relax} \def\BeeMax {10000} \def\BeeShapeA {\scratchdimen\numexpr\recurselevel-1\relax \dimexpr\BeeEdge/\BeeLast\relax \appendetoks \the\dimexpr\BeeEdge- \scratchdimen\relax\space \the\dimexpr\hsize +2\scratchdimen\relax\space \to\scratchtoks} \def\BeeShapeB {\appendetoks \zeropoint\space \the\dimexpr\hsize+2\BeeEdge\relax\space \to\scratchtoks} \doloop {\bgroup \forgetall \dontcomplain \edef\BeeLast {\the\numexpr(\BeeLines\ifodd\BeeLines-1\fi)/2\relax}% \hsize\dimexpr\BeeStart+\recurselevel\dimexpr\BeeStep\relax\relax \BeeEdge=\cldcontext{math.cosd(60)}\hsize \BeeSize=\cldcontext{math.sind(60)}\hsize \BeeLine=\dimexpr2\BeeSize/\numexpr2*\BeeLast+1\relax\relax \setupinterlinespace[line=\BeeLine,stretch=.5]% \setuptolerance[verytolerant]% \setupalign[hz]% \parfillskip\zeropoint \scratchtoks\emptytoks \ifodd\BeeLines \dostepwiserecurse{1}{\BeeLast}{+1}{\BeeShapeA}% \BeeShapeB \dostepwiserecurse{\BeeLast}{1}{-1}{\BeeShapeA}% \rightskip\zeropoint \else % we want to stay inside the shape, so we need % to compensate the right side \advance\hsize +\dimexpr\BeeEdge/\BeeLast\relax \dostepwiserecurse{1}{\BeeLast}{+1}{\BeeShapeA}% \dostepwiserecurse{\BeeLast}{1}{-1}{\BeeShapeA}% \advance\hsize -\dimexpr\BeeEdge/\BeeLast\relax \rightskip\dimexpr\BeeEdge/\BeeLast\relax \fi \setbox\scratchbox\vbox \bgroup % we set it like this in case grid is turned on \baselineskip=1\baselineskip plus 20pt minus 20pt \parshape\numexpr\BeeLines\relax\the\scratchtoks \begstrut \ignorespaces\setups[beetext]\removeunwantedspaces \endstrut \endgraf \xdef\BeeTotal{\number\prevgraf}% \xdef\BeeRate {\number\badness }% \egroup \writestatus {beestate} { run: \recurselevel\space target: \BeeLines \space lines: \BeeTotal \space badness: \BeeRate}% \CheckBeeLines % sets 'done' \ifdone \vbox to 2\BeeSize {\unvbox\ifvoid\BeeBox\scratchbox\else\BeeBox\fi}% \egroup \exitloop \else \egroup \fi} \stopsetups \stopbuffer \getbuffer \typebuffer The end criterium is determined by: \startbuffer \def\CheckBeeLines {\ifnum\BeeTotal>\BeeLines\relax \donefalse \else \donetrue \fi} \stopbuffer \getbuffer \typebuffer This solution is rather safe and, at the cost of the ugly saving of the number of lines as registered in \type {\prevgraf}, works better than measuring the height of the box. We could build the loop out of more isolated pieces of code like this but the reason why we do it for the checker is that we now can redefine it. At the cost of a few more tests, the following checker is better, because it goes on for a while and keeps looking for better solutions. If you have no idea what badness is, just skip the following code snippet. \startbuffer \def\CheckBeeLines {\ifnum\BeeTotal>\BeeLines\relax \donefalse \else\ifnum\BeeTotal=\BeeLines\relax \ifnum\BeeRate=\zerocount \global\setbox\BeeBox=\box\scratchbox \donetrue \else\ifnum\BeeRate<\BeeMax\relax \global\let\BeeMax\BeeRate \global\setbox\BeeBox=\box\scratchbox \donefalse \else \donefalse \fi\fi \else \donetrue \fi\fi} \stopbuffer \getbuffer \typebuffer Well, this is not the kind of code you want a designer to enter, but providing it as feature in a desk top publishing application is also non||trivial because each case differs and turning many knobs to get things done is not easy either, so basically it comes down to manual work (neglectable to the total amount of work involved in getting such a musical product done). Of course one can ask someone to typeset the text in \TEX\ and provide it as image, but that would make coordination the production more complex. The criterium (here \BeeStep) can be made smaller when you encounter problems. If we set it to 1mm, we get one case where the amount of lines jumps~2 and the loop is exit unexpected. Of course one can catch such cases but it does not make much sense in such a one||shot macro. The previous setup is applied as follows: \startbuffer \startsetups beeloner \framed [offset=overlay, frame=off, background=beecell, foregroundstyle=\BeeFont] {\setups[beeloop]} \stopsetups \stopbuffer \getbuffer \typebuffer We will now put several variants alongside. For this we use a layer: \startbuffer \startsetups beesample \definelayer [beekeeper] [width=13cm, height=9cm] \setlayer [beekeeper] [preset=lefttop] {\scale[width=5cm]{\def\BeeLines{16}\setups[beeloner]}} \setlayer [beekeeper] [preset=leftbottom] {\scale[width=5cm]{\def\BeeLines{17}\setups[beeloner]}} \setlayer [beekeeper] [preset=righttop] {\scale[width=5cm]{\def\BeeLines{18}\setups[beeloner]}} \setlayer [beekeeper] [preset=rightbottom] {\scale[width=5cm]{\def\BeeLines{19}\setups[beeloner]}} \setlayer [beekeeper] [preset=middle] {\scale[width=5cm]{\def\BeeLines{20}\setups[beeloner]}} \tightlayer[beekeeper] \stopsetups \stopbuffer \getbuffer \typebuffer \startbuffer[a] \startsetups [beetext] \getbuffer[parasol] \stopsetups \definecolor[BeeColor][BeeColorA] \setups[beesample] \stopbuffer \startbuffer[b] \startsetups [beetext] \getbuffer[beekeeper] \stopsetups \definecolor[BeeColor][BeeColorB] \setups[beesample] \stopbuffer \startpostponing \placefigure [here] [fig:parasol] {Parasol} {\getbuffer[a]} \placefigure [here] [fig:beekeeper] {The Beekeeper} {\getbuffer[b]} \page \stoppostponing The first samples, shown in \in {figure} [fig:parasol], will be typeset using: \typebuffer[a] The second example, shown in \in {figure} [fig:beekeeper], is done in a similar way. We redefine the \type {beetext} setup. \typebuffer[b] You can zoom in on cells using your viewer. An enlarged example is shown in \in {figure} [fig:big]. \startbuffer \definecolor[BeeColor][BeeColorC]% \startcombination {\scale [width=.475\textwidth] {\startsetups[beetext]\getbuffer[parasol]\stopsetups \def\BeeLines{17}\setups[beeloner]}} {Parasol} {\scale [width=.475\textwidth] {\startsetups[beetext]\getbuffer[beekeeper]\stopsetups \def\BeeLines{20}\setups[beeloner]}} {The Beekeeper} \stopcombination \stopbuffer \typebuffer Choosing the best alternative is a matter of taste. If you ever get a change to see the \CD\ (a good buy anyway) you will note the difference. It is possible to improve the spacing at the top and bottom but we leave this as an exercise. \placefigure [here] [fig:big] {An few enlarged examples.} {\getbuffer} The downside of this exercise was that in the process my laptop suddenly made some funny noises and made me end up with a cracked \CD. So in the end the message may be not to bother too much about badly typeset paragraphs in \CD\ booklets. \vbox to \vsize \bgroup \vfil \hbox to \hsize \bgroup \hss \scale [height=.45\textheight] {\startsetups[beetext]\getbuffer[parasol]\stopsetups \defineoverlay[beecell][]\def\BeeLines{17}\setups[beeloner]}% \hss \egroup \vfil \vfil \hbox to \hsize \bgroup \hss \scale [height=.45\textheight] {\startsetups[beetext]\getbuffer[beekeeper]\stopsetups \defineoverlay[beecell][]\def\BeeLines{20}\setups[beeloner]}% \hss \egroup \vfil \egroup \stopdocument