diff options
25 files changed, 1574 insertions, 471 deletions
diff --git a/doc/context/documents/general/manuals/luametatex.pdf b/doc/context/documents/general/manuals/luametatex.pdf Binary files differindex 52c93219f..635e56000 100644 --- a/doc/context/documents/general/manuals/luametatex.pdf +++ b/doc/context/documents/general/manuals/luametatex.pdf diff --git a/doc/context/sources/general/manuals/followingup/followingup-fonts.tex b/doc/context/sources/general/manuals/followingup/followingup-fonts.tex index a3059847e..2f7320b12 100644 --- a/doc/context/sources/general/manuals/followingup/followingup-fonts.tex +++ b/doc/context/sources/general/manuals/followingup/followingup-fonts.tex @@ -6,104 +6,376 @@ \startchapter[title={Scaled fonts}] -Although \CONTEXT\ is quite efficient with fonts there is always room for -improvement. However, after years of fine tuning the font mechanisms there was -not that much room left. On the average, given that \TEX\ is mostly about fonts, -what we have is quite okay, but for some scripts, like \CJK\ the demands on -resources can be rather high. This made me think of a different approach to -scaling. Nowadays fonts seldom come in design sizes. Also, in \CONTEXT\ \MKIV\ -and therefore \LMTX\ we always had so called dynamic features: apply additional -features locally, although that comes with a small penalty in performance, it -saves additional font instances. It is a good approach for the occasional small -stretch of glyphs, like small capped logos and such. - -Using a font at a larger size means that we need to provide \TEX\ with the right -dimensions: it needs them for instance for for braking paragraph into lines or -wrapping text in boxes. The amount of information that \TEX\ needs is small: only -dimensions. Of course there are ligatures and kerns but in so called base mode we -seldom have many. The data needed for node mode (used for more complex \OPENTYPE\ -fonts) can be shared and lives at the \LUA\ end. But even then the character -table is copied and scaled. Actually, we seldom need that information but it is -good to have it and in the case of virtual fonts it is needed. But, when a font -is used at a different size, and al the features are the same, we can think of -a different approach. - -That approach is tagged as \quote {dynamic font scaling}, which means that we -don't need to define a new font instance when the same feature set is used. Or -course in addition to this features one can still use the dynamic features. This -means that for instance chapter titling can use the bodyfont instance and just -apply additional scaling. Although for a normal run the number of loaded fonts is -normally small, and the number of instances also isn't that impressive it can -happen in a large document that you end up with a few dozen. That number can now -be reduced to half a dozen. - -Of course there can be side effects, which is why it's currently tagged as -experimental. There is also a small performance hit because we now need to track -it but that is gained back because we load less fonts and have less glyph runs. - -So how does it work? Here is an example: +\startsection[title={History}] + +The infrastructure for fonts makes for a large part of the code of any \TEX\ +macro package. We have to go back in time to understand why. When \TEX\ showed +up, fonts were collections of bitmaps and measures. There were at most 256 glyphs +in a font and in order to do its job, \TEX\ needed to know (and still needs to +know) the width, height and depth of glyphs. If you want ligatures it also needs +to know how to construct them from the input and when you want kerning there has +to be additional information about what neighboring glyphs need a kern in +between. Math is yet another subtask that demands extra information, like chains +of glyphs that grow in size and if needed even recipes of how to construct large +shapes from smaller ones. + +Fonts come in sizes, and for instance Latin Modern has quite a few variants where +the shapes are adapted to the size. This means that when you need a 9pt regular +shape alongside a 12pt one, two fonts have to be loaded. This is quite visible in +math where we have three related sizes: text, script and scriptscript, grouped in +so called families. + +Plenty has been written (in various documents that come with \CONTEXT) about how +this all works together and how it impacts the design of the system, so here I +just give a short summary of what a font system has to deal with. + +\startitemize +\startitem + In a bodyfont setup different sizes (9pt, 10pt, 12pt) can have their own + specific set of fonts. This can result in quite some definitions that relate + to the style, like regular, bold, italic, bolditalic, slanted and + boldslanted, etc. When possible loading the fonts is delayed. +\stopitem +\startitem + Some font designs have different shapes per bodyfont size. A minor + complication is that when one is missing some heuristic best match choice + might be needed. +\stopitem +\startitem + Within a bodyfont size we distinguish size variants. We can go smaller (x and + xx), for instance when we use sub- and superscripts in text, or we can go + larger, for instance in titling (a, b, c, d, \unknown). Fortunately most of + the loading of these can be delayed too. +\stopitem +\startitem + When instances are not available, scaling can be used, as happens for + instance with 11pt in Computer Modern. Actually, this is why in \CONTEXT\ we + default to 12pt, because the scaled versions didn't look as nice as the + others. +\stopitem +\startitem + Special features, like smallcaps or oldstyle numerals, can demand their own + definitions. More loading and automatic definitions can be triggered by sizes + needed in for instance scripts and titling. +\stopitem +\startitem + A document can have a mixed setup, that is: use different font designs in one + document, so some kind of namespace subsystem is needed. +\stopitem +\startitem + In traditional eight bit engine the dependency of hyphenation on the encoding + of a font can result in the necessity to load a font multiple times in + different encodings, something that depends on the language mix used. +\stopitem +\startitem + In the more modern \OPENTYPE\ fonts combinations of features can demand + additional instances (one can thing of language|/|script combinations, + substitutions in base mode, special effects like boldening, color fonts, + etc.). +\stopitem +\startitem + Math is complicated by the fact that alphabets come from different fonts + (sometimes a font has several alphabets which means that some mapping can be + needed). Operating on the size, shape, encoding and style axes puts some + demands on the font system. Add to this (often) partial (due to lack of + fonts) bold support and it gets even more complicated. +\stopitem +\startitem + There is additional math auto|-|definition and loading code for the sizes + used in text scripts and titling. +\stopitem +\stopitemize + +All this has resulted in a pretty complex subsystem. Although going \OPENTYPE\ +(and emulated \OPENTYPE\ with \TYPEONE\ fonts as we do in \MKIV) removes some +complications, like encodings, it does also add complexity because of the many +possible font features, either or not dependent on script and language. + +Also, in order to let a font subsystem not impact performance to much, let alone +extensive memory usage, the \CONTEXT\ font subsystem is rather optimized. The +biggest burden comes from fonts that have a dynamic (adaptive) definition because +then we need to do quite a bit of testing per font switch, but even that has +always been rather fast. + +\stopsection + +\startsection[title={Reality}] + +In \MKIV\ and therefore also in \LMTX\ more font magic happens. The initial node +lists that makes up a box or paragraph can get manipulated in several ways and +often fonts are involved. The mentioned font features can be defines static (as +part of the definition) or dynamic (resolved on the spot at the cost of some +overhead). Characters can be remapped, fonts can be replaced. The math subsystem +in \MKIV\ was different right from the start: we use a limited number of families +(regular, bold, l2r and r2l), and stay abstract till the moment we need to deal +with the specific alphabets. But still, in \MKIV, we have the families with three +fonts. + +In the \LUAMETATEX\ manual we show some math magic and we do so for different +fonts. As a side effect, we set up half a dozen bodyfont collections. Even with +delayed and shared font loading, we end up with 158 instances but quite a bit of +them are math fonts, at least six per bodyfont size: regular and bold (boldened) +text, script and scriptscript. Of course most are just copies with different +scaling that reuse already loaded resources. + +If we look at the math fonts that we use today, there is however quite some +overlap. It starts with a text font. From that script and scriptscript variants +are derived, but often these variants use many text size related shapes too. Some +shapes get alternatives (from the \type {ssty} feature), and the whole clone get +scaled. But, much of the logic of for instance extensibles is the same. + +A similar situation happens with for instance large \CJK\ fonts: there are hardly +any advanced features involved there, so any size is basically a copy with scaled +dimensions, and these fonts can be real huge! + +Actually, when we talk about features, in many cases in \CONTEXT\ you don't +define them as part of the font. For instance small caps can best be triggered by +using a dynamic feature: applied to a specific stretch of text. When the font +handler does its work there are actually four cases: no features get applied, +something that for instance happens with most monospaced fonts, base mode is +used, which means that the \TEX\ machinery takes care of constructing ligatures +and injecting kerns, and node mode, where \LUA\ handles the features. The fourth +case is a special case of node mode where a different features set is applied. +\footnote {We also have so called plug mode where an external rendered can do the +work but that one is only around for some experiments during Idris' font +development.} At the cost of some extra overhead (for each node mode run) dynamic +features are quite powerful and save quite some memory and definitions. The +overhead comes from much more testing regarding the font we deal with because +suddenly the same font can demand different treatments, depending on what dynamic +features is active. + +Although the font handling is responsible for much of the time spent in \LUA, it is +still reasonable given what has to be done. Actually, because we have an extensible +system, it's often the extensions that takes additional runtime. Flexibility comes +at a price. + +\stopsection + +\startsection[title={Progress}] + +At some point I started playing with realtime glyph scaling. Here realtime means that +it doesn't depend on the font definition. To get an idea, here is an example: \startbuffer -\definescaledfont[MyLargerFontA][scale=2000,style=bold] - -test {\MyLargerFontA test} test +test {\glyphxscale 2500 test} test \stopbuffer \typebuffer -This gives: - \getbuffer -You can also say: +The glyphs in the current font get scaled horizontally without the need for an extra +font instance. Now, this kind of trickery puts some constraints on the font handling, +as is demonstrated in the next example. We use Latin Modern because that font has +all these ligatures: \startbuffer -\definescaledfont[MyLargerFontB][xscale=1200,yscale=2000,style=bold] - -test {\MyLargerFontB test} test +\definedfont[lmroman-regular*default]% +e{\glyphxscale 2500 ff}icient +ef{\glyphxscale 2500 f}icient +ef{\glyphxscale 2500 fi}cient +e{\glyphxscale 2500 ffi}cient \stopbuffer \typebuffer -Which scaled the over two axis: +{\getbuffer} + +So, in order to deal with this kind of scaling, we now operate not only on the +font (id) and dynamic feature axes, but also on the scales of which we have three +variants: glyph scale, glyph xscale and glyph y scale). There is actually also a +state dimension but we leave that for now. Think of flagging glyphs as initial or +final. This brings the number of axis to six. It is important to stress that in +these examples the same font instance is used! + +Just for the record: there are several approaches to switching fonts possible but +for now we stick to a simple font id switch plus glyph scale settings at the +\TEX\ end. A variant would be to introduce a new mechanism where id's and scales +go together but for now I see no real gain in that. + +\stopsection + +\startsection[title={Math}] + +Given what is written in the previous sections, a logical question is if we can +apply scaling to math and the answer is \quotation {yes, we can}. But we actually +go a bit further and that is partly due to some other properties of the engine. + +From \PDFTEX\ the \LUATEX\ engines inherited character protrusion and glyph +expansions, aka hz. However, where in \PDFTEX\ copies of the font are made that +carry the expanded dimensions, in \LUATEX\ at some point this was replaced by an +expansion field in the glyph and kern nodes. So, instead of changing the font id +of expanded glyphs, the same id is used but with the applied expansion factor set +in the glyph. A side effect was that in places where dimensions are needed, calls +to functions that calculate the expanded widths on request (as these can change +during linebreak calculations) in combination with accessing font dimensions +directly, we now always use accessor functions. This level of abstraction is even +more present in \LUAMETATEX. This means that we have an uniform interface to +fonts and as a side effect scaling is to be dealt with in only a few places in +the code. + +Now, in math we have a few more complications. First of all, we have three sizes +to consider and we also have lots of parameters that depend on the size. But, as +I wanted to be able to apply scaling to math, the whole machinery was also +abstracted in a way that, at the cost of some extra overhead, made it easier to +work with scaled glyph properties. This means that we can stick to loading only +one bodyfont size of math (note that each math family has three sizes, where the +script and script sizes can have different, fine tuned, shapes) and just scale +that on demand. + +Once all that was in place it was a logical next step to see if we could actually +stick to one instance. Because in \LUAMETATEX\ we try to load fonts efficiently +we store only the minimally needed information at the \TEX\ end. A font with no +math therefore has less data per glyph. Again, this brings some abstraction that +actually helped to implement the one instance mechanism. A math glyph has +optional lists of increasing sizes and vertical or horizontal extensibles. So +what got added was an optional chain of smaller sizes. If a character has 3 +different glyphs for the three sizes, the text glyph has a pointer to the script +glyph which in turn has a pointer to the scriptscript glyph. This means that when +the math engine needs a specific character at a given size (text, script, +scriptscript) we just follow that chain. + +In an \OPENTYPE\ math font the script and scriptscript sizes are specified as +percentages of the text size. So, when the dimensions of a glyph are needed, we +just scale on the fly. Again this adds some overhead but I'm pretty sure that no +user will notice this. + +So, to summarize: if we need a character at scriptscript size, we access the text +size glyph, check for a pointer to a script size, go there, and again check for a +smaller size. We just use what fits the bill. And, when we need dimensions we +just scale. In order to scale we need the relative size, so we need to set that +up when we load the font. Because in \CONTEXT\ we also can assemble a virtual +\OPENTYPE\ font from \TYPEONE\ fonts, it was actually that (old) compatibility +feature that took most time to adapt, not so much because it is complicates but +because in \LMTX\ we have to bypass some advanced loading mechanisms. + +The end result is that for math we now only need to define two fonts per bodyfont +setup: regular and bold at the natural scale (normally 10pt) and we share these +for all sizes. It is because of this and what we describe in the next section +that the 158 instances for the \LUAMETATEX\ manual can now be brought down to 30. + +\stopsection + +\startsection[title={Text}] + +Sharing instances in text mode is relatively simple, although we do have to keep +in mind that these are extra axis when dealing with font features: two +neighboring glyphs with the same font id and dynamics but with different scales +are effectively from different fonts. + +Another complication is that when we use font fallbacks (read: take missing +glyphs from another font) we no longer have a dedicated instance but use a shared +one. This in itself if not a problem but we do need to handle specified relative +scales. This was not that hard to patch in \CONTEXT\ \LMTX. + +We can enforce aggressive font sharing with: -\getbuffer +\starttyping +\enableexperiments[fonts.compact] +\stoptyping + +After that we often use less instances. Just to give an idea, on the \LUAMETATEX\ +manual we get these stats: + +\starttyping +290 pages, 10.8 sec, 292M lua, 99M tex, 158 instances +290 pages, 9.5 sec, 149M lua, 35M tex, 30 instances +\stoptyping -The low level implementation uses the two variables \type {\glyphxscale} and -\type {\glyphyscale} that take a number. As with other \TEX\ scaling related -variables, a value of 1000 represents 1.0 so we have three digits precision. +So, we win on all fronts when we use this glyph scale mechanism. The magic +primitive that deals with this is \type {\glyphscale} which accepts a number, +where \type {1200} and \type {1.2} both mean scaling to 20\percent\ more than +normal. But one can best not use this primitive directly. -Because we just scale, the next also works: +A specific font can be defined using the \type {\definefont} command. In \LMTX\ a +regular scaler can be followed by two scale factors. The nest example +demonstrates this: \startbuffer -test {\MyLargerFontB test {\em test} test} test +\definefont[FooA][Serif*default @ 12pt 1800 500] +\definefont[FooB][Serif*default @ 12pt 0.85 0.4] +\definefont[FooC][Serif*default @ 12pt] + +\definetweakedfont[runwider] [xscale=1.5] +\definetweakedfont[runtaller][yscale=2.5,xscale=.8,yoffset=-.2ex] + +{\FooA test test \runwider test test \runtaller test test}\par +{\FooB test test \runwider test test \runtaller test test}\par +{\FooC test test \runwider test test \runtaller test test}\par \stopbuffer \typebuffer -The nested emphasis still works: +We also use the new \type {\definetweakedfont} command here. This example not +only shows the two scales but also introduces the offset. \getbuffer -For now we only have a simple user interface but it might eventually be more -integrated. For instance, we can consider defining \type {\tfa} and friends this -way. +Here is another one: + +\startbuffer +\definetweakedfont[squeezed] [xscale=0.9] +\definetweakedfont[squeezed] [xscale=0.9] + +\startlines +$a = b^2 + \sqrt{c}$ +{\squeezed $a = b^2 + \sqrt{c}$} +\stoplines +\stopbuffer + +\typebuffer + +\getbuffer -In order to make it useful, math is also supported: +Watch this: \startbuffer -\definescaledfont[MyLargerFontC][xscale=1200,yscale=2000] +\startcombination[4*1] + {\bTABLE + \bTR \bTD foo \eTD \bTD[style=\squeezed] $x = 1$ \eTD \eTR + \bTR \bTD oof \eTD \bTD[style=\squeezed] $x = 2$ \eTD \eTR + \eTABLE} + {local} + {\bTABLE[style=squeezed] + \bTR \bTD $x = 1$ \eTD \bTD $x = 3$ \eTD \eTR + \bTR \bTD $x = 2$ \eTD \bTD $x = 4$ \eTD \eTR + \eTABLE} + {global} + {\bTABLE[style=\squeezed\squeezed] + \bTR \bTD $x = 1$ \eTD \bTD $x = 3$ \eTD \eTR + \bTR \bTD $x = 2$ \eTD \bTD $x = 4$ \eTD \eTR + \eTABLE} + {multiple} +\stopcombination +\stopbuffer -test {\MyLargerFontC test $\sqrt{x}$ test} test +\typebuffer + +\startlinecorrection +\getbuffer +\stoplinecorrection + +An additional style parameter is also honored: + +\startbuffer +\definetweakedfont[MyLargerFontA][scale=2000,style=bold] +test {\MyLargerFontA test} test \stopbuffer \typebuffer -gives: +This gives: \getbuffer -You can actually use negative values, as is demonstrated in the following +Just for the record: the Latin Modern fonts, when set up to use design sizes, will +still use the specific size related files. + +\stopsection + +\startsection[title={Hackery}] + +You can actually use negative scale values, as is demonstrated in the following code: \startbuffer @@ -131,45 +403,8 @@ gives: \getbuffer \stoplinecorrection -Because loading and switching fonts in \CONTEXT\ is rather efficient, the gain in -performance is less than you expect. After all, not all time is spent on fonts. Of -course for huge \CJK\ fonts it does make a difference. But, when we do a massive switch -to a different body font, we can save a little more. Think of definitions like: - -\starttyping -\definescaledfontbody[BigBodyFont] [1.200] -\definescaledfontbody[VeryBigBodyFont] [1.440] -\definescaledfont [MyLargerFont] [xscale=1.200,yscale=2.000] -\stoptyping - -And then: - -\starttyping -\setuphead[title][style=\MyLargerFont\bf] -\stoptyping - -The savings in runtime can be quite noticeable now, for instance this: - -\starttyping -\dorecurse {100} { - \starttitle[title={Test #1] - \samplefile{ward}\par - \start \bf - \samplefile{ward}\par - \stop - \start \BigBodyFont \bf - \samplefile{ward}\par - \stop - \stoptitle -} -\stoptyping - -Speeds up a bit for Pagella and even more when we use Cambria. Keep in mind that -that a body font switch also involves setting up some math. Anyway, as a proof of -concept it worked out well. - -Glyphs can have offsets and these are used for implementing \OPENTYPE\ features. However, -they are also available at the \TEX\ end. Take this +Glyphs can have offsets and these are used for implementing \OPENTYPE\ features. +However, they are also available at the \TEX\ end. Take this \startbuffer \ruledhbox{ @@ -225,57 +460,136 @@ Both these lines produce the same integer: \getbuffer \stoplines -When there is a fraction, the value is multiplied by 1000. - -So how should this be further integrated? If we drop design sizes at the file -definition level the whole font mechanism could be simplified a lot, but we cannot -do that. Another approach is to let users use the previously mentioned more low -level commands. A but of help can be provided with a command like - -\starttyping -\enableautoglyphscaling -\stoptyping - -which pays of when it is issues before \type {\starttext} and before a body fonts -gets defined because it does some overloading of defining commands. At the brink -of 2021 I'm not sure yet what is best. One reason is that a compatibility layer -is not really compatible because glyph scaling gives slightly different scaled in -practice (which relates to some old time accuracy hackery). This sounds worse -that it is because in practice scaling is done for sections heads and so, which -are less present and don't interfere with the running text. - -Let me stress once more: if you need this kind of scaling it pays off. If you -load multiple instances of large (e.g.\ \CJK) fonts, it makes sense too. But, for -instance on a document like the \LUAMETATEX\ manual it only saves 2 instances on -158, and those are the few bold fonts used for titling. - -That said, here is another example: +You can do strange things with these primitives but keep in mind that you can +also waste the defaults. -\startbuffer +\startbuffer[definition] \def\UnKernedTeX {T% {\glyph xoffset -.2ex yoffset -.4ex `E}% {\glyph xoffset -.4ex options "60 `X}} +\stopbuffer +\startbuffer[example] We use \UnKernedTeX\ and {\bf \UnKernedTeX} and {\bs \UnKernedTeX}: the slanted version could use some more left shifting of the E. \stopbuffer -\typebuffer +\typebuffer[definition,example] This gives is the \TEX\ logos (but of course we normally use the real ones instead). -\getbuffer +\startnarrower + \darkred \getbuffer[definition,example] +\stopnarrower + +Because offsets are (also) used for handling font features like mark and cursive +placement as well as special inter|-|character positioning, the above is +suboptimal. Here is a better alternative: + +\startbuffer[definition] +\def\UnKernedTeX + {T\glyph left .2ex raise -.4ex `E\glyph left .4ex `X\relax} +\stopbuffer + +\typebuffer[definition] + +The result is the same: + +\startnarrower + \darkgreen \getbuffer[definition,example] +\stopnarrower + +But anyway: don't over|-|do it. We could deal with such cases for decades without +these fancy new features. The next example show margins in action: + +\startlinecorrection +\bTABLE[align=middle,width=.33\textwidth] + \bTR + \bTD \showglyphs \multiply\glyphscale by 2 <{\darkgray \glyph `M}>\eTD + \bTD \showglyphs \multiply\glyphscale by 2 <{\darkgray \glyph raise 3pt `M}>\eTD + \bTD \showglyphs \multiply\glyphscale by 2 <{\darkgray \glyph raise -3pt `M}>\eTD + \eTR + \bTR[frame=off] + \bTD \tttf \eTD + \bTD \tttf raise 3pt \eTD + \bTD \tttf raise -3pt \eTD + \eTR + \bTR + \bTD \showglyphs \multiply\glyphscale by 2 <{\darkgray \glyph left 3pt `M}>\eTD + \bTD \showglyphs \multiply\glyphscale by 2 <{\darkgray \glyph right 2pt `M}>\eTD + \bTD \showglyphs \multiply\glyphscale by 2 <{\darkgray \glyph left 3pt right 2pt `M}>\eTD + \eTR + \bTR[frame=off] + \bTD \tttf left 3pt \eTD + \bTD \tttf right 2pt\eTD + \bTD \tttf left 3pt right 2pt\eTD + \eTR + \bTR + \bTD \showglyphs \multiply\glyphscale by 2 <{\darkgray \glyph left -3pt `M}>\eTD + \bTD \showglyphs \multiply\glyphscale by 2 <{\darkgray \glyph right -2pt `M}>\eTD + \bTD \showglyphs \multiply\glyphscale by 2 <{\darkgray \glyph left -3pt right -2pt `M}>\eTD + \eTR + \bTR[frame=off] + \bTD \tttf left -3pt \eTD + \bTD \tttf right -2pt \eTD + \bTD \tttf left -3pt right -2pt \eTD + \eTR +\eTABLE +\stoplinecorrection + +Here is another way of looking at it: + +\startbuffer +\glyphscale 4000 +\vl\glyph `M\vl\quad +\vl\glyph raise .2em `M\vl\quad +\vl\glyph left .3em `M\vl\quad +\vl\glyph right .2em`M\vl\quad +\vl\glyph left -.2em right -.2em`M\vl\quad +\vl\glyph raise -.2em right .4em`M\vl +\stopbuffer + +\typebuffer + +The raise as well as left and right margins are taken into account when calculating the +dimensions of a glyph. + +{\getbuffer} + + +\stopsection + +\startsection[title={Implementation}] + +Discussing the implementation in the engine makes no sense here, also because +details might change. However, it is good to know that quite some properties +travel with the glyph nodes, for instance the scales and offsets. The dimensions +(width, height and depth) are not stored in the glyph node but calculated from +the font, scales and optionally the offsets and expansion factor. One problem is +that the more clever (and nice) solutions we cook up, the more it might impact +performance. So, I will delay some experiments till I have a more powerful +machine. + +One reason for {\em not} storing the dimensions in a glyph node is that we often +copy those nodes or change character fields in the font handler and we definitely +don't want the wrong dimensions there. At that moment, offsets and margin fields +don't reflect features yet, so copying them is no big deal. However, dimensions +are rather character bound so every time a character is set, we also would have +to set the dimensions. Even worse, when we can set them, the question arises if +they actually were already set explicitly. So, this is a can of worms we're not +going to open: the basic width, height and depth of the glyph as specified in the +font is used and combines with actual dimensions (likely already scaled according +the glyph scales) in offset and margin fields. + +Now, I have to admit that especially playing with using margins to glyphs instead +of font kerns is more of an experiment to see what the consequences are than a +necessity, but what would be the joy of \TEX\ without such experiments. And as +usual, in \CONTEXT\ these will become options in the font handler that one can +either or not enable. -After doing this with text, I wondered if we can avoid multiple sizes of an -otherwise same math font. Currently text, script and script script styles all use -the same font, but scaled. And, indeed, experiments show that this can be done, -although the math renderer had to be adapted to it. So, for now it's an -experiment. Users have to check if things are okay. Also, we have slightly -different rounding than with normal scaling. Of course the big advantage is that -we have less instances (math can add up more than text as every size has in fact -three instances). +\stopsection \stopchapter @@ -294,7 +608,7 @@ three instances). \starttext \startbuffer -\definescaledfont[bfe][xscale=2000,yscale=6000,style=bf] +\definetweakedfont[bfe][xscale=2000,yscale=6000,style=bf] \setuphead[chapter][style=\bfe] @@ -332,3 +646,8 @@ three instances). % anything) on YT. Both examples of where (imo extensive) practicing real % instruments can bring you with todays diverse equipment. These are interesting % times (it's now last days of 2020). +% +% The initial \definescaledfont was replaced by \definetweakedfont in the first +% week of Januari 2021, when I watched the joyful covers of Peter Gabriels +% "Steam" by Yoyoka which shows that the future belongs to the real young kids +% (so much explosive creativity!) and not to the old folks. diff --git a/doc/context/sources/general/manuals/followingup/followingup-style.tex b/doc/context/sources/general/manuals/followingup/followingup-style.tex index 68b52043c..51519f137 100644 --- a/doc/context/sources/general/manuals/followingup/followingup-style.tex +++ b/doc/context/sources/general/manuals/followingup/followingup-style.tex @@ -3,6 +3,8 @@ \startenvironment followingup-style +\enableexperiments[fonts.compact] % needed for font chapter + \usemodule[abbreviations-smallcaps] \logo [LUAMETATEX] {LuaMeta\TeXsuffix} diff --git a/doc/context/sources/general/manuals/luametatex/luametatex.tex b/doc/context/sources/general/manuals/luametatex/luametatex.tex index 588cc8952..1327ea3a1 100644 --- a/doc/context/sources/general/manuals/luametatex/luametatex.tex +++ b/doc/context/sources/general/manuals/luametatex/luametatex.tex @@ -79,7 +79,7 @@ % 290 pages, 9.5 sec, 149M lua, 35M tex, 30 instances \enableexperiments[fonts.compact] -\enabledirectives[fonts.injections.method=advance] +% \enabledirectives[fonts.injections.method=advance] % tricky ... not all xoffsets are advance robust \pushoverloadmode \unprotect % test code diff --git a/tex/context/base/mkii/cont-new.mkii b/tex/context/base/mkii/cont-new.mkii index ea233b8d2..d06fbe463 100644 --- a/tex/context/base/mkii/cont-new.mkii +++ b/tex/context/base/mkii/cont-new.mkii @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\newcontextversion{2021.01.08 11:44} +\newcontextversion{2021.01.11 16:28} %D This file is loaded at runtime, thereby providing an %D excellent place for hacks, patches, extensions and new diff --git a/tex/context/base/mkii/context.mkii b/tex/context/base/mkii/context.mkii index ea64e263e..c36274955 100644 --- a/tex/context/base/mkii/context.mkii +++ b/tex/context/base/mkii/context.mkii @@ -20,7 +20,7 @@ %D your styles an modules. \edef\contextformat {\jobname} -\edef\contextversion{2021.01.08 11:44} +\edef\contextversion{2021.01.11 16:28} %D For those who want to use this: diff --git a/tex/context/base/mkii/mult-cs.mkii b/tex/context/base/mkii/mult-cs.mkii index d2a51a8ab..d4b39ad3b 100644 --- a/tex/context/base/mkii/mult-cs.mkii +++ b/tex/context/base/mkii/mult-cs.mkii @@ -1131,6 +1131,7 @@ \setinterfaceconstant{reference}{odkaz} \setinterfaceconstant{referencemethod}{referencemethod} \setinterfaceconstant{referenceprefix}{referenceprefix} +\setinterfaceconstant{referencetext}{referencetext} \setinterfaceconstant{referencing}{odkazujici} \setinterfaceconstant{region}{region} \setinterfaceconstant{regionin}{oblastuvnitr} diff --git a/tex/context/base/mkiv/cont-new.mkiv b/tex/context/base/mkiv/cont-new.mkiv index da0388cf0..49c850c9b 100644 --- a/tex/context/base/mkiv/cont-new.mkiv +++ b/tex/context/base/mkiv/cont-new.mkiv @@ -13,7 +13,7 @@ % \normalend % uncomment this to get the real base runtime -\newcontextversion{2021.01.08 11:44} +\newcontextversion{2021.01.11 16:28} %D This file is loaded at runtime, thereby providing an excellent place for hacks, %D patches, extensions and new features. There can be local overloads in cont-loc diff --git a/tex/context/base/mkiv/context.mkiv b/tex/context/base/mkiv/context.mkiv index 23eabed71..93744dfda 100644 --- a/tex/context/base/mkiv/context.mkiv +++ b/tex/context/base/mkiv/context.mkiv @@ -45,7 +45,7 @@ %D {YYYY.MM.DD HH:MM} format. \edef\contextformat {\jobname} -\edef\contextversion{2021.01.08 11:44} +\edef\contextversion{2021.01.11 16:28} %D Kind of special: diff --git a/tex/context/base/mkiv/status-files.pdf b/tex/context/base/mkiv/status-files.pdf Binary files differindex 74deffd1f..d55bfa411 100644 --- a/tex/context/base/mkiv/status-files.pdf +++ b/tex/context/base/mkiv/status-files.pdf diff --git a/tex/context/base/mkiv/status-lua.pdf b/tex/context/base/mkiv/status-lua.pdf Binary files differindex 27cd2367e..78ade839c 100644 --- a/tex/context/base/mkiv/status-lua.pdf +++ b/tex/context/base/mkiv/status-lua.pdf diff --git a/tex/context/base/mkxl/cont-new.mkxl b/tex/context/base/mkxl/cont-new.mkxl index 376101d29..31dbd327a 100644 --- a/tex/context/base/mkxl/cont-new.mkxl +++ b/tex/context/base/mkxl/cont-new.mkxl @@ -13,7 +13,7 @@ % \normalend % uncomment this to get the real base runtime -\newcontextversion{2021.01.08 11:44} +\newcontextversion{2021.01.11 16:28} %D This file is loaded at runtime, thereby providing an excellent place for hacks, %D patches, extensions and new features. There can be local overloads in cont-loc diff --git a/tex/context/base/mkxl/context.mkxl b/tex/context/base/mkxl/context.mkxl index 10674b4ee..751712225 100644 --- a/tex/context/base/mkxl/context.mkxl +++ b/tex/context/base/mkxl/context.mkxl @@ -29,7 +29,7 @@ %D {YYYY.MM.DD HH:MM} format. \immutable\edef\contextformat {\jobname} -\immutable\edef\contextversion{2021.01.08 11:44} +\immutable\edef\contextversion{2021.01.11 16:28} %overloadmode 1 % check frozen / warning %overloadmode 2 % check frozen / error diff --git a/tex/context/base/mkxl/driv-shp.lmt b/tex/context/base/mkxl/driv-shp.lmt index 57ac9e294..9747ba82d 100644 --- a/tex/context/base/mkxl/driv-shp.lmt +++ b/tex/context/base/mkxl/driv-shp.lmt @@ -627,7 +627,7 @@ local hlist_out, vlist_out do for current, id, subtype in nextnode, current do if id == glyph_code then local char, font = isglyph(current) - local x_offset, y_offset = getoffsets(current) -- todo: also get scales here + local x_offset, y_offset, left, right, raise = getoffsets(current) if x_offset ~= 0 or y_offset ~= 0 then if pos_r == righttoleft_code then pos_h = ref_h - (cur_h + x_offset) @@ -637,8 +637,10 @@ local hlist_out, vlist_out do pos_v = ref_v - (cur_v - y_offset) -- synced end + pos_v = pos_v + raise + pos_h = pos_h - left local wd = flush_character(current,font,char,false,true,pos_h,pos_v,pos_r) - cur_h = cur_h + wd + cur_h = cur_h + wd - right elseif id == glue_code then local gluewidth = effectiveglue(current,this_box) if gluewidth ~= 0 then @@ -834,8 +836,8 @@ local hlist_out, vlist_out do if total > 0 then local xoffset, yoffset, left, right = getoffsets(current) -- top bottom if left ~= 0 then - pos_v = pos_v + left - total = total - left + pos_v = pos_v + left + total = total - left end if right ~= 0 then depth = depth - right @@ -995,8 +997,7 @@ local hlist_out, vlist_out do local total = height + depth if getid(leader) == rule_code then depth = 0 -- hm --- forgotten ... needs testing -total = glueheight + total = glueheight -- forgotten ... needs testing if total > 0 then if width == running then width = boxwidth diff --git a/tex/context/base/mkxl/font-col.lmt b/tex/context/base/mkxl/font-col.lmt index 200551099..21c731946 100644 --- a/tex/context/base/mkxl/font-col.lmt +++ b/tex/context/base/mkxl/font-col.lmt @@ -33,6 +33,8 @@ local trace_collecting = false trackers.register("fonts.collecting", function local report_fonts = logs.reporter("fonts","collections") +local texconditionals = tex.conditionals + local enableaction = nodes.tasks.enableaction local disableaction = nodes.tasks.disableaction @@ -236,7 +238,7 @@ function collections.clonevector(name) if factor then vector.factor = factor end -if tex.conditionals["c_font_compact"] then +if texconditionals["c_font_compact"] then if rscale then vector.rscale = rscale end @@ -342,7 +344,7 @@ function collections.prepare(name) -- we can do this in lua now .. todo local f = d[i] local name = f.font local scale = f.rscale or 1 -if tex.conditionals["c_font_compact"] then +if texconditionals["c_font_compact"] then scale = 1 end if fontpatternhassize(name) then diff --git a/tex/context/base/mkxl/font-ctx.lmt b/tex/context/base/mkxl/font-ctx.lmt index 3dbf7bbbd..b307f8f5d 100644 --- a/tex/context/base/mkxl/font-ctx.lmt +++ b/tex/context/base/mkxl/font-ctx.lmt @@ -1095,13 +1095,14 @@ local specifiers = { } do -- else too many locals - local starttiming = statistics.starttiming - local stoptiming = statistics.stoptiming + local starttiming = statistics.starttiming + local stoptiming = statistics.stoptiming - local setmacro = tokens.setters.macro - local ctxcatcodes = tex.ctxcatcodes + local setmacro = tokens.setters.macro + local ctxcatcodes = tex.ctxcatcodes + local texconditionals = tex.conditionals - local reported = setmetatableindex(function(t,k) + local reported = setmetatableindex(function(t,k) local v = setmetatableindex(function(t,k) t[k] = true return false @@ -1226,6 +1227,9 @@ do -- else too many locals name = o_name sub = o_sub end +if texconditionals["c_font_compact"] then + size = 655360 +end -- so far -- some settings can have been overloaded if lookup and lookup ~= "" then diff --git a/tex/context/base/mkxl/font-glf.mklx b/tex/context/base/mkxl/font-glf.mklx index 023c632dc..2c23f226e 100644 --- a/tex/context/base/mkxl/font-glf.mklx +++ b/tex/context/base/mkxl/font-glf.mklx @@ -25,8 +25,8 @@ % we can optimize for zero and 1000 .. maybe also options? \overloaded\frozen\protected\edefcsname\currenttweakedfont\endcsname {\begincsname\tweakedfontparameter\c!style\endcsname - \glyphxscale \numexpr\glyphxscale *\numericscale\tweakedfontparameter\c!xscale /\plusthousand\relax - \glyphyscale \numexpr\glyphyscale *\numericscale\tweakedfontparameter\c!yscale /\plusthousand\relax + \glyphxscale \numexpr\numericscale\tweakedfontparameter\c!xscale*\glyphxscale/\plusthousand\relax + \glyphyscale \numexpr\numericscale\tweakedfontparameter\c!yscale*\glyphyscale/\plusthousand\relax \glyphxoffset\dimexpr\glyphxoffset+\tweakedfontparameter\c!xoffset\relax \glyphyoffset\dimexpr\glyphyoffset+\tweakedfontparameter\c!yoffset\relax}% \to \everydefinetweakedfont diff --git a/tex/context/base/mkxl/font-imp-math.lmt b/tex/context/base/mkxl/font-imp-math.lmt index 47fcf344c..079c867e2 100644 --- a/tex/context/base/mkxl/font-imp-math.lmt +++ b/tex/context/base/mkxl/font-imp-math.lmt @@ -14,6 +14,8 @@ local registerotffeature = fonts.handlers.otf.features.register local setmetatableindex = table.setmetatableindex +local texconditionals = tex.conditionals + -- tfmdata.properties.mathnolimitsmode = tonumber(value) or 0 local splitter = lpeg.splitat(",",tonumber) @@ -69,7 +71,7 @@ registerotffeature { -- A quick and dirty and low level implementation but okay for testing: local function manipulate(tfmdata,key,value) - if tex.conditionals["c_font_compact"] then + if texconditionals["c_font_compact"] then local rawdata = tfmdata.shared.rawdata local rawresources = rawdata and rawdata.resources local rawfeatures = rawresources and rawresources.features @@ -110,7 +112,7 @@ local function manipulate(tfmdata,key,value) end local function initialize(tfmdata,key,value) - if tex.conditionals["c_font_compact"] then + if texconditionals["c_font_compact"] then local rawdata = tfmdata.shared.rawdata local rawresources = rawdata and rawdata.resources local mathconstants = rawresources.mathconstants diff --git a/tex/context/base/mkxl/font-ini.mklx b/tex/context/base/mkxl/font-ini.mklx index 764fbfdab..0a158e69d 100644 --- a/tex/context/base/mkxl/font-ini.mklx +++ b/tex/context/base/mkxl/font-ini.mklx @@ -884,9 +884,8 @@ \fi \c_font_future_glyph_scale\numexpr\plushundred*\d_font_scaled_font_size/\maxcard\relax \glyphscale\numexpr\plushundred*\d_font_scaled_font_size/\maxcard\relax % needed ? for math i guess -% \glyphscale\plusthousand - \d_font_scaled_font_size\d_font_scaled_default - \d_font_scaled_text_face\d_font_scaled_default + % \d_font_scaled_font_size\d_font_scaled_default + % \d_font_scaled_text_face\d_font_scaled_default % \edef\somefontspec{at \number\d_font_scaled_font_size sp}% % this has to happen at the tex end ... @@ -898,23 +897,6 @@ \font_helpers_update_font_class_parameters % ... till here %\writestatus{fonts}{low level define: #csname/\somefontfile/\number\d_font_scaled_font_size/\fontface/\number\d_font_scaled_text_face}% -% \clf_definefont_two -% \ifempty\fontclass\s!false\else\s!true\fi -% {#csname}% -% {\somefontfile}% -% \d_font_scaled_font_size -% \c_font_feature_inheritance_mode -% {\m_font_class_features}% -% {\m_font_features}% -% {\m_font_class_fallbacks}% -% {\m_font_fallbacks}% -% \fontface -% \d_font_scaled_text_face -% {\m_font_class_goodies}% -% {\m_font_goodies}% -% {\m_font_class_designsize}% -% {\m_font_designsize}% -% \scaledfontmode \clf_definefont_two \ifempty\fontclass\s!false\else\s!true\fi {#csname}% @@ -926,7 +908,7 @@ \m_font_class_fallbacks \m_font_fallbacks \fontface - \d_font_scaled_text_face + \d_font_scaled_default \m_font_class_goodies \m_font_goodies \m_font_class_designsize @@ -943,7 +925,7 @@ % \glyphscale \ifnum\c_font_scaled_font_mode_saved>\plusfour - \numexpr\plusthousand*\dimexpr\d_font_scaled_font_size\relax/\c_font_scaled_points\relax + \numexpr\plusthousand*\dimexpr\d_font_scaled_default\relax/\c_font_scaled_points\relax \else \c_font_future_glyph_scale \fi diff --git a/tex/context/base/mkxl/font-otj.lmt b/tex/context/base/mkxl/font-otj.lmt index 15a78d122..e9e9d524e 100644 --- a/tex/context/base/mkxl/font-otj.lmt +++ b/tex/context/base/mkxl/font-otj.lmt @@ -25,11 +25,13 @@ if not modules then modules = { } end modules ['font-otj'] = { -- An alternative is to have a list per base of all marks and then do a run over the node -- list that resolves the accumulated l/r/x/y and then do an inject pass. --- if needed we can flag a kern node as immutable - -- The thing with these positioning options is that it is not clear what Uniscribe does with -- the 2rl flag and we keep oscillating a between experiments. +-- Beware: combining advance with cursive and marks can be a problem because marks have no +-- dimensions .. maybe we need a special field for that ... the advance also sets the apply +-- x offset flag! + if not nodes.properties then return end local next, rawget, tonumber = next, rawget, tonumber @@ -80,19 +82,26 @@ local nodepool = nuts.pool local tonode = nuts.tonode local tonut = nuts.tonut -local setfield = nuts.setfield local getnext = nuts.getnext local getprev = nuts.getprev local getid = nuts.getid local getfont = nuts.getfont local getchar = nuts.getchar +local setchar = nuts.setchar +----- getxoffset = nuts.getxoffset +----- getyoffset = nuts.getyoffset local getoffsets = nuts.getoffsets local getxscale = nuts.getxscale local getyscale = nuts.getyscale +local getxyscales = nuts.getxyscales +local xscaled = nuts.xscaled +local yscaled = nuts.yscaled local getboth = nuts.getboth local getdisc = nuts.getdisc local setdisc = nuts.setdisc local setoffsets = nuts.setoffsets +local addxoffset = nuts.addxoffset +local addyoffset = nuts.addyoffset local ischar = nuts.ischar local isnextchar = nuts.isnextchar local getkern = nuts.getkern @@ -100,7 +109,8 @@ local setkern = nuts.setkern local setlink = nuts.setlink local setwidth = nuts.setwidth local getwidth = nuts.getwidth -local setadvance = nuts.setadvance +local addxymargins = nuts.addxymargins -- we delegate scaling +local copynode = nuts.copy local nextchar = nuts.traversers.char local nextglue = nuts.traversers.glue @@ -113,8 +123,9 @@ local properties = nodes.properties.data local fontkern = nuts.pool and nuts.pool.fontkern local italickern = nuts.pool and nuts.pool.italickern -local useitalickerns = false -- context only +local useitalickerns = false local useadvance = false +local usezwjkerns = true -- when useadvance directives.register("fonts.injections.useitalics", function(v) if v then @@ -618,21 +629,206 @@ end -- D-post +D-post -- +D-replace +D-replace -local function inject_kerns_only(head,where) +-- Beware! pre/post/replace can have non glyphs too + +-- local function inject_kerns_only(head,where) +-- if trace_injections then +-- trace(head,"kerns") +-- end +-- local current = head +-- local prev -- = nil +-- local prevdisc -- = nil +-- local pre -- = nil -- saves a lookup +-- local post -- = nil -- saves a lookup +-- local replace -- = nil -- saves a lookup +-- local pretail -- = nil -- saves a lookup +-- local posttail -- = nil -- saves a lookup +-- local replacetail -- = nil -- saves a lookup +-- while current do +-- local next, char, id = isnextchar(current) +-- if char then +-- local p = rawget(properties,current) +-- if p then +-- local i = p.injections +-- if i then +-- -- left|glyph|right +-- local leftkern = i.leftkern +-- if leftkern and leftkern ~= 0 then +-- leftkern = xscaled(current,leftkern) +-- if useadvance then +-- addmargins(current,-leftkern) +-- elseif prev and getid(prev) == glue_code then +-- if useitalickerns then +-- head = insert_node_before(head,current,italickern(leftkern)) +-- else +-- setwidth(prev, getwidth(prev) + leftkern) +-- end +-- else +-- head = insert_node_before(head,current,fontkern(leftkern)) +-- end +-- end +-- end +-- if prevdisc then +-- local done = false +-- if post then +-- local i = p.postinjections +-- if i then +-- local leftkern = i.leftkern +-- if leftkern and leftkern ~= 0 then +-- leftkern = xscaled(current,leftkern) +-- if useadvance then +-- addmargins(posttail,false,-leftkern) +-- else +-- setlink(posttail,fontkern(leftkern)) +-- done = true +-- end +-- end +-- end +-- end +-- if replace then +-- local i = p.replaceinjections +-- if i then +-- local leftkern = i.leftkern +-- if leftkern and leftkern ~= 0 then +-- leftkern = xscaled(current,leftkern) +-- if useadvance then +-- addmargins(replacetail,false,-leftkern) +-- else +-- setlink(replacetail,fontkern(leftkern)) +-- done = true +-- end +-- end +-- end +-- else +-- local i = p.emptyinjections +-- if i then +-- -- glyph|disc|glyph (special case) +-- local leftkern = i.leftkern +-- if leftkern and leftkern ~= 0 then +-- leftkern = xscaled(current,leftkern) +-- -- if useadvance then +-- -- add zwj with offset +-- -- else +-- replace = fontkern(leftkern) +-- done = true +-- -- end +-- end +-- end +-- end +-- if done then +-- setdisc(prevdisc,pre,post,replace) +-- end +-- end +-- end +-- prevdisc = nil +-- -- prevglyph = current +-- elseif char == false then +-- -- other font +-- prevdisc = nil +-- -- prevglyph = current +-- elseif id == disc_code then +-- pre, post, replace, pretail, posttail, replacetail = getdisc(current,true) +-- local done = false +-- if pre then +-- -- left|pre glyphs|right +-- for n in nextchar, pre do +-- local p = rawget(properties,n) +-- if p then +-- local i = p.injections or p.preinjections +-- if i then +-- local leftkern = i.leftkern +-- if leftkern and leftkern ~= 0 then +-- leftkern = xscaled(n,leftkern) +-- if useadvance then +-- addmargins(n,-leftkern) +-- else +-- pre = insert_node_before(pre,n,fontkern(leftkern)) +-- done = true +-- end +-- end +-- end +-- end +-- end +-- end +-- if post then +-- -- left|post glyphs|right +-- for n in nextchar, post do +-- local p = rawget(properties,n) +-- if p then +-- local i = p.injections or p.postinjections +-- if i then +-- local leftkern = i.leftkern +-- if leftkern and leftkern ~= 0 then +-- leftkern = xscaled(n,leftkern) +-- if useadvance then +-- addmargins(n,-leftkern) +-- else +-- post = insert_node_before(post,n,fontkern(leftkern)) +-- done = true +-- end +-- end +-- end +-- end +-- end +-- end +-- if replace then +-- -- left|replace glyphs|right +-- for n in nextchar, replace do +-- local p = rawget(properties,n) +-- if p then +-- local i = p.injections or p.replaceinjections +-- if i then +-- local leftkern = i.leftkern +-- if leftkern and leftkern ~= 0 then +-- leftkern = xscaled(n,leftkern) +-- if useadvance then +-- addmargins(n,-leftkern) +-- else +-- replace = insert_node_before(replace,n,fontkern(leftkern)) +-- done = true +-- end +-- end +-- end +-- end +-- end +-- end +-- if done then +-- setdisc(current,pre,post,replace) +-- end +-- -- prevglyph = nil +-- prevdisc = current +-- else +-- -- prevglyph = nil +-- prevdisc = nil +-- end +-- prev = current +-- current = next +-- end +-- -- +-- if keepregisteredcounts then +-- keepregisteredcounts = false +-- else +-- nofregisteredkerns = 0 +-- end +-- if trace_injections then +-- show_result(head) +-- end +-- return head +-- end + +local function inject_kerns_only_kerns(head,where) if trace_injections then trace(head,"kerns") end - local current = head - local prev = nil - local next = nil - local prevdisc = nil --- local prevglyph = nil - local pre = nil -- saves a lookup - local post = nil -- saves a lookup - local replace = nil -- saves a lookup - local pretail = nil -- saves a lookup - local posttail = nil -- saves a lookup - local replacetail = nil -- saves a lookup + local current = head + local prev -- = nil + local prevdisc -- = nil + local pre -- = nil -- saves a lookup + local post -- = nil -- saves a lookup + local replace -- = nil -- saves a lookup + local pretail -- = nil -- saves a lookup + local posttail -- = nil -- saves a lookup + local replacetail -- = nil -- saves a lookup while current do local next, char, id = isnextchar(current) if char then @@ -643,19 +839,15 @@ local function inject_kerns_only(head,where) -- left|glyph|right local leftkern = i.leftkern if leftkern and leftkern ~= 0 then - if useadvance then - setadvance(current,leftkern,0) - else - leftkern = leftkern * getxscale(current) - if prev and getid(prev) == glue_code then - if useitalickerns then - head = insert_node_before(head,current,italickern(leftkern)) - else - setwidth(prev, getwidth(prev) + leftkern) - end + leftkern = xscaled(current,leftkern) + if prev and getid(prev) == glue_code then + if useitalickerns then + head = insert_node_before(head,current,italickern(leftkern)) else - head = insert_node_before(head,current,fontkern(leftkern)) + setwidth(prev,getwidth(prev) + leftkern) end + else + head = insert_node_before(head,current,fontkern(leftkern)) end end end @@ -666,13 +858,8 @@ local function inject_kerns_only(head,where) if i then local leftkern = i.leftkern if leftkern and leftkern ~= 0 then - if useadvance then - setadvance(posttail,0,leftkern) - else - leftkern = leftkern * getxscale(current) - setlink(posttail,fontkern(leftkern)) - done = true - end + setlink(posttail,fontkern(xscaled(current,leftkern))) + done = true end end end @@ -681,13 +868,8 @@ local function inject_kerns_only(head,where) if i then local leftkern = i.leftkern if leftkern and leftkern ~= 0 then - if useadvance then - setadvance(replacetail,0,leftkern) - else - leftkern = leftkern * getxscale(current) - setlink(replacetail,fontkern(leftkern)) - done = true - end + setlink(replacetail,fontkern(xscaled(current,leftkern))) + done = true end end else @@ -696,13 +878,8 @@ local function inject_kerns_only(head,where) -- glyph|disc|glyph (special case) local leftkern = i.leftkern if leftkern and leftkern ~= 0 then - -- if useadvance then - -- add zwj with offset - -- else - leftkern = leftkern * getxscale(current) - replace = fontkern(leftkern) - done = true - -- end + replace = fontkern(xscaled(current,leftkern)) + done = true end end end @@ -712,11 +889,11 @@ local function inject_kerns_only(head,where) end end prevdisc = nil - -- prevglyph = current + -- prevglyph = current elseif char == false then -- other font prevdisc = nil - -- prevglyph = current + -- prevglyph = current elseif id == disc_code then pre, post, replace, pretail, posttail, replacetail = getdisc(current,true) local done = false @@ -729,13 +906,8 @@ local function inject_kerns_only(head,where) if i then local leftkern = i.leftkern if leftkern and leftkern ~= 0 then - if useadvance then - setadvance(n,leftkern,0) - else - leftkern = leftkern * getxscale(n) - pre = insert_node_before(pre,n,fontkern(leftkern)) - done = true - end + pre = insert_node_before(pre,n,fontkern(xscaled(n,leftkern))) + done = true end end end @@ -750,13 +922,8 @@ local function inject_kerns_only(head,where) if i then local leftkern = i.leftkern if leftkern and leftkern ~= 0 then - if useadvance then - setadvance(n,leftkern,0) - else - leftkern = leftkern * getxscale(n) - post = insert_node_before(post,n,fontkern(leftkern)) - done = true - end + post = insert_node_before(post,n,fontkern(xscaled(n,leftkern))) + done = true end end end @@ -771,13 +938,8 @@ local function inject_kerns_only(head,where) if i then local leftkern = i.leftkern if leftkern and leftkern ~= 0 then - if useadvance then - setadvance(n,leftkern,0) - else - leftkern = leftkern * getxscale(n) - replace = insert_node_before(replace,n,fontkern(leftkern)) - done = true - end + replace = insert_node_before(replace,n,fontkern(xscaled(n,leftkern))) + done = true end end end @@ -786,10 +948,10 @@ local function inject_kerns_only(head,where) if done then setdisc(current,pre,post,replace) end - -- prevglyph = nil + -- prevglyph = nil prevdisc = current else - -- prevglyph = nil + -- prevglyph = nil prevdisc = nil end prev = current @@ -807,7 +969,450 @@ local function inject_kerns_only(head,where) return head end -local function inject_positions_only(head,where) +local function inject_kerns_only_margins(head,where) + if trace_injections then + trace(head,"kerns") + end + local current = head + local prevdisc -- = nil + local pre -- = nil -- saves a lookup + local post -- = nil -- saves a lookup + local replace -- = nil -- saves a lookup + local pretail -- = nil -- saves a lookup + local posttail -- = nil -- saves a lookup + local replacetail -- = nil -- saves a lookup + while current do + local next, char, id = isnextchar(current) + if char then + local p = rawget(properties,current) + if p then + if prevdisc then + if post then + local i = p.postinjections + if i then + local leftkern = i.leftkern + if leftkern and leftkern ~= 0 then + addxymargins(posttail,false,-leftkern) + end + end + end + if replace then + local i = p.replaceinjections + if i then + local leftkern = i.leftkern + if leftkern and leftkern ~= 0 then + addxymargins(replacetail,false,-leftkern) + end + end + else + local i = p.emptyinjections + if i then + local leftkern = i.leftkern + if leftkern and leftkern ~= 0 then + if usezwjkerns then + replace = copynode(current) + setchar(replace,0x200D) -- zwj + addxymargins(replace,-leftkern) + else + replace = fontkern(xscaled(current,leftkern)) + end + setdisc(prevdisc,pre,post,replace) -- setreplace + end + end + end + end + -- moved down so we can copy zwj + local i = p.injections + if i then + local leftkern = i.leftkern + if leftkern and leftkern ~= 0 then + addxymargins(current,-leftkern) + end + end + end + prevdisc = nil + elseif char == false then + prevdisc = nil + elseif id == disc_code then + pre, post, replace, pretail, posttail, replacetail = getdisc(current,true) + if pre then + for n in nextchar, pre do + local p = rawget(properties,n) + if p then + local i = p.injections or p.preinjections + if i then + local leftkern = i.leftkern + if leftkern and leftkern ~= 0 then + addxymargins(n,-leftkern) + end + end + end + end + end + if post then + for n in nextchar, post do + local p = rawget(properties,n) + if p then + local i = p.injections or p.postinjections + if i then + local leftkern = i.leftkern + if leftkern and leftkern ~= 0 then + addxymargins(n,-leftkern) + end + end + end + end + end + if replace then + for n in nextchar, replace do + local p = rawget(properties,n) + if p then + local i = p.injections or p.replaceinjections + if i then + local leftkern = i.leftkern + if leftkern and leftkern ~= 0 then + addxymargins(n,-leftkern) + end + end + end + end + end + prevdisc = current + else + prevdisc = nil + end + current = next + end + if keepregisteredcounts then + keepregisteredcounts = false + else + nofregisteredkerns = 0 + end + if trace_injections then + show_result(head) + end + return head +end + +-- local function inject_positions_only(head,where) +-- if trace_injections then +-- trace(head,"positions") +-- end +-- local current = head +-- local prev = nil +-- local next = nil +-- local prevdisc = nil +-- local prevglyph = nil +-- local pre = nil -- saves a lookup +-- local post = nil -- saves a lookup +-- local replace = nil -- saves a lookup +-- local pretail = nil -- saves a lookup +-- local posttail = nil -- saves a lookup +-- local replacetail = nil -- saves a lookup +-- while current do +-- local next, char, id = isnextchar(current) +-- if char then +-- local p = rawget(properties,current) +-- if p then +-- local i = p.injections +-- if i then +-- -- left|glyph|right +-- local yoffset = i.yoffset +-- if yoffset and yoffset ~= 0 then +-- -- use raise when advance +-- addyoffset(current,yscaled(current,yoffset)) +-- end +-- local leftkern = i.leftkern or 0 +-- local rightkern = i.rightkern or 0 +-- if leftkern ~= 0 then +-- leftkern = xscaled(current,leftkern) +-- end +-- if rightkern ~= 0 then +-- rightkern = xscaled(current,rightkern) +-- end +-- if useadvance then +-- if leftkern ~= 0 or rightkern ~= 0 then +-- addmargins(current,-leftkern,-rightkern) +-- end +-- else +-- if leftkern ~= 0 then +-- if leftkern == -rightkern then +-- addxoffset(current,leftkern) +-- rightkern = 0 +-- elseif prev and getid(prev) == glue_code then +-- if useitalickerns then +-- head = insert_node_before(head,current,italickern(leftkern)) +-- else +-- setwidth(prev,getwidth(prev)+leftkern) +-- end +-- else +-- head = insert_node_before(head,current,fontkern(leftkern)) +-- end +-- end +-- if rightkern ~= 0 then +-- if next and getid(next) == glue_code then +-- if useitalickerns then +-- insert_node_after(head,current,italickern(rightkern)) +-- else +-- setwidth(next, getwidth(next)+rightkern) +-- end +-- else +-- insert_node_after(head,current,fontkern(rightkern)) +-- end +-- end +-- end +-- else +-- local i = p.emptyinjections +-- if i then +-- -- glyph|disc|glyph (special case) +-- local rightkern = i.rightkern +-- if rightkern and rightkern ~= 0 then +-- if next and getid(next) == disc_code then +-- if replace then +-- -- error, we expect an empty one +-- else +-- rightkern = xscaled(current,rightkern) +-- replace = fontkern(rightkern) -- maybe also leftkern +-- done = true --KE +-- end +-- end +-- end +-- end +-- end +-- if prevdisc then +-- local done = false +-- if post then +-- local i = p.postinjections +-- if i then +-- local leftkern = i.leftkern +-- if leftkern and leftkern ~= 0 then +-- leftkern = xscaled(current,leftkern) +-- if useadvance then +-- addmargins(posttail,-leftkern) +-- else +-- setlink(posttail,fontkern(leftkern)) +-- done = true +-- end +-- end +-- end +-- end +-- if replace then +-- local i = p.replaceinjections +-- if i then +-- local leftkern = i.leftkern +-- if leftkern and leftkern ~= 0 then +-- leftkern = xscaled(current,leftkern) +-- if useadvance then +-- addmargins(replacetail,-leftkern) +-- else +-- setlink(replacetail,fontkern(leftkern)) +-- done = true +-- end +-- end +-- end +-- else +-- local i = p.emptyinjections +-- if i then +-- -- new .. okay? +-- local leftkern = i.leftkern +-- if leftkern and leftkern ~= 0 then +-- replace = fontkern(xscaled(current,leftkern)) +-- done = true +-- end +-- end +-- end +-- if done then +-- setdisc(prevdisc,pre,post,replace) +-- end +-- end +-- end +-- prevdisc = nil +-- prevglyph = current +-- elseif char == false then +-- prevdisc = nil +-- prevglyph = current +-- elseif id == disc_code then +-- pre, post, replace, pretail, posttail, replacetail = getdisc(current,true) +-- local done = false +-- if pre then +-- -- left|pre glyphs|right +-- for n in nextchar, pre do +-- local p = rawget(properties,n) +-- if p then +-- local i = p.injections or p.preinjections +-- if i then +-- local yoffset = i.yoffset +-- local leftkern = i.leftkern or 0 +-- local rightkern = i.rightkern or 0 +-- if yoffset and yoffset ~= 0 then +-- -- use raise when advance +-- addyoffset(n,yscaled(n,yoffset)) +-- end +-- if leftkern ~= 0 then +-- leftkern = xscaled(n,leftkern) +-- end +-- if rightkern ~= 0 then +-- rightkern = xscaled(n,rightkern) +-- end +-- if useadvance then +-- if leftkern ~= 0 or rightkern ~= 0 then +-- addmargins(pre,-leftkern,-rightkern) +-- end +-- else +-- if leftkern ~= 0 then +-- pre = insert_node_before(pre,n,fontkern(leftkern)) +-- done = true +-- end +-- if rightkern ~= 0 then +-- insert_node_after(pre,n,fontkern(rightkern)) +-- done = true +-- end +-- end +-- end +-- end +-- end +-- end +-- if post then +-- -- left|post glyphs|right +-- for n in nextchar, post do +-- local p = rawget(properties,n) +-- if p then +-- local i = p.injections or p.postinjections +-- if i then +-- local yoffset = i.yoffset +-- local leftkern = i.leftkern or 0 +-- local rightkern = i.rightkern or 0 +-- if yoffset and yoffset ~= 0 then +-- -- use raise when advance +-- addyoffset(n,yscaled(n,yoffset)) +-- end +-- if leftkern ~= 0 then +-- leftkern = xscaled(n,leftkern) +-- end +-- if rightkern ~= 0 then +-- rightkern = xscaled(n,rightkern) +-- end +-- if useadvance then +-- if leftkern ~= 0 or rightkern ~= 0 then +-- addmargins(post,-leftkern,-rightkern) +-- end +-- else +-- if leftkern ~= 0 then +-- post = insert_node_before(post,n,fontkern(leftkern)) +-- done = true +-- end +-- if rightkern ~= 0 then +-- insert_node_after(post,n,fontkern(rightkern)) +-- done = true +-- end +-- end +-- end +-- end +-- end +-- end +-- if replace then +-- -- left|replace glyphs|right +-- for n in nextchar, replace do +-- local p = rawget(properties,n) +-- if p then +-- local i = p.injections or p.replaceinjections +-- if i then +-- local yoffset = i.yoffset +-- local leftkern = i.leftkern or 0 +-- local rightkern = i.rightkern or 0 +-- if yoffset and yoffset ~= 0 then +-- -- use raise when advance +-- addyoffset(n,yscaled(n,yoffset)) +-- end +-- if leftkern ~= 0 then +-- leftkern = xscaled(n,leftkern) +-- end +-- if rightkern ~= 0 then +-- rightkern = xscaled(n,rightkern) +-- end +-- if useadvance then +-- if leftkern ~= 0 or rightkern ~= 0 then +-- addmargins(replace,-leftkern,-rightkern) +-- end +-- else +-- if leftkern ~= 0 then +-- replace = insert_node_before(replace,n,fontkern(leftkern)) +-- done = true +-- end +-- if rightkern ~= 0 then +-- insert_node_after(replace,n,fontkern(rightkern)) +-- done = true +-- end +-- end +-- end +-- end +-- end +-- end +-- if prevglyph then +-- if pre then +-- local p = rawget(properties,prevglyph) +-- if p then +-- local i = p.preinjections +-- if i then +-- -- glyph|pre glyphs +-- local rightkern = i.rightkern +-- if rightkern and rightkern ~= 0 then +-- rightkern = xscaled(prevglyph,rightkern) +-- if useadvance then +-- addmargins(pre,-rightkern) +-- else +-- pre = insert_node_before(pre,pre,fontkern(rightkern)) +-- done = true +-- end +-- end +-- end +-- end +-- end +-- if replace then +-- local p = rawget(properties,prevglyph) +-- if p then +-- local i = p.replaceinjections +-- if i then +-- -- glyph|replace glyphs +-- local rightkern = i.rightkern +-- if rightkern and rightkern ~= 0 then +-- rightkern = xscaled(prevglyph,rightkern) +-- if useadvance then +-- addmargins(replace,-rightkern) +-- else +-- replace = insert_node_before(replace,replace,fontkern(rightkern)) +-- done = true +-- end +-- end +-- end +-- end +-- end +-- end +-- if done then +-- setdisc(current,pre,post,replace) +-- end +-- prevglyph = nil +-- prevdisc = current +-- else +-- prevglyph = nil +-- prevdisc = nil +-- end +-- prev = current +-- current = next +-- end +-- -- +-- if keepregisteredcounts then +-- keepregisteredcounts = false +-- else +-- nofregisteredpositions = 0 +-- end +-- if trace_injections then +-- show_result(head) +-- end +-- return head +-- end + +local function inject_positions_only_kerns(head,where) if trace_injections then trace(head,"positions") end @@ -832,33 +1437,37 @@ local function inject_positions_only(head,where) -- left|glyph|right local yoffset = i.yoffset if yoffset and yoffset ~= 0 then -yoffset = yoffset * getxscale(current) - setoffsets(current,false,yoffset) + -- use raise when advance + addyoffset(current,yscaled(current,yoffset)) end - local leftkern = i.leftkern - local rightkern = i.rightkern - if leftkern and leftkern ~= 0 then -leftkern = leftkern * getxscale(current) - if rightkern and leftkern == -rightkern then - setoffsets(current,leftkern,false) + local leftkern = i.leftkern or 0 + local rightkern = i.rightkern or 0 + if leftkern ~= 0 then + leftkern = xscaled(current,leftkern) + end + if rightkern ~= 0 then + rightkern = xscaled(current,rightkern) + end + if leftkern ~= 0 then + if leftkern == -rightkern then + addxoffset(current,leftkern) rightkern = 0 elseif prev and getid(prev) == glue_code then if useitalickerns then head = insert_node_before(head,current,italickern(leftkern)) else - setwidth(prev, getwidth(prev) + leftkern) + setwidth(prev,getwidth(prev)+leftkern) end else head = insert_node_before(head,current,fontkern(leftkern)) end end - if rightkern and rightkern ~= 0 then -rightkern = rightkern * getxscale(current) + if rightkern ~= 0 then if next and getid(next) == glue_code then if useitalickerns then insert_node_after(head,current,italickern(rightkern)) else - setwidth(next, getwidth(next) + rightkern) + setwidth(next, getwidth(next)+rightkern) end else insert_node_after(head,current,fontkern(rightkern)) @@ -874,10 +1483,8 @@ rightkern = rightkern * getxscale(current) if replace then -- error, we expect an empty one else - -- KE setfield(next,"replace",fontkern(rightkern)) -- maybe also leftkern -rightkern = rightkern * getxscale(current) - replace = fontkern(rightkern) -- maybe also leftkern - done = true --KE + replace = fontkern(xscaled(current,rightkern)) -- maybe also leftkern + done = true --KE end end end @@ -890,8 +1497,7 @@ rightkern = rightkern * getxscale(current) if i then local leftkern = i.leftkern if leftkern and leftkern ~= 0 then -leftkern = leftkern * getxscale(current) - setlink(posttail,fontkern(leftkern)) + setlink(posttail,fontkern(xscaled(current,leftkern))) done = true end end @@ -901,8 +1507,7 @@ leftkern = leftkern * getxscale(current) if i then local leftkern = i.leftkern if leftkern and leftkern ~= 0 then -leftkern = leftkern * getxscale(current) - setlink(replacetail,fontkern(leftkern)) + setlink(replacetail,fontkern(xscaled(current,leftkern))) done = true end end @@ -912,8 +1517,7 @@ leftkern = leftkern * getxscale(current) -- new .. okay? local leftkern = i.leftkern if leftkern and leftkern ~= 0 then -leftkern = leftkern * getxscale(current) - replace = fontkern(leftkern) + replace = fontkern(xscaled(current,leftkern)) done = true end end @@ -942,17 +1546,14 @@ leftkern = leftkern * getxscale(current) local leftkern = i.leftkern local rightkern = i.rightkern if yoffset and yoffset ~= 0 then -yoffset = yoffset * getyscale(current) - setoffsets(n,false,yoffset) + addyoffset(n,yscaled(n,yoffset)) end if leftkern and leftkern ~= 0 then -leftkern = leftkern * getxscale(n) - pre = insert_node_before(pre,n,fontkern(leftkern)) + pre = insert_node_before(pre,n,fontkern(xscaled(n,leftkern))) done = true end if rightkern and rightkern ~= 0 then -rightkern = rightkern * getxscale(n) - insert_node_after(pre,n,fontkern(rightkern)) + insert_node_after(pre,n,fontkern(xscaled(n,rightkern))) done = true end end @@ -970,17 +1571,14 @@ rightkern = rightkern * getxscale(n) local leftkern = i.leftkern local rightkern = i.rightkern if yoffset and yoffset ~= 0 then -yoffset = yoffset * getyscale(current) - setoffsets(n,false,yoffset) + addyoffset(n,yscaled(n,yoffset)) end if leftkern and leftkern ~= 0 then -leftkern = leftkern * getxscale(n) - post = insert_node_before(post,n,fontkern(leftkern)) + post = insert_node_before(post,n,fontkern(xscaled(n,leftkern))) done = true end if rightkern and rightkern ~= 0 then -rightkern = rightkern * getxscale(n) - insert_node_after(post,n,fontkern(rightkern)) + insert_node_after(post,n,fontkern(xscaled(n,rightkern))) done = true end end @@ -998,17 +1596,14 @@ rightkern = rightkern * getxscale(n) local leftkern = i.leftkern local rightkern = i.rightkern if yoffset and yoffset ~= 0 then -yoffset = yoffset * getyscale(current) - setoffsets(n,false,yoffset) + addyoffset(n,yscaled(n,yoffset)) end if leftkern and leftkern ~= 0 then -leftkern = leftkern * getxscale(n) - replace = insert_node_before(replace,n,fontkern(leftkern)) + replace = insert_node_before(replace,n,fontkern(xscaled(n,leftkern))) done = true end if rightkern and rightkern ~= 0 then -rightkern = rightkern * getxscale(n) - insert_node_after(replace,n,fontkern(rightkern)) + insert_node_after(replace,n,fontkern(xscaled(n,rightkern))) done = true end end @@ -1016,6 +1611,7 @@ rightkern = rightkern * getxscale(n) end end if prevglyph then + -- there can only be useful properties when pre/replace start with a glyph if pre then local p = rawget(properties,prevglyph) if p then @@ -1024,8 +1620,7 @@ rightkern = rightkern * getxscale(n) -- glyph|pre glyphs local rightkern = i.rightkern if rightkern and rightkern ~= 0 then -rightkern = rightkern * getxscale(prevglyph) - pre = insert_node_before(pre,pre,fontkern(rightkern)) + pre = insert_node_before(pre,pre,fontkern(xscaled(prevglyph,rightkern))) done = true end end @@ -1039,8 +1634,7 @@ rightkern = rightkern * getxscale(prevglyph) -- glyph|replace glyphs local rightkern = i.rightkern if rightkern and rightkern ~= 0 then -rightkern = rightkern * getxscale(prevglyph) - replace = insert_node_before(replace,replace,fontkern(rightkern)) + replace = insert_node_before(replace,replace,fontkern(xscaled(prevglyph,rightkern))) done = true end end @@ -1071,6 +1665,202 @@ rightkern = rightkern * getxscale(prevglyph) return head end +local function inject_positions_only_margins(head,where) + if trace_injections then + trace(head,"positions") + end + local current = head + local prev = nil + local next = nil + local prevdisc = nil + local prevglyph = nil + local pre = nil -- saves a lookup + local post = nil -- saves a lookup + local replace = nil -- saves a lookup + local pretail = nil -- saves a lookup + local posttail = nil -- saves a lookup + local replacetail = nil -- saves a lookup + while current do + local next, char, id = isnextchar(current) + if char then + local p = rawget(properties,current) + if p then + local i = p.injections + if i then + -- left|glyph|right + local yoffset = i.yoffset or 0 + local leftkern = i.leftkern or 0 + local rightkern = i.rightkern or 0 + if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then + addxymargins(n,-leftkern,-rightkern,yoffset) -- also scales + end + else + local i = p.emptyinjections + if i then + -- glyph|disc|glyph (special case) + local rightkern = i.rightkern + if rightkern and rightkern ~= 0 then + if next and getid(next) == disc_code then + if replace then + -- error, we expect an empty one + else + if usezwjkerns then + replace = copynode(current) + setchar(replace,0x200D) -- zwj + addxymargins(replace,false,-rightkern) + else + replace = fontkern(xscaled(current,rightkern)) -- maybe also leftkern + end + done = true --KE + end + end + end + end + end + if prevdisc then + if post then + local i = p.postinjections + if i then + local leftkern = i.leftkern + if leftkern and leftkern ~= 0 then + addxymargins(posttail,-leftkern) + end + end + end + if replace then + local i = p.replaceinjections + if i then + local leftkern = i.leftkern + if leftkern and leftkern ~= 0 then + addxymargins(replacetail,-leftkern) + end + end + else + local i = p.emptyinjections + if i then + -- new .. okay? + local leftkern = i.leftkern + if leftkern and leftkern ~= 0 then + if usezwjkerns then + replace = copynode(current) + setchar(replace,0x200D) -- zwj + addxymargins(replace,-leftkern) + else + replace = fontkern(xscaled(current,leftkern)) + end + setdisc(prevdisc,pre,post,replace) + end + end + end + end + end + prevdisc = nil + prevglyph = current + elseif char == false then + prevdisc = nil + prevglyph = current + elseif id == disc_code then + pre, post, replace, pretail, posttail, replacetail = getdisc(current,true) + if pre then + -- left|pre glyphs|right + for n in nextchar, pre do + local p = rawget(properties,n) + if p then + local i = p.injections or p.preinjections + if i then + local yoffset = i.yoffset or 0 + local leftkern = i.leftkern or 0 + local rightkern = i.rightkern or 0 + if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then + addxymargins(n,-leftkern,-rightkern,yoffset) -- also scales + end + end + end + end + end + if post then + -- left|post glyphs|right + for n in nextchar, post do + local p = rawget(properties,n) + if p then + local i = p.injections or p.postinjections + if i then + local yoffset = i.yoffset or 0 + local leftkern = i.leftkern or 0 + local rightkern = i.rightkern or 0 + if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then + addxymargins(n,-leftkern,-rightkern,yoffset) -- also scales + end + end + end + end + end + if replace then + -- left|replace glyphs|right + for n in nextchar, replace do + local p = rawget(properties,n) + if p then + local i = p.injections or p.replaceinjections + if i then + local yoffset = i.yoffset or 0 + local leftkern = i.leftkern or 0 + local rightkern = i.rightkern or 0 + if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then + addxymargins(n,-leftkern,-rightkern,yoffset) -- also scales + end + end + end + end + end + if prevglyph then + if pre then + local p = rawget(properties,prevglyph) + if p then + local i = p.preinjections + if i then + -- glyph|pre glyphs + local rightkern = i.rightkern + if rightkern and rightkern ~= 0 then + addxymargins(pre,-rightkern,0,0) + end + end + end + end + if replace then + local p = rawget(properties,prevglyph) + if p then + local i = p.replaceinjections + if i then + -- glyph|replace glyphs + local rightkern = i.rightkern + if rightkern and rightkern ~= 0 then + addxymargins(replace,-rightkern,0,0) + end + end + end + end + end + prevglyph = nil + prevdisc = current + else + prevglyph = nil + prevdisc = nil + end + prev = current + current = next + end + -- + if keepregisteredcounts then + keepregisteredcounts = false + else + nofregisteredpositions = 0 + end + if trace_injections then + show_result(head) + end + return head +end + local function showoffset(n,flag) local x, y = getoffsets(n) if x ~= 0 or y ~= 0 then @@ -1078,6 +1868,90 @@ local function showoffset(n,flag) end end +local function processmark(p,n,pn) -- p = basenode + local px, py = getoffsets(p) + local nx, ny = getoffsets(n) + local sx, sy = getxyscales(n) -- we assume n and p to have equal scales + local ox = 0 + local rightkern = nil + local pp = rawget(properties,p) + if pp then + pp = pp.injections + if pp then + rightkern = pp.rightkern + end + end + local markdir = pn.markdir + if rightkern then -- x and w ~= 0 + ox = px - sx * (pn.markx or 0) - rightkern + if markdir and markdir < 0 then + -- kern(w-x) glyph(p) kern(x) mark(n) + if not pn.markmark then + ox = ox + sx * (pn.leftkern or 0) + end + else + -- kern(x) glyph(p) kern(w-x) mark(n) + -- + -- According to Kai we don't need to handle leftkern here but I'm + -- pretty sure I've run into a case where it was needed so maybe + -- some day we need something more clever here. + -- + -- maybe we need to apply both then + -- + if false then + -- a mark with kerning (maybe husayni needs it ) + local leftkern = pp.leftkern + if leftkern then + ox = ox - sx * leftkern + end + end + end + else + ox = px - sx * (pn.markx or 0) + if markdir and markdir < 0 then + if not pn.markmark then + local leftkern = pn.leftkern + if leftkern then + ox = ox + sx * leftkern -- husayni needs it + end + end + end + if pn.checkmark then + local wn = getwidth(n) -- in arial marks have widths + if wn and wn ~= 0 then + wn = wn/2 + if trace_injections then + report_injections("correcting non zero width mark %C",getchar(n)) + end + -- -- bad: we should center + -- + -- pn.leftkern = -wn + -- pn.rightkern = -wn + -- + -- -- we're too late anyway as kerns are already injected so we do it the + -- -- ugly way (no checking if the previous is already a kern) .. maybe we + -- -- should fix the font instead + -- + -- todo: head and check for prev / next kern + -- + insert_node_before(n,n,fontkern(-wn)) + insert_node_after(n,n,fontkern(-wn)) + end + end + end + local oy = ny + py + sy * (pn.marky or 0) + if not pn.markmark then + local yoffset = pn.yoffset + if yoffset then + oy = oy + sy * yoffset -- husayni needs it + end + end + setoffsets(n,ox,oy) + if trace_marks then + showoffset(n,true) + end +end + local function inject_everything(head,where) if trace_injections then trace(head,"everything") @@ -1105,92 +1979,6 @@ local function inject_everything(head,where) local marks = { } local nofmarks = 0 -- - -- move out - -- - local function processmark(p,n,pn) -- p = basenode - local px, py = getoffsets(p) - local nx, ny = getoffsets(n) - local ox = 0 - local rightkern = nil - local pp = rawget(properties,p) - if pp then - pp = pp.injections - if pp then - rightkern = pp.rightkern - end - end - local markdir = pn.markdir - if rightkern then -- x and w ~= 0 - ox = px - (pn.markx or 0) - rightkern - if markdir and markdir < 0 then - -- kern(w-x) glyph(p) kern(x) mark(n) - if not pn.markmark then - ox = ox + (pn.leftkern or 0) - end - else - -- kern(x) glyph(p) kern(w-x) mark(n) - -- - -- According to Kai we don't need to handle leftkern here but I'm - -- pretty sure I've run into a case where it was needed so maybe - -- some day we need something more clever here. - -- - -- maybe we need to apply both then - -- - if false then - -- a mark with kerning (maybe husayni needs it ) - local leftkern = pp.leftkern - if leftkern then - ox = ox - leftkern - end - end - end - else - ox = px - (pn.markx or 0) - if markdir and markdir < 0 then - if not pn.markmark then - local leftkern = pn.leftkern - if leftkern then - ox = ox + leftkern -- husayni needs it - end - end - end - if pn.checkmark then - local wn = getwidth(n) -- in arial marks have widths - if wn and wn ~= 0 then - wn = wn/2 - if trace_injections then - report_injections("correcting non zero width mark %C",getchar(n)) - end - -- -- bad: we should center - -- - -- pn.leftkern = -wn - -- pn.rightkern = -wn - -- - -- -- we're too late anyway as kerns are already injected so we do it the - -- -- ugly way (no checking if the previous is already a kern) .. maybe we - -- -- should fix the font instead - -- - -- todo: head and check for prev / next kern - -- - insert_node_before(n,n,fontkern(-wn)) - insert_node_after(n,n,fontkern(-wn)) - end - end - end - local oy = ny + py + (pn.marky or 0) - if not pn.markmark then - local yoffset = pn.yoffset - if yoffset then - oy = oy + yoffset -- husayni needs it - end - end -ox = ox * getxscale(p) -oy = oy * getyscale(p) - setoffsets(n,ox,oy) - if trace_marks then - showoffset(n,true) - end - end while current do local next, char, id = isnextchar(current) if char then @@ -1205,8 +1993,7 @@ oy = oy * getyscale(p) else local yoffset = i.yoffset if yoffset and yoffset ~= 0 then -yoffset = yoffset * getyscale(current) - setoffsets(current,false,yoffset) + addyoffset(current,yscaled(current,yoffset)) -- or just * sy end if hascursives then local cursivex = i.cursivex @@ -1232,9 +2019,8 @@ yoffset = yoffset * getyscale(current) local nx, ny = getoffsets(current) for i=maxc,minc,-1 do local ti = glyphs[i] - ny = ny + properties[ti].cursivedy -* getyscale(current) - setoffsets(ti,false,ny) -- why not add ? + ny = ny + yscaled(current,properties[ti].cursivedy) -- stepwise increment + setoffsets(ti,false,ny) if trace_cursive then showoffset(ti) end @@ -1249,9 +2035,8 @@ yoffset = yoffset * getyscale(current) local nx, ny = getoffsets(current) for i=maxc,minc,-1 do local ti = glyphs[i] - ny = ny + properties[ti].cursivedy -* getyscale(current) - setoffsets(ti,false,ny) -- why not add ? + ny = ny + yscaled(current,properties[ti].cursivedy) -- stepwise increment + setoffsets(ti,false,ny) if trace_cursive then showoffset(ti) end @@ -1265,9 +2050,10 @@ yoffset = yoffset * getyscale(current) local leftkern = i.leftkern local rightkern = i.rightkern if leftkern and leftkern ~= 0 then -leftkern = leftkern * getxscale(current) - if rightkern and leftkern == -rightkern then - setoffsets(current,leftkern,false) + local opposite = rightkern and leftkern == -rightkern + leftkern = xscaled(current,leftkern) + if opposite then + addxoffset(current,leftkern) rightkern = 0 elseif prev and getid(prev) == glue_code then if useitalickerns then @@ -1280,7 +2066,7 @@ leftkern = leftkern * getxscale(current) end end if rightkern and rightkern ~= 0 then -rightkern = rightkern * getxscale(current) + rightkern = xscaled(current,rightkern) if next and getid(next) == glue_code then if useitalickerns then insert_node_after(head,current,italickern(rightkern)) @@ -1302,8 +2088,7 @@ rightkern = rightkern * getxscale(current) if replace then -- error, we expect an empty one else -rightkern = rightkern * getxscale(current) - replace = fontkern(rightkern) + replace = fontkern(xscaled(current,rightkern)) done = true end end @@ -1318,8 +2103,7 @@ rightkern = rightkern * getxscale(current) if i then local leftkern = i.leftkern if leftkern and leftkern ~= 0 then -leftkern = leftkern * getxscale(current) - setlink(posttail,fontkern(leftkern)) + setlink(posttail,fontkern(xscaled(current,leftkern))) done = true end end @@ -1329,8 +2113,7 @@ leftkern = leftkern * getxscale(current) if i then local leftkern = i.leftkern if leftkern and leftkern ~= 0 then -leftkern = leftkern * getxscale(current) - setlink(replacetail,fontkern(leftkern)) + setlink(replacetail,fontkern(xscaled(current,leftkern))) done = true end end @@ -1339,8 +2122,7 @@ leftkern = leftkern * getxscale(current) if i then local leftkern = i.leftkern if leftkern and leftkern ~= 0 then -leftkern = leftkern * getxscale(current) - replace = fontkern(leftkern) + replace = fontkern(xscaled(current,leftkern)) done = true end end @@ -1356,10 +2138,8 @@ leftkern = leftkern * getxscale(current) local nx, ny = getoffsets(current) for i=maxc,minc,-1 do local ti = glyphs[i] - ny = ny + properties[ti].cursivedy -* getyscale(current) - local xi, yi = getoffsets(ti) - setoffsets(ti,xi,yi + ny) -- can be mark, we could use properties + ny = ny + yscaled(current,properties[ti].cursivedy) + setoffsets(ti,false,ny) -- can be mark, we could use properties end maxc = 0 cursiveanchor = nil @@ -1384,18 +2164,16 @@ leftkern = leftkern * getxscale(current) if i then local yoffset = i.yoffset if yoffset and yoffset ~= 0 then - setoffsets(n,false,yoffset) + addyoffset(n,yscaled(n,yoffset)) end local leftkern = i.leftkern if leftkern and leftkern ~= 0 then -leftkern = leftkern * getxscale(n) - pre = insert_node_before(pre,n,fontkern(leftkern)) + pre = insert_node_before(pre,n,fontkern(xscaled(n,leftkern))) done = true end local rightkern = i.rightkern if rightkern and rightkern ~= 0 then -rightkern = rightkern * getxscale(n) - insert_node_after(pre,n,fontkern(rightkern)) + insert_node_after(pre,n,fontkern(xscaled(n,rightkern))) done = true end if hasmarks then @@ -1417,19 +2195,17 @@ rightkern = rightkern * getxscale(n) if i then local yoffset = i.yoffset if yoffset and yoffset ~= 0 then - setoffsets(n,false,yoffset) + addyoffset(n,yscaled(n,yoffset)) end local leftkern = i.leftkern if leftkern and leftkern ~= 0 then -leftkern = leftkern * getsxcale(n) - post = insert_node_before(post,n,fontkern(leftkern)) + post = insert_node_before(post,n,fontkern(xscaled(n,leftkern))) done = true end local rightkern = i.rightkern if rightkern and rightkern ~= 0 then -rightkern = rightkern * getsxcale(n) - insert_node_after(post,n,fontkern(rightkern)) done = true + insert_node_after(post,n,fontkern(xscaled(n,rightkern))) end if hasmarks then local pm = i.markbasenode @@ -1450,18 +2226,16 @@ rightkern = rightkern * getsxcale(n) if i then local yoffset = i.yoffset if yoffset and yoffset ~= 0 then - setoffsets(n,false,yoffset) + addyoffset(n,yscaled(n,yoffset)) end local leftkern = i.leftkern if leftkern and leftkern ~= 0 then -leftkern = leftkern * getxscale(n) - replace = insert_node_before(replace,n,fontkern(leftkern)) + replace = insert_node_before(replace,n,fontkern(xscaled(n,leftkern))) done = true end local rightkern = i.rightkern if rightkern and rightkern ~= 0 then -rightkern = rightkern * getxscale(n) - insert_node_after(replace,n,fontkern(rightkern)) + insert_node_after(replace,n,fontkern(xscaled(n,rightkern))) done = true end if hasmarks then @@ -1483,8 +2257,7 @@ rightkern = rightkern * getxscale(n) -- glyph|pre glyphs local rightkern = i.rightkern if rightkern and rightkern ~= 0 then -rightkern = rightkern * getxscale(prevglyph) - pre = insert_node_before(pre,pre,fontkern(rightkern)) + pre = insert_node_before(pre,pre,fontkern(xscaled(prevglyph,rightkern))) done = true end end @@ -1498,8 +2271,7 @@ rightkern = rightkern * getxscale(prevglyph) -- glyph|replace glyphs local rightkern = i.rightkern if rightkern and rightkern ~= 0 then -rightkern = rightkern * getxscale(prevglyph) - replace = insert_node_before(replace,replace,fontkern(rightkern)) + replace = insert_node_before(replace,replace,fontkern(xscaled(prevglyph,rightkern))) done = true end end @@ -1524,8 +2296,8 @@ rightkern = rightkern * getxscale(prevglyph) local nx, ny = getoffsets(last) for i=maxc,minc,-1 do local ti = glyphs[i] - ny = ny + properties[ti].cursivedy - setoffsets(ti,false,ny) -- why not add ? + ny = ny + yscaled(properties[ti].cursivedy) + setoffsets(ti,false,ny) if trace_cursive then showoffset(ti) end @@ -1635,8 +2407,7 @@ local function injectspaces(head) local threshold = 0 local leftkern = false local rightkern = false - -local xscale = 1 + local xscale = 1 local function updatefont(font,trig) leftkerns = trig.left @@ -1660,7 +2431,7 @@ local xscale = 1 if rightkerns then rightkern = rightkerns[nextchar] end -xscale = getxscale(next) + xscale = getxscale(next) end end if prevchar then @@ -1673,7 +2444,7 @@ xscale = getxscale(next) if leftkerns then leftkern = leftkerns[prevchar] end -xscale = getxscale(prev) + xscale = getxscale(prev) end end if leftkern then @@ -1686,8 +2457,8 @@ xscale = getxscale(prev) if trace_spaces then report_spaces("%C [%p + %p + %p] %C",prevchar,lnew,old,rnew,nextchar) end -lnew = lnew * xscale -rnew = rnew * xscale + lnew = lnew * xscale + rnew = rnew * xscale head = insert_node_before(head,n,italickern(lnew)) insert_node_after(head,n,italickern(rnew)) else @@ -1704,11 +2475,10 @@ rnew = rnew * xscale if trace_spaces then report_spaces("%C [%p + %p]",prevchar,old,new) end -new = new * xscale + new = new * xscale insert_node_after(head,n,italickern(new)) -- tricky with traverse but ok else --- local new = old + leftkern * factor -local new = old + leftkern * factor * xscale + local new = old + leftkern * factor * xscale if trace_spaces then report_spaces("%C [%p -> %p]",prevchar,old,new) end @@ -1720,16 +2490,14 @@ local new = old + leftkern * factor * xscale elseif rightkern then local old = getwidth(n) if old > threshold then + local new = rightkern * factor * xscale if useitalickerns then - local new = rightkern * factor -new = new * xscale if trace_spaces then report_spaces("[%p + %p] %C",old,new,nextchar) end insert_node_after(head,n,italickern(new)) else --- local new = old + rightkern * factor -local new = old + rightkern * factor * xscale + new = old + new if trace_spaces then report_spaces("[%p -> %p] %C",old,new,nextchar) end @@ -1747,28 +2515,40 @@ local new = old + rightkern * factor * xscale return head end --- +-- When advance is found to be okay there will be split functions (which is abit +-- more efficient) but first we need to have scales done (we can also have addoffset +-- and alike then) function injections.handler(head,where) if triggers then head = injectspaces(head) end - -- todo: marks only run too if nofregisteredmarks > 0 or nofregisteredcursives > 0 then if trace_injections then - report_injections("injection variant %a","everything") + report_injections("injection variant %a (%s)","everything","kerns") end + -- I will do useadvance here when I am playing with fonts that follow this + -- injection routem, which is seldom. return inject_everything(head,where) elseif nofregisteredpositions > 0 then if trace_injections then - report_injections("injection variant %a","positions") + report_injections("injection variant %a (%s)","positions",useadvance and "margins" or "kerns") + end + if useadvance then + return inject_positions_only_margins(head,where) + else + return inject_positions_only_kerns(head,where) end - return inject_positions_only(head,where) elseif nofregisteredkerns > 0 then if trace_injections then - report_injections("injection variant %a","kerns") + report_injections("injection variant %a (%s)","kerns",useadvance and "margins" or "kerns") + end + if useadvance then + return inject_kerns_only_margins(head,where) +-- return inject_positions_only_margins(head,where) + else + return inject_kerns_only_kerns(head,where) end - return inject_kerns_only(head,where) else return head end diff --git a/tex/context/base/mkxl/lang-hyp.lmt b/tex/context/base/mkxl/lang-hyp.lmt index c2862f536..c356dbf3c 100644 --- a/tex/context/base/mkxl/lang-hyp.lmt +++ b/tex/context/base/mkxl/lang-hyp.lmt @@ -1438,8 +1438,8 @@ featureset.hyphenonly = hyphenonly == v_yes rightchar = rightchar or (instance and prehyphenchar (instance)) -- efficient if needed leftexchar = (instance and preexhyphenchar (instance)) rightexchar = (instance and postexhyphenchar(instance)) - leftmin = leftcharmin or getfield(current,"left") - rightmin = rightcharmin or getfield(current,"right") + leftmin = leftcharmin or getfield(current,"lhmin") + rightmin = rightcharmin or getfield(current,"rhmin") if not leftchar or leftchar < 0 then leftchar = false end @@ -1516,8 +1516,8 @@ featureset.hyphenonly = hyphenonly == v_yes rightchar = rightchar or (instance and prehyphenchar (instance)) -- efficient if needed leftexchar = (instance and preexhyphenchar (instance)) rightexchar = (instance and postexhyphenchar(instance)) - leftmin = leftcharmin or getfield(current,"left") - rightmin = rightcharmin or getfield(current,"right") + leftmin = leftcharmin or getfield(current,"lhmin") + rightmin = rightcharmin or getfield(current,"rhmin") if not leftchar or leftchar < 0 then leftchar = false end diff --git a/tex/context/base/mkxl/node-nut.lmt b/tex/context/base/mkxl/node-nut.lmt index dbd5c7ef2..296f6f446 100644 --- a/tex/context/base/mkxl/node-nut.lmt +++ b/tex/context/base/mkxl/node-nut.lmt @@ -97,9 +97,12 @@ local nuts = { getnormalizedline = direct.getnormalizedline, getnucleus = direct.getnucleus, getoffsets = direct.getoffsets, + -- getxyoffsets = direct.getxyoffsets, getscales = direct.getscales, getxscale = direct.getxscale, getyscale = direct.getyscale, + xscaled = direct.xscaled, + yscaled = direct.yscaled, getxyscales = direct.getxyscales, getorientation = direct.getorientation, getoptions = direct.getoptions, @@ -156,7 +159,8 @@ local nuts = { remove = d_remove_node, reverse = direct.reverse, set_attribute = direct.set_attribute, - setadvance = direct.setadvance, + addmargins = direct.addmargins, + addxymargins = direct.addxymargins, setattr = direct.set_attribute, setattrs = direct.set_attributes, setattributelist = direct.setattributelist, @@ -189,6 +193,8 @@ local nuts = { setnucleus = direct.setnucleus, setscales = direct.setscales, setoffsets = direct.setoffsets, + addxoffset = direct.addxoffset, + addyoffset = direct.addyoffset, setorientation = direct.setorientation, setoptions = direct.setoptions, setpenalty = direct.setpenalty, diff --git a/tex/context/base/mkxl/trac-vis.lmt b/tex/context/base/mkxl/trac-vis.lmt index 2be9529a9..8c04a0372 100644 --- a/tex/context/base/mkxl/trac-vis.lmt +++ b/tex/context/base/mkxl/trac-vis.lmt @@ -57,6 +57,7 @@ local getwidth = nuts.getwidth local getdepth = nuts.getdepth local getexpansion = nuts.getexpansion local getstate = nuts.getstate +local getoffsets = nuts.getoffsets local isglyph = nuts.isglyph @@ -878,18 +879,20 @@ local ruledglyph do local prev = previous setboth(current) local linewidth = emwidth/(2*fraction) - local info - -- - info = setlink( - (dp == 0 and outlinerule and outlinerule(wd,ht,dp,linewidth)) or userrule { - width = wd, - height = ht, - depth = dp, - line = linewidth, - type = "box", - }, - new_kern(-wd) - ) + local x_offset, y_offset, l_margin, r_margin, raise = getoffsets(current) + wd = wd - l_margin - r_margin + local info = (dp == 0 and outlinerule and outlinerule(wd,ht,dp,linewidth)) or userrule { + width = wd, + height = ht, + depth = dp, + line = linewidth, + type = "box", + } + if l_margin == 0 then + info = setlink(info,new_kern(-wd)) + else + info = setlink(new_kern(-l_margin),info,new_kern(-wd+l_margin)) + end -- local c, f = isglyph(current) local char = chardata[f][c] diff --git a/tex/context/interface/mkii/keys-cs.xml b/tex/context/interface/mkii/keys-cs.xml index eddd75da9..9765bf47f 100644 --- a/tex/context/interface/mkii/keys-cs.xml +++ b/tex/context/interface/mkii/keys-cs.xml @@ -1137,6 +1137,7 @@ <cd:constant name='reference' value='odkaz'/> <cd:constant name='referencemethod' value='referencemethod'/> <cd:constant name='referenceprefix' value='referenceprefix'/> + <cd:constant name='referencetext' value='referencetext'/> <cd:constant name='referencing' value='odkazujici'/> <cd:constant name='region' value='region'/> <cd:constant name='regionin' value='oblastuvnitr'/> diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua index f987a7c06..c1300a71b 100644 --- a/tex/generic/context/luatex/luatex-fonts-merged.lua +++ b/tex/generic/context/luatex/luatex-fonts-merged.lua @@ -1,6 +1,6 @@ -- merged file : c:/data/develop/context/sources/luatex-fonts-merged.lua -- parent file : c:/data/develop/context/sources/luatex-fonts.lua --- merge date : 2021-01-08 11:44 +-- merge date : 2021-01-11 16:28 do -- begin closure to overcome local limits and interference |