if not modules then modules = { } end modules ['font-inj'] = {
    version   = 1.001,
    comment   = "companion to font-lib.mkiv",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files",
}

-- This property based variant is not faster but looks nicer than the attribute one. We
-- need to use rawget (which is apbout 4 times slower than a direct access but we cannot
-- get/set that one for our purpose! This version does a bit more with discretionaries
-- (and Kai has tested it with his collection of weird fonts.)

-- There is some duplicate code here (especially in the the pre/post/replace branches) but
-- we go for speed. We could store a list of glyph and mark nodes when registering but it's
-- cleaner to have an identification pass here. Also, I need to keep tracing in mind so
-- being too clever here is dangerous.

-- The subtype test is not needed as there will be no (new) properties set, given that we
-- reset the properties.

if not nodes.properties then return end

local next, rawget = next, rawget
local fastcopy = table.fastcopy

local trace_injections = false  trackers.register("fonts.injections", function(v) trace_injections = v end)

local report_injections = logs.reporter("fonts","injections")

local attributes, nodes, node = attributes, nodes, node

fonts                    = fonts
local fontdata           = fonts.hashes.identifiers

nodes.injections         = nodes.injections or { }
local injections         = nodes.injections

local nodecodes          = nodes.nodecodes
local glyph_code         = nodecodes.glyph
local disc_code          = nodecodes.disc
local kern_code          = nodecodes.kern

local nuts               = nodes.nuts
local nodepool           = nuts.pool

local newkern            = nodepool.kern

local tonode             = nuts.tonode
local tonut              = nuts.tonut

local getfield           = nuts.getfield
local setfield           = nuts.setfield
local getnext            = nuts.getnext
local getprev            = nuts.getprev
local getid              = nuts.getid
local getfont            = nuts.getfont
local getsubtype         = nuts.getsubtype
local getchar            = nuts.getchar

local traverse_id        = nuts.traverse_id
local insert_node_before = nuts.insert_before
local insert_node_after  = nuts.insert_after
local find_tail          = nuts.tail

local properties         = nodes.properties.data

function injections.installnewkern(nk)
    newkern = nk or newkern
end

local nofregisteredkerns    = 0
local nofregisteredpairs    = 0
local nofregisteredmarks    = 0
local nofregisteredcursives = 0
local keepregisteredcounts  = false

function injections.keepcounts()
    keepregisteredcounts = true
end

function injections.resetcounts()
    nofregisteredkerns    = 0
    nofregisteredpairs    = 0
    nofregisteredmarks    = 0
    nofregisteredcursives = 0
    keepregisteredcounts  = false
end

-- We need to make sure that a possible metatable will not kick in unexpectedly.

-- function injections.reset(n)
--     local p = rawget(properties,n)
--     if p and rawget(p,"injections") then
--         p.injections = nil
--     end
-- end

-- function injections.copy(target,source)
--     local sp = rawget(properties,source)
--     if sp then
--         local tp = rawget(properties,target)
--         local si = rawget(sp,"injections")
--         if si then
--             si = fastcopy(si)
--             if tp then
--                 tp.injections = si
--             else
--                 propertydata[target] = {
--                     injections = si,
--                 }
--             end
--         else
--             if tp then
--                 tp.injections = nil
--             end
--         end
--     end
-- end

function injections.reset(n)
    local p = rawget(properties,n)
    if p then
        p.injections = false -- { }
    else
        properties[n] = false -- { injections = { } }
    end
end

function injections.copy(target,source)
    local sp = rawget(properties,source)
    if sp then
        local tp = rawget(properties,target)
        local si = rawget(sp,"injections")
        if si then
            si = fastcopy(si)
            if tp then
                tp.injections = si
            else
                propertydata[target] = {
                    injections = si,
                }
            end
        elseif tp then
            tp.injections = false -- { }
        else
            properties[target] = { injections = { } }
        end
    else
        local tp = rawget(properties,target)
        if tp then
            tp.injections = false -- { }
        else
            properties[target] = false -- { injections = { } }
        end
    end
end

function injections.setligaindex(n,index)
    local p = rawget(properties,n)
    if p then
        local i = rawget(p,"injections")
        if i then
            i.ligaindex = index
        else
            p.injections = {
                ligaindex = index
            }
        end
    else
        properties[n] = {
            injections = {
                ligaindex = index
            }
        }
    end
end

function injections.getligaindex(n,default)
    local p = rawget(properties,n)
    if p then
        local i = rawget(p,"injections")
        if i then
            return i.ligaindex or default
        end
    end
    return default
end

function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) -- hm: nuts or nodes
    local dx =  factor*(exit[1]-entry[1])
    local dy = -factor*(exit[2]-entry[2])
    local ws = tfmstart.width
    local wn = tfmnext.width
    nofregisteredcursives = nofregisteredcursives + 1
    if rlmode < 0 then
        dx = -(dx + wn)
    else
        dx = dx - ws
    end
    --
    local p = rawget(properties,start)
    if p then
        local i = rawget(p,"injections")
        if i then
            i.cursiveanchor = true
        else
            p.injections = {
                cursiveanchor = true,
            }
        end
    else
        properties[start] = {
            injections = {
                cursiveanchor = true,
            },
        }
    end
    local p = rawget(properties,nxt)
    if p then
        local i = rawget(p,"injections")
        if i then
            i.cursivex = dx
            i.cursivey = dy
        else
            p.injections = {
                cursivex = dx,
                cursivey = dy,
            }
        end
    else
        properties[nxt] = {
            injections = {
                cursivex = dx,
                cursivey = dy,
            },
        }
    end
    return dx, dy, nofregisteredcursives
