evenmore-threesix.tex /size: 23 Kb    last modification: 2021-10-28 13:50
1% language=us runpath=texruns:manuals/evenmore
2
3\environment evenmore-style
4
5\useMPlibrary[threesix]
6
7\startcomponent evenmore-threesix
8
9\startchapter[title={ThreeSix, Don Knuths first colorfont?}]
10
11In the process of reaching completion and perfection Don Knuth occasionally posts
12links to upcoming parts of the TAOCP series on his web pages. Now, I admit that
13much is way beyond me but I do understand (and like) the graphics and I know that
14Don uses \METAPOST. The next example code is just a proof of concept but might
15eventually become a decent module (with helpers) for making (runtime) fonts.
16After all, we need to adapt to current developments and \TEX ies always are
17willing to adapt and experiment. This chapter was written at the same time as
18the previous one on \TYPETHREE\ fonts so you might want to read that first.
19
20The font explored here is \type {FONT36}, used in \quotation {A potpourri of
21puzzles} and flagged as \quotation {a special font designed for dissection
22puzzles} (in fascicle 9b for Volume 4). Playing with and visualizing for me often
23works better than formulas, which then distracts me from the original purpose,
24but let's have a closer look anyway.
25
26\startlinecorrection
27    \scale[width=\textwidth]{\DEKFontA 1234567890} \vskip1ex
28    \scale[width=\textwidth]{\DEKFontA ABC{\red DE}FGHIJ{\red K}LM} \vskip1ex
29    \scale[width=\textwidth]{\DEKFontA NOPQRSTUVWXYZ}
30\stoplinecorrection
31
32The font has a fixed maximum height of 8~quantities. There is no depth in the
33characters. Some characters are wider. In this example we use a tight bounding
34box. In \CONTEXT\ speak this font is just a regular font but with a special
35feature enabled.
36
37\starttyping
38\definefontfeature
39  [fontthreesix]
40  [default]
41  [metapost=fontthreesix]
42
43\definefont[DEKFontA][Serif*fontthreesix]
44\stoptyping
45
46After this the \type {\DEKFontA} command will set this font as current font. The
47definition mentions \type {Serif} as font name. In \CONTEXT\ this name will
48resolve in the currently defined Serif, so when your document uses Latin Modern
49that will be the one. The \type {fontthreesix} will make this instance use that
50feature set, and the feature definition has the defaults as parent (so we get
51kerning, ligatures, etc.) but as extra feature also \type {metapost}. This means
52that the new glyphs that are about to be defined will actually be injected in the
53\type {Serif}! We will replace characters in that instance. So, the following:
54
55\startbuffer[taocp]
56This font is used in \quotation {The Art Of Computer Programming} by
57Don Knuth, not in volume~1, 2 or~3, but in number~4!
58\stopbuffer
59
60\typebuffer[taocp]
61
62comes out as:
63
64\startnarrower
65\DEKFontA \getbuffer[taocp]
66\stopnarrower
67
68But that doesn't look too good, so we will tweak the font a bit:
69
70\starttyping
71\definefontfeature
72  [fontthreesix-color]
73  [default]
74  [metapost={category=fontthreesix,spread=.1}]
75
76\definefont[DEKFontD][Serif*fontthreesix]
77\stoptyping
78
79The \type {spread} (multiplied by the font unit, which is 12 basepoints here)
80will add a bit more spacing around the blob:
81
82\startnarrower
83\DEKFontD \getbuffer[taocp]
84\stopnarrower
85
86Now, keep in mind that we're talking of a real font here. You can cut and paste
87these characters. It's just the default uppercase Latin alphabet plus digits.
88
89Before we go and look at some of the code needed to render this, a few more
90examples will be given.
91
92\startlinecorrection
93    \scale[width=\textwidth]{\DEKFontB 1234567890}    \vskip4pt
94    \scale[width=\textwidth]{\DEKFontB ABCDEFGHIJKLM} \vskip4pt
95    \scale[width=\textwidth]{\DEKFontB NOPQRSTUVWXYZ}
96\stoplinecorrection
97
98In the above example we not only use color, but also a different shape and random
99colors (that is: random per \TEX\ job). The feature definition for this is:
100
101\starttyping
102\definefontfeature
103  [fontthreesix-color]
104  [default]
105  [metapost={%
106    category=fontthreesix,shape=diamond,%
107    color=random,pen=fancy,spread=.1%
108  }]
109\stoptyping
110
111Possible shapes are \type {circle}, \type {diamond} and \type {square} and
112instead of a random color one can give a known color name. Using transparency
113makes no sense in this font.
114
115A nice usage for this font are initials: % 4 for lm
116
117\startbuffer
118\setupinitial[font=Serif*fontthreesix-initial sa 5]
119{\DEKFontB \placeinitial \input zapf\par}
120\stopbuffer
121
122\typebuffer
123
124The initial feature is defined as:
125
126\starttyping
127\definefontfeature
128  [fontthreesix-initial]
129  [metapost={category=fontthreesix,color=random,shape=circle}]
130\stoptyping
131
132We use this in quoting Hermann Zapf, one that for sure is very applicable in
133a case like this:
134
135\startnarrower
136\getbuffer
137\stopnarrower
138
139Some combinations of sub|-|features are shown in \in {figure} [threesix:dek]. We
140blow up the diamond with fancy pen example in \in {figure} [threesix:tex]. Alas,
141the \TEX\ logo doesn't look that good in such a font. Using it for acronyms is
142not a good idea anyway, but maybe you can figure out \in {figure}
143[threesix:taocp].
144
145% todo penx peny penr
146
147\definefontfeature
148  [fontthreesix-circle]
149  [metapost={category=fontthreesix,shape=circle,color=random}]
150\definefontfeature
151  [fontthreesix-square]
152  [metapost={category=fontthreesix,shape=square,color=random}]
153\definefontfeature
154  [fontthreesix-diamond]
155  [metapost={category=fontthreesix,shape=diamond,color=random}]
156\definefontfeature
157  [fontthreesix-circle-pen]
158  [metapost={category=fontthreesix,shape=circle,color=random,pen=fancy}]
159\definefontfeature
160  [fontthreesix-square-pen]
161  [metapost={category=fontthreesix,shape=square,color=random,pen=fancy}]
162\definefontfeature
163  [fontthreesix-diamond-pen]
164  [metapost={category=fontthreesix,shape=diamond,color=random,pen=fancy}]
165\definefontfeature
166  [fontthreesix-circle-random]
167  [metapost={category=fontthreesix,random=yes,shape=circle,color=random}]
168\definefontfeature
169  [fontthreesix-square-random]
170  [metapost={category=fontthreesix,random=yes,shape=square,color=random}]
171\definefontfeature
172  [fontthreesix-diamond-random]
173  [metapost={category=fontthreesix,random=yes,shape=diamond,color=random}]
174
175\definefontfeature
176  [fontthreesix-circle-random-spread]
177  [metapost={category=fontthreesix,random=yes,shape=circle,color=random,spread=.1}]
178
179\startplacefigure[reference=threesix:dek]
180    \startcombination[3*3]
181        {\scale[width=.3\textwidth]{\definedfont        [Serif*fontthreesix-circle]D\kern1pt E\kern 1ptK}} {\type{shape=circle}}
182        {\scale[width=.3\textwidth]{\definedfont        [Serif*fontthreesix-square]D\kern1pt E\kern 1ptK}} {\type{shape=square}}
183        {\scale[width=.3\textwidth]{\definedfont       [Serif*fontthreesix-diamond]D\kern1pt E\kern 1ptK}} {\type{shape=diamond}}
184        {\scale[width=.3\textwidth]{\definedfont    [Serif*fontthreesix-circle-pen]D\kern1pt E\kern 1ptK}} {\type{shape=circle,pen=fancy}}
185        {\scale[width=.3\textwidth]{\definedfont    [Serif*fontthreesix-square-pen]D\kern1pt E\kern 1ptK}} {\type{shape=square,pen=fancy}}
186        {\scale[width=.3\textwidth]{\definedfont   [Serif*fontthreesix-diamond-pen]D\kern1pt E\kern 1ptK}} {\type{shape=diamond,pen=fancy}}
187        {\scale[width=.3\textwidth]{\definedfont [Serif*fontthreesix-circle-random]D\kern1pt E\kern 1ptK}} {\type{shape=circle,random=yes}}
188        {\scale[width=.3\textwidth]{\definedfont [Serif*fontthreesix-square-random]D\kern1pt E\kern 1ptK}} {\type{shape=square,random=yes}}
189        {\scale[width=.3\textwidth]{\definedfont[Serif*fontthreesix-diamond-random]D\kern1pt E\kern 1ptK}} {\type{shape=diamond,random=yes}}
190    \stopcombination
191\stopplacefigure
192
193\startplacefigure[reference=threesix:tex]
194\scale[width=\textwidth]{\definedfont[Serif*fontthreesix-diamond-pen]T\lower.5ex\hbox spread .1em{\hss E\hss}X}
195\stopplacefigure
196
197\startplacefigure[reference=threesix:taocp]
198\scale[width=\textwidth]{\definedfont[Serif*fontthreesix-circle-random-spread]TAOCP}
199\stopplacefigure
200
201You can quit reading now or expose yourself to how this is coded. We use a
202combination of \LUA\ and \METAPOST, but different solutions are possible. The
203shapes are entered (or course) with zeros and ones.
204
205\starttyping
206\startluacode
207local font36 = {
208    ["0"] = "00111100 01111110 11000011 11000011 11000011 ...",
209    ["1"] = "00011100 11111100 11101100 00001100 00001100 ...",
210    .....
211    ["D"] = "11111100 11100010 01100011 01100011 01100011 ...",
212    ["E"] = "1111111 1110001 0110101 0111100 0110100 0110001 ...",
213    .....
214    ["K"] = "11101110 11100100 01101000 01110000 01111000 ...",
215    .....
216}
217\stopluacode
218\stoptyping
219
220We also use \LUA\ to register this font. The actual code looks slightly different
221because it uses some helpers from the \CONTEXT\ \LUA\ libraries. We remap the
222bits pattern onto \METAPOST\ macro calls.
223
224\starttyping
225\startluacode
226local replace = {
227    ["0"] = "N;",
228    ["1"] = "Y;",
229    [" "] = "L;",
230}
231
232function MP.registerthreesix(name)
233    fonts.dropins.registerglyphs {
234        name     = name,
235        units    = 12,
236        usecolor = true,
237    }
238    for u, v in table.sortedhash(font36) do
239        local ny     = 8
240        local nx     = (#v - ny + 1) // ny
241        local height = ny * 1.1 - 0.1
242        local width  = nx * 1.1 - 0.1
243        local code   = string.gsub(v,".",replace)
244        fonts.dropins.registerglyph {
245            category = name,
246            unicode  = utf.byte(u),
247            width    = width,
248            height   = height,
249            code     = string.format("ThreeSix(%s);",code),
250        }
251    end
252end
253
254MP.registerthreesix("fontthreesix")
255\stopluacode
256\stoptyping
257
258So, after this the font \type {fontthreesix} is known to the system but we still
259need to provide \METAPOST\ code to generate it. The glyphs themselves are now
260just sequences of \type {N}, \type {Y} and \type {L} with some wrapper code
261around it. The definitions are put in the \type {MP} namespace simply because a
262first version initialized in \METAPOST, and there could create variants, but in
263the end I settled on the parameter interface at the \TEX\ end.
264
265The next definition looks a bit complex but normally such a macro is
266stepwise constructed. Notice how we can query the sub features. In order to make
267that possible the regular \METAFUN\ parameter handling code is used. We just push
268the sub|-|features into to \type {mpsfont} namespace.
269
270\starttyping
271\startMPcalculation{simplefun}
272
273def InitializeThreeSix =
274    save Y, N, L, S ; save dx, dy, nx, ny ; save currentpen ;
275    save shape, fillcolor, mypen, random, spread, hoffset ;
276    string shape, fillcolor, mypen ; boolean random ;
277    pen currentpen ;
278    dx :=   11/10 ;
279    dy := - 11/10 ;
280    nx := - dx ;
281    ny :=   0 ;
282    shape      := getparameterdefault "mpsfont" "shape" "circle" ;
283    random     := hasoption           "mpsfont" "random" "true" ;
284    fillcolor  := getparameterdefault "mpsfont" "color" "" ;
285    mypen      := getparameterdefault "mpsfont" "pen" "" ;
286    spread     := getparameterdefault "mpsfont" "spread" 0 ;
287    hoffset    := 12 * spread / 2 ;
288    currentpen := pencircle
289        if mypen = "fancy" :
290            xscaled 1/20 yscaled 2/20 rotated 45
291        else :
292            scaled 1/20
293        fi ;
294    if shape == "square" :
295        def S =
296            unitsquare if random : randomized 1/10 fi
297            shifted (nx,ny)
298        enddef ;
299    elseif shape = "diamond" :
300        def S =
301            unitdiamond if random : randomized 1/10 fi
302            shifted (nx,ny)
303        enddef ;
304    else :
305        def S =
306            unitcircle if random : randomizedcontrols 1/20 fi
307            shifted (nx,ny)
308        enddef ;
309    fi ;
310    def N =
311        nx := nx + dx ;
312        draw S ;
313    enddef ;
314    if fillcolor = "random" :
315        def Y =
316            nx := nx + dx ;
317            fillup S withcolor white randomized (2/3,2/3,2/3) ;
318        enddef ;
319    elseif fillcolor = "" :
320        def Y =
321            nx := nx + dx ;
322            fillup S ;
323        enddef ;
324    else :
325        def Y =
326            nx := nx + dx ;
327            fillup S withcolor fillcolor ;
328        enddef ;
329    fi ;
330    def L =
331        nx := - dx ;
332        ny := ny + dy ;
333    enddef ;
334enddef ;
335
336vardef ThreeSix (text code) =
337    InitializeThreeSix ; % todo: once per instance run
338    draw image (code) shifted (hoffset,-ny) ;
339enddef ;
340
341\stopMPcalculation
342\stoptyping
343
344This code is not that efficient in the sense that there's quite some
345\METAPOST|-|\LUA|-|\METAPOST\ traffic going on, for instance each parameter check
346involves this, but in practice performance is quite okay, certainly for such a
347small font. There will be an initializer option some day soon. The \type
348{simplefun} is a reference to an \MPLIB\ instance that does load \METAFUN\ but
349only the modules that make no sense for this kind of usage. It also enforces
350double mode. The calculations wrapper just executes the code and does not place
351some (otherwise empty) graphic.
352
353% After playing with the font I see the beauty of the descriptions in the
354% pre|-|fascicle 9b but I still feel pretty stupid. Lucky for me there are
355% exercises like 999, tagged as \quote {for dummies}, so I'm not alone.
356
357Those who have seen (and|/|or read) \quotation {Concrete Mathematics} will have
358noticed the use of inline images, like dice. Dice are also used in \quotation
359{pre-fascicle 5a} (I need a few more lives to grasp that, so I stick to the
360images for now!). So, to compensate the somewhat complex code above, I will show
361how to accomplish that. This time we do all in \METAPOST:
362
363\startMPcalculation{simplefun}
364
365def DiceFrame =
366    pickup pencircle scaled 1/2 ;
367    draw unitsquare scaled 8 ;
368    pickup pencircle scaled 3/2 ;
369enddef ;
370
371vardef DiceOne =
372    DiceFrame ;
373    draw (4,4) ;
374enddef ;
375vardef DiceTwoA =
376    DiceFrame ;
377    draw (2,6) ; draw (6,2) ;
378enddef ;
379vardef DiceTwoB =
380    DiceFrame ;
381    draw (6,6) ; draw (2,2) ;
382enddef ;
383vardef DiceTwo =
384    if hasoption "mpsfont" "option" "reverse" :
385        DiceTwoB
386    else :
387        DiceTwoA
388    fi ;
389enddef ;
390vardef DiceThreeA =
391    DiceFrame ;
392    draw (2,6) ; draw (4,4) ; draw (6,2) ;
393enddef ;
394vardef DiceThreeB =
395    DiceFrame ;
396    draw (6,6) ; draw (4,4) ; draw (2,2) ;
397enddef ;
398vardef DiceThree =
399    if hasoption "mpsfont" "option" "reverse" :
400        DiceThreeB
401    else :
402        DiceThreeA
403    fi ;
404enddef ;
405vardef DiceFour =
406    DiceFrame ;
407    draw (2,6) ; draw (6,6) ; draw (2,2) ; draw (6,2) ;
408enddef ;
409vardef DiceFive =
410    DiceFrame ;
411    draw (2,6) ; draw (6,6) ; draw (4,4) ; draw (2,2) ; draw (6,2) ;
412enddef ;
413vardef DiceSix =
414    DiceFrame ;
415    draw (2,6) ; draw (6,6) ; draw (2,4) ; draw (6,4) ; draw (2,2) ; draw (6,2) ;
416enddef ;
417
418vardef DiceBad =
419    pickup pencircle scaled 1/2 ;
420    draw unitsquare scaled 8 ;
421    draw (1,7) -- (7,1) ; draw (1,1) -- (7,7) ;
422enddef ;
423
424lmt_registerglyphs [
425    name     = "dice",
426    units    = 12,
427    width    = 8,
428    height   = 8,
429    depth    = 0,
430    usecolor = true,
431] ;
432
433lmt_registerglyph [ category = "dice", unicode = "0x2680", code = "DiceOne;" ] ;
434lmt_registerglyph [ category = "dice", unicode = "0x2681", code = "DiceTwo;" ] ;
435lmt_registerglyph [ category = "dice", unicode = "0x2682", code = "DiceThree;" ] ;
436lmt_registerglyph [ category = "dice", unicode = "0x2683", code = "DiceFour;" ] ;
437lmt_registerglyph [ category = "dice", unicode = "0x2684", code = "DiceFive;" ] ;
438lmt_registerglyph [ category = "dice", unicode = "0x2685", code = "DiceSix;" ] ;
439
440lmt_registerglyph [ category = "dice", private = "invaliddice", code = "DiceBad;" ] ;
441
442\stopMPcalculation
443
444This is not that hard to follow. We define some shapes first. These could have
445been assigned to the \type {code} parameter directly but this is nicer. Next we
446register the font itself and after that we set glyphs. We also set the official
447\UNICODE\ slots. So, copying a dice can either result in a digit or in a
448\UNICODE\ slot for a dice. In the example below we switch to a color which
449demonstrates that our dice can be colored at the \TEX\ end. It's either that or
450coloring at the \METAPOST\ end as both demand a different kind of \TYPETHREE\
451embedding trickery.
452
453We actually predefine three features. The digits one will map regular digit in
454the input to dice. We accomplish that via a font feature:
455
456\startbuffer
457\startluacode
458fonts.handlers.otf.addfeature("dice:digits", {
459    type      = "substitution",
460    order     = { "dice:digits" },
461    nocheck   = true,
462    data      = {
463        [0x30] = "invaliddice",
464        [0x31] = 0x2680,
465        [0x32] = 0x2681,
466        [0x33] = 0x2682,
467        [0x34] = 0x2683,
468        [0x35] = 0x2684,
469        [0x36] = 0x2685,
470        [0x37] = "invaliddice",
471        [0x38] = "invaliddice",
472        [0x39] = "invaliddice",
473    },
474} )
475\stopluacode
476\stopbuffer
477
478\typebuffer \getbuffer
479
480This kind of trickery is part of the font machinery used in \CONTEXT\ and permits
481runtime adaption of fonts, so we just use the same mechanism. The \type {nocheck}
482is needed to avoid this feature not kicking in due to lack of (at the time of
483checking) yet undefined dice.
484
485\startbuffer
486\definefontfeature
487  [dice:normal]
488  [default]
489  [metapost={category=dice}]
490\definefontfeature
491  [dice:reverse]
492  [default]
493  [metapost={category=dice,option=reverse}]
494\definefontfeature
495  [dice:digits]
496  [dice:digits=yes]
497
498\definefont[DiceN] [Serif*dice:normal]
499\definefont[DiceD] [Serif*dice:normal,dice:digits]
500\definefont[DiceR] [Serif*dice:reverse,dice:digits]
501
502{\DiceD Does 1 it 4 work? And {\darkgreen 3} too?} {\DiceR And how about
503{\darkred 3} then? But 8 should sort of fail!}
504\stopbuffer
505
506\typebuffer \getbuffer
507
508The six digits and \UNICODE\ characters come out the same:
509
510\startbuffer
511\red  \DiceD \dostepwiserecurse   {`1}   {`6}{1}{\char#1\quad}%
512\blue \DiceN \dostepwiserecurse{"2680}{"2685}{1}{\char#1\quad}%
513\stopbuffer
514
515\typebuffer
516
517\startlinecorrection
518    \scale[width=\textwidth]{\getbuffer\unskip}
519\stoplinecorrection
520
521It is tempting to implement for instance~7 as two dice (a one to multi mapping in
522\OPENTYPE\ speak) but then one has to decide what combination is taken. One can
523also implement ligatures so that for instance 12 results in two six dice. But I
524think that's over the top and only showing \TEX\ muscles. It is anyway not that
525hard to do as we have an interface for that already.
526
527So why not do the dominos as well? Because there are so many dominos we predefine
528the shapes and then register the lot in a loop. We have horizontal and vertical
529variants. Being lazy I just made two helpers while one could have done but with
530some rotation and shifting of the horizontal one. The loop could be a macro but
531we don't save much code that way.
532
533\startbuffer
534\startMPcalculation{simplefun}
535
536picture Dominos[] ;
537
538Dominos[0] := image() ;
539Dominos[1] := image(draw(4,4);) ;
540Dominos[2] := image(draw(2,6);draw(6,2););
541Dominos[3] := image(draw(2,6);draw(4,4);draw(6,2););
542Dominos[4] := image(draw(2,6);draw(6,6);draw(2,2);draw(6,2););
543Dominos[5] := image(draw(2,6);draw(6,6);draw(4,4);draw(2,2);draw(6,2););
544Dominos[6] := image(draw(2,6);draw(4,6);draw(6,6);draw(2,2);draw(4,2);draw(6,2););
545
546lmt_registerglyphs [
547    name     = "dominos",
548    units    = 12,
549    width    = 16,
550    height   = 8,
551    depth    = 0,
552    usecolor = true,
553] ;
554
555def DrawDominoH(expr a, b) =
556    draw image (
557        pickup pencircle scaled 1/2 ;
558        if (getparameterdefault "mpsfont" "color" "") = "black" :
559            fillup unitsquare xyscaled (16,8) ;
560            draw (8,.5) -- (8,7.5) withcolor white ;
561            pickup pencircle scaled 3/2 ;
562            draw Dominos[a]
563                withpen currentpen
564                withcolor white ;
565            draw Dominos[b] shifted (8,0)
566                withpen currentpen
567                withcolor white ;
568        else :
569            draw unitsquare xyscaled (16,8) ;
570            draw (8,0) -- (8,8) ;
571            pickup pencircle scaled 3/2 ;
572            draw Dominos[a]
573                withpen currentpen ;
574            draw Dominos[b] shifted (8,0)
575                withpen currentpen ;
576        fi ;
577    ) ;
578enddef ;
579
580def DrawDominoV(expr a, b) = % is H rotated and shifted
581    draw image (
582        pickup pencircle scaled 1/2 ;
583        if (getparameterdefault "mpsfont" "color" "") = "black" :
584            fillup unitsquare xyscaled (8,16) ;
585            draw (.5,8) -- (7.5,8) withcolor white ;
586            pickup pencircle scaled 3/2 ;
587            draw Dominos[a] rotatedaround(center Dominos[a],90)
588                withpen currentpen
589                withcolor white  ;
590            draw Dominos[b] rotatedaround(center Dominos[b],90) shifted (0,8)
591                withpen currentpen
592                withcolor white  ;
593        else :
594            draw unitsquare xyscaled (8,16) ;
595            draw (0,8) -- (8,8) ;
596            pickup pencircle scaled 3/2 ;
597            draw Dominos[a] rotatedaround(center Dominos[a],90)
598                withpen currentpen ;
599            draw Dominos[b] rotatedaround(center Dominos[b],90) shifted (0,8)
600                withpen currentpen ;
601        fi ;
602    ) ;
603enddef ;
604
605begingroup
606    save unicode ; numeric unicode ; unicode := 127025 ; % 1F031
607
608    for i=0 upto 6 :
609        for j=0 upto 6 :
610            lmt_registerglyph [
611                category = "dominos",
612                unicode  = unicode,
613                code     = "DrawDominoH(" & decimal i & "," & decimal j & ");",
614                width    = 16,
615                height   = 8,
616            ] ;
617            unicode := unicode + 1 ;
618        endfor ;
619    endfor ;
620
621    save unicode ; numeric unicode ; unicode := 127075 ;
622
623    for i=0 upto 6 :
624        for j=0 upto 6 :
625            lmt_registerglyph [
626                category = "dominos",
627                unicode  = unicode,
628                code     = "DrawDominoV(" & decimal i & "," & decimal j & ");",
629                width    = 8,
630                height   = 16,
631            ] ;
632            unicode := unicode + 1 ;
633        endfor ;
634    endfor ;
635endgroup ;
636
637\stopMPcalculation
638\stopbuffer
639
640\typebuffer \getbuffer
641
642Again we predefine a couple of features:
643
644\startbuffer
645\definefontfeature
646  [dominos:white]
647  [default]
648  [metapost={category=dominos}]
649
650\definefontfeature
651  [dominos:black]
652  [default]
653  [metapost={category=dominos,color=black}]
654
655\definefontfeature
656  [dominos:digits]
657  [dominos:digits=yes]
658\stopbuffer
659
660\typebuffer \getbuffer
661
662This last feature is yet to be defined. We could deal with the invalid dominos
663with some substitution trickery but let's keep it simple.
664
665\startbuffer
666\startluacode
667local ligatures = { }
668local unicode   = 127025
669
670for i=0x30,0x36 do
671    for j=0x30,0x36 do
672        ligatures[unicode] = { i, j }
673        unicode = unicode + 1 ;
674    end
675end
676
677fonts.handlers.otf.addfeature("dominos:digits", {
678    type      = "ligature",
679    order     = { "dominos:digits" },
680    nocheck   = true,
681    data      = ligatures,
682} )
683\stopluacode
684\stopbuffer
685
686\typebuffer \getbuffer
687
688That leaves showing an example. We define a few fonts and again we just extend
689the Serif:
690
691\startbuffer
692\definefont[DominoW][Serif*dominos:white]
693\definefont[DominoB][Serif*dominos:black]
694\definefont[DominoD][Serif*dominos:white,dominos:digits]
695\stopbuffer
696
697\typebuffer \getbuffer
698
699The example is:
700
701\startbuffer
702\DominoW
703    \char"1F043\quad 🀱\quad
704    \char"1F052\quad 🀲\quad
705    \char"1F038\quad 🀳\quad
706    \darkgreen\char"1F049\quad \char"1F07B\quad
707\DominoB
708    \char"1F087\quad
709    \char"1F088\quad
710    \char"1F089\quad
711\DominoD
712    \darkred 12\quad56\quad64
713\stopbuffer
714
715\typebuffer
716
717Watch the ligatures in action:
718
719\startlinecorrection
720    \scale[width=\textwidth]{\getbuffer\unskip}
721\stoplinecorrection
722
723To what extent the usage of symbols like dice and dominos as characters in the
724mentioned book are responsible for them being in \UNICODE, I don't know. Now with
725all these emoji showing up one can wonder about graphics in such a standard
726anyway. But for sure, even after more than three decades, Don still makes nice
727fonts.
728
729A treasure of tiny graphics can be found in \quotation {pre-fascicle 5c} and many
730are in color! In fact, it has dominos too. It must have been a lot of fun writing
731this! I'm thinking of turning the pentominoes into a font where a \type {GPOS}
732feature can deal with the inter|-|pentomino kerning (which mighty work out okay
733for example~36. The windmill dominos also make a nice example for a font where
734ligatures will boil down to the base form and the (one or more) blades are laid
735over. It's definitely an inspiring read.
736
737\stopchapter
738
739\stoptext
740