1
2
3
4
5
6\environment hybridenvironment
7
8\definecombination
9 [whatever]
10 [location=top]
11
12\startcomponent hybridpartests
13
14\startchapter[title={Optical optimization}]
15
16One of the objectives of the oriental \TEX\ project has always been to play with
17paragraph optimization. The original assumption was that we needed an advanced
18nonstandard paragraph builder to Arabic done right but in the end we found out
19that a more straightforward approach is to use a sophisticated \OPENTYPE\ font in
20combination with a paragraph postprocessor that uses the advanced font
21capabilities. This solution is somewhat easier to imagine that a complex
22paragraph builder but still involves quite some juggling.
23
24At the June 2012 meeting of the \NTG\ there was a talk about typesetting
25Devanagari and as fonts are always a nice topic (if only because there is
26something to show) it made sense to tell a bit more about optimizing Arabic at
27the same time. In fact, that presentation was already a few years too late
28because a couple of years back, when the oriental \TEX\ project was presented at
29TUG and Dante meetings, the optimizer was already part of the \CONTEXT\ core
30code. The main reason for not advocating is was the simple fact that no font
31other than the (not yet finished) Husayni font provided the relevant feature set.
32
33The lack of advanced fonts does not prevent us from showing what were dealing
34with. This is because the \CONTEXT\ mechanisms are generic in the sense that they
35can also be used with regular Latin fonts, although it does not make that much
36sense. Of course only \MKIV\ is supported. In this chapter we will stick to
37Latin. A more extensive article is published by Idris Samawi Hamid and myself in
38the proceedings of the combined euro\TEX and \CONTEXT\ conference.
39
40When discussing optical optimization of a paragraph, a few alternatives come to
41mind:
42
43\startitemize
44
45\startitem One can get rid of extensive spaces by adding additional kerns between
46glyphs. This is often used by poor mans typesetting programns (or routines) and
47can be applied to nonconnecting scripts. It just looks bad. \stopitem
48
49\startitem Glyphs can be widened a few percent and this is an option that
50\LUATEX\ inherits from its predecessor \PDFTEX. Normally this goes unnoticed
51although excessive scaling makes things worse, and yes, one can run into such
52examples. This strategy goes under the name hzoptimization (the hz refers to
53Hermann Zaph, who first came with this solution). \stopitem
54
55\startitem A real nice solution is to replace glyphs by narrower or wider
56variants. This is in fact the ideal hz solution but for it to happen one not only
57needs needs fonts with alternative shapes, but also a machinery that can deal
58with them. \stopitem
59
60\startitem An already old variant is the one first used by Gutenberg, who used
61alternative cuts for certain combinations of characters. This is comparable with
62ligatures. However, to make the look and feel optimal, one needs to analyze the
63text and make decisions on what to replace without loosing consistency. \stopitem
64
65\stopitemize
66
67The solution described here does a bit of everything. As it is mostly meant for a
68connective script, the starting point is how a scribe works when filling up a
69line nicely. Depending on how well he or she can see it coming, the writing can
70be adapted to widen or narrow following words. And it happens that in Arabic
71scripts there are quite some ways to squeeze more characters in a small area
72andor expand some to the extreme to fill up the available space. Shapes can be
73wider or narrower, they can be stacked and they can get replaced by ligatures. Of
74course there is some interference with the optional marks on top and below but
75even there we have some freedom. The only condition is that the characters in a
76word stay connected.
77
78So, given enough alternative glyphs, one can imagine that excessive interword
79spacing can be avoided. However, it is nontrivial to check all possible
80combinations. Actually, it is not needed either, as esthetic rules put some
81bounds on what can be done. One should more think in terms of alternative
82strategies or solutions and this is the terminology that we will therefore use.
83
84Easiest is to demonstrate this with Latin, if only because its more intuitive to
85see what happens. This is not the place to discuss all the gory details so you
86have to take some of the configuration options on face value. Once this mechanism
87is stable and used, the options can be described. For now we stick to presenting
88the idea.
89
90Lets assume that you know what font features are. The idea is to work with
91combinations of such features and figure out what combination suits best. In
92order not to clutter a document style, these sets are defined in so called goodie
93files. Here is an except of \type {demo.lfg}:
94
95\starttyping
96return {
97 name = "demo",
98 version = "1.01",
99 comment = "An example of goodies.",
100 author = "Hans Hagen",
101 featuresets = {
102 simple = {
103 mode = "node",
104 script = "latn"
105 },
106 default = {
107 mode = "node",
108 script = "latn",
109 kern = "yes",
110 },
111 ligatures = {
112 mode = "node",
113 script = "latn",
114 kern = "yes",
115 liga = "yes",
116 },
117 smallcaps = {
118 mode = "node",
119 script = "latn",
120 kern = "yes",
121 smcp = "yes",
122 },
123 },
124 solutions = {
125 experimental = {
126 less = {
127 "ligatures", "simple",
128 },
129 more = {
130 "smallcaps",
131 },
132 },
133 },
134}
135\stoptyping
136
137We see four sets of features here. You can use these sets in a \CONTEXT\
138feature definition, like:
139
140\startbuffer
141\definefontfeature
142 [solutiondemo]
143 [goodies=demo,
144 featureset=default]
145\stopbuffer
146
147\typebuffer \getbuffer
148
149You can use a set as follows:
150
151\startbuffer
152\definefont
153 [SomeTestFont]
154 [texgyrepagellaregular*solutiondemo at 10pt]
155\stopbuffer
156
157\typebuffer \getbuffer
158
159So far, there is nothing special and new, but we can go a step further.
160
161\startbuffer[solutionab]
162\definefontsolution
163 [solutiona]
164 [goodies=demo,
165 solution=experimental,
166 method={normal,preroll},
167 criterium=1]
168
169\definefontsolution
170 [solutionb]
171 [goodies=demo,
172 solution=experimental,
173 method={normal,preroll,split},
174 criterium=1]
175\stopbuffer
176
177\typebuffer[solutionab] \getbuffer[solutionab]
178
179Here we have defined two solutions. They refer to the \type {experimental}
180solution in the goodie file \type {demo.lfg}. A solution has a \type {less}
181and a \type {more} entry. The featuresets mentioned there reflect ways to
182make a word narrower of wider. There can be more than one way to do that,
183although it comes at a performance price. Before we see how this works out
184we turn on a tracing option:
185
186\startbuffer
187\enabletrackers
188 [builders.paragraphs.solutions.splitters.colors]
189\stopbuffer
190
191\typebuffer \getbuffer
192
193This will color the words in the result according to what has happened. When a
194featureset out of the \type {more} category has been applied, the words turn
195green, when \type {less} is applied, the word becomes yellow. The \type
196{preroll} option in the \type {method} list makes sure that we do a more
197extensive test beforehand.
198
199
200
201
202\startbuffer[normal]
203\SomeTestFont
204\input zapf \par
205\stopbuffer
206
207\startbuffer[solutiona]
208\SomeTestFont \startfontsolution[solutiona]
209\input zapf \par
210\stopfontsolution
211\stopbuffer
212
213\typebuffer[solutiona]
214
215In \in {figure} [solutiona] we see what happens. In each already split line
216words get wider or narrower until were satisfied. A criterium of1 is pretty
217strict \footnote {This number reflects the maximum badness and future versions
218might have a different measure with more granularity.}. Keep in mind that we use
219some arbitrary features here. We try removing kerns to get narrower although
220there is nothing that guarantees that kerns are positive. On the other hand,
221using ligatures might help. In order to get wider we use smallcaps. Okay, the
222result will look somewhat strange but so does much typesetting nowadays.
223
224There is one pitfall here. This mechanism is made for a connective script where
225hyphenation is not used. As a result a word here is actually split up when it has
226discretionaries and of course this text fragment has. It goes unnoticed in the
227rendering but is of course far from optimal.
228
229\startbuffer[solutionb]
230\SomeTestFont \startfontsolution[solutionb]
231\input zapf \par
232\stopfontsolution
233\stopbuffer
234
235\typebuffer[solutionb]
236
237In this example (\in {figure} [solutionb]) we keep words as a whole but as a
238side effect we skip words that are broken across a line. This is mostly because
239it makes not much sense to implement it as Latin is not our target. Future
240versions of \CONTEXT\ might get more sophisticated font machinery so then things
241might look better.
242
243We show two more methods:
244
245\startbuffer[solutioncd]
246\definefontsolution
247 [solutionc]
248 [goodies=demo,
249 solution=experimental,
250 method={reverse,preroll},
251 criterium=1]
252
253\definefontsolution
254 [solutiond]
255 [goodies=demo,
256 solution=experimental,
257 method={random,preroll,split},
258 criterium=1]
259\stopbuffer
260
261\typebuffer[solutioncd] \getbuffer[solutioncd]
262
263In \in {figure} [solutionc] we start at the other end of a line. As we sort of
264mimick a scribe, we can be one who plays safe at the start of corrects at the
265end.
266
267\startbuffer[solutionc]
268\SomeTestFont \startfontsolution[solutionc]
269\input zapf \par
270\stopfontsolution
271\stopbuffer
272
273In \in {figure} [solutiond] we add some randomness but to what extent this works
274well depends on how many words we need to retypeset before we get the badness of
275the line within the constraints.
276
277\startbuffer[solutiond]
278\SomeTestFont \startfontsolution[solutiond]
279\input zapf \par
280\stopfontsolution
281\stopbuffer
282
283\startplacefigure[title={Solution a.},reference=solutiona]
284 \startcombination[whatever]
285 {\framed[strut=no,align={normal,verytolerant},width=.45\textwidth]{\showfontkerns\getbuffer[normal]}} {normal}
286 {\framed[strut=no,align={normal,verytolerant},width=.45\textwidth]{\showfontkerns\getbuffer[solutiona]}} {solution}
287 \stopcombination
288\stopplacefigure
289
290\startplacefigure[title={Solution b.},reference=solutionb]
291 \startcombination[whatever]
292 {\framed[strut=no,align={normal,verytolerant},width=.45\textwidth]{\showfontkerns\getbuffer[normal]}} {normal}
293 {\framed[strut=no,align={normal,verytolerant},width=.45\textwidth]{\showfontkerns\getbuffer[solutionb]}} {solution}
294 \stopcombination
295\stopplacefigure
296
297\startplacefigure[title={Solution c.},reference=solutionc]
298 \startcombination[whatever]
299 {\framed[strut=no,align={normal,verytolerant},width=.45\textwidth]{\showfontkerns\getbuffer[normal]}} {normal}
300 {\framed[strut=no,align={normal,verytolerant},width=.45\textwidth]{\showfontkerns\getbuffer[solutionc]}} {solution}
301 \stopcombination
302\stopplacefigure
303
304\startplacefigure[title={Solution d.},reference=solutiond]
305 \startcombination[whatever]
306 {\framed[strut=no,align={normal,verytolerant},width=.45\textwidth]{\showfontkerns\getbuffer[normal]}} {normal}
307 {\framed[strut=no,align={normal,verytolerant},width=.45\textwidth]{\showfontkerns\getbuffer[solutiond]}} {solution}
308 \stopcombination
309\stopplacefigure
310
311\stopchapter
312
313\stopcomponent
314 |