end

function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2lflag & tfmchr not used
    local x = factor*spec[1]
    local y = factor*spec[2]
    local w = factor*spec[3]
    local h = factor*spec[4]
    if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then -- okay?
        local yoffset   = y - h
        local leftkern  = x      -- both kerns are set in a pair kern compared
        local rightkern = w - x  -- to normal kerns where we set only leftkern
        if leftkern ~= 0 or rightkern ~= 0 or yoffset ~= 0 then
            nofregisteredpairs = nofregisteredpairs + 1
            if rlmode and rlmode < 0 then
                leftkern, rightkern = rightkern, leftkern
            end
            if not injection then
                injection = "injections"
            end
            local p = rawget(properties,current)
            if p then
                local i = rawget(p,injection)
                if i then
                    if leftkern ~= 0 then
                        i.leftkern = (i.leftkern  or 0) + leftkern
                    end
                    if rightkern ~= 0 then
                        i.rightkern = (i.rightkern or 0) + rightkern
                    end
                    if yoffset ~= 0 then
                        i.yoffset = (i.yoffset or 0) + yoffset
                    end
                elseif leftkern ~= 0 or rightkern ~= 0 then
                    p[injection] = {
                        leftkern  = leftkern,
                        rightkern = rightkern,
                        yoffset   = yoffset,
                    }
                else
                    p[injection] = {
                        yoffset = yoffset,
                    }
                end
            elseif leftkern ~= 0 or rightkern ~= 0 then
                properties[current] = {
                    [injection] = {
                        leftkern  = leftkern,
                        rightkern = rightkern,
                        yoffset   = yoffset,
                    },
                }
            else
                properties[current] = {
                    [injection] = {
                        yoffset = yoffset,
                    },
                }
            end
            return x, y, w, h, nofregisteredpairs
         end
    end
    return x, y, w, h -- no bound
end

-- This needs checking for rl < 0 but it is unlikely that a r2l script uses kernclasses between
-- glyphs so we're probably safe (KE has a problematic font where marks interfere with rl < 0 in
-- the previous case)

function injections.setkern(current,factor,rlmode,x,injection)
    local dx = factor * x
    if dx ~= 0 then
        nofregisteredkerns = nofregisteredkerns + 1
        local p = rawget(properties,current)
        if not injection then
            injection = "injections"
        end
        if p then
            local i = rawget(p,injection)
            if i then
                i.leftkern = dx + (i.leftkern or 0)
            else
                p[injection] = {
                    leftkern = dx,
                }
            end
        else
            properties[current] = {
                [injection] = {
                    leftkern = dx,
                },
            }
        end
        return dx, nofregisteredkerns
    else
        return 0, 0
    end
end

