font-imp-tracing.lua / last modification: 2020-01-30 14:16
if not modules then modules = { } end modules ['font-imp-tracing'] = {
    version   = 1.001,
    comment   = "companion to font-ini.mkiv and hand-ini.mkiv",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files"
}

if not context then return end

local next, type = next, type
local concat = table.concat
local formatters = string.formatters

local fonts              = fonts

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

local settings_to_array  = utilities.parsers.settings_to_array
local setmetatableindex  = table.setmetatableindex

local helpers            = fonts.helpers
local appendcommandtable = helpers.appendcommandtable
local prependcommands    = helpers.prependcommands
local charcommand        = helpers.commands.char

local variables          = interfaces.variables

local v_background       = variables.background
local v_frame            = variables.frame
local v_empty            = variables.empty
local v_none             = variables.none

-- for zhichu chen (see mailing list archive): we might add a few more variants
-- in due time
--
-- \definefontfeature[boxed][default][boundingbox=yes] % paleblue
--
-- maybe:
--
-- \definecolor[DummyColor][s=.75,t=.5,a=1] {\DummyColor test} \nopdfcompression
--
-- local gray  = { "pdf", "origin", "/Tr1 gs .75 g" }
-- local black = { "pdf", "origin", "/Tr0 gs 0 g" }

-- boundingbox={yes|background|frame|empty|<color>}

local bp  = number.dimenfactors.bp
local r   = 16384 * bp -- 65536 // 4
local f_1 = formatters["%.6F w 0 %.6F %.6F %.6F re f"]
local f_2 = formatters["[] 0 d 0 J %.6F w %.6F %.6F %.6F %.6F re S"]

-- change this into w h d instead of h d w

local backcache = setmetatableindex(function(t,h)
    local h = h * bp
    local v = setmetatableindex(function(t,d)
        local d = d * bp
        local v = setmetatableindex(function(t,w)
            local v = { "pdf", "origin", f_1(r,-d,w*bp,h+d) }
            t[w] = v
            return v
        end)
        t[d] = v
        return v
    end)
    t[h] = v
    return v
end)

local forecache = setmetatableindex(function(t,h)
    local h = h * bp
    local v = setmetatableindex(function(t,d)
        local d = d * bp
        local v = setmetatableindex(function(t,w)
            -- the frame goes through the boundingbox
            local v = { "pdf", "origin", f_2(r,r/2,-d+r/2,w*bp-r,h+d-r) }
            t[w] = v
            return v
        end)
        t[d] = v
        return v
    end)
    t[h] = v
    return v
end)

local startcolor = nil
local stopcolor  = nil

local function initialize(tfmdata,key,value)
    if value then
        if not backcolors then
            local vfspecials = backends.pdf.tables.vfspecials
            startcolor = vfspecials.startcolor
            stopcolor  = vfspecials.stopcolor
        end
        local characters = tfmdata.characters
        local rulecache  = backcache
        local showchar   = true
        local color      = "palegray"
        if type(value) == "string" then
            value = settings_to_array(value)
            for i=1,#value do
                local v = value[i]
                if v == v_frame then
                    rulecache = forecache
                elseif v == v_background then
                    rulecache = backcache
                elseif v == v_empty then
                    showchar = false
                elseif v == v_none then
                    color = nil
                else
                    color = v
                end
            end
        end
        local gray  = color and startcolor(color) or nil
        local black = gray and stopcolor or nil
        for unicode, character in next, characters do
            local width  = character.width  or 0
            local height = character.height or 0
            local depth  = character.depth  or 0
            local rule   = rulecache[height][depth][width]
            if showchar then
                local commands = character.commands
                if commands then
                    if gray then
                        character.commands = prependcommands (
                            commands, gray, rule, black
                        )
                    else
                        character.commands = prependcommands (
                            commands, rule
                        )
                    end
                else
                    local char = charcommand[unicode]
                    if gray then
                        character.commands = {
                            gray, rule, black, char
                        }
                     else
                        character.commands = {
                            rule, char
                        }
                    end
                end
            else
                if gray then
                    character.commands = {
                        gray, rule, black
                    }
                else
                    character.commands = {
                        rule
                    }
                end
            end
        end
    end
end

local specification = {
    name        = "boundingbox",
    description = "show boundingbox",
    manipulators = {
        base = initialize,
        node = initialize,
    }
}

registerotffeature(specification)
registerafmfeature(specification)

local f_m = formatters["%F %F m"]
local f_l = formatters["%F %F l"]
local f_b = formatters["[] 0 d 0 J %.6F w"]
local f_e = formatters["s"]

local function ladder(list,docolor,nocolor,lst,offset,sign,default)
    local l  = lst[1]
    local x1 = bp * (offset + l.kern) * sign
    local y1 = bp * l.height
    local t  = { f_b(r,r/2), f_m(x1,y1) }
    local n  = 2
    local m  = #lst
    if default > 0 then
        default = default * bp + r
    else
        default = default * bp - r
    end
    if m == 1 then
        n = n + 1 t[n] = f_l(x1,default)
    else
        for i=1,m do
            local l  = lst[i]
            local x2 = bp * (offset + l.kern) * sign
            local y2 = bp * l.height
            if i > 1 and y2 == 0 then
                y2 = default
            end
            n = n + 1 t[n] = f_l(x2,y1)
            n = n + 1 t[n] = f_l(x2,y2)
            x1, y1 = x2, y2
        end
    end
    n = n + 1 t[n] = f_e()
    list[#list+1] = docolor
    list[#list+1] = { "pdf", "origin", concat(t," ") }
    list[#list+1] = nocolor
end

local function initialize(tfmdata,key,value)
    if value then
        if not backcolors then
            local vfspecials = backends.pdf.tables.vfspecials
            startcolor = vfspecials.startcolor
            stopcolor  = vfspecials.stopcolor
        end
        local characters = tfmdata.characters
        local brcolor    = startcolor("darkred")
        local trcolor    = startcolor("darkgreen")
        local blcolor    = startcolor("darkblue")
        local tlcolor    = startcolor("darkyellow")
        local black      = stopcolor
        for unicode, character in next, characters do
            local mathkern = character.mathkern
            if mathkern then
                -- more efficient would be co collect more in one pdf
                -- directive but this is hardly used so not worth the
                -- effort
                local width  = character.width  or 0
                local height = character.height or 0
                local depth  = character.depth  or 0
                local list   = { }
                local br     = mathkern.bottom_right
                local tr     = mathkern.top_right
                local bl     = mathkern.bottom_left
                local tl     = mathkern.top_left
                if br then
                    ladder(list,brcolor,black,br,width,1,height)
                end
                if tr then
                    ladder(list,trcolor,black,tr,width,1,-depth)
                end
                if bl then
                    ladder(list,blcolor,black,bl,0,-1,height)
                end
                if tl then
                    ladder(list,tlcolor,black,tl,0,-1,-depth)
                end
                if #list > 0 then
                    local commands = character.commands
                    if commands then
                        character.commands = appendcommandtable(commands,list)
                    else
                        list[#list+1] = charcommand[unicode]
                        character.commands = list
                    end
                end
            end
        end
    end
end

local specification = {
    name        = "staircase",
    description = "show staircase kerns",
    position=1,
    manipulators = {
        base = initialize,
        node = initialize,
    }
}

registerotffeature(specification)
registerafmfeature(specification)