ontarget-gettingridof.tex /size: 73 Kb    last modification: 2024-01-16 10:21
1% language=us runpath=texruns:manuals/ontarget
2
3\startcomponent ontarget-gettingridof
4
5\environment ontarget-style
6
7\startchapter[title={Issues in math fonts}]
8
9\startsection[title=Introduction]
10
11After trying to improve math rendering of \OPENTYPE\ math fonts, we \footnote
12{Mikael Sundqvist and Hans Hagen} ended up with a mix of improving the engine and
13fixing fonts runtime, and we are rather satisfied with the results so far.
14
15However, as we progress and also improve the more structural and input related
16features of \CONTEXT, we wonder why we don't simply are more drastic when it
17comes to fonts. The \OPENTYPE\ specifications are vague, and most existing
18\OPENTYPE\ math fonts use a mixture of the \OPENTYPE\ features and the old \TEX\
19habits, so we are sort of on our own. The advantage of this situation is that we
20feel free to experiment and do as we like.
21
22In another article we discuss our issues with \UNICODE\ math, and we have
23realized that good working solutions will be bound to a macro package anyway.
24Also, math typesetting has not evolved much after Don Knuth set the standard,
25even if the limitations of those times in terms of memory, processing speed and
26font technologies have been lifted already for a while. And right from the start
27Don invited users to extend and adapt \TEX\ to one's needs.
28
29Here we will zoom in on a few aspects: font parameters, glyph dimensions and
30properties and kerning of scripts and atoms. We discuss \OPENTYPE\ math fonts
31only, and start with a summary of how we tweak them. We leave a detailed engine
32discussion to a future article, since that would demand way more pages, and could
33confuse the reader.
34\stopsection
35
36\startsection[title={Tweaks, also known as goodies}]
37
38The easiest tweaks to describe are those that wipe features. Because the
39\TEXGYRE\ fonts have many bad top accent anchors (they sit above the highest
40point of the shape) the \typ {wipeanchors} tweak can remove them, and we do that
41per specified alphabet.
42
43\bgroup
44\definefontfeature[mathextra][goodies=]
45\switchtobodyfont[modern]
46\startformula
47\scale[s=2]{$\widehat{7}$}
48% \widehat{7}
49\stopformula
50\egroup
51\stopformulas
52
53In a similar fashion we \typ {wipeitalics} from upright shapes. Okay, maybe they
54can play a role for subscript placement, but then they can also interfere, and
55they do not fit with the \OPENTYPE\ specification. The \typ {wipecues} tweak
56zeros the dimensions of the invisible times and friends so that they don't
57interfere and \typ {wipevariants} gets rid of bad variants of specified
58characters.
59
60The fixers is another category, and the names indicate what gets fixed. Tweaks
61like these take lists of code points and specific properties to fix. We could
62leave it to your imagination what \typ {fixaccents}, \typ {fixanchors}, \typ
63{fixellipses}, \typ {fixoldschool}, \typ {fixprimes}, \typ {fixradicals} and \typ
64{fixslashes} do, but here are some details. Inconsistencies in the dimensions of
65accents make them jump all over the place so we normalize them. We support
66horizontal stretching at the engine level.
67
68\startformula
69\scale[s=2]{\dm{\widehat{a+b+c+d} = \widetilde{u+v+w+x+y}}}
70\stopformula
71
72It required only a few lines of code thanks to already present scaling features.
73
74% MPS: I thought of an example showing the accents, but I could
75%      not get it to work fine with the goodies loaded the second
76%      time the font was loaded
77%
78% \startformulas
79% \bgroup \definefontfeature[mathextra][goodies=]\setupbodyfont[modern]
80% \startformula[width=2em]
81% \widehat{7}
82% \stopformula
83% \egroup
84% \bgroup\setupbodyfont[modern]
85% \startformula[width=2em]
86% \widehat{7}
87% \stopformula
88% \egroup
89% \stopformulas
90
91Anchors can be off so we fix these in a way so that they look better on
92especially italic shapes. We make sure that the automated sizing works
93consistently, as this is driven by width and overshoot. Several kind of ellipses
94can be inconsistent with each other as well as with periods (shape and size wise)
95so we have to deal with that. Radicals and other extensibles have old school
96dimensions (\TEX\ fonts have a limited set of widths and heights). We need to fix
97for instance fences of various size because we want to apply kerns to scripts on
98the four possible corners for which we need to know the real height and depth,
99
100Discussing primes would take many paragraphs so we stick to mentioning that they
101are a mess. We now have native prime support in the engine as well as assume
102properly dimensioned symbols to be used. Slashes are used for skewed fractions so
103we'd better make sure they are set up right.
104
105A nice tweak is \typ {replacealphabets}. We use this to provide alternative
106script (roundhand) and calligraphic (chancery) alphabets (yes we have both
107natively in \CONTEXT\ while \UNICODE\ combines them in one alphabet). Many
108available \OPENTYPE\ math fonts come with one of the two alphabets only, some
109with roundhand and some with chancery. For the record: this tweak replaces the
110older \typ {variants} tweak that filtered scripts from a stylistic font feature.
111
112We also use the \typ {replacealphabets} tweak to drop in Arabic shapes so that we
113can do bidirectional math. In practice that doesn't really boil down to a
114replacement but more to an addition. The \typ {addmirrors} features accompanies
115this, and it is again a rather small extension to the engine to make sure we can
116do this efficiently: when a character is looked up we check a mirror variant when
117we are in r2l mode, just like we look up a smaller variant when we're in compact
118font mode (a \CONTEXT\ feature).
119
120\bgroup
121\definefontfeature[mathextra][xitsarabic=yes]
122\switchtobodyfont[bonum]
123\setupmathematics[bidi=yes,align=righttoleft]\par
124\setupalign[righttoleft] %
125\startformula
126\scale[s=2]{\dm{
127\sum_{\char"1EE4E=\char"1EE01}^{\char"1EE02} \char"1EE03^{\char"1EE4E} =
128\char"1EE03^{\char"1EE01}\frac{\char"0661 - \char"1EE03^{\char"1EE02 -
129\char"1EE01 + \char"0661}}{\char"0661 - \char"1EE03} \quad (\char"1EE03\neq
130\char"0661)
131}}
132
133% \int_{\char"0627}^{\char"0628} \char"1EE03 '(\char"1EE4E)
134% %\mathatom class \mathdifferentialcode {\char"062F}
135% \dd
136% \char"1EE4E
137% = \char"1EE03(\char"0628) - \char"1EE03(\char"0627)
138% \stopformula
139% \startformula
140% \sqrt[\char"0663](\char"1EE30) = (\char"1EE30)^{1/\char"0663}
141\stopformula
142\egroup
143
144Another application of \typ {replacealphabets} is to drop in single characters
145from another font. We use this for instance to replace the \quote {not really an
146alpha} in Bonum by one of our own liking. Below we show a math italic a and the
147original alpha, together with the modified alpha.
148
149\startformula
150\scale[s=2]{\dm{
151a + \text{\getnamedglyphdirect{file:TeXGyreBonumMath-Companion.otf}{alpha.old}} + \alpha
152}}
153\stopformula
154
155For that we ship a companion font. On our disks (and in the distribution) you can
156find:
157
158\starttyping
159/tex/texmf-fonts/fonts/data/cms/companion/RalphSmithsFormalScript-Companion.otf
160/tex/texmf-fonts/fonts/data/cms/companion/TeXGyreBonumMath-Companion.otf
161/tex/texmf-fonts/fonts/data/cms/companion/XITSMath-Companion.otf
162\stoptyping
163
164All these are efficient drop|-|ins that are injected by the \typ
165{replacealphabets}, some under user control, some always. We tried to limit the
166overhead and actually bidirectional math could be simplified which also had the
167benefit that when one does tens of thousands of bodyfont switches a bit of
168runtime is gained.
169
170There are more addition tweaks: \typ {addactuarian} creates the relevant symbols
171which is actually a right sided radical (the engine has support for two|-|sided
172radicals). It takes a bit of juggling with virtual glyphs and extensible recipes,
173but the results are rewarding.
174
175\setupmathradical[annuity][strut=no]
176\definemathdelimited
177   [myannuity]
178   [topoffset=.2\exheight,
179    strut=no,
180    rightmargin=.05\emwidth,
181    right=\delimitedrightanutityuc]
182
183\startformula
184\scale[s=2]{\dm{
185\widebar{A}__{\myannuity{m}}^^{2}_{x:\annuity{n}}^{1}
186}}
187\stopformula
188
189In a similar fashion we try to add missing extensible arrows with \typ
190{addarrows}, bars with \typ {addbars}, equals with \typ {addequals} and again
191using the radical mechanism fourier notation symbols (like hats) with \typ
192{addfourier}. That one involves subtle kerning because these symbols end up at
193the right top of a fence like symbol.
194
195\startformula
196\scale[s=2]{\dm{
197\widehat{f \ast g \ast h}(\xi) = \fourier{\F1\left(f\ast g \ast h\right)}(\xi)
198}}
199\stopformula
200
201It was actually one of the reasons to introduce a more advanced kerning mechanism
202in the engine, which is not entirely trivial because one has to carry around more
203information, since all this is font and character bound, and when wrapped in
204boxes that gets hard to analyze. The \typ {addrules} makes sure that we can do
205bars over and under constructs properly. The \typ {addparts} is there to add
206extensible recipes to characters.
207
208Some of these tweaks actually are not new and are also available in \MKIV\ but
209more as features (optionally driven by the goodie file). An example is \typ
210{addscripts} that is there for specially positioned and scaled signs (high minus
211and such) but that tweak will probably be redone as part of the \quotation {deal
212with all these plus and minus issues}. The dedicated to Alan Braslau \typ
213{addprivates} tweak is an example of this: we add specific variants for unary
214minus and plus that users can enable on demand, which in turn of course gives
215class specific spacing, but we promised not to discuss those engine features
216here.
217
218\startformula
219\scale[s=2]{\dm{
220\int_1^2 \left[(x+2)^{\frac[strut=no]{1}{2}} - (x+2)^{\um\frac[strut=no]{1}{2}}\right] \dd x
221}}
222\stopformula
223
224There is a handful of tweaks that deals with fixing glyph properties (in detail).
225We mention: \typ {dimensions} and \typ {accentdimensions} that can reposition in
226the boundingbox, fix the width and italic correction, squeeze and expand etc. The
227\typ {kernpairs} tweak adds kern pairs to combinations of characters. The \typ
228{kerns} provides a way to add top left, bottom left, top right and bottom right
229kerns and those really make the results look better so we love it!
230
231\startformula
232\scale[s=2]{\showglyphs\dm{\F3\left(\frac{1}{1+x^2}\right)^n \quad x^2/(1+x)}}
233\stopformula
234
235The \typ {margins} tweak sets margin fields that the engine can use to calculate
236accents over the base character better. The same is true for \typ {setovershoots}
237that can make accents lean over a bit. The \typ {staircase} feature can be used
238to add the somewhat complicated \OPENTYPE\ kerns. From all this you can deduce
239that the engine has all types of kerning that \OPENTYPE\ requires, and more.
240
241Accents as specified in fonts can be a pain to deal with so we have more tweaks
242for them: \typ {copyaccents} moves them to the right slots and \typ
243{extendaccents} makes sure that we can extend them. Not all font makers have the
244same ideas about where these symbols should sit and what their dimensions should
245be.
246
247The \typ {checkspacing} tweak fixes bad or missing spacing related to \UNICODE\
248character entries in the font, because after all, we might need them. We need to
249keep for instance \MATHML\ in mind, which means: processing content that we don't
250see and that can contain whatever an editor puts in. The \typ {replacements}
251feature replaces one character by another from the same font. The \typ
252{substitutes} replaces a character by one from a stylistic feature.
253
254Relatively late we added the \typ {setoptions} which was needed to control the
255engine for specific fonts. The rendering is controlled by a bunch of options
256(think of kerning, italic correction, and such). Some are per font, many per
257class. Because we can (and do) use mixed math fonts in a document, we might need
258to adapt the engine level options per font, and that is what this tweak does: it
259passes options to the font so that the engine can consult them and prefer them
260over the \quote {global} ones. We needed this for some fonts that have old school
261dimensions for extensibles (like Lucida), simply because they imitated Computer
262Modern. Normally that goes unnoticed, but, as mentioned before, it interferes
263with our optional kerning. The \typ {fixoldschool} tweak sort of can fix that too
264so \typ {setoptions} is seldom needed. Luckily, some font providers are willing
265to fix their fonts!
266
267We set and configure all these tweaks in a so-called goodie file, basically a
268runtime module that returns a \LUA\ table with specifications. In addition to the
269tweaks subtable in the math namespace, there is a subtable that overloads the
270font parameters: the ones that \OPENTYPE\ specifies, but also new ones that we
271added. In the next section we elaborate more on these font bound parameters.
272
273\stopsection
274
275\startsection[title=Font parameters]
276
277At some point in the upgrading of the math machinery we discussed some of the
278inconsistencies between the math constants of the XITS and STIX fonts. Now, one
279has to keep in mind that XITS was based on a first release of STIX that only had
280\TYPEONE\ fonts so what follows should not to be seen as criticism, but more as
281observations and reason for discussion, as well as a basis for decisions to be
282made.
283
284One thing we have to mention in advance, is that we often wonder why some weird
285and|/|or confusing stuff in math fonts go unnoticed. We have some suggestions:
286
287\startitemize
288\startitem
289    The user doesn't care that much how math comes out. This can easily be
290    observed when you run into documents on the internet or posts on forums. And
291    publishers don't always seem to care either. Consistency with old documents
292    sometimes seems to be more important than quality.
293\stopitem
294\startitem
295    The user switches to another math font when the current one doesn't handle
296    its intended math domain well. We have seen that happening and it's the
297    easiest way out when you have not much control anyway (for instance when
298    using online tools).
299\stopitem
300\startitem
301    The user eventually adds some skips and kerns to get things right, because
302    after all \TEX\ is also about tweaking.
303\stopitem
304\startitem
305    The user doesn't typeset that complex math. It's mostly inline math with an
306    occasional alignment (also in text style) and very few multi|-|level display
307    math (with left and right fences that span at most a fraction).
308\stopitem
309\stopitemize
310
311We do not claim to be perfect, but we care for details, so let's go on. The next
312table shows the math constants as they can be found in the \STIX\ (two) and
313\XITS\ (one) fonts. When you typeset with these fonts you will notice that \XITS\
314is somewhat smaller, so two additional columns show the values compensated for
315the axis height and accent base height.
316
317\startluacode
318local one = {
319   ["AccentBaseHeight"]=450,
320   ["AxisHeight"]=250,
321   ["DelimitedSubFormulaMinHeight"]=1500,
322   ["DisplayOperatorMinHeight"]=1450,
323   ["FlattenedAccentBaseHeight"]=662,
324   ["FractionDenominatorDisplayStyleGapMin"]=198,
325   ["FractionDenominatorDisplayStyleShiftDown"]=700,
326   ["FractionDenominatorGapMin"]=66,
327   ["FractionDenominatorShiftDown"]=480,
328   ["FractionNumeratorDisplayStyleGapMin"]=198,
329   ["FractionNumeratorDisplayStyleShiftUp"]=580,
330   ["FractionNumeratorGapMin"]=66,
331   ["FractionNumeratorShiftUp"]=480,
332   ["FractionRuleThickness"]=66,
333   ["LowerLimitBaselineDropMin"]=600,
334   ["LowerLimitGapMin"]=150,
335   ["MathLeading"]=150,
336   ["MinConnectorOverlap"]=50,
337   ["OverbarExtraAscender"]=66,
338   ["OverbarRuleThickness"]=66,
339   ["OverbarVerticalGap"]=198,
340   ["RadicalDegreeBottomRaisePercent"]=70,
341   ["RadicalDisplayStyleVerticalGap"]=186,
342   ["RadicalExtraAscender"]=66,
343   ["RadicalKernAfterDegree"]=-555,
344   ["RadicalKernBeforeDegree"]=277,
345   ["RadicalRuleThickness"]=66,
346   ["RadicalVerticalGap"]=82,
347   ["ScriptPercentScaleDown"]=75,
348   ["ScriptScriptPercentScaleDown"]=60,
349   ["SkewedFractionHorizontalGap"]=300,
350   ["SkewedFractionVerticalGap"]=66,
351   ["SpaceAfterScript"]=41,
352   ["StackBottomDisplayStyleShiftDown"]=900,
353   ["StackBottomShiftDown"]=800,
354   ["StackDisplayStyleGapMin"]=462,
355   ["StackGapMin"]=198,
356   ["StackTopDisplayStyleShiftUp"]=580,
357   ["StackTopShiftUp"]=480,
358   ["StretchStackBottomShiftDown"]=600,
359   ["StretchStackGapAboveMin"]=150,
360   ["StretchStackGapBelowMin"]=150,
361   ["StretchStackTopShiftUp"]=300,
362   ["SubSuperscriptGapMin"]=264,
363   ["SubscriptBaselineDropMin"]=50,
364   ["SubscriptShiftDown"]=250,
365   ["SubscriptTopMax"]=400,
366   ["SuperscriptBaselineDropMax"]=375,
367   ["SuperscriptBottomMaxWithSubscript"]=400,
368   ["SuperscriptBottomMin"]=125,
369   ["SuperscriptShiftUp"]=400,
370   ["SuperscriptShiftUpCramped"]=275,
371   ["UnderbarExtraDescender"]=66,
372   ["UnderbarRuleThickness"]=66,
373   ["UnderbarVerticalGap"]=198,
374   ["UpperLimitBaselineRiseMin"]=300,
375   ["UpperLimitGapMin"]=150,
376}
377
378local two = {
379   ["AccentBaseHeight"]=480,
380   ["AxisHeight"]=258,
381   ["DelimitedSubFormulaMinHeight"]=1325,
382   ["DisplayOperatorMinHeight"]=1800,
383   ["FlattenedAccentBaseHeight"]=656,
384   ["FractionDenominatorDisplayStyleGapMin"]=150,
385   ["FractionDenominatorDisplayStyleShiftDown"]=640,
386   ["FractionDenominatorGapMin"]=68,
387   ["FractionDenominatorShiftDown"]=585,
388   ["FractionNumeratorDisplayStyleGapMin"]=150,
389   ["FractionNumeratorDisplayStyleShiftUp"]=640,
390   ["FractionNumeratorGapMin"]=68,
391   ["FractionNumeratorShiftUp"]=585,
392   ["FractionRuleThickness"]=68,
393   ["LowerLimitBaselineDropMin"]=670,
394   ["LowerLimitGapMin"]=135,
395   ["MathLeading"]=150,
396   ["MinConnectorOverlap"]=100,
397   ["OverbarExtraAscender"]=68,
398   ["OverbarRuleThickness"]=68,
399   ["OverbarVerticalGap"]=175,
400   ["RadicalDegreeBottomRaisePercent"]=55,
401   ["RadicalDisplayStyleVerticalGap"]=170,
402   ["RadicalExtraAscender"]=78,
403   ["RadicalKernAfterDegree"]=-335,
404   ["RadicalKernBeforeDegree"]=65,
405   ["RadicalRuleThickness"]=68,
406   ["RadicalVerticalGap"]=85,
407   ["ScriptPercentScaleDown"]=70,
408   ["ScriptScriptPercentScaleDown"]=55,
409   ["SkewedFractionHorizontalGap"]=350,
410   ["SkewedFractionVerticalGap"]=68,
411   ["SpaceAfterScript"]=40,
412   ["StackBottomDisplayStyleShiftDown"]=690,
413   ["StackBottomShiftDown"]=385,
414   ["StackDisplayStyleGapMin"]=300,
415   ["StackGapMin"]=150,
416   ["StackTopDisplayStyleShiftUp"]=780,
417   ["StackTopShiftUp"]=470,
418   ["StretchStackBottomShiftDown"]=590,
419   ["StretchStackGapAboveMin"]=68,
420   ["StretchStackGapBelowMin"]=68,
421   ["StretchStackTopShiftUp"]=800,
422   ["SubSuperscriptGapMin"]=150,
423   ["SubscriptBaselineDropMin"]=160,
424   ["SubscriptShiftDown"]=210,
425   ["SubscriptTopMax"]=368,
426   ["SuperscriptBaselineDropMax"]=230,
427   ["SuperscriptBottomMaxWithSubscript"]=380,
428   ["SuperscriptBottomMin"]=120,
429   ["SuperscriptShiftUp"]=360,
430   ["SuperscriptShiftUpCramped"]=252,
431   ["UnderbarExtraDescender"]=68,
432   ["UnderbarRuleThickness"]=68,
433   ["UnderbarVerticalGap"]=175,
434   ["UpperLimitBaselineRiseMin"]=300,
435   ["UpperLimitGapMin"]=135,
436}
437
438local designrelated = {
439   ["AccentBaseHeight"]                = "optional**",
440   ["AxisHeight"]                      = "mandate",
441   ["FlattenedAccentBaseHeight"]       = "optional**",
442   ["FractionRuleThickness"]           = "optional",
443   ["MinConnectorOverlap"]             = "mandate",
444   ["OverbarRuleThickness"]            = "optional*",
445   ["RadicalDegreeBottomRaisePercent"] = "mandate",
446   ["UnderbarRuleThickness"]           = "optional*",
447}
448
449local a1 = two.AccentBaseHeight / one.AccentBaseHeight
450local a2 = two.AxisHeight / one.AxisHeight
451
452context.starttabulate { "|l|r|r|r|r|l|" }
453    context.FL()
454        context.BC() context("constant")
455        context.BC() context("stix")
456        context.BC() context("xits")
457        context.BC() context("base")
458        context.BC() context("axis")
459        context.BC() context("relevance")
460        context.BC() context.NR()
461    context.ML()
462        for key, oldvalue in table.sortedhash(one) do
463            local newvalue = two[key]
464            local accvalue = math.round(oldvalue * a1)
465            local axivalue = math.round(oldvalue * a2)
466            context.NC() context(key)
467            context.NC() context(newvalue)
468            context.NC() context(oldvalue)
469            context.NC() if newvalue == accvalue then context.bold(accvalue) else context(accvalue) end
470            context.NC() if newvalue == axivalue then context.bold(axivalue) else context(axivalue) end
471            context.NC() context(designrelated[key])
472            context.NC() context.NR()
473        end
474    context.LL()
475context.stoptabulate()
476\stopluacode
477
478Very few values are the same. So, what exactly do these constants tell us? You
479can even wonder why they are there at all. Just think of this: we want to typeset
480math, and we have an engine that we can control. We know how we want it to look.
481So, what do these constants actually contribute? Plenty relates to the height and
482depth of the nucleus and|/|or the axis. The fact that we have to fix some in the
483goodie files, and the fact that we actually need more variables that control
484positioning, makes for a good argument to just ignore most of the ones provided
485by the font, especially when they seem somewhat arbitrarily. Can it be that font
486designers are just gambling a bit, looking at another font, and starting from
487there?
488
489The relationship between \TEX's math font parameters and the \OPENTYPE\ math
490constants is not one|-|to|-|one. Mapping them onto each other is possible but
491actually is font dependent. However, we can assume that the values of Computer
492Modern are leading.
493
494The \typ {AxisHeight}, \typ {AccentBaseHeight} and \typ
495{FlattenedAccentBaseHeight} are set to the x|-|height, a value that is defined in
496all fonts. The \typ {SkewedFractionVerticalGap} also gets that value. Other
497variables relate to the em|-|width (or \type {\quad}), for instance the \typ
498{SkewedFractionHorizontalGap} that gets half that value. Of course these last two
499then assume that the engine handles skewed fractions.
500
501Variables that directly map onto each other are \typ {StretchStackGapBelowMin} as
502\typ {bigopspacing1}, \typ {StretchStackTopShiftUp} as \typ {bigopspacing3}, \typ
503{StretchStackGapAboveMin} as \typ {bigopspacing2} and \typ
504{StretchStackBottomShiftDown} as \typ {bigopspacing4}. However, these clash with
505\typ {UpperLimitBaselineRiseMin} as \typ {bigopspacing3}, \typ {UpperLimitGapMin}
506as \typ {bigopspacing1}, \typ {LowerLimitBaselineDropMin} as \typ {bigopspacing4}
507and \typ {LowerLimitGapMin} as \typ {bigopspacing2}. Where in traditional fonts
508these are the same, in \OPENTYPE\ they can be different. Should they be?
509
510Internally we use different names for variables, simply because the engine has
511some parameters that \OPENTYPE\ maths hasn't. So we have \typ {limit_above_kern}
512and \typ {limit_below_kern} for \typ {bigopspacing5}.
513
514A couple of parameters have different values for (cramped) displaystyle. The \typ
515{FractionDelimiterSize} and \typ {FractionDelimiterDisplayStyleSize} use \typ
516{delim2} and \typ {delim1}. The \typ {FractionDenominatorShiftDown} and \typ
517{FractionDenominatorDisplayStyleShiftDown} map onto \typ {denom2} and \typ
518{denom1} and their numerator counterparts from \typ {num2} and \typ {num1}. The
519\typ {Stack*} parameters also use these. The \typ {sub1}, \typ{sub2}, \typ{sup1},
520\typ{sup2}, \typ{sup3}, and \typ {supdrop} can populate the \type {Sub*} and
521\type {Super*} parameters, also in different styles.
522
523The rest of the parameters can be defined in terms of the default rulethickness,
524quad or xheight, often multiplied by a factor. For some we see the \type {1/18}
525show up a number that we also see with muskips. Some constants can be set from
526registers, like \typ {SpaceAfterScript} which is just \type {\scriptspace}.
527
528If you look at the \LUATEX\ source you wil find a section where this mapping is
529done in the case of a traditional font, that is: one without a math constants
530table. In \LUAMETATEX\ we don't need to do this because font loading happens in
531\LUA. So we simply issue an error when the math engine can't resolve a mandate
532parameter. The fact that we have a partial mapping from math constants onto
533traditional parameters and that \LUATEX\ has to deal with the traditional ones
534too make for a somewhat confusing landscape. When in \LUAMETATEX\ we assume wide
535fonts to be used that have a math constants table, we can probably clean up some
536of this.
537
538We need to keep in mind that Cambria was the starting point, and it did borrow
539some concepts from \TEX. But \TEX\ had parameters because there was not enough
540information in the glyphs! Also, Cambria was meant for \MSWORD, and a word
541processor is unlikely to provide the level of control that \TEX\ offers, so it
542needs some directions with respect to e.g.\ spacing. Without user control, it has
543to come up with acceptable compromises. So actually the \LUAMETATEX\ math engine
544can be made a bit cleaner when we just get rid of these parameters.
545
546So, which constants are actually essential? The \typ {AxisHeight} is important
547and also design related. Quite likely this is where the minus sits above the
548baseline. It is used for displacements of the baseline so that for instance
549fractions nicely align. When testing script anchored to fences we noticed that
550the parenthesis in XITS had too little depth while STIX had the expected amount.
551This relates to anchoring relative to the math axis.
552
553Is there a reason why \typ {UnderbarRuleThickness} and \typ
554{OverbarRuleThickness} should differ? If not, then we only need a variable that
555somehow tells us what thickness fits best with the other top and bottom accents.
556It is quite likely the same as the \typ {RadicalRuleThickness}, which is needed
557to extend the radical symbol. So, here three constants can be replaced by one
558design related one. The \typ {FractionRuleThickness} can also be derived from
559that, but more likely is that it is a quantity that the macro package sets up
560anyway, maybe related to rules used elsewhere.
561
562The \typ {MinConnectorOverlap} and \typ {RadicalDegreeBottomRaisePercent} also
563are related to the design although one could abuse the top accent anchor for the
564second one. So they are important. However, given the small number of
565extensibles, they could have been part of the extensible recipes.
566
567The \typ {AccentBaseHeight} and \typ {FlattenedAccentBaseHeight} might relate to
568the margin that the designer put below the accent as part of the glyph, so it is
569kind of a design related constant. Nevertheless, we fix quite some accents in the
570goodie files because they can be inconsistent. That makes these constants
571somewhat dubious too. If we have to check a font, we can just as well set up
572constants that we need in the goodie file. Also, isn't it weird that there are no
573bottom variants?
574
575We can forget about \typ {MathLeading} as it serves no purpose in \TEX. The \typ
576{DisplayOperatorMinHeight} is often set wrong so although we fix that in the
577goodie file it might be that we just can use an internal variable. It is not the
578font designer who decides that anyway. The same is true for \typ
579{DelimitedSubFormulaMinHeight}.
580
581If we handle skewed fractions, \typ {SkewedFractionHorizontalGap} and \typ
582{SkewedFractionVerticalGap} might give an indication of the tilt but why do we
583need two? It is design related though, so they have some importance, when set
584right.
585
586The rest can be grouped, and basically we can replace them by a consistent set of
587engine parameters. We can still set them up per font, but at least we can then
588use a clean set. Currently, we already have more. For instance, why only \typ
589{SpaceAfterScript} and not one for before, and how about prescripts and primes?
590If we have to complement them with additional ones and also fix them, we can as
591well set up all these script related variables.
592
593For fractions the font provides \typ {FractionDenominatorDisplayStyleGapMin},
594\typ {FractionDenominatorDisplayStyleShiftDown}, \typ
595{FractionDenominatorGapMin}, \typ {FractionDenominatorShiftDown}, \typ
596{FractionNumeratorDisplayStyleGapMin}, \typ
597{FractionNumeratorDisplayStyleShiftUp}, \typ {FractionNumeratorGapMin} and \typ
598{FractionNumeratorShiftUp}. We might try to come up with a simpler model.
599
600Limits have: \typ {LowerLimitBaselineDropMin}, \typ {LowerLimitGapMin}, \typ
601{UpperLimitBaselineRiseMin} and \typ {UpperLimitGapMin}. Limits are tricky anyway
602as they also depend on abusing the italic correction for anchoring.
603
604Horizontal bars are driven by \typ {OverbarExtraAscender}, \typ
605{OverbarVerticalGap}, \typ {UnderbarExtraDescender} and \typ
606{UnderbarVerticalGap}, but for e.g.\ arrows we are on our own, so again a not so
607useful set.
608
609Then radicals: we need some more than these \typ
610{RadicalDisplayStyleVerticalGap}, \typ {RadicalExtraAscender}, \typ
611{RadicalKernAfterDegree}, \typ {RadicalKernBeforeDegree} and \typ
612{RadicalVerticalGap}, and because we really need to check these there is no gain
613having them in the font.
614
615Isn't it more a decision by the macro package how script and scriptscript should
616be scaled? Currently we listen to \typ {ScriptPercentScaleDown} and \typ
617{ScriptScriptPercentScaleDown}, but maybe it relates more to usage.
618
619We need more control than just \typ {SpaceAfterScript} and an engine could
620provide it more consistently. It's a loner.
621
622How about \typ {StackBottomDisplayStyleShiftDown}, \typ {StackBottomShiftDown},
623\typ {StackDisplayStyleGapMin}, \typ {StackGapMin}, \typ
624{StackTopDisplayStyleShiftUp} and \typ {StackTopShiftUp}? And isn't this more for
625the renderer to decide: \typ {StretchStackBottomShiftDown}, \typ
626{StretchStackGapAboveMin}, \typ {StretchStackGapBelowMin} and \typ
627{StretchStackTopShiftUp}?
628
629This messy bit can also be handled more convenient so what exactly is the
630relationship with the font design of \typ {SubSuperscriptGapMin}, \typ
631{SubscriptBaselineDropMin}, \typ {SubscriptShiftDown}, \typ {SubscriptTopMax},
632\typ {SuperscriptBaselineDropMax}, \typ {SuperscriptBottomMaxWithSubscript}, \typ
633{SuperscriptBottomMin}, \typ {SuperscriptShiftUp} and \typ
634{SuperscriptShiftUpCramped}?
635
636Just for the record, here are the (font related) ones we added so far. A set of
637prime related constants similar to the script ones: \typ {PrimeRaisePercent},
638\typ {PrimeRaiseComposedPercent}, \typ {PrimeShiftUp}, \typ
639{PrimeBaselineDropMax}, \typ {PrimeShiftUpCramped}, \typ {PrimeSpaceAfter} and
640\typ {PrimeWidthPercent}. Of course, we also added \typ {SpaceBeforeScript} just
641because we want to be symmetrical in the engine where we also have to deal with
642prescripts.
643
644These we provide for some further limit positioning: \typ {NoLimitSupFactor} and
645\typ {NoLimitSubFactor}; these for delimiters: \typ {DelimiterPercent} and \typ
646{DelimiterShortfall}; and these for radicals in order to compensate for sloping
647shapes: \typ {RadicalKernAfterExtensible} and \typ {RadicalKernBeforeExtensible}
648because we have doublesided radicals.
649
650Finally, there are quite some (horrible) accent tuning parameters: \typ
651{AccentTopShiftUp}, \typ {AccentBottomShiftDown}, \typ
652{FlattenedAccentTopShiftUp}, \typ {FlattenedAccentBottomShiftDown}, \typ
653{AccentBaseDepth}, \typ {AccentFlattenedBaseDepth}, \typ {AccentTopOvershoot},
654\typ {AccentBottomOvershoot}, \typ {AccentSuperscriptDrop}, \typ
655{AccentSuperscriptPercent} and \typ {AccentExtendMargin}, but we tend to move
656some of that to the tweaks on a per accent basis.
657
658Setting these parameters right is not trivial, and also a bit subjective. We might,
659however, assume that for instance the math axis is set right, but alas, when we
660were fixing the less and greater symbols in Lucida Bright Math, we found that all
661symbols actually were designed for a math axis of 325, instead of the given
662value 313, and that difference can be seen!
663
664\startbuffer
665\scale[s=2]{\dm{
666    2 > -\left\{\frac{1}{1+x^2}\right\}
667}}
668\stopbuffer
669
670\startlinecorrection
671\startcombination[nx=2]
672    \startcontent
673    % \definefontfeature[mathextra][goodies=]
674         \switchtobodyfont[lucidaold]
675         \showglyphs
676         \getbuffer
677     \stopcontent
678     \startcaption
679         Old Lucida
680     \stopcaption
681     \startcontent
682         \switchtobodyfont[lucida]
683         \showglyphs
684         \getbuffer
685     \stopcontent
686     \startcaption
687         New Lucida
688     \stopcaption
689\stopcombination
690\stoplinecorrection
691
692The assumption is that the axis goes trough the middle of
693the minus. Luckily it was relatively easy to fix these two symbols (they also had
694to be scaled, maybe they originate in the text font?) and adapt the
695axis. We still need to check all the other fonts, but it looks like they are okay,
696which is good because the math axis plays an important role in rendering math.
697It is one of the few parameters that has to be present and right. A nice side
698effect of this is that we end up with discussing new (\CONTEXT) features. One
699can for instance shift all non-character symbols down just a little and lower
700the math axis, to get a bit more tolerance in lines with many inline fractions,
701radicals or superscripts, that otherwise would result in interline skips.
702
703A first step in getting out of this mess is to define {\em all} these parameters
704in the goodie file where we fix them anyway. That way we are at least not
705dependent on changes in the font. We are not a word processor so we have way more
706freedom to control matters. And preset font parameters sometimes do more harm
707than good. A side effect of a cleanup can be that we get rid of the evolved mix
708of uppercase and lowercase math control variables and can be more consistent.
709Ever since \LUATEX\ got support for \OPENTYPE, math constants names have been
710mapped and matched to traditional \TEX\ font parameters.
711
712\stopsection
713
714\startsection[title=Metrics]
715
716With metrics we refer to the dimensions and other properties of math glyphs. The
717origin of digital math fonts is definitely Computer Modern and thereby the
718storage of properties is bound to the \TFM\ file format. That format is binary
719and can be loaded fast. It can also be stored in the format, unless you're using
720\LUATEX\ or \LUAMETATEX\ where \LUA\ is the storage format. A \TFM\ file stores
721per character the width, height, depth and italic correction. The file also
722contains font parameters. In math fonts there are extensible recipes and there is
723information about next in size glyphs. The file has kerning and ligature tables
724too.
725
726Given the times \TEX\ evolved in, the format is rather compact. For instance, the
727height, depth and italic correction are shared and indices to three shared values
728are used. There can be 16 heights and depths and 64 italic corrections. That way
729much fits into a memory word.
730
731The documentation tells us that \quotation {The italic correction of a character
732has two different uses. (a)~In ordinary text, the italic correction is added to
733the width only if the \TEX\ user specifies \quote{\type {\/}} after the
734character. (b)~In math formulas, the italic correction is always added to the
735width, except with respect to the positioning of subscripts.} It is this last
736phenomena that gives us some trouble with fonts in \OPENTYPE\ math. The fact that
737traditional fonts cheat with the width and that we add and selectively remove or
738ignore the correction makes for fuzzy code in \LUATEX\ although splitting the
739code paths and providing options to control all this helps a bit. In \LUAMETATEX\
740we have more control but also expect an \OPENTYPE\ font. In \OPENTYPE\ math there
741are italic corrections, and we even have the peculiar usage of it in positioning
742limits. However, the idea was that staircase kerns do the detailed relative
743positioning.
744
745Before we dive into this a bit more, it is worth mentioning that Don Knuth paid a
746lot of attention to details. The italic alphabet in math uses the same shapes as
747the text italic but metrics are different as is shown below. We have also met fonts
748where it looked like the text italics were taken, and where the metrics were handled
749via more excessive italic correction, sometimes combined with staircase kerns that
750basically were corrections for the side bearing. This is why we always come back to
751Latin Modern and Cambria when we investigate fonts: one is based on the traditional
752\TEX\ model, with carefully chosen italic corrections, and the other is based on the
753\OPENTYPE\ model with staircase kerning. They are our reference fonts.
754
755\startlinecorrection
756\startcombination[nx=1,ny=2]
757    \startcontent
758        \definedfont[file:lmroman10italic.otf]%
759        \showglyphs
760        \scale[s=3]{abcdefghijklmnopqrstuvwxyz}
761    \stopcontent
762    \startcaption
763        Latin Modern Roman Italic
764    \stopcaption
765    \startcontent
766        \definedfont[file:latinmodernmath.otf]%
767        \showglyphs
768        \scale[s=3]{𝑎𝑏𝑐𝑑𝑒𝑓𝑔ℎ𝑖𝑗𝑘𝑙𝑚𝑛𝑜𝑝𝑞𝑟𝑠𝑡𝑢𝑣𝑤𝑥𝑦𝑧}
769    \stopcontent
770    \startcaption
771        Latin Modern Math Italic
772    \stopcaption
773\stopcombination
774\stoplinecorrection
775
776In \CONTEXT\ \MKIV\ we played a lot with italic correction in math and there were
777ways to enforce, ignore, selectively apply it, etc. But, because fonts actually
778demand a mixture, in \LUAMETATEX\ we ended up with more extensive runtime
779patching of them. Another reason for this was that math fonts can have weird
780properties. It looks like when these standards are set and fonts are made, the
781font makers can do as they like as long as the average formula comes out right,
782and metrics to some extent resemble a traditional font. However, when testing how
783well a font behaves in a real situation there can be all kind of interferences
784from the macro package: inter|-|atom kerning, spacing corrections macros,
785specific handling of cases, etc. We even see \OPENTYPE\ fonts that seem to
786have the same limited number of heights, depths and italic corrections. And, as a
787consequence we get for instance larger sizes of fences having the same depth for
788all the size variants, something that is pretty odd for an \OPENTYPE\ font with no
789limitations.
790
791The italic correction in traditional \TEX\ math gets added to the width. When a
792subscript is attached to a kernel character it sits tight against that character:
793its position is driven by the width of the kernel. A superscript on the other
794hand is moved over the italic width so that it doesn't overlap or touch the
795likely sticking out bit of the kernel. This means that a traditional font (and
796quite some \OPENTYPE\ math fonts are modelled after Computer Modern) have to find
797compromises of width and italic correction for characters where the subscript is
798supposed to move left (inside the bounding box of the kernel).
799
800The \OPENTYPE\ specification has some vague remarks about applying italic
801correction between the last in a series of slanted shapes and operators, as well
802as positioning limits, and suggests that it relates to relative super- and
803subscript positioning. It doesn't mention that the correction is to be added to
804the width. However, the main mechanism for anchoring script are these top and
805bottom edge kerns. It's why in fonts that provide these, we are unlikely to find
806italic correction unless it is used for positioning limits.
807
808It is for that reason that an engine can produce reasonable results for fonts
809that either provide italics or provide kerns for anchoring: having both on the
810same glyph would mean troubles. It means that we can configure the engine options
811to add italic correction as well as kerns, assuming distinctive usage of those
812features. For a font that uses both we need to make a choice (this is possible,
813since we can configure options per font). But that will never lead to always nicely
814typeset math. In fact, without tweaks many fonts will look right because in
815practice they use some mixture. But we are not aiming at partial success, we want
816all to look good.
817
818Here is another thing to keep in mind (although now we are guessing a bit). There
819is a limited number of heights and depths in \TEX\ fonts possible (16), but four
820times as many italic corrections can be defined (64). Is it because Don Knuth
821wanted to properly position the sub- and subscripts? Adding italic correction to
822the width is pretty safe: shapes should not overlap. Choosing the right width for
823a subscript needs more work because it's is more visual. In the end we have a
824width that is mostly driven by superscript placement! That also means that as
825soon as we remove the italic correction things start looking bad. In fact,
826because also upright math characters have italic correction the term \quote
827{italic} is a bit of a cheat: it's all about script positioning and has little to
828do with the slope of the shapes.
829
830One of the reasons why for instance spacing between an italic shape and an
831upright one in \TEX\ works out okay is that in most cases they come from a
832different font, which can be used as criterium for keeping the correction;
833between a sequence of same|-|font characters it gets removed. However, in
834\OPENTYPE\ math there is a good chance that all comes from the same font (at
835least in \CONTEXT), unless one populates many families as in traditional \TEX. We
836have no clue how other macro packages deal with this but it might well be the
837case that using many families (one for each alphabet) works better in the end.
838The engine is really shape and alphabet agnostic, but one can actually wonder if
839we should add a glyph property indicating the distinctive range. It would provide
840engine level control over a run of glyphs (like multiplying a variable
841represented by a greek alpha by another variable presented by an upright b).
842
843But glyph properties cannot really be used here because we are still dealing with
844characters when the engine transforms the noad list into a node list. So, when we
845discussed this, we started wondering how the engine could know about a specific
846shape (and tilt) property at all, and that brought us to pondering about an
847additional axis of options. We already group characters in classes, but we can
848also group them with properties like \typ {tilted}, \typ {dotless}, \typ {bold}.
849When we pair atoms we can apply options, spacing and such based on the specific
850class pair, and we can do something similar with category pairs. It basically
851boils down to for instance \type {\mccode} that binds a character to a category.
852Then we add a command like \typ {\setmathcategorization} (analogue to \typ
853{\setmathspacing}) that binds options to pairs of categories. An easier variant
854of this might be to let the \type {\mccode} carry a (bit)set of options that then
855get added to the already existing options that can be bound to character noads as
856we create them. This saves us some configuration. Deciding what suits best
857depends on what we want to do: the fact that \TEX\ doesn't do this means that
858probably no one ever gave it much thought, but once we do have this mechanism it
859might actually trigger demand, if only by staring at existing documents where
860characters of a different kind sit next to each other (take this \quote {a}
861invisible times \quote {x}). It would not be the first time that (in \CONTEXT)
862the availability of some feature triggers creative (ab)usage.
863
864Because the landscape has settled, because we haven't seen much fundamental
865evolution in \OPENTYPE\ math, because in general \TEX\ math doesn't really
866evolve, and because \CONTEXT\ in the past has not been seen as suitable for
867 math, we can, as mentioned before, basically decide what approach we
868follow. So, that is why we can pick up on this italic correction in a more
869drastic way: we can add the correction to the width, thereby creating a nicely
870bounded glyph, and moving the original correction to the right bottom kern, as
871that is something we already support. In fact, this feature is already available,
872we only had to add setting the right bottom kern. The good news is that we don't
873need to waste time on trying to get something extra in the font format, which is
874unlikely to happen anyway after two decades.
875
876It is worth noticing that when we were exploring this as part of using \METAPOST\
877to analyze and visualize these aspects, we also reviewed the \typ {wipeitalics}
878tweak and wondered if, in retrospect, it might be a dangerous one when applied to
879alphabets (for digits and blackboard bold letters it definitely makes
880sense): it can make traditional super- and subscript anchoring less optimal.
881However, for some fonts we found that improper bounding boxes can badly interfere
882anyway: for instance the upright \quote {f} in EBGaramond sticks out left and
883right, and has staircase kerns that make scripts overlap. The right top of the
884shape sticks out a lot and that is because the text font variant is used. We already decided to
885add a \typ {moveitalics} tweak that moves italic kerns into the
886width and then setting a right bottom kern that compensates it that can be a
887pretty good starting point for our further exploration of optimal kerns at the
888corners. That tweak also fixes the side bearings (negative llx) and compensates
889left kerns (when present) accordingly. An additional \typ {simplifykerns} tweak
890can later migrate staircase kerns to simple kerns.
891
892So, does that free us from tweaks like \typ {dimensions} and \typ {kerns}? Not
893completely. But we can forget about the italic correction
894in most cases. We have to set up less lower right kerns and maybe correct a few.
895It is just a more natural solution. So how about these kerns that we need to
896define? After all, we also have to deal with proper top kerns, and like to add
897kerns that are not there simply because the mentioned comprise between width,
898italic and the combination was impossible. More about that in the next section.
899
900\stopsection
901
902\startsection[title=Kerning]
903
904In the next pictures we will try to explain more visual what we have in mind and
905are experimenting with as we write this. In the traditional approach we have
906shapes that can communicate the width, height, depth and italic correction to the
907engine so that is what the engine can work with. The engine also has the
908challenge to anchor subscripts and superscripts in a visual pleasing way.
909
910\startMPdefinitions
911    numeric UsedUnit ; UsedUnit = 1mm ;
912    numeric UsedWidth ; UsedWidth := 10UsedUnit ;
913    numeric UsedItalic ; UsedItalic := 2UsedUnit ;
914    numeric UsedScript ; UsedScript = 5UsedUnit;
915    picture LeftCharA ; LeftCharA := image(
916        draw origin -- (UsedWidth,UsedWidth)
917            withpen pencircle scaled 2UsedUnit
918            withcolor .4white ;
919        path p ; p := boundingbox currentpicture ;
920        draw rightboundary currentpicture
921            bottomenlarged -5UsedUnit
922            withpen pencircle scaled .5UsedUnit ;
923        setbounds currentpicture to p ;
924        draw origin
925            withpen pencircle scaled 1UsedUnit
926            withcolor .1white ;
927        setbounds currentpicture to boundingbox currentpicture
928            leftenlarged -UsedItalic
929            rightenlarged -UsedItalic ;
930        draw boundingbox currentpicture
931            withpen pencircle scaled .1UsedUnit ;
932    ) ;
933    picture RightCharA ; RightCharA := image(
934        draw (0,UsedWidth) -- (UsedWidth,0)
935            withpen pencircle scaled 2UsedUnit
936            withcolor .6white ;
937        path p ; p := boundingbox currentpicture ;
938        draw rightboundary currentpicture
939            bottomenlarged -5UsedUnit
940            withpen pencircle scaled .5UsedUnit ;
941        setbounds currentpicture to p ;
942        draw origin
943            withpen pencircle scaled 1UsedUnit
944            withcolor .1white ;
945        setbounds currentpicture to boundingbox currentpicture
946            leftenlarged -UsedItalic
947            rightenlarged -UsedItalic ;
948        draw boundingbox currentpicture
949            withpen pencircle scaled .1UsedUnit ;
950    ) ;
951    picture LeftCharB ; LeftCharB := image(
952        draw origin -- (UsedWidth,UsedWidth)
953            withpen pencircle scaled 2UsedUnit
954            withcolor .4white ;
955        path p ; p := boundingbox currentpicture ;
956        draw origin
957            withpen pencircle scaled 1UsedUnit
958            withcolor .1white ;
959        draw boundingbox currentpicture
960            withpen pencircle scaled .1UsedUnit ;
961        draw lrcorner p
962            shifted (-UsedItalic,0)
963            withpen pencircle scaled 1UsedUnit
964            withcolor .1white ;
965        draw urcorner p
966            withpen pencircle scaled 1UsedUnit
967            withcolor .1white ;
968        setbounds currentpicture to p ;
969    ) ;
970    picture RightCharB ; RightCharB := image(
971        draw (0,UsedWidth) -- (UsedWidth,0)
972            withpen pencircle scaled 2UsedUnit
973            withcolor .6white ;
974        path p ; p := boundingbox currentpicture ;
975        draw origin
976            withpen pencircle scaled 1UsedUnit
977            withcolor .1white ;
978        draw boundingbox currentpicture
979            withpen pencircle scaled .1UsedUnit ;
980        draw lrcorner p
981            shifted (-UsedItalic,0)
982            withpen pencircle scaled 1UsedUnit
983            withcolor .1white ;
984        draw urcorner p
985            withpen pencircle scaled 1mm
986            withcolor .1white ;
987        setbounds currentpicture to p ;
988    ) ;
989    picture SuperScript ; SuperScript := image(
990        draw unitsquare scaled UsedScript
991            shifted (0,-UsedScript/2)
992    ) ;
993    picture SubScript ; SubScript := image(
994        draw unitsquare scaled UsedScript
995            shifted (0,-UsedScript/2)
996    ) ;
997    def WidenResultA =
998        setbounds currentpicture to boundingbox currentpicture
999            leftenlarged 6UsedUnit
1000            rightenlarged 6UsedUnit;
1001    enddef ;
1002    def WidenResultB =
1003        setbounds currentpicture to boundingbox currentpicture
1004            topenlarged .5UsedScript
1005            leftenlarged 6UsedUnit
1006            rightenlarged 6UsedUnit;
1007    enddef ;
1008\stopMPdefinitions
1009
1010\startlinecorrection
1011    \startcombination[nx=3,ny=1]
1012        \startcontent
1013            \startMPcode
1014                draw LeftCharA ;
1015                draw RightCharA xshifted (UsedWidth+UsedItalic+3UsedUnit) ;
1016                WidenResultA ;
1017            \stopMPcode
1018        \stopcontent
1019        \startcaption
1020            two characters
1021        \stopcaption
1022        \startcontent
1023            \startMPcode
1024                draw LeftCharA ;
1025                draw RightCharA xshifted UsedWidth ;
1026                WidenResultA ;
1027            \stopMPcode
1028        \stopcontent
1029        \startcaption
1030            width only
1031        \stopcaption
1032        \startcontent
1033            \startMPcode
1034                draw LeftCharA ;
1035                draw RightCharA xshifted (UsedWidth+UsedItalic) ;
1036                WidenResultA ;
1037            \stopMPcode
1038        \stopcontent
1039        \startcaption
1040            with italic
1041        \stopcaption
1042    \stopcombination
1043\stoplinecorrection
1044
1045In this graphic we show two pseudo characters. The shown bounding box indicates
1046the width as seen by the engine. An example of such a shape is the math italic~f,
1047and as it is used a lot in formulas it is also one of the most hard ones to handle
1048when it comes to spacing: in nearly all fonts the right top sticks out and in
1049some fonts the left part also does that. Imagine how that works out with scripts,
1050fences and preceding characters.
1051
1052When we put two such characters together they will overlap, and this is why we need
1053to add the italic correction. That is also why the \TEX\ documentation speaks in
1054terms of \quotation {always add the italic correction to the width}. This also
1055means that we need to remove it occasionally, something that you will notice when
1056you study for instance the \LUATEX\ source, that has a mix of traditional and
1057\OPENTYPE\ code paths. Actually, compensating can either be done by changing the
1058width property of a glyph node or by explicitly adding a kern. In \LUAMETATEX\ we
1059always add real kerns because we can then trace better.
1060
1061The last graphic in the above set shows how we compensate the width for the bit
1062that sticks out. It also shows that we definitely need to take neighboring shapes
1063into account when we determine the width and italic correction, especially when
1064the later is {\em not} applied (read: removed).
1065
1066\startlinecorrection
1067    \startcombination[nx=3,ny=1]
1068        \startcontent
1069            \startMPcode
1070                draw LeftCharA ;
1071                path p ; p := boundingbox currentpicture ;
1072                draw SuperScript
1073                    shifted urcorner p xshifted UsedScript ;
1074                draw SubScript
1075                    shifted lrcorner p xshifted UsedScript  ;
1076                WidenResultB ;
1077            \stopMPcode
1078        \stopcontent
1079        \startcaption
1080            kernel
1081        \stopcaption
1082        \startcontent
1083            \startMPcode
1084                draw LeftCharA ;
1085                path p ; p := boundingbox currentpicture ;
1086                draw SuperScript
1087                    shifted urcorner p ;
1088                draw SubScript
1089                    shifted lrcorner p ;
1090                WidenResultB ;
1091            \stopMPcode
1092        \stopcontent
1093        \startcaption
1094            subscript
1095        \stopcaption
1096        \startcontent
1097            \startMPcode
1098                draw LeftCharA ;
1099                path p ; p := boundingbox currentpicture ;
1100                draw SuperScript
1101                    shifted urcorner p
1102                    xshifted UsedItalic ;
1103                draw SubScript
1104                    shifted lrcorner p ;
1105                WidenResultB ;
1106            \stopMPcode
1107        \stopcontent
1108        \startcaption
1109            superscript
1110        \stopcaption
1111    \stopcombination
1112\stoplinecorrection
1113
1114Here we anchored a super- and subscript. The subscript position it tight to the
1115advance width, again indicated by the box. The superscript however is moved by
1116the italic correction and in the engine additional spacing before and after can
1117be applied as well, but we leave that for now. It will be clear that when the
1118font designer chooses the width and italic correction, the fact that scripts get
1119attached has to be taken into account.
1120
1121\startlinecorrection
1122    \startcombination[nx=2,ny=1]
1123        \startcontent
1124            \startMPcode
1125                draw LeftCharB ;
1126                draw RightCharB xshifted (UsedWidth+UsedItalic+3UsedUnit) ;
1127                WidenResultA ;
1128            \stopMPcode
1129        \stopcontent
1130        \startcaption
1131            two characters
1132        \stopcaption
1133        \startcontent
1134            \startMPcode
1135                draw LeftCharB ;
1136                draw RightCharB xshifted (UsedWidth+UsedItalic) ;
1137                WidenResultA ;
1138            \stopMPcode
1139        \stopcontent
1140        \startcaption
1141            width only
1142        \stopcaption
1143    \stopcombination
1144\stoplinecorrection
1145
1146In this graphic we combine the italic correction with the width. Keep in mind
1147that in these examples we use tight values but in practice that correction can
1148also add some extra right side bearing (white space). This addition is an
1149operation that we can do when loading a font. At the same time we also compensate
1150the left edge for which we can use the x coordinate of the left corner of the
1151glyphs real bounding box. The advance width starts at zero and that corner is
1152then left of the origin. By looking at shapes we concluded that in most cases
1153that shift is valid for usage in math where we don't need that visual overlap. In
1154fact, when we tested some of that we found that the results can be quite horrible
1155when you don't do that; not all fonts have left bottom kerning implemented.
1156
1157The dot at the right is actually indicating the old italic correction. Here we
1158let it sit on the edge but as mentioned there can be additional (or maybe less)
1159italic correction than tight.
1160
1161\startlinecorrection
1162    \startcombination[nx=3,ny=1]
1163        \startcontent
1164            \startMPcode
1165                draw LeftCharB ;
1166                path p ; p := boundingbox currentpicture ;
1167                draw SuperScript
1168                    shifted urcorner p xshifted UsedScript ;
1169                draw SubScript
1170                    shifted lrcorner p xshifted UsedScript ;
1171                WidenResultB ;
1172            \stopMPcode
1173        \stopcontent
1174        \startcaption
1175            kernel
1176        \stopcaption
1177        \startcontent
1178            \startMPcode
1179                draw LeftCharB ;
1180                path p ; p := boundingbox currentpicture ;
1181                draw SuperScript
1182                    shifted urcorner p ;
1183                draw SubScript
1184                    shifted lrcorner p ;
1185                WidenResultB ;
1186            \stopMPcode
1187        \stopcontent
1188        \startcaption
1189            superscript
1190        \stopcaption
1191        \startcontent
1192            \startMPcode
1193                draw LeftCharB ;
1194                path p ; p := boundingbox currentpicture ;
1195                draw SuperScript
1196                    shifted urcorner p ;
1197                draw SubScript
1198                    shifted (-UsedItalic,0)
1199                    shifted lrcorner p ;
1200                WidenResultB ;
1201            \stopMPcode
1202        \stopcontent
1203        \startcaption
1204            subscript
1205        \stopcaption
1206    \stopcombination
1207\stoplinecorrection
1208
1209Finally we add the scripts here. This time we position the superscript and
1210subscript at the top and bottom anchors. The bottom anchor is, as mentioned, the
1211old italic correction, and the top one currently just the edge. And this is what
1212our next project is about: identify the ideal anchors and use these instead.
1213
1214In the \CONTEXT\ goodie files (the files that tweak the math fonts runtime) we
1215can actually already set these top and bottom anchors and the engine will use
1216them when set. These kerns are not to be confused with the more complicated
1217staircase kerns. They are much simpler and lightweight. The fact that we already
1218have them makes it relatively easy to experiment with this.
1219
1220It must be noted that we talk about three kinds of kerns: inter character kerns,
1221corner kerns and staircase kerns. We can set them all up with tweaks but so far
1222we only did that for the most significant ones, like integrals. The question is:
1223can we automate this? We should be careful because the bad top accent anchors in
1224the \TEXGYRE\ fonts demonstrate how flawed heuristics can be. Interesting is that
1225the developers of these font used \METAPOST\ and are highly qualified in that
1226area. And for us using \METAPOST\ is also natural!
1227
1228The approach that we follow is somewhat interactive. When working on the math
1229update we like to chat (with zoom) about these matters. We discuss and explore
1230plenty and with these kerns we do the same. Because \METAPOST\ produces such nice
1231and crispy graphics, and because \METAFUN\ is well integrated into \CONTEXT\ we
1232can link all these subsystems and just look at what we get. A lot is about
1233visualization: if we discuss so called \quote {grayness} in the perspective of
1234kerning, we end up with calculating areas, then look at what it tells us and as a
1235next step figure out some heuristic. And of course we challenge each other into
1236new trickery.
1237
1238% THIS WILL BECOME A MODULE!
1239
1240\startluacode
1241local formatters = string.formatters
1242
1243local glyph  = nil
1244local mpdata = nil
1245
1246local f_boundingbox = formatters["((%N,%N)--(%N,%N)--(%N,%N)--(%N,%N)--cycle)"]
1247local f_vertical    = formatters["((%N,%N)--(%N,%N))"]
1248
1249function mp.lmt_glyphshape_start(id,character)
1250    if type(id) == "string" then
1251        id = fonts.definers.internal({ name = id } ,"<module:fonts:shapes:font>")
1252    end
1253    local fontid       = (id and id ~= 0 and id) or font.current()
1254    local shapedata    = fonts.hashes.shapes[fontid] -- by index
1255    local characters   = fonts.hashes.characters[fontid] -- by unicode
1256    local descriptions = fonts.hashes.descriptions[fontid] -- by unicode
1257    local shapeglyphs  = shapedata.glyphs or { }
1258    if type(character) == "string" and character ~= "" then
1259        local hex = string.match(character,"^0x(.+)")
1260        if hex then
1261            character = tonumber(hex,16)
1262        else
1263            character = utf.byte(character)
1264        end
1265    else
1266        character = tonumber(character)
1267    end
1268    local chardata = characters[character]
1269    local descdata = descriptions[character]
1270    if chardata then
1271        glyph = shapeglyphs[chardata.index]
1272        if glyph and (glyph.segments or glyph.sequence) and not glyph.paths then
1273            local units  = shapedata.units or 1000
1274            local factor = 100/units
1275            local width  = (descdata.width or 0)  * factor
1276            local height = descdata.boundingbox[4] * factor
1277            local depth  = descdata.boundingbox[2] * factor
1278            local math   = descdata.math
1279            local italic = (math and math.italic or 0) * factor
1280            local accent = (math and math.accent or 0) * factor
1281            mpdata = {
1282                paths       = fonts.metapost.paths(glyph,factor),
1283                boundingbox = fonts.metapost.boundingbox(glyph,factor),
1284                baseline    = fonts.metapost.baseline(glyph,factor),
1285                width       = width,
1286                height      = height,
1287                depth       = depth,
1288                italic      = italic,
1289                accent      = accent,
1290                usedbox     = f_boundingbox(0,depth,width,depth,width,height,0,height),
1291                usedline    = f_vertical(0,0,width,0),
1292            }
1293        end
1294    else
1295        print("NO",id,character)
1296    end
1297end
1298
1299function mp.lmt_glyphshape_stop()
1300    glyph  = nil
1301    mpdata = nil
1302end
1303
1304function mp.lmt_glyphshape_n()
1305    if mpdata then
1306        mp.print(#mpdata.paths)
1307    else
1308        mp.inject.numeric(0)
1309    end
1310end
1311
1312function mp.lmt_glyphshape_path(i)
1313    if mpdata then
1314        mp.print(mpdata.paths[i])
1315    else
1316        mp.inject.pair(0,0)
1317    end
1318end
1319
1320function mp.lmt_glyphshape_boundingbox()
1321    if mpdata then
1322        mp.print(mpdata.boundingbox)
1323    else
1324        mp.inject.pair(0,0)
1325    end
1326end
1327function mp.lmt_glyphshape_usedbox()
1328    if mpdata then
1329        mp.print(mpdata.usedbox)
1330    else
1331        mp.inject.pair(0,0)
1332    end
1333end
1334
1335function mp.lmt_glyphshape_baseline()
1336    if mpdata then
1337        mp.print(mpdata.baseline)
1338    else
1339        mp.inject.pair(0,0)
1340    end
1341end
1342function mp.lmt_glyphshape_usedline()
1343    if mpdata then
1344        mp.print(mpdata.usedline)
1345    else
1346        mp.inject.pair(0,0)
1347    end
1348end
1349
1350function mp.lmt_glyphshape_width () mp.print(mpdata and mpdata.width  or 0) end
1351function mp.lmt_glyphshape_depth () mp.print(mpdata and mpdata.depth  or 0) end
1352function mp.lmt_glyphshape_height() mp.print(mpdata and mpdata.height or 0) end
1353function mp.lmt_glyphshape_italic() mp.print(mpdata and mpdata.italic or 0) end
1354function mp.lmt_glyphshape_accent() mp.print(mpdata and mpdata.accent or 0) end
1355
1356\stopluacode
1357
1358\startMPdefinitions
1359    presetparameters "glyphshape" [
1360      % id        = "",
1361      % character = "",
1362        shape       = true,
1363        boundingbox = false,
1364        baseline    = false,
1365        usedline    = true,
1366        usedbox     = true,
1367    ] ;
1368
1369def lmt_glyphshape = applyparameters "glyphshape" "lmt_do_glyphshape" enddef ;
1370
1371vardef glyphshape_start(expr id, character) =
1372    lua.mp.lmt_glyphshape_start(id, character) ;
1373enddef ;
1374
1375vardef glyphshape_stop         = lua.mp.lmt_glyphshape_stop() ;      enddef ;
1376vardef glyphshape_n            = lua.mp.lmt_glyphshape_n()           enddef ;
1377vardef glyphshape_path(expr i) = lua.mp.lmt_glyphshape_path(i)       enddef ;
1378vardef glyphshape_boundingbox  = lua.mp.lmt_glyphshape_boundingbox() enddef ;
1379vardef glyphshape_baseline     = lua.mp.lmt_glyphshape_baseline()    enddef ;
1380vardef glyphshape_usedbox      = lua.mp.lmt_glyphshape_usedbox()     enddef ;
1381vardef glyphshape_usedline     = lua.mp.lmt_glyphshape_usedline()    enddef ;
1382vardef glyphshape_width        = lua.mp.lmt_glyphshape_width()       enddef ;
1383vardef glyphshape_height       = lua.mp.lmt_glyphshape_height()      enddef ;
1384vardef glyphshape_depth        = lua.mp.lmt_glyphshape_depth()       enddef ;
1385vardef glyphshape_italic       = lua.mp.lmt_glyphshape_italic()      enddef ;
1386vardef glyphshape_accent       = lua.mp.lmt_glyphshape_accent()      enddef ;
1387
1388vardef lmt_do_glyphshape =
1389    image (
1390        pushparameters "glyphshape" ;
1391            lua.mp.lmt_glyphshape_start(getparameter "id", getparameter "character") ;
1392            if getparameter "shape" :
1393                draw for i=1 upto lua.mp.lmt_glyphshape_n() :
1394                    lua.mp.lmt_glyphshape_path(i) &&
1395                endfor cycle ;
1396            fi ;
1397            if getparameter "boundingbox" :
1398                draw
1399                    lua.mp.lmt_glyphshape_boundingbox()
1400                    withcolor red
1401                ;
1402            fi ;
1403            if getparameter "usedline" :
1404                draw
1405                    lua.mp.lmt_glyphshape_usedline()
1406                    withcolor green
1407                ;
1408            fi ;
1409            if getparameter "usedbox" :
1410                draw
1411                    lua.mp.lmt_glyphshape_usedbox()
1412                    withcolor blue
1413                ;
1414            fi ;
1415            lua.mp.lmt_glyphshape_stop() ;
1416        popparameters ;
1417    )
1418enddef ;
1419
1420\stopMPdefinitions
1421
1422\startplacefigure[location=none]
1423\startMPcode[offset=1dk]
1424picture leftchar ;
1425picture rightchar ;
1426path leftbbox ;
1427path rightbbox ;
1428numeric leftitalic ;
1429numeric rightitalic ;
1430numeric leftaccent ;
1431numeric rightaccent ;
1432
1433numeric N ; N := 50 ;
1434
1435glyphshape_start("file:texgyrebonum-math.otf", "0x1D453") ;
1436    leftchar   := image (draw for i=1 upto glyphshape_n : glyphshape_path(i) && endfor cycle ;) ;
1437    leftbbox   := glyphshape_usedbox ;
1438    leftaccent := glyphshape_accent ;
1439    leftitalic := xpart urcorner leftbbox - glyphshape_italic ;
1440glyphshape_stop ;
1441glyphshape_start("file:texgyrebonum-math.otf", "0x1D45A") ;
1442    rightchar := image (draw for i=1 upto glyphshape_n : glyphshape_path(i) && endfor cycle ;) ;
1443    rightbbox := glyphshape_usedbox ;
1444    rightaccent := glyphshape_accent ;
1445    rightitalic := xpart urcorner rightbbox - glyphshape_italic ;
1446glyphshape_stop ;
1447
1448rightchar := rightchar xshifted (xpart lrcorner leftbbox) ;
1449rightbbox := rightbbox xshifted (xpart lrcorner leftbbox) ;
1450
1451rightaccent := rightaccent  + xpart lrcorner leftbbox ;
1452rightitalic := rightitalic  + xpart lrcorner leftbbox ;
1453
1454numeric d ; d := (xpart lrcorner leftbbox) - leftitalic ;
1455rightchar := rightchar shifted (d,0);
1456rightbbox := rightbbox shifted (d,0);
1457
1458draw leftbbox  withcolor 0.5white ;
1459draw rightbbox withcolor 0.5white ;
1460draw leftchar  withpen pencircle scaled 1 ;
1461draw rightchar withpen pencircle scaled 1 ;
1462
1463numeric miny, maxy ;
1464
1465miny := max(ypart lrcorner leftbbox, ypart llcorner rightbbox) ;
1466maxy := min(ypart urcorner leftbbox, ypart ulcorner rightbbox) ;
1467
1468path testv ; testv := ((0,miny) -- (0,maxy)) xshifted (xpart lrcorner leftbbox) ;
1469
1470% %   testv := testv shifted (d,0);
1471% draw testv withcolor darkred ;
1472
1473path midpath, leftintersections, rightintersections ;
1474pair leftintersection[], rightintersection[] ;
1475
1476numeric yta ; yta := 0 ;
1477numeric minl ; minl := 1000 ;
1478
1479for i = 1 upto (N-1) :
1480    midpath := (0, ypart point (i/N) along testv) -- (xpart urcorner rightbbox, ypart point (i/N) along testv);
1481    for j within leftchar :
1482        midpath := midpath cutbeforelast pathpart j ;
1483    endfor
1484    for j within rightchar :
1485        midpath := midpath cutafterfirst pathpart j ;
1486    endfor
1487
1488    if ( (i = 1) or ((xpart point 1 of midpath) - (xpart point 0 of midpath) < minl) ) :
1489        minl := (xpart point 1 of midpath) - (xpart point 0 of midpath) ;
1490    fi
1491
1492    if ((xpart point 0 of midpath) < eps) or ((xpart point 1 of midpath) > ((xpart urcorner rightbbox) - eps)) :
1493        draw midpath withpen pencircle scaled 1 withcolor 0.1[white,darkgreen] withtransparency (1,0.5) ;
1494        midpath := (point 0 of midpath) && cycle ;
1495    fi
1496
1497    draw midpath withcolor 0.4[white,darkgreen] ;
1498    draw point 0 of midpath withpen pencircle scaled 1 withcolor darkgreen ;
1499    draw point 1 of midpath withpen pencircle scaled 1.25 withcolor darkgreen ;
1500
1501    yta := yta + (1/N)*((xpart point 1 of midpath) - (xpart point 0 of midpath)) ;
1502endfor
1503
1504drawarrow (origin -- ((xpart lrcorner leftbbox) - leftitalic,0)) shifted (urcorner leftbbox) withcolor "orange" ;
1505drawarrow (origin -- ((xpart lrcorner rightbbox) - rightitalic - d,0)) shifted (urcorner rightbbox) withcolor "orange" ;
1506
1507% draw (leftaccent,   (ypart urcorner leftbbox )) withcolor "darkblue" withpen pencircle scaled 3 ;
1508% draw (rightaccent + d,  (ypart urcorner rightbbox)) withcolor "darkblue" withpen pencircle scaled 3 ;
1509
1510\stopMPcode
1511
1512\stopplacefigure
1513
1514
1515We are sure that getting this next stage in the perfection of math typesetting in
1516\CONTEXT\ and \LUAMETATEX\ will take quite some time, but the good news is that
1517all machinery is in place. We also have to admit that it all might not work out
1518well, so that we stick to what we have now. But at least we had the fun then. And
1519it is also a nice example of both applying mathematics and programming graphics.
1520
1521That said, if it works out well, we can populate the goodie files with output
1522from \METAPOST, tweak a little when needed, and that saves us some time. One
1523danger is that when we try to improve rendering the whole system also evolves
1524which in turn will give different output, but we can always implement all this as
1525features because after all \CONTEXT\ is very much about configuration. And it
1526makes nice topics for articles and talks too!
1527
1528The kerns discussed in the previous paragraphs are not the ones that we
1529find in \OPENTYPE\ fonts. There we have \quote {staircase} kerns that stepwise go
1530up or down by height and kern. So, one can have different kerns depending on the
1531height and sort of follow the shape. This permits quite precise kerning between
1532for instance the right bottom of a kernel and left top of a subscript. So how is
1533that used in practice? The reference font Cambria has these kerns but close
1534inspection shows that these are not that accurate. Fortunately, we never enter
1535the danger zone with subscripts, because other parameters prevent that. If we look
1536at for instance Lucida and Garamond, then we see that their kerns are mostly used
1537as side bearing, and not really as staircase kerns.
1538
1539\usemodule[s][fonts-shapes]
1540
1541\startlinecorrection
1542\startcombination[nx=5,ny=1]
1543    \startcontent
1544        \ShowGlyphShape{name:cambria-math}{100bp}{0x1D6FD}
1545    \stopcontent
1546    \startcaption
1547        \type {U+1D6FD}
1548    \stopcaption
1549    \startcontent
1550        \ShowGlyphShape{name:cambria-math}{100bp}{0x003A4}
1551    \stopcontent
1552    \startcaption
1553        \type {U+003A4}
1554    \stopcaption
1555    \startcontent
1556        \ShowGlyphShape{name:cambria-math}{100bp}{0x1D4CC}
1557    \stopcontent
1558    \startcaption
1559        \type {U+1D4CC}
1560    \stopcaption
1561    \startcontent
1562        \ShowGlyphShape{name:cambria-math}{100bp}{0x1D6B8}
1563    \stopcontent
1564    \startcaption
1565        \type {U+1D6B8}
1566    \stopcaption
1567    \startcontent
1568        \ShowGlyphShape{name:cambria-math}{100bp}{0x1D70C}
1569    \stopcontent
1570    \startcaption
1571        \type {U+1D70C}
1572    \stopcaption
1573\stopcombination
1574\stoplinecorrection
1575
1576In these figures you see a few glyphs from cambria with staircase kerns and
1577although we show them small you will notice that some kern boundaries touch the
1578shape. As subscripts never go that high it goes unnoticed but it also shows that
1579sticking to the lowest boundary makes sense.
1580
1581We conclude that we can simplify these kerns, and just transform them into our
1582(upto four) corner kerns. It is unlikely that Cambria gets updates and that other
1583fonts become more advanced. One can even wonder if multiple steps really give
1584better results. The risk of overlap increases with more granularity because not
1585every pair of glyphs is checked. Also, the repertoire of math characters will
1586likely not grow and include shapes that differ much from what we can look at now.
1587Reducing these kerns to simple ones, that can easily be patched at will in a
1588goodie file, has advantages. We can even simplify the engine.
1589
1590\stopsection
1591
1592\startsection[title=Conclusion]
1593
1594So how can we summarize the above? The first conclusion is that we can only get
1595good results when we runtime patch fonts to suite the engine and our (\CONTEXT)
1596need. The second conclusion is that we should seriously consider to drop (read:
1597ignore) most math font parameter and|/|or to reorganize them. There is no
1598need to be conforming, because these parameters are often not that well
1599implemented (thumb in mouth). The third conclusion (or observation) is that we
1600should get rid of the excessive use of italic correction, and go for our new
1601corner kerns instead. Last, we can conclude that it makes sense to explore how we
1602can use \METAPOST\ to analyze the shapes in such a way that we can improve inter
1603character kerning, corner kerns and maybe even, in a limited way, staircase kerns.
1604
1605And, to come back to accents: very few characters need a top kern. Most can be
1606handled with centered anchors, and we need tweaks for margins and overshoot
1607anyway. The same is true for many other tweaks: they are there to stay.
1608
1609This is how we plan to go forward:
1610
1611\startitemize[packed]
1612    \startitem
1613        We pass no italic corrections in the math fonts to the engine,
1614        but instead we have four dedicated simple corner kerns, top and
1615        bottom anchors, and we also compensate negative left side bearing. We
1616        should have gone that route earlier (as follow up on a \MKIV\ feature)
1617        but were still in some backward compatibility mindset.
1618    \stopitem
1619    \startitem
1620        The \LUAMETATEX\ math engine might then be simplified by removing all
1621        code related to italic correction. Of course it hurts that we spent so
1622        much time on that over the years. We can anyway disable engine options
1623        related to italic correction in the \CONTEXT\ setup. Of course the engine
1624        is less old school generic then but that is the price of progress.
1625    \stopitem
1626    \startitem
1627        A default goodie file is applied that takes care of this when no goodie
1628        file is provided. We could do some in the engine, but there is no real
1629        need for that. We can simplify the mid 2022 goodie files because we have
1630        to fix less glyphs.
1631    \stopitem
1632    \startitem
1633        If we ever need italic correction (that is: backtrack) then we use the
1634        (new) \type {\mccode} option code that can identity sloped shapes. But,
1635        given that ignoring the correction between sloped shapes looks pretty bad,
1636        we can as well forget about this. After all, italic correction never
1637        really was about correcting italics, but more about anchoring scripts.
1638    \stopitem
1639    \startitem
1640        Staircase kerns can be reduced to simple corner kerns and the engine can
1641        be simplified a bit more. In the end, all we need is true widths and simple
1642        corner kerns.
1643    \stopitem
1644    \startitem
1645        We reorganize the math parameters and get rid of those that are not
1646        really font design dependent. This also removes a bit of overlap. This will
1647        be done as we document.
1648    \stopitem
1649    \startitem
1650        Eventually we can remove tweaks that are no longer needed in the new
1651        setup, which is a good thing as it also save us some documenting and
1652        maintenance.
1653    \stopitem
1654\stopitemize
1655
1656All this will happen in the perspective of \CONTEXT\ and \LUAMETATEX\ but we
1657expect that after a few years of usage we can with confidence come to some
1658conclusions that can trickle back in the other engines so that other macro
1659packages can benefit from a somewhat radical different but reliable approach to
1660math rendering, one that works well with the old and new fonts.
1661
1662\stopsection
1663
1664\stopchapter
1665
1666\stopcomponent
1667