function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk) -- ba=baseanchor, ma=markanchor
    local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2])
    nofregisteredmarks = nofregisteredmarks + 1
 -- markanchors[nofregisteredmarks] = base
    if rlmode >= 0 then
        dx = tfmbase.width - dx -- see later commented ox
    end
    local p = rawget(properties,start)
    -- hm, dejavu serif does a sloppy mark2mark before mark2base
    if p then
        local i = rawget(p,"injections")
        if i then
            if i.markmark then
                -- out of order mkmk: yes or no or option
            else
                i.markx        = dx
                i.marky        = dy
                i.markdir      = rlmode or 0
                i.markbase     = nofregisteredmarks
                i.markbasenode = base
                i.markmark     = mkmk
            end
        else
            p.injections = {
                markx        = dx,
                marky        = dy,
                markdir      = rlmode or 0,
                markbase     = nofregisteredmarks,
                markbasenode = base,
                markmark     = mkmk,
            }
        end
    else
        properties[start] = {
            injections = {
                markx        = dx,
                marky        = dy,
                markdir      = rlmode or 0,
                markbase     = nofregisteredmarks,
                markbasenode = base,
                markmark     = mkmk,
            },
        }
    end
    return dx, dy, nofregisteredmarks
end

local function dir(n)
    return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset"
end

local function showchar(n,nested)
    local char = getchar(n)
    report_injections("%wfont %s, char %U, glyph %c",nested and 2 or 0,getfont(n),char,char)
end

local function show(n,what,nested,symbol)
    if n then
        local p = rawget(properties,n)
        if p then
            local i = rawget(p,what)
            if i then
                local leftkern  = i.leftkern  or 0
                local rightkern = i.rightkern or 0
                local yoffset   = i.yoffset   or 0
                local markx     = i.markx     or 0
                local marky     = i.marky     or 0
                local markdir   = i.markdir   or 0
                local markbase  = i.markbase  or 0
                local cursivex  = i.cursivex  or 0
                local cursivey  = i.cursivey  or 0
                local ligaindex = i.ligaindex or 0
                local cursbase  = i.cursiveanchor
                local margin    = nested and 4 or 2
                --
                if rightkern ~= 0 or yoffset ~= 0 then
                    report_injections("%w%s pair: lx %p, rx %p, dy %p",margin,symbol,leftkern,rightkern,yoffset)
                elseif leftkern ~= 0 then
                    report_injections("%w%s kern: dx %p",margin,symbol,leftkern)
                end
                if markx ~= 0 or marky ~= 0 or markbase ~= 0 then
                    report_injections("%w%s mark: dx %p, dy %p, dir %s, base %s",margin,symbol,markx,marky,markdir,markbase ~= 0 and "yes" or "no")
                end
                if cursivex ~= 0 or cursivey ~= 0 then
                    if cursbase then
                        report_injections("%w%s curs: base dx %p, dy %p",margin,symbol,cursivex,cursivey)
                    else
                        report_injections("%w%s curs: dx %p, dy %p",margin,symbol,cursivex,cursivey)
                    end
                elseif cursbase then
                    report_injections("%w%s curs: base",margin,symbol)
                end
                if ligaindex ~= 0 then
                    report_injections("%w%s liga: index %i",margin,symbol,ligaindex)
                end
            end
        end
    end
end

local function showsub(n,what,where)
    report_injections("begin subrun: %s",where)
    for n in traverse_id(glyph_code,n) do
        showchar(n,where)
        show(n,what,where," ")
    end
    report_injections("end subrun")
end

local function trace(head,where)
    report_injections("begin run %s: %s kerns, %s pairs, %s marks and %s cursives registered",
        where or "",nofregisteredkerns,nofregisteredpairs,nofregisteredmarks,nofregisteredcursives)
    local n = head
    while n do
        local id = getid(n)
        if id == glyph_code then
            showchar(n)
            show(n,"injections",false," ")
            show(n,"preinjections",false,"<")
            show(n,"postinjections",false,">")
            show(n,"replaceinjections",false,"=")
        elseif id == disc_code then
            local pre     = getfield(n,"pre")
            local post    = getfield(n,"post")
            local replace = getfield(n,"replace")
            if pre then
                showsub(pre,"preinjections","pre")
            end
            if post then
                showsub(post,"postinjections","post")
            end
            if replace then
                showsub(replace,"replaceinjections","replace")
            end
        end
        n = getnext(n)
    end
    report_injections("end run")
