if not modules then modules = { } end modules ['font-mps'] = {
    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"
}

local concat     = table.concat
local formatters = string.formatters

-- QP0 [QP1] QP2 => CP0 [CP1 CP2] CP3

-- CP0 = QP0
-- CP3 = QP2
--
-- CP1 = QP0 + 2/3 *(QP1-QP0)
-- CP2 = QP2 + 2/3 *(QP1-QP2)

fonts               = fonts or { }
local metapost      = fonts.metapost or { }
fonts.metapost      = metapost

local trace_skips   = false  trackers.register("metapost.outlines.skips",function(v) trace_skips = v end)

local f_moveto      = formatters["(%.4F,%.4F)"]
local f_lineto      = formatters["--(%.4F,%.4F)"]
local f_curveto     = formatters["..controls(%.4F,%.4F)and(%.4F,%.4F)..(%.4F,%.4F)"]
local s_cycle       = "--cycle"

local f_nofill      = formatters["nofill %s;"]
local f_dofill      = formatters["fill %s;"]

local f_draw_trace  = formatters["drawpathonly %s;"]
local f_draw        = formatters["draw %s;"]

local f_boundingbox = formatters["((%.4F,%.4F)--(%.4F,%.4F)--(%.4F,%.4F)--(%.4F,%.4F)--cycle)"]
local f_vertical    = formatters["((%.4F,%.4F)--(%.4F,%.4F))"]

function metapost.boundingbox(d,factor)
    local bounds = d.boundingbox
    local factor = factor or 1
    local llx    = factor*bounds[1]
    local lly    = factor*bounds[2]
    local urx    = factor*bounds[3]
    local ury    = factor*bounds[4]
    return f_boundingbox(llx,lly,urx,lly,urx,ury,llx,ury)
end

function metapost.widthline(d,factor)
    local bounds = d.boundingbox
    local factor = factor or 1
    local lly    = factor*bounds[2]
    local ury    = factor*bounds[4]
    local width  = factor*d.width
    return f_vertical(width,lly,width,ury)
end

function metapost.zeroline(d,factor)
    local bounds = d.boundingbox
    local factor = factor or 1
    local lly    = factor*bounds[2]
    local ury    = factor*bounds[4]
    return f_vertical(0,lly,0,ury)
end

