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

if context then
    texio.write_nl("fatal error: this module is not for context")
    os.exit()
end

local fonts       = fonts
local otffeatures = fonts.constructors.newfeatures("otf")

-- A few generic extensions.

local function initializeitlc(tfmdata,value)
    if value then
        -- the magic 40 and it formula come from Dohyun Kim but we might need another guess
        local parameters = tfmdata.parameters
        local italicangle = parameters.italicangle
        if italicangle and italicangle ~= 0 then
            local properties = tfmdata.properties
            local factor = tonumber(value) or 1
            properties.hasitalics = true
            properties.autoitalicamount = factor * (parameters.uwidth or 40)/2
        end
    end
end

otffeatures.register {
    name        = "itlc",
    description = "italic correction",
    initializers = {
        base = initializeitlc,
        node = initializeitlc,
    }
}

-- slant and extend

local function initializeslant(tfmdata,value)
    value = tonumber(value)
    if not value then
        value =  0
    elseif value >  1 then
        value =  1
    elseif value < -1 then
        value = -1
    end
    tfmdata.parameters.slantfactor = value
end

otffeatures.register {
    name        = "slant",
    description = "slant glyphs",
    initializers = {
        base = initializeslant,
        node = initializeslant,
    }
}

local function initializeextend(tfmdata,value)
    value = tonumber(value)
    if not value then
        value =  0
    elseif value >  10 then
        value =  10
    elseif value < -10 then
        value = -10
    end
    tfmdata.parameters.extendfactor = value
end

otffeatures.register {
    name        = "extend",
    description = "scale glyphs horizontally",
    initializers = {
        base = initializeextend,
        node = initializeextend,
    }
}

-- expansion and protrusion

fonts.protrusions        = fonts.protrusions        or { }
fonts.protrusions.setups = fonts.protrusions.setups or { }

local setups = fonts.protrusions.setups

local function initializeprotrusion(tfmdata,value)
    if value then
        local setup = setups[value]
        if setup then
            local factor, left, right = setup.factor or 1, setup.left or 1, setup.right or 1
            local emwidth = tfmdata.parameters.quad
            tfmdata.parameters.protrusion = {
                auto = true,
            }
            for i, chr in next, tfmdata.characters do
                local v, pl, pr = setup[i], nil, nil
                if v then
                    pl, pr = v[1], v[2]
                end
                if pl and pl ~= 0 then chr.left_protruding  = left *pl*factor end
                if pr and pr ~= 0 then chr.right_protruding = right*pr*factor end
            end
        end
    end
end

otffeatures.register {
    name        = "protrusion",
    description = "shift characters into the left and or right margin",
    initializers = {
        base = initializeprotrusion,
        node = initializeprotrusion,
    }
}

fonts.expansions         = fonts.expansions        or { }
fonts.expansions.setups  = fonts.expansions.setups or { }

local setups = fonts.expansions.setups

local function initializeexpansion(tfmdata,value)
    if value then
        local setup = setups[value]
        if setup then
            local factor = setup.factor or 1
            tfmdata.parameters.expansion = {
                stretch = 10 * (setup.stretch or 0),
                shrink  = 10 * (setup.shrink  or 0),
                step    = 10 * (setup.step    or 0),
                auto    = true,
            }
            for i, chr in next, tfmdata.characters do
                local v = setup[i]
                if v and v ~= 0 then
                    chr.expansion_factor = v*factor
                else -- can be option
                    chr.expansion_factor = factor
                end
            end
        end
    end
end

otffeatures.register {
    name        = "expansion",
    description = "apply hz optimization",
    initializers = {
        base = initializeexpansion,
        node = initializeexpansion,
    }
}

-- left over

function fonts.loggers.onetimemessage() end

-- example vectors

local byte = string.byte