end

local function show_result(head)
    local current  = head
    local skipping = false
    while current do
        local id = getid(current)
        if id == glyph_code then
            report_injections("char: %C, width %p, xoffset %p, yoffset %p",
                getchar(current),getfield(current,"width"),getfield(current,"xoffset"),getfield(current,"yoffset"))
            skipping = false
        elseif id == kern_code then
            report_injections("kern: %p",getfield(current,"kern"))
            skipping = false
        elseif not skipping then
            report_injections()
            skipping = true
        end
        current = getnext(current)
    end
end

local function collect_glyphs(head,offsets)
    local glyphs, glyphi, nofglyphs = { }, { }, 0
    local marks, marki, nofmarks = { }, { }, 0
    local nf, tm = nil, nil
    local n = head

    local function identify(n,what)
        local f = getfont(n)
        if f ~= nf then
            nf = f
            -- other hash in ctx:
            tm = fontdata[nf].resources
            if tm then
                tm = tm.marks
            end
        end
        if tm and tm[getchar(n)] then
            nofmarks = nofmarks + 1
            marks[nofmarks] = n
            marki[nofmarks] = "injections"
        else
            nofglyphs = nofglyphs + 1
            glyphs[nofglyphs] = n
            glyphi[nofglyphs] = what
        end
        if offsets then
            -- yoffsets can influence curs steps
            local p = rawget(properties,n)
            if p then
                local i = rawget(p,what)
                if i then
                    local yoffset = i.yoffset
                    if yoffset and yoffset ~= 0 then
                        setfield(n,"yoffset",yoffset)
                    end
                end
            end
        end
    end

    while n do -- only needed for relevant fonts
        local id = getid(n)
        if id == glyph_code then
            identify(n,"injections")
        elseif id == disc_code then
            local d = getfield(n,"pre")
            if d then
                for n in traverse_id(glyph_code,d) do
                    if getsubtype(n) < 256 then
                        identify(n,"preinjections")
                    end
                end
            end
            local d = getfield(n,"post")
            if d then
                for n in traverse_id(glyph_code,d) do
                    if getsubtype(n) < 256 then
                        identify(n,"postinjections")
                    end
                end
            end
            local d = getfield(n,"replace")
            if d then
                for n in traverse_id(glyph_code,d) do
                    if getsubtype(n) < 256 then
                        identify(n,"replaceinjections")
                    end
                end
            end
        end
        n = getnext(n)
    end

    return glyphs, glyphi, nofglyphs, marks, marki, nofmarks
end

local function inject_marks(marks,marki,nofmarks)
    for i=1,nofmarks do
        local n  = marks[i]
        local pn = rawget(properties,n)
        if pn then
            local ni = marki[i]
            local pn = rawget(pn,ni)
            if pn then
                local p = pn.markbasenode
                if p then
                    local px = getfield(p,"xoffset")
                    local ox = 0
                    local rightkern = nil
                    local pp = rawget(properties,p)
                    if pp then
                        pp = rawget(pp,ni)
                        if pp then
                            rightkern = pp.rightkern
                        end
                    end
                    if rightkern then -- x and w ~= 0
                        if pn.markdir < 0 then
                            -- kern(w-x) glyph(p) kern(x) mark(n)
                            ox = px - pn.markx - rightkern
                         -- report_injections("r2l case 1: %p",ox)
                        else
                            -- kern(x) glyph(p) kern(w-x) mark(n)
                         -- ox = px - getfield(p,"width") + pn.markx - pp.leftkern
                            --
                            -- According to Kai we don't need to handle leftkern here but I'm
                            -- pretty sure I've run into a case where it was needed so maybe
                            -- some day we need something more clever here.
                            --
                            if false then
                                -- a mark with kerning
                                local leftkern = pp.leftkern
                                if leftkern then
                                    ox = px - pn.markx - leftkern
                                else
                                    ox = px - pn.markx
                                end
                            else
                                ox = px - pn.markx
                            end
                        end
                    else
                        -- we need to deal with fonts that have marks with width
                     -- if pn.markdir < 0 then
                     --     ox = px - pn.markx
                     --  -- report_injections("r2l case 3: %p",ox)
                     -- else
                     --  -- ox = px - getfield(p,"width") + pn.markx
                            ox = px - pn.markx
                         -- report_injections("l2r case 3: %p",ox)
                     -- end
                        local wn = getfield(n,"width") -- in arial marks have widths
                        if wn ~= 0 then
                            -- bad: we should center
                         -- insert_node_before(head,n,newkern(-wn/2))
                         -- insert_node_after(head,n,newkern(-wn/2))
                            pn.leftkern  = -wn/2
                            pn.rightkern = -wn/2
                         -- wx[n] = { 0, -wn/2, 0, -wn }
                        end
                    end
                    setfield(n,"xoffset",ox)
                    --
                    local py = getfield(p,"yoffset")
                 -- local oy = 0
                 -- if marks[p] then
                 --     oy = py + pn.marky
                 -- else
                 --     oy = getfield(n,"yoffset") + py + pn.marky
                 -- end
                    local oy = getfield(n,"yoffset") + py + pn.marky
                    setfield(n,"yoffset",oy)
                else
                    -- normally this can't happen (only when in trace mode which is a special case anyway)
                 -- report_injections("missing mark anchor %i",pn.markbase or 0)
                end
            end
        end
    end
