From deecfe09c774d4c2835f6999b2cdd9ca07e9bdae Mon Sep 17 00:00:00 2001
From: Hans Hagen 
Date: Mon, 20 Aug 2007 10:21:00 +0200
Subject: stable 2007.08.20 10:21
---
 tex/context/base/attr-ini.lua     | 697 +++++++++++++++---------------
 tex/context/base/attr-ini.tex     | 122 +++---
 tex/context/base/char-ini.lua     |  11 +-
 tex/context/base/char-utf.lua     |  10 +-
 tex/context/base/colo-hex.tex     |   4 +
 tex/context/base/colo-ini.tex     | 182 ++++----
 tex/context/base/colo-run.tex     |  67 +--
 tex/context/base/cont-new.mkiv    |  37 +-
 tex/context/base/cont-new.tex     |  53 ++-
 tex/context/base/context.tex      |  11 +-
 tex/context/base/core-con.lua     |  96 +++--
 tex/context/base/core-con.mkiv    |  60 +--
 tex/context/base/core-mat.tex     |   2 +-
 tex/context/base/core-obj.tex     |  10 +
 tex/context/base/core-rul.tex     |  49 ++-
 tex/context/base/core-sec.tex     |  39 +-
 tex/context/base/core-spa.lua     |  26 +-
 tex/context/base/core-spa.tex     | 130 +++---
 tex/context/base/core-tab.tex     |  41 +-
 tex/context/base/core-uti.mkiv    |  12 +-
 tex/context/base/core-var.tex     |   3 +
 tex/context/base/font-afm.lua     | 158 ++++---
 tex/context/base/font-def.lua     |  75 ++--
 tex/context/base/font-enc.lua     |  21 +-
 tex/context/base/font-ini.lua     |   3 +-
 tex/context/base/font-ini.mkiv    |  22 +-
 tex/context/base/font-map.lua     | 185 +++-----
 tex/context/base/font-otf.lua     | 484 ++++++++++++++++++---
 tex/context/base/font-syn.lua     |   6 +-
 tex/context/base/font-tfm.lua     |  90 ++--
 tex/context/base/l-aux.lua        |  48 ++-
 tex/context/base/l-boolean.lua    |   2 +
 tex/context/base/l-file.lua       |   2 +-
 tex/context/base/l-string.lua     |  12 +
 tex/context/base/l-table.lua      |  36 ++
 tex/context/base/l-tex.lua        |   2 +-
 tex/context/base/l-utils.lua      |   7 +-
 tex/context/base/l-xml.lua        | 887 ++++++++++++++++++++++++++++++++++++++
 tex/context/base/lang-ini.lua     |   2 +-
 tex/context/base/lang-sla.mkiv    |   4 +-
 tex/context/base/luat-cbk.lua     |   2 +-
 tex/context/base/luat-crl.lua     |   6 +-
 tex/context/base/luat-ini.lua     |  17 +-
 tex/context/base/luat-ini.tex     |  22 +-
 tex/context/base/luat-inp.lua     |  82 +++-
 tex/context/base/luat-lib.tex     |   2 +-
 tex/context/base/luat-tex.lua     |  34 +-
 tex/context/base/luat-tmp.lua     | 106 ++---
 tex/context/base/luat-tra.lua     | 104 +++--
 tex/context/base/lxml-ini.lua     |  33 ++
 tex/context/base/lxml-ini.tex     |  23 +
 tex/context/base/meta-mis.tex     |   2 +-
 tex/context/base/meta-pdf.lua     |  67 ++-
 tex/context/base/meta-pdf.mkiv    |   2 -
 tex/context/base/meta-pdf.tex     |  49 ++-
 tex/context/base/mult-con.tex     |  12 +
 tex/context/base/mult-sys.tex     |   2 +
 tex/context/base/node-ini.lua     | 578 +++++++++++++------------
 tex/context/base/page-flt.tex     | 497 ++++++++++-----------
 tex/context/base/page-imp.tex     |   4 +-
 tex/context/base/page-ini.tex     |   3 -
 tex/context/base/prop-ini.tex     |  27 +-
 tex/context/base/prop-lay.tex     |   8 -
 tex/context/base/prop-mis.mkii    | 155 +++++++
 tex/context/base/prop-mis.mkiv    |  46 ++
 tex/context/base/prop-mis.tex     | 177 +-------
 tex/context/base/s-abr-01.tex     |   1 +
 tex/context/base/spec-dpx.tex     |   2 +
 tex/context/base/spec-fdf.tex     |  15 +-
 tex/context/base/spec-tpd.tex     |   2 +
 tex/context/base/syst-con.lua     |  44 +-
 tex/context/base/syst-con.mkiv    |  16 +-
 tex/context/base/toks-ini.lua     |  58 +--
 tex/context/base/toks-ini.tex     |  10 +-
 tex/context/interface/keys-cz.xml |   5 +-
 tex/context/interface/keys-de.xml |   5 +-
 tex/context/interface/keys-en.xml |   5 +-
 tex/context/interface/keys-fr.xml |   5 +-
 tex/context/interface/keys-it.xml |   5 +-
 tex/context/interface/keys-nl.xml |   5 +-
 tex/context/interface/keys-ro.xml |   5 +-
 81 files changed, 3844 insertions(+), 2107 deletions(-)
 create mode 100644 tex/context/base/l-xml.lua
 create mode 100644 tex/context/base/lxml-ini.lua
 create mode 100644 tex/context/base/lxml-ini.tex
 create mode 100644 tex/context/base/prop-mis.mkii
 create mode 100644 tex/context/base/prop-mis.mkiv
(limited to 'tex')
diff --git a/tex/context/base/attr-ini.lua b/tex/context/base/attr-ini.lua
index 4469608c4..81ca873d9 100644
--- a/tex/context/base/attr-ini.lua
+++ b/tex/context/base/attr-ini.lua
@@ -29,11 +29,17 @@ function totokens(str)
     return t
 end
 
-function pdfliteral(str)
-  local t = node.new('whatsit',8)
-  t.mode = 1    -- direct
-  t.data = str  -- totokens(str)
-  return t
+-- temp hack, will be proper driver stuff
+
+backends     = backends     or { }
+backends.pdf = backends.pdf or { }
+backend      = backend      or backends.pdf
+
+function backends.pdf.literal(str)
+    local t = node.new('whatsit',8)
+    t.mode = 1    -- direct
+    t.data = str  -- totokens(str)
+    return t
 end
 
 -- shipouts
@@ -45,44 +51,44 @@ do
 
     local hlist, vlist = node.id('hlist'), node.id('vlist')
 
-    local function do_process_page(attribute,processor,head) -- maybe work with ranges
-        local previous, stack = nil, head
-        while stack do
-            local id = stack.id
-            if id == hlist or id == vlist then
-                local content = stack.list
-                if content then
-                    stack.list = do_process_page(attribute,processor,content)
-                end
-            else
-                stack, previous, head = processor(attribute,stack,previous,head)
-            end
-            previous = stack
-            stack = stack.next
-        end
-        return head
-    end
+    local contains = node.has_attribute
+
+    nodes.trigger    = false
+    nodes.triggering = false
+
+    -- we used to do the main processor loop here and call processor for each node
+    -- but eventually this was too much a slow down (1 sec on 23 for 120 pages mk)
+    -- so that we moved looping to teh processor itself; this may lead to a bit of
+    -- duplicate code once that we have more state handlers
 
     function nodes.process_page(head)
+        local trigger = nodes.trigger
         if head then
-            input.start_timing(nodes)
+            input.start_timing(attributes)
             local done, used = false, { }
             for name, plugin in pairs(shipouts.plugins) do
                 local attribute = attributes.numbers[name]
                 if attribute then
-                    local initializer = plugin.initializer
-                    local processor   = plugin.processor
-                    local finalizer   = plugin.finalizer
-                    if initializer then
-                        initializer(attribute,head)
-                    end
-                    if processor then
-                        head = do_process_page(attribute,processor,head)
-                    end
-                    if finalizer then
-                        local ok
-                        ok, head, used[attribute] = finalizer(attribute,head)
-                        done = done or ok
+                    local namespace   = plugin.namespace
+                    if namespace.enabled then
+                        local initializer = plugin.initializer
+                        local processor   = plugin.processor
+                        local finalizer   = plugin.finalizer
+                        local resolver    = plugin.resolver
+                        if initializer then
+                            initializer(namespace,attribute,head)
+                        end
+                        if processor then
+                            local inheritance = (resolver and resolver()) or -1
+                            local ok
+                            ok, head = processor(namespace,attribute,head,inheritance)
+                            done = done or ok
+                        end
+                        if finalizer then -- no need when not ok
+                            local ok
+                            ok, head, used[attribute] = finalizer(namespace,attribute,head)
+                            done = done or ok
+                        end
                     end
                 else
                     texio.write_nl(string.format("undefined attribute %s",name))
@@ -92,14 +98,17 @@ do
                 for name, plugin in pairs(shipouts.plugins) do
                     local attribute = attributes.numbers[name]
                     if used[attribute] then
-                        local flusher = plugin.flusher
-                        if flusher then
-                            head = flusher(attribute,head,used[attribute])
+                        local namespace = plugin.namespace
+                        if namespace.enabled then
+                            local flusher  = plugin.flusher
+                            if flusher then
+                                head = flusher(namespace,attribute,head,used[attribute])
+                            end
                         end
                     end
                 end
             end
-            input.stop_timing(nodes)
+            input.stop_timing(attributes)
         end
         return head
     end
@@ -132,7 +141,7 @@ states = { }
 
 do
 
-    local glyph, rule, whatsit = node.id('glyph'), node.id('rule'), node.id('whatsit')
+    local glyph, rule, whatsit, hlist, vlist = node.id('glyph'), node.id('rule'), node.id('whatsit'), node.id('hlist'), node.id('vlist')
 
     local current, used, done = 0, { }, false
 
@@ -140,11 +149,11 @@ do
         current, used, done = 0, { }, false
     end
 
-    local contains = node.has_attribute
+    local contains, copy = node.has_attribute, node.copy
 
-    function insert(n,stack,previous,head)
+    local function insert(n,stack,previous,head)
         if n then
-            n = node.copy(n)
+            n = copy(n)
             n.next = stack
             if previous then
                 previous.next = n
@@ -156,94 +165,128 @@ do
         return stack, previous, head
     end
 
-    function states.finalize(what,attribute,head)
-        if what.enabled and what.none and current > 0 and head.list then
-            local head = head.list
-            stack, previous, head = insert(what.none,list,nil,list)
+    function states.finalize(namespace,attribute,head)
+        if current > 0 and namespace.none then
+            if head.id == hlist or head.id == vlist then
+                local stack, previous, head = insert(namespace.none,head.list,nil,head.list)
+            else
+                local stack, previous, head = insert(namespace.none,head,nil,head)
+            end
+            return true, head, true
+        else
+            return false, head, false
         end
-        return done, head, used
     end
 
---~     function states.process(what,attribute,stack,previous,head) -- one attribute
---~         if what.enabled then
---~             local c = contains(stack,attribute)
---~             if c then
---~                 if current ~= c then
---~                     local id = stack.id
---~                     if id == glyph or id == rule or id == whatsit then
---~                         stack, previous, head = insert(what.data[c],stack,previous,head)
---~                         current, done, used[c] = c, true, true
---~                     end
---~                 end
---~             elseif current > 0 then
---~                 stack, previous, head = insert(what.none,stack,previous,head)
---~                 current, done, used[0] = 0, true, true
---~             end
---~         end
---~         return stack, previous, head
---~     end
-
-    function states.process(what,attribute,stack,previous,head) -- one attribute
-        if what.enabled then
+    function states.process(namespace,attribute,head,inheritance,default) -- one attribute
+        local trigger = nodes.triggering and nodes.trigger
+        local stack, previous, done, process = head, nil, false, states.process
+        while stack do
             local id = stack.id
-            if id == glyph or id == rule then -- or id == whatsit then
+            if id == hlist or id == vlist then
+                local content = stack.list
+                if content then
+                    local ok = false
+                    if trigger and contains(stack,trigger) then
+                        local outer = contains(stack,attribute)
+                        if outer ~= inheritance then
+                            ok, stack.list = process(namespace,attribute,content,inheritance,outer)
+                        else
+                            ok, stack.list = process(namespace,attribute,content,inheritance,default)
+                        end
+                    else
+                        ok, stack.list = process(namespace,attribute,content,inheritance,default)
+                    end
+                    done = done or ok
+                end
+            elseif id == glyph or id == rule or id == whatsit then -- special
                 local c = contains(stack,attribute)
                 if c then
-                    if current ~= c then
-                        stack, previous, head = insert(what.data[c],stack,previous,head)
+                    if default and c == inheritance then
+                        if current ~= default then
+                            local data = namespace.data[default] or namespace.reviver(default)
+                            stack, previous, head = insert(data,stack,previous,head)
+                            current, done, used[default] = default, true, true
+                        end
+                    elseif current ~= c then
+                        local data = namespace.data[c] or namespace.reviver(c)
+                        stack, previous, head = insert(data,stack,previous,head)
                         current, done, used[c] = c, true, true
                     end
+                elseif default and inheritance then
+                    if current ~= default then
+                        local data = namespace.data[default] or namespace.reviver(default)
+                        stack, previous, head = insert(data,stack,previous,head)
+                        current, done, used[default] = default, true, true
+                    end
                 elseif current > 0 then
-                    stack, previous, head = insert(what.none,stack,previous,head)
+                    stack, previous, head = insert(namespace.none,stack,previous,head)
                     current, done, used[0] = 0, true, true
                 end
             end
+            previous = stack
+            stack = stack.next
         end
-        return stack, previous, head
+        return done, head
     end
 
---~     function states.selective(what,attribute,stack,previous,head) -- two attributes
---~         if what.enabled then
---~             local c = contains(stack,attribute)
---~             if c then
---~                 if current ~= c then
---~                     local id = stack.id
---~                     if id == glyph or id == rule then -- or id == whatsit then
---~                         stack, previous, head = insert(what.data[c][contains(stack,what.selector) or what.default],stack,previous,head)
---~                         current, done, used[c] = c, true, true
---~                     end
---~                 end
---~             elseif current > 0 then
---~                 local id = stack.id
---~                 if id == glyph or id == rule then -- or id == whatsit then
---~                     stack, previous, head = insert(what.none,stack,previous,head)
---~                     current, done, used[0] = 0, true, true
---~                 end
---~             end
---~         end
---~         return stack, previous, head
---~     end
+    -- we can force a selector, e.g. document wide color spaces, saves a little
 
-    function states.selective(what,attribute,stack,previous,head) -- two attributes
-        if what.enabled then
+    function states.selective(namespace,attribute,head,inheritance,default) -- two attributes
+        local trigger = nodes.triggering and nodes.trigger
+        local stack, previous, done, selective = head, nil, false, states.selective
+        local defaultselector, forcedselector, selector, reviver = namespace.default, namespace.forced, namespace.selector, namespace.reviver
+        local none = namespace.none
+        while stack do
             local id = stack.id
-            if id == glyph or id == rule then -- or id == whatsit then
+            if id == hlist or id == vlist then
+                local content = stack.list
+                if content then
+                    local ok = false
+                    if trigger and contains(stack,trigger) then
+                        local outer = contains(stack,attribute)
+                        if outer ~= inheritance then
+                            ok, stack.list = selective(namespace,attribute,content,inheritance,outer)
+                        else
+                            ok, stack.list = selective(namespace,attribute,content,inheritance,default)
+                        end
+                    else
+                        ok, stack.list = selective(namespace,attribute,content,inheritance,default)
+                    end
+                    done = done or ok
+                end
+            elseif id == glyph or id == rule or id == whatsit then -- special
                 local c = contains(stack,attribute)
                 if c then
-                    if current ~= c then
-                        stack, previous, head = insert(what.data[c][contains(stack,what.selector) or what.default],stack,previous,head)
+                    if default and c == inheritance then
+                        if current ~= default then
+                            local data = namespace.data[default] or reviver(default)
+                            stack, previous, head = insert(data[forcedselector or contains(stack,selector) or defaultselector],stack,previous,head)
+                            current, done, used[default] = default, true, true
+                        end
+                    elseif current ~= c then
+                        local data = namespace.data[c] or reviver(c)
+                        stack, previous, head = insert(data[forcedselector or contains(stack,selector) or defaultselector],stack,previous,head)
                         current, done, used[c] = c, true, true
                     end
+                elseif default and inheritance then
+                    if current ~= default then
+                        local data = namespace.data[default] or reviver(default)
+                        stack, previous, head = insert(data[forcedselector or contains(stack,selector) or defaultselector],stack,previous,head)
+                        current, done, used[default] = default, true, true
+                    end
                 elseif current > 0 then
-                    stack, previous, head = insert(what.none,stack,previous,head)
+                    stack, previous, head = insert(none,stack,previous,head)
                     current, done, used[0] = 0, true, true
                 end
             end
+            previous = stack
+            stack = stack.next
         end
-        return stack, previous, head
+        return done, head
     end
 
-    collected = { }
+    local collected = { }
 
     function states.collect(str)
         collected[#collected+1] = str
@@ -269,24 +312,40 @@ end
 
 -- we also need to store the colorvalues because we need then in mp
 
-colors            = colors or { }
-colors.enabled    = true
-colors.data       = colors.data or { }
-colors.strings    = colors.strings or { }
+colors            = colors            or { }
+colors.data       = colors.data       or { }
+colors.values     = colors.values     or { }
 colors.registered = colors.registered or { }
+colors.enabled    = true
 colors.weightgray = true
 colors.attribute  = 0
 colors.selector   = 0
 colors.default    = 1
+colors.main     = nil
 
-input.storage.register(true,"colors/data", colors.strings, "colors.data")
+-- This is a compromis between speed and simplicity. We used to store the
+-- values and data in one array, which made in neccessary to store the
+-- converters that need node constructor into strings and evaluate them
+-- at runtime (after reading from storage). Think of:
+--
+-- colors.strings    = colors.strings    or { }
+--
+-- if environment.initex then
+--     colors.strings[color] = "return colors." .. colorspace .. "(" .. table.concat({...},",") .. ")"
+-- end
+--
+-- input.storage.register(true,"colors/data", colors.strings, "colors.data") -- evaluated
+--
+-- We assume that only processcolors are defined in the format.
+
+input.storage.register(false,"colors/values",     colors.values,     "colors.values")
 input.storage.register(false,"colors/registered", colors.registered, "colors.registered")
 
 colors.stamps = {
     rgb  = "r:%s:%s:%s",
     cmyk = "c:%s:%s:%s:%s",
     gray = "s:%s",
-    spot = "p:%s:%s"
+    spot = "p:%s:%s:%s:%s"
 }
 
 colors.models = {
@@ -303,23 +362,23 @@ do
     local format = string.format
     local concat = table.concat
 
-    local function rgbdata(r,g,b)
-        return pdfliteral(format("%s %s %s rg %s %s %s RG",r,g,b,r,g,b))
+    local function rgbdata(r,g,b) -- dodo: backends.pdf.rgbdata
+        return backends.pdf.literal(format("%s %s %s rg %s %s %s RG",r,g,b,r,g,b))
     end
 
     local function cmykdata(c,m,y,k)
-        return pdfliteral(format("%s %s %s %s k %s %s %s %s K",c,m,y,k,c,m,y,k))
+        return backends.pdf.literal(format("%s %s %s %s k %s %s %s %s K",c,m,y,k,c,m,y,k))
     end
 
     local function graydata(s)
-        return pdfliteral(format("%s g %s G",s,s))
+        return backends.pdf.literal(format("%s g %s G",s,s))
     end
 
-    local function spotdata(n,p) -- name, parent, ratio
+    local function spotdata(n,f,d,p)
         if type(p) == "string" then
             p = p:gsub(","," ") -- brr misuse of spot
         end
-        return pdfliteral(format("/%s cs /%s CS %s SCN %s scn",n,n,p,p))
+        return backends.pdf.literal(format("/%s cs /%s CS %s SCN %s scn",n,n,p,p))
     end
 
     local function rgbtocmyk(r,g,b)
@@ -349,33 +408,54 @@ do
     -- default color space
 
     function colors.gray(s)
-        local gray = graydata(s)
-        return { gray, gray, gray, gray, 2, s, 0, 0, 0, 0, 0, 0, 1 }
+        return { 2, s, 0, 0, 0, 0, 0, 0, 1 }
     end
 
     function colors.rgb(r,g,b)
         local s = rgbtogray(r,g,b)
         local c, m, y, k = rgbtocmyk(r,g,b)
-        local gray, rgb, cmyk = graydata(s), rgbdata(r,g,b), cmykdata(c,m,y,k)
-        return { rgb, gray, rgb, cmyk, 3, s, r, g, b, c, m, y, k }
+        return { 3, s, r, g, b, c, m, y, k }
     end
 
     function colors.cmyk(c,m,y,k)
         local s = cmyktogray(c,m,y,k)
         local r, g, b = cmyktorgb(c,m,y,k)
-        local gray, rgb, cmyk = graydata(s), rgbdata(r,g,b), cmykdata(c,m,y,k)
-        return { cmyk, gray, rgb, cmyk, 4, s, r, g, b, c, m, y, k }
+        return { 4, s, r, g, b, c, m, y, k }
     end
 
-    function colors.spot(parent,p) -- parent, ratio
-        local spot = spotdata(parent,p)
-        if type(p) == "string" and p:find(",") then
-            -- use converted replacement (combination color)
-        else
-            -- todo: map gray, rgb, cmyk onto fraction*parent
+    function colors.spot(parent,f,d,p)
+--~         if type(p) == "string" and p:find(",") then
+--~             -- use converted replacement (combination color)
+--~         else
+--~             -- todo: map gray, rgb, cmyk onto fraction*parent
+--~         end
+        return { 5, .5, .5, .5, .5, 0, 0, 0, .5, parent, f, d, p }
+    end
+
+    function colors.reviver(n)
+        local d = colors.data[n]
+        if not d then
+            local v = colors.values[n]
+            if not v then
+                local gray = graydata(0)
+                d = { gray, gray, gray, gray }
+                logs.report("attributes",string.format("unable to revive color %s",n or "?"))
+            else
+                local kind, gray, rgb, cmyk = v[1], graydata(v[2]), rgbdata(v[3],v[4],v[5]), cmykdata(v[6],v[7],v[8],v[9])
+                if kind == 2 then
+                    d = { gray, gray, gray, gray }
+                elseif kind == 3 then
+                    d = { rgb, gray, rgb, cmyk }
+                elseif kind == 4 then
+                    d = { cmyk, gray, rgb, cmyk }
+                elseif kind == 5 then
+                    local spot = spotdata(v[10],v[11],v[12],v[13])
+                    d = { spot, gray, rgb, cmyk }
+                end
+            end
+            colors.data[n] = d
         end
-        local gray, rgb, cmyk = graydata(.5), rgbdata(.5,.5,.5), cmykdata(0,0,0,.5)
-        return { spot, gray, rgb, cmyk, 5 }
+        return d
     end
 
     function colors.filter(n)
@@ -386,284 +466,197 @@ do
 
 end
 
--- conversion models
-
 function colors.setmodel(attribute,name)
     colors.selector = attributes.numbers[attribute]
     colors.default = colors.models[name] or 1
     return colors.default
 end
 
-function colors.register(attribute, name, colorspace, ...)
+function colors.register(attribute, name, colorspace, ...) -- passing 9 vars is faster
     local stamp = string.format(colors.stamps[colorspace], ...)
     local color = colors.registered[stamp]
     if not color then
-        local cd = colors.data
-        color = #cd+1
-        cd[color] = colors[colorspace](...)
-        if environment.initex then
-            colors.strings[color] = "return colors." .. colorspace .. "(" .. table.concat({...},",") .. ")"
-        end
+        color = #colors.values+1
+        colors.values[color] = colors[colorspace](...)
         colors.registered[stamp] = color
+        colors.reviver(color)
+    end
+    if name then
+        attributes.list[attributes.numbers[attribute]][name] = color -- not grouped, so only global colors
     end
-    attributes.list[attributes.numbers[attribute]][name] = color
     return colors.registered[stamp]
 end
 
+function colors.value(id)
+    return colors.values[id]
+end
+
 shipouts.plugins.color = {
-    initializer = function(...) return states.initialize(colors,...) end,
-    finalizer   = function(...) return states.finalize  (colors,...) end,
-    processor   = function(...) return states.selective (colors,...) end,
+    namespace   = colors,
+    initializer = states.initialize,
+    finalizer   = states.finalize  ,
+    processor   = states.selective ,
+    resolver    = function(...) return colors.main end,
+}
+
+-- transparencies
+
+-- for the moment we manage transparencies in the pdf driver because
+-- first we need a nice interface to some pdf things
+
+transparencies            = transparencies            or { }
+transparencies.registered = transparencies.registered or { }
+transparencies.data       = transparencies.data       or { }
+transparencies.values     = transparencies.values     or { }
+transparencies.enabled    = true
+transparencies.template   = "%s:%s"
+
+input.storage.register(false, "transparencies/registered", transparencies.registered, "transparencies.registered")
+input.storage.register(false, "transparencies/data",       transparencies.data,       "transparencies.data")
+input.storage.register(false, "transparencies/values",     transparencies.values,     "transparencies.values")
+
+function transparencies.reference(n)
+    return backends.pdf.literal(string.format("/Tr%s gs",n))
+end
+
+transparencies.none = transparencies.reference(0)
+
+function transparencies.register(name,...)
+    local stamp = string.format(transparencies.template, ...)
+    local n = transparencies.registered[stamp]
+    if not n then
+        n = #transparencies.data+1
+        transparencies.data[n] = transparencies.reference(n)
+        transparencies.values[n] = { ... }
+        transparencies.registered[stamp] = n
+        states.collect(string.format("\\presetPDFtransparency{%s}{%s}",...)) -- too many, but experimental anyway
+    end
+    return transparencies.registered[stamp]
+end
+
+function transparencies.value(id)
+    return transparencies.values[id]
+end
+
+shipouts.plugins.transparency = {
+    namespace   = transparencies,
+    initializer = states.initialize,
+    finalizer   = states.finalize  ,
+    processor   = states.process   ,
 }
 
 --- overprint / knockout
 
-overprints = { enabled = true , data = { } }
+overprints         = overprints      or { }
+overprints.data    = overprints.data or { }
+overprints.enabled = true
+
+overprints.data[1] = backends.pdf.literal(string.format("/GSoverprint gs"))
+overprints.data[2] = backends.pdf.literal(string.format("/GSknockout  gs"))
 
-overprints.none    = pdfliteral(string.format("/GSoverprint gs"))
-overprints.data[1] = pdfliteral(string.format("/GSknockout gs"))
+overprints.none    = overprints.data[1]
 
 overprints.registered = {
-    overprint = 0,
-    knockout  = 1,
+    overprint = 1,
+    knockout  = 2,
 }
 
 function overprints.register(stamp)
+--  states.collect(tex.sprint(tex.ctxcatcodes,"\\initializePDFoverprint")) -- to be testd
     return overprints.registered[stamp] or overprints.registered.overprint
 end
 
 shipouts.plugins.overprint = {
-    initializer = function(...) return states.initialize(overprints,...) end,
-    finalizer   = function(...) return states.finalize  (overprints,...) end,
-    processor   = function(...) return states.process   (overprints,...) end,
+    namespace   = overprints,
+    initializer = states.initialize,
+    finalizer   = states.finalize  ,
+    processor   = states.process   ,
 }
 
 --- negative / positive
 
-negatives = { enabled = true, data = { } }
+negatives         = netatives      or { }
+negatives.data    = negatives.data or { }
+negatives.enabled = true
 
-negatives.none    = pdfliteral(string.format("/GSpositive gs"))
-negatives.data[1] = pdfliteral(string.format("/GSnegative gs"))
+negatives.data[1] = backends.pdf.literal(string.format("/GSpositive gs"))
+negatives.data[2] = backends.pdf.literal(string.format("/GSnegative gs"))
+
+negatives.none    = negatives.data[1]
 
 negatives.registered = {
-    positive = 0,
-    negative = 1,
+    positive = 1,
+    negative = 2,
 }
 
 function negatives.register(stamp)
+--  states.collect(tex.sprint(tex.ctxcatcodes,"\\initializePDFnegative")) -- to be testd
     return negatives.registered[stamp] or negatives.registered.positive
 end
 
 shipouts.plugins.negative = {
-    initializer = function(...) return states.initialize(negatives,...) end,
-    finalizer   = function(...) return states.finalize  (negatives,...) end,
-    processor   = function(...) return states.process   (negatives,...) end,
+    namespace   = negatives,
+    initializer = states.initialize,
+    finalizer   = states.finalize,
+    processor   = states.process,
 }
 
 -- effects
 
-effects = { enabled = true, data = { } }
-
-effects.none    = pdfliteral(string.format("0 Tr"))
-effects.data[1] = pdfliteral(string.format("1 Tr"))
-effects.data[2] = pdfliteral(string.format("2 Tr"))
-effects.data[3] = pdfliteral(string.format("3 Tr"))
+effects            = effects            or { }
+effects.data       = effects.data       or { }
+effects.registered = effects.registered or { }
+effects.enabled    = true
+effects.stamp      = "%s:%s:%s"
+
+input.storage.register(false, "effects/registered", effects.registered, "effects.registered")
+input.storage.register(false, "effects/data",       effects.data,       "effects.data")
+
+function effects.register(effect,stretch,rulethickness)
+    local stamp = string.format(effects.stamp,effect,stretch,rulethickness)
+    local n = effects.registered[stamp]
+    if not n then
+        n = #effects.data+1
+        effects.data[n] = effects.reference(effect,stretch,rulethickness)
+        effects.registered[stamp] = n
+    --  states.collect("") -- nothing
+    end
+    return effects.registered[stamp]
+end
 
-effects.registered = {
-    normal = 0,
-    inner  = 0,
-    outer  = 1,
-    both   = 2,
-    hidden = 3,
+backends.pdf.effects = {
+    normal = 1,
+    inner  = 1,
+    outer  = 2,
+    both   = 3,
+    hidden = 4,
 }
 
-function effects.register(stamp)
-    return effects.registered[stamp] or effects.registered.normal
+function effects.reference(effect,stretch,rulethickness) -- will move, test code, we will develop a proper model for that
+    effect = backends.pdf.effects[effects] or backends.pdf.effects['normal']
+    if stretch > 0 then
+        stretch = stretch .. " w "
+    else
+        stretch = ""
+    end
+    if rulethickness > 0 then
+        rulethickness = number.dimenfactors["bp"]*rulethickness.. " Tc "
+    else
+        rulethickness = ""
+    end
+    return backends.pdf.literal(string.format("%s%s%s Tr",stretch,rulethickness,effect)) -- watch order
 end
 
+effects.none = effects.reference(effect,0,0)
+
 shipouts.plugins.effect = {
-    initializer = function(...) return states.initialize(effects,...) end,
-    finalizer   = function(...) return states.finalize  (effects,...) end,
-    processor   = function(...) return states.process   (effects,...) end,
+    namespace   = effects,
+    initializer = states.initialize,
+    finalizer   = states.finalize  ,
+    processor   = states.process   ,
 }
 
 -- layers
 
 --~ /OC /somename BDC
 --~ EMC
-
--- transparencies
-
--- for the moment we manage transparencies in the pdf driver because
--- first we need a nice interface to some pdf things
-
-transparencies = {
-    enabled    = true,
-    data       = { },
-    registered = { },
-    hack       = { }
-}
-
-input.storage.register(false, "transparencies/registed", transparencies.registered, "transparencies.registered")
-input.storage.register(false, "transparencies/data",     transparencies.data,       "transparencies.data")
-input.storage.register(false, "transparencies/hack",     transparencies.hack,       "transparencies.hack")
-
-function transparencies.reference(n)
-    return pdfliteral(string.format("/Tr%s gs",n))
-end
-
-transparencies.none = transparencies.reference(0)
-
-transparencies.stamp = "%s:%s"
-
-function transparencies.register(...)
-    local stamp = string.format(transparencies.stamp, ...)
-    if not transparencies.registered[stamp] then
-        local n = #transparencies.data+1
-        transparencies.data[n] = transparencies.reference(n)
-        transparencies.registered[stamp] = n
-        states.collect(string.format("\\presetPDFtransparency{%s}{%s}",...)) -- too many, but experimental anyway
-    end
-    return transparencies.registered[stamp]
-end
-
-shipouts.plugins.transparency = {
-    initializer = function(...) return states.initialize(transparencies,...) end,
-    finalizer   = function(...) return states.finalize  (transparencies,...) end,
-    processor   = function(...) return states.process   (transparencies,...) end,
-}
-
---~ shipouts.plugins.transparency.flusher = function(attribute,head,used)
---~     local max = 0
---~     for k,v in pairs(used) do
---~     end
---~     return head
---~ end
-
---~ from the time that node lists were tables and not userdata ...
---~
---~     local function do_collapse_page(stack,existing_t)
---~         if stack then
---~             local t = existing_t or { }
---~             for _, node in ipairs(stack) do
---~                 if node then
---~                     local kind = node[1]
---~                     node[3] = nil
---~                     if kind == 'hlist' or kind == 'vlist' then
---~                         node[8] = do_collapse_page(node[8]) -- maybe here check for nil
---~                         t[#t+1] = node
---~                     elseif kind == 'inline' then -- appending literals cost too much time
---~                         local nodes = node[4]
---~                         if #nodes == 1 then
---~                             t[#t+1] = nodes[1]
---~                         else
---~                             do_collapse_page(nodes,t)
---~                         end
---~                     else
---~                         t[#t+1] = node
---~                     end
---~                 end
---~             end
---~             return t
---~         else
---~             return nil
---~         end
---~     end
---~
---~     local function do_process_page(attribute,processor,stack)
---~         if stack then
---~             for i, node in ipairs(stack) do
---~                 if node then
---~                     local kind = node[1]
---~                     if kind == 'hlist' or kind == "vlist" then
---~                         local content = node[8]
---~                         if not content then
---~                             -- nil node
---~                         elseif type(content) == "table" then
---~                             node[8] = do_process_page(attribute,processor,content)
---~                         else
---~                             node[8] = do_process_page(attribute,processor,tex.get_node_list(content))
---~                         end
---~                     elseif kind == 'inline' then
---~                         node[4] = do_process_page(attribute,processor,node[4])
---~                     else
---~                         processor(attribute,stack,i,node,kind)
---~                     end
---~                 end
---~             end
---~         end
---~         return stack
---~     end
---~
---~     function nodes.process_page(stack,...)
---~         if stack then
---~             input.start_timing(nodes)
---~             local done, used = false, { }
---~             for name, plugin in pairs(shipouts.plugins) do
---~                 local attribute = attributes.numbers[name]
---~                 if attribute then
---~                     local initializer = plugin.initializer
---~                     local processor   = plugin.processor
---~                     local finalizer   = plugin.finalizer
---~                     if initializer then
---~                         initializer(attribute,stack)
---~                     end
---~                     if processor then
---~                         do_process_page(attribute,processor,stack)
---~                     end
---~                     if finalizer then
---~                         local ok
---~                         ok, used[attribute] = finalizer(attribute,stack)
---~                         done = done or ok
---~                     end
---~                 else
---~                     texio.write_nl(string.format("undefined attribute %s",name))
---~                 end
---~             end
---~             if done then
---~                 stack = do_collapse_page(stack)
---~                 for name, plugin in pairs(shipouts.plugins) do
---~                     local attribute = attributes.numbers[name]
---~                     if used[attribute] then
---~                         local flusher = plugin.flusher
---~                         if flusher then
---~                             flusher(attribute,stack,used[attribute])
---~                         end
---~                     end
---~                 end
---~             else
---~                 stack = true
---~             end
---~             input.stop_timing(nodes)
---~         end
---~         return stack
---~     end
---~
---~     function states.finalize(what,attribute,stack)
---~         if what.enabled then
---~             if current > 0 then
---~                 local list = stack
---~                 if #stack == 1 then
---~                     list = stack[#stack][8]
---~                 end
---~                 list[#list+1], current, done, used[0] = what.none, 0, true, true
---~             end
---~         end
---~         return done, used
---~     end
---~
---~     function states.process(what,attribute,stack,i,node,kind)
---~         if what.enabled then
---~             local a = node[3]
---~             if a then
---~                 local c = a[attribute]
---~                 if c then
---~                     if current ~= c and (kind == 'glyph' or kind == 'rule') then
---~                         stack[i], current, done, used[c] = nodes.inline(what.data[c], node), c, true, true
---~                     end
---~                 elseif current > 0 then
---~                     stack[i], current, done, used[0] = nodes.inline(what.none, node), 0, true, true
---~                 end
---~             end
---~         end
---~     end
diff --git a/tex/context/base/attr-ini.tex b/tex/context/base/attr-ini.tex
index 60746d560..d579429dd 100644
--- a/tex/context/base/attr-ini.tex
+++ b/tex/context/base/attr-ini.tex
@@ -51,10 +51,15 @@
 
 \defineattribute[mark]
 \defineattribute[status]
+\defineattribute[trigger] % feature inheritance
 % \defineattribute[language]
 \defineattribute[skip]
 \defineattribute[penalty]
 
+\startruntimectxluacode
+    nodes.trigger = \dogetattributeid{trigger}
+\stopruntimectxluacode
+
 % \dosetattribute{status}{1}
 
 % temp here / will be indirect ! just for testing
@@ -67,8 +72,10 @@
 
 % 1=off  2=gray  3=spot  4=rgb  5=cmyk  6=cmy % only 1/2/4/5 are supported
 %
-% we could combine this in one attribute but this is not faster and also
-% less flexible because sometimes we want to freeze the attribute bit
+% We could combine this in one attribute but this is not faster and also
+% less flexible because sometimes we want to freeze the attribute bit.
+%
+% Watch out: real color support will be implemented later.
 
 \newcount\currentcolormodel
 
@@ -76,51 +83,11 @@
   {\currentcolormodel\ctxlua{tex.print(colors.setmodel('colormodel','#1'))}%
    \dosetattribute{colormodel}{\the\currentcolormodel}}
 
-\setcolormodel{all} % when no color, reset ! !
-
-\def\registerrgbcolor#1#2#3#4%  r g b -- print or sprint
-  {\scratchcounter\ctxlua{tex.print(colors.register('color','#1','rgb',#2,#3,#4))}%
-   \setevalue{(ca:#1)}{\number\scratchcounter}%
-   \setevalue{(cv:#1)}{\ctxlua{tex.print(colors.filter(\number\scratchcounter))}}%
-   \setevalue{(cs:#1)}{\dosetattribute{color}{\the\scratchcounter}}}
-
-\def\registercmykcolor#1#2#3#4#5%  c m y k
-  {\scratchcounter\ctxlua{tex.print(colors.register('color','#1','cmyk',#2,#3,#4,#5))}%
-   \setevalue{(ca:#1)}{\number\scratchcounter}%
-   \setevalue{(cv:#1)}{\ctxlua{tex.print(colors.filter(\number\scratchcounter))}}%
-   \setevalue{(cs:#1)}{\dosetattribute{color}{\the\scratchcounter}}}
-
-\def\registergraycolor#1#2% s
-  {\scratchcounter\ctxlua{tex.print(colors.register('color','#1','gray',#2))}%
-   \setevalue{(ca:#1)}{\number\scratchcounter}%
-   \setevalue{(cv:#1)}{\ctxlua{tex.print(colors.filter(\number\scratchcounter))}}%
-   \setevalue{(cs:#1)}{\dosetattribute{color}{\the\scratchcounter}}}
-
-\def\registerspotcolor#1#2#3% p name
-  {\scratchcounter\ctxlua{tex.print(colors.register('color','#1','spot',#2,#3))}%
-   \setevalue{(ca:#1)}{\number\scratchcounter}%
-   \setevalue{(cv:#1)}{\ctxlua{tex.print(colors.filter(\number\scratchcounter))}}%
-   \setevalue{(cs:#1)}{\dosetattribute{color}{\the\scratchcounter}}}
-
-\def\somecolorvalue    #1{\csname(cv:#1)\endcsname}
-\def\somecolorswitch   #1{\csname(cs:#1)\endcsname}
-\def\somecolorattribute#1{\csname(ca:#1)\endcsname}
-
-\def\getMPcolorspec#1%
-  {\expandafter\expandafter\expandafter\dogetMPcolorspec\csname (cv:#1)\endcsname\relax}
-
-\def\dogetMPcolorspec#1:#2:#3:#4:#5:#6:#7:#8:#9\relax % some day we will just pass a attribute number in pre/post
-  {\ifcase\currentcolormodel\or
-     \ifcase#1\or#2\or(#3,#4,#5)\or(#6,#7,#8,#9)\fi\or#2\or(#3,#4,#5)\or(#6,#7,#8,#9)%
-   \fi}
-
-% \registerrgbcolor  {red}     {1}{0}{0}
-% \registerrgbcolor  {green}   {0}{1}{0}
-% \registerrgbcolor  {blue}    {0}{0}{1}
-% \registercmykcolor {cyan}    {1}{0}{0}{0}
-% \registercmykcolor {magenta} {0}{1}{0}{0}
-% \registercmykcolor {yellow}  {0}{0}{1}{0}
-% \registergraycolor {black}   {0}
+\setcolormodel{all}
+
+\def\registerrgbcolor   #1#2#3#4{\ctxlua{colors.register('color','#1','rgb' ,#2,#3,#4)}}
+\def\registercmykcolor#1#2#3#4#5{\ctxlua{colors.register('color','#1','cmyk',#2,#3,#4,#5)}}
+\def\registergraycolor      #1#2{\ctxlua{colors.register('color','#1','gray',#2)}}
 
 % transparency
 
@@ -139,9 +106,12 @@
 \defineattribute[overprint]
 
 \def\registeroverprint#1#2%
-  {\initializePDFoverprint % temp here
+  {\initializePDFoverprint % temp here, to be tested in la code (states.collect)
    \setvalue{(os:#1)}{\dosetattribute{overprint}{\ctxlua{tex.print(overprints.register('#2'))}}}}
 
+\def\dotriggeroverprint#1%
+  {\csname(os:#1)\endcsname}
+
 % \registeroverprint{knockout} {knockout}
 % \registeroverprint{overprint}{overprint}
 
@@ -150,9 +120,12 @@
 \defineattribute[negative]
 
 \def\registernegative#1#2%
-  {\initializePDFnegative % temp here
+  {\initializePDFnegative % temp here, to be tested in la code (states.collect)
    \setvalue{(ns:#1)}{\dosetattribute{negative}{\ctxlua{tex.print(negatives.register('#2'))}}}}
 
+\def\dotriggernegative#1%
+  {\csname(ns:#1)\endcsname}
+
 % \registernegative{positive}{positive}
 % \registernegative{negative}{negative}
 
@@ -160,8 +133,13 @@
 
 \defineattribute[effect]
 
-\def\registereffect#1%
-  {\setevalue{(es:#1)}{\dosetattribute{effect}{\ctxlua{tex.print(effects.register('#1'))}}}}
+\def\registereffect#1#2#3% #2=stretch #3=rulethickness
+  {\setxvalue{(es:#1:#2:\number\dimexpr#3\relax)}%
+     {\dosetattribute{effect}{\ctxlua{tex.print(effects.register('#1',#2,\number\dimexpr#3\relax))}}}}
+
+\def\dotriggereffect#1#2#3%
+  {\ifcsname(es:#1:#2:\number\dimexpr#3\relax)\endcsname\else\registereffect{#1}{#2}{#3}\fi
+   \csname(es:#1:#2:\number\dimexpr#3\relax)\endcsname}
 
 % \registereffect{normal}
 % \registereffect{inner}
@@ -186,14 +164,48 @@
 %      {\ctxlua{nodes.process_page(tex.box[\number\nextbox])}%
 %       \primitive\shipout\box\nextbox}}
 
-\def\processshipoutbox#1% % hack till we have access to pdf backend
-  {\setbox\nextbox\hbox{#1}%
-   \ctxlua{nodes.process_page(tex.box[\number\nextbox])}%
-   \hbox{\ctxlua{states.flush()}\box\nextbox}}
+\newbox\finalizedshipoutbox
+
+\def\finalizeshipoutbox#1% % hack till we have access to pdf backend
+  {\global\setbox\finalizedshipoutbox\hbox{#1}%
+   \ctxlua{nodes.process_page(tex.box[\number\finalizedshipoutbox])}%
+   \hbox{\ctxlua{states.flush()}\box\finalizedshipoutbox}}
 
 % \def\shipout
 %   {\dowithnextbox{\ctxlua{tex.primitive('shipout', nodes.process_page(tex.nextbox)}}}
 
 \let\normalshipout\shipout
 
+% Objects are processed indepently \unknown\ actually we may need a proper callback.
+
+\def\finalizeobjectbox#1%
+  {\ctxlua{nodes.process_page(tex.box[\number#1])}}
+
+% tricky stuff:
+
+\newcount\attributeboxcount
+
+\edef\startinheritattributes{\dosetattribute  {trigger}{1}}
+\edef\stopinheritattributes {\doresetattribute{trigger}}
+
+\def\doattributedcopy  {\afterassignment\dodoattributedcopy\attributeboxcount}
+\def\doattributedbox   {\afterassignment\dodoattributedbox \attributeboxcount}
+
+\def\dodoattributedcopy{\startinheritattributes\ifvbox\attributeboxcount\vbox\else\hbox\fi{\unhcopy\attributeboxcount}\stopinheritattributes}
+\def\dodoattributedbox {\startinheritattributes\ifvbox\attributeboxcount\vbox\else\hbox\fi{\unhbox \attributeboxcount}\stopinheritattributes}
+
+\def\enableattributeinheritance
+  {\ctxlua{nodes.triggering=true}%
+   \let\attributedcopy\doattributedcopy
+   \let\attributedbox \doattributedbox}
+
+\def\disableattributeinheritance
+  {\ctxlua{nodes.triggering=false}%
+   \let\attributedcopy\copy
+   \let\attributedbox \box}
+
+\disableattributeinheritance
+
+% \enableattributeinheritance % will become default
+
 \protect \endinput
diff --git a/tex/context/base/char-ini.lua b/tex/context/base/char-ini.lua
index 0daa91c0d..d7a2044a2 100644
--- a/tex/context/base/char-ini.lua
+++ b/tex/context/base/char-ini.lua
@@ -23,10 +23,15 @@ characters         = characters         or { }
 characters.data    = characters.data    or { }
 characters.context = characters.context or { }
 
-_empty_table_         = { }
-_empty_table_.__index = function(t,k) return "" end
+do
+    local _empty_table_ = { __index = function(t,k) return "" end }
 
-setmetatable(characters.data,_empty_table_)
+    function table.set_empty_metatable(t)
+        setmetatable(t,_empty_table_)
+    end
+end
+
+table.set_empty_metatable(characters.data)
 
 --[[ldx--
 At this point we assume that the big data table is loaded. From this
diff --git a/tex/context/base/char-utf.lua b/tex/context/base/char-utf.lua
index 79f5718fd..2d11a1794 100644
--- a/tex/context/base/char-utf.lua
+++ b/tex/context/base/char-utf.lua
@@ -62,7 +62,9 @@ end
 
 function characters.filters.utf.collapse(str)
     if characters.filters.utf.collapsing and str and #str > 1 then
-        characters.filters.utf.initialize()
+        if not characters.filters.utf.initialized then -- saves a call
+            characters.filters.utf.initialize()
+        end
         local tokens, first, done = { }, false, false
         local cg = characters.graphemes
         for second in string.utfcharacters(str) do
@@ -152,7 +154,9 @@ do
     function characters.filters.utf.collapse(str)
         if characters.filters.utf.collapsing and str then
             if #str > 1 then
-                characters.filters.utf.initialize()
+                if not characters.filters.utf.initialized then -- saves a call
+                    characters.filters.utf.initialize()
+                end
                 local tokens, first, done = { }, false, false
                 for second in string.utfcharacters(str) do
                     if cr[second] then
@@ -257,7 +261,7 @@ end
 
 function characters.filters.process(str)
     if characters.filters.activated then
-        for _,v in pairs(characters.filters.sequences) do
+        for _,v in ipairs(characters.filters.sequences) do
             str = v(str)
         end
         return str
diff --git a/tex/context/base/colo-hex.tex b/tex/context/base/colo-hex.tex
index b493b8c6b..e60f2a0ae 100644
--- a/tex/context/base/colo-hex.tex
+++ b/tex/context/base/colo-hex.tex
@@ -11,6 +11,10 @@
 %C therefore copyrighted by \PRAGMA. See mreadme.pdf for
 %C details.
 
+\beginLUATEX
+    \endinput
+\endLUATEX
+
 \ifx\dodododefinecolor\undefined
   \beginTEX \endinput \endTEX
 \else
diff --git a/tex/context/base/colo-ini.tex b/tex/context/base/colo-ini.tex
index 687d41da3..4e747707f 100644
--- a/tex/context/base/colo-ini.tex
+++ b/tex/context/base/colo-ini.tex
@@ -13,9 +13,6 @@
 
 \writestatus{loading}{Context Color Macros / initialization}
 
-%D To do: stroke versus fill color
-%D 1000 100 10 -> constants
-
 %D Possible optimization: store level in mark instead of name
 
 \unprotect
@@ -367,7 +364,6 @@
    \fi}
 
 \def\paletcolorspec#1%
-% {\executeifdefined{\??cr\currentpalet#1}{\executeifdefined{\??cr#1}\empty}}
   {\csname\??cr\currentpalet#1\endcsname}
 
 %D Hex color support is not enabled by default. You need to say \type
@@ -661,6 +657,12 @@
 
 \newif\ifpermitcolormode \permitcolormodetrue
 
+\def\startregistercolor[#1]%
+  {\permitcolormodefalse\startcolor[#1]\permitcolormodetrue}
+
+\def\stopregistercolor
+  {\permitcolormodefalse\stopcolor\permitcolormodetrue}
+
 \def\dowithcolor#1#2% #1=\action #2=color
   {\ifincolor\ifpermitcolormode
      \ifcsname\??cr\currentpalet#2\endcsname
@@ -1572,7 +1574,7 @@
                g=>\chardef\currentcolorchannel6,%
                b=>\chardef\currentcolorchannel7,%
                s=>\chardef\currentcolorchannel8,%
-          \v!no=>,%      \currentcolorchannel0,% all colors
+           \v!no=>,%      \currentcolorchannel0,% all colors
       \s!default=>,%      \currentcolorchannel0,% all colors
       \s!unknown=>\filterspotcolortrue
                   \edef\currentspotcolor{\commalistelement}]%
@@ -1660,6 +1662,13 @@
      \expandafter\secondoftwoarguments
    \fi}
 
+\def\doifcolor#1%
+  {\ifcsname\??cr\ifcsname\??cr\currentpalet#1\endcsname\currentpalet\fi#1\endcsname
+     \expandafter\firstofoneargument
+   \else
+     \expandafter\gobbleoneargument
+   \fi}
+
 %D \macros
 %D   {localstartcolor,localstopcolor}
 %D
@@ -2191,17 +2200,45 @@
 %D don't cross page boundaries in the way color does. Therefore
 %D we don't need stacks and marks. Just to be compatible with
 %D color support we offer both 'global' and 'local' commands.
+%D
+%D \starttyping
+%D \def\localstartraster[#1]%
+%D   {\doifelsenothing{#1}
+%D      {\dostartgraymode\@@rsscreen}
+%D      {\dostartgraymode{#1}}}
+%D
+%D \def\localstopraster
+%D   {\dostopgraymode}
+%D
+%D \let\startraster\localstartraster
+%D \let\stopraster \localstopraster
+%D \stoptyping
+%D
+%D The next alternative is slower, since it works on top of the
+%D color (stack) mechanism, but it does provide nesting.
+
+\def\dosetrastercolor#1%
+  {\edef\@@cl@@s{#1}%
+   \ifx\@@cl@@s\empty
+     \let\@@cl@@s\@@rsscreen
+   \fi
+   \let\@@cl@@t\@@cl@@z % else we get rogue
+   \let\@@cl@@a\@@cl@@z % transpancies
+   \setevalue{\??cr\??rs}{\colorSpattern}}
+
+% beware, don't add extra grouping, else color in tables
+% fails
 
 \def\localstartraster[#1]%
-  {\doifelsenothing{#1}
-     {\dostartgraymode\@@rsscreen}
-     {\dostartgraymode{#1}}}
+  {\ifincolor\dosetrastercolor{#1}\localstartcolor[\??rs]\fi}
+
+\def\startraster[#1]%
+  {\ifincolor\dosetrastercolor{#1}\startcolor[\??rs]\fi}
 
-\def\localstopraster
-  {\dostopgraymode}
+\def\localstopraster{\ifincolor\localstopcolor\fi}
+\def\stopraster     {\ifincolor\stopcolor\fi}
 
-\let\startraster\localstartraster
-\let\stopraster \localstopraster
+\def\raster[#1]{\groupedcommand{\startraster[#1]}{\stopraster}}
 
 %D In this documentation we will not go into too much details
 %D on palets. Curious users can find more information on this
@@ -2280,22 +2317,23 @@
         {\doifvaluesomething{\??pa#1}
            {\setevalue{\??pa#1}{\csname\??pa#1\endcsname,}}%
          \setevalue{\??pa#1}{\csname\??pa#1\endcsname##1}%
-         \doifassignmentelse{##2}
-           {% == \definepalet[test][xx={y=.4}]
-            \definecolor[\??pa#1:##1][##2]%
-            \iffreezecolors\@EA\setevalue\else\@EA\setvalue\fi
-              {\??cr#1:##1}{\csname\??cr\??pa#1:##1\endcsname}}
-           {% == \definepalet[test][xx=green]
-            \doifdefinedelse{\??cr##2}
-              {\iffreezecolors\@EA\setevalue\else\@EA\setvalue\fi
-                 {\??cr#1:##1}{\csname\??cr##2\endcsname}}
-              {\letvalue{\??cr#1:##1}\colorXpattern}}}%
+         \dodefinepaletcolor{#1}{##1}{##2}}%
       \def\dododefinepalet##1%
         {\dodododefinepalet[##1]}%
       \processcommalist[#2]\dododefinepalet}
      {\doifdefined{\??pa#2}
         {\expanded{\dodefinepalet[#1][\csname\??pa\??pa#2\endcsname]}}}}
 
+\def\dodefinepaletcolor#1#2#3%
+  {\doifassignmentelse{#3}
+     {% == \definepalet[test][xx={y=.4}]
+      \definecolor[\??pa#1:#2][#3]%
+       \iffreezecolors\@EA\setevalue\else\@EA\setvalue\fi{\??cr#1:#2}{\csname\??cr\??pa#1:#2\endcsname}}
+     {% == \definepalet[test][xx=green]
+      \doifdefinedelse{\??cr#3}
+        {\iffreezecolors\@EA\setevalue\else\@EA\setvalue\fi{\??cr#1:#2}{\csname\??cr#3\endcsname}}
+        {\letvalue{\??cr#1:#2}\colorXpattern}}}
+
 \let\paletsize\!!zerocount
 
 \def\getpaletsize[#1]%
@@ -2386,28 +2424,51 @@
 \def\definecolorgroup
   {\dotripleempty\dodefinecolorgroup}
 
-\def\dodefinecolorgroup[#1][#2][#3]%
+% \def\dodefinecolorgroup[#1][#2][#3]%
+%   {\ifthirdargument
+%      \processaction
+%        [#2]
+%        [    \v!cmyk=>\edef\currentcolorspace{C},
+%              \v!rgb=>\edef\currentcolorspace{R},
+%             \v!gray=>\edef\currentcolorspace{S},
+%             \v!spot=>\edef\currentcolorspace{P},
+%                \v!s=>\edef\currentcolorspace{S},
+%          \s!unknown=>\edef\currentcolorspace{R}]%
+%      \colorcount\zerocount
+%      \def\dododefinecolorgroup##1%
+%        {\advance\colorcount \plusone
+%         \setevalue{\??cr#1:\the\colorcount}{\currentcolorspace:##1:0:0}}%
+%      \processcommalist[#3]\dododefinecolorgroup
+%    \else
+%      \doifinstringelse{:}{#2}
+%        {\definecolorgroup[#1][\v!rgb][#2]}
+%        {\doloop
+%           {\doifdefinedelse{\??cr#2:\recurselevel}
+%              {\setevalue{\??cr#1:\recurselevel}%
+%                 {\csname\??cr#2:\recurselevel\endcsname}}
+%              {\exitloop}}}%
+%    \fi}
+
+\def\dododefinecolorgroupgray         [#1][#2:#3]{\definecolor   [#1:\the\colorcount][s=#2]}
+\def\dododefinecolorgrouprgb    [#1][#2:#3:#4:#5]{\definecolor   [#1:\the\colorcount][r=#2,g=#3,b=#4]}
+\def\dododefinecolorgroupcmyk[#1][#2:#3:#4:#5:#6]{\definecolor   [#1:\the\colorcount][c=#2,m=#3=,y=#4,k=#5]}
+\def\dododefinecolorgroupspot      [#1][#2:#3:#4]{\definespotolor[#1:\the\colorcount][#2][p=#3]}
+
+\def\dododefinecolorgroup#1#2%
+  {\advance\colorcount\plusone
+   \getvalue{dododefinecolorgroup\currentcolorspace}[#1][#2:0:0:0:0]}
+
+\def\dodefinecolorgroup[#1][#2][#3]% obsolete, just use palets
   {\ifthirdargument
-     \processaction
-       [#2]
-       [    \v!cmyk=>\edef\currentcolorspace{C},
-             \v!rgb=>\edef\currentcolorspace{R},
-            \v!gray=>\edef\currentcolorspace{S},
-            \v!spot=>\edef\currentcolorspace{P},
-               \v!s=>\edef\currentcolorspace{S},
-         \s!unknown=>\edef\currentcolorspace{R}]%
+     \doifelsenothing{#2}{\let\currentcolorspace\v!rgb}{\def\currentcolorspace{#2}}%
      \colorcount\zerocount
-     \def\dododefinecolorgroup##1%
-       {\advance\colorcount \plusone
-        \setevalue{\??cr#1:\the\colorcount}{\currentcolorspace:##1:0:0}}%
-     \processcommalist[#3]\dododefinecolorgroup
+     \processcommalist[#3]{\dododefinecolorgroup{#1}}%
    \else
      \doifinstringelse{:}{#2}
        {\definecolorgroup[#1][\v!rgb][#2]}
        {\doloop
           {\doifdefinedelse{\??cr#2:\recurselevel}
-             {\setevalue{\??cr#1:\recurselevel}%
-                {\csname\??cr#2:\recurselevel\endcsname}}
+             {\setevalue{\??cr#1:\recurselevel}{\csname\??cr#2:\recurselevel\endcsname}}
              {\exitloop}}}%
    \fi}
 
@@ -2443,11 +2504,11 @@
 %D \hbox to \hsize
 %D   {\hss
 %D    \showcolorgroup [red]    [vertical,name,number]\hss
-%D    \showcolorgroup [green]   [vertical,name]\hss
+%D    \showcolorgroup [green]  [vertical,name]\hss
 %D    \showcolorgroup [blue]   [vertical,name]\hss
 %D    \showcolorgroup [cyan]   [vertical,name]\hss
-%D    \showcolorgroup [magenta] [vertical,name]\hss
-%D    \showcolorgroup [yellow]    [vertical,name]\hss}
+%D    \showcolorgroup [magenta][vertical,name]\hss
+%D    \showcolorgroup [yellow] [vertical,name]\hss}
 %D \stoplinecorrection
 %D
 %D These groups are used to define palets {\em alfa} upto {\em
@@ -2584,9 +2645,6 @@
 \def\doMPcolor#1:% #1 can be \relax ! ! ! i.e. an empty color
   {\csname MPc\@EA\ifx\csname MPc\string#1\endcsname\relax B\else#1\fi\endcsname}
 
-% \def\doMPcolor#1:% #1 can be \relax ! ! ! i.e. an empty color
-%   {\csname MPc\ifcsname MPc#1\endcsname#1\else B\fi\endcsname}
-
 \def\MPcR{\doMPrgb}
 \def\MPcC{\ifMPcmykcolors\@EA\doMPcmykY\else\@EA\doMPcmykN\fi}
 \def\MPcS{\doMPgray}
@@ -2600,8 +2658,6 @@
 \def\grayMP        {scaledgray}
 \def\spotMP        {spotcolor}
 
-\let\processMP\spotMP % for some time, will become obsolete
-
 \def\doMPtransparent#1#2:#3:#4\end
   {\ifcase#2\space(#1)\else\transparentMP(#2,#3,(#1))\fi}
 
@@ -2617,12 +2673,6 @@
 \def\doMPcmykN#1:#2:#3:#4:#5\end#6\end
   {\doMPtransparent{\cmykASrgbMP(#1,#2,#3,#4,#6)}#5\end}
 
-% \def\doMPspotY#1:#2:#3\end#4\end
-%   {\doMPtransparent{\spotMP("#1",#2)}#3\end}
-%
-% \def\doMPspotN#1:#2:#3\end#4\end
-%   {\scaledMPcolor{#2}{#1}}
-
 \def\doMPspotY#1:#2:#3:#4:#5\end#6\end % best make #3 same as #1 when empty
   {\doMPtransparent{multitonecolor("#1",#2,"#3","#4")}#5\end}
 
@@ -2635,6 +2685,8 @@
 \def\unknownMPcolor
   {(0,0,0)}
 
+\let\processMP\spotMP % for some time, will become obsolete, brrr
+
 %D \macros
 %D   {PDFcolor,FDFcolor}
 %D
@@ -2644,30 +2696,6 @@
 \def\PDFcolorvalue#1{\handlecolorwith\doPDFcolorvalue\csname\??cr#1\endcsname:::::::\end}
 \def\FDFcolor     #1{\handlecolorwith\doFDFcolor     \csname\??cr#1\endcsname:::::::\end}
 
-% \def\doPDFcolor#1:#2:#3:#4:#5:#6:#7:#8\end
-%   {\if     #1R#2 #3 #4 rg%
-%    \else\if#1C#2 #3 #4 #5 k%
-%    \else\if#1S#2 g%
-%    \else\if#1P#3 g% todo
-%    \else       0 g%
-%    \fi\fi\fi\fi}
-%
-% \def\doPDFcolorvalue#1:#2:#3:#4:#5:#6:#7:#8\end
-%   {\if     #1R#2 #3 #4%
-%    \else\if#1C#2 #3 #4 #5%
-%    \else\if#1S#2%
-%    \else\if#1P#3%
-%    \else       0%
-%    \fi\fi\fi\fi}
-%
-% \def\doFDFcolor#1:#2:#3:#4:#5:#6:#7:#8\end
-%   {[\if     #1R#2 #3 #4%
-%     \else\if#1C#2 #3 #4 #5%
-%     \else\if#1S#2%
-%     \else\if#1P#3% todo
-%     \else       0%
-%     \fi\fi\fi\fi]}
-
 \def\doPDFcolor#1:#2:#3:#4:#5:#6:#7:#8\end
   {\if     #1R#2 #3 #4 rg%
    \else\if#1C#2 #3 #4 #5 k%
@@ -2795,8 +2823,6 @@
 
 \let\negatedcolorcomponent\firstofoneargument
 
-\beginETEX
-
 \def\negatedcolorcomponent#1%
   {\ifdim\dimexpr\onepoint-#1\onepoint\relax<\zeropoint
      \!!zerocount
@@ -2807,6 +2833,4 @@
 \def\negatecolorcomponent#1% #1 = \macro
   {\edef#1{\negatedcolorcomponent{#1}}}
 
-\endETEX
-
 \protect \endinput
diff --git a/tex/context/base/colo-run.tex b/tex/context/base/colo-run.tex
index 0b44f2ee5..9cb797c4d 100644
--- a/tex/context/base/colo-run.tex
+++ b/tex/context/base/colo-run.tex
@@ -98,7 +98,7 @@
   {\dodoubleargument\doshowcolorgroup}
 
 \gdef\doshowcolorgroup[#1][#2]%
-  {\doifdefined{\??cr#1:1}
+  {\doifcolor{#1:1}
      {\doifinsetelse\v!vertical{#2}
         {\showverticalcolorgroup[#1][#2]}
         {\showhorizontalcolorgroup[#1][#2]}}}
@@ -119,8 +119,8 @@
             \graycolor[#1:##1]{\vrule\!!width4em\!!height\zeropoint\!!depth\strutdp}\cr
             \doifinset\v!value{#2}{\colorvalue{#1:##1}\strut}\crcr}}
       \def\doshowgroup##1%
-        {\doifdefined{\??cr#1:##1}
-           {\vbox{\dodoshowgroup{##1}}}}
+        {\doifcolor{#1:##1}
+           {\vbox{\dodoshowgroup{##1}}}}%
       \hbox
         {\doifinset\v!name{#2}
            {\strut
@@ -137,12 +137,12 @@
       \setuppalet
       \tabskip\zeropoint
       \def\rule
-        {\vrule\!!width2.5em\!!height\strutht\!!depth\strutdp}
+        {\vrule\!!width2.5em\!!height\strutht\!!depth\strutdp}%
       \def\doshowgroup##1%
-        {\doifdefined{\??cr#1:##1}
+        {\doifcolor{#1:##1}
            {\doifinset\v!number{#2}{##1\hskip.5em}&
             \color[#1:##1]{\rule}\graycolor[#1:##1]{\rule}&
-            \doifinset\v!value{#2}{\hskip.5em\colorvalue{#1:##1}}\crcr}}
+            \doifinset\v!value{#2}{\hskip.5em\colorvalue{#1:##1}}\crcr}}%
       \halign
         {\hss##&\hss##\hss#\hss\cr
          &\doifinset\v!name{#2}{\strut#1}&\crcr
@@ -210,7 +210,7 @@
   {\dosingleargument\docomparecolorgroup}
 
 \gdef\docomparecolorgroup[#1]%
-  {\doifdefined{\??cr#1:1}
+  {\doifcolor{#1:1}
      {\hbox
         {\dodocomparecolorgroup\color[#1]%
          \quad
@@ -220,7 +220,7 @@
   {\localvbox
      {\!!counta\zerocount
       \dorecurse{15}
-        {\doifdefined{\??cr#2:\recurselevel}{\advance\!!counta\plusone}}
+        {\doifcolor{#2:\recurselevel}{\advance\!!counta\plusone}}
       \!!widtha2em\relax
       \hsize\!!counta\!!widtha
       \def\rule
@@ -236,36 +236,37 @@
          \endgraf}
       \dorecurse\!!counta{\dododocomparecolorgroup\recurselevel}}}
 
-% \def\execcolorRCSP#1:{\csname execcolor#1\endcsname} -> \execcolorR
-
 \gdef\dogetcolorcomponents#1%
   {\doifelsenothing{#1}
-     {\global\globalscratchtoks{\TB}}
-     {\startnointerference
-      \localcolortrue
-      \def\doexeccolorR    ##1:##2:##3:##4:##5\od{\global\globalscratchtoks{\NC\Od#1 \NC\Nm #1 \NC a=\Do##4 \enspace t=\Do##5 \NC r=\Do##1 \enspace g=\Do##2 \enspace b=\Do##3 \NC\NR}}%
-      \def\doexeccolorC##1:##2:##3:##4:##5:##6\od{\global\globalscratchtoks{\NC\Od#1 \NC\Nm #1 \NC a=\Do##5 \enspace t=\Do##6 \NC c=\Do##1 \enspace m=\Do##2 \enspace y=\Do##3 \enspace k=\Do##4 \NC\NR}}%
-      \def\doexeccolorS            ##1:##2:##3\od{\global\globalscratchtoks{\NC\Od#1 \NC\Nm #1 \NC a=\Do##2 \enspace t=\Do##3 \NC s=\Do##1 \NC\NR}}%
-     %\def\doexeccolorP##1:##2:##3:##4:##5:##6\od{\global\globalscratchtoks{\NC\Od#1 \NC\Nm #1 \NC a=\Do##5 \enspace t=\Do##6 \NC p=\Do##4 \enspace f=\Do##2 \enspace d=\Do##3 \enspace n=##1 \NC\NR}}%
-      \def\doexeccolorP##1:##2:##3:##4:##5:##6\od{\global\globalscratchtoks{\NC\Od#1 \NC\Nm #1 \NC a=\Do##5 \enspace t=\Do##6 \NC p=\Do##4 \enspace n=##1 \NC\NR}}%
-      \let\doexeccolorPindex\doexeccolorP
-      \backgroundline[#1]{}%
-      \stopnointerference}%
-   \appendetoks\the\globalscratchtoks\to\scratchtoks}
+     {\appendtoks
+        \TB
+      \to \scratchtoks}
+     {\appendtoks
+        \NC\showcolorbar[#1]\NC#1\NC\transparencycomponents{#1}\NC\colorcomponents{#1}\NC \NR
+      \to \scratchtoks}}
+
+% \gdef\showcolorcomponents[#1]%
+%   {\bgroup
+%    \def\bars##1{\backgroundline[##1]{\strut\enspace\color[white]{white}\enspace\color[black]{black}\enspace}}%
+%    \scratchtoks\emptytoks
+%    \appendtoks\starttabulate[|l|l|l|l|]\to\scratchtoks
+%    \appendtoks\NC color \NC name \NC transparency \NC specification \NC\NR\TB\to\scratchtoks
+%    \processcommacommand[#1]\dogetcolorcomponents
+%    \appendtoks\stoptabulate\to\scratchtoks
+%    \tt\the\scratchtoks
+%    \egroup}
 
-\newdimen\colorcomponentwidth % for my eyes only
+\gdef\showcolorbar[#1]%
+  {\backgroundline[#1]{\strut\enspace\color[white]{white}\enspace\color[black]{black}\enspace}}
 
 \gdef\showcolorcomponents[#1]%
-  {\bgroup
-   \def\Od##1 {\backgroundline[##1]{\strut\enspace\color[white]{white}\enspace\color[black]{black}\enspace}}%
-   \def\Do##1 {\twodigitrounding{##1}}%
-   \def\Nm##1 {\ifdim\colorcomponentwidth>\zeropoint\hbox to \colorcomponentwidth\fi{##1}}%
-   \scratchtoks\emptytoks
-   \appendtoks\starttabulate[|l|l|l|p|]\to\scratchtoks
-   \appendtoks\NC color \NC name \NC transparency \NC specification \NC\NR\TB\to\scratchtoks
+  {\begingroup
+   \scratchtoks{\TB}%
    \processcommacommand[#1]\dogetcolorcomponents
-   \appendtoks\stoptabulate\to\scratchtoks
-   \tt\the\scratchtoks
-   \egroup}
+   \starttabulate[|lT|lT|lT|lT|]
+   \NC color \NC name \NC transparency \NC specification \NC\NR
+   \the\scratchtoks
+   \stoptabulate
+   \endgroup}
 
 \protect \endinput
diff --git a/tex/context/base/cont-new.mkiv b/tex/context/base/cont-new.mkiv
index 0a4803c4a..a3296003a 100644
--- a/tex/context/base/cont-new.mkiv
+++ b/tex/context/base/cont-new.mkiv
@@ -15,7 +15,7 @@
     \writestatus\m!lua{tfm over afm, wide fonts not yet supported}
 \to \everystoptext
 
-\ctxlua { fonts.define.method = 2 }  % normally 3
+% \ctxlua { fonts.define.method = 2 }  % normally 3
 
 \unprotect
 
@@ -26,21 +26,30 @@
 % texmf.instance will become just texmf
 
 \appendtoks
-    \writestatus\m!lua{input load time         - \ctxlua{input.loadtime(texmf.instance)} seconds}%
-    \writestatus\m!lua{fonts load time         - \ctxlua{input.loadtime(fonts)} seconds}%
-    \writestatus\m!lua{mps conversion time     - \ctxlua{input.loadtime(mptopdf)} seconds}%
-    \writestatus\m!lua{node processing time    - \ctxlua{input.loadtime(nodes)} second}%
-    \writestatus\m!lua{used config path        - \ctxlua{tex.print(cache.configpath(texmf.instance))}}%
-    \writestatus\m!lua{used cache path         - \ctxlua{tex.print(cache.path)}}%
-    \writestatus\m!lua{modules/dumps/instances - \ctxlua{tex.print((status.luabytecodes-500).."/"..input.storage.done.."/"..status.luastates)}}%
-    \writestatus\m!lua{current memory usage    - \ctxlua{tex.print(status.luastate_bytes)} bytes}%
-    \writestatus\m!lua{loaded fonts            - \ctxlua{tex.print(fonts.logger.report())}}%
+    \writestatus\m!lua{input load time           - \ctxlua{input.loadtime(texmf.instance)} seconds}%
+    \writestatus\m!lua{fonts load time           - \ctxlua{input.loadtime(fonts)} seconds}%
+    \writestatus\m!lua{mps conversion time       - \ctxlua{input.loadtime(mptopdf)} seconds}%
+    \writestatus\m!lua{node processing time      - \ctxlua{input.loadtime(nodes)} seconds}%
+    \writestatus\m!lua{attribute processing time - \ctxlua{input.loadtime(attributes)} seconds}%
+    \writestatus\m!lua{used config path          - \ctxlua{tex.print(caches.configpath(texmf.instance))}}%
+    \writestatus\m!lua{used cache path           - \ctxlua{tex.print(caches.path)}}%
+    \writestatus\m!lua{modules/dumps/instances   - \ctxlua{tex.print((status.luabytecodes-500).."/"..input.storage.done.."/"..status.luastates)}}%
+    \writestatus\m!lua{current memory usage      - \ctxlua{tex.print(status.luastate_bytes)} bytes}%
+    \writestatus\m!lua{loaded fonts              - \ctxlua{tex.print(fonts.logger.report())}}%
 \to \everystoptext
 
 \def\resettimer    {\ctxlua{environment.starttime = os.clock()}}
 \def\elapsedtime   {\ctxlua{tex.sprint(os.clock()-environment.starttime)}}
 \let\elapsedseconds \elapsedtime
 
+%D For me.
+
+\def\traceluausage
+  {\ctxlua{debugger.enable()}%
+   \appendtoks
+     \ctxlua{debugger.disable() debugger.showstats(texio.write,5000)}%
+    \to \everybye}
+
 %D Fonts (experimental AFM loading}
 
 % \ctxlua {
@@ -56,10 +65,7 @@
 \appendtoksonce \loadallXfontmapfiles \to \everystarttext
 \appendtoksonce \loadallXfontmapfiles \to \everybeforepagebody
 
-\def\loadallXfontmapfiles{\ctxlua {
-    local s = fonts.map.flushlines("pdftex","")
-    tex.sprint(tex.ctxcatcodes,s)
-}}
+\def\loadallXfontmapfiles{\ctxlua{fonts.map.flush("pdftex")}}
 
 % \ctxlua{
 %     do
@@ -95,3 +101,6 @@
 % }
 
 \protect \endinput
+
+% \expanded{\defineactivecharacter \number"2000E} {\textdir TRT\relax}
+% \expanded{\defineactivecharacter \number"2000F} {\textdir TLT\relax}
diff --git a/tex/context/base/cont-new.tex b/tex/context/base/cont-new.tex
index 0ee61b10a..906eabec9 100644
--- a/tex/context/base/cont-new.tex
+++ b/tex/context/base/cont-new.tex
@@ -11,7 +11,7 @@
 %C therefore copyrighted by \PRAGMA. See mreadme.pdf for
 %C details.
 
-\newcontextversion{2007.08.09 13:04}
+\newcontextversion{2007.08.20 10:21}
 
 %D This file is loaded at runtime, thereby providing an
 %D excellent place for hacks, patches, extensions and new
@@ -31,6 +31,31 @@
 
 \let\then\relax % \ifnum1>2\then -)
 
+% \setupcaption [figure]   [align=flushleft]
+% \setupcaption [figure-1] [align=flushleft,leftmargin=10mm]
+% \setupcaption [figure-2] [align=flushleft,leftmargin=10mm,rightmargin=-10mm,width=\textwidth]
+%
+% \startsetups somefigure
+%     \ifdim\floatsetupwidth>\textwidth
+%         \placesetupfloat[figure-2]
+%     \else
+%         \placesetupfloat[figure-1]
+%     \fi
+% \stopsetups
+%
+% \placefloatwithsetups[somefigure]{}{\externalfigure[dummy][width=5cm,height=2cm]}
+
+\def\placefloatwithsetups
+  {\dotripleempty\doplacefloatwithsetups}
+
+\def\doplacefloatwithsetups[#1][#2][#3]#4%
+  {\def\floatsetupcaption   {#4}%
+   \def\floatsetupcontent   {\copy\nextbox}%
+   \def\floatsetupwidth     {\wd\nextbox}%
+   \def\floatsetupheight    {\ht\nextbox}%
+   \def\placesetupfloat[##1]{\placefloat[##1][#2][#3]{\floatsetupcaption}{\floatsetupcontent}}%
+   \dowithnextbox{\setups[#1]}\vbox}
+
 \chardef\baselinegridmode=0 % option in layout / 1=permit_half_lines
 
 \def\dodosetupwhitespace
@@ -1763,32 +1788,6 @@
   \setinnerparpositions % see "techniek" for application
 \to \everytabulate
 
-%D This alternative is slower, since it works on top of the
-%D color (stack) mechanism, but it does provide nesting.
-
-\def\dosetrastercolor#1%
-  {\edef\@@cl@@s{#1}%
-   \ifx\@@cl@@s\empty
-     \let\@@cl@@s\@@rsscreen
-   \fi
-   \let\@@cl@@t\@@cl@@z % else we get rogue
-   \let\@@cl@@a\@@cl@@z % transpancies
-   \setevalue{\??cr\??rs}{\colorSpattern}}
-
-% beware, don't add extra grouping, else color in tables
-% fails
-
-\def\localstartraster[#1]%
-  {\ifincolor\dosetrastercolor{#1}\localstartcolor[\??rs]\fi}
-
-\def\startraster[#1]%
-  {\ifincolor\dosetrastercolor{#1}\startcolor[\??rs]\fi}
-
-\def\localstopraster{\ifincolor\localstopcolor\fi}
-\def\stopraster     {\ifincolor\stopcolor\fi}
-
-\def\raster[#1]{\groupedcommand{\startraster[#1]}{\stopraster}}
-
 \def\fontclassname#1#2%
   {\ifcsname\??ff#1#2\endcsname
      \fontclassname{#1}{\csname\??ff#1#2\endcsname}%
diff --git a/tex/context/base/context.tex b/tex/context/base/context.tex
index 541ae1ead..024af80f5 100644
--- a/tex/context/base/context.tex
+++ b/tex/context/base/context.tex
@@ -42,7 +42,7 @@
 %D your styles an modules.
 
 \edef\contextformat {\jobname}
-\edef\contextversion{2007.08.09 13:04}
+\edef\contextversion{2007.08.20 10:21}
 
 %D For those who want to use this:
 
@@ -208,7 +208,6 @@
 \loadmkivfile{attr-ini.tex}
 \loadmkivfile{node-ini.tex}
 
-
 %D We also use some third party macros. These are loaded by
 %D saying:
 
@@ -300,7 +299,7 @@
 \loadcorefile{spec-def.tex}
 \loadcorefile{spec-var.tex}
 
-\loadcorefile{colo-ini.tex}
+\doiffileelse{colo-new.tex}{\loadcorefile{colo-new.tex}}{\loadcorefile{colo-ini.tex}}
 \loadcorefile{colo-ext.tex}
 
 %D For the moment we load a lot of languages. In the future
@@ -481,6 +480,8 @@
 %D These macros are loaded last since they overload and|/|or
 %D extend previously defined ones.
 
+\loadmkivfile{lxml-ini.tex}
+
 \loadcorefile{xtag-ini.tex}
 \loadcorefile{xtag-ext.tex}
 \loadcorefile{xtag-prs.tex}
@@ -625,8 +626,8 @@
 \unprotect
 \beginLUATEX
     \appendtoks
-        \writestatus\m!lua{used config path - \ctxlua{tex.print(cache.configpath(texmf.instance))}}%
-        \writestatus\m!lua{used cache path  - \ctxlua{tex.print(cache.path)}}%
+        \writestatus\m!lua{used config path - \ctxlua{tex.print(caches.configpath(texmf.instance))}}%
+        \writestatus\m!lua{used cache path  - \ctxlua{tex.print(caches.path)}}%
     \to \everydump
 \endLUATEX
 \protect
diff --git a/tex/context/base/core-con.lua b/tex/context/base/core-con.lua
index 349d33688..b6af6be9a 100644
--- a/tex/context/base/core-con.lua
+++ b/tex/context/base/core-con.lua
@@ -14,10 +14,10 @@ slower but look nicer this way.
 Some code may move to a module in the language namespace.
 --ldx]]--
 
-convert  = convert  or { }
-language = language or { }
+converters = converters or { }
+languages  = languages  or { }
 
-language.counters = {
+languages.counters = {
     ['**'] = {
         0x0061, 0x0062, 0x0063, 0x0064, 0x0065,
         0x0066, 0x0067, 0x0068, 0x0069, 0x006A,
@@ -70,95 +70,99 @@ language.counters = {
     }
 }
 
-function convert.chr(n, m)
+function converters.chr(n, m)
     if n > 0 and n < 27 then
         tex.sprint(string.char(n+m))
     end
 end
 
-function convert.maxchrs(n,m,cmd)
+function converters.maxchrs(n,m,cmd)
     if n <= m then
         tex.sprint(tex.texcatcodes, cmd .. "{" .. n .. "}")
     else
-        convert.maxchrs(math.floor((n-1)/m),m,cmd)
+        converters.maxchrs(math.floor((n-1)/m),m,cmd)
         tex.sprint(tex.texcatcodes, cmd .. "{" .. ((n-1)%m + 1) .. "}")
     end
 end
-function convert.chrs(n,m)
+function converters.chrs(n,m)
     if n <= 26 then
         tex.sprint(string.char(n+m))
     else
-        convert.chrs(math.floor((n-1)/26),m)
+        converters.chrs(math.floor((n-1)/26),m)
         tex.sprint(string.char(((n-1)%26 + 1)+m))
     end
 end
 
-function convert.alphabetic(n,code)
-    local code = language.counters[code] or language.counters['**']
-    convert.do_alphabetic(n,#code,function(n) return code[n] end)
-end
+do
 
-function convert.Alphabetic(n,code)
-    local code = language.counters[code] or language.counters['**']
-    convert.do_alphabetic(n,#code,function(n) return characters.uccode(code[n]) end)
-end
+    local function do_alphabetic(n,max,chr)
+        if n <= max then
+            characters.flush(chr(n))
+        else
+            do_alphabetic(math.floor((n-1)/max),max,chr)
+            characters.flush(chr((n-1)%max+1))
+        end
+    end
 
-function convert.do_alphabetic(n,max,chr)
-    if n <= max then
-        characters.flush(chr(n))
-    else
-        convert.do_alphabetic(math.floor((n-1)/max),max,chr)
-        characters.flush(chr((n-1)%max+1))
+    function converters.alphabetic(n,code)
+        local code = languages.counters[code] or languages.counters['**']
+        do_alphabetic(n,#code,function(n) return code[n] end)
     end
+
+    function converters.Alphabetic(n,code)
+        local code = languages.counters[code] or languages.counters['**']
+        do_alphabetic(n,#code,function(n) return characters.uccode(code[n]) end)
+    end
+
 end
 
-function convert.character(n)  convert.chr (n,96) end
-function convert.Character(n)  convert.chr (n,64) end
-function convert.characters(n) convert.chrs(n,96) end
-function convert.Characters(n) convert.chrs(n,64) end
+function converters.character(n)  converters.chr (n,96) end
+function converters.Character(n)  converters.chr (n,64) end
+function converters.characters(n) converters.chrs(n,96) end
+function converters.Characters(n) converters.chrs(n,64) end
 
-function convert.weekday(year,month,day)
+function converters.weekday(year,month,day)
     tex.sprint(os.date("%w",os.time{year=year,month=month,day=day})+1)
 end
 
-function convert.lpy(year)
+function converters.lpy(year)
     return (year % 400 == 0) or ((year % 100 ~= 0) and (year % 4 == 0))
 end
 
-function convert.leapyear(year)
-    if convert.lpy(year) then tex.sprint(1) else tex.sprint(0) end
+function converters.leapyear(year)
+    if converters.lpy(year) then tex.sprint(1) else tex.sprint(0) end
 end
 
-convert.mth = {
+converters.mth = {
     [false] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
     [true]  = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
 }
 
-function convert.nofdays(year,month)
-    tex.sprint(convert.mth[convert.lpy(year)][month])
+function converters.nofdays(year,month)
+    tex.sprint(converters.mth[converters.lpy(year)][month])
 end
 
-function convert.year   () tex.sprint(os.date("%Y")) end
-function convert.month  () tex.sprint(os.date("%m")) end
-function convert.hour   () tex.sprint(os.date("%H")) end
-function convert.minute () tex.sprint(os.date("%M")) end
-function convert.second () tex.sprint(os.date("%S")) end
-function convert.textime() tex.sprint(tonumber(os.date("%H"))*60+tonumber(os.date("%M"))) end
+function converters.year   () tex.sprint(os.date("%Y")) end
+function converters.month  () tex.sprint(os.date("%m")) end
+function converters.hour   () tex.sprint(os.date("%H")) end
+function converters.minute () tex.sprint(os.date("%M")) end
+function converters.second () tex.sprint(os.date("%S")) end
+function converters.textime() tex.sprint(tonumber(os.date("%H"))*60+tonumber(os.date("%M"))) end
 
-convert.rom = {
+converters.rom = {
     { [0] = '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX' },
     { [0] = '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC' },
     { [0] = '', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM' },
 }
 
-function convert.toroman(n)
+function converters.toroman(n)
     if n >= 4000 then
-        return convert.toroman(math.floor(n/1000)) .. " " .. convert.toroman(n%1000)
+        return converters.toroman(math.floor(n/1000)) .. " " .. converters.toroman(n%1000)
     else
-        return string.rep("M",math.floor(n/1000)) .. convert.rom[3][math.floor((n%1000)/100)] ..
-            convert.rom[2][math.floor((n%100)/10)] .. convert.rom[1][math.floor((n% 10)/1)]
+        return string.rep("M",math.floor(n/1000)) .. converters.rom[3][math.floor((n%1000)/100)] ..
+            converters.rom[2][math.floor((n%100)/10)] .. converters.rom[1][math.floor((n% 10)/1)]
     end
 end
 
-function convert.romannumerals(n) return tex.sprint(string.tolower(convert.toroman(n))) end
-function convert.Romannumerals(n) return tex.sprint(               convert.toroman(n) ) end
+function converters.romannumerals(n) return tex.sprint(string.lower(converters.toroman(n))) end
+function converters.Romannumerals(n) return tex.sprint(             converters.toroman(n) ) end
diff --git a/tex/context/base/core-con.mkiv b/tex/context/base/core-con.mkiv
index afed14f1d..390f9c595 100644
--- a/tex/context/base/core-con.mkiv
+++ b/tex/context/base/core-con.mkiv
@@ -15,47 +15,47 @@
 
 \registerctxluafile{core-con}{1.001}
 
-\def\romannumerals#1{\ctxlua{convert.romannumerals(\number#1)}}
-\def\Romannumerals#1{\ctxlua{convert.Romannumerals(\number#1)}}
+\def\romannumerals#1{\ctxlua{converters.romannumerals(\number#1)}}
+\def\Romannumerals#1{\ctxlua{converters.Romannumerals(\number#1)}}
 
-\def\greeknumerals#1{\ctxlua{convert.alphabetic(\number#1,"gr")}}
-\def\Greeknumerals#1{\ctxlua{convert.Alphabetic(\number#1,"gr")}}
+\def\greeknumerals#1{\ctxlua{converters.alphabetic(\number#1,"gr")}}
+\def\Greeknumerals#1{\ctxlua{converters.Alphabetic(\number#1,"gr")}}
 
-\def\character #1{\ctxlua{convert.character (\number#1)}}
-\def\Character #1{\ctxlua{convert.Character (\number#1)}}
-\def\characters#1{\ctxlua{convert.characters(\number#1)}}
-\def\Characters#1{\ctxlua{convert.Characters(\number#1)}}
+\def\character #1{\ctxlua{converters.character (\number#1)}}
+\def\Character #1{\ctxlua{converters.Character (\number#1)}}
+\def\characters#1{\ctxlua{converters.characters(\number#1)}}
+\def\Characters#1{\ctxlua{converters.Characters(\number#1)}}
 
-\def\languagecharacters#1{\ctxlua{convert.alphabetic(\number#1,"\currentlanguage")}} % new
-\def\languageCharacters#1{\ctxlua{convert.Alphabetic(\number#1,"\currentlanguage")}} % new
+\def\languagecharacters#1{\ctxlua{converters.alphabetic(\number#1,"\currentlanguage")}} % new
+\def\languageCharacters#1{\ctxlua{converters.Alphabetic(\number#1,"\currentlanguage")}} % new
 
-\def\getdayoftheweek#1#2#3{\normalweekday\ctxlua{convert.weekday(\number#1,\number#2,\number#3)}}
-\def\dayoftheweek   #1#2#3{\doconvertday{\ctxlua{convert.weekday(\number#1,\number#2,\number#3)}}}
+\def\getdayoftheweek#1#2#3{\normalweekday\ctxlua{converters.weekday(\number#1,\number#2,\number#3)}}
+\def\dayoftheweek   #1#2#3{\doconvertday{\ctxlua{converters.weekday(\number#1,\number#2,\number#3)}}}
 
 \def\doifleapyearelse#1%
-  {\ifcase\ctxlua{convert.leapyear(\number#1)}
+  {\ifcase\ctxlua{converters.leapyear(\number#1)}
      \@EA\secondoftwoarguments
    \else
      \@EA\firstoftwoarguments
    \fi}
 
 \def\getdayspermonth#1#2%
-  {\edef\numberofdays{\ctxlua{convert.nofdays(\number#1,\number#2)}}}
+  {\edef\numberofdays{\ctxlua{converters.nofdays(\number#1,\number#2)}}}
 
 \def\dayspermonth#1#2%
-  {\ctxlua{convert.nofdays(\number#1,\number#2)}}
+  {\ctxlua{converters.nofdays(\number#1,\number#2)}}
 
 \def\calculatecurrenttime
-  {\edef\currenthour  {\ctxlua{convert.hour  ()}}%
-   \edef\currentminute{\ctxlua{convert.minute()}}%
-   \edef\currentsecond{\ctxlua{convert.second()}}}
+  {\edef\currenthour  {\ctxlua{converters.hour  ()}}%
+   \edef\currentminute{\ctxlua{converters.minute()}}%
+   \edef\currentsecond{\ctxlua{converters.second()}}}
 
 % problem is that we calculate with those numbers
 %
-% \def\time {\numexpr\ctxlua{convert.textime()}\relax}
-% \def\year {\numexpr\ctxlua{convert.year   ()}\relax}
-% \def\month{\numexpr\ctxlua{convert.month  ()}\relax}
-% \def\day  {\numexpr\ctxlua{convert.day    ()}\relax}
+% \def\time {\numexpr\ctxlua{converters.textime()}\relax}
+% \def\year {\numexpr\ctxlua{converters.year   ()}\relax}
+% \def\month{\numexpr\ctxlua{converters.month  ()}\relax}
+% \def\day  {\numexpr\ctxlua{converters.day    ()}\relax}
 
 % \dayoftheweek{2006}{9}{15}
 % \doifleapyearelse{2000}{OK}{NOT OK}
@@ -67,17 +67,17 @@
 
 % we could use an auxiliary macro to save some bytes in the format
 %
-% \def\dolanguagecharacters#1#2{\ctxlua{convert.alphabetic(\number#2,"#1")}}
+% \def\dolanguagecharacters#1#2{\ctxlua{converters.alphabetic(\number#2,"#1")}}
 
 % this does not belong here, but in a lang-module
 
-\def\arabicnumerals    #1{\ctxlua{convert.alphabetic(\number#1,"arabic")}}
-\def\persiannumerals   #1{\ctxlua{convert.alphabetic(\number#1,"persian")}}
-\def\thainumerals      #1{\ctxlua{convert.alphabetic(\number#1,"thai")}}
-\def\devanagarinumerals#1{\ctxlua{convert.alphabetic(\number#1,"devanagari")}}
-\def\gurmurkhinumerals #1{\ctxlua{convert.alphabetic(\number#1,"gurmurkhi")}}
-\def\gujaratinumerals  #1{\ctxlua{convert.alphabetic(\number#1,"gujarati")}}
-\def\tibetannumerals   #1{\ctxlua{convert.alphabetic(\number#1,"tibetan")}}
+\def\arabicnumerals    #1{\ctxlua{converters.alphabetic(\number#1,"arabic")}}
+\def\persiannumerals   #1{\ctxlua{converters.alphabetic(\number#1,"persian")}}
+\def\thainumerals      #1{\ctxlua{converters.alphabetic(\number#1,"thai")}}
+\def\devanagarinumerals#1{\ctxlua{converters.alphabetic(\number#1,"devanagari")}}
+\def\gurmurkhinumerals #1{\ctxlua{converters.alphabetic(\number#1,"gurmurkhi")}}
+\def\gujaratinumerals  #1{\ctxlua{converters.alphabetic(\number#1,"gujarati")}}
+\def\tibetannumerals   #1{\ctxlua{converters.alphabetic(\number#1,"tibetan")}}
 
 \defineconversion[arabicnumerals]    [\arabicnumerals]
 \defineconversion[persiannumerals]   [\persiannumerals]
diff --git a/tex/context/base/core-mat.tex b/tex/context/base/core-mat.tex
index c73ee1cca..b994580f9 100644
--- a/tex/context/base/core-mat.tex
+++ b/tex/context/base/core-mat.tex
@@ -2308,7 +2308,7 @@
    \tabskip\zeropoint
    \eqaligncolumn\zerocount % could be \scratchcounter
    \processcommacommand[\mathmatrixparameter\c!align]{\advance\eqaligncolumn\plusone\dosetmatrixcolumn}%
-   \scratchcounter=\ifnum\eqaligncolumn>\scratchcounter \eqaligncolumn \else \plusone \fi
+   \scratchcounter=\ifnum\eqaligncolumn>\zerocount \eqaligncolumn \else \plusone \fi
    \global\eqaligncolumn\plusone
    \preparemathmatrix } % uses scratchcounter
 
diff --git a/tex/context/base/core-obj.tex b/tex/context/base/core-obj.tex
index d8de15133..a1b0796d1 100644
--- a/tex/context/base/core-obj.tex
+++ b/tex/context/base/core-obj.tex
@@ -118,6 +118,16 @@
   {\checkobjectreferences
    \letbeundefined{\r!object#1::#2}}
 
+%D \macros
+%D   {finalizeobjectbox}
+%D
+%D This one provides a hook for last minute object box processing
+%D we need this in \MKIV.
+
+\ifx\finalizeobjectbox\undefined
+    \let\finalizeobjectbox\gobbleoneargument
+\fi
+
 %D Somehow there is a rounding error problem in either \PDFTEX\
 %D or in viewers, or maybe it is conforming the specs. The next
 %D variable compensate for it by removing the rather tight
diff --git a/tex/context/base/core-rul.tex b/tex/context/base/core-rul.tex
index 696f3f01c..ea6ff441c 100644
--- a/tex/context/base/core-rul.tex
+++ b/tex/context/base/core-rul.tex
@@ -482,7 +482,34 @@
 %D by \TEX\ itself, the latter one depends on the driver. This
 %D macro also support a negative offset.
 
-\def\dooutlinebox
+% \def\dooutlinebox
+%   {\setbox\framebox\vbox % rules on top of box
+%      {\scratchdimen \framedparameter\c!frameoffset\relax
+%       \frameddimenwd\dimexpr\wd\framebox+2\scratchdimen\relax
+%       \frameddimenht\dimexpr\ht\framebox+ \scratchdimen\relax
+%       \frameddimendp\dimexpr\dp\framebox+ \scratchdimen+\framedparameter\c!framedepth\relax
+%       \ifdim\frameddimendp<\zeropoint
+%         \advance\frameddimenht \frameddimendp
+%         \scratchdimen-\frameddimendp
+%         \frameddimendp\zeropoint
+%       \else
+%         \scratchdimen\zeropoint
+%       \fi
+%       \setbox\extraframebox\hbox
+%         {\dostrokedbox}%
+%       \setbox\extraframebox\hbox
+%         {\raise\scratchdimen\vbox
+%            {\moveleft\framedparameter\c!frameoffset\box\extraframebox}}%
+%       \wd\extraframebox\wd\framebox
+%       \ht\extraframebox\ht\framebox
+%       \dp\extraframebox\dp\framebox
+%       \hbox
+%         {\box\framebox\hskip-\wd\extraframebox
+%          \doifsomething{\framedparameter\c!framecolor}% no else needed,
+%            {\color[\framedparameter\c!framecolor]}%   %
+%            {\box\extraframebox}}}}                    % but {} here
+
+\def\dooutlinebox % we needed to move the color command in order to apply attributes properly
   {\setbox\framebox\vbox % rules on top of box
      {\scratchdimen \framedparameter\c!frameoffset\relax
       \frameddimenwd\dimexpr\wd\framebox+2\scratchdimen\relax
@@ -496,18 +523,15 @@
         \scratchdimen\zeropoint
       \fi
       \setbox\extraframebox\hbox
-        {\dostrokedbox}%
+        {\doifsomething{\framedparameter\c!framecolor}{\color[\framedparameter\c!framecolor]}{\dostrokedbox}}%
       \setbox\extraframebox\hbox
         {\raise\scratchdimen\vbox
-           {\moveleft\framedparameter\c!frameoffset\box\extraframebox}}%
+           {\moveleft\framedparameter\c!frameoffset
+            \box\extraframebox}}%
       \wd\extraframebox\wd\framebox
       \ht\extraframebox\ht\framebox
       \dp\extraframebox\dp\framebox
-      \hbox
-        {\box\framebox\hskip-\wd\extraframebox
-         \doifsomething{\framedparameter\c!framecolor}% no else needed,
-           {\color[\framedparameter\c!framecolor]}%   %
-           {\box\extraframebox}}}}                    % but {} here
+      \hbox{\box\framebox\hskip-\wd\extraframebox\box\extraframebox}}}
 
 \def\dostrokedbox
   {\doifelse{\framedparameter\c!framecorner}\v!rectangular
@@ -779,8 +803,6 @@
    \fi
    % new, experimental dirty hook
    \framedparameter\c!extras
-   % we need to register the (outer) color
-   \startregistercolor[\framedparameter\c!foregroundcolor]%
    % to get the right spacing
    \doifvaluesomething{\@@framed\c!foregroundstyle}
      {\@EA\doconvertfont\csname\@@framed\c!foregroundstyle\endcsname\empty}%
@@ -912,9 +934,9 @@
    \else\ifx\localstrut\v!local
      \setfontstrut
    \else
+     \setstrut
    \fi\fi\fi
    \ifboxhasstrut
-     \setstrut
      \let\localbegstrut\begstrut
      \let\localendstrut\endstrut
      \let\localstrut   \strut
@@ -977,6 +999,8 @@
      {\ifdim\!!framedwidth >\zeropoint\the\!!framedwidth \else\zeropoint\fi}%
    \edef\framedheight% a new feature, visible for user
      {\ifdim\!!framedheight>\zeropoint\the\!!framedheight\else\zeropoint\fi}%
+   % we need to register the (outer) color
+   \startregistercolor[\framedparameter\c!foregroundcolor]%
    % first alternative
    %\def\dowithframedbox%
    %  {\let\postprocessframebox\relax %new
@@ -989,7 +1013,6 @@
    %   \let\postprocessframebox\relax %new
    %   \stoplocalframed}
    %  \next}
-   % third alternative
    \@@startframedorientation
    \afterassignment\dodowithframebox
    \setbox\framebox\next}
@@ -1039,6 +1062,7 @@
 \def\stoplocalframed
   {\dontshowcomposition
    \@@stopframedorientation % hm, wrong place ! should rotatethe result (after reshape)
+   \stopregistercolor
    \handleframedlocator\c!before\@@locallocation
    \ifboxhasformat
      \ifx\@@localautowidth\v!force
@@ -1071,7 +1095,6 @@
       \ht\scratchbox\ht\framebox
       \dp\scratchbox\dp\framebox
       \setbox\framebox\box\scratchbox}%
-   \stopregistercolor
    \docolorframebox
    \ifboxhasoffset
      \dooffsetframebox
diff --git a/tex/context/base/core-sec.tex b/tex/context/base/core-sec.tex
index b92fbb21c..79ee538d7 100644
--- a/tex/context/base/core-sec.tex
+++ b/tex/context/base/core-sec.tex
@@ -1196,22 +1196,32 @@
    \dosomebreak{\penalty\!!counta}%
    \egroup}
 
+\newconditional\ignorehandlepagebreak
+
 \def\handlepagebreak#1%
-  {\dohandlepagebreakAA{#1}%
-   \ifnum\countervalue{\??se\previoussection\@@sectie}>\zerocount\relax
-     \ifnum\countervalue{\??se\@@sectie}>\zerocount
-       \dohandlepagebreakB{#1}%
+  {\ifconditional\ignorehandlepagebreak
+     \setfalse\ignorehandlepagebreak
+   \else
+     \dohandlepagebreakAA{#1}%
+     \ifnum\countervalue{\??se\previoussection\@@sectie}>\zerocount\relax
+       \ifnum\countervalue{\??se\@@sectie}>\zerocount
+         \dohandlepagebreakB{#1}%
+       \else
+         \doifnotvalue{\??ko#1\c!continue}\v!yes{\dohandlepagebreakB{#1}}%
+       \fi
      \else
-       \doifnotvalue{\??ko#1\c!continue}\v!yes{\dohandlepagebreakB{#1}}%
+       \dohandlepagebreakB{#1}%
      \fi
-   \else
-     \dohandlepagebreakB{#1}%
-   \fi
-   \dohandlepagebreakAB{#1}}
+     \dohandlepagebreakAB{#1}%
+   \fi}
 
-\def\handlepagebreakC#1%
-  {\xdef\@@kolevel{\getvalue{\??se\@@sectie\c!level}}%
-   \nobreak}
+\def\handlenopagebreak#1%
+  {\ifconditional\ignorehandlepagebreak
+     \setfalse\ignorehandlepagebreak
+   \else
+     \xdef\@@kolevel{\getvalue{\??se\@@sectie\c!level}}%
+     \nobreak
+   \fi}
 
 \def\localheadheight    {\strutht}
 \def\localheaddepth     {\strutdp}
@@ -1419,6 +1429,8 @@
 % todo: write to list etc in both args or in enclosing h/vbox else it gets
 % lost when no #1 or #2 is typeset
 
+% we will use variables here
+
 \def\dodododoconstructhead#1[#2]#3#4% [ref] {number} {title}
   {\def\currenthead{#1}% dus #1 overal vervangen
    \let\finalsectionnumber\dofinalsectionnumber % overloaded ungrouped -)
@@ -1641,6 +1653,7 @@
    \fi
    \flushingcolumnfloatstrue
    \someheadconversionfalse
+   \setfalse\ignorehandlepagebreak
    \let\fullsectionnumber\limitedfullsectionnumber
    % ignorespaces prevents spaces creeping in when after=\dontleavehmode
    \ifdisplaysectionhead\ignorespaces\else\expandafter\GotoPar\fi}
@@ -2052,7 +2065,7 @@
   {\ifhmode
      \scratchcounter=\lastpenalty\unpenalty % no beauty in this
      \ifdim\lastskip=\headsignal
-       \handlepagebreakC{#1}%
+       \handlenopagebreak{#1}%
        \global\settrue\continuoussectionhead
      \else
        \penalty\scratchcounter
diff --git a/tex/context/base/core-spa.lua b/tex/context/base/core-spa.lua
index c6090ab13..586a90d3d 100644
--- a/tex/context/base/core-spa.lua
+++ b/tex/context/base/core-spa.lua
@@ -238,8 +238,10 @@ do
         end
     end
 
-    function collapser(head,where)
-        if head then
+    -- local free = node.free
+
+    local function collapser(head,where)
+        if head and head.next then
             local trace = nodes.trace_collapse
             local current, tail = head, nil
             local glue_order, glue_data = 0, nil
@@ -364,21 +366,21 @@ do
     local head, tail = nil, nil
 
     function nodes.flush_vertical_spacing()
-        input.start_timing(nodes)
         if head then
-            t = collapser(head)
+            input.start_timing(nodes)
+            local t = collapser(head)
             head = nil
         --  tail = nil
+            input.stop_timing(nodes)
+            return t
         else
-            t = nil
+            return nil
         end
-        input.stop_timing(nodes)
-        return t
     end
 
     function nodes.handle_page_spacing(t, where)
     --  we need to add the latest t too, else we miss skips and such
-        if t then
+        if t and t.next then
             local tt = node.slide(t)
             local id = tt.id
             if id == glue then -- or id == penalty then -- or maybe: if not hlist or vlist
@@ -409,8 +411,12 @@ do
     end
 
     function nodes.handle_vbox_spacing(t)
-        local tail = node.slide(t)
-        return collapser(t,'whole')
+        if t and t.next then
+            local tail = node.slide(t)
+            return collapser(t,'whole')
+        else
+            return t
+        end
     end
 
 end
diff --git a/tex/context/base/core-spa.tex b/tex/context/base/core-spa.tex
index 159624c0d..aef7ac698 100644
--- a/tex/context/base/core-spa.tex
+++ b/tex/context/base/core-spa.tex
@@ -2387,8 +2387,7 @@
            \!!height\strutheight
            \!!depth \strutdepth
          \hss}}%
-   \struttotal\strutht
-   \advance\struttotal\strutdp}
+   \struttotal\dimexpr\strutht+\strutdp\relax}
 
 %D The dimen \type {\struttotal} holds the exact size of the
 %D strut; occasionally a one scaled point difference can show
@@ -3225,52 +3224,31 @@
 
 \beginETEX
 
-% \unexpanded\def\dostartattributes#1#2#3%
-%   {\begingroup  % geen \bgroup, anders in mathmode lege \hbox
-%    \ifcsname#1#3\endcsname
-%      \let\dostopattributes\@@dostopattributes
-%      \startcolor[\csname#1#3\endcsname]%
-%    \else
-%      \let\dostopattributes\@@nostopattributes
-%    \fi
-%    \ifcsname#1#2\endcsname
-%      \@EAEAEA\doconvertfont\@EA\@EA\csname#1#2\endcsname
-%    \fi}
-%
-% \unexpanded\def\@@dostopattributes%
-%   {\stopcolor
-%    \finishparbasedattributes
-%    \endgroup}
-%
-% \unexpanded\def\@@nostopattributes%
-%   {\finishparbasedattributes
-%    \endgroup}
-
-\unexpanded\def\dostartattributes#1#2#3%
-  {\begingroup  % geen \bgroup, anders in mathmode lege \hbox
-   \ifincolor
-     \ifcsname#1#3\endcsname
-       \let\dostopattributes\@@dostopattributes
-       \doglobalstartcolor[\csname#1#3\endcsname]%
-     \else
-       \let\dostopattributes\@@nostopattributes
-     \fi
-   \else
-     \let\dostopattributes\@@nostopattributes
-   \fi
-   \ifcsname#1#2\endcsname
-   % \@EAEAEA\doconvertfont\@EA\@EA\csname#1#2\endcsname
-     \@EA\doconvertfont\csname#1#2\@EA\endcsname
-   \fi}
+    \unexpanded\def\dostartattributes#1#2#3%
+      {\begingroup  % geen \bgroup, anders in mathmode lege \hbox
+       \ifincolor
+         \ifcsname#1#3\endcsname
+           \let\dostopattributes\@@dostopattributes
+           \faststartcolor[\csname#1#3\endcsname]%
+         \else
+           \let\dostopattributes\@@nostopattributes
+         \fi
+       \else
+         \let\dostopattributes\@@nostopattributes
+       \fi
+       \ifcsname#1#2\endcsname
+       % \@EAEAEA\doconvertfont\@EA\@EA\csname#1#2\endcsname
+         \@EA\doconvertfont\csname#1#2\@EA\endcsname
+       \fi}
 
-\unexpanded\def\@@dostopattributes
-  {\doglobalstopcolor
-   \finishparbasedattributes
-   \endgroup}
+    \unexpanded\def\@@dostopattributes
+      {\faststopcolor
+       \finishparbasedattributes
+       \endgroup}
 
-\unexpanded\def\@@nostopattributes
-  {\finishparbasedattributes
-   \endgroup}
+    \unexpanded\def\@@nostopattributes
+      {\finishparbasedattributes
+       \endgroup}
 
 \endETEX
 
@@ -3278,20 +3256,19 @@
 
 \beginTEX
 
-\unexpanded\def\dosetfontattribute#1#2%
-  {\@EA\ifx\csname#1#2\endcsname\relax\else
-     \@EAEAEA\doconvertfont\@EA\@EA\csname#1#2\endcsname
-   \fi\empty}
+    \unexpanded\def\dosetfontattribute#1#2%
+      {\@EA\ifx\csname#1#2\endcsname\relax\else
+         \@EA\doconvertfont\csname#1#2\@EA\endcsname
+       \fi\empty}
 
 \endTEX
 
 \beginETEX \ifcsname
 
-\unexpanded\def\dosetfontattribute#1#2%
-  {\ifcsname#1#2\endcsname
-   % \@EAEAEA\doconvertfont\@EA\@EA\csname#1#2\endcsname
-     \@EA\doconvertfont\csname#1#2\@EA\endcsname
-   \fi\empty}
+    \unexpanded\def\dosetfontattribute#1#2%
+      {\ifcsname#1#2\endcsname
+         \@EA\doconvertfont\csname#1#2\@EA\endcsname
+       \fi\empty}
 
 \endETEX
 
@@ -3300,24 +3277,24 @@
 
 \beginETEX \ifcsname
 
-\unexpanded\def\doattributes#1#2#3#4%
-  {\begingroup  % geen \bgroup, anders in mathmode lege \hbox
-   \ifincolor
-     \ifcsname#1#3\endcsname
-       \let\dostopattributes\@@dostopattributes
-       \doglobalstartcolor[\csname#1#3\endcsname]%
-     \else
-       \let\dostopattributes\endgroup
-     \fi
-   \else
-     \let\dostopattributes\endgroup
-   \fi
-   \ifcsname#1#2\endcsname
-   % \@EAEAEA\doconvertfont\@EA\@EA\csname#1#2\endcsname
-     \@EA\doconvertfont\csname#1#2\@EA\endcsname
-   \fi
-   {#4}%
-   \dostopattributes}
+    \unexpanded\def\doattributes#1#2#3#4%
+      {\begingroup  % geen \bgroup, anders in mathmode lege \hbox
+       \ifincolor
+         \ifcsname#1#3\endcsname
+           \let\dostopattributes\@@dostopattributes
+           \faststartcolor[\csname#1#3\endcsname]%
+         \else
+           \let\dostopattributes\endgroup
+         \fi
+       \else
+         \let\dostopattributes\endgroup
+       \fi
+       \ifcsname#1#2\endcsname
+       % \@EAEAEA\doconvertfont\@EA\@EA\csname#1#2\endcsname
+         \@EA\doconvertfont\csname#1#2\@EA\endcsname
+       \fi
+       {#4}%
+       \dostopattributes}
 
 \endETEX
 
@@ -3963,6 +3940,13 @@
 \newtoks \everyleftofalignedline
 \newtoks \everyrightofalignedline
 
+\def\shiftalignedline#1#2#3#4% left, right, inner, outer
+  {\rightorleftpageaction
+     {\everyleftofalignedline {\hskip\dimexpr#1+#3\relax}%
+      \everyrightofalignedline{\hskip\dimexpr#2+#4\relax}}
+     {\everyleftofalignedline {\hskip\dimexpr#1+#4\relax}%
+      \everyrightofalignedline{\hskip\dimexpr#2+#3\relax}}}
+
 % \def\doalignline#1#2% \\ == newline
 %   {\begingroup
 %    \setlocalhsize % new
diff --git a/tex/context/base/core-tab.tex b/tex/context/base/core-tab.tex
index 5828e85f7..edfa4b27b 100644
--- a/tex/context/base/core-tab.tex
+++ b/tex/context/base/core-tab.tex
@@ -366,13 +366,21 @@
 \catcode`\|=\@@active
 \catcode`\"=\@@active
 
+% \gdef\pushouterbarandquote
+%   {\ifForgetTableBarAndQuote
+%      \let|\letterbar
+%      \let"\letterdoublequote
+%      \ifnum\catcode`\|=\@@active \let\outertablebar  |\else\let\outertablebar  \relax\fi
+%      \ifnum\catcode`\"=\@@active \let\outertablequote"\else\let\outertablequote\relax\fi
+%    \fi}
+
 \gdef\pushouterbarandquote
-  {\ifForgetTableBarAndQuote
-     \let|\letterbar
-     \let"\letterdoublequote
-     \ifnum\catcode`\|=\@@active \let\outertablebar  |\else\let\outertablebar  \relax\fi
-     \ifnum\catcode`\"=\@@active \let\outertablequote"\else\let\outertablequote\relax\fi
-   \fi}
+   {\ifForgetTableBarAndQuote
+      \ifnum\catcode`\|=\@@active \let\outertablebar  |\else\let\outertablebar  \relax\fi
+      \ifnum\catcode`\"=\@@active \let\outertablequote"\else\let\outertablequote\relax\fi
+      \let|\letterbar
+      \let"\letterdoublequote
+    \fi}
 
 \gdef\popouterbarandquote
   {\ifForgetTableBarAndQuote
@@ -1197,13 +1205,22 @@
    \global\TABLEinbreakfalse
    \firststagestartTABLE}
 
+% \def\stoptables
+%   {\ifconditional\tablerepeattail\else\insertTABLEtail\fi
+%    \finishTABLE
+%    \egroup
+%    \dosplittablebox\tablecontentbox
+%    \flushnotes
+%    \egroup}
+
 \def\stoptables
-  {\ifconditional\tablerepeattail\else\insertTABLEtail\fi
-   \finishTABLE
-   \egroup
-   \dosplittablebox\tablecontentbox
-   \flushnotes
-   \egroup}
+   {\chuckTABLEautorow % AM: before the tail, else noalign problem
+    \ifconditional\tablerepeattail\else\insertTABLEtail\fi
+    \finishTABLE
+    \egroup
+    \dosplittablebox\tablecontentbox
+    \flushnotes
+    \egroup}
 
 \newdimen\TABLEcaptionheight % obsolete
 
diff --git a/tex/context/base/core-uti.mkiv b/tex/context/base/core-uti.mkiv
index 6883f2227..a9f7982f8 100644
--- a/tex/context/base/core-uti.mkiv
+++ b/tex/context/base/core-uti.mkiv
@@ -55,6 +55,8 @@
     \immediatewriteutilitytua{-- escape\space:           \!!bs\space...\space\!!es}%
     \immediatewriteutilitytua{-- version:                \utilityversion}%
     \immediatewriteutilitytua{}%
+    \immediatewriteutilitytua{-- begin of utility file}%
+    \immediatewriteutilitytua{}%
     \immediatewriteutilitytua{do}%
     \immediatewriteutilitytua{if job and job.version and not job.version == "\utilityversion" then return end}%
     \immediatewriteutilitytua{if not job then job = { } end}%
@@ -63,6 +65,8 @@
 
 \appendtoks
     \immediatewriteutilitytua{end}%
+    \immediatewriteutilitytua{}%
+    \immediatewriteutilitytua{-- end of utility file}%
    %immediate\closeout\utility@tua
 \to \everycloseutilities
 
@@ -72,8 +76,12 @@
     \ctxlua { do
         if not job then job = { } end
         job.version = "\utilityversion"
-        local settings = loadfile("\jobname.tuc")
-        if settings then settings() end
+      % local settings = loadfile("\jobname.tuc")
+        local settings = io.loaddata("\jobname.tuc")
+        if settings:find("\letterpercent -\letterpercent -\letterpercent s*end of utility file \letterpercent s$") then
+            settings = loadstring(data)
+            if settings then settings() end
+        end
     end}%
 \to \everyjob
 
diff --git a/tex/context/base/core-var.tex b/tex/context/base/core-var.tex
index 0fba2b3b3..0b0c7e2ca 100644
--- a/tex/context/base/core-var.tex
+++ b/tex/context/base/core-var.tex
@@ -279,6 +279,9 @@
 \newevery \everyfontswitch           \EveryFontSwitch
 \newevery \everydefinedfont          \relax
 
+\newevery \everybeforeoutput         \relax
+\newevery \everyafteroutput          \relax
+
 \newevery \everybeforedisplayformula \relax
 
 \def\cleanupfeatures{\the\everycleanupfeatures}
diff --git a/tex/context/base/font-afm.lua b/tex/context/base/font-afm.lua
index 8afeb84e0..c4e326d0a 100644
--- a/tex/context/base/font-afm.lua
+++ b/tex/context/base/font-afm.lua
@@ -19,7 +19,7 @@ away.
 
 fonts                      = fonts     or { }
 fonts.afm                  = fonts.afm or { }
-fonts.afm.version          = 1.10 -- incrementing this number one up will force a re-cache
+fonts.afm.version          = 1.13 -- incrementing this number one up will force a re-cache
 fonts.afm.syncspace        = true -- when true, nicer stretch values
 fonts.afm.enhance_data     = true -- best leave this set to true
 fonts.afm.trace_features   = false
@@ -54,7 +54,7 @@ do
         if designsize then data.designsize = tonumber(designsize) end
     end
 
-    local function get_charmetrics(characters,charmetrics)
+    local function get_charmetrics(data,charmetrics,vector)
         local characters = data.characters
         local chr, str, ind = { }, "", 0
         for k,v in charmetrics:gmatch("([%a]+) +(.-) *;") do
@@ -82,10 +82,12 @@ do
                 chr.ligatures[plus] = becomes
             end
         end
-        if str ~= "" then characters[str] = chr end
+        if str ~= "" then
+            characters[str] = chr
+        end
     end
 
-    local function get_kernpairs(characters,kernpairs)
+    local function get_kernpairs(data,kernpairs)
         local characters = data.characters
         for one, two, value in kernpairs:gmatch("KPX +(.-) +(.-) +(.-)\n") do
             local chr = characters[one]
@@ -102,17 +104,41 @@ do
         end
     end
 
+    local function get_indexes(data,filename)
+        local pfbname = input.find_file(texmf.instance,file.removesuffix(file.basename(filename))..".pfb","pfb") or ""
+        if pfbname ~= "" then
+            data.luatex = data.luatex or { }
+            data.luatex.filename = pfbname
+            local pfbblob = fontforge.open(pfbname)
+            if pfbblob then
+                local characters = data.characters
+                local pfbdata = fontforge.to_table(pfbblob)
+                if pfbdata and pfbdata.glyphs then
+                    for index, glyph in pairs(pfbdata.glyphs) do
+                        local name = glyph.name
+                        if name then
+                            local char = characters[name]
+                            if char then
+                                char.index = index
+                            end
+                        end
+                    end
+                end
+           end
+        end
+    end
+
     function fonts.afm.read_afm(filename)
         local ok, afmblob, size = input.loadbinfile(texmf.instance,filename) -- has logging
     --  local ok, afmblob = true, file.readdata(filename)
         if ok and afmblob then
-            data = {
+            local data = {
                 version = version or '0',
                 characters = { },
                 filename = file.removesuffix(file.basename(filename))
             }
             afmblob = afmblob:gsub("StartCharMetrics(.-)EndCharMetrics", function(charmetrics)
-                get_charmetrics(data,charmetrics)
+                get_charmetrics(data,charmetrics,vector)
                 return ""
             end)
             afmblob = afmblob:gsub("StartKernPairs(.-)EndKernPairs", function(kernpairs)
@@ -124,6 +150,7 @@ do
                 get_variables(data,fontmetrics)
                 return ""
             end)
+            get_indexes(data,filename)
             return data
         else
             return nil
@@ -268,11 +295,12 @@ function fonts.afm.copy_to_tfm(data)
                 tfm.characters[t.unicode] = t
             end
         end
-        tfm.encodingbytes      = 2
-        tfm.units              = 1000
-        tfm.name               = data.filename
-        tfm.type               = "real"
+        tfm.encodingbytes      = data.encodingbytes or 2
         tfm.fullname           = data.fullname
+        tfm.filename           = data.filename
+        tfm.name               = data.name
+        tfm.type               = "real"
+        tfm.units              = 1000
         tfm.stretch            = stretch
         tfm.slant              = slant
         tfm.direction          = 0
@@ -341,10 +369,6 @@ function fonts.afm.copy_to_tfm(data)
     end
 end
 
-
---~ function set_x(w,h) return  h*slant+w*stretch       end
---~ function set_y(h)   return  h                       end
-
 --[[ldx--
 Originally we had features kind of hard coded for 
 files but since I expect to support more font formats, I decided
@@ -367,7 +391,7 @@ function fonts.afm.set_features(tfmdata)
         local mode = tfmdata.mode or fonts.mode
         local fi = fonts.initializers[mode]
         if fi and fi.afm then
-            function initialize(list) -- using tex lig and kerning
+            local function initialize(list) -- using tex lig and kerning
                 if list then
                     for _, f in ipairs(list) do
                         local value = features[f]
@@ -387,7 +411,7 @@ function fonts.afm.set_features(tfmdata)
         end
         local fm = fonts.methods[mode]
         if fm and fm.afm then
-            function register(list) -- node manipulations
+            local function register(list) -- node manipulations
                 if list then
                     for _, f in ipairs(list) do
                         if features[f] and fm.afm[f] then -- brr
@@ -406,25 +430,46 @@ function fonts.afm.set_features(tfmdata)
 end
 
 function fonts.afm.afm_to_tfm(specification)
-    local afmfile  = specification.filename or specification.name
-    local features = specification.features.normal
-    local cache_id = specification.hash
-    local tfmdata  = containers.read(fonts.tfm.cache, cache_id) -- cache with features applied
-    if not tfmdata then
-        local afmdata = fonts.afm.load(afmfile)
-        if not table.is_empty(afmdata) then
-            tfmdata = fonts.afm.copy_to_tfm(afmdata)
-            if not table.is_empty(tfmdata) then
-                tfmdata.shared = tfmdata.shared or { }
-                tfmdata.unique = tfmdata.unique or { }
-                tfmdata.shared.afmdata  = afmdata
-                tfmdata.shared.features = features
-                fonts.afm.set_features(tfmdata)
+    local afmname = specification.filename or specification.name
+    local encoding, filename = afmname:match("^(.-)%-(.*)$") -- context: encoding-name.*
+    if encoding and filename and fonts.enc.known[encoding] then
+-- only when no bla-name is found
+        fonts.tfm.set_normal_feature(specification,'encoding',encoding) -- will go away
+        if fonts.trace then
+            logs.report("define font", string.format("stripping encoding prefix from filename %s",afmname))
+        end
+        afmname = filename
+    else
+        local tfmname = input.findbinfile(texmf.instance,afmname,"ofm") or ""
+        if tfmname ~= "" then
+            if fonts.trace then
+                logs.report("define font", string.format("fallback from afm to tfm for %s",afmname))
+            end
+            afmname = ""
+        end
+    end
+    if afmname == "" then
+        return nil
+    else
+        local features = specification.features.normal
+        local cache_id = specification.hash
+        local tfmdata  = containers.read(fonts.tfm.cache, cache_id) -- cache with features applied
+        if not tfmdata then
+            local afmdata = fonts.afm.load(afmname)
+            if not table.is_empty(afmdata) then
+                tfmdata = fonts.afm.copy_to_tfm(afmdata)
+                if not table.is_empty(tfmdata) then
+                    tfmdata.shared = tfmdata.shared or { }
+                    tfmdata.unique = tfmdata.unique or { }
+                    tfmdata.shared.afmdata  = afmdata
+                    tfmdata.shared.features = features
+                    fonts.afm.set_features(tfmdata)
+                end
             end
+            tfmdata = containers.write(fonts.tfm.cache,cache_id,tfmdata)
         end
-        tfmdata = containers.write(fonts.tfm.cache,cache_id,tfmdata)
+        return tfmdata
     end
-    return tfmdata
 end
 
 --[[ldx--
@@ -446,39 +491,36 @@ function fonts.tfm.set_normal_feature(specification,name,value)
 end
 
 function fonts.tfm.read_from_afm(specification)
-    local name, size, tfmtable = specification.name, specification.size, nil
-    local encoding, filename = name:match("^(.-)%-(.*)$") -- context: encoding-name.*
-    if filename and encoding and fonts.enc.known[encoding] then
-        fonts.tfm.set_normal_feature(specification,'encoding',encoding)
-    else
-        encoding = nil -- fonts.tfm.default_encoding
-        filename = name
-    end
-    if filename ~= "" then
-        tfmtable = fonts.afm.afm_to_tfm(specification)
-        if tfmtable then
-            tfmtable.name = name
-            tfmtable = fonts.tfm.scale(tfmtable, size)
-            filename = input.findbinfile(texmf.instance,filename,"pfb")
-            if filename then
-                tfmtable.format, tfmtable.filename = 'type1', filename
-            else
-                tfmtable.format, tfmtable.filename = 'pk', nil
-            end
-            if fonts.dontembed[filename] then
-                tfmtable.file = nil
-            end
-            -- begin of map hack
+    local tfmtable = fonts.afm.afm_to_tfm(specification)
+    if tfmtable then
+        tfmtable.name = specification.name
+        tfmtable = fonts.tfm.scale(tfmtable, specification.size)
+        local afmdata = tfmtable.shared.afmdata
+        local filename = afmdata and afmdata.luatex and afmdata.luatex.filename
+        if not filename then
+            -- try to locate anyway and set afmdata.luatex.filename
+        end
+        if filename then
+            tfmtable.encodingbytes = 2
+            tfmtable.filename = input.findbinfile(texmf.instance,filename,"") or filename
+            tfmtable.fullname = afmdata.fullname or afmdata.fontname
+            tfmtable.format   = 'type1'
+            tfmtable.name     = afmdata.luatex.filename or tfmtable.name
+        end
+        if fonts.dontembed[filename] then
+            tfmtable.file = nil
+        end
+        if false then -- no afm with pk
             local mapentry = {
                 name     = tfmtable.name,
                 fullname = tfmtable.fullname,
                 stretch  = tfmtable.stretch,
                 slant    = tfmtable.slant,
-                file     = tfmtable.filename,
+                fontfile = tfmtable.filename,
             }
-            -- end of map hack
-            fonts.map.data[name] = mapentry
+            fonts.map.data[specification.name] = mapentry
         end
+        fonts.logger.save(tfmtable,'afm',specification)
     end
     return tfmtable
 end
diff --git a/tex/context/base/font-def.lua b/tex/context/base/font-def.lua
index 581005c8a..16b3e90b7 100644
--- a/tex/context/base/font-def.lua
+++ b/tex/context/base/font-def.lua
@@ -20,7 +20,7 @@ fonts.vf     = fonts.vf     or { }
 fonts.used   = fonts.used   or { }
 
 fonts.tfm.version = 1.01
-fonts.tfm.cache   = containers.define("fonts", "tfm", fonts.tfm.version, false)
+fonts.tfm.cache   = containers.define("fonts", "tfm", fonts.tfm.version, false) -- better in font-tfm
 
 --[[ldx--
 
Choosing a font by name and specififying its size is only part of the
@@ -73,8 +73,9 @@ and prepares a table that will move along as we proceed.
 --ldx]]--
 
 function fonts.define.analyze(name, size, id)
-    local specification = name or 'unknown'
-    local lookup, rest = name:match("^(.-):(.+)$")
+    name = name or 'unknown'
+    local specification = name
+    local lookup, rest = specification:match("^(.-):(.+)$")
     local sub = ""
     if lookup == 'file' or lookup == 'name' then
         name = rest
@@ -235,15 +236,19 @@ function fonts.tfm.read_and_define(name,size) -- no id
     local id = fonts.tfm.internalized[hash]
     if not id then
         local fontdata = fonts.tfm.read(specification)
-        if not fonts.tfm.internalized[hash] then
-            id = font.define(fontdata)
-            fonts.tfm.id[id] = fontdata
-            fonts.tfm.internalized[hash] = id
-            if fonts.trace then
-                logs.report("define font", string.format("at 1 id %s, hash: %s",id,hash))
+        if fontdata then
+            if not fonts.tfm.internalized[hash] then
+                id = font.define(fontdata)
+                fonts.tfm.id[id] = fontdata
+                fonts.tfm.internalized[hash] = id
+                if fonts.trace then
+                    logs.report("define font", string.format("at 1 id %s, hash: %s",id,hash))
+                end
+            else
+                id = fonts.tfm.internalized[hash]
             end
         else
-            id = fonts.tfm.internalized[hash]
+            id = 0  -- signal
         end
     end
     return fonts.tfm.id[id], id
@@ -272,7 +277,6 @@ function fonts.tfm.readers.opentype(specification,suffix,what)
         if fullname and fullname ~= "" then
             specification.filename, specification.format = fullname, what -- hm, so we do set the filename, then
             tfmtable = fonts.tfm.read_from_open_type(specification)       -- we need to do it for all matches / todo
-            fonts.logger.save(tfmtable,suffix,specification)
         end
         return tfmtable
     else
@@ -288,23 +292,19 @@ function fonts.tfm.readers.afm(specification,method)
     local fullname, tfmtable = nil, nil
     method = method or fonts.define.method
     if method == 2 then
-        fullname = input.findbinfile(texmf.instance,specification.name,"ofm") -- ?
-        if not fullname or fullname == "" then
+        fullname = input.findbinfile(texmf.instance,specification.name,"ofm") or ""
+        if fullname == "" then
             tfmtable = fonts.tfm.read_from_afm(specification)
-            fonts.logger.save(tfmtable,'afm',specification)
         else -- redundant
             specification.filename = fullname
             tfmtable = fonts.tfm.read_from_tfm(specification)
-            fonts.logger.save(tfmdata,'tfm',specification)
         end
     elseif method == 3 then -- maybe also findbinfile here
         if fonts.define.auto_afm then
             tfmtable = fonts.tfm.read_from_afm(specification)
-            fonts.logger.save(tfmtable,'afm',specification)
         end
     elseif method == 4 then -- maybe also findbinfile here
         tfmtable = fonts.tfm.read_from_afm(specification)
-        fonts.logger.save(tfmtable,'afm',specification)
     end
     return tfmtable
 end
@@ -312,7 +312,6 @@ end
 function fonts.tfm.readers.tfm(specification)
     local fullname, tfmtable = nil, nil
     tfmtable = fonts.tfm.read_from_tfm(specification)
-    fonts.logger.save(tfmtable,'tfm',specification)
     return tfmtable
 end
 
@@ -394,6 +393,10 @@ function fonts.define.specify.preset_context(name,features)
     fonts.define.specify.context_setups[name] = t
 end
 
+function fonts.define.specify.context_tostring(name,kind,separator,yes,no,strict)
+    return aux.hash_to_string(table.merged(fonts[kind].features.default or {},fonts.define.specify.context_setups[name] or {}),separator,yes,no,strict)
+end
+
 function fonts.define.specify.split_context(features)
     if fonts.define.specify.context_setups[features] then
         return fonts.define.specify.context_setups[features]
@@ -472,9 +475,19 @@ function fonts.define.read(name,size,id)
         else
             fontdata = fonts.tfm.internalized[hash]
         end
+
     end
     if not fontdata then
-        logs.error("defining font", string.format("name: %s, loading aborted",specification.name))
+        logs.error("define font", string.format("name: %s, loading aborted",specification.name))
+    elseif fonts.trace and type(fontdata) == "table" then
+        logs.report("use font",string.format("%s font n:%s s:%s b:%s e:%s p:%s f:%s",
+            fontdata.type          or "unknown",
+            fontdata.name          or "?",
+            fontdata.size          or "default",
+            fontdata.encodingbytes or "?",
+            fontdata.encodingname  or "unicode",
+            fontdata.fullname      or "?",
+            file.basename(fontdata.filename or "?")))
     end
     return fontdata
 end
@@ -490,11 +503,25 @@ end
 --~ end
 
 function fonts.vf.find(name)
-    local format = fonts.logger.format(name)
-    if format == 'tfm' or format == 'ofm' then
-        return input.findbinfile(texmf.instance,name,"ovf")
+    name = file.removesuffix(file.basename(name))
+    if fonts.tfm.resolve_vf then
+        local format = fonts.logger.format(name)
+        if format == 'tfm' or format == 'ofm' then
+            if fonts.trace then
+                logs.report("define font",string.format("locating vf for %s",name))
+            end
+            return input.findbinfile(texmf.instance,name,"ovf")
+        else
+            if fonts.trace then
+                logs.report("define font",string.format("vf for %s is already taken care of",name))
+            end
+            return nil -- ""
+        end
     else
-        return nil -- ""
+        if fonts.trace then
+            logs.report("define font",string.format("locating vf for %s",name))
+        end
+        return input.findbinfile(texmf.instance,name,"ovf")
     end
 end
 
@@ -503,4 +530,4 @@ end
 --ldx]]--
 
 callback.register('define_font' , fonts.define.read)
-callback.register('find_vf_file', fonts.vf.find    )
+callback.register('find_vf_file', fonts.vf.find    ) -- not that relevant any more
diff --git a/tex/context/base/font-enc.lua b/tex/context/base/font-enc.lua
index a29ed83d3..3cc6433b2 100644
--- a/tex/context/base/font-enc.lua
+++ b/tex/context/base/font-enc.lua
@@ -12,8 +12,8 @@ them in tables. But we may do so some day, for consistency.
 --ldx]]--
 
 fonts.enc         = fonts.enc or { }
-fonts.enc.version = 1.01
-fonts.enc.cache   = containers.define("fonts", "enc", fonts.enc.version, false)
+fonts.enc.version = 1.03
+fonts.enc.cache   = containers.define("fonts", "enc", fonts.enc.version, true)
 
 fonts.enc.known = {
     texnansi = true,
@@ -52,12 +52,13 @@ will be used.
 function fonts.enc.load(filename)
     local name = file.removesuffix(filename)
     local data = containers.read(fonts.enc.cache,name)
-    if data then
-        local vector, tag, hash = { }, "", { }
+    if not data then
+        local vector, tag, hash, unicodes = { }, "", { }, { }
         local foundname = input.find_file(texmf.instance,filename,'enc')
         if foundname and foundname ~= "" then
             local ok, encoding, size = input.loadbinfile(texmf.instance,foundname)
             if ok and encoding then
+                local enccodes = characters.context.enccodes
                 encoding = encoding:gsub("%%(.-)\n","")
                 local tag, vec = encoding:match("/(%w+)%s*%[(.*)%]%s*def")
                 local i = 0
@@ -69,12 +70,22 @@ function fonts.enc.load(filename)
                         else
                             -- duplicate, play safe for tex ligs and take first
                         end
+                        if enccodes[ch] then
+                            unicodes[enccodes[ch]] = i
+                        end
                     end
                     i = i + 1
                 end
             end
         end
-        data = containers.write(fonts.enc.cache, name, { name=name, tag=tag, vector=vector, hash=hash })
+        local data = {
+            name=name,
+            tag=tag,
+            vector=vector,
+            hash=hash,
+            unicodes=unicodes
+        }
+        data = containers.write(fonts.enc.cache, name, data)
     end
     return data
 end
diff --git a/tex/context/base/font-ini.lua b/tex/context/base/font-ini.lua
index 16fcf7271..d4adf360b 100644
--- a/tex/context/base/font-ini.lua
+++ b/tex/context/base/font-ini.lua
@@ -49,7 +49,8 @@ do
     local unset_attribute = node.unset_attribute
 
     function fonts.color.set(n,c)
-        set_attribute(n,attribute,mapping[c] or -1)
+    --  local mc = mapping[c] if mc then unset_attribute((n,attribute) else set_attribute(n,attribute,mc) end
+        set_attribute(n,attribute,mapping[c] or -1) -- also handles -1 now
     end
     function fonts.color.reset(n)
         unset_attribute(n,attribute)
diff --git a/tex/context/base/font-ini.mkiv b/tex/context/base/font-ini.mkiv
index dd597d41f..45ff3480e 100644
--- a/tex/context/base/font-ini.mkiv
+++ b/tex/context/base/font-ini.mkiv
@@ -31,12 +31,14 @@
 
 \def\otfchar#1{\ctxlua{fonts.otf.char("#1")}}
 
-\registerrgbcolor {font:init} {1}{0}{0}%
-\registerrgbcolor {font:medi} {0}{1}{0}%
-\registerrgbcolor {font:fina} {0}{0}{1}%
-\registerrgbcolor {font:isol} {0}{1}{1}%
-\registerrgbcolor {font:mark} {1}{0}{1}%
-\registerrgbcolor {font:rest} {1}{1}{0}%
+%D: We cannot yet inherit because no colors are predefined.
+
+\definecolor[font:init][r=.75]
+\definecolor[font:medi][g=.75]
+\definecolor[font:fina][b=.75]
+\definecolor[font:isol][y=.75]
+\definecolor[font:mark][m=.75]
+\definecolor[font:rest][c=.75]
 
 %D goodies:
 %D
@@ -69,4 +71,12 @@
 \def\doinstallfontfeature[#1][#2]%
   {\ctxlua{fonts.install_feature("#1","#2")}}
 
+%D Not yet in \MKII.
+
+\def\fontfeatureslist
+  {\dodoubleargument\dofontfeatureslist}
+
+\def\dofontfeatureslist[#1][#2]% todo: arg voor type
+  {\ctxlua{tex.sprint(tex.ctxcatcodes,fonts.define.specify.context_tostring("#1","otf","\luaescapestring{#2}","yes","no",true))}}
+
 \protect \endinput
diff --git a/tex/context/base/font-map.lua b/tex/context/base/font-map.lua
index 4aecde445..64ff268fb 100644
--- a/tex/context/base/font-map.lua
+++ b/tex/context/base/font-map.lua
@@ -11,38 +11,39 @@ if not modules then modules = { } end modules ['font-map'] = {
 of obsolete. Some code may move to runtime or auxiliary modules.
 --ldx]]--
 
-fonts            = fonts            or { }
-fonts.map        = fonts.map        or { }
-fonts.map.data   = fonts.map.data   or { }
-fonts.map.done   = fonts.map.done   or { }
-fonts.map.line   = fonts.map.line   or { }
-fonts.map.loaded = fonts.map.loaded or { }
-fonts.map.direct = fonts.map.direct or { }
+fonts               = fonts               or { }
+fonts.map           = fonts.map           or { }
+fonts.map.data      = fonts.map.data      or { }
+fonts.map.encodings = fonts.map.encodings or { }
+fonts.map.done      = fonts.map.done      or { }
+fonts.map.loaded    = fonts.map.loaded    or { }
+fonts.map.direct    = fonts.map.direct    or { }
+fonts.map.line      = fonts.map.line      or { }
 
 function fonts.map.line.pdfmapline(tag,str)
     return "\\loadmapline[" .. tag .. "][" .. str .. "]"
 end
 
 function fonts.map.line.pdftex(e) -- so far no combination of slant and stretch
-    if e.name and e.file then
+    if e.name and e.fontfile then
         local fullname = e.fullname or ""
-        if e.slant and tonumber(e.slant) ~= 0 then
+        if e.slant and e.slant ~= 0 then
             if e.encoding then
-                return fonts.map.line.pdfmapline("=",string.format('%s %s "%g SlantFont" <%s <%s',e.name,fullname,e.slant,e.encoding,e.file))
+                return fonts.map.line.pdfmapline("=",string.format('%s %s "%g SlantFont" <%s <%s',e.name,fullname,e.slant,e.encoding,e.fontfile))
             else
-                return fonts.map.line.pdfmapline("=",string.format('%s %s "%g SlantFont" <%s',e.name,fullname,e.slant,e.file))
+                return fonts.map.line.pdfmapline("=",string.format('%s %s "%g SlantFont" <%s',e.name,fullname,e.slant,e.fontfile))
             end
-        elseif e.stretch and tonumber(e.stretch) ~= 1 and tonumber(e.stretch) ~= 0 then
+        elseif e.stretch and e.stretch ~= 1 and e.stretch ~= 0 then
             if e.encoding then
-                return fonts.map.line.pdfmapline("=",string.format('%s %s "%g ExtendFont" <%s <%s',e.name,fullname,e.stretch,e.encoding,e.file))
+                return fonts.map.line.pdfmapline("=",string.format('%s %s "%g ExtendFont" <%s <%s',e.name,fullname,e.stretch,e.encoding,e.fontfile))
             else
-                return fonts.map.line.pdfmapline("=",string.format('%s %s "%g ExtendFont" <%s',e.name,fullname,e.stretch,e.file))
+                return fonts.map.line.pdfmapline("=",string.format('%s %s "%g ExtendFont" <%s',e.name,fullname,e.stretch,e.fontfile))
             end
         else
             if e.encoding then
-                return fonts.map.line.pdfmapline("=",string.format('%s %s <%s <%s',e.name,fullname,e.encoding,e.file))
+                return fonts.map.line.pdfmapline("=",string.format('%s %s <%s <%s',e.name,fullname,e.encoding,e.fontfile))
             else
-                return fonts.map.line.pdfmapline("=",string.format('%s %s <%s',e.name,fullname,e.file))
+                return fonts.map.line.pdfmapline("=",string.format('%s %s <%s',e.name,fullname,e.fontfile))
             end
         end
     else
@@ -50,123 +51,71 @@ function fonts.map.line.pdftex(e) -- so far no combination of slant and stretch
     end
 end
 
-function fonts.map.flushlines(backend,separator)
-    local t = { }
-    for k,v in pairs(fonts.map.data) do
-        if not fonts.map.done[k] then
-            local str = fonts.map.line[backend](v)
-            if str then
-                t[#t+1] = str
-            end
-            fonts.map.done[k] = true
-        end
+function fonts.map.flush(backend) -- will also erase the accumulated data
+    local flushline = fonts.map.line[backend or "pdftex"] or fonts.map.line.pdftex
+    for _, e in pairs(fonts.map.data) do
+        tex.sprint(tex.ctxcatcodes,flushline(e))
     end
-    return table.join(t,separator or "")
+    fonts.map.data = { }
 end
 
-fonts.map.line.dvips    = fonts.map.line.pdftex
-fonts.map.line.dvipdfmx = function() end
-
-function fonts.map.process_entries(filename, backend, handle)
-    local root = xml.load(filename,true)
-    if root then
-        if not handle then handle = texio.write_nl end
-        xml.process_attributes(root, "/fontlist/font", function(e,k)
-            local str = fonts.map.line[backend](e)
-            if str then
-                handle(str)
-            end
-        end)
-    end
-end
+fonts.map.line.dvips     = fonts.map.line.pdftex
+fonts.map.line.dvipdfmx  = function() end
 
 function fonts.map.convert_entries(filename)
     if not fonts.map.loaded[filename] then
-        local root = xml.load(filename,true) -- todo: stop garbage collector
-        if root then
-            garbagecollector.push()
-            xml.process_attributes(root, "/fontlist/font", function(e,k)
-                if e.name and e.file then
-                    fonts.map.data[e.name] = e
-                --  fonts.map.data[e.name].name = nil -- beware, deletes xml entry as well
-                end
-            end)
-            garbagecollector.pop()
-        end
+        fonts.map.data, fonts.map.encodings = fonts.map.load_file(filename,fonts.map.data, fonts.map.encodings)
         fonts.map.loaded[filename] = true
     end
 end
 
-function fonts.map.direct.pdftex(filename)
-    fonts.map.process_entries(filename,'pdftex',function(str)
-        tex.sprint(tex.ctxcatcodes,str)
-    end)
-end
-
--- the next one will go to a runtime module, no need to put this in the format
-
-function fonts.map.convert_file(filename, handle) -- when handle is string, then assume file
-    local f, g = io.open(filename), nil
+function fonts.map.load_file(filename, entries, encodings)
+    entries   = entries   or { }
+    encodings = encodings or { }
+    local f = io.open(filename)
     if f then
-        if not handle then
-            handle = print
-        elseif type(handle) == "string" then
-            g = io.open(handle,"w")
-            function handle(str)
-                g:write(str .. "\n")
-            end
-        end
-        handle("\n")
-        handle(string.format("\n", "generated by context"))
-        handle(string.format("\n", filename))
-        for line in f:lines() do
-            local comment = line:match("^[%#%%]%s*(.*)%s*$")
-            if comment then -- todo: optional
-                handle(string.format("  ", comment))
-            elseif line:find("^\s*$") then
-                handle("")
-            else
-                name, fullname, spec, encoding, file = line:match("^(%S+)%s+(%S-)%s*\"([^\"]-)\"%s*<%s*(%S-)%s*<%s*(%S-)%s*$")
-                if not name then
-                    name, fullname, spec, file = line:match("^(%S+)%s+(%S-)%s*\"([^\"]-)\"%s*<%s*(%S-)%s*$")
-                end
-                if not name then
-                    name, fullname, encoding, file = line:match("^(%S+)%s+(%S-)%s*<%s*(%S-)%s*<%s*(%S-)%s*$")
-                end
-                if not name then
-                    name, fullname, file = line:match("^(%S+)%s+(%S-)%s*<%s*(%S-)%s*$")
-                end
-                if name and name ~= "" and file and file ~= "" then
-                    t = { }
-                    if name                        then t[#t+1] = string.format("name='%s'"    , name)     end
-                    if fullname and fullname ~= "" then t[#t+1] = string.format("fullname='%s'", fullname) end
-                    if encoding and encoding ~= "" then t[#t+1] = string.format("encoding='%s'", encoding) end
-                    if file                        then t[#t+1] = string.format("file='%s'"    , file)     end
-                    if spec then
-                        local a, b = spec:match("^([%d%.])%s+(%a+)$")
-                        if a and b and b == "ExtendFont" then t[#t+1] = string.format("slant='%s'"  , a) end
-                        if a and b and b == "SlantFont"  then t[#t+1] = string.format("stretch='%s'", a) end
+        local data = f:read("*a")
+        if data then
+            for line in data:gmatch("(.-)[\n\t]") do
+                if line:find("^[%#%%%s]") then
+                    -- print(line)
+                else
+                    local stretch, slant, name, fullname, fontfile, encoding
+                    line = line:gsub('"(.+)"', function(s)
+                        stretch = s:find('"([^"]+) ExtendFont"')
+                        slant = s:find('"([^"]+) SlantFont"')
+                        return ""
+                    end)
+                    if not name then
+                        -- name fullname encoding fontfile
+                        name, fullname, encoding, fontfile = line:match("^(%S+)%s+(%S*)[%s<]+(%S*)[%s<]+(%S*)%s*$")
+                    end
+                    if not name then
+                        -- name fullname (flag) fontfile encoding
+                        name, fullname, fontfile, encoding = line:match("^(%S+)%s+(%S*)[%d%s<]+(%S*)[%s<]+(%S*)%s*$")
+                    end
+                    if not name then
+                        -- name fontfile
+                        name, fontfile = line:match("^(%S+)%s+[%d%s<]+(%S*)%s*$")
+                    end
+                    if name then
+                        if encoding == "" then encoding = nil end
+                        entries[name] = {
+                            name     = name, -- handy
+                            fullname = fullname,
+                            encoding = encoding,
+                            fontfile = fontfile,
+                            slant    = tonumber(slant),
+                            stretch  = tonumber(stretch)
+                        }
+                        encodings[name] = encoding
+                    elseif line ~= "" then
+                    --  print(line)
                     end
-                    handle(string.format("  ",table.concat(t," ")))
                 end
             end
         end
-        handle("\n")
         f:close()
-        if g then g:close() end
     end
+    return entries, encodings
 end
-
---~ fonts.map.convert_file("c:/data/develop/tex/texmf/fonts/map/dvips/lm/lm-ec.map")
---~ fonts.map.convert_file("maptest.map")
---~ fonts.map.convert_file("maptest.map", "maptest-1.xml")
---~ fonts.map.convert_file("c:/data/develop/tex/texmf/fonts/map/pdftex/updmap/pdftex.map")
---~ fonts.map.convert_file("c:/data/develop/tex/texmf/fonts/map/pdftex/updmap/pdftex.map", "maptest-2.xml")
-
---~ fonts.map.convert_entries('maptest-2.xml')
---~ fonts.map.process_entries('maptest.xml','pdftex')
-
---~ print(table.serialize(fonts.map.data))
-
---~ tex.sprint(fonts.map.flushlines("pdftex","\n"))
---~ str = fonts.map.flushlines("pdftex")
diff --git a/tex/context/base/font-otf.lua b/tex/context/base/font-otf.lua
index 8dec45481..8331a5177 100644
--- a/tex/context/base/font-otf.lua
+++ b/tex/context/base/font-otf.lua
@@ -33,7 +33,7 @@ in the  table.
 
 fonts                        = fonts or { }
 fonts.otf                    = fonts.otf or { }
-fonts.otf.version            = 1.56 -- incrementing this number one up will force a re-cache
+fonts.otf.version            = 1.60 -- incrementing this number one up will force a re-cache
 fonts.otf.tables             = fonts.otf.tables or { }
 fonts.otf.meanings           = fonts.otf.meanings or { }
 fonts.otf.enhance_data       = false
@@ -727,7 +727,9 @@ function fonts.otf.load(filename,format,sub,featurefile)
                 fonts.otf.enhance.analyze(data,filename)
                 logs.report("load otf","enhance: after")
                 fonts.otf.enhance.after(data,filename)
-                logs.report("load otf","save in cache")
+                logs.report("load otf","enhance: patch")
+                fonts.otf.enhance.patch(data,filename)
+                logs.report("load otf","saving: in cache")
                 data = containers.write(fonts.otf.cache, name, data)
             else
                 logs.error("load otf","loading failed")
@@ -750,6 +752,8 @@ function fonts.otf.load(filename,format,sub,featurefile)
     return data
 end
 
+-- todo: normalize, design_size => designsize
+
 function fonts.otf.enhance.analyze(data,filename)
     local t = {
         filename = file.basename(filename),
@@ -764,27 +768,150 @@ function fonts.otf.enhance.analyze(data,filename)
     data.luatex = t
 end
 
+function fonts.otf.load_cidmap(filename)
+    local data = io.loaddata(filename)
+    if data then
+        local unicodes, names = { }, {}
+        data = data:gsub("^(%d+)%s+(%d+)\n","")
+        for a,b in data:gmatch("(%d+)%s+([%d%a]+)\n") do
+            unicodes[tonumber(a)] = tonumber(b,16)
+        end
+        for a,b,c in data:gmatch("(%d+)%.%.(%d+)%s+([%d%a]+)%s*\n") do
+            c = tonumber(c,16)
+            for i=tonumber(a),tonumber(b) do
+                unicodes[i] = c
+                c = c + 1
+            end
+        end
+        for a,b in data:gmatch("(%d+)%s+\/(%S+)%s*\n") do
+            names[tonumber(a)] = b
+        end
+        local supplement, registry, ordering = filename:match("^(.-)%-(.-)%-()%.(.-)$")
+        return {
+            supplement = supplement,
+            registry   = registry,
+            ordering   = ordering,
+            filename   = filename,
+            unicodes   = unicodes,
+            names      = names
+        }
+    else
+        return nil
+    end
+end
+
+fonts.otf.cidmaps = { }
+
+function fonts.otf.cidmap(registry,ordering,supplement)
+    local template = "%s-%s-%s.cidmap"
+    local filename = string.format(template,registry,ordering,supplement)
+    local supplement = tonumber(supplement)
+    if not fonts.otf.cidmaps[filename] then
+        for i=supplement,0,-1 do
+            logs.report("load otf",string.format("checking cidmap, registry: %s, ordering: %s, supplement: %s",registry,ordering,i))
+            filename = string.format(template,registry,ordering,i)
+            local fullname = input.find_file(texmf.instance,filename,'cid') or ""
+            if fullname ~= "" then
+                local cidmap = fonts.otf.load_cidmap(fullname)
+                if cidmap then
+                    logs.report("load otf",string.format("using cidmap file %s",filename))
+                    fonts.otf.cidmaps[filename] = cidmap
+                    if i < supplement then
+                        for j=i+1,supplement do
+                            filename = string.format(template,registry,ordering,j)
+                            fonts.otf.cidmaps[filename] = cidmap -- copy of ref
+                        end
+                    end
+                    return cidmap
+                end
+            end
+        end
+    end
+    return nil
+end
+
+--~  ["cidinfo"]={
+--~   ["ordering"]="Japan1",
+--~   ["registry"]="Adobe",
+--~   ["supplement"]=6,
+--~   ["version"]=6,
+--~  },
+
 function fonts.otf.enhance.before(data,filename)
     local private = 0xE000
-    local uni_to_int = data.map.map
-    local int_to_uni = data.map.backmap
-    for index, glyph in pairs(data.glyphs) do
-        if index > 0 and glyph.unicodeenc == -1 then
-            while uni_to_int[private] do
-                private = private + 1
+    if data.subfonts and table.is_empty(data.glyphs) then
+        local cidinfo = data.cidinfo
+        if cidinfo.registry then
+            local cidmap = fonts.otf.cidmap(cidinfo.registry,cidinfo.ordering,cidinfo.supplement)
+            if cidmap then
+                local glyphs, uni_to_int, int_to_uni, nofnames, nofunicodes, zerobox = { }, { }, { }, 0, 0, { 0, 0, 0, 0 }
+                local unicodes, names = cidmap.unicodes, cidmap.names
+                for n, subfont in pairs(data.subfonts) do
+                    for index, g in pairs(subfont.glyphs) do
+                        if not next(g) then
+                            -- dummy entry
+                        else
+                            local unicode, name = unicodes[index], names[index]
+                            g.cidindex = n
+                            g.boundingbox = g.boundingbox or zerobox
+                            g.name = g.name or name     or "unknown"
+                            if unicode then
+                                g.unicodeenc = unicode
+                                uni_to_int[unicode] = index
+                                int_to_uni[index] = unicode
+                                nofunicodes = nofunicodes + 1
+                            elseif name then
+                                g.unicodeenc = -1
+                                nofnames = nofnames + 1
+                            end
+                            glyphs[index] = g
+                        end
+                    end
+                    subfont.glyphs = nil
+                end
+                logs.report("load otf",string.format("cid font remapped, %s unicode points, %s symbolic names, %s glyphs",nofunicodes, nofnames, nofunicodes+nofnames))
+                data.glyphs = glyphs
+                data.map = data.map or { }
+                data.map.map = uni_to_int
+                data.map.backmap = int_to_uni
+            else
+                logs.report("load otf",string.format("unable to remap cid font, missing cid file for %s",filename))
             end
-            uni_to_int[private] = index
-            int_to_uni[index] = private
-            glyph.unicodeenc = private
-            logs.report("load otf",string.format("enhance: glyph %s at index %s is moved to private unicode slot %s",glyph.name,index,private))
+        else
+            logs.report("load otf",string.format("font %s has no glyphs",filename))
         end
     end
+    if data.map then
+        local uni_to_int = data.map.map
+        local int_to_uni = data.map.backmap
+        for index, glyph in pairs(data.glyphs) do
+            if index > 0 and glyph.unicodeenc == -1 then
+                while uni_to_int[private] do
+                    private = private + 1
+                end
+                uni_to_int[private] = index
+                int_to_uni[index] = private
+                glyph.unicodeenc = private
+                if fonts.trace then
+                    logs.report("load otf",string.format("enhance: glyph %s at index %s is moved to private unicode slot %s",glyph.name,index,private))
+                end
+            end
+        end
+    else
+        data.map = { map = {}, backmap = {} }
+    end
     if data.ttf_tables then
         for _, v in ipairs(data.ttf_tables) do
             if v.data then v.data = "deleted" end
         --~ if v.data then v.data = v.data:gsub("\026","\\026") end -- does not work out well
         end
     end
+    table.compact(data.glyphs)
+    if data.subfonts then
+        for _, subfont in pairs(data.subfonts) do
+            table.compact(subfont.glyphs)
+        end
+    end
 end
 
 function fonts.otf.enhance.after(data,filename)
@@ -792,13 +919,21 @@ function fonts.otf.enhance.after(data,filename)
     for index, glyph in pairs(data.glyphs) do
         if glyph.kerns then
             local mykerns = { }
-            for k,v in ipairs(glyph.kerns) do
-                local mkl = mykerns[v.lookup]
-                if not mkl then
-                    mkl = { [unicodes[v.char]] = v.off }
-                    mykerns[v.lookup] = mkl
-                else
-                    mkl[unicodes[v.char]] = v.off
+            for k,v in pairs(glyph.kerns) do
+                local vc, vo, vl = v.char, v.off, v.lookup
+                if vc and vo and vl then -- brrr, wrong! we miss the non unicode ones
+                    local uvc = unicodes[vc]
+                    if uvc then
+                        local mkl = mykerns[vl]
+                        if not mkl then
+                            mkl = { [unicodes[vc]] = vo }
+                            mykerns[v.lookup] = mkl
+                        else
+                            mkl[unicodes[vc]] = vo
+                        end
+                    else
+                        logs.report("load otf", string.format("problems with unicode %s of kern %s at glyph %s",vc,k,index))
+                    end
                 end
             end
             glyph.mykerns = mykerns
@@ -806,6 +941,35 @@ function fonts.otf.enhance.after(data,filename)
     end
 end
 
+fonts.otf.enhance.patches = { }
+
+function fonts.otf.enhance.patch(data,filename)
+    local basename = file.basename(filename)
+    for pattern, action in pairs(fonts.otf.enhance.patches) do
+        if basename:find(pattern) then
+            action(data,filename)
+        end
+    end
+end
+
+do -- will move to a typescript
+
+    local function patch(data,filename)
+        if data.design_size == 0 then
+            local ds = (file.basename(filename)):match("(%d+)")
+            if ds then
+                logs.report("load otf",string.format("patching design size (%s)",ds))
+                data.design_size = tonumber(ds) * 10
+            end
+        end
+    end
+
+    fonts.otf.enhance.patches["^lmroman"] = patch
+    fonts.otf.enhance.patches["^lmsans"]  = patch
+    fonts.otf.enhance.patches["^lmmono"]  = patch
+
+end
+
 function fonts.otf.analyze_class(data,class)
     local classes = { }
     for index, glyph in pairs(data.glyphs) do
@@ -941,7 +1105,7 @@ function fonts.otf.set_features(tfmdata)
         local mode = tfmdata.mode or fonts.mode
         local fi = fonts.initializers[mode]
         if fi and fi.otf then
-            function initialize(list) -- using tex lig and kerning
+            local function initialize(list) -- using tex lig and kerning
                 if list then
                     for _, f in ipairs(list) do
                         local value = features[f]
@@ -962,7 +1126,7 @@ function fonts.otf.set_features(tfmdata)
         end
         local fm = fonts.methods[mode]
         if fm and fm.otf then
-            function register(list) -- node manipulations
+            local function register(list) -- node manipulations
                 if list then
                     for _, f in ipairs(list) do
                         if features[f] and fm.otf[f] then -- brr
@@ -1042,39 +1206,42 @@ function fonts.otf.copy_to_tfm(data)
         for k,v in pairs(data.map.map) do
             -- k = unicode, v = slot
             local d = data.glyphs[v]
-         -- if d then
             if d and (force or d.name) then
-                local t = { }
-                t.index       =   v
-                t.unicode     =   k
-                t.name        =   d.name           or ".notdef"
-                t.boundingbox =   d.boundingbox    or nil
-                t.width       =   d.width          or 0
-                t.height      =   d.boundingbox[4] or 0
-                t.depth       = - d.boundingbox[2] or 0
-                t.class       =   d.class
+                local t = {
+                    index       =   v,
+                    unicode     =   k,
+                    name        =   d.name           or ".notdef",
+                    boundingbox =   d.boundingbox    or nil,
+                    width       =   d.width          or 0,
+                    height      =   d.boundingbox[4] or 0,
+                    depth       = - d.boundingbox[2] or 0,
+                    class       =   d.class,
+                }
                 if d.class == "mark" then
-                    t.width = -t.width
+                    t.width = - t.width
                 end
                 characters[k] =   t
             end
         end
+        local designsize = data.designsize or data.design_size or 100
+        if designsize == 0 then
+            designsize = 100
+        end
+        local spaceunits = 500
         tfm.units              = data.units_per_em or 1000
-        -- we need a runtime lookup because of running from cdrom or zip
+        -- we need a runtime lookup because of running from cdrom or zip, brrr
         tfm.filename           = input.findbinfile(texmf.instance,data.luatex.filename,"") or data.luatex.filename
         tfm.fullname           = data.fullname or data.fontname
+        tfm.encodingbytes      = 2
         tfm.cidinfo            = data.cidinfo
-    --  if not tfm.cidinfo.registry then tfm.cidinfo.registry = "" end
         tfm.cidinfo.registry   = tfm.cidinfo.registry or ""
-        tfm.name               = file.removesuffix(file.basename(tfm.filename))
         tfm.type               = "real"
         tfm.stretch            = 0 -- stretch
         tfm.slant              = 0 -- slant
         tfm.direction          = 0
         tfm.boundarychar_label = 0
         tfm.boundarychar       = 65536
-        tfm.designsize         = ((data.designsize or 100)/10)*65536
-        local spaceunits       = 500
+        tfm.designsize         = (designsize/10)*65536
         tfm.spacer             = "500 units"
         data.isfixedpitch      = data.pfminfo and data.pfminfo.panose and data.pfminfo.panose["proportion"] == "Monospaced"
         data.charwidth         = nil
@@ -1084,19 +1251,25 @@ function fonts.otf.copy_to_tfm(data)
         if data.isfixedpitch then
             if data.glyphs[unicodes['space']] then
                 spaceunits, tfm.spacer = data.glyphs[unicodes['space']].width, "space"
-            elseif data.glyphs[unicodes['emdash']] then
+            end
+            if not spaceunits and data.glyphs[unicodes['emdash']] then
                 spaceunits, tfm.spacer = data.glyphs[unicodes['emdash']].width, "emdash"
-            elseif data.charwidth then
+            end
+            if not spaceunits and data.charwidth then
+                spaceunits, tfm.spacer = data.charwidth, "charwidth"
+            end
+        else
+            if data.glyphs[unicodes['space']] then
+                spaceunits, tfm.spacer = data.glyphs[unicodes['space']].width, "space"
+            end
+            if not spaceunits and data.glyphs[unicodes['emdash']] then
+                spaceunits, tfm.spacer = data.glyphs[unicodes['emdash']].width/2, "emdash/2"
+            end
+            if not spaceunits and data.charwidth then
                 spaceunits, tfm.spacer = data.charwidth, "charwidth"
             end
-        elseif data.glyphs[unicodes['space']] then
-            spaceunits, tfm.spacer = data.glyphs[unicodes['space']].width, "space"
-        elseif data.glyphs[unicodes['emdash']] then
-            spaceunits, tfm.spacer = data.glyphs[unicodes['emdash']].width/2, "emdash/2"
-        elseif data.charwidth then
-            spaceunits, tfm.spacer = data.charwidth, "charwidth"
         end
-        spaceunits = tonumber(spaceunits)
+        spaceunits = tonumber(spaceunits) or 500 -- brrr
         tfm.parameters[1] = 0          -- slant
         tfm.parameters[2] = spaceunits -- space
         tfm.parameters[3] = 500        -- space_stretch
@@ -1122,9 +1295,13 @@ function fonts.otf.copy_to_tfm(data)
         end
         if data.pfminfo and data.pfminfo.os2_xheight and data.pfminfo.os2_xheight > 0 then
             tfm.parameters[5] = data.pfminfo.os2_xheight
-        elseif data.glyphs[unicodes['x']] and data.glyphs[unicodes['x']].height then
-            tfm.parameters[5] = data.glyphs[unicodes['x']].height
+        else
+            local x = characters[unicodes['x']]
+            if x then
+                tfm.parameters[5] = x.height
+            end
         end
+        -- [6]
         return tfm
     else
         return nil
@@ -1137,8 +1314,19 @@ function fonts.tfm.read_from_open_type(specification)
         tfmtable.name = specification.name
         tfmtable.sub = specification.sub
         tfmtable = fonts.tfm.scale(tfmtable, specification.size)
-        tfmtable.file = file.basename(specification.filename)
-        tfmtable.format = specification.format
+     -- here we resolve the name; file can be relocated, so this info is not in the cache
+        local otfdata = tfmtable.shared.otfdata
+        local filename = otfdata and otfdata.luatex and otfdata.luatex.filename
+        if not filename then
+            -- try to locate anyway and set otfdata.luatex.filename
+        end
+        if filename then
+            tfmtable.encodingbytes = 2
+            tfmtable.filename = input.findbinfile(texmf.instance,filename,"") or filename
+            tfmtable.fullname = otfdata.fullname or otfdata.fontname
+            tfmtable.format = specification.format
+        end
+        fonts.logger.save(tfmtable,file.extname(specification.filename),specification)
     end
     return tfmtable
 end
@@ -1148,27 +1336,52 @@ end
 fonts.otf.default_language = 'latn'
 fonts.otf.default_script   = 'dflt'
 
-function fonts.otf.valid_feature(otfdata,language,script) -- return hash is faster
+--~ function fonts.otf.valid_feature(otfdata,language,script) -- return hash is faster
+--~     local language = language or fonts.otf.default_language
+--~     local script   = script   or fonts.otf.default_script
+--~     if not (script and language) then
+--~         return boolean.alwaystrue
+--~     else
+--~         language = string.padd(language:lower(),4)
+--~         script   = string.padd(script:lower  (),4)
+--~         local t = { }
+--~         for k,v in pairs(otfdata.luatex.subtables) do
+--~             local vv = v[script]
+--~             if vv and vv[language] then
+--~                 t[k] = vv[language].valid
+--~             end
+--~         end
+--~         local always = otfdata.luatex.always_valid -- for the moment not per feature
+--~         --~         return function(kind,tag) -- is the kind test needed
+--~         --~             return always[tag] or (kind and t[kind] and t[kind][tag])
+--~         --~         end
+--~         return function(kind,tag) -- better inline
+--~             return always[tag] or (t[kind] and t[kind][tag])
+--~         end
+--~     end
+--~ end
+
+function fonts.otf.valid_feature(otfdata,language,script,feature) -- return hash is faster
     local language = language or fonts.otf.default_language
     local script   = script   or fonts.otf.default_script
     if not (script and language) then
-        return boolean.alwaystrue
+        return true
     else
         language = string.padd(language:lower(),4)
         script   = string.padd(script:lower  (),4)
-        local t = { }
-        for k,v in pairs(otfdata.luatex.subtables) do
-            local vv = v[script]
-            if vv and vv[language] then
-                t[k] = vv[language].valid
-            end
-        end
-        local always = otfdata.luatex.always_valid -- for the moment not per feature
-        return function(kind,tag) -- is the kind test needed
-            return always[tag] or kind and t[kind] and t[kind][tag]
-        end
+--~         local t = { }
+--~         for k,v in pairs(otfdata.luatex.subtables) do
+--~             local vv = v[script]
+--~             if vv and vv[language] then
+--~                 t[k] = vv[language].valid
+--~             end
+--~         end
+        local ft = otfdata.luatex.subtables[feature]
+        local st = ft[script]
+        return false, otfdata.luatex.always_valid, st and st[language] and st[language].valid
     end
 end
+
 function fonts.otf.some_valid_feature(otfdata,language,script,kind)
     local language = language or fonts.otf.default_language
     local script   = script   or fonts.otf.default_script
@@ -1464,13 +1677,21 @@ do
                 end
             end
         else -- check if this valid is still ok
-            local valid = fonts.otf.valid_feature(otfdata,tfmdata.language,tfmdata.script)
+--~             local valid = fonts.otf.valid_feature(otfdata,tfmdata.language,tfmdata.script)
+            local forced, always, okay = fonts.otf.valid_feature(otfdata,tfmdata.language,tfmdata.script,kind)
             for _,o in pairs(otfdata.glyphs) do
                 if o.lookups then
-                    for lookup, ps in pairs(o.lookups) do
-                        if valid(kind,lookup) then
-                            collect(lookup,o,ps)
-                        end
+--~                     for lookup, ps in pairs(o.lookups) do
+--~                         if valid(kind,lookup) then
+--~                             collect(lookup,o,ps)
+--~                         end
+--~                     end
+                    if forced then
+                        for lookup, ps in pairs(o.lookups) do                                        collect(lookup,o,ps)     end
+                    elseif okay then
+                        for lookup, ps in pairs(o.lookups) do if always[lookup] or okay[lookup] then collect(lookup,o,ps) end end
+                    else
+                        for lookup, ps in pairs(o.lookups) do if always[lookup]                 then collect(lookup,o,ps) end end
                     end
                 end
             end
@@ -1818,7 +2039,7 @@ end
 
 do
 
-    prepare = fonts.otf.features.prepare.feature
+    local prepare = fonts.otf.features.prepare.feature
 
     function fonts.initializers.node.otf.afrc(tfm,value) return prepare(tfm,'afrc',value) end
     function fonts.initializers.node.otf.akhn(tfm,value) return prepare(tfm,'akhn',value) end
@@ -1984,7 +2205,7 @@ do
 
     -- todo: components / else subtype 0 / maybe we should be able to force this
 
-    function toligature(start,stop,char,markflag)
+    local function toligature(start,stop,char,markflag)
         if start ~= stop then
             local deletemarks = markflag ~= "mark"
             start.subtype = 1
@@ -2356,7 +2577,7 @@ do
         return start, done
     end
 
-    chainprocs = { } -- we can probably optimize this because they're all internal lookups
+    local chainprocs = { } -- we can probably optimize this because they're all internal lookups
 
     -- For the moment we save each looked up glyph in the sequence, which is ok because
     -- each lookup in the chain has its own sequence. This saves memory. Only ligatures
@@ -3191,6 +3412,9 @@ end
 do
 
     local glyph           = node.id('glyph')
+    local glue            = node.id('glue')
+    local penalty         = node.id('penalty')
+
     local fontdata        = fonts.tfm.id
     local set_attribute   = node.set_attribute
     local has_attribute   = node.has_attribute
@@ -3236,6 +3460,8 @@ do
 
     -- arab / todo: 0640 tadwil
 
+    -- this info eventually will go into char-def
+
     local isol = {
         [0x0621] = true,
     }
@@ -3377,6 +3603,122 @@ do
         return head, done
     end
 
+    -- han (chinese) (unfinished)
+
+    -- this info eventually will go into char-def
+
+    local no_init = table.tohash {
+        0x3001, 0x3002, 0x2014, 0xFF5E, 0x2019, 0x201D, 0x3015, 0x3009,
+        0x300B, 0x300D, 0x300F, 0x3017, 0x3011, 0x2237, 0x00B0, 0x2032,
+        0x2033, 0xFF01, 0xFF02, 0xFF07, 0xFF09, 0xFF0C, 0xFF0E, 0xFF1A,
+        0xFF1B, 0xFF1F, 0xFF3D, 0xFF5D,
+    }
+
+    local no_fina = table.tohash {
+        0x2018, 0x201C, 0x3014, 0x3008, 0x300A, 0x300C, 0x300E, 0x3016,
+        0x3010, 0xFF08, 0xFF3B, 0xFF40, 0xFF5B,
+    }
+
+    local no_xxxx = table.tohash {
+        0x00B7, 0x00A8, 0x2026, 0xFF1E,
+    }
+
+    local allowbreak = node.new("penalty")
+    local nobreak    = node.new("penalty")
+--~ local zeroskip   = node.new("glue")
+
+    allowbreak.penalty    =   -100
+    nobreak.penalty       =  10000
+--~ zeroskip.subtype      =      0
+--~ zeroskip.spec         = node.new('glue_spec')
+--~ zeroskip.spec.width   =      0
+--~ zeroskip.spec.stretch =      0
+--~ zeroskip.spec.shrink  =      0
+
+    -- an alternative is to deal with it in the line breaker
+
+    local function is_han_character(char)
+        return
+            (char>=0x04E00 and char<=0x09FFF) or
+            (char>=0x03400 and char<=0x04DFF) or
+            (char>=0x20000 and char<=0x2A6DF) or
+            (char>=0x0F900 and char<=0x0FAFF) or
+            (char>=0x2F800 and char<=0x2FA1F)
+    end
+
+    function fonts.analyzers.methods.hang(head,font) -- maybe make a special version with no trace
+        local characters = fontdata[font].characters
+        local current, done = head, false
+        local trace = fonts.color.trace
+        local prevchinese = false
+        local function unskip()
+            if prevchinese then
+                local temp = current.prev
+                while temp and temp.id == glue do
+                    head, temp = nodes.delete(head,temp)
+                    if temp then temp = temp.prev end
+                end
+            end
+        end
+        while current do
+            if current.id == glyph then
+                if current.font == font then
+                    local char = current.char
+                    if no_init[char] then
+                        done = true
+                        set_attribute(current,state,1) -- before
+                        if trace then fcs(current,"font:init") end -- we need nice names
+                        if current.prev then
+                            unskip()
+                            head, current = node.insert_before(head,current,node.copy(nobreak))
+                            current = current.next
+                        end
+                        prevchinese = true
+                    elseif no_fina[char] then
+                        done = true
+                        set_attribute(current,state,2) -- after
+                        if trace then fcs(current,"font:fina") end -- we need nice names
+                        if current.prev then
+                            unskip()
+                            head, current = node.insert_before(head,current,node.copy(allowbreak))
+                            current = current.next
+                        end
+                        if current and current.next then
+                            head, current = node.insert_after(head,current,node.copy(nobreak))
+                            current = current.next
+                        end
+                        prevchinese = true
+                    elseif no_xxxx[char] then
+                        done = true
+                        set_attribute(current,state,3) -- xxxx
+                        if trace then fcs(current,"font:medi") end -- we need nice names
+                        if current.prev then -- todo
+                            unskip()
+                            head, current = node.insert_before(head,current,node.copy(allowbreak))
+                            current = current.next
+                        end
+                        prevchinese = true
+                    elseif is_han_character(char) then
+                        if current.prev and current.prev.id ~= penalty then
+                            unskip()
+                            head, current = node.insert_before(head,current,node.copy(allowbreak))
+                            current = current.next
+                        end
+                        prevchinese = true
+                    else
+                        prevchinese = false
+                    end
+                else
+                    prevchinese = false
+                end
+            end
+            if current then
+                current = current.next
+            end
+        end
+        return head, done
+    end
+
 end
 
 -- experimental and will probably change
diff --git a/tex/context/base/font-syn.lua b/tex/context/base/font-syn.lua
index 77898a4a2..fdd99e6f2 100644
--- a/tex/context/base/font-syn.lua
+++ b/tex/context/base/font-syn.lua
@@ -54,6 +54,10 @@ function fonts.names.filters.afm(name)
     end
 end
 
+function fonts.names.filters.pfb(name)
+    return fontforge.info(name)
+end
+
 --[[ldx--
 The scanner loops over the filters using the information stored in
 the file databases. Watch how we check not only for the names, but also
@@ -61,7 +65,7 @@ for combination with the weight of a font.
 --ldx]]--
 
 fonts.names.filters.list = {
-    "otf", "ttf", "ttc", "afm"
+    "otf", "ttf", "ttc", "afm" -- pfb is quite messy, too many messages, maybe broken
 }
 
 fonts.names.filters.fixes = {
diff --git a/tex/context/base/font-tfm.lua b/tex/context/base/font-tfm.lua
index 21a02a67b..103f71c12 100644
--- a/tex/context/base/font-tfm.lua
+++ b/tex/context/base/font-tfm.lua
@@ -23,6 +23,35 @@ fonts.triggers   = fonts.triggers   or { } -- brrr
 supplied by .
 --ldx]]--
 
+fonts.tfm.resolve_vf = true -- false
+
+function fonts.tfm.enhance(tfmdata,specification)
+    local name, size = specification.name, specification.size
+    local encoding, filename = name:match("^(.-)%-(.*)$") -- context: encoding-name.*
+    if filename and encoding and fonts.enc.known[encoding] then
+        local data = fonts.enc.load(encoding)
+        if data then
+            local characters = tfmdata.characters
+            tfmdata.encoding = encoding
+            local vector = data.vector
+            for k, v in pairs(characters) do
+                v.name = vector[k]
+                v.index = k
+            end
+            for k,v in pairs(data.unicodes) do
+                if k ~= v then
+                --  if not characters[k] then
+                        if fonts.trace then
+                            logs.report("define font",string.format("mapping %s onto %s",k,v))
+                        end
+                        characters[k] = characters[v]
+                --  end
+                end
+            end
+        end
+    end
+end
+
 function fonts.tfm.read_from_tfm(specification)
     local fname, tfmdata = specification.filename, nil
     if fname then
@@ -41,32 +70,24 @@ function fonts.tfm.read_from_tfm(specification)
         if fonts.trace then
             logs.report("define font",string.format("loading tfm file %s at size %s",fname,specification.size))
         end
-        tfmdata = font.read_tfm(fname,specification.size)
+        tfmdata = font.read_tfm(fname,specification.size) -- not cached, fast enough
         if tfmdata then
---~ fonts.logger.save(tfmdata,'tfm',specification)
---~ if false then
-            fname = input.findbinfile(texmf.instance, specification.name, 'ovf')
-            if fname and fname ~= "" then
-callback.register('find_vf_file', nil)
-                local vfdata = font.read_vf(fname,specification.size)
-                if vfdata then
-                    local chars = tfmdata.characters
-                    for k,v in ipairs(vfdata.characters) do
-                        chars[k].commands = v.commands
-                    end
---~                     tfmdata.type = 'virtual'
-                    local fnts = vfdata.fonts
-                    for k,v in ipairs(fnts) do
-                        local dummy, id = fonts.tfm.read_and_define(v.name,v.size)
-                        fnts[k].id = id
-                        if fonts.trace then
-                            logs.report("define font",string.format("vf file %s needs tfm file %s (id %s)", fname, v.name, id))
+            if fonts.tfm.resolve_vf then
+                fonts.logger.save(tfmdata,file.extname(fname),specification) -- strange, why here
+                fname = input.findbinfile(texmf.instance, specification.name, 'ovf')
+                if fname and fname ~= "" then
+                    local vfdata = font.read_vf(fname,specification.size) -- not cached, fast enough
+                    if vfdata then
+                        local chars = tfmdata.characters
+                        for k,v in pairs(vfdata.characters) do -- no ipairs, can have holes
+                            chars[k].commands = v.commands
                         end
+                        tfmdata.type = 'virtual'
+                        tfmdata.fonts = vfdata.fonts
                     end
-                    tfmdata.fonts = fnts
                 end
             end
---~ end
+            fonts.tfm.enhance(tfmdata,specification)
         end
     else
         if fonts.trace then
@@ -123,13 +144,13 @@ function fonts.tfm.scale(tfmtable, scaledpoints)
     local tc = t.characters
     for k,v in pairs(tfmtable.characters) do
         local chr = {
-            unicode     = v.unicode,
-            name        = v.name,
-            index       = v.index or k,
-            width       = scale(v.width , delta),
-            height      = scale(v.height, delta),
-            depth       = scale(v.depth , delta),
-            class       = v.class
+            unicode = v.unicode,
+            name    = v.name,
+            index   = v.index or k,
+            width   = scale(v.width , delta),
+            height  = scale(v.height, delta),
+            depth   = scale(v.depth , delta),
+            class   = v.class
         }
         local b = v.boundingbox -- maybe faster to have llx etc not in table
         if b then
@@ -158,13 +179,15 @@ function fonts.tfm.scale(tfmtable, scaledpoints)
             tp[k] = scale(v,delta)
         end
     end
---~     t.encodingbytes = tfmtable.encodingbytes or 2
+--~     t.encodingbytes = tfmtable.encodingbytes or 1
+--~     t.filename      = t.filename or tfmtable.filename or tfmtable.file or nil
+--~     t.fullname      = t.fullname or tfmtable.fullname or tfmtable.full or nil
+--~     t.name          = t.name or tfmtable.name or nil
     t.size          = scaledpoints
     t.italicangle   = tfmtable.italicangle
     t.ascender      = scale(tfmtable.ascender  or 0,delta)
     t.descender     = scale(tfmtable.descender or 0,delta)
-    -- new / some data will move here
-    t.shared = tfmtable.shared or { }
+    t.shared        = tfmtable.shared or { }
     if t.unique then
         t.unique = table.fastcopy(tfmtable.unique)
     else
@@ -180,8 +203,11 @@ we now have several readers it may be handy to know what reader is
 used for which font.
 --ldx]]--
 
-function fonts.logger.save(tfmtable,source,specification)
+function fonts.logger.save(tfmtable,source,specification) -- save file name in spec here ! ! ! ! ! !
     if tfmtable and specification and specification.specification then
+        if fonts.trace then
+            logs.report("define font",string.format("registering %s as %s",specification.name,source))
+        end
         specification.source = source
         fonts.loaded[specification.specification] = specification
         fonts.used[specification.name] = source
diff --git a/tex/context/base/l-aux.lua b/tex/context/base/l-aux.lua
index 59ff42539..f5aa7e67e 100644
--- a/tex/context/base/l-aux.lua
+++ b/tex/context/base/l-aux.lua
@@ -10,7 +10,7 @@ do
 
     hash = { }
 
-    function set(key,value)
+    local function set(key,value)
         hash[key] = value
     end
 
@@ -34,6 +34,50 @@ do
         return hash
     end
 
+    local pattern = lpeg.Ct((space * value * comma)^1)
+
+    function aux.settings_to_array(str)
+        return lpeg.match(pattern,str)
+    end
+
+end
+
+--~ do
+--~     str = "a=1, b=2, c=3, d={abc}"
+
+--~     for k,v in pairs(aux.settings_to_hash (str)) do print(k,v) end
+--~     for k,v in pairs(aux.settings_to_array(str)) do print(k,v) end
+--~ end
+
+function aux.hash_to_string(h,separator,yes,no,strict)
+    if h then
+        local t = { }
+        for _,k in ipairs(table.sortedkeys(h)) do
+            local v = h[k]
+            if type(v) == "boolean" then
+                if yes and no then
+                    if v then
+                        t[#t+1] = k .. '=' .. yes
+                    elseif not strict then
+                        t[#t+1] = k .. '=' .. no
+                    end
+                elseif v or not strict then
+                    t[#t+1] = k .. '=' .. tostring(v)
+                end
+            else
+                t[#t+1] = k .. '=' .. v
+            end
+        end
+        return table.concat(t,separator or ",")
+    else
+        return ""
+    end
 end
 
---~ print(table.serialize(aux.settings_to_hash("aaa=bbb, ccc={d,d,d}")))
+function aux.array_to_string(a,separator)
+    if a then
+        return table.concat(a,separator or ",")
+    else
+        return ""
+    end
+end
diff --git a/tex/context/base/l-boolean.lua b/tex/context/base/l-boolean.lua
index 128e1d069..e1efa4ad4 100644
--- a/tex/context/base/l-boolean.lua
+++ b/tex/context/base/l-boolean.lua
@@ -16,6 +16,8 @@ function toboolean(str)
         return str == "true" or str == "yes" or str == "on" or str == "1"
     elseif type(str) == "number" then
         return tonumber(str) ~= 0
+    elseif type(str) == "nil" then
+        return false
     else
         return str
     end
diff --git a/tex/context/base/l-file.lua b/tex/context/base/l-file.lua
index 67bf44b5a..496346b33 100644
--- a/tex/context/base/l-file.lua
+++ b/tex/context/base/l-file.lua
@@ -101,7 +101,7 @@ end
 --~ print("a/a"            .. " == " .. file.collapse_path("a/b/c/../../a"))
 
 function file.collapse_path(str)
-    local ok = false
+    local ok, n = false, 0
     while not ok do
         ok = true
         str, n = str:gsub("[^%./]+/%.%./", function(s)
diff --git a/tex/context/base/l-string.lua b/tex/context/base/l-string.lua
index 9b594ff8a..9940de9b2 100644
--- a/tex/context/base/l-string.lua
+++ b/tex/context/base/l-string.lua
@@ -305,3 +305,15 @@ end
 --~ print(is_number("+0.1"))
 --~ print(is_number("-.1"))
 --~ print(is_number("+.1"))
+
+function string:split_settings() -- no {} handling, see l-aux for lpeg variant
+    if self:find("=") then
+        local t = { }
+        for k,v in self:gmatch("(%a+)=([^%,]*)") do
+            t[k] = v
+        end
+        return t
+    else
+        return nil
+    end
+end
diff --git a/tex/context/base/l-table.lua b/tex/context/base/l-table.lua
index 79d87139b..f68e29d23 100644
--- a/tex/context/base/l-table.lua
+++ b/tex/context/base/l-table.lua
@@ -65,6 +65,24 @@ function table.prepend(t, list)
     end
 end
 
+function table.merge(t, ...)
+    for _, list in ipairs({...}) do
+        for k,v in pairs(list) do
+            t[k] = v
+        end
+    end
+end
+
+function table.merged(...)
+    local tmp = { }
+    for _, list in ipairs({...}) do
+        for k,v in pairs(list) do
+            tmp[k] = v
+        end
+    end
+    return tmp
+end
+
 if not table.fastcopy then
 
     function table.fastcopy(old) -- fast one
@@ -486,6 +504,24 @@ function table.are_equal(a,b,n,m)
     end
 end
 
+function table.compact(t)
+    if t then
+        for k,v in pairs(t) do
+            if not next(v) then
+                t[k] = nil
+            end
+        end
+    end
+end
+
+function table.tohash(t)
+    local h = { }
+    for _, v in pairs(t) do -- no ipairs here
+        h[v] = true
+    end
+    return h
+end
+
 --~ function table.are_equal(a,b)
 --~     return table.serialize(a) == table.serialize(b)
 --~ end
diff --git a/tex/context/base/l-tex.lua b/tex/context/base/l-tex.lua
index 3d87fe6e3..89925f602 100644
--- a/tex/context/base/l-tex.lua
+++ b/tex/context/base/l-tex.lua
@@ -89,7 +89,7 @@ end
 
 if lua then do
 
-    delayed = { } -- could also be done with closures
+    local delayed = { } -- could also be done with closures
 
     function lua.delay(f)
         delayed[#delayed+1] = f
diff --git a/tex/context/base/l-utils.lua b/tex/context/base/l-utils.lua
index 02efebe08..c4676a83d 100644
--- a/tex/context/base/l-utils.lua
+++ b/tex/context/base/l-utils.lua
@@ -113,10 +113,15 @@ function utils.merger.selfclean(name)
     )
 end
 
+utils.lua.compile_strip = true
+
 function utils.lua.compile(luafile, lucfile)
  -- utils.report("compiling",luafile,"into",lucfile)
     os.remove(lucfile)
-    local command = "-s -o " .. string.quote(lucfile) .. " " .. string.quote(luafile)
+    local command = "-o " .. string.quote(lucfile) .. " " .. string.quote(luafile)
+    if utils.lua.compile_strip then
+        command = "-s " .. command
+    end
     if os.execute("texluac " .. command) == 0 then
         return true
     elseif os.execute("luac " .. command) == 0 then
diff --git a/tex/context/base/l-xml.lua b/tex/context/base/l-xml.lua
new file mode 100644
index 000000000..b60f521d2
--- /dev/null
+++ b/tex/context/base/l-xml.lua
@@ -0,0 +1,887 @@
+if not modules then modules = { } end modules ['l-xml'] = {
+    version   = 1.001,
+    comment   = "this module is the basis for the lxml-* ones",
+    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+    copyright = "PRAGMA ADE / ConTeXt Development Team",
+    license   = "see context related readme files"
+}
+
+--[[ldx--
+Tthe parser used here is inspired by the variant discussed in the lua book, but
+handles comment and processing instructions, has a different structure, provides
+parent access; a first version used different tricky but was less optimized to we
+went this route.
+
+Expecially the lpath code is experimental, we will support some of xpath, but
+only things that make sense for us; as compensation it is possible to hook in your
+own functions. Apart from preprocessing content for  we also need
+this module for process management, like handling  and 
+files.
+
+
+a/b/c /*/c (todo: a/b/(pattern)/d)
+a/b/c/first() a/b/c/last() a/b/c/index(n) a/b/c/index(-n)
+a/b/c/text() a/b/c/text(1) a/b/c/text(-1) a/b/c/text(n)
+
+
+Beware, the interface may change. For instance at, ns, tg, dt may get more
+verbose names. Once the code is stable we will also removee some tracing and
+optimize the code.
+--ldx]]--
+
+xml = xml or { }
+tex = tex or { }
+
+do
+
+    -- The dreadful doctype comes in many disguises:
+    --
+    -- 
+    -- 
+    -- 
+    -- 
+    -- 
+    -- 
+
+    local doctype_patterns = {
+        "",
+        "",
+        "",
+        "",
+        "",
+        ""
+    }
+
+    -- We assume no "<" which is the lunatic part of the xml spec
+    -- especially since ">" is permitted; otherwise we need a char
+    -- by char parser ... more something for later ... normally
+    -- entities will be used anyway.
+
+    local function prepare(data) -- todo: option to delete doctype stuff, comment, etc
+        -- pack (for backward compatibility)
+        if type(data) == "table" then
+            data = table.concat(data,"")
+        end
+        -- CDATA
+        data = data:gsub("<%!%[CDATA%[(.-)%]%]>", function(txt)
+            return string.format("<@cd@>%s@cd@>",txt:to_hex())
+        end)
+        -- DOCTYPE
+        data = data:gsub("^(.-)(<[^%!%?])", function(a,b)
+            if a:find("%s@dd@>",d:to_hex()) end)
+                end
+            end
+            return a .. b
+        end,1)
+        -- comment / does not catch doctype
+        data = data:gsub("<%!%-%-(.-)%-%->", function(txt)
+            return string.format("<@cm@>%s@cm@>",txt:to_hex())
+        end)
+        -- processing instructions
+        data = data:gsub("<%?(.-)%?>", function(txt)
+            return string.format("<@pi@>%s@pi@>",txt:to_hex())
+        end)
+        return data
+    end
+
+    local function split(s)
+        local t = {}
+        for namespace, tag, _,val in s:gmatch("(%w-):?(%w+)=([\"\'])(.-)%3") do
+            if namespace == "" then
+                t[tag] = val
+            else
+                t[tag] = val
+            end
+        end
+        return t
+    end
+
+    function xml.convert(data,no_root,collapse)
+        local data = prepare(data)
+        local stack, top = {}, {}
+        local i, j, errorstr = 1, 1, nil
+        stack[#stack+1] = top
+        top.dt = { }
+        local dt = top.dt
+        while true do
+            local ni, first, attributes, last, fulltag
+            ni, j, first, fulltag, attributes, last = data:find("<(/-)([^%s%>/]+)%s*([^>]-)%s*(/-)>", j)
+            if not ni then break end
+            local namespace, tag = fulltag:match("^(.-):(.+)$")
+            if not tag then
+                namespace, tag = "", fulltag
+            end
+            local text = data:sub(i, ni-1)
+            if (text == "") or (collapse and text:find("^%s*$")) then
+                -- no need for empty text nodes, beware, also packs x y z
+                -- so is not that useful unless used with empty elements
+            else
+                dt[#dt+1] = text
+            end
+            if first == "/" then
+                -- end tag
+                local toclose = table.remove(stack)  -- remove top
+                top = stack[#stack]
+                if #stack < 1 then
+                    errorstr = "nothing to close with " .. tag
+                    break
+                end
+                if toclose.tg ~= tag then
+                    errorstr = "unable to close " .. toclose.tg .. " with " .. tag
+                    break
+                end
+                dt= top.dt
+                dt[#dt+1] = toclose
+            elseif last == "/" then
+                -- empty element tag
+                if attributes == "" then
+                    dt[#dt+1] = { ns=namespace, tg=tag }
+                else
+                    dt[#dt+1] = { ns=namespace, tg=tag, at=split(attributes) }
+                end
+                setmetatable(top, { __tostring = xml.text } )
+                dt[#dt].__p__ = top
+            else
+                -- begin tag
+                if attributes == "" then
+                    top = { ns=namespace, tg=tag, dt = { } }
+                else
+                    top = { ns=namespace, tg=tag, at=split(attributes), dt = { } }
+                end
+                top.__p__ = stack[#stack]
+                setmetatable(top, { __tostring = xml.text } )
+                dt = top.dt
+                stack[#stack+1] = top
+            end
+            i = j + 1
+        end
+        if not errorstr then
+            local text = data:sub(i)
+            if dt and not text:find("^%s*$") then
+                dt[#dt+1] = text
+            end
+            if #stack > 1 then
+                errorstr = "unclosed " .. stack[#stack].tg
+            end
+        end
+        if errorstr then
+        --  stack = { [1] = { tg = "error", dt = { [1] = errorstr } } }
+            stack = { { tg = "error", dt = { errorstr } } }
+            setmetatable(stack, { __tostring = xml.text } )
+        end
+        if no_root then
+            return stack[1]
+        else
+            local t = { tg = '@rt@', dt = stack[1].dt }
+            setmetatable(t, { __tostring = xml.text } )
+            return t
+        end
+    end
+
+end
+
+function xml.load(filename,collapse)
+    if type(filename) == "string" then
+        local root, f = { }, io.open(filename,'r') -- no longer 'rb'
+        if f then
+            root = xml.convert(f:read("*all"),false,collapse)
+            f:close()
+        end
+        return root
+    else
+        return xml.convert(filename:read("*all"),false,collapse)
+    end
+end
+
+function xml.toxml(data,collapse)
+    local t = { xml.convert(data,true,collapse) }
+    if #t > 1 then
+        return t
+    else
+        return t[1]
+    end
+end
+
+function xml.serialize(e, handle, textconverter, attributeconverter) -- check if string:format is faster
+    local format = string.format
+    handle = handle or (tex and tex.sprint) or io.write
+    local function flush(before,e,after)
+        handle(before)
+        for _,ee in ipairs(e.dt) do -- i added, todo iloop
+            xml.serialize(ee,handle,string.from_hex)
+        end
+        handle(after)
+    end
+    if e then
+        if e.tg then
+            if e.tg == "@pi@" then
+                flush("",e,"?>")
+            elseif e.tg == "@cm@" then
+                flush("")
+            elseif e.tg == "@cd@" then
+                flush("")
+            elseif e.tg == "@dd@" then
+                flush("")
+            elseif e.tg == "@rt@" then
+                xml.serialize(e.dt,handle,textconverter,attributeconverter)
+            else
+                if e.ns ~= "" then
+                    handle(format("<%s:%s",e.ns,e.tg))
+                else
+                    handle(format("<%s",e.tg))
+                end
+                if e.at then
+                    if attributeconverter then
+                        for k,v in pairs(e.at) do
+                            handle(format(' %s=%q',k,attributeconverter(v)))
+                        end
+                    else
+                        for k,v in pairs(e.at) do
+                            handle(format(' %s=%q',k,v))
+                        end
+                    end
+                end
+                if e.dt then
+                    handle(">")
+                    for k,ee in ipairs(e.dt) do -- i added, for i=1,n is faster
+                        xml.serialize(ee,handle,textconverter,attributeconverter)
+                    end
+                    handle(format("%s>",e.tg))
+                else
+                    handle("/>")
+                end
+            end
+        elseif type(e) == "string" then
+            if textconverter then
+                handle(textconverter(e))
+            else
+                handle(e)
+            end
+        else
+            for _,ee in ipairs(e) do -- i added
+                xml.serialize(ee,handle,textconverter,attributeconverter)
+            end
+        end
+    end
+end
+
+function xml.string(e,handle)
+    if e.tg then
+        if e.dt then
+            for _,ee in ipairs(e.dt) do -- i added
+                xml.string(ee,handle)
+            end
+        end
+    else
+        handle(e)
+    end
+end
+
+function xml.save(root,name)
+    local f = io.open(name,"w")
+    if f then
+        xml.serialize(root,function(s) f:write(s) end)
+        f:close()
+    end
+end
+
+function xml.stringify(root)
+    local result = { }
+    xml.serialize(root,function(s) result[#result+1] = s end)
+    return table.concat(result,"")
+end
+
+xml.tostring = xml.stringify
+
+function xml.stringify_text(root) -- no root element
+    if root and root.dt then
+        return xml.stringify(root)
+    else
+        return ""
+    end
+end
+
+function xml.text(dt) -- no root element
+    if dt then
+        return xml.stringify(dt)
+    else
+        return ""
+    end
+end
+
+function xml.body(t) -- removes initial pi
+    if t and t.dt and t.tg == "@rt@" then
+        for k,v in ipairs(t.dt) do
+            if type(v) == "table" and v.tg ~= "@pi@" then
+                return v
+            end
+        end
+    end
+    return t
+end
+
+-- call: e[k] = xml.empty() or xml.empty(e,k)
+
+function xml.empty(e,k) -- erases an element but keeps the table intact
+    if e and k then
+        e[k] = ""
+        return e[k]
+    else
+        return ""
+    end
+end
+
+-- call: e[k] = xml.assign(t) or xml.assign(e,k,t)
+
+function xml.assign(e,k,t) -- assigns xml tree / more testing will be done
+    if e and k then
+        if type(t) == "table" then
+            e[k] = xml.body(t)
+        else
+            e[k] = t -- no parsing
+        end
+        return e[k]
+    else
+        return xml.body(t)
+    end
+end
+
+-- 0=nomatch 1=match 2=wildcard 3=ancestor
+
+-- "tag"
+-- "tag1/tag2/tag3"
+-- "*/tag1/tag2/tag3"
+-- "/tag1/tag2/tag3"
+-- "/tag1/tag2|tag3"
+-- "tag[@att='value']
+-- "tag1|tag2[@att='value']
+
+xml.trace_lpath  = false
+
+function xml.tag(e)
+    return e.tg or ""
+end
+
+function xml.att(e,a)
+    return (e.at and e.at[a]) or ""
+end
+
+xml.attribute = xml.att
+
+do
+
+    local cache = { }
+
+    local function fault   ( ) return 0 end
+    local function wildcard( ) return 2 end
+    local function result  (b) if b then return 1 else return 0 end end
+
+    -- we can avoid functions: m[i] = number|function
+
+    function xml.lpath(str) --maybe @rt@ special
+        str = str or "*"
+        local m = cache[str]
+        if not m then
+            -- todo: text()
+            if not str then
+                if xml.trace_lpath then print("lpath", "no string", "wildcard") end
+                m = {
+                    function(e)
+                        if xml.trace_lpath then print(2, 1, "wildcard", e.tg) end
+                        return 2
+                    end
+                }
+            elseif type(str) == "table" then
+                if xml.trace_lpath then print("lpath", "table" , "inherit") end
+                m = str
+            else
+                if str == "" or str == "*" then
+                    if xml.trace_lpath then print("lpath", "empty or *", "wildcard") end
+                    m = {
+                        function(e)
+                            if xml.trace_lpath then print(2, 2, "wildcard", e.tg) end
+                            return 2
+                        end
+                    }
+                else
+                    m = { }
+                    if str:find("^/") then
+                        -- done in split
+                    else
+                        if xml.trace_lpath then print("lpath", "/", "wildcard") end
+                        m[#m+1] = function(e,i)
+                            if xml.trace_lpath then print(2, 3, "wildcard", e.tg) end
+                            return 2
+                        end
+                    end
+                    for v in str:gmatch("([^/]+)") do
+                        if v == "" or v == "*" then
+                          if #m > 0 then -- when not, then we get problems with root being second (after  (we could start at dt[2])
+                                if xml.trace_lpath then print("lpath", "empty or *", "wildcard") end
+                                m[#m+1] = function(e,i)
+                                    if xml.trace_lpath then print(2, 4, "wildcard", e.ns, e.tg) end
+                                    return 2
+                                end
+                          end
+                        elseif v == ".." then
+                            if xml.trace_lpath then print("lpath", "..", "ancestor") end
+                            m[#m+1] = function(e,i)
+                                if xml.trace_lpath then print(3, 5, "ancestor", e.__p__.tg) end
+                                return 3
+                            end
+                        else
+                            local n, a, t = v:match("^(.-)%[@(.-)=(.-)%]$")
+                            if n and a and t then
+                                local s = ""
+                                local ns, tg = n:match("^(.-):(.+)$")
+                                if tg then
+                                    s, n = ns, tg
+                                end
+                            --  t = t:gsub("^([\'\"])([^%1]*)%1$", "%2") -- todo
+                                t = t:gsub("^\'(.*)\'$", "%1") -- todo
+                                t = t:gsub("^\"(.*)\"$", "%1") -- todo
+                                if v:find("|") then
+                                    -- todo: ns ! ! ! ! ! ! ! ! !
+                                    local tt = n:split("|")
+                                    if xml.trace_lpath then print("lpath", "match", t, n) end
+                                    m[#m+1] = function(e,i)
+                                        for _,v in ipairs(tt) do -- i added, todo: iloop
+                                            if e.at and e.tg == v and e.at[a] == t then
+                                                if xml.trace_lpath then print(1, 6, "element ", v, "attribute", t) end
+                                                return 1
+                                            end
+                                        end
+                                        if xml.trace_lpath then print(1, 6, "no match") end
+                                        return 0
+                                    end
+                                else
+                                    if xml.trace_lpath then print("lpath", "match", t, n) end
+                                    m[#m+1] = function(e,i)
+                                        if e.at and e.ns == s and e.tg == n and e.at[a] == t then
+                                            if xml.trace_lpath then print(1, 7, "element ", n, "attribute", t) end
+                                            return 1
+                                        else
+                                            if xml.trace_lpath then print(1, 7, "no match") end
+                                            return 0
+                                        end
+                                    end
+                                end
+                            elseif v:find("|") then
+                                local tt = v:split("|")
+                                for k,v in ipairs(tt) do -- i added, iloop is faster
+                                    if xml.trace_lpath then print("lpath", "or match", v) end
+                                    local ns, tg = v:match("^(.-):(.+)$")
+                                    if not tg then
+                                        ns, tg = "", v
+                                    end
+                                    tt[k] = function(e,i)
+                                        if ns == e.ns and tg == e.tg then
+                                            if xml.trace_lpath then print(1, 8, "element ", ns, tg) end
+                                            return 1
+                                        else
+                                            if xml.trace_lpath then print(1, 8, "no match", ns, tg) end
+                                            return 0
+                                        end
+                                    end
+                                end
+                                m[#m+1] = function(e,i)
+                                    for _,v in ipairs(tt) do -- i added, iloop is faster
+                                        if v(e,i) then
+                                            return 1
+                                        end
+                                    end
+                                    return 0
+                                end
+                            else
+                                if xml.trace_lpath then print("lpath", "match", v) end
+                                local ns, tg = v:match("^(.-):(.+)$")
+                                if not tg then
+                                    ns, tg = "", v
+                                end
+                                m[#m+1] = function(e,i)
+                                    if ns == e.ns and tg == e.tg then
+                                        if xml.trace_lpath then print(1, 9, "element ", ns, tg) end
+                                        return 1
+                                    else
+                                        if xml.trace_lpath then print(1, 9, "no match", ns, tg) end
+                                        return 0
+                                    end
+                                end
+                            end
+                        end
+                    end
+                end
+                if xml.trace_lpath then print("lpath", "result", str, "size", #m) end
+            end
+            cache[str] = m
+        end
+        return m
+    end
+
+    function xml.traverse_tree(root,pattern,handle,reverse,index,wildcard)
+        if root and root.dt then
+            index = index or 1
+            local match = pattern[index] or wildcard
+--~             local prev = pattern[index-1] or fault -- better use the wildcard
+            local traverse = xml.traverse_tree
+            local rootdt = root.dt
+            local start, stop, step = 1, #rootdt, 1
+            if reverse and index == #pattern then
+                start, stop, step = stop, start, -1
+            end
+            for k=start,stop,step do
+                local e = rootdt[k]
+                if e.tg then
+                    local m = (type(match) == "function" and match(e,index)) or match
+                    if m == 1 then -- match
+                        if index < #pattern then
+                            if not traverse(e,pattern,handle,reverse,index+1) then return false end
+                        elseif handle(rootdt,k) then
+                            return false
+                        end
+                    elseif m == 2 then -- wildcard (not ok, now same as 3)
+                        if index < #pattern then
+                            if not traverse(e,pattern,handle,reverse,index+1,true) then return false end
+                        elseif handle(rootdt,k) then
+                            return false
+                        end
+                    elseif m == 3 then -- ancestor
+                        local ep = e.__p__
+                        if index < #pattern then
+                            if not traverse(ep,pattern,handle,reverse,index+1) then return false end
+                        elseif handle(rootdt,k) then
+                            return false
+                        end
+--~                     elseif prev(e,index) == 2 then -- wildcard
+--~                         if not traverse(e,pattern,handle,reverse,index) then return false end
+                    elseif wildcard then -- maybe two kind of wildcards: * ** //
+                        if not traverse(e,pattern,handle,reverse,index,wildcard) then return false end
+                    end
+                else
+                    local edt = e.dt
+                    if edt then
+                        -- todo ancester
+                        for kk=1,#edt do
+                            local ee = edt[kk]
+                            if match(ee,index) > 0 then
+                                if index < #pattern then
+                                    if not traverse(ee,pattern,handle,reverse,index+1) then return false end
+                                elseif handle(rootdt,k) then
+--~                                 elseif handle(edt,kk) then
+                                    return false
+                                end
+                            elseif prev(ee) == 2 then
+                                if not traverse(ee,pattern,handle,reverse,index) then return false end
+                            end
+                        end
+                    end
+                end
+            end
+        end
+        return true
+    end
+
+    local traverse, lpath, convert = xml.traverse_tree, xml.lpath, xml.convert
+
+    xml.filters = { }
+
+    function xml.filters.default(root,pattern)
+        local ee, kk
+        traverse(root, lpath(pattern), function(e,k) ee,kk = e,k return true end)
+        return ee and ee[kk], ee, kk
+    end
+    function xml.filters.reverse(root,pattern)
+        local ee, kk
+        traverse(root, lpath(pattern), function(e,k) ee,kk = e,k return true end,'reverse')
+        return ee and ee[kk], ee, kk
+    end
+    function xml.filters.count(root, pattern)
+        local n = 0
+        traverse(root, lpath(pattern), function(e,k) n = n + 1 end)
+        return n
+    end
+    function xml.filters.first(root,pattern)
+        local ee, kk
+        traverse(root, lpath(pattern), function(e,k) ee,kk = e,k return true end)
+        return ee and ee[kk], ee, kk
+    end
+    function xml.filters.last(root,pattern)
+        local ee, kk
+        traverse(root, lpath(pattern), function(e,k) ee,kk = e,k return true end, 'reverse')
+        return ee and ee[kk], ee, kk
+    end
+    function xml.filters.index(root,pattern,arguments)
+        local ee, kk, reverse, i = nil, ni, false, tonumber(arguments or '1') or 1
+        if i and i ~= 0 then
+            if i < 0 then
+                reverse, i = true, -i
+            end
+            traverse(root, lpath(pattern), function(e,k) ee, kk, i = e, k , i-1 return i == 0 end, reverse)
+            if i > 0 then
+                return nil, nil, nil
+            else
+                return ee and ee[kk], ee, kk
+            end
+        else
+            return nil, nil, nil
+        end
+    end
+    function xml.filters.attributes(root,pattern,arguments)
+        local ee, kk
+        traverse(root, lpath(pattern), function(e,k) ee,kk = e,k return true end)
+        local ekat = ee and ee[kk] and ee[kk].at
+        if ekat then
+            if arguments then
+                return ekat[arguments] or "", ee, kk
+            else
+                return ekat, ee, kk
+            end
+        else
+            return {}, ee, kk
+        end
+    end
+    function xml.filters.text(root,pattern,arguments)
+        local ek, ee, kk = xml.filters.index(root,pattern,arguments)
+        return (ek and ek.dt and ek.dt[1]) or ""
+    end
+
+    function xml.filter(root,pattern)
+        local pat, fun, arg = pattern:match("^(.+)/(.-)%((.*)%)$")
+        if fun then
+            return (xml.filters[fun] or xml.filters.default)(root,pat,arg)
+        else
+            pat, arg = pattern:match("^(.+)/@(.-)$")
+            if arg then
+                return xml.filters.attributes(root,pat,arg)
+            else
+                return xml.filters.default(root,pattern)
+            end
+        end
+    end
+
+    xml.filters.position = xml.filters.index
+
+    -- these may go away
+
+    xml.index_element  = xml.filters.index
+    xml.count_elements = xml.filters.count
+    xml.first_element  = xml.filters.first
+    xml.last_element   = xml.filters.last
+    xml.index_text     = xml.filters.text
+    xml.first_text     = function (root,pattern) return xml.filters.text(root,pattern, 1) end
+    xml.last_text      = function (root,pattern) return xml.filters.text(root,pattern,-1) end
+
+    -- so far
+
+    function xml.get_text(root,pattern,reverse)
+        local ek
+        traverse(root, lpath(pattern), function(e,k) ek = e and e[k] end, reverse)
+        return (ek and ek.dt and ek.dt[1]) or ""
+    end
+
+    function xml.each_element(root, pattern, handle, reverse)
+        local ok
+        traverse(root, lpath(pattern), function(e,k) ok = true handle(e[k],e,k) end, reverse)
+        return ok
+    end
+
+    function xml.get_element(root,pattern,reverse)
+        local ee, kk
+        traverse(root, lpath(pattern), function(e,k) ee,kk = e,k end, reverse)
+        return ee and ee[kk], ee, kk
+    end
+
+    function xml.all_elements(root, pattern, handle, reverse)
+        local t = { }
+        traverse(root, lpath(pattern), function(e,k) t[#t+1] = e[k] end)
+        return t
+    end
+
+    function xml.all_texts(root, pattern, handle, reverse)
+        local t = { }
+        traverse(root, lpath(pattern), function(e,k)
+            local ek = e[k]
+            t[#t+1] = (ek and ek.dt and ek.dt[1]) or ""
+        end)
+        return t
+    end
+
+    -- array for each
+
+    function xml.insert_element(root, pattern, element, before) -- todo: element als functie
+        if root and element then
+            local matches, collect = { }, nil
+            if type(element) == "string" then
+                element = convert(element,true)
+            end
+            if element and element.td == "@rt@" then
+                element = element.dt
+            end
+            if element then
+                if before then
+                    collect = function(e,k) matches[#matches+1] = { e, element, k     } end
+                else
+                    collect = function(e,k) matches[#matches+1] = { e, element, k + 1 } end
+                end
+                traverse(root, lpath(pattern), collect)
+                for i=#matches,1,-1 do
+                    local m = matches[i]
+                    local t, element, at = m[1],m[2],m[3]
+                    -- when string, then element is { dt = { ... }, { ... } }
+                    if element.tg then
+                        table.insert(t,at,element) -- untested
+                    elseif element.dt then
+                        for _,v in ipairs(element.dt) do -- i added
+                            table.insert(t,at,v)
+                            at = at + 1
+                        end
+                    end
+                end
+            end
+        end
+    end
+
+    -- first, last, each
+
+    xml.insert_element_after  =                 xml.insert_element
+    xml.insert_element_before = function(r,p,e) xml.insert_element(r,p,e,true) end
+
+    function xml.delete_element(root, pattern)
+        local matches, deleted = { }, { }
+        local collect = function(e,k) matches[#matches+1] = { e, k } end
+        traverse(root, lpath(pattern), collect)
+        for i=#matches,1,-1 do
+            local m = matches[i]
+            deleted[#deleted+1] = table.remove(m[1],m[2])
+        end
+        return deleted
+    end
+
+    function xml.replace_element(root, pattern, element)
+        if type(element) == "string" then
+            element = convert(element,true)
+        end
+        if element and element.td == "@rt@" then
+            element = element.dt
+        end
+        if element then
+            local collect = function(e,k)
+                e[k] = element.dt -- maybe not clever enough
+            end
+            traverse(root, lpath(pattern), collect)
+        end
+    end
+
+    function xml.process(root, pattern, handle)
+        traverse(root, lpath(pattern), function(e,k)
+            if e[k].dt then
+                for k,v in ipairs(e[k].dt) do if v.tg then handle(v) end end -- i added
+            end
+        end)
+    end
+
+ --  function xml.process_attributes(root, pattern, handle)
+ --      traverse(root, lpath(pattern), function(e,k) handle(e[k].at) end)
+ --  end
+
+    function xml.process_attributes(root, pattern, handle)
+        traverse(root, lpath(pattern), function(e,k)
+            local ek = e[k]
+            local a = ek.at or { }
+            handle(a)
+            if next(a) then
+                ek.at = a
+            else
+                ek.at = nil
+            end
+        end)
+    end
+
+    function xml.package(tag,attributes,data)
+        local n, t = tag:match("^(.-):(.+)$")
+        if attributes then
+            return { ns = n or "", tg = t or tag, dt = data or "", at = attributes }
+        else
+            return { ns = n or "", tg = t or tag, dt = data or "" }
+        end
+    end
+
+    -- some special functions, handy for the manual:
+
+    function xml.gsub(t,old,new)
+        if t.dt then
+            for k,v in ipairs(t.dt) do
+                if type(v) == "string" then
+                    t.dt[k] = v:gsub(old,new)
+                else
+                    xml.gsub(v,old,new)
+                end
+            end
+        end
+    end
+
+    function xml.strip_leading_spaces(ek, e, k) -- cosmetic, for manual
+        if e and k and e[k-1] and type(e[k-1]) == "string" then
+            local s = e[k-1]:match("\n(%s+)")
+            xml.gsub(ek,"\n"..string.rep(" ",#s),"\n")
+        end
+    end
+
+    function xml.serialize_path(root,lpath,handle)
+        local ek, e, k = xml.first_element(root,lpath)
+        ek = table.copy(ek)
+        xml.strip_leading_spaces(ek,e,k)
+        xml.serialize(ek,handle)
+    end
+
+end
+
+xml.count   = xml.filters.count
+xml.index   = xml.filters.index
+xml.first   = xml.filters.first
+xml.last    = xml.filters.last
+
+xml.each    = xml.each_element
+xml.all     = xml.all_elements
+
+xml.insert  = xml.insert_element_after
+xml.after   = xml.insert_element_after
+xml.before  = xml.insert_element_before
+xml.delete  = xml.delete_element
+xml.replace = xml.replace_element
+
+-- a few helpers, the may move to lxml modules
+
+function xml.include(xmldata,element,attribute,pathlist,collapse)
+    element   = element   or 'ctx:include'
+    attribute = attribute or 'name'
+    pathlist  = pathlist or { '.' }
+    local function include(ek,e,k)
+        local name = (ek.at and ek.at[attribute]) or ""
+        if name ~= "" then
+            -- maybe file lookup in tree
+            local fullname
+            for _, path in ipairs(pathlist) do
+                if path == '.' then
+                    fullname = name
+                else
+                    fullname = file.join(path,name)
+                end
+                local f = io.open(fullname)
+                if f then
+                    xml.assign(e,k,xml.load(f,collapse))
+                    f:close()
+                    break
+                else
+                    xml.empty(e,k)
+                end
+            end
+        else
+            xml.empty(e,k)
+        end
+    end
+    while xml.each(xmldata, element, include) do end
+end
+
diff --git a/tex/context/base/lang-ini.lua b/tex/context/base/lang-ini.lua
index 5259a085e..aa00a8fd1 100644
--- a/tex/context/base/lang-ini.lua
+++ b/tex/context/base/lang-ini.lua
@@ -108,7 +108,7 @@ do
     local bynode = node.traverse
     local bychar = string.utfcharacters
 
-    function reconstruct(prev,str,fnt)
+    local function reconstruct(prev,str,fnt)
         local done = false
         if #str < 4 then
             -- too short
diff --git a/tex/context/base/lang-sla.mkiv b/tex/context/base/lang-sla.mkiv
index dfa56bab5..479012615 100644
--- a/tex/context/base/lang-sla.mkiv
+++ b/tex/context/base/lang-sla.mkiv
@@ -11,7 +11,7 @@
 %C therefore copyrighted by \PRAGMA. See mreadme.pdf for
 %C details.
 
-\def\sloveniancharacters#1{\ctxlua{convert.alphabetic(\number#1,"sl")}}
-\def\slovenianCharacters#1{\ctxlua{convert.Alphabetic(\number#1,"sl")}}
+\def\sloveniancharacters#1{\ctxlua{converters.alphabetic(\number#1,"sl")}}
+\def\slovenianCharacters#1{\ctxlua{converters.Alphabetic(\number#1,"sl")}}
 
 \endinput
diff --git a/tex/context/base/luat-cbk.lua b/tex/context/base/luat-cbk.lua
index a6145ec3b..efb534d7d 100644
--- a/tex/context/base/luat-cbk.lua
+++ b/tex/context/base/luat-cbk.lua
@@ -104,7 +104,7 @@ do
     garbagecollector.trace = false
     garbagecollector.tune  = false -- for the moment
 
-    function report(format)
+    local function report(format)
         if garbagecollector.trace then
          -- texio.write_nl(string.format(format,level,status.luastate_bytes))
             texio.write_nl(string.format(format,level,collectgarbage("count")))
diff --git a/tex/context/base/luat-crl.lua b/tex/context/base/luat-crl.lua
index 9d15b4896..129832765 100644
--- a/tex/context/base/luat-crl.lua
+++ b/tex/context/base/luat-crl.lua
@@ -4,10 +4,10 @@
 -- copyright: PRAGMA ADE / ConTeXt Development Team
 -- license  : see context related readme files
 
-if not versions    then versions    = { } end versions['luat-crl'] = 1.001
-if not curl        then curl        = { } end
+if not versions then versions = { } end versions['luat-crl'] = 1.001
+if not curl     then curl     = { } end
 
-curl.cachepath = cache.setpath(texmf.instance,"curl")
+curl.cachepath = caches.setpath(texmf.instance,"curl")
 
 curl.cached = { }
 
diff --git a/tex/context/base/luat-ini.lua b/tex/context/base/luat-ini.lua
index 022055e7f..e7757d51b 100644
--- a/tex/context/base/luat-ini.lua
+++ b/tex/context/base/luat-ini.lua
@@ -7,5 +7,20 @@ if not modules then modules = { } end modules ['luat-ini'] = {
 }
 
 --[[ldx--
-We cannot load anything yet.
+We cannot load anything yet. However what we will do us reserve a fewtables.
+These can be used for runtime user data or third party modules and will not be
+cluttered by macro package code.
+--ldx]]--
+
+userdata  = userdata  or { }
+thirddata = thirddata or { }
+document  = document or { }
+
+--[[ldx--
+Please create a namespace within these tables before using them!
+
+
+userdata ['my.name'] = { }
+thirddata['tricks' ] = { }
+
 --ldx]]--
diff --git a/tex/context/base/luat-ini.tex b/tex/context/base/luat-ini.tex
index 800950baf..3b0a61e62 100644
--- a/tex/context/base/luat-ini.tex
+++ b/tex/context/base/luat-ini.tex
@@ -41,27 +41,25 @@
 
 %D A few more goodies:
 
-\def\dostartlua#1%
+\long\def\dostartlua#1%
   {\begingroup
    \obeylualines
-   \directlua#1\iftrue{\else}\fi}
+   \dodostartlua{#1}}
 
-\def\dostoplua
-  {\iffalse{\else}\fi
-   \endgroup}
+\long\def\dodostartlua#1#2\stoplua
+  {\expanded{\endgroup\noexpand\directlua#1{#2}}}
 
-\def\dostartluacode#1%
+\long\def\dostartluacode#1%
   {\begingroup
    \obeylualines
    \obeyluatokens
-   \directlua#1\iftrue{\else}\fi}
+   \dodostartluacode{#1}}
 
-\def\dostopluacode % no unexpanded, else no } seen
-  {\iffalse{\else}\fi
-   \endgroup}
+\long\def\dodostartluacode#1#2\stopluacode
+  {\expanded{\endgroup\noexpand\directlua#1{#2}}}
 
-\def\startlua    {\dostartlua    \zerocount} \def\stoplua     {\dostoplua}
-\def\startluacode{\dostartluacode\zerocount} \def\stopluacode {\dostopluacode}
+\def\startlua    {\dostartlua    \zerocount}
+\def\startluacode{\dostartluacode\zerocount}
 
 %D Some delayed definitions:
 
diff --git a/tex/context/base/luat-inp.lua b/tex/context/base/luat-inp.lua
index 6551d1011..541bde5c3 100644
--- a/tex/context/base/luat-inp.lua
+++ b/tex/context/base/luat-inp.lua
@@ -72,6 +72,7 @@ input.formats['pfb'] = 'T1FONTS'        input.suffixes['pfb'] = { 'pfb', 'pfa' }
 input.formats['vf']  = 'VFFONTS'        input.suffixes['vf']  = { 'vf' }
 
 input.formats['fea'] = 'FONTFEATURES'   input.suffixes['fea'] = { 'fea' }
+input.formats['cid'] = 'FONTCIDMAPS'    input.suffixes['cid'] = { 'cid', 'cidmap' }
 
 input.formats ['texmfscripts'] = 'TEXMFSCRIPTS' -- new
 input.suffixes['texmfscripts'] = { 'rb', 'pl', 'py' } -- 'lua'
@@ -79,10 +80,10 @@ input.suffixes['texmfscripts'] = { 'rb', 'pl', 'py' } -- 'lua'
 input.formats ['lua'] = 'LUAINPUTS' -- new
 input.suffixes['lua'] = { 'lua', 'luc', 'tma', 'tmc' }
 
--- here we catch a few new thingies
+-- here we catch a few new thingies (todo: add these paths to context.tmf)
 
 function input.checkconfigdata(instance)
-    function fix(varname,default)
+    local function fix(varname,default)
         local proname = varname .. "." .. instance.progname or "crap"
         if not instance.environment[proname] and not instance.variables[proname] == "" and not instance.environment[varname] and not instance.variables[varname] == "" then
             instance.variables[varname] = default
@@ -90,6 +91,7 @@ function input.checkconfigdata(instance)
     end
     fix("LUAINPUTS"   , ".;$TEXINPUTS;$TEXMFSCRIPTS")
     fix("FONTFEATURES", ".;$OPENTYPEFONTS;$TTFONTS;$T1FONTS;$AFMFONTS")
+    fix("FONTCIDMAPS" , ".;$OPENTYPEFONTS;$TTFONTS;$T1FONTS;$AFMFONTS")
 end
 
 -- backward compatible ones
@@ -98,6 +100,8 @@ input.alternatives = { }
 
 input.alternatives['map files']            = 'map'
 input.alternatives['enc files']            = 'enc'
+input.alternatives['cid files']            = 'cid'
+input.alternatives['fea files']            = 'fea'
 input.alternatives['opentype fonts']       = 'otf'
 input.alternatives['truetype fonts']       = 'ttf'
 input.alternatives['truetype collections'] = 'ttc'
@@ -609,8 +613,11 @@ function input.generators.tex(instance,specification)
                 full = spec
             end
             for name in directory(full) do
-                if name == '.' or name == ".." then
-                    -- skip
+                if name:find("^%.") then
+                  -- skip
+                elseif name:find("[%~%`%!%#%$%%%^%&%*%(%)%=%{%}%[%]%:%;\"\'%|%|%<%>%,%?\n\r\t]") then
+                  -- texio.write_nl("skipping " .. name)
+                  -- skip
                 else
                     mode = attributes(full..name,'mode')
                     if mode == "directory" then
@@ -1407,7 +1414,7 @@ function input.aux.find_file(instance,filename) -- todo : plugin (scanners, chec
                     pathname = pathname:gsub("([%-%.])","%%%1") -- this also influences
                     pathname = pathname:gsub("/+$", '/.*')      -- later usage of pathname
                     pathname = pathname:gsub("//", '/.-/')
-                    expr = "^" .. pathname
+                    local expr = "^" .. pathname
                     -- input.debug('?',expr)
                     for _, f in pairs(filelist) do
                         if f:find(expr) then
@@ -1529,7 +1536,8 @@ end
 function input.find_given_files(instance,filename)
     local bname, result = file.basename(filename), { }
     for k, hash in ipairs(instance.hashes) do
-        local blist = instance.files[hash.tag][bname]
+        local files = instance.files[hash.tag]
+        local blist = files[bname]
         if not blist then
             local rname = "remap:"..bname
             blist = files[rname]
@@ -1596,9 +1604,11 @@ function input.find_wildcard_files(instance,filename) -- todo: remap:
     if name:find("%*") then
         for k, hash in ipairs(instance.hashes) do
             for kk, hh in pairs(files[hash.tag]) do
-                if (kk:lower()):find(name) then
-                    if doit(hh,kk,hash,allresults) then done = true end
-                    if done and not allresults then break end
+                if not kk:find("^remap:") then
+                    if (kk:lower()):find(name) then
+                        if doit(hh,kk,hash,allresults) then done = true end
+                        if done and not allresults then break end
+                    end
                 end
             end
         end
@@ -1812,7 +1822,7 @@ function input.validators.visibility.context(path, name)
     path = path[1] or path -- some day a loop
     return not (
         path:find("latex")    or
-        path:find("doc")      or
+--      path:find("doc")      or
         path:find("tex4ht")   or
         path:find("source")   or
 --      path:find("config")   or
@@ -1869,17 +1879,49 @@ function input.with_files(instance,pattern,handle)
     end
 end
 
-function input.update_script(oldname,newname) -- oldname -> own.name, not per se a suffix
+--~ function input.update_script(oldname,newname) -- oldname -> own.name, not per se a suffix
+--~     newname = file.addsuffix(newname,"lua")
+--~     local newscript = input.clean_path(input.find_file(instance, newname))
+--~     local oldscript = input.clean_path(oldname)
+--~     input.report("old script", oldscript)
+--~     input.report("new script", newscript)
+--~     if oldscript ~= newscript and (oldscript:find(file.removesuffix(newname).."$") or oldscript:find(newname.."$")) then
+--~         local newdata = io.loaddata(newscript)
+--~         if newdata then
+--~             input.report("old script content replaced by new content")
+--~             io.savedata(oldscript,newdata)
+--~         end
+--~     end
+--~ end
+
+function input.update_script(instance,oldname,newname) -- oldname -> own.name, not per se a suffix
+    local scriptpath = "scripts/context/lua"
     newname = file.addsuffix(newname,"lua")
-    newscript = input.clean_path(input.find_file(instance, newname))
-    oldscript = input.clean_path(oldname)
-    input.report("old script", oldscript)
-    input.report("new script", newscript)
-    if oldscript ~= newscript and (oldscript:find(file.removesuffix(newname).."$") or oldscript:find(newname.."$")) then
-        newdata = io.loaddata(newscript)
-        if newdata then
-            input.report("old script content replaced by new content")
-            io.savedata(oldscript,newdata)
+    local oldscript = input.clean_path(oldname)
+    input.report("to be replaced old script", oldscript)
+    local newscripts = input.find_files(instance, newname) or { }
+    if #newscripts == 0 then
+        input.report("unable to locate new script")
+    else
+        for _, newscript in ipairs(newscripts) do
+            newscript = input.clean_path(newscript)
+            input.report("checking new script", newscript)
+            if oldscript == newscript then
+                input.report("old and new script are the same")
+            elseif not newscript:find(scriptpath) then
+                input.report("new script should come from",scriptpath)
+            elseif not (oldscript:find(file.removesuffix(newname).."$") or oldscript:find(newname.."$")) then
+                input.report("invalid new script name")
+            else
+                local newdata = io.loaddata(newscript)
+                if newdata then
+                    input.report("old script content replaced by new content")
+                    io.savedata(oldscript,newdata)
+                    break
+                else
+                    input.report("unable to load new script")
+                end
+            end
         end
     end
 end
diff --git a/tex/context/base/luat-lib.tex b/tex/context/base/luat-lib.tex
index 6c5c63227..84b5bcfff 100644
--- a/tex/context/base/luat-lib.tex
+++ b/tex/context/base/luat-lib.tex
@@ -49,7 +49,7 @@
 \registerctxluafile{l-utils}  {1.001}
 \registerctxluafile{l-tex}    {1.001}
 \registerctxluafile{l-xml}    {1.001}
-\registerctxluafile{l-xmlctx} {1.001}
+%registerctxluafile{l-xmlctx} {1.001}
 
 \registerctxluafile{luat-cbk} {1.001}
 \registerctxluafile{luat-lib} {1.001}
diff --git a/tex/context/base/luat-tex.lua b/tex/context/base/luat-tex.lua
index 8e770f9de..1b2412594 100644
--- a/tex/context/base/luat-tex.lua
+++ b/tex/context/base/luat-tex.lua
@@ -60,7 +60,7 @@ if texconfig and not texlua then
             t = {
                 utftype = u, -- may go away
                 lines = l,
-                current = 0,
+                current = 0, -- line number, not really needed
                 handle = nil,
                 noflines = #l,
                 close = function()
@@ -68,15 +68,23 @@ if texconfig and not texlua then
                     input.show_close(filename)
                 end,
                 reader = function(self)
-                    if not self then self = t end
-                    if self.current >= #self.lines then
+                    self = self or t
+                    local current, lines = self.current, self.lines
+                    if current >= #lines then
                         return nil
                     else
-                        self.current = self.current + 1
-                        if input.filters.utf_translator then
-                            return input.filters.utf_translator(self.lines[t.current])
+                        self.current = current + 1
+                        local line = lines[self.current]
+                        if line == "" then
+                            return ""
                         else
-                            return self.lines[self.current]
+                            local translator = input.filters.utf_translator
+                        --  return (translator and translator(line)) or line
+                            if translator then
+                                return translator(line)
+                            else
+                                return line
+                            end
                         end
                     end
                 end
@@ -86,13 +94,15 @@ if texconfig and not texlua then
             -- todo: file;name -> freeze / eerste regel scannen -> freeze
             t = {
                 reader = function(self)
-                    if not self then self = t end -- not used
-                    if input.filters.dynamic_translator then
-                        return input.filters.dynamic_translator(file_handle:read())
+                    local line = file_handle:read()
+                    if line == "" then
+                        return ""
                     elseif input.filters.utf_translator then
-                        return input.filters.utf_translator(file_handle:read())
+                        return input.filters.utf_translator(line)
+                    elseif input.filters.dynamic_translator then
+                        return input.filters.dynamic_translator(line)
                     else
-                        return file_handle:read()
+                        return line
                     end
                 end,
                 close = function()
diff --git a/tex/context/base/luat-tmp.lua b/tex/context/base/luat-tmp.lua
index 84966bd2c..58a195986 100644
--- a/tex/context/base/luat-tmp.lua
+++ b/tex/context/base/luat-tmp.lua
@@ -22,30 +22,30 @@ being written at the same time is small. We also need to extend
 luatools with a recache feature.
 --ldx]]--
 
-cache = cache  or { }
-dir   = dir    or { }
-texmf = texmf  or { }
-
-cache.path   = cache.path or nil
-cache.base   = cache.base or "luatex-cache"
-cache.more   = cache.more or "context"
-cache.direct = false -- true is faster but may need huge amounts of memory
-cache.trace  = false
-cache.tree   = false
-cache.temp   = cache.temp or os.getenv("TEXMFCACHE") or os.getenv("HOME") or os.getenv("HOMEPATH") or os.getenv("VARTEXMF") or os.getenv("TEXMFVAR") or os.getenv("TMP") or os.getenv("TEMP") or os.getenv("TMPDIR") or nil
-cache.paths  = cache.paths or { cache.temp }
-
-if not cache.temp or cache.temp == "" then
+caches = caches  or { }
+dir    = dir    or { }
+texmf  = texmf  or { }
+
+caches.path   = caches.path or nil
+caches.base   = caches.base or "luatex-cache"
+caches.more   = caches.more or "context"
+caches.direct = false -- true is faster but may need huge amounts of memory
+caches.trace  = false
+caches.tree   = false
+caches.temp   = caches.temp or os.getenv("TEXMFCACHE") or os.getenv("HOME") or os.getenv("HOMEPATH") or os.getenv("VARTEXMF") or os.getenv("TEXMFVAR") or os.getenv("TMP") or os.getenv("TEMP") or os.getenv("TMPDIR") or nil
+caches.paths  = caches.paths or { caches.temp }
+
+if not caches.temp or caches.temp == "" then
     print("\nFATAL ERROR: NO VALID TEMPORARY PATH\n")
     os.exit()
 end
 
-function cache.configpath(instance)
+function caches.configpath(instance)
     return input.expand_var(instance,"TEXMFCNF")
 end
 
-function cache.treehash(instance)
-    local tree = cache.configpath(instance)
+function caches.treehash(instance)
+    local tree = caches.configpath(instance)
     if not tree or tree == "" then
         return false
     else
@@ -53,49 +53,49 @@ function cache.treehash(instance)
     end
 end
 
-function cache.setpath(instance,...)
-    if not cache.path then
+function caches.setpath(instance,...)
+    if not caches.path then
         if lfs and instance then
-            for _,v in  pairs(cache.paths) do
+            for _,v in  pairs(caches.paths) do
                 for _,vv in pairs(input.expanded_path_list(instance,v)) do
                     if lfs.isdir(vv) then
-                        cache.path = vv
+                        caches.path = vv
                         break
                     end
                 end
-                if cache.path then break end
+                if caches.path then break end
             end
         end
-        if not cache.path then
-            cache.path = cache.temp
+        if not caches.path then
+            caches.path = caches.temp
         end
-        cache.path = input.clean_path(cache.path) -- to be sure
+        caches.path = input.clean_path(caches.path) -- to be sure
         if lfs then
-            cache.tree = cache.tree or cache.treehash(instance)
-            if cache.tree then
-                cache.path = dir.mkdirs(cache.path,cache.base,cache.more,cache.tree)
+            caches.tree = caches.tree or caches.treehash(instance)
+            if caches.tree then
+                caches.path = dir.mkdirs(caches.path,caches.base,caches.more,caches.tree)
             else
-                cache.path = dir.mkdirs(cache.path,cache.base,cache.more)
+                caches.path = dir.mkdirs(caches.path,caches.base,caches.more)
             end
         end
     end
-    if not cache.path then
-        cache.path = '.'
+    if not caches.path then
+        caches.path = '.'
     end
-    cache.path = input.clean_path(cache.path)
+    caches.path = input.clean_path(caches.path)
     if lfs and not table.is_empty({...}) then
-        local pth = dir.mkdirs(cache.path,...)
+        local pth = dir.mkdirs(caches.path,...)
         return pth
     end
-    return cache.path
+    return caches.path
 end
 
-function cache.setluanames(path,name)
+function caches.setluanames(path,name)
     return path .. "/" .. name .. ".tma", path .. "/" .. name .. ".tmc"
 end
 
-function cache.loaddata(path,name)
-    local tmaname, tmcname = cache.setluanames(path,name)
+function caches.loaddata(path,name)
+    local tmaname, tmcname = caches.setluanames(path,name)
     local loader = loadfile(tmcname) or loadfile(tmaname)
     if loader then
         return loader()
@@ -104,18 +104,18 @@ function cache.loaddata(path,name)
     end
 end
 
-function cache.is_writable(filepath,filename)
-    local tmaname, tmcname = cache.setluanames(filepath,filename)
+function caches.is_writable(filepath,filename)
+    local tmaname, tmcname = caches.setluanames(filepath,filename)
     return file.is_writable(tmaname)
 end
 
-function cache.savedata(filepath,filename,data,raw) -- raw needed for file cache
-    local tmaname, tmcname = cache.setluanames(filepath,filename)
+function caches.savedata(filepath,filename,data,raw) -- raw needed for file cache
+    local tmaname, tmcname = caches.setluanames(filepath,filename)
     local reduce, simplify = true, true
     if raw then
         reduce, simplify = false, false
     end
-    if cache.direct then
+    if caches.direct then
         file.savedata(tmaname, table.serialize(data,'return',true,true))
     else
         table.tofile (tmaname,                 data,'return',true,true) -- maybe not the last true
@@ -127,7 +127,7 @@ end
 
 if tex and texconfig and texconfig.formatname and texconfig.formatname == "" then
     if not texconfig.luaname then texconfig.luaname = "cont-en.lua" end
-    texconfig.formatname = cache.setpath(instance,"format") .. "/" .. texconfig.luaname:gsub("%.lu.$",".fmt")
+    texconfig.formatname = caches.setpath(instance,"format") .. "/" .. texconfig.luaname:gsub("%.lu.$",".fmt")
 end
 
 --[[ldx--
@@ -149,7 +149,7 @@ containers.trace = false
 do -- local report
 
     local function report(container,tag,name)
-        if cache.trace or containers.trace or container.trace then
+        if caches.trace or containers.trace or container.trace then
             logs.report(string.format("%s cache",container.subcategory),string.format("%s: %s",tag,name or 'invalid'))
         end
     end
@@ -163,7 +163,7 @@ do -- local report
                 enabled = enabled,
                 version = version or 1.000,
                 trace = false,
-                path = cache.setpath(texmf.instance,category,subcategory),
+                path = caches.setpath(texmf.instance,category,subcategory),
             }
         else
             return nil
@@ -171,13 +171,13 @@ do -- local report
     end
 
     function containers.is_usable(container, name)
-        return container.enabled and cache.is_writable(container.path, name)
+        return container.enabled and caches.is_writable(container.path, name)
     end
 
     function containers.is_valid(container, name)
         if name and name ~= "" then
-            local cs = container.storage[name]
-            return cs and not table.is_empty(cs) and cs.cache_version == container.version
+            local storage = container.storage[name]
+            return storage and not table.is_empty(storage) and storage.cache_version == container.version
         else
             return false
         end
@@ -185,7 +185,7 @@ do -- local report
 
     function containers.read(container,name)
         if container.enabled and not container.storage[name] then
-            container.storage[name] = cache.loaddata(container.path,name)
+            container.storage[name] = caches.loaddata(container.path,name)
             if containers.is_valid(container,name) then
                 report(container,"loaded",name)
             else
@@ -204,7 +204,7 @@ do -- local report
             if container.enabled then
                 local unique, shared = data.unique, data.shared
                 data.unique, data.shared = nil, nil
-                cache.savedata(container.path, name, data)
+                caches.savedata(container.path, name, data)
                 report(container,"saved",name)
                 data.unique, data.shared = unique, shared
             end
@@ -229,7 +229,7 @@ function input.aux.save_data(instance, dataname, check)
     for cachename, files in pairs(instance[dataname]) do
         local name
         if input.usecache then
-            name = file.join(cache.setpath(instance,"trees"),md5.hex(cachename))
+            name = file.join(caches.setpath(instance,"trees"),md5.hex(cachename))
         else
             name = file.join(cachename,dataname)
         end
@@ -321,7 +321,7 @@ end
 function input.aux.load_data(instance,pathname,dataname,filename)
     local luaname, lucname, pname, fname
     if input.usecache then
-        pname, fname = cache.setpath(instance,"trees"), md5.hex(pathname)
+        pname, fname = caches.setpath(instance,"trees"), md5.hex(pathname)
         filename = file.join(pname,fname)
     else
         if not filename or (filename == "") then
@@ -357,7 +357,7 @@ input.automounted = input.automounted or { }
 function input.automount(instance,usecache)
     local mountpaths = input.simplified_list(input.expansion(instance,'TEXMFMOUNT'))
     if table.is_empty(mountpaths) and usecache then
-        mountpaths = { cache.setpath(instance,"mount") }
+        mountpaths = { caches.setpath(instance,"mount") }
     end
     if not table.is_empty(mountpaths) then
         input.starttiming(instance)
diff --git a/tex/context/base/luat-tra.lua b/tex/context/base/luat-tra.lua
index 90756d359..f5c077f41 100644
--- a/tex/context/base/luat-tra.lua
+++ b/tex/context/base/luat-tra.lua
@@ -8,61 +8,79 @@ if not versions then versions = { } end versions['luat-tra'] = 1.001
 
 debugger = { }
 
-debugger.counters = { }
-debugger.names    = { }
-
-function debugger.hook()
-    local f = debug.getinfo(2,"f").func
-    if debugger.counters[f] == nil then
-        debugger.counters[f] = 1
-        debugger.names[f] = debug.getinfo(2,"Sn")
-    else
-        debugger.counters[f] = debugger.counters[f] + 1
+do
+
+    local counters = { }
+    local names    = { }
+    local getinfo  = debug.getinfo
+
+    local function hook()
+        local f = getinfo(2,"f").func
+        if f then
+            if counters[f] == nil then
+                counters[f] = 1
+--~                 names[f] = debug.getinfo(2,"Sn")
+--~                 names[f] = debug.getinfo(2,"n")
+                names[f] = debug.getinfo(f)
+            else
+                counters[f] = counters[f] + 1
+            end
+        end
     end
-end
 
-function debugger.getname(func)
-    local n = debugger.names[func]
-    if n.what == "C" then
-        return n.name
+    local function getname(func)
+        local n = names[func]
+        if n then
+            if n.what == "C" then
+                return n.name or ''
+            else
+                -- source short_src linedefined what name namewhat nups func
+                local name = n.name or n.namewhat or n.what
+                if not name or name == "" then name = "?" end
+                return string.format("%s : %s : %s", n.short_src or "unknown source", n.linedefined or "--", name)
+            end
+        else
+            return "unknown"
+        end
     end
-    local lc = string.format("[%s]:%s", n.short_src, n.linedefined)
-    if n.namewhat ~= "" then
-        return string.format("%s (%s)", lc, n.name)
-    else
-        return lc
+
+    function debugger.showstats(printer,threshold)
+        printer   = printer   or texio.write or print
+        threshold = threshold or 0
+        local total, grandtotal, functions = 0, 0, 0
+        printer("\n") -- ugly but ok
+        for func, count in pairs(counters) do
+            if count > threshold then
+                printer(string.format("%8i  %s\n", count, getname(func)))
+                total = total + count
+            end
+            grandtotal = grandtotal + count
+            functions = functions + 1
+        end
+        printer(string.format("functions: %s, total: %s, grand total: %s, threshold: %s\n", functions, total, grandtotal, threshold))
     end
-end
 
-function debugger.showstats(printer,threshold)
-    if not printer   then printer   = print end
-    if not threshold then threshold = 0     end
-    for func, count in pairs(debugger.counters) do
-        if count > threshold then
-            printer(string.format("%8i  %s\n", count, debugger.getname(func)))
+    function debugger.savestats(filename,threshold)
+        local f = io.open(filename,'w')
+        if f then
+            debugger.showstats(function(str) f:write(str) end,threshold)
+            f:close()
         end
     end
-end
 
-function debugger.savestats(filename,threshold)
-    local f = io.open(filename,'w')
-    if f then
-        debugger.showstats(function(str) f:write(str) end,threshold)
-        f:close()
+    function debugger.enable()
+        debug.sethook(hook,"c")
     end
-end
 
-function debugger.enable()
-    debug.sethook(debugger.hook,"c")
-end
+    function debugger.disable()
+        debug.sethook()
+    --~ counters[debug.getinfo(2,"f").func] = nil
+    end
 
-function debugger.disable()
-    debug.sethook()
---~     debugger.counters[debug.getinfo(2,"f").func] = nil
-end
+    function debugger.tracing()
+        return tonumber((os.env['MTX.TRACE.CALLS'] or os.env['MTX_TRACE_CALLS'] or 0)) > 0
+    end
 
-function debugger.tracing()
-    return tonumber((os.env['MTX.TRACE.CALLS'] or os.env['MTX_TRACE_CALLS'] or 0)) > 0
 end
 
 --~ debugger.enable()
diff --git a/tex/context/base/lxml-ini.lua b/tex/context/base/lxml-ini.lua
new file mode 100644
index 000000000..f2a42eeea
--- /dev/null
+++ b/tex/context/base/lxml-ini.lua
@@ -0,0 +1,33 @@
+if not modules then modules = { } end modules ['lxml-ini'] = {
+    version   = 1.001,
+    comment   = "companion to lxml-ini.tex",
+    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+    copyright = "PRAGMA ADE / ConTeXt Development Team",
+    license   = "see context related readme files"
+}
+
+document     = document or { }
+document.xml = document.xml or { }
+
+lxml         = { }
+lxml.loaded  = { }
+
+function lxml.root(id)
+    return lxml.loaded[id]
+end
+
+function lxml.load(id,filename)
+    lxml.loaded[id] = xml.load(filename)
+end
+
+function lxml.first(id,pattern)
+    tex.sprint(xml.tostring(xml.first_text(lxml.loaded[id],pattern)))
+end
+
+function lxml.last(id,pattern)
+    tex.sprint(xml.tostring(xml.last_text (lxml.loaded[id],pattern)))
+end
+
+function lxml.index(id,pattern,i)
+    tex.sprint(xml.tostring(xml.index_text(lxml.loaded[id],pattern,i)))
+end
diff --git a/tex/context/base/lxml-ini.tex b/tex/context/base/lxml-ini.tex
new file mode 100644
index 000000000..3f21bf0fa
--- /dev/null
+++ b/tex/context/base/lxml-ini.tex
@@ -0,0 +1,23 @@
+%D \module
+%D   [       file=lxml-ini,
+%D        version=2007.08.17,
+%D          title=\CONTEXT\ \LUA\ based \XML\ Support,
+%D       subtitle=Initialization,
+%D         author=Hans Hagen,
+%D           date=\currentdate,
+%D      copyright={PRAGMA / Hans Hagen \& Ton Otten}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\writestatus{loading}{Context L-XML Macros (initialization)}
+
+\registerctxluafile{lxml-ini}{1.001}
+
+\def\xmlload   #1#2{\ctxlua{lxml.load ("#1","#2")}}
+\def\xmlfirst  #1#2{\ctxlua{lxml.first("#1","#2")}}
+\def\xmllast   #1#2{\ctxlua{lxml.last ("#1","#2")}}
+\def\xmlindex#1#2#3{\ctxlua{lxml.index("#1","#2",#3)}}
+
+\endinput
diff --git a/tex/context/base/meta-mis.tex b/tex/context/base/meta-mis.tex
index 6b39fa0dd..29ab43007 100644
--- a/tex/context/base/meta-mis.tex
+++ b/tex/context/base/meta-mis.tex
@@ -27,7 +27,7 @@
     currentpicture := currentpicture shifted (-4cm,0) ;
     fill fullcircle scaled 3cm withcolor cmyk(0,0,1,0) ;
     fill fullcircle scaled 2cm withcolor cmyk(0,1,0,0) ;
-    fill fullcircle scaled 1cm withcolor cmyk(0,0,1,0) ;
+    fill fullcircle scaled 1cm withcolor cmyk(1,0,0,0) ;
     currentpicture := currentpicture shifted (-4cm,0) ;
     draw fullcircle scaled 3cm dashed evenly ;
     draw fullcircle scaled 2cm dashed withdots  ;
diff --git a/tex/context/base/meta-pdf.lua b/tex/context/base/meta-pdf.lua
index 56e09bb1f..3a745da17 100644
--- a/tex/context/base/meta-pdf.lua
+++ b/tex/context/base/meta-pdf.lua
@@ -105,7 +105,7 @@ end
 
 function mptopdf.steps.convert()
     mptopdf.data = mptopdf.data:gsub("%c%((.-)%) (.-) (.-) fshow", function(str,font,scale)
-        table.insert(mptopdf.texts,{mptopdf.steps.descape(str), font, scale})
+        mptopdf.texts[mptopdf.texts+1] = {mptopdf.steps.descape(str), font, scale}
         return "\n" .. #mptopdf.texts .. " textext"
     end)
     mptopdf.data = mptopdf.data:gsub("%[%s*(.-)%s*%]", function(str)
@@ -165,17 +165,17 @@ function mptopdf.flushpath(cmd)
             local function concat(px, py)
                 return (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d
             end
-            for _,v in pairs(mptopdf.stack.path) do
+            for _,v in ipairs(mptopdf.stack.path) do
                 v[1],v[2] = concat(v[1],v[2])
                 if #v == 7 then
                     v[3],v[4] = concat(v[3],v[4])
                     v[5],v[6] = concat(v[5],v[6])
                 end
-                table.insert(path, table.concat(v," "))
+                path[#path+1] = table.concat(v," ")
             end
         else
-            for _,v in pairs(mptopdf.stack.path) do
-                table.insert(path, table.concat(v," "))
+            for _,v in ipairs(mptopdf.stack.path) do
+                path[#path+1] = table.concat(v," ")
             end
         end
         mptopdf.flushconcat()
@@ -363,23 +363,52 @@ end
 --~     end
 --~ end
 
-function mp.setrgbcolor(r,g,b) -- extra check
-    r, g = tonumber(r), tonumber(g) -- needed when we use lpeg
-    if r == 0.0123 and g < 0.01 then
-        mptopdf.texcode("\\MPSspecial{" .. g*10000 .. "}{" .. b*10000 .. "}")
-    elseif r == 0.123 and r < 0.1 then
-        mptopdf.texcode("\\MPSspecial{" .. g* 1000 .. "}{" .. b* 1000 .. "}")
-    else
-        mptopdf.texcode("\\MPSrgb{" .. r .. "}{" .. g .. "}{" .. b .. "}")
+if ctx and ctx.aux and ctx.aux.definecolor then
+
+    logs.report("mptopdf", "using attribute based mps colors")
+
+    -- todo: convert pdf specials to lua code
+
+    function mp.setrgbcolor(r,g,b) -- extra check
+        r, g, b = tonumber(r), tonumber(g), tonumber(b) -- needed when we use lpeg
+        if r == 0.0123 and g < 0.01 then
+            mptopdf.texcode("\\doresetattribute{transparency}\\MPSspecial{" .. g*10000 .. "}{" .. b*10000 .. "}")
+        elseif r == 0.123 and r < 0.1 then
+            mptopdf.texcode("\\doresetattribute{transparency}\\MPSspecial{" .. g* 1000 .. "}{" .. b* 1000 .. "}")
+        else
+            mptopdf.texcode("\\doresetattribute{transparency}\\dosetattribute{color}{" .. colors.register('color',nil,'rgb',r,g,b) .. "}")
+        end
     end
-end
 
-function mp.setcmykcolor(c,m,y,k)
-    mptopdf.texcode("\\MPScmyk{" .. c .. "}{" .. m .. "}{" .. y .. "}{" .. k .. "}")
-end
+    function mp.setcmykcolor(c,m,y,k)
+        mptopdf.texcode("\\doresetattribute{transparency}\\dosetattribute{color}{" .. colors.register('color',nil,'cmyk',tonumber(c),tonumber(m),tonumber(y),tonumber(k)) .. "}")
+    end
+
+    function mp.setgray(s)
+        mptopdf.texcode("\\doresetattribute{transparency}\\dosetattribute{color}{" .. colors.register('color',nil,'gray',tonumber(s)) .. "}")
+    end
+
+else
+
+    function mp.setrgbcolor(r,g,b) -- extra check
+        r, g = tonumber(r), tonumber(g) -- needed when we use lpeg
+        if r == 0.0123 and g < 0.01 then
+            mptopdf.texcode("\\MPSspecial{" .. g*10000 .. "}{" .. b*10000 .. "}")
+        elseif r == 0.123 and r < 0.1 then
+            mptopdf.texcode("\\MPSspecial{" .. g* 1000 .. "}{" .. b* 1000 .. "}")
+        else
+            mptopdf.texcode("\\MPSrgb{" .. r .. "}{" .. g .. "}{" .. b .. "}")
+        end
+    end
+
+    function mp.setcmykcolor(c,m,y,k)
+        mptopdf.texcode("\\MPScmyk{" .. c .. "}{" .. m .. "}{" .. y .. "}{" .. k .. "}")
+    end
+
+    function mp.setgray(s)
+        mptopdf.texcode("\\MPSgray{" .. s .. "}")
+    end
 
-function mp.setgray(s)
-    mptopdf.texcode("\\MPSgray{" .. s .. "}")
 end
 
 function mp.specials(version,signal,factor) -- 2.0 123 1000
diff --git a/tex/context/base/meta-pdf.mkiv b/tex/context/base/meta-pdf.mkiv
index dadf760a6..d10734547 100644
--- a/tex/context/base/meta-pdf.mkiv
+++ b/tex/context/base/meta-pdf.mkiv
@@ -24,8 +24,6 @@
 %  7.4 : mkiv, simulated clean direct lua from mp
 %  0.3 : time taken by tex to handle converted code
 
-% Maybe delayed load, only at first call of the converter.
-
 \registerctxluafile{meta-pdf}{1.003}
 
 %D Plugin.
diff --git a/tex/context/base/meta-pdf.tex b/tex/context/base/meta-pdf.tex
index ad4a57fd9..8ac736818 100644
--- a/tex/context/base/meta-pdf.tex
+++ b/tex/context/base/meta-pdf.tex
@@ -811,7 +811,7 @@
      {\global\let\MPresolvedspace\MPcmykspace
       \xdef\MPresolvedcolor{##1 ##2 ##3 ##4}}%
    \def\dostartspotcolormode##1##2%
-     {\global\let\MPspotspace\empty
+     {\global\let\MPspotspace\empty % left over ?
       \xdef\MPresolvedspace{##1}%
       \xdef\MPresolvedcolor{##2}%
       \global\let\MPspotspace\MPresolvedspace}% signal
@@ -961,4 +961,51 @@
 %     filldraw boundingbox currentpicture enlarged -3cm withpen pencircle scaled 1pt withcolor .5white ;
 % \stopMPcode
 
+% This code will move to meta-pdf.mkiv and the call to lua will move to the
+% converter code (saves a lua call). We will do this when we made the final
+% move to attribute bases color .
+
+\ifx\colorversion\undefined \else \ifnum\colorversion>\plusone
+
+    \def\dohandleMPgraycolor     #1{\dosetattribute{color}{\ctxlua{tex.sprint(colors.register('color',nil,'gray',#1))}}}
+    \def\dohandleMPrgbcolor  #1#2#3{\dosetattribute{color}{\ctxlua{tex.sprint(colors.register('color',nil,'rgb' ,#1,#2,#3))}}}
+    \def\dohandleMPcmykcolor#1#2#3#4{\dosetattribute{color}{\ctxlua{tex.sprint(colors.register('color',nil,'cmyk',#1,#2,#3,#4))}}}
+    \def\dohandleMPspotcolor#1#2#3#4{\dosetattribute{color}{\ctxlua{tex.sprint(colors.register('color',nil,'spot',"#1",#2,"#3","#4"))}}}
+
+    \def\dohandleMPgraytransparency      #1#2#3{\dosetattribute{color}{\ctxlua{tex.sprint(colors.register('color',nil,'gray',#1))}}%
+                                                \dosetattribute{transparency}{\ctxlua{tex.sprint(transparencies.register(nil,#2,#3))}}}
+    \def\dohandleMPrgbtransparency   #1#2#3#4#5{\dosetattribute{color}{\ctxlua{tex.sprint(colors.register('color',nil,'rgb' ,#1,#2,#3))}}%
+                                                \dosetattribute{transparency}{\ctxlua{tex.sprint(transparencies.register(nil,#4,#5))}}}
+    \def\dohandleMPcmyktransparency#1#2#3#4#5#6{\dosetattribute{color}{\ctxlua{tex.sprint(colors.register('color',nil,'cmyk',#1,#2,#3,#4))}}%
+                                                \dosetattribute{transparency}{\ctxlua{tex.sprint(transparencies.register(nil,#5,#6))}}}
+    \def\dohandleMPspottransparency#1#2#3#4#5#6{\dosetattribute{color}{\ctxlua{tex.sprint(colors.register('color',nil,'spot',"#1",#2,"#3","#4"))}}%
+                                                \dosetattribute{transparency}{\ctxlua{tex.sprint(transparencies.register(nil,#5,#6))}}}
+
+    % \dostartgraycolormode\!!zerocount} % kind of hackery initialization
+
+    \def\resolveMPgraycolor#1\to#2%
+      {\global\let\MPresolvedspace\MPgrayspace
+       \edef#2{\ctxlua{tex.sprint(ctx.pdfcolorvalue(\number\currentcolormodel,colors.register('color',nil,'gray',#1)))}}}
+
+    \def\resolveMPrgbcolor#1#2#3\to#4%
+      {\global\let\MPresolvedspace\MPrgbspace
+       \edef#4{\ctxlua{tex.sprint(ctx.pdfcolorvalue(\number\currentcolormodel,colors.register('color',nil,'rgb' ,#1,#2,#3)))}}}
+
+    \def\resolveMPcmykcolor#1#2#3#4\to#5%
+      {\global\let\MPresolvedspace\MPcmykspace
+       \edef#5{\ctxlua{tex.sprint(ctx.pdfcolorvalue(\number\currentcolormodel,colors.register('color',nil,'cmyk',#1,#2,#3,#4)))}}}
+
+    \def\resolveMPspotcolor#1#2#3#4\to#5%
+      {\xdef\MPresolvedspace{#1}%
+       \xdef\MPresolvedcolor{#4}%
+       \global\let\MPspotspace\MPresolvedspace
+       \edef#5{\ctxlua{tex.sprint(ctx.pdfcolorvalue(\number\currentcolormodel,colors.register('color',nil,'spot',"#1",#2,"#3","#4")))}}}
+
+    \let\MPSgray\dohandleMPgraycolor
+    \let\MPSrgb \dohandleMPrgbcolor
+    \let\MPScmyk\dohandleMPcmykcolor
+    \let\MPspot \dohandleMPspotcolor
+
+\fi \fi
+
 \protect \endinput
diff --git a/tex/context/base/mult-con.tex b/tex/context/base/mult-con.tex
index 043d5fb6e..22103e615 100644
--- a/tex/context/base/mult-con.tex
+++ b/tex/context/base/mult-con.tex
@@ -736,6 +736,10 @@ subsubsubsubsubsubject: subsubsubsubsubonderwerp                 subsubsubsubsub
                            ausgleichen               rovnovaha
                            bilanciamento             balanta
                            equilibre
+                 bookmark: bookmark                  bookmark
+                           bookmark                  zalozka
+                           segnalibro                semncarte
+                           marquepage
                   wfactor: bfactor                   wfactor
                            bfaktor                   sfaktor
                            wfactor                   factorw
@@ -1160,6 +1164,14 @@ subsubsubsubsubsubject: subsubsubsubsubonderwerp                 subsubsubsubsub
                            linkerrand                levyokraj
                            marginesinistro           marginestanga
                            margegauche
+              innermargin: binnenmarge               innermargin
+                           innermargin               innermargin
+                           margineinterno            innermargin
+                           margeinterieure
+              outermargin: buitenmarge               outermargin
+                           outermargin               outermargin
+                           margineesterno            outermargin
+                           margeexterieure
        leftmargindistance: linkermargeafstand        leftmargindistance
                            linkerrandabstand         vzdalenostlevehookraje
                            distanzamarginesinistro   distantamarginestanga
diff --git a/tex/context/base/mult-sys.tex b/tex/context/base/mult-sys.tex
index f10e91cfe..74538a2aa 100644
--- a/tex/context/base/mult-sys.tex
+++ b/tex/context/base/mult-sys.tex
@@ -177,6 +177,8 @@
 \definesystemconstant  {layer}
 \definesystemconstant  {effect}
 \definesystemconstant  {negative}
+\definesystemconstant  {color}
+\definesystemconstant  {transparency}
 
 \definesystemconstant  {black}
 \definesystemconstant  {white}
diff --git a/tex/context/base/node-ini.lua b/tex/context/base/node-ini.lua
index 77835e04b..4ef9e05eb 100644
--- a/tex/context/base/node-ini.lua
+++ b/tex/context/base/node-ini.lua
@@ -11,8 +11,8 @@ if not modules then modules = { } end modules ['node-ini'] = {
 implement a few helper functions.
 --ldx]]--
 
-nodes            = nodes or { }
-nodes.trace      = false
+nodes       = nodes or { }
+nodes.trace = false
 
 -- handy helpers
 
@@ -59,7 +59,7 @@ nodes.processors.char = { }
 nodes.processors.char.proc = { }
 
 function nodes.report(t,done)
-    if nodes.trace then
+    if nodes.trace then -- best also test this before calling
         if done then
             if status.output_active then
                 texio.write(string.format("<++ %s>",nodes.count(t)))
@@ -296,6 +296,309 @@ if not fonts        then fonts        = { } end
 if not fonts.tfm    then fonts.tfm    = { } end
 if not fonts.tfm.id then fonts.tfm.id = { } end
 
+do
+
+    local glyph, hlist, vlist = node.id('glyph'), node.id('hlist'), node.id('vlist')
+    local pushmarks  = false
+
+    function nodes.process_glyphs(head)
+        if status.output_active then  -- not ok, we need a generic blocker, pagebody ! / attr tex.attibutes
+            -- 25% calls
+            return true
+        elseif not head then
+            -- 25% calls
+            return true
+        elseif not head.next and (head.id == hlist or head.id == vlist) then
+            return head
+        else
+            -- either next or not, but definitely no already processed list
+            input.start_timing(nodes)
+            local usedfonts, found, fontdata, done = { }, false, fonts.tfm.id, false
+            for n in node.traverse_id(glyph,head) do
+                local font = n.font
+                if not usedfonts[font] then
+                    local shared = fontdata[font].shared
+                    if shared and shared.processors then
+                        usedfonts[font], found = shared.processors, true
+                    end
+                end
+            end
+            if found then
+                local tail = head
+                if head.next then
+                    tail = node.slide(head)
+                else
+                    head.prev = nil
+                end
+                for font, processors in pairs(usedfonts) do
+                    if pushmarks then
+                        local h, d = fonts.pushmarks(head,font)
+                        head, done = head or h, done or d
+                    end
+                    for _, processor in ipairs(processors) do
+                        local h, d = processor(head,font)
+                        head, done = head or h, done or d
+                    end
+                    if pushmarks then
+                        local h, d = fonts.popmarks(head,font)
+                        head, done = head or h, done or d
+                    end
+                end
+            end
+            input.stop_timing(nodes)
+            if nodes.trace then
+                nodes.report(head,done)
+            end
+            if done then
+                return head  -- something changed
+            elseif head then
+                return true  -- nothing changed
+            else
+                return false -- delete list
+            end
+        end
+    end
+
+end
+
+-- vbox: grouptype: vbox vtop output split_off split_keep  | box_type: exactly|aditional
+-- hbox: grouptype: hbox adjusted_hbox(=hbox_in_vmode)     | box_type: exactly|aditional
+
+do
+
+    local contains, set, attribute = node.has_attribute, node.set_attribute, tex.attribute
+
+    function nodes.inherit_attributes(n)
+        if n then
+            local i = 1
+            while true do
+                local a = attribute[i]
+                if a < 0 then
+                    break
+                else
+                    local ai = contains(n,i)
+                    if not ai then
+                        set(n,i,a)
+                    end
+                    i = i + 1
+                end
+            end
+        end
+    end
+
+end
+
+callback.register('pre_linebreak_filter', nodes.process_glyphs)
+callback.register('hpack_filter',         nodes.process_glyphs)
+
+--~ callback.register('pre_linebreak_filter', function(t,...)
+--~     print("pre_linebreak_filter",...)
+--~     return nodes.process_glyphs(t)
+--~ end )
+--~ callback.register('hpack_filter',         function(t,...)
+--~     print("hpack_filter",...)
+--~     return nodes.process_glyphs(t)
+--~ end )
+
+function nodes.length(head)
+    if head then
+        local m = 0
+        for n in node.traverse(head) do
+            m = m + 1
+        end
+        return m
+    else
+        return 0
+    end
+end
+
+do
+
+--~     function nodes.totable(n)
+--~         local function totable(n)
+--~             local f, tt = node.fields(n.id,n.subtype), { }
+--~             for _,v in ipairs(f) do
+--~                 local nv = n[v]
+--~                 if nv then
+--~                     local tnv = type(nv)
+--~                     if tnv == "string" or tnv == "number" then
+--~                         tt[v] = nv
+--~                     else -- userdata
+--~                         tt[v] = nodes.totable(nv)
+--~                     end
+--~                 end
+--~             end
+--~             return tt
+--~         end
+--~         local t = { }
+--~         while n do
+--~             t[#t+1] = totable(n)
+--~             n = n.next
+--~         end
+--~         return t
+--~     end
+
+    local expand = {
+        list = true,
+        pre = true,
+        post = true,
+        spec = true,
+        attr = true,
+        components = true,
+    }
+
+    -- flat: don't use next, but indexes
+    -- verbose: also add type
+
+    function nodes.totable(n,flat,verbose)
+        local function totable(n,verbose)
+            local f = node.fields(n.id,n.subtype)
+            local tt = { }
+            for _,v in ipairs(f) do
+                if n[v] then
+                    if v == "ref_count" then
+                        -- skip
+                    elseif expand[v] then -- or: type(n[v]) ~= "string" or type(n[v]) ~= "number"
+                        tt[v] = nodes.totable(n[v],flat,verbose)
+                    else
+                        tt[v] = n[v]
+                    end
+                end
+            end
+            if verbose then
+                tt.type = node.type(tt.id)
+            end
+            return tt
+        end
+        if n then
+            if flat then
+                local t = { }
+                while n do
+                    t[#t+1] = totable(n,verbose)
+                    n = n.next
+                end
+                return t
+            else
+                local t = totable(n,verbose)
+                if n.next then
+                    t.next = nodes.totable(n.next,flat,verbose)
+                end
+                return t
+            end
+        else
+            return { }
+        end
+    end
+
+    local function key(k)
+        if type(k) == "number" then
+            return "["..k.."]"
+        else
+            return k
+        end
+    end
+
+    local function serialize(root,name,handle,depth,m)
+        handle = handle or print
+        if depth then
+            depth = depth .. " "
+            handle(("%s%s={"):format(depth,key(name)))
+        else
+            depth = ""
+            if type(name) == "string" then
+                if name == "return" then
+                    handle("return {")
+                else
+                    handle(name .. "={")
+                end
+            elseif type(name) == "number" then
+                handle("[" .. name .. "]={")
+            else
+                handle("t={")
+            end
+        end
+        if root then
+            local fld
+            if root.id then
+                fld = node.fields(root.id,root.subtype)
+            else
+                fld = table.sortedkeys(root)
+            end
+            if type(root) == 'table' and root['type'] then -- userdata or table
+                handle(("%s %s=%q,"):format(depth,'type',root['type']))
+            end
+            for _,k in ipairs(fld) do
+                if k then
+                    local v = root[k]
+                    local t = type(v)
+                    if t == "number" then
+                        handle(("%s %s=%s,"):format(depth,key(k),v))
+                    elseif t == "string" then
+                        handle(("%s %s=%q,"):format(depth,key(k),v))
+                    elseif v then -- userdata or table
+                        serialize(v,k,handle,depth,m+1)
+                    end
+                end
+            end
+            if root['next'] then -- userdata or table
+                serialize(root['next'],'next',handle,depth,m+1)
+            end
+        end
+        if m and m > 0 then
+            handle(("%s},"):format(depth))
+        else
+            handle(("%s}"):format(depth))
+        end
+    end
+
+    function nodes.serialize(root,name)
+        local t = { }
+        local function flush(s)
+            t[#t+1] = s
+        end
+        serialize(root, name, flush, nil, 0)
+        return table.concat(t,"\n")
+    end
+
+    function nodes.serializebox(n,flat,verbose)
+        return nodes.serialize(nodes.totable(tex.box[n],flat,verbose))
+    --  return nodes.serialize(tex.box[n])
+    end
+
+    function nodes.visualizebox(...)
+    --  tex.sprint(tex.ctxcatcodes,"\\starttyping\n" .. nodes.serializebox(...) .. "\n\\stoptyping\n")
+        tex.print(tex.ctxcatcodes,"\\starttyping")
+        tex.print(nodes.serializebox(...))
+        tex.print("\\stoptyping")
+    end
+
+end
+
+if not node.list_has_attribute then
+
+    function node.list_has_attribute(list,attribute)
+        if list and attribute then
+            for n in node.traverse(list) do
+                local a = has_attribute(n,attribute)
+                if a then return a end
+            end
+        end
+        return false
+    end
+
+end
+
+function nodes.pack_list(head)
+    local t = { }
+    for n in node.traverse(head) do
+        t[#t+1] = tostring(n)
+    end
+    return t
+end
+
+-- old code
+
+
 --~ function nodes.do_process_glyphs(stack)
 --~     if not stack or #stack == 0 then
 --~         return false
@@ -634,272 +937,3 @@ if not fonts.tfm.id then fonts.tfm.id = { } end
 --~     end
 
 --~ end
-
-do
-
-    local glyph      = node.id('glyph')
-    local pushmarks  = false
-
-    function do_process_glyphs(head) -- beware, we need to handle shifted heads -- todo
-        if not head then
-            return head
-        end
-        local usedfonts, found, fontdata = { }, false, fonts.tfm.id
-        for n in node.traverse_id(glyph,head) do
-            local font = n.font
-            if not usedfonts[font] then
-                local shared = fontdata[font].shared
-                if shared and shared.processors then
-                    usedfonts[font], found = shared.processors, true
-                end
-            end
-        end
-        if not found then
-            return head, false
-        else
-            local tail, done = node.slide(head), false
-            for font, processors in pairs(usedfonts) do
-                if pushmarks then
-                    local h, d = fonts.pushmarks(head,font)
-                    head, done = head or h, done or d
-                end
-                for _, processor in ipairs(processors) do
-                    local h, d = processor(head,font)
-                    head, done = head or h, done or d
-                end
-                if pushmarks then
-                    local h, d = fonts.popmarks(head,font)
-                    head, done = head or h, done or d
-                end
-            end
-            return head, done
-        end
-    end
-
-    function nodes.process_glyphs(head)
-        if status.output_active then  -- not ok, we need a generic blocker, pagebody ! / attr tex.attibutes
-            return true
-        else
-            input.start_timing(nodes)
-            local head, done = do_process_glyphs(head)
-            input.stop_timing(nodes)
-            nodes.report(head,done)
-            if done then
-                return head  -- something changed
-            elseif head then
-                return true  -- nothing changed
-            else
-                return false -- delete list
-            end
-        end
-    end
-
-end
-
--- vbox: grouptype: vbox vtop output split_off split_keep  | box_type: exactly|aditional
--- hbox: grouptype: hbox adjusted_hbox(=hbox_in_vmode)     | box_type: exactly|aditional
-
-callback.register('pre_linebreak_filter', nodes.process_glyphs)
-callback.register('hpack_filter',         nodes.process_glyphs)
-
---~ callback.register('pre_linebreak_filter', function(t,...)
---~     print("pre_linebreak_filter",...)
---~     return nodes.process_glyphs(t)
---~ end )
---~ callback.register('hpack_filter',         function(t,...)
---~     print("hpack_filter",...)
---~     return nodes.process_glyphs(t)
---~ end )
-
-function nodes.length(head)
-    if head then
-        local m = 0
-        for n in node.traverse(head) do
-            m = m + 1
-        end
-        return m
-    else
-        return 0
-    end
-end
-
-do
-
---~     function nodes.totable(n)
---~         function totable(n)
---~             local f, tt = node.fields(n.id,n.subtype), { }
---~             for _,v in ipairs(f) do
---~                 local nv = n[v]
---~                 if nv then
---~                     local tnv = type(nv)
---~                     if tnv == "string" or tnv == "number" then
---~                         tt[v] = nv
---~                     else -- userdata
---~                         tt[v] = nodes.totable(nv)
---~                     end
---~                 end
---~             end
---~             return tt
---~         end
---~         local t = { }
---~         while n do
---~             t[#t+1] = totable(n)
---~             n = n.next
---~         end
---~         return t
---~     end
-
-    local expand = {
-        list = true,
-        pre = true,
-        post = true,
-        spec = true,
-        attr = true,
-        components = true,
-    }
-
-    -- flat: don't use next, but indexes
-    -- verbose: also add type
-
-    function nodes.totable(n,flat,verbose)
-        function totable(n,verbose)
-            local f = node.fields(n.id,n.subtype)
-            local tt = { }
-            for _,v in ipairs(f) do
-                if n[v] then
-                    if v == "ref_count" then
-                        -- skip
-                    elseif expand[v] then -- or: type(n[v]) ~= "string" or type(n[v]) ~= "number"
-                        tt[v] = nodes.totable(n[v],flat,verbose)
-                    else
-                        tt[v] = n[v]
-                    end
-                end
-            end
-            if verbose then
-                tt.type = node.type(tt.id)
-            end
-            return tt
-        end
-        if flat then
-            local t = { }
-            while n do
-                t[#t+1] = totable(n,verbose)
-                n = n.next
-            end
-            return t
-        else
-            local t = totable(n,verbose)
-            if n.next then
-                t.next = nodes.totable(n.next,flat,verbose)
-            end
-            return t
-        end
-    end
-
-    local function key(k)
-        if type(k) == "number" then
-            return "["..k.."]"
-        else
-            return k
-        end
-    end
-
-    local function serialize(root,name,handle,depth,m)
-        handle = handle or print
-        if depth then
-            depth = depth .. " "
-            handle(("%s%s={"):format(depth,key(name)))
-        else
-            depth = ""
-            if type(name) == "string" then
-                if name == "return" then
-                    handle("return {")
-                else
-                    handle(name .. "={")
-                end
-            elseif type(name) == "number" then
-                handle("[" .. name .. "]={")
-            else
-                handle("t={")
-            end
-        end
-        if root then
-            local fld
-            if root.id then
-                fld = node.fields(root.id,root.subtype)
-            else
-                fld = table.sortedkeys(root)
-            end
-            if type(root) == 'table' and root['type'] then -- userdata or table
-                handle(("%s %s=%q,"):format(depth,'type',root['type']))
-            end
-            for _,k in ipairs(fld) do
-                if k then
-                    local v = root[k]
-                    local t = type(v)
-                    if t == "number" then
-                        handle(("%s %s=%s,"):format(depth,key(k),v))
-                    elseif t == "string" then
-                        handle(("%s %s=%q,"):format(depth,key(k),v))
-                    elseif v then -- userdata or table
-                        serialize(v,k,handle,depth,n+1)
-                    end
-                end
-            end
-            if root['next'] then -- userdata or table
-                serialize(root['next'],'next',handle,depth,n+1)
-            end
-        end
-        if m and m > 0 then
-            handle(("%s},"):format(depth))
-        else
-            handle(("%s}"):format(depth))
-        end
-    end
-
-    function nodes.serialize(root,name)
-        local t = { }
-        local function flush(s)
-            t[#t+1] = s
-        end
-        serialize(root, name, flush, nil, nil)
-        return table.concat(t,"\n")
-    end
-
-    function nodes.serializebox(n,flat,verbose)
-        return nodes.serialize(nodes.totable(tex.box[n],flat,verbose))
-    --  return nodes.serialize(tex.box[n])
-    end
-
-    function nodes.visualizebox(...)
-    --  tex.sprint(tex.ctxcatcodes,"\\starttyping\n" .. nodes.serializebox(...) .. "\n\\stoptyping\n")
-        tex.print(tex.ctxcatcodes,"\\starttyping")
-        tex.print(nodes.serializebox(...))
-        tex.print("\\stoptyping")
-    end
-
-end
-
-if not node.list_has_attribute then
-
-    function node.list_has_attribute(list,attribute)
-        if list and attribute then
-            for n in node.traverse(list) do
-                local a = has_attribute(n,attribute)
-                if a then return a end
-            end
-        end
-        return false
-    end
-
-end
-
-function nodes.pack_list(head)
-    local t = { }
-    for n in node.traverse(head) do
-        t[#t+1] = tostring(n)
-    end
-    return t
-end
-
diff --git a/tex/context/base/page-flt.tex b/tex/context/base/page-flt.tex
index 6ebade65d..157864d44 100644
--- a/tex/context/base/page-flt.tex
+++ b/tex/context/base/page-flt.tex
@@ -186,6 +186,9 @@
 \def\floatparameter       #1{\csname\??fl\currentfloat#1\endcsname}
 \def\floatcaptionparameter#1{\csname\??kj\currentfloat#1\endcsname}
 
+% \def\floatparameter       #1{\csname     \ifcsname\??fl\currentfloat#1\endcsname\??fl\currentfloat\else\??bk\fi#1\endcsname}
+% \def\floatcaptionparameter#1{\csname\??kj\ifcsname\??kj\currentfloat#1\endcsname     \currentfloat          \fi#1\endcsname}
+
 % for the moment we need to define the parameters anyway, first we need to implement a
 % proper parent chain (also for framed); no problem now that machines are fast (tests
 % show that this may save 20 k or more in the format)
@@ -194,7 +197,7 @@
 % \def\floatcaptionparameter#1{\executeifdefined{\??kj\currentfloat#1}{\csname\??bk#1\endcsname}}
 
 \def\setupfloats
-  {\dodoubleargument\getparameters[\??bk]}
+  {\dodoubleargument\getparameters[\??bk]} % funny, why not \??fl, must be a reason
 
 \def\setupcaptions
   {\dodoubleargument\getparameters[\??kj]}
@@ -233,7 +236,7 @@
   {\edef\currentfloat{#1}%
    \doifelsenothing\currentfloat
      {\let\currentfloat\v!figure}
-     {\doifundefined{\??fl#1\c!default}{\let\currentfloat\v!figure}}%
+     {}% {\doifundefined{\??fl#1\c!default}{\let\currentfloat\v!figure}}%
    \doifelsenothing{#2}
      {\edef\floatlocation{\floatparameter\c!default}}
      {\edef\floatlocation{#2}}%
@@ -320,7 +323,7 @@
    \global\chardef\textfloatmethod\floatparameter\c!textmethod
    \global\chardef\sidefloatalign\zerocount
    \globallet\floatrotation\!!zerocount
-   \calculatefloatskips{#1}%
+   \calculatefloatskips
    \ifparfloat
      \processaction
        [\floatparameter\c!sidealign]
@@ -510,41 +513,41 @@
 
 % pas op, maxbreedte niet instellen als plaats=links/rechts
 
-\def\setlocalfloatdimensions#1#2#3#4% experimental / #3 box number #4 prefix
+\def\setlocalfloatdimensions#1#2#3% experimental / #3 box number #4 prefix
   {\global\sidefloatshift  \zeropoint       % duplicate
    \global\sidefloatmaximum\zeropoint\relax % duplicate
    \ifextrafloatactions
      \ifdim\sidefloatdownshift=\zeropoint\else
-       #4\setbox#3\vbox
+       #3\setbox#2\vbox
         {\vskip\sidefloatdownshift\nointerlineskip\box#3}%
      \fi
      \doifsomething{\floatparameter\c!minwidth}
        {\scratchdimen\floatparameter\c!minwidth\relax
-        \ifdim\wd#3<\scratchdimen
-          #4\setbox#3\hbox to \scratchdimen
+        \ifdim\wd#2<\scratchdimen
+          #3\setbox#2\hbox to \scratchdimen
             {\doifnot{\floatparameter\c!location}\v!left \hss
-             \box#3%
+             \box#2%
              \doifnot{\floatparameter\c!location}\v!right\hss}%
         \fi}%
      % todo: rand / rug
-     \doifinset\v!hanging{#2}
-       {\doifcommonelse{\v!inleft,\v!leftmargin}{#2}
+     \doifinset\v!hanging{#1}
+       {\doifcommonelse{\v!inleft,\v!leftmargin}{#1}
           {\letvalue{\??fl\currentfloat\c!maxwidth}\leftmarginwidth}%
-          {\doifcommon{\v!inright,\v!rightmargin}{#2}
+          {\doifcommon{\v!inright,\v!rightmargin}{#1}
              {\letvalue{\??fl\currentfloat\c!maxwidth}\rightmarginwidth}}}%
      \doifsomething{\floatparameter\c!maxwidth}
        {\scratchdimen\floatparameter\c!maxwidth\relax
-        \ifdim\wd#3>\scratchdimen
+        \ifdim\wd#2>\scratchdimen
           \doifcommonelse{\v!inright,\v!rightmargin,\v!rightedge
-                          \v!inleft,\v!leftmargin,\v!leftedge}{#2}
+                          \v!inleft,\v!leftmargin,\v!leftedge}{#1}
             {\global\sidefloatmaximum\scratchdimen}
-            {#4\setbox#3\hbox to \scratchdimen
-              {\doifcommonelse{\v!right,\v!left}{#2}
-                 {\doifnotinset\v!right{#2}\hss
-                  \box#3%
-                  \doifnotinset\v!left{#2}\hss}%
+            {#3\setbox#2\hbox to \scratchdimen
+              {\doifcommonelse{\v!right,\v!left}{#1}
+                 {\doifnotinset\v!right{#1}\hss
+                  \box#2%
+                  \doifnotinset\v!left{#1}\hss}%
                  {\doifnot{\floatparameter\c!location}\v!left\hss
-                  \box#3%
+                  \box#2%
                   \doifnot{\floatparameter\c!location}\v!right\hss}}}%
         \fi}%
   \fi}
@@ -582,18 +585,22 @@
      [#1]
      [%\c!width=8\lineheight, % 15\bodyfontsize,
       %\c!height=6\lineheight,  % 10\bodyfontsize,
-\c!offset=\v!overlay,
-\c!width=\v!fit,
-\c!height=\v!fit,
+      \c!offset=\v!overlay,
+      \c!width=\v!fit,
+      \c!height=\v!fit,
       \c!minwidth=,
       \c!maxwidth=,
       \c!maxheight=,
       \c!criterium=,
-% inherited
+      % inherited
       \c!sidespacebefore=\@@bksidespacebefore,
       \c!sidespaceafter=\@@bksidespaceafter,
       \c!sidealign=\@@bksidealign, % \v!line
       \c!margin=\@@bkmargin,
+      \c!leftmargin=\@@bkleftmargin,
+      \c!rightmargin=\@@bkrightmargin,
+      \c!innermargin=\@@bkinnermargin,
+      \c!outermargin=\@@bkoutermargin,
       \c!leftmargindistance=\@@bkleftmargindistance,
       \c!rightmargindistance=\@@bkrightmargindistance,
       \c!frame=\@@bkframe,
@@ -612,6 +619,7 @@
      %\c!local=\@@bklocal,
       \c!textmethod=\@@bktextmethod,
       \c!sidemethod=\@@bksidemethod,
+      \c!method=\@@bkmethod,
       \c!pageboundaries=,
       \c!default=]%
    \setupcaption
@@ -627,9 +635,9 @@
      %\c!before=\@@kjbefore,
       \c!inbetween=\@@kjinbetween,
      %\c!after=\@@kjafter,
-\c!spacebefore=\@@kjspacebefore,
-\c!spaceinbetween=\@@kjspaceinbetween,
-\c!spaceafter=\@@kjspaceafter,
+      \c!spacebefore=\@@kjspacebefore,
+      \c!spaceinbetween=\@@kjspaceinbetween,
+      \c!spaceafter=\@@kjspaceafter,
       \c!width=\@@kjwidth,
       \c!minwidth=\@@kjminwidth,
       \c!align=\@@kjalign,
@@ -642,16 +650,17 @@
       \c!stopper=\@@kjstopper,
       \c!suffix=\@@kjsuffix, % hook
       \c!command=\@@kjcommand,
-      \c!conversion=\@@kjconversion
+      \c!conversion=\@@kjconversion,
+      \c!leftmargin=\@@kjleftmargin,
+      \c!rightmargin=\@@kjrightmargin,
+      \c!outermargin=\@@kjoutermargin,
+      \c!innermargin=\@@kjinnermargin,
+      \c!setups=\@@kjsetups,
      ]%
    \definenumber % \definelabel
      [#1]
      [\c!text=#1,
       \c!location=\v!intext,
-%       \c!way=\getvalue{\??kj#1\c!way},
-%       \c!blockway=\getvalue{\??kj#1\c!blockway},
-%       \c!sectionnumber=\getvalue{\??kj#1\c!sectionnumber},
-%       \c!conversion=\getvalue{\??kj#1\c!conversion}]%
       \c!way=\floatcaptionparameter\c!way,
       \c!blockway=\floatcaptionparameter\c!blockway,
       \c!sectionnumber=\floatcaptionparameter\c!sectionnumber,
@@ -684,17 +693,19 @@
       \c!maxwidth,\c!maxheight,\c!minwidth,
       \c!margin,\c!sidespacebefore,\c!sidespaceafter,\c!sidealign,
       \c!leftmargindistance,\c!rightmargindistance,\c!criterium,
+      \c!leftmargin,\c!rightmargin,\c!innermargin,\c!outermargin,
       \c!frame,\c!radius,\c!corner,\c!location,\c!background,\c!framecolor,
       \c!backgroundscreen,\c!backgroundcolor,\c!backgroundoffset,
       \c!topframe,\c!bottomframe,\c!leftframe,\c!rightframe,
       \c!frameoffset,\c!pageboundaries,\c!default,
-      \c!textmethod,\c!sidemethod]%
+      \c!textmethod,\c!sidemethod,\c!method]%
    \copyparameters[\??kj#1][\??kj#3]
      [\c!location,\c!before,\c!inbetween,\c!after,
       \c!spacebefore,\c!spaceinbetween,\c!spaceafter,
       \c!width,\c!headstyle,\c!headcolor,\c!style,\c!color,
       \c!textstyle,\c!textcolor,\c!minwidth,
-      \c!align,\c!number,\c!way,\c!blockway,
+      \c!align,\c!number,\c!way,\c!blockway,\c!setups,
+      \c!leftmargin,\c!rightmargin,\c!innermargin,\c!outermargin,
       \c!sectionnumber,\c!separator,\c!stopper,\c!suffix,\c!distance,\c!conversion]%
    \definenumber[#1][#3]%
    \presetlabeltext[#1=\labeltext{#3}]%
@@ -710,6 +721,7 @@
       \c!maxwidth,\c!maxheight,\c!minwidth,
       \c!margin,\c!sidespacebefore,\c!sidespaceafter,\c!sidealign,
       \c!leftmargindistance,\c!rightmargindistance,\c!criterium,
+      \c!leftmargin,\c!rightmargin,\c!innermargin,\c!outermargin,
       \c!frame,\c!radius,\c!corner,\c!location,\c!background,\c!framecolor,
       \c!backgroundscreen,\c!backgroundcolor,\c!backgroundoffset,
       \c!topframe,\c!bottomframe,\c!leftframe,\c!rightframe,
@@ -720,7 +732,8 @@
       \c!spacebefore,\c!spaceinbetween,\c!spaceafter,
       \c!width,\c!headstyle,\c!headcolor,\c!style,\c!color,
       \c!textstyle,\c!textcolor,\c!minwidth,
-      \c!align,\c!number,\c!way,\c!blockway,
+      \c!leftmargin,\c!rightmargin,\c!innermargin,\c!outermargin,
+      \c!align,\c!number,\c!way,\c!blockway,\c!setups,
       \c!sectionnumber,\c!separator,\c!stopper,\c!suffix,\c!distance,\c!conversion]%
    \definenumber[#1][#3]%
    \presetlabeltext[#1=\labeltext{#3}]%
@@ -867,35 +880,6 @@
 \def\doinsertfloatinfo
   {\dofloatinfomessage<4{\the\totalnoffloats}}
 
-% \def\dogetfloat
-%   {\ifsomefloatwaiting
-%      \global\setbox\floatlist\vbox
-%        {\unvbox\floatlist
-%         \global\setbox\globalscratchbox\lastbox}%
-%      \ifcenterfloatbox
-%        \ifdim\wd\globalscratchbox<\hsize
-%          \setbox\floatbox\hbox to \hsize{\hss\box\globalscratchbox\hss}%
-%        \else
-%          \setbox\floatbox\box\globalscratchbox % local !
-%          % retain special alignments
-%          \ifinsidecolumns
-%            \ifdim\wd\floatbox>\makeupwidth
-%              \wd\floatbox\makeupwidth
-%            \fi
-%          \fi
-%        \fi
-%      \else
-%        \setbox\floatbox\box\globalscratchbox % local !
-%      \fi
-%      \global\advance\savednoffloats \minusone
-%      \ifcase\savednoffloats
-%        \global\somefloatwaitingfalse
-%      \fi
-%    \else
-%      \global\savednoffloats\zerocount
-%      \global\setbox\floatbox\box\voidb@x
-%    \fi}
-
 \def\dogetfloat
   {\ifsomefloatwaiting
      \global\setbox\floatlist\vbox
@@ -1233,7 +1217,7 @@
 % voor het plaatsen van tabellen en figuren (klopt niet
 % meer).
 %
-% \dofloat         {plaats} {label1} {label2} {kader}
+% \dofloat         {plaats} {label1} {label2}
 %
 % \docompletefloat {nummer} {referentie} {lijst}
 %                  {plaats} {label1} {label2} {inhoud}
@@ -1270,17 +1254,17 @@
         {\setbox0\vbox{\whitespace\expanded{\blank[#2]}}%
          \global#1\ht0}}}
 
-\def\calculatefloatskips#1% todo floatparam
-  {{\docalculatefloatskip\floattopskip       \@@bkspacebefore
-    \docalculatefloatskip\floatbottomskip    \@@bkspaceafter
-    \docalculatefloatskip\sidefloattopskip   {\getvalue{\??fl#1\c!sidespacebefore}}% \@@bksidespacebefore
-    \docalculatefloatskip\sidefloatbottomskip{\getvalue{\??fl#1\c!sidespaceafter}}% \@@bksidespaceafter
-    \gdef\sidefloattopoffset{\openstrutdepth}% was \def
-    \global\floatsideskip      \getvalue{\??fl#1\c!margin}%
-    \global\sidefloatleftshift \getvalue{\??fl#1\c!leftmargindistance}%
-    \global\sidefloatrightshift\getvalue{\??fl#1\c!rightmargindistance}%
-    \global\noftopfloats\@@bkntop   \relax
-    \global\nofbotfloats\@@bknbottom\relax}}
+\def\calculatefloatskips
+  {{\docalculatefloatskip\floattopskip        \@@bkspacebefore
+    \docalculatefloatskip\floatbottomskip     \@@bkspaceafter
+    \docalculatefloatskip\sidefloattopskip   {\floatparameter\c!sidespacebefore}%
+    \docalculatefloatskip\sidefloatbottomskip{\floatparameter\c!sidespaceafter }%
+    \gdef  \sidefloattopoffset{\openstrutdepth}% was \def
+    \global\floatsideskip      \floatparameter\c!margin
+    \global\sidefloatleftshift \floatparameter\c!leftmargindistance
+    \global\sidefloatrightshift\floatparameter\c!rightmargindistance
+    \global\noftopfloats       \@@bkntop   \relax
+    \global\nofbotfloats       \@@bknbottom\relax}}
 
 \let\floatcaptionsuffix\empty % an optional suffix
 \let\floatcaptionnumber\empty % a logical counter
@@ -1338,20 +1322,20 @@
 \let\placefloatlabeltext      \placefloatcaptiontext
 \let\placefloatlabelreference \placefloatcaptionreference
 
-\def\borderedfloatbox#1%
-  {\localframed[\??fl#1][\c!location=\v!normal]{\box\floatbox}}
+\def\borderedfloatbox
+  {\localframed[\??fl\currentfloat][\c!location=\v!normal]{\box\floatbox}}
 
 \newbox\captionbox
 
-\def\putcompletecaption#1#2#3%
+\def\putcompletecaption#1#2%
   {\doifsomething{\floatcaptionparameter\c!spacebefore}{\blank[\floatcaptionparameter\c!spacebefore]}%
 % \floatcaptionparameter\c!before % test for side effects first
    \noindent
-   \xdef\lastcaptiontag{\strut#2}%
-   \dostartattributes{\??kj#1}\c!style\c!color\empty
+   \xdef\lastcaptiontag{\strut#1}%
+   \dostartattributes{\??kj\currentfloat}\c!style\c!color\empty
      \ifnofloatnumber
      \else
-       \hbox{\doattributes{\??kj#1}\c!headstyle\c!headcolor{\strut#2}}%
+       \hbox{\doattributes{\??kj\currentfloat}\c!headstyle\c!headcolor{\strut#1}}%
        \ifnofloatcaption \else \ifemptyfloatcaption \else
          \doifelsenothing{\floatcaptionparameter\c!spaceinbetween}
            {\scratchskip\floatcaptionparameter\c!distance\relax
@@ -1363,10 +1347,10 @@
        \globallet\lastcaptionht\!!zeropoint
        \globallet\lastcaptiondp\!!zeropoint
      \else
-       \doattributes{\??kj#1}\c!textstyle\c!textcolor
+       \doattributes{\??kj\currentfloat}\c!textstyle\c!textcolor
          {\xdef\lastcaptionht{\strutheight}%
           \xdef\lastcaptiondp{\strutdepth}%
-          \begstrut#3\endstrut\endgraf}%
+          \begstrut#2\endstrut\endgraf}%
      \fi
    \dostopattributes
 % \floatcaptionparameter\c!after % test for side effects first
@@ -1389,10 +1373,42 @@
 %\stelblokkopjesin[\c!align=\v!left]
 %\stelblokkopjesin[\c!align=\v!right]
 
-\def\docheckcaptioncontent#1#2#3#4%
+
+% \definefloat  [figure-1] [figure]
+% \definefloat  [figure-2] [figure]
+% \setupfloat   [figure-1] [location=left,leftmargin=10mm]
+% \setupfloat   [figure-2] [location=left,leftmargin=-5mm]
+% \setupcaption [figure-1] [align=flushleft]
+% \setupcaption [figure-2] [align=flushleft,leftmargin=15mm]
+%
+% \startsetups somefigure
+%     \ifdim\wd\nextbox>\textwidth
+%         \placefloat[figure-2][][]{}{\box\nextbox}
+%     \else
+%         \placefloat[figure-1][][]{}{\box\nextbox}
+%     \fi
+% \stopsetups
+%
+% \def\setupswithbox[#1]{\dowithnextbox{\setups[#1]}\vbox}
+%
+% test \setupswithbox[somefigure]{\framed[width=3cm]                         {}} test
+% test \setupswithbox[somefigure]{\framed[width=\dimexpr\textwidth+3cm\relax]{}} test
+
+\def\dosetcaptionthings
+  {\setups[\floatcaptionparameter\c!setups]% expanded ?
+%    \advance\leftskip \floatcaptionparameter\c!leftmargin
+%    \advance\rightskip\floatcaptionparameter\c!rightmargin
+   \relax}
+
+\def\dofakecaptionthings
+  {\hbox{\dosetcaptionthings\hskip\leftskip\hskip\rightskip}}
+
+\def\docheckcaptioncontent#1#2%
   {\ifnofloatcaption \else
      \setbox\tempcaptionbox\hbox
-       {\trialtypesettingtrue\notesenabledfalse\putcompletecaption{#4}{#2}{#3}}%
+       {\trialtypesettingtrue
+        \notesenabledfalse
+        \putcompletecaption{#1}{#2}}%
      % new, \placefigure{\XMLflush{somecaption}}{} passes earlier empty check
      % so here we misuse the scratch box; actually this means that the previous
      % test can go away (some day, when i redo this module)
@@ -1401,6 +1417,8 @@
        \ifnofloatnumber
          \global\nofloatcaptiontrue
        \fi
+     \else
+       \setbox\tempcaptionbox\hbox{\dosetcaptionthings\hskip\leftskip\box\tempcaptionbox}% yet incomplete
      \fi
    \fi}
 
@@ -1408,56 +1426,21 @@
 
 \ifx\moveboxontogrid\undefined \let\movecaptionontogrid\gobblethreearguments \fi
 
-% \def\dosetpagfloat#1#2#3#4% \copy wegwerken
-%   {\bgroup
-%    \setlocalfloathsize
-%    \ifnum\floatrotation>0
-%      \swapdimens\hsize\vsize
-%    \fi
-%    \forgetall
-%    \postponenotes
-%    \dontcomplain
-%    \setbox\tempfloatbox\vbox{\borderedfloatbox{#4}}%
-%    \def\locatefloat
-%      {\chardef\alignstrutmode\zerocount
-%       \alignedline{\floatparameter\c!location}\v!middle}%
-%    \docheckcaptioncontent{#1}{#2}{#3}{#4}%
-%    \ifnofloatcaption
-%      \dopreparenocaption{#1}{#2}{#3}{#4}%
-%      \edef\width{\the\wd\floatbox}%
-%      \doglobal\addlocalbackgroundtobox\floatbox
-%    \else
-%      % todo: installable maken, variant/method=auto vs macro
-%      \doifinsetelse{\floatcaptionparameter\c!location}{\v!high,\v!middle,\v!low}
-%        {\dopreparesidecaption{#1}{#2}{#3}{#4}}
-%        {\doifelse{\floatcaptionparameter\c!minwidth}\v!fit
-%           {\doifelse{\floatcaptionparameter\c!width}\v!max
-%              {\dopreparestackcaptionmax{#1}{#2}{#3}{#4}}
-%              {\ifdim\wd\tempcaptionbox>\wd\tempfloatbox % wider caption
-%                 \doifelse{\floatcaptionparameter\c!width}\v!fit
-%                   {\dopreparestackcaptionaut{#1}{#2}{#3}{#4}}
-%                   {\dopreparestackcaptionwid{#1}{#2}{#3}{#4}}%
-%               \else
-%                 \dopreparestackcaptionmin{#1}{#2}{#3}{#4}%
-%               \fi}}
-%           {\dopreparestackcaptionfix{#1}{#2}{#3}{#4}}}% new, special effects (see icare)
-%      \edef\width{\the\wd\tempfloatbox}%
-%      \addlocalbackgroundtobox\tempfloatbox
-%      \setbox\tempcaptionbox\hbox{\floatcaptionparameter\c!command{\box\tempcaptionbox}}%
-%      \moveboxontogrid\tempcaptionbox{\floatcaptionparameter\c!grid}\lastcaptionht
-%      \addlocalbackgroundtobox\tempcaptionbox
-%      \buildfloatbox
-%    \fi
-%    \ifnum\floatrotation>0
-%      \global\setbox\floatbox\vbox
-%         {\rotate[\c!rotation=\floatrotation]{\box\floatbox}}%
-%          \edef\width{\the\wd\tempfloatbox}%
-%    \else
-%      \postcenterfloatbox\width
-%    \fi
-%    \egroup}
-
-\def\dosetpagfloat#1#2#3#4% \copy wegwerken
+\def\locatefloatbox
+  {\chardef\alignstrutmode\zerocount
+   \shiftalignedline
+     {\floatparameter\c!leftmargin }{\floatparameter\c!rightmargin}%
+     {\floatparameter\c!innermargin}{\floatparameter\c!outermargin}%
+   \alignedline{\floatparameter\c!location}\v!middle}
+
+\def\locatecaptionbox
+  {\chardef\alignstrutmode\zerocount
+   \shiftalignedline
+     {\floatcaptionparameter\c!leftmargin }{\floatcaptionparameter\c!rightmargin}%
+     {\floatcaptionparameter\c!innermargin}{\floatcaptionparameter\c!outermargin}%
+   \alignedline{\floatparameter\c!location}\v!middle}
+
+\def\dosetpagfloat#1#2#3% \copy wegwerken
   {\bgroup
    \setlocalfloathsize
    \ifnum\floatrotation>0
@@ -1466,25 +1449,31 @@
    \forgetall
    \postponenotes
    \dontcomplain
-   \setbox\tempfloatbox\vbox{\borderedfloatbox{#4}}%
-   \def\locatefloat
-     {\chardef\alignstrutmode\zerocount
-      \alignedline{\floatparameter\c!location}\v!middle}%
-   \docheckcaptioncontent{#1}{#2}{#3}{#4}%
-   \ifnofloatcaption
-     \dopreparenocaption{#1}{#2}{#3}{#4}%
-     \edef\width{\the\wd\floatbox}%
-     \doglobal\addlocalbackgroundtobox\floatbox
-   \else
-     % todo: installable maken, variant/method=auto vs macro
-     \dopreparedocaption{#1}{#2}{#3}{#4}%
-     \settracedcaptionbox
-     \edef\width{\the\wd\tempfloatbox}%
-     \addlocalbackgroundtobox\tempfloatbox
-     \setbox\tempcaptionbox\hbox{\floatcaptionparameter\c!command{\box\tempcaptionbox}}%
-     \moveboxontogrid\tempcaptionbox{\floatcaptionparameter\c!grid}\lastcaptionht
-     \addlocalbackgroundtobox\tempcaptionbox
-     \buildfloatbox
+   \setbox\tempfloatbox\vbox{\borderedfloatbox}%
+   \let\locatefloat  \locatefloatbox
+   \let\locatecaption\locatecaptionbox
+   \docheckcaptioncontent{#2}{#3}%
+   \ifcase\floatparameter\c!method
+   \or % automatic
+     \ifnofloatcaption
+       \dopreparenocaption{#1}{#2}{#3}%
+       \edef\width{\the\wd\floatbox}%
+       \doglobal\addlocalbackgroundtobox\floatbox
+     \else
+       % todo: installable maken, variant/method=auto vs macro
+       \dopreparedocaption{#1}{#2}{#3}%
+       \settracedcaptionbox
+       \edef\width{\the\wd\tempfloatbox}%
+       \addlocalbackgroundtobox\tempfloatbox
+       \setbox\tempcaptionbox\hbox
+         {\dosetcaptionthings
+          \floatcaptionparameter\c!command{\box\tempcaptionbox}}%
+       \moveboxontogrid\tempcaptionbox{\floatcaptionparameter\c!grid}\lastcaptionht
+       \addlocalbackgroundtobox\tempcaptionbox
+       \buildfloatbox
+     \fi
+   \or % semi automatic
+   \or % manual
    \fi
    \ifnum\floatrotation>0
      \global\setbox\floatbox\vbox
@@ -1498,31 +1487,31 @@
 \def\captionminwidth  {15\bodyfontsize}
 \def\captionovershoot {2em}
 
-\def\dopreparenocaption#1#2#3#4%
+\def\dopreparenocaption#1#2#3%
   {\global\setbox\floatbox\vbox % pas op als wd groter dan hsize
      {\ifinsidecolumns\ifdim\wd\tempfloatbox>\hsize
         \let\locatefloat\relax
       \fi\fi
       \locatefloat{\copy\tempfloatbox}}}
 
-\def\dopreparedocaption#1#2#3#4%
+\def\dopreparedocaption#1#2#3%
   {\doifinsetelse{\floatcaptionparameter\c!location}{\v!top,\v!bottom}
      {\doifinsetelse{\floatcaptionparameter\c!width}{\v!fit,\v!max}
         {\doifelse{\floatcaptionparameter\c!minwidth}\v!fit
            {\doifelse{\floatcaptionparameter\c!width}\v!max
-             {\dopreparestackcaptionmax{#1}{#2}{#3}{#4}}
+             {\dopreparestackcaptionmax{#1}{#2}{#3}}
              {\ifdim\wd\tempcaptionbox>\wd\tempfloatbox % wider caption
                 \doifelse{\floatcaptionparameter\c!width}\v!fit
-                  {\dopreparestackcaptionaut{#1}{#2}{#3}{#4}}
-                  {\dopreparestackcaptionwid{#1}{#2}{#3}{#4}}%
+                  {\dopreparestackcaptionaut{#1}{#2}{#3}}
+                  {\dopreparestackcaptionwid{#1}{#2}{#3}}%
               \else
-                \dopreparestackcaptionmin{#1}{#2}{#3}{#4}%
+                \dopreparestackcaptionmin{#1}{#2}{#3}%
               \fi}}
-           {\dopreparestackcaptionfix{#1}{#2}{#3}{#4}}}%
-        {\dopreparesidewidthcaption{#1}{#2}{#3}{#4}}}% new, special effects (see icare)
+           {\dopreparestackcaptionfix{#1}{#2}{#3}}}%
+        {\dopreparesidewidthcaption{#1}{#2}{#3}}}% new, special effects (see icare)
      {\doifinsetelse{\floatcaptionparameter\c!width}{\v!fit,\v!max}
-        {\dopreparesideautocaption{#1}{#2}{#3}{#4}}
-        {\dopreparesidewidthcaption{#1}{#2}{#3}{#4}}}}
+        {\dopreparesideautocaption{#1}{#2}{#3}}
+        {\dopreparesidewidthcaption{#1}{#2}{#3}}}}
 
 % \def\dosettempcaptionbox
 %   {\dosetraggedvbox{\floatcaptionparameter\c!align}%
@@ -1530,11 +1519,12 @@
 
 \def\dosettempcaptionbox
   {\setbox\tempcaptionbox\vbox\bgroup
-   %expanded{\setupalign[\v!new,\v!reset,\floatcaptionparameter\c!align,\v!old]}% wrong! see icare
-    \expanded{\setupalign[\v!reset,\floatcaptionparameter\c!align]}% i need to check what reset does
-    \let\next}
+     %expanded{\setupalign[\v!new,\v!reset,\floatcaptionparameter\c!align,\v!old]}% wrong! see icare
+     \expanded{\setupalign[\v!reset,\floatcaptionparameter\c!align]}% i need to check what reset does
+     \dosetcaptionthings
+     \let\next}
 
-\def\dopreparesideautocaption#1#2#3#4%
+\def\dopreparesideautocaption#1#2#3%
   {\scratchdimen\dimexpr\hsize-\wd\tempfloatbox-\@@bkmargin\relax % was \tfskipsize\relax
    \ifdim\wd\tempcaptionbox>\scratchdimen
      \ifdim\wd\tempcaptionbox<1.3\scratchdimen
@@ -1543,35 +1533,35 @@
    \fi
    \dosettempcaptionbox
      {\hsize\scratchdimen
-      \putcompletecaption{#4}{#2}{#3}}}
+      \putcompletecaption{#2}{#3}}}
 
-\def\dopreparesidewidthcaption#1#2#3#4%
+\def\dopreparesidewidthcaption#1#2#3%
   {\dosettempcaptionbox
      {\hsize\floatcaptionparameter\c!width
-      \putcompletecaption{#4}{#2}{#3}}}
+      \putcompletecaption{#2}{#3}}}
 
-\def\dopreparestackcaptionfix#1#2#3#4%
+\def\dopreparestackcaptionfix#1#2#3%
   {\dosettempcaptionbox
      {\hsize\floatcaptionparameter\c!minwidth % special effects
-      \putcompletecaption{#4}{#2}{#3}}}
+      \putcompletecaption{#2}{#3}}}
 
-\def\dopreparestackcaptionmax#1#2#3#4%
+\def\dopreparestackcaptionmax#1#2#3%
   {\dosettempcaptionbox
      {\hsize\wd\tempfloatbox
-      \putcompletecaption{#4}{#2}{#3}}}
+      \putcompletecaption{#2}{#3}}}
 
-\def\dopreparestackcaptionwid#1#2#3#4%
+\def\dopreparestackcaptionwid#1#2#3%
   {\dosettempcaptionbox
      {\hsize\floatcaptionparameter\c!width
-      \putcompletecaption{#4}{#2}{#3}}}
+      \putcompletecaption{#2}{#3}}}
 
-\def\dopreparestackcaptionmin#1#2#3#4%
+\def\dopreparestackcaptionmin#1#2#3%
   {\dosettempcaptionbox
      {\hsize\wd\tempfloatbox
       \doifnothing{\floatcaptionparameter\c!align}\raggedcenter % on purpose overloads align !
-      \putcompletecaption{#4}{#2}{#3}}}
+      \putcompletecaption{#2}{#3}}}
 
-\def\dopreparestackcaptionaut#1#2#3#4%
+\def\dopreparestackcaptionaut#1#2#3%
   {\doifsomething{\floatcaptionparameter\c!align}
      {\doifnotinset{\v!middle}{\floatcaptionparameter\c!align}%
         {\let\captionovershoot\!!zeropoint}}%
@@ -1582,7 +1572,7 @@
        {\trialtypesettingtrue
         \hsize\captionhsize
         \notesenabledfalse
-        \putcompletecaption{#4}{#2}{#3}}%
+        \putcompletecaption{#2}{#3}}%
      \ifdim\ht\scratchbox>\lineheight % more lines
        \dosettempcaptionbox
          {\hsize\captionhsize
@@ -1590,11 +1580,11 @@
           \ifdim\hsize<\captionminwidth\relax
             \hsize\captionhsize
           \fi
-          \putcompletecaption{#4}{#2}{#3}}%
+          \putcompletecaption{#2}{#3}}%
      \else
        \dosettempcaptionbox
          {\hsize\captionhsize
-          \putcompletecaption{#4}{#2}{#3}}%
+          \putcompletecaption{#2}{#3}}%
      \fi
    \else
      % float is smaller of equal to \hsize
@@ -1609,30 +1599,30 @@
         \advance\scratchdimen 3em % an average word length
         \ifdim\scratchdimen<\hsize \hsize\scratchdimen \fi
         \notesenabledfalse
-        \putcompletecaption{#4}{#2}{#3}}%
+        \putcompletecaption{#2}{#3}}%
      \ifdim\ht\scratchbox>\lineheight
        % at least an average word longer than a line
        \dosettempcaptionbox
          {\scratchdimen\captionhsize
           \advance\scratchdimen \captionovershoot
           \ifdim\scratchdimen<\hsize \hsize\scratchdimen \fi
-          \putcompletecaption{#4}{#2}{#3}}%
+          \putcompletecaption{#2}{#3}}%
      \else
        % just over a line, don't use an overshoot % % % todo: outer/inner and such
        \doifcommonelse{\floatcaptionparameter\c!align}{\v!left,\v!right,\v!flushleft,\v!flushright}
          {\dosettempcaptionbox
             {\hsize\captionhsize
              % strange : \raggedcenter
-             \putcompletecaption{#4}{#2}{#3}}}
+             \putcompletecaption{#2}{#3}}}
          {% nicer
           \dosettempcaptionbox
             {\hsize\captionhsize
              \doifnothing{\floatcaptionparameter\c!align}\raggedcenter% overloads
-             \putcompletecaption{#4}{#2}{#3}}}%
+             \putcompletecaption{#2}{#3}}}%
      \fi
    \fi}
 
-\def\dopreparesidecaption#1#2#3#4%
+\def\dopreparesidecaption#1#2#3%
   {\scratchdimen\dimexpr\hsize-\wd\tempfloatbox-\@@bkmargin\relax % was \tfskipsize\relax
    \ifdim\wd\tempcaptionbox>\scratchdimen
      \ifdim\wd\tempcaptionbox<1.3\scratchdimen
@@ -1642,7 +1632,7 @@
    \dosettempcaptionbox %    \setbox\tempcaptionbox\vbox
      {\hsize\scratchdimen
       \doifnothing{\floatcaptionparameter\c!align}\raggedright % on purpose overloads align !
-      \putcompletecaption{#4}{#2}{#3}}}
+      \putcompletecaption{#2}{#3}}}
 
 \newdimen\tempfloatheight
 \newdimen\tempfloatwidth
@@ -1781,62 +1771,6 @@
 \def\dofloatboxmiddlebuilder
   {\dofloatboxnextbuilder{\vfill\box\tempcaptionbox\vfill}}
 
-% \def\dofloatboxnormalstackbuilder#1#2#3#4% hbox needed
-%   {\tempfloatwidth\wd\tempfloatbox
-%    \ifparfloat
-%      \hbox{#3}\dofloatboxbetweenstack\hbox{#4}%
-%    \else
-%      \hbox{#1}\dofloatboxbetweenstack\hbox{#2}%
-%    \fi}
-
-% \def\dofloatboxgridstackbuilder#1#2#3#4%
-%   {\dp\tempcaptionbox\strutdepth
-%    \setbox\scratchbox\vbox
-%      {\tempfloatwidth\wd\tempfloatbox
-%       \ifparfloat
-%         #3\vss\dofloatboxbetweenstack#4%
-%       \else
-%         #1\vss\dofloatboxbetweenstack#2%
-%       \fi}%
-%    \getnoflines{\dimexpr\htdp\scratchbox-10\scaledpoint\relax}% get rid of inaccuracy
-%    \vbox to \noflines\lineheight{\unvbox\scratchbox}}
-
-% \def\dofloatboxstretchstackbuilder#1#2#3#4%
-%   {\dp\tempcaptionbox\strutdepth
-%    \setbox\scratchbox\vbox
-%      {\locatefloat{\copy#1}%
-%       \locatefloat{\copy#2}}%
-%    \getnoflines{\dimexpr\htdp\scratchbox-10\scaledpoint\relax}% get rid of inaccuracy
-%    \vbox to \noflines\lineheight
-%      {\tempfloatwidth\wd\tempfloatbox
-%       \ifparfloat
-%         #3\vss\dofloatboxbetweenstack\vss#4%
-%       \else
-%         #1\vss\dofloatboxbetweenstack\vss#2%
-%       \fi}}
-
-% \def\dofloatboxtopbuilder
-%   {\let\next\dofloatboxnormalstackbuilder
-%    \expanded{\processallactionsinset[\floatcaptionparameter\c!location]}
-%      [   \v!grid=>\let\next\dofloatboxgridstackbuilder,
-%       \v!stretch=>\let\next\dofloatboxstretchstackbuilder]%
-%    \next
-%      {\locatetextfloat{\box\tempcaptionbox}}
-%      {\locatefloat    {\box\tempfloatbox  }}
-%      {\locatesidefloat{\box\tempcaptionbox}}
-%      {\hbox           {\box\tempfloatbox  }}}
-
-% \def\dofloatboxbottombuilder
-%   {\let\next\dofloatboxnormalstackbuilder
-%    \expanded{\processallactionsinset[\floatcaptionparameter\c!location]}
-%      [   \v!grid=>\let\next\dofloatboxgridstackbuilder,
-%       \v!stretch=>\let\next\dofloatboxstretchstackbuilder]%
-%    \next
-%      {\locatefloat    {\box\tempfloatbox  }}
-%      {\locatetextfloat{\box\tempcaptionbox}}
-%      {\hbox           {\box\tempfloatbox  }}
-%      {\locatesidefloat{\box\tempcaptionbox}}}
-
 % \definefloat
 %   [lefty][lefties][figure]
 % \setupfloat
@@ -1917,8 +1851,8 @@
 \def\dofloatboxstretchtopstackbuilder
   {\dp\tempcaptionbox\strutdepth
    \setbox\scratchbox\vbox
-     {\locatefloat{\copy\tempcaptionbox}%
-      \locatefloat{\copy\tempfloatbox  }}%
+     {\locatecaption{\copy\tempcaptionbox}%
+      \locatefloat  {\copy\tempfloatbox  }}%
    \getnoflines{\dimexpr\htdp\scratchbox-10\scaledpoint\relax}% get rid of inaccuracy
    \vbox to \noflines\lineheight
      {\tempfloatwidth\wd\tempfloatbox
@@ -1935,8 +1869,8 @@
 \def\dofloatboxstretchbotstackbuilder
   {\dp\tempcaptionbox\strutdepth
    \setbox\scratchbox\vbox
-     {\locatefloat{\copy\tempfloatbox  }%
-      \locatefloat{\copy\tempcaptionbox}}%
+     {\locatefloat  {\copy\tempfloatbox  }%
+      \locatecaption{\copy\tempcaptionbox}}%
    \getnoflines{\dimexpr\htdp\scratchbox-10\scaledpoint\relax}% get rid of inaccuracy
    \vbox to \noflines\lineheight
      {\tempfloatwidth\wd\tempfloatbox
@@ -1964,8 +1898,8 @@
       \v!stretch=>\let\next\dofloatboxstretchstackbuilder]%
    \next}
 
-\def\relocatecaptionright#1{\locatefloat{\hbox to \tempfloatwidth{\hss#1}}}
-\def\relocatecaptionleft #1{\locatefloat{\hbox to \tempfloatwidth{#1\hss}}}
+\def\relocatecaptionright#1{\locatecaption{\hbox to \tempfloatwidth{\hss#1}}}
+\def\relocatecaptionleft #1{\locatecaption{\hbox to \tempfloatwidth{#1\hss}}}
 
 \long\def\installfloatboxbuilder#1#2{\setvalue{\??kj:#1}{#2}}
 
@@ -1980,7 +1914,7 @@
       \executeifdefined{\??kj:\floatcaptionarrangement}{\getvalue{\??kj:\s!default}}}}
 
 \def\locatetextfloat
-  {\let\next\locatefloat
+  {\let\next\locatecaption
    \expanded{\processallactionsinset[\floatcaptionparameter\c!location]}
      [ \v!left=>\let\next\relocatecaptionleft,
       \v!right=>\let\next\relocatecaptionright,
@@ -2041,19 +1975,19 @@
       \box\floatbox
       \ifdone\hskip\effectiverightskip\fi\hss}}
 
-\def\dosetparfloat#1#2#3#4%
+\def\dosetparfloat#1#2#3%
   {\bgroup
    \forgetall
    \postponenotes
    \dontcomplain
    %\showcomposition
-   \setbox\tempfloatbox\vbox{\borderedfloatbox{#4}}%
+   \setbox\tempfloatbox\vbox{\borderedfloatbox}%
    \addlocalbackgroundtobox\tempfloatbox % no \doglobal
-   \docheckcaptioncontent{#1}{#2}{#3}{#4}%
+   \docheckcaptioncontent{#2}{#3}%
    \ifnofloatcaption
      \global\setbox\floatbox\vbox{\box\tempfloatbox}%
    \else
-     \dopreparedosidecaption{#1}{#2}{#3}{#4}%
+     \dopreparedosidecaption{#1}{#2}{#3}%
      \settracedcaptionbox
      \setbox\tempcaptionbox\hbox{\floatcaptionparameter\c!command{\box\tempcaptionbox}}%
      \moveboxontogrid\tempcaptionbox{\floatcaptionparameter\c!grid}\lastcaptionht
@@ -2062,24 +1996,29 @@
    \fi
    \egroup}
 
-\def\dopreparedosidecaption#1#2#3#4% will be enhanced
+\def\dopreparedosidecaption#1#2#3% will be enhanced
   {\doifelse{\floatcaptionparameter\c!width}\v!max
      {\dosettempcaptionbox
-        {\hsize\wd\tempfloatbox\putcompletecaption{#4}{#2}{#3}}}%
+        {\hsize\wd\tempfloatbox
+         \putcompletecaption{#2}{#3}}}%
      {\doifelse{\floatcaptionparameter\c!width}\v!fit
         {\ifdim\wd\tempcaptionbox>\wd\tempfloatbox\relax
            \setbox\tempcaptionbox\vbox
-             {\forgetall\hsize\wd\tempfloatbox\putcompletecaption{#4}{#2}{#3}}%
+             {\forgetall % needed?
+              \hsize\wd\tempfloatbox
+              \dosetcaptionthings
+              \putcompletecaption{#2}{#3}}%
          \else
            \setbox\tempcaptionbox\hbox to \wd\tempfloatbox
              {\hss\box\tempcaptionbox\hss}%
          \fi}
         {\dosettempcaptionbox
            {\hsize\floatcaptionparameter\c!width % \wd\tempfloatbox
-            \putcompletecaption{#4}{#2}{#3}}}}}
+            \putcompletecaption{#2}{#3}}}}}
 
 \def\buildsidefloatbox
-  {\let\locatefloat\relax
+  {\let\locatefloat  \relax
+   \let\locatecaption\relax
    \def\locatesidefloat##1%
      {\begingroup
       \chardef\alignstrutmode\zerocount
@@ -2090,16 +2029,12 @@
 
 \newif\ifparfloat
 
-\long\def\dosetfloatbox#1#2#3#4% todo : \global\setbox
+\long\def\dosetfloatbox#1#2#3% todo : \global\setbox
   {\ifvisible
      \par
      \edef\floatcaptiondirectives{\floatparameter\c!location,\floatcaptionparameter\c!location}%
-     \ifparfloat
-       \@EA\dosetparfloat % {#1}{#2}{#3}{#4}%
-     \else
-       \@EA\dosetpagfloat % {#1}{#2}{#3}{#4}%
-     \fi{#1}{#2}{#3}{#4}%
-     \setlocalfloatdimensions{#4}{#1}\floatbox\global % tzt arg 3/4 weg
+     \ifparfloat\@EA\dosetparfloat\else\@EA\dosetpagfloat\fi{#1}{#2}{#3}%
+     \setlocalfloatdimensions{#1}\floatbox\global % tzt arg 3/4 weg
      \setbox\floatbox\hbox
        {\dosavefloatdata\restoretextcolor{\box\floatbox}}%
      \global\floatheight\ht\floatbox
@@ -2126,8 +2061,8 @@
 
 \newcounter\noxfloatlocations
 
-\long\def\dofloat#1#2#3#4%
-  {\dosetfloatbox{#1}{#2}{#3}{#4}%
+\long\def\dofloat#1#2#3%
+  {\dosetfloatbox{#1}{#2}{#3}%
    \dogetfloatbox{#1}\empty}
 
 \let\naturalfloatheight\!!zeropoint
@@ -2159,7 +2094,7 @@
         \vss % gets rid of the depth (unless tabulate)
         \rawpagereference\s!flt{#2}}%
       \egroup
-      \dofloat{#4}{}{#6}{#1}%
+      \dofloat{#4}{}{#6}%
    \else
      \doglobal\convertargument#6\to\asciititle % \asciititle is global
      \ifnofloatnumber
@@ -2167,7 +2102,7 @@
          {\unvbox\floatbox % no \vss, keep the depth
           \rawreference\s!flt{#2}{{}{\asciititle}}}%
        \egroup
-       \dofloat{#4}{}{#6}{#1}%
+       \dofloat{#4}{}{#6}%
      \else
        \preparefloatnumber{#1}%
        \global\setbox\floatbox\vbox
@@ -2177,7 +2112,7 @@
           \dowritetolist{#3}{\composedsectionnumber}{#6}{#3}}%
        \egroup
        \preparefullnumber{\??kj#1}\composedsectionnumber\preparednumber
-       \dofloat{#4}{\labeltexts{#5}{\preparednumber}}{#6}{#1}%
+       \dofloat{#4}{\labeltexts{#5}{\preparednumber}}{#6}%
      \fi
    \fi
    \global\insidefloatfalse}
@@ -2454,13 +2389,18 @@
    \c!before=, % not used (yet)
    \c!inbetween={\blank[\v!medium]},
    \c!after=, % not used (yet)
-\c!spacebefore=,
-\c!spaceinbetween=, % replaces fuzzy inbetween dual usage
-\c!spaceafter=,
+   \c!spacebefore=,
+   \c!spaceinbetween=, % replaces fuzzy inbetween dual usage
+   \c!spaceafter=,
    \c!width=\v!fit,
    \c!minwidth=\v!fit, % id est: the width of the floatbox in some cases
    \c!headstyle=\v!bold,
    \c!headcolor=,
+   \c!leftmargin=\zeropoint,
+   \c!rightmargin=\zeropoint,
+   \c!outermargin=\zeropoint,
+   \c!innermargin=\zeropoint,
+   \c!setups=,
    \c!style=\v!normal,
    \c!color=,
    \c!textstyle=,
@@ -2505,6 +2445,11 @@
    \c!sidemethod=\ifgridsnapping2\else1\fi,   % 0=raw 1=safe (.99pg) 2=tight (-1pt)
    \c!indentnext=\v!no,
    \c!margin=1em,
+   \c!method=1,
+   \c!leftmargin=\zeropoint,  % displacement in 'normal floats'
+   \c!rightmargin=\zeropoint, % idem
+   \c!innermargin=\zeropoint, % idem
+   \c!outermargin=\zeropoint, % idem
    \c!leftmargindistance=\zeropoint,
    \c!rightmargindistance=\@@bkleftmargindistance,
    \c!ntop=2,
diff --git a/tex/context/base/page-imp.tex b/tex/context/base/page-imp.tex
index 3acf201bc..039f36f4c 100644
--- a/tex/context/base/page-imp.tex
+++ b/tex/context/base/page-imp.tex
@@ -144,7 +144,7 @@
 \let\pagestoshipout\empty      % {1,3,6}
 \chardef\whichpagetoshipout=0  % 0=all 1=odd 2=even
 
-\ifx\processshipoutbox\undefined \let\processshipoutbox\firstofoneargument \fi
+\ifx\finalizeshipoutbox\undefined \let\finalizeshipoutbox\firstofoneargument \fi
 
 \def\actualshipout#1%
   {\global\advance\shippedoutpages\plusone
@@ -172,7 +172,7 @@
         \vskip\scratchdimen
         \hskip\scratchdimen
         \hbox % \setbox0=\box.. is nicer
-          {\setbox0\hbox{\processshipoutbox{#1}}% just in case there are objects there, hook for testing
+          {\setbox0\hbox{\finalizeshipoutbox{#1}}% just in case there are objects there, hook for testing
            \setbox\scratchbox\hbox
              {% before the main one !
               \ifcase\realfolio \or
diff --git a/tex/context/base/page-ini.tex b/tex/context/base/page-ini.tex
index a51395730..2582f2dc0 100644
--- a/tex/context/base/page-ini.tex
+++ b/tex/context/base/page-ini.tex
@@ -780,9 +780,6 @@
 
 %D Some hooks:
 
-\newtoks \everybeforeoutput
-\newtoks \everyafteroutput
-
 \output{\inotrtrue\the\everybeforeoutput\the\mainoutput\the\everyafteroutput}
 
 \installoutput\synchronizeoutput % maybe add pagediscards
diff --git a/tex/context/base/prop-ini.tex b/tex/context/base/prop-ini.tex
index db6a45c8d..b7aef6e18 100644
--- a/tex/context/base/prop-ini.tex
+++ b/tex/context/base/prop-ini.tex
@@ -39,14 +39,6 @@
   {\csname\s!check\currentpropertytype property\endcsname
    \global\expandafter\let\csname\??py\s!check\currentproperty\endcsname\empty}
 
-% \def\checkproperty[#1]%
-%   {\bgroup
-%    \def\currentproperty{#1}%
-%    \docheckproperty
-%    \egroup}
-%
-% oeps, was wrong, no reset
-
 \def\checkproperty[#1]%
   {\bgroup
    \def\currentproperty{#1}%
@@ -122,14 +114,14 @@
    \doifelsevalue{\??py#1\c!method}\v!command
      {\doifelsevalue{\??py#1\c!global}\v!yes
         {\setgvalue{\e!start#1}{\dostartproperty{#1}}%
-         \letgvalue{\e!stop#1}\dostopproperty}%
+         \letgvalue{\e!stop #1}\dostopproperty}%
         {\setgvalue{\e!start#1}{\dostartgproperty{#1}}%
-         \letgvalue{\e!stop#1}\dostopgproperty}}%
+         \letgvalue{\e!stop #1}\dostopgproperty}}%
      {\doifelsevalue{\??py#1\c!global}\v!yes
         {\setgvalue{\e!start#2}[##1]{\dostartproperty{##1}}%
-         \letgvalue{\e!stop#2}\dostopproperty}%
+         \letgvalue{\e!stop #2}\dostopproperty}%
         {\setgvalue{\e!start#2}[##1]{\dostartgproperty{##1}}%
-         \letgvalue{\e!stop#2}\dostopgproperty}}}
+         \letgvalue{\e!stop #2}\dostopgproperty}}}
 
 \def\nododefineproperty[#1][#2][#3]%
   {}
@@ -146,17 +138,6 @@
      \getparameters[\??py][#1]%
    \fi}
 
-% \def\propertyparameter#1% expands to #1 when not defined (see \define...)
-%   {\csname\??py
-%      \ifcsname\??py\currentproperty#1\endcsname
-%        \currentproperty#1%
-%      \else\ifcsname\??py\currentpropertytype#1\endcsname
-%        \currentpropertytype#1%
-%      \else
-%        :n:\currentproperty
-%      \fi\fi
-%    \endcsname}
-
 \letvalue{\??py\s!empty}\empty
 
 % beware, normally \*parameter concerns the current one
diff --git a/tex/context/base/prop-lay.tex b/tex/context/base/prop-lay.tex
index 0e5038d19..f29298976 100644
--- a/tex/context/base/prop-lay.tex
+++ b/tex/context/base/prop-lay.tex
@@ -17,14 +17,6 @@
 
 \unprotect
 
-% \def\checklayerproperty
-%   {\dodefineviewerlayer
-%      \currentproperty % tag
-%      {\checkedpropertyparameter\c!title\currentproperty}%
-%      {\checkedpropertyparameter\c!state\v!start}% visible or hidden
-%      {0}% type (1=frozen)
-%      {0}}% printable
-
 \def\checklayerproperty
   {\doifelse{\checkedpropertyparameter\v!printable\currentproperty}\v!no
      {\def\printviewerlayer{0}}
diff --git a/tex/context/base/prop-mis.mkii b/tex/context/base/prop-mis.mkii
new file mode 100644
index 000000000..3b372546d
--- /dev/null
+++ b/tex/context/base/prop-mis.mkii
@@ -0,0 +1,155 @@
+%D \module
+%D   [       file=prop-mis,
+%D        version=2004.05.29, % some code moved from private modules
+%D          title=\CONTEXT\ Property Macros,
+%D       subtitle=Miscelaneous,
+%D         author=Hans Hagen,
+%D           date=\currentdate,
+%D      copyright={PRAGMA / Hans Hagen \& Ton Otten}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+%D Overprint cum suis.
+
+\definepropertyhandler \v!overprint {\dostartoverprint}
+\definepropertyhandler \v!knockout  {\dostopoverprint }
+
+\def\startoverprintproperty
+  {\ifincolor
+     \propertyhandler\currentproperty
+     \dooverprintmark\currentproperty
+   \fi}
+
+\def\stopoverprintproperty
+  {\ifincolor
+     \ifcase\currentpropertylevel\or
+       \dostopoverprint
+       \dooverprintmark\empty
+     \else
+       \propertyhandler\previousproperty
+       \dooverprintmark\previousproperty
+     \fi
+   \fi}
+
+\rawnewmark\overprintmark
+
+\def\dooverprintmark#1%
+  {\ifinpagebody \else \ifinframed \else
+     \expanded{\rawsetmark\noexpand\overprintmark{#1}}%
+   \fi \fi}
+
+\def\pushoverprintproperty
+  {\doifsomething{\rawgetbotmark\overprintmark}\dostopoverprint}
+
+\def\popoverprintproperty
+  {\doifsomething{\rawgetbotmark\overprintmark}%
+     {\propertyhandler{\rawgetbotmark\overprintmark}}}
+
+\def\popsplitoverprintproperty
+  {\getsplitmarks\overprintmark  % hier wel
+   \doifsomething{\rawgetsplitbotmark\overprintmark}%
+     {\propertyhandler{\rawgetsplitbotmark\overprintmark}}}
+
+\appendtoks \pushoverprintproperty     \to \everypushproperties
+\appendtoks \popoverprintproperty      \to \everypopproperties
+\appendtoks \popsplitoverprintproperty \to \everypopsplitproperties
+
+%D Negative cum suis.
+
+\definepropertyhandler \v!negative {\dostartnegative}
+\definepropertyhandler \v!positive {\dostopnegative }
+
+\def\startnegativeproperty
+  {\ifincolor
+     \propertyhandler\currentproperty
+     \donegativemark\currentproperty
+   \fi}
+
+\def\stopnegativeproperty
+  {\ifincolor
+     \ifcase\currentpropertylevel\or
+       \dostopnegative
+       \donegativemark\empty
+     \else
+       \propertyhandler\previousproperty
+       \donegativemark\previousproperty
+     \fi
+   \fi}
+
+\rawnewmark\negativemark
+
+\def\donegativemark#1%
+  {\ifinpagebody \else \ifinframed \else
+     \expanded{\rawsetmark\noexpand\negativemark{#1}}%
+   \fi \fi}
+
+\def\pushnegativeproperty
+  {\doifsomething{\rawgetbotmark\negativemark}\dostopnegative}
+
+\def\popnegativeproperty
+  {\doifsomething{\rawgetbotmark\overprintmark}%
+     {\propertyhandler{\rawgetbotmark\negativemark}}}
+
+\def\popsplitnegativeproperty
+  {\getsplitmarks\negativemark  % hier wel
+   \doifsomething{\rawgetsplitbotmark\negativemark}%
+     {\propertyhandler{\rawgetsplitbotmark\negativemark}}}
+
+\appendtoks \pushnegativeproperty     \to \everypushproperties
+\appendtoks \popnegativeproperty      \to \everypopproperties
+\appendtoks \popsplitnegativeproperty \to \everypopsplitproperties
+
+%D Effects.
+
+\definepropertyhandler \v!normal {0}
+\definepropertyhandler \v!inner  {0}
+\definepropertyhandler \v!outer  {1}
+\definepropertyhandler \v!both   {2}
+\definepropertyhandler \v!hidden {3}
+
+\def\effectpropertydata#1%
+  {{\propertyhandler{#1}}%
+   {\propertyparameter{#1}\c!rulethickness}%
+   {\propertyparameter{#1}\c!stretch}}
+
+\def\starteffectproperty
+  {\expanded{\dostartfonteffect\effectpropertydata\currentproperty}%
+   \doeffectmark{\effectpropertydata\currentproperty}}
+
+\def\stopeffectproperty
+  {\dostopfonteffect
+   \ifcase\currentpropertylevel\or
+     \doeffectmark\empty
+   \else
+     \expanded{\dostartfonteffect\effectpropertydata\previousproperty}%
+     \doeffectmark{\effectpropertydata\previousproperty}%
+   \fi}
+
+\rawnewmark\effectmark
+
+\def\doeffectmark#1%
+  {\ifinpagebody \else \ifinframed \else
+     \expanded{\rawsetmark\noexpand\effectmark{#1}}% could be number
+   \fi \fi}
+
+\def\pusheffectproperty
+  {\doifsomething{\rawgetbotmark\effectmark}\dostopfonteffect}
+
+\def\popeffectproperty
+  {\doifsomething{\rawgetbotmark\effectmark}%
+     {\expanded{\dostartfonteffect\rawgetbotmark\effectmark}}}
+
+\def\popspliteffectproperty
+  {\getsplitmarks\effectmark
+   \doifsomething{\rawgetsplitbotmark\effectmark}%
+     {\expanded{\dostartfonteffect\rawgetsplitbotmark\effectmark}}}
+
+\appendtoks \pusheffectproperty     \to \everypushproperties
+\appendtoks \popeffectproperty      \to \everypopproperties
+\appendtoks \popspliteffectproperty \to \everypopsplitproperties
+
+\protect \endinput
diff --git a/tex/context/base/prop-mis.mkiv b/tex/context/base/prop-mis.mkiv
new file mode 100644
index 000000000..ee292155e
--- /dev/null
+++ b/tex/context/base/prop-mis.mkiv
@@ -0,0 +1,46 @@
+%D \module
+%D   [       file=prop-mis,
+%D        version=2004.05.29, % some code moved from private modules
+%D          title=\CONTEXT\ Property Macros,
+%D       subtitle=Miscelaneous,
+%D         author=Hans Hagen,
+%D           date=\currentdate,
+%D      copyright={PRAGMA / Hans Hagen \& Ton Otten}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+\unprotect
+
+%D Overprint cum suis.
+
+\definepropertyhandler \v!overprint {\dotriggeroverprint\v!overprint}
+\definepropertyhandler \v!knockout  {\dotriggeroverprint\v!knockout }
+
+\def\startoverprintproperty{\dotriggeroverprint\v!overprint}
+\def\stopoverprintproperty {\dotriggeroverprint\v!knockout }
+
+%D Negative cum suis.\def\dotriggeroverprint#1{\csname(os:#1)\endcsname}
+
+\definepropertyhandler \v!negative {\dotriggernegative\v!negative}
+\definepropertyhandler \v!positive {\dotriggernegative\v!positive}
+
+\def\startnegativeproperty{\dotriggernegative\v!negative}
+\def\stopnegativeproperty {\dotriggernegative\v!positive}
+
+%D Effects.
+
+\def\mktriggereffect#1%
+  {\dotriggereffect{#1}{\propertyparameter{#1}\c!stretch}{\propertyparameter{#1}\c!rulethickness}}
+
+\definepropertyhandler \v!normal {\mktriggereffect\v!normal}
+\definepropertyhandler \v!inner  {\mktriggereffect\v!inner }
+\definepropertyhandler \v!outer  {\mktriggereffect\v!outer }
+\definepropertyhandler \v!both   {\mktriggereffect\v!both  }
+\definepropertyhandler \v!hidden {\mktriggereffect\v!hidden}
+
+\def\starteffectproperty{\mktriggereffect\currentproperty}
+\def\stopeffectproperty {\mktriggereffect\v!normal       }
+
+\protect \endinput
diff --git a/tex/context/base/prop-mis.tex b/tex/context/base/prop-mis.tex
index 769fc33f4..ed287d044 100644
--- a/tex/context/base/prop-mis.tex
+++ b/tex/context/base/prop-mis.tex
@@ -26,97 +26,11 @@
 \defineproperty[\v!overprint][\s!overprint] [\c!method=\v!command]
 \defineproperty[\v!knockout] [\s!overprint] [\c!method=\v!command]
 
-\definepropertyhandler \v!overprint {\dostartoverprint}
-\definepropertyhandler \v!knockout  {\dostopoverprint }
-
-\def\startoverprintproperty
-  {\ifincolor
-     \propertyhandler\currentproperty
-     \dooverprintmark\currentproperty
-   \fi}
-
-\def\stopoverprintproperty
-  {\ifincolor
-     \ifcase\currentpropertylevel\or
-       \dostopoverprint
-       \dooverprintmark\empty
-     \else
-       \propertyhandler\previousproperty
-       \dooverprintmark\previousproperty
-     \fi
-   \fi}
-
-\rawnewmark\overprintmark
-
-\def\dooverprintmark#1%
-  {\ifinpagebody \else \ifinframed \else
-     \expanded{\rawsetmark\noexpand\overprintmark{#1}}%
-   \fi \fi}
-
-\def\pushoverprintproperty
-  {\doifsomething{\rawgetbotmark\overprintmark}\dostopoverprint}
-
-\def\popoverprintproperty
-  {\doifsomething{\rawgetbotmark\overprintmark}%
-     {\propertyhandler{\rawgetbotmark\overprintmark}}}
-
-\def\popsplitoverprintproperty
-  {\getsplitmarks\overprintmark  % hier wel
-   \doifsomething{\rawgetsplitbotmark\overprintmark}%
-     {\propertyhandler{\rawgetsplitbotmark\overprintmark}}}
-
-\appendtoks \pushoverprintproperty     \to \everypushproperties
-\appendtoks \popoverprintproperty      \to \everypopproperties
-\appendtoks \popsplitoverprintproperty \to \everypopsplitproperties
-
 %D Negation.
 
 \defineproperty [\v!negative] [\s!negative] [\c!method=\v!command]
 \defineproperty [\v!positive] [\s!negative] [\c!method=\v!command]
 
-\definepropertyhandler \v!negative {\dostartnegative}
-\definepropertyhandler \v!positive {\dostopnegative }
-
-\def\startnegativeproperty
-  {\ifincolor
-     \propertyhandler\currentproperty
-     \donegativemark\currentproperty
-   \fi}
-
-\def\stopnegativeproperty
-  {\ifincolor
-     \ifcase\currentpropertylevel\or
-       \dostopnegative
-       \donegativemark\empty
-     \else
-       \propertyhandler\previousproperty
-       \donegativemark\previousproperty
-     \fi
-   \fi}
-
-\rawnewmark\negativemark
-
-\def\donegativemark#1%
-  {\ifinpagebody \else \ifinframed \else
-     \expanded{\rawsetmark\noexpand\negativemark{#1}}%
-   \fi \fi}
-
-\def\pushnegativeproperty
-  {\doifsomething{\rawgetbotmark\negativemark}\dostopnegative}
-
-\def\popnegativeproperty
-  {\doifsomething{\rawgetbotmark\overprintmark}%
-     {\propertyhandler{\rawgetbotmark\negativemark}}}
-
-\def\popsplitnegativeproperty
-  {\getsplitmarks\negativemark  % hier wel
-   \doifsomething{\rawgetsplitbotmark\negativemark}%
-     {\propertyhandler{\rawgetsplitbotmark\negativemark}}}
-
-\appendtoks \pushnegativeproperty     \to \everypushproperties
-\appendtoks \popnegativeproperty      \to \everypopproperties
-\appendtoks \popsplitnegativeproperty \to \everypopsplitproperties
-
 %D Special font effects.
 
 \setupproperty
@@ -130,95 +44,8 @@
 \defineproperty [\v!normal] [\s!effect]
 \defineproperty [\v!hidden] [\s!effect]
 
-\definepropertyhandler \v!normal {0}
-\definepropertyhandler \v!inner  {0}
-\definepropertyhandler \v!outer  {1}
-\definepropertyhandler \v!both   {2}
-\definepropertyhandler \v!hidden {3}
-
-% \def\handleeffectproperty#1%
-%   {\expanded{\dostartfonteffect
-%      {\propertyhandler{#1}}%
-%      {\propertyparameter{#1}\c!lijndikte}%
-%      {\propertyparameter{#1}\c!rek}}}
-
-% \def\starteffectproperty
-%   {\handleeffectproperty\currentproperty
-%    \doeffectmark\currentproperty}
-
-% \def\stopeffectproperty
-%   {\dostopfonteffect
-%    \ifcase\currentpropertylevel\or
-%      \doeffectmark\empty
-%    \else
-%      \handleeffectproperty\previousproperty
-%      \doeffectmark\previousproperty
-%    \fi}
-
-% \rawnewmark\effectmark
-
-% \def\doeffectmark#1%
-%   {\ifinpagebody \else \ifinframed \else
-%      \expanded{\rawsetmark\noexpand\effectmark{#1}}% could be number
-%    \fi \fi}
-
-% \def\pusheffectproperty
-%   {\doifsomething{\rawgetbotmark\effectmark}\dostopfonteffect}
-
-% \def\popeffectproperty
-%   {\doifsomething{\rawgetbotmark\effectmark}%
-%      {\handleeffectproperty{\rawgetbotmark\effectmark}}}
-
-% \def\popspliteffectproperty
-%   {\getsplitmarks\effectmark
-%    \doifsomething{\rawgetsplitbotmark\effectmark}%
-%      {\handleeffectproperty{\rawgetsplitbotmark\effectmark}}}
-
-% \appendtoks \pusheffectproperty     \to \everypushproperties
-% \appendtoks \popeffectproperty      \to \everypopproperties
-% \appendtoks \popspliteffectproperty \to \everypopsplitproperties
-
-% %
-
-\def\effectpropertydata#1%
-  {{\propertyhandler{#1}}%
-   {\propertyparameter{#1}\c!rulethickness}%
-   {\propertyparameter{#1}\c!stretch}}
-
-\def\starteffectproperty
-  {\expanded{\dostartfonteffect\effectpropertydata\currentproperty}%
-   \doeffectmark{\effectpropertydata\currentproperty}}
-
-\def\stopeffectproperty
-  {\dostopfonteffect
-   \ifcase\currentpropertylevel\or
-     \doeffectmark\empty
-   \else
-     \expanded{\dostartfonteffect\effectpropertydata\previousproperty}%
-     \doeffectmark{\effectpropertydata\previousproperty}%
-   \fi}
-
-\rawnewmark\effectmark
-
-\def\doeffectmark#1%
-  {\ifinpagebody \else \ifinframed \else
-     \expanded{\rawsetmark\noexpand\effectmark{#1}}% could be number
-   \fi \fi}
-
-\def\pusheffectproperty
-  {\doifsomething{\rawgetbotmark\effectmark}\dostopfonteffect}
-
-\def\popeffectproperty
-  {\doifsomething{\rawgetbotmark\effectmark}%
-     {\expanded{\dostartfonteffect\rawgetbotmark\effectmark}}}
-
-\def\popspliteffectproperty
-  {\getsplitmarks\effectmark
-   \doifsomething{\rawgetsplitbotmark\effectmark}%
-     {\expanded{\dostartfonteffect\rawgetsplitbotmark\effectmark}}}
+%D Plugin:
 
-\appendtoks \pusheffectproperty     \to \everypushproperties
-\appendtoks \popeffectproperty      \to \everypopproperties
-\appendtoks \popspliteffectproperty \to \everypopsplitproperties
+\loadmarkfile{prop-mis}
 
 \protect \endinput
diff --git a/tex/context/base/s-abr-01.tex b/tex/context/base/s-abr-01.tex
index 98e36e2f1..3933b0a63 100644
--- a/tex/context/base/s-abr-01.tex
+++ b/tex/context/base/s-abr-01.tex
@@ -46,6 +46,7 @@
 \logo [CD]         {cd}
 \logo [CDROM]      {cdrom}
 \logo [CID]        {cid}
+\logo [CJK]        {cjk}
 \logo [CMR]        {cmr}
 \logo [CMYK]       {cmyk}
 \logo [CODHOST]    {CodHost}
diff --git a/tex/context/base/spec-dpx.tex b/tex/context/base/spec-dpx.tex
index 44bbe10b6..d8ec1e518 100644
--- a/tex/context/base/spec-dpx.tex
+++ b/tex/context/base/spec-dpx.tex
@@ -393,6 +393,7 @@
            \global\let\currentPDFresources\empty
          \fi
          \special{pdf:exobj}}%
+         \finalizeobjectbox\nextbox
          \smashbox\nextbox
          \flushatshipout{\box\nextbox}%
          \egroup}%
@@ -749,6 +750,7 @@
            {\scratchdimen\wd#2\scratchdimen.5\scratchdimen\hskip-\the\scratchdimen
             \special{pdf:uxobj @MPPDF::\MPPDFobjectcounter}}}}%
    \expanded{\doDVIPDFMXstartobject\zerocount{MPPDF}\MPPDFobjectcounter{\the\wd#2}{\the\ht#2}{\the\dp#2}}%
+   \finalizeobjectbox#2%
    \box#2%
    \doDVIPDFMXstopobject}
 
diff --git a/tex/context/base/spec-fdf.tex b/tex/context/base/spec-fdf.tex
index 7b99dfcc9..3531ca0d9 100644
--- a/tex/context/base/spec-fdf.tex
+++ b/tex/context/base/spec-fdf.tex
@@ -329,12 +329,9 @@
 
 \def\doPDFsetupwhateverbox#1#2#3#4#5#6% watch the extra arguments
   {\bgroup
-   \!!widtha#5%
-   \advance\!!widtha#3%
-   \!!heighta-#6%
-   \!!heightb#2% extra argument
-   \advance\!!heightb -#4%
-   \advance\!!heighta \!!heightb
+   \!!widtha \dimexpr#5+#3\relax
+   \!!heightb\dimexpr#2-#4\relax
+   \!!heighta\dimexpr\!!heightb-#6\relax
    % sometimes whole values give better results
    % \PointsToWholeBigPoints{#3}\left
    % \PointsToWholeBigPoints\!!heighta\bottom
@@ -2972,12 +2969,6 @@
   {\doPDFregistersomeindexcolor{#1}{#2}{#3}{#4}{Gray}{0.0 1.0}%
      {pop}}
 
-% \def\doPDFregisterfigurecolor#1%
-%   {\dogetobjectreference
-%      {PDF\ifcase\internalspotcolorsize{#1} CS\or CS\else IX\fi}
-%      {\internalspotcolorname{#1}}
-%      \PDFimagecolorreference}
-
 \let\checkpredefinedcolor\predefineindexcolor % we need an index in order to negate bitmaps
 
 \def\doPDFregisterfigurecolor#1% always an index color
diff --git a/tex/context/base/spec-tpd.tex b/tex/context/base/spec-tpd.tex
index 428eb2750..37926efb5 100644
--- a/tex/context/base/spec-tpd.tex
+++ b/tex/context/base/spec-tpd.tex
@@ -906,6 +906,7 @@
           % won't work in xforms; some day I will optimize
           % this.
           \the\everyPDFxform
+          \finalizeobjectbox\nextbox
           \immediate\pdfxform
             resources {\currentPDFresources\the\pdfpageresources}%
             \nextbox
@@ -1360,6 +1361,7 @@
 
   \def\setMPPDFobject#1#2% resources boxnumber
     {\the\everyPDFxform
+     \finalizeobjectbox{#2}%
      \immediate\pdfxform resources{#1}#2%
      \edef\getMPPDFobject{\noexpand\pdfrefxform\the\pdflastxform}}
 
diff --git a/tex/context/base/syst-con.lua b/tex/context/base/syst-con.lua
index 273bc5b5c..9f35d68b6 100644
--- a/tex/context/base/syst-con.lua
+++ b/tex/context/base/syst-con.lua
@@ -1,30 +1,28 @@
--- filename : syst-con.lua
--- comment  : companion to syst-con.tex (in ConTeXt)
--- author   : Hans Hagen, PRAGMA-ADE, Hasselt NL
--- copyright: PRAGMA ADE / ConTeXt Development Team
--- license  : see context related readme files
+if not modules then modules = { } end modules ['syst-con'] = {
+    version   = 1.001,
+    comment   = "companion to syst-con.tex",
+    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+    copyright = "PRAGMA ADE / ConTeXt Development Team",
+    license   = "see context related readme files"
+}
 
--- remark   : compact version
+converters = converters or { }
 
-if not versions then versions = { } end versions['syst-con'] = 1.001
-if not convert  then convert  = { } end
-
--- For raw 8 bit characters, the offset is 0x110000 (bottom of plane 18)
--- at the top of luatex's char range but outside the unicode range.
-
-function convert.lchexnumber      (n) tex.sprint(string.format("%x"  ,n)) end
-function convert.uchexnumber      (n) tex.sprint(string.format("%X"  ,n)) end
-function convert.lchexnumbers     (n) tex.sprint(string.format("%02x",n)) end
-function convert.uchexnumbers     (n) tex.sprint(string.format("%02X",n)) end
-function convert.octnumber        (n) tex.sprint(string.format("%03o",n)) end
-function convert.hexstringtonumber(n) tex.sprint(tonumber(n,16))          end
-function convert.octstringtonumber(n) tex.sprint(tonumber(n, 8))          end
-function convert.rawcharacter     (n) tex.sprint(unicode.utf8.char(0x110000+n))  end
+--[[ldx--
+For raw 8 bit characters, the offset is 0x110000 (bottom of plane 18) at
+the top of 's char range but outside the unicode range.
+--ldx]]--
 
 do
-    local char  = unicode.utf8.char
-    local flush = tex.sprint
+    local char, flush, format = unicode.utf8.char, tex.sprint, string.format
 
-    function convert.rawcharacter(n) flush(char(0x110000+n)) end
+    function converters.lchexnumber      (n) flush(format("%x"  ,n)) end
+    function converters.uchexnumber      (n) flush(format("%X"  ,n)) end
+    function converters.lchexnumbers     (n) flush(format("%02x",n)) end
+    function converters.uchexnumbers     (n) flush(format("%02X",n)) end
+    function converters.octnumber        (n) flush(format("%03o",n)) end
+    function converters.hexstringtonumber(n) flush(tonumber(n,16))   end
+    function converters.octstringtonumber(n) flush(tonumber(n, 8))   end
+    function converters.rawcharacter     (n) flush(char(0x110000+n)) end
 
 end
diff --git a/tex/context/base/syst-con.mkiv b/tex/context/base/syst-con.mkiv
index ddc9fe564..2f84395f0 100644
--- a/tex/context/base/syst-con.mkiv
+++ b/tex/context/base/syst-con.mkiv
@@ -15,13 +15,13 @@
 
 \unprotect
 
-\def\lchexnumber      #1{\ctxlua{convert.lchexnumber(\number#1)}}
-\def\uchexnumber      #1{\ctxlua{convert.uchexnumber(\number#1)}}
-\def\lchexnumbers     #1{\ctxlua{convert.lchexnumbers(\number#1)}}
-\def\uchexnumbers     #1{\ctxlua{convert.uchexnumbers(\number#1)}}
-\def\octnumber        #1{\ctxlua{convert.octnumber(\number#1)}}
-\def\hexstringtonumber#1{\ctxlua{convert.hexstringtonumber("#1")}}
-\def\octstringtonumber#1{\ctxlua{convert.octstringtonumber("#1")}}
-\def\rawcharacter     #1{\ctxlua{convert.rawcharacter(\number#1)}}
+\def\lchexnumber      #1{\ctxlua{converters.lchexnumber(\number#1)}}
+\def\uchexnumber      #1{\ctxlua{converters.uchexnumber(\number#1)}}
+\def\lchexnumbers     #1{\ctxlua{converters.lchexnumbers(\number#1)}}
+\def\uchexnumbers     #1{\ctxlua{converters.uchexnumbers(\number#1)}}
+\def\octnumber        #1{\ctxlua{converters.octnumber(\number#1)}}
+\def\hexstringtonumber#1{\ctxlua{converters.hexstringtonumber("#1")}}
+\def\octstringtonumber#1{\ctxlua{converters.octstringtonumber("#1")}}
+\def\rawcharacter     #1{\ctxlua{converters.rawcharacter(\number#1)}}
 
 \protect \endinput
diff --git a/tex/context/base/toks-ini.lua b/tex/context/base/toks-ini.lua
index ad046be50..cf313ceb3 100644
--- a/tex/context/base/toks-ini.lua
+++ b/tex/context/base/toks-ini.lua
@@ -48,8 +48,8 @@ tokens.letters = function(str)
     return t
 end
 
-collector       = collector      or { }
-collector.data  = collector.data or { }
+collectors       = collectors      or { }
+collectors.data  = collectors.data or { }
 
 function tex.printlist(data)
     callbacks.push('token_filter', function ()
@@ -58,27 +58,27 @@ function tex.printlist(data)
     end)
 end
 
-function collector.flush(tag)
-    tex.printlist(collector.data[tag])
+function collectors.flush(tag)
+    tex.printlist(collectors.data[tag])
 end
 
-function collector.test(tag)
-    tex.printlist(collector.data[tag])
+function collectors.test(tag)
+    tex.printlist(collectors.data[tag])
 end
 
-collector.registered = { }
+collectors.registered = { }
 
-function collector.register(name)
-    collector.registered[token.csname_id(name)] = name
+function collectors.register(name)
+    collectors.registered[token.csname_id(name)] = name
 end
 
-function collector.install(tag,end_cs)
-    collector.data[tag] = { }
-    local data   = collector.data[tag]
+function collectors.install(tag,end_cs)
+    collectors.data[tag] = { }
+    local data   = collectors.data[tag]
     local call   = token.command_id("call")
     local relax  = token.command_id("relax")
     local endcs  = token.csname_id(end_cs)
-    local expand = collector.registered
+    local expand = collectors.registered
     local get    = token.get_next -- so no callback!
     while true do
         local t = get()
@@ -93,13 +93,13 @@ function collector.install(tag,end_cs)
     end
 end
 
-collector.show_methods = { }
+collectors.show_methods = { }
 
-function collector.show(tag, method)
+function collectors.show(tag, method)
     if type(tag) == "table" then
-        collector.show_methods[method or 'a'](tag)
+        collectors.show_methods[method or 'a'](tag)
     else
-        collector.show_methods[method or 'a'](collector.data[tag])
+        collectors.show_methods[method or 'a'](collectors.data[tag])
     end
 end
 
@@ -108,7 +108,7 @@ commands = commands or { }
 commands.letter = token.command_id("letter")
 commands.other  = token.command_id("other_char")
 
-function collector.default_words(t,str)
+function collectors.default_words(t,str)
     t[#t+1] = tokens.bgroup
     t[#t+1] = token.create("red")
     for k,v in ipairs(str) do
@@ -117,10 +117,10 @@ function collector.default_words(t,str)
     t[#t+1] = tokens.egroup
 end
 
-function collector.with_words(tag,handle)
+function collectors.with_words(tag,handle)
     local t, w = { }, { }
-    handle = handle or collector.default_words
-    for _,v in ipairs(collector.data[tag]) do
+    handle = handle or collectors.default_words
+    for _,v in ipairs(collectors.data[tag]) do
         if v[1] == commands.letter then
             w[#w+1] = v[2]
         else
@@ -134,10 +134,10 @@ function collector.with_words(tag,handle)
     if #w > 0 then
         handle(t,w)
     end
-    collector.data[tag] = t
+    collectors.data[tag] = t
 end
 
-function collector.show_token(t)
+function collectors.show_token(t)
     if t then
         local cmd, chr, id, cs, name = t[1], t[2], t[3], nil, token.command_name(t) or ""
         if cmd == commands.letter or cmd == commands.other then
@@ -159,13 +159,13 @@ function collector.show_token(t)
     end
 end
 
-function collector.trace()
+function collectors.trace()
     local t = token.get_next()
-    texio.write_nl(collector.show_token(t))
+    texio.write_nl(collectors.show_token(t))
     return t
 end
 
-collector.show_methods.a = function(data) -- no need to store the table, just pass directly
+collectors.show_methods.a = function(data) -- no need to store the table, just pass directly
     local flush, ct = tex.sprint, tex.ctxcatcodes
     local template = "\\NC %s\\NC %s\\NC %s\\NC %s\\NC %s\\NC\\NR "
     flush(ct, "\\starttabulate[|T|Tr|cT|Tr|T|]")
@@ -192,7 +192,7 @@ collector.show_methods.a = function(data) -- no need to store the table, just pa
     flush(ct, "\\stoptabulate")
 end
 
-collector.show_methods.b_c = function(data,swap) -- no need to store the table, just pass directly
+collectors.show_methods.b_c = function(data,swap) -- no need to store the table, just pass directly
     local flush, ct = tex.sprint, tex.ctxcatcodes
     local template = "\\NC %s\\NC %s\\NC %s\\NC\\NR"
     if swap then
@@ -228,5 +228,5 @@ collector.show_methods.b_c = function(data,swap) -- no need to store the table,
     flush(ct, "\\stoptabulate")
 end
 
-collector.show_methods.b = function(tag) collector.show_methods.b_c(tag,false) end
-collector.show_methods.c = function(tag) collector.show_methods.b_c(tag,true ) end
+collectors.show_methods.b = function(tag) collectors.show_methods.b_c(tag,false) end
+collectors.show_methods.c = function(tag) collectors.show_methods.b_c(tag,true ) end
diff --git a/tex/context/base/toks-ini.tex b/tex/context/base/toks-ini.tex
index 873cfa4d5..d3aa9cb5d 100644
--- a/tex/context/base/toks-ini.tex
+++ b/tex/context/base/toks-ini.tex
@@ -15,11 +15,11 @@
 
 \registerctxluafile{toks-ini}{1.001}
 
-\def\starttokens  [#1]{\ctxlua{collector.install("#1", "stoptokens")}}
+\def\starttokens  [#1]{\ctxlua{collectors.install("#1", "stoptokens")}}
 \let\stoptokens        \relax
-\def\flushtokens  [#1]{\ctxlua{collector.flush("#1")}}
-\def\showtokens   [#1]{\ctxlua{collector.show("#1")}}
-\def\testtokens   [#1]{\ctxlua{collector.with_words("#1")}}
-\def\registertoken  #1{\ctxlua{collector.register("#1")}}
+\def\flushtokens  [#1]{\ctxlua{collectors.flush("#1")}}
+\def\showtokens   [#1]{\ctxlua{collectors.show("#1")}}
+\def\testtokens   [#1]{\ctxlua{collectors.with_words("#1")}}
+\def\registertoken  #1{\ctxlua{collectors.register("#1")}}
 
 \endinput
diff --git a/tex/context/interface/keys-cz.xml b/tex/context/interface/keys-cz.xml
index 05766e2c8..8a5946ba9 100644
--- a/tex/context/interface/keys-cz.xml
+++ b/tex/context/interface/keys-cz.xml
@@ -1,6 +1,6 @@
 
 
-
+
 
   
     
@@ -182,6 +182,7 @@
     
     
     
+    
     
     
     
@@ -287,6 +288,8 @@
     
     
     
+    
+    
     
     
     
diff --git a/tex/context/interface/keys-de.xml b/tex/context/interface/keys-de.xml
index 3461fc0fa..7fd1c0971 100644
--- a/tex/context/interface/keys-de.xml
+++ b/tex/context/interface/keys-de.xml
@@ -1,6 +1,6 @@
 
 
-
+
 
   
     
@@ -182,6 +182,7 @@
     
     
     
+    
     
     
     
@@ -287,6 +288,8 @@
     
     
     
+    
+    
     
     
     
diff --git a/tex/context/interface/keys-en.xml b/tex/context/interface/keys-en.xml
index 3867cf38f..530e61e66 100644
--- a/tex/context/interface/keys-en.xml
+++ b/tex/context/interface/keys-en.xml
@@ -1,6 +1,6 @@
 
 
-
+
 
   
     
@@ -182,6 +182,7 @@
     
     
     
+    
     
     
     
@@ -287,6 +288,8 @@
     
     
     
+    
+    
     
     
     
diff --git a/tex/context/interface/keys-fr.xml b/tex/context/interface/keys-fr.xml
index 5680b44a7..2ba33594d 100644
--- a/tex/context/interface/keys-fr.xml
+++ b/tex/context/interface/keys-fr.xml
@@ -1,6 +1,6 @@
 
 
-
+
 
   
     
@@ -182,6 +182,7 @@
     
     
     
+    
     
     
     
@@ -287,6 +288,8 @@
     
     
     
+    
+    
     
     
     
diff --git a/tex/context/interface/keys-it.xml b/tex/context/interface/keys-it.xml
index c9de9c5e9..088c25d10 100644
--- a/tex/context/interface/keys-it.xml
+++ b/tex/context/interface/keys-it.xml
@@ -1,6 +1,6 @@
 
 
-
+
 
   
     
@@ -182,6 +182,7 @@
     
     
     
+    
     
     
     
@@ -287,6 +288,8 @@
     
     
     
+    
+    
     
     
     
diff --git a/tex/context/interface/keys-nl.xml b/tex/context/interface/keys-nl.xml
index 08c82396f..63fff53f1 100644
--- a/tex/context/interface/keys-nl.xml
+++ b/tex/context/interface/keys-nl.xml
@@ -1,6 +1,6 @@
 
 
-
+
 
   
     
@@ -182,6 +182,7 @@
     
     
     
+    
     
     
     
@@ -287,6 +288,8 @@
     
     
     
+    
+    
     
     
     
diff --git a/tex/context/interface/keys-ro.xml b/tex/context/interface/keys-ro.xml
index 9a0c50c08..7d3b33673 100644
--- a/tex/context/interface/keys-ro.xml
+++ b/tex/context/interface/keys-ro.xml
@@ -1,6 +1,6 @@
 
 
-
+
 
   
     
@@ -182,6 +182,7 @@
     
     
     
+    
     
     
     
@@ -287,6 +288,8 @@
     
     
     
+    
+    
     
     
     
-- 
cgit v1.2.3