1
2
3\startcomponent ontargetgettingridof
4
5\environment ontargetstyle
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 dont 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 ones 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
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 dont
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{abcd} = \widetilde{uvwxy}}}
70\stopformula
71
72It required only a few lines of code thanks to already present scaling features.
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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
103wed 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 doesnt 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 were 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
134
135
136
137
138
139
140
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:TeXGyreBonumMathCompanion.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
159textexmffontsfontsdatacmscompanionRalphSmithsFormalScriptCompanion.otf
160textexmffontsfontsdatacmscompanionTeXGyreBonumMathCompanion.otf
161textexmffontsfontsdatacmscompanionXITSMathCompanion.otf
162\stoptyping
163
164All these are efficient dropins 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 twosided
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_12 \left[(x2){\frac[strut=no]{1}{2}} (x2){\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}{1x2}\right)n \quad x2(1x)}}
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 dont
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 socalled 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
285andor confusing stuff in math fonts go unnoticed. We have some suggestions:
286
287\startitemize
288\startitem
289 The user doesnt 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 dont 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 doesnt handle
296 its intended math domain well. We have seen that happening and its 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 doesnt typeset that complex math. Its mostly inline math with an
306 occasional alignment (also in text style) and very few multilevel 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 lets 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 andor 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 \TEXs math font parameters and the \OPENTYPE\ math
490constants is not onetoone. 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 xheight, a value that is defined in
496all fonts. The \typ {SkewedFractionVerticalGap} also gets that value. Other
497variables relate to the emwidth (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 hasnt. So we have \typ {limitabovekern}
512and \typ {limitbelowkern} 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 {118}
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 dont need to do this because font loading happens in
531\LUA. So we simply issue an error when the math engine cant 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, isnt 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
615Isnt 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. Its a loner.
621
622How about \typ {StackBottomDisplayStyleShiftDown}, \typ {StackBottomShiftDown},
623\typ {StackDisplayStyleGapMin}, \typ {StackGapMin}, \typ
624{StackTopDisplayStyleShiftUp} and \typ {StackTopShiftUp}? And isnt 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}{1x2}\right\}
667}}
668\stopbuffer
669
670\startlinecorrection
671\startcombination[nx=2]
672 \startcontent
673
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 noncharacter 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 youre 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: interatom 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 doesnt 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 doesnt 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. Its 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 its 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: its 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 samefont 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\ doesnt 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 havent seen much fundamental
865evolution in \OPENTYPE\ math, because in general \TEX\ math doesnt 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 dont
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,UsedScript2)
992 ) ;
993 picture SubScript ; SubScript := image(
994 draw unitsquare scaled UsedScript
995 shifted (0,UsedScript2)
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 (UsedWidthUsedItalic+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 (UsedWidthUsedItalic) ;
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 italicf,
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 (UsedWidthUsedItalic+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 (UsedWidthUsedItalic) ;
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 dont need that visual overlap. In
1154fact, when we tested some of that we found that the results can be quite horrible
1155when you dont 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
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]
1255 local characters = fonts.hashes.characters[fontid]
1256 local descriptions = fonts.hashes.descriptions[fontid]
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
1361
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
1471
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 (iN) along testv) -- (xpart urcorner rightbbox, ypart point (iN) 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 (1N)((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
1508
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][fontsshapes]
1540
1541\startlinecorrection
1542\startcombination[nx=5,ny=1]
1543 \startcontent
1544 \ShowGlyphShape{name:cambriamath}{100bp}{0x1D6FD}
1545 \stopcontent
1546 \startcaption
1547 \type {U1D6FD}
1548 \stopcaption
1549 \startcontent
1550 \ShowGlyphShape{name:cambriamath}{100bp}{0x003A4}
1551 \stopcontent
1552 \startcaption
1553 \type {U003A4}
1554 \stopcaption
1555 \startcontent
1556 \ShowGlyphShape{name:cambriamath}{100bp}{0x1D4CC}
1557 \stopcontent
1558 \startcaption
1559 \type {U1D4CC}
1560 \stopcaption
1561 \startcontent
1562 \ShowGlyphShape{name:cambriamath}{100bp}{0x1D6B8}
1563 \stopcontent
1564 \startcaption
1565 \type {U1D6B8}
1566 \stopcaption
1567 \startcontent
1568 \ShowGlyphShape{name:cambriamath}{100bp}{0x1D70C}
1569 \stopcontent
1570 \startcaption
1571 \type {U1D70C}
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 andor 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 |