end

local function inject_cursives(glyphs,glyphi,nofglyphs)
    local cursiveanchor, lastanchor = nil, nil
    local minc, maxc, last = 0, 0, nil
    for i=1,nofglyphs do
        local n  = glyphs[i]
        local pn = rawget(properties,n)
        if pn then
            pn = rawget(pn,glyphi[i])
        end
        if pn then
            local cursivex = pn.cursivex
            if cursivex then
                if cursiveanchor then
                    if cursivex ~= 0 then
                        pn.leftkern = (pn.leftkern or 0) + cursivex
                    end
                    if lastanchor then
                        if maxc == 0 then
                            minc = lastanchor
                        end
                        maxc = lastanchor
                        properties[cursiveanchor].cursivedy = pn.cursivey
                    end
                    last = n
                else
                    maxc = 0
                end
            elseif maxc > 0 then
                local ny = getfield(n,"yoffset")
                for i=maxc,minc,-1 do
                    local ti = glyphs[i]
                    ny = ny + properties[ti].cursivedy
                    setfield(ti,"yoffset",ny) -- why not add ?
                end
                maxc = 0
            end
            if pn.cursiveanchor then
                cursiveanchor = n
                lastanchor = i
            else
                cursiveanchor = nil
                lastanchor = nil
                if maxc > 0 then
                    local ny = getfield(n,"yoffset")
                    for i=maxc,minc,-1 do
                        local ti = glyphs[i]
                        ny = ny + properties[ti].cursivedy
                        setfield(ti,"yoffset",ny) -- why not add ?
                    end
                    maxc = 0
                end
            end
        elseif maxc > 0 then
            local ny = getfield(n,"yoffset")
            for i=maxc,minc,-1 do
                local ti = glyphs[i]
                ny = ny + properties[ti].cursivedy
                setfield(ti,"yoffset",getfield(ti,"yoffset") + ny) -- ?
            end
            maxc = 0
            cursiveanchor = nil
            lastanchor = nil
        end
     -- if maxc > 0 and not cursiveanchor then
     --     local ny = getfield(n,"yoffset")
     --     for i=maxc,minc,-1 do
     --         local ti = glyphs[i][1]
     --         ny = ny + properties[ti].cursivedy
     --         setfield(ti,"yoffset",ny) -- why not add ?
     --     end
     --     maxc = 0
     -- end
    end
    if last and maxc > 0 then
        local ny = getfield(last,"yoffset")
        for i=maxc,minc,-1 do
            local ti = glyphs[i]
            ny = ny + properties[ti].cursivedy
            setfield(ti,"yoffset",ny) -- why not add ?
        end
    end
end

-- G  +D-pre        G
--     D-post+
--    +D-replace+
--
-- G  +D-pre       +D-pre
--     D-post      +D-post
--    +D-replace   +D-replace

