Current File : //usr/share/texlive/texmf-dist/tex/luatex/luaotfload/fontloader-font-imp-effects.lua
if not modules then modules = { } end modules ['font-imp-effects'] = {
    version   = 1.001,
    comment   = "companion to font-ini.mkiv",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files"
}

-- todo: pickup from goodies: if type(effect) then ...

local next, type, tonumber = next, type, tonumber
local is_boolean = string.is_boolean

local fonts              = fonts

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

local settings_to_hash   = utilities.parsers.settings_to_hash_colon_too

local helpers            = fonts.helpers
local prependcommands    = helpers.prependcommands
local charcommand        = helpers.commands.char
local leftcommand        = helpers.commands.left
local rightcommand       = helpers.commands.right
local upcommand          = helpers.commands.up
local downcommand        = helpers.commands.down
local dummycommand       = helpers.commands.dummy

----- constructors       = fonts.constructors
----- getmathparameter   = constructors.getmathparameter
----- setmathparameter   = constructors.setmathparameter

local report_effect      = logs.reporter("fonts","effect")
local report_slant       = logs.reporter("fonts","slant")
local report_extend      = logs.reporter("fonts","extend")
local report_squeeze     = logs.reporter("fonts","squeeze")

local trace              = false

trackers.register("fonts.effect", function(v) trace = v end)
trackers.register("fonts.slant",  function(v) trace = v end)
trackers.register("fonts.extend", function(v) trace = v end)
trackers.register("fonts.squeeze",function(v) trace = v end)

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
    if trace then
        report_slant("applying %0.3f",value)
    end
    tfmdata.parameters.slantfactor = value
end

local specification = {
    name        = "slant",
    description = "slant glyphs",
    initializers = {
        base = initializeslant,
        node = initializeslant,
    }
}

registerotffeature(specification)
registerafmfeature(specification)

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
    if trace then
        report_extend("applying %0.3f",value)
    end
    tfmdata.parameters.extendfactor = value
end

local specification = {
    name        = "extend",
    description = "scale glyphs horizontally",
    initializers = {
        base = initializeextend,
        node = initializeextend,
    }
}

registerotffeature(specification)
registerafmfeature(specification)

local function initializesqueeze(tfmdata,value)
    value = tonumber(value)
    if not value then
        value =  0
    elseif value >  10 then
        value =  10
    elseif value < -10 then
        value = -10
    end
    if trace then
        report_squeeze("applying %0.3f",value)
    end
    tfmdata.parameters.squeezefactor = value
end

local specification = {
    name        = "squeeze",
    description = "scale glyphs vertically",
    initializers = {
        base = initializesqueeze,
        node = initializesqueeze,
    }
}

registerotffeature(specification)
registerafmfeature(specification)

local effects = {
    inner   = 0,
    normal  = 0,
    outer   = 1,
    outline = 1,
    both    = 2,
    hidden  = 3,
}

local function initializeeffect(tfmdata,value)
    local spec
    if type(value) == "number" then
        spec = { width = value }
    else
        spec = settings_to_hash(value)
    end
    local effect = spec.effect or "both"
    local width  = tonumber(spec.width) or 0
    local mode   = effects[effect]
    if not mode then
        report_effect("invalid effect %a",effect)
    elseif width == 0 and mode == 0 then
        report_effect("invalid width %a for effect %a",width,effect)
    else
        local parameters = tfmdata.parameters
        local properties = tfmdata.properties
        parameters.mode  = mode
        parameters.width = width * 1000
        if is_boolean(spec.auto) == true then
            local squeeze = 1 - width/20
            local average = (1 - squeeze) * width * 100
            spec.squeeze  = squeeze
            spec.extend   = 1 + width/2
            spec.wdelta   = average
            spec.hdelta   = average/2
            spec.ddelta   = average/2
            spec.vshift   = average/2
        end
        local factor  = tonumber(spec.factor)  or 0
        local hfactor = tonumber(spec.hfactor) or factor
        local vfactor = tonumber(spec.vfactor) or factor
        local delta   = tonumber(spec.delta)   or 1
        local wdelta  = tonumber(spec.wdelta)  or delta
        local hdelta  = tonumber(spec.hdelta)  or delta
        local ddelta  = tonumber(spec.ddelta)  or hdelta
        local vshift  = tonumber(spec.vshift)  or 0
        local slant   = spec.slant
        local extend  = spec.extend
        local squeeze = spec.squeeze
        if slant then
            initializeslant(tfmdata,slant)
        end
        if extend then
            initializeextend(tfmdata,extend)
        end
        if squeeze then
            initializesqueeze(tfmdata,squeeze)
        end
        properties.effect = {
            effect  = effect,
            width   = width,
            factor  = factor,
            hfactor = hfactor,
            vfactor = vfactor,
            wdelta  = wdelta,
            hdelta  = hdelta,
            ddelta  = ddelta,
            vshift  = vshift,
            slant   = tfmdata.parameters.slantfactor,
            extend  = tfmdata.parameters.extendfactor,
            squeeze = tfmdata.parameters.squeezefactor,
        }
    end
end

local rules = {
    "RadicalRuleThickness",
    "OverbarRuleThickness",
    "FractionRuleThickness",
    "UnderbarRuleThickness",
}

-- local commands = char.commands
-- if commands then
--     local command = commands[1]
--     if command and command[1] == "right" then
--         commands[1] = rightcommand[command[2]-snap]
--     end
-- end

-- radicals are not yet ok

local setmathparameters
local setmathcharacters

if CONTEXTLMTXMODE and CONTEXTLMTXMODE > 0 then

    setmathparameters = function(tfmdata,characters,mathparameters,dx,dy,squeeze,multiplier)
        if delta ~= 0 then
            for i=1,#rules do
                local name  = rules[i]
                local value = mathparameters[name]
                if value then
                   mathparameters[name] = (squeeze or 1) * (value + dy)
                end
            end
        end
    end

    setmathcharacters = function()
    end

