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
    os.exit()
end

local byte = string.byte

local fonts              = fonts
local handlers           = fonts.handlers
local otf                = handlers.otf
local afm                = handlers.afm
local registerotffeature = otf.features.register
local registerafmfeature = afm.features.register

-- extra generic stuff

function fonts.loggers.onetimemessage() end

-- done elsewhere
--
-- loadmodule('font-ext-imp-italic.lua')
-- loadmodule('font-ext-imp-effect.lua')
-- loadmodule('luatex-fonts-lig.lua')

-- protrusion (simplified version)

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

setups['default'] = { -- demo vector

    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       Û”

}

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

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

registerotffeature(specification)
registerafmfeature(specification)

-- expansion (simplified version)

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

setups['default'] = { -- demo vector

    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,
}

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

local specification = {
    name        = "expansion",
    description = "apply hz optimization",
    initializers = {
        base = initializeexpansion,
        node = initializeexpansion,
    }
}

registerotffeature(specification)
registerafmfeature(specification)

-- normalizer (generic only)

if not otf.features.normalize then

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

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
--  }

-- reencoding (generic only)

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 initialize(tfmdata,value)
    tfmdata.postprocessors = tfmdata.postprocessors or { }
    table.insert(tfmdata.postprocessors,
        function(tfmdata)
            return specialreencode(tfmdata,value)
        end
    )
end

registerotffeature {
    name         = "reencode",
    description  = "reencode characters",
    manipulators = {
        base = initialize,
        node = initialize,
    }
}

-- math stuff (generic only)

local function initialize(tfmdata,key,value)
    if value then
        tfmdata.mathparameters = nil
    end
end

registerotffeature {
    name         = "ignoremathconstants",
    description  = "ignore math constants table",
    initializers = {
        base = initialize,
        node = initialize,
    }
}