local function inject_kerns(head,glist,ilist,length) -- not complete ! compare with inject_kerns_only (but unlikely disc here)
    for i=1,length do
        local n  = glist[i]
        local pn = rawget(properties,n)
        if pn then
            local dp = nil
            local dr = nil
            local ni = ilist[i]
            local p  = nil
            if ni == "injections" then
                p = getprev(n)
                if p then
                    local id = getid(p)
                    if id == disc_code then
                        dp = getfield(p,"post")
                        dr = getfield(p,"replace")
                    end
                end
            end
            if dp then
                local i = rawget(pn,"postinjections")
                if i then
                    local leftkern = i.leftkern
                    if leftkern and leftkern ~= 0 then
                        local t = find_tail(dp)
                        insert_node_after(dp,t,newkern(leftkern))
                        setfield(p,"post",dp) -- currently we need to force a tail refresh
                    end
                end
            end
            if dr then
                local i = rawget(pn,"replaceinjections")
                if i then
                    local leftkern = i.leftkern
                    if leftkern and leftkern ~= 0 then
                        local t = find_tail(dr)
                        insert_node_after(dr,t,newkern(leftkern))
                        setfield(p,"replace",dr) -- currently we need to force a tail refresh
                    end
                end
            else
                local i = rawget(pn,ni)
                if i then
                    local leftkern = i.leftkern
                    if leftkern and leftkern ~= 0 then
                        insert_node_before(head,n,newkern(leftkern)) -- type 0/2
                    end
                    local rightkern = i.rightkern
                    if rightkern and rightkern ~= 0 then
                        insert_node_after(head,n,newkern(rightkern)) -- type 0/2
                    end
                end
            end
        end
    end
end

local function inject_everything(head,where)
    head = tonut(head)
    if trace_injections then
        trace(head,"everything")
    end
    local glyphs, glyphi, nofglyphs, marks, marki, nofmarks = collect_glyphs(head,nofregisteredpairs > 0)
    if nofglyphs > 0 then
        if nofregisteredcursives > 0 then
            inject_cursives(glyphs,glyphi,nofglyphs)
        end
        if nofregisteredmarks > 0 then -- and nofmarks > 0
            inject_marks(marks,marki,nofmarks)
        end
        inject_kerns(head,glyphs,glyphi,nofglyphs)
    end
    if nofmarks > 0 then
        inject_kerns(head,marks,marki,nofmarks)
    end
    if keepregisteredcounts then
        keepregisteredcounts  = false
    else
        nofregisteredkerns    = 0
        nofregisteredpairs    = 0
        nofregisteredmarks    = 0
        nofregisteredcursives = 0
    end
    return tonode(head), true
end

-- G  +D-pre        G
--     D-post+
--    +D-replace+
--
-- G  +D-pre       +D-pre
--     D-post      +D-post
--    +D-replace   +D-replace

