font-otj.lmt /size: 97 Kb    last modification: 2024-01-16 09:02
1if not modules then modules = { } end modules ['font-otj'] = {
2    version   = 1.001,
3    optimize  = true,
4    comment   = "companion to font-lib.mkiv",
5    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
6    copyright = "PRAGMA ADE / ConTeXt Development Team",
7    license   = "see context related readme files",
8}
9
10-- This property based variant is not faster but looks nicer than the attribute one. We
11-- need to use rawget (which is about 4 times slower than a direct access but we cannot
12-- get/set that one for our purpose! This version does a bit more with discretionaries
13-- (and Kai has tested it with his collection of weird fonts.)
14
15-- There is some duplicate code here (especially in the the pre/post/replace branches) but
16-- we go for speed. We could store a list of glyph and mark nodes when registering but it's
17-- cleaner to have an identification pass here. Also, I need to keep tracing in mind so
18-- being too clever here is dangerous.
19
20-- As we have a rawget on properties we don't need one on injections.
21
22-- The use_advance code was just a test and is meant for testing and manuals. There is no
23-- performance (or whatever) gain and using kerns is somewhat cleaner (at least for now).
24
25-- An alternative is to have a list per base of all marks and then do a run over the node
26-- list that resolves the accumulated l/r/x/y and then do an inject pass.
27
28-- The thing with these positioning options is that it is not clear what Uniscribe does with
29-- the 2rl flag and we keep oscillating a between experiments.
30
31-- Beware: combining advance with cursive and marks can be a problem because marks have no
32-- dimensions .. maybe we need a special field for that ... the advance also sets the apply
33-- x offset flag!
34
35if not nodes.properties then return end
36
37local next, rawget, tonumber = next, rawget, tonumber
38local fastcopy = table.fastcopy
39
40local registertracker   = trackers.register
41local registerdirective = directives.register
42
43local trace_injections  = false  registertracker("fonts.injections",         function(v) trace_injections = v end)
44local trace_marks       = false  registertracker("fonts.injections.marks",   function(v) trace_marks      = v end)
45local trace_cursive     = false  registertracker("fonts.injections.cursive", function(v) trace_cursive    = v end)
46local trace_spaces      = false  registertracker("fonts.injections.spaces",  function(v) trace_spaces  = v end)
47
48-- local fix_cursive_marks = false
49--
50-- registerdirective("fonts.injections.fixcursivemarks", function(v)
51--     fix_cursive_marks = v
52-- end)
53
54local report_injections = logs.reporter("fonts","injections")
55local report_spaces     = logs.reporter("fonts","spaces")
56
57local attributes, nodes, node = attributes, nodes, node
58
59fonts                    = fonts
60local hashes             = fonts.hashes
61local fontdata           = hashes.identifiers
62local fontmarks          = hashes.marks
63local parameters         = fonts.hashes.parameters -- not in generic
64----- resources          = fonts.hashes.resources  -- not in generic
65
66nodes.injections         = nodes.injections or { }
67local injections         = nodes.injections
68
69local tracers            = nodes.tracers
70local setcolor           = tracers and tracers.colors.set
71local resetcolor         = tracers and tracers.colors.reset
72
73local nodecodes          = nodes.nodecodes
74local glyph_code         = nodecodes.glyph
75local disc_code          = nodecodes.disc
76local kern_code          = nodecodes.kern
77local glue_code          = nodecodes.glue
78
79local nuts               = nodes.nuts
80local nodepool           = nuts.pool
81
82local tonode             = nuts.tonode
83local tonut              = nuts.tonut
84
85local getnext            = nuts.getnext
86local getprev            = nuts.getprev
87local getid              = nuts.getid
88local getchar            = nuts.getchar
89local getcharspec        = nuts.getcharspec
90local setchar            = nuts.setchar
91----- getxoffset         = nuts.getxoffset
92----- getyoffset         = nuts.getyoffset
93local getoffsets         = nuts.getoffsets
94local getxscale          = nuts.getxscale
95local getyscale          = nuts.getyscale
96local getxyscales        = nuts.getxyscales
97local xscaled            = nuts.xscaled
98local yscaled            = nuts.yscaled
99local getboth            = nuts.getboth
100local getdisc            = nuts.getdisc
101local setdisc            = nuts.setdisc
102local getreplace         = nuts.getreplace
103local setreplace         = nuts.setreplace
104local setoffsets         = nuts.setoffsets
105local addxoffset         = nuts.addxoffset
106local addyoffset         = nuts.addyoffset
107local ischar             = nuts.ischar
108local isnextchar         = nuts.isnextchar
109local getkern            = nuts.getkern
110local setkern            = nuts.setkern
111local setlink            = nuts.setlink
112local setwidth           = nuts.setwidth
113local getwidth           = nuts.getwidth
114local addxymargins       = nuts.addxymargins -- we delegate scaling
115local copynode           = nuts.copy
116local setattrlist        = nuts.setattrlist
117
118local nextchar           = nuts.traversers.char
119local nextglue           = nuts.traversers.glue
120
121local insertnodebefore   = nuts.insertbefore
122local insertnodeafter    = nuts.insertafter
123
124local properties         = nodes.properties.data
125
126local fontkern           = nuts.pool and nuts.pool.fontkern
127local spacefontkern      = nuts.pool and nuts.pool.spacefontkern
128
129local function somekern(makekern,amount,current)
130    local kern = makekern(amount)
131    setattrlist(kern,current)
132    return kern
133end
134
135local usespacefontkerns  = false
136local useadvance         = false
137
138directives.register("fonts.injections.usespacefontkerns", function(v)
139    if v then
140        report_injections("using spacefontkerns for space kerns (tracing only)")
141    end
142    usespacefontkerns = v
143end)
144
145registerdirective("fonts.injections.method", function(v)
146    useadvance = v == "advance"
147end)
148
149function injections.installnewkern() end -- obsolete
150
151local nofregisteredkerns     = 0
152local nofregisteredpositions = 0
153local nofregisteredmarks     = 0
154local nofregisteredcursives  = 0
155local keepregisteredcounts   = false
156
157function injections.keepcounts()
158    keepregisteredcounts = true
159end
160
161function injections.resetcounts()
162    nofregisteredkerns     = 0
163    nofregisteredpositions = 0
164    nofregisteredmarks     = 0
165    nofregisteredcursives  = 0
166    keepregisteredcounts   = false
167end
168
169-- We need to make sure that a possible metatable will not kick in unexpectedly.
170
171function injections.reset(n)
172    local p = rawget(properties,n)
173    if p then
174        p.injections = false -- { } -- nil should work too as we use rawget
175    else
176        properties[n] = false -- { injections = { } } -- nil should work too as we use rawget
177    end
178end
179
180function injections.copy(target,source)
181    local sp = rawget(properties,source)
182    if sp then
183        local tp = rawget(properties,target)
184        local si = sp.injections
185        if si then
186            si = fastcopy(si)
187            if tp then
188                tp.injections = si
189            else
190                properties[target] = {
191                    injections = si,
192                }
193            end
194        elseif tp then
195            tp.injections = false -- { }
196        else
197            properties[target] = { injections = { } }
198        end
199    else
200        local tp = rawget(properties,target)
201        if tp then
202            tp.injections = false -- { }
203        else
204            properties[target] = false -- { injections = { } }
205        end
206    end
207end
208
209function injections.setligaindex(n,index) -- todo: don't set when 0
210    local p = rawget(properties,n)
211    if p then
212        local i = p.injections
213        if i then
214            i.ligaindex = index
215        else
216            p.injections = {
217                ligaindex = index
218            }
219        end
220    else
221        properties[n] = {
222            injections = {
223                ligaindex = index
224            }
225        }
226    end
227end
228
229function injections.getligaindex(n,default)
230    local p = rawget(properties,n)
231    if p then
232        local i = p.injections
233        if i then
234            return i.ligaindex or default
235        end
236    end
237    return default
238end
239
240function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext,r2lflag)
241
242    -- The standard says something about the r2lflag related to the first in a series:
243    --
244    --   When this bit is set, the last glyph in a given sequence to which the cursive
245    --   attachment lookup is applied, will be positioned on the baseline.
246    --
247    -- But it looks like we don't need to consider it.
248
249    local dx =  factor*(exit[1]-entry[1])
250    local dy = -factor*(exit[2]-entry[2])
251    local ws = tfmstart.width
252    local wn = tfmnext.width
253    nofregisteredcursives = nofregisteredcursives + 1
254    if rlmode < 0 then
255        dx = -(dx + wn)
256    else
257        dx = dx - ws
258    end
259    if dx == 0 then
260     -- get rid of funny -0
261        dx = 0
262    end
263    --
264    local p = rawget(properties,start)
265    if p then
266        local i = p.injections
267        if i then
268            i.cursiveanchor = true
269        else
270            p.injections = {
271                cursiveanchor = true,
272            }
273        end
274    else
275        properties[start] = {
276            injections = {
277                cursiveanchor = true,
278            },
279        }
280    end
281    local p = rawget(properties,nxt)
282    if p then
283        local i = p.injections
284        if i then
285            i.cursivex = dx
286            i.cursivey = dy
287        else
288            p.injections = {
289                cursivex = dx,
290                cursivey = dy,
291            }
292        end
293    else
294        properties[nxt] = {
295            injections = {
296                cursivex = dx,
297                cursivey = dy,
298            },
299        }
300    end
301    return dx, dy, nofregisteredcursives
302end
303
304-- kind: 0=single 1=first of pair, 2=second of pair
305
306function injections.setposition(kind,current,factor,rlmode,spec,injection)
307    local x = factor * (spec[1] or 0)
308    local y = factor * (spec[2] or 0)
309    local w = factor * (spec[3] or 0)
310    local h = factor * (spec[4] or 0)
311    if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then -- okay?
312        local yoffset   = y - h
313        local leftkern  = x      -- both kerns are set in a pair kern compared
314        local rightkern = w - x  -- to normal kerns where we set only leftkern
315        if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then
316            nofregisteredpositions = nofregisteredpositions + 1
317            if rlmode and rlmode < 0 then
318                leftkern, rightkern = rightkern, leftkern
319            end
320            if not injection then
321                injection = "injections"
322            end
323            local p = rawget(properties,current)
324            if p then
325                local i = p[injection]
326                if i then
327                    if leftkern ~= 0 then
328                        i.leftkern  = (i.leftkern  or 0) + leftkern
329                    end
330                    if rightkern ~= 0 then
331                        i.rightkern = (i.rightkern or 0) + rightkern
332                    end
333                    if yoffset ~= 0 then
334                        i.yoffset = (i.yoffset or 0) + yoffset
335                    end
336                elseif leftkern ~= 0 or rightkern ~= 0 then
337                    p[injection] = {
338                        leftkern  = leftkern,
339                        rightkern = rightkern,
340                        yoffset   = yoffset,
341                    }
342                else
343                    p[injection] = {
344                        yoffset = yoffset,
345                    }
346                end
347            elseif leftkern ~= 0 or rightkern ~= 0 then
348                properties[current] = {
349                    [injection] = {
350                        leftkern  = leftkern,
351                        rightkern = rightkern,
352                        yoffset   = yoffset,
353                    },
354                }
355            else
356                properties[current] = {
357                    [injection] = {
358                        yoffset = yoffset,
359                    },
360                }
361            end
362            return x, y, w, h, nofregisteredpositions
363         end
364    end
365    return x, y, w, h -- no bound
366end
367
368-- The next one is used for simple kerns coming from a truetype kern table. The r2l
369-- variant variant needs checking but it is unlikely that a r2l script uses thsi
370-- feature.
371
372function injections.setkern(current,factor,rlmode,x,injection)
373    local dx = factor * x
374    if dx ~= 0 then
375        nofregisteredkerns = nofregisteredkerns + 1
376        local p = rawget(properties,current)
377        if not injection then
378            injection = "injections"
379        end
380        if p then
381            local i = p[injection]
382            if i then
383                i.leftkern = dx + (i.leftkern or 0)
384            else
385                p[injection] = {
386                    leftkern = dx,
387                }
388            end
389        else
390            properties[current] = {
391                [injection] = {
392                    leftkern = dx,
393                },
394            }
395        end
396        return dx, nofregisteredkerns
397    else
398        return 0, 0
399    end
400end
401
402-- This one is an optimization of pairs where we have only a "w" entry. This one is
403-- potentially different from the previous one wrt r2l. It needs checking. The
404-- optimization relates to smaller tma files.
405
406function injections.setmove(current,factor,rlmode,x,injection)
407    local dx = factor * x
408    if dx ~= 0 then
409        nofregisteredkerns = nofregisteredkerns + 1
410        local p = rawget(properties,current)
411        if not injection then
412            injection = "injections"
413        end
414        if rlmode and rlmode < 0 then
415            -- we need to swap with a single so then we also need to to it here
416            -- as move is just a simple single
417            if p then
418                local i = p[injection]
419                if i then
420                    i.rightkern = dx + (i.rightkern or 0)
421                else
422                    p[injection] = {
423                        rightkern = dx,
424                    }
425                end
426            else
427                properties[current] = {
428                    [injection] = {
429                        rightkern = dx,
430                    },
431                }
432            end
433        else
434            if p then
435                local i = p[injection]
436                if i then
437                    i.leftkern = dx + (i.leftkern or 0)
438                else
439                    p[injection] = {
440                        leftkern = dx,
441                    }
442                end
443            else
444                properties[current] = {
445                    [injection] = {
446                        leftkern = dx,
447                    },
448                }
449            end
450        end
451        return dx, nofregisteredkerns
452    else
453        return 0, 0
454    end
455end
456
457function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk,checkmark) -- ba=baseanchor, ma=markanchor
458    local dx = factor*(ba[1]-ma[1])
459    local dy = factor*(ba[2]-ma[2])
460    nofregisteredmarks = nofregisteredmarks + 1
461    if rlmode >= 0 then
462        dx = tfmbase.width - dx -- see later commented ox
463    end
464    local p = rawget(properties,start)
465    -- hm, dejavu serif does a sloppy mark2mark before mark2base
466    if p then
467        local i = p.injections
468        if i then
469            if i.markmark then
470                -- out of order mkmk: yes or no or option
471            else
472             -- if dx ~= 0 then
473             --     i.markx    = dx
474             -- end
475             -- if y ~= 0 then
476             --     i.marky    = dy
477             -- end
478             -- if rlmode then
479             --     i.markdir  = rlmode
480             -- end
481                i.markx        = dx
482                i.marky        = dy
483                i.markdir      = rlmode or 0
484                i.markbase     = nofregisteredmarks
485                i.markbasenode = base
486                i.markmark     = mkmk
487                i.checkmark    = checkmark
488            end
489        else
490            p.injections = {
491                markx        = dx,
492                marky        = dy,
493                markdir      = rlmode or 0,
494                markbase     = nofregisteredmarks,
495                markbasenode = base,
496                markmark     = mkmk,
497                checkmark    = checkmark,
498            }
499        end
500    else
501        properties[start] = {
502            injections = {
503                markx        = dx,
504                marky        = dy,
505                markdir      = rlmode or 0,
506                markbase     = nofregisteredmarks,
507                markbasenode = base,
508                markmark     = mkmk,
509                checkmark    = checkmark,
510            },
511        }
512    end
513    return dx, dy, nofregisteredmarks
514end
515
516local function dir(n)
517    return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset"
518end
519
520local function showchar(n,nested)
521    local char, font = getcharspec(n)
522    report_injections("%wfont %s, char %U, glyph %c",nested and 2 or 0,font,char,char)
523end
524
525local function show(n,what,nested,symbol)
526    if n then
527        local p = rawget(properties,n)
528        if p then
529            local i = p[what]
530            if i then
531                local leftkern  = i.leftkern  or 0
532                local rightkern = i.rightkern or 0
533                local yoffset   = i.yoffset   or 0
534                local markx     = i.markx     or 0
535                local marky     = i.marky     or 0
536                local markdir   = i.markdir   or 0
537                local markbase  = i.markbase  or 0
538                local cursivex  = i.cursivex  or 0
539                local cursivey  = i.cursivey  or 0
540                local ligaindex = i.ligaindex or 0
541                local cursbase  = i.cursiveanchor
542                local margin    = nested and 4 or 2
543                --
544                if rightkern ~= 0 or yoffset ~= 0 then
545                    report_injections("%w%s pair: lx %p, rx %p, dy %p",margin,symbol,leftkern,rightkern,yoffset)
546                elseif leftkern ~= 0 then
547                    report_injections("%w%s kern: dx %p",margin,symbol,leftkern)
548                end
549                if markx ~= 0 or marky ~= 0 or markbase ~= 0 then
550                    report_injections("%w%s mark: dx %p, dy %p, dir %s, base %s",margin,symbol,markx,marky,markdir,markbase ~= 0 and "yes" or "no")
551                end
552                if cursivex ~= 0 or cursivey ~= 0 then
553                    if cursbase then
554                        report_injections("%w%s curs: base dx %p, dy %p",margin,symbol,cursivex,cursivey)
555                    else
556                        report_injections("%w%s curs: dx %p, dy %p",margin,symbol,cursivex,cursivey)
557                    end
558                elseif cursbase then
559                    report_injections("%w%s curs: base",margin,symbol)
560                end
561                if ligaindex ~= 0 then
562                    report_injections("%w%s liga: index %i",margin,symbol,ligaindex)
563                end
564            end
565        end
566    end
567end
568
569local function showsub(n,what,where)
570    report_injections("begin subrun: %s",where)
571    for n in nextchar, n do
572        showchar(n,where)
573        show(n,what,where," ")
574    end
575    report_injections("end subrun")
576end
577
578local function trace(head,where)
579    report_injections()
580    report_injections("begin run %s: %s kerns, %s positions, %s marks and %s cursives registered",
581        where or "",nofregisteredkerns,nofregisteredpositions,nofregisteredmarks,nofregisteredcursives)
582    local n = head
583    while n do
584        local id = getid(n)
585        if id == glyph_code then
586            showchar(n)
587            show(n,"injections",false," ")
588            show(n,"preinjections",false,"<")
589            show(n,"postinjections",false,">")
590            show(n,"replaceinjections",false,"=")
591            show(n,"emptyinjections",false,"*")
592        elseif id == disc_code then
593            local pre, post, replace = getdisc(n)
594            if pre then
595                showsub(pre,"preinjections","pre")
596            end
597            if post then
598                showsub(post,"postinjections","post")
599            end
600            if replace then
601                showsub(replace,"replaceinjections","replace")
602            end
603            show(n,"emptyinjections",false,"*")
604        end
605        n = getnext(n)
606    end
607    report_injections("end run")
608end
609
610local function show_result(head)
611    local current  = head
612    local skipping = false
613    while current do
614        local id = getid(current)
615        if id == glyph_code then
616            local w = getwidth(current)
617            local x, y = getoffsets(current)
618            report_injections("char: %C, width %p, xoffset %p, yoffset %p",getchar(current),w,x,y)
619            skipping = false
620        elseif id == kern_code then
621            report_injections("kern: %p",getkern(current))
622            skipping = false
623        elseif not skipping then
624            report_injections()
625            skipping = true
626        end
627        current = getnext(current)
628    end
629    report_injections()
630end
631
632-- G  +D-pre        G
633--     D-post+
634--    +D-replace+
635--
636-- G  +D-pre       +D-pre
637--     D-post      +D-post
638--    +D-replace   +D-replace
639
640-- Beware! pre/post/replace can have non glyphs too
641
642-- local function inject_kerns_only(head,where)
643--     if trace_injections then
644--         trace(head,"kerns")
645--     end
646--     local current        = head
647--     local prev        -- = nil
648--     local prevdisc    -- = nil
649--     local pre         -- = nil -- saves a lookup
650--     local post        -- = nil -- saves a lookup
651--     local replace     -- = nil -- saves a lookup
652--     local pretail     -- = nil -- saves a lookup
653--     local posttail    -- = nil -- saves a lookup
654--     local replacetail -- = nil -- saves a lookup
655--     while current do
656--         local next, char, id = isnextchar(current)
657--         if char then
658--             local p = rawget(properties,current)
659--             if p then
660--                 local i = p.injections
661--                 if i then
662--                     -- left|glyph|right
663--                     local leftkern = i.leftkern
664--                     if leftkern and leftkern ~= 0 then
665--                         leftkern = xscaled(current,leftkern)
666--                         if useadvance then
667--                             addmargins(current,-leftkern)
668--                         elseif prev and getid(prev) == glue_code then
669--                             if usespacefontkerns then
670--                                 head = insertnodebefore(head,current,spacefontkern(leftkern))
671--                             else
672--                                 setwidth(prev, getwidth(prev) + leftkern)
673--                             end
674--                         else
675--                             head = insertnodebefore(head,current,fontkern(leftkern))
676--                         end
677--                     end
678--                 end
679--                 if prevdisc then
680--                     local done = false
681--                     if post then
682--                         local i = p.postinjections
683--                         if i then
684--                             local leftkern = i.leftkern
685--                             if leftkern and leftkern ~= 0 then
686--                                 leftkern = xscaled(current,leftkern)
687--                                 if useadvance then
688--                                     addmargins(posttail,false,-leftkern)
689--                                 else
690--                                     setlink(posttail,fontkern(leftkern))
691--                                     done = true
692--                                 end
693--                             end
694--                         end
695--                     end
696--                     if replace then
697--                         local i = p.replaceinjections
698--                         if i then
699--                             local leftkern = i.leftkern
700--                             if leftkern and leftkern ~= 0 then
701--                                 leftkern = xscaled(current,leftkern)
702--                                 if useadvance then
703--                                     addmargins(replacetail,false,-leftkern)
704--                                 else
705--                                     setlink(replacetail,fontkern(leftkern))
706--                                     done = true
707--                                 end
708--                             end
709--                         end
710--                     else
711--                         local i = p.emptyinjections
712--                         if i then
713--                             -- glyph|disc|glyph (special case)
714--                             local leftkern = i.leftkern
715--                             if leftkern and leftkern ~= 0 then
716--                                 leftkern = xscaled(current,leftkern)
717--                              -- if useadvance then
718--                              --     add zwj with offset
719--                              -- else
720--                                     replace = fontkern(leftkern)
721--                                     done    = true
722--                              -- end
723--                             end
724--                         end
725--                     end
726--                     if done then
727--                         setdisc(prevdisc,pre,post,replace)
728--                     end
729--                 end
730--             end
731--             prevdisc  = nil
732--         -- prevglyph = current
733--         elseif char == false then
734--             -- other font
735--             prevdisc  = nil
736--         -- prevglyph = current
737--         elseif id == disc_code then
738--             pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
739--             local done = false
740--             if pre then
741--                 -- left|pre glyphs|right
742--                 for n in nextchar, pre do
743--                     local p = rawget(properties,n)
744--                     if p then
745--                         local i = p.injections or p.preinjections
746--                         if i then
747--                             local leftkern = i.leftkern
748--                             if leftkern and leftkern ~= 0 then
749--                                 leftkern = xscaled(n,leftkern)
750--                                 if useadvance then
751--                                     addmargins(n,-leftkern)
752--                                 else
753--                                     pre  = insertnodebefore(pre,n,fontkern(leftkern))
754--                                     done = true
755--                                 end
756--                             end
757--                         end
758--                     end
759--                 end
760--             end
761--             if post then
762--                 -- left|post glyphs|right
763--                 for n in nextchar, post do
764--                     local p = rawget(properties,n)
765--                     if p then
766--                         local i = p.injections or p.postinjections
767--                         if i then
768--                             local leftkern = i.leftkern
769--                             if leftkern and leftkern ~= 0 then
770--                                 leftkern = xscaled(n,leftkern)
771--                                 if useadvance then
772--                                     addmargins(n,-leftkern)
773--                                 else
774--                                     post = insertnodebefore(post,n,fontkern(leftkern))
775--                                     done = true
776--                                 end
777--                             end
778--                         end
779--                     end
780--                 end
781--             end
782--             if replace then
783--                 -- left|replace glyphs|right
784--                 for n in nextchar, replace do
785--                     local p = rawget(properties,n)
786--                     if p then
787--                         local i = p.injections or p.replaceinjections
788--                         if i then
789--                             local leftkern = i.leftkern
790--                             if leftkern and leftkern ~= 0 then
791--                                 leftkern = xscaled(n,leftkern)
792--                                 if useadvance then
793--                                     addmargins(n,-leftkern)
794--                                 else
795--                                     replace = insertnodebefore(replace,n,fontkern(leftkern))
796--                                     done    = true
797--                                 end
798--                             end
799--                         end
800--                     end
801--                 end
802--             end
803--             if done then
804--                 setdisc(current,pre,post,replace)
805--             end
806--          -- prevglyph = nil
807--             prevdisc  = current
808--         else
809--          -- prevglyph = nil
810--             prevdisc  = nil
811--         end
812--         prev    = current
813--         current = next
814--     end
815--     --
816--     if keepregisteredcounts then
817--         keepregisteredcounts = false
818--     else
819--         nofregisteredkerns   = 0
820--     end
821--     if trace_injections then
822--         show_result(head)
823--     end
824--     return head
825-- end
826
827local function inject_kerns_only_kerns(head,where)
828    if trace_injections then
829        trace(head,"kerns")
830    end
831    local current        = head
832    local prev        -- = nil
833    local prevdisc    -- = nil
834    local pre         -- = nil -- saves a lookup
835    local post        -- = nil -- saves a lookup
836    local replace     -- = nil -- saves a lookup
837    local pretail     -- = nil -- saves a lookup
838    local posttail    -- = nil -- saves a lookup
839    local replacetail -- = nil -- saves a lookup
840    while current do
841        local next, char, id = isnextchar(current)
842        if char then
843            local p = rawget(properties,current)
844            if p then
845                local i = p.injections
846                if i then
847                    -- left|glyph|right
848                    local leftkern = i.leftkern
849                    if leftkern and leftkern ~= 0 then
850                        leftkern = xscaled(current,leftkern)
851                        if prev and getid(prev) == glue_code then
852                            if usespacefontkerns then
853                                head = insertnodebefore(head,current,spacefontkern(leftkern))
854                            else
855                                setwidth(prev,getwidth(prev) + leftkern)
856                            end
857                        else
858                            head = insertnodebefore(head,current,somekern(fontkern,leftkern,current))
859                        end
860                    end
861                end
862                if prevdisc then
863                    local done = false
864                    if post then
865                        local i = p.postinjections
866                        if i then
867                            local leftkern = i.leftkern
868                            if leftkern and leftkern ~= 0 then
869                                setlink(posttail,somekern(fontkern,xscaled(current,leftkern),current))
870                                done = true
871                            end
872                        end
873                    end
874                    if replace then
875                        local i = p.replaceinjections
876                        if i then
877                            local leftkern = i.leftkern
878                            if leftkern and leftkern ~= 0 then
879                                setlink(replacetail,somekern(fontkern,xscaled(current,leftkern),current))
880                                done = true
881                            end
882                        end
883                    else
884                        local i = p.emptyinjections
885                        if i then
886                            -- glyph|disc|glyph (special case)
887                            local leftkern = i.leftkern
888                            if leftkern and leftkern ~= 0 then
889                                replace = somekern(fontkern,xscaled(current,leftkern),current)
890                                done    = true
891                            end
892                        end
893                    end
894                    if done then
895                        setdisc(prevdisc,pre,post,replace)
896                    end
897                end
898            end
899            prevdisc  = nil
900         -- prevglyph = current
901        elseif char == false then
902            -- other font
903            prevdisc  = nil
904         -- prevglyph = current
905        elseif id == disc_code then
906            pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
907            local done = false
908            if pre then
909                -- left|pre glyphs|right
910                for n in nextchar, pre do
911                    local p = rawget(properties,n)
912                    if p then
913                        local i = p.injections or p.preinjections
914                        if i then
915                            local leftkern = i.leftkern
916                            if leftkern and leftkern ~= 0 then
917                                pre  = insertnodebefore(pre,n,somekern(fontkern,xscaled(n,leftkern),n))
918                                done = true
919                            end
920                        end
921                    end
922                end
923            end
924            if post then
925                -- left|post glyphs|right
926                for n in nextchar, post do
927                    local p = rawget(properties,n)
928                    if p then
929                        local i = p.injections or p.postinjections
930                        if i then
931                            local leftkern = i.leftkern
932                            if leftkern and leftkern ~= 0 then
933                                post = insertnodebefore(post,n,somekern(fontkern,xscaled(n,leftkern),n))
934                                done = true
935                            end
936                        end
937                    end
938                end
939            end
940            if replace then
941                -- left|replace glyphs|right
942                for n in nextchar, replace do
943                    local p = rawget(properties,n)
944                    if p then
945                        local i = p.injections or p.replaceinjections
946                        if i then
947                            local leftkern = i.leftkern
948                            if leftkern and leftkern ~= 0 then
949                                replace = insertnodebefore(replace,n,somekern(fontkern,xscaled(n,leftkern),n))
950                                done    = true
951                            end
952                        end
953                    end
954                end
955            end
956            if done then
957                setdisc(current,pre,post,replace)
958            end
959         -- prevglyph = nil
960            prevdisc  = current
961        else
962         -- prevglyph = nil
963            prevdisc  = nil
964        end
965        prev    = current
966        current = next
967    end
968    --
969    if keepregisteredcounts then
970        keepregisteredcounts = false
971    else
972        nofregisteredkerns   = 0
973    end
974    if trace_injections then
975        show_result(head)
976    end
977    return head
978end
979
980local function inject_kerns_only_margins(head,where)
981    if trace_injections then
982        trace(head,"kerns")
983    end
984    local current        = head
985    local prevdisc    -- = nil
986    local pre         -- = nil -- saves a lookup
987    local post        -- = nil -- saves a lookup
988    local replace     -- = nil -- saves a lookup
989    local pretail     -- = nil -- saves a lookup
990    local posttail    -- = nil -- saves a lookup
991    local replacetail -- = nil -- saves a lookup
992    while current do
993        local next, char, id = isnextchar(current)
994        if char then
995            local p = rawget(properties,current)
996            if p then
997                if prevdisc then
998                    if post then
999                        local i = p.postinjections
1000                        if i then
1001                            local leftkern = i.leftkern
1002                            if leftkern and leftkern ~= 0 then
1003                                addxymargins(posttail,false,-leftkern)
1004                            end
1005                        end
1006                    end
1007                    if replace then
1008                        local i = p.replaceinjections
1009                        if i then
1010                            local leftkern = i.leftkern
1011                            if leftkern and leftkern ~= 0 then
1012                                addxymargins(replacetail,false,-leftkern)
1013                            end
1014                        end
1015                    else
1016                        local i = p.emptyinjections
1017                        if i then
1018                            local leftkern = i.leftkern
1019                            if leftkern and leftkern ~= 0 then
1020                                -- prev disc | current
1021                                addxymargins(current,-leftkern) -- NEEDS CHECKING
1022                            end
1023                        end
1024                    end
1025                end
1026                -- moved down so we can copy zwj
1027                local i = p.injections
1028                if i then
1029                    local leftkern = i.leftkern
1030                    if leftkern and leftkern ~= 0 then
1031                        addxymargins(current,-leftkern)
1032                    end
1033                end
1034            end
1035            prevdisc = nil
1036        elseif char == false then
1037            prevdisc = nil
1038        elseif id == disc_code then
1039            pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
1040            if pre then
1041                for n in nextchar, pre do
1042                    local p = rawget(properties,n)
1043                    if p then
1044                        local i = p.injections or p.preinjections
1045                        if i then
1046                            local leftkern = i.leftkern
1047                            if leftkern and leftkern ~= 0 then
1048                                addxymargins(n,-leftkern)
1049                            end
1050                        end
1051                    end
1052                end
1053            end
1054            if post then
1055                for n in nextchar, post do
1056                    local p = rawget(properties,n)
1057                    if p then
1058                        local i = p.injections or p.postinjections
1059                        if i then
1060                            local leftkern = i.leftkern
1061                            if leftkern and leftkern ~= 0 then
1062                                addxymargins(n,-leftkern)
1063                            end
1064                        end
1065                    end
1066                end
1067            end
1068            if replace then
1069                for n in nextchar, replace do
1070                    local p = rawget(properties,n)
1071                    if p then
1072                        local i = p.injections or p.replaceinjections
1073                        if i then
1074                            local leftkern = i.leftkern
1075                            if leftkern and leftkern ~= 0 then
1076                                addxymargins(n,-leftkern)
1077                            end
1078                        end
1079                    end
1080                end
1081            end
1082            prevdisc = current
1083        else
1084            prevdisc = nil
1085        end
1086        current = next
1087    end
1088    if keepregisteredcounts then
1089        keepregisteredcounts = false
1090    else
1091        nofregisteredkerns   = 0
1092    end
1093    if trace_injections then
1094        show_result(head)
1095    end
1096    return head
1097end
1098
1099-- local function inject_positions_only(head,where)
1100--     if trace_injections then
1101--         trace(head,"positions")
1102--     end
1103--     local current     = head
1104--     local prev        = nil
1105--     local next        = nil
1106--     local prevdisc    = nil
1107--     local prevglyph   = nil
1108--     local pre         = nil -- saves a lookup
1109--     local post        = nil -- saves a lookup
1110--     local replace     = nil -- saves a lookup
1111--     local pretail     = nil -- saves a lookup
1112--     local posttail    = nil -- saves a lookup
1113--     local replacetail = nil -- saves a lookup
1114--     while current do
1115--         local next, char, id = isnextchar(current)
1116--         if char then
1117--             local p = rawget(properties,current)
1118--             if p then
1119--                 local i = p.injections
1120--                 if i then
1121--                     -- left|glyph|right
1122--                     local yoffset = i.yoffset
1123--                     if yoffset and yoffset ~= 0 then
1124--                         -- use raise when advance
1125--                         addyoffset(current,yscaled(current,yoffset))
1126--                     end
1127--                     local leftkern  = i.leftkern or 0
1128--                     local rightkern = i.rightkern or 0
1129--                     if leftkern ~= 0 then
1130--                         leftkern = xscaled(current,leftkern)
1131--                     end
1132--                     if rightkern ~= 0 then
1133--                         rightkern = xscaled(current,rightkern)
1134--                     end
1135--                     if useadvance then
1136--                         if leftkern ~= 0 or rightkern ~= 0 then
1137--                             addmargins(current,-leftkern,-rightkern)
1138--                         end
1139--                     else
1140--                         if leftkern ~= 0 then
1141--                             if leftkern == -rightkern then
1142--                                 addxoffset(current,leftkern)
1143--                                 rightkern = 0
1144--                             elseif prev and getid(prev) == glue_code then
1145--                                 if usespacefontkerns then
1146--                                     head = insertnodebefore(head,current,spacefontkern(leftkern))
1147--                                 else
1148--                                     setwidth(prev,getwidth(prev)+leftkern)
1149--                                 end
1150--                             else
1151--                                 head = insertnodebefore(head,current,fontkern(leftkern))
1152--                             end
1153--                         end
1154--                         if rightkern ~= 0 then
1155--                             if next and getid(next) == glue_code then
1156--                                 if usespacefontkerns then
1157--                                     insertnodeafter(head,current,spacefontkern(rightkern))
1158--                                 else
1159--                                     setwidth(next, getwidth(next)+rightkern)
1160--                                 end
1161--                             else
1162--                                 insertnodeafter(head,current,fontkern(rightkern))
1163--                             end
1164--                         end
1165--                     end
1166--                 else
1167--                     local i = p.emptyinjections
1168--                     if i then
1169--                         -- glyph|disc|glyph (special case)
1170--                         local rightkern = i.rightkern
1171--                         if rightkern and rightkern ~= 0 then
1172--                             if next and getid(next) == disc_code then
1173--                                 if replace then
1174--                                     -- error, we expect an empty one
1175--                                 else
1176--                                     rightkern = xscaled(current,rightkern)
1177--                                     replace = fontkern(rightkern) -- maybe also leftkern
1178--                                     done = true	--KE
1179--                                 end
1180--                             end
1181--                         end
1182--                     end
1183--                 end
1184--                 if prevdisc then
1185--                     local done = false
1186--                     if post then
1187--                         local i = p.postinjections
1188--                         if i then
1189--                             local leftkern = i.leftkern
1190--                             if leftkern and leftkern ~= 0 then
1191--                                 leftkern = xscaled(current,leftkern)
1192--                                 if useadvance then
1193--                                     addmargins(posttail,-leftkern)
1194--                                 else
1195--                                     setlink(posttail,fontkern(leftkern))
1196--                                     done = true
1197--                                 end
1198--                             end
1199--                         end
1200--                     end
1201--                     if replace then
1202--                         local i = p.replaceinjections
1203--                         if i then
1204--                             local leftkern = i.leftkern
1205--                             if leftkern and leftkern ~= 0 then
1206--                                 leftkern = xscaled(current,leftkern)
1207--                                 if useadvance then
1208--                                     addmargins(replacetail,-leftkern)
1209--                                 else
1210--                                     setlink(replacetail,fontkern(leftkern))
1211--                                     done = true
1212--                                 end
1213--                             end
1214--                         end
1215--                     else
1216--                         local i = p.emptyinjections
1217--                         if i then
1218--                             -- new .. okay?
1219--                             local leftkern = i.leftkern
1220--                             if leftkern and leftkern ~= 0 then
1221--                                 replace = fontkern(xscaled(current,leftkern))
1222--                                 done = true
1223--                             end
1224--                         end
1225--                     end
1226--                     if done then
1227--                         setdisc(prevdisc,pre,post,replace)
1228--                     end
1229--                 end
1230--             end
1231--             prevdisc  = nil
1232--             prevglyph = current
1233--         elseif char == false then
1234--             prevdisc  = nil
1235--             prevglyph = current
1236--         elseif id == disc_code then
1237--             pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
1238--             local done = false
1239--             if pre then
1240--                 -- left|pre glyphs|right
1241--                 for n in nextchar, pre do
1242--                     local p = rawget(properties,n)
1243--                     if p then
1244--                         local i = p.injections or p.preinjections
1245--                         if i then
1246--                             local yoffset   = i.yoffset
1247--                             local leftkern  = i.leftkern or 0
1248--                             local rightkern = i.rightkern or 0
1249--                             if yoffset and yoffset ~= 0 then
1250--                                 -- use raise when advance
1251--                                 addyoffset(n,yscaled(n,yoffset))
1252--                             end
1253--                             if leftkern ~= 0 then
1254--                                 leftkern = xscaled(n,leftkern)
1255--                             end
1256--                             if rightkern ~= 0 then
1257--                                 rightkern = xscaled(n,rightkern)
1258--                             end
1259--                             if useadvance then
1260--                                 if leftkern ~= 0 or rightkern ~= 0 then
1261--                                     addmargins(pre,-leftkern,-rightkern)
1262--                                 end
1263--                             else
1264--                                 if leftkern ~= 0 then
1265--                                     pre  = insertnodebefore(pre,n,fontkern(leftkern))
1266--                                     done = true
1267--                                 end
1268--                                 if rightkern ~= 0 then
1269--                                     insertnodeafter(pre,n,fontkern(rightkern))
1270--                                     done = true
1271--                                 end
1272--                             end
1273--                         end
1274--                     end
1275--                 end
1276--             end
1277--             if post then
1278--                 -- left|post glyphs|right
1279--                 for n in nextchar, post do
1280--                     local p = rawget(properties,n)
1281--                     if p then
1282--                         local i = p.injections or p.postinjections
1283--                         if i then
1284--                             local yoffset   = i.yoffset
1285--                             local leftkern  = i.leftkern or 0
1286--                             local rightkern = i.rightkern or 0
1287--                             if yoffset and yoffset ~= 0 then
1288--                                 -- use raise when advance
1289--                                 addyoffset(n,yscaled(n,yoffset))
1290--                             end
1291--                             if leftkern ~= 0 then
1292--                                 leftkern = xscaled(n,leftkern)
1293--                             end
1294--                             if rightkern ~= 0 then
1295--                                 rightkern = xscaled(n,rightkern)
1296--                             end
1297--                             if useadvance then
1298--                                 if leftkern ~= 0 or rightkern ~= 0 then
1299--                                     addmargins(post,-leftkern,-rightkern)
1300--                                 end
1301--                             else
1302--                                 if leftkern ~= 0 then
1303--                                     post = insertnodebefore(post,n,fontkern(leftkern))
1304--                                     done = true
1305--                                 end
1306--                                 if rightkern ~= 0 then
1307--                                     insertnodeafter(post,n,fontkern(rightkern))
1308--                                     done = true
1309--                                 end
1310--                             end
1311--                         end
1312--                     end
1313--                 end
1314--             end
1315--             if replace then
1316--                 -- left|replace glyphs|right
1317--                 for n in nextchar, replace do
1318--                     local p = rawget(properties,n)
1319--                     if p then
1320--                         local i = p.injections or p.replaceinjections
1321--                         if i then
1322--                             local yoffset   = i.yoffset
1323--                             local leftkern  = i.leftkern or 0
1324--                             local rightkern = i.rightkern or 0
1325--                             if yoffset and yoffset ~= 0 then
1326--                                 -- use raise when advance
1327--                                 addyoffset(n,yscaled(n,yoffset))
1328--                             end
1329--                             if leftkern ~= 0 then
1330--                                 leftkern = xscaled(n,leftkern)
1331--                             end
1332--                             if rightkern ~= 0 then
1333--                                 rightkern = xscaled(n,rightkern)
1334--                             end
1335--                             if useadvance then
1336--                                 if leftkern ~= 0 or rightkern ~= 0 then
1337--                                     addmargins(replace,-leftkern,-rightkern)
1338--                                 end
1339--                             else
1340--                                 if leftkern ~= 0 then
1341--                                     replace = insertnodebefore(replace,n,fontkern(leftkern))
1342--                                     done    = true
1343--                                 end
1344--                                 if rightkern ~= 0 then
1345--                                     insertnodeafter(replace,n,fontkern(rightkern))
1346--                                     done = true
1347--                                 end
1348--                             end
1349--                         end
1350--                     end
1351--                 end
1352--             end
1353--             if prevglyph then
1354--                 if pre then
1355--                     local p = rawget(properties,prevglyph)
1356--                     if p then
1357--                         local i = p.preinjections
1358--                         if i then
1359--                             -- glyph|pre glyphs
1360--                             local rightkern = i.rightkern
1361--                             if rightkern and rightkern ~= 0 then
1362--                                 rightkern = xscaled(prevglyph,rightkern)
1363--                                 if useadvance then
1364--                                     addmargins(pre,-rightkern)
1365--                                 else
1366--                                     pre  = insertnodebefore(pre,pre,fontkern(rightkern))
1367--                                     done = true
1368--                                 end
1369--                             end
1370--                         end
1371--                     end
1372--                 end
1373--                 if replace then
1374--                     local p = rawget(properties,prevglyph)
1375--                     if p then
1376--                         local i = p.replaceinjections
1377--                         if i then
1378--                             -- glyph|replace glyphs
1379--                             local rightkern = i.rightkern
1380--                             if rightkern and rightkern ~= 0 then
1381--                                 rightkern = xscaled(prevglyph,rightkern)
1382--                                 if useadvance then
1383--                                     addmargins(replace,-rightkern)
1384--                                 else
1385--                                     replace = insertnodebefore(replace,replace,fontkern(rightkern))
1386--                                     done    = true
1387--                                 end
1388--                             end
1389--                         end
1390--                     end
1391--                 end
1392--             end
1393--             if done then
1394--                 setdisc(current,pre,post,replace)
1395--             end
1396--             prevglyph = nil
1397--             prevdisc  = current
1398--         else
1399--             prevglyph = nil
1400--             prevdisc  = nil
1401--         end
1402--         prev    = current
1403--         current = next
1404--     end
1405--     --
1406--     if keepregisteredcounts then
1407--         keepregisteredcounts = false
1408--     else
1409--         nofregisteredpositions = 0
1410--     end
1411--     if trace_injections then
1412--         show_result(head)
1413--     end
1414--     return head
1415-- end
1416
1417local function inject_positions_only_kerns(head,where)
1418    if trace_injections then
1419        trace(head,"positions")
1420    end
1421    local current     = head
1422    local prev        = nil
1423    local next        = nil
1424    local prevdisc    = nil
1425    local prevglyph   = nil
1426    local pre         = nil -- saves a lookup
1427    local post        = nil -- saves a lookup
1428    local replace     = nil -- saves a lookup
1429    local pretail     = nil -- saves a lookup
1430    local posttail    = nil -- saves a lookup
1431    local replacetail = nil -- saves a lookup
1432    while current do
1433        local next, char, id = isnextchar(current)
1434        if char then
1435            local p = rawget(properties,current)
1436            if p then
1437                local i = p.injections
1438                if i then
1439                    -- left|glyph|right
1440                    local yoffset = i.yoffset
1441                    if yoffset and yoffset ~= 0 then
1442                        -- use raise when advance
1443                        addyoffset(current,yscaled(current,yoffset))
1444                    end
1445                    local leftkern  = i.leftkern or 0
1446                    local rightkern = i.rightkern or 0
1447                    if leftkern ~= 0 then
1448                        leftkern = xscaled(current,leftkern)
1449                    end
1450                    if rightkern ~= 0 then
1451                        rightkern = xscaled(current,rightkern)
1452                    end
1453                    if leftkern ~= 0 then
1454                        if leftkern == -rightkern then
1455                            addxoffset(current,leftkern)
1456                            rightkern = 0
1457                        elseif prev and getid(prev) == glue_code then
1458                            if usespacefontkerns then
1459                                head = insertnodebefore(head,current,spacefontkern(leftkern))
1460                            else
1461                                setwidth(prev,getwidth(prev)+leftkern)
1462                            end
1463                        else
1464                            head = insertnodebefore(head,current,somekern(fontkern,leftkern,current))
1465                        end
1466                    end
1467                    if rightkern ~= 0 then
1468                        if next and getid(next) == glue_code then
1469                            if usespacefontkerns then
1470                                insertnodeafter(head,current,spacefontkern(rightkern))
1471                            else
1472                                setwidth(next, getwidth(next)+rightkern)
1473                            end
1474                        else
1475                            insertnodeafter(head,current,somekern(fontkern,rightkern,current))
1476                        end
1477                    end
1478                elseif next then
1479                    local i = p.emptyinjections
1480                    if i then
1481                        -- glyph|disc|glyph (special case)
1482                        local rightkern = i.rightkern
1483                        if rightkern and rightkern ~= 0 and getid(next) == disc_code then
1484                            local replace = getreplace(next)
1485                            if replace then
1486                                -- can't happen
1487                            else
1488                                setreplace(next,somekern(fontkern,xscaled(current,rightkern),current))
1489                            end
1490                        end
1491                    end
1492                end
1493                if prevdisc then
1494                    local done = false
1495                    if post then
1496                        local i = p.postinjections
1497                        if i then
1498                            local leftkern = i.leftkern
1499                            if leftkern and leftkern ~= 0 then
1500                                setlink(posttail,somekern(fontkern,xscaled(current,leftkern),current))
1501                                done = true
1502                            end
1503                        end
1504                    end
1505                    if replace then
1506                        local i = p.replaceinjections
1507                        if i then
1508                            local leftkern = i.leftkern
1509                            if leftkern and leftkern ~= 0 then
1510                                setlink(replacetail,somekern(fontkern,xscaled(current,leftkern),current))
1511                                done = true
1512                            end
1513                        end
1514                    else
1515                        local i = p.emptyinjections
1516                        if i then
1517                            -- new .. okay?
1518                            local leftkern = i.leftkern
1519                            if leftkern and leftkern ~= 0 then
1520                                replace = somekern(fontkern,xscaled(current,leftkern),current)
1521                                done = true
1522                            end
1523                        end
1524                    end
1525                    if done then
1526                        setdisc(prevdisc,pre,post,replace)
1527                    end
1528                end
1529            end
1530            prevdisc  = nil
1531            prevglyph = current
1532        elseif char == false then
1533            prevdisc  = nil
1534            prevglyph = current
1535        elseif id == disc_code then
1536            pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
1537            local done = false
1538            if pre then
1539                -- left|pre glyphs|right
1540                for n in nextchar, pre do
1541                    local p = rawget(properties,n)
1542                    if p then
1543                        local i = p.injections or p.preinjections
1544                        if i then
1545                            local yoffset   = i.yoffset
1546                            local leftkern  = i.leftkern
1547                            local rightkern = i.rightkern
1548                            if yoffset and yoffset ~= 0 then
1549                                addyoffset(n,yscaled(n,yoffset))
1550                            end
1551                            if leftkern and leftkern ~= 0 then
1552                                pre  = insertnodebefore(pre,n,somekern(fontkern,xscaled(n,leftkern),n))
1553                                done = true
1554                            end
1555                            if rightkern and rightkern ~= 0 then
1556                                insertnodeafter(pre,n,somekern(fontkern,xscaled(n,rightkern),n))
1557                                done = true
1558                            end
1559                        end
1560                    end
1561                end
1562            end
1563            if post then
1564                -- left|post glyphs|right
1565                for n in nextchar, post do
1566                    local p = rawget(properties,n)
1567                    if p then
1568                        local i = p.injections or p.postinjections
1569                        if i then
1570                            local yoffset   = i.yoffset
1571                            local leftkern  = i.leftkern
1572                            local rightkern = i.rightkern
1573                            if yoffset and yoffset ~= 0 then
1574                                addyoffset(n,yscaled(n,yoffset))
1575                            end
1576                            if leftkern and leftkern ~= 0 then
1577                                post = insertnodebefore(post,n,somekern(fontkern,xscaled(n,leftkern),n))
1578                                done = true
1579                            end
1580                            if rightkern and rightkern ~= 0 then
1581                                insertnodeafter(post,n,somekern(fontkern,xscaled(n,rightkern),n))
1582                                done = true
1583                            end
1584                        end
1585                    end
1586                end
1587            end
1588            if replace then
1589                -- left|replace glyphs|right
1590                for n in nextchar, replace do
1591                    local p = rawget(properties,n)
1592                    if p then
1593                        local i = p.injections or p.replaceinjections
1594                        if i then
1595                            local yoffset   = i.yoffset
1596                            local leftkern  = i.leftkern
1597                            local rightkern = i.rightkern
1598                            if yoffset and yoffset ~= 0 then
1599                                addyoffset(n,yscaled(n,yoffset))
1600                            end
1601                            if leftkern and leftkern ~= 0 then
1602                                replace = insertnodebefore(replace,n,somekern(fontkern,xscaled(n,leftkern),n))
1603                                done    = true
1604                            end
1605                            if rightkern and rightkern ~= 0 then
1606                                insertnodeafter(replace,n,somekern(fontkern,xscaled(n,rightkern),n))
1607                                done = true
1608                            end
1609                        end
1610                    end
1611                end
1612            end
1613            if prevglyph then
1614                -- there can only be useful properties when pre/replace start with a glyph
1615                if pre then
1616                    local p = rawget(properties,prevglyph)
1617                    if p then
1618                        local i = p.preinjections
1619                        if i then
1620                            -- glyph|pre glyphs
1621                            local rightkern = i.rightkern
1622                            if rightkern and rightkern ~= 0 then
1623                                pre  = insertnodebefore(pre,pre,somekern(fontkern,xscaled(prevglyph,rightkern),prevglyph))
1624                                done = true
1625                            end
1626                        end
1627                    end
1628                end
1629                if replace then
1630                    local p = rawget(properties,prevglyph)
1631                    if p then
1632                        local i = p.replaceinjections
1633                        if i then
1634                            -- glyph|replace glyphs
1635                            local rightkern = i.rightkern
1636                            if rightkern and rightkern ~= 0 then
1637                                replace = insertnodebefore(replace,replace,somekern(fontkern,xscaled(prevglyph,rightkern),prevglyph))
1638                                done    = true
1639                            end
1640                        end
1641                    end
1642                end
1643            end
1644            if done then
1645                setdisc(current,pre,post,replace)
1646            end
1647            prevglyph = nil
1648            prevdisc  = current
1649        else
1650            prevglyph = nil
1651            prevdisc  = nil
1652        end
1653        prev    = current
1654        current = next
1655    end
1656    --
1657    if keepregisteredcounts then
1658        keepregisteredcounts = false
1659    else
1660        nofregisteredpositions = 0
1661    end
1662    if trace_injections then
1663        show_result(head)
1664    end
1665    return head
1666end
1667
1668local function inject_positions_only_margins(head,where)
1669    if trace_injections then
1670        trace(head,"positions")
1671    end
1672    local current     = head
1673    local prev        = nil
1674    local next        = nil
1675    local prevdisc    = nil
1676    local prevglyph   = nil
1677    local pre         = nil -- saves a lookup
1678    local post        = nil -- saves a lookup
1679    local replace     = nil -- saves a lookup
1680    local pretail     = nil -- saves a lookup
1681    local posttail    = nil -- saves a lookup
1682    local replacetail = nil -- saves a lookup
1683    while current do
1684        local next, char, id = isnextchar(current)
1685        if char then
1686            local p = rawget(properties,current)
1687            if p then
1688                local i = p.injections
1689                if i then
1690                    -- left|glyph|right
1691                    local yoffset   = i.yoffset or 0
1692                    local leftkern  = i.leftkern or 0
1693                    local rightkern = i.rightkern or 0
1694                    if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then
1695                        addxymargins(n,-leftkern,-rightkern,yoffset) -- also scales
1696                    end
1697                elseif next then
1698                    local i = p.emptyinjections
1699                    if i then
1700                        -- glyph|disc|glyph (special case)
1701                        local rightkern = i.rightkern
1702                        if rightkern and rightkern ~= 0 and getid(next) == disc_code then
1703                            local replace = getreplace(next)
1704                            if replace then
1705                                -- can't happen
1706                            else
1707                                setreplace(next,somekern(fontkern,xscaled(current,rightkern),current))
1708                            end
1709                        end
1710                    end
1711                end
1712                if prevdisc then
1713                    if post then
1714                        local i = p.postinjections
1715                        if i then
1716                            local leftkern = i.leftkern
1717                            if leftkern and leftkern ~= 0 then
1718                                addxymargins(posttail,-leftkern)
1719                            end
1720                        end
1721                    end
1722                    if replace then
1723                        local i = p.replaceinjections
1724                        if i then
1725                            local leftkern = i.leftkern
1726                            if leftkern and leftkern ~= 0 then
1727                                addxymargins(replacetail,-leftkern)
1728                            end
1729                        end
1730                    else
1731                        local i = p.emptyinjections
1732                        if i then
1733                            -- new .. okay?
1734                            local leftkern = i.leftkern
1735                            if leftkern and leftkern ~= 0 then
1736                                -- prev disc | current
1737                                addxymargins(current,-leftkern) -- NEEDS CHECKING
1738                            end
1739                        end
1740                    end
1741                end
1742            end
1743            prevdisc  = nil
1744            prevglyph = current
1745        elseif char == false then
1746            prevdisc  = nil
1747            prevglyph = current
1748        elseif id == disc_code then
1749            pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
1750            if pre then
1751                -- left|pre glyphs|right
1752                for n in nextchar, pre do
1753                    local p = rawget(properties,n)
1754                    if p then
1755                        local i = p.injections or p.preinjections
1756                        if i then
1757                            local yoffset   = i.yoffset or 0
1758                            local leftkern  = i.leftkern or 0
1759                            local rightkern = i.rightkern or 0
1760                            if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then
1761                                addxymargins(n,-leftkern,-rightkern,yoffset) -- also scales
1762                            end
1763                        end
1764                    end
1765                end
1766            end
1767            if post then
1768                -- left|post glyphs|right
1769                for n in nextchar, post do
1770                    local p = rawget(properties,n)
1771                    if p then
1772                        local i = p.injections or p.postinjections
1773                        if i then
1774                            local yoffset   = i.yoffset or 0
1775                            local leftkern  = i.leftkern or 0
1776                            local rightkern = i.rightkern or 0
1777                            if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then
1778                                addxymargins(n,-leftkern,-rightkern,yoffset) -- also scales
1779                            end
1780                        end
1781                    end
1782                end
1783            end
1784            if replace then
1785                -- left|replace glyphs|right
1786                for n in nextchar, replace do
1787                    local p = rawget(properties,n)
1788                    if p then
1789                        local i = p.injections or p.replaceinjections
1790                        if i then
1791                            local yoffset   = i.yoffset or 0
1792                            local leftkern  = i.leftkern or 0
1793                            local rightkern = i.rightkern or 0
1794                            if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then
1795                                addxymargins(n,-leftkern,-rightkern,yoffset) -- also scales
1796                            end
1797                        end
1798                    end
1799                end
1800            end
1801            if prevglyph then
1802                if pre then
1803                    local p = rawget(properties,prevglyph)
1804                    if p then
1805                        local i = p.preinjections
1806                        if i then
1807                            -- glyph|pre glyphs
1808                            local rightkern = i.rightkern
1809                            if rightkern and rightkern ~= 0 then
1810                                addxymargins(pre,-rightkern,0,0)
1811                            end
1812                        end
1813                    end
1814                end
1815                if replace then
1816                    local p = rawget(properties,prevglyph)
1817                    if p then
1818                        local i = p.replaceinjections
1819                        if i then
1820                            -- glyph|replace glyphs
1821                            local rightkern = i.rightkern
1822                            if rightkern and rightkern ~= 0 then
1823                                addxymargins(replace,-rightkern,0,0)
1824                            end
1825                        end
1826                    end
1827                end
1828            end
1829            prevglyph = nil
1830            prevdisc  = current
1831        else
1832            prevglyph = nil
1833            prevdisc  = nil
1834        end
1835        prev    = current
1836        current = next
1837    end
1838    --
1839    if keepregisteredcounts then
1840        keepregisteredcounts = false
1841    else
1842        nofregisteredpositions = 0
1843    end
1844    if trace_injections then
1845        show_result(head)
1846    end
1847    return head
1848end
1849
1850local function showoffset(n,flag)
1851    local x, y = getoffsets(n)
1852    if x ~= 0 or y ~= 0 then
1853        setcolor(n,"darkgray")
1854    end
1855end
1856
1857local function processmark(p,n,pn) -- p = basenode
1858    local px, py = getoffsets(p)
1859    local nx, ny = getoffsets(n)
1860    local sx, sy = getxyscales(n) -- we assume n and p to have equal scales
1861    local ox = 0
1862    local rightkern = nil
1863    local pp = rawget(properties,p)
1864    if pp then
1865        pp = pp.injections
1866        if pp then
1867            rightkern = pp.rightkern
1868        end
1869    end
1870    local markdir = pn.markdir
1871    if rightkern then -- x and w ~= 0
1872        ox = px - sx * (pn.markx or 0) - rightkern
1873        if markdir and markdir < 0 then
1874            -- kern(w-x) glyph(p) kern(x) mark(n)
1875            if not pn.markmark then
1876                ox = ox + sx * (pn.leftkern or 0)
1877            end
1878        else
1879            -- kern(x) glyph(p) kern(w-x) mark(n)
1880            --
1881            -- According to Kai we don't need to handle leftkern here but I'm
1882            -- pretty sure I've run into a case where it was needed so maybe
1883            -- some day we need something more clever here.
1884            --
1885            -- maybe we need to apply both then
1886            --
1887            if false then
1888                -- a mark with kerning (maybe husayni needs it )
1889                local leftkern = pp.leftkern
1890                if leftkern then
1891                    ox = ox - sx * leftkern
1892                end
1893            end
1894        end
1895    else
1896        ox = px - sx * (pn.markx or 0)
1897        if markdir and markdir < 0 then
1898            if not pn.markmark then
1899                local leftkern = pn.leftkern
1900                if leftkern then
1901                    ox = ox + sx * leftkern -- husayni needs it
1902                end
1903            end
1904        end
1905        if pn.checkmark then
1906            local wn = getwidth(n) -- in arial marks have widths
1907            if wn and wn ~= 0 then
1908                wn = wn/2
1909                if trace_injections then
1910                    report_injections("correcting non zero width mark %C",getchar(n))
1911                end
1912                -- -- bad: we should center
1913                --
1914                -- pn.leftkern  = -wn
1915                -- pn.rightkern = -wn
1916                --
1917                -- -- we're too late anyway as kerns are already injected so we do it the
1918                -- -- ugly way (no checking if the previous is already a kern) .. maybe we
1919                -- -- should fix the font instead
1920                --
1921                -- todo: head and check for prev / next kern
1922                --
1923                insertnodebefore(n,n,somekern(fontkern,-wn,n))
1924                insertnodeafter(n,n,somekern(fontkern,-wn,n))
1925            end
1926        end
1927    end
1928    local oy = ny + py + sy * (pn.marky or 0)
1929    if not pn.markmark then
1930        local yoffset = pn.yoffset
1931        if yoffset then
1932            oy = oy + sy * yoffset -- husayni needs it
1933        end
1934    end
1935    setoffsets(n,ox,oy)
1936    if trace_marks then
1937        showoffset(n,true)
1938    end
1939end
1940
1941local function inject_everything(head,where)
1942    if trace_injections then
1943        trace(head,"everything")
1944    end
1945    local hascursives   = nofregisteredcursives > 0
1946    local hasmarks      = nofregisteredmarks    > 0
1947    --
1948    local current       = head
1949    local last          = nil
1950    local prev          = nil
1951    local next          = nil
1952    local prevdisc      = nil
1953    local prevglyph     = nil
1954    local pre           = nil -- saves a lookup
1955    local post          = nil -- saves a lookup
1956    local replace       = nil -- saves a lookup
1957    local pretail       = nil -- saves a lookup
1958    local posttail      = nil -- saves a lookup
1959    local replacetail   = nil -- saves a lookup
1960    --
1961    local cursiveanchor = nil
1962    local minc          = 0
1963    local maxc          = 0
1964    local glyphs        = { }
1965    local marks         = { }
1966    local nofmarks      = 0
1967    --
1968    while current do
1969        local next, char, id = isnextchar(current)
1970        if char then
1971            local p = rawget(properties,current)
1972            if p then
1973                local i = p.injections
1974                if i then
1975                    local pm = i.markbasenode
1976                    if pm then
1977                        nofmarks = nofmarks + 1
1978                        marks[nofmarks] = current
1979                    else
1980                        local yoffset = i.yoffset
1981                        if yoffset and yoffset ~= 0 then
1982                            addyoffset(current,yscaled(current,yoffset)) -- or just * sy
1983                        end
1984                        if hascursives then
1985                            local cursivex = i.cursivex
1986                            if cursivex then
1987                                if cursiveanchor then
1988                                    if cursivex ~= 0 then
1989                                        i.leftkern = (i.leftkern or 0) + cursivex
1990                                    end
1991                                    if maxc == 0 then
1992                                        minc = 1
1993                                        maxc = 1
1994                                        glyphs[1] = cursiveanchor
1995                                    else
1996                                        maxc = maxc + 1
1997                                        glyphs[maxc] = cursiveanchor
1998                                    end
1999                                    properties[cursiveanchor].cursivedy = i.cursivey -- cursiveprops
2000                                    last = current
2001                                else
2002                                    maxc = 0
2003                                end
2004                            elseif maxc > 0 then
2005                                local nx, ny = getoffsets(current)
2006                                for i=maxc,minc,-1 do
2007                                    local ti = glyphs[i]
2008                                    ny = ny + yscaled(current,properties[ti].cursivedy) -- stepwise increment
2009                                    setoffsets(ti,false,ny)
2010                                    if trace_cursive then
2011                                        showoffset(ti)
2012                                    end
2013                                end
2014                                maxc = 0
2015                                cursiveanchor = nil
2016                            end
2017                            if i.cursiveanchor then
2018                                cursiveanchor = current -- no need for both now
2019                            else
2020                                if maxc > 0 then
2021                                    local nx, ny = getoffsets(current)
2022                                    for i=maxc,minc,-1 do
2023                                        local ti = glyphs[i]
2024                                        ny = ny + yscaled(current,properties[ti].cursivedy) -- stepwise increment
2025                                        setoffsets(ti,false,ny)
2026                                        if trace_cursive then
2027                                            showoffset(ti)
2028                                        end
2029                                    end
2030                                    maxc = 0
2031                                end
2032                                cursiveanchor = nil
2033                            end
2034                        end
2035                        -- left|glyph|right
2036                        local leftkern  = i.leftkern
2037                        local rightkern = i.rightkern
2038                        if leftkern and leftkern ~= 0 then
2039                            local opposite = rightkern and leftkern == -rightkern
2040                            leftkern = xscaled(current,leftkern)
2041                            if opposite then
2042                                addxoffset(current,leftkern)
2043                                rightkern = 0
2044                            elseif prev and getid(prev) == glue_code then
2045                                if usespacefontkerns then
2046                                    head = insertnodebefore(head,current,spacefontkern(leftkern))
2047                                else
2048                                    setwidth(prev, getwidth(prev) + leftkern)
2049                                end
2050                            else
2051                                head = insertnodebefore(head,current,somekern(fontkern,leftkern,current))
2052                            end
2053                        end
2054                        if rightkern and rightkern ~= 0 then
2055                            rightkern = xscaled(current,rightkern)
2056                            if next and getid(next) == glue_code then
2057                                if usespacefontkerns then
2058                                    insertnodeafter(head,current,spacefontkern(rightkern))
2059                                else
2060                                    setwidth(next, getwidth(next) + rightkern)
2061                                end
2062                            else
2063                                insertnodeafter(head,current,somekern(fontkern,rightkern,current))
2064                            end
2065                        end
2066                    end
2067                elseif next then
2068                    local i = p.emptyinjections
2069                    if i then
2070                        -- glyph|disc|glyph (special case)
2071                        local rightkern = i.rightkern
2072                        if rightkern and rightkern ~= 0 and getid(next) == disc_code then
2073                            local replace = getreplace(next)
2074                            if replace then
2075                                -- can't happen
2076                            else
2077                                setreplace(next,somekern(fontkern,xscaled(current,rightkern),current))
2078                            end
2079                        end
2080                    end
2081                end
2082                if prevdisc then
2083                    if p then
2084                        local done = false
2085                        if post then
2086                            local i = p.postinjections
2087                            if i then
2088                                local leftkern = i.leftkern
2089                                if leftkern and leftkern ~= 0 then
2090                                    setlink(posttail,somekern(fontkern,xscaled(current,leftkern),current))
2091                                    done = true
2092                                end
2093                            end
2094                        end
2095                        if replace then
2096                            local i = p.replaceinjections
2097                            if i then
2098                                local leftkern = i.leftkern
2099                                if leftkern and leftkern ~= 0 then
2100                                    setlink(replacetail,somekern(fontkern,xscaled(current,leftkern),current))
2101                                    done = true
2102                                end
2103                            end
2104                        else
2105                            local i = p.emptyinjections
2106                            if i then
2107                                local leftkern = i.leftkern
2108                                if leftkern and leftkern ~= 0 then
2109                                    replace = somekern(fontkern,xscaled(current,leftkern),current)
2110                                    done    = true
2111                                end
2112                            end
2113                        end
2114                        if done then
2115                            setdisc(prevdisc,pre,post,replace)
2116                        end
2117                    end
2118                end
2119            else
2120                -- cursive
2121                if hascursives and maxc > 0 then
2122                    local nx, ny = getoffsets(current)
2123                    for i=maxc,minc,-1 do
2124                        local ti = glyphs[i]
2125                        ny = ny + yscaled(current,properties[ti].cursivedy)
2126                        setoffsets(ti,false,ny) -- can be mark, we could use properties
2127                    end
2128                    maxc = 0
2129                    cursiveanchor = nil
2130                end
2131            end
2132            prevdisc  = nil
2133            prevglyph = current
2134        elseif char == false then
2135         -- base = nil
2136            prevdisc  = nil
2137            prevglyph = current
2138        elseif id == disc_code then
2139         -- base = nil
2140            pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)
2141            local done = false
2142            if pre then
2143                -- left|pre glyphs|right
2144                for n in nextchar, pre do
2145                    local p = rawget(properties,n)
2146                    if p then
2147                        local i = p.injections or p.preinjections
2148                        if i then
2149                            local yoffset = i.yoffset
2150                            if yoffset and yoffset ~= 0 then
2151                                addyoffset(n,yscaled(n,yoffset))
2152                            end
2153                            local leftkern = i.leftkern
2154                            if leftkern and leftkern ~= 0 then
2155                                pre  = insertnodebefore(pre,n,somekern(fontkern,xscaled(n,leftkern),n))
2156                                done = true
2157                            end
2158                            local rightkern = i.rightkern
2159                            if rightkern and rightkern ~= 0 then
2160                                insertnodeafter(pre,n,somekern(fontkern,xscaled(n,rightkern),n))
2161                                done = true
2162                            end
2163                            if hasmarks then
2164                                local pm = i.markbasenode
2165                                if pm then
2166                                    processmark(pm,n,i)
2167                                end
2168                            end
2169                        end
2170                    end
2171                end
2172            end
2173            if post then
2174                -- left|post glyphs|right
2175                for n in nextchar, post do
2176                    local p = rawget(properties,n)
2177                    if p then
2178                        local i = p.injections or p.postinjections
2179                        if i then
2180                            local yoffset = i.yoffset
2181                            if yoffset and yoffset ~= 0 then
2182                                addyoffset(n,yscaled(n,yoffset))
2183                            end
2184                            local leftkern = i.leftkern
2185                            if leftkern and leftkern ~= 0 then
2186                                post = insertnodebefore(post,n,somekern(fontkern,xscaled(n,leftkern),n))
2187                                done = true
2188                            end
2189                            local rightkern = i.rightkern
2190                            if rightkern and rightkern ~= 0 then
2191                                done = true
2192                                insertnodeafter(post,n,somekern(fontkern,xscaled(n,rightkern),n))
2193                            end
2194                            if hasmarks then
2195                                local pm = i.markbasenode
2196                                if pm then
2197                                    processmark(pm,n,i)
2198                                end
2199                            end
2200                        end
2201                    end
2202                end
2203            end
2204            if replace then
2205                -- left|replace glyphs|right
2206                for n in nextchar, replace do
2207                    local p = rawget(properties,n)
2208                    if p then
2209                        local i = p.injections or p.replaceinjections
2210                        if i then
2211                            local yoffset = i.yoffset
2212                            if yoffset and yoffset ~= 0 then
2213                                addyoffset(n,yscaled(n,yoffset))
2214                            end
2215                            local leftkern = i.leftkern
2216                            if leftkern and leftkern ~= 0 then
2217                                replace = insertnodebefore(replace,n,somekern(fontkern,xscaled(n,leftkern),n))
2218                                done    = true
2219                            end
2220                            local rightkern = i.rightkern
2221                            if rightkern and rightkern ~= 0 then
2222                                insertnodeafter(replace,n,somekern(fontkern,xscaled(n,rightkern),n))
2223                                done = true
2224                            end
2225                            if hasmarks then
2226                                local pm = i.markbasenode
2227                                if pm then
2228                                    processmark(pm,n,i)
2229                                end
2230                            end
2231                        end
2232                    end
2233                end
2234            end
2235            if prevglyph then
2236                if pre then
2237                    local p = rawget(properties,prevglyph)
2238                    if p then
2239                        local i = p.preinjections
2240                        if i then
2241                            -- glyph|pre glyphs
2242                            local rightkern = i.rightkern
2243                            if rightkern and rightkern ~= 0 then
2244                                pre  = insertnodebefore(pre,pre,somekern(fontkern,xscaled(prevglyph,rightkern),prevglyph))
2245                                done = true
2246                            end
2247                        end
2248                    end
2249                end
2250                if replace then
2251                    local p = rawget(properties,prevglyph)
2252                    if p then
2253                        local i = p.replaceinjections
2254                        if i then
2255                            -- glyph|replace glyphs
2256                            local rightkern = i.rightkern
2257                            if rightkern and rightkern ~= 0 then
2258                                replace = insertnodebefore(replace,replace,somekern(fontkern,xscaled(prevglyph,rightkern),prevglyph))
2259                                done    = true
2260                            end
2261                        end
2262                    end
2263                end
2264            end
2265            if done then
2266                setdisc(current,pre,post,replace)
2267            end
2268            prevglyph = nil
2269            prevdisc  = current
2270        else
2271         -- base      = nil
2272            prevglyph = nil
2273            prevdisc  = nil
2274        end
2275        prev    = current
2276        current = next
2277    end
2278    -- cursive
2279    if hascursives and maxc > 0 then
2280        local nx, ny = getoffsets(last)
2281        for i=maxc,minc,-1 do
2282            local ti = glyphs[i]
2283            ny = ny + yscaled(properties[ti].cursivedy)
2284            setoffsets(ti,false,ny)
2285            if trace_cursive then
2286                showoffset(ti)
2287            end
2288        end
2289    end
2290    --
2291    if nofmarks > 0 then
2292        for i=1,nofmarks do
2293            local m = marks[i]
2294            local p = rawget(properties,m)
2295            local i = p.injections
2296            local b = i.markbasenode
2297            processmark(b,m,i)
2298        end
2299    elseif hasmarks then
2300        -- sometyhing bad happened
2301    end
2302    --
2303    if keepregisteredcounts then
2304        keepregisteredcounts = false
2305    else
2306        nofregisteredkerns     = 0
2307        nofregisteredpositions = 0
2308        nofregisteredmarks     = 0
2309        nofregisteredcursives  = 0
2310    end
2311    if trace_injections then
2312        show_result(head)
2313    end
2314    return head
2315end
2316
2317-- space triggers
2318
2319local triggers = false
2320
2321function nodes.injections.setspacekerns(font,sequence)
2322    if triggers then
2323        triggers[font] = sequence
2324    else
2325        triggers = { [font] = sequence }
2326    end
2327end
2328
2329local threshold = 1 -- todo: add a few methods for context
2330
2331directives.register("otf.threshold", function(v) threshold = tonumber(v) or 1 end)
2332
2333local getthreshold  = function(font)
2334    local p = parameters[font]
2335    local f = p.factor
2336    local s = p.spacing
2337    local t = threshold * (s and s.width or p.space or 0) - 2
2338    return t > 0 and t or 0, f
2339end
2340
2341injections.getthreshold = getthreshold
2342
2343function injections.isspace(n,threshold,id)
2344    if (id or getid(n)) == glue_code then
2345        local w = getwidth(n)
2346        if threshold and w > threshold then -- was >=
2347            return 32
2348        end
2349    end
2350end
2351
2352-- We have a plugin so that Kai can use the next in plain. Such a plugin is rather application
2353-- specific.
2354--
2355-- local getboth = nodes.direct.getboth
2356-- local getid   = nodes.direct.getid
2357-- local getprev = nodes.direct.getprev
2358-- local getnext = nodes.direct.getnext
2359--
2360-- local whatsit_code = nodes.nodecodes.whatsit
2361-- local glyph_code   = nodes.nodecodes.glyph
2362--
2363-- local function getspaceboth(n) -- fragile: what it prev/next has no width field
2364--     local prev, next = getboth(n)
2365--     while prev and (getid(prev) == whatsit_code or (getwidth(prev) == 0 and getid(prev) ~= glyph_code)) do
2366--         prev = getprev(prev)
2367--     end
2368--     while next and (getid(next) == whatsit_code or (getwidth(next) == 0 and getid(next) ~= glyph_code)) do
2369--         next = getnext(next)
2370--     end
2371-- end
2372--
2373-- injections.installgetspaceboth(getspaceboth)
2374
2375local getspaceboth = getboth
2376
2377function injections.installgetspaceboth(gb)
2378    getspaceboth = gb or getboth
2379end
2380
2381local function injectspaces(head)
2382
2383    if not triggers then
2384        return head
2385    end
2386    local lastfont   = nil
2387    local spacekerns = nil
2388    local leftkerns  = nil
2389    local rightkerns = nil
2390    local factor     = 0
2391    local threshold  = 0
2392    local leftkern   = false
2393    local rightkern  = false
2394    local xscale     = 1
2395
2396    local function updatefont(font,trig)
2397        leftkerns  = trig.left
2398        rightkerns = trig.right
2399        lastfont   = font
2400        threshold,
2401        factor     = getthreshold(font)
2402    end
2403
2404    for n in nextglue, head do
2405        local prev, next = getspaceboth(n)
2406        local prevchar, prevfont = getcharspec(prev)
2407        local nextchar, nextfont = getcharspec(next)
2408        if nextchar then
2409            local trig = triggers[nextfont]
2410            if trig then
2411                if lastfont ~= nextfont then
2412                    updatefont(nextfont,trig)
2413                end
2414                if rightkerns then
2415                    rightkern = rightkerns[nextchar]
2416                    if rightkern then
2417                        xscale = getxscale(next)
2418                    end
2419                end
2420            end
2421        end
2422        if prevchar then
2423            local trig = triggers[prevfont]
2424            if trig then
2425                if lastfont ~= prevfont then
2426                    updatefont(prevfont,trig)
2427                end
2428                if leftkerns then
2429                    leftkern = leftkerns[prevchar]
2430                    if leftkern then
2431                        xscale = getxscale(prev)
2432                    end
2433                end
2434            end
2435        end
2436        if leftkern then
2437            local old = getwidth(n)
2438            if old > xscale*threshold then
2439                if rightkern then
2440                    if usespacefontkerns then
2441                        local lnew = leftkern  * factor
2442                        local rnew = rightkern * factor
2443                        if trace_spaces then
2444                            report_spaces("%C [%p + %p + %p] %C",prevchar,lnew,old,rnew,nextchar)
2445                        end
2446                        lnew = lnew * xscale
2447                        rnew = rnew * xscale
2448                        head = insertnodebefore(head,n,spacefontkern(lnew))
2449                        insertnodeafter(head,n,spacefontkern(rnew))
2450                    else
2451                        local new = old + (leftkern + rightkern) * factor * xscale
2452                        if trace_spaces then
2453                            report_spaces("%C [%p -> %p] %C",prevchar,old,new,nextchar)
2454                        end
2455                        setwidth(n,new)
2456                    end
2457                    rightkern = false
2458                else
2459                    if usespacefontkerns then
2460                        local new = leftkern * factor * xscale
2461                        if trace_spaces then
2462                            report_spaces("%C [%p + %p]",prevchar,old,new)
2463                        end
2464                        insertnodeafter(head,n,spacefontkern(new)) -- tricky with traverse but ok
2465                    else
2466                        local new = old + leftkern * factor * xscale
2467                        if trace_spaces then
2468                            report_spaces("%C [%p -> %p]",prevchar,old,new)
2469                        end
2470                        setwidth(n,new)
2471                    end
2472                end
2473            end
2474            leftkern  = false
2475        elseif rightkern then
2476            local old = getwidth(n)
2477            if old > xscale*threshold then
2478                local new = rightkern * factor * xscale
2479                if usespacefontkerns then
2480                    if trace_spaces then
2481                        report_spaces("[%p + %p] %C",old,new,nextchar)
2482                    end
2483                    insertnodeafter(head,n,spacefontkern(new))
2484                else
2485                    new = old + new
2486                    if trace_spaces then
2487                        report_spaces("[%p -> %p] %C",old,new,nextchar)
2488                    end
2489                    setwidth(n,new)
2490                end
2491            else
2492                -- message
2493            end
2494            rightkern = false
2495        end
2496    end
2497
2498    triggers = false
2499
2500    return head
2501end
2502
2503-- When advance is found to be okay there will be split functions (which is abit
2504-- more efficient) but first we need to have scales done (we can also have addoffset
2505-- and alike then)
2506
2507function injections.handler(head,where)
2508    if triggers then
2509        head = injectspaces(head)
2510    end
2511    if nofregisteredmarks > 0 or nofregisteredcursives > 0 then
2512        if trace_injections then
2513            report_injections("injection variant %a (%s)","everything","kerns")
2514        end
2515        -- I will do useadvance here when I am playing with fonts that follow this
2516        -- injection routem, which is seldom.
2517        return inject_everything(head,where)
2518    elseif nofregisteredpositions > 0 then
2519        if trace_injections then
2520            report_injections("injection variant %a (%s)","positions",useadvance and "margins" or "kerns")
2521        end
2522        if useadvance then
2523            return inject_positions_only_margins(head,where)
2524        else
2525            return inject_positions_only_kerns(head,where)
2526        end
2527    elseif nofregisteredkerns > 0 then
2528        if trace_injections then
2529            report_injections("injection variant %a (%s)","kerns",useadvance and "margins" or "kerns")
2530        end
2531        if useadvance then
2532            return inject_kerns_only_margins(head,where)
2533--             return inject_positions_only_margins(head,where)
2534        else
2535            return inject_kerns_only_kerns(head,where)
2536        end
2537    else
2538        return head
2539    end
2540end
2541
2542