summaryrefslogtreecommitdiff
path: root/doc/context/sources/general/magazines/mag-0010-mkiv.tex
blob: 65c20180c68541ec08103a1032c1ec2a558f5360 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
% language=uk

% author    : Hans Hagen
% copyright : PRAGMA ADE & 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