else

    setmathparameters = function(tfmdata,characters,mathparameters,dx,dy,squeeze,multiplier)
        if delta ~= 0 then
            for i=1,#rules do
                local name  = rules[i]
                local value = mathparameters[name]
                if value then
                   mathparameters[name] = (squeeze or 1) * (value + dy)
                end
            end
        end
    end

    setmathcharacters = function(tfmdata,characters,mathparameters,dx,dy,squeeze,wdelta,hdelta,ddelta)

        -- still not the perfect rule

        local function wdpatch(char)
            if wsnap ~= 0 then
                char.width  = char.width + wdelta/2
            end
        end

        local function htpatch(char)
            if hsnap ~= 0 then
                local height = char.height
                if height then
                    char.height = char.height + 2 * dy
                end
            end
        end

        local character = characters[0x221A]

        if character and character.next then
            local char = character
            local next = character.next
            wdpatch(char)
            htpatch(char)
            while next do
                char = characters[next]
                wdpatch(char)
                htpatch(char)
                next = char.next
            end
            if char then
                local v = char.vert_variants
                if v then
                    local top = v[#v]
                    if top then
                        local char = characters[top.glyph]
                        htpatch(char)
                    end
                end
            end
        end

    end

end

-- local show_effect = { "lua", function(f,c)
--     report_effect("font id %i, char %C",f,c)
--     inspect(fonts.hashes.characters[f][c])
-- end }

local shiftmode = CONTEXTLMTXMODE and CONTEXTLMTXMODE > 0

local function manipulateeffect(tfmdata)
    local effect = tfmdata.properties.effect
    if effect then
        local characters     = tfmdata.characters
        local parameters     = tfmdata.parameters
        local mathparameters = tfmdata.mathparameters
        local multiplier     = effect.width * 100
        local factor         = parameters.factor
        local hfactor        = parameters.hfactor
        local vfactor        = parameters.vfactor
        local wdelta         = effect.wdelta * hfactor * multiplier
        local hdelta         = effect.hdelta * vfactor * multiplier
        local ddelta         = effect.ddelta * vfactor * multiplier
        local vshift         = effect.vshift * vfactor * multiplier
        local squeeze        = effect.squeeze
        local hshift         = wdelta / 2
        local dx             = multiplier * vfactor
        local dy             = vshift
        local factor         = (1 + effect.factor)  * factor
        local hfactor        = (1 + effect.hfactor) * hfactor
        local vfactor        = (1 + effect.vfactor) * vfactor
        if shiftmode then
            parameters.hshift = hshift
            parameters.vshift = vshift
        else
            vshift = vshift ~= 0 and upcommand[vshift] or false
            hshift = rightcommand[hshift]
        end
        for unicode, character in next, characters do
            local oldwidth  = character.width
            local oldheight = character.height
            local olddepth  = character.depth
            if oldwidth and oldwidth > 0 then
                character.width = oldwidth + wdelta
                if not shiftmode then
                    local commands = character.commands
                    if vshift then
                        if commands then
                            prependcommands ( commands,
                             -- show_effect,
                                hshift,
                                vshift
                            )
                        else
                            character.commands = {
                             -- show_effect,
                                hshift,
                                vshift,
                                charcommand[unicode]
                            }
                        end
                    else
                        if commands then
                          prependcommands ( commands,
                           -- show_effect,
                              hshift
                          )
                        else
                            character.commands = {
                             -- show_effect,
                                hshift,
                                charcommand[unicode]
                          }
                        end
                    end
                end
            end
            if oldheight and oldheight > 0 then
               character.height = oldheight + hdelta
            end
            if olddepth and olddepth > 0 then
               character.depth = olddepth + ddelta
            end
        end
        if mathparameters then
            setmathparameters(tfmdata,characters,mathparameters,dx,dy,squeeze,multiplier)
            setmathcharacters(tfmdata,characters,mathparameters,dx,dy,squeeze,wdelta,hdelta,ddelta)
        end
        parameters.factor  = factor
        parameters.hfactor = hfactor
        parameters.vfactor = vfactor
        if trace then
            report_effect("applying")
            report_effect("  effect  : %s", effect.effect)
            report_effect("  width   : %s => %s", effect.width,  multiplier)
            report_effect("  factor  : %s => %s", effect.factor, factor )
            report_effect("  hfactor : %s => %s", effect.hfactor,hfactor)
            report_effect("  vfactor : %s => %s", effect.vfactor,vfactor)
            report_effect("  wdelta  : %s => %s", effect.wdelta, wdelta)
            report_effect("  hdelta  : %s => %s", effect.hdelta, hdelta)
            report_effect("  ddelta  : %s => %s", effect.ddelta, ddelta)
        end
    end
end

local specification = {
    name        = "effect",
    description = "apply effects to glyphs",
    initializers = {
        base = initializeeffect,
        node = initializeeffect,
    },
    manipulators = {
        base = manipulateeffect,
        node = manipulateeffect,
    },
}

registerotffeature(specification)
registerafmfeature(specification)

local function initializeoutline(tfmdata,value)
    value = tonumber(value)
    if not value then
        value = 0
    else
        value = tonumber(value) or 0
    end
    local parameters = tfmdata.parameters
    local properties = tfmdata.properties
    parameters.mode  = effects.outline
    parameters.width = value * 1000
    properties.effect = {
        effect = effect,
        width  = width,
    }
end

local specification = {
    name        = "outline",
    description = "outline glyphs",
    initializers = {
        base = initializeoutline,
        node = initializeoutline,
    }
}

registerotffeature(specification)
registerafmfeature(specification)