function metapost.paths(d,xfactor,yfactor)
    local sequence = d.sequence
    local segments = d.segments
    local list     = { }
    local path     = { } -- recycled
    local size     = 0
    local xfactor  = xfactor or 1
    local yfactor  = yfactor or xfactor
    if sequence then
        local i = 1
        local n = #sequence
        while i < n do
            local operator = sequence[i]
            if operator == "m" then -- "moveto"
                if size > 0 then
                    size = size + 1
                    path[size] = s_cycle
                    list[#list+1] = concat(path,"",1,size)
                    size = 1
                else
                    size = size + 1
                end
                path[size] = f_moveto(xfactor*sequence[i+1],yfactor*sequence[i+2])
                i = i + 3
            elseif operator == "l" then -- "lineto"
                size = size + 1
                path[size] = f_lineto(xfactor*sequence[i+1],yfactor*sequence[i+2])
                i = i + 3
            elseif operator == "c" then -- "curveto"
                size = size + 1
                path[size] = f_curveto(xfactor*sequence[i+1],yfactor*sequence[i+2],xfactor*sequence[i+3],yfactor*sequence[i+4],xfactor*sequence[i+5],yfactor*sequence[i+6])
                i = i + 7
            elseif operator =="q" then -- "quadraticto"
                size = size + 1
                -- first is always a moveto
                local l_x, l_y = xfactor*sequence[i-2], yfactor*sequence[i-1]
                local m_x, m_y = xfactor*sequence[i+1], yfactor*sequence[i+2]
                local r_x, r_y = xfactor*sequence[i+3], yfactor*sequence[i+4]
                path[size] = f_curveto (
                    l_x + 2/3 * (m_x-l_x),
                    l_y + 2/3 * (m_y-l_y),
                    r_x + 2/3 * (m_x-r_x),
                    r_y + 2/3 * (m_y-r_y),
                    r_x, r_y
                )
                i = i + 5
            else
                -- weird
                i = i + 1
            end
        end
    elseif segments then
        for i=1,#segments do
            local segment  = segments[i]
            local operator = segment[#segment]
            if operator == "m" then -- "moveto"
                if size > 0 then
                    size = size + 1
                    path[size] = s_cycle
                    list[#list+1] = concat(path,"",1,size)
                    size = 1
                else
                    size = size + 1
                end
                path[size] = f_moveto(xfactor*segment[1],yfactor*segment[2])
            elseif operator == "l" then -- "lineto"
                size = size + 1
                path[size] = f_lineto(xfactor*segment[1],yfactor*segment[2])
            elseif operator == "c" then -- "curveto"
                size = size + 1
                path[size] = f_curveto(xfactor*segment[1],yfactor*segment[2],xfactor*segment[3],yfactor*segment[4],xfactor*segment[5],yfactor*segment[6])
            elseif operator =="q" then -- "quadraticto"
                size = size + 1
                -- first is always a moveto
                local prev = segments[i-1]
                local l_x, l_y = xfactor*prev[#prev-2], yfactor*prev[#prev-1]
                local m_x, m_y = xfactor*segment[1], yfactor*segment[2]
                local r_x, r_y = xfactor*segment[3], yfactor*segment[4]
                path[size] = f_curveto (
                    l_x + 2/3 * (m_x-l_x),
                    l_y + 2/3 * (m_y-l_y),
                    r_x + 2/3 * (m_x-r_x),
                    r_y + 2/3 * (m_y-r_y),
                    r_x, r_y
                )
            else
                -- weird
            end
        end
    else
        return
    end
    if size > 0 then
        size = size + 1
        path[size] = s_cycle
        list[#list+1] = concat(path,"",1,size)
    end
    return list
end

function metapost.fill(paths)
    local r = { }
    local n = #paths
    for i=1,n do
        if i < n then
            r[i] = f_nofill(paths[i])
        else
            r[i] = f_dofill(paths[i])
        end
    end
    return concat(r)
end

function metapost.draw(paths,trace)
    local r = { }
    local n = #paths
    for i=1,n do
        if trace then
            r[i] = f_draw_trace(paths[i])
        else
            r[i] = f_draw(paths[i])
        end
    end
    return concat(r)
end

function metapost.maxbounds(data,index,factor)
    local maxbounds   = data.maxbounds
    local factor      = factor or 1
    local glyphs      = data.glyphs
    local glyph       = glyphs[index]
    local boundingbox = glyph.boundingbox
    local xmin, ymin, xmax, ymax
    if not maxbounds then
        xmin, ymin, xmax, ymax = 0, 0, 0, 0
        for i=1,#glyphs do
            local d = glyphs[i]
            if d then
                local b = d.boundingbox
                if b then
                    if b[1] < xmin then xmin = b[1] end
                    if b[2] < ymin then ymin = b[2] end
                    if b[3] > xmax then xmax = b[3] end
                    if b[4] > ymax then ymax = b[4] end
                end
            end
        end
        maxbounds = { xmin, ymin, xmax, ymax }
        data.maxbounds = maxbounds
    else
        xmin = maxbounds[1]
        ymin = maxbounds[2]
        xmax = maxbounds[3]
        ymax = maxbounds[4]
    end
    local llx   = boundingbox[1]
    local lly   = boundingbox[2]
    local urx   = boundingbox[3]
    local ury   = boundingbox[4]
    local width = glyph.width
    if llx > 0 then
        llx = 0
    end
    if width > urx then
        urx = width
    end
    return f_boundingbox(
        factor*llx,factor*ymin,
        factor*urx,factor*ymin,
        factor*urx,factor*ymax,
        factor*llx,factor*ymax
    )
end

----- formatters   = string.formatters
----- concat       = table.concat

local nodecodes    = nodes.nodecodes -- no nuts yet

local glyph_code   = nodecodes.glyph
local disc_code    = nodecodes.disc
local kern_code    = nodecodes.kern
local glue_code    = nodecodes.glue
local hlist_code   = nodecodes.hlist
local vlist_code   = nodecodes.vlist
local rule_code    = nodecodes.rule
local penalty_code = nodecodes.penalty

local find_tail    = nodes.tail

----- metapost     = fonts.glyphs.metapost

local characters   = fonts.hashes.characters
local quaddata     = fonts.hashes.emwidths
local shapes       = fonts.hashes.shapes
local topaths      = metapost.paths

local f_code       = formatters["mfun_do_outline_text_flush(%q,%i,%.4F,%.4F)(%,t);"]
local s_nothing    = "(origin scaled 10)"
local f_trace_rule = formatters["draw rule(%6F,%6F,%6F) shifted (%6F,%6F) withcolor .5white;"]
local f_strut      = formatters["strut(%6F,%6F);"]
local f_hrule      = formatters["draw rule(%6F,%6F,%6F);"]
local f_vrule      = formatters["draw rule(%6F,%6F,%6F) shifted (%6F,%6F);"]
local f_bounds     = formatters["checkbounds(%6F,%6F,%6F,%6F);"]

local sc = 10
local fc = number.dimenfactors.bp * sc / 10

-- todo: make the next more efficient:

function metapost.output(kind,font,char,advance,shift,ex)
    local character = characters[font][char]
    if character then
        local index = character.index
        if index then
            local shapedata = shapes[font]
            local glyphs    = shapedata.glyphs -- todo: subfonts fonts.shapes.indexed(font,sub)
            if glyphs then
                local glyf = glyphs[index]
                if glyf then
                    local units     = shapedata.units or 1000
                    local yfactor   = sc/units
                    local xfactor   = yfactor
                    local shift     = shift or 0
                    local advance   = advance or 0
                    local exfactor  = ex or 0
                    local wfactor   = 1
                    if exfactor ~= 0 then
                        wfactor = (1+(ex/units)/1000)
                        xfactor = xfactor * wfactor
                    end
                    local paths = topaths(glyf,xfactor,yfactor)
                    local code  = f_code(kind,#paths,advance,shift,paths)
                    return code, character.width * fc * wfactor
                end
            end
        end
    end
    return s_nothing, 10 * sc/1000
end

-- not ok yet: leftoffset in framed not handled well

local signal = -0x3FFFFFFF - 1

function fonts.metapost.boxtomp(n,kind)

    local result   = { }
    local advance  = 0   -- in bp
    local distance = 0

    local llx, lly, urx, ury = 0, 0, 0, 0

    local boxtomp

    local function horizontal(current,shift,glue_sign,glue_set,glue_order,ht,dp)
        shift = shift or 0
        while current do
            local id = current.id
            if id == glyph_code then
                local code, width = metapost.output(kind,current.font,current.char,advance,-shift*fc,current.expansion_factor)
                result[#result+1] = code
                advance = advance + width
            elseif id == disc_code then
                local replace = current.replace
                if replace then
                    horizontal(replace,shift,glue_sign,glue_set,glue_order,ht,dp)
                end
            elseif id == kern_code then
                local kern = current.kern * fc
                if trace_skips then
                    result[#result+1] = f_trace_rule(kern,0.8*ht*fc,0.8*dp*fc,advance,-shift*fc)
                end
                advance = advance + kern
            elseif id == glue_code then
                local width = current.width
                if glue_sign == 1 then
                    if current.stretch_order == glue_order then
                        width = (width + current.stretch * glue_set) * fc
                    else
                        width = width * fc
                    end
                elseif glue_sign == 2 then
                    if current.shrink_order == glue_order then
                        width = (width - current.shrink * glue_set) * fc
                    else
                        width = width * fc
                    end
                else
                    width = width * fc
                end
                if trace_skips then
                    result[#result+1] = f_trace_rule(width,0.1*ht*fc,0.1*dp*fc,advance,-shift*fc)
                end
                advance = advance + width
            elseif id == hlist_code then
                local a = advance
                boxtomp(current,shift+current.shift,current.glue_sign,current.glue_set,current.glue_order)
                advance = a + current.width * fc
            elseif id == vlist_code then
                boxtomp(current) -- ,distance + shift,current.glue_set*current.glue_sign)
                advance = advance + current.width * fc
            elseif id == rule_code then
                local wd = current.width
                local ht = current.height
                local dp = current.depth
                if not (ht == signal or dp == signal or wd == signal) then
                    ht = ht - shift
                    dp = dp - shift
                    if wd == 0 then
                        result[#result+1] = f_strut(ht*fc,-dp*fc)
                    else
                        result[#result+1] = f_hrule(wd*fc,ht*fc,-dp*fc)
                    end
                end
                if wd ~= signal then
                    advance = advance + wd * fc
                end
            end
            current = current.next
        end
    end

    local function vertical(current,shift)
        shift = shift or 0
        current = find_tail(current) -- otherwise bad bbox
        while current do
            local id = current.id
            if id == hlist_code then
                distance = distance - current.depth
                boxtomp(current,distance + shift,current.glue_set*current.glue_sign)
                distance = distance - current.height
            elseif id == vlist_code then
                print("vertical >>>")
                vertical(current.list,0)
            elseif id == kern_code then
                distance = distance - current.kern
                advance  = 0
            elseif id == glue_code then
                distance = distance - current.width
                advance  = 0
            elseif id == rule_code then
                local wd = current.width
                local ht = current.height
                local dp = current.depth
                if not (ht == signal or dp == signal or wd == signal) then
                    distance = distance - dp
                    if wd == 0 then
                        result[#result+1] = f_strut(ht*fc,-dp*fc)
                    else
                        result[#result+1] = f_vrule(wd*fc,ht*fc,-dp*fc,0,distance+shift)
                    end
                    distance = distance - ht
                end
            end
            current = current.prev
        end
    end

    boxtomp = function(list,shift)
        local current = list.list
        if current then
            if list.id == hlist_code then
                horizontal(current,shift,list.glue_sign,list.glue_set,list.glue_order,list.height,list.depth)
            else
                vertical(current,shift)
            end
        end
    end

    local box = tex.box[n]

    boxtomp(box,box.shift,box.glue_sign,box.glue_set,box.glue_order)

    local wd = box.width
    local ht = box.height
    local dp = box.depth
    local sh = box.shift

    result[#result+1] = f_bounds(0,-dp*fc,wd*fc,ht*fc)

    return concat(result)

end
