% language=us runpath=texruns:manuals/fonts \startcomponent fonts-extensions \environment fonts-environment \startchapter[title=Extensions][color=darkorange] \startsection[title=Introduction] One of the benefits of using \TEX\ is that you can add your own features and try to optimize the look and feel. Of course this can also go wrong and output can look pretty awful when you don't know what you're doing, but on the average it works out well. In many aspects the move to an \UNICODE\ data path and \OPENTYPE\ fonts is a good one and solves a lot of problems with traditional \TEX\ engines and helps us to avoid complex and ugly hacks. But, if you look into the source code of \CONTEXT\ you will notice that there's still quite some complex coding needed. This is because we want to control mechanisms, even if it's only for dealing with some border cases. It's also the reason why \LUATEX\ is what it is: an extensible engine, building on tradition. As always with \TEX, fonts are an area where many tuning happens and this is also true in \CONTEXT. In this chapter some of the extensions will be discussed. Some extensions run on top of the (rather generic) feature mechanism and some are using dedicated code. \stopsection \startsection[title=Italics] Although \OPENTYPE\ fonts are more rich in features than traditional \TEX\ and \TYPEONE\ fonts, one important feature is missing: italic correction. This might sound strange but you need to keep in mind that in practice it's a feature that needs to be applied manually. \starttyping test {\it test\/} test \stoptyping It is possible to automate this mechanism and this is what the \type {\em} command does in \MKII: \starttyping test {\em test} test \stoptyping This command knows that it switches to italic (or slanted) and when used nested it knows to switch back. It also knows if a bold italic or slanted font is used. Therefore it can add italic correction between an italic and upright shape. An italic correction is bound to a glyph and bound to a font. In \in {figure} [latinmodern-italic] we see how an italic shape extends out of the bounding box. This is not the case in Dejavu: watch \in {figure} [dejavu-italic]. \startplacefigure[reference=latinmodern-italic,title={Italic overshoot in Latin Modern.}] \startcombination \startcontent \backgroundline[gray]{\color[maincolor]{\definedfont[lmroman10-regular*default sa 8]test}} \stopcontent \startcaption Latin Modern Roman Regular \stopcaption \startcontent \backgroundline[gray]{\color[maincolor]{\definedfont[lmroman10-italic*default sa 8]test}} \stopcontent \startcaption Latin Modern Roman Italic \stopcaption \stopcombination \stopplacefigure \startplacefigure[reference=dejavu-italic,title={Italic overshoot in Dejavu Serif.}] \startcombination \startcontent \backgroundline[gray]{\color[maincolor]{\definedfont[dejavuserif*default sa 8]test}} \stopcontent \startcaption Dejavu Regular \stopcaption \startcontent \backgroundline[gray]{\color[maincolor]{\definedfont[dejavuserifitalic*default sa 8]test}} \stopcontent \startcaption Dejavu Italic \stopcaption \stopcombination \stopplacefigure This means that the application of italic correction should never been applied without knowing the font. In \in {figure} [italic-upright] we see an upright word following an italic. The space is determined by the upright one. \startplacefigure[reference=italic-upright,title={Italic followed by upright.}] \startcombination \startcontent \backgroundline [gray] {\color[maincolor]{\definedfont[lmroman10-italic*default sa 4]test} \color[maincolor]{\definedfont[lmroman10-regular*default sa 4]\space test}} \stopcontent \startcaption Latin Modern \stopcaption \startcontent \backgroundline [gray] {\color[maincolor]{\definedfont[dejavuserifitalic*default sa 4]test}% \color[maincolor]{\definedfont[dejavuserif*default sa 4]\space test}} \stopcontent \startcaption Dejavu \stopcaption \stopcombination \stopplacefigure Because it is to be used with care you need to enable this feature per font, You also need to explicitly enable the application of this correction. in \in {figure} [italic-one] we see italic correction in action. \startbuffer \definefontfeature [italic] [default] [itlc=yes] \stopbuffer \typebuffer \getbuffer \startplacefigure[reference=italic-one,title={Italic correction.}] \startcombination \startcontent \backgroundline [maincolor] {\color[white]{\definedfont[lmroman10-italic*default sa 4]test} \color[white]{\definedfont[lmroman10-regular*default sa 4]\space test}} \stopcontent \startcaption \backgroundline [maincolor] {\setupitaliccorrection[text]% \color[white]{\definedfont[lmroman10-italic*italic sa 4]test} \color[white]{\definedfont[lmroman10-regular*default sa 4]\space test}} \stopcaption \startcontent \backgroundline [maincolor] {\color[white]{\definedfont[dejavuserifitalic*default sa 4]test} \color[white]{\definedfont[dejavuserif*default sa 4]\space test}} \stopcontent \startcaption \backgroundline [maincolor] {\setupitaliccorrection[text]% \color[white]{\definedfont[dejavuserifitalic*italic sa 4]test} \color[white]{\definedfont[dejavuserif*default sa 4]\space test}} \stopcaption \stopcombination \stopplacefigure This only signals the font constructor that additional italic information has to be added to the font metrics. As we already mentioned, the application of correction is driven by the \type {\/} primitive and that one consults the font metrics. Because the correction is not part of the original font metrics it is calculated automatically by adding a small value to the width. This value is calculated as follows: \starttyping factor * (parameters.uwidth or 40) / 2 \stoptyping The \type {uwidth} parameter is sometimes part of the specification but if not, we take a reasonable default. The factor is under user control: \startbuffer \definefontfeature [moreitalic] [default] [itlc=5] \stopbuffer \typebuffer \getbuffer This is demonstrated in \in {figure} [italic-two]. You will notice that for Latin Modern (any) correction makes sense, but for Dejavu it probably makes things look worse. This is why italic correction is disabled by default. When enabled there are several variants: \starttabulate[|Bl|l|] \NC global \NC always apply correction \NC \NR \NC text \NC only apply correction to text \NC \NR \NC always \NC apply correction between text and boxes \NC \NR \NC none \NC forget about correction \NC \NR \stoptabulate We keep track of the state using attributes but that comes at a (small) price in terms of extra memory and runtime. The \type {global} option simply assumes that we always need to check for correction (of course only for fonts that have this feature enables). In the given example we used: \starttyping \setupitaliccorrection [text] \stoptyping You can combine keys: \starttyping \setupitaliccorrection [global,always] \stoptyping \startplacefigure[reference=italic-two,title={Italic correction (factor 5).}] \startcombination \startcontent \backgroundline [maincolor] {\color[white]{\definedfont[lmroman10-italic*default sa 4]test} \color[white]{\definedfont[lmroman10-regular*default sa 4]\space test}} \stopcontent \startcaption \backgroundline [maincolor] {\setupitaliccorrection[text]% \color[white]{\definedfont[lmroman10-italic*italic sa 4]test} \color[white]{\definedfont[lmroman10-regular*default sa 4]\space test}} \stopcaption \startcontent \backgroundline [maincolor] {\color[white]{\definedfont[dejavuserifitalic*default sa 4]test} \color[white]{\definedfont[dejavuserif*default sa 4]\space test}} \stopcontent \startcaption \backgroundline [maincolor] {\setupitaliccorrection[text]% \color[white]{\definedfont[dejavuserifitalic*italic sa 4]test} \color[white]{\definedfont[dejavuserif*default sa 4]\space test}} \stopcaption \stopcombination \stopplacefigure The \type {itlc} feature controls if a font gets italic correction applied. In principle this is all that the user needs to do, given that the mechanism is enabled. These is an extra feature that controls the implementation: \starttabulate[|T|T|p|] \NC itlc \NC no \NC don't apply italic correction (default) \NC \NR \NC \NC yes \NC apply italic correction \NC \NR \NC textitalics \NC no \NC precalculate italic corrections (permit engine usage) \NC \NR \NC \NC yes \NC precalculate italic corrections (inhibit engine) \NC \NR \NC \NC delay \NC delay calculation of corrections \NC \NR \stoptabulate When \type {textitalics} is set to \type {yes} or \type {delay} the mechanism built into the engine is completely disabled. When set to \type {no} the engine can kick in but normally the alternative method takes precedence so that the engine sees no reason for further action. You can trace italic corrections with: \starttyping \enabletrackers[typesetters.italics] \stoptyping \stopsection \startsection[title=Bounding boxes] \startbuffer \definefontfeature [withbbox] [boundingbox=yes] \definefont [FontWithBB] [Normal*withbbox] \stopbuffer \start \getbuffer \FontWithBB There are some features that are rather useless and only make sense when figuring out issues. An example of such a feature is the following: \typebuffer This feature adds a background to each character in a font. In some fonts a glyph has a tight bounding box, while on other fonts some extra space is put on the left and right. Keep in mind that this feature blocks colored text. \par \stop \stopsection \startsection[title=Math italics] In the traditional \TEX\ fonts the width of a glyph was not the real width because one had to add the italic correction to it. The engine then juggles a bit with these properties. If you run into fonts that are designed this way, you can do this: \starttyping \definefontfeature[mathextra][italicwidths=yes] % fix latin modern \stoptyping This might make \type {$\left|V\right| = \left|W\right|$} look better for such fonts. Of course there can be side effects because these fonts assume a traditional engine. \stopsection \startsection[title=Slanting] This features (as well as the one described in the next section) are seldom used but provided because they were introduced in \PDFTEX. \startbuffer[define] \definefontfeature [abitslanted] [default] [slant=.1] \definefontfeature [abitmoreslanted] [default] [slant=.2] \stopbuffer \startbuffer[sample] \definedfont[Normal*abitslanted]This is a bit slanted. \definedfont[Normal*abitmoreslanted]And this is a bit more slanted. \stopbuffer \typebuffer[define,sample] The result is: \getbuffer[define] \startlines \getbuffer[sample] \stoplines \stopsection \startsection[title=Extending] The second manipulation is extending the shapes horizontally: \startbuffer[define] \definefontfeature [abitbolder] [default] [extend=1.3] \definefontfeature [abitnarrower] [default] [extend=0.7] \stopbuffer \startbuffer[sample] \definedfont[Normal*abitbolder]This looks a bit bolder. \definedfont[Normal*abitnarrower]And this is a bit narrower. \stopbuffer \typebuffer[define,sample] The result is: \getbuffer[define] \startlines \getbuffer[sample] \stoplines We can also combine slanting and extending: \startbuffer[define] \definefontfeature [abitofboth] [default] [extend=1.3, slant=.1] \stopbuffer \startbuffer[sample] \definedfont[Normal*abitofboth]This is a bit bolder but also slanted. \stopbuffer \typebuffer[define,sample] If you remember those first needle matrix printers you might recognize the next rendering: \getbuffer[define] \startlines \getbuffer[sample] \stoplines \stopsection \startsection[title=Fixing] % dimensions This is a rather special one. First we show a couple of definitions: \startbuffer \definefontfeature [dimensions-a] [default] [dimensions={1,1,1}] \definefontfeature [dimensions-b] [default] [dimensions={1,2,3}] \definefontfeature [dimensions-c] [default] [dimensions={1,3,2}] \definefontfeature [dimensions-d] [default] [dimensions={3,3,3}] \stopbuffer \typebuffer \getbuffer When you don't want a dimension to change you leave an entry empty, so valid entries are for instance: \type {,3,} and \type {1,,}. As usual you apply such a feature as follows: \starttyping \definefont[MyFont][Serif*dimensions-a sa 2] \stoptyping Alternatively you can use such a feature on its own: \starttyping \definefontfeature [dimensions-333] [dimensions={3,3,3}] \definefont[MyFont][Serif*default,dimensions-333 sa 2] \stoptyping In \in {figure} [dimensions-side-by-side] you see these four definitions in action. The leftmost rendering is the default rendering. The three numbers in the definitions represent the width (in em), height and depth (in ex). \startplacefigure[reference={dimensions-side-by-side},title={Freezing dimensions of glyphs.}] \startcombination[5*1] \startcontent \hbox to 7em {\hss\ruledhbox{\definedfont[Serif*default sa 2]g}\hss}\stopcontent \startcaption default \stopcaption \startcontent \hbox to 7em {\hss\ruledhbox{\definedfont[Serif*dimensions-a sa 2]g}\hss}\stopcontent \startcaption \hbox{1em 1ex 1ex} \stopcaption \startcontent \hbox to 7em {\hss\ruledhbox{\definedfont[Serif*dimensions-b sa 2]g}\hss}\stopcontent \startcaption \hbox{1em 2ex 3ex} \stopcaption \startcontent \hbox to 7em {\hss\ruledhbox{\definedfont[Serif*dimensions-c sa 2]g}\hss}\stopcontent \startcaption \hbox{1em 3ex 2ex} \stopcaption \startcontent \hbox to 7em {\hss\ruledhbox{\definedfont[Serif*dimensions-d sa 2]g}\hss}\stopcontent \startcaption \hbox{3em 3ex 3ex} \stopcaption \stopcombination \stopplacefigure This feature only makes sense for fonts that need a fixed width, like the \CJK\ fonts that are used for asian scripts. Normally those fonts already have fixed dimensions, but this feature can be used to fix problematic fonts or add some more space. However, for such large fonts this also brings a larger memory footprint. A special case is the following: \startbuffer \definefontfeature [dimensions-e] [dimensions=strut] \stopbuffer \typebuffer \getbuffer This will make the height and depth the same as the {\em current} strut height and depth: \startbuffer \ruledhbox{\definedfont[Serif*default,dimensions-e at 8pt]clipped} \ruledhbox{\definedfont[Serif*default,dimensions-e at 12pt]clipped} \ruledhbox{\definedfont[Serif*default,dimensions-e at 24pt]clipped} \stopbuffer \typebuffer The dimensions are (in this case) limited: \startlinecorrection[blank] \dontleavehmode \hpack{\maincolor\inlinebuffer} \stoplinecorrection Another special case is \type {dimensions=mono} which will make an characters the fonts em|-|width. This is handy when you define font fallbacks where glyphs come from a non monospaced font. \stopsection \startsection[title=Unicoding] Nowadays we will mostly use fonts that ship with a \UNICODE\ aware encoding. And in \CONTEXT, even if we use a \TYPEONE\ font, it gets mapped onto \UNICODE. However, there are some exceptions, for instance the Zapf Dingbats in \TYPEONE\ format. These have a rather obscure private encoding and the glyph names run from \type {a1} upto \type {a206} and have no relation to what the glyph represents. In the case of Dingbats we're somewhat lucky that they ended up in \UNICODE, so we can relocate the glyphs to match their rightful place. This is done by means of a goodies file. We already discussed this in \in {section} [goodies] so we only repeat the usage. \startbuffer \definefontfeature [dingbats] [mode=base, goodies=dingbats, unicoding=yes] \definefontsynonym [ZapfDingbats] [file:uzdr.afm] [features=dingbats] \stopbuffer \typebuffer \getbuffer I tend to qualify the Dingbat font in \TEX\ distributions as rather unstable because of name changes and them either or not being included. Therefore it's best to use the hard coded name because that triggers the most visible error message when the font is not found. A font like this can for instance be used with the glyph placement macros as is demonstrated below. In the last line we see that a direct \UTF\ input also works out well. \starttabulate[|||T|] \HL \NC \type{\getglyphdirect {ZapfDingbats*dingbats}{\number"2701}} \NC \getglyphdirect {ZapfDingbats*dingbats}{\number"2701} \NC \NC \NR \NC \type{\getglyphdirect {ZapfDingbats*dingbats}{\char"2701}} \NC \getglyphdirect {ZapfDingbats*dingbats}{\char"2701} \NC \NC \NR \NC \type{\getnamedglyphdirect{ZapfDingbats*dingbats}{a1}} \NC \getnamedglyphdirect{ZapfDingbats*dingbats}{a1} \NC \NC \NR \NC \type{\getnamedglyphdirect{ZapfDingbats*dingbats}{a11}} \NC \getnamedglyphdirect{ZapfDingbats*dingbats}{a11} \NC \NC \NR \HL \NC \type{\getglyphdirect {ZapfDingbats}{\number"2701}} \NC \getglyphdirect {ZapfDingbats}{\number"2701} \NC unknown \NC \NR \NC \type{\getglyphdirect {ZapfDingbats}{\char"2701}} \NC \getglyphdirect {ZapfDingbats}{\char"2701} \NC unknown \NC \NR \NC \type{\getnamedglyphdirect{ZapfDingbats}{a1}} \NC \getnamedglyphdirect{ZapfDingbats}{a1} \NC \NC \NR \NC \type{\getnamedglyphdirect{ZapfDingbats}{a11}} \NC \getnamedglyphdirect{ZapfDingbats}{a11} \NC \NC \NR \HL \NC \type{\definedfont[ZapfDingbats*dingbats]✁} \NC \definedfont[ZapfDingbats*dingbats]✁ \NC \NC \NR \HL \stoptabulate Keep in mind that fonts like Dejavu (that we use here as document font) already has these characters which is why it shows up in the verbose part of the table. \stopsection \startsection[title=Protrusion] Protrusion is a feature that \LUATEX\ inherits from \PDFTEX. It is sometimes referred to as hanging punctuation but in our case any character qualifies. Also, hanging is not frozen but can be tuned in detail. Currently the engine defines protrusion in terms of the emwidth which is unfortunate and likely to change. \footnote {In general the low level implementation can be optimized as there are better mechanisms in \LUATEX.} It is sometimes believed that protrusion improves for instance narrower columns, but I'm pretty sure that this is not the case. It is true that it is taken into account when breaking a paragraph into lines, and that we then have a little bit more width available, but at the same time it is an extra constraint: if we protrude we have to do it for each line (and the whole main body of text) so it's just a different solution space. The main reason for applying this feature is {\em not} that the lines look better or that we get better looking narrow lines but that the right and left margins look nicer. Personally I don't like half protrusion of punctuation and hyphens. Best is to have small values for regular characters to improve the visual appearance and use full protrusion for hyphens (and maybe punctuation). \startsubsubject[title=protrusion classes] In \CONTEXT\ we've always defined protrusion as a percentage of the width of a glyph. From \MKII\ we inherit the level of control as well as the ability to define vectors. The shared properties are collected in so called classes and the character specific properties in vectors. The following classes are predefined: \showprotrusionclass The names are used in the definitions: \starttyping \definefontfeature[default][protrusion=quality] \stoptyping Currently adding a class only has a \LUA\ interface. \startbuffer \startluacode fonts.protrusions.classes.myown = { vector = 'myown', factor = 1, } \stopluacode \stopbuffer \typebuffer \getbuffer \stopsubsubject \startsubsubject[title=protrusion vectors] Vectors are larger but not as large as you might expect. Only a subset of characters needs to be defined. This is because in practice only latin scripts are candidates and these scripts have glyphs that look a lot like each other. As we only operate on the horizontal direction characters like \quote {aàáâãäå} look the same from the left and right so we only have to define the protrusion for \quote {a}. As with classes, you can define your own vectors: \startbuffer \startluacode fonts.protrusions.vectors.myown = table.merged ( fonts.protrusions.vectors.quality, { [0x002C] = { 0, 2 }, -- comma } ) \stopluacode \stopbuffer \typebuffer \getbuffer \stopsubsubject \startsubsubject[title=protrusion vector pure] \showprotrusionvector[name=pure] \stopsubsubject \startsubsubject[title=protrusion vector punctuation] \showprotrusionvector[name=punctuation] \stopsubsubject \startsubsubject[title=protrusion vector alpha] \showprotrusionvector[name=alpha] \stopsubsubject \startsubsubject[title=protrusion vector quality] \showprotrusionvector[name=quality] \stopsubsubject \startsubsubject[title=examples of protrusion] Next we show the quality protrusion. For this we use \type {tufte.tex} as this one for sure will result in punctuation and other candidates for protrusion. \startbuffer[define] \definefontfeature [whatever] [default] [protrusion=quality] \definefont[MyTestA][Serif*default at 10pt] \definefont[MyTestB][Serif*whatever at 10pt] \stopbuffer \startbuffer[example] \startoverlay {\ruledvbox \bgroup \hsize\textwidth \MyTestA \setupalign[normal] \input{tufte} \egroup} {\ruledvbox \bgroup \hsize\textwidth \MyTestB \setupalign[hanging,normal] \maincolor \input{tufte} \egroup} \stopoverlay \stopbuffer \typebuffer[define] \getbuffer [define] We use the following example. The results are shown in \in {figure} [protrusion:quality]. The colored text is the protruding one. \typebuffer[example] \startplacefigure[reference=protrusion:quality,title=The difference between no protrusion and quality protrusion.] \getbuffer [example] \stopplacefigure The previously defined own class and vector is somewhat more extreme: \startbuffer[define] \definefontfeature [whatever] [default] [protrusion=myown] \definefont[MyTestA][Serif*default at 10pt] \definefont[MyTestB][Serif*whatever at 10pt] \stopbuffer \typebuffer[define] \getbuffer [define] In \in {figure} [protrusion:myown] we see that the somewhat extreem definition of the comma also pulls the preceding character into the margin. \startplacefigure[reference=protrusion:myown,title=The influence of extreme protrusion on preceding characters.] \getbuffer [example] \stopplacefigure \stopsubsubject \stopsection \startsection[title=Expansion] Expansion is also an inheritance of \PDFTEX. \footnote {As with protrusion the implementation in the engine is somewhat suboptimal and inefficient and will be upgraded to a more \LUATEX-ish way.} This mechanism selectively expands characters, normally upto 5\%. One reason for applying it is that we have less visually incompatible spacing, especially when we have underfull or cramped lines. For each (broken) line the badness is reconsidered with either shrink or stretch applied to all characters in that line. So, in the worst case a shrunken line is followed by a stretched one and that can be visible when the scaling factors are chosen wrong. As with protrusion, the solution space is larger but so are the constraints. But contrary to protrusion here the look and feel of the whole line can be made better but at the cost of much more runtime and larger (\PDF) files. \startsubsubject[title=protrusion classes] The amount of expansion depends in the shape of the character. Vertical strokes are more sensitive for expansion then horizontal ones. So an \quote {o} can get a different scaling than an \quote {m}. As with protrusion we have collected the properties in classes: \showexpansionclass The smaller the step, the more instances of a font we get, the better it looks, and the larger the files become. it's best not to use too many stretch and shrink steps. A stretch of 2 and shrink of 2 and step of .25 results in upto 8~instances plus the regular sized one. \stopsubsubject \startsubsubject[title=expansion vectors] We only have one vector: \type {quality}: \showexpansionvector[name=quality] \stopsubsubject \startsubsubject[title=an example of expansion] We use \type {zapf.tex} as example text, if only because Hermann Zapf introduced this optimization. Keep in mind that you can combine expansion and protrusion. \startbuffer[define] \definefontfeature [whatever] [default] [expansion=quality] \definefont[MyTestA][Serif*default at 10pt] \definefont[MyTestB][Serif*whatever at 10pt] \stopbuffer \startbuffer[example] \startoverlay {\ruledvbox \bgroup \hsize\textwidth \MyTestA \setupalign[normal] \input{tufte} \egroup} {\ruledvbox \bgroup \hsize\textwidth \MyTestB \setupalign[hz,normal] \maincolor \input{tufte} \egroup} \stopoverlay \stopbuffer \typebuffer[define] \getbuffer [define] We use the following example. The results are shown in \in {figure} [expansion:quality]. The colored text is the protruding one. \typebuffer[example] \startplacefigure[reference=expansion:quality,title=The difference between no expansion and quality expansion.] \getbuffer [example] \stopplacefigure You can see what happens in \in {figure} [expansion:visualized]. \startbuffer[example] \setupalign[hz] \enabletrackers[*expansion*] \definefontfeature[boundingbox][boundingbox={frame,empty}] \definedfont[Serif*default,quality,boundingbox @ 12.1pt] \samplefile{sapolsky}\par \disabletrackers[*expansion*] \stopbuffer \typebuffer[example] \startplacefigure[reference=expansion:visualized,title=The injected expansion kerns.] \getbuffer [example] \stopplacefigure \stopsubsubject \startsubsubject[title=Expansion and kerning] When we expand glyphs we also need to look at the font kerns between them. In the original implementation taken from \PDFTEX\ expansion was implemented using pseudo fonts (with expanded glyph widths) and expansion of inter|-|character kerns was based on font information. In \LUATEX\ we have expansion factors in glyph nodes instead which is more efficient and gives a cleaner separation between front- and backend as the backend has no need to consult the font. For the font kerns we set the kern compensation directly and for that we use the average expansion factors of the neighbouring fonts so technically we support kerns between different fonts). This also has the advantage that kerns injected in node mode are treated well, given that they are tagged as font kern. So what is the effect (and need) of scaling font kerns? Let's look at an example. Kerns can be positive but also negative: \startlinecorrection \startcombination {\vbox { \forgetall \hpack to 3cm{\hss\ruledhbox{\maincolor V\kern-1ptA}\hss} \hpack to 3cm{\hss\ruledhbox{\maincolor V\kern 0ptA}\hss} }} {negative} {\vbox { \forgetall \hpack to 3cm{\hss\ruledhbox{\maincolor I\kern.25ptI}\hss} \hpack to 3cm{\hss\ruledhbox{\maincolor I\kern 0ptI}\hss} }} {positive} \stopcombination \stoplinecorrection If we use a rediculous amount of stretch we get the following. In the top line we scale the kern, in the bottom line we don't. \startlinecorrection \startcombination {\vbox { \definedfont[file:texgyrepagella-regular.otf at 12pt]% \forgetall \hpack to 3cm{\maincolor \hss\strut \scale[xscale=5000]{V}\kern-5pt\scale[xscale=5000]{A}\hss} \hpack to 3cm{\maincolor \hss\strut \scale[xscale=5000]{V}\kern-1pt\scale[xscale=5000]{A}\hss} }} {negative} {\vbox { \definedfont[file:texgyrepagella-regular.otf at 12pt]% \forgetall \hpack to 3cm{\maincolor \hss\strut \scale[xscale=5000]{I}\kern1.25pt\scale[xscale=5000]{I}\hss} \hpack to 3cm{\maincolor \hss\strut \scale[xscale=5000]{I}\kern0.25pt\scale[xscale=5000]{I}\hss} }} {positive} \stopcombination \stoplinecorrection The reason that we mention this is that when we apply \OPENTYPE\ features, positioning not necessarily result in font kerns. For instance ligatures can be the result of careful applied kerns and in some scripts kerns are used to connect glyphs. This means that we best cannot expand kerns by default. How bad is that? By looking at the examples above one would say \quotation {real bad}. But say that we have about 1pt of font kerns, then a 5\% expansion (which is already a lot) amounts to 0.05pt so to \blackrule [width=1pt, height=max, depth=max] we add \blackrule [width=.05pt, height=max, depth=max] which is so little that it probably goes unnoticed. Even if we use extreme kerns, as between VA, in practice the small amount of stretch or shrink added to a font kern goes unnoticed. In \in {figure} [hz:natural] we have overlayed the different strategies. The sample and width is chosen such that we see something. On a display you can scale up these examples and inspect if there is really something to see, but on paper zooming in helps, as in \in {figure} [hz:zoomed]. Even then the effect of expanded kerns is invisible. The used definitions are: \definecolor[hz:test:tr][r=1,a=1,t=.5] \definecolor[hz:test:tg][g=1,a=1,t=.5] \definecolor[hz:test:tb][b=1,a=1,t=.5] \startbuffer \setupfontexpansion [extremehz] [stretch=5,shrink=5,step=.5,vector=default,factor=1] \setupfontexpansion [regularhz] [stretch=2,shrink=2,step=.5,vector=default,factor=1] \setupfontexpansion [minimalhz] [stretch=2,shrink=2,step=.5,vector=default,factor=.5] \definefontfeature [extremehz] [default] [mode=node,expansion=extremehz] \definefontfeature [regularhz] [default] [mode=node,expansion=regularhz] \definefontfeature [minimalhz] [default] [mode=node,expansion=minimalhz] \definefont [ExtremeHzFont] [file:texgyrepagella-regular.otf*extremehz at 10pt] \definefont [RegularHzFont] [file:texgyrepagella-regular.otf*regularhz at 10pt] \definefont [MinimalHzFont] [file:texgyrepagella-regular.otf*minimalhz at 10pt] \stopbuffer \typebuffer \getbuffer \edef\HzSampleText{\cldloadfile{ward}} \def\NoHzSample {\vbox{\hsize 10cm \color[hz:test:tr]{\setupalign [nohz]\HzSampleText\par}}} \def\HzSample {\vbox{\hsize 10cm \color[hz:test:tg]{\setupalign [hz]\HzSampleText\par}}} \def\FullHzSample{\vbox{\hsize 10cm \color[hz:test:tb]{\setupalign[fullhz]\HzSampleText\par}}} \startplacefigure[reference=hz:natural,title={The two expansion methods compared.}] \showfontkerns \dontcomplain \startcombination[1*3] {\ExtremeHzFont\ruledhpack{\startoverlay {\NoHzSample} {\HzSample } \stopoverlay}} {no hz \& hz} {\ExtremeHzFont\ruledhpack{\startoverlay {\NoHzSample} {\FullHzSample} \stopoverlay}} {no hz \& full hz} {\ExtremeHzFont\ruledhpack{\startoverlay {\HzSample } {\FullHzSample} \stopoverlay}} {hz \& full hz} \stopcombination \stopplacefigure \startplacefigure[reference=hz:zoomed,title={The two expansion methods compared (zoomed in).}] \showfontkerns \dontcomplain \startcombination[3*3] {\ExtremeHzFont \clip[nx=6,ny=5,x=2,y=2,sx=2]{\startoverlay {\NoHzSample} {\HzSample } \stopoverlay}} {extreme: no hz \& hz} {\ExtremeHzFont \clip[nx=6,ny=5,x=2,y=2,sx=2]{\startoverlay {\NoHzSample} {\FullHzSample} \stopoverlay}} {extreme: no hz \& full hz} {\ExtremeHzFont \clip[nx=6,ny=5,x=2,y=2,sx=2]{\startoverlay {\HzSample } {\FullHzSample} \stopoverlay}} {extreme: hz \& full hz} {\RegularHzFont \clip[nx=6,ny=5,x=2,y=2,sx=2]{\startoverlay {\NoHzSample} {\HzSample } \stopoverlay}} {regular: no hz \& hz} {\RegularHzFont \clip[nx=6,ny=5,x=2,y=2,sx=2]{\startoverlay {\NoHzSample} {\FullHzSample} \stopoverlay}} {regular: no hz \& full hz} {\RegularHzFont \clip[nx=6,ny=5,x=2,y=2,sx=2]{\startoverlay {\HzSample } {\FullHzSample} \stopoverlay}} {regular: hz \& full hz} {\MinimalHzFont \clip[nx=6,ny=5,x=2,y=2,sx=2]{\startoverlay {\NoHzSample} {\HzSample } \stopoverlay}} {minimal: no hz \& hz} {\MinimalHzFont \clip[nx=6,ny=5,x=2,y=2,sx=2]{\startoverlay {\NoHzSample} {\FullHzSample} \stopoverlay}} {minimal: no hz \& full hz} {\MinimalHzFont \clip[nx=6,ny=5,x=2,y=2,sx=2]{\startoverlay {\HzSample } {\FullHzSample} \stopoverlay}} {minimal: hz \& full hz} \stopcombination \stopplacefigure In \CONTEXT\ the \type {hz} alignment option only enables expansion of glyphs, while \type {fullhz} also applies it to kerns. It will be clear that you can just stick to using the \type {hz} directive (if you want expansion at all) because this directive is normally disabled and because most fonts are processed in node mode. \stopsubsubject \stopsection \startsection[title=Composing] This feature is seldom needed but can come in handy for old fonts or when some special language is to be supported. When writing this section I tested this feature with Dejavu and only two additional characters were added: \definefontfeature [default-plus-compose] [compose=yes] \definefont [MyComposedSerif] [Serif*default-plus-compose] % we need to cheat a bit as we don't have the main character in mono \startlines \MyComposedSerif \type{fonts > combining > }\hbox to .5em{\hss Ѷ\hss}\type{ (U+00476) = }\hbox to .5em{\hss Ѵ\hss}\type{ (U+00474) + ̏ (U+0030F)} \type{fonts > combining > }\hbox to .5em{\hss ѷ\hss}\type{ (U+00477) = }\hbox to .5em{\hss ѵ\hss}\type{ (U+00475) + ̏ (U+0030F)} \stoplines This trace showed up after giving: \starttyping \enabletrackers [fonts.composing.define] \definefontfeature [default-plus-compose] [compose=yes] \definefont [MyFont] [Serif*default-plus-compose] \stoptyping Fonts like Latin Modern have lots of glyphs but still lack some. Although the composer can add some of the missing, some of those new virtual glyphs probably will never look real good. For instance, putting additional accents on top of already accented uppercase characters will fail when that character has a rather tight (or even clipped) boundingbox in order not to spoil the lineheight. You can get some more insight in the process by turning on tracing: \starttyping \enabletrackers[fonts.composing.visualize] \stoptyping One reason why composing can be suboptimal is that it uses the boundingbox of the characters that are combined. If you really depend on a specific font and need some of the missing characters it makes sense to spend some time on optimizing the rendering. This can be done via the goodies mechanism. As an example we've added \type {lm-compose-test.lfg} to the distribution. First we show how it looks at the \TEX\ end: \startbuffer \enabletrackers[fonts.composing.visualize] \definefontfeature [default-plus-compose] [compose=yes] \loadfontgoodies [lm-compose-test] % playground \definefont [MyComposedSerif] [file:lmroman10regular*default-plus-compose at 48pt] \stopbuffer \typebuffer \getbuffer \blank \backgroundline [halfcolor] {\MyComposedSerif B\quad\char"1E02\quad\char"1E04} \blank The positions of the dot accents on top and below the capital B is defined in a goodie file: \starttyping return { name = "lm-compose-test", version = "1.00", comment = "Goodies that demonstrate composition.", author = "Hans and Mojca", copyright = "ConTeXt development team", compositions = { ["lmroman12-regular"] = compose, } } \stoptyping As this is an experimental feature there are several ways to deal with this. For instance: \starttyping local defaultfraction = 10.0 local compose = { dy = defaultfraction, [0x1E02] = { -- B dot above dy = 150 }, [0x1E04] = { -- B dot below dy = 150 }, } \stoptyping Here the fraction is relative to the difference between the height of the accentee and the accent. A better solution is the following: \starttyping local compose = { [0x1E02] = { -- B dot above anchored = "top", }, [0x1E04] = { -- B dot below anchored = "bottom", }, [0x0042] = { -- B anchors = { top = { x = 300, y = 700, }, bottom = { x = 300, y = -30, }, }, }, [0x0307] = { anchors = { top = { x = -250, y = 550, }, }, }, [0x0323] = { anchors = { bottom = { x = -250, y = -80, }, }, }, } \stoptyping This approach is more or less the same as \OPENTYPE\ anchoring. It takes a bit more effort to define these tables but the result is better. \stopsection \startsection[title=Kerning] Inter|-|character kerning is not supported at the font level and with good reason. The fact that something is conceptually possible doesn't mean that we should use or support it. Normally proper kerning (or the lack of it) is part of a font design and for some scripts different kerning is not even an option. On the average \TEX\ does a proper job on justification but not all programs are that capable. As a consequence designers (at least we ran into it) tend to stick to flush left rendering because they don't trust their system to do a proper job otherwise. On the other hand they seem to have no problem with messing up the inter|-|character spacing and even combine that with excessive inter|-|word spacing {\em if} they want to achieve justification (without hyphenation). And it can become even worse when extreme glyph expansion (like hz) is applied. Anyhow, it will be clear that consider messing with properties like kerning that are part of the font design is to be done careful. \definecharacterkerning [extremekerning] [factor=.125] \start \setcharacterkerning[extremekerning] For running text additional kerning makes no sense. It not only looks bad, it also spoils the grayness of a text. When it is applied we need to deal with special cases. For instance ligatures make no sense so they should be disabled. Additional kerning should relate to already present kerning and interword spacing should be adapted accordingly. Embedded non|-|characters also need to be treated well. \par \stop This paragraph was typeset as follows: \starttyping \definecharacterkerning [extremekerning] [factor=.125] \setcharacterkerning[extremekerning] ... text ... \stoptyping Where additional kerning can make sense, is in titles. The previous command can do that job. In addition we have a mechanism that fills a given space. This mechanism uses the following definition: \starttyping \setupcharacterkerning [stretched] [factor=max, width=\availablehsize] \stoptyping \startbuffer \stretched{\bfd to the limit} \stopbuffer \typebuffer \blank \start \color[maincolor]{\getbuffer} \stop \blank The following does not work: \startbuffer \ruledhbox to 5cm{\stretched{\bfd to the limit}} \stopbuffer \typebuffer \blank \start \color[maincolor]{\getbuffer} \stop \blank But this works ok: \startbuffer \setupcharacterkerning [stretched] [width=] \stretched{\bfd to the limit} \stopbuffer \typebuffer \blank \start \color[maincolor]{\getbuffer} \stop \blank You can also say this: \startbuffer \stretched[width=]{\bfd to the limit} \stopbuffer \typebuffer \blank \start \color[maincolor]{\getbuffer} \stop \blank or: \startbuffer \ruledhbox{\stretched[width=10cm]{\bfd to the limit}} \stopbuffer \typebuffer \blank \start \color[maincolor]{\getbuffer} \stop \blank You can get some insight in what kerning does to your font by the following command: \startbuffer \usemodule[typesetting-kerning] \starttext \showcharacterkerningsteps [style=Bold, sample=how to violate a proper font design, text=rubish, first=0, last=45, step=5] \stoptext \stopbuffer \typebuffer \blank \getbuffer \blank \stopsection \startsection[title=Extra font kerns] Fonts are processed independent of each other. Sometimes that is unfortunate for kerning, although in practice it won't happen that often. We can enable an additional kerning mechanism to deal with these cases. The \type {\setextrafontkerns} command takes one argument between square brackets. The effect can be seen below: \startbuffer VA {\smallcaps va} V{\smallcaps a} VA {\bf VA} V{\bf A} {\bf V}A V{\it A} \stopbuffer \starttabulate[|Tl|l|p|] \HL \BC key \BC result \BC logic \NC \NR \HL \NC no kerns \NC \showfontkerns\setextrafontkerns[reset]\subff{f:kern}\inlinebuffer \NC no kerns at all \NC \NR \NC kerns \NC \showfontkerns\setextrafontkerns[reset]\inlinebuffer \NC kerns within a font (feature) run \NC \NR \HL \NC none \NC \showfontkerns\setextrafontkerns [none]\inlinebuffer \NC only extra kerns within fonts \NC \NR \NC min \NC \showfontkerns\setextrafontkerns [min]\inlinebuffer \NC minimal kerns within and across fonts \NC \NR \NC max \NC \showfontkerns\setextrafontkerns [max]\inlinebuffer \NC maximum kerns within and across fonts \NC \NR \NC mixed \NC \showfontkerns\setextrafontkerns[mixed]\inlinebuffer \NC averaged kerns within and across fonts \NC \NR \HL \stoptabulate The content is defined as: \typebuffer This mechanism obeys grouping so you have complete control over where and when it gets applied. The \type {\showfontkerns} command can be used to trace the injection of (font) kerns. \stopsection \startsection[title=Ligatures] For some Latin fonts ligature building is quite advanced, take Unifraktur. I have no problem admitting that I find fraktur hard to read, but this one actually is sort of an exception. It's also a good candidate for a screen presentation where you mainly made notes for yourself: no one has to read it, but it looks great, especially if you consider it to be drawn by a pen. Anyway, we will use the following code as example (based on some remarks on the fonts website). \startbuffer[sample] sitzen / ſitzen / effe fietsen / ch ck ſt tz ſi fi \stopbuffer \typebuffer[sample] Some ligatures are implemented in the usual way, using the \type {liga} and \type {dlig} features, others kick in thanks to \type {ccmp}. This fact alone is an illustration that the low level \OPENTYPE\ ligature feature is not related to ligatures at all but a more generic mechanism: you can basically combine multiple shapes into one in all features exposed to the user. We define a bunch of specific feature sets: \startbuffer \definefontfeature [unifraktur-a] [default] \definefontfeature [unifraktur-b] [default] [goodies=unifraktur,keepligatures=yes] \definefontfeature [unifraktur-c] [default] [ccmp=yes] \definefontfeature [unifraktur-d] [default] [ccmp=yes,goodies=unifraktur,keepligatures=yes] \definefontfeature [unifraktur-e] [default] [liga=no,rlig=no,clig=no,dlig=no,ccmp=yes,keepligatures=auto] \stopbuffer \getbuffer \typebuffer and also some fonts: \startbuffer \definefont[TestA][UnifrakturCook*unifraktur-a sa 0.9] \definefont[TestB][UnifrakturCook*unifraktur-b sa 0.9] \definefont[TestC][UnifrakturCook*unifraktur-c sa 0.9] \definefont[TestD][UnifrakturCook*unifraktur-d sa 0.9] \definefont[TestE][UnifrakturCook*unifraktur-e sa 0.9] \stopbuffer \getbuffer \typebuffer We show these five alternatives here: \starttabulate[|T||] \NC liga \NC \TestA\getbuffer[sample] \NC \NR \NC liga + keepligatures \NC \TestB\getbuffer[sample] \NC \NR \NC liga + ccmp \NC \TestC\getbuffer[sample] \NC \NR \NC liga + ccmp + keepligatures \NC \TestD\getbuffer[sample] \NC \NR \NC ccmp + keepligatures \NC \TestE\getbuffer[sample] \NC \NR \stoptabulate The real fun starts when we want to add extra spacing between characters. Some ligatures need to get broken and some kept. \startbuffer \setupcharacterkerning[kerncharacters][factor=0.5] \setupcharacterkerning[letterspacing] [factor=0.5] \stopbuffer \getbuffer \typebuffer \enabletrackers[typesetters.kerns.ligatures] Next we will see how ligatures behave depending on how the mechanisms are set up. The colors indicate what trickery is used: \starttabulate[|T||] \NC \color[darkred] {red} \NC kept by dynamic feature \NC \NR \NC \color[darkgreen]{green} \NC kept by static feature \NC \NR \NC \color[darkblue] {blue} \NC keep by goodie \NC \NR \stoptabulate First we use \type {\kerncharacters}: \starttabulate[|T||] \NC liga \NC \kerncharacters {\TestA\getbuffer[sample]} \NC \NR \NC liga + keepligatures \NC \kerncharacters {\TestB\getbuffer[sample]} \NC \NR \NC liga + ccmp \NC \kerncharacters {\TestC\getbuffer[sample]} \NC \NR \NC liga + ccmp + keepligatures \NC \kerncharacters {\TestD\getbuffer[sample]} \NC \NR \NC ccmp + keepligatures \NC \kerncharacters {\TestE\getbuffer[sample]} \NC \NR \stoptabulate In the next example we use \type {\letterspacing}: \starttabulate[|T||] \NC liga \NC \letterspacing {\TestA\getbuffer[sample]} \NC \NR \NC liga + keepligatures \NC \letterspacing {\TestB\getbuffer[sample]} \NC \NR \NC liga + ccmp \NC \letterspacing {\TestC\getbuffer[sample]} \NC \NR \NC liga + ccmp + keepligatures \NC \letterspacing {\TestD\getbuffer[sample]} \NC \NR \NC ccmp + keepligatures \NC \letterspacing {\TestE\getbuffer[sample]} \NC \NR \stoptabulate \disabletrackers[typesetters.kerns.ligatures] The difference is that the letterspacing variant dynamically adds the predefined featureset \type {letterspacing} which is defined in a similar way as \type {unifraktur-e}. In the case of this font, this variant is the better one to use. In fact, this variant probably works okay with most fonts. However, by not hard coding this behaviour we keep control, as one never knows what the demands are. When no features are used, information from the (given) goodie file \type {unifraktur.lfg} is consulted: \starttyping letterspacing = { -- watch it: zwnj's are used (in the tounicodes too) keptligatures = { ["c_afii301_k.ccmp"] = true, -- ck ["c_afii301_h.ccmp"] = true, -- ch ["t_afii301_z.ccmp"] = true, -- tz ["uniFB05"] = true, -- ſt }, } \stoptyping These kick in when we don't disable ligatures by setting features (case~e). There are two pseudo features that can help us out when a font doesn't provide the wanted ligatures but has the right glyphs for building them. The \UNICODE\ database has some information about how characters can be (de)composed and we can use that information to create virtual glyphs: \starttyping \definefontfeature [default] [default] [char-ligatures=yes,mode=node] \stoptyping and: \starttyping \definefontfeature [default] [default] [compat-ligatures=yes,mode=node] \stoptyping This feature was added after some discussion on the \CONTEXT\ mailing list about the following use case. \startbuffer \definefontfeature [default-l] [default] [char-ligatures=yes, compat-ligatures=yes, mode=node] \definefont[LigCd][cambria*default] \definefont[LigPd][texgyrepagellaregular*default] \definefont[LigCl][cambria*default-l] \definefont[LigPl][texgyrepagellaregular*default-l] \stopbuffer \typebuffer \getbuffer These definitions result in: \starttabulate[|l|l|l|l|l|] \NC \NC \type {\LigCd} \NC \type {\LigPd} \NC \type {\LigCl} \NC \type {\LigPl} \NC \NR \NC \type{PEL·LÍCULES} \NC \LigCd PEL·LÍCULES \NC \LigPd PEL·LÍCULES \NC \LigCl PEL·LÍCULES \NC \LigPl PEL·LÍCULES \NC \NR \NC \type{pel·lícules} \NC \LigCd pel·lícules \NC \LigPd pel·lícules \NC \LigCl pel·lícules \NC \LigPl pel·lícules \NC \NR \NC \type{PEĿLÍCULES} \NC \LigCd PEĿLÍCULES \NC \LigPd PEĿLÍCULES \NC \LigCl PEĿLÍCULES \NC \LigPl PEĿLÍCULES \NC \NR \NC \type{peŀlícules} \NC \LigCd peŀlícules \NC \LigPd peŀlícules \NC \LigCl peŀlícules \NC \LigPl peŀlícules \NC \NR \stoptabulate Of course one can wonder is this is the right approach and if it's not better to use a font that provides the needed characters in the first place. \stopsection \startsection[title=New features] \startsubsection[title=Substitution] It is possible to add new features via \LUA. Here is an example of a single substitution: \startbuffer \startluacode fonts.handlers.otf.addfeature { name = "stest", type = "substitution", data = { a = "X", b = "P", } } \stopluacode \stopbuffer \typebuffer \getbuffer We show an overview at the end of this section, but here is a simple example already. You need to define the feature before defining a font because otherwise the font will not know about it. \startbuffer \definefontfeature[stest][stest=yes] \definedfont[file:dejavu-serifbold.ttf*default] abracadabra: \addff{stest}abracadabra \stopbuffer \typebuffer \start \blank \maincolor \getbuffer \blank \stop Instead of (more readable) glyph names you can also give \UNICODE\ numbers: \starttyping \startluacode fonts.handlers.otf.addfeature { name = "stest", type = "substitution", data = { [0x61] = 0x58 [0x62] = 0x50 } } \stopluacode \stoptyping The definition is quite simple: we just map glyph names (or unicodes) onto other ones. An alternate is also possible: \startbuffer \startluacode fonts.handlers.otf.addfeature { name = "atest", type = "alternate", data = { a = { "X", "Y" }, b = { "P", "Q" }, } } \stopluacode \stopbuffer \typebuffer \getbuffer Less useful is a multiple substitution. Normally this one is part of a chain of replacements. \startbuffer \startluacode fonts.handlers.otf.addfeature { name = "mtest", type = "multiple", data = { a = { "X", "Y" }, b = { "P", "Q" }, } } \stopluacode \stopbuffer \typebuffer \getbuffer A ligature (or multiple to one) is also possible but normally only makes sense when there is indeed a ligature. We use a similar definition for mapping the \TEX\ input sequence \type {---} onto an \emdash. \startbuffer \startluacode fonts.handlers.otf.addfeature { name = "ltest", type = "ligature", data = { ['1'] = { "a", "b" }, ['2'] = { "d", "a" }, } } \stopluacode \stopbuffer \typebuffer \getbuffer \stopsubsection \startsubsection[title=Positioning] You can define a kern feature too but when doing so you need to use measures in font units. \startbuffer \startluacode fonts.handlers.otf.addfeature { name = "ktest", type = "kern", data = { a = { b = -500 }, } } \stopluacode \stopbuffer \typebuffer \getbuffer Pairwise positioning is more complex and involves two (optional) arrays that specify \type {{dx dy wd ht}} for each of the two glyphs. In the next example we only displace the second glyph. \startbuffer \startluacode fonts.handlers.otf.addfeature { name = "ptest", type = "pair", data = { ["a"] = { ["b"] = { false, { -1000, 1200, 0, 0 } } }, } } \stopluacode \stopbuffer \typebuffer \getbuffer Of course you need to know a bit about the metrics of the glyphs involved so in practice this boils down to trial and error. A single character (glyph) can also be tweaked, although normally this is done better in a manipulator when loading the font. Anyway: \startbuffer \startluacode fonts.handlers.otf.addfeature { name = "stest", type = "single", data = { a = { -30, 0, -50, 0 }, } } \stopluacode \stopbuffer \typebuffer \getbuffer This will reduce the left and right edges and make the glyph a pretty tight one. The values are for Latin Modern. \stopsubsection \startsubsection[title=Examples] We didn't show usage yet. This is because we need to define a feature before we define a font. New features will be added to a font when it gets defined. \startbuffer \definefontfeature[stest][stest=yes] \definefontfeature[atest][atest=2] \definefontfeature[mtest][mtest=yes] \definefontfeature[ltest][ltest=yes] \definefontfeature[ktest][ktest=yes] \definefontfeature[ptest][ptest=yes] \definefontfeature[ctest][ctest=yes] \definedfont[file:dejavu-serif.ttf*default] \starttabulate[|l|l|l|] \NC operation \NC feature \NC abracadabra \NC \NR \HL \NC substitution \NC \type {stest} \NC \addff{stest}abracadabra \NC \NR \NC alternate \NC \type {atest} \NC \addff{atest}abracadabra \NC \NR \NC multiple \NC \type {mtest} \NC \addff{mtest}abracadabra \NC \NR \NC ligature \NC \type {ltest} \NC \addff{ltest}abracadabra \NC \NR \NC kern \NC \type {ktest} \NC \addff{ktest}abracadabra \NC \NR \NC pair \NC \type {ptest} \NC \addff{ptest}abracadabra \NC \NR \NC chain sub \NC \type {ctest} \NC \addff{ctest}abracadabra \NC \NR \stoptabulate \stopbuffer \typebuffer \getbuffer \stopsubsection \startsubsection[title=Contexts] A more complex substitution is the following: \startbuffer \startluacode fonts.handlers.otf.addfeature { name = "ytest", type = "chainsubstitution", lookups = { { type = "substitution", data = { ["b"] = "B", ["c"] = "C", }, }, }, data = { rules = { { before = { { "a" } }, current = { { "b", "c" } }, lookups = { 1 }, }, }, }, } \stopluacode \stopbuffer \typebuffer \getbuffer Here the dataset is a sequence of rules. There can be a \type {before}, \type {current} and \type {after} match. The replacements are specified with the \type {lookups} entry and the numbers are indices in the provided \type {lookups} table. Here is another example. This one demonstrates that one can check against spaces (some fonts kerns against them) and against boundaries as well. The later is something \CONTEXT\ specific. First we define a feature that create ligatures but only when we touch a space: \startbuffer \startluacode fonts.handlers.otf.addfeature { name = "test-a", type = "chainsubstitution", lookups = { { type = "ligature", data = { ['1'] = { "a", "b" }, ['2'] = { "c", "d" }, }, }, }, data = { rules = { { before = { { " " } }, current = { { "a" }, { "b" } }, lookups = { 1 }, }, { current = { { "c" }, { "d" } }, after = { { " " } }, lookups = { 1 }, }, }, }, } \stopluacode \stopbuffer \typebuffer \getbuffer The next example also checks against whatever boundary we have. \startbuffer \startluacode fonts.handlers.otf.addfeature { name = "test-b", type = "chainsubstitution", lookups = { { type = "ligature", data = { ['1'] = { "a", "b" }, ['2'] = { "c", "d" }, }, }, }, data = { rules = { { before = { { " ", 0xFFFC } }, current = { { "a" }, { "b" } }, lookups = { 1 }, }, { current = { { "c" }, { "d" } }, after = { { 0xFFFC, " " } }, lookups = { 1 }, }, }, }, } \stopluacode \stopbuffer \typebuffer \getbuffer We can actually simplify this one to: \startbuffer \startluacode fonts.handlers.otf.addfeature { name = "test-c", type = "chainsubstitution", lookups = { { type = "ligature", data = { ['1'] = { "a", "b" }, ['2'] = { "c", "d" }, }, }, }, data = { rules = { { before = { { 0xFFFC } }, current = { { "a" }, { "b" } }, lookups = { 1 }, }, { current = { { "c" }, { "d" } }, after = { { 0xFFFC } }, lookups = { 1 }, }, }, }, } \stopluacode \stopbuffer \typebuffer \getbuffer As a bonus we show how to do more complex things: \startbuffer \startluacode fonts.handlers.otf.addfeature { name = "test-d", type = "chainsubstitution", lookups = { { type = "substitution", data = { ["a"] = "A", ["b"] = "B", ["c"] = "C", ["d"] = "D", }, }, { type = "ligature", data = { ['1'] = { "a", "b" }, ['2'] = { "c", "d" }, }, }, }, data = { rules = { { before = { { 0xFFFC } }, current = { { "a" }, { "b" } }, lookups = { 2 }, }, { current = { { "c" }, { "d" } }, after = { { 0xFFFC } }, lookups = { 2 }, }, { current = { { "a" } }, after = { { "b" } }, lookups = { 1 }, }, { current = { { "c" } }, after = { { "d" } }, lookups = { 1 }, }, }, }, } \stopluacode \stopbuffer \typebuffer \getbuffer \definefontfeature[test-a][test-a=yes] \definefontfeature[test-b][test-b=yes] \definefontfeature[test-c][test-c=yes] \definefontfeature[test-d][test-d=yes] \startbuffer abababcdcd abababcdcd abababcdcd \stopbuffer With the test text: \typebuffer These four result in: \blank \start \definedfont[file:dejavu-serif.ttf*default] \start \addff{test-a} \getbuffer \stop\par \start \addff{test-b} \getbuffer \stop\par \start \addff{test-c} \getbuffer \stop\par \start \addff{test-d} \getbuffer \stop\par \stop \blank \stopsubsection \startsubsection[title={Language dependencies}] When \OPENTYPE\ was not around we only had to deal with ligatures, smallcaps and oldstyle and of course kerns. Their number was so small that the term \quote {features} was not even used. In practice one just loaded a font that had oldstyle or smallcaps or none of that and was done. There were different fonts and sold separately. In \OPENTYPE\ we have more variation and although these fonts can be much more advanced the lack of standardization (for instance what gets initialized, or what shapes are in the default slots) can lead to messy setups. Some fonts bind features to scripts, some don't, which means that: \starttyping \definefontfeature[smallcaps][smcp=yes,script=dflt] \definefontfeature[smallcaps][smcp=yes,script=latn] \definefontfeature[smallcaps][smcp=yes,script=cyrl] \stoptyping are in fact different and you don't know in advance if you need to specify \type {dflt} or \type {latn}. In practice for a feature like smallcaps there is no difference between languages, but for ligatures there can be. When we extend an existing feature we can think of: \starttyping \definefontfeature[smallcaps][default][smcp=yes,script=auto] \definefontfeature[smallcaps][default][smcp=yes,script=*] \stoptyping but that can have side effects too (for instance disabling language specific features). The easiest way to explore this language dependency is to make a feature of our own. \startbuffer \startluacode fonts.handlers.otf.addfeature { name = "simplify", type = "multiple", prepend = true, features = { ["*"] = { ["deu"] = true } }, data = { [utf.byte("ä")] = { "a", "e" }, [utf.byte("Ä")] = { "A", "E" }, [utf.byte("ü")] = { "u", "e" }, [utf.byte("Ü")] = { "U", "E" }, [utf.byte("ö")] = { "o", "e" }, [utf.byte("Ö")] = { "O", "E" }, [utf.byte("ß")] = { "s", "z" }, [utf.byte("ẞ")] = { "S", "Z" }, }, } \stopluacode \stopbuffer \typebuffer \getbuffer Here we implement a language specific feature that we use at the \TEX\ end: \startbuffer \definefontfeature [simplify-de] [simplify=yes, language=deu] \stopbuffer \typebuffer \getbuffer that we can use as: \startbuffer \definedfont[Serif*default,simplify-de]% äüöß {\de äüöß} {\nl äüöß} \stopbuffer \typebuffer and get: \start \maincolor \inlinebuffer \stop, but as you see, both German and Dutch get the same treatment, which might not be what you want, because in Dutch the diearesis has a different meaning. \startbuffer \definedfont[Serif*default]% äüöß {\de\addff{simplify-de}äüöß} {\nl äüöß} \stopbuffer \typebuffer The above is restricts the usage so now we get: \start \maincolor \inlinebuffer \stop, which is more language bound. You don't need much imagination for extending this: \startbuffer \definefontfeature [simplify] [simplify=yes, language=deu] \stopbuffer \typebuffer \getbuffer \startbuffer \definedfont[Serif*default]% äüöß {\de\addff{simplify}äüöß} {\nl\addff{simplify}äüöß} \stopbuffer So what do we expect with the next? \typebuffer We get: \start \maincolor \inlinebuffer \stop, and we see that the language setting is not taken into account! This is because the font already has been set up with a script and language combination. The solution is to temporary set the font related language explicitly: \definefontfeature [simplify] [simplify=yes] \startbuffer \definedfont[Serif*default]% äüöß {\de\addfflanguage\addff{simplify}äüöß} {\nl\addfflanguage\addff{simplify}äüöß} \stopbuffer \typebuffer So we can automatically switch to language specific features if we want to: \start \maincolor \inlinebuffer \stop. Let's now move to another level of complexity: support for more than one language as in fact this example was made for Dutch in the first place, but the German outcome is a bit more visible. \startbuffer \startluacode fonts.handlers.otf.addfeature { name = "simplify", type = "multiple", prepend = true, -- prepend = "smcp", dataset = { { features = { ["*"] = { ["nld"] = true } }, data = { -- [utf.byte("ä")] = { "a" }, -- [utf.byte("Ä")] = { "A" }, -- [utf.byte("ü")] = { "u" }, -- [utf.byte("Ü")] = { "U" }, -- [utf.byte("ö")] = { "o" }, -- [utf.byte("Ö")] = { "O" }, [utf.byte("ij")] = { "i", "j" }, [utf.byte("IJ")] = { "I", "J" }, [utf.byte("æ")] = { "a", "e" }, [utf.byte("Æ")] = { "A", "E" }, }, }, { -- type = "multiple", -- local values possible features = { ["*"] = { ["deu"] = true } }, data = { [utf.byte("ä")] = { "a", "e" }, [utf.byte("Ä")] = { "A", "E" }, [utf.byte("ü")] = { "u", "e" }, [utf.byte("Ü")] = { "U", "E" }, [utf.byte("ö")] = { "o", "e" }, [utf.byte("Ö")] = { "O", "E" }, [utf.byte("ß")] = { "s", "z" }, [utf.byte("ẞ")] = { "S", "Z" }, }, } } } \stopluacode \stopbuffer \typebuffer \getbuffer For this we use the following example: \startbuffer \definedfont[Serif*default,simplify]% äüöß ijæ {\de\addfflanguage äüöß ijæ} {\nl\addfflanguage äüöß ijæ} \stopbuffer \typebuffer Because the Dutch is hard to check we use an \type {æ} replacement too and commented the similarities with German: \start \maincolor \inlinebuffer \stop. But still we're not done, say that we want smallcaps too: \startbuffer \definefontfeature[alwayssmcp][smcp=always]% \definedfont[Serif*default,simplify,alwayssmcp]% äüöß ijæ {\de\addfflanguage äüöß ijæ} {\nl\addfflanguage äüöß ijæ} \stopbuffer \typebuffer This comes out as: \start \maincolor \inlinebuffer \stop. The reason for specifying \type{smcp} as \type {always} is that otherwise we get language specific smallcaps while often they are not bound to a language but to the defaults. The good news is that we can do this automatically: \startbuffer \setupfonts[language=auto]% \definefontfeature[alwayssmcp][smcp=always]% \definedfont[Serif*default,simplify,alwayssmcp]% äüöß ijæ {\de äüöß ijæ} {\nl äüöß ijæ} \stopbuffer \typebuffer But be aware that this applies to all situations. Here we get: \start \maincolor \inlinebuffer \stop. \stopsubsection \startsubsection[title=Syntax summary] In the examples we have seen several ways to define features. One of the differences is that you either set a \type {data} field directly, or that you specify a dataset. The fields in a dataset entry overload the ones given at the top level or when not set the top level value will be taken. There is a bit of (downward compatibility) tolerance built in, but best not depend on that. \starttyping fonts.handlers.otf.addfeature { name = "demo", features = { [