summaryrefslogtreecommitdiff
path: root/doc/context/sources/general/manuals/mk/mk-zapfino.tex
blob: f427f13a1c241e37ed23918b43b71b6d69381fa8 (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
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
% language=uk

\startcomponent mk-zapfino

\environment mk-environment

\nonknuthmode

\definefontfeature
   [SampleFont]
   [language=dflt,
    script=latn,
    calt=yes,
    clig=yes,
    rlig=yes,
    tlig=yes,
    mode=node]

\font\Sample=ZapfinoExtraLTPro*SampleFont at 24pt

\def\SampleChar#1{\dontleavehmode\struttedbox{\Sample\fontchar{#1}}}
\def\SampleText#1{\dontleavehmode\struttedbox{\Sample#1}}

\doifmodeelse {tug} {

    \title{Zapfing fonts}

    \subject{by Hans Hagen \& Taco Hoekwater}

    This is Chapter~XII from \notabene {\CONTEXT, from \MKII\ to \MKIV}, a document
    that describes our explorations, experiments and decisions made while
    we develop \LUATEX. This text has not been copy-edited.

    \blank[3*big]

} {

    \chapter{Zapfing fonts}

}

\subject {remark}

{\it The actual form of the tables shown here might have changed
in the meantime. However, since this document describes the
stepwise development of \LUATEX\ and \CONTEXT\ \MKIV\ we don't
update the following information. The rendering might differ from
earlier rendering simply because the code used to process this
chapter evolves.}

\subject {features}

In previous chapters we've seen support for \OPENTYPE\ features creep into \LUATEX\ and
\CONTEXT\ \MKIV. However, it may not have been clear that so far we were just feeding
the traditional \TEX\ machinery with the right data: ligatures and kerns. Here we will
show what so called features can do for you. Not much \LUA\ code will be shown, if
only because relatively complex code is needed to handle this kind of trickery with
acceptable performance.

In order to support features in their full glory more is needed than \TEX's ligature
and kern mechanisms: we need to manipulate the node list. As a result, we have now a
second mechanism built into \MKIV\ and users can choose what method they like most. The
first method, called \type {base}, is less powerful and less complete
than the one named \type {node}. Eventually \CONTEXT\ will use the node method by
default.

There are two variants of features: substitutions and positioning. Here we
concentrate on substitutions of which there are several. Positioning is for instance
used for specialized kerning as needed in for instance typesetting Arab.

One character representation can be replaced by one or more fixed alternatives or alternatives
chosen from a list of alternatives (substitutions or alternates). Multiple characters
can be replaces by one character (substitutions, alternates or a ligature). The
replacements can depend on preceding and|/|or following glyphs in which case we say that
the replacement is driven by rules. Rules can deal with single glyphs, combinations of
glyphs, classes (defined in the font) of glyphs and|/|or ranges of  glyphs.

Because the available documentation of \OPENTYPE\ is rather minimalistic and because
most fonts are relatively simple, you can imagine that figuring out how to
implement support for fonts with advanced features is not entirely trivial
and involves some trial and error. What also complicate things is that features can
interfere. Yet another complicating factor is that in the order of applying a rule may
obscure a later rule. Such fonts don't ship with manuals and examples of correct output
are not part of the buy.

We like testing \LUATEX's open type support with Palatino Regular and Palatino Sans and
good old \TYPEONE\ support with Optima Nova. So it makes sense to test advanced
features with Zapfino Pro. This font has many features, which happen to be
implemented by Adam Twardoch, a well known font expert and familiar with the \TEX\
community. We had the feeling that when \LUATEX\ can support Zapfino Pro, designed by
Hermann Zapf and enhanced by Adam, we have reached a crucial point in the development.

The first thing that you will observe when using this font is that the files are larger
than normal, especially the cached versions in \MKIV. This made me extend some of the
serialization code that we use for caching font data so that it could handle huge
tables better but at the cost of some speed. Once we could handle the data conveniently
and as a side effect look into the font data with an editor, it became clear that
implementing for the \type {calt} and \type {clig} features would take a bit
of coding.

\subject{example}

Before some details will be discussed, we will show two of the test texts that \CONTEXT\
users normally use when testing layouts or new features, a quote from E.R.\ Tufte and
one from Hermann Zapf. The \TEX\ code shows how features are set in \CONTEXT.

\startbuffer
\definefontfeature
   [zapfino]
   [language=nld,script=latn,mode=node,
    calt=yes,clig=yes,liga=yes,rlig=yes,tlig=yes]

\definefont
    [Zapfino]
    [ZapfinoExtraLTPro*zapfino at 24pt]
    [line=40pt]
\Zapfino
\input tufte \par
\stopbuffer

\typebuffer  \blank[disable] \start \getbuffer \stop

You don't even have to look too closely in order to notice that characters are
represented by different glyphs, depending on the context in which they appear.

\startbuffer
\definefontsynonym
  [Zapfino]
  [ZapfinoExtraLTPro]
  [features=zapfino]
\definedfont
  [Zapfino at 24pt]
\setupinterlinespace
  [line=40pt]
\input zapf \par
\stopbuffer

\typebuffer \blank[disable] \start \getbuffer \stop

\subject{obeying rules}

When we were testing node based feature support, the only way to check this was to
identify the rules that lead to certain glyphs. The more unique glyphs are good
candidates for this. For instance

\startitemize[packed]
\item there is s special glyph representing \SampleChar{c_slash_o}
\item in the input stream this is the character sequence \type{c/o}
\item so there most be a rule that tells us that this sequence becomes that ligature
\stopitemize

As said, in this case, the replacement glyph is supposed to be a ligature and indeed
there is such a ligature: \type {c_slash_o}. Of course, this replacement will only
take place when the sequence is surrounded by spaces.

However, when testing this, we were not looking at this rule but at the (randomly
chosen) rule that was meant to intercept the alternative \type {h.2} followed
by \type {z.4}. Interesting was that this resolved to a ligature indeed, but
the shape associated with this ligature was an~\type {h}, which is not right.
Actually, a few more of such rules turned out to be wrong. It took a bit of
an effort to reach this conclusion because of the mentioned interferences
of features and rules. At that time, the rule entry (in raw \LUATEX\ table
format) looks as follows:

\starttyping
[44] = {
    ["format"] = "coverage",
    ["rules"] = {
        [1] = {
            ["coverage"] = {
                ["ncovers"] = {
                    [1] = "h.2",
                    [2] = "z.4",
                }
            },
            ["lookups"] = {
                [1] = {
                    ["lookup_tag"] = "L084",
                    ["seq"] = 0,
                }
            }
        }
    }
    ["script_lang_index"] = 1,
    ["tag"] = "calt",
    ["type"] = "chainsub"
}
\stoptyping

Instead of reinventing the wheel, we used the \FONTFORGE\ libraries for reading the
\OPENTYPE\ font files. Therefore the \LUATEX\ table is resembling the internal \FONTFORGE\
data structures. Currently we show the version~1 format.

Here \type {ncovers} means that when the current character has shape \SampleChar
{h.2} (\type{h.2}) and the next one is \SampleChar{z.4} (\type{z.4}) (a sequence)
then we need to apply the lookup internally tagged \type {L084}. Such a rule
can be more extensive, for instance instead of \type {h.2} one can have a list of
characters, and there can be \type {bcovers} and \type {fcovers} as well, which means
that preceding or following character need to be taken into account.

When this rule matches, it resolves to a specification like:

\starttyping
[6] = {
    ["flags"] = 0,
    ["lig"] = {
        ["char"] = "h",
        ["components"] = "h.2 z.4",
    },
    ["script_lang_index"] = 65535,
    ["tag"] = "L084",
    ["type"] = "ligature",
}
\stoptyping

Here \type {tag} and \type {script_lang_index} are kind of special and
are part of an private feature system, i.e.\ they make up the cross reference
between rules and glyphs. Watch how the components don't match the character,
which is even more peculiar when we realize that these are the initials of the
author of the font. It took a couple of Skype sessions and mails before
we came to the conclusion that this was probably a glitch in the font. So,
what to do when a font has bugs like this? Should one disable the feature?
That would be a pitty because a font like Zapfino depends on it. On the other
hand, given the number of rules and given the fact that there are different
rule sets for some languages, you can imagine that making up the rules and
checking them is not trivial.

We should realize that Zapfino is an extraordinary case, because it used
the \OPENTYPE\ features extensively. We can also be sure that the problems will
be fixed once they are known, if only because Adam Twardoch (who did the job)
has exceptionally high standards but it may take a while before the fix reached
the user (who then has to update his or her font). As said, it also takes some
effort to run into the situation described here so the likelihood of running
into this rule is small. This also brings to our attention the fact that fonts
can now contain bugs and updating them makes sense but can break existing
documents. Since such fonts are copyrighted and not available on line, font
vendors need to find ways to communicate these fixes to their customers.

Can we add some additional checks for problems like this? For a while I
thought that it was possible by assuming that ligatures have names like
\type {h.2_z.4} but alas, sequences of glyphs are mapped onto ligatures
using mappings like the following:

\starttabulate[||||]
\NC \type{three fraction four.2} \NC \type{threequarters} \NC \SampleChar{threequarters} \NC\NR
\NC \type{three fraction four}   \NC \type{threequarters} \NC \SampleChar{threequarters} \NC\NR
\NC \type{d r}                   \NC \type{d_r}           \NC \SampleChar{d_r}           \NC\NR
\NC \type{e period}              \NC \type{e_period}      \NC \SampleChar{e_period}      \NC\NR
\NC \type{f i}                   \NC \type{fi}            \NC \SampleChar{fi}            \NC\NR
\NC \type{f l}                   \NC \type{fl}            \NC \SampleChar{fl}            \NC\NR
\NC \type{f f i}                 \NC \type{f_f_i}         \NC \SampleChar{f_f_i}         \NC\NR
\NC \type{f t}                   \NC \type{f_t}           \NC \SampleChar{f_t}           \NC\NR
\stoptabulate

Some ligature have no \type {_} in their names and there are also some
inconsistencies, compare the \type {fl} and \type {f_f_i}. Here font
history is painfully reflected in inconsistency and no solution can be
found here.

So, in order to get rid of this problem, \MKIV\ implements a method to ignore
certain rules but then, this only makes sense if one knows how the rules
are tagged internally. So, in practice this is no solution. However, you can
imagine that at some point \CONTEXT\ ships with a database of fixes that
are applied to known fonts with certain version numbers.

We also found out that the font table that we used was not good enough for our
purpose because the exact order in what rules have to be applies was not
available. Then we noticed that in the meantime \FONTFORGE\ had moved on
to version~2 and after consulting the author we quickly came to the conclusion
that it made sense to use the updated representation.

In version~2 the snippet with the previously mentioned rule looks as follows:

\starttyping
["ks_latn_l_66_c_19"]={
 ["format"]="coverage",
 ["rules"]={
  [1]={
   ["coverage"]={
    ["current"]={
     [1]="h.2",
     [2]="z.4",
    }
   },
   ["lookups"]={
    [1]={
     ["lookup"]="ls_l_84",
     ["seq"]=0,
    }
   }
  }
 },
 ["type"]="chainsub",
},
\stoptyping

The main rule table is now indexed by name which is possible because the order
of rules is specified somewhere else. The key \type {ncovers} has been replaced
by \type {current}. As long as \LUATEX\ is in beta stage, we have the freedom to
change such labels as some of them are rather \FONTFORGE\ specific.

This rule is mentioned in a feature specification table. Here specific features are
associated with languages and scripts. This is just one of the entries concerning
\type {calt}. You can imagine that it took a while to figure out how best to
deal with this, but eventually the \MKIV\ code could do the trick. The cryptic
names are replacements for pointers in the \FONTFORGE\ datastructure. In order to be
able to use \FONTFORGE\ for font development and analysis, the decision was made to
stick closely to its idiom.

\starttyping
 ["gsub"]={
  ...
  [67]={
   ["features"]={
    [1]={
     ["scripts"]={
      [1]={
       ["langs"]={
        [1]="AFK ",
        [2]="DEU ",
        [3]="NLD ",
        [4]="ROM ",
        [5]="TRK ",
        [6]="dflt",
       },
       ["script"]="latn",
      }
     },
     ["tag"]="calt",
    }
   },
   ["name"]="ks_latn_l_66",
   ["subtables"]={
    [1]={
     ["name"]="ks_latn_l_66_c_0",
    },
    ...
    [20]={
     ["name"]="ks_latn_l_66_c_19",
    },
    ...
   },
   ["type"]="gsub_context_chain",
  },
\stoptyping

\subject{practice}

The few snapshots of the font table probably don't make much sense if you
haven't seen the whole table. Well, it certainly helps to see the whole picture,
but we're talking of a 14 MB file (1.5 MB bytecode). When resolving ligatures,
we can follow a straightforward approach:

\startitemize[packed]
\item walk over the nodelist and at each character (glyph node) call a function
\item this function inspects the character and takes a look at the following ones
\item when a ligature is identified, the sequence of nodes is replaced
\stopitemize

Substitutions are not much different but there we look at just one character.
However, contextual substitutions (and ligatures) are more complex. Here we need
to loop over a list of rules (dependent on script and language) and this involves
a sequence as well as preceding and following characters. When we have a hit, the
sequence will be replaced by another one, determined by a lookup in the character
table. Since this is a rather time consuming operation, especially because many
surrounding characters need to be taken into account, you can imagine that we need
a bit of trickery to get an acceptable performance. Fortunately \LUA\ is pretty fast
when it comes down to manipulating strings and tables, so we can prepare some handy
datastructures in advance.

When testing the implementation of features one need to be aware of the fact that
some appearance are also implemented using the regular ligature mechanisms. Take the
following definitions:

\startbuffer[a]
\definefontfeature
   [none]
   [language=dflt,script=latn,mode=node,liga=no]
\definefontfeature
   [calt]
   [language=dflt,script=latn,mode=node,liga=no,calt=yes]
\definefontfeature
   [clig]
   [language=dflt,script=latn,mode=node,liga=no,clig=yes]
\definefontfeature
   [dlig]
   [language=dflt,script=latn,mode=node,liga=no,dlig=yes]
\definefontfeature
   [liga]
   [language=dflt,script=latn,mode=node]
\stopbuffer

\startbuffer[b]
\starttabulate[||||]
\NC \type{none } \NC \definedfont[ZapfinoExtraLTPro*none at 24pt]\hbox{on the synthesis}\NC\definedfont[ZapfinoExtraLTPro*none at 24pt]\hbox{winnow the wheat}\NC \NR
\NC \type{calt } \NC \definedfont[ZapfinoExtraLTPro*calt at 24pt]\hbox{on the synthesis}\NC\definedfont[ZapfinoExtraLTPro*calt at 24pt]\hbox{winnow the wheat}\NC \NR
\NC \type{clig } \NC \definedfont[ZapfinoExtraLTPro*clig at 24pt]\hbox{on the synthesis}\NC\definedfont[ZapfinoExtraLTPro*clig at 24pt]\hbox{winnow the wheat}\NC \NR
\NC \type{dlig } \NC \definedfont[ZapfinoExtraLTPro*dlig at 24pt]\hbox{on the synthesis}\NC\definedfont[ZapfinoExtraLTPro*dlig at 24pt]\hbox{winnow the wheat}\NC \NR
\NC \type{liga } \NC \definedfont[ZapfinoExtraLTPro*liga at 24pt]\hbox{on the synthesis}\NC\definedfont[ZapfinoExtraLTPro*liga at 24pt]\hbox{winnow the wheat}\NC \NR
\stoptabulate
\stopbuffer

\typebuffer[a]

This gives:

\start \getbuffer[a] \getbuffer[b] \stop

Here are Adam's recommendations with regards to the \type {dlig} feature:
\quotation {The \type{dlig} feature is supposed to by use only upon user's
discretion, usually on single runs, words or even pairs. It makes little
sense to enable \type {dlig} for an entire sentence or paragraph. That's
how the \OPENTYPE\ specification envisions it.}

When testing features it helps to use words that look similar so next we will
show some examples that used. When we look at these examples, we need to
understand that when a specific character representation is analyzed, the
rules can take preceding and following characters into account. The rules
take characters as well as their shapes, or more precisely: one of their
shapes since Zapfino has many variants, into account. Since different rules
are used for languages (okay, this is limited to only a subset of languages
that use the latin script) not only shapes but also the way words are
constructed are taken into account. Designing te rules is definitely non trivial.

When testing the implementation we ran into cases where the initial \type
{t} showed up wrong, for instance in the the Dutch word \type {troef}.
Because space can be part of the rules, we need to handle the
cases where words end and start and boxes are then kind of special.

\definefontfeature
   [zapfing]
   [language=dflt,
    script=latn,
    calt=yes,
    clig=yes,
    rlig=yes,
    tlig=yes,
    mode=node]

\font\Zapfing=ZapfinoExtraLTPro*zapfing at 24pt

\startbuffer
troef troef troef troeftroef troef  \par
\ruledhbox{troef troef troef troeftroef troef} \par
\ruledhbox{troef 123} \par
\ruledhbox{troef} \ruledhbox{troef } \ruledhbox{ troef} \ruledhbox { troef } \par
\stopbuffer

\typebuffer \start \Zapfing \getbuffer \stop

Unfortunately, this does not work well with punctuation, which is less
prominent in the rules than space. In our favourite test quote of Tufte, we have
lots of commas and there it shows up:

\startbuffer
review review review, review \par
itemize, review \par
itemize, review, \par
\stopbuffer

\typebuffer \start \Zapfing \getbuffer \stop

Of course we can decide to extend the rule base at runtime and this may
well happen when we experiment more with this font.

The next one was one of our first test lines, Watch the initial and the
Zapfino ligature.

\startbuffer
Welcome to Zapfino
\stopbuffer

\typebuffer \start \Zapfing \getbuffer \stop

For a while there was a bug in the rule handler that resulted in the variant of
the \type {y} that has a very large descender. Incidentally the word \type
{synthesize} is also a good test case for the \type {the} pattern which gets
special treatment because there is a ligature available.

\startbuffer
synopsize versus synthesize versus
synthase versus sympathy versus synonym
\stopbuffer

\typebuffer \start \Zapfing \getbuffer \stop

Here are some examples that use the \type {g}, \type {d} and \type {f} in
several places.

\startbuffer
eggen groet ogen hagen \par
dieren druiven onder aard  donder modder \par
fiets effe flater triest troef \par
\stopbuffer

\typebuffer \start \Zapfing \getbuffer \stop

Let's see how well Hermann has taken care of the \type {h}'s
representations. There are quite some variants of the lowercase one:

\starttabulate
\NC \type {h}      \NC \SampleChar{h}      \NC \NR
\NC \type {h.2}    \NC \SampleChar{h.2}    \NC \NR
\NC \type {h.3}    \NC \SampleChar{h.3}    \NC \NR
\NC \type {h.4}    \NC \SampleChar{h.4}    \NC \NR
\NC \type {h.5}    \NC \SampleChar{h.5}    \NC \NR
\NC \type {h.init} \NC \SampleChar{h.init} \NC \NR
\NC \type {h.sups} \NC \SampleChar{h.sups} \NC \NR
\NC \type {h.sc}   \NC \SampleChar{h.sc}   \NC \NR
\NC \type {orn.73} \NC \SampleChar{orn.73} \NC \NR
\stoptabulate

How about the uppercase variant, as used in his name:

\startbuffer
M Mr Mr. H He Her Herm Herma Herman Hermann Z Za Zap Zapf \par
Mr. Hermann Zapf
\stopbuffer

\typebuffer \start \Zapfing \getbuffer \stop

Of course we have to test another famous name:

\startbuffer
D Do Don Dona Donal Donald K Kn Knu Knut Knuth \par
Don Knuth Donald Knuth Donald E. Knuth DEK \par
Prof. Dr. Donald E. Knuth \par
\stopbuffer

\typebuffer \start \Zapfing \getbuffer \stop

Unfortunately the \LUA\ and \TEX\ logos don't come out that well:

\startbuffer
L Lu Lua l lu lua t te tex TeX luatex luaTeX LuaTeX
\stopbuffer

\typebuffer \start \Zapfing \getbuffer \stop

This font has quite some ornaments and there is an \type {ornm} feature
that can be applied. We're still not sure about its usage, but when one
keys in text in lowercase, \type {hermann} comes out as follows:

\definefontfeature
  [gebarentaal]
  [language=dflt,
   script=latn,
   mode=node,
   ornm=yes,
   liga=no]

{\font\Sample=ZapfinoExtraLTPro*gebarentaal at 24pt \Sample hermann}

As said in the beginning, dirty implementation details will be kept away from
the reader. Also, you should not be surprised if the current code had some
bugs or does some things wrong. Also, if spacing looks a bit weird to you,
keep in mind that we're still in the middle of sorting things out.

\start \Zapfing Taco Hoekwater \& Hans Hagen \stop

\stopcomponent