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