fonts.expansions.setups['default'] = {

    stretch = 2, shrink = 2, step = .5, factor = 1,

    [byte('A')] = 0.5, [byte('B')] = 0.7, [byte('C')] = 0.7, [byte('D')] = 0.5, [byte('E')] = 0.7,
    [byte('F')] = 0.7, [byte('G')] = 0.5, [byte('H')] = 0.7, [byte('K')] = 0.7, [byte('M')] = 0.7,
    [byte('N')] = 0.7, [byte('O')] = 0.5, [byte('P')] = 0.7, [byte('Q')] = 0.5, [byte('R')] = 0.7,
    [byte('S')] = 0.7, [byte('U')] = 0.7, [byte('W')] = 0.7, [byte('Z')] = 0.7,
    [byte('a')] = 0.7, [byte('b')] = 0.7, [byte('c')] = 0.7, [byte('d')] = 0.7, [byte('e')] = 0.7,
    [byte('g')] = 0.7, [byte('h')] = 0.7, [byte('k')] = 0.7, [byte('m')] = 0.7, [byte('n')] = 0.7,
    [byte('o')] = 0.7, [byte('p')] = 0.7, [byte('q')] = 0.7, [byte('s')] = 0.7, [byte('u')] = 0.7,
    [byte('w')] = 0.7, [byte('z')] = 0.7,
    [byte('2')] = 0.7, [byte('3')] = 0.7, [byte('6')] = 0.7, [byte('8')] = 0.7, [byte('9')] = 0.7,
}

fonts.protrusions.setups['default'] = {

    factor = 1, left = 1, right = 1,

    [0x002C] = { 0, 1    }, -- comma
    [0x002E] = { 0, 1    }, -- period
    [0x003A] = { 0, 1    }, -- colon
    [0x003B] = { 0, 1    }, -- semicolon
    [0x002D] = { 0, 1    }, -- hyphen
    [0x2013] = { 0, 0.50 }, -- endash
    [0x2014] = { 0, 0.33 }, -- emdash
    [0x3001] = { 0, 1    }, -- ideographic comma      、
    [0x3002] = { 0, 1    }, -- ideographic full stop  。
    [0x060C] = { 0, 1    }, -- arabic comma           ،
    [0x061B] = { 0, 1    }, -- arabic semicolon       ؛
    [0x06D4] = { 0, 1    }, -- arabic full stop       ۔

}

-- normalizer

fonts.handlers.otf.features.normalize = function(t)
    if t.rand then
        t.rand = "random"
    end
    return t
end

-- bonus

function fonts.helpers.nametoslot(name)
    local t = type(name)
    if t == "string" then
        local tfmdata = fonts.hashes.identifiers[currentfont()]
        local shared  = tfmdata and tfmdata.shared
        local fntdata = shared and shared.rawdata
        return fntdata and fntdata.resources.unicodes[name]
    elseif t == "number" then
        return n
    end
end

-- \font\test=file:somefont:reencode=mymessup
--
--  fonts.encodings.reencodings.mymessup = {
--      [109] = 110, -- m
--      [110] = 109, -- n
--  }

fonts.encodings             = fonts.encodings or { }
local reencodings           = { }
fonts.encodings.reencodings = reencodings

local function specialreencode(tfmdata,value)
    -- we forget about kerns as we assume symbols and we
    -- could issue a message if ther are kerns but it's
    -- a hack anyway so we odn't care too much here
    local encoding = value and reencodings[value]
    if encoding then
        local temp = { }
        local char = tfmdata.characters
        for k, v in next, encoding do
            temp[k] = char[v]
        end
        for k, v in next, temp do
            char[k] = temp[k]
        end
        -- if we use the font otherwise luatex gets confused so
        -- we return an additional hash component for fullname
        return string.format("reencoded:%s",value)
    end
end

local function reencode(tfmdata,value)
    tfmdata.postprocessors = tfmdata.postprocessors or { }
    table.insert(tfmdata.postprocessors,
        function(tfmdata)
            return specialreencode(tfmdata,value)
        end
    )
end

otffeatures.register {
    name         = "reencode",
    description  = "reencode characters",
    manipulators = {
        base = reencode,
        node = reencode,
    }
}