local function inject_kerns_only(head,where)
    head = tonut(head)
    if trace_injections then
        trace(head,"kerns")
    end
    local n = head
    local p = nil -- disc node when non-nil
    while n do
        local id = getid(n)
        if id == glyph_code then
            if getsubtype(n) < 256 then
                local pn = rawget(properties,n)
                if pn then
                    if p then
                        local d = getfield(p,"post")
                        if d then
                            local i = rawget(pn,"postinjections")
                            if i then
                                local leftkern = i.leftkern
                                if leftkern and leftkern ~= 0 then
                                    local t = find_tail(d)
                                    insert_node_after(d,t,newkern(leftkern))
                                    setfield(p,"post",d) -- currently we need to force a tail refresh
                                end
                            end
                        end
                        local d = getfield(p,"replace")
                        if d then
                            local i = rawget(pn,"replaceinjections")
                            if i then
                                local leftkern = i.leftkern
                                if leftkern and leftkern ~= 0 then
                                    local t = find_tail(d)
                                    insert_node_after(d,t,newkern(leftkern))
                                    setfield(p,"replace",d) -- currently we need to force a tail refresh
                                end
                            end
                        else
                            local i = rawget(pn,"injections")
                            if i then
                                local leftkern = i.leftkern
                                if leftkern and leftkern ~= 0 then
                                    setfield(p,"replace",newkern(leftkern))
                                end
                            end
                        end
                    else
                        -- this is the most common case
                        local i = rawget(pn,"injections")
                        if i then
                            local leftkern = i.leftkern
                            if leftkern and leftkern ~= 0 then
                                head = insert_node_before(head,n,newkern(leftkern))
                            end
                        end
                    end
                end
            end
            p = nil
        elseif id == disc_code then
            local d = getfield(n,"pre")
            if d then
                local h = d
                for n in traverse_id(glyph_code,d) do
                    if getsubtype(n) < 256 then
                        local pn = rawget(properties,n)
                        if pn then
                            local i = rawget(pn,"preinjections")
                            if i then
                                local leftkern = i.leftkern
                                if leftkern and leftkern ~= 0 then
                                    h = insert_node_before(h,n,newkern(leftkern))
                                end
                            end
                        end
                    else
                        break
                    end
                end
                if h ~= d then
                    setfield(n,"pre",h)
                end
            end
            local d = getfield(n,"post")
            if d then
                local h = d
                for n in traverse_id(glyph_code,d) do
                    if getsubtype(n) < 256 then
                        local pn = rawget(properties,n)
                        if pn then
                            local i = rawget(pn,"postinjections")
                            if i then
                                local leftkern = i.leftkern
                                if leftkern and leftkern ~= 0 then
                                    h = insert_node_before(h,n,newkern(leftkern))
                                end
                            end
                        end
                    else
                        break
                    end
                end
                if h ~= d then
                    setfield(n,"post",h)
                end
            end
            local d = getfield(n,"replace")
            if d then
                local h = d
                for n in traverse_id(glyph_code,d) do
                    if getsubtype(n) < 256 then
                        local pn = rawget(properties,n)
                        if pn then
                            local i = rawget(pn,"replaceinjections")
                            if i then
                                local leftkern = i.leftkern
                                if leftkern and leftkern ~= 0 then
                                    h = insert_node_before(h,n,newkern(leftkern))
                                end
                            end
                        end
                    else
                        break
                    end
                end
                if h ~= d then
                    setfield(n,"replace",h)
                end
            end
            p = n
        else
            p = nil
        end
        n = getnext(n)
    end
    --
    if keepregisteredcounts then
        keepregisteredcounts = false
    else
        nofregisteredkerns   = 0
    end
    return tonode(head), true
end

local function inject_pairs_only(head,where)
    head = tonut(head)
    if trace_injections then
        trace(head,"pairs")
    end
    local n = head
    local p = nil -- disc node when non-nil
    while n do
        local id = getid(n)
        if id == glyph_code then
            if getsubtype(n) < 256 then
                local pn = rawget(properties,n)
                if pn then
                    if p then
                        local d = getfield(p,"post")
                        if d then
                            local i = rawget(pn,"postinjections")
                            if i then
                                local leftkern = i.leftkern
                                if leftkern and leftkern ~= 0 then
                                    local t = find_tail(d)
                                    insert_node_after(d,t,newkern(leftkern))
                                    setfield(p,"post",d) -- currently we need to force a tail refresh
                                end
                             -- local rightkern = i.rightkern
                             -- if rightkern and rightkern ~= 0 then
                             --     insert_node_after(head,n,newkern(rightkern))
                             --     n = getnext(n) -- to be checked
                             -- end
                            end
                        end
                        local d = getfield(p,"replace")
                        if d then
                            local i = rawget(pn,"replaceinjections")
                            if i then
                                local leftkern = i.leftkern
                                if leftkern and leftkern ~= 0 then
                                    local t = find_tail(d)
                                    insert_node_after(d,t,newkern(leftkern))
                                    setfield(p,"replace",d) -- currently we need to force a tail refresh
                                end
                             -- local rightkern = i.rightkern
                             -- if rightkern and rightkern ~= 0 then
                             --     insert_node_after(head,n,newkern(rightkern))
                             --     n = getnext(n) -- to be checked
                             -- end
                            end
                        else
                            local i = rawget(pn,"injections")
                            if i then
                                local leftkern = i.leftkern
                                if leftkern and leftkern ~= 0 then
                                    setfield(p,"replace",newkern(leftkern))
                                end
                             -- local rightkern = i.rightkern
                             -- if rightkern and rightkern ~= 0 then
                             --     insert_node_after(head,n,newkern(rightkern))
                             --     n = getnext(n) -- to be checked
                             -- end
                            end
                        end
                    else
                        -- this is the most common case
                        local i = rawget(pn,"injections")
                        if i then
                            local leftkern = i.leftkern
                            if leftkern and leftkern ~= 0 then
                                head = insert_node_before(head,n,newkern(leftkern))
                            end
                            local rightkern = i.rightkern
                            if rightkern and rightkern ~= 0 then
                                insert_node_after(head,n,newkern(rightkern))
                                n = getnext(n) -- to be checked
                            end
                            local yoffset = i.yoffset
                            if yoffset and yoffset ~= 0 then
                                setfield(n,"yoffset",yoffset)
                            end
                        end
                    end
                end
            end
            p = nil
        elseif id == disc_code then
            local d = getfield(n,"pre")
            if d then
                local h = d
                for n in traverse_id(glyph_code,d) do
                    if getsubtype(n) < 256 then
                        local pn = rawget(properties,n)
                        if pn then
                            local i = rawget(pn,"preinjections")
                            if i then
                                local leftkern = i.leftkern
                                if leftkern and leftkern ~= 0 then
                                    h = insert_node_before(h,n,newkern(leftkern))
                                end
                                local rightkern = i.rightkern
                                if rightkern and rightkern ~= 0 then
                                    insert_node_after(head,n,newkern(rightkern))
                                    n = getnext(n) -- to be checked
                                end
                                local yoffset = i.yoffset
                                if yoffset and yoffset ~= 0 then
                                    setfield(n,"yoffset",yoffset)
                                end
                            end
                        end
                    else
                        break
                    end
                end
                if h ~= d then
                    setfield(n,"pre",h)
                end
            end
            local d = getfield(n,"post")
            if d then
                local h = d
                for n in traverse_id(glyph_code,d) do
                    if getsubtype(n) < 256 then
                        local pn = rawget(properties,n)
                        if pn then
                            local i = rawget(pn,"postinjections")
                            if i then
                                local leftkern = i.leftkern
                                if leftkern and leftkern ~= 0 then
                                    h = insert_node_before(h,n,newkern(leftkern))
                                end
                                local rightkern = i.rightkern
                                if rightkern and rightkern ~= 0 then
                                    insert_node_after(head,n,newkern(rightkern))
                                    n = getnext(n) -- to be checked
                                end
                                local yoffset = i.yoffset
                                if yoffset and yoffset ~= 0 then
                                    setfield(n,"yoffset",yoffset)
                                end
                            end
                        end
                    else
                        break
                    end
                end
                if h ~= d then
                    setfield(n,"post",h)
                end
            end
            local d = getfield(n,"replace")
            if d then
                local h = d
                for n in traverse_id(glyph_code,d) do
                    if getsubtype(n) < 256 then
                        local pn = rawget(properties,n)
                        if pn then
                            local i = rawget(pn,"replaceinjections")
                            if i then
                                local leftkern = i.leftkern
                                if leftkern and leftkern ~= 0 then
                                    h = insert_node_before(h,n,newkern(leftkern))
                                end
                                local rightkern = i.rightkern
                                if rightkern and rightkern ~= 0 then
                                    insert_node_after(head,n,newkern(rightkern))
                                    n = getnext(n) -- to be checked
                                end
                                local yoffset = i.yoffset
                                if yoffset and yoffset ~= 0 then
                                    setfield(n,"yoffset",yoffset)
                                end
                            end
                        end
                    else
                        break
                    end
                end
                if h ~= d then
                    setfield(n,"replace",h)
                end
            end
            p = n
        else
            p = nil
        end
        n = getnext(n)
    end
    --
    if keepregisteredcounts then
        keepregisteredcounts = false
    else
        nofregisteredpairs = 0
        nofregisteredkerns = 0
    end
    return tonode(head), true
end

function injections.handler(head,where)
    if nofregisteredmarks > 0 or nofregisteredcursives > 0 then
        return inject_everything(head,where)
    elseif nofregisteredpairs > 0 then
        return inject_pairs_only(head,where)
    elseif nofregisteredkerns > 0 then
        return inject_kerns_only(head,where)
    else
        return head, false
    end
end
