From b1d691809f3556327b45caf09444c50a77335b8f Mon Sep 17 00:00:00 2001
From: Marius <mariausol@gmail.com>
Date: Fri, 12 Jul 2013 20:20:46 +0300
Subject: beta 2013.07.12 19:10

---
 tex/context/base/anch-pos.lua                      |   45 +-
 tex/context/base/attr-eff.lua                      |    4 +-
 tex/context/base/attr-ini.lua                      |    9 +-
 tex/context/base/back-exp.lua                      |   17 +-
 tex/context/base/back-pdf.mkiv                     |    4 +-
 tex/context/base/blob-ini.lua                      |  110 +-
 tex/context/base/blob-ini.mkiv                     |   14 +-
 tex/context/base/chem-str.lua                      |   31 +-
 tex/context/base/cldf-bas.lua                      |    9 +-
 tex/context/base/cldf-ini.lua                      |    8 +-
 tex/context/base/cont-new.mkiv                     |    2 +-
 tex/context/base/context-version.pdf               |  Bin 4103 -> 4097 bytes
 tex/context/base/context.mkiv                      |    2 +-
 tex/context/base/core-dat.lua                      |   28 +-
 tex/context/base/core-env.lua                      |   18 +-
 tex/context/base/core-uti.lua                      |   15 +-
 tex/context/base/data-met.lua                      |   12 +-
 tex/context/base/data-res.lua                      |    2 +-
 tex/context/base/file-ini.lua                      |    6 +-
 tex/context/base/font-chk.lua                      |    7 +-
 tex/context/base/font-col.lua                      |    7 +-
 tex/context/base/font-con.lua                      |   10 +-
 tex/context/base/font-ctx.lua                      |   67 +-
 tex/context/base/font-ext.lua                      |    2 +-
 tex/context/base/font-gds.lua                      |  104 +-
 tex/context/base/font-nod.lua                      |   13 +-
 tex/context/base/font-ota.lua                      |    5 +-
 tex/context/base/font-otn.lua                      |    4 +-
 tex/context/base/font-otx.lua                      |    3 +-
 tex/context/base/font-pre.mkiv                     |   41 +-
 tex/context/base/font-syn.lua                      |   46 +
 tex/context/base/grph-inc.lua                      |   16 +-
 tex/context/base/grph-inc.mkiv                     |    3 +-
 tex/context/base/l-file.lua                        |   26 +-
 tex/context/base/l-io.lua                          |    1 +
 tex/context/base/l-lpeg.lua                        |   15 +-
 tex/context/base/l-pdfview.lua                     |  133 +-
 tex/context/base/l-table.lua                       |    1 +
 tex/context/base/lang-rep.lua                      |  198 ++
 tex/context/base/lang-wrd.lua                      |   18 +-
 tex/context/base/layo-ini.lua                      |   25 +-
 tex/context/base/lpdf-ano.lua                      |    5 +-
 tex/context/base/lpdf-fmt.lua                      |    2 +-
 tex/context/base/lpdf-nod.lua                      |   12 +-
 tex/context/base/lpdf-tag.lua                      |   11 +-
 tex/context/base/lpdf-wid.lua                      |   65 +-
 tex/context/base/luat-env.lua                      |    6 +-
 tex/context/base/luat-fio.lua                      |   20 -
 tex/context/base/lxml-css.lua                      |   15 +-
 tex/context/base/lxml-tab.lua                      |    2 +-
 tex/context/base/m-nodechart.lua                   |  175 ++
 tex/context/base/m-nodechart.mkvi                  |  192 +-
 tex/context/base/m-r.mkii                          |  174 ++
 tex/context/base/m-r.tex                           |  174 --
 tex/context/base/m-spreadsheet.lua                 |    2 +-
 tex/context/base/m-translate.mkiv                  |    1 -
 tex/context/base/m-zint.mkiv                       |   33 +-
 tex/context/base/math-act.lua                      |  179 +-
 tex/context/base/math-def.mkiv                     |   27 +-
 tex/context/base/math-fbk.lua                      |  146 +-
 tex/context/base/math-ini.lua                      |   18 +-
 tex/context/base/math-map.lua                      |   18 +-
 tex/context/base/math-noa.lua                      |  147 +-
 tex/context/base/math-ren.lua                      |    4 -
 tex/context/base/math-stc.mkvi                     |  108 +-
 tex/context/base/meta-pdf.lua                      |    6 +-
 tex/context/base/meta-pdf.mkiv                     |    2 +-
 tex/context/base/mlib-pps.lua                      |   15 +-
 tex/context/base/mult-de.mkii                      |    2 +
 tex/context/base/mult-def.lua                      |    6 +
 tex/context/base/mult-def.mkiv                     |    3 +
 tex/context/base/mult-en.mkii                      |    2 +
 tex/context/base/mult-fr.mkii                      |    2 +
 tex/context/base/mult-it.mkii                      |    2 +
 tex/context/base/mult-nl.mkii                      |    2 +
 tex/context/base/mult-pe.mkii                      |    2 +
 tex/context/base/mult-ro.mkii                      |    2 +
 tex/context/base/mult-sys.mkiv                     |    3 +
 tex/context/base/node-acc.lua                      |  114 +-
 tex/context/base/node-aux.lua                      |   22 +-
 tex/context/base/node-fnt.lua                      |  283 +-
 tex/context/base/node-ini.lua                      |  197 +-
 tex/context/base/node-ini.mkiv                     |    4 +
 tex/context/base/node-inj.lua                      |    2 +
 tex/context/base/node-ltp.lua                      | 3207 ++++++++++++++++++++
 tex/context/base/node-met.lua                      |  616 ++++
 tex/context/base/node-pro.lua                      |   30 +-
 tex/context/base/node-ref.lua                      |  106 +-
 tex/context/base/node-res.lua                      |   44 +-
 tex/context/base/node-rul.lua                      |   28 +-
 tex/context/base/node-ser.lua                      |    4 +-
 tex/context/base/node-shp.lua                      |    8 +-
 tex/context/base/node-tra.lua                      |   50 +-
 tex/context/base/node-typ.lua                      |   58 +-
 tex/context/base/pack-obj.lua                      |    3 +-
 tex/context/base/pack-rul.lua                      |   37 +-
 tex/context/base/pack-rul.mkiv                     |   30 +-
 tex/context/base/page-app.mkiv                     |    4 +-
 tex/context/base/page-flt.lua                      |   29 +-
 tex/context/base/page-inj.lua                      |   10 +-
 tex/context/base/page-lin.lua                      |   14 +-
 tex/context/base/page-lin.mkiv                     |   48 +-
 tex/context/base/page-mix.lua                      |   21 +-
 tex/context/base/page-mix.mkiv                     |   48 +-
 tex/context/base/page-pst.lua                      |   10 +-
 tex/context/base/page-str.lua                      |   30 +-
 tex/context/base/s-inf-03.pdf                      |  Bin 0 -> 1971 bytes
 tex/context/base/scrn-fld.lua                      |    4 +-
 tex/context/base/scrn-hlp.lua                      |    6 +-
 tex/context/base/scrn-wid.lua                      |   10 +-
 tex/context/base/scrn-wid.mkvi                     |   32 +-
 tex/context/base/spac-ali.lua                      |   14 +-
 tex/context/base/spac-chr.lua                      |    3 -
 tex/context/base/spac-ver.lua                      |  187 +-
 tex/context/base/status-files.pdf                  |  Bin 24635 -> 24573 bytes
 tex/context/base/status-lua.log                    |   78 +
 tex/context/base/status-lua.pdf                    |  Bin 213195 -> 0 bytes
 tex/context/base/status-mkiv.lua                   |   14 +-
 tex/context/base/strc-ini.lua                      |    9 +-
 tex/context/base/strc-itm.lua                      |   14 +-
 tex/context/base/strc-lnt.mkvi                     |    9 +-
 tex/context/base/strc-lst.lua                      |    5 +-
 tex/context/base/strc-mar.lua                      |   18 +-
 tex/context/base/strc-not.lua                      |   12 +-
 tex/context/base/strc-num.lua                      |    2 +-
 tex/context/base/strc-pag.lua                      |   20 +-
 tex/context/base/strc-ref.lua                      |   27 +-
 tex/context/base/strc-reg.lua                      |    7 +-
 tex/context/base/supp-box.lua                      |   16 +-
 tex/context/base/tabl-tbl.lua                      |    8 +-
 tex/context/base/tabl-xtb.lua                      |   69 +-
 tex/context/base/task-ini.lua                      |    2 +-
 tex/context/base/trac-deb.lua                      |   25 +-
 tex/context/base/trac-inf.lua                      |   29 +-
 tex/context/base/trac-jus.lua                      |   71 +-
 tex/context/base/trac-log.lua                      |   41 +-
 tex/context/base/trac-vis.lua                      |  129 +-
 tex/context/base/typo-bld.lua                      |   70 +
 tex/context/base/typo-brk.lua                      |    6 +-
 tex/context/base/typo-cap.lua                      |    6 +-
 tex/context/base/typo-cln.lua                      |    8 +-
 tex/context/base/typo-dig.lua                      |    4 +-
 tex/context/base/typo-dir.lua                      |    6 +-
 tex/context/base/typo-itc.lua                      |   17 +-
 tex/context/base/typo-krn.lua                      |    4 +-
 tex/context/base/typo-mar.lua                      |   42 +-
 tex/context/base/typo-par.lua                      |   21 +-
 tex/context/base/typo-rep.lua                      |   28 +-
 tex/context/base/typo-spa.lua                      |    6 +-
 tex/context/base/util-dim.lua                      |   32 +-
 tex/context/base/util-jsn.lua                      |   16 +-
 tex/context/base/util-lua.lua                      |   22 +-
 tex/context/base/util-prs.lua                      |    2 +-
 tex/context/base/util-seq.lua                      |    2 +
 tex/context/base/util-str.lua                      |    2 +-
 tex/context/base/util-tab.lua                      |   13 +-
 tex/context/base/util-tpl.lua                      |    8 +
 tex/context/base/x-asciimath.lua                   |    2 +
 tex/context/fonts/ebgaramond.lfg                   |   53 +
 tex/context/fonts/lm.lfg                           |    2 +-
 tex/context/interface/keys-cs.xml                  |    2 +
 tex/context/interface/keys-de.xml                  |    2 +
 tex/context/interface/keys-en.xml                  |    2 +
 tex/context/interface/keys-fr.xml                  |    2 +
 tex/context/interface/keys-it.xml                  |    2 +
 tex/context/interface/keys-nl.xml                  |    2 +
 tex/context/interface/keys-pe.xml                  |    2 +
 tex/context/interface/keys-ro.xml                  |    2 +
 tex/generic/context/luatex/luatex-basics-gen.lua   |   22 +-
 tex/generic/context/luatex/luatex-basics-nod.lua   |   31 +-
 tex/generic/context/luatex/luatex-fonts-merged.lua |  730 +++--
 tex/generic/context/luatex/luatex-fonts.lua        |    3 +-
 172 files changed, 7306 insertions(+), 2452 deletions(-)
 create mode 100644 tex/context/base/lang-rep.lua
 create mode 100644 tex/context/base/m-nodechart.lua
 create mode 100644 tex/context/base/m-r.mkii
 delete mode 100644 tex/context/base/m-r.tex
 create mode 100644 tex/context/base/node-ltp.lua
 create mode 100644 tex/context/base/node-met.lua
 create mode 100644 tex/context/base/s-inf-03.pdf
 create mode 100644 tex/context/base/status-lua.log
 delete mode 100644 tex/context/base/status-lua.pdf
 create mode 100644 tex/context/fonts/ebgaramond.lfg

(limited to 'tex')

diff --git a/tex/context/base/anch-pos.lua b/tex/context/base/anch-pos.lua
index 7321d7d99..9cc9fb128 100644
--- a/tex/context/base/anch-pos.lua
+++ b/tex/context/base/anch-pos.lua
@@ -26,9 +26,14 @@ local rawget = rawget
 local lpegmatch = lpeg.match
 local insert, remove = table.insert, table.remove
 local allocate, mark = utilities.storage.allocate, utilities.storage.mark
-local texsp, texcount, texbox, texdimen, texsetcount = tex.sp, tex.count, tex.box, tex.dimen, tex.setcount
+local texsp = tex.sp
 ----- texsp = string.todimen -- because we cache this is much faster but no rounding
 
+local texgetcount       = tex.getcount
+local texgetbox         = tex.getbox
+local texsetcount       = tex.setcount
+local texget            = tex.get
+
 local pdf               = pdf -- h and v are variables
 
 local setmetatableindex = table.setmetatableindex
@@ -170,7 +175,7 @@ local function setdim(name,w,h,d,extra) -- will be used when we move to sp allov
     if extra == "" then extra = nil end
     -- todo: sparse
     tobesaved[name] = {
-        p = texcount.realpageno,
+        p = texgetcount("realpageno"),
         x = x,
         y = y,
         w = w,
@@ -217,7 +222,7 @@ local function enhance(data)
         data.y = pdf.v
     end
     if data.p == true then
-        data.p = texcount.realpageno
+        data.p = texgetcount("realpageno")
     end
     if data.c == true then
         data.c = column
@@ -315,16 +320,16 @@ function jobpositions.b_region(tag)
     local last = tobesaved[tag]
     last.x = pdf.h
     last.y = pdf.v
-    last.p = texcount.realpageno
+    last.p = texgetcount("realpageno")
     insert(regions,tag)
     region = tag
 end
 
 function jobpositions.e_region(correct)
     local last = tobesaved[region]
-if correct then
-    last.h = last.y - pdf.v
-end
+    if correct then
+        last.h = last.y - pdf.v
+    end
     last.y = pdf.v
     remove(regions)
     region = regions[#regions]
@@ -335,7 +340,7 @@ function jobpositions.markregionbox(n,tag,correct)
         nofregions = nofregions + 1
         tag = f_region(nofregions)
     end
-    local box = texbox[n]
+    local box = texgetbox(n)
     local w = box.width
     local h = box.height
     local d = box.depth
@@ -378,7 +383,7 @@ local nofparagraphs = 0
 function commands.parpos() -- todo: relate to localpar (so this is an intermediate variant)
     nofparagraphs = nofparagraphs + 1
     texsetcount("global","c_anch_positions_paragraph",nofparagraphs)
-    local strutbox = texbox.strutbox
+    local strutbox = texgetbox("strutbox")
     local t = {
         p  = true,
         c  = true,
@@ -387,14 +392,14 @@ function commands.parpos() -- todo: relate to localpar (so this is an intermedia
         y  = true,
         h  = strutbox.height,
         d  = strutbox.depth,
-        hs = tex.hsize,
+        hs = texget("hsize"),
     }
-    local leftskip   = tex.leftskip.width
-    local rightskip  = tex.rightskip.width
-    local hangindent = tex.hangindent
-    local hangafter  = tex.hangafter
-    local parindent  = tex.parindent
-    local parshape   = tex.parshape
+    local leftskip   = texget("leftskip").width
+    local rightskip  = texget("rightskip").width
+    local hangindent = texget("hangindent")
+    local hangafter  = texget("hangafter")
+    local parindent  = texget("parindent")
+    local parshape   = texget("parshape")
     if leftskip ~= 0 then
         t.ls = leftskip
     end
@@ -462,7 +467,7 @@ function commands.posplus(name,w,h,d,extra)
 end
 
 function commands.posstrut(name,w,h,d)
-    local strutbox = texbox.strutbox
+    local strutbox = texgetbox("strutbox")
     tobesaved[name] = {
         p = true,
         c = column,
@@ -478,7 +483,7 @@ end
 
 function jobpositions.getreserved(tag,n)
     if tag == v_column then
-        local fulltag = f_tag_three(tag,texcount.realpageno,n or 1)
+        local fulltag = f_tag_three(tag,texgetcount("realpageno"),n or 1)
         local data = collected[fulltag]
         if data then
             return data, fulltag
@@ -486,7 +491,7 @@ function jobpositions.getreserved(tag,n)
         tag = v_text
     end
     if tag == v_text then
-        local fulltag = f_tag_two(tag,texcount.realpageno)
+        local fulltag = f_tag_two(tag,texgetcount("realpageno"))
         return collected[fulltag] or false, fulltag
     end
     return collected[tag] or false, tag
@@ -1015,7 +1020,7 @@ function commands.doifpositionsonsamepageelse(list,page)
 end
 
 function commands.doifpositionsonthispageelse(list)
-    doifelse(onsamepage(list,tostring(tex.count.realpageno)))
+    doifelse(onsamepage(list,tostring(texgetcount("realpageno"))))
 end
 
 function commands.doifelsepositionsused()
diff --git a/tex/context/base/attr-eff.lua b/tex/context/base/attr-eff.lua
index 4dce5419a..b187b64c7 100644
--- a/tex/context/base/attr-eff.lua
+++ b/tex/context/base/attr-eff.lua
@@ -13,7 +13,7 @@ local tex = tex
 local states            = attributes.states
 local tasks             = nodes.tasks
 local nodeinjections    = backends.nodeinjections
-local settexattribute   = tex.setattribute
+local texsetattribute   = tex.setattribute
 local allocate          = utilities.storage.allocate
 local setmetatableindex = table.setmetatableindex
 local formatters        = string.formatters
@@ -107,5 +107,5 @@ function commands.triggereffect(specification)
         enable()
         enabled = true
     end
-    settexattribute(a_effect,register(specification))
+    texsetattribute(a_effect,register(specification))
 end
diff --git a/tex/context/base/attr-ini.lua b/tex/context/base/attr-ini.lua
index 206a86d79..ad4081681 100644
--- a/tex/context/base/attr-ini.lua
+++ b/tex/context/base/attr-ini.lua
@@ -20,6 +20,9 @@ local attributes      = attributes
 
 local sharedstorage   = storage.shared
 
+local texgetcount     = tex.getcount
+local texsetattribute = tex.setattribute
+
 attributes.names      = attributes.names    or { }
 attributes.numbers    = attributes.numbers  or { }
 attributes.list       = attributes.list     or { }
@@ -64,7 +67,7 @@ sharedstorage.attributes_last_private = sharedstorage.attributes_last_private or
 -- setmetatable(private, {
 --     __index = function(t,name)
 --         local number = sharedstorage.attributes_last_private
---         if number < 1023 then -- tex.count.minallocatedattribute - 1
+--         if number < 1023 then -- texgetcount("minallocatedattribute") - 1
 --             number = number + 1
 --             sharedstorage.attributes_last_private = number
 --         end
@@ -81,7 +84,7 @@ function attributes.private(name) -- at the lua end (hidden from user)
     local number = numbers[name]
     if not number then
         local last = sharedstorage.attributes_last_private
-        if last < 1023 then -- tex.count.minallocatedattribute - 1
+        if last < 1023 then -- texgetcount("minallocatedattribute") - 1
             last = last + 1
             sharedstorage.attributes_last_private = last
         else
@@ -155,7 +158,7 @@ function commands.restorecurrentattributes(name)
         local font = t.font
         if attr then
             for k, v in next, attr do
-                tex.attribute[k] = v
+                texsetattribute(k,v)
             end
         end
         if font then
diff --git a/tex/context/base/back-exp.lua b/tex/context/base/back-exp.lua
index 4d219a18b..1ff1cd7a0 100644
--- a/tex/context/base/back-exp.lua
+++ b/tex/context/base/back-exp.lua
@@ -81,6 +81,8 @@ local xspaceskip_code   = skipcodes.xspaceskip
 
 local line_code         = listcodes.line
 
+local texgetcount       = tex.getcount
+
 local a_characters      = attributes.private('characters')
 local a_exportstatus    = attributes.private('exportstatus')
 
@@ -94,9 +96,6 @@ local a_textblock       = attributes.private("textblock")
 local traverse_id       = node.traverse_id
 local traverse_nodes    = node.traverse
 local slide_nodelist    = node.slide
-local texattribute      = tex.attribute
-local texdimen          = tex.dimen
-local texcount          = tex.count
 local locate_node       = nodes.locate
 
 local references        = structures.references
@@ -454,7 +453,7 @@ local function checkdocument(root)
 end
 
 function extras.document(result,element,detail,n,fulltag,di)
-    result[#result+1] = format(" language=%q",languagenames[tex.count.mainlanguagenumber])
+    result[#result+1] = format(" language=%q",languagenames[texgetcount("mainlanguagenumber")])
     if not less_state then
         result[#result+1] = format(" file=%q",tex.jobname)
         result[#result+1] = format(" date=%q",os.date())
@@ -2353,7 +2352,7 @@ local function stopexport(v)
             images     = uniqueusedimages(),
             root       = xhtmlfile,
             files      = files,
-            language   = languagenames[tex.count.mainlanguagenumber],
+            language   = languagenames[texgetcount("mainlanguagenumber")],
             title      = validstring(finetuning.title) or validstring(identity.title),
             subtitle   = validstring(finetuning.subtitle) or validstring(identity.subtitle),
             author     = validstring(finetuning.author) or validstring(identity.author),
@@ -2377,13 +2376,13 @@ end
 local function startexport(v)
     if v and not exporting then
         report_export("enabling export to xml")
--- not yet known in task-ini
+     -- not yet known in task-ini
         appendaction("shipouts","normalizers", "nodes.handlers.export")
---      enableaction("shipouts","nodes.handlers.export")
+     -- enableaction("shipouts","nodes.handlers.export")
         enableaction("shipouts","nodes.handlers.accessibility")
         enableaction("math",    "noads.handlers.tags")
---~ appendaction("finalizers","lists","builders.paragraphs.tag")
---~ enableaction("finalizers","builders.paragraphs.tag")
+     -- appendaction("finalizers","lists","builders.paragraphs.tag")
+     -- enableaction("finalizers","builders.paragraphs.tag")
         luatex.registerstopactions(function() stopexport(v) end)
         exporting = true
     end
diff --git a/tex/context/base/back-pdf.mkiv b/tex/context/base/back-pdf.mkiv
index 1cf7a3703..3be1c4ec1 100644
--- a/tex/context/base/back-pdf.mkiv
+++ b/tex/context/base/back-pdf.mkiv
@@ -195,14 +195,14 @@
          \pdfrestore
          \advance\backendtransformlevel\minusone}}}
 
-\unexpanded\def\dostartclipping#1#2#3%
+\unexpanded\def\dostartclipping#1#2#3% we can move this to lua and only set a box here
   {\PointsToBigPoints{#2}\width
    \PointsToBigPoints{#3}\height
    \meta_grab_clip_path{#1}\width\height{0 0 m \width\space 0 l \width \height l 0 \height l}%
    \pdfliteral{q 0 w \MPclippath\space W n}}
 
 \unexpanded\def\dostopclipping
-  {\pdfliteral{Q n}}
+  {\pdfliteral{Q}}
 
 %D The following will move to the backend \LUA\ code:
 
diff --git a/tex/context/base/blob-ini.lua b/tex/context/base/blob-ini.lua
index 4debaf94c..32fac7662 100644
--- a/tex/context/base/blob-ini.lua
+++ b/tex/context/base/blob-ini.lua
@@ -31,30 +31,37 @@ if not modules then modules = { } end modules ['blob-ini'] = {
 local type, tostring = type, tostring
 local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
 
-local report_blobs = logs.reporter("blobs")
+local report_blobs    = logs.reporter("blobs")
 
-local t_tonodes         = typesetters.tonodes
-local t_hpack           = typesetters.hpack
+local flush_node_list = node.flush_list
+local hpack_node_list = node.hpack
+local vpack_node_list = node.vpack
+local write_node      = node.write
 
-local flush_node_list   = node.flush_list
-local hpack_node_list   = node.hpack
-local vpack_node_list   = node.vpack
-local write_node        = node.write
+local typesetters     = nodes.typesetters
+local tonodes         = typesetters.tonodes
+local tohpack         = typesetters.tohpack
+local tohpackfast     = typesetters.tohpackfast
+local tovpack         = typesetters.tovpack
+local tovpackfast     = typesetters.tovpackfast
 
 blobs = blobs or  { }
 
-local newline = lpegpatterns.newline
-local space   = lpegpatterns.spacer
-local spacing = newline * space^0
-local content = (space^1)/" " + (1-spacing)
+-- provide copies here (nicer for manuals)
 
-local ctxtextcapture = lpeg.Ct ( ( -- needs checking (see elsewhere)
-    space^0 * (
-        newline^2 * space^0 * lpeg.Cc("")
-      + newline   * space^0 * lpeg.Cc(" ")
-      + lpeg.Cs(content^1)
-    )
-)^0)
+blobs.tonodes     = tonodes
+blobs.tohpack     = tohpack
+blobs.tohpackfast = tohpackfast
+blobs.tovpack     = tovpack
+blobs.tovpackfast = tovpackfast
+
+-- end of helpers
+
+local newline = lpeg.patterns.newline
+local space   = lpeg.patterns.spacer
+local newpar  = (space^0*newline*space^0)^2
+
+local ctxtextcapture = lpeg.Ct ( ( space^0 * ( newpar + lpeg.Cs(((space^1/" " + 1)-newpar)^1) ) )^0)
 
 function blobs.new()
     return {
@@ -81,33 +88,12 @@ function blobs.append(t,str) -- compare concat and link
         str = tostring(str)
         typ = "string"
     end
-    local list = t.list
     if typ == "string" then
         local pars = lpegmatch(ctxtextcapture,str)
-        local noflist = #list
+        local list = t.list
         for p=1,#pars do
-            local str = pars[p]
-            if #str == 0 then
-                noflist = noflist + 1
-                list[noflist] = { head = nil, tail = nil }
-            else
-                local l = list[noflist]
-                if not l then
-                    l = { head = nil, tail = nil }
-                    noflist = noflist + 1
-                    list[noflist] = l
-                end
-                local head, tail = t_tonodes(str,nil,nil)
-                if head then
-                    if l.head then
-                        l.tail.next = head
-                        head.prev = l.tail
-                        l.tail = tail
-                    else
-                        l.head, l.tail = head, tail
-                    end
-                end
-            end
+            local head, tail = tonodes(pars[p],nil,nil)
+            list[#list+1] = { head = head, tail = tail }
         end
     end
 end
@@ -121,7 +107,7 @@ function blobs.pack(t,how)
         end
         if how == "vertical" then
             -- we need to prepend a local par node
-            -- list[i].pack = node.vpack(list[i].head,"exactly")
+            -- list[i].pack = vpack_node_list(list[i].head,"exactly")
             report_blobs("vpack not yet supported")
         else
             list[i].pack = hpack_node_list(list[i].head,"exactly")
@@ -176,12 +162,44 @@ end
 
 -- for the moment here:
 
-function commands.widthofstring(str)
-    local l = t_hpack(str)
-    context(number.todimen(l.width))
+local function strwd(str)
+    local l = tohpack(str)
+    local w = l.width
     flush_node_list(l)
+    return w
 end
 
+local function strht(str)
+    local l = tohpack(str)
+    local h = l.height
+    flush_node_list(l)
+    return h
+end
+
+local function strdp(str)
+    local l = tohpack(str)
+    local d = l.depth
+    flush_node_list(l)
+    return d
+end
+
+local function strhd(str)
+    local l = tohpack(str)
+    local s = l.height + l.depth
+    flush_node_list(l)
+    return s
+end
+
+blobs.strwd = strwd
+blobs.strht = strht
+blobs.strdp = strdp
+blobs.strhd = strhd
+
+function commands.strwd(str) context(strwd(str)) end
+function commands.strht(str) context(strht(str)) end
+function commands.strdp(str) context(strdp(str)) end
+function commands.strhd(str) context(strhd(str)) end
+
 -- less efficient:
 --
 -- function commands.widthof(str)
diff --git a/tex/context/base/blob-ini.mkiv b/tex/context/base/blob-ini.mkiv
index 4fdb9e4b6..1dfb766f4 100644
--- a/tex/context/base/blob-ini.mkiv
+++ b/tex/context/base/blob-ini.mkiv
@@ -27,7 +27,19 @@
 
 % this one takes simple (utf) strings
 
-\def\widthofstring#1{\ctxcommand{widthofstring(\!!bs#1\!!es)}}
+\def\wdofstring#1{\dimexpr\ctxcommand{strwd(\!!bs#1\!!es)}\scaledpoint\relax}
+\def\htofstring#1{\dimexpr\ctxcommand{strht(\!!bs#1\!!es)}\scaledpoint\relax}
+\def\dpofstring#1{\dimexpr\ctxcommand{strdp(\!!bs#1\!!es)}\scaledpoint\relax}
+\def\hdofstring#1{\dimexpr\ctxcommand{strhd(\!!bs#1\!!es)}\scaledpoint\relax}
+
+\def\widthofstring         {\the\wdofstring}
+\def\heightofstring        {\the\htofstring}
+\def\depthofstring         {\the\dpofstring}
+\def\heightanddepthofstring{\the\hdofstring}
+
+\let\htdpofstring           \hdofstring
+
+\let\hd\htdp % if yes then move this
 
 % this one takes anything that can be typeset
 
diff --git a/tex/context/base/chem-str.lua b/tex/context/base/chem-str.lua
index 8c1bf38fc..347363345 100644
--- a/tex/context/base/chem-str.lua
+++ b/tex/context/base/chem-str.lua
@@ -37,25 +37,24 @@ local concat, insert, remove, unique, sorted = table.concat, table.insert, table
 local processor_tostring = typesetters and typesetters.processors.tostring
 local settings_to_array = utilities.parsers.settings_to_array
 local settings_to_array_with_repeat = utilities.parsers.settings_to_array_with_repeat
-local formatters = string.formatters
 
 local lpegmatch = lpeg.match
 local P, R, S, C, Cs, Ct, Cc, Cmt = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Ct, lpeg.Cc, lpeg.Cmt
 
-local variables  = interfaces and interfaces.variables
-local commands   = commands
-local context    = context
-local formatters = string.formatters
-local texcount   = tex.count
-
-local v_default = variables.default
-local v_small   = variables.small
-local v_medium  = variables.medium
-local v_big     = variables.big
-local v_normal  = variables.normal
-local v_fit     = variables.fit
-local v_on      = variables.on
-local v_none    = variables.none
+local variables    = interfaces and interfaces.variables
+local commands     = commands
+local context      = context
+local formatters   = string.formatters
+local texgetcount  = tex.getcount
+
+local v_default    = variables.default
+local v_small      = variables.small
+local v_medium     = variables.medium
+local v_big        = variables.big
+local v_normal     = variables.normal
+local v_fit        = variables.fit
+local v_on         = variables.on
+local v_none       = variables.none
 
 local mpnamedcolor = attributes.colors.mpnamedcolor
 local topoints     = number.topoints
@@ -717,7 +716,7 @@ function chemistry.start(settings)
     width,  left,   right, sp_width  = calculated(width, left,  right,factor,unit,scale)
     height, bottom, top,   sp_height = calculated(height,bottom,top,  factor,unit,scale)
     --
-    if width ~= "true" and height ~= "true" and texcount["@@trialtypesetting"] ~= 0 then
+    if width ~= "true" and height ~= "true" and texgetcount("@@trialtypesetting") ~= 0 then
         if trace_structure then
             report_chemistry("skipping trial run")
         end
diff --git a/tex/context/base/cldf-bas.lua b/tex/context/base/cldf-bas.lua
index 6adeb2272..b982fc364 100644
--- a/tex/context/base/cldf-bas.lua
+++ b/tex/context/base/cldf-bas.lua
@@ -38,7 +38,8 @@ local new_rule     = nodepool.rule
 local new_glyph    = nodepool.glyph
 
 local current_font = font.current
-local texcount     = tex.count
+local texgetcount  = tex.getcount
+local texsetcount  = tex.setcount
 
 function context.char(k) -- used as escape too, so don't change to utf
     if type(k) == "table" then
@@ -163,9 +164,9 @@ context.endhbox = context.egroup
 
 local function allocate(name,what,cmd)
     local a = format("c_syst_last_allocated_%s",what)
-    local n = texcount[a] + 1
-    if n <= texcount.c_syst_max_allocated_register then
-        texcount[a] = n
+    local n = texgetcount(a) + 1
+    if n <= texgetcount("c_syst_max_allocated_register") then
+        texsetcount(a,n)
     end
     context("\\global\\expandafter\\%sdef\\csname %s\\endcsname %s\\relax",cmd or what,name,n)
     return n
diff --git a/tex/context/base/cldf-ini.lua b/tex/context/base/cldf-ini.lua
index 2272c05ea..44bf522d3 100644
--- a/tex/context/base/cldf-ini.lua
+++ b/tex/context/base/cldf-ini.lua
@@ -38,7 +38,7 @@ local texsprint         = tex.sprint
 local textprint         = tex.tprint
 local texprint          = tex.print
 local texwrite          = tex.write
-local texcount          = tex.count
+local texgetcount       = tex.getcount
 
 local isnode            = node.is_node -- after 0.65 just node.type
 local writenode         = node.write
@@ -86,13 +86,13 @@ local function _flush_f_(n)
     else
         local tn = type(sn)
         if tn == "function" then
-            if not sn() and texcount["@@trialtypesetting"] == 0 then  -- @@trialtypesetting is private!
+            if not sn() and texgetcount("@@trialtypesetting") == 0 then  -- @@trialtypesetting is private!
                 _stack_f_[n] = nil
             else
                 -- keep, beware, that way the stack can grow
             end
         else
-            if texcount["@@trialtypesetting"] == 0 then  -- @@trialtypesetting is private!
+            if texgetcount("@@trialtypesetting") == 0 then  -- @@trialtypesetting is private!
                 writenode(sn)
                 _stack_f_[n] = nil
             else
@@ -107,7 +107,7 @@ local function _flush_n_(n)
     local sn = _stack_n_[n]
     if not sn then
         report_cld("data with id %a cannot be found on stack",n)
-    elseif texcount["@@trialtypesetting"] == 0 then  -- @@trialtypesetting is private!
+    elseif texgetcount("@@trialtypesetting") == 0 then  -- @@trialtypesetting is private!
         writenode(sn)
         _stack_n_[n] = nil
     else
diff --git a/tex/context/base/cont-new.mkiv b/tex/context/base/cont-new.mkiv
index 0e3ff6cf6..283e924cc 100644
--- a/tex/context/base/cont-new.mkiv
+++ b/tex/context/base/cont-new.mkiv
@@ -11,7 +11,7 @@
 %C therefore copyrighted by \PRAGMA. See mreadme.pdf for
 %C details.
 
-\newcontextversion{2013.06.10 22:51}
+\newcontextversion{2013.07.12 19:10}
 
 %D This file is loaded at runtime, thereby providing an excellent place for
 %D hacks, patches, extensions and new features.
diff --git a/tex/context/base/context-version.pdf b/tex/context/base/context-version.pdf
index f08b763d1..3bf5f1c53 100644
Binary files a/tex/context/base/context-version.pdf and b/tex/context/base/context-version.pdf differ
diff --git a/tex/context/base/context.mkiv b/tex/context/base/context.mkiv
index 2d018984d..e6cc65a9d 100644
--- a/tex/context/base/context.mkiv
+++ b/tex/context/base/context.mkiv
@@ -25,7 +25,7 @@
 %D up and the dependencies are more consistent.
 
 \edef\contextformat {\jobname}
-\edef\contextversion{2013.06.10 22:51}
+\edef\contextversion{2013.07.12 19:10}
 \edef\contextkind   {beta}
 
 %D For those who want to use this:
diff --git a/tex/context/base/core-dat.lua b/tex/context/base/core-dat.lua
index 826d3a675..242d362d0 100644
--- a/tex/context/base/core-dat.lua
+++ b/tex/context/base/core-dat.lua
@@ -21,13 +21,17 @@ local trace_pagestates = false  trackers.register("job.pagestates", function(v)
 local report_dataset   = logs.reporter("dataset")
 local report_pagestate = logs.reporter("pagestate")
 
-local allocate = utilities.storage.allocate
+local allocate         = utilities.storage.allocate
 local settings_to_hash = utilities.parsers.settings_to_hash
-local texcount = tex.count
-local formatters = string.formatters
-local v_yes = interfaces.variables.yes
 
-local new_latelua = nodes.pool.latelua
+local texgetcount      = tex.getcount
+local texsetcount      = tex.setcount
+
+local formatters       = string.formatters
+
+local v_yes            = interfaces.variables.yes
+
+local new_latelua      = nodes.pool.latelua
 
 local collected = allocate()
 local tobesaved = allocate()
@@ -86,7 +90,7 @@ local function setdata(settings)
         set.index = index
         data.index = index
         data.order = index
-        data.realpage = texcount.realpageno
+        data.realpage = texgetcount("realpageno")
         if trace_datasets then
             report_dataset("action %a, name %a, tag %a, index %a","assign delayed",name,tag,index)
         end
@@ -101,7 +105,7 @@ datasets.setdata = setdata
 function datasets.extend(name,tag)
     local set = sets[name]
     local order = set.order + 1
-    local realpage = texcount.realpageno
+    local realpage = texgetcount("realpageno")
     set.order = order
     local t = tobesaved[name][tag]
     t.realpage = realpage
@@ -207,7 +211,7 @@ local function setstate(settings)
     else
         tag = tonumber(tag) or tag -- autonumber saves keys
     end
-    local realpage = texcount.realpageno
+    local realpage = texgetcount("realpageno")
     local data = realpage
     list[tag] = data
     if trace_pagestates then
@@ -219,7 +223,7 @@ end
 pagestates.setstate = setstate
 
 function pagestates.extend(name,tag)
-    local realpage = texcount.realpageno
+    local realpage = texgetcount("realpageno")
     if trace_pagestates then
         report_pagestate("action %a, name %a, tag %a, preset %a","synchronize",name,tag,realpage)
     end
@@ -261,9 +265,5 @@ end
 function commands.setpagestaterealpageno(name,tag)
     local t = collected[name]
     t = t and (t[tag] or t[tonumber(tag)])
-    if t then
-        texcount.realpagestateno = t
-    else
-        texcount.realpagestateno = texcount.realpageno
-    end
+    texsetcount("realpagestateno",t or texgetcount("realpageno"))
 end
diff --git a/tex/context/base/core-env.lua b/tex/context/base/core-env.lua
index de977d0bb..a4d1fdd92 100644
--- a/tex/context/base/core-env.lua
+++ b/tex/context/base/core-env.lua
@@ -15,7 +15,7 @@ local P, C, S, Cc, lpegmatch, patterns = lpeg.P, lpeg.C, lpeg.S, lpeg.Cc, lpeg.m
 
 local csname_id         = token.csname_id
 local create            = token.create
-local texcount          = tex.count
+local texgetcount       = tex.getcount
 local texsetcount       = tex.setcount
 
 local allocate          = utilities.storage.allocate
@@ -44,8 +44,8 @@ setmetatableindex(tex.modes, function(t,k)
         if csname_id(n) == undefined then
             return false
         else
-            modes[k] = function() return texcount[n] >= 1 end
-            return texcount[n] >= 1
+            modes[k] = function() return texgetcount(n) >= 1 end
+            return texgetcount(n) >= 1
         end
     end
 end)
@@ -59,18 +59,18 @@ setmetatableindex(tex.systemmodes, function(t,k)
         if csname_id(n) == undefined then
             return false
         else
-            systemmodes[k] = function() return texcount[n] >= 1 end
-            return texcount[n] >= 1
+            systemmodes[k] = function() return texgetcount(n) >= 1 end
+            return texgetcount(n) >= 1
         end
     end
 end)
 
 setmetatableindex(tex.constants, function(t,k)
-    return csname_id(k) ~= undefined and texcount[k] or 0
+    return csname_id(k) ~= undefined and texgetcount(k) or 0
 end)
 
 setmetatableindex(tex.conditionals, function(t,k) -- 0 == true
-    return csname_id(k) ~= undefined and texcount[k] == 0
+    return csname_id(k) ~= undefined and texgetcount(k) == 0
 end)
 
 setmetatableindex(tex.ifs, function(t,k)
@@ -86,7 +86,7 @@ end)
 --     if glob then
 --         texsetcount("global",name,0)
 --     else
---         texcount[name] = 0
+--         texsetcount(name,0)
 --     end
 -- end
 --
@@ -94,7 +94,7 @@ end)
 --     if glob then
 --         texsetcount("global",name,1)
 --     else
---         texcount[name] = 1
+--         texsetcount(name,1)
 --     end
 -- end
 
diff --git a/tex/context/base/core-uti.lua b/tex/context/base/core-uti.lua
index 53ff891e2..4e3c839bd 100644
--- a/tex/context/base/core-uti.lua
+++ b/tex/context/base/core-uti.lua
@@ -20,7 +20,6 @@ saves much runtime but at the cost of more memory usage.</p>
 local format, match = string.format, string.match
 local next, type, tostring = next, type, tostring
 local concat = table.concat
-local texcount = tex.count
 
 local definetable   = utilities.tables.definetable
 local accesstable   = utilities.tables.accesstable
@@ -30,6 +29,8 @@ local packers       = utilities.packers
 local allocate      = utilities.storage.allocate
 local mark          = utilities.storage.mark
 
+local texgetcount   = tex.getcount
+
 local report_passes = logs.reporter("job","passes")
 
 job                 = job or { }
@@ -149,7 +150,7 @@ function job.save(filename) -- we could return a table but it can get pretty lar
     local f = io.open(filename,'w')
     if f then
         f:write("local utilitydata = { }\n\n")
-        f:write(serialize(comment,"utilitydata.comment",true,true),"\n\n")
+        f:write(serialize(comment,"utilitydata.comment",true),"\n\n")
         for l=1,#savelist do
             local list      = savelist[l]
             local target    = format("utilitydata.%s",list[1])
@@ -162,11 +163,11 @@ function job.save(filename) -- we could return a table but it can get pretty lar
                 packers.pack(data,jobpacker,true)
             end
             local definer, name = definetable(target,true,true) -- no first and no last
-            f:write(definer,"\n\n",serialize(data,name,true,true),"\n\n")
+            f:write(definer,"\n\n",serialize(data,name,true),"\n\n")
         end
         if job.pack then
             packers.strip(jobpacker)
-            f:write(serialize(jobpacker,"utilitydata.job.packed",true,true),"\n\n")
+            f:write(serialize(jobpacker,"utilitydata.job.packed",true),"\n\n")
         end
         f:write("return utilitydata")
         f:close()
@@ -264,7 +265,7 @@ end)
 
 statistics.register("callbacks", function()
     local total, indirect = status.callbacks or 0, status.indirect_callbacks or 0
-    local pages = texcount['realpageno'] - 1
+    local pages = texgetcount('realpageno') - 1
     if pages > 1 then
         return format("direct: %s, indirect: %s, total: %s (%i per page)", total-indirect, indirect, total, total/pages)
     else
@@ -280,8 +281,8 @@ end)
 
 function statistics.formatruntime(runtime)
     if not environment.initex then -- else error when testing as not counters yet
-        local shipped = texcount['nofshipouts']
-        local pages = texcount['realpageno']
+        local shipped = texgetcount('nofshipouts')
+        local pages = texgetcount('realpageno')
         if pages > shipped then
             pages = shipped
         end
diff --git a/tex/context/base/data-met.lua b/tex/context/base/data-met.lua
index 96da70bfd..ee9de3fd9 100644
--- a/tex/context/base/data-met.lua
+++ b/tex/context/base/data-met.lua
@@ -36,7 +36,7 @@ local function splitmethod(filename) -- todo: filetype in specification
     end
     filename = file.collapsepath(filename,".") -- hm, we should keep ./ in some cases
 
--- filename = gsub(filename,"^%./",getcurrentdir().."/") -- we will merge dir.expandname and collapse some day
+ -- filename = gsub(filename,"^%./",getcurrentdir().."/") -- we will merge dir.expandname and collapse some day
 
     if not find(filename,"://") then
         return { scheme = "file", path = filename, original = filename, filename = filename }
@@ -49,6 +49,16 @@ local function splitmethod(filename) -- todo: filetype in specification
     end
 end
 
+-- local function splitmethod(filename) -- todo: filetype in specification
+--     if not filename then
+--         return { scheme = "unknown", original = filename }
+--     end
+--     if type(filename) == "table" then
+--         return filename -- already split
+--     end
+--     return url.hashed(filename)
+-- end
+
 resolvers.splitmethod = splitmethod -- bad name but ok
 
 -- the second argument is always analyzed (saves time later on) and the original
diff --git a/tex/context/base/data-res.lua b/tex/context/base/data-res.lua
index 532b6261f..f1ddb62aa 100644
--- a/tex/context/base/data-res.lua
+++ b/tex/context/base/data-res.lua
@@ -1303,7 +1303,7 @@ local function find_otherwise(filename,filetype,wantedfiles,allresults) -- other
 end
 
 -- we could have a loop over the 6 functions but then we'd have to
--- always analyze
+-- always analyze .. todo: use url split
 
 collect_instance_files = function(filename,askedformat,allresults) -- uses nested
     askedformat = askedformat or ""
diff --git a/tex/context/base/file-ini.lua b/tex/context/base/file-ini.lua
index 1872ed3d3..2bc742a1f 100644
--- a/tex/context/base/file-ini.lua
+++ b/tex/context/base/file-ini.lua
@@ -13,13 +13,13 @@ if not modules then modules = { } end modules ['file-ini'] = {
 
 resolvers.jobs = resolvers.jobs or { }
 
-local texcount = tex.count
-local setvalue = context.setvalue
+local texsetcount = tex.setcount
+local setvalue    = context.setvalue
 
 function commands.splitfilename(fullname)
     local t = file.nametotable(fullname)
     local path = t.path
-    texcount.splitoffkind = (path == "" and 0) or (path == '.' and 1) or 2
+    texsetcount("splitoffkind",(path == "" and 0) or (path == '.' and 1) or 2)
     setvalue("splitofffull",fullname)
     setvalue("splitoffpath",path)
     setvalue("splitoffname",t.name)
diff --git a/tex/context/base/font-chk.lua b/tex/context/base/font-chk.lua
index 467afae9b..6dc1667bb 100644
--- a/tex/context/base/font-chk.lua
+++ b/tex/context/base/font-chk.lua
@@ -68,7 +68,7 @@ local function onetimemessage(font,char,message) -- char == false returns table
     if char == false then
         return table.sortedkeys(category)
     elseif not category[char] then
-        report_fonts("char %U in font %a with id %a: %s",char,tfmdata.properties.fullname,font,message)
+        report_fonts("char %C in font %a with id %a: %s",char,tfmdata.properties.fullname,font,message)
         category[char] = true
     end
 end
@@ -210,6 +210,7 @@ function checkers.missing(head)
         local char = n.char
         if font ~= lastfont then
             characters = fontcharacters[font]
+            lastfont = font
         end
         if not characters[char] and is_character[chardata[char].category] then
             if action == "remove" then
@@ -363,7 +364,7 @@ end)
 
 -- for the moment here
 
-function helpers.expandglyph(characters,index,done)
+local function expandglyph(characters,index,done)
     done = done or { }
     if not done[index] then
         local data = characters[index]
@@ -390,3 +391,5 @@ function helpers.expandglyph(characters,index,done)
         end
     end
 end
+
+helpers.expandglyph = expandglyph
diff --git a/tex/context/base/font-col.lua b/tex/context/base/font-col.lua
index 20c99c9b4..d461d60b4 100644
--- a/tex/context/base/font-col.lua
+++ b/tex/context/base/font-col.lua
@@ -15,9 +15,9 @@ local file, lpeg, table, string = file, lpeg, table, string
 local type, next, toboolean = type, next, toboolean
 local gmatch = string.gmatch
 local fastcopy = table.fastcopy
------ P, Cc, lpegmatch = lpeg.P, lpeg.Cc, lpeg.match
 
-local traverse_id        = node.traverse_id
+local traverse_id        = nodes.traverse_id
+
 local settings_to_hash   = utilities.parsers.settings_to_hash
 
 local trace_collecting   = false  trackers.register("fonts.collecting", function(v) trace_collecting = v end)
@@ -215,7 +215,8 @@ function collections.process(head) -- this way we keep feature processing
                     if trace_collecting then
                         report_fonts("remapping character %a in font %a to character %a in font %a",n.char,n.font,newchar,newid)
                     end
-                    n.font, n.char = newid, newchar
+                    n.font = newid
+                    n.char = newchar
                 else
                     if trace_collecting then
                         report_fonts("remapping font %a to %a for character %a",n.font,id,n.char)
diff --git a/tex/context/base/font-con.lua b/tex/context/base/font-con.lua
index 790d4877a..6a466a027 100644
--- a/tex/context/base/font-con.lua
+++ b/tex/context/base/font-con.lua
@@ -1278,7 +1278,8 @@ function constructors.collectprocessors(what,tfmdata,features,trace,report)
         local whathandler    = handlers[what]
         local whatfeatures   = whathandler.features
         local whatprocessors = whatfeatures.processors
-        local processors     = whatprocessors[properties.mode]
+        local mode           = properties.mode
+        local processors     = whatprocessors[mode]
         if processors then
             for i=1,#processors do
                 local step = processors[i]
@@ -1295,7 +1296,7 @@ function constructors.collectprocessors(what,tfmdata,features,trace,report)
                 end
             end
         elseif trace then
-            report("no feature processors for mode %a for font %a",mode,tfmdata.properties.fullname)
+            report("no feature processors for mode %a for font %a",mode,properties.fullname)
         end
     end
     return processes
@@ -1309,7 +1310,8 @@ function constructors.applymanipulators(what,tfmdata,features,trace,report)
         local whathandler      = handlers[what]
         local whatfeatures     = whathandler.features
         local whatmanipulators = whatfeatures.manipulators
-        local manipulators     = whatmanipulators[properties.mode]
+        local mode             = properties.mode
+        local manipulators     = whatmanipulators[mode]
         if manipulators then
             for i=1,#manipulators do
                 local step = manipulators[i]
@@ -1318,7 +1320,7 @@ function constructors.applymanipulators(what,tfmdata,features,trace,report)
                 if value then
                     local action = step.action
                     if trace then
-                        report("applying feature manipulator %a for mode %a for font %a",feature,mode,tfmdata.properties.fullname)
+                        report("applying feature manipulator %a for mode %a for font %a",feature,mode,properties.fullname)
                     end
                     if action then
                         action(tfmdata,feature,value)
diff --git a/tex/context/base/font-ctx.lua b/tex/context/base/font-ctx.lua
index 31de74578..7388a49ca 100644
--- a/tex/context/base/font-ctx.lua
+++ b/tex/context/base/font-ctx.lua
@@ -14,7 +14,6 @@ if not modules then modules = { } end modules ['font-ctx'] = {
 
 local context, commands = context, commands
 
-local texcount, texsetcount = tex.count, tex.setcount
 local format, gmatch, match, find, lower, gsub, byte = string.format, string.gmatch, string.match, string.find, string.lower, string.gsub, string.byte
 local concat, serialize, sort, fastcopy, mergedtable = table.concat, table.serialize, table.sort, table.fastcopy, table.merged
 local sortedhash, sortedkeys, sequenced = table.sortedhash, table.sortedkeys, table.sequenced
@@ -56,8 +55,15 @@ local fontgoodies         = fonts.goodies
 local helpers             = fonts.helpers
 local hashes              = fonts.hashes
 local currentfont         = font.current
-local texattribute        = tex.attribute
-local texdimen            = tex.dimen
+
+local texgetattribute     = tex.getattribute
+local texsetattribute     = tex.setattribute
+local texgetdimen         = tex.getdimen
+local texsetcount         = tex.setcount
+local texget              = tex.get
+
+local texdefinefont       = tex.definefont
+local texsp               = tex.sp
 
 local fontdata            = hashes.identifiers
 local characters          = hashes.chardata
@@ -659,14 +665,14 @@ end
 -- local withcache = { } -- concat might be less efficient than nested tables
 --
 -- local function withset(name,what)
---     local zero = texattribute[0]
+--     local zero = texgetattribute(0)
 --     local hash = zero .. "+" .. name .. "*" .. what
 --     local done = withcache[hash]
 --     if not done then
 --         done = mergecontext(zero,name,what)
 --         withcache[hash] = done
 --     end
---     texattribute[0] = done
+--     texsetattribute(0,done)
 -- end
 --
 -- local function withfnt(name,what,font)
@@ -677,7 +683,7 @@ end
 --         done = registercontext(font,name,what)
 --         withcache[hash] = done
 --     end
---     texattribute[0] = done
+--     texsetattribute(0,done)
 -- end
 
 function specifiers.showcontext(name)
@@ -848,18 +854,18 @@ function commands.definefont_one(str)
     if size and size ~= "" then
         local mode, size = lpegmatch(sizepattern,size)
         if size and mode then
-            texcount.scaledfontmode = mode
+            texsetcount("scaledfontmode",mode)
             setsomefontsize(size)
         else
-            texcount.scaledfontmode = 0
+            texsetcount("scaledfontmode",0)
             setemptyfontsize()
         end
     elseif true then
         -- so we don't need to check in tex
-        texcount.scaledfontmode = 2
+        texsetcount("scaledfontmode",2)
         setemptyfontsize()
     else
-        texcount.scaledfontmode = 0
+        texsetcount("scaledfontmode",0)
         setemptyfontsize()
     end
     specification = definers.makespecification(str,lookup,name,sub,method,detail,size)
@@ -983,7 +989,7 @@ function commands.definefont_two(global,cs,str,size,inheritancemode,classfeature
                 name,tfmdata,nice_cs(cs),classfeatures,fontfeatures,classfallbacks,fontfallbacks,classgoodies,goodies,classdesignsize,fontdesignsize)
         end
         csnames[tfmdata] = specification.cs
-        tex.definefont(global,cs,tfmdata)
+        texdefinefont(global,cs,tfmdata)
         -- resolved (when designsize is used):
         setsomefontsize((fontdata[tfmdata].parameters.size or 0) .. "sp")
         lastfontid = tfmdata
@@ -1001,7 +1007,7 @@ function commands.definefont_two(global,cs,str,size,inheritancemode,classfeature
         csnames[id] = specification.cs
         tfmdata.properties.id = id
         definers.register(tfmdata,id) -- to be sure, normally already done
-        tex.definefont(global,cs,id)
+        texdefinefont(global,cs,id)
         constructors.cleanuptable(tfmdata)
         constructors.finalize(tfmdata)
         if trace_defining then
@@ -1050,7 +1056,7 @@ function definers.define(specification)
         specification.detail        = specification.detail or (detail ~= "" and detail) or ""
         --
         if type(specification.size) == "string" then
-            specification.size = tex.sp(specification.size) or 655260
+            specification.size = texsp(specification.size) or 655260
         end
         --
         specification.specification = "" -- not used
@@ -1074,7 +1080,7 @@ function definers.define(specification)
             return -1, nil
         elseif type(tfmdata) == "number" then
             if cs then
-                tex.definefont(specification.global,cs,tfmdata)
+                texdefinefont(specification.global,cs,tfmdata)
                 csnames[tfmdata] = cs
             end
             return tfmdata, fontdata[tfmdata]
@@ -1083,7 +1089,7 @@ function definers.define(specification)
             tfmdata.properties.id = id
             definers.register(tfmdata,id)
             if cs then
-                tex.definefont(specification.global,cs,id)
+                texdefinefont(specification.global,cs,id)
                 csnames[id] = cs
             end
             constructors.cleanuptable(tfmdata)
@@ -1103,7 +1109,7 @@ local n = 0
 function definers.internal(specification,cs)
     specification = specification or { }
     local name    = specification.name
-    local size    = specification.size and number.todimen(specification.size) or texdimen.bodyfontsize
+    local size    = specification.size and number.todimen(specification.size) or texgetdimen("bodyfontsize")
     local number  = tonumber(specification.number)
     local id      = nil
     if number then
@@ -1423,11 +1429,11 @@ function commands.featureattribute(tag)
 end
 
 function commands.setfontfeature(tag)
-    texattribute[0] = contextnumber(tag)
+    texsetattribute(0,contextnumber(tag))
 end
 
 function commands.resetfontfeature()
-    texattribute[0] = 0
+    texsetattribute(0,0)
 end
 
 -- function commands.addfs(tag) withset(tag, 1) end
@@ -1578,19 +1584,24 @@ end
 local quads       = hashes.quads
 local xheights    = hashes.xheights
 
-setmetatableindex(number.dimenfactors, function(t,k)
+setmetatableindex(dimenfactors, function(t,k)
     if k == "ex" then
-        return xheigths[currentfont()]
+        return 1/xheights[currentfont()]
     elseif k == "em" then
-        return quads[currentfont()]
-    elseif k == "%" then
-        return dimen.hsize/100
+        return 1/quads[currentfont()]
+    elseif k == "pct" or k == "%" then
+        return 1/(texget("hsize")/100)
     else
      -- error("wrong dimension: " .. (s or "?")) -- better a message
         return false
     end
 end)
 
+dimenfactors.ex   = nil
+dimenfactors.em   = nil
+dimenfactors["%"] = nil
+dimenfactors.pct  = nil
+
 --[[ldx--
 <p>Before a font is passed to <l n='tex'/> we scale it. Here we also need
 to scale virtual characters.</p>
@@ -1668,17 +1679,17 @@ local hows = {
 
 function commands.feature(how,parent,name,font) -- 0/1 test temporary for testing
     if not how or how == 0 then
-        if trace_features and texattribute[0] ~= 0 then
+        if trace_features and texgetattribute(0) ~= 0 then
             report_cummulative("font %!font:name!, reset",fontdata[font or true])
         end
-        texattribute[0] = 0
+        texsetattribute(0,0)
     elseif how == true or how == 1 then
         local hash = "feature > " .. parent
         local done = cache[hash]
         if trace_features and done then
             report_cummulative("font %!font:name!, revive %a : %!font:features!",fontdata[font or true],parent,setups[numbers[done]])
         end
-        texattribute[0] = done or 0
+        texsetattribute(0,done or 0)
     else
         local full = parent .. how .. name
         local hash = "feature > " .. full
@@ -1696,7 +1707,7 @@ function commands.feature(how,parent,name,font) -- 0/1 test temporary for testin
                 report_cummulative("font %!font:name!, %s %a : %!font:features!",fontdata[font or true],hows[how],full,setups[numbers[done]])
             end
         end
-        texattribute[0] = done
+        texsetattribute(0,done)
     end
 end
 
@@ -1823,7 +1834,7 @@ local function analyzeprocessor(head,font,attr)
 end
 
 registerotffeature { -- adapts
-    name         = "analyze",
+    name       = "analyze",
     processors = {
         node     = analyzeprocessor,
     }
diff --git a/tex/context/base/font-ext.lua b/tex/context/base/font-ext.lua
index 89d5927d4..3d78b0a3c 100644
--- a/tex/context/base/font-ext.lua
+++ b/tex/context/base/font-ext.lua
@@ -469,7 +469,7 @@ end
 
 registerotffeature {
     name         = "protrusion",
-    description  = "shift characters into the left and or right margin",
+    description  = "l/r margin character protrusion",
     initializers = {
         base = initializeprotrusion,
         node = initializeprotrusion,
diff --git a/tex/context/base/font-gds.lua b/tex/context/base/font-gds.lua
index 6332f40b0..1b2ac0009 100644
--- a/tex/context/base/font-gds.lua
+++ b/tex/context/base/font-gds.lua
@@ -18,6 +18,7 @@ local trace_goodies      = false  trackers.register("fonts.goodies", function(v)
 local report_goodies     = logs.reporter("fonts","goodies")
 
 local allocate           = utilities.storage.allocate
+local setmetatableindex  = table.setmetatableindex
 
 local otf                = fonts.handlers.otf
 local afm                = fonts.handlers.afm
@@ -43,6 +44,10 @@ local addotffeature      = otf.enhancers.addfeature
 
 local findfile           = resolvers.findfile
 
+local glyph_code         = nodes.nodecodes.glyph
+
+local traverse_id        = nodes.traverse_id
+
 function fontgoodies.report(what,trace,goodies)
     if trace_goodies or trace then
         local whatever = goodies[what]
@@ -298,27 +303,104 @@ local function setcolorscheme(tfmdata,scheme)
     tfmdata.properties.colorscheme = false
 end
 
-local fontdata      = fonts.hashes.identifiers
-local setnodecolor  = nodes.tracers.colors.set
-local traverse_id   = node.traverse_id
-local a_colorscheme = attributes.private('colorscheme')
-local glyph         = node.id("glyph")
+local fontproperties = fonts.hashes.properties
+
+local a_colorscheme  = attributes.private('colorscheme')
+local setnodecolor   = nodes.tracers.colors.set
+
+-- function colorschemes.coloring(head)
+--     local lastfont, lastscheme
+--     local done = false
+--     for n in traverse_id(glyph_code,head) do
+--         local a = n[a_colorscheme]
+--         if a then
+--             local f = n.font
+--             if f ~= lastfont then
+--                 lastscheme = fontproperties[f].colorscheme
+--                 lastfont   = f
+--             end
+--             if lastscheme then
+--                 local sc = lastscheme[n.char]
+--                 if sc then
+--                     done = true
+--                     setnodecolor(n,"colorscheme:"..a..":"..sc) -- slow
+--                 end
+--             end
+--         end
+--     end
+--     return head, done
+-- end
+
+-- seldom used, mostly in manuals, so non critical .. anyhow, somewhat faster:
+
+-- function colorschemes.coloring(head)
+--     local lastfont   = nil
+--     local lastattr   = nil
+--     local lastscheme = nil
+--     local lastprefix = nil
+--     local done      = nil
+--     for n in traverse_id(glyph_code,head) do
+--         local a = n[a_colorscheme]
+--         if a then
+--             if a ~= lastattr then
+--                 lastattr   = a
+--                 lastprefix = "colorscheme:" .. a .. ":"
+--             end
+--             local f = n.font
+--             if f ~= lastfont then
+--                 lastfont   = f
+--                 lastscheme = fontproperties[f].colorscheme
+--             end
+--             if lastscheme then
+--                 local sc = lastscheme[n.char]
+--                 if sc then
+--                     setnodecolor(n,lastprefix .. sc) -- slow
+--                     done = true
+--                 end
+--             end
+--         end
+--     end
+--     return head, done
+-- end
+
+-- ok, in case we have hundreds of pages colored:
+
+local cache = { } -- this could be a weak table
+
+setmetatableindex(cache,function(t,a)
+    local v = { }
+    setmetatableindex(v,function(t,c)
+        local v = "colorscheme:" .. a .. ":" .. c
+        t[c] = v
+        return c
+    end)
+    t[a]= v
+    return v
+end)
 
 function colorschemes.coloring(head)
-    local lastfont, lastscheme
-    local done = false
-    for n in traverse_id(glyph,head) do
+    local lastfont   = nil
+    local lastattr   = nil
+    local lastcache  = nil
+    local lastscheme = nil
+    local done       = nil
+    for n in traverse_id(glyph_code,head) do
         local a = n[a_colorscheme]
         if a then
             local f = n.font
             if f ~= lastfont then
-                lastscheme, lastfont = fontdata[f].properties.colorscheme, f
+                lastfont   = f
+                lastscheme = fontproperties[f].colorscheme
+            end
+            if a ~= lastattr then
+                lastattr  = a
+                lastcache = cache[a]
             end
             if lastscheme then
                 local sc = lastscheme[n.char]
                 if sc then
+                    setnodecolor(n,lastcache[sc]) -- we could inline this one
                     done = true
-                    setnodecolor(n,"colorscheme:"..a..":"..sc) -- slow
                 end
             end
         end
@@ -691,7 +773,7 @@ function fontgoodies.designsizes.register(name,size,specification)
         d.default = specification
     else
         if type(size) == "string" then
-            size = texsp(size)
+            size = texsp(size) -- hm
         end
         local ranges = d.ranges
         ranges[#ranges+1] = { size, specification }
diff --git a/tex/context/base/font-nod.lua b/tex/context/base/font-nod.lua
index f99130279..7fa3297d4 100644
--- a/tex/context/base/font-nod.lua
+++ b/tex/context/base/font-nod.lua
@@ -41,10 +41,12 @@ tracers.characters     = char_tracers
 local step_tracers     = tracers.steppers or { }
 tracers.steppers       = step_tracers
 
-local copy_node_list   = node.copy_list
-local hpack_node_list  = node.hpack
-local free_node_list   = node.flush_list
-local traverse_nodes   = node.traverse
+local texsetbox        = tex.setbox
+
+local copy_node_list   = nodes.copy_list
+local hpack_node_list  = nodes.hpack
+local free_node_list   = nodes.flush_list
+local traverse_nodes   = nodes.traverse
 
 local nodecodes        = nodes.nodecodes
 local whatcodes        = nodes.whatcodes
@@ -232,7 +234,8 @@ end
 function step_tracers.glyphs(n,i)
     local c = collection[i]
     if c then
-        tex.box[n] = hpack_node_list(copy_node_list(c))
+        local b = hpack_node_list(copy_node_list(c)) -- multiple arguments 
+        texsetbox(n,b)
     end
 end
 
diff --git a/tex/context/base/font-ota.lua b/tex/context/base/font-ota.lua
index 79fcf3fa2..295452cce 100644
--- a/tex/context/base/font-ota.lua
+++ b/tex/context/base/font-ota.lua
@@ -32,6 +32,7 @@ local a_state             = attributes.private('state')
 
 local nodecodes           = nodes.nodecodes
 local glyph_code          = nodecodes.glyph
+local disc_code           = nodecodes.disc
 local math_code           = nodecodes.math
 
 local traverse_id         = node.traverse_id
@@ -118,7 +119,7 @@ function analyzers.setstate(head,font)
             end
         elseif id == disc_code then
             -- always in the middle
-            current[a_state] = s_midi
+            current[a_state] = s_medi
             last = current
         else -- finish
             if first and first == last then
@@ -179,7 +180,7 @@ end
 
 registerotffeature {
     name         = "analyze",
-    description  = "analysis of (for instance) character classes",
+    description  = "analysis of character classes",
     default      = true,
     initializers = {
         node     = analyzeinitializer,
diff --git a/tex/context/base/font-otn.lua b/tex/context/base/font-otn.lua
index e0cb9acbc..dbec53ca3 100644
--- a/tex/context/base/font-otn.lua
+++ b/tex/context/base/font-otn.lua
@@ -530,7 +530,7 @@ local function multiple_glyphs(head,start,multiple) -- marks ?
 end
 
 function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence)
-    local value  = featurevalue == true and tfmdata.shared.features[kind] or featurevalue
+    local value = featurevalue == true and tfmdata.shared.features[kind] or featurevalue
     local choice, comment = get_alternative_glyph(start,alternative,value,trace_alternatives)
     if choice then
         if trace_alternatives then
@@ -648,7 +648,7 @@ function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence
             if marks[basechar] then
                 while true do
                     base = base.prev
-                    if base and base.id == glyph_code  and base.font == currentfont and base.subtype<256 then
+                    if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then
                         basechar = base.char
                         if not marks[basechar] then
                             break
diff --git a/tex/context/base/font-otx.lua b/tex/context/base/font-otx.lua
index 5c41ad66f..e3f680c22 100644
--- a/tex/context/base/font-otx.lua
+++ b/tex/context/base/font-otx.lua
@@ -32,6 +32,7 @@ local a_state             = attributes.private('state')
 
 local nodecodes           = nodes.nodecodes
 local glyph_code          = nodecodes.glyph
+local disc_code           = nodecodes.disc
 local math_code           = nodecodes.math
 
 local traverse_id         = node.traverse_id
@@ -123,7 +124,7 @@ function analyzers.setstate(head,font)
             end
         elseif id == disc_code then
             -- always in the middle
-            current[a_state] = s_midi
+            current[a_state] = s_medi
             last = current
         else -- finish
             if first and first == last then
diff --git a/tex/context/base/font-pre.mkiv b/tex/context/base/font-pre.mkiv
index ef3694c2b..919a40805 100644
--- a/tex/context/base/font-pre.mkiv
+++ b/tex/context/base/font-pre.mkiv
@@ -72,28 +72,38 @@
   [mode=none,
    features=no]
 
-\definefontfeature % might move
-  [arabic]
-  [mode=node,language=dflt,script=arab,ccmp=yes,
+\definefontfeature
+  [semetic-complete]
+  [mode=node,analyze=yes,language=dflt,ccmp=yes,
    init=yes,medi=yes,fina=yes,isol=yes,
-   liga=yes,dlig=yes,rlig=yes,clig=yes,calt=yes,
-   mark=yes,mkmk=yes,kern=yes,curs=yes]
+   mark=yes,mkmk=yes,kern=yes,curs=yes,
+   liga=yes,dlig=yes,rlig=yes,clig=yes,calt=yes]
+
+\definefontfeature
+  [semetic-simple]
+  [mode=node,analyze=yes,language=dflt,ccmp=yes,
+   init=yes,medi=yes,fina=yes,isol=yes,
+   mark=yes,mkmk=yes,kern=yes,curs=yes,
+   rlig=yes,calt=yes]
 
 \definefontfeature
-  [hebrew]
   [arabic]
+  [semetic-complete]
+  [script=arab]
+
+\definefontfeature
+  [hebrew]
+  [semetic-complete]
   [script=hebr]
 
-\definefontfeature % might move
+\definefontfeature
   [simplearabic]
-  [mode=node,language=dflt,script=arab,
-   init=yes,medi=yes,fina=yes,
-   rlig=yes,calt=yes,
-   mark=yes,mkmk=yes,curs=yes]
+  [semetic-simple]
+  [script=arab]
 
 \definefontfeature
   [simplehebrew]
-  [simplearabic]
+  [semetic-simple]
   [script=hebr]
 
 % \definefont [DevaOne] [file:chandas.ttf*devanagari-one at 12pt]
@@ -195,6 +205,13 @@
   [missing]
   [missing=yes]
 
+%D Nice to have too:
+
+\definefontfeature
+  [quality]
+  [expansion=quality,
+   protrusion=quality]
+
 %D We define some colors that are used in tracing (for instance \OPENTYPE\
 %D features). We cannot yet inherit because no colors are predefined.
 
diff --git a/tex/context/base/font-syn.lua b/tex/context/base/font-syn.lua
index eb420e5be..7e62771a3 100644
--- a/tex/context/base/font-syn.lua
+++ b/tex/context/base/font-syn.lua
@@ -1722,3 +1722,49 @@ function names.resolvespec(askedname,sub) -- overloads previous definition
         report_names("unresolved: %s",askedname)
     end
 end
+
+-- We could generate typescripts with designsize info from the name database but
+-- it's not worth the trouble as font names remain a mess: for instance how do we
+-- idenfity a font? Names, families, subfamilies or whatever snippet can contain
+-- a number related to the design size and so we end up with fuzzy logic again. So,
+-- instead it's easier to make a few goody files.
+--
+-- local hash = { }
+--
+-- for i=1,#specifications do
+--     local s = specifications[i]
+--     local min = s.minsize
+--     local max = s.maxsize
+--     if min ~= 0 or max ~= 0 then
+--         -- the usual name mess:
+--         --   antykwa has modifiers so we need to take these into account, otherwise we get weird combinations
+--         --   ebgaramond has modifiers with the size encoded, so we need to strip this in order to recognized similar styles
+--         --   lm has 'slanted appended in some names so how to choose that one
+--         --
+--         local modifier = string.gsub(s.modifiers or "normal","%d","")
+--         -- print funny modifier
+--         local instance = string.formatters["%s-%s-%s-%s-%s-%s"](s.familyname,s.width,s.style,s.weight,s.variant,modifier)
+--         local h = hash[instance]
+--         if not h then
+--             h = { }
+--             hash[instance] = h
+--         end
+--         size = string.formatters["%0.1fpt"]((min)/10)
+--         h[size] = s.filename
+--     end
+-- end
+--
+-- local newhash = { }
+--
+-- for k, v in next, hash do
+--     if next(v,next(v)) then
+--      -- local instance = string.match(k,"(.+)%-.+%-.+%-.+$")
+--         local instance = string.match(k,"(.+)%-.+%-.+$")
+--         local instance = string.gsub(instance,"%-normal$","")
+--         if not newhash[instance] then
+--             newhash[instance] = v
+--         end
+--     end
+-- end
+--
+-- inspect(newhash)
diff --git a/tex/context/base/grph-inc.lua b/tex/context/base/grph-inc.lua
index be1b7f45a..4f9d7fd92 100644
--- a/tex/context/base/grph-inc.lua
+++ b/tex/context/base/grph-inc.lua
@@ -38,7 +38,6 @@ run TeX code from within Lua. Some more functionality will move to Lua.
 ]]--
 
 local format, lower, find, match, gsub, gmatch = string.format, string.lower, string.find, string.match, string.gsub, string.gmatch
-local texbox = tex.box
 local contains = table.contains
 local concat, insert, remove = table.concat, table.insert, table.remove
 local todimen = string.todimen
@@ -57,6 +56,11 @@ local replacetemplate   = utilities.templates.replace
 
 local images            = img
 
+local texgetbox         = tex.getbox
+local texsetbox         = tex.setbox
+
+local hpack             = node.hpack
+
 local context           = context
 
 local variables         = interfaces.variables
@@ -975,7 +979,7 @@ function figures.done(data)
     figures.nofprocessed = figures.nofprocessed + 1
     data = data or callstack[#callstack] or lastfiguredata
     local dr, du, ds, nr = data.request, data.used, data.status, figures.boxnumber
-    local box = texbox[nr]
+    local box = texgetbox(nr)
     ds.width  = box.width
     ds.height = box.height
     ds.xscale = ds.width /(du.width  or 1)
@@ -987,7 +991,7 @@ end
 function figures.dummy(data)
     data = data or callstack[#callstack] or lastfiguredata
     local dr, du, nr = data.request, data.used, figures.boxnumber
-    local box  = node.hpack(node.new("hlist")) -- we need to set the dir (luatex 0.60 buglet)
+    local box  = hpack(node.new("hlist")) -- we need to set the dir (luatex 0.60 buglet)
     du.width   = du.width  or figures.defaultwidth
     du.height  = du.height or figures.defaultheight
     du.depth   = du.depth  or figures.defaultdepth
@@ -995,7 +999,7 @@ function figures.dummy(data)
     box.width  = du.width
     box.height = du.height
     box.depth  = du.depth
-    texbox[nr] = box -- hm, should be global (to be checked for consistency)
+    texsetbox(nr,box) -- hm, should be global (to be checked for consistency)
 end
 
 -- -- -- generic -- -- --
@@ -1096,10 +1100,10 @@ function includers.generic(data)
     if figure then
         local nr = figures.boxnumber
         -- it looks like we have a leak in attributes here .. todo
-        local box = node.hpack(images.node(figure)) -- images.node(figure) not longer valid
+        local box = hpack(images.node(figure)) -- images.node(figure) not longer valid
         indexed[figure.index] = figure
         box.width, box.height, box.depth = figure.width, figure.height, 0 -- new, hm, tricky, we need to do that in tex (yet)
-        texbox[nr] = box
+        texsetbox(nr,box)
         ds.objectnumber = figure.objnum
         context.relocateexternalfigure()
     end
diff --git a/tex/context/base/grph-inc.mkiv b/tex/context/base/grph-inc.mkiv
index 8557bbb0b..4e2f8da93 100644
--- a/tex/context/base/grph-inc.mkiv
+++ b/tex/context/base/grph-inc.mkiv
@@ -297,11 +297,12 @@
    %
    \edef\p_width {\externalfigureparameter\c!width}%
    \edef\p_height{\externalfigureparameter\c!height}%
+   \edef\p_label {\externalfigureparameter\c!label}%
    %
    \dostarttagged\t!image\empty
    \ctxlua{figures.push {
         name       = "\p_grph_include_name",
-        label      = "\p_grph_include_label",
+        label      = "\ifx\p_label\empty\p_grph_include_label\else\p_label\fi",
         page       = "\externalfigureparameter\c!page",
         size       = "\externalfigureparameter\c!size",
         object     = "\externalfigureparameter\c!object",
diff --git a/tex/context/base/l-file.lua b/tex/context/base/l-file.lua
index a64ee8656..ebb2b39f4 100644
--- a/tex/context/base/l-file.lua
+++ b/tex/context/base/l-file.lua
@@ -368,11 +368,14 @@ function file.joinpath(tab,separator) -- table
     return tab and concat(tab,separator or io.pathseparator) -- can have trailing //
 end
 
+local someslash = S("\\/")
 local stripper  = Cs(P(fwslash)^0/"" * reslasher)
-local isnetwork = fwslash * fwslash * (1-fwslash) + (1-fwslash-colon)^1 * colon
+local isnetwork = someslash * someslash * (1-someslash)
+                + (1-fwslash-colon)^1 * colon
 local isroot    = fwslash^1 * -1
 local hasroot   = fwslash^1
 
+local reslasher = lpeg.replacer(S("\\/"),"/")
 local deslasher = lpeg.replacer(S("\\/")^1,"/")
 
 -- If we have a network or prefix then there is a change that we end up with two
@@ -386,8 +389,13 @@ function file.join(...)
     local lst = { ... }
     local one = lst[1]
     if lpegmatch(isnetwork,one) then
+        local one = lpegmatch(reslasher,one)
         local two = lpegmatch(deslasher,concat(lst,"/",2))
-        return one .. "/" .. two
+        if lpegmatch(hasroot,two) then
+            return one .. two
+        else
+            return one .. "/" .. two
+        end
     elseif lpegmatch(isroot,one) then
         local two = lpegmatch(deslasher,concat(lst,"/",2))
         if lpegmatch(hasroot,two) then
@@ -412,6 +420,8 @@ end
 -- print(file.join("http://a","/y"))
 -- print(file.join("http:///a","/y"))
 -- print(file.join("//nas-1","/y"))
+-- print(file.join("//nas-1/a/b/c","/y"))
+-- print(file.join("\\\\nas-1\\a\\b\\c","\\y"))
 
 -- The previous one fails on "a.b/c"  so Taco came up with a split based
 -- variant. After some skyping we got it sort of compatible with the old
@@ -421,9 +431,14 @@ end
 -- finds were replaced by lpegs.
 
 local drivespec    = R("az","AZ")^1 * colon
-local anchors      = fwslash + drivespec
-local untouched    = periods + (1-period)^1 * P(-1)
-local splitstarter = (Cs(drivespec * (bwslash/"/" + fwslash)^0) + Cc(false)) * Ct(lpeg.splitat(S("/\\")^1))
+local anchors      = fwslash
+                   + drivespec
+local untouched    = periods
+                   + (1-period)^1 * P(-1)
+local mswindrive   = Cs(drivespec * (bwslash/"/" + fwslash)^0)
+local mswinuncpath = (bwslash + fwslash) * (bwslash + fwslash) * Cc("//")
+local splitstarter = (mswindrive + mswinuncpath + Cc(false))
+                   * Ct(lpeg.splitat(S("/\\")^1))
 local absolute     = fwslash
 
 function file.collapsepath(str,anchor) -- anchor: false|nil, true, "."
@@ -490,6 +505,7 @@ end
 -- test("a/./b/..") test("a/aa/../b/bb") test("a/.././././b/..") test("a/./././b/..")
 -- test("a/b/c/../..") test("./a/b/c/../..") test("a/b/c/../..")
 -- test("./a")
+-- test([[\\a.b.c\d\e]])
 
 local validchars = R("az","09","AZ","--","..")
 local pattern_a  = lpeg.replacer(1-validchars)
diff --git a/tex/context/base/l-io.lua b/tex/context/base/l-io.lua
index 06e1fb5ef..e3a443be8 100644
--- a/tex/context/base/l-io.lua
+++ b/tex/context/base/l-io.lua
@@ -35,6 +35,7 @@ local function readall(f)
         return f:read('*all')
     else
         local done = f:seek("set",0)
+        local step
         if size < 1024*1024 then
             step = 1024 * 1024
         elseif size > 16*1024*1024 then
diff --git a/tex/context/base/l-lpeg.lua b/tex/context/base/l-lpeg.lua
index 7be86d38f..b33df96b7 100644
--- a/tex/context/base/l-lpeg.lua
+++ b/tex/context/base/l-lpeg.lua
@@ -13,6 +13,19 @@ if not modules then modules = { } end modules ['l-lpeg'] = {
 
 lpeg = require("lpeg")
 
+-- The latest lpeg doesn't have print any more, and even the new ones are not
+-- available by default (only when debug mode is enabled), which is a pitty as
+-- as it helps bailign down bottlenecks. Performance seems comparable, although
+--
+-- local p = lpeg.C(lpeg.P(1)^0 * lpeg.P(-1))
+-- local a = string.rep("123",10)
+-- lpeg.match(p,a)
+--
+-- is nearly 20% slower and also still suboptimal (i.e. a match that runs from
+-- begin to end, one of the cases where string matchers win).
+
+if not lpeg.print then function lpeg.print(...) print(lpeg.pcode(...)) end end
+
 -- tracing (only used when we encounter a problem in integration of lpeg in luatex)
 
 -- some code will move to unicode and string
@@ -212,7 +225,7 @@ patterns.propername    = (uppercase + lowercase + underscore) * (uppercase + low
 patterns.somecontent   = (anything - newline - space)^1 -- (utf8char - newline - space)^1
 patterns.beginline     = #(1-newline)
 
-patterns.longtostring  = Cs(whitespace^0/"" * nonwhitespace^0 * ((whitespace^0/" " * (patterns.quoted + nonwhitespace)^1)^0))
+patterns.longtostring  = Cs(whitespace^0/"" * ((patterns.quoted + nonwhitespace^1 + whitespace^1/"" * (P(-1) + Cc(" ")))^0))
 
 local function anywhere(pattern) --slightly adapted from website
     return P { P(pattern) + 1 * V(1) }
diff --git a/tex/context/base/l-pdfview.lua b/tex/context/base/l-pdfview.lua
index fc3875ef4..6302fd6f6 100644
--- a/tex/context/base/l-pdfview.lua
+++ b/tex/context/base/l-pdfview.lua
@@ -6,6 +6,8 @@ if not modules then modules = { } end modules ['l-pdfview'] = {
     license   = "see context related readme files"
 }
 
+-- Todo: add options in cnf file
+
 -- Todo: figure out pdfopen/pdfclose on linux. Calling e.g. okular directly
 -- doesn't work in linux when issued from scite as it blocks the editor (no
 -- & possible or so). Unfortunately pdfopen keeps changing with not keeping
@@ -15,66 +17,107 @@ if not modules then modules = { } end modules ['l-pdfview'] = {
 
 local format, concat = string.format, table.concat
 
+local report  = logs.reporter("pdfview")
+local replace = utilities.templates.replace
+
 pdfview = pdfview or { }
 
-local opencalls, closecalls, allcalls, runner
+local opencalls  -- a table with templates that open a given pdf document
+local closecalls -- a table with templates that close a given pdf document
+local allcalls   -- a table with templates that close all open pdf documents
+local runner     -- runner function
+local expander   -- filename cleanup function
 
--- this might become template based
+-- maybe spawn/execute spec in calls
 
 if os.type == "windows" then
 
+    -- os.setenv("path",os.getenv("path") .. ";" .. "c:/data/system/pdf-xchange")
+    -- os.setenv("path",os.getenv("path") .. ";" .. "c:/data/system/sumatrapdf")
+
+    -- start is more flexible as it locates binaries in more places and doesn't lock
+
     opencalls = {
-        ['default']     = "pdfopen --rxi --file",
-        ['acrobat']     = "pdfopen --rxi --file",
-        ['fullacrobat'] = "pdfopen --axi --file",
-        ['okular']      = 'start "test" "c:/data/system/kde/bin/okular.exe" --unique', -- todo!
-        ['sumatra']     = 'start "test" "c:/data/system/sumatrapdf/sumatrapdf.exe" -reuse-instance',
-        ['okular']      = 'start "test" "okular.exe" --unique',
-        ['sumatra']     = 'start "test" "sumatrapdf.exe" -reuse-instance -bg-color 0xCCCCCC',
+        ['default']     = [[pdfopen --rxi --file "%filename%"]],
+        ['acrobat']     = [[pdfopen --rxi --file "%filename%"]],
+        ['fullacrobat'] = [[pdfopen --axi --file "%filename%"]],
+        ['okular']      = [[start "test" okular.exe --unique "%filename%"]],
+        ['pdfxcview']   = [[start "test" pdfxcview.exe /A "nolock=yes=OpenParameters" "%filename%"]],
+        ['sumatra']     = [[start "test" sumatrapdf.exe -reuse-instance -bg-color 0xCCCCCC "%filename%"]],
+        ['auto']        = [[start "%filename%"]],
     }
     closecalls= {
-        ['default'] = "pdfclose --file",
-        ['acrobat'] = "pdfclose --file",
-        ['okular']  = false,
-        ['sumatra'] = false,
+        ['default']     = [[pdfclose --file "%filename%"]],
+        ['acrobat']     = [[pdfclose --file "%filename%"]],
+        ['okular']      = false,
+        ['pdfxcview']   = false, -- [[pdfxcview.exe /close:discard "%filename%"]],
+        ['sumatra']     = false,
+        ['auto']        = false,
     }
     allcalls = {
-        ['default'] = "pdfclose --all",
-        ['acrobat'] = "pdfclose --all",
-        ['okular']  = false,
-        ['sumatra'] = false,
+        ['default']     = [[pdfclose --all]],
+        ['acrobat']     = [[pdfclose --all]],
+        ['okular']      = false,
+        ['pdfxcview']   = false,
+        ['sumatra']     = false,
+        ['auto']        = false,
     }
 
-    pdfview.method = "acrobat" -- no longer useful due to green pop up line and clasing reader/full
+    pdfview.method = "acrobat" -- no longer useful due to green pop up line and clashing reader/full
+ -- pdfview.method = "pdfxcview"
     pdfview.method = "sumatra"
 
-    runner = function(cmd)
-        os.execute(cmd) -- .. " > /null"
+    runner = function(template,variables)
+        local cmd = replace(template,variables)
+     -- cmd = cmd  .. " > /null"
+        report("command: %s",cmd)
+        os.execute(cmd)
+    end
+
+    expander = function(name)
+        -- We need to avoid issues with chdir to UNC paths and therefore expand
+        -- the path when we're current. (We could use one of the helpers instead)
+        if file.pathpart(name) == "" then
+            return file.collapsepath(file.join(lfs.currentdir(),name))
+        else
+            return name
+        end
     end
 
 else
 
     opencalls = {
-        ['default'] = "pdfopen", -- we could pass the default here
-        ['okular']  = 'okular --unique',
-        ['sumatra'] = 'wine "sumatrapdf.exe" -reuse-instance -bg-color 0xCCCCCC',
+        ['default']   = [[pdfopen "%filename%"]],
+        ['okular']    = [[okular --unique "%filename%"]],
+        ['sumatra']   = [[wine "sumatrapdf.exe" -reuse-instance -bg-color 0xCCCCCC "%filename%"]],
+        ['pdfxcview'] = [[wine "pdfxcview.exe" /A "nolock=yes=OpenParameters" "%filename%"]],
+        ['auto']      = [[open "%filename%"]],
     }
     closecalls= {
-        ['default'] = "pdfclose --file",
-        ['okular']  = false,
-        ['sumatra'] = false,
+        ['default']   = [[pdfclose --file "%filename%"]],
+        ['okular']    = false,
+        ['sumatra']   = false,
+        ['auto']      = false,
     }
     allcalls = {
-        ['default'] = "pdfclose --all",
-        ['okular']  = false,
-        ['sumatra'] = false,
+        ['default']   = [[pdfclose --all]],
+        ['okular']    = false,
+        ['sumatra']   = false,
+        ['auto']      = false,
     }
 
     pdfview.method = "okular"
-    pdfview.method = "sumatra"
+    pdfview.method = "sumatra" -- faster and more complete
+
+    runner = function(template,variables)
+        local cmd = replace(template,variables)
+        cmd = cmd .. " 1>/dev/null 2>/dev/null &"
+        report("command: %s",cmd)
+        os.execute(cmd)
+    end
 
-    runner = function(cmd)
-        os.execute(cmd .. " 1>/dev/null 2>/dev/null &")
+    expander = function(name)
+        return name
     end
 
 end
@@ -97,8 +140,6 @@ function pdfview.status()
     return format("pdfview methods: %s, current method: %s (directives_pdfview_method)",pdfview.methods(),tostring(pdfview.method))
 end
 
--- local openedfiles = { }
-
 local function fullname(name)
     return file.addsuffix(name,"pdf")
 end
@@ -108,10 +149,9 @@ function pdfview.open(...)
     if opencall then
         local t = { ... }
         for i=1,#t do
-            local name = fullname(t[i])
+            local name = expander(fullname(t[i]))
             if io.exists(name) then
-                runner(format('%s "%s"', opencall, name))
-             -- openedfiles[name] = true
+                runner(opencall,{ filename = name })
             end
         end
     end
@@ -122,14 +162,10 @@ function pdfview.close(...)
     if closecall then
         local t = { ... }
         for i=1,#t do
-            local name = fullname(t[i])
-         -- if openedfiles[name] then
-                runner(format('%s "%s"', closecall, name))
-         --     openedfiles[name] = nil
-         -- else
-         --     pdfview.closeall()
-         --     break
-         -- end
+            local name = expander(fullname(t[i]))
+            if io.exists(name) then
+                replace(closecall,{ filename = name })
+            end
         end
     end
 end
@@ -137,13 +173,8 @@ end
 function pdfview.closeall()
     local allcall = allcalls[pdfview.method]
     if allcall then
-        runner(format('%s', allcall))
+        runner(allcall)
     end
- -- openedfiles = { }
 end
 
---~ pdfview.open("t:/document/show-exa.pdf")
---~ os.sleep(3)
---~ pdfview.close("t:/document/show-exa.pdf")
-
 return pdfview
diff --git a/tex/context/base/l-table.lua b/tex/context/base/l-table.lua
index 4f6b9b4ed..11cb66bef 100644
--- a/tex/context/base/l-table.lua
+++ b/tex/context/base/l-table.lua
@@ -346,6 +346,7 @@ local noquotes, hexify, handle, reduce, compact, inline, functions
 local reserved = table.tohash { -- intercept a language inconvenience: no reserved words as key
     'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 'function', 'if',
     'in', 'local', 'nil', 'not', 'or', 'repeat', 'return', 'then', 'true', 'until', 'while',
+    'NaN', 'goto',
 }
 
 local function simple_table(t)
diff --git a/tex/context/base/lang-rep.lua b/tex/context/base/lang-rep.lua
new file mode 100644
index 000000000..16cd93e4a
--- /dev/null
+++ b/tex/context/base/lang-rep.lua
@@ -0,0 +1,198 @@
+if not modules then modules = { } end modules ['lang-rep'] = {
+    version   = 1.001,
+    comment   = "companion to lang-rep.mkiv",
+    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+    copyright = "PRAGMA ADE / ConTeXt Development Team",
+    license   = "see context related readme files"
+}
+
+-- A BachoTeX 2013 experiment, probably not that useful. Eventually I used a simpler
+-- more generic example.
+
+local utfbyte, utfsplit = utf.byte, utf.split
+
+local trace_replacements = false  trackers.register("languages.replacements",        function(v) trace_replacements = v end)
+local trace_detail       = false  trackers.register("languages.replacements.detail", function(v) trace_detail       = v end)
+
+local report_replacement = logs.reporter("languages","replacements")
+
+local glyph_code         = nodes.nodecodes.glyph
+
+local insert_node_before = nodes.insert_before
+local remove_node        = nodes.remove
+local copy_node          = nodes.copy
+
+local texsetattribute    = tex.setattribute
+local unsetvalue         = attributes.unsetvalue
+
+local v_reset            = interfaces.variables.reset
+
+local replacements       = languages.replacements or { }
+languages.replacements   = replacements
+
+local a_replacements     = attributes.private("replacements")
+
+local lists = { }
+local last  = 0
+local trees = { }
+
+table.setmetatableindex(lists,function(lists,name)
+    last = last + 1
+    local list = { }
+    local data = { name = name, list = list, attribute = last }
+    lists[last] = data
+    lists[name] = data
+    trees[last] = list
+    return data
+end)
+
+local function add(root,word,replacement)
+    local list = utfsplit(word,true)
+    for i=1,#list do
+        local l = utfbyte(list[i])
+        if not root[l] then
+            root[l] = { }
+        end
+        if i == #list then
+            local newlist = utfsplit(replacement,true)
+            for i=1,#newlist do
+                newlist[i] = utfbyte(newlist[i])
+            end
+            root[l].final = {
+                word        = word,
+                replacement = replacement,
+                oldlength   = #list,
+                newcodes    = newlist,
+            }
+        end
+        root = root[l]
+    end
+end
+
+function replacements.add(category,word,replacement)
+    local root = lists[category].list
+    if type(word) == "table" then
+        for word, replacement in next, word do
+            add(root,word,replacement)
+        end
+    else
+        add(root,word,replacement or "")
+    end
+end
+
+local function hit(a,head)
+    local tree = trees[a]
+    if tree then
+        local root = tree[head.char]
+        if root then
+            local current   = head.next
+            local lastrun   = false
+            local lastfinal = false
+            while current and current.id == glyph_code do
+                local newroot = root[current.char]
+                if not newroot then
+                    return lastrun, lastfinal
+                else
+                    local final = newroot.final
+                    if final then
+                        if trace_detail then
+                            report_replacement("hitting word %a, replacement %a",final.word,final.replacement)
+                        end
+                        lastrun   = current
+                        lastfinal = final
+                    else
+                        root = newroot
+                    end
+                end
+                current = current.next
+            end
+            if lastrun then
+                return lastrun, lastfinal
+            end
+        end
+    end
+end
+
+local function process(namespace,attribute,head)
+    -- we could avoid this wrapper and use:
+ -- local function process(head)
+    local current = head
+    local done = false
+    while current do
+        if current.id == glyph_code then
+         -- local a = current[a_replacements]
+            local a = current[attribute]
+            if a then
+                local last, final = hit(a,current)
+                if last then
+                    local oldlength = final.oldlength
+                    local newcodes  = final.newcodes
+                    local newlength = #newcodes
+                    if report_replacement then
+                        report_replacement("replacing word %a by %a",final.word,final.replacement)
+                    end
+                    if oldlength == newlength then -- #old == #new
+                        for i=1,newlength do
+                            current.char = newcodes[i]
+                            current = current.next
+                        end
+                    elseif oldlength < newlength then -- #old < #new
+                        for i=1,newlength-oldlength do
+                            local n = copy_node(current)
+                            n.char = newcodes[i]
+                            head, current = insert_node_before(head,current,n)
+                            current = current.next
+                        end
+                        for i=newlength-oldlength+1,newlength do
+                            current.char = newcodes[i]
+                            current = current.next
+                        end
+                    else -- #old > #new
+                        for i=1,oldlength-newlength do
+                            head, current = remove_node(head,current,true)
+                        end
+                        for i=1,newlength do
+                            current.char = newcodes[i]
+                            current = current.next
+                        end
+                    end
+                    done = true
+                end
+            end
+        end
+        current = current.next
+    end
+    return head, done
+end
+
+local enabled = false
+
+function replacements.set(n) -- number or 'reset'
+    if n == v_reset then
+        n = unsetvalue
+    else
+        n = lists[n].attribute
+        if not enabled then
+            nodes.tasks.enableaction("processors","languages.replacements.handler")
+            if trace_replacements then
+                report_replacement("enabling replacement handler")
+            end
+            enabled = true
+        end
+    end
+    texsetattribute(a_replacements,n)
+end
+
+replacements.handler = nodes.installattributehandler {
+    name      = "replacements",
+    namespace = replacements,
+    processor = process,
+}
+
+-- interface
+
+commands.setreplacements = replacements.set
+commands.addreplacements = replacements.add
+
+nodes.tasks.prependaction("processors","words","languages.replacements.handler")
+nodes.tasks.disableaction("processors","languages.replacements.handler")
diff --git a/tex/context/base/lang-wrd.lua b/tex/context/base/lang-wrd.lua
index 06a2311a6..bf066fc09 100644
--- a/tex/context/base/lang-wrd.lua
+++ b/tex/context/base/lang-wrd.lua
@@ -334,17 +334,17 @@ end
 
 -- for the moment we hook it into the attribute handler
 
---~ languagehacks = { }
+-- languagehacks = { }
 
---~ function languagehacks.process(namespace,attribute,head)
---~     return languages.check(head)
---~ end
+-- function languagehacks.process(namespace,attribute,head)
+--     return languages.check(head)
+-- end
 
---~ chars.plugins[chars.plugins+1] = {
---~     name = "language",
---~     namespace = languagehacks,
---~     processor = languagehacks.process
---~ }
+-- chars.plugins[chars.plugins+1] = {
+--     name = "language",
+--     namespace = languagehacks,
+--     processor = languagehacks.process
+-- }
 
 -- interface
 
diff --git a/tex/context/base/layo-ini.lua b/tex/context/base/layo-ini.lua
index 56ced2c0b..d35d7ef69 100644
--- a/tex/context/base/layo-ini.lua
+++ b/tex/context/base/layo-ini.lua
@@ -6,17 +6,10 @@ if not modules then modules = { } end modules ['layo-ini'] = {
     license   = "see context related readme files"
 }
 
--- We need to share information between the TeX and Lua end
--- about the typographical model. This happens here.
---
--- Code might move.
+-- We need to share information between the TeX and Lua end about the typographical
+-- model. This happens here. This code might move.
 
--- conditionals.layoutisdoublesided
--- conditionals.layoutissinglesided
--- texcount.pagenoshift
--- texcount.realpageno
-
-local texcount     = tex.count
+local texgetcount  = tex.getcount
 local conditionals = tex.conditionals
 
 layouts = {
@@ -33,14 +26,14 @@ function status.leftorrightpagection(left,right)
         return left, right
     elseif conditionals.layoutissinglesided then
         return left, right
-    elseif texcount.pagenoshift % 2 == 0 then
-        if texcount.realpageno % 2 == 0 then
+    elseif texgetcount("pagenoshift") % 2 == 0 then
+        if texgetcount("realpageno") % 2 == 0 then
             return right, left
         else
             return left, right
         end
     else
-        if texcount.realpageno % 2 == 0 then
+        if texgetcount("realpageno") % 2 == 0 then
             return left, right
         else
             return right, left
@@ -53,9 +46,9 @@ function status.isleftpage()
         return false
     elseif conditionals.layoutissinglesided then
         return false
-    elseif texcount.pagenoshift % 2 == 0 then
-        return texcount.realpageno % 2 == 0
+    elseif texgetcount("pagenoshift") % 2 == 0 then
+        return texgetcount("realpageno") % 2 == 0
     else
-        return not texcount.realpageno % 2 == 0
+        return not texgetcount("realpageno") % 2 == 0
     end
 end
diff --git a/tex/context/base/lpdf-ano.lua b/tex/context/base/lpdf-ano.lua
index 990e87abd..3f0e718b3 100644
--- a/tex/context/base/lpdf-ano.lua
+++ b/tex/context/base/lpdf-ano.lua
@@ -12,7 +12,6 @@ if not modules then modules = { } end modules ['lpdf-ano'] = {
 
 local next, tostring = next, tostring
 local rep, format = string.rep, string.format
-local texcount = tex.count
 local lpegmatch = lpeg.match
 local formatters = string.formatters
 
@@ -52,6 +51,8 @@ local pdfannotation_node      = nodepool.pdfannotation
 local pdfdestination_node     = nodepool.pdfdestination
 local latelua_node            = nodepool.latelua
 
+local texgetcount             = tex.getcount
+
 local pdfdictionary           = lpdf.dictionary
 local pdfarray                = lpdf.array
 local pdfreference            = lpdf.reference
@@ -512,7 +513,7 @@ end
 function specials.deltapage(var,actions)
     local p = tonumber(var.operation)
     if p then
-        p = references.checkedrealpage(p + texcount.realpageno)
+        p = references.checkedrealpage(p + texgetcount("realpageno"))
         return link(nil,nil,nil,p,actions)
     end
 end
diff --git a/tex/context/base/lpdf-fmt.lua b/tex/context/base/lpdf-fmt.lua
index 94c005f65..b444f03c3 100644
--- a/tex/context/base/lpdf-fmt.lua
+++ b/tex/context/base/lpdf-fmt.lua
@@ -36,7 +36,7 @@ local pdfstring                = lpdf.string
 local pdfverbose               = lpdf.verbose
 local pdfflushstreamfileobject = lpdf.flushstreamfileobject
 
-local texset                   = tex.set -- we could make tex.setglobal
+local texset                   = tex.set
 
 local addtoinfo                = lpdf.addtoinfo
 local injectxmpinfo            = lpdf.injectxmpinfo
diff --git a/tex/context/base/lpdf-nod.lua b/tex/context/base/lpdf-nod.lua
index 60d3fcd5b..d90ca1612 100644
--- a/tex/context/base/lpdf-nod.lua
+++ b/tex/context/base/lpdf-nod.lua
@@ -6,7 +6,7 @@ if not modules then modules = { } end modules ['lpdf-nod'] = {
     license   = "see context related readme files"
 }
 
-local format         = string.format
+local formatters     = string.formatters
 
 local copy_node      = node.copy
 local new_node       = node.new
@@ -59,7 +59,7 @@ end
 
 function nodepool.pdfsetmatrix(rx,sx,sy,ry,tx,ty)
     local t = copy_node(pdfsetmatrix)
-    t.data = format("%s %s %s %s",rx or 0,sx or 0,sy or 0,ry or 0) -- todo: tx ty
+    t.data = formatters["%s %s %s %s"](rx or 0,sx or 0,sy or 0,ry or 0) -- todo: tx ty
     return t
 end
 
@@ -127,8 +127,12 @@ function nodepool.pdfdestination(w,h,d,name,view,n)
         local m = copy_node(pdfsetmatrix)
         local r = copy_node(pdfrestore)
         m.data = "1 0 0 1"
-        s.next = m  m.next = t  t.next = r
-        m.prev = s  t.prev = m  r.prev = t
+        s.next = m  
+        m.next = t  
+        t.next = r
+        m.prev = s  
+        t.prev = m  
+        r.prev = t
         return s -- a list
     else
         return t
diff --git a/tex/context/base/lpdf-tag.lua b/tex/context/base/lpdf-tag.lua
index 8cdb5f6a4..29ffcd207 100644
--- a/tex/context/base/lpdf-tag.lua
+++ b/tex/context/base/lpdf-tag.lua
@@ -32,6 +32,8 @@ local pdfflushobject   = lpdf.flushobject
 local pdfreserveobject = lpdf.reserveobject
 local pdfpagereference = lpdf.pagereference
 
+local texgetcount      = tex.getcount
+
 local nodepool         = nodes.pool
 
 local pdfliteral       = nodepool.pdfliteral
@@ -69,10 +71,9 @@ local dashsplitter    = lpeg.splitat("-")
 
 local add_ids         = false -- true
 
-
---~ function codeinjections.maptag(original,target,kind)
---~     mapping[original] = { target, kind or "inline" }
---~ end
+-- function codeinjections.maptag(original,target,kind)
+--     mapping[original] = { target, kind or "inline" }
+-- end
 
 local function finishstructure()
     if #structure_kids > 0 then
@@ -133,7 +134,7 @@ local pdf_struct_element = pdfconstant("StructElem")
 
 local function initializepage()
     index = 0
-    pagenum = tex.count.realpageno
+    pagenum = texgetcount("realpageno")
     pageref = pdfreference(pdfpagereference(pagenum))
     list = pdfarray()
     tree[pagenum] = list -- we can flush after done, todo
diff --git a/tex/context/base/lpdf-wid.lua b/tex/context/base/lpdf-wid.lua
index 5bb809327..30bd3572b 100644
--- a/tex/context/base/lpdf-wid.lua
+++ b/tex/context/base/lpdf-wid.lua
@@ -8,7 +8,6 @@ if not modules then modules = { } end modules ['lpdf-wid'] = {
 
 local gmatch, gsub, find, lower, format = string.gmatch, string.gsub, string.find, string.lower, string.format
 local stripstring = string.strip
-local texbox, texcount = tex.box, tex.count
 local settings_to_array = utilities.parsers.settings_to_array
 local settings_to_hash = utilities.parsers.settings_to_hash
 
@@ -20,6 +19,8 @@ local lpdf                     = lpdf
 local nodes                    = nodes
 local context                  = context
 
+local texgetcount              = tex.getcount
+
 local nodeinjections           = backends.pdf.nodeinjections
 local codeinjections           = backends.pdf.codeinjections
 local registrations            = backends.pdf.registrations
@@ -490,8 +491,8 @@ end
 
 local function insertrenderingwindow(specification)
     local label = specification.label
---~     local openpage = specification.openpage
---~     local closepage = specification.closepage
+ -- local openpage = specification.openpage
+ -- local closepage = specification.closepage
     if specification.option == v_auto then
         if openpageaction then
             -- \handlereferenceactions{\v!StartRendering{#2}}
@@ -507,7 +508,7 @@ local function insertrenderingwindow(specification)
             PC = (closepage and lpdf.action(closepage)) or nil,
         }
     end
-    local page = tonumber(specification.page) or texcount.realpageno -- todo
+    local page = tonumber(specification.page) or texgetcount("realpageno") -- todo
     local r = mu[label] or pdfreserveannotation() -- why the reserve here?
     local a = pdfdictionary {
         S  = pdfconstant("Rendition"),
@@ -539,34 +540,34 @@ local function insertrendering(specification)
     if not mf[label] then
         local filename = specification.filename
         local isurl = find(filename,"://")
-    --~ local start = pdfdictionary {
-    --~     Type = pdfconstant("MediaOffset"),
-    --~     S = pdfconstant("T"), -- time
-    --~     T = pdfdictionary { -- time
-    --~         Type = pdfconstant("Timespan"),
-    --~         S    = pdfconstant("S"),
-    --~         V    = 3, -- time in seconds
-    --~     },
-    --~ }
-    --~ local start = pdfdictionary {
-    --~     Type = pdfconstant("MediaOffset"),
-    --~     S = pdfconstant("F"), -- frame
-    --~     F = 100 -- framenumber
-    --~ }
-    --~ local start = pdfdictionary {
-    --~     Type = pdfconstant("MediaOffset"),
-    --~     S = pdfconstant("M"), -- mark
-    --~     M = "somemark",
-    --~ }
-    --~ local parameters = pdfdictionary {
-    --~     BE = pdfdictionary {
-    --~          B = start,
-    --~     }
-    --~ }
-    --~ local parameters = pdfdictionary {
-    --~     Type = pdfconstant(MediaPermissions),
-    --~     TF   = pdfstring("TEMPALWAYS") }, -- TEMPNEVER TEMPEXTRACT TEMPACCESS TEMPALWAYS
-    --~ }
+     -- local start = pdfdictionary {
+     --     Type = pdfconstant("MediaOffset"),
+     --     S = pdfconstant("T"), -- time
+     --     T = pdfdictionary { -- time
+     --         Type = pdfconstant("Timespan"),
+     --         S    = pdfconstant("S"),
+     --         V    = 3, -- time in seconds
+     --     },
+     -- }
+     -- local start = pdfdictionary {
+     --     Type = pdfconstant("MediaOffset"),
+     --     S = pdfconstant("F"), -- frame
+     --     F = 100 -- framenumber
+     -- }
+     -- local start = pdfdictionary {
+     --     Type = pdfconstant("MediaOffset"),
+     --     S = pdfconstant("M"), -- mark
+     --     M = "somemark",
+     -- }
+     -- local parameters = pdfdictionary {
+     --     BE = pdfdictionary {
+     --          B = start,
+     --     }
+     -- }
+     -- local parameters = pdfdictionary {
+     --     Type = pdfconstant(MediaPermissions),
+     --     TF   = pdfstring("TEMPALWAYS") }, -- TEMPNEVER TEMPEXTRACT TEMPACCESS TEMPALWAYS
+     -- }
         local descriptor = pdfdictionary {
             Type = pdfconstant("Filespec"),
             F    = filename,
diff --git a/tex/context/base/luat-env.lua b/tex/context/base/luat-env.lua
index 8753972c6..5558e0303 100644
--- a/tex/context/base/luat-env.lua
+++ b/tex/context/base/luat-env.lua
@@ -20,6 +20,8 @@ local report_lua = logs.reporter("resolvers","lua")
 local luautilities = utilities.lua
 local luasuffixes  = luautilities.suffixes
 
+local texgettoks   = tex and tex.gettoks
+
 environment        = environment or { }
 local environment  = environment
 
@@ -28,7 +30,7 @@ local environment  = environment
 local mt = {
     __index = function(_,k)
         if k == "version" then
-            local version = tex.toks and tex.toks.contextversiontoks
+            local version = texgettoks and texgettoks("contextversiontoks")
             if version and version ~= "" then
                 rawset(environment,"version",version)
                 return version
@@ -36,7 +38,7 @@ local mt = {
                 return "unknown"
             end
         elseif k == "kind" then
-            local kind = tex.toks and tex.toks.contextkindtoks
+            local kind = texgettoks and texgettoks("contextkindtoks")
             if kind and kind ~= "" then
                 rawset(environment,"kind",kind)
                 return kind
diff --git a/tex/context/base/luat-fio.lua b/tex/context/base/luat-fio.lua
index d61c6f142..dcc183167 100644
--- a/tex/context/base/luat-fio.lua
+++ b/tex/context/base/luat-fio.lua
@@ -84,26 +84,6 @@ if not resolvers.instance then
 
 end
 
-local report_system = logs.reporter("system","files")
-local report_files  = logs.reporter("used files")
-
-luatex.registerstopactions(function()
-    local foundintrees = resolvers.instance.foundintrees
-    if #foundintrees > 0 then
-        logs.pushtarget("logfile")
-        logs.newline()
-        report_system("start used files")
-        logs.newline()
-        for i=1,#foundintrees do
-            report_files("%4i: % T",i,foundintrees[i])
-        end
-        logs.newline()
-        report_system("stop used files")
-        logs.newline()
-        logs.poptarget()
-    end
-end)
-
 statistics.register("resource resolver", function()
     local scandata = resolvers.scandata()
     return format("loadtime %s seconds, %s scans with scantime %s seconds, %s shared scans, %s found files, scanned paths: %s",
diff --git a/tex/context/base/lxml-css.lua b/tex/context/base/lxml-css.lua
index c5a85c2bd..5b389626c 100644
--- a/tex/context/base/lxml-css.lua
+++ b/tex/context/base/lxml-css.lua
@@ -30,8 +30,9 @@ if tex then
 
     local exheights = fonts.hashes.exheights
     local emwidths  = fonts.hashes.emwidths
+    local texget    = tex.get
 
-    percentage = function(s,pcf) return tonumber(s) * (pcf or tex.hsize)          end
+    percentage = function(s,pcf) return tonumber(s) * (pcf or texget("hsize")     end
     exheight   = function(s,exf) return tonumber(s) * (exf or exheights[true])    end
     emwidth    = function(s,emf) return tonumber(s) * (emf or emwidths[true])     end
     pixels     = function(s,pxf) return tonumber(s) * (pxf or emwidths[true]/300) end
@@ -109,17 +110,17 @@ css.padding   = padding
 
 -- print(padding("0",pixel,hsize,exheight,emwidth))
 
--- local currentfont  = font.current
--- local texdimen     = tex.dimen
--- local hashes       = fonts.hashes
--- local quads        = hashes.quads
--- local xheights     = hashes.xheights
+-- local currentfont = font.current
+-- local texget      = tex.get
+-- local hashes      = fonts.hashes
+-- local quads       = hashes.quads
+-- local xheights    = hashes.xheights
 --
 -- local function padding(str)
 --     local font     = currentfont()
 --     local exheight = xheights[font]
 --     local emwidth  = quads[font]
---     local hsize    = texdimen.hsize/100
+--     local hsize    = texget("hsize")/100
 --     local pixel    = emwidth/100
 --     return padding(str,pixel,hsize,exheight,emwidth)
 -- end
diff --git a/tex/context/base/lxml-tab.lua b/tex/context/base/lxml-tab.lua
index 9d2266886..3e10eb96d 100644
--- a/tex/context/base/lxml-tab.lua
+++ b/tex/context/base/lxml-tab.lua
@@ -34,7 +34,7 @@ as the current variant was written when <l n='lpeg'/> showed up and it's easier
 build tables in one go.</p>
 --ldx]]--
 
-lpeg.setmaxstack(1000) -- deeply nested xml files
+if lpeg.setmaxstack then lpeg.setmaxstack(1000) end -- deeply nested xml files
 
 xml = xml or { }
 local xml = xml
diff --git a/tex/context/base/m-nodechart.lua b/tex/context/base/m-nodechart.lua
new file mode 100644
index 000000000..612b73767
--- /dev/null
+++ b/tex/context/base/m-nodechart.lua
@@ -0,0 +1,175 @@
+if not modules then modules = { } end modules ['m-nodechart'] = {
+    version   = 1.001,
+    comment   = "companion to m-nodechart.mkiv",
+    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+    copyright = "PRAGMA ADE / ConTeXt Development Team",
+    license   = "see context related readme files"
+}
+
+local format       = string.format
+local points       = number.nopts
+local ptfactor     = number.dimenfactors.pt
+
+local nodecodes    = nodes.nodecodes
+local kerncodes    = nodes.kerncodes
+local penaltycodes = nodes.penaltycodes
+local gluecodes    = nodes.gluecodes
+local whatsitcodes = nodes.whatsitcodes
+
+moduledata.charts       = moduledata.charts       or { }
+moduledata.charts.nodes = moduledata.charts.nodes or { }
+
+local formatters = { }
+
+-- subtype font char lang left right uchyph components xoffset yoffset width height depth
+
+function formatters.glyph(n,comment)
+    return format("\\doFLOWglyphnode{%s}{%s}{%s}{%s}{U+%05X}",comment,n.subtype,n.font,n.char,n.char)
+end
+
+-- pre post replace
+
+function formatters.disc(n,comment)
+    return format("\\doFLOWdiscnode{%s}{%s}",comment,n.subtype)
+end
+
+-- subtype kern
+
+function formatters.kern(n,comment)
+ -- return format("\\doFLOWkernnode{%s}{%s}{%s}",comment,kerncodes[n.subtype],points(n.kern))
+    return format("\\doFLOWkernnode{%s}{%s}{%.4f}",comment,kerncodes[n.subtype],n.kern*ptfactor)
+end
+
+-- subtype penalty
+
+function formatters.penalty(n,comment)
+    return format("\\doFLOWpenaltynode{%s}{%s}{%s}",comment,"penalty",n.penalty)
+end
+
+-- subtype width leader spec (stretch shrink ...
+
+function formatters.glue(n,comment)
+    local s = n.spec
+ -- return format("\\doFLOWgluenode{%s}{%s}{%s}{%s}{%s}",comment,gluecodes[n.subtype],points(s.width),points(s.stretch),points(s.shrink))
+    return format("\\doFLOWgluenode{%s}{%s}{%.4f}{%.4f}{%.4f}",comment,gluecodes[n.subtype],s.width*ptfactor,s.stretch*ptfactor,s.shrink*ptfactor)
+end
+
+-- subtype width leader spec (stretch shrink ...
+
+function formatters.whatsit(n,comment)
+    local subtype = n.subtype
+    local whatsit = whatsitcodes[subtype]
+    if whatsit == "dir" or whatsit == "localpar" then
+        return format("\\doFLOWdirnode{%s}{%s}{%s}",comment,whatsit,n.dir)
+    else
+        return nodecodes[n.id]
+    end
+end
+
+-- I will make a dedicated set of shapes for this.
+
+local shapes = {
+    glyph   = "procedure",
+    disc    = "procedure",
+    kern    = "action",
+    penalty = "action",
+    glue    = "action",
+}
+
+local function flow_nodes_to_chart(specification)
+    local head    = specification.head
+    local box     = specification.box
+    local comment = specification.comment or ""
+    local x       = specification.x or 1
+    local y       = specification.y or 0
+    --
+    if box then
+          box  = tex.getbox(tonumber(box))
+          head = box and box.list
+    end
+    --
+    local current = head
+    --
+    while current do
+        local nodecode  = nodecodes[current.id]
+        local formatter = formatters[nodecode]
+        local shape     = shapes[nodecode]
+        y = y + 1
+        local next = current.next
+        commands.flow_start_cell { shape = { framecolor = "nodechart:" .. nodecode } }
+        commands.flow_set_name(tostring(current))
+        commands.flow_set_location(x,y)
+        if shape then
+            commands.flow_set_shape(shape)
+        end
+        if formatter then
+            commands.flow_set_text("node",formatter(current,comment))
+        else
+            commands.flow_set_text("node",nodecode)
+        end
+        if next then
+            commands.flow_set_connection("bt","",tostring(next))
+        end
+        if nodecode == "glyph" then
+            local components = current.components
+            if components then
+                commands.flow_set_connection("rl","",tostring(components))
+                commands.flow_stop_cell()
+                n = flow_nodes_to_chart { head = components, comment = "component",x = x+2, y = y-1 }
+            else
+                commands.flow_stop_cell()
+            end
+        elseif nodecode == "disc" then
+            local pre = current.pre
+            local pos = current.post
+            local rep = current.replace
+            if pre and not rep and not rep then
+                if pre then
+                    commands.flow_set_connection("rl","",tostring(pre))
+                end
+                commands.flow_stop_cell()
+                if pre then
+                    n = flow_nodes_to_chart { head = pre, comment = "prebreak", x = x+1, y = y-1 }
+                end
+            else
+                if pre then
+                    commands.flow_set_connection("+rl","",tostring(pre))
+                end
+                if rep then
+                    commands.flow_set_connection("rl","",tostring(rep))
+                end
+                if pos then
+                    commands.flow_set_connection("-rl","",tostring(pos))
+                end
+                commands.flow_stop_cell()
+                if pre then
+                    n = flow_nodes_to_chart{ head = pre, comment = "prebreak", x = x+1, y = y-2 }
+                end
+                if rep then
+                    n = flow_nodes_to_chart{ head = rep, comment = "replacement", x = x+1, y = y-1 }
+                end
+                if pos then
+                    n = flow_nodes_to_chart{ head = pos, comment = "postbreak", x = x+1, y = y }
+                end
+            end
+        elseif nodecode == "hlist" then
+            local list = current.list
+            if list then
+                commands.flow_set_connection("rl","",tostring(list))
+                commands.flow_stop_cell()
+                n = flow_nodes_to_chart { head = list, comment = "list", x = x+2, y = y-1 }
+            else
+                commands.flow_stop_cell()
+            end
+        else
+            commands.flow_stop_cell()
+        end
+        current = next
+    end
+end
+
+function moduledata.charts.nodes.chart(specification)
+    commands.flow_start_chart(specification.name)
+    flow_nodes_to_chart(specification)
+    commands.flow_stop_chart()
+end
diff --git a/tex/context/base/m-nodechart.mkvi b/tex/context/base/m-nodechart.mkvi
index 359d598ce..c9d985850 100644
--- a/tex/context/base/m-nodechart.mkvi
+++ b/tex/context/base/m-nodechart.mkvi
@@ -1,154 +1,19 @@
-\usemodule[chart]
+%D \module
+%D   [       file=m-nodechart,
+%D        version=2011.11.11, % nos sure when it started, needed for fonts-mkiv
+%D          title=\CONTEXT\ Modules,
+%D       subtitle=Node Visualization,
+%D         author=Hans Hagen,
+%D           date=\currentdate,
+%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
 
-\startluacode
-
-local format       = string.format
-local points       = number.nopts
-local ptfactor     = number.dimenfactors.pt
-
-local nodecodes    = nodes.nodecodes
-local kerncodes    = nodes.kerncodes
-local penaltycodes = nodes.penaltycodes
-local gluecodes    = nodes.gluecodes
-local whatsitcodes = nodes.whatsitcodes
-
-local formatters = { }
-
-function formatters.glyph(n,comment)
-    -- subtype font char lang left right uchyph components xoffset yoffset width height depth
-    return format("\\doFLOWglyphnode{%s}{%s}{%s}{%s}{U+%05X}",comment,n.subtype,n.font,n.char,n.char)
-end
-
-function formatters.disc(n,comment)
-    -- pre post replace
-    return format("\\doFLOWdiscnode{%s}{%s}",comment,n.subtype)
-end
-
-function formatters.kern(n,comment)
-    -- subtype kern
- -- return format("\\doFLOWkernnode{%s}{%s}{%s}",comment,kerncodes[n.subtype],points(n.kern))
-    return format("\\doFLOWkernnode{%s}{%s}{%.4f}",comment,kerncodes[n.subtype],n.kern*ptfactor)
-end
-
-function formatters.penalty(n,comment)
-    -- subtype penalty
-    return format("\\doFLOWpenaltynode{%s}{%s}{%s}",comment,"penalty",n.penalty)
-end
-
-function formatters.glue(n,comment)
-    -- subtype width leader spec (stretch shrink ...
-    local s = n.spec
- -- return format("\\doFLOWgluenode{%s}{%s}{%s}{%s}{%s}",comment,gluecodes[n.subtype],points(s.width),points(s.stretch),points(s.shrink))
-    return format("\\doFLOWgluenode{%s}{%s}{%.4f}{%.4f}{%.4f}",comment,gluecodes[n.subtype],s.width*ptfactor,s.stretch*ptfactor,s.shrink*ptfactor)
-end
-
-function formatters.whatsit(n,comment)
-    -- subtype width leader spec (stretch shrink ...
-    local subtype = n.subtype
-    local whatsit = whatsitcodes[subtype]
-    if whatsit == "dir" or whatsit == "localpar" then
-        return format("\\doFLOWdirnode{%s}{%s}{%s}",comment,whatsit,n.dir)
-    else
-        return nodecodes[n.id]
-    end
-end
-
-local shapes = { -- I will make a dedicated set of shapes for this.
-    glyph   = "procedure",
-    disc    = "procedure",
-    kern    = "action",
-    penalty = "action",
-    glue    = "action",
-}
-
-local function flow_nodes_to_chart(head,comment,x,y,how)
-    local current = head
-    while current do
-        local nodecode  = nodecodes[current.id]
-        local formatter = formatters[nodecode]
-        local shape     = shapes[nodecode]
-        y = y + 1
-        local next = current.next
-        commands.flow_start_cell { shape = { framecolor = "nodechart:" .. nodecode } }
-        commands.flow_set_name(tostring(current))
-        commands.flow_set_location(x,y)
-        if shape then
-            commands.flow_set_shape(shape)
-        end
-        if formatter then
-            commands.flow_set_text("node",formatter(current,comment))
-        else
-            commands.flow_set_text("node",nodecode)
-        end
-        if next then
-            commands.flow_set_connection("bt","",tostring(next))
-        end
-        if nodecode == "glyph" then
-            local components = current.components
-            if components then
-                commands.flow_set_connection("rl","",tostring(components))
-                commands.flow_stop_cell()
-                n = flow_nodes_to_chart(components,"component",x+2,y-1)
-            else
-                commands.flow_stop_cell()
-            end
-        elseif nodecode == "disc" then
-            local pre = current.pre
-            local pos = current.post
-            local rep = current.replace
-            if pre and not rep and not rep then
-                if pre then
-                    commands.flow_set_connection("rl","",tostring(pre))
-                end
-                commands.flow_stop_cell()
-                if pre then
-                    n = flow_nodes_to_chart(pre,"prebreak",x+1,y-1)
-                end
-            else
-                if pre then
-                    commands.flow_set_connection("+rl","",tostring(pre))
-                end
-                if rep then
-                    commands.flow_set_connection("rl","",tostring(rep))
-                end
-                if pos then
-                    commands.flow_set_connection("-rl","",tostring(pos))
-                end
-                commands.flow_stop_cell()
-                if pre then
-                    n = flow_nodes_to_chart(pre,"prebreak",x+1,y-2)
-                end
-                if rep then
-                    n = flow_nodes_to_chart(rep,"replacement",x+1,y-1)
-                end
-                if pos then
-                    n = flow_nodes_to_chart(pos,"postbreak",x+1,y)
-                end
-            end
-        elseif nodecode == "hlist" then
-            local list = current.list
-            if list then
-                commands.flow_set_connection("rl","",tostring(list))
-                commands.flow_stop_cell()
-                n = flow_nodes_to_chart(list,"list",x+2,y-1)
-            else
-                commands.flow_stop_cell()
-            end
-        else
-            commands.flow_stop_cell()
-        end
-        current = next
-    end
-    return n
-end
-
-function commands.flow_nodes_to_chart(name,head,max)
-    commands.flow_start_chart(name)
-    flow_nodes_to_chart(head,"",1,0)
-    commands.flow_stop_chart()
-end
-
-\stopluacode
+\registerctxluafile{m-nodechart}{1.001}
+
+\usemodule[chart]
 
 \unprotect
 
@@ -200,17 +65,20 @@ end
 
 % this is a temporary interface ... we will have instances and optional settings
 
-\unexpanded\def\boxtoFLOWchart#name#max#box%
-  {\ctxcommand{flow_nodes_to_chart("#name",tex.box[\number#box].list,\number#max)}}
+\unexpanded\def\boxtoFLOWchart[#name]#box%
+  {\ctxlua{moduledata.charts.nodes.chart {
+       name = "#name",
+       box  = \number#box,
+   }}}
 
-\unexpanded\def\nextboxtoFLOWchart#name#max%
-  {\dowithnextbox{\boxtoFLOWchart{#name}{#max}\nextbox}}
+\unexpanded\def\nextboxtoFLOWchart[#name]%
+  {\dowithnextbox{\boxtoFLOWchart[#name]\nextbox}}
 
-\unexpanded\def\hboxtoFLOWchart#name#max%
-  {\nextboxtoFLOWchart{#name}{#max}\hbox}
+\unexpanded\def\hboxtoFLOWchart[#name]%
+  {\nextboxtoFLOWchart[#name]\hbox}
 
-\unexpanded\def\vboxtoFLOWchart#name#max%
-  {\nextboxtoFLOWchart{#name}{#max}\vbox}
+\unexpanded\def\vboxtoFLOWchart[#name]%
+  {\nextboxtoFLOWchart[#name]\vbox}
 
 \protect
 
@@ -224,7 +92,7 @@ end
 
 \startTEXpage[offset=10pt]
 
-    \hboxtoFLOWchart{dummy}{3}{an affil\discretionary{-}{-}{!}iation}
+    \hboxtoFLOWchart[dummy]{an affil\discretionary{-}{-}{!}iation}
 
     \FLOWchart[dummy][width=14em,height=3em,dx=1em,dy=.75em,hcompact=yes]
 
@@ -232,7 +100,7 @@ end
 
 \startTEXpage[offset=10pt]
 
-    \hboxtoFLOWchart{dummy}{3}{an affiliation}
+    \hboxtoFLOWchart[dummy]{an affiliation}
 
     \FLOWchart[dummy][width=14em,height=3em,dx=.5em,dy=.75em,hcompact=yes]
 
@@ -240,7 +108,7 @@ end
 
 \startTEXpage[offset=10pt]
 
-    \hboxtoFLOWchart{dummy}{3}{\nl effe fijn fietsen}
+    \hboxtoFLOWchart[dummy]{\nl effe fijn fietsen}
 
     \FLOWchart[dummy][width=14em,height=3em,dx=.5em,dy=.75em,hcompact=yes]
 
@@ -248,7 +116,7 @@ end
 
 \startTEXpage[offset=10pt]
 
-    \hboxtoFLOWchart{dummy}{3}{\righttoleft t\kern 1pt est}
+    \hboxtoFLOWchart[dummy]{\righttoleft t\kern 1pt est}
 
     \FLOWchart[dummy][width=14em,height=3em,dx=.5em,dy=.75em,hcompact=yes]
 
diff --git a/tex/context/base/m-r.mkii b/tex/context/base/m-r.mkii
new file mode 100644
index 000000000..c2cb7ba88
--- /dev/null
+++ b/tex/context/base/m-r.mkii
@@ -0,0 +1,174 @@
+%D \module
+%D   [       file=m-r,
+%D        version=2006.06.06,
+%D          title=\CONTEXT\ Modules,
+%D       subtitle=R Support,
+%D         author={Johan Sandblom \& Hans Hagen},
+%D           date=\currentdate,
+%D      copyright={Johan Sandblom \& Hans Hagen}]
+%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 The following R-processor is a variation on Johan Sandblom's
+%D prototype.
+%D
+%D We can combine both variants in one macro definition. Also, we
+%D can minimize the number of runs by checking for a change.
+
+%D JS: The call to R has \type {-q} in order to prevent banner,
+%D \type {--save} to make sure it saves the workspace after the run,
+%D \type {--restore} to make sure it reads any workspace from a
+%D previous session.
+
+%D An easier and better solution is to use the buffering mechanisms:
+
+\def\Rbufferprefix{r-}
+
+\newcounter\nofRfiles
+
+\def\Rfile{\TEXbufferfile{\Rbufferprefix\nofRfiles}}
+
+\def\startR
+  {\doglobal\increment\nofRfiles
+   \dostartbuffer[\Rbufferprefix\nofRfiles][startR][stopR]}
+
+\def\stopR
+  {\doifmode{*\v!first}\runR
+   \typefile{\Rfile.out}}
+
+\def\startRhidden
+  {\doglobal\increment\nofRfiles
+   \dostartbuffer[\Rbufferprefix\nofRfiles][startRhidden][stopRhidden]}
+
+\def\stopRhidden
+  {\doifmode{*\v!first}\runR}
+
+\def\runR
+  {\executesystemcommand{texmfstart
+      --ifchanged=\Rfile\space --direct R
+      CMD BATCH -q --save --restore \Rfile\space \Rfile.out}}
+
+\protect \doifnotmode{demo}{\endinput}
+
+% Johan's test file:
+
+\usemodule[r]
+
+\def\R{R}
+
+\setupcolors[state=start]
+
+\setuptyping
+ [Rtype]
+ [color=darkgreen]
+
+\starttext
+
+First a test of whether the workspace is persistent:
+bla
+
+\startR
+a <- "bla"
+b <- "blabla"
+ls()
+\stopR
+
+One \R run ends, another begins.
+
+\startR
+ls()
+\stopR
+
+Now follows a hidden \R run which cleans the R workspace
+
+\startRhidden
+rm(list=ls())
+save.image()
+\stopRhidden
+
+What is in the workspace now?
+
+\startR
+ls()
+\stopR
+
+Then a small test of generating a graphic, in this case a pdf
+\startR
+ushape <- c(rexp(500000), 12-rexp(500000))
+pdf("ushape.pdf")
+par(mfrow=c(1,2))
+hist(ushape)
+plot(density(ushape), main="Density")
+dev.off()
+\stopR
+
+The graphic \type{ushape.pdf} can be included in the standard \CONTEXT\ way
+\startbuffer
+\placefigure{An ugly distribution}{\externalfigure[ushape]}
+\stopbuffer
+\typebuffer
+\getbuffer
+
+\startR
+x <- rnorm(900)
+y <- rexp(900)
+# test comment
+f <- gl(9,9,900)
+summary(aov(y~x+Error(f)))
+library(lattice)
+pdf("lattice.pdf")
+xyplot(y~x|f)
+dev.off()
+\stopR
+
+With \type{Sweave} lattice graphics calls must be enclosed in
+\type{print()} statements but that is not necessary here.
+
+\startbuffer
+\placefigure[here]{Lattice graphics}{\externalfigure[lattice]}
+\stopbuffer
+\typebuffer
+\getbuffer
+
+A test string with nasty characters. In \R, the result of a statement
+is not printed by default. Enclosing the statement in parentheses,
+however causes the parser to see only the value of the statement and
+applying the \type{print()} method.
+\startR
+(test <- ".*\\\\ [[{[{]{[{[{}\]\}=?!+%#|<|>@$")
+cat(test)
+\stopR
+
+A combination
+\startbuffer
+\placefigure{A combination of two previously used graphics}{
+\startcombination[2*1]
+ {\externalfigure[ushape][width=.4\textwidth]}{The first graphic, rescaled}
+ {\externalfigure[lattice][width=.4\textwidth]}{The second graphic, rescaled}}
+\stopcombination
+\stopbuffer
+\typebuffer
+\getbuffer
+
+Testing a function definition.
+
+\startR
+a.df <- data.frame(a=1:2, b=rnorm(2))
+a.df$a
+testfunction <- function(a=NULL, ...) {
+ for(i in 1:length(a)) {
+   gsub(a[[i]], "([a-r]|[A-R])", "bla")}
+ print(a)}
+\stopR
+
+What is in the workspace now?
+
+\startR
+ls()
+\stopR
+
+\stoptext
diff --git a/tex/context/base/m-r.tex b/tex/context/base/m-r.tex
deleted file mode 100644
index ac895905c..000000000
--- a/tex/context/base/m-r.tex
+++ /dev/null
@@ -1,174 +0,0 @@
-%D \module
-%D   [       file=m-r,
-%D        version=2006.06.06,
-%D          title=\CONTEXT\ Modules,
-%D       subtitle=R Support,
-%D         author={Johan Sandblom \& Hans Hagen},
-%D           date=\currentdate,
-%D      copyright={Johan Sandblom \& Hans Hagen}]
-%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 The following R-processor is a variation on Johan Sandblom's
-%D prototype.
-%D
-%D We can combine both variants in one macro definition. Also, we
-%D can minimize the number of runs by checking for a change.
-
-%D JS: The call to R has \type {-q} in order to prevent banner,
-%D \type {--save} to make sure it saves the workspace after the run,
-%D \type {--restore} to make sure it reads any workspace from a
-%D previous session.
-
-%D An easier and better solution is to use the buffering mechanisms:
-
-\def\Rbufferprefix{r-}
-
-\newcounter\nofRfiles
-
-\def\Rfile{\TEXbufferfile{\Rbufferprefix\nofRfiles}}%
-
-\def\startR
-  {\doglobal\increment\nofRfiles
-   \dostartbuffer[\Rbufferprefix\nofRfiles][startR][stopR]}
-
-\def\stopR
-  {\doifmode{*\v!first}\runR
-   \typefile{\Rfile.out}}
-
-\def\startRhidden
-  {\doglobal\increment\nofRfiles
-   \dostartbuffer[\Rbufferprefix\nofRfiles][startRhidden][stopRhidden]}
-
-\def\stopRhidden
-  {\doifmode{*\v!first}\runR}
-
-\def\runR
-  {\executesystemcommand{texmfstart
-      --ifchanged=\Rfile\space --direct R
-      CMD BATCH -q --save --restore \Rfile\space \Rfile.out}}
-
-\protect \doifnotmode{demo}{\endinput}
-
-% Johan's test file:
-
-\usemodule[r]
-
-\def\R{R}
-
-\setupcolors[state=start]
-
-\setuptyping
- [Rtype]
- [color=darkgreen]
-
-\starttext
-
-First a test of whether the workspace is persistent:
-bla
-
-\startR
-a <- "bla"
-b <- "blabla"
-ls()
-\stopR
-
-One \R run ends, another begins.
-
-\startR
-ls()
-\stopR
-
-Now follows a hidden \R run which cleans the R workspace
-
-\startRhidden
-rm(list=ls())
-save.image()
-\stopRhidden
-
-What is in the workspace now?
-
-\startR
-ls()
-\stopR
-
-Then a small test of generating a graphic, in this case a pdf
-\startR
-ushape <- c(rexp(500000), 12-rexp(500000))
-pdf("ushape.pdf")
-par(mfrow=c(1,2))
-hist(ushape)
-plot(density(ushape), main="Density")
-dev.off()
-\stopR
-
-The graphic \type{ushape.pdf} can be included in the standard \CONTEXT\ way
-\startbuffer
-\placefigure{An ugly distribution}{\externalfigure[ushape]}
-\stopbuffer
-\typebuffer
-\getbuffer
-
-\startR
-x <- rnorm(900)
-y <- rexp(900)
-# test comment
-f <- gl(9,9,900)
-summary(aov(y~x+Error(f)))
-library(lattice)
-pdf("lattice.pdf")
-xyplot(y~x|f)
-dev.off()
-\stopR
-
-With \type{Sweave} lattice graphics calls must be enclosed in
-\type{print()} statements but that is not necessary here.
-
-\startbuffer
-\placefigure[here]{Lattice graphics}{\externalfigure[lattice]}
-\stopbuffer
-\typebuffer
-\getbuffer
-
-A test string with nasty characters. In \R, the result of a statement
-is not printed by default. Enclosing the statement in parentheses,
-however causes the parser to see only the value of the statement and
-applying the \type{print()} method.
-\startR
-(test <- ".*\\\\ [[{[{]{[{[{}\]\}=?!+%#|<|>@$")
-cat(test)
-\stopR
-
-A combination
-\startbuffer
-\placefigure{A combination of two previously used graphics}{
-\startcombination[2*1]
- {\externalfigure[ushape][width=.4\textwidth]}{The first graphic, rescaled}
- {\externalfigure[lattice][width=.4\textwidth]}{The second graphic, rescaled}}
-\stopcombination
-\stopbuffer
-\typebuffer
-\getbuffer
-
-Testing a function definition.
-
-\startR
-a.df <- data.frame(a=1:2, b=rnorm(2))
-a.df$a
-testfunction <- function(a=NULL, ...) {
- for(i in 1:length(a)) {
-   gsub(a[[i]], "([a-r]|[A-R])", "bla")}
- print(a)}
-\stopR
-
-What is in the workspace now?
-
-\startR
-ls()
-\stopR
-
-\stoptext
diff --git a/tex/context/base/m-spreadsheet.lua b/tex/context/base/m-spreadsheet.lua
index 9d5106e35..f329acf9a 100644
--- a/tex/context/base/m-spreadsheet.lua
+++ b/tex/context/base/m-spreadsheet.lua
@@ -172,7 +172,7 @@ function functions._s_(row,col,c,f,t)
     for i=f,t do
         local ci = c[i]
         if type(ci) == "number" then
-            r = r + c[i]
+            r = r + ci
         end
     end
     return r
diff --git a/tex/context/base/m-translate.mkiv b/tex/context/base/m-translate.mkiv
index 363f115cb..f36f9a9fb 100644
--- a/tex/context/base/m-translate.mkiv
+++ b/tex/context/base/m-translate.mkiv
@@ -89,7 +89,6 @@
 
 \continueifinputfile{m-translate.mkiv}
 
-
 \starttext
 
     \translateinput[Moica][Mojca]
diff --git a/tex/context/base/m-zint.mkiv b/tex/context/base/m-zint.mkiv
index 95b265c57..4957c8461 100644
--- a/tex/context/base/m-zint.mkiv
+++ b/tex/context/base/m-zint.mkiv
@@ -29,17 +29,21 @@ moduledata.zint = { }
 local format, lower, gsub  = string.format, string.lower, string.gsub
 local patterns = lpeg.patterns
 
-local zint       = "zint" -- '"c:/program files/zint/zint.exe"'
+local zint        = "zint" -- '"c:/program files/zint/zint.exe"'
+local defaultcode = "PDF417"
 
-local whitespace = patterns.whitespace
-local spaces     = whitespace^0
-local key        = (spaces / "") * patterns.digit^0 * (patterns.colon * spaces / "")
-local value      = (whitespace / "" + (1 - key))^1
-local pattern    = lpeg.Cf(lpeg.Ct("") * (lpeg.Cg((lpeg.Cs(key) / tonumber) * (lpeg.Cs(value) / lower)) + patterns.anything)^0,rawset)
+local whitespace  = patterns.whitespace
+local spaces      = whitespace^0
+local key         = (spaces / "") * patterns.digit^0 * (patterns.colon * spaces / "")
+local value       = (whitespace / "" + (1 - key))^1
+local pattern     = lpeg.Cf(lpeg.Ct("") * (lpeg.Cg((lpeg.Cs(key) / tonumber) * (lpeg.Cs(value) / lower)) + patterns.anything)^0,rawset)
 
 local reverse
 
 local function cleancode(code)
+    if not code or code == "" then
+        code = defaultcode
+    end
     return lower(gsub(code," ",""))
 end
 
@@ -76,6 +80,19 @@ end
 
 \stopluacode
 
+\unprotect
+
+\unexpanded\def\barcode[#1]% [alternative=,text=]
+  {\bgroup
+   \getdummyparameters
+     [\c!alternative=,\c!text=,#1]%
+   \externalfigure
+     [\cldcontext{moduledata.zint.generate("\dummyparameter\c!alternative",\!!bs\dummyparameter\c!text\!!es)}]%
+     [#1,\c!alternative=,\c!text=]%
+   \egroup}
+
+\protect
+
 \continueifinputfile{m-zint.mkiv}
 
 \starttext
@@ -85,6 +102,10 @@ end
     \externalfigure[\cldcontext{moduledata.zint.generate("PDF417","Ton Otten")}]
     \blank
     \externalfigure[\cldcontext{moduledata.zint.generate("ISBN","9789490688011")}]
+    \blank
+    \barcode[text=Does It Work?,width=\textwidth]
+    \blank
+    \barcode[alternative=isbn,text=9789490688011,width=3cm]
 
 \stoptext
 
diff --git a/tex/context/base/math-act.lua b/tex/context/base/math-act.lua
index 7c75dc56e..a03542b3e 100644
--- a/tex/context/base/math-act.lua
+++ b/tex/context/base/math-act.lua
@@ -18,7 +18,7 @@ local report_math    = logs.reporter("mathematics","initializing")
 local context        = context
 local commands       = commands
 local mathematics    = mathematics
-local texdimen       = tex.dimen
+local texsetdimen    = tex.setdimen
 local abs            = math.abs
 
 local sequencers     = utilities.sequencers
@@ -296,78 +296,95 @@ function tweaks.fixbadprime(target,original)
     target.characters[0xFE325] = target.characters[0x2032]
 end
 
-local function accent_to_extensible(target,newchr,original,oldchr,height,depth)
-    local characters = target.characters
- -- if not characters[newchr] then -- xits needs an enforce
-        local olddata = characters[oldchr]
-        if olddata then
-            height = height or 0
-            depth = depth or 0
-            local addprivate = fonts.helpers.addprivate
-            local correction = { "down", olddata.height }
-            local newdata = {
-                commands = { correction, { "slot", 1, oldchr } },
-                width    = olddata.width,
-                height   = height,
-                depth    = depth,
-            }
-            characters[newchr] = newdata
-            local nextglyph = olddata.next
-            while nextglyph do
-                local oldnextdata = characters[nextglyph]
-                local newnextdata = {
-                    commands = { correction, { "slot", 1, nextglyph } },
-                    width    = oldnextdata.width,
-                    height   = height,
-                    depth    = depth,
-                }
-                local newnextglyph = addprivate(target,formatters["overline-%H"](nextglyph),newnextdata)
-                newdata.next = newnextglyph
-                local nextnextglyph = oldnextdata.next
-                if nextnextglyph == nextglyph then
-                    break
-                else
-                    olddata   = oldnextdata
-                    newdata   = newnextdata
-                    nextglyph = nextnextglyph
-                end
-            end
-            local hv = olddata.horiz_variants
-            if hv then
-                hv = fastcopy(hv)
-                newdata.horiz_variants = hv
-                for i=1,#hv do
-                    local hvi = hv[i]
-                    local oldglyph = hvi.glyph
-                    local olddata = characters[oldglyph]
-                    local newdata = {
-                        commands = { correction, { "slot", 1, oldglyph } },
-                        width    = olddata.width,
-                        height   = height,
-                        depth    = depth,
-                    }
-                    hvi.glyph = addprivate(target,formatters["overline-%H"](oldglyph),newdata)
-                end
-            end
-        end
- -- end
-end
-
-function tweaks.fixoverline(target,original)
-    local height, depth = 0, 0
-    local mathparameters = target.mathparameters
-    if mathparameters then
-        height = mathparameters.OverbarVerticalGap
-        depth  = mathparameters.UnderbarVerticalGap
-    else
-        height = target.parameters.xheight/4
-        depth  = height
-    end
-    accent_to_extensible(target,0x203E,original,0x0305,height,depth)
---  inspect(fonts.helpers.expandglyph(target.characters,0x203E))
-end
-
-sequencers.appendaction("aftercopyingcharacters", "system","mathematics.tweaks.fixoverline") -- for the moment always
+-- these could go to math-fbk
+
+-- local function accent_to_extensible(target,newchr,original,oldchr,height,depth,swap)
+--     local characters = target.characters
+--  -- if not characters[newchr] then -- xits needs an enforce
+--     local addprivate = fonts.helpers.addprivate
+--         local olddata = characters[oldchr]
+--         if olddata then
+--             if swap then
+--                 swap = characters[swap]
+--                 height = swap.depth
+--                 depth  = 0
+--             else
+--                 height = height or 0
+--                 depth  = depth  or 0
+--             end
+--             local correction = swap and { "down", (olddata.height or 0) - height } or { "down", olddata.height }
+--             local newdata = {
+--                 commands = { correction, { "slot", 1, oldchr } },
+--                 width    = olddata.width,
+--                 height   = height,
+--                 depth    = depth,
+--             }
+--             characters[newchr] = newdata
+--             local nextglyph = olddata.next
+--             while nextglyph do
+--                 local oldnextdata = characters[nextglyph]
+--                 local newnextdata = {
+--                     commands = { correction, { "slot", 1, nextglyph } },
+--                     width    = oldnextdata.width,
+--                     height   = height,
+--                     depth    = depth,
+--                 }
+--                 local newnextglyph = addprivate(target,formatters["original-%H"](nextglyph),newnextdata)
+--                 newdata.next = newnextglyph
+--                 local nextnextglyph = oldnextdata.next
+--                 if nextnextglyph == nextglyph then
+--                     break
+--                 else
+--                     olddata   = oldnextdata
+--                     newdata   = newnextdata
+--                     nextglyph = nextnextglyph
+--                 end
+--             end
+--             local hv = olddata.horiz_variants
+--             if hv then
+--                 hv = fastcopy(hv)
+--                 newdata.horiz_variants = hv
+--                 for i=1,#hv do
+--                     local hvi = hv[i]
+--                     local oldglyph = hvi.glyph
+--                     local olddata = characters[oldglyph]
+--                     local newdata = {
+--                         commands = { correction, { "slot", 1, oldglyph } },
+--                         width    = olddata.width,
+--                         height   = height,
+--                         depth    = depth,
+--                     }
+--                     hvi.glyph = addprivate(target,formatters["original-%H"](oldglyph),newdata)
+--                 end
+--             end
+--         end
+--  -- end
+-- end
+
+-- function tweaks.fixoverline(target,original)
+--     local height, depth = 0, 0
+--     local mathparameters = target.mathparameters
+--     if mathparameters then
+--         height = mathparameters.OverbarVerticalGap
+--         depth  = mathparameters.UnderbarVerticalGap
+--     else
+--         height = target.parameters.xheight/4
+--         depth  = height
+--     end
+--     accent_to_extensible(target,0x203E,original,0x0305,height,depth)
+--     -- also crappy spacing for our purpose: push to top of baseline
+--     accent_to_extensible(target,0xFE3DE,original,0x23DE,height,depth,0x23DF)
+--     accent_to_extensible(target,0xFE3DC,original,0x23DC,height,depth,0x23DD)
+--     accent_to_extensible(target,0xFE3B4,original,0x23B4,height,depth,0x23B5)
+--     -- for symmetry
+--     target.characters[0xFE3DF] = original.characters[0x23DF]
+--     target.characters[0xFE3DD] = original.characters[0x23DD]
+--     target.characters[0xFE3B5] = original.characters[0x23B5]
+--  -- inspect(fonts.helpers.expandglyph(target.characters,0x203E))
+--  -- inspect(fonts.helpers.expandglyph(target.characters,0x23DE))
+-- end
+
+-- sequencers.appendaction("aftercopyingcharacters", "system","mathematics.tweaks.fixoverline") -- for the moment always
 
 -- helpers
 
@@ -455,24 +472,24 @@ function commands.horizontalcode(family,unicode)
         local charlist = data[3].horiz_variants
         local characters = fontcharacters[font]
         local left = charlist[1]
-        texdimen.scratchleftoffset  = abs((left["start"] or 0) - (left["end"] or 0))
-        texdimen.scratchrightoffset = 0
+        texsetdimen("scratchleftoffset",abs((left["start"] or 0) - (left["end"] or 0)))
+        texsetdimen("scratchrightoffset",0)
     elseif kind == e_right then
         local charlist = data[3].horiz_variants
         local characters = fontcharacters[font]
         local right = charlist[#charlist]
-        texdimen.scratchleftoffset  = 0
-        texdimen.scratchrightoffset = abs((right["start"] or 0) - (right["end"] or 0))
+        texsetdimen("scratchleftoffset",0)
+        texsetdimen("scratchrightoffset",abs((right["start"] or 0) - (right["end"] or 0)))
      elseif kind == e_horizontal then
         local charlist = data[3].horiz_variants
         local characters = fontcharacters[font]
         local left = charlist[1]
         local right = charlist[#charlist]
-        texdimen.scratchleftoffset  = abs((left["start"] or 0) - (left["end"] or 0))
-        texdimen.scratchrightoffset = abs((right["start"] or 0) - (right["end"] or 0))
+        texsetdimen("scratchleftoffset", abs((left ["start"] or 0) - (left ["end"] or 0)))
+        texsetdimen("scratchrightoffset",abs((right["start"] or 0) - (right["end"] or 0)))
     else
-        texdimen.scratchleftoffset  = 0
-        texdimen.scratchrightoffset = 0
+        texsetdimen("scratchleftoffset",0)
+        texsetdimen("scratchrightoffset",0)
     end
     context(kind)
 end
diff --git a/tex/context/base/math-def.mkiv b/tex/context/base/math-def.mkiv
index 9201fc540..99e6a8a18 100644
--- a/tex/context/base/math-def.mkiv
+++ b/tex/context/base/math-def.mkiv
@@ -348,18 +348,21 @@
 \unexpanded\def\mathopwithlimits#1#2{\mathop{#1{#2}}\limits}
 \unexpanded\def\stackrel        #1#2{\mathrel{\mathop{#2}\limits^{#1}}}
 
-\unexpanded\def\overbrace      {\mathopwithlimits\normaloverbrace      }
-\unexpanded\def\underbrace     {\mathopwithlimits\normalunderbrace     }
-\unexpanded\def\doublebrace    {\mathopwithlimits\normaldoublebrace    }
-\unexpanded\def\overparent     {\mathopwithlimits\normaloverparent     }
-\unexpanded\def\underparent    {\mathopwithlimits\normalunderparent    }
-\unexpanded\def\overbracket    {\mathopwithlimits\normaloverbracket    }
-\unexpanded\def\underbracket   {\mathopwithlimits\normalunderbracket   }
-\unexpanded\def\doubleparent   {\mathopwithlimits\normaldoubleparent   }
-\unexpanded\def\underleftarrow {\mathopwithlimits\normalunderleftarrow }
-\unexpanded\def\overleftarrow  {\mathopwithlimits\normaloverleftarrow  }
-\unexpanded\def\underrightarrow{\mathopwithlimits\normalunderrightarrow}
-\unexpanded\def\overrightarrow {\mathopwithlimits\normaloverrightarrow }
+% these are stackers now
+
+%\unexpanded\def\overbrace      {\mathopwithlimits\normaloverbrace      }
+%\unexpanded\def\underbrace     {\mathopwithlimits\normalunderbrace     }
+%\unexpanded\def\doublebrace    {\mathopwithlimits\normaldoublebrace    }
+%\unexpanded\def\overparent     {\mathopwithlimits\normaloverparent     }
+%\unexpanded\def\underparent    {\mathopwithlimits\normalunderparent    }
+%\unexpanded\def\overbracket    {\mathopwithlimits\normaloverbracket    }
+%\unexpanded\def\underbracket   {\mathopwithlimits\normalunderbracket   }
+%\unexpanded\def\doubleparent   {\mathopwithlimits\normaldoubleparent   }
+
+%unexpanded\def\underleftarrow {\mathopwithlimits\normalunderleftarrow }
+%unexpanded\def\overleftarrow  {\mathopwithlimits\normaloverleftarrow  }
+%unexpanded\def\underrightarrow{\mathopwithlimits\normalunderrightarrow}
+%unexpanded\def\overrightarrow {\mathopwithlimits\normaloverrightarrow }
 
 \let\lceil \lceiling
 \let\rceil \rceiling
diff --git a/tex/context/base/math-fbk.lua b/tex/context/base/math-fbk.lua
index eebc4e4e7..0800f3007 100644
--- a/tex/context/base/math-fbk.lua
+++ b/tex/context/base/math-fbk.lua
@@ -10,6 +10,9 @@ local trace_fallbacks = false  trackers.register("math.fallbacks", function(v) t
 
 local report_fallbacks = logs.reporter("math","fallbacks")
 
+local formatters = string.formatters
+local fastcopy = table.fastcopy
+
 local fallbacks       = { }
 mathematics.fallbacks = fallbacks
 
@@ -39,7 +42,7 @@ function fallbacks.apply(target,original)
         end
         -- This is not okay yet ... we have no proper way to refer to 'self'
         -- otherwise I will make my own id allocator).
-local self = #usedfonts == 0 and font.nextid() or nil -- will be true
+        local self = #usedfonts == 0 and font.nextid() or nil -- will be true
         local textid, scriptid, scriptscriptid
         local textindex, scriptindex, scriptscriptindex
         local textdata, scriptdata, scriptscriptdata
@@ -48,19 +51,19 @@ local self = #usedfonts == 0 and font.nextid() or nil -- will be true
          -- textid         = nil -- self
          -- scriptid       = nil -- no smaller
          -- scriptscriptid = nil -- no smaller
-textid = self
-scriptid = self
-scriptscriptid = self
+            textid         = self
+            scriptid       = self
+            scriptscriptid = self
         elseif mathsize == 2 then
             -- scriptsize
          -- textid         = nil -- self
-textid = self
+            textid         = self
             scriptid       = lastmathids[3]
             scriptscriptid = lastmathids[3]
         else
             -- textsize
          -- textid         = nil -- self
-textid = self
+            textid         = self
             scriptid       = lastmathids[2]
             scriptscriptid = lastmathids[3]
         end
@@ -87,8 +90,7 @@ textid = self
             scriptscriptindex = scriptindex
             scriptscriptdata  = scriptdata
         end
--- report_fallbacks("used textid: %s, used script id: %s, used scriptscript id: %s",
---     tostring(textid),tostring(scriptid),tostring(scriptscriptid))
+     -- report_fallbacks("used textid: %S, used script id: %S, used scriptscript id: %S",textid,scriptid,scriptscriptid)
         local data = {
             textdata          = textdata,
             scriptdata        = scriptdata,
@@ -103,7 +105,7 @@ textid = self
             size              = size,
             mathsize          = mathsize,
         }
--- inspect(usedfonts)
+     -- inspect(usedfonts)
         for k, v in next, virtualcharacters do
             if not characters[k] then
                 local tv = type(v)
@@ -310,3 +312,129 @@ virtualcharacters[0xFE352] = function(data)
     end
 end
 
+-- we could move the defs from math-act here
+
+addextra(0xFE3DE, { description="EXTENSIBLE OF 0x03DE", unicodeslot=0xFE3DE, mathextensible = "r", mathstretch = "h" } )
+addextra(0xFE3DF, { description="EXTENSIBLE OF 0x03DF", unicodeslot=0xFE3DF, mathextensible = "r", mathstretch = "h" } )
+addextra(0xFE3DC, { description="EXTENSIBLE OF 0x03DC", unicodeslot=0xFE3DC, mathextensible = "r", mathstretch = "h" } )
+addextra(0xFE3DD, { description="EXTENSIBLE OF 0x03DD", unicodeslot=0xFE3DD, mathextensible = "r", mathstretch = "h" } )
+addextra(0xFE3B4, { description="EXTENSIBLE OF 0x03B4", unicodeslot=0xFE3B4, mathextensible = "r", mathstretch = "h" } )
+addextra(0xFE3B5, { description="EXTENSIBLE OF 0x03B5", unicodeslot=0xFE3B5, mathextensible = "r", mathstretch = "h" } )
+
+local function accent_to_extensible(target,newchr,original,oldchr,height,depth,swap)
+    local characters = target.characters
+    local addprivate = fonts.helpers.addprivate
+    local olddata = characters[oldchr]
+    if olddata then
+        if swap then
+            swap = characters[swap]
+            height = swap.depth
+            depth  = 0
+        else
+            height = height or 0
+            depth  = depth  or 0
+        end
+        local correction = swap and { "down", (olddata.height or 0) - height } or { "down", olddata.height }
+        local newdata = {
+            commands = { correction, { "slot", 1, oldchr } },
+            width    = olddata.width,
+            height   = height,
+            depth    = depth,
+        }
+        local glyphdata = newdata
+        local nextglyph = olddata.next
+        while nextglyph do
+            local oldnextdata = characters[nextglyph]
+            local newnextdata = {
+                commands = { correction, { "slot", 1, nextglyph } },
+                width    = oldnextdata.width,
+                height   = height,
+                depth    = depth,
+            }
+            local newnextglyph = addprivate(target,formatters["original-%H"](nextglyph),newnextdata)
+            newdata.next = newnextglyph
+            local nextnextglyph = oldnextdata.next
+            if nextnextglyph == nextglyph then
+                break
+            else
+                olddata   = oldnextdata
+                newdata   = newnextdata
+                nextglyph = nextnextglyph
+            end
+        end
+        local hv = olddata.horiz_variants
+        if hv then
+            hv = fastcopy(hv)
+            newdata.horiz_variants = hv
+            for i=1,#hv do
+                local hvi = hv[i]
+                local oldglyph = hvi.glyph
+                local olddata = characters[oldglyph]
+                local newdata = {
+                    commands = { correction, { "slot", 1, oldglyph } },
+                    width    = olddata.width,
+                    height   = height,
+                    depth    = depth,
+                }
+                hvi.glyph = addprivate(target,formatters["original-%H"](oldglyph),newdata)
+            end
+        end
+        return glyphdata
+    end
+end
+
+virtualcharacters[0x203E] = function(data) -- could be FE33E instead
+    local target = data.target
+    local height, depth = 0, 0
+    local mathparameters = target.mathparameters
+    if mathparameters then
+        height = mathparameters.OverbarVerticalGap
+        depth  = mathparameters.UnderbarVerticalGap
+    else
+        height = target.parameters.xheight/4
+        depth  = height
+    end
+    return accent_to_extensible(target,0x203E,data.original,0x0305,height,depth)
+end
+
+virtualcharacters[0xFE3DE] = function(data)
+    local target, original = data.target, data.original
+    local chardata = target.characters[0x23DE]
+    if chardata and chardata.height > target.parameters.xheight then
+        return accent_to_extensible(target,0xFE3DE,original,0x23DE,0,0,0x23DF)
+    else
+        return original.characters[0x23DE]
+    end
+end
+
+virtualcharacters[0xFE3DC] = function(data)
+    local target, original = data.target, data.original
+    local chardata = target.characters[0x23DC]
+    if chardata and chardata.height > target.parameters.xheight then
+        return accent_to_extensible(target,0xFE3DC,original,0x23DC,0,0,0x23DD)
+    else
+        return original.characters[0x23DC]
+    end
+end
+
+virtualcharacters[0xFE3B4] = function(data)
+    local target, original = data.target, data.original
+    local chardata = target.characters[0x23B4]
+    if chardata and chardata.height > target.parameters.xheight then
+        return accent_to_extensible(target,0xFE3B4,original,0x23B4,0,0,0x23B5)
+    else
+        return original.characters[0x23B4]
+    end
+end
+
+virtualcharacters[0xFE3DF] = function(data)
+    return data.original.characters[0x23DF]
+end
+
+virtualcharacters[0xFE3DD] = function(data)
+    return data.original.characters[0x23DD]
+end
+
+virtualcharacters[0xFE3B5] = function(data)
+    return data.original.characters[0x23B5]
+end
diff --git a/tex/context/base/math-ini.lua b/tex/context/base/math-ini.lua
index 6e667d083..dc1e9f29a 100644
--- a/tex/context/base/math-ini.lua
+++ b/tex/context/base/math-ini.lua
@@ -17,8 +17,6 @@ if not modules then modules = { } end modules ['math-ini'] = {
 
 local formatters, find = string.formatters, string.find
 local utfchar, utfbyte = utf.char, utf.byte
-local setmathcode, setdelcode = tex.setmathcode, tex.setdelcode
-local settexattribute = tex.setattribute
 local floor = math.floor
 
 local context           = context
@@ -41,6 +39,10 @@ local unsetvalue        = attributes.unsetvalue
 local allocate          = utilities.storage.allocate
 local chardata          = characters.data
 
+local texsetattribute   = tex.setattribute
+local setmathcode       = tex.setmathcode
+local setdelcode        = tex.setdelcode
+
 local families = allocate {
     mr = 0,
     mb = 1,
@@ -503,10 +505,10 @@ end
 --
 -- function commands.taggedmathfunction(tag,label)
 --     if label then
---         settexattribute(a_mathcategory,registercategory(1,tag,tag))
+--         texsetattribute(a_mathcategory,registercategory(1,tag,tag))
 --         context.mathlabeltext(tag)
 --     else
---         settexattribute(a_mathcategory,1)
+--         texsetattribute(a_mathcategory,1)
 --         context(tag)
 --     end
 -- end
@@ -529,13 +531,13 @@ function commands.taggedmathfunction(tag,label,apply)
             noffunctions = noffunctions + 1
             functions[noffunctions] = tag
             functions[tag] = noffunctions
-            settexattribute(a_mathcategory,noffunctions + delta)
+            texsetattribute(a_mathcategory,noffunctions + delta)
         else
-            settexattribute(a_mathcategory,n + delta)
+            texsetattribute(a_mathcategory,n + delta)
         end
         context.mathlabeltext(tag)
     else
-        settexattribute(a_mathcategory,1000 + delta)
+        texsetattribute(a_mathcategory,1000 + delta)
         context(tag)
     end
 end
@@ -554,6 +556,6 @@ function commands.resetmathattributes()
         end
     end
     for i=1,#list do
-        settexattribute(list[i],unsetvalue)
+        texsetattribute(list[i],unsetvalue)
     end
 end
diff --git a/tex/context/base/math-map.lua b/tex/context/base/math-map.lua
index a0d7457d1..9158d945a 100644
--- a/tex/context/base/math-map.lua
+++ b/tex/context/base/math-map.lua
@@ -36,11 +36,15 @@ local merged = table.merged
 local extract = bit32.extract
 
 local allocate            = utilities.storage.allocate
-local texattribute        = tex.attribute
+
 local otffeatures         = fonts.constructors.newfeatures("otf")
 local registerotffeature  = otffeatures.register
+
 local setmetatableindex   = table.setmetatableindex
 
+local texgetattribute        = tex.getattribute
+local texsetattribute        = tex.setattribute
+
 local trace_greek         = false  trackers.register("math.greek",  function(v) trace_greek = v end)
 local report_remapping    = logs.reporter("mathematics","remapping")
 
@@ -524,7 +528,7 @@ function mathematics.getboth(alphabet,style)
 end
 
 function mathematics.getstyle(style)
-    local r = mathremap[texattribute[mathalphabet]]
+    local r = mathremap[texgetattribute(mathalphabet)]
     local alphabet = r and r.alphabet or "regular"
     local data = alphabets[alphabet][style]
     return data and data.attribute
@@ -533,22 +537,22 @@ end
 function mathematics.syncboth(alphabet,style)
     local data = alphabet and alphabets[alphabet] or regular
     data = style and data[style] or data.tf
-    texattribute[mathalphabet] = data and data.attribute or texattribute[mathalphabet]
+    texsetattribute(mathalphabet,data and data.attribute or texattribute[mathalphabet])
 end
 
 function mathematics.syncstyle(style)
-    local r = mathremap[texattribute[mathalphabet]]
+    local r = mathremap[texgetattribute(mathalphabet)]
     local alphabet = r and r.alphabet or "regular"
     local data = alphabets[alphabet][style]
-    texattribute[mathalphabet] = data and data.attribute or texattribute[mathalphabet]
+    texsetattribute(mathalphabet,data and data.attribute or texattribute[mathalphabet])
 end
 
 function mathematics.syncname(alphabet)
  -- local r = mathremap[mathalphabet]
-    local r = mathremap[texattribute[mathalphabet]]
+    local r = mathremap[texgetattribute(mathalphabet)]
     local style = r and r.style or "tf"
     local data = alphabets[alphabet][style]
-    texattribute[mathalphabet] = data and data.attribute or texattribute[mathalphabet]
+    texsetattribute(mathalphabet,data and data.attribute or texattribute[mathalphabet])
 end
 
 local islcgreek = regular_tf.lcgreek
diff --git a/tex/context/base/math-noa.lua b/tex/context/base/math-noa.lua
index b309ba077..903c91f34 100644
--- a/tex/context/base/math-noa.lua
+++ b/tex/context/base/math-noa.lua
@@ -62,7 +62,6 @@ local new_node            = node.new -- todo: pool: math_noad math_sub
 
 local new_kern            = nodes.pool.kern
 local new_rule            = nodes.pool.rule
-local concat_nodes        = nodes.concat
 
 local topoints            = number.points
 
@@ -75,7 +74,8 @@ local fontemwidths        = fonthashes.emwidths
 local fontexheights       = fonthashes.exheights
 
 local variables           = interfaces.variables
-local texattribute        = tex.attribute
+local texsetattribute     = tex.setattribute
+local texgetattribute     = tex.getattribute
 local unsetvalue          = attributes.unsetvalue
 
 local chardata            = characters.data
@@ -462,135 +462,6 @@ function handlers.resize(head,style,penalties)
     return true
 end
 
--- respacing
-
--- local mathpunctuation = attributes.private("mathpunctuation")
---
--- local respace = { } processors.respace = respace
-
--- only [nd,ll,ul][po][nd,ll,ul]
-
--- respace[math_char] = function(pointer,what,n,parent) -- not math_noad .. math_char ... and then parent
---     pointer = parent
---     if pointer and pointer.subtype == noad_ord then
---         local a = pointer[mathpunctuation]
---         if a and a > 0 then
---             pointer[mathpunctuation] = 0
---             local current_nucleus = pointer.nucleus
---             if current_nucleus.id == math_char then
---                 local current_char = current_nucleus.char
---                 local fc = chardata[current_char]
---                 fc = fc and fc.category
---                 if fc == "nd" or fc == "ll" or fc == "lu" then
---                     local next_noad = pointer.next
---                     if next_noad and next_noad.id == math_noad and next_noad.subtype == noad_punct then
---                         local next_nucleus = next_noad.nucleus
---                         if next_nucleus.id == math_char then
---                             local next_char = next_nucleus.char
---                             local nc = chardata[next_char]
---                             nc = nc and nc.category
---                             if nc == "po" then
---                                 local last_noad = next_noad.next
---                                 if last_noad and last_noad.id == math_noad and last_noad.subtype == noad_ord then
---                                     local last_nucleus = last_noad.nucleus
---                                     if last_nucleus.id == math_char then
---                                         local last_char = last_nucleus.char
---                                         local lc = chardata[last_char]
---                                         lc = lc and lc.category
---                                         if lc == "nd" or lc == "ll" or lc == "lu" then
---                                             local ord = new_node(math_noad) -- todo: pool
---                                             ord.subtype, ord.nucleus, ord.sub, ord.sup, ord.attr = noad_ord, next_noad.nucleus, next_noad.sub, next_noad.sup, next_noad.attr
---                                         --  next_noad.nucleus, next_noad.sub, next_noad.sup, next_noad.attr = nil, nil, nil, nil
---                                             next_noad.nucleus, next_noad.sub, next_noad.sup = nil, nil, nil -- else crash with attributes ref count
---                                         --~ next_noad.attr = nil
---                                             ord.next = last_noad
---                                             pointer.next = ord
---                                             free_node(next_noad)
---                                         end
---                                     end
---                                 end
---                             end
---                         end
---                     end
---                 end
---             end
---         end
---     end
--- end
-
--- local comma  = 0x002C
--- local period = 0x002E
---
--- respace[math_char] = function(pointer,what,n,parent)
---     pointer = parent
---     if pointer and pointer.subtype == noad_punct then
---         local current_nucleus = pointer.nucleus
---         if current_nucleus.id == math_char then
---             local current_nucleus = pointer.nucleus
---             if current_nucleus.id == math_char then
---                 local current_char = current_nucleus.char
---                 local a = pointer[mathpunctuation]
---                 if not a or a == 0 then
---                     if current_char == comma then
---                         -- default tex: 2,5 or 2, 5 --> 2, 5
---                     elseif current_char == period then
---                         -- default tex: 2.5 or 2. 5 --> 2.5
---                         pointer.subtype = noad_ord
---                     end
---                 elseif a == 1 then
---                     local next_noad = pointer.next
---                     if next_noad and next_noad.id == math_noad then
---                         local next_nucleus = next_noad.nucleus
---                         if next_nucleus.id == math_char and next_nucleus.char == 0 then
---                             nodes.remove(pointer,next_noad,true)
---                         end
---                         if current_char == comma then
---                             -- default tex: 2,5 or 2, 5 --> 2, 5
---                         elseif current_char == period then
---                             -- default tex: 2.5 or 2. 5 --> 2.5
---                             pointer.subtype = noad_ord
---                         end
---                     end
---                 elseif a == 2 then
---                     if current_char == comma or current_char == period then
---                         local next_noad = pointer.next
---                         if next_noad and next_noad.id == math_noad then
---                             local next_nucleus = next_noad.nucleus
---                             if next_nucleus.id == math_char and next_nucleus.char == 0 then
---                                 if current_char == comma then
---                                     -- adaptive: 2, 5 --> 2, 5
---                                 elseif current_char == period then
---                                     -- adaptive: 2. 5 --> 2. 5
---                                 end
---                                 nodes.remove(pointer,next_noad,true)
---                             else
---                                 if current_char == comma then
---                                     -- adaptive: 2,5  --> 2,5
---                                     pointer.subtype = noad_ord
---                                 elseif current_char == period then
---                                     -- adaptive: 2.5  --> 2.5
---                                     pointer.subtype = noad_ord
---                                 end
---                             end
---                         end
---                     end
---                 end
---             end
---         end
---     end
--- end
---
--- function handlers.respace(head,style,penalties)
---     processnoads(head,respace,"respace")
---     return true
--- end
-
--- The following code is dedicated to Luigi Scarso who pointed me
--- to the fact that \not= is not producing valid pdf-a code.
--- The code does not solve this for virtual characters but it does
--- a decent job on collapsing so that fonts that have the right
--- glyph will have a decent unicode point. In the meantime this code
--- has been moved elsewhere.
 
 local collapse = { } processors.collapse = collapse
 
@@ -824,7 +695,7 @@ function mathematics.setalternate(fam,tag)
     local mathalternates = tfmdata.shared and tfmdata.shared.mathalternates
     if mathalternates then
         local m = mathalternates[tag]
-        tex.attribute[a_mathalternate] = m and m.attribute or unsetvalue
+        texsetattribute(a_mathalternate,m and m.attribute or unsetvalue)
     end
 end
 
@@ -947,11 +818,7 @@ trackers.register("math.italics", function(v)
             if k > 0 then
                 return setcolor(new_rule(k,ex,ex),c_positive_d)
             else
-                return concat_nodes {
-                    old_kern(k),
-                    setcolor(new_rule(-k,ex,ex),c_negative_d),
-                    old_kern(k),
-                }
+                return old_kern(k) .. setcolor(new_rule(-k,ex,ex),c_negative_d) .. old_kern(k)
             end
         end
     else
@@ -1063,14 +930,14 @@ function mathematics.setitalics(n)
         enable()
     end
     if n == variables.reset then
-        texattribute[a_mathitalics] = unsetvalue
+        texsetattribute(a_mathitalics,unsetvalue)
     else
-        texattribute[a_mathitalics] = tonumber(n) or unsetvalue
+        texsetattribute(a_mathitalics,tonumber(n) or unsetvalue)
     end
 end
 
 function mathematics.resetitalics()
-    texattribute[a_mathitalics] = unsetvalue
+    texsetattribute(a_mathitalics,unsetvalue)
 end
 
 -- variants
diff --git a/tex/context/base/math-ren.lua b/tex/context/base/math-ren.lua
index 2e7dba13d..5c4c13369 100644
--- a/tex/context/base/math-ren.lua
+++ b/tex/context/base/math-ren.lua
@@ -63,7 +63,3 @@ mathematics.renderset = renderset
 function commands.mathrenderset(list)
     context(renderset(list))
 end
-
--- function commands.setmatrendering(list)
---     tex.setattribute(renderset(list))
--- end
diff --git a/tex/context/base/math-stc.mkvi b/tex/context/base/math-stc.mkvi
index ae601bc4c..5f92f3801 100644
--- a/tex/context/base/math-stc.mkvi
+++ b/tex/context/base/math-stc.mkvi
@@ -395,11 +395,23 @@
 
 %D The next one deals with under and over extensibles (arrows mostly):
 
-\unexpanded\def\math_stackers_double#where#category#codepoint#text%
+\installcorenamespace {mathclasses}
+
+\letvalue{\??mathclasses    }\mathord
+\letvalue{\??mathclasses rel}\mathrel
+\letvalue{\??mathclasses ord}\mathord
+
+\def\math_class_by_parameter#1%
+  {\normalexpanded{\noexpand\math_class_by_parameter_indeed{#1\c!mathclass}}}
+
+\def\math_class_by_parameter_indeed#1%
+  {\csname\??mathclasses\ifcsname\??mathclasses#1\endcsname#1\fi\endcsname}
+
+\unexpanded\def\math_stackers_make_double#top#bottom#category#codepoint#codeextra#text%
   {\begingroup
    \edef\currentmathstackers{#category}%
    \mathstackersparameter\c!left\relax
-   \ifmmode\mathrel\else\dontleavehmode\fi
+   \ifmmode\math_class_by_parameter\mathstackersparameter\else\dontleavehmode\fi
      {\edef\currentmathstackers{#category}%
       \edef\m_math_stackers_text_middle {#text}%
       %
@@ -436,12 +448,18 @@
       %
       \ifdim\htdp\scratchboxtwo>\zeropoint
         \kern-\scratchwidth
-        \ifcase#where\relax
+        \ifcase#top\else
           \math_stackers_top\bgroup
             \raise\dimexpr\scratchheight+\scratchtopoffset\relax
             \box\scratchboxtwo
           \egroup
-        \else
+        \fi
+        \scratchunicode#codeextra\relax
+        \ifcase\scratchunicode\else
+          \kern-\scratchwidth
+          \setbox\scratchboxtwo\math_stackers_content
+        \fi
+        \ifcase#bottom\else
           \math_stackers_bottom\bgroup
             \lower\dimexpr\scratchdepth+\ht\scratchboxtwo+\scratchbottomoffset\relax
             \box\scratchboxtwo
@@ -449,36 +467,53 @@
         \fi
       \fi}%
   \mathstackersparameter\c!right\relax
-  \endgroup}
+  \edef\p_limits{\mathstackersparameter\c!mathlimits}%
+  \ifx\p_limits\v!yes
+    \expandafter\endgroup\expandafter\limits
+  \else
+    \expandafter\endgroup
+  \fi}
 
-\unexpanded\def\definemathoverextensible {\dotripleempty\math_extensiblies_define_over }
-\unexpanded\def\definemathunderextensible{\dotripleempty\math_extensiblies_define_under}
+\unexpanded\def\definemathoverextensible  {\dotripleempty   \math_extensiblies_define_over }
+\unexpanded\def\definemathunderextensible {\dotripleempty   \math_extensiblies_define_under}
+\unexpanded\def\definemathdoubleextensible{\doquadrupleempty\math_extensiblies_define_double}
 
 \def\math_extensiblies_define_over[#1][#2][#3]%
   {\ifthirdargument
-     \setuevalue{#2}{\math_stackers_double\zerocount{#1}{\number#3}}%
+     \setuevalue{#2}{\math_stackers_make_double\plusone  \zerocount{#1}{\number#3}{0}}%
    \else
-     \setuevalue{#1}{\math_stackers_double\zerocount\noexpand\currentmathstackers{\number#2}}%
+     \setuevalue{#1}{\math_stackers_make_double\plusone  \zerocount\noexpand\currentmathstackers{\number#2}{0}}%
    \fi}
 
 \def\math_extensiblies_define_under[#1][#2][#3]%
   {\ifthirdargument
-     \setuevalue{#2}{\math_stackers_double\plusone{#1}{\number#3}}%
+     \setuevalue{#2}{\math_stackers_make_double\zerocount\plusone{#1}{\number#3}{0}}%
    \else
-     \setuevalue{#1}{\math_stackers_double\plusone\noexpand\currentmathstackers{\number#2}}%
+     \setuevalue{#1}{\math_stackers_make_double\zerocount\plusone\noexpand\currentmathstackers{\number#2}{0}}%
    \fi}
 
-\unexpanded\def\mathover {\begingroup\dosingleempty\math_stackers_handle_over }
-\unexpanded\def\mathunder{\begingroup\dosingleempty\math_stackers_handle_under}
+\def\math_extensiblies_define_double[#1][#2][#3][#4]%
+  {\ifthirdargument
+     \setuevalue{#2}{\math_stackers_make_double\plusone  \plusone{#1}{\number#3}{\number#4}}%
+   \else
+     \setuevalue{#1}{\math_stackers_make_double\plusone  \plusone\noexpand\currentmathstackers{\number#2}{\number#3}}%
+   \fi}
+
+\unexpanded\def\mathover  {\begingroup\dosingleempty\math_stackers_handle_over  }
+\unexpanded\def\mathunder {\begingroup\dosingleempty\math_stackers_handle_under }
+\unexpanded\def\mathdouble{\begingroup\dodoubleempty\math_stackers_handle_double}
 
 \def\math_stackers_handle_over[#category]%
-  {\math_stackers_handle_double\zerocount{\iffirstargument#category\else\v!top   \fi}} % will be defined later on
+  {\math_stackers_direct_double\plusone\zerocount{\iffirstargument#category\else\v!top   \fi}} % will be defined later on
 
 \def\math_stackers_handle_under[#category]#codepoint#bottomtext%
-  {\math_stackers_handle_double\plusone  {\iffirstargument#category\else\v!bottom\fi}} % will be defined later on
+  {\math_stackers_direct_double\zerocount\plusone{\iffirstargument#category\else\v!bottom\fi}} % will be defined later on
+
+\def\math_stackers_handle_double[#category]#codepoint#bottomtext%
+  {\math_stackers_direct_double\plusone\plusone  {\iffirstargument#category\else\v!bottom\fi}} % will be defined later on
 
-\def\math_stackers_handle_double#location#category#codepoint#text%
-  {\math_stackers_double#location{#category}{#codepoint}{#text}%
+\def\math_stackers_direct_double#top#bottom#category#codepoint#text%
+  {\math_stackers_make_double#top#bottom{#category}{#codepoint}{#text}%
    \endgroup}
 
 %D Here is a bonus macro that takes three texts. It can be used to get consistent
@@ -558,20 +593,26 @@
   [\c!order=\v!reverse]
 
 \definemathstackers
-  [\v!top]
+  [\v!both]
   [\v!mathematics]
-  [\c!location=\v!top,
+  [\c!location=\v!top, % ?
    \c!strut=\v!no,
    \c!middlecommand=\mathematics,
    \c!hoffset=\zeropoint]
 
+\definemathstackers
+  [\v!top]
+  [\v!both]
+
 \definemathstackers
   [\v!bottom]
-  [\v!mathematics]
-  [\c!location=\v!top, % ?
-   \c!strut=\v!no,
-   \c!middlecommand=\mathematics,
-   \c!hoffset=\zeropoint]
+  [\v!both]
+
+\definemathstackers
+  [vfenced]
+  [\v!both]
+  [\c!mathclass=\s!ord,
+   \c!mathlimits=\v!yes]
 
 % These are compatibity definitions, math only.
 
@@ -680,8 +721,23 @@
 % stuff in the backend (okay, we still need to deal with some cut and paste
 % issues but at least we now know what we deal with.
 
-\definemathunderextensible [\v!bottom] [underbar] ["203E]
-\definemathoverextensible  [\v!top]    [overbar]  ["203E]
+\definemathunderextensible  [vfenced] [underbar]      ["203E]
+\definemathoverextensible   [vfenced] [overbar]       ["203E]
+\definemathoverextensible   [vfenced] [doublebar]     ["203E]  ["203E]
+
+\definemathoverextensible   [vfenced] [overbrace]     ["FE3DE]          % ["023DE]
+\definemathunderextensible  [vfenced] [underbrace]    ["FE3DF]          % ["023DF]
+\definemathdoubleextensible [vfenced] [doublebrace]   ["FE3DE] ["FE3DF]
+
+\definemathoverextensible   [vfenced] [overparent]    ["FE3DC]          % ["023DC]
+\definemathunderextensible  [vfenced] [underparent]   ["FE3DD]          % ["023DD]
+\definemathdoubleextensible [vfenced] [doubleparent]  ["FE3DC] ["FE3DD]
+
+\definemathoverextensible   [vfenced] [overbracket]   ["FE3B4]          % ["023B4]
+\definemathunderextensible  [vfenced] [underbracket]  ["FE3B5]          % ["023B5]
+\definemathdoubleextensible [vfenced] [doublebracket] ["FE3B4] ["FE3B5]
+
+% \unexpanded\def\mathopwithlimits#1#2{\mathop{#1{#2}}\limits}
 
 %D Some bonus ones (for the moment here):
 
diff --git a/tex/context/base/meta-pdf.lua b/tex/context/base/meta-pdf.lua
index e51290e42..c49ad92ae 100644
--- a/tex/context/base/meta-pdf.lua
+++ b/tex/context/base/meta-pdf.lua
@@ -23,6 +23,8 @@ local report_mptopdf = logs.reporter("graphics","mptopdf")
 
 local mplib, metapost, lpdf, context = mplib, metapost, lpdf, context
 
+local texgetattribute           = tex.getattribute
+
 local pdfrgbcode                = lpdf.rgbcode
 local pdfcmykcode               = lpdf.cmykcode
 local pdfgraycode               = lpdf.graycode
@@ -84,7 +86,7 @@ end
 
 local function flushconcat()
     if m_stack_concat then
-        mpscode(f_concatm(unpack(m_stack_concat)))
+        mpscode(f_concat(unpack(m_stack_concat)))
         m_stack_concat = nil
     end
 end
@@ -539,7 +541,7 @@ function mptopdf.convertmpstopdf(name)
     resetall()
     local ok, m_data, n = resolvers.loadbinfile(name, 'tex') -- we need a binary load !
     if ok then
-        mps.colormodel = tex.attribute[a_colorspace]
+        mps.colormodel = texgetattribute(a_colorspace)
         statistics.starttiming(mptopdf)
         mptopdf.nofconverted = mptopdf.nofconverted + 1
         pdfcode(formatters["\\letterpercent\\space mptopdf begin: n=%s, file=%s"](mptopdf.nofconverted,file.basename(name)))
diff --git a/tex/context/base/meta-pdf.mkiv b/tex/context/base/meta-pdf.mkiv
index a8fdaff42..3469419d4 100644
--- a/tex/context/base/meta-pdf.mkiv
+++ b/tex/context/base/meta-pdf.mkiv
@@ -37,7 +37,7 @@
 
 \def\PDFMPformoffset{\ifdefined\objectoffset\objectoffset\else\zeropoint\fi} % obsolete, will go
 
-\def\convertMPtoPDF#1#2#3% scaling no longer supported at this level (so #2 & #3 are ignored)
+\unexpanded\def\convertMPtoPDF#1#2#3% scaling no longer supported at this level (so #2 & #3 are ignored)
   {\dostarttagged\t!mpgraphic\empty
    \naturalvbox attr \imageattribute 1 \bgroup
      \message{[MP to PDF]}%
diff --git a/tex/context/base/mlib-pps.lua b/tex/context/base/mlib-pps.lua
index 93bddc2dd..4a887150d 100644
--- a/tex/context/base/mlib-pps.lua
+++ b/tex/context/base/mlib-pps.lua
@@ -19,7 +19,8 @@ local formatters = string.formatters
 
 local mplib, metapost, lpdf, context = mplib, metapost, lpdf, context
 
-local texbox               = tex.box
+local texgetbox            = tex.getbox
+local texsetbox            = tex.setbox
 local copy_list            = node.copy_list
 local free_list            = node.flush_list
 local setmetatableindex    = table.setmetatableindex
@@ -227,8 +228,8 @@ local function freeboxes()
         local tn = textexts[n]
         if tn then
             free_list(tn)
-          -- texbox[scratchbox] = tn
-          -- texbox[scratchbox] = nil -- this frees too
+          -- texsetbox("scratchbox",tn)
+          -- texsetbox("scratchbox",nil) -- this frees too
             if trace_textexts then
                 report_textexts("freeing box %s",n)
             end
@@ -244,15 +245,15 @@ end
 metapost.resettextexts = freeboxes
 
 function metapost.settext(box,slot)
-    textexts[slot] = copy_list(texbox[box])
-    texbox[box] = nil
+    textexts[slot] = copy_list(texgetbox(box))
+    texsetbox(box,nil)
     -- this will become
-    -- textexts[slot] = texbox[box]
+    -- textexts[slot] = texgetbox(box)
     -- unsetbox(box)
 end
 
 function metapost.gettext(box,slot)
-    texbox[box] = copy_list(textexts[slot])
+    texsetbox(box,copy_list(textexts[slot]))
     if trace_textexts then
         report_textexts("putting text %s in box %s",slot,box)
     end
diff --git a/tex/context/base/mult-de.mkii b/tex/context/base/mult-de.mkii
index 893a9d358..60eccd7f7 100644
--- a/tex/context/base/mult-de.mkii
+++ b/tex/context/base/mult-de.mkii
@@ -801,6 +801,8 @@
 \setinterfaceconstant{marking}{beschriftung}
 \setinterfaceconstant{marstyle}{beschrstil}
 \setinterfaceconstant{mask}{mask}
+\setinterfaceconstant{mathclass}{mathclass}
+\setinterfaceconstant{mathlimits}{mathlimits}
 \setinterfaceconstant{mathstyle}{mathstyle}
 \setinterfaceconstant{max}{max}
 \setinterfaceconstant{maxdepth}{maxdepth}
diff --git a/tex/context/base/mult-def.lua b/tex/context/base/mult-def.lua
index fdb8803bf..10e99888e 100644
--- a/tex/context/base/mult-def.lua
+++ b/tex/context/base/mult-def.lua
@@ -10363,9 +10363,15 @@ return {
    ["pe"]="بست",
    ["ro"]="strut",
   },
+  ["mathclass"]={
+   ["en"]="mathclass",
+  },
   ["mathstyle"]={
    ["en"]="mathstyle",
   },
+  ["mathlimits"]={
+   ["en"]="mathlimits",
+  },
   ["style"]={
    ["cs"]="pismeno",
    ["de"]="stil",
diff --git a/tex/context/base/mult-def.mkiv b/tex/context/base/mult-def.mkiv
index cb165b055..6fff33f6a 100644
--- a/tex/context/base/mult-def.mkiv
+++ b/tex/context/base/mult-def.mkiv
@@ -88,6 +88,9 @@
 
 \def\s!current          {current}
 
+\def\s!rel              {rel}
+\def\s!ord              {ord}
+
 \def\c!HL               {HL}
 \def\c!VL               {VL}
 \def\c!NL               {NL}
diff --git a/tex/context/base/mult-en.mkii b/tex/context/base/mult-en.mkii
index c3ab2fc16..49076741c 100644
--- a/tex/context/base/mult-en.mkii
+++ b/tex/context/base/mult-en.mkii
@@ -801,6 +801,8 @@
 \setinterfaceconstant{marking}{marking}
 \setinterfaceconstant{marstyle}{marstyle}
 \setinterfaceconstant{mask}{mask}
+\setinterfaceconstant{mathclass}{mathclass}
+\setinterfaceconstant{mathlimits}{mathlimits}
 \setinterfaceconstant{mathstyle}{mathstyle}
 \setinterfaceconstant{max}{max}
 \setinterfaceconstant{maxdepth}{maxdepth}
diff --git a/tex/context/base/mult-fr.mkii b/tex/context/base/mult-fr.mkii
index 1ba4f3c8c..945c5f74f 100644
--- a/tex/context/base/mult-fr.mkii
+++ b/tex/context/base/mult-fr.mkii
@@ -801,6 +801,8 @@
 \setinterfaceconstant{marking}{marquage}
 \setinterfaceconstant{marstyle}{stylemarquage}
 \setinterfaceconstant{mask}{mask}
+\setinterfaceconstant{mathclass}{mathclass}
+\setinterfaceconstant{mathlimits}{mathlimits}
 \setinterfaceconstant{mathstyle}{mathstyle}
 \setinterfaceconstant{max}{max}
 \setinterfaceconstant{maxdepth}{maxdepth}
diff --git a/tex/context/base/mult-it.mkii b/tex/context/base/mult-it.mkii
index 0d1ea911d..20fe0be3f 100644
--- a/tex/context/base/mult-it.mkii
+++ b/tex/context/base/mult-it.mkii
@@ -801,6 +801,8 @@
 \setinterfaceconstant{marking}{marcatura}
 \setinterfaceconstant{marstyle}{stilemarcatura}
 \setinterfaceconstant{mask}{mask}
+\setinterfaceconstant{mathclass}{mathclass}
+\setinterfaceconstant{mathlimits}{mathlimits}
 \setinterfaceconstant{mathstyle}{mathstyle}
 \setinterfaceconstant{max}{max}
 \setinterfaceconstant{maxdepth}{maxdepth}
diff --git a/tex/context/base/mult-nl.mkii b/tex/context/base/mult-nl.mkii
index 5f1bada7a..11c25f4e9 100644
--- a/tex/context/base/mult-nl.mkii
+++ b/tex/context/base/mult-nl.mkii
@@ -801,6 +801,8 @@
 \setinterfaceconstant{marking}{markering}
 \setinterfaceconstant{marstyle}{marletter}
 \setinterfaceconstant{mask}{masker}
+\setinterfaceconstant{mathclass}{mathclass}
+\setinterfaceconstant{mathlimits}{mathlimits}
 \setinterfaceconstant{mathstyle}{mathstyle}
 \setinterfaceconstant{max}{max}
 \setinterfaceconstant{maxdepth}{maxdepth}
diff --git a/tex/context/base/mult-pe.mkii b/tex/context/base/mult-pe.mkii
index f55a7ab59..39ae60042 100644
--- a/tex/context/base/mult-pe.mkii
+++ b/tex/context/base/mult-pe.mkii
@@ -801,6 +801,8 @@
 \setinterfaceconstant{marking}{نشانه‌گذاری}
 \setinterfaceconstant{marstyle}{سبک‌حاش}
 \setinterfaceconstant{mask}{mask}
+\setinterfaceconstant{mathclass}{mathclass}
+\setinterfaceconstant{mathlimits}{mathlimits}
 \setinterfaceconstant{mathstyle}{mathstyle}
 \setinterfaceconstant{max}{بیشترین}
 \setinterfaceconstant{maxdepth}{maxdepth}
diff --git a/tex/context/base/mult-ro.mkii b/tex/context/base/mult-ro.mkii
index 34dd385a3..f61321e8b 100644
--- a/tex/context/base/mult-ro.mkii
+++ b/tex/context/base/mult-ro.mkii
@@ -801,6 +801,8 @@
 \setinterfaceconstant{marking}{marcaje}
 \setinterfaceconstant{marstyle}{stilmarcaj}
 \setinterfaceconstant{mask}{mask}
+\setinterfaceconstant{mathclass}{mathclass}
+\setinterfaceconstant{mathlimits}{mathlimits}
 \setinterfaceconstant{mathstyle}{mathstyle}
 \setinterfaceconstant{max}{max}
 \setinterfaceconstant{maxdepth}{maxdepth}
diff --git a/tex/context/base/mult-sys.mkiv b/tex/context/base/mult-sys.mkiv
index f0db9fa67..74e4d70ea 100644
--- a/tex/context/base/mult-sys.mkiv
+++ b/tex/context/base/mult-sys.mkiv
@@ -163,6 +163,9 @@
 
 \definesystemconstant {both}
 
+\definesystemconstant {internal}
+\definesystemconstant {external}
+
 \definesystemconstant {attribute}
 
 \definesystemconstant {none}
diff --git a/tex/context/base/node-acc.lua b/tex/context/base/node-acc.lua
index 4380ec3a4..81ae496b2 100644
--- a/tex/context/base/node-acc.lua
+++ b/tex/context/base/node-acc.lua
@@ -35,7 +35,7 @@ local function injectspaces(head)
     while n do
         local id = n.id
         if id == glue_code then -- todo: check for subtype related to spacing (13/14 but most seems to be 0)
---~ if n.spec.width > 0 then -- threshold
+       -- if n.spec.width > 0 then -- threshold
             if p and p.id == glyph_code then
                 local g = copy_node(p)
                 local c = g.components
@@ -56,7 +56,7 @@ local function injectspaces(head)
                 s[a_characters] = 0
                 n[a_characters] = 0
             end
---~ end
+       -- end
         elseif id == hlist_code or id == vlist_code then
             injectspaces(n.list,attribute)
      -- elseif id == kern_code then -- the backend already collapses
@@ -83,58 +83,58 @@ nodes.handlers.accessibility = injectspaces
 
 -- todo:
 
---~ local a_hyphenated = attributes.private('hyphenated')
---~
---~ local hyphenated, codes = { }, { }
---~
---~ local function compact(n)
---~     local t = { }
---~     for n in traverse_id(glyph_code,n) do
---~         t[#t+1] = utfchar(n.char) -- check for unicode
---~     end
---~     return concat(t,"")
---~ end
---~
---~ local function injectspans(head)
---~     for n in traverse_nodes(head) do
---~         local id = n.id
---~         if id == disc then
---~             local r, p = n.replace, n.pre
---~             if r and p then
---~                 local str = compact(r)
---~                 local hsh = hyphenated[str]
---~                 if not hsh then
---~                     hsh = #codes + 1
---~                     hyphenated[str] = hsh
---~                     codes[hsh] = str
---~                 end
---~                 n[a_hyphenated] = hsh
---~             end
---~         elseif id == hlist_code or id == vlist_code then
---~             injectspans(n.list)
---~         end
---~     end
---~     return head, true
---~ end
---~
---~ nodes.injectspans = injectspans
---~
---~ tasks.appendaction("processors", "words", "nodes.injectspans")
---~
---~ local function injectspans(head)
---~     for n in traverse_nodes(head) do
---~         local id = n.id
---~         if id == disc then
---~             local a = n[a_hyphenated]
---~             if a then
---~                 local str = codes[a]
---~                 local b = new_pdfliteral(format("/Span << /ActualText %s >> BDC", lpdf.tosixteen(str)))
---~                 local e = new_pdfliteral("EMC")
---~                 node.insert_before(head,n,b)
---~                 node.insert_after(head,n,e)
---~             end
---~         elseif id == hlist_code or id == vlist_code then
---~             injectspans(n.list)
---~         end
---~     end
---~ end
+-- local a_hyphenated = attributes.private('hyphenated')
+--
+-- local hyphenated, codes = { }, { }
+--
+-- local function compact(n)
+--     local t = { }
+--     for n in traverse_id(glyph_code,n) do
+--         t[#t+1] = utfchar(n.char) -- check for unicode
+--     end
+--     return concat(t,"")
+-- end
+--
+-- local function injectspans(head)
+--     for n in traverse_nodes(head) do
+--         local id = n.id
+--         if id == disc then
+--             local r, p = n.replace, n.pre
+--             if r and p then
+--                 local str = compact(r)
+--                 local hsh = hyphenated[str]
+--                 if not hsh then
+--                     hsh = #codes + 1
+--                     hyphenated[str] = hsh
+--                     codes[hsh] = str
+--                 end
+--                 n[a_hyphenated] = hsh
+--             end
+--         elseif id == hlist_code or id == vlist_code then
+--             injectspans(n.list)
+--         end
+--     end
+--     return head, true
+-- end
+--
+-- nodes.injectspans = injectspans
+--
+-- tasks.appendaction("processors", "words", "nodes.injectspans")
+--
+-- local function injectspans(head)
+--     for n in traverse_nodes(head) do
+--         local id = n.id
+--         if id == disc then
+--             local a = n[a_hyphenated]
+--             if a then
+--                 local str = codes[a]
+--                 local b = new_pdfliteral(format("/Span << /ActualText %s >> BDC", lpdf.tosixteen(str)))
+--                 local e = new_pdfliteral("EMC")
+--                 node.insert_before(head,n,b)
+--                 node.insert_after(head,n,e)
+--             end
+--         elseif id == hlist_code or id == vlist_code then
+--             injectspans(n.list)
+--         end
+--     end
+-- end
diff --git a/tex/context/base/node-aux.lua b/tex/context/base/node-aux.lua
index e3fc7ad6f..16e6d55a0 100644
--- a/tex/context/base/node-aux.lua
+++ b/tex/context/base/node-aux.lua
@@ -43,7 +43,7 @@ local unsetvalue         = attributes.unsetvalue
 
 local current_font       = font.current
 
-local texbox             = tex.box
+local texgetbox          = tex.getbox
 
 local report_error       = logs.reporter("node-aux:error")
 
@@ -195,7 +195,7 @@ function nodes.firstcharacter(n,untagged) -- tagged == subtype > 255
 end
 
 function nodes.firstcharinbox(n)
-    local l = texbox[n].list
+    local l = texgetbox(n).list
     if l then
         for g in traverse_id(glyph_code,l) do
             return g.char
@@ -369,21 +369,3 @@ local function locate(start,wantedid,wantedsubtype)
 end
 
 nodes.locate =  locate
-
-function nodes.concat(list)
-    local head, tail
-    for i=1,#list do
-        local li = list[i]
-        if not li then
-            -- skip
-        elseif head then
-            tail.next = li
-            li.prev = tail
-            tail = li.next and slide_nodes(li) or li
-        else
-            head = li
-            tail = li.next and slide_nodes(li) or li
-        end
-    end
-    return head, tail
-end
diff --git a/tex/context/base/node-fnt.lua b/tex/context/base/node-fnt.lua
index 54359117e..6d5ed6ca0 100644
--- a/tex/context/base/node-fnt.lua
+++ b/tex/context/base/node-fnt.lua
@@ -48,12 +48,31 @@ local run = 0
 local setfontdynamics = { }
 local fontprocesses   = { }
 
+-- setmetatableindex(setfontdynamics, function(t,font)
+--     local tfmdata = fontdata[font]
+--     local shared = tfmdata.shared
+--     local v = shared and shared.dynamics and otf.setdynamics or false
+--     t[font] = v
+--     return v
+-- end)
+
 setmetatableindex(setfontdynamics, function(t,font)
     local tfmdata = fontdata[font]
     local shared = tfmdata.shared
-    local v = shared and shared.dynamics and otf.setdynamics or false
-    t[font] = v
-    return v
+    local f = shared and shared.dynamics and otf.setdynamics or false
+    if f then
+        local v = { }
+        t[font] = v
+        setmetatableindex(v,function(t,k)
+            local v = f(font,k)
+            t[k] = v
+            return v
+        end)
+        return v
+    else
+        t[font] = false
+        return false
+    end
 end)
 
 setmetatableindex(fontprocesses, function(t,font)
@@ -75,8 +94,8 @@ fonts.hashes.processes   = fontprocesses
 function handlers.characters(head)
     -- either next or not, but definitely no already processed list
     starttiming(nodes)
-    local usedfonts, attrfonts, done = { }, { }, false
-    local a, u, prevfont, prevattr = 0, 0, nil, 0
+        local usedfonts, attrfonts = { }, { }
+        local a, u, prevfont, prevattr, done = 0, 0, nil, 0, false
     if trace_fontrun then
         run = run + 1
         report_fonts()
@@ -107,16 +126,8 @@ function handlers.characters(head)
                     attrfonts[font] = used
                 end
                 if not used[attr] then
-                    local sd = setfontdynamics[font]
-                    if sd then -- always true ?
-                        local d = sd(font,attr) -- can we cache this one?
-                        if d then
-                            used[attr] = d
-                            a = a + 1
-                        else
-                            -- can't happen ... otherwise best use nil/false distinction
-                        end
-                    end
+                    used[attr] = setfontdynamics[font][attr]
+                    a = a + 1
                 end
             else
                 local used = usedfonts[font]
@@ -125,9 +136,7 @@ function handlers.characters(head)
                     if fp then
                         usedfonts[font] = fp
                         u = u + 1
-                    else
-                        -- can't happen ... otherwise best use nil/false distinction
-                    end
+                   end
                 end
             end
             prevfont = font
@@ -141,34 +150,25 @@ function handlers.characters(head)
         report_fonts("dynamics: %s",(a > 0 and concat(keys(attrfonts)," ")) or "none")
         report_fonts()
     end
+    -- in context we always have at least 2 processors
     if u == 0 then
         -- skip
     elseif u == 1 then
         local font, processors = next(usedfonts)
-        local n = #processors
-        if n > 0 then
-            local h, d = processors[1](head,font,0)
-            head = h or head
-            done = done or d
-            if n > 1 then
-                for i=2,n do
-                    local h, d = processors[i](head,font,0)
-                    head = h or head
-                    done = done or d
-                end
+        for i=1,#processors do
+            local h, d = processors[i](head,font,0)
+            if d then
+                head = h or head
+                done = true
             end
         end
     else
         for font, processors in next, usedfonts do
-            local n = #processors
-            local h, d = processors[1](head,font,0)
-            head = h or head
-            done = done or d
-            if n > 1 then
-                for i=2,n do
-                    local h, d = processors[i](head,font,0)
+            for i=1,#processors do
+                local h, d = processors[i](head,font,0)
+                if d then
                     head = h or head
-                    done = done or d
+                    done = true
                 end
             end
         end
@@ -178,38 +178,22 @@ function handlers.characters(head)
     elseif a == 1 then
         local font, dynamics = next(attrfonts)
         for attribute, processors in next, dynamics do -- attr can switch in between
-            local n = #processors
-            if n == 0 then
-                report_fonts("no processors associated with dynamic %s",attribute)
-            else
-                local h, d = processors[1](head,font,attribute)
-                head = h or head
-                done = done or d
-                if n > 1 then
-                    for i=2,n do
-                        local h, d = processors[i](head,font,attribute)
-                        head = h or head
-                        done = done or d
-                    end
+            for i=1,#processors do
+                local h, d = processors[i](head,font,attribute)
+                if d then
+                    head = h or head
+                    done = true
                 end
             end
         end
     else
         for font, dynamics in next, attrfonts do
             for attribute, processors in next, dynamics do -- attr can switch in between
-                local n = #processors
-                if n == 0 then
-                    report_fonts("no processors associated with dynamic %s",attribute)
-                else
-                    local h, d = processors[1](head,font,attribute)
-                    head = h or head
-                    done = done or d
-                    if n > 1 then
-                        for i=2,n do
-                            local h, d = processors[i](head,font,attribute)
-                            head = h or head
-                            done = done or d
-                        end
+                for i=1,#processors do
+                    local h, d = processors[i](head,font,attribute)
+                    if d then
+                        head = h or head
+                        done = true
                     end
                 end
             end
@@ -222,5 +206,178 @@ function handlers.characters(head)
     return head, true
 end
 
+
+--     local formatters = string.formatters
+
+--     local function make(processors,font,attribute)
+--         _G.__temp = processors
+--         local t = { }
+--         for i=1,#processors do
+--             if processors[i] then
+--                 t[#t+1] = formatters["local p_%s = _G.__temp[%s]"](i,i)
+--             end
+--         end
+--         t[#t+1] = "return function(head,done)"
+--         if #processors == 1 then
+--             t[#t+1] = formatters["return p_%s(head,%s,%s)"](1,font,attribute or 0)
+--         else
+--             for i=1,#processors do
+--                 if processors[i] then
+--                     t[#t+1] = formatters["local h,d=p_%s(head,%s,%s) if d then head=h or head done=true end"](i,font,attribute or 0)
+--                 end
+--             end
+--             t[#t+1] = "return head, done"
+--         end
+--         t[#t+1] = "end"
+--         t = concat(t,"\n")
+--         t = load(t)(processors)
+--         _G.__temp = nil
+--         return t
+--     end
+
+--     setmetatableindex(fontprocesses, function(t,font)
+--         local tfmdata = fontdata[font]
+--         local shared = tfmdata.shared -- we need to check shared, only when same features
+--         local processes = shared and shared.processes
+--         if processes and #processes > 0 then
+--             processes = make(processes,font,0)
+--             t[font] = processes
+--             return processes
+--         else
+--             t[font] = false
+--             return false
+--         end
+--     end)
+
+--     setmetatableindex(setfontdynamics, function(t,font)
+--         local tfmdata = fontdata[font]
+--         local shared = tfmdata.shared
+--         local f = shared and shared.dynamics and otf.setdynamics or false
+--         if f then
+--             local v = { }
+--             t[font] = v
+--             setmetatableindex(v,function(t,k)
+--                 local v = f(font,k)
+--                 v = make(v,font,k)
+--                 t[k] = v
+--                 return v
+--             end)
+--             return v
+--         else
+--             t[font] = false
+--             return false
+--         end
+--     end)
+
+--     function handlers.characters(head)
+--         -- either next or not, but definitely no already processed list
+--         starttiming(nodes)
+--         local usedfonts, attrfonts
+--         local a, u, prevfont, prevattr, done = 0, 0, nil, 0, false
+--         if trace_fontrun then
+--             run = run + 1
+--             report_fonts()
+--             report_fonts("checking node list, run %s",run)
+--             report_fonts()
+--             local n = head
+--             while n do
+--                 local id = n.id
+--                 if id == glyph_code then
+--                     local font = n.font
+--                     local attr = n[0] or 0
+--                     report_fonts("font %03i, dynamic %03i, glyph %s",font,attr,utf.char(n.char))
+--                 else
+--                     report_fonts("[%s]",nodecodes[n.id])
+--                 end
+--                 n = n.next
+--             end
+--         end
+--         for n in traverse_id(glyph_code,head) do
+--          -- if n.subtype<256 then -- all are 1
+--             local font = n.font
+--             local attr = n[0] or 0 -- zero attribute is reserved for fonts in context
+--             if font ~= prevfont or attr ~= prevattr then
+--                 if attr > 0 then
+--                     if not attrfonts then
+--                         attrfonts = {
+--                             [font] = {
+--                                 [attr] = setfontdynamics[font][attr]
+--                             }
+--                         }
+--                         a = 1
+--                     else
+--                         local used = attrfonts[font]
+--                         if not used then
+--                             attrfonts[font] = {
+--                                 [attr] = setfontdynamics[font][attr]
+--                             }
+--                             a = a + 1
+--                         elseif not used[attr] then
+--                             used[attr] = setfontdynamics[font][attr]
+--                             a = a + 1
+--                         end
+--                     end
+--                 else
+--                     if not usedfonts then
+--                         local fp = fontprocesses[font]
+--                         if fp then
+--                             usedfonts = {
+--                                 [font] = fp
+--                             }
+--                             u = 1
+--                         end
+--                     else
+--                         local used = usedfonts[font]
+--                         if not used then
+--                             local fp = fontprocesses[font]
+--                             if fp then
+--                                 usedfonts[font] = fp
+--                                 u = u + 1
+--                             end
+--                         end
+--                     end
+--                 end
+--                 prevfont = font
+--                 prevattr = attr
+--             end
+--         -- end
+--         end
+--         if trace_fontrun then
+--             report_fonts()
+--             report_fonts("statics : %s",(u > 0 and concat(keys(usedfonts)," ")) or "none")
+--             report_fonts("dynamics: %s",(a > 0 and concat(keys(attrfonts)," ")) or "none")
+--             report_fonts()
+--         end
+--         if not usedfonts then
+--             -- skip
+--         elseif u == 1 then
+--             local font, processors = next(usedfonts)
+--             head, done = processors(head,done)
+--         else
+--             for font, processors in next, usedfonts do
+--                 head, done = processors(head,done)
+--             end
+--         end
+--         if not attrfonts then
+--             -- skip
+--         elseif a == 1 then
+--             local font, dynamics = next(attrfonts)
+--             for attribute, processors in next, dynamics do
+--                 head, done = processors(head,done)
+--             end
+--         else
+--             for font, dynamics in next, attrfonts do
+--                 for attribute, processors in next, dynamics do
+--                     head, done = processors(head,done)
+--                 end
+--             end
+--         end
+--         stoptiming(nodes)
+--         if trace_characters then
+--             nodes.report(head,done)
+--         end
+--         return head, true
+--     end
+
 handlers.protectglyphs   = node.protect_glyphs
 handlers.unprotectglyphs = node.unprotect_glyphs
diff --git a/tex/context/base/node-ini.lua b/tex/context/base/node-ini.lua
index 5a3986c3a..ae50215cb 100644
--- a/tex/context/base/node-ini.lua
+++ b/tex/context/base/node-ini.lua
@@ -13,13 +13,10 @@ modules.</p>
 
 -- this module is being reconstructed
 
-local next, type = next, type
-local format, match, gsub = string.format, string.match, string.gsub
+local next, type, tostring = next, type, tostring
+local gsub = string.gsub
 local concat, remove = table.concat, table.remove
-local sortedhash, sortedkeys, swapped, tohash = table.sortedhash, table.sortedkeys, table.swapped, table.tohash
-local utfchar = utf.char
-local lpegmatch = lpeg.match
-local formatcolumns = utilities.formatters.formatcolumns
+local sortedhash, sortedkeys, swapped = table.sortedhash, table.sortedkeys, table.swapped
 
 --[[ldx--
 <p>Access to nodes is what gives <l n='luatex'/> its power. Here we
@@ -54,20 +51,12 @@ into the <l n='tex'/> engine, but this is a not so natural extension.</p>
 also ignore the empty nodes. [This is obsolete!]</p>
 --ldx]]--
 
-local traverse           = node.traverse
-local traverse_id        = node.traverse_id
-local free_node          = node.free
-local remove_node        = node.remove
-local insert_node_before = node.insert_before
-local insert_node_after  = node.insert_after
-local node_fields        = node.fields
-
-local allocate = utilities.storage.allocate
+nodes               = nodes or { }
+local nodes         = nodes
+nodes.handlers      = nodes.handlers or { }
 
-nodes          = nodes or { }
-local nodes    = nodes
-
-nodes.handlers = nodes.handlers or { }
+local allocate      = utilities.storage.allocate
+local formatcolumns = utilities.formatters.formatcolumns
 
 -- there will be more of this:
 
@@ -248,174 +237,4 @@ function nodes.showcodes()
     end
 end
 
-local whatsit_node = nodecodes.whatsit
-
-local messyhack    = tohash { -- temporary solution
-    nodecodes.attributelist,
-    nodecodes.attribute,
-    nodecodes.gluespec,
-    nodecodes.action,
-}
-
-function nodes.fields(n)
-    local id = n.id
-    if id == whatsit_node then
-        return node_fields(id,n.subtype)
-    else
-        local t = node_fields(id)
-        if messyhack[id] then
-            for i=1,#t do
-                if t[i] == "subtype" then
-                    remove(t,i)
-                    break
-                end
-            end
-        end
-        return t
-    end
-end
-
 trackers.register("system.showcodes", nodes.showcodes)
-
-local hlist_code = nodecodes.hlist
-local vlist_code = nodecodes.vlist
-local glue_code  = nodecodes.glue
-
--- if t.id == glue_code then
---     local s = t.spec
--- print(t)
--- print(s,s and s.writable)
---     if s and s.writable then
---         free_node(s)
---     end
---     t.spec = nil
--- end
-
-local function remove(head, current, free_too)
-   local t = current
-   head, current = remove_node(head,current)
-   if t then
-        if free_too then
-            free_node(t)
-            t = nil
-        else
-            t.next = nil
-            t.prev = nil
-        end
-   end
-   return head, current, t
-end
-
-nodes.remove = remove
-
-function nodes.delete(head,current)
-    return remove(head,current,true)
-end
-
-nodes.before = insert_node_before
-nodes.after  = insert_node_after
-
--- we need to test this, as it might be fixed now
-
-function nodes.before(h,c,n)
-    if c then
-        if c == h then
-            n.next = h
-            n.prev = nil
-            h.prev = n
-        else
-            local cp = c.prev
-            n.next = c
-            n.prev = cp
-            if cp then
-                cp.next = n
-            end
-            c.prev = n
-            return h, n
-        end
-    end
-    return n, n
-end
-
-function nodes.after(h,c,n)
-    if c then
-        local cn = c.next
-        if cn then
-            n.next = cn
-            cn.prev = n
-        else
-            n.next = nil
-        end
-        c.next = n
-        n.prev = c
-        return h, n
-    end
-    return n, n
-end
-
--- local h, c = nodes.replace(head,current,new)
--- local c = nodes.replace(false,current,new)
--- local c = nodes.replace(current,new)
-
-function nodes.replace(head,current,new) -- no head returned if false
-    if not new then
-        head, current, new = false, head, current
-    end
-    local prev, next = current.prev, current.next
-    if next then
-        new.next = next
-        next.prev = new
-    end
-    if prev then
-        new.prev = prev
-        prev.next = new
-    end
-    if head then
-        if head == current then
-            head = new
-        end
-        free_node(current)
-        return head, new
-    else
-        free_node(current)
-        return new
-    end
-end
-
--- will move
-
-local function count(stack,flat)
-    local n = 0
-    while stack do
-        local id = stack.id
-        if not flat and id == hlist_code or id == vlist_code then
-            local list = stack.list
-            if list then
-                n = n + 1 + count(list) -- self counts too
-            else
-                n = n + 1
-            end
-        else
-            n = n + 1
-        end
-        stack  = stack.next
-    end
-    return n
-end
-
-nodes.count = count
-
-local left, space = lpeg.P("<"), lpeg.P(" ")
-
-local reference = left * (1-left)^0 * left * space^0 * lpeg.C((1-space)^0)
-
-function nodes.reference(n)
-    return lpegmatch(reference,tostring(n))
-end
-
-if not node.next then
-
-    function node.next(n) return n and n.next end
-    function node.prev(n) return n and n.prev end
-
-end
diff --git a/tex/context/base/node-ini.mkiv b/tex/context/base/node-ini.mkiv
index 39d48a00a..e99653327 100644
--- a/tex/context/base/node-ini.mkiv
+++ b/tex/context/base/node-ini.mkiv
@@ -18,6 +18,10 @@
 \newcount\filterstate \filterstate\plusone % hm, public
 
 \registerctxluafile{node-ini}{1.001}
+\registerctxluafile{node-met}{1.001}
+
+\ctxlua{if nodes.gonuts then context.registerctxluafile("node-nut","1.001") end}
+
 \registerctxluafile{node-res}{1.001}
 \registerctxluafile{node-dir}{1.001}
 \registerctxluafile{node-aux}{1.001}
diff --git a/tex/context/base/node-inj.lua b/tex/context/base/node-inj.lua
index 697370cfb..abbe9b449 100644
--- a/tex/context/base/node-inj.lua
+++ b/tex/context/base/node-inj.lua
@@ -11,6 +11,8 @@ if not modules then modules = { } end modules ['node-inj'] = {
 -- test fonts. Btw, future versions of luatex will have extended glyph properties
 -- that can be of help. Some optimizations can go away when we have faster machines.
 
+-- todo: make a special one for context
+
 local next = next
 local utfchar = utf.char
 
diff --git a/tex/context/base/node-ltp.lua b/tex/context/base/node-ltp.lua
new file mode 100644
index 000000000..97e61cf18
--- /dev/null
+++ b/tex/context/base/node-ltp.lua
@@ -0,0 +1,3207 @@
+if not modules then modules = { } end modules ['node-par'] = {
+    version   = 1.001,
+    comment   = "companion to node-par.mkiv",
+    author    = "Hans Hagen",
+    copyright = "ConTeXt Development Team",
+    license   = "see context related readme files",
+    comment   = "a translation of the built in parbuilder, initial convertsin by Taco Hoekwater",
+}
+
+-- todo: remove nest_stack from  linebreak.w
+-- todo: use ex field as signal (index in ?)
+-- todo: attr driven unknown/on/off
+-- todo: permit global steps i.e. using an attribute that sets min/max/step and overloads the font parameters
+-- todo: split the three passes into three functions
+-- todo: simplify the direction stack, no copy needed
+-- todo: add more mkiv like tracing
+-- todo: add a couple of plugin hooks
+-- todo: maybe split expansion code paths
+-- todo: fix line numbers (cur_list.pg_field needed)
+-- todo: make kerns stretch an option and disable it by default (definitely not shrink)
+-- todo: check and improve protrusion
+-- todo: arabic etc (we could use pretty large scales there) .. marks and cursive
+
+--[[
+
+    This code is derived from traditional TeX and has bits of pdfTeX, Aleph (Omega), and of course LuaTeX. So,
+    the basic algorithm for sure is not our work. On the other hand, the directional model in LuaTeX is cleaned
+    up as is other code. And of course there are hooks for callbacks.
+
+    The first version of the code below was a conversion of the C code that in turn was a conversion from the
+    original Pascal code. Around September 2008 we experimented with cq. discussed possible approaches to improved
+    typesetting of Arabic and as our policy is that extensions happen in Lua this means that we need a parbuilder
+    in Lua. Taco's first conversion still looked quite C-ish and in the process of cleaning up we uncovered some odd
+    bits and pieces in the original code as well. I did some first cleanup to get rid of C-artefacts, and Taco and I
+    spent the usual amount of Skyping to sort out problems. At that point we diverted to other LuaTeX issues.
+
+    A while later I decided to pick up this thread and decided to look into better ways to deal with font expansion
+    (aka hz). I got it running using a simpler method. One reason why the built-in mechanims is slow is that there is
+    lots of redudancy in calculations. Expanded widths are recalculated each time and because the hpakc routine does
+    it again that gives some overhead. In the process extra fonts are created with different dimensions so that the
+    backend can deal with it. The alternative method doesn't create fonts but passes an expansion factor to the
+    pdf generator. The small patch needed for the backend code worked more or less okay but was never intergated into
+    LuaTeX due to lack of time.
+
+    This all happened in 2010 while listening to Peter Gabriels "Scratch My Back" and Camels "Rayaz" so it was a
+    rather relaxed job.
+
+    In 2012 I picked up this thread. Because both languages are similar but also quite different it took some time
+    to get compatible output. Because the C code uses macros, careful checking was needed. Of course Lua's table model
+    and local variables brought some work as well. And still the code looks a bit C-ish. We could not divert too much
+    from the original model simply because it's well documented but future versions (or variants) might as well look
+    different.
+
+    Eventually I'll split this code into passes so that we can better see what happens, but first we need to reach
+    a decent level of stability. The current expansion results are not the same as the built-in but that was never
+    the objective. It all has to do with slightly different calculations.
+
+    The original C-code related to protrusion and expansion is not that efficient as many (redundant) function
+    calls take place in the linebreaker and packer. As most work related to fonts is done in the backend, we
+    can simply stick to width calculations here. Also, it is no problem at all that we use floating point
+    calculations (as Lua has only floats). The final result will look ok as the hpack will nicely compensate
+    for rounding errors as it will normally distribute the content well enough. And let's admit: most texies
+    won't see it anyway. As long as we're cross platform compatible it's fine.
+
+    We use the table checked_expansion to keep track of font related parameters (per paragraph). The table is
+    also the signal that we have adjustments > 1. In retrospect one might wonder if adjusting kerns is such a
+    good idea because other spacing is also not treated. If we would stick to the regular hpack routine
+    we do have to follow the same logic, but I decided to use a Lua hpacker so that constraint went away. And
+    anyway, instead of doing a lookup in the kern table (that we don't have in node mode) the set kern value
+    is used. Disabling kern scaling will become an option in Luatex some day. You can blame me for all errors
+    that crept in and I know that there are some.
+
+    To be honest, I slowly start to grasp the magic here as normally I start from scratch when implementing
+    something (as it's the only way I can understand things). This time I had a recently acquired stack of
+    Porcupine Tree disks to get me through.
+
+    Picking up this effort was inspired by discussions between Luigi Scarso and me about efficiency of Lua
+    code and we needed some stress tests to compare regular LuaTeX and LuajitTeX. One of the tests was
+    processing tufte.tex as that one has lots of hyphenations and is a tough one to get right.
+
+    tufte: boxed 1000 times, no flushing in backend:
+
+                           \testfeatureonce{1000}{\setbox0\hbox{\tufte}}
+                           \testfeatureonce{1000}{\setbox0\vbox{\tufte}}
+    \startparbuilder[basic]\testfeatureonce{1000}{\setbox0\vbox{\tufte}}\stopparbuilder
+
+                method     normal   hz      comment
+
+    luatex      tex hbox    9.64     9.64   baseline font feature processing, hyphenation etc: 9.74
+                tex vbox    9.84    10.16   0.20 linebreak / 0.52 with hz -> 0.32 hz overhead (150pct more)
+                lua vbox   17.28    18.43   7.64 linebreak / 8.79 with hz -> 1.33 hz overhead ( 20pct more)
+
+    luajittex   tex hbox    6.33     6.33   baseline font feature processing, hyphenation etc: 6.33
+                tex vbox    6.53     6.81   0.20 linebreak / 0.48 with hz -> 0.28 hz overhead (expected 0.32)
+                lua vbox   11.06    11.81   4.53 linebreak / 5.28 with hz -> 0.75 hz overhead
+
+    Interesting is that the runtime for the built-in parbuilder indeed increases much when expansion
+    is enabled, but in the Lua variant the extra overhead is way less significant. This means that when we
+    retrofit the same approach into the core, the overhead of expansion can be sort of nilled.
+
+]]--
+
+local utfchar = utf.char
+local write, write_nl = texio.write, texio.write_nl
+local sub, format = string.sub, string.format
+local round = math.round
+local insert, remove = table.insert, table.remove
+
+local fonts, nodes, node = fonts, nodes, node
+
+local trace_basic         = false  trackers.register("builders.paragraphs.basic",       function(v) trace_basic       = v end)
+local trace_lastlinefit   = false  trackers.register("builders.paragraphs.lastlinefit", function(v) trace_lastlinefit = v end)
+local trace_adjusting     = false  trackers.register("builders.paragraphs.adjusting",   function(v) trace_adjusting   = v end)
+local trace_protruding    = false  trackers.register("builders.paragraphs.protruding",  function(v) trace_protruding  = v end)
+local trace_expansion     = false  trackers.register("builders.paragraphs.expansion",   function(v) trace_expansion   = v end)
+local trace_quality       = false  trackers.register("builders.paragraphs.quality",     function(v) trace_quality     = v end)
+
+local report_parbuilders  = logs.reporter("nodes","parbuilders")
+local report_hpackers     = logs.reporter("nodes","hpackers")
+
+local calculate_badness   = tex.badness
+local texnest             = tex.nest
+local texlists            = tex.lists
+
+-- (t == 0 and 0) or (s <= 0 and 10000) or calculate_badness(t,s)
+
+-- local function calculate_badness(t,s)
+--     if t == 0 then
+--         return 0
+--     elseif s <= 0 then
+--         return 10000 -- infinite_badness
+--     else
+--         local r
+--         if t <= 7230584 then
+--             r = (t * 297) / s
+--         elseif s >= 1663497 then
+--             r = t / (s / 297)
+--         else
+--             r = t
+--         end
+--         if r > 1290 then
+--             return 10000 -- infinite_badness
+--         else
+--             return (r * r * r + 0x20000) / 0x40000
+--         end
+--     end
+-- end
+
+local parbuilders          = builders.paragraphs
+local constructors         = parbuilders.constructors
+
+local setmetatableindex    = table.setmetatableindex
+
+local fonthashes           = fonts.hashes
+local fontdata             = fonthashes.identifiers
+local chardata             = fonthashes.characters
+local quaddata             = fonthashes.quads
+local parameters           = fonthashes.parameters
+
+local slide_nodes          = node.slide
+local new_node             = node.new
+local copy_node            = node.copy
+local copy_node_list       = node.copy_list
+local flush_node           = node.free
+local flush_node_list      = node.flush_list
+local hpack_nodes          = node.hpack
+local xpack_nodes          = node.hpack
+local replace_node         = nodes.replace
+local insert_node_after    = node.insert_after
+local insert_node_before   = node.insert_before
+local traverse_by_id       = node.traverse_id
+
+local setnodecolor         = nodes.tracers.colors.set
+
+local nodepool             = nodes.pool
+
+local nodecodes            = nodes.nodecodes
+local whatcodes            = nodes.whatcodes
+local kerncodes            = nodes.kerncodes
+local glyphcodes           = nodes.glyphcodes
+local gluecodes            = nodes.gluecodes
+local margincodes          = nodes.margincodes
+local disccodes            = nodes.disccodes
+local mathcodes            = nodes.mathcodes
+local fillcodes            = nodes.fillcodes
+
+local temp_code            = nodecodes.temp
+local glyph_code           = nodecodes.glyph
+local ins_code             = nodecodes.ins
+local mark_code            = nodecodes.mark
+local adjust_code          = nodecodes.adjust
+local penalty_code         = nodecodes.penalty
+local whatsit_code         = nodecodes.whatsit
+local disc_code            = nodecodes.disc
+local math_code            = nodecodes.math
+local kern_code            = nodecodes.kern
+local glue_code            = nodecodes.glue
+local hlist_code           = nodecodes.hlist
+local vlist_code           = nodecodes.vlist
+local unset_code           = nodecodes.unset
+local marginkern_code      = nodecodes.marginkern
+
+local leaders_code         = gluecodes.leaders
+
+local localpar_code        = whatcodes.localpar
+local dir_code             = whatcodes.dir
+local pdfrefximage_code    = whatcodes.pdfrefximage
+local pdfrefxform_code     = whatcodes.pdfrefxform
+
+local kerning_code         = kerncodes.kerning -- font kern
+local userkern_code        = kerncodes.userkern
+
+local ligature_code        = glyphcodes.ligature
+
+local stretch_orders       = nodes.fillcodes
+
+local leftmargin_code      = margincodes.left
+local rightmargin_code     = margincodes.right
+
+local automatic_disc_code  = disccodes.automatic
+local regular_disc_code    = disccodes.regular
+local first_disc_code      = disccodes.first
+local second_disc_code     = disccodes.second
+
+local endmath_code         = mathcodes.endmath
+
+local nosubtype_code       = 0
+
+local unhyphenated_code    = nodecodes.unhyphenated or 1
+local hyphenated_code      = nodecodes.hyphenated   or 2
+local delta_code           = nodecodes.delta        or 3
+local passive_code         = nodecodes.passive      or 4
+
+local maxdimen             = number.maxdimen
+
+local max_halfword         = 0x7FFFFFFF
+local infinite_penalty     =  10000
+local eject_penalty        = -10000
+local infinite_badness     =  10000
+local awful_badness        = 0x3FFFFFFF
+
+local fit_very_loose_class = 0  -- fitness for lines stretching more than their stretchability
+local fit_loose_class      = 1  -- fitness for lines stretching 0.5 to 1.0 of their stretchability
+local fit_decent_class     = 2  -- fitness for all other lines
+local fit_tight_class      = 3  -- fitness for lines shrinking 0.5 to 1.0 of their shrinkability
+
+local new_penalty          = nodepool.penalty
+local new_dir              = nodepool.textdir
+local new_leftmarginkern   = nodepool.leftmarginkern
+local new_rightmarginkern  = nodepool.rightmarginkern
+local new_leftskip         = nodepool.leftskip
+local new_rightskip        = nodepool.rightskip
+local new_lineskip         = nodepool.lineskip
+local new_baselineskip     = nodepool.baselineskip
+local new_temp             = nodepool.temp
+local new_rule             = nodepool.rule
+
+local is_rotated           = nodes.is_rotated
+local is_parallel          = nodes.textdir_is_parallel
+local is_opposite          = nodes.textdir_is_opposite
+local textdir_is_equal     = nodes.textdir_is_equal
+local pardir_is_equal      = nodes.pardir_is_equal
+local glyphdir_is_equal    = nodes.glyphdir_is_equal
+
+local dir_pops             = nodes.dir_is_pop
+local dir_negations        = nodes.dir_negation
+local is_skipable          = node.protrusion_skippable
+
+-- helpers --
+
+-- It makes more sense to move the somewhat messy dir state tracking
+-- out of the main functions. First we create a stack allocator.
+
+local function new_dir_stack(dir) -- also use elsewhere
+    return { n = 0, dir }
+end
+
+-- The next function checks a dir node and returns the new dir state. By
+-- using s static table we are quite efficient. This function is used
+-- in the parbuilder.
+
+local function checked_line_dir(stack,current)
+    if not dir_pops[current] then
+        local n = stack.n + 1
+        stack.n = n
+        stack[n] = current
+        return current.dir
+    elseif n > 0 then
+        local n = stack.n
+        local dirnode = stack[n]
+        dirstack.n = n - 1
+        return dirnode.dir
+    else
+        report_parbuilders("warning: missing pop node (%a)",1) -- in line ...
+    end
+end
+
+-- The next function checks a dir nodes in a list and appends the negations
+-- that are currently needed (some day LuaTeX will be more tolerant). We use
+-- the negations for the next line.
+
+local function inject_dirs_at_end_of_line(stack,current,start,stop)
+    local e = start
+    local n = stack.n
+    local h = nil
+    while start and start ~= stop do
+        if start.id == whatsit_code and start.subtype == dir_code then
+            if not dir_pops[start.dir] then
+                n = n + 1
+                stack[n] = start
+            elseif n > 0 then
+                n = n - 1
+            else
+                report_parbuilders("warning: missing pop node (%a)",2) -- in line ...
+            end
+        end
+        start = start.next
+    end
+    for i=n,1,-1 do
+        h, current = insert_node_after(current,current,new_dir(dir_negations[stack[i].dir]))
+    end
+    stack.n = n
+    return current
+end
+
+local function inject_dirs_at_begin_of_line(stack,current)
+    local h = nil
+    for i=stack.n,1,-1 do
+        h, current = insert_node_after(current,current,new_dir(stack[i]))
+    end
+    stack.n = 0
+    return current
+end
+
+-- diagnostics --
+
+local dummy = function() end
+
+local diagnostics = {
+    start          = dummy,
+    stop           = dummy,
+    current_pass   = dummy,
+    break_node     = dummy,
+    feasible_break = dummy,
+}
+
+-- statistics --
+
+local nofpars, noflines, nofprotrudedlines, nofadjustedlines = 0, 0, 0, 0
+
+local function register_statistics(par)
+    local statistics  = par.statistics
+    nofpars           = nofpars           + 1
+    noflines          = noflines          + statistics.noflines
+    nofprotrudedlines = nofprotrudedlines + statistics.nofprotrudedlines
+    nofadjustedlines  = nofadjustedlines  + statistics.nofadjustedlines
+end
+
+-- resolvers --
+
+local whatsiters = {
+    get_width      = { },
+    get_dimensions = { },
+}
+
+local get_whatsit_width      = whatsiters.get_width
+local get_whatsit_dimensions = whatsiters.get_dimensions
+
+local function get_width     (n) return n.width end
+local function get_dimensions(n) return n.width, n.height, n.depth end
+
+get_whatsit_width[pdfrefximage_code] = get_width
+get_whatsit_width[pdfrefxform_code ] = get_width
+
+get_whatsit_dimensions[pdfrefximage_code] = get_dimensions
+get_whatsit_dimensions[pdfrefxform_code ] = get_dimensions
+
+-- expansion etc --
+
+local function calculate_fraction(x,n,d,max_answer)
+    local the_answer = x * n/d + 1/2 -- round ?
+    if the_answer > max_answer then
+        return  max_answer
+    elseif the_answer < -max_answer then
+        return -max_answer
+    else
+        return  the_answer
+    end
+end
+
+local function check_shrinkage(par,n)
+    -- called often, so maybe move inline
+    if n.shrink_order ~= 0 and n.shrink ~= 0 then
+        if par.no_shrink_error_yet then
+            par.no_shrink_error_yet = false
+            report_parbuilders("infinite glue shrinkage found in a paragraph and removed")
+        end
+        n = copy_node(n)
+        n.shrink_order = 0
+    end
+    return n
+end
+
+-- It doesn't really speed up much but the additional memory usage is
+-- rather small so it doesn't hurt too much.
+
+local expansions = { }
+local nothing    = { stretch = 0, shrink = 0 }
+
+setmetatableindex(expansions,function(t,font)
+    local expansion = parameters[font].expansion -- can be an extra hash
+    if expansion and expansion.auto then
+        local factors = { }
+        local c = chardata[font]
+        setmetatableindex(factors,function(t,char)
+            local fc = c[char]
+            local ef = fc.expansion_factor
+            if ef and ef > 0 then
+                local stretch = expansion.stretch
+                local shrink  = expansion.shrink
+                if stretch ~= 0 or shrink ~= 0 then
+                    local factor  = ef / 1000
+                    local ef_quad = factor * quaddata[font] / 1000
+                    local v = {
+                        glyphstretch = stretch * ef_quad,
+                        glyphshrink  = shrink  * ef_quad,
+                        factor       = factor,
+                        stretch      = stretch,
+                        shrink       = shrink ,
+                    }
+                    t[char] = v
+                    return v
+                end
+            end
+            t[char] = nothing
+            return nothing
+        end)
+        t[font] = factors
+        return factors
+    else
+        t[font] = false
+        return false
+    end
+end)
+
+-- local function char_stretch_shrink(p)
+--     local data = expansions[p.font][p.char]
+--     if data then
+--         return data.glyphstretch, data.glyphshrink
+--     else
+--         return 0, 0
+--     end
+-- end
+--
+-- local cal_margin_kern_var = char_stretch_shrink
+
+-- local function kern_stretch_shrink(p,d)
+--     local l = p.prev
+--     if l and l.id == glyph_code then -- how about disc nodes?
+--         local r = p.next
+--         if r and r.id == glyph_code then
+--             local lf, rf = l.font, r.font
+--             if lf == rf then
+--                 local data = expansions[lf][l.char]
+--                 if data then
+--                     local stretch = data.stretch
+--                     local shrink  = data.shrink
+--                     if stretch ~= 0 then
+--                      -- stretch = data.factor * (d *  stretch - d)
+--                         stretch = data.factor *  d * (stretch - 1)
+--                     end
+--                     if shrink ~= 0 then
+--                      -- shrink = data.factor * (d *  shrink - d)
+--                         shrink = data.factor *  d * (shrink - 1)
+--                     end
+--                     return stretch, shrink
+--                 end
+--             end
+--         end
+--     end
+--     return 0, 0
+-- end
+
+local function kern_stretch_shrink(p,d)
+    local left = p.prev
+    if left and left.id == glyph_code then -- how about disc nodes?
+        local data = expansions[left.font][left.char]
+        if data then
+            local stretch = data.stretch
+            local shrink  = data.shrink
+            if stretch ~= 0 then
+             -- stretch = data.factor * (d *  stretch - d)
+                stretch = data.factor *  d * (stretch - 1)
+            end
+            if shrink ~= 0 then
+             -- shrink = data.factor * (d *  shrink - d)
+                shrink = data.factor *  d * (shrink - 1)
+            end
+            return stretch, shrink
+        end
+    end
+    return 0, 0
+end
+
+local function kern_stretch_shrink(p,d)
+    return 0, 0
+end
+
+-- state:
+
+local function check_expand_pars(checked_expansion,f)
+    local expansion = parameters[f].expansion
+    if not expansion then
+        checked_expansion[f] = false
+        return false
+    end
+    local step    = expansion.step    or 0
+    local stretch = expansion.stretch or 0
+    local shrink  = expansion.shrink  or 0
+    if step == 0 or (stretch == 0 and schrink == 0) then
+        checked_expansion[f] = false
+        return false
+    end
+    local par = checked_expansion.par
+    if par.cur_font_step < 0 then
+        par.cur_font_step = step
+    elseif par.cur_font_step ~= step then
+        report_parbuilders("using fonts with different step of expansion in one paragraph is not allowed")
+        checked_expansion[f] = false
+        return false
+    end
+    if stretch == 0 then
+        -- okay
+    elseif par.max_stretch_ratio < 0 then
+        par.max_stretch_ratio = stretch -- expansion_factor
+    elseif par.max_stretch_ratio ~= stretch then
+        report_parbuilders("using fonts with different stretch limit of expansion in one paragraph is not allowed")
+        checked_expansion[f] = false
+        return false
+    end
+    if shrink == 0 then
+        -- okay
+    elseif par.max_shrink_ratio < 0 then
+        par.max_shrink_ratio = shrink -- - expansion_factor
+    elseif par.max_shrink_ratio ~= shrink then
+        report_parbuilders("using fonts with different shrink limit of expansion in one paragraph is not allowed")
+        checked_expansion[f] = false
+        return false
+    end
+    if trace_adjusting then
+        report_parbuilders("expanding font %a using step %a, shrink %a and stretch %a",f,step,stretch,shrink)
+    end
+    local e = expansions[f]
+    checked_expansion[f] = e
+    return e
+end
+
+local function check_expand_lines(checked_expansion,f)
+    local expansion = parameters[f].expansion
+    if not expansion then
+        checked_expansion[f] = false
+        return false
+    end
+    local step    = expansion.step    or 0
+    local stretch = expansion.stretch or 0
+    local shrink  = expansion.shrink  or 0
+    if step == 0 or (stretch == 0 and schrink == 0) then
+        checked_expansion[f] = false
+        return false
+    end
+    if trace_adjusting then
+        report_parbuilders("expanding font %a using step %a, shrink %a and stretch %a",f,step,stretch,shrink)
+    end
+    local e = expansions[f]
+    checked_expansion[f] = e
+    return e
+end
+
+-- protrusion
+
+local function find(head) -- do we really want to recurse into an hlist?
+    while head do
+        local id = head.id
+        if id == glyph_code then
+            return head
+        elseif id == hlist_code then
+            local found = find(head.list)
+            if found then
+                return found
+            else
+                head = head.next
+            end
+        elseif is_skipable(head) then
+            head = head.next
+        else
+            return head
+        end
+    end
+    return nil
+end
+
+local function find_protchar_left(l) -- weird function
+    local ln = l.next
+    if ln and ln.id == hlist_code and not ln.list and ln.width == 0 and ln.height == 0 and ln.depth == 0 then
+        l = l.next
+    else -- if d then -- was always true
+        local id = l.id
+        while ln and not (id == glyph_code or id < math_code) do -- is there always a glyph?
+            l = ln
+            ln = l.next
+            id = ln.id
+        end
+    end
+ -- if l.id == glyph_code then
+ --     return l
+ -- end
+    return find(l) or l
+end
+
+local function find(head,tail)
+    local tail = tail or slide_nodes(head)
+    while tail do
+        local id = tail.id
+        if id == glyph_code then
+            return tail
+        elseif id == hlist_code then
+            local found = find(tail.list)
+            if found then
+                return found
+            else
+                tail = tail.prev
+            end
+        elseif is_skipable(tail) then
+            tail = tail.prev
+        else
+            return tail
+        end
+    end
+    return nil
+end
+
+local function find_protchar_right(l,r)
+    return r and find(l,r) or r
+end
+
+local function left_pw(p)
+    local font = p.font
+    local prot = chardata[font][p.char].left_protruding
+    if not prot or prot == 0 then
+        return 0
+    end
+    return prot * quaddata[font] / 1000, p
+end
+
+local function right_pw(p)
+    local font = p.font
+    local prot = chardata[font][p.char].right_protruding
+    if not prot or prot == 0 then
+        return 0
+    end
+    return prot * quaddata[font] / 1000, p
+end
+
+-- par parameters
+
+local function reset_meta(par)
+    local active = {
+        id          = hyphenated_code,
+        line_number = max_halfword,
+    }
+    active.next = par.active -- head of metalist
+    par.active  = active
+    par.passive = nil
+end
+
+local function add_to_width(line_break_dir,checked_expansion,s) -- split into two loops (normal and expansion)
+    local size           = 0
+    local adjust_stretch = 0
+    local adjust_shrink  = 0
+    while s do
+        local id = s.id
+        if id == glyph_code then
+            if is_rotated[line_break_dir] then -- can be shared
+                size = size + s.height + s.depth
+            else
+                size = size + s.width
+            end
+            if checked_expansion then
+                local data = checked_expansion[s.font]
+                if data then
+                    data = data[s.char]
+                    if data then
+                        adjust_stretch = adjust_stretch + data.glyphstretch
+                        adjust_shrink  = adjust_shrink  + data.glyphshrink
+                    end
+                end
+            end
+        elseif id == hlist_code or id == vlist_code then
+            if is_parallel[s.dir][line_break_dir] then
+                size = size + s.width
+            else
+                size = size + s.depth + s.height
+            end
+        elseif id == kern_code then
+            if checked_expansion and s.subtype == kerning_code then
+                local d = s.kern
+                if d ~= 0 then
+                    local stretch, shrink = kern_stretch_shrink(s,d)
+                    adjust_stretch = adjust_stretch + stretch
+                    adjust_shrink  = adjust_shrink  + shrink
+                end
+            end
+            size = size + s.kern
+        elseif id == rule_code then
+            size = size + s.width
+        else
+            report_parbuilders("unsupported node at location %a",6)
+        end
+        s = s.next
+    end
+    return size, adjust_stretch, adjust_shrink
+end
+
+local function compute_break_width(par,break_type,p) -- split in two
+    local break_width = par.break_width
+    if break_type > unhyphenated_code then
+        local disc_width           = par.disc_width
+        local checked_expansion    = par.checked_expansion
+        local line_break_dir       = par.line_break_dir
+        local break_size           = break_width.size           + disc_width.size
+        local break_adjust_stretch = break_width.adjust_stretch + disc_width.adjust_stretch
+        local break_adjust_shrink  = break_width.adjust_shrink  + disc_width.adjust_shrink
+        local replace = p.replace
+        if replace then
+            local size, adjust_stretch, adjust_shrink = add_to_width(line_break_dir,checked_expansion,replace)
+            break_size           = break_size           - size
+            break_adjust_stretch = break_adjust_stretch - adjust_stretch
+            break_adjust_shrink  = break_adjust_shrink  - adjust_shrink
+        end
+        local post = p.post
+        if post then
+            local size, adjust_stretch, adjust_shrink = add_to_width(line_break_dir,checked_expansion,post)
+            break_size           = break_size           + size
+            break_adjust_stretch = break_adjust_stretch + adjust_stretch
+            break_adjust_shrink  = break_adjust_shrink  + adjust_shrink
+        end
+        break_width.size           = break_size
+        break_width.adjust_stretch = break_adjust_stretch
+        break_width.adjust_shrink  = break_adjust_shrink
+        if not post then
+            p = p.next
+        else
+            return
+        end
+    end
+    while p do -- skip spacing etc
+        local id = p.id
+        if id == glyph_code then
+            return -- happens often
+        elseif id == glue_code then
+            local spec = p.spec
+            local order = stretch_orders[spec.stretch_order]
+            break_width.size   = break_width.size   - spec.width
+            break_width[order] = break_width[order] - spec.stretch
+            break_width.shrink = break_width.shrink - spec.shrink
+        elseif id == penalty_code then
+            -- do nothing
+        elseif id == kern_code then
+            if p.subtype == userkern_code then
+                break_width.size = break_width.size - p.kern
+            else
+                return
+            end
+        elseif id == math_code then
+            break_width.size = break_width.size - p.surround
+        else
+            return
+        end
+        p = p.next
+    end
+end
+
+local function append_to_vlist(par, b)
+    local prev_depth = par.prev_depth
+    if prev_depth > par.ignored_dimen then
+        if b.id == hlist_code then
+            local d = par.baseline_skip.width - prev_depth - b.height -- deficiency of space between baselines
+            local s = d < par.line_skip_limit and new_lineskip(tex.lineskip) or new_baselineskip(d)
+         -- local s = d < par.line_skip_limit
+         -- if s then
+         --     s = new_lineskip()
+         --     s.spec = tex.lineskip
+         -- else
+         --     s = new_baselineskip(d)
+         -- end
+            local head_field = par.head_field
+            if head_field then
+                local n = slide_nodes(head_field)
+                n.next, s.prev = s, n
+            else
+                par.head_field = s
+            end
+        end
+    end
+    local head_field = par.head_field
+    if head_field then
+        local n = slide_nodes(head_field)
+        n.next, b.prev = b, n
+    else
+        par.head_field = b
+    end
+    if b.id == hlist_code then
+        local pd = b.depth
+        par.prev_depth = pd
+        texnest[texnest.ptr].prevdepth = pd
+    end
+end
+
+local function append_list(par, b)
+    local head_field = par.head_field
+    if head_field then
+        local n = slide_nodes(head_field)
+        n.next, b.prev = b, n
+    else
+        par.head_field = b
+    end
+end
+
+-- We can actually make par local to this module as we never break inside a break call and that way the
+-- array is reused. At some point the information will be part of the paragraph spec as passed.
+
+local function initialize_line_break(head,display)
+
+    local hang_indent    = tex.hangindent or 0
+    local hsize          = tex.hsize or 0
+    local hang_after     = tex.hangafter or 0
+    local par_shape_ptr  = tex.parshape
+    local left_skip      = tex.leftskip -- nodes
+    local right_skip     = tex.rightskip -- nodes
+    local pretolerance   = tex.pretolerance
+    local tolerance      = tex.tolerance
+    local adjust_spacing = tex.pdfadjustspacing
+    local protrude_chars = tex.pdfprotrudechars
+    local last_line_fit  = tex.lastlinefit
+
+    local newhead = new_temp()
+    newhead.next  = head
+
+    local adjust_spacing_status = adjust_spacing > 1 and -1 or 0
+
+    -- metatables
+
+    local par = {
+        head                         = newhead,
+        head_field                   = nil,
+        display                      = display,
+        font_in_short_display        = 0,
+        no_shrink_error_yet          = true,   -- have we complained about infinite shrinkage?
+        second_pass                  = false,  -- is this our second attempt to break this paragraph?
+        final_pass                   = false,  -- is this our final attempt to break this paragraph?
+        threshold                    = 0,      -- maximum badness on feasible lines
+
+        passive                      = nil,    -- most recent node on passive list
+        printed_node                 = head,   -- most recent node that has been printed
+        pass_number                  = 0,      -- the number of passive nodes allocated on this pass
+        auto_breaking                = 0,      -- make auto_breaking accessible out of line_break
+
+        active_width                 = { size = 0, stretch = 0, fi = 0, fil = 0, fill = 0, filll = 0, shrink = 0, adjust_stretch = 0, adjust_shrink = 0 },
+        break_width                  = { size = 0, stretch = 0, fi = 0, fil = 0, fill = 0, filll = 0, shrink = 0, adjust_stretch = 0, adjust_shrink = 0 },
+        disc_width                   = { size = 0,                                                                adjust_stretch = 0, adjust_shrink = 0 },
+        fill_width                   = {           stretch = 0, fi = 0, fil = 0, fill = 0, filll = 0, shrink = 0                                        },
+        background                   = { size = 0, stretch = 0, fi = 0, fil = 0, fill = 0, filll = 0, shrink = 0                                        },
+
+        hang_indent                  = hang_indent,
+        hsize                        = hsize,
+        hang_after                   = hang_after,
+        par_shape_ptr                = par_shape_ptr,
+        left_skip                    = left_skip,
+        right_skip                   = right_skip,
+        pretolerance                 = pretolerance,
+        tolerance                    = tolerance,
+
+        protrude_chars               = protrude_chars,
+        adjust_spacing               = adjust_spacing,
+        max_stretch_ratio            = adjust_spacing_status,
+        max_shrink_ratio             = adjust_spacing_status,
+        cur_font_step                = adjust_spacing_status,
+        checked_expansion            = false,
+        tracing_paragraphs           = tex.tracingparagraphs > 0,
+
+        emergency_stretch            = tex.emergencystretch     or 0,
+        looseness                    = tex.looseness            or 0,
+        line_penalty                 = tex.linepenalty          or 0,
+        hyphen_penalty               = tex.hyphenpenalty        or 0,
+        broken_penalty               = tex.brokenpenalty        or 0,
+        inter_line_penalty           = tex.interlinepenalty     or 0,
+        club_penalty                 = tex.clubpenalty          or 0,
+        widow_penalty                = tex.widowpenalty         or 0,
+        display_widow_penalty        = tex.displaywidowpenalty  or 0,
+        ex_hyphen_penalty            = tex.exhyphenpenalty      or 0,
+
+        adj_demerits                 = tex.adjdemerits          or 0,
+        double_hyphen_demerits       = tex.doublehyphendemerits or 0,
+        final_hyphen_demerits        = tex.finalhyphendemerits  or 0,
+
+        first_line                   = 0, -- tex.nest.modeline, -- 0, -- cur_list.pg_field
+
+        each_line_height             = tex.pdfeachlineheight    or 0, -- this will go away
+        each_line_depth              = tex.pdfeachlinedepth     or 0, -- this will go away
+        first_line_height            = tex.pdffirstlineheight   or 0, -- this will go away
+        last_line_depth              = tex.pdflastlinedepth     or 0, -- this will go away
+        ignored_dimen                = tex.pdfignoreddimen      or 0, -- this will go away
+
+        baseline_skip                = tex.baselineskip         or 0,
+        line_skip_limit              = tex.lineskiplimit        or 0,
+
+        prev_depth                   = texnest[texnest.ptr].prevdepth,
+
+        final_par_glue               = slide_nodes(head), -- todo: we know tail already, slow
+
+        par_break_dir                = tex.pardir,
+        line_break_dir               = tex.pardir,
+
+        internal_pen_inter           = 0,   -- running localinterlinepenalty
+        internal_pen_broken          = 0,   -- running localbrokenpenalty
+        internal_left_box            = nil, -- running localleftbox
+        internal_left_box_width      = 0,   -- running localleftbox width
+        init_internal_left_box       = nil, -- running localleftbox
+        init_internal_left_box_width = 0,   -- running localleftbox width
+        internal_right_box           = nil, -- running localrightbox
+        internal_right_box_width     = 0,   -- running localrightbox width
+
+        best_place                   = { }, -- how to achieve minimal_demerits
+        best_pl_line                 = { }, -- corresponding line number
+        easy_line                    = 0,   -- line numbers easy_line are equivalent in break nodes
+        last_special_line            = 0,   -- line numbers last_special_line all have the same width
+        first_width                  = 0,   -- the width of all lines last_special_line, if no parshape has been specified
+        second_width                 = 0,   -- the width of all lines last_special_line
+        first_indent                 = 0,   -- left margin to go with first_width
+        second_indent                = 0,   -- left margin to go with second_width
+
+        best_bet                     = nil, -- use this passive node and its predecessors
+        fewest_demerits              = 0,   -- the demerits associated with best_bet
+        best_line                    = 0,   -- line number following the last line of the new paragraph
+        line_diff                    = 0,   -- the difference between the current line number and the optimum best_line
+
+        -- not yet used
+
+        best_pl_short                = { }, -- shortfall corresponding to minimal_demerits
+        best_pl_glue                 = { }, -- corresponding glue stretch or shrink
+        do_last_line_fit             = false,
+        last_line_fit                = last_line_fit,
+
+        minimum_demerits             = awful_badness,
+
+        minimal_demerits             = {
+
+            [fit_very_loose_class] = awful_badness,
+            [fit_loose_class]      = awful_badness,
+            [fit_decent_class]     = awful_badness,
+            [fit_tight_class]      = awful_badness,
+
+        },
+
+        prev_char_p                  = nil,
+
+        font_steps                   = { }, -- mine
+
+        statistics                   = {
+
+            noflines          = 0,
+            nofprotrudedlines = 0,
+            nofadjustedlines  = 0,
+
+        }
+
+    }
+
+    if adjust_spacing > 1 then
+        local checked_expansion = { par = par }
+        setmetatableindex(checked_expansion,check_expand_pars)
+        par.checked_expansion = checked_expansion
+    end
+
+    -- we need par for the error message
+
+    local background = par.background
+
+    local l = check_shrinkage(par,left_skip)
+    local r = check_shrinkage(par,right_skip)
+    local l_order = stretch_orders[l.stretch_order]
+    local r_order = stretch_orders[r.stretch_order]
+
+    background.size     = l.width + r.width
+    background.shrink   = l.shrink + r.shrink
+    background[l_order] = l.stretch
+    background[r_order] = r.stretch + background[r_order]
+
+    -- this will move up so that we can assign the whole par table
+
+    if not par_shape_ptr then
+        if hang_indent == 0 then
+            par.second_width  = hsize
+            par.second_indent = 0
+        else
+            local abs_hang_after  = hang_after >0 and hang_after  or -hang_after
+            local abs_hang_indent = hang_indent>0 and hang_indent or -hang_indent
+            par.last_special_line = abs_hang_after
+            if hang_after < 0 then
+                par.first_width = hsize - abs_hang_indent
+                if hang_indent >= 0 then
+                    par.first_indent = hang_indent
+                else
+                    par.first_indent = 0
+                end
+                par.second_width  = hsize
+                par.second_indent = 0
+            else
+                par.first_width  = hsize
+                par.first_indent = 0
+                par.second_width = hsize - abs_hang_indent
+                if hang_indent >= 0 then
+                    par.second_indent = hang_indent
+                else
+                    par.second_indent = 0
+                end
+            end
+        end
+    else
+        local last_special_line = #par_shape_ptr
+        par.last_special_line = last_special_line
+        local ptr = par_shape_ptr[last_special_line]
+        par.second_width  = ptr[2]
+        par.second_indent = ptr[1]
+    end
+
+    if par.looseness == 0 then
+        par.easy_line = par.last_special_line
+    else
+        par.easy_line = max_halfword
+    end
+
+    if pretolerance >= 0 then
+        par.threshold   = pretolerance
+        par.second_pass = false
+        par.final_pass  = false
+    else
+        par.threshold   = tolerance
+        par.second_pass = true
+        par.final_pass  = par.emergency_stretch <= 0
+        if trace_basic then
+            if par.final_pass then
+                report_parbuilders("enabling second and final pass")
+            else
+                report_parbuilders("enabling second pass")
+            end
+        end
+    end
+
+    if last_line_fit > 0 then
+        local spec          = par.final_par_glue.spec
+        local stretch       = spec.stretch
+        local stretch_order = spec.stretch_order
+        if stretch > 0 and stretch_order > 0 and background.fi == 0 and background.fil == 0 and background.fill == 0 and background.filll == 0 then
+            par.do_last_line_fit = true
+            local si = stretch_orders[stretch_order]
+            if trace_lastlinefit or trace_basic then
+                report_parbuilders("enabling last line fit, stretch order %a set to %a, linefit is %a",si,stretch,last_line_fit)
+            end
+            par.fill_width[si] = stretch
+        end
+    end
+
+    return par
+end
+
+local function post_line_break(par)
+
+    local prevgraf       = texnest[texnest.ptr].prevgraf
+    local cur_line       = prevgraf + 1 -- the current line number being justified
+    local cur_p          = nil
+
+    local adjust_spacing = par.adjust_spacing
+    local protrude_chars = par.protrude_chars
+    local statistics     = par.statistics
+
+    local p, s, k, w  -- check when local
+
+    local q = par.best_bet.break_node
+    repeat -- goto first breakpoint
+        local r = q
+        q = q.prev_break
+        r.prev_break = cur_p
+        cur_p = r
+    until not q
+
+    local stack = new_dir_stack()
+
+    repeat
+
+        inject_dirs_at_begin_of_line(stack,par.head)
+
+        local q = nil
+        local r = cur_p.cur_break
+
+        local disc_break      = false
+        local post_disc_break = false
+        local glue_break      = false
+
+        if not r then
+            r = slide_nodes(par.head)
+            if r == par.final_par_glue then
+                q = r      -- q refers to the last node of the line (and paragraph)
+                r = r.prev -- r refers to the node after which the dir nodes should be closed
+            end
+        else
+            local id = r.id
+            if id == glue_code then
+                -- r is normal skip
+                r = replace_node(r,new_rightskip(par.right_skip))
+                glue_break = true
+                q = r      -- q refers to the last node of the line
+                r = r.prev -- r refers to the node after which the dir nodes should be closed
+            elseif id == disc_code then
+                -- todo: use insert_before/after
+                local prev_r  = r.prev
+                local next_r  = r.next
+                local subtype = r.subtype
+                local pre     = r.pre
+                local post    = r.post
+                local replace = r.replace
+                if subtype == second_disc_code then
+                    if not (prev_r.id == disc_code and prev_r.subtype == first_disc_code) then
+                        report_parbuilders('unsupported disc at location %a',3)
+                    end
+                    if pre then
+                        flush_node_list(pre)
+                        r.pre = nil
+                        pre   = nil -- signal
+                    end
+                    if replace then
+                        local n      = slide_nodes(replace)
+                        prev_r.next  = replace
+                        replace.prev = prev_r
+                        n.next       = r
+                        r.prev       = n
+                        r.replace    = nil
+                        replace      = nil -- signal
+                    end
+                    local pre     = prev_r.pre
+                    local post    = prev_r.post
+                    local replace = prev_r.replace
+                    if pre then
+                        flush_node_list(pre)
+                        prev_r.pre = nil
+                    end
+                    if replace then
+                        flush_node_list(replace)
+                        prev_r.replace = nil
+                    end
+                    if post then
+                        flush_node_list(post)
+                        prev_r.post = nil
+                    end
+                elseif subtype == first_disc_code then
+                    if not (v.id == disc_code and v.subtype == second_disc_code) then
+                        report_parbuilders('unsupported disc at location %a',4)
+                    end
+                    next_r.subtype = regular_disc_code
+                    next_r.replace = post
+                    r.post         = nil
+                end
+                if replace then
+                    r.replace = nil -- free
+                    flush_node_list(replace)
+                end
+                if pre then
+                    local n     = slide_nodes(pre)
+                    prev_r.next = pre
+                    pre.prev    = prev_r
+                    n.next      = r
+                    r.prev      = n
+                    r.pre       = nil
+                end
+                if post then
+                    local n         = slide_nodes(post)
+                    r.next          = post
+                    post.prev       = r
+                    n.next          = next_r
+                    next_r.prev     = n
+                    r.post          = nil
+                    post_disc_break = true
+                end
+                disc_break = true
+            elseif id == kern_code then
+                r.kern = 0
+            elseif r.id == math_code then
+                r.surround = 0
+            end
+        end
+        r = inject_dirs_at_end_of_line(stack,r,par.head.next,cur_p.cur_break)
+        local crb  = cur_p.passive_right_box
+        if crb then
+            local s = copy_node(crb)
+            local e = r.next
+            r.next = s
+            s.prev = r
+            s.next = e
+            if e then
+                e.prev = s
+            end
+            r = s
+        end
+        if not q then
+            q = r
+        end
+        if q and q ~= par.head and protrude_chars > 0 then
+            local id = q.id
+            local c = (disc_break and (id == glyph_code or id ~= disc_code) and q) or q.prev
+            local p = find_protchar_right(par.head.next,c)
+            if p and p.id == glyph_code then
+                local w, last_rightmost_char = right_pw(p)
+                if last_rightmost_char and w ~= 0 then
+                    -- so we inherit attributes, q is new pseudo head
+                    q, c = insert_node_after(q,c,new_rightmarginkern(copy_node(last_rightmost_char),-w))
+                end
+            end
+        end
+        if not glue_break then
+            local h
+            h, q = insert_node_after(q,q,new_rightskip(par.right_skip)) -- q moves on as pseudo head
+        end
+        r           = q.next
+        q.next      = nil
+        local phead = par.head
+        q           = phead.next
+        phead.next  = r
+        if r then
+            r.prev = phead
+        end
+        local clb  = cur_p.passive_left_box
+        if clb then -- here we miss some prev links
+            local s = copy_node(cb)
+            s       = q.next
+            r.next  = q
+            q       = r
+            if s and cur_line == (par.first_line + 1) and s.id == hlist_code and not s.list then
+                q      = q.next
+                r.next = s.next
+                s.next = r
+            end
+        end
+        if protrude_chars > 0 then
+            local p = find_protchar_left(q)
+            if p and p.id == glyph_code then
+                local w, last_leftmost_char = left_pw(p)
+                if last_leftmost_char and w ~= 0 then
+                    -- so we inherit attributes, q is pseudo head and moves back
+                    q = insert_node_before(q,q,new_leftmarginkern(copy_node(last_leftmost_char),-w))
+                end
+            end
+        end
+        local ls = par.left_skip
+        if ls and (ls.width ~= 0 or ls.stretch ~= 0 or ls.shrink ~= 0) then
+            q = insert_node_before(q,q,new_leftskip(ls))
+        end
+        local curwidth, cur_indent
+        if cur_line > par.last_special_line then
+            cur_indent = par.second_indent
+            cur_width  = par.second_width
+        else
+            local psp = par.par_shape_ptr
+            if psp then
+                cur_indent = psp[cur_line][1]
+                cur_width  = psp[cur_line][2]
+            else
+                cur_indent = par.first_indent
+                cur_width  = par.first_width
+            end
+        end
+        statistics.noflines = statistics.noflines + 1
+        if adjust_spacing > 0 then
+            statistics.nofadjustedlines = statistics.nofadjustedlines + 1
+            just_box = xpack_nodes(q,cur_width,"cal_expand_ratio",par.par_break_dir) -- ,cur_p.analysis)
+        else
+            just_box = xpack_nodes(q,cur_width,"exactly",par.par_break_dir) -- ,cur_p.analysis)
+        end
+        if protrude_chars > 0 then
+            statistics.nofprotrudedlines = statistics.nofprotrudedlines + 1
+        end
+        -- wrong:
+        local adjust_head     = texlists.adjust_head
+        local pre_adjust_head = texlists.pre_adjust_head
+        --
+        just_box.shift = cur_indent
+        if par.each_line_height ~= par.ignored_dimen then
+            just_box.height = par.each_line_height
+        end
+        if par.each_line_depth ~= par.ignored_dimen then
+            just_box.depth = par.each_line_depth
+        end
+        if par.first_line_height ~= par.ignored_dimen and (cur_line == par.first_line + 1) then
+            just_box.height = par.first_line_height
+        end
+        if par.last_line_depth ~= par.ignored_dimen and cur_line + 1 == par.best_line then
+            just_box.depth = par.last_line_depth
+        end
+        if texlists.pre_adjust_head ~= pre_adjust_head then
+            append_list(par, texlists.pre_adjust_head)
+            texlists.pre_adjust_head = pre_adjust_head
+        end
+        append_to_vlist(par, just_box)
+        if texlists.adjust_head ~= adjust_head then
+            append_list(par, texlists.adjust_head)
+            texlists.adjust_head = adjust_head
+        end
+        local pen
+        if cur_line + 1 ~= par.best_line then
+            if cur_p.passive_pen_inter then
+                pen = cur_p.passive_pen_inter
+            else
+                pen = par.inter_line_penalty
+            end
+            if cur_line == prevgraf + 1 then
+                pen = pen + par.club_penalty
+            end
+            if cur_line + 2 == par.best_line then
+                if par.display then
+                    pen = pen + par.display_widow_penalty
+                else
+                    pen = pen + par.widow_penalty
+                end
+            end
+            if disc_break then
+                if cur_p.passive_pen_broken ~= 0 then
+                    pen = pen + cur_p.passive_pen_broken
+                else
+                    pen = pen + par.broken_penalty
+                end
+            end
+            if pen ~= 0 then
+                append_to_vlist(par,new_penalty(pen))
+             end
+        end
+        cur_line = cur_line + 1
+        cur_p = cur_p.prev_break
+        if cur_p and not post_disc_break then
+            local phead = par.head
+            local r = phead
+            while true do
+                q = r.next
+                if q == cur_p.cur_break or q.id == glyph_code then
+                    break
+                end
+                local id = q.id
+                if not (id == whatsit_code and q.subtype == localpar_code) then
+                    if id < math_code or (id == kern_code and q.subtype ~= userkern_code) then
+                        break
+                    end
+                end
+                r = q
+            end
+            if r ~= phead then
+                r.next = nil
+                flush_node_list(phead.next)
+                phead.next = q
+                if q then
+                    q.prev = phead
+                end
+            end
+        end
+    until not cur_p
+    if cur_line ~= par.best_line then -- or not par.head.next then
+        report_parbuilders("line breaking")
+    end
+    if par.head then -- added
+--         flush_node(par.head) -- the localpar_code whatsit
+        par.head = nil
+    end
+    cur_line = cur_line - 1
+    if trace_basic then
+        report_parbuilders("paragraph broken into %a lines",cur_line)
+    end
+    texnest[texnest.ptr].prevgraf  = cur_line
+end
+
+local function wrap_up(par)
+    if par.tracing_paragraphs then
+        diagnostics.stop()
+    end
+    if par.do_last_line_fit then
+        local best_bet     = par.best_bet
+        local active_short = best_bet.active_short
+        local active_glue  = best_bet.active_glue
+        if active_short == 0 then
+            if trace_lastlinefit then
+                report_parbuilders("disabling last line fit, no active_short")
+            end
+            par.do_last_line_fit = false
+        else
+            local glue = par.final_par_glue
+            local spec = copy_node(glue.spec)
+            spec.width = spec.width + active_short - active_glue
+            spec.stretch = 0
+        --  flush_node(glue.spec) -- brrr, when we do this we can get an "invalid id stretch message", maybe dec refcount
+            glue.spec = spec
+            if trace_lastlinefit then
+                report_parbuilders("applying last line fit, short %a, glue %p",active_short,active_glue)
+            end
+        end
+    end
+    -- we have a bunch of glue and and temp nodes not freed
+    local head = par.head
+    if head.id == temp_code then
+        par.head = head.next
+        flush_node(head)
+    end
+    post_line_break(par)
+    reset_meta(par)
+    register_statistics(par)
+    return par.head_field
+end
+
+-- we could do active nodes differently ... table instead of linked list or a list
+-- with prev nodes
+
+local function deactivate_node(par,prev_prev_r,prev_r,r,cur_active_width,checked_expansion) -- no need for adjust if disabled
+    local active = par.active
+    local active_width = par.active_width
+    prev_r.next = r.next
+    -- removes r
+    -- r = nil
+    if prev_r == active then
+        r = active.next
+        if r.id == delta_code then
+            local aw = active_width.size    + r.size    active_width.size    = aw  cur_active_width.size    = aw
+            local aw = active_width.stretch + r.stretch active_width.stretch = aw  cur_active_width.stretch = aw
+            local aw = active_width.fi      + r.fi      active_width.fi      = aw  cur_active_width.fi      = aw
+            local aw = active_width.fil     + r.fil     active_width.fil     = aw  cur_active_width.fil     = aw
+            local aw = active_width.fill    + r.fill    active_width.fill    = aw  cur_active_width.fill    = aw
+            local aw = active_width.filll   + r.filll   active_width.filll   = aw  cur_active_width.filll   = aw
+            local aw = active_width.shrink  + r.shrink  active_width.shrink  = aw  cur_active_width.shrink  = aw
+            if checked_expansion then
+                local aw = active_width.adjust_stretch + r.adjust_stretch  active_width.adjust_stretch = aw  cur_active_width.adjust_stretch = aw
+                local aw = active_width.adjust_shrink  + r.adjust_shrink   active_width.adjust_shrink  = aw  cur_active_width.adjust_shrink  = aw
+            end
+            active.next = r.next
+            -- removes r
+            -- r = nil
+        end
+    elseif prev_r.id == delta_code then
+        r = prev_r.next
+        if r == active then
+            cur_active_width.size    = cur_active_width.size    - prev_r.size
+            cur_active_width.stretch = cur_active_width.stretch - prev_r.stretch
+            cur_active_width.fi      = cur_active_width.fi      - prev_r.fi
+            cur_active_width.fil     = cur_active_width.fil     - prev_r.fil
+            cur_active_width.fill    = cur_active_width.fill    - prev_r.fill
+            cur_active_width.filll   = cur_active_width.filll   - prev_r.filll
+            cur_active_width.shrink  = cur_active_width.shrink  - prev_r.shrink
+            if checked_expansion then
+                cur_active_width.adjust_stretch = cur_active_width.adjust_stretch - prev_r.adjust_stretch
+                cur_active_width.adjust_shrink  = cur_active_width.adjust_shrink  - prev_r.adjust_shrink
+            end
+            prev_prev_r.next = active
+            -- removes prev_r
+            -- prev_r = nil
+            prev_r = prev_prev_r
+        elseif r.id == delta_code then
+            local rn = r.size     cur_active_width.size    = cur_active_width.size    + rn  prev_r.size    = prev_r.size    + rn
+            local rn = r.stretch  cur_active_width.stretch = cur_active_width.stretch + rn  prev_r.stretch = prev_r.stretch + rn
+            local rn = r.fi       cur_active_width.fi      = cur_active_width.fi      + rn  prev_r.fi      = prev_r.fi      + rn
+            local rn = r.fil      cur_active_width.fil     = cur_active_width.fil     + rn  prev_r.fil     = prev_r.fil     + rn
+            local rn = r.fill     cur_active_width.fill    = cur_active_width.fill    + rn  prev_r.fill    = prev_r.fill    + rn
+            local rn = r.filll    cur_active_width.filll   = cur_active_width.filll   + rn  prev_r.filll   = prev_r.fill    + rn
+            local rn = r.shrink   cur_active_width.shrink  = cur_active_width.shrink  + rn  prev_r.shrink  = prev_r.shrink  + rn
+            if checked_expansion then
+                local rn = r.adjust_stretch  cur_active_width.adjust_stretch = cur_active_width.adjust_stretch + rn  prev_r.adjust_stretch = prev_r.adjust_stretch    + rn
+                local rn = r.adjust_shrink   cur_active_width.adjust_shrink  = cur_active_width.adjust_shrink  + rn  prev_r.adjust_shrink  = prev_r.adjust_shrink     + rn
+            end
+            prev_r.next = r.next
+            -- removes r
+            -- r = nil
+        end
+    end
+    return prev_r, r
+end
+
+local function lastlinecrap(shortfall,active_short,active_glue,cur_active_width,fill_width,last_line_fit)
+    if active_short == 0 or active_glue <= 0 then
+        return false, 0, fit_decent_class, 0, 0
+    end
+    if cur_active_width.fi ~= fill_width.fi or cur_active_width.fil ~= fill_width.fil or cur_active_width.fill ~= fill_width.fill or cur_active_width.filll ~= fill_width.filll then
+        return false, 0, fit_decent_class, 0, 0
+    end
+    local adjustment = active_short > 0 and cur_active_width.stretch or cur_active_width.shrink
+    if adjustment <= 0 then
+        return false, 0, fit_decent_class, adjustment, 0
+    end
+    adjustment = calculate_fraction(adjustment,active_short,active_glue,maxdimen)
+    if last_line_fit < 1000 then
+        adjustment = calculate_fraction(adjustment,last_line_fit,1000,maxdimen) -- uses previous adjustment
+    end
+    local fit_class = fit_decent_class
+    if adjustment > 0 then
+        local stretch = cur_active_width.stretch
+        if adjustment > shortfall then
+            adjustment = shortfall
+        end
+        if adjustment > 7230584 and stretch < 1663497 then
+            return true, fit_very_loose_class, shortfall, adjustment, infinite_badness
+        end
+     -- if adjustment == 0 then -- badness = 0
+     --     return true, shortfall, fit_decent_class, 0, 0
+     -- elseif stretch <= 0 then -- badness = 10000
+     --     return true, shortfall, fit_very_loose_class, adjustment, 10000
+     -- end
+     -- local badness = (adjustment == 0 and 0) or (stretch <= 0 and 10000) or calculate_badness(adjustment,stretch)
+        local badness = calculate_badness(adjustment,stretch)
+        if badness > 99 then
+            return true, shortfall, fit_very_loose_class, adjustment, badness
+        elseif badness > 12 then
+            return true, shortfall, fit_loose_class, adjustment, badness
+        else
+            return true, shortfall, fit_decent_class, adjustment, badness
+        end
+    elseif adjustment < 0 then
+        local shrink = cur_active_width.shrink
+        if -adjustment > shrink then
+            adjustment = -shrink
+        end
+        local badness = calculate_badness(-adjustment,shrink)
+        if badness > 12 then
+            return true, shortfall, fit_tight_class, adjustment, badness
+        else
+            return true, shortfall, fit_decent_class, adjustment, badness
+        end
+    else
+        return false, 0, fit_decent_class, 0, 0
+    end
+end
+
+local function try_break(pi, break_type, par, first_p, cur_p, checked_expansion)
+
+    if pi >= infinite_penalty then
+        return                              -- this breakpoint is inhibited by infinite penalty
+    elseif pi <= -infinite_penalty then
+        pi = eject_penalty                  -- this breakpoint will be forced
+    end
+
+    local prev_prev_r         = nil         -- a step behind prev_r, if type(prev_r)=delta_code
+    local prev_r              = par.active  -- stays a step behind r
+    local r                   = nil         -- runs through the active list
+    local no_break_yet        = true        -- have we found a feasible break at cur_p?
+    local node_r_stays_active = false       -- should node r remain in the active list?
+    local line_width          = 0           -- the current line will be justified to this width
+    local line_number         = 0           -- line number of current active node
+    local old_line_number     = 0           -- maximum line number in current equivalence class of lines
+
+    local protrude_chars      = par.protrude_chars
+    local checked_expansion   = par.checked_expansion
+    local break_width         = par.break_width
+    local active_width        = par.active_width
+    local background          = par.background
+    local minimal_demerits    = par.minimal_demerits
+    local best_place          = par.best_place
+    local best_pl_line        = par.best_pl_line
+    local best_pl_short       = par.best_pl_short
+    local best_pl_glue        = par.best_pl_glue
+    local do_last_line_fit    = par.do_last_line_fit
+    local final_pass          = par.final_pass
+    local tracing_paragraphs  = par.tracing_paragraphs
+ -- local par_active          = par.active
+
+    local cur_active_width = checked_expansion and { -- distance from current active node
+        size           = active_width.size,
+        stretch        = active_width.stretch,
+        fi             = active_width.fi,
+        fil            = active_width.fil,
+        fill           = active_width.fill,
+        filll          = active_width.filll,
+        shrink         = active_width.shrink,
+        adjust_stretch = active_width.adjust_stretch,
+        adjust_shrink  = active_width.adjust_shrink,
+    } or {
+        size           = active_width.size,
+        stretch        = active_width.stretch,
+        fi             = active_width.fi,
+        fil            = active_width.fil,
+        fill           = active_width.fill,
+        filll          = active_width.filll,
+        shrink         = active_width.shrink,
+    }
+
+    while true do
+        r = prev_r.next
+        if r.id == delta_code then
+            cur_active_width.size    = cur_active_width.size    + r.size
+            cur_active_width.stretch = cur_active_width.stretch + r.stretch
+            cur_active_width.fi      = cur_active_width.fi      + r.fi
+            cur_active_width.fil     = cur_active_width.fil     + r.fil
+            cur_active_width.fill    = cur_active_width.fill    + r.fill
+            cur_active_width.filll   = cur_active_width.filll   + r.filll
+            cur_active_width.shrink  = cur_active_width.shrink  + r.shrink
+            if checked_expansion then
+                cur_active_width.adjust_stretch = cur_active_width.adjust_stretch + r.adjust_stretch
+                cur_active_width.adjust_shrink  = cur_active_width.adjust_shrink  + r.adjust_shrink
+            end
+            prev_prev_r = prev_r
+            prev_r = r
+        else
+            line_number = r.line_number
+            if line_number > old_line_number then
+                local minimum_demerits = par.minimum_demerits
+                if minimum_demerits < awful_badness and (old_line_number ~= par.easy_line or r == par.active) then
+                    if no_break_yet then
+                        no_break_yet = false
+                        break_width.size    = background.size
+                        break_width.stretch = background.stretch
+                        break_width.fi      = background.fi
+                        break_width.fil     = background.fil
+                        break_width.fill    = background.fill
+                        break_width.filll   = background.filll
+                        break_width.shrink  = background.shrink
+                        if checked_expansion then
+                            break_width.adjust_stretch = 0
+                            break_width.adjust_shrink  = 0
+                        end
+                        if cur_p then
+                            compute_break_width(par,break_type,cur_p)
+                        end
+                    end
+                    if prev_r.id == delta_code then
+                        prev_r.size    = prev_r.size    - cur_active_width.size   + break_width.size
+                        prev_r.stretch = prev_r.stretch - cur_active_width.stretc + break_width.stretch
+                        prev_r.fi      = prev_r.fi      - cur_active_width.fi     + break_width.fi
+                        prev_r.fil     = prev_r.fil     - cur_active_width.fil    + break_width.fil
+                        prev_r.fill    = prev_r.fill    - cur_active_width.fill   + break_width.fill
+                        prev_r.filll   = prev_r.filll   - cur_active_width.filll  + break_width.filll
+                        prev_r.shrink  = prev_r.shrink  - cur_active_width.shrink + break_width.shrink
+                        if checked_expansion then
+                            prev_r.adjust_stretch = prev_r.adjust_stretch - cur_active_width.adjust_stretch + break_width.adjust_stretch
+                            prev_r.adjust_shrink  = prev_r.adjust_shrink  - cur_active_width.adjust_shrink  + break_width.adjust_shrink
+                        end
+                    elseif prev_r == par.active then
+                        active_width.size    = break_width.size
+                        active_width.stretch = break_width.stretch
+                        active_width.fi      = break_width.fi
+                        active_width.fil     = break_width.fil
+                        active_width.fill    = break_width.fill
+                        active_width.filll   = break_width.filll
+                        active_width.shrink  = break_width.shrink
+                        if checked_expansion then
+                            active_width.adjust_stretch = break_width.adjust_stretch
+                            active_width.adjust_shrink  = break_width.adjust_shrink
+                        end
+                    else
+                        local q = checked_expansion and {
+                            id             = delta_code,
+                            subtype        = nosubtype_code,
+                            next           = r,
+                            size           = break_width.size           - cur_active_width.size,
+                            stretch        = break_width.stretch        - cur_active_width.stretch,
+                            fi             = break_width.fi             - cur_active_width.fi,
+                            fil            = break_width.fil            - cur_active_width.fil,
+                            fill           = break_width.fill           - cur_active_width.fill,
+                            filll          = break_width.filll          - cur_active_width.filll,
+                            shrink         = break_width.shrink         - cur_active_width.shrink,
+                            adjust_stretch = break_width.adjust_stretch - cur_active_width.adjust_stretch,
+                            adjust_shrink  = break_width.adjust_shrink  - cur_active_width.adjust_shrink,
+                        } or {
+                            id             = delta_code,
+                            subtype        = nosubtype_code,
+                            next           = r,
+                            size           = break_width.size           - cur_active_width.size,
+                            stretch        = break_width.stretch        - cur_active_width.stretch,
+                            fi             = break_width.fi             - cur_active_width.fi,
+                            fil            = break_width.fil            - cur_active_width.fil,
+                            fill           = break_width.fill           - cur_active_width.fill,
+                            filll          = break_width.filll          - cur_active_width.filll,
+                            shrink         = break_width.shrink         - cur_active_width.shrink,
+                        }
+                        prev_r.next = q
+                        prev_prev_r = prev_r
+                        prev_r = q
+                    end
+                    local adj_demerits     = par.adj_demerits
+                    local abs_adj_demerits = adj_demerits > 0 and adj_demerits or -adj_demerits
+                    if abs_adj_demerits >= awful_badness - minimum_demerits then
+                        minimum_demerits = awful_badness - 1
+                    else
+                        minimum_demerits = minimum_demerits + abs_adj_demerits
+                    end
+                    for fit_class = fit_very_loose_class, fit_tight_class do
+                        if minimal_demerits[fit_class] <= minimum_demerits then
+                            -- insert a new active node from best_place[fit_class] to cur_p
+                            par.pass_number = par.pass_number + 1
+                            local prev_break = best_place[fit_class]
+                            local passive = {
+                                id                          = passive_code,
+                                subtype                     = nosubtype_code,
+                                next                        = par.passive,
+                                cur_break                   = cur_p,
+                                serial                      = par.pass_number,
+                                prev_break                  = prev_break,
+                                passive_pen_inter           = par.internal_pen_inter,
+                                passive_pen_broken          = par.internal_pen_broken,
+                                passive_last_left_box       = par.internal_left_box,
+                                passive_last_left_box_width = par.internal_left_box_width,
+                                passive_left_box            = prev_break and prev_break.passive_last_left_box or par.init_internal_left_box,
+                                passive_left_box_width      = prev_break and prev_break.passive_last_left_box_width or par.init_internal_left_box_width,
+                                passive_right_box           = par.internal_right_box,
+                                passive_right_box_width     = par.internal_right_box_width,
+-- analysis = table.fastcopy(cur_active_width),
+                            }
+                            par.passive = passive
+                            local q = {
+                                id             = break_type,
+                                subtype        = fit_class,
+                                break_node     = passive,
+                                line_number    = best_pl_line[fit_class] + 1,
+                                total_demerits = minimal_demerits[fit_class], --  or 0,
+                                next           = r,
+                            }
+                            if do_last_line_fit then
+                                local active_short = best_pl_short[fit_class]
+                                local active_glue  = best_pl_glue[fit_class]
+                                q.active_short = active_short
+                                q.active_glue  = active_glue
+                                if trace_lastlinefit then
+                                    report_parbuilders("setting short to %i and glue to %p using class %a",active_short,active_glue,fit_class)
+                                end
+                            end
+                         -- q.next = r -- already done
+                            prev_r.next = q
+                            prev_r = q
+                            if tracing_paragraphs then
+                                diagnostics.break_node(par,q,fit_class,break_type,cur_p)
+                            end
+                        end
+                        minimal_demerits[fit_class] = awful_badness
+                    end
+                    par.minimum_demerits = awful_badness
+                    if r ~= par.active then
+                        local q = checked_expansion and {
+                            id             = delta_code,
+                            subtype        = nosubtype_code,
+                            next           = r,
+                            size           = cur_active_width.size           - break_width.size,
+                            stretch        = cur_active_width.stretch        - break_width.stretch,
+                            fi             = cur_active_width.fi             - break_width.fi,
+                            fil            = cur_active_width.fil            - break_width.fil,
+                            fill           = cur_active_width.fill           - break_width.fill,
+                            filll          = cur_active_width.filll          - break_width.filll,
+                            shrink         = cur_active_width.shrink         - break_width.shrink,
+                            adjust_stretch = cur_active_width.adjust_stretch - break_width.adjust_stretch,
+                            adjust_shrink  = cur_active_width.adjust_shrink  - break_width.adjust_shrink,
+                        } or {
+                            id             = delta_code,
+                            subtype        = nosubtype_code,
+                            next           = r,
+                            size           = cur_active_width.size           - break_width.size,
+                            stretch        = cur_active_width.stretch        - break_width.stretch,
+                            fi             = cur_active_width.fi             - break_width.fi,
+                            fil            = cur_active_width.fil            - break_width.fil,
+                            fill           = cur_active_width.fill           - break_width.fill,
+                            filll          = cur_active_width.filll          - break_width.filll,
+                            shrink         = cur_active_width.shrink         - break_width.shrink,
+                        }
+                     -- q.next = r -- already done
+                        prev_r.next = q
+                        prev_prev_r = prev_r
+                        prev_r = q
+                    end
+                end
+                if r == par.active then
+                    return
+                end
+                if line_number > par.easy_line then
+                    old_line_number = max_halfword - 1
+                    line_width = par.second_width
+                else
+                    old_line_number = line_number
+                    if line_number > par.last_special_line then
+                        line_width = par.second_width
+                    elseif par.par_shape_ptr then
+                        line_width = par.par_shape_ptr[line_number][2]
+                    else
+                        line_width = par.first_width
+                    end
+                end
+            end
+            local artificial_demerits = false -- has d been forced to zero
+            local shortfall = line_width - cur_active_width.size - par.internal_right_box_width -- used in badness calculations
+            if not r.break_node then
+                shortfall = shortfall - par.init_internal_left_box_width
+            else
+                shortfall = shortfall - (r.break_node.passive_last_left_box_width or 0)
+            end
+            local pw, lp, rp -- used later on
+            if protrude_chars > 1 then
+                -- this is quite time consuming
+                local b = r.break_node
+                local l = b and b.cur_break or first_p
+                local o = cur_p and cur_p.prev
+                if cur_p and cur_p.id == disc_code and cur_p.pre then
+                    o = slide_nodes(cur_p.pre)
+                else
+                    o = find_protchar_right(l,o)
+                end
+                if o and o.id == glyph_code then
+                    pw, rp = right_pw(o)
+                    shortfall = shortfall + pw
+                end
+                local id = l.id
+                if id == glyph_code then
+                    -- ok ?
+                elseif id == disc_code and l.post then
+                    l = l.post -- TODO: first char could be a disc
+                else
+                    l = find_protchar_left(l)
+                end
+                if l and l.id == glyph_code then
+                    pw, lp = left_pw(l)
+                    shortfall = shortfall + pw
+                end
+            end
+            if checked_expansion and shortfall ~= 0 then
+                local margin_kern_stretch = 0
+                local margin_kern_shrink  = 0
+                if protrude_chars > 1 then
+                    if lp then
+--                         margin_kern_stretch, margin_kern_shrink = cal_margin_kern_var(lp)
+local data = expansions[lp.font][lp.char]
+if data then
+    margin_kern_stretch, margin_kern_shrink = data.glyphstretch, data.glyphshrink
+end
+                    end
+                    if rp then
+--                         local mka, mkb = cal_margin_kern_var(rp)
+--                         margin_kern_stretch = margin_kern_stretch + mka
+--                         margin_kern_shrink  = margin_kern_shrink  + mkb
+local data = expansions[lp.font][lp.char]
+if data then
+    margin_kern_stretch = margin_kern_stretch + data.glyphstretch
+    margin_kern_shrink  = margin_kern_shrink  + data.glyphshrink
+end
+                    end
+                end
+                local total = cur_active_width.adjust_stretch + margin_kern_stretch
+                if shortfall > 0 and total > 0 then
+                    if total > shortfall then
+                        shortfall = total / (par.max_stretch_ratio / par.cur_font_step) / 2
+                    else
+                        shortfall = shortfall - total
+                    end
+                else
+                    total = cur_active_width.adjust_shrink + margin_kern_shrink
+                    if shortfall < 0 and total > 0 then
+                        if total > - shortfall then
+                            shortfall = - total / (par.max_shrink_ratio / par.cur_font_step) / 2
+                        else
+                            shortfall = shortfall + total
+                        end
+                    end
+                end
+                par.font_steps[line_number] = par.cur_font_step -- mine
+            else
+                par.font_steps[line_number] = 0 -- mine
+            end
+            local b = 0
+            local g = 0
+            local fit_class = fit_decent_class
+            local found = false
+            if shortfall > 0  then
+                if cur_active_width.fi ~= 0 or cur_active_width.fil ~= 0 or cur_active_width.fill ~= 0 or cur_active_width.filll ~= 0 then
+                    if not do_last_line_fit then
+                        -- okay
+                    elseif not cur_p then
+                        found, shortfall, fit_class, g, b = lastlinecrap(shortfall,r.active_short,r.active_glue,cur_active_width,par.fill_width,par.last_line_fit)
+                    else
+                        shortfall = 0
+                    end
+                else
+                    local stretch = cur_active_width.stretch
+                    if shortfall > 7230584 and stretch < 1663497 then
+                        b = infinite_badness
+                        fit_class = fit_very_loose_class
+                    else
+                        b = calculate_badness(shortfall,stretch)
+                        if b > 99 then
+                            fit_class = fit_very_loose_class
+                        elseif b > 12 then
+                            fit_class = fit_loose_class
+                        else
+                            fit_class = fit_decent_class
+                        end
+                    end
+                end
+            else
+                local shrink = cur_active_width.shrink
+                if -shortfall > shrink then
+                    b = infinite_badness + 1
+                else
+                    b = calculate_badness(-shortfall,shrink)
+                end
+                if b > 12 then
+                    fit_class = fit_tight_class
+                else
+                    fit_class = fit_decent_class
+                end
+            end
+            if do_last_line_fit and not found then
+                if not cur_p then
+                 -- g = 0
+                    shortfall = 0
+                elseif shortfall > 0 then
+                    g = cur_active_width.stretch
+                elseif shortfall < 0 then
+                    g = cur_active_width.shrink
+                else
+                    g = 0
+                end
+            end
+            -- ::FOUND::
+            local continue_only = false -- brrr
+            if b > infinite_badness or pi == eject_penalty then
+                if final_pass and par.minimum_demerits == awful_badness and r.next == par.active and prev_r == par.active then
+                    artificial_demerits = true -- set demerits zero, this break is forced
+                    node_r_stays_active = false
+                elseif b > par.threshold then
+                    prev_r, r = deactivate_node(par,prev_prev_r,prev_r,r,cur_active_width,checked_expansion)
+                    continue_only = true
+                else
+                    node_r_stays_active = false
+                end
+            else
+                prev_r = r
+                if b > par.threshold then
+                    continue_only = true
+                else
+                    node_r_stays_active = true
+                end
+            end
+            if not continue_only then
+                local d = 0
+                if not artificial_demerits then
+                    d = par.line_penalty + b
+                    if (d >= 0 and d or -d) >= 10000 then -- abs(d)
+                        d = 100000000
+                    else
+                        d = d * d
+                    end
+                    if pi == 0 then
+                        -- nothing
+                    elseif pi > 0 then
+                        d = d + pi * pi
+                    elseif pi > eject_penalty then
+                        d = d - pi * pi
+                    end
+                    if break_type == hyphenated_code and r.id == hyphenated_code then
+                        if cur_p then
+                            d = d + par.double_hyphen_demerits
+                        else
+                            d = d + par.final_hyphen_demerits
+                        end
+                    end
+                    local delta = fit_class - r.subtype
+                    if (delta >= 0 and delta or -delta) > 1 then -- abs(delta)
+                        d = d + par.adj_demerits
+                    end
+                end
+                if tracing_paragraphs then
+                    diagnostics.feasible_break(par,cur_p,r,b,pi,d,artificial_demerits)
+                end
+                d = d + r.total_demerits -- this is the minimum total demerits from the beginning to cur_p via r
+                if d <= minimal_demerits[fit_class] then
+                    minimal_demerits[fit_class] = d
+                    best_place      [fit_class] = r.break_node
+                    best_pl_line    [fit_class] = line_number
+                    if do_last_line_fit then
+                        best_pl_short[fit_class] = shortfall
+                        best_pl_glue [fit_class] = g
+                        if trace_lastlinefit then
+                            report_parbuilders("storing last line fit short %a and glue %p in class %a",shortfall,g,fit_class)
+                        end
+                    end
+                    if d < par.minimum_demerits then
+                        par.minimum_demerits = d
+                    end
+                end
+                if not node_r_stays_active then
+                    prev_r, r = deactivate_node(par,prev_prev_r,prev_r,r,cur_active_width,checked_expansion)
+                end
+            end
+        end
+    end
+end
+
+local function kern_break(par, cur_p, first_p, checked_expansion) -- move inline if needed
+    local v = cur_p.next
+    if par.auto_breaking and v.id == glue_code then
+        try_break(0, unhyphenated_code, par, first_p, cur_p, checked_expansion)
+    end
+    local active_width = par.active_width
+    if cur_p.id ~= math_code then
+        active_width.size = active_width.size + cur_p.kern
+    else
+        active_width.size = active_width.size + cur_p.surround
+    end
+end
+
+-- we can call the normal one for simple box building in the otr so we need
+-- frequent enabling/disabling
+
+local temp_head = new_temp()
+
+function constructors.methods.basic(head,d)
+    if trace_basic then
+        report_parbuilders("starting at %a",head)
+    end
+
+    local par = initialize_line_break(head,d)
+
+    local checked_expansion  = par.checked_expansion
+    local active_width       = par.active_width
+    local disc_width         = par.disc_width
+    local background         = par.background
+    local tracing_paragraphs = par.tracing_paragraphs
+
+    local dirstack = new_dir_stack()
+
+    if tracing_paragraphs then
+        diagnostics.start()
+        if par.pretolerance >= 0 then
+            diagnostics.current_pass(par,"firstpass")
+        end
+    end
+
+    while true do
+        reset_meta(par)
+        if par.threshold > infinite_badness then
+            par.threshold = infinite_badness
+        end
+        par.active.next = {
+            id             = unhyphenated_code,
+            subtype        = fit_decent_class,
+            next           = par.active,
+            break_node     = nil,
+            line_number    = par.first_line + 1,
+            total_demerits = 0,
+            active_short   = 0,
+            active_glue    = 0,
+        }
+        active_width.size           = background.size
+        active_width.stretch        = background.stretch
+        active_width.fi             = background.fi
+        active_width.fil            = background.fil
+        active_width.fill           = background.fill
+        active_width.filll          = background.filll
+        active_width.shrink         = background.shrink
+
+        if checked_expansion then
+            active_width.adjust_stretch = 0
+            active_width.adjust_shrink  = 0
+        end
+
+        par.passive                 = nil -- = 0
+        par.printed_node            = temp_head -- only when tracing, shared
+        par.printed_node.next       = head
+        par.pass_number             = 0
+        par.auto_breaking           = true
+
+        local cur_p                 = head
+        local first_p               = cur_p
+
+        par.font_in_short_display   = 0
+
+        if cur_p and cur_p.id == whatsit_code and cur_p.subtype == localpar_code then
+            par.init_internal_left_box       = cur_p.box_left
+            par.init_internal_left_box_width = cur_p.box_left_width
+            par.internal_pen_inter           = cur_p.pen_inter
+            par.internal_pen_broken          = cur_p.pen_broken
+            par.internal_left_box            = par.init_internal_left_box
+            par.internal_left_box_width      = par.init_internal_left_box_width
+            par.internal_right_box           = cur_p.box_right
+            par.internal_right_box_width     = cur_p.box_right_width
+        end
+
+        -- all passes are combined in this loop so maybe we should split this into
+        -- three function calls; we then also need to do the wrap_up elsewhere
+
+        -- split into normal and expansion loop
+
+        -- use an active local
+
+        local fontexp, lastfont -- we can pass fontexp to calculate width if needed
+
+        while cur_p and par.active.next ~= par.active do
+            while cur_p and cur_p.id == glyph_code do
+                if is_rotated[par.line_break_dir] then
+                    active_width.size = active_width.size + cur_p.height + cur_p.depth
+                else
+                    active_width.size = active_width.size + cur_p.width
+                end
+                if checked_expansion then
+                    local data= checked_expansion[cur_p.font]
+                    if data then
+                        local currentfont = cur_p.font
+                        if currentfont ~= lastfont then
+                            fontexps = checked_expansion[currentfont] -- a bit redundant for the par line packer
+                            lastfont = currentfont
+                        end
+                        if fontexps then
+                            local expansion = fontexps[cur_p.char]
+                            if expansion then
+                                active_width.adjust_stretch = active_width.adjust_stretch + expansion.glyphstretch
+                                active_width.adjust_shrink  = active_width.adjust_shrink  + expansion.glyphshrink
+                            end
+                        end
+                    end
+                end
+                cur_p = cur_p.next
+            end
+            if not cur_p then -- TODO
+                report_parbuilders("problems with linebreak_tail")
+                os.exit()
+            end
+            local id = cur_p.id
+            if id == hlist_code or id == vlist_code then
+                if is_parallel[cur_p.dir][par.line_break_dir] then
+                    active_width.size = active_width.size + cur_p.width
+                else
+                    active_width.size = active_width.size + cur_p.depth + cur_p.height
+                end
+            elseif id == glue_code then
+                if par.auto_breaking then
+                    local prev_p = cur_p.prev
+                    if prev_p and prev_p ~= temp_head then
+                        local id = prev_p.id
+                        if id == glyph_code or
+                            (id < math_code and (id ~= whatsit_code or prev_p.subtype ~= dir_code)) or -- was: precedes_break(prev_p)
+                            (id == kern_code and prev_p.subtype ~= userkern_code) then
+                            try_break(0, unhyphenated_code, par, first_p, cur_p, checked_expansion)
+                        end
+                    end
+                end
+                local spec = check_shrinkage(par,cur_p.spec)
+                local order = stretch_orders[spec.stretch_order]
+                cur_p.spec = spec
+                active_width.size   = active_width.size   + spec.width
+                active_width[order] = active_width[order] + spec.stretch
+                active_width.shrink = active_width.shrink + spec.shrink
+            elseif id == disc_code then
+                local subtype = cur_p.subtype
+                if subtype ~= second_disc_code then -- are there still second_disc_code in luatex
+                    local line_break_dir = par.line_break_dir
+                    if par.second_pass then -- todo: make second pass local
+                        local actual_pen = subtype == automatic_disc_code and par.ex_hyphen_penalty or par.hyphen_penalty
+                        local pre = cur_p.pre
+                        if not pre then    --  trivial pre-break
+                            disc_width.size = 0
+                            if checked_expansion then
+                                disc_width.adjust_stretch = 0
+                                disc_width.adjust_shrink  = 0
+                            end
+                            try_break(actual_pen, hyphenated_code, par, first_p, cur_p, checked_expansion)
+                        else
+                            local size, adjust_stretch, adjust_shrink = add_to_width(line_break_dir,checked_expansion,pre)
+                            disc_width.size   = size
+                            active_width.size = active_width.size + size
+                            if checked_expansion then
+                                disc_width.adjust_stretch   = adjust_stretch
+                                disc_width.adjust_shrink    = adjust_shrink
+                                active_width.adjust_stretch = active_width.adjust_stretch + adjust_stretch
+                                active_width.adjust_shrink  = active_width.adjust_shrink  + adjust_shrink
+                            else
+                             -- disc_width.adjust_stretch = 0
+                             -- disc_width.adjust_shrink  = 0
+                            end
+                            try_break(actual_pen, hyphenated_code, par, first_p, cur_p, checked_expansion)
+                            if subtype == first_disc_code then
+                                local cur_p_next = cur_p.next
+                                if cur_p_next.id ~= disc_code or cur_p_next.subtype ~= second_disc_code then
+                                    report_parbuilders("unsupported disc at location %a",1)
+                                else
+                                    local pre = cur_p_next.pre
+                                    if pre then
+                                        local size, adjust_stretch, adjust_shrink = add_to_width(line_break_dir,checked_expansion,pre)
+                                        disc_width.size = disc_width.size + size
+                                        if checked_expansion then
+                                            disc_width.adjust_stretch = disc_width.adjust_stretch + adjust_stretch
+                                            disc_width.adjust_shrink  = disc_width.adjust_shrink  + adjust_shrink
+                                        end
+                                        try_break(actual_pen, hyphenated_code, par, first_p, cur_p_next, checked_expansion)
+                                        --
+                                        -- I will look into this some day ... comment in linebreak.w says that this fails,
+                                        -- maybe this is what Taco means with his comment in the luatex manual.
+                                        --
+                                        -- do_one_seven_eight(sub_disc_width_from_active_width);
+                                        -- do_one_seven_eight(reset_disc_width);
+                                        -- s = vlink_no_break(vlink(cur_p));
+                                        -- add_to_widths(s, line_break_dir, pdf_adjust_spacing,disc_width);
+                                        -- ext_try_break(...,first_p,vlink(cur_p));
+                                        --
+                                    else
+                                        report_parbuilders("unsupported disc at location %a",2)
+                                    end
+                                end
+                            end
+                            -- beware, we cannot restore to a saved value as the try_break adapts active_width
+                            active_width.size = active_width.size - disc_width.size
+                            if checked_expansion then
+                                active_width.adjust_stretch = active_width.adjust_stretch - disc_width.adjust_stretch
+                                active_width.adjust_shrink  = active_width.adjust_shrink  - disc_width.adjust_shrink
+                            end
+                        end
+                    end
+                    local replace = cur_p.replace
+                    if replace then
+                        local size, adjust_stretch, adjust_shrink = add_to_width(line_break_dir,checked_expansion,replace)
+                        active_width.size = active_width.size + size
+                        if checked_expansion then
+                            active_width.adjust_stretch = active_width.adjust_stretch + adjust_stretch
+                            active_width.adjust_shrink  = active_width.adjust_shrink  + adjust_shrink
+                        end
+                    end
+                end
+            elseif id == kern_code then
+                if cur_p.subtype == userkern_code then
+                    kern_break(par,cur_p,first_p, checked_expansion)
+                else
+                    active_width.size = active_width.size + cur_p.kern
+                    if checked_expansion and cur_p.subtype == kerning_code then
+                        local d = cur_p.kern
+                        if d ~= 0 then
+                            local stretch, shrink = kern_stretch_shrink(cur_p,d)
+                            active_width.adjust_stretch = active_width.adjust_stretch + stretch
+                            active_width.adjust_shrink  = active_width.adjust_shrink  + shrink
+                        end
+                    end
+                end
+            elseif id == math_code then
+                par.auto_breaking = cur_p.subtype == endmath_code
+                kern_break(par,cur_p, first_p, checked_expansion)
+            elseif id == rule_code then
+                active_width.size = active_width.size + cur_p.width
+            elseif id == penalty_code then
+                try_break(cur_p.penalty, unhyphenated_code, par, first_p, cur_p, checked_expansion)
+            elseif id == whatsit_code then
+                local subtype = cur_p.subtype
+                if subtype == localpar_code then
+                    par.internal_pen_inter       = cur_p.pen_inter
+                    par.internal_pen_broken      = cur_p.pen_broken
+                    par.internal_left_box        = cur_p.box_left
+                    par.internal_left_box_width  = cur_p.box_left_width
+                    par.internal_right_box       = cur_p.box_right
+                    par.internal_right_box_width = cur_p.box_right_width
+                elseif subtype == dir_code then
+                    par.line_break_dir = checked_line_dir(dirstack) or par.line_break_dir
+                else
+                    local get_width = get_whatsit_width[subtype]
+                    if get_width then
+                        active_width.size = active_width.size + get_width(cur_p)
+                    end
+                end
+            elseif id == mark_code or id == ins_code or id == adjust_code then
+                -- skip
+            else
+                report_parbuilders("node of type %a found in paragraph",type(id))
+            end
+            cur_p = cur_p.next
+        end
+        if not cur_p then
+            try_break(eject_penalty, hyphenated_code, par, first_p, cur_p, checked_expansion)
+            local p_active = par.active
+            local n_active = p_active.next
+            if n_active ~= p_active then
+                local r = n_active
+                par.fewest_demerits = awful_badness
+                repeat -- use local d
+                    if r.id ~= delta_code and r.total_demerits < par.fewest_demerits then
+                        par.fewest_demerits = r.total_demerits
+                        par.best_bet = r
+                    end
+                    r = r.next
+                until r == p_active
+                par.best_line = par.best_bet.line_number
+                local asked_looseness = par.looseness
+                if asked_looseness == 0 then
+                    return wrap_up(par)
+                end
+                local r = n_active
+                local actual_looseness = 0
+                -- minimize assignments to par but happens seldom
+                repeat
+                    if r.id ~= delta_code then
+                        local line_diff = r.line_number - par.best_line
+                        par.line_diff = line_diff
+                        if (line_diff < actual_looseness and asked_looseness <= line_diff)   or
+                           (line_diff > actual_looseness and asked_looseness >= line_diff) then
+                            par.best_bet = r
+                            actual_looseness = line_diff
+                            par.fewest_demerits = r.total_demerits
+                        elseif line_diff == actual_looseness and r.total_demerits < par.fewest_demerits then
+                            par.best_bet = r
+                            par.fewest_demerits = r.total_demerits
+                        end
+                    end
+                    r = r.next
+                until r == p_active -- weird, loop list?
+                par.best_line = par.best_bet.line_number
+                if actual_looseness == asked_looseness or par.final_pass then
+                    return wrap_up(par)
+                end
+            end
+        end
+        reset_meta(par) -- clean up the memory by removing the break nodes
+        if not par.second_pass then
+            if tracing_paragraphs then
+                diagnostics.current_pass(par,"secondpass")
+            end
+            par.threshold = par.tolerance
+            par.second_pass = true
+            par.final_pass = par.emergency_stretch <= 0
+        else
+            if tracing_paragraphs then
+                diagnostics.current_pass(par,"emergencypass")
+            end
+            par.background.stretch = par.background.stretch + par.emergency_stretch
+            par.final_pass = true
+        end
+    end
+    return wrap_up(par)
+end
+
+-- standard tex logging .. will be adapted .. missing font names and to many []
+
+local function write_esc(cs)
+    local esc = tex.escapechar
+    if esc then
+        write("log",utfchar(esc),cs)
+    else
+        write("log",cs)
+    end
+end
+
+function diagnostics.start()
+end
+
+function diagnostics.stop()
+    write_nl("log",'')
+end
+
+function diagnostics.current_pass(par,what)
+    write_nl("log",format("@%s",what))
+end
+
+local function short_display(a,font_in_short_display)
+    while a do
+        local id = a.id
+        if id == glyph_code then
+            local font = a.font
+            if font ~= font_in_short_display then
+                write("log",tex.fontidentifier(font) .. ' ')
+                font_in_short_display = font
+            end
+            if a.subtype == ligature_code then
+                font_in_short_display = short_display(a.components,font_in_short_display)
+            else
+                write("log",utfchar(a.char))
+            end
+--         elseif id == rule_code then
+--             write("log","|")
+--         elseif id == glue_code then
+--             if a.spec.writable then
+--                 write("log"," ")
+--             end
+--         elseif id == math_code then
+--             write("log","$")
+        elseif id == disc_code then
+            font_in_short_display = short_display(a.pre,font_in_short_display)
+            font_in_short_display = short_display(a.post,font_in_short_display)
+        else -- no explicit checking
+            write("log",format("[%s]",nodecodes[id]))
+        end
+        a = a.next
+    end
+    return font_in_short_display
+end
+
+diagnostics.short_display = short_display
+
+function diagnostics.break_node(par, q, fit_class, break_type, cur_p) -- %d ?
+    local passive = par.passive
+    local typ_ind = break_type == hyphenated_code and '-' or ""
+    if par.do_last_line_fit then
+        local s = number.toscaled(q.active_short)
+        local g = number.toscaled(q.active_glue)
+        if cur_p then
+            write_nl("log",format("@@%d: line %d.%d%s t=%s s=%s g=%s",
+                passive.serial or 0,q.line_number-1,fit_class,typ_ind,q.total_demerits,s,g))
+        else
+            write_nl("log",format("@@%d: line %d.%d%s t=%s s=%s a=%s",
+                passive.serial or 0,q.line_number-1,fit_class,typ_ind,q.total_demerits,s,g))
+        end
+    else
+        write_nl("log",format("@@%d: line %d.%d%s t=%s",
+            passive.serial or 0,q.line_number-1,fit_class,typ_ind,q.total_demerits))
+    end
+    if not passive.prev_break then
+        write("log"," -> @0")
+    else
+        write("log",format(" -> @%d", passive.prev_break.serial or 0))
+    end
+end
+
+function diagnostics.feasible_break(par, cur_p, r, b, pi, d, artificial_demerits)
+    local printed_node = par.printed_node
+    if printed_node ~= cur_p then
+        write_nl("log","")
+        if not cur_p then
+            par.font_in_short_display = short_display(printed_node.next,par.font_in_short_display)
+        else
+            local save_link = cur_p.next
+            cur_p.next = nil
+            write_nl("log","")
+            par.font_in_short_display = short_display(printed_node.next,par.font_in_short_display)
+            cur_p.next = save_link
+        end
+        par.printed_node = cur_p
+    end
+    write_nl("log","@")
+    if not cur_p then
+        write_esc("par")
+    else
+        local id = cur_p.id
+        if id == glue_code then
+            -- print nothing
+        elseif id == penalty_code then
+            write_esc("penalty")
+        elseif id == disc_code then
+            write_esc("discretionary")
+        elseif id == kern_code then
+            write_esc("kern")
+        elseif id == math_code then
+            write_esc("math")
+        else
+            write_esc("unknown")
+        end
+    end
+    local via, badness, demerits = 0, '*', '*'
+    if r.break_node then
+        via = r.break_node.serial or 0
+    end
+    if b <= infinite_badness then
+        badness = tonumber(d) -- format("%d", b)
+    end
+    if not artificial_demerits then
+        demerits = tonumber(d) -- format("%d", d)
+    end
+    write("log",format(" via @%d b=%s p=%s d=%s", via, badness, pi, demerits))
+end
+
+-- reporting --
+
+statistics.register("alternative parbuilders", function()
+    if nofpars > 0 then
+        return format("%s paragraphs, %s lines (%s protruded, %s adjusted)", nofpars, noflines, nofprotrudedlines, nofadjustedlines)
+    end
+end)
+
+-- actually scaling kerns is not such a good idea and it will become
+-- configureable
+
+-- This is no way a replacement for the built in (fast) packer
+-- it's just an alternative for special (testing) purposes.
+--
+-- We could use two hpacks: one to be used in the par builder
+-- and one to be used for other purposes. The one in the par
+-- builder is much more simple as it does not need the expansion
+-- code but only need to register the effective expansion factor
+-- with the glyph.
+
+local function glyph_width_height_depth(curdir,pdir,p)
+    if is_rotated[curdir] then
+        if is_parallel[curdir][pdir] then
+            local half = (p.height + p.depth) / 2
+            return p.width, half, half
+        else
+            local half = p.width / 2
+            return p.height + p.depth, half, half
+        end
+    elseif is_rotated[pdir] then
+        if is_parallel[curdir][pdir] then
+            local half = (p.height + p.depth) / 2
+            return p.width, half, half
+        else
+            return p.height + p.depth, p.width, 0 -- weird
+        end
+    else
+        if glyphdir_is_equal[curdir][pdir] then
+            return p.width, p.height, p.depth
+        elseif is_opposite[curdir][pdir] then
+            return p.width, p.depth, p.height
+        else -- can this happen?
+            return p.height + p.depth, p.width, 0 -- weird
+        end
+    end
+end
+
+local function pack_width_height_depth(curdir,pdir,p)
+    if is_rotated[curdir] then
+        if is_parallel[curdir][pdir] then
+            local half = (p.height + p.depth) / 2
+            return p.width, half, half
+        else -- can this happen?
+            local half = p.width / 2
+            return p.height + p.depth, half, half
+        end
+    else
+        if pardir_is_equal[curdir][pdir] then
+            return p.width, p.height, p.depth
+        elseif is_opposite[curdir][pdir] then
+            return p.width, p.depth, p.height
+        else -- weird dimensions, can this happen?
+         -- return p.width, p.depth, p.height
+            return p.height + p.depth, p.width, 0
+        end
+    end
+end
+
+-- local function xpack(head,width,method,direction,analysis)
+--
+--     -- inspect(analysis)
+--
+--     local expansion         = method == "cal_expand_ratio"
+--     local natural           = analysis.size
+--     local font_stretch      = analysis.adjust_stretch
+--     local font_shrink       = analysis.adjust_shrink
+--     local font_expand_ratio = 0
+--     local delta             = width - natural
+--
+--     local hlist             = new_node("hlist")
+--
+--     hlist.list              = head
+--     hlist.dir               = direction or tex.textdir
+--     hlist.width             = width
+--     hlist.height            = height
+--     hlist.depth             = depth
+--
+--     if delta == 0 then
+--
+--         hlist.glue_sign  = 0
+--         hlist.glue_order = 0
+--         hlist.glue_set   = 0
+--
+--     else
+--
+--         local order = analysis.filll ~= 0 and fillcodes.filll or
+--                       analysis.fill  ~= 0 and fillcodes.fill  or
+--                       analysis.fil   ~= 0 and fillcodes.fil   or
+--                       analysis.fi    ~= 0 and fillcodes.fi    or 0
+--
+--         if delta > 0 then
+--
+--             if expansion and order == 0 and font_stretch > 0 then
+--                 font_expand_ratio = (delta/font_stretch) * 1000
+--             else
+--                 local stretch = analysis.stretch
+--                 if stretch ~= 0 then
+--                     hlist.glue_sign  = 1 -- stretch
+--                     hlist.glue_order = order
+--                     hlist.glue_set   = delta/stretch
+--                 else
+--                     hlist.glue_sign  = 0 -- nothing
+--                     hlist.glue_order = order
+--                     hlist.glue_set   = 0
+--                 end
+--             end
+-- print("stretch",hlist.glue_sign,hlist.glue_order,hlist.glue_set)
+--
+--         else
+--
+--             if expansion and order == 0 and font_shrink > 0 then
+--                 font_expand_ratio = (delta/font_shrink) * 1000
+--             else
+--                 local shrink = analysis.shrink
+--                 if shrink ~= 0 then
+--                     hlist.glue_sign  = 2 -- shrink
+--                     hlist.glue_order = order
+--                     hlist.glue_set   = - delta/shrink
+--                 else
+--                     hlist.glue_sign  = 0 -- nothing
+--                     hlist.glue_order = order
+--                     hlist.glue_set   = 0
+--                 end
+--             end
+-- print("shrink",hlist.glue_sign,hlist.glue_order,hlist.glue_set)
+--
+--         end
+--
+--     end
+--
+--     if not expansion or font_expand_ratio == 0 then
+--         -- nothing
+--     elseif font_expand_ratio > 0 then
+--         if font_expand_ratio > 1000 then
+--             font_expand_ratio = 1000
+--         end
+--         local current = head
+--         while current do
+--             local id = current.id
+--             if id == glyph_code then
+--                 local stretch, shrink = char_stretch_shrink(current) -- get only one
+--                 if stretch then
+--                     if trace_expansion then
+--                         setnodecolor(g,"hz:positive")
+--                     end
+--                     current.expansion_factor = font_expand_ratio * stretch
+--                 end
+--             elseif id == kern_code then
+--                 local kern = current.kern
+--                 if kern ~= 0 and current.subtype == kerning_code then
+--                     current.kern = font_expand_ratio * current.kern
+--                 end
+--             end
+--             current = current.next
+--         end
+--     elseif font_expand_ratio < 0 then
+--         if font_expand_ratio < -1000 then
+--             font_expand_ratio = -1000
+--         end
+--         local current = head
+--         while current do
+--             local id = current.id
+--             if id == glyph_code then
+--                 local stretch, shrink = char_stretch_shrink(current) -- get only one
+--                 if shrink then
+--                     if trace_expansion then
+--                         setnodecolor(g,"hz:negative")
+--                     end
+--                     current.expansion_factor = font_expand_ratio * shrink
+--                 end
+--             elseif id == kern_code then
+--                 local kern = current.kern
+--                 if kern ~= 0 and current.subtype == kerning_code then
+--                     current.kern = font_expand_ratio * current.kern
+--                 end
+--             end
+--             current = current.next
+--         end
+--     end
+--     return hlist, 0
+-- end
+
+-- local expansion_stack = { } -- no dealloc
+
+local function hpack(head,width,method,direction) -- fast version when head = nil
+
+    -- we can pass the adjust_width and adjust_height so that we don't need to recalculate them but
+    -- with the glue mess it's less trivial as we lack detail
+
+    local hlist = new_node("hlist")
+
+    if head == nil then
+        return hlist, 0
+    end
+
+    local cal_expand_ratio  = method == "cal_expand_ratio" -- "subst_ex_font" -- is gone
+
+    direction               = direction or tex.textdir
+
+    local pack_begin_line   = 0
+    local line              = 0
+
+    local height            = 0
+    local depth             = 0
+    local natural           = 0
+    local font_stretch      = 0
+    local font_shrink       = 0
+    local font_expand_ratio = 0
+    local last_badness      = 0
+    local disc_stack        = { }
+    local disc_level        = 0
+    local expansion_stack   = cal_expand_ratio and { } -- todo: optionally pass this
+    local expansion_index   = 0
+    local total_stretch     = { [0] = 0, 0, 0, 0, 0 }
+    local total_shrink      = { [0] = 0, 0, 0, 0, 0 }
+
+    local hpack_dir         = direction
+
+    local adjust_head       = texlists.adjust_head
+    local pre_adjust_head   = texlists.pre_adjust_head
+    local adjust_tail       = adjust_head and slide_nodes(adjust_head)
+    local pre_adjust_tail   = pre_adjust_head and slide_nodes(pre_adjust_head)
+
+    hlist.list = head
+    hlist.dir  = hpack_dir
+
+    new_dir_stack(hpack_dir)
+
+    local checked_expansion = false
+
+    if cal_expand_ratio then
+        checked_expansion = { }
+        setmetatableindex(checked_expansion,check_expand_lines)
+    end
+
+    -- this one also needs to check the font, so in the end indeed we might end up with two variants
+
+    local fontexps, lastfont
+
+    local current = head
+
+    while current do
+        local id = current.id
+        if id == glyph_code then
+            if cal_expand_ratio then
+                local currentfont = current.font
+                if currentfont ~= lastfont then
+                    fontexps = checked_expansion[currentfont] -- a bit redundant for the par line packer
+                    lastfont = currentfont
+                end
+                if fontexps then
+                    local expansion = fontexps[current.char]
+                    if expansion then
+                        font_stretch = font_stretch + expansion.glyphstretch
+                        font_shrink  = font_shrink  + expansion.glyphshrink
+                        expansion_index = expansion_index + 1
+                        expansion_stack[expansion_index] = current
+                    end
+                end
+            end
+            -- use inline if no expansion
+            local wd, ht, dp = glyph_width_height_depth(hpack_dir,"TLT",current) -- was TRT ?
+            natural = natural + wd
+            if ht > height then
+                height = ht
+            end
+            if dp > depth then
+                depth = dp
+            end
+            current = current.next
+        elseif id == kern_code then
+            local kern = current.kern
+            if kern == 0 then
+                -- no kern
+            elseif current.subtype == kerning_code then -- check p.kern
+                if cal_expand_ratio then
+                    local stretch, shrink = kern_stretch_shrink(current,kern)
+                    font_stretch = font_stretch + stretch
+                    font_shrink  = font_shrink + shrink
+                    expansion_index = expansion_index + 1
+                    expansion_stack[expansion_index] = current
+                end
+                natural = natural + kern
+            else
+                natural = natural + kern
+            end
+            current = current.next
+        elseif id == disc_code then
+            if current.subtype ~= second_disc_code then
+                -- todo : local stretch, shrink = char_stretch_shrink(s)
+                local replace = current.replace
+                if replace then
+                    disc_level = disc_level + 1
+                    disc_stack[disc_level] = current.next
+                    current = replace
+                else
+                    current = current.next
+                end
+            else
+                -- -- pre post replace
+                --
+                -- local stretch, shrink = char_stretch_shrink(current.pre)
+                -- font_stretch = font_stretch + stretch
+                -- font_shrink  = font_shrink + shrink
+                -- expansion_index = expansion_index + 1
+                -- expansion_stack[expansion_index] = current.pre
+                --
+                current = current.next
+            end
+        elseif id == glue_code then
+            local spec = current.spec
+            natural = natural + spec.width
+            local op = spec.stretch_order
+            local om = spec.shrink_order
+            total_stretch[op] = total_stretch[op] + spec.stretch
+            total_shrink [om] = total_shrink [om] + spec.shrink
+            if current.subtype >= leaders_code then
+                local leader = current.leader
+                local ht = leader.height
+                local dp = leader.depth
+                if ht > height then
+                    height = ht
+                end
+                if dp > depth then
+                    depth = dp
+                end
+            end
+            current = current.next
+        elseif id == hlist_code or id == vlist_code then
+            local sh = current.shift
+            local wd, ht, dp = pack_width_height_depth(hpack_dir,current.dir or hpack_dir,current) -- added: or pack_dir
+            local hs, ds = ht - sh, dp + sh
+            natural = natural + wd
+            if hs > height then
+                height = hs
+            end
+            if ds > depth then
+                depth = ds
+            end
+            current = current.next
+        elseif id == rule_code then
+            local wd = current.width
+            local ht = current.height
+            local dp = current.depth
+            natural = natural + wd
+            if ht > height then
+                height = ht
+            end
+            if dp > depth then
+                depth = dp
+            end
+            current = current.next
+        elseif id == math_code then
+            natural = natural + current.surround
+            current = current.next
+        elseif id == unset_code then
+            local wd = current.width
+            local ht = current.height
+            local dp = current.depth
+            local sh = current.shift
+            local hs = ht - sh
+            local ds = dp + sh
+            natural = natural + wd
+            if hs > height then
+                height = hs
+            end
+            if ds > depth then
+                depth = ds
+            end
+            current = current.next
+        elseif id == ins_code or id == mark_code then
+            local prev = current.prev
+            local next = current.next
+            if adjust_tail then -- todo
+                if next then
+                    next.prev = prev
+                end
+                if prev then
+                    prev.next = next
+                end
+                current.prev = adjust_tail
+                current.next = nil
+                adjust_tail.next = current
+                adjust_tail = current
+            else
+                adjust_head = current
+                adjust_tail = current
+                current.prev = nil
+                current.next = nil
+            end
+            current = next
+        elseif id == adjust_code then
+            local list = current.list
+            if adjust_tail then
+                adjust_tail.next = list
+                adjust_tail = slide_nodes(list)
+            else
+                adjust_head = list
+                adjust_tail = slide_nodes(list)
+            end
+            current = current.next
+        elseif id == whatsit_code then
+            local subtype = current.subtype
+            if subtype == dir_code then
+                hpack_dir = checked_line_dir(stack,current) or hpack_dir
+            else
+                local get_dimensions = get_whatsit_dimensions[subtype]
+                if get_dimensions then
+                    local wd, ht, dp = get_dimensions(current)
+                    natural = natural + wd
+                    if ht > height then
+                        height = ht
+                    end
+                    if dp > depth then
+                        depth = dp
+                    end
+                end
+            end
+            current = current.next
+        elseif id == marginkern_code then
+            if cal_expand_ratio then
+                local glyph = current.glyph
+                local char_pw = current.subtype == leftmargin_code and left_pw or right_pw
+                font_stretch = font_stretch - current.width - char_pw(glyph)
+                font_shrink  = font_shrink  - current.width - char_pw(glyph)
+                expansion_index = expansion_index + 1
+                expansion_stack[expansion_index] = glyph
+            end
+            natural = natural + current.width
+            current = current.next
+        else
+            current = current.next
+        end
+        if not current and disc_level > 0 then
+            current = disc_stack[disc_level]
+            disc_level = disc_level - 1
+        end
+    end
+    if adjust_tail then
+        adjust_tail.next = nil -- todo
+    end
+    if pre_adjust_tail then
+        pre_adjust_tail.next = nil -- todo
+    end
+    if mode == "additional" then
+        width = width + natural
+    end
+    hlist.width  = width
+    hlist.height = height
+    hlist.depth  = depth
+    local delta  = width - natural
+    if delta == 0 then
+        hlist.glue_sign = 0
+        hlist.glue_order = 0
+        hlist.glue_set = 0
+    elseif delta > 0 then
+        local order = (total_stretch[4] ~= 0 and 4 or total_stretch[3] ~= 0 and 3) or
+                      (total_stretch[2] ~= 0 and 2 or total_stretch[1] ~= 0 and 1) or 0
+        if cal_expand_ratio and order == 0 and font_stretch > 0 then
+            font_expand_ratio = (delta/font_stretch) * 1000 -- round(delta/font_stretch * 1000)
+        else
+            local tso = total_stretch[order]
+            if tso ~= 0 then
+                hlist.glue_sign = 1
+                hlist.glue_order = order
+                hlist.glue_set = delta/tso
+            else
+                hlist.glue_sign = 0
+                hlist.glue_order = order
+                hlist.glue_set = 0
+            end
+-- print("stretch",hlist.glue_sign,hlist.glue_order,hlist.glue_set)
+            if order == 0 and hlist.list then
+                last_badness = calculate_badness(delta,total_stretch[0])
+                if last_badness > tex.hbadness then
+                    if last_badness > 100 then
+                        diagnostics.underfull_hbox(hlist,pack_begin_line,line,last_badness)
+                    else
+                        diagnostics.loose_hbox(hlist,pack_begin_line,line,last_badness)
+                    end
+                end
+            end
+        end
+    else
+        local order = total_shrink[4] ~= 0 and 4 or total_shrink[3] ~= 0 and 3
+                   or total_shrink[2] ~= 0 and 2 or total_shrink[1] ~= 0 and 1 or 0
+        if cal_expand_ratio and order == 0 and font_shrink > 0 then
+            font_expand_ratio = (delta/font_shrink) * 1000 -- round(delta/font_shrink * 1000)
+        else -- why was this else commented
+            local tso = total_shrink[order]
+            if tso ~= 0 then
+                hlist.glue_sign  = 2
+                hlist.glue_order = order
+                hlist.glue_set   = -delta/tso
+            else
+                hlist.glue_sign  = 0
+                hlist.glue_order = order
+                hlist.glue_set   = 0
+            end
+-- print("shrink",hlist.glue_sign,hlist.glue_order,hlist.glue_set)
+            if total_shrink[order] < -delta and order == 0 and hlist.list then
+                last_badness = 1000000
+                hlist.glue_set = 1
+                local fuzz = - delta - total_shrink[0]
+                local hfuzz = tex.hfuzz
+                if fuzz > hfuzz or tex.hbadness < 100 then
+                    local overfullrule = tex.overfullrule
+                    if fuzz > hfuzz and overfullrule > 0 then
+                        -- weird, is always called and no rules shows up
+                        slide_nodes(list).next = new_rule(overfullrule,nil,nil,hlist.dir)
+                    end
+                    diagnostics.overfull_hbox(hlist,pack_begin_line,line,-delta) -- - added
+                end
+            elseif order == 0 and hlist.list and last_badness > tex.hbadness then
+                diagnostics.bad_hbox(hlist,pack_begin_line,line,last_badness)
+            end
+        end
+    end
+    if cal_expand_ratio and font_expand_ratio ~= 0 then
+        -- if font_expand_ratio > 1000 then
+        --     font_expand_ratio = 1000
+        -- elseif font_expand_ratio < -1000 then
+        --     font_expand_ratio = -1000
+        -- end
+
+        local fontexps, lastfont
+
+        if font_expand_ratio > 0 then
+            for i=1,expansion_index do
+                local g = expansion_stack[i]
+                if g.id == glyph_code then
+                    local currentfont = g.font
+                    if currentfont ~= lastfont then
+                        fontexps = expansions[currentfont]
+                        lastfont = currentfont
+                    end
+                    local data = fontexps[g.char]
+                    if trace_expansion then
+                        setnodecolor(g,"hz:positive")
+                    end
+                    g.expansion_factor = font_expand_ratio * data.glyphstretch
+                else
+                    local stretch, shrink = kern_stretch_shrink(g,g.kern)
+                    g.expansion_factor = font_expand_ratio * stretch
+                end
+            end
+        else
+            for i=1,expansion_index do
+                local g = expansion_stack[i]
+                if g.id == glyph_code then
+                    local currentfont = g.font
+                    if currentfont ~= lastfont then
+                        fontexps = expansions[currentfont]
+                        lastfont = currentfont
+                    end
+                    local data = fontexps[g.char]
+                    if trace_expansion then
+                        setnodecolor(g,"hz:negative")
+                    end
+                    g.expansion_factor = font_expand_ratio * data.glyphshrink
+                else
+                    local stretch, shrink = kern_stretch_shrink(g,g.kern)
+                    g.expansion_factor = font_expand_ratio * shrink
+                end
+            end
+        end
+
+    end
+    return hlist, last_badness
+end
+
+nodes.hpack = hpack
+hpack_nodes = hpack -- comment this for old fashioned expansion
+xpack_nodes = hpack -- comment this for old fashioned expansion
+
+local function common_message(hlist,pack_begin_line,line,str)
+    write_nl("")
+    if status.output_active then -- unset
+        write(str," has occurred while \\output is active")
+    end
+    if pack_begin_line > 0 then
+        write(str," in paragraph at lines ",pack_begin_line,"--",line)
+    elseif pack_begin_line < 0 then
+        write(str," in alignment at lines ",-pack_begin_line,"--",line)
+    else
+        write(str," detected at line ",line)
+    end
+    write_nl("")
+    diagnostics.short_display(hlist.list,false)
+    write_nl("")
+ -- diagnostics.start()
+ -- show_box(hlist.list)
+ -- diagnostics.stop()
+end
+
+function diagnostics.overfull_hbox(hlist,pack_begin_line,line,d)
+    common_message(hlist,pack_begin_line,line,format("Overfull \\hbox (%spt too wide)",number.toscaled(d)))
+end
+
+function diagnostics.bad_hbox(hlist,pack_begin_line,line,b)
+    common_message(hlist,pack_begin_line,line,format("Tight \\hbox (badness %i)",b))
+end
+
+function diagnostics.underfull_hbox(hlist,pack_begin_line,line,b)
+    common_message(hlist,pack_begin_line,line,format("Underfull \\hbox (badness %i)",b))
+end
+
+function diagnostics.loose_hbox(hlist,pack_begin_line,line,b)
+    common_message(hlist,pack_begin_line,line,format("Loose \\hbox (badness %i)",b))
+end
+
+-- for the moment here:
+
+local utfchar = utf.char
+local concat = table.concat
+
+local nodecodes     = nodes.nodecodes
+local hlist_code    = nodecodes.hlist
+local vlist_code    = nodecodes.vlist
+local glyph_code    = nodecodes.glyph
+local kern_code     = nodecodes.kern
+local setnodecolor  = nodes.tracers.colors.set
+local parameters    = fonts.hashes.parameters
+local basepoints    = number.basepoints
+
+-- definecolor[hz:positive] [r=0.6]
+-- definecolor[hz:negative] [g=0.6]
+-- definecolor[hz:zero]     [b=0.6]
+
+-- scale = multiplier + ef/multiplier
+
+local trace_verbose = false  trackers.register("builders.paragraphs.expansion.verbose", function(v) trace_verbose = v end)
+
+local report_verbose = logs.reporter("fonts","expansion")
+
+local function colorize(n)
+    local size, font, ef, width, scale, list, flush, length
+    if trace_verbose then
+        width  = 0
+        length = 0
+        list   = { }
+        flush  = function()
+            if length > 0 then
+                report_verbose("%0.3f : %10s  %10s  %s",scale,basepoints(width),basepoints(width*scale),concat(list,"",1,length))
+                width  = 0
+                length = 0
+            end
+        end
+    else
+        length = 0
+    end
+    -- tricky: the built-in method creates dummy fonts and the last line normally has the
+    -- original font and that one then has ex.auto set
+    while n do
+        local id = n.id
+        if id == glyph_code then
+            local ne = n.expansion_factor
+            if ne == 0 then
+                if length > 0 then flush() end
+            else
+                local f = n.font
+                if f ~= font then
+                    if length > 0 then
+                        flush()
+                    end
+                    local pf = parameters[f]
+                    local ex = pf.expansion
+                    if ex and ex.auto then
+                        size = pf.size
+                        font = f -- save lookups
+                    else
+                        size = false
+                    end
+                end
+                if size then
+                    if ne ~= ef then
+                        if length > 0 then
+                            flush()
+                        end
+                        ef = ne
+                    end
+                 -- scale = 1.0 + ef / 1000 / 1000 / 1000
+                    scale = 1.0 + ef / 1000000000
+                    if scale > 1 then
+                        setnodecolor(n,"hz:positive")
+                    elseif scale < 1 then
+                        setnodecolor(n,"hz:negative")
+                    else
+                        setnodecolor(n,"hz:zero")
+                    end
+                    if report_verbose then
+                        length = length + 1
+                        list[length] = utfchar(n.char)
+                        width = width + n.width -- no kerning yet
+                    end
+                end
+            end
+        elseif id == hlist_code or id == vlist_code then
+            if length > 0 then
+                flush()
+            end
+            colorize(n.list,flush)
+        else -- nothing to show on kerns
+            if length > 0 then
+                flush()
+            end
+        end
+        n = n.next
+    end
+    if length > 0 then
+        flush()
+    end
+end
+
+builders.paragraphs.expansion = builders.paragraphs.expansion or { }
+
+function builders.paragraphs.expansion.trace(head)
+    colorize(head,true)
+    return head
+end
+
+local tasks = nodes.tasks
+
+tasks.prependaction("shipouts","normalizers","builders.paragraphs.expansion.trace")
+tasks.disableaction("shipouts","builders.paragraphs.expansion.trace")
+
+trackers.register("builders.paragraphs.expansion.verbose", function(v)
+    if v then
+        tasks.enableaction("shipouts","builders.paragraphs.expansion.trace")
+    else
+        tasks.disableaction("shipouts","builders.paragraphs.expansion.trace")
+    end
+end)
diff --git a/tex/context/base/node-met.lua b/tex/context/base/node-met.lua
new file mode 100644
index 000000000..aff427185
--- /dev/null
+++ b/tex/context/base/node-met.lua
@@ -0,0 +1,616 @@
+if not modules then modules = { } end modules ['node-nut'] = {
+    version   = 1.001,
+    comment   = "companion to node-ini.mkiv",
+    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+    copyright = "PRAGMA ADE / ConTeXt Development Team",
+    license   = "see context related readme files"
+}
+
+-- This is an experimental module. Don't use nuts for generic code, at least not till
+-- the regular code is proven stable. No support otherwise.
+
+-- luatex: todo: copylist should return h, t
+-- todo: see if using insert_before and insert_after makes sense here
+
+-- This file is a side effect of the \LUATEX\ speed optimization project of Luigi
+-- Scarso and me. As \CONTEXT\ spends over half its time in \LUA, we though that
+-- using \LUAJIT\ could improve performance. We've published some of our experiences
+-- elsewhere, but to summarize: \LUAJITTEX\ benefits a lot from the faster virtual
+-- machine, but when jit is turned of we loose some again. We experimented with
+-- ffi (without messing up the \CONTEXT\ code too much) but there we also lost more
+-- than we gained (mostly due to lack of compatible userdata support: it's all or
+-- nothing). This made us decide to look into the \LUA||\TEX\ interfacing and by
+-- profiling and careful looking at the (of course then still beta source code) we
+-- could come up with some improvements. The first showed up in 0.75 and we've more
+-- on the agenda for 0.80. Although some interfaces could be sped up significantly
+-- in practice we're only talking of 5||10\% on a \CONTEXT\ run and maybe more when
+-- complex and extensive node list manipulations happens (we're talking of hundreds
+-- of millions cross boundary calls then for documents of hundreds pages). One of the
+-- changes in the \CONTEXT\ code base is that we went from indexed access to nodes to
+-- function calls (in principle faster weren't it that the accessors need to do more
+-- checking which makes them slower) and from there to optimizing these calls as well
+-- as providing fast variants for well defined situations. At first optimizations were
+-- put in a separate \type {node.fast} table although some improvements could be
+-- ported to the main node functions. Because we got the feeling that more gain was
+-- possible (especially when using more complex fonts and \MKIV\ functionality) we
+-- eventually abandoned this approach and dropped the \type {fast} table in favour of
+-- another hack. In the process we had done lots of profiling and testing so we knew
+-- where time was wasted,
+--
+-- As lots of testing and experimenting was part of this project, I could not have
+-- done without stacks of new \CD s and \DVD s. This time Porcupine Tree, No-Man
+-- and Archive were came to rescue.
+
+local type, select = type, select
+local setmetatableindex = table.setmetatableindex
+
+-- First we get the metatable of a node:
+
+local metatable = nil
+
+do
+    local glyph = node.new("glyph",0)
+    metatable = getmetatable(glyph)
+    node.free(glyph)
+end
+
+-- statistics.tracefunction(node,       "node",       "getfield","setfield")
+-- statistics.tracefunction(node.direct,"node.direct","getfield","setfield")
+
+-- We start with some helpers and provide all relevant basic functions in the
+-- node namespace as well.
+
+local gonuts               = type(node.direct) == "table"
+-- local gonuts            = false
+
+nodes                      = nodes or { }
+local nodes                = nodes
+
+nodes.gonuts               = gonuts
+
+local nodecodes            = nodes.codes
+local hlist_code           = nodecodes.hlist
+local vlist_code           = nodecodes.vlist
+
+nodes.tostring             = node.tostring or tostring
+nodes.copy                 = node.copy
+nodes.copy_list            = node.copy_list
+nodes.delete               = node.delete
+nodes.dimensions           = node.dimensions
+nodes.end_of_math          = node.end_of_math
+nodes.flush_list           = node.flush_list
+nodes.flush_node           = node.flush_node
+nodes.free                 = node.free
+nodes.insert_after         = node.insert_after
+nodes.insert_before        = node.insert_before
+nodes.hpack                = node.hpack
+nodes.new                  = node.new
+nodes.tail                 = node.tail
+nodes.traverse             = node.traverse
+nodes.traverse_id          = node.traverse_id
+nodes.slide                = node.slide
+nodes.vpack                = node.vpack
+
+nodes.first_glyph          = node.first_glyph
+nodes.first_character      = node.first_character
+nodes.has_glyph            = node.has_glyph or node.first_glyph
+
+nodes.current_attr         = node.current_attr
+nodes.do_ligature_n        = node.do_ligature_n
+nodes.has_field            = node.has_field
+nodes.last_node            = node.last_node
+nodes.usedlist             = node.usedlist
+nodes.protrusion_skippable = node.protrusion_skippable
+nodes.write                = node.write
+
+nodes.has_attribute        = node.has_attribute
+nodes.set_attribute        = node.set_attribute
+nodes.unset_attribute      = node.unset_attribute
+
+nodes.protect_glyphs       = node.protect_glyphs
+nodes.unprotect_glyphs     = node.unprotect_glyphs
+nodes.kerning              = node.kerning
+nodes.ligaturing           = node.ligaturing
+nodes.mlist_to_hlist       = node.mlist_to_hlist
+
+
+if not gonuts or not node.getfield then
+    node.getfield = metatable.__index
+    node.setfield = metatable.__newindex
+end
+
+if not gonuts then
+    nodes.tonode   = function(n) return n end
+    nodes.todirect = function(n) return n end
+    nodes.tonut    = function(n) return n end
+end
+
+local getfield          = node.getfield
+local setfield          = node.setfield
+
+local getattr           = getfield
+local setattr           = setfield
+
+local getnext           = node.getnext    or function(n) return getfield(n,"next")    end
+local getprev           = node.getprev    or function(n) return getfield(n,"prev")    end
+local getid             = node.getid      or function(n) return getfield(n,"id")      end
+local getchar           = node.getchar    or function(n) return getfield(n,"char")    end
+local getfont           = node.getfont    or function(n) return getfield(n,"font")    end
+local getsubtype        = node.getsubtype or function(n) return getfield(n,"subtype") end
+local getlist           = node.getlist    or function(n) return getfield(n,"list")    end
+local getleader         = node.getleader  or function(n) return getfield(n,"leader")  end
+
+nodes.getfield          = getfield
+nodes.getattr           = getattr
+
+nodes.setfield          = setfield
+nodes.setattr           = setattr
+
+nodes.getnext           = getnext
+nodes.getprev           = getprev
+nodes.getid             = getid
+nodes.getchar           = getchar
+nodes.getfont           = getfont
+nodes.getsubtype        = getsubtype
+nodes.getlist           = getlist
+nodes.getleader         = getleader
+
+nodes.getbox            = node.getbox  or tex.getbox
+nodes.setbox            = node.setbox  or tex.setbox
+nodes.getskip           = node.getskip or tex.get
+
+local n_new_node        = nodes.new
+local n_free_node       = nodes.free
+local n_setfield        = nodes.setfield
+local n_getfield        = nodes.getfield
+local n_getnext         = nodes.getnext
+local n_getprev         = nodes.getprev
+local n_getid           = nodes.getid
+local n_getlist         = nodes.getlist
+local n_copy_node       = nodes.copy
+local n_copy_list       = nodes.copy_list
+local n_find_tail       = nodes.tail
+local n_insert_after    = nodes.insert_after
+local n_insert_before   = nodes.insert_before
+local n_slide           = nodes.slide
+
+local n_remove_node     = node.remove -- not yet nodes.remove
+
+-- if t.id == glue_code then
+--     local s = t.spec
+--     if s and s.writable then
+--         free_node(s)
+--     end
+--     t.spec = nil
+-- end
+
+local function remove(head,current,free_too)
+   local t = current
+   head, current = n_remove_node(head,current)
+   if t then
+        if free_too then
+            n_free_node(t)
+            t = nil
+        else
+            n_setfield(t,"next",nil)
+            n_setfield(t,"prev",nil)
+        end
+   end
+   return head, current, t
+end
+
+nodes.remove = remove
+
+function nodes.delete(head,current)
+    return remove(head,current,true)
+end
+
+-- local h, c = nodes.replace(head,current,new)
+-- local c = nodes.replace(false,current,new)
+-- local c = nodes.replace(current,new)
+--
+-- todo: check for new.next and find tail
+
+function nodes.replace(head,current,new) -- no head returned if false
+    if not new then
+        head, current, new = false, head, current
+--         current, new = head, current
+    end
+    local prev = n_getprev(current)
+    local next = n_getnext(current)
+    if next then
+        n_setfield(new,"next",next)
+        n_setfield(next,"prev",new)
+    end
+    if prev then
+        n_setfield(new,"prev",prev)
+        n_setfield(prev,"next",new)
+    end
+    if head then
+        if head == current then
+            head = new
+        end
+        n_free_node(current)
+        return head, new
+    else
+        n_free_node(current)
+        return new
+    end
+end
+
+local function count(stack,flat)
+    local n = 0
+    while stack do
+        local id = n_getid(stack)
+        if not flat and id == hlist_code or id == vlist_code then
+            local list = n_getlist(stack)
+            if list then
+                n = n + 1 + count(list) -- self counts too
+            else
+                n = n + 1
+            end
+        else
+            n = n + 1
+        end
+        stack = n_getnext(stack)
+    end
+    return n
+end
+
+nodes.count = count
+
+function nodes.append(head,current,...)
+    for i=1,select("#",...) do
+        head, current = n_insert_after(head,current,(select(i,...)))
+    end
+    return head, current
+end
+
+function nodes.prepend(head,current,...)
+    for i=1,select("#",...) do
+        head, current = n_insert_before(head,current,(select(i,...)))
+    end
+    return head, current
+end
+
+function nodes.linked(...)
+    local head, last
+    for i=1,select("#",...) do
+        local next = select(i,...)
+        if next then
+            if head then
+                n_setfield(last,"next",next)
+                n_setfield(next,"prev",last)
+            else
+                head = next
+            end
+            last = n_find_tail(next) -- we could skip the last one
+        end
+    end
+    return head
+end
+
+function nodes.concat(list) -- consider tail instead of slide
+    local head, tail
+    for i=1,#list do
+        local li = list[i]
+        if li then
+            if head then
+                n_setfield(tail,"next",li)
+                n_setfield(li,"prev",tail)
+            else
+                head = li
+            end
+            tail = n_slide(li)
+        end
+    end
+    return head, tail
+end
+
+--[[
+<p>At some point we ran into a problem that the glue specification
+of the zeropoint dimension was overwritten when adapting a glue spec
+node. This is a side effect of glue specs being shared. After a
+couple of hours tracing and debugging Taco and I came to the
+conclusion that it made no sense to complicate the spec allocator
+and settled on a writable flag. This all is a side effect of the
+fact that some glues use reserved memory slots (with the zeropoint
+glue being a noticeable one). So, next we wrap this into a function
+and hide it for the user. And yes, LuaTeX now gives a warning as
+well.</p>
+]]--
+
+function nodes.writable_spec(n) -- not pool
+    local spec = n_getfield(n,"spec")
+    if not spec then
+        spec = n_copy_node(glue_spec)
+        n_setfield(n,"spec",spec)
+    elseif not n_getfield(spec,"writable") then
+        spec = n_copy_node(spec)
+        n_setfield(n,"spec",spec)
+    end
+    return spec
+end
+
+if gonuts then
+
+    function nodes.reference(n)
+        return n and tonut(n) or "<none>"
+    end
+
+else
+
+    local left, space = lpeg.P("<"), lpeg.P(" ")
+
+    local reference = left * (1-left)^0 * left * space^0 * lpeg.C((1-space)^0)
+
+    function nodes.reference(n)
+        return n and lpegmatch(reference,tostring(n)) or "<none>"
+    end
+
+end
+
+-- Here starts an experiment with metatables. Of course this only works with nodes
+-- wrapped in userdata with a metatable.
+--
+-- Nodes are kind of special in the sense that you need to keep an eye on creation
+-- and destruction. This is quite natural if you consider that changing the content
+-- of a node would also change any copy (or alias). As there are too many pitfalls
+-- we don't have this kind of support built in \LUATEX, which means that macro
+-- packages are free to provide their own. One can even use local variants.
+--
+-- n1 .. n2 : append nodes, no copies
+-- n1 * 5   : append 4 copies of nodes
+-- 5 + n1   : strip first 5 nodes
+-- n1 - 5   : strip last 5 nodes
+-- n1 + n2  : inject n2 after first of n1
+-- n1 - n2  : inject n2 before last of n1
+-- n1^2     : two copies of nodes (keep orginal)
+-- - n1     : reverse nodes
+-- n1/f     : apply function to nodes
+
+-- local s = nodes.typesetters.tonodes
+--
+-- local function w(lst)
+--     context.dontleavehmode()
+--     context(lst)
+--     context.par()
+-- end
+--
+-- local n1 = s("a")
+-- local n2 = s("b")
+-- local n3 = s("c")
+-- local n4 = s("d")
+-- local n5 = s("e")
+-- local n6 = s("f")
+-- local n7 = s("g")
+--
+-- local n0 = n1 .. (n2 * 10).. n3 .. (5 * n4) .. n5 .. ( 5 * n6 ) .. n7 / function(n) n.char = string.byte("!") return n end
+--
+-- w(#n0)
+--
+-- w(n0)
+--
+-- local n1 = s("a") * 10
+-- local n2 = s("b") * 10
+--
+-- local n0 = ((5 + n1) .. (n2 - 5) )
+-- local n0 = - n0
+--
+-- local n0 = nil .. n0^3 .. nil
+--
+-- w(n0)
+--
+-- w ( s("a") + s("b") ) w ( s("a") + 4*s("b") ) w ( 4*s("a") + s("b") ) w ( 4*s("a") + 4*s("b") )
+-- w ( s("a") - s("b") ) w ( s("a") - 4*s("b") ) w ( 4*s("a") - s("b") ) w ( 4*s("a") - 4*s("b") )
+
+local n_remove_node = nodes.remove
+
+metatable.__concat = function(n1,n2) -- todo: accept nut on one end
+    if not n1 then
+        return n2
+    elseif not n2 then
+        return n1
+    elseif n1 == n2 then
+        -- or abort
+        return n2 -- or n2 * 2
+    else
+        local tail = n_find_tail(n1)
+        n_setfield(tail,"next",n2)
+        n_setfield(n2,"prev",tail)
+        return n1
+    end
+end
+
+metatable.__mul = function(n,multiplier)
+    if type(multiplier) ~= "number" then
+        n, multiplier = multiplier, n
+    end
+    if multiplier <= 1 then
+        return n
+    elseif n_getnext(n) then
+        local head
+        for i=2,multiplier do
+            local h = n_copy_list(n)
+            if head then
+                local t = n_find_tail(h)
+                n_setfield(t,"next",head)
+                n_setfield(head,"prev",t)
+            end
+            head = h
+        end
+        local t = n_find_tail(n)
+        n_setfield(t,"next",head)
+        n_setfield(head,"prev",t)
+    else
+        local head
+        for i=2,multiplier do
+            local c = n_copy_node(n)
+            if head then
+                n_setfield(c,"next",head)
+                n_setfield(head,"prev",c)
+            end
+            head = c
+        end
+        n_setfield(n,"next",head)
+        n_setfield(head,"prev",n)
+    end
+    return n
+end
+
+metatable.__sub = function(first,second)
+    if type(second) == "number" then
+        local tail = n_find_tail(first)
+        for i=1,second do
+            local prev = n_getfield(tail,"prev")
+            n_free_node(tail) -- can become flushlist/flushnode
+            if prev then
+                tail = prev
+            else
+                return nil
+            end
+        end
+        if tail then
+            n_setfield(tail,"next",nil)
+            return first
+        else
+            return nil
+        end
+    else
+       -- aaaaa - bbb => aaaabbba
+        local firsttail = n_find_tail(first)
+        local prev = n_getprev(firsttail)
+        if prev then
+            local secondtail = n_find_tail(second)
+            n_setfield(secondtail,"next",firsttail)
+            n_setfield(firsttail,"prev",ltail)
+            n_setfield(prev,"next",second)
+            n_setfield(second,"prev",prev)
+            return first
+        else
+            local secondtail = n_find_tail(second)
+            n_setfield(secondtail,"next",first)
+            n_setfield(first,"prev",ltail)
+            return second
+        end
+    end
+end
+
+metatable.__add = function(first,second)
+    if type(first) == "number" then
+        local head = second
+        for i=1,first do
+            local second = n_getnext(head)
+            n_free_node(head) -- can become flushlist/flushnode
+            if second then
+                head = second
+            else
+                return nil
+            end
+        end
+        if head then
+            n_setfield(head,"prev",nil)
+            return head
+        else
+            return nil
+        end
+    else
+       -- aaaaa + bbb => abbbaaaa
+        local next = n_getnext(first)
+        if next then
+            local secondtail = n_find_tail(second)
+            n_setfield(first,"next",second)
+            n_setfield(second,"prev",first)
+            n_setfield(secondtail,"next",next)
+            n_setfield(next,"prev",secondtail)
+        else
+            n_setfield(first,"next",second)
+            n_setfield(second,"prev",first)
+        end
+        return first
+    end
+end
+
+metatable.__len = function(current)
+    local length = 0
+    while current do
+        current = n_getnext(current)
+        length = length + 1
+    end
+    return length
+end
+
+metatable.__div = function(list,action)
+    return action(list) or list -- always a value
+end
+
+metatable.__pow = function(n,multiplier)
+    local tail = n
+    local head = nil
+    if getnext(n) then
+        if multiplier == 1 then
+            head = n_copy_list(n)
+        else
+            for i=1,multiplier do
+                local h = n_copy_list(n)
+                if head then
+                    local t = n_find_tail(h)
+                    n_setfield(t,"next",head)
+                    n_setfield(head,"prev",t)
+                end
+                head = h
+            end
+        end
+    else
+        if multiplier == 1 then
+            head = n_copy_node(n)
+        else
+            for i=2,multiplier do
+                local c = n_copy_node(n)
+                if head then
+                    n_setfield(head,"next",c)
+                    n_setfield(c,"prev",head)
+                end
+                head = c
+            end
+        end
+    end
+    -- todo: tracing
+    return head
+end
+
+metatable.__unm = function(head)
+    local last = head
+    local first = head
+    local current = n_getnext(head)
+    while current do
+        local next = n_getnext(current)
+        n_setfield(first,"prev",current)
+        n_setfield(current,"next",first)
+        first = current
+        current = next
+    end
+    n_setfield(first,"prev",nil)
+    n_setfield(last,"next",nil)
+    return first
+end
+
+-- see node-nut.lua for more info on going nuts
+
+if not gonuts then
+
+    local nuts = { }
+    nodes.nuts = nuts
+
+    local function dummy(f) return f end
+
+    nodes.vianuts  = dummy
+    nodes.vianodes = dummy
+
+    for k, v in next, nodes do
+        if type(v) == "function" then
+            nuts[k] = v
+        end
+    end
+
+end
diff --git a/tex/context/base/node-pro.lua b/tex/context/base/node-pro.lua
index b98292abb..aa6692d7b 100644
--- a/tex/context/base/node-pro.lua
+++ b/tex/context/base/node-pro.lua
@@ -66,36 +66,12 @@ processors.tracer = tracer
 
 processors.enabled = true -- this will become a proper state (like trackers)
 
--- function processors.pre_linebreak_filter(head,groupcode,size,packtype,direction)
---     local first, found = first_glyph(head) -- they really need to be glyphs
---     if found then
---         if trace_callbacks then
---             local before = nodes.count(head,true)
---             local head, done = actions(head,groupcode,size,packtype,direction) -- todo : pass first
---             local after = nodes.count(head,true)
---             if done then
---                 tracer("pre_linebreak","changed",head,groupcode,before,after,true)
---             else
---                 tracer("pre_linebreak","unchanged",head,groupcode,before,after,true)
---             end
---             return done and head or true
---         else
---             local head, done = actions(head,groupcode,size,packtype,direction) -- todo : pass first
---             return done and head or true
---         end
---     elseif trace_callbacks then
---         local n = nodes.count(head,false)
---         tracer("pre_linebreak","no chars",head,groupcode,n,n)
---     end
---     return true
--- end
-
-function processors.pre_linebreak_filter(head,groupcode)
+function processors.pre_linebreak_filter(head,groupcode) -- ,size,packtype,direction
     local first, found = first_glyph(head) -- they really need to be glyphs
     if found then
         if trace_callbacks then
             local before = nodes.count(head,true)
-            local head, done = actions(head,groupcode)
+            local head, done = actions(head,groupcode) -- ,size,packtype,direction
             local after = nodes.count(head,true)
             if done then
                 tracer("pre_linebreak","changed",head,groupcode,before,after,true)
@@ -104,7 +80,7 @@ function processors.pre_linebreak_filter(head,groupcode)
             end
             return done and head or true
         else
-            local head, done = actions(head,groupcode)
+            local head, done = actions(head,groupcode) -- ,size,packtype,direction
             return done and head or true
         end
     elseif trace_callbacks then
diff --git a/tex/context/base/node-ref.lua b/tex/context/base/node-ref.lua
index 09e066434..baece6e50 100644
--- a/tex/context/base/node-ref.lua
+++ b/tex/context/base/node-ref.lua
@@ -16,61 +16,60 @@ if not modules then modules = { } end modules ['node-ref'] = {
 
 -- is grouplevel still used?
 
-local format = string.format
-
-local allocate, mark = utilities.storage.allocate, utilities.storage.mark
+local attributes, nodes, node = attributes, nodes, node
 
-local cleanupreferences, cleanupdestinations = false, true
+local allocate            = utilities.storage.allocate, utilities.storage.mark
+local mark                = utilities.storage.allocate, utilities.storage.mark
 
-local attributes, nodes, node = attributes, nodes, node
 
-local nodeinjections  = backends.nodeinjections
-local codeinjections  = backends.codeinjections
+local nodeinjections      = backends.nodeinjections
+local codeinjections      = backends.codeinjections
 
-local transparencies  = attributes.transparencies
-local colors          = attributes.colors
-local references      = structures.references
-local tasks           = nodes.tasks
+local cleanupreferences   = false
+local cleanupdestinations = true
 
-local hpack_list      = node.hpack
-local list_dimensions = node.dimensions
+local transparencies      = attributes.transparencies
+local colors              = attributes.colors
+local references          = structures.references
+local tasks               = nodes.tasks
 
--- current.glue_set current.glue_sign
+local hpack_list          = node.hpack
+local list_dimensions     = node.dimensions
 
-local trace_backend      = false  trackers.register("nodes.backend",      function(v) trace_backend      = v end)
-local trace_references   = false  trackers.register("nodes.references",   function(v) trace_references   = v end)
-local trace_destinations = false  trackers.register("nodes.destinations", function(v) trace_destinations = v end)
+local trace_backend       = false  trackers.register("nodes.backend",      function(v) trace_backend      = v end)
+local trace_references    = false  trackers.register("nodes.references",   function(v) trace_references   = v end)
+local trace_destinations  = false  trackers.register("nodes.destinations", function(v) trace_destinations = v end)
 
-local report_reference   = logs.reporter("backend","references")
-local report_destination = logs.reporter("backend","destinations")
-local report_area        = logs.reporter("backend","areas")
+local report_reference    = logs.reporter("backend","references")
+local report_destination  = logs.reporter("backend","destinations")
+local report_area         = logs.reporter("backend","areas")
 
-local nodecodes        = nodes.nodecodes
-local skipcodes        = nodes.skipcodes
-local whatcodes        = nodes.whatcodes
-local listcodes        = nodes.listcodes
+local nodecodes           = nodes.nodecodes
+local skipcodes           = nodes.skipcodes
+local whatcodes           = nodes.whatcodes
+local listcodes           = nodes.listcodes
 
-local hlist_code       = nodecodes.hlist
-local vlist_code       = nodecodes.vlist
-local glue_code        = nodecodes.glue
-local whatsit_code     = nodecodes.whatsit
+local hlist_code          = nodecodes.hlist
+local vlist_code          = nodecodes.vlist
+local glue_code           = nodecodes.glue
+local whatsit_code        = nodecodes.whatsit
 
-local leftskip_code    = skipcodes.leftskip
-local rightskip_code   = skipcodes.rightskip
-local parfillskip_code = skipcodes.parfillskip
+local leftskip_code       = skipcodes.leftskip
+local rightskip_code      = skipcodes.rightskip
+local parfillskip_code    = skipcodes.parfillskip
 
-local localpar_code    = whatcodes.localpar
-local dir_code         = whatcodes.dir
+local localpar_code       = whatcodes.localpar
+local dir_code            = whatcodes.dir
 
-local line_code        = listcodes.line
+local line_code           = listcodes.line
 
-local nodepool         = nodes.pool
+local nodepool            = nodes.pool
 
-local new_kern         = nodepool.kern
+local new_kern            = nodepool.kern
 
-local traverse         = node.traverse
-local find_node_tail   = node.tail or node.slide
-local tosequence       = nodes.tosequence
+local traverse            = node.traverse
+local find_node_tail      = node.tail or node.slide
+local tosequence          = nodes.tosequence
 
 -- local function dimensions(parent,start,stop)
 --     stop = stop and stop.next
@@ -362,20 +361,19 @@ local function colorize(width,height,depth,n,reference,what)
     end
 end
 
-local nodepool     = nodes.pool
+-- references:
 
-local new_kern     = nodepool.kern
+local nodepool        = nodes.pool
+local new_kern        = nodepool.kern
 
-local texattribute = tex.attribute
-local texcount     = tex.count
+local texsetattribute = tex.setattribute
+local texsetcount     = tex.setcount
 
--- references:
-
-local stack         = { }
-local done          = { }
-local attribute     = attributes.private('reference')
-local nofreferences = 0
-local topofstack    = 0
+local stack           = { }
+local done            = { }
+local attribute       = attributes.private('reference')
+local nofreferences   = 0
+local topofstack      = 0
 
 nodes.references = {
     attribute = attribute,
@@ -390,8 +388,8 @@ local function setreference(h,d,r)
     -- the preroll permits us to determine samepage (but delayed also has some advantages)
     -- so some part of the backend work is already done here
     stack[topofstack] = { r, h, d, codeinjections.prerollreference(r) }
- -- texattribute[attribute] = topofstack -- todo -> at tex end
-    texcount.lastreferenceattribute = topofstack
+ -- texsetattribute(attribute,topofstack) -- todo -> at tex end
+    texsetcount("lastreferenceattribute",topofstack)
 end
 
 function references.get(n) -- not public so functionality can change
@@ -540,7 +538,7 @@ function references.inject(prefix,reference,h,d,highlight,newwindow,layer) -- to
         -- unknown ref, just don't set it and issue an error
     else
         -- check
-        set.highlight, set.newwindow,set.layer = highlight, newwindow, layer
+        set.highlight, set.newwindow, set.layer = highlight, newwindow, layer
         setreference(h,d,set) -- sets attribute / todo: for set[*].error
     end
 end
@@ -573,7 +571,7 @@ end
 
 statistics.register("interactive elements", function()
     if nofreferences > 0 or nofdestinations > 0 then
-        return format("%s references, %s destinations",nofreferences,nofdestinations)
+        return string.format("%s references, %s destinations",nofreferences,nofdestinations)
     else
         return nil
     end
diff --git a/tex/context/base/node-res.lua b/tex/context/base/node-res.lua
index 768aac404..ca9d67f91 100644
--- a/tex/context/base/node-res.lua
+++ b/tex/context/base/node-res.lua
@@ -35,6 +35,9 @@ local glyph_code   = nodecodes.glyph
 
 local allocate     = utilities.storage.allocate
 
+local texgetbox    = tex.getbox
+local texgetcount  = tex.getcount
+
 local reserved, nofreserved = { }, 0
 
 local function register_node(n)
@@ -57,11 +60,10 @@ function pool.cleanup(nofboxes) -- todo
     --  end
     end
     if nofboxes then
-        local tb = tex.box
         for i=0,nofboxes do
-            local l = tb[i]
+            local l = texgetbox(i)
             if l then
-                free_node(tb[i])
+                free_node(l) -- also list ?
                 nl = nl + 1
             end
         end
@@ -293,12 +295,38 @@ function pool.noad()
     return copy_node(noad)
 end
 
-function pool.hlist()
-    return copy_node(hlist)
+function pool.hlist(list,width,height,depth)
+    local n = copy_node(hlist)
+    if list then
+        n.list = list
+    end
+    if width then
+        n.width = width
+    end
+    if height then
+        n.height = height
+    end
+    if depth then
+        n.depth = depth
+    end
+    return n
 end
 
-function pool.vlist()
-    return copy_node(vlist)
+function pool.vlist(list,width,height,depth)
+    local n = copy_node(vlist)
+    if list then
+        n.list = list
+    end
+    if width then
+        n.width = width
+    end
+    if height then
+        n.height = height
+    end
+    if depth then
+        n.depth = depth
+    end
+    return n
 end
 
 --[[
@@ -396,7 +424,7 @@ function pool.special(str)
 end
 
 statistics.register("cleaned up reserved nodes", function()
-    return format("%s nodes, %s lists of %s", pool.cleanup(tex.count["c_syst_last_allocated_box"]))
+    return format("%s nodes, %s lists of %s", pool.cleanup(texgetcount("c_syst_last_allocated_box")))
 end) -- \topofboxstack
 
 statistics.register("node memory usage", function() -- comes after cleanup !
diff --git a/tex/context/base/node-rul.lua b/tex/context/base/node-rul.lua
index 953beb186..5c64c0113 100644
--- a/tex/context/base/node-rul.lua
+++ b/tex/context/base/node-rul.lua
@@ -85,6 +85,9 @@ local variables          = interfaces.variables
 local dimenfactor        = fonts.helpers.dimenfactor
 local splitdimen         = number.splitdimen
 
+local v_yes              = variables.yes
+local v_foreground       = variables.foreground
+
 local nodecodes          = nodes.nodecodes
 local skipcodes          = nodes.skipcodes
 local whatcodes          = nodes.whatcodes
@@ -150,7 +153,7 @@ local function processwords(attribute,data,flush,head,parent) -- we have hlistdi
                     else
                         -- possible extensions: when in same class then keep spanning
                         local newlevel, newclass = floor(aa/1000), aa%1000
---~                         strip = not continue or level == 1 -- 0
+                     -- strip = not continue or level == 1 -- 0
                         if f then
                             if class == newclass then -- and newlevel > level then
                                 head, done = flush(head,f,l,d,level,parent,false), true
@@ -161,7 +164,7 @@ local function processwords(attribute,data,flush,head,parent) -- we have hlistdi
                         f, l, a = n, n, aa
                         level, class = newlevel, newclass
                         d = data[class]
-                        continue = d.continue == variables.yes
+                        continue = d.continue == v_yes
                     end
                 else
                     if f then
@@ -169,8 +172,16 @@ local function processwords(attribute,data,flush,head,parent) -- we have hlistdi
                     end
                     f, l, a = nil, nil, nil
                 end
-            elseif f and (id == disc_code or (id == kern_code and n.subtype == kerning_code)) then
-                l = n
+--             elseif f and (id == disc_code or (id == kern_code and n.subtype == kerning_code)) then
+--                 l = n
+            elseif id == disc_code then
+                if f then
+                    l = n
+                end
+            elseif id == kern_code and n.subtype == kerning_code then
+                if f then
+                    l = n
+                end
             elseif id == hlist_code or id == vlist_code then
                 if f then
                     head, done = flush(head,f,l,d,level,parent,strip), true
@@ -188,13 +199,12 @@ local function processwords(attribute,data,flush,head,parent) -- we have hlistdi
                 if continue then
                     if id == penalty_code then
                         l = n
-                    elseif id == kern_code then
-                        l = n
+                 -- elseif id == kern_code then
+                 --     l = n
                     elseif id == glue_code then
                         -- catch \underbar{a} \underbar{a} (subtype test is needed)
                         local subtype = n.subtype
-                        if continue and n[attribute] and
-                            (subtype == userskip_code or subtype == spaceskip_code or subskip == xspaceskip_code) then
+                        if n[attribute] and (subtype == userskip_code or subtype == spaceskip_code or subskip == xspaceskip_code) then
                             l = n
                         else
                             head, done = flush(head,f,l,d,level,parent,strip), true
@@ -261,7 +271,7 @@ local function flush_ruled(head,f,l,d,level,parent,strip) -- not that fast but a
     local colorspace   = ma > 0 and ma or f[a_colorspace] or 1
     local color        = ca > 0 and ca or f[a_color]
     local transparency = ta > 0 and ta or f[a_transparency]
-    local foreground = order == variables.foreground
+    local foreground = order == v_foreground
 
     local e = dimenfactor(unit,fontdata[f.font]) -- what if no glyph node
 
diff --git a/tex/context/base/node-ser.lua b/tex/context/base/node-ser.lua
index 70743123c..ab59616bc 100644
--- a/tex/context/base/node-ser.lua
+++ b/tex/context/base/node-ser.lua
@@ -18,8 +18,8 @@ local context     = context
 local nodes       = nodes
 local node        = node
 
-local traverse    = node.traverse
-local is_node     = node.is_node
+local traverse    = nodes.traverse
+local is_node     = nodes.is_node
 
 local nodecodes   = nodes.nodecodes
 local noadcodes   = nodes.noadcodes
diff --git a/tex/context/base/node-shp.lua b/tex/context/base/node-shp.lua
index 8f7a411a7..32024e5da 100644
--- a/tex/context/base/node-shp.lua
+++ b/tex/context/base/node-shp.lua
@@ -24,7 +24,7 @@ local mark_code      = nodecodes.mark
 local kern_code      = nodecodes.kern
 local glue_code      = nodecodes.glue
 
-local texbox         = tex.box
+local texgetbox      = tex.getbox
 
 local free_node      = node.free
 local remove_node    = node.remove
@@ -73,7 +73,7 @@ end
 -- interface
 
 function commands.finalizebox(n)
-    actions(texbox[n])
+    actions(texgetbox(n))
 end
 
 -- just in case we want to optimize lookups:
@@ -129,8 +129,8 @@ trackers.register("nodes.frequencies",function(v)
     if type(v) == "string" then
         frequencies.filename = v
     end
-    handlers.frequencies_shipouts_before   = register("shipouts", "begin")
-    handlers.frequencies_shipouts_after    = register("shipouts", "end")
+    handlers.frequencies_shipouts_before   = register("shipouts",   "begin")
+    handlers.frequencies_shipouts_after    = register("shipouts",   "end")
     handlers.frequencies_processors_before = register("processors", "begin")
     handlers.frequencies_processors_after  = register("processors", "end")
     tasks.prependaction("shipouts",   "before", "nodes.handlers.frequencies_shipouts_before")
diff --git a/tex/context/base/node-tra.lua b/tex/context/base/node-tra.lua
index 916b2143d..bfdfa543b 100644
--- a/tex/context/base/node-tra.lua
+++ b/tex/context/base/node-tra.lua
@@ -22,6 +22,8 @@ nodes = nodes or { }
 
 local nodes, node, context = nodes, node, context
 
+local texgetattribute  = tex.getattribute
+
 local tracers          = nodes.tracers or { }
 nodes.tracers          = tracers
 
@@ -57,6 +59,7 @@ local localpar_code    = whatcodes.localpar
 local dir_code         = whatcodes.dir
 
 local nodepool         = nodes.pool
+local new_rule         = nodepool.rule
 
 local dimenfactors     = number.dimenfactors
 local formatters       = string.formatters
@@ -342,7 +345,7 @@ local function numbertodimen(d,unit,fmt,strip)
         local str = formatters[fmt](d*dimenfactors[unit],unit)
         return strip and lpegmatch(stripper,str) or str
     end
-    local id = node.id
+    local id = d.id
     if id == kern_code then
         local str = formatters[fmt](d.width*dimenfactors[unit],unit)
         return strip and lpegmatch(stripper,str) or str
@@ -522,8 +525,49 @@ end
 
 -- for the moment here
 
-nodes.visualizers = { }
+local visualizers = nodes.visualizers or { }
+nodes.visualizers = visualizers
 
-function nodes.visualizers.handler(head)
+function visualizers.handler(head)
     return head, false
 end
+
+-- we could cache attribute lists and set attr (copy will increment count) .. todo ..
+-- although tracers are used seldom
+
+local function setproperties(n,c,s)
+    local mm = texgetattribute(a_colormodel)
+    n[a_colormodel]   = mm > 0 and mm or 1
+    n[a_color]        = m_color[c]
+    n[a_transparency] = m_transparency[c]
+    return n
+end
+
+tracers.setproperties = setproperties
+
+function tracers.setlistv(n,c,s)
+    local f = n
+    local mc = m_color[c]
+    local mt = m_transparency[c]
+    local mm = texgetattribute(a_colormodel)
+    if mm <= 0 then
+        mm = 1
+    end
+    while n do
+        n[a_colormodel]   = mm
+        n[a_color]        = mc
+        n[a_transparency] = mt
+        n = n.next
+    end
+    return f
+end
+
+function tracers.resetproperties(n)
+    n[a_color]        = unsetvalue
+    n[a_transparency] = unsetvalue
+    return n
+end
+
+function tracers.rule(w,h,d,c,s) -- so some day we can consider using literals (speedup)
+    return setproperties(new_rule(w,h,d),c,s)
+end
diff --git a/tex/context/base/node-typ.lua b/tex/context/base/node-typ.lua
index 6e1a31643..4a2ef8d49 100644
--- a/tex/context/base/node-typ.lua
+++ b/tex/context/base/node-typ.lua
@@ -6,21 +6,23 @@ if not modules then modules = { } end modules ['node-typ'] = {
     license   = "see context related readme files"
 }
 
-local utfvalues      = utf.values
+-- code has been moved to blob-ini.lua
 
-local currentfont    = font.current
-local fontparameters = fonts.hashes.parameters
+local typesetters = nodes.typesetters or { }
+nodes.typesetters = typesetters
 
-local hpack          = node.hpack
-local vpack          = node.vpack
-local fast_hpack     = nodes.fasthpack
+local hpack_node_list = nodes.hpack
+local vpack_node_list = nodes.vpack
+local fast_hpack_list = nodes.fasthpack
 
-local nodepool       = nodes.pool
+local nodepool        = nodes.pool
+local new_glyph       = nodepool.glyph
+local new_glue        = nodepool.glue
 
-local newglyph       = nodepool.glyph
-local newglue        = nodepool.glue
+local utfvalues       = utf.values
 
-typesetters = typesetters or { }
+local currentfont    = font.current
+local fontparameters = fonts.hashes.parameters
 
 local function tonodes(str,fontid,spacing) -- quick and dirty
     local head, prev = nil, nil
@@ -39,11 +41,11 @@ local function tonodes(str,fontid,spacing) -- quick and dirty
         local next
         if c == 32 then
             if not spacedone then
-                next = newglue(s,p,m)
+                next = new_glue(s,p,m)
                 spacedone = true
             end
         else
-            next = newglyph(fontid or 1,c)
+            next = new_glyph(fontid or 1,c)
             spacedone = false
         end
         if not next then
@@ -59,21 +61,33 @@ local function tonodes(str,fontid,spacing) -- quick and dirty
     return head
 end
 
-typesetters.tonodes = tonodes
-
-function typesetters.hpack(str,fontid,spacing)
-    return hpack(tonodes(str,fontid,spacing),"exactly")
+local function tohpack(str,fontid,spacing)
+    return hpack_node_list(tonodes(str,fontid,spacing),"exactly")
 end
 
-function typesetters.fast_hpack(str,fontid,spacing)
-    return fast_hpack(tonodes(str,fontid,spacing),"exactly")
+local function tohpackfast(str,fontid,spacing)
+    return fast_hpack_list(tonodes(str,fontid,spacing),"exactly")
 end
 
-function typesetters.vpack(str,fontid,spacing)
+local function tovpack(str,fontid,spacing)
     -- vpack is just a hack, and a proper implentation is on the agenda
     -- as it needs more info etc than currently available
-    return vpack(tonodes(str,fontid,spacing))
+    return vpack_node_list(tonodes(str,fontid,spacing))
 end
 
---~ node.write(typesetters.hpack("Hello World!"))
---~ node.write(typesetters.hpack("Hello World!",1,100*1024*10))
+local tovpackfast = tovpack
+
+typesetters.tonodes     = tonodes
+typesetters.tohpack     = tohpack
+typesetters.tohpackfast = tohpackfast
+typesetters.tovpack     = tovpack
+typesetters.tovpackfast = tovpackfast
+
+typesetters.hpack       = tohpack
+typesetters.fast_hpack  = tohpackfast
+typesetters.vpack       = tovpack
+
+-- node.write(nodes.typestters.hpack("Hello World!"))
+-- node.write(nodes.typestters.hpack("Hello World!",1,100*1024*10))
+
+string.tonodes = tonodes -- quite convenient
diff --git a/tex/context/base/pack-obj.lua b/tex/context/base/pack-obj.lua
index 1e4e0f59e..70876a346 100644
--- a/tex/context/base/pack-obj.lua
+++ b/tex/context/base/pack-obj.lua
@@ -13,8 +13,7 @@ reusable components.</p>
 
 local commands, context = commands, context
 
-local texcount = tex.count
-local allocate = utilities.storage.allocate
+local allocate  = utilities.storage.allocate
 
 local collected = allocate()
 local tobesaved = allocate()
diff --git a/tex/context/base/pack-rul.lua b/tex/context/base/pack-rul.lua
index a990936e7..909c0c168 100644
--- a/tex/context/base/pack-rul.lua
+++ b/tex/context/base/pack-rul.lua
@@ -10,16 +10,21 @@ if not modules then modules = { } end modules ['pack-rul'] = {
 <p>An explanation is given in the history document <t>mk</t>.</p>
 --ldx]]--
 
-local texsetdimen, texsetcount, texbox = tex.setdimen, tex.setcount, tex.box
-local hpack, free, copy, traverse_id = node.hpack, node.free, node.copy_list, node.traverse_id
-local texdimen, texcount = tex.dimen, tex.count
+local hpack           = node.hpack
+local free            = node.free
+local copy            = node.copy_list
+local traverse_id     = node.traverse_id
+local node_dimensions = node.dimensions
 
 local hlist_code      = nodes.nodecodes.hlist
 local box_code        = nodes.listcodes.box
-local node_dimensions = node.dimensions
+
+local texsetdimen     = tex.setdimen
+local texsetcount     = tex.setcount
+local texgetbox       = tex.getbox
 
 function commands.doreshapeframedbox(n)
-    local box            = texbox[n]
+    local box            = texgetbox(n)
     local noflines       = 0
     local firstheight    = nil
     local lastdepth      = nil
@@ -76,17 +81,16 @@ function commands.doreshapeframedbox(n)
             end
         end
     end
- -- print("reshape", noflines, firstheight or 0, lastdepth or 0)
-    texsetcount("global","framednoflines",     noflines)
-    texsetdimen("global","framedfirstheight",  firstheight or 0)
-    texsetdimen("global","framedlastdepth",    lastdepth or 0)
-    texsetdimen("global","framedminwidth",     minwidth)
-    texsetdimen("global","framedmaxwidth",     maxwidth)
-    texsetdimen("global","framedaveragewidth", noflines > 0 and totalwidth/noflines or 0)
+    texsetcount("global","framednoflines",noflines)
+    texsetdimen("global","framedfirstheight",firstheight or 0)
+    texsetdimen("global","framedlastdepth",lastdepth or 0)
+    texsetdimen("global","framedminwidth",minwidth)
+    texsetdimen("global","framedmaxwidth",maxwidth)
+    texsetdimen("global","framedaveragewidth",noflines > 0 and totalwidth/noflines or 0)
 end
 
 function commands.doanalyzeframedbox(n)
-    local box         = texbox[n]
+    local box         = texgetbox(n)
     local noflines    = 0
     local firstheight = nil
     local lastdepth   = nil
@@ -102,8 +106,7 @@ function commands.doanalyzeframedbox(n)
             end
         end
     end
- -- print("analyze", noflines, firstheight or 0, lastdepth or 0)
-    texsetcount("global","framednoflines",    noflines)
-    texsetdimen("global","framedfirstheight", firstheight or 0)
-    texsetdimen("global","framedlastdepth",   lastdepth or 0)
+    texsetcount("global","framednoflines",noflines)
+    texsetdimen("global","framedfirstheight",firstheight or 0)
+    texsetdimen("global","framedlastdepth",lastdepth or 0)
 end
diff --git a/tex/context/base/pack-rul.mkiv b/tex/context/base/pack-rul.mkiv
index e7db332b4..a591e5576 100644
--- a/tex/context/base/pack-rul.mkiv
+++ b/tex/context/base/pack-rul.mkiv
@@ -1749,17 +1749,45 @@
    \edef\currentmathframed{#1}%
    \dosingleempty\pack_framed_mathframed_indeed}
 
+% \def\pack_framed_mathframed_indeed[#1]#2% no fancy nesting supported here
+%   {\iffirstargument
+%       \setupcurrentmathframed[#1]%
+%    \fi
+%    \c_framed_mstyle\mathstyle
+%    \doifnot{\mathframedparameter\c!location}\v!low{\let\normalstrut\pack_framed_math_strut}%
+%    \inheritedmathframedframed{\Ustartmath\triggermathstyle\c_framed_mstyle#2\Ustopmath}%
+%    \endgroup}
+
+\newcount\c_pack_framed_mc
+
+\def\pack_framed_math_pos
+  {\global\advance\c_pack_framed_mc\plusone
+   \xdef\pack_framed_mc_one{mcf:1:\number\c_pack_framed_mc}%
+   \xdef\pack_framed_mc_two{mcf:2:\number\c_pack_framed_mc}%
+   \xypos\pack_framed_mc_two}
+
 \def\pack_framed_mathframed_indeed[#1]#2% no fancy nesting supported here
   {\iffirstargument
       \setupcurrentmathframed[#1]%
    \fi
    \c_framed_mstyle\mathstyle
-   \doifnot{\mathframedparameter\c!location}\v!low{\let\normalstrut\pack_framed_math_strut}%
+   \edef\m_framed_location{\mathframedparameter\c!location}%
+   \ifx\m_framed_location\v!mathematics
+     \let\normalstrut\pack_framed_math_pos
+   \else\ifx\m_framed_location\v!low\else
+     \let\normalstrut\pack_framed_math_strut
+   \fi\fi
    \inheritedmathframedframed{\Ustartmath\triggermathstyle\c_framed_mstyle#2\Ustopmath}%
    \endgroup}
 
+\installframedlocator \v!mathematics
+  {}
+  {\lower\dimexpr\MPy\pack_framed_mc_two-\MPy\pack_framed_mc_one\relax
+   \hbox{\xypos\pack_framed_mc_one\box\b_framed_normal}}
+
 \definemathframed[mframed]
 \definemathframed[inmframed][\c!location=\v!low]
+\definemathframed[mcframed] [\c!location=\v!mathematics]
 
 %D So instead of the rather versatile \type {\framed}, we use \type {\mframed}:
 %D
diff --git a/tex/context/base/page-app.mkiv b/tex/context/base/page-app.mkiv
index 5f1c2f297..272245cce 100644
--- a/tex/context/base/page-app.mkiv
+++ b/tex/context/base/page-app.mkiv
@@ -106,7 +106,7 @@
    \fi
    \d_page_fitting_width \wd\b_page_fitting
    \d_page_fitting_height\ht\b_page_fitting
-   \startlocallayout % still valid?
+% wrong now:    \startlocallayout % still valid?
    \let\checkcurrentlayout\relax % else interference with odd/even layout
    \processaction
      [\fittingpageparameter\c!paper]
@@ -117,7 +117,7 @@
    \startmakeup[fittingpage]%
      \box\b_page_fitting
    \stopmakeup
-   \stoplocallayout % still valid?
+% wrong now:   \stoplocallayout % still valid?
    \egroup
    \autostoptext}
 
diff --git a/tex/context/base/page-flt.lua b/tex/context/base/page-flt.lua
index ab7a534eb..11aa2be21 100644
--- a/tex/context/base/page-flt.lua
+++ b/tex/context/base/page-flt.lua
@@ -11,9 +11,6 @@ if not modules then modules = { } end modules ['page-flt'] = {
 
 local insert, remove = table.insert, table.remove
 local find = string.find
-local setdimen, setcount, texbox = tex.setdimen, tex.setcount, tex.box
-
-local copy_node_list = node.copy_list
 
 local trace_floats = false  trackers.register("graphics.floats", function(v) trace_floats = v end) -- name might change
 
@@ -24,10 +21,20 @@ local C, S, P, lpegmatch = lpeg.C, lpeg.S, lpeg.P, lpeg.match
 -- we use floatbox, floatwidth, floatheight
 -- text page leftpage rightpage (todo: top, bottom, margin, order)
 
-floats       = floats or { }
-local floats = floats
+local copy_node_list = node.copy_list
+
+local setdimen       = tex.setdimen
+local setcount       = tex.setcount
+local texgetbox      = tex.getbox
+local texsetbox      = tex.setbox
+
+floats               = floats or { }
+local floats         = floats
 
-local noffloats, last, default, pushed = 0, nil, "text", { }
+local noffloats      = 0
+local last           = nil
+local default        = "text"
+local pushed         = { }
 
 local function initialize()
     return {
@@ -98,7 +105,7 @@ end
 
 function floats.save(which,data)
     which = which or default
-    local b = texbox.floatbox
+    local b = texgetbox("floatbox")
     if b then
         local stack = stacks[which]
         noffloats = noffloats + 1
@@ -108,7 +115,7 @@ function floats.save(which,data)
             data = data or { },
             box  = copy_node_list(b),
         }
-        texbox.floatbox = nil
+        texsetbox("floatbox",nil)
         insert(stack,t)
         setcount("global","savednoffloats",#stacks[default])
         if trace_floats then
@@ -125,10 +132,10 @@ function floats.resave(which)
     if last then
         which = which or default
         local stack = stacks[which]
-        local b = texbox.floatbox
+        local b = texgetbox("floatbox")
         local w, h, d = b.width, b.height, b.depth
         last.box = copy_node_list(b)
-        texbox.floatbox = nil
+        texsetbox("floatbox",nil)
         insert(stack,1,last)
         setcount("global","savednoffloats",#stacks[default])
         if trace_floats then
@@ -152,7 +159,7 @@ function floats.flush(which,n,bylabel)
         else
             interfaces.showmessage("floatblocks",3,t.n)
         end
-        texbox.floatbox = b
+        texsetbox("floatbox",b)
         last = remove(stack,n)
         last.box = nil
         setcount("global","savednoffloats",#stacks[default]) -- default?
diff --git a/tex/context/base/page-inj.lua b/tex/context/base/page-inj.lua
index f9b56fddb..56e5a234e 100644
--- a/tex/context/base/page-inj.lua
+++ b/tex/context/base/page-inj.lua
@@ -8,6 +8,8 @@ if not modules then modules = { } end modules ["page-inj"] = {
 
 -- Adapted a bit by HH: numbered states, tracking, delayed, order, etc.
 
+local type, tonumber = type, tonumber
+
 local injections        = pagebuilders.injections or { }
 pagebuilders.injections = injections
 
@@ -19,6 +21,8 @@ local variables         = interfaces.variables
 local context           = context
 local commands          = commands
 
+local texsetcount       = tex.setcount
+
 local v_yes             = variables.yes
 local v_previous        = variables.previous
 local v_next            = variables.next
@@ -34,7 +38,7 @@ function injections.save(specification) -- maybe not public, just commands.*
         state      = tonumber(specification.state) or specification.state,
         parameters = specification.userdata,
     }
-    tex.setcount("global","c_page_boxes_flush_n",#cache)
+    texsetcount("global","c_page_boxes_flush_n",#cache)
 end
 
 function injections.flushbefore() -- maybe not public, just commands.*
@@ -65,7 +69,7 @@ function injections.flushbefore() -- maybe not public, just commands.*
         end
         context.unprotect()
         cache = delayed
-        tex.setcount("global","c_page_boxes_flush_n",#cache)
+        texsetcount("global","c_page_boxes_flush_n",#cache)
     end
 end
 
@@ -95,7 +99,7 @@ function injections.flushafter() -- maybe not public, just commands.*
         end
         context.protect()
         cache = delayed
-        tex.setcount("global","c_page_boxes_flush_n",#cache)
+        texsetcount("global","c_page_boxes_flush_n",#cache)
     end
 end
 
diff --git a/tex/context/base/page-lin.lua b/tex/context/base/page-lin.lua
index e6b500e8b..7e8e9ad8a 100644
--- a/tex/context/base/page-lin.lua
+++ b/tex/context/base/page-lin.lua
@@ -12,8 +12,6 @@ local trace_numbers = false  trackers.register("lines.numbers",  function(v) tra
 
 local report_lines = logs.reporter("lines")
 
-local texbox = tex.box
-
 local attributes, nodes, node, context = attributes, nodes, node, context
 
 nodes.lines       = nodes.lines or { }
@@ -23,6 +21,8 @@ lines.data        = lines.data or { } -- start step tag
 local data        = lines.data
 local last        = #data
 
+local texgetbox   = tex.getbox
+
 lines.scratchbox  = lines.scratchbox or 0
 
 local leftmarginwidth = nodes.leftmarginwidth
@@ -208,7 +208,7 @@ local function identify(list)
 end
 
 function boxed.stage_zero(n)
-    return identify(texbox[n].list)
+    return identify(texgetbox(n).list)
 end
 
 -- reset ranges per page
@@ -217,9 +217,9 @@ end
 
 function boxed.stage_one(n,nested)
     current_list = { }
-    local head = texbox[n]
-    if head then
-        local list = head.list
+    local box = texgetbox(n)
+    if box then
+        local list = box.list
         if nested then
             list = identify(list)
         end
@@ -268,7 +268,7 @@ function boxed.stage_two(n,m)
     if #current_list > 0 then
         m = m or lines.scratchbox
         local t, tn = { }, 0
-        for l in traverse_id(hlist_code,texbox[m].list) do
+        for l in traverse_id(hlist_code,texgetbox(m).list) do
             tn = tn + 1
             t[tn] = copy_node(l)
         end
diff --git a/tex/context/base/page-lin.mkiv b/tex/context/base/page-lin.mkiv
index 0f8b78398..876d2e781 100644
--- a/tex/context/base/page-lin.mkiv
+++ b/tex/context/base/page-lin.mkiv
@@ -156,28 +156,28 @@
 
 % todo: text
 
-\installcorenamespace{linennumberinglocation}
-\installcorenamespace{linennumberingalternative}
-
-\expandafter\let\csname\??linennumberinglocation\v!middle       \endcsname \zerocount
-\expandafter\let\csname\??linennumberinglocation\v!left         \endcsname \plusone
-\expandafter\let\csname\??linennumberinglocation\v!margin       \endcsname \plusone
-\expandafter\let\csname\??linennumberinglocation\v!inmargin     \endcsname \plusone
-\expandafter\let\csname\??linennumberinglocation\v!inleft       \endcsname \plusone
-\expandafter\let\csname\??linennumberinglocation\v!right        \endcsname \plustwo
-\expandafter\let\csname\??linennumberinglocation\v!inright      \endcsname \plustwo
-\expandafter\let\csname\??linennumberinglocation\v!inner        \endcsname \plusthree
-\expandafter\let\csname\??linennumberinglocation\v!outer        \endcsname \plusfour
-\expandafter\let\csname\??linennumberinglocation\v!text         \endcsname \plusfive
-\expandafter\let\csname\??linennumberinglocation\v!begin        \endcsname \plussix
-\expandafter\let\csname\??linennumberinglocation\v!end          \endcsname \plusseven
-
-\expandafter\let\csname\??linennumberingalternative\v!middle    \endcsname \zerocount
-\expandafter\let\csname\??linennumberingalternative\v!right     \endcsname \plusone
-\expandafter\let\csname\??linennumberingalternative\v!flushleft \endcsname \plusone
-\expandafter\let\csname\??linennumberingalternative\v!left      \endcsname \plustwo
-\expandafter\let\csname\??linennumberingalternative\v!flushright\endcsname \plustwo
-\expandafter\let\csname\??linennumberingalternative\v!auto      \endcsname \plusfive
+\installcorenamespace{linenumberinglocation}
+\installcorenamespace{linenumberingalternative}
+
+\expandafter\let\csname\??linenumberinglocation\v!middle       \endcsname \zerocount
+\expandafter\let\csname\??linenumberinglocation\v!left         \endcsname \plusone
+\expandafter\let\csname\??linenumberinglocation\v!margin       \endcsname \plusone
+\expandafter\let\csname\??linenumberinglocation\v!inmargin     \endcsname \plusone
+\expandafter\let\csname\??linenumberinglocation\v!inleft       \endcsname \plusone
+\expandafter\let\csname\??linenumberinglocation\v!right        \endcsname \plustwo
+\expandafter\let\csname\??linenumberinglocation\v!inright      \endcsname \plustwo
+\expandafter\let\csname\??linenumberinglocation\v!inner        \endcsname \plusthree
+\expandafter\let\csname\??linenumberinglocation\v!outer        \endcsname \plusfour
+\expandafter\let\csname\??linenumberinglocation\v!text         \endcsname \plusfive
+\expandafter\let\csname\??linenumberinglocation\v!begin        \endcsname \plussix
+\expandafter\let\csname\??linenumberinglocation\v!end          \endcsname \plusseven
+
+\expandafter\let\csname\??linenumberingalternative\v!middle    \endcsname \zerocount
+\expandafter\let\csname\??linenumberingalternative\v!right     \endcsname \plusone
+\expandafter\let\csname\??linenumberingalternative\v!flushleft \endcsname \plusone
+\expandafter\let\csname\??linenumberingalternative\v!left      \endcsname \plustwo
+\expandafter\let\csname\??linenumberingalternative\v!flushright\endcsname \plustwo
+\expandafter\let\csname\??linenumberingalternative\v!auto      \endcsname \plusfive
 
 % \startlinenumbering[<startvalue>|continue|settings|name]
 % \startlinenumbering[name][<startvalue>|continue|settings]
@@ -339,8 +339,8 @@
    \else
      \setfalse\c_page_lines_fake_number
    \fi
-   \c_page_lines_location \executeifdefined{\??linennumberinglocation   \linenumberingparameter\c!location}\plusone \relax % left
-   \c_page_lines_alignment\executeifdefined{\??linennumberingalternative\linenumberingparameter\c!align   }\plusfive\relax % auto
+   \c_page_lines_location \executeifdefined{\??linenumberinglocation   \linenumberingparameter\c!location}\plusone \relax % left
+   \c_page_lines_alignment\executeifdefined{\??linenumberingalternative\linenumberingparameter\c!align   }\plusfive\relax % auto
    \ifcase\c_page_lines_last_column\relax
      \settrue \c_page_lines_fake_number
    \or
diff --git a/tex/context/base/page-mix.lua b/tex/context/base/page-mix.lua
index f265ac8f9..f86e2f279 100644
--- a/tex/context/base/page-mix.lua
+++ b/tex/context/base/page-mix.lua
@@ -34,11 +34,14 @@ local new_glue         = nodepool.glue
 local hpack            = node.hpack
 local vpack            = node.vpack
 local freenode         = node.free
+local concatnodes      = nodes.concat
+
+local texgetbox        = tex.getbox
+local texsetbox        = tex.setbox
+local texgetskip       = tex.getskip
 
-local texbox           = tex.box
-local texskip          = tex.skip
-local texdimen         = tex.dimen
 local points           = number.points
+
 local settings_to_hash = utilities.parsers.settings_to_hash
 
 local variables        = interfaces.variables
@@ -79,7 +82,7 @@ local function collectinserts(result,nxt,nxtid)
             if not c then
                 c = { }
                 inserts[s] = c
-                local width = texskip[s].width
+                local width = texgetskip(s).width
                 if not result.inserts[s] then
                     currentskips = currentskips + width
                 end
@@ -199,7 +202,7 @@ local function setsplit(specification) -- a rather large function
         report_state("fatal error, no box")
         return
     end
-    local list = texbox[box]
+    local list = texgetbox(box)
     if not list then
         report_state("fatal error, no list")
         return
@@ -464,7 +467,7 @@ local function setsplit(specification) -- a rather large function
     specification.overflow       = overflow
     specification.discarded      = discarded
 
-    texbox[specification.box].head = nil
+    texgetbox(specification.box).list = nil
 
     return specification
 end
@@ -613,9 +616,9 @@ function mixedcolumns.getsplit(result,n)
     end
 
     for c, list in next, r.inserts do
-     -- tex.setbox("global",c,vpack(nodes.concat(list)))
-     -- tex.setbox(c,vpack(nodes.concat(list)))
-        texbox[c] = vpack(nodes.concat(list))
+        local b = vpack(concatnodes(list)) -- multiple arguments
+     -- texsetbox("global",c,b)
+        texsetbox(c,b)
         r.inserts[c] = nil
     end
 
diff --git a/tex/context/base/page-mix.mkiv b/tex/context/base/page-mix.mkiv
index ef26c5f70..56a1249ca 100644
--- a/tex/context/base/page-mix.mkiv
+++ b/tex/context/base/page-mix.mkiv
@@ -59,7 +59,7 @@
   %\c!align=, % inherit
   %\c!before=,
   %\c!after=,
-  %\c!rule=\v!none,
+  %\c!separator=\v!none,
   %\c!setups=,
    \c!frame=\v!off,
    \c!strut=\v!no,
@@ -69,6 +69,7 @@
    \c!maxwidth=\makeupwidth,
    \c!grid=\v!tolerant,
    \c!step=.25\lineheight, % needs some experimenting
+  %\c!splitmethod=\v!fixed, % will be default
    \c!method=\ifinner\s!box\else\s!otr\fi] % automatic as suggested by WS
 
 \let\startmixedcolumns\relax % defined later
@@ -163,7 +164,8 @@
 \definemixedcolumns
   [\s!itemgroupcolumns]
   [\c!n=\itemgroupparameter\c!n,
-   \c!rule=\v!off,
+   \c!separator=\v!none,
+   \c!splitmethod=\v!none,
    \c!balance=\v!yes]
 
 \unexpanded\def\strc_itemgroups_start_columns
@@ -294,7 +296,7 @@
 \unexpanded\def\page_mix_command_inject_separator
   {\bgroup
    \hss
-   \csname\??mixedcolumnsseparator\mixedcolumnsparameter\c!separator\endcsname
+   \csname\??mixedcolumnsseparator\mixedcolumnsparameter\c!separator\endcsname % was \c!rule
    \hss
    \egroup}
 
@@ -487,10 +489,17 @@
    \usealignparameter\mixedcolumnsparameter
    \usesetupsparameter\mixedcolumnsparameter}
 
+% \setvalue{\??mixedcolumnsstop\s!otr}%
+%   {\par
+%    \ifcase\c_page_mix_otr_nesting\or
+%      \c_page_mix_routine\c_page_mix_routine_balance
+%      \page_otr_trigger_output_routine
+%    \fi}
+
 \setvalue{\??mixedcolumnsstop\s!otr}%
   {\par
    \ifcase\c_page_mix_otr_nesting\or
-     \c_page_mix_routine\c_page_mix_routine_balance
+     \doif{\mixedcolumnsparameter\c!balance}\v!yes{\c_page_mix_routine\c_page_mix_routine_balance}%
      \page_otr_trigger_output_routine
    \fi}
 
@@ -518,34 +527,13 @@
        strutht     = \number\strutht,
        strutdp     = \number\strutdp,
        threshold   = \number\d_page_mix_threshold,
+       splitmethod = "\mixedcolumnsparameter\c!splitmethod",
        balance     = "#1",
        alternative = "\mixedcolumnsparameter\c!alternative",
        grid        = \ifgridsnapping true\else false\fi,
    }}%
    \deadcycles\zerocount}
 
-% \unexpanded\def\page_mix_routine_package
-%   {\ctxcommand{mixfinalize()}%
-%    \setbox\b_page_mix_collected\vbox \bgroup
-%      \ifvoid\b_page_mix_preceding \else
-%        \box\b_page_mix_preceding
-%        \global\d_page_mix_preceding_height\zeropoint
-%        \nointerlineskip
-%      \fi
-%      \hskip\d_page_mix_leftskip
-%      \page_mix_hbox to \d_page_mix_max_width \bgroup
-%        \letmixedcolumnsparameter\c!strut \v!no
-%        \letmixedcolumnsparameter\c!align \empty % probably not needed as we could use a simple variant
-%        \letmixedcolumnsparameter\c!setups\empty
-%        \dorecurse\c_page_mix_n_of_columns {%
-%          \inheritedmixedcolumnsframed{\page_mix_command_package_column}%
-%          \ifnum\recurselevel<\c_page_mix_n_of_columns
-%            \page_mix_command_inject_separator
-%          \fi
-%        }%
-%      \egroup
-%    \egroup}
-
 \unexpanded\def\page_mix_routine_package
   {\ctxcommand{mixfinalize()}%
    \setbox\b_page_mix_collected\vbox \bgroup
@@ -557,9 +545,13 @@
      \hskip\d_page_mix_leftskip
      \page_mix_hbox to \d_page_mix_max_width \bgroup
        \dorecurse\c_page_mix_n_of_columns{%
-       % \inheritedmixedcolumnsframed{\page_mix_command_package_column}% needs reset of strut, align, setups etc
-         \setbox\scratchbox\page_mix_command_package_column % needs packaging anyway
+         % needs packaging anyway
+         \setbox\scratchbox\page_mix_command_package_column
+         % for the moment a quick and dirty patch .. we need to go into the box (hence the \plusone) .. a slowdowner
+         \page_lines_add_numbers_to_box\scratchbox\recurselevel\c_page_mix_n_of_columns\plusone
+         % the framed needs a reset of strut, align, setups etc
          \inheritedmixedcolumnsframedbox\currentmixedcolumns\scratchbox
+         % optional
          \ifnum\recurselevel<\c_page_mix_n_of_columns
            \page_mix_command_inject_separator
          \fi
diff --git a/tex/context/base/page-pst.lua b/tex/context/base/page-pst.lua
index f714c25d9..2fc400a14 100644
--- a/tex/context/base/page-pst.lua
+++ b/tex/context/base/page-pst.lua
@@ -8,12 +8,16 @@ if not modules then modules = { } end modules ['page-pst'] = {
 
 -- todo: adapt message
 
+local tonumber, next = tonumber, next
 local format, validstring = string.format, string.valid
 local sortedkeys = table.sortedkeys
 
 local context  = context
 local commands = commands
 
+local texgetcount = tex.getcount
+local texsetcount = tex.setcount
+
 local cache = { }
 
 local function flush(page)
@@ -35,14 +39,14 @@ local function setnextpage()
     elseif n > 0 then
                         -- upcoming page (realpageno)
     end
-    tex.setcount("global","c_page_postponed_blocks_next_page",n)
+    texsetcount("global","c_page_postponed_blocks_next_page",n)
 end
 
 function commands.flushpostponedblocks(page)
     -- we need to flush previously pending pages as well and the zero
     -- slot is the generic one so that one is always flushed
     local t = sortedkeys(cache)
-    local p = tonumber(page) or tex.count.realpageno or 0
+    local p = tonumber(page) or texgetcount("realpageno") or 0
     for i=1,#t do
         local ti = t[i]
         if ti <= p then
@@ -57,7 +61,7 @@ end
 function commands.registerpostponedblock(page)
     if type(page) == "string" then
         if string.find(page,"^+") then
-            page = tex.count.realpageno + (tonumber(page) or 1) -- future delta page
+            page = texgetcount("realpageno") + (tonumber(page) or 1) -- future delta page
         else
             page = tonumber(page) or 0 -- preferred page or otherwise first possible occasion
         end
diff --git a/tex/context/base/page-str.lua b/tex/context/base/page-str.lua
index f6314657f..35ce85609 100644
--- a/tex/context/base/page-str.lua
+++ b/tex/context/base/page-str.lua
@@ -12,18 +12,25 @@ if not modules then modules = { } end modules ['page-str'] = {
 
 local concat, insert, remove = table.concat, table.insert, table.remove
 
-local find_tail, write_node, free_node, copy_nodelist = node.slide, node.write, node.free, node.copy_list
-local vpack_nodelist, hpack_nodelist = node.vpack, node.hpack
-local texdimen, texbox = tex.dimen, tex.box
-local settings_to_array = utilities.parsers.settings_to_array
-
 local nodes, node = nodes, node
 
-local nodepool  = nodes.pool
-local tasks     = nodes.tasks
+local nodepool          = nodes.pool
+local tasks             = nodes.tasks
+
+local new_kern          = nodepool.kern
+local new_glyph         = nodepool.glyph
+
+local find_tail         = node.slide
+local write_node        = node.write
+local free_node         = node.free
+local copy_nodelist     = node.copy_list
+local vpack_nodelist    = node.vpack
+local hpack_nodelist    = node.hpack
+
+local settings_to_array = utilities.parsers.settings_to_array
 
-local new_kern  = nodepool.kern
-local new_glyph = nodepool.glyph
+local texgetdimen       = tex.getdimen
+local texgetbox         = tex.getbox
 
 local trace_collecting = false  trackers.register("streams.collecting", function(v) trace_collecting = v end)
 local trace_flushing   = false  trackers.register("streams.flushing",   function(v) trace_flushing   = v end)
@@ -175,7 +182,8 @@ function streams.synchronize(list) -- this is an experiment !
         if trace_flushing then
             report_streams("slot %s has max height %p and max depth %p",m,height,depth)
         end
-        local strutht, strutdp = texdimen.globalbodyfontstrutheight, texdimen.globalbodyfontstrutdepth
+        local strutht = texgetdimen("globalbodyfontstrutheight")
+        local strutdp = texgetdimen("globalbodyfontstrutdepth")
         local struthtdp = strutht + strutdp
         for i=1,#list do
             local name = list[i]
@@ -198,7 +206,7 @@ function streams.synchronize(list) -- this is an experiment !
                         local n, delta = 0, delta_height -- for tracing
                         while delta > 0 do
                             -- we need to add some interline penalties
-                            local line = copy_nodelist(tex.box.strutbox)
+                            local line = copy_nodelist(texgetbox("strutbox"))
                             line.height, line.depth = strutht, strutdp
                             if tail then
                                 tail.next, line.prev = line, tail
diff --git a/tex/context/base/s-inf-03.pdf b/tex/context/base/s-inf-03.pdf
new file mode 100644
index 000000000..a2de80fab
Binary files /dev/null and b/tex/context/base/s-inf-03.pdf differ
diff --git a/tex/context/base/scrn-fld.lua b/tex/context/base/scrn-fld.lua
index 9836cbebe..69480b887 100644
--- a/tex/context/base/scrn-fld.lua
+++ b/tex/context/base/scrn-fld.lua
@@ -11,6 +11,8 @@ if not modules then modules = { } end modules ['scrn-fld'] = {
 local variables     = interfaces.variables
 local v_yes         = variables.yes
 
+local texsetbox     = tex.setbox
+
 local fields        = { }
 interactions.fields = fields
 
@@ -43,7 +45,7 @@ commands.definefieldset = defineset
 commands.clonefield     = clone
 
 function commands.insertfield(name,specification)
-    tex.box["b_scrn_field_body"] = insert(name,specification)
+    texsetbox("b_scrn_field_body",insert(name,specification))
 end
 
 -- (for the monent) only tex interface
diff --git a/tex/context/base/scrn-hlp.lua b/tex/context/base/scrn-hlp.lua
index 1e9d1f570..d344ce280 100644
--- a/tex/context/base/scrn-hlp.lua
+++ b/tex/context/base/scrn-hlp.lua
@@ -21,6 +21,8 @@ local hpack_nodelist = node.hpack
 
 local register_list  = nodes.pool.register
 
+local texgetbox      = tex.getbox
+
 local nodecodes      = nodes.nodecodes
 
 local hlist_code     = nodecodes.hlist
@@ -51,7 +53,7 @@ function help.register(number,name,box)
         interactions.javascripts.setpreamble("HelpTexts",helpscript)
         helpscript = false
     end
-    local b = copy_nodelist(tex.box[box])
+    local b = copy_nodelist(texgetbox(box))
     register_list(b)
     data[number] = b
     if name and name ~= "" then
@@ -84,7 +86,7 @@ end
 
 function help.collect(box)
     if next(data) then
-        return collect(tex.box[box].list)
+        return collect(texgetbox(box).list)
     end
 end
 
diff --git a/tex/context/base/scrn-wid.lua b/tex/context/base/scrn-wid.lua
index a2b3e28b9..90aaed609 100644
--- a/tex/context/base/scrn-wid.lua
+++ b/tex/context/base/scrn-wid.lua
@@ -24,8 +24,12 @@ interactions.soundclips  = soundclips
 interactions.renderings  = renderings
 interactions.linkedlists = linkedlists
 
+local texsetbox          = tex.setbox
+
 local jobpasses          = job.passes
 
+local texgetcount        = tex.getcount
+
 local codeinjections     = backends.codeinjections
 local nodeinjections     = backends.nodeinjections
 
@@ -107,7 +111,7 @@ end
 commands.registerattachment = attachments.register
 
 function commands.insertattachment(specification)
-    tex.box["b_scrn_attachment_link"] = attachments.insert(specification)
+    texsetbox("b_scrn_attachment_link",attachments.insert(specification))
 end
 
 -- Comment
@@ -121,7 +125,7 @@ function comments.insert(specification)
 end
 
 function commands.insertcomment(specification)
-    tex.box["b_scrn_comment_link"] = comments.insert(specification)
+    texsetbox("b_scrn_comment_link",comments.insert(specification))
 end
 
 -- Soundclips
@@ -199,7 +203,7 @@ end
 function commands.enhancelinkedlist(tag,n)
     local ll = jobpasses.gettobesaved(tag)
     if ll then
-        ll[n] = texcount.realpageno
+        ll[n] = texgetcount("realpageno")
     end
 end
 
diff --git a/tex/context/base/scrn-wid.mkvi b/tex/context/base/scrn-wid.mkvi
index ae5f7c556..fad451651 100644
--- a/tex/context/base/scrn-wid.mkvi
+++ b/tex/context/base/scrn-wid.mkvi
@@ -16,6 +16,7 @@
 \registerctxluafile{scrn-wid}{1.001}
 
 % todo: expansion in comments (default is expanded)
+% todo: check renderings ... acrobat crashes too easily on missing one
 
 \unprotect
 
@@ -566,8 +567,8 @@
 \definereference[PauseCurrentRendering] [\v!PauseRendering {\currentrendering}]
 \definereference[ResumeCurrentRendering][\v!ResumeRendering{\currentrendering}]
 
-\def\useexternalrendering{\doquadrupleempty\scrn_rendering_use}
-\def\setinternalrendering{\dodoubleempty   \scrn_rendering_set}
+\unexpanded\def\useexternalrendering{\doquadrupleempty\scrn_rendering_use}
+\unexpanded\def\setinternalrendering{\dodoubleempty   \scrn_rendering_set}
 
 \def\scrn_rendering_use[#tag][#mime][#file][#option]%
   {\ctxcommand{registerrendering{
@@ -578,7 +579,7 @@
       option   = "#option",
   }}}
 
-\def\scrn_rendering_set[#tag][#option]% {content}
+\def\scrn_rendering_set[#tag][#option]% {content} % crappy
   {\bgroup
    \dowithnextbox
      {\ctxcommand{registerrendering{
@@ -615,17 +616,9 @@
 \unexpanded\def\placerenderingwindow
   {\dodoubleempty\scrn_rendering_place_window}
 
-\def\scrn_rendering_place_window[#window][#rendering]%
+\def\scrn_rendering_place_window[#window][#rendering]% do all in lua
   {\bgroup
    \edef\currentrendering{\ifsecondargument#rendering\else#window\fi}%
-   \doifelse{\renderingtype\currentrendering}{internal} % an object
-     {\getobjectdimensions{IRO}\currentrendering
-      \d_scrn_rendering_height\dimexpr\objectheight+\objectdepth\relax
-      \d_scrn_rendering_width\objectwidth\relax
-      \dogetobjectreferencepage{IRO}\currentrendering\m_scrn_rendering_page}%
-     {\d_scrn_rendering_height\vsize
-      \d_scrn_rendering_width\hsize
-      \let\m_scrn_rendering_page\realpageno}%
    % create fall back if needed
    \edef\currentrenderingwindow{\namedrenderingwindowparameter{#window}\c!width}% stupid test, we need a proper one here
    \ifx\currentrenderingwindow\empty
@@ -634,6 +627,21 @@
    \else
      \edef\currentrenderingwindow{#window}%
    \fi
+   \edef\currentrenderingtype{\renderingtype\currentrendering}%
+   \ifx\currentrenderingtype\s!internal
+     \getobjectdimensions{IRO}\currentrendering
+     \d_scrn_rendering_height\dimexpr\objectheight+\objectdepth\relax
+     \d_scrn_rendering_width\objectwidth\relax
+     \dogetobjectreferencepage{IRO}\currentrendering\m_scrn_rendering_page
+   \else\ifx\currentrenderingwindow\s!default
+     \d_scrn_rendering_height\vsize
+     \d_scrn_rendering_width \hsize
+     \let\m_scrn_rendering_page\realpageno
+   \else
+     \d_scrn_rendering_height\renderingwindowparameter\c!height
+     \d_scrn_rendering_width \renderingwindowparameter\c!width
+     \let\m_scrn_rendering_page\realpageno
+   \fi\fi
  % todo:
  % \handlereferenceactions{\renderingwindowparameter\c!openpageaction }\dosetuprenderingopenpageaction
  % \handlereferenceactions{\renderingwindowparameter\c!closepageaction}\dosetuprenderingclosepageaction
diff --git a/tex/context/base/spac-ali.lua b/tex/context/base/spac-ali.lua
index ceb278433..25cc6cd66 100644
--- a/tex/context/base/spac-ali.lua
+++ b/tex/context/base/spac-ali.lua
@@ -20,8 +20,6 @@ local hpack_nodes      = node.hpack -- nodes.fasthpack not really faster here
 
 local unsetvalue       = attributes.unsetvalue
 
-local concat_nodes     = nodes.concat
-
 local nodecodes        = nodes.nodecodes
 local listcodes        = nodes.listcodes
 
@@ -35,8 +33,8 @@ local new_stretch      = nodepool.stretch
 
 local a_realign        = attributes.private("realign")
 
-local texattribute     = tex.attribute
-local texcount         = tex.count
+local texsetattribute  = tex.setattribute
+local texgetcount      = tex.getcount
 
 local isleftpage       = layouts.status.isleftpage
 
@@ -77,12 +75,12 @@ local function handler(head,leftpage,realpageno)
                             action = leftpage and 2 or 1
                         end
                         if action == 1 then
-                            current.list = hpack_nodes(concat_nodes{current.list,new_stretch(3)},current.width,"exactly")
+                            current.list = hpack_nodes(current.list .. new_stretch(3),current.width,"exactly")
                             if trace_realign then
                                 report_realign("flushing left, align %a, page %a, realpage %a",align,pageno,realpageno)
                             end
                         elseif action == 2 then
-                            current.list = hpack_nodes(concat_nodes{new_stretch(3),current.list},current.width,"exactly")
+                            current.list = hpack_nodes(new_stretch(3) .. current.list,current.width,"exactly")
                             if trace_realign then
                                 report_realign("flushing right. align %a, page %a, realpage %a",align,pageno,realpageno)
                             end
@@ -106,7 +104,7 @@ end
 
 function alignments.handler(head)
     local leftpage = isleftpage(true,false)
-    local realpageno = texcount.realpageno
+    local realpageno = texgetcount("realpageno")
     return handler(head,leftpage,realpageno)
 end
 
@@ -120,7 +118,7 @@ function alignments.set(n)
             report_realign("enabled")
         end
     end
-    texattribute[a_realign] = texcount.realpageno * 10 + n
+    texsetattribute(a_realign,texgetcount("realpageno") * 10 + n)
 end
 
 commands.setrealign = alignments.set
diff --git a/tex/context/base/spac-chr.lua b/tex/context/base/spac-chr.lua
index 24364978a..f3c62bb77 100644
--- a/tex/context/base/spac-chr.lua
+++ b/tex/context/base/spac-chr.lua
@@ -74,7 +74,6 @@ local function inject_char_space(unicode,head,current,parent)
     local font = current.font
     local char = fontcharacters[font][parent]
     local glue = new_glue(char and char.width or fontparameters[font].space)
- -- glue.attr = copy_node_list(current.attr)
     glue.attr = current.attr
     current.attr = nil
     glue[a_character] = unicode
@@ -86,10 +85,8 @@ local function inject_nobreak_space(unicode,head,current,space,spacestretch,spac
     local attr = current.attr
     local glue = new_glue(space,spacestretch,spaceshrink)
     local penalty = new_penalty(10000)
- -- glue.attr = copy_node_list(attr)
     glue.attr = attr
     current.attr = nil
- -- penalty.attr = attr
     glue[a_character] = unicode
     head, current = insert_node_after(head,current,penalty)
     head, current = insert_node_after(head,current,glue)
diff --git a/tex/context/base/spac-ver.lua b/tex/context/base/spac-ver.lua
index 36fa357d0..406896e4d 100644
--- a/tex/context/base/spac-ver.lua
+++ b/tex/context/base/spac-ver.lua
@@ -23,8 +23,7 @@ if not modules then modules = { } end modules ['spac-ver'] = {
 
 local next, type, tonumber = next, type, tonumber
 local gmatch, concat = string.gmatch, table.concat
-local ceil, floor, max, min, round, abs = math.ceil, math.floor, math.max, math.min, math.round, math.abs
-local texlists, texdimen, texbox = tex.lists, tex.dimen, tex.box
+local ceil, floor = math.ceil, math.floor
 local lpegmatch = lpeg.match
 local unpack = unpack or table.unpack
 local allocate = utilities.storage.allocate
@@ -33,12 +32,13 @@ local formatters = string.formatters
 
 local P, C, R, S, Cc = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cc
 
-local nodes, node, trackers, attributes, context, commands =  nodes, node, trackers, attributes, context, commands
+local nodes, node, trackers, attributes, context, commands, tex =  nodes, node, trackers, attributes, context, commands, tex
 
-local variables   = interfaces.variables
+----- texlists    = tex.lists
+local texgetdimen = tex.getdimen
+local texgetbox   = tex.getbox
 
-local starttiming = statistics.starttiming
-local stoptiming  = statistics.stoptiming
+local variables   = interfaces.variables
 
 -- vertical space handler
 
@@ -87,7 +87,6 @@ local new_gluespec        = nodepool.gluespec
 
 local nodecodes           = nodes.nodecodes
 local skipcodes           = nodes.skipcodes
-local fillcodes           = nodes.fillcodes
 
 local penalty_code        = nodecodes.penalty
 local kern_code           = nodecodes.kern
@@ -96,7 +95,6 @@ local hlist_code          = nodecodes.hlist
 local vlist_code          = nodecodes.vlist
 local whatsit_code        = nodecodes.whatsit
 
-local userskip_code       = skipcodes.userskip
 
 local vspacing            = builders.vspacing or { }
 builders.vspacing         = vspacing
@@ -288,20 +286,24 @@ local function snap_hlist(where,current,method,height,depth) -- method.strut is
     local snapht, snapdp
     if method["local"] then
         -- snapping is done immediately here
-        snapht, snapdp = texdimen.bodyfontstrutheight, texdimen.bodyfontstrutdepth
+        snapht = texgetdimen("bodyfontstrutheight")
+        snapdp = texgetdimen("bodyfontstrutdepth")
         if t then
             t[#t+1] = formatters["local: snapht %p snapdp %p"](snapht,snapdp)
         end
     elseif method["global"] then
-        snapht, snapdp = texdimen.globalbodyfontstrutheight, texdimen.globalbodyfontstrutdepth
+        snapht = texgetdimen("globalbodyfontstrutheight")
+        snapdp = texgetdimen("globalbodyfontstrutdepth")
         if t then
             t[#t+1] = formatters["global: snapht %p snapdp %p"](snapht,snapdp)
         end
     else
         -- maybe autolocal
         -- snapping might happen later in the otr
-        snapht, snapdp = texdimen.globalbodyfontstrutheight, texdimen.globalbodyfontstrutdepth
-        local lsnapht, lsnapdp = texdimen.bodyfontstrutheight, texdimen.bodyfontstrutdepth
+        snapht = texgetdimen("globalbodyfontstrutheight")
+        snapdp = texgetdimen("globalbodyfontstrutdepth")
+        local lsnapht = texgetdimen("bodyfontstrutheight")
+        local lsnapdp = texgetdimen("bodyfontstrutdepth")
         if snapht ~= lsnapht and snapdp ~= lsnapdp then
             snapht, snapdp = lsnapht, lsnapdp
         end
@@ -342,13 +344,16 @@ local function snap_hlist(where,current,method,height,depth) -- method.strut is
             id = thebox and thebox.id
         end
         if thebox and id == vlist_code then
-            local list, lh, ld = thebox.list
+            local list = thebox.list
+            local lh, ld
             for n in traverse_nodes_id(hlist_code,list) do
-                lh, ld = n.height, n.depth
+                lh = n.height
+                ld = n.depth
                 break
             end
             if lh then
-                local ht, dp = thebox.height, thebox.depth
+                local ht = thebox.height
+                local dp = thebox.depth
                 if t then
                     t[#t+1] = formatters["first line: height %p depth %p"](lh,ld)
                     t[#t+1] = formatters["dimensions: height %p depth %p"](ht,dp)
@@ -379,10 +384,12 @@ local function snap_hlist(where,current,method,height,depth) -- method.strut is
         if thebox and id == vlist_code then
             local list, lh, ld = thebox.list
             for n in traverse_nodes_id(hlist_code,list) do
-                lh, ld = n.height, n.depth
+                lh = n.height
+                ld = n.depth
             end
             if lh then
-                local ht, dp = thebox.height, thebox.depth
+                local ht = thebox.height
+                local dp = thebox.depth
                 if t then
                     t[#t+1] = formatters["last line: height %p depth %p" ](lh,ld)
                     t[#t+1] = formatters["dimensions: height %p depth %p"](ht,dp)
@@ -479,7 +486,7 @@ local function snap_hlist(where,current,method,height,depth) -- method.strut is
     local lines = (ch+cd)/snaphtdp
     if t then
         local original = (h+d)/snaphtdp
-        local whatever = (ch+cd)/(texdimen.globalbodyfontstrutheight + texdimen.globalbodyfontstrutdepth)
+        local whatever = (ch+cd)/(texgetdimen("globalbodyfontstrutheight") + texgetdimen("globalbodyfontstrutdepth"))
         t[#t+1] = formatters["final lines: %s -> %s (%s)"](original,lines,whatever)
         t[#t+1] = formatters["final height: %p -> %p"](h,ch)
         t[#t+1] = formatters["final depth: %p -> %p"](d,cd)
@@ -495,7 +502,8 @@ local function snap_topskip(current,method)
     local w = spec.width
     local wd = w
     if spec.writable then
-        spec.width, wd = 0, 0
+        spec.width = 0
+        wd = 0
     end
     return w, wd
 end
@@ -740,21 +748,11 @@ local topskip_code               = skipcodes.topskip
 local splittopskip_code          = skipcodes.splittopskip
 
 local free_glue_node = free_node
-local discard, largest, force, penalty, add, disable, nowhite, goback, together = 0, 1, 2, 3, 4, 5, 6, 7, 8
-
--- local function free_glue_node(n)
---  -- free_node(n.spec)
---     print("before",n)
---     logs.flush()
---     free_node(n)
---     print("after")
---     logs.flush()
--- end
 
 function vspacing.snapbox(n,how)
     local sv = snapmethods[how]
     if sv then
-        local box = texbox[n]
+        local box = texgetbox(n)
         local list = box.list
         if list then
             local s = list[a_snapmethod]
@@ -763,7 +761,8 @@ function vspacing.snapbox(n,how)
                 --  report_snapper("box list not snapped, already done")
                 end
             else
-                local ht, dp = box.height, box.depth
+                local ht = box.height
+                local dp = box.depth
                 if false then -- todo: already_done
                     -- assume that the box is already snapped
                     if trace_vsnapping then
@@ -772,7 +771,8 @@ function vspacing.snapbox(n,how)
                     end
                 else
                     local h, d, ch, cd, lines = snap_hlist("box",box,sv,ht,dp)
-                    box.height, box.depth = ch, cd
+                    box.height= ch
+                    box.depth = cd
                     if trace_vsnapping then
                         report_snapper("box list snapped from (%p,%p) to (%p,%p) using method %a (%s) for %a (%s lines): %s",
                             h,d,ch,cd,sv.name,sv.specification,"direct",lines,listtoutf(list))
@@ -805,6 +805,8 @@ end
 
 -- penalty only works well when before skip
 
+local discard, largest, force, penalty, add, disable, nowhite, goback, together = 0, 1, 2, 3, 4, 5, 6, 7, 8 -- move into function when upvalue 60 issue
+
 local function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also pass tail
     if trace then
         reset_tracing(head)
@@ -823,11 +825,12 @@ local function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also
             head = insert_node_before(head,current,p)
         end
         if glue_data then
+            local spec = glue_data.spec
             if force_glue then
                 if trace then trace_done("flushed due to " .. why,glue_data) end
-                head = forced_skip(head,current,glue_data.spec.width,"before",trace)
+                head = forced_skip(head,current,spec.width,"before",trace)
                 free_glue_node(glue_data)
-            elseif glue_data.spec.writable then
+            elseif spec.writable then
                 if trace then trace_done("flushed due to " .. why,glue_data) end
                 head = insert_node_before(head,current,glue_data)
             else
@@ -841,8 +844,9 @@ local function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also
     end
     if trace_vsnapping then
         report_snapper("global ht/dp = %p/%p, local ht/dp = %p/%p",
-            texdimen.globalbodyfontstrutheight, texdimen.globalbodyfontstrutdepth,
-            texdimen.bodyfontstrutheight, texdimen.bodyfontstrutdepth)
+            texgetdimen("globalbodyfontstrutheight"), texgetdimen("globalbodyfontstrutdepth"),
+            texgetdimen("bodyfontstrutheight"), texgetdimen("bodyfontstrutdepth")
+        )
     end
     if trace then trace_info("start analyzing",where,what) end
     while current do
@@ -865,7 +869,8 @@ local function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also
                     if sv then
                         -- check if already snapped
                         if list and already_done(id,list,a_snapmethod) then
-                            local ht, dp = current.height, current.depth
+                            local ht = current.height
+                            local dp = current.depth
                             -- assume that the box is already snapped
                             if trace_vsnapping then
                                 report_snapper("mvl list already snapped at (%p,%p): %s",ht,dp,listtoutf(list))
@@ -936,9 +941,9 @@ local function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also
                                     previous.spec = new_gluespec(pw + cw, pp + cp, pm + cm) -- else topskip can disappear
                                     if trace then trace_natural("removed",current) end
                                     head, current = remove_node(head, current, true)
-                                --  current = previous
+                                 -- current = previous
                                     if trace then trace_natural("collapsed",previous) end
-                                --  current = current.next
+                                 -- current = current.next
                                 else
                                     if trace then trace_natural("filler",current) end
                                     current = current.next
@@ -1075,7 +1080,8 @@ local function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also
                     if trace then trace_natural("ignored parskip",current) end
                     head, current = remove_node(head, current, true)
                 elseif glue_data then
-                    local ps, gs = current.spec, glue_data.spec
+                    local ps = current.spec
+                    local gs = glue_data.spec
                     if ps.writable and gs.writable and ps.width > gs.width then
                         glue_data.spec = copy_node(ps)
                         if trace then trace_natural("taking parskip",current) end
@@ -1131,11 +1137,14 @@ local function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also
                 current = current.next
                 --
             else -- other glue
-                if snap and trace_vsnapping and current.spec.writable and current.spec.width ~= 0 then
-                    report_snapper("glue %p of type %a kept",current.spec.width,skipcodes[subtype])
-                --~ current.spec.width = 0
+                if snap and trace_vsnapping then
+                    local spec = current.spec
+                    if spec.writable and spec.width ~= 0 then
+                        report_snapper("glue %p of type %a kept",current.spec.width,skipcodes[subtype])
+                     -- spec.width = 0
+                    end
                 end
-                if trace then trace_skip(formatted["glue of type %a"](subtype),sc,so,sp,current) end
+                if trace then trace_skip(formatter["glue of type %a"](subtype),sc,so,sp,current) end
                 flush("some glue")
                 current = current.next
             end
@@ -1227,11 +1236,11 @@ function vspacing.pagehandler(newhead,where)
             if stackhack then
                 stackhack = false
                 if trace_collect_vspacing then report("processing %s nodes: %s",newhead) end
---~                 texlists.contrib_head = collapser(newhead,"page",where,trace_page_vspacing,true,a_snapmethod)
+                 -- texlists.contrib_head = collapser(newhead,"page",where,trace_page_vspacing,true,a_snapmethod)
                     newhead = collapser(newhead,"page",where,trace_page_vspacing,true,a_snapmethod)
             else
                 if trace_collect_vspacing then report("flushing %s nodes: %s",newhead) end
---~                 texlists.contrib_head = newhead
+             -- texlists.contrib_head = newhead
             end
         else
             if stackhead then
@@ -1258,97 +1267,21 @@ local ignore = table.tohash {
 
 function vspacing.vboxhandler(head,where)
     if head and not ignore[where] and head.next then
-    --  starttiming(vspacing)
         head = collapser(head,"vbox",where,trace_vbox_vspacing,true,a_snapvbox) -- todo: local snapper
-    --  stoptiming(vspacing)
     end
     return head
 end
 
 function vspacing.collapsevbox(n) -- for boxes but using global a_snapmethod
-    local list = texbox[n].list
-    if list then
-    --  starttiming(vspacing)
-        texbox[n].list = vpack_node(collapser(list,"snapper","vbox",trace_vbox_vspacing,true,a_snapmethod))
-    --  stoptiming(vspacing)
-    end
-end
-
--- We will split this module so a few locals are repeated. Also this will be
--- rewritten.
-
-nodes.builders = nodes.builder or { }
-local builders = nodes.builders
-
-local actions = nodes.tasks.actions("vboxbuilders")
-
-function builders.vpack_filter(head,groupcode,size,packtype,maxdepth,direction)
-    local done = false
-    if head then
-        starttiming(builders)
-        if trace_vpacking then
-            local before = nodes.count(head)
-            head, done = actions(head,groupcode,size,packtype,maxdepth,direction)
-            local after = nodes.count(head)
-            if done then
-                nodes.processors.tracer("vpack","changed",head,groupcode,before,after,true)
-            else
-                nodes.processors.tracer("vpack","unchanged",head,groupcode,before,after,true)
-            end
-        else
-            head, done = actions(head,groupcode)
-        end
-        stoptiming(builders)
-    end
-    return head, done
-end
-
--- This one is special in the sense that it has no head and we operate on the mlv. Also,
--- we need to do the vspacing last as it removes items from the mvl.
-
-local actions = nodes.tasks.actions("mvlbuilders")
-
-local function report(groupcode,head)
-    report_page_builder("trigger: %s",groupcode)
-    report_page_builder("  vsize    : %p",tex.vsize)
-    report_page_builder("  pagegoal : %p",tex.pagegoal)
-    report_page_builder("  pagetotal: %p",tex.pagetotal)
-    report_page_builder("  list     : %s",head and nodeidstostring(head) or "<empty>")
-end
-
-function builders.buildpage_filter(groupcode)
-    local head, done = texlists.contrib_head, false
- -- if head and head.next and head.next.id == hlist_code and head.next.width == 1 then
- --     report_page_builder("trigger otr calculations")
- --     free_node_list(head)
- --     head = nil
- -- end
-    if head then
-        starttiming(builders)
-        if trace_page_builder then
-            report(groupcode,head)
-        end
-        head, done = actions(head,groupcode)
-        stoptiming(builders)
-     -- -- doesn't work here (not passed on?)
-     -- tex.pagegoal = tex.vsize - tex.dimen.d_page_floats_inserted_top - tex.dimen.d_page_floats_inserted_bottom
-        texlists.contrib_head = head
-        return done and head or true
-    else
-        if trace_page_builder then
-            report(groupcode)
+    local box = texgetbox(n)
+    if box then
+        local list = box.list
+        if list then
+            setfield(box,"list",vpack_node(collapser(list,"snapper","vbox",trace_vbox_vspacing,true,a_snapmethod)))
         end
-        return nil, false
     end
 end
 
-callbacks.register('vpack_filter',     builders.vpack_filter,     "vertical spacing etc")
-callbacks.register('buildpage_filter', builders.buildpage_filter, "vertical spacing etc (mvl)")
-
-statistics.register("v-node processing time", function()
-    return statistics.elapsedseconds(builders)
-end)
-
 -- interface
 
 commands.vspacing          = vspacing.analyze
diff --git a/tex/context/base/status-files.pdf b/tex/context/base/status-files.pdf
index bff528ee0..af631be91 100644
Binary files a/tex/context/base/status-files.pdf and b/tex/context/base/status-files.pdf differ
diff --git a/tex/context/base/status-lua.log b/tex/context/base/status-lua.log
new file mode 100644
index 000000000..71ade66ca
--- /dev/null
+++ b/tex/context/base/status-lua.log
@@ -0,0 +1,78 @@
+(cont-yes.mkiv
+
+ConTeXt  ver: 2013.07.12 19:10 MKIV beta  fmt: 2013.7.12  int: english/english
+
+system          > 'cont-new.mkiv' loaded
+(cont-new.mkiv)
+system          > 'cont-loc.mkiv' loaded
+(c:/data/develop/context/sources/cont-loc.mkiv)
+system          > 'cont-exp.mkiv' loaded
+interface       > macros > processed mkvi file 'c:/data/develop/context/sources/cont-exp.mkiv', delta 0
+interface       > macros > processed mkvi file 'c:/data/develop/context/sources/cont-exp.mkiv', delta 0
+(c:/data/develop/context/sources/cont-exp.mkiv)
+system          > files > jobname 's-inf-03', input './s-inf-03.mkiv', result 'status-lua.pdf'
+fonts           > latin modern fonts are not preloaded
+languages       > language 'en' is active
+(c:/data/develop/context/texmf/tex/context/base/s-inf-03.mkiv (c:/data/develop/context/sources/type-loc.mkiv) (type-imp-dejavu.mkiv) (type-imp-xits.mkiv (type-imp-texgyre.mkiv)){c:/data/develop/tex-context/tex/texmf-context/fonts/map/pdftex/context/mkiv-base.map} (virtual://buffer.noname.1
+references      > unknown reference '[][]'
+references      > unknown reference '[][attributes]'
+references      > unknown reference '[][backends]'
+references      > unknown reference '[][bibtex]'
+references      > unknown reference '[][bit32]'
+references      > unknown reference '[][blobs]'
+references      > unknown reference '[][boolean]'
+references      > unknown reference '[][buffers]'
+references      > unknown reference '[][builders]'
+references      > unknown reference '[][caches]'
+references      > unknown reference '[][callback]'
+references      > unknown reference '[][callbacks]'
+references      > unknown reference '[][catcodes]'
+references      > unknown reference '[][characters]'
+references      > unknown reference '[][chemistry]'
+references      > unknown reference '[][commands]'
+references      > unknown reference '[][containers]'
+references      > unknown reference '[][context]'
+references      > unknown reference '[][converters]'
+references      > unknown reference '[][coroutine]'
+references      > unknown reference '[][ctxrunner]'
+references      > unknown reference '[][debug]'
+references      > unknown reference '[][dir]'
+references      > unknown reference '[][directives]'
+references      > unknown reference '[][document]'
+references      > unknown reference '[][documentdata]'
+references      > unknown reference '[][environment]'
+references      > unknown reference '[][epdf]'
+references      > unknown reference '[][experiments]'
+references      > unknown reference '[][figures]'
+references      > unknown reference '[][file]'
+references      > unknown reference '[][floats]'
+references      > unknown reference '[][font]'
+references      > unknown reference '[][fontloader]'
+references      > unknown reference '[][fonts]'
+references      > unknown reference '[][functions]'
+references      > unknown reference '[][graphics]'
+references      > unknown reference '[][gzip]'
+references      > unknown reference '[][img]'
+references      > unknown reference '[][interactions]'
+references      > unknown reference '[][interfaces]'
+references      > unknown reference '[][io]'
+references      > unknown reference '[][job]'
+references      > unknown reference '[][lang]'
+references      > unknown reference '[][languages]'
+references      > unknown reference '[][layouts]'
+references      > unknown reference '[][lfs]'
+references      > unknown reference '[][libraries]'
+references      > unknown reference '[][lmx]'
+references      > unknown reference '[][logs]'
+references      > unknown reference '[][lpdf]'
+references      > unknown reference '[][lpeg]'
+references      > unknown reference '[][ltn12]'
+references      > unknown reference '[][lua]'
+references      > unknown reference '[][luatex]'
+references      > unknown reference '[][lxml]'
+references      > unknown reference '[][math]'
+references      > unknown reference '[][mathematics]'
+references      > unknown reference '[][mbox]'
+references      > unknown reference '[][md5]'
+references      > unknown reference '[][metapost]'
+references      > unknown reference '[][mime]'
\ No newline at end of file
diff --git a/tex/context/base/status-lua.pdf b/tex/context/base/status-lua.pdf
deleted file mode 100644
index c2a6132f1..000000000
Binary files a/tex/context/base/status-lua.pdf and /dev/null differ
diff --git a/tex/context/base/status-mkiv.lua b/tex/context/base/status-mkiv.lua
index 443eee60e..1e8d55c9c 100644
--- a/tex/context/base/status-mkiv.lua
+++ b/tex/context/base/status-mkiv.lua
@@ -3714,7 +3714,12 @@ return {
   {
    category = "lua",
    filename = "m-database",
-   status   = "todo",
+   status   = "okay",
+  },
+  {
+   category = "lua",
+   filename = "m-nodechart",
+   status   = "okay",
   },
   {
    category = "lua",
@@ -5090,7 +5095,12 @@ return {
   {
    category = "mkiv",
    filename = "m-database",
-   status   = "todo",
+   status   = "okay",
+  },
+  {
+   category = "mkiv",
+   filename = "m-nodechart",
+   status   = "okay",
   },
   {
    category = "tex",
diff --git a/tex/context/base/strc-ini.lua b/tex/context/base/strc-ini.lua
index 1e60e253f..09ed79288 100644
--- a/tex/context/base/strc-ini.lua
+++ b/tex/context/base/strc-ini.lua
@@ -20,12 +20,13 @@ but it does not make sense to store all processdata.
 
 ]]--
 
-local formatters = string.formatters
 local lpegmatch = lpeg.match
-local count = tex.count
 local type, next, tonumber, select = type, next, tonumber, select
-local settings_to_array, settings_to_hash = utilities.parsers.settings_to_array, utilities.parsers.settings_to_hash
-local allocate = utilities.storage.allocate
+
+local formatters        = string.formatters
+local settings_to_array = utilities.parsers.settings_to_array
+local settings_to_hash  = utilities.parsers.settings_to_hash
+local allocate          = utilities.storage.allocate
 
 local catcodenumbers    = catcodes.numbers -- better use the context(...) way to switch
 
diff --git a/tex/context/base/strc-itm.lua b/tex/context/base/strc-itm.lua
index 8a745f356..4945c282f 100644
--- a/tex/context/base/strc-itm.lua
+++ b/tex/context/base/strc-itm.lua
@@ -10,19 +10,19 @@ local structures = structures
 local itemgroups = structures.itemgroups
 local jobpasses  = job.passes
 
-local setfield   = jobpasses.save
-local getfield   = jobpasses.getfield
+local setvariable   = jobpasses.save
+local getvariable   = jobpasses.getfield
 
 function itemgroups.register(name,nofitems,maxwidth)
-    setfield("itemgroup", { nofitems, maxwidth })
+    setvariable("itemgroup", { nofitems, maxwidth })
 end
 
 function itemgroups.nofitems(name,index)
-    return getfield("itemgroup", index, 1, 0)
+    return getvariable("itemgroup", index, 1, 0)
 end
 
 function itemgroups.maxwidth(name,index)
-    return getfield("itemgroup", index, 2, 0)
+    return getvariable("itemgroup", index, 2, 0)
 end
 
 -- interface (might become counter/dimension)
@@ -30,9 +30,9 @@ end
 commands.registeritemgroup = itemgroups.register
 
 function commands.nofitems(name,index)
-    context(getfield("itemgroup", index, 1, 0))
+    context(getvariable("itemgroup", index, 1, 0))
 end
 
 function commands.maxitemwidth(name,index)
-    context(getfield("itemgroup", index, 2, 0))
+    context(getvariable("itemgroup", index, 2, 0))
 end
diff --git a/tex/context/base/strc-lnt.mkvi b/tex/context/base/strc-lnt.mkvi
index 4a2cd1cc0..2c26acec7 100644
--- a/tex/context/base/strc-lnt.mkvi
+++ b/tex/context/base/strc-lnt.mkvi
@@ -79,6 +79,8 @@
 \letvalue\??linenotespreviousfrom\empty
 \letvalue\??linenotespreviousto  \empty
 
+% maybe do this in lua
+
 \def\page_lines_in_from{\in[lr:b:\currentlinenotereference]}
 \def\page_lines_in_to  {\in[lr:e:\currentlinenotereference]}
 
@@ -113,14 +115,11 @@
            \notationparameter\c!compressseparator
          \else
            \page_lines_in_from
+           \ifx\m_page_lines_current_from\m_page_lines_current_to\else\endash\page_lines_in_to\fi
          \fi
        \else
          \page_lines_in_from
-         \ifx\m_page_lines_current_from\m_page_lines_current_to
-         \else
-           \endash
-           \page_lines_in_to
-         \fi
+         \ifx\m_page_lines_current_from\m_page_lines_current_to\else\endash\page_lines_in_to\fi
        \fi
      \else
        \page_lines_in_from
diff --git a/tex/context/base/strc-lst.lua b/tex/context/base/strc-lst.lua
index d5597109f..f6a355707 100644
--- a/tex/context/base/strc-lst.lua
+++ b/tex/context/base/strc-lst.lua
@@ -17,7 +17,6 @@ if not modules then modules = { } end modules ['strc-lst'] = {
 
 local format, gmatch, gsub = string.format, string.gmatch, string.gsub
 local tonumber = tonumber
-local texcount = tex.count
 local concat, insert, remove = table.concat, table.insert, table.remove
 local lpegmatch = lpeg.match
 local simple_hash_to_string, settings_to_hash = utilities.parsers.simple_hash_to_string, utilities.parsers.settings_to_hash
@@ -30,6 +29,8 @@ local report_lists      = logs.reporter("structure","lists")
 local context           = context
 local commands          = commands
 
+local texgetcount       = tex.getcount
+
 local structures        = structures
 local lists             = structures.lists
 local sections          = structures.sections
@@ -208,7 +209,7 @@ function lists.enhance(n)
         -- save in the right order (happens at shipout)
         lists.tobesaved[#lists.tobesaved+1] = l
         -- default enhancer (cross referencing)
-        references.realpage = texcount.realpageno
+        references.realpage = texgetcount("realpageno")
         -- tags
         local kind = metadata.kind
         local name = metadata.name
diff --git a/tex/context/base/strc-mar.lua b/tex/context/base/strc-mar.lua
index 26603fe72..02d676fb9 100644
--- a/tex/context/base/strc-mar.lua
+++ b/tex/context/base/strc-mar.lua
@@ -24,9 +24,10 @@ local glyph_code         = nodecodes.glyph
 local hlist_code         = nodecodes.hlist
 local vlist_code         = nodecodes.vlist
 
-local traversenodes      = node.traverse
+local traversenodes      = nodes.traverse
+
 local texsetattribute    = tex.setattribute
-local texbox             = tex.box
+local texgetbox          = tex.getbox
 
 local a_marks            = attributes.private("structure","marks")
 
@@ -137,7 +138,7 @@ setmetatableindex(classes, function(t,k) local s = settings_to_array(k) t[k] = s
 local lasts = { }
 
 function marks.synchronize(class,n,option)
-    local box = texbox[n]
+    local box = texgetbox(n)
     if box then
         local first, last = sweep(box.list,0,0)
         if option == v_keep and first == 0 and last == 0 then
@@ -153,11 +154,16 @@ function marks.synchronize(class,n,option)
             for i=1,#classlist do
                 local class = classlist[i]
                 local range = ranges[class]
-                if not range then
-                    range = { }
+                if range then
+                    range.first = first
+                    range.last  = last
+                else
+                    range = {
+                        first = first,
+                        last  = last,
+                    }
                     ranges[class] = range
                 end
-                range.first, range.last = first, last
                 if trace_marks_get or trace_marks_set then
                     report_marks("action %a, class %a, first %a, last %a","synchronize",class,range.first,range.last)
                 end
diff --git a/tex/context/base/strc-not.lua b/tex/context/base/strc-not.lua
index 8aae8878b..40b78d59f 100644
--- a/tex/context/base/strc-not.lua
+++ b/tex/context/base/strc-not.lua
@@ -8,7 +8,6 @@ if not modules then modules = { } end modules ['strc-not'] = {
 
 local format = string.format
 local next = next
-local texcount = tex.count
 
 local trace_notes      = false  trackers.register("structures.notes",            function(v) trace_notes      = v end)
 local trace_references = false  trackers.register("structures.notes.references", function(v) trace_references = v end)
@@ -24,6 +23,9 @@ local notes            = structures.notes
 local references       = structures.references
 local counterspecials  = counters.specials
 
+local texgetcount      = tex.getcount
+local texgetbox        = tex.getbox
+
 notes.states           = notes.states or { }
 lists.enhancers        = lists.enhancers or { }
 
@@ -189,7 +191,7 @@ local function hascontent(tag)
     local ok = notestates[tag]
     if ok then
         if ok.kind == "insert" then
-            ok = tex.box[ok.number]
+            ok = texgetbox(ok.number)
             if ok then
                 ok = tbs.list
                 ok = lst and lst.next
@@ -257,7 +259,7 @@ function notes.checkpagechange(tag) -- called before increment !
             end
         elseif current then
             -- we need to locate the next one, best guess
-            if texcount.realpageno > current.pagenumber.number then
+            if texgetcount("realpageno") > current.pagenumber.number then
                 counters.reset(tag)
             end
         end
@@ -280,7 +282,7 @@ commands.postponenotes = notes.postpone
 function notes.setsymbolpage(tag,n,l)
     local l = l or listindex(tag,n)
     if l then
-        local p = texcount.realpageno
+        local p = texgetcount("realpageno")
         if trace_notes or trace_references then
             report_notes("note %a of %a with list index %a gets symbol page %a",n,tag,l,p)
         end
@@ -382,7 +384,7 @@ function commands.flushnotes(tag,whatkind,how) -- store and postpone
                         local rp = get(tag,i)
                         rp = rp and rp.references
                         rp = rp and rp.symbolpage or 0
-                        if rp > texcount.realpageno then
+                        if rp > texgetcount("realpageno") then
                             state.start = i
                             return
                         end
diff --git a/tex/context/base/strc-num.lua b/tex/context/base/strc-num.lua
index b0eae6b78..866dc146c 100644
--- a/tex/context/base/strc-num.lua
+++ b/tex/context/base/strc-num.lua
@@ -9,7 +9,7 @@ if not modules then modules = { } end modules ['strc-num'] = {
 local format = string.format
 local next, type = next, type
 local min, max = math.min, math.max
-local texcount, texsetcount = tex.count, tex.setcount
+local texsetcount = tex.setcount
 
 -- Counters are managed here. They can have multiple levels which makes it easier to synchronize
 -- them. Synchronization is sort of special anyway, as it relates to document structuring.
diff --git a/tex/context/base/strc-pag.lua b/tex/context/base/strc-pag.lua
index a71c6bca6..02ed5610f 100644
--- a/tex/context/base/strc-pag.lua
+++ b/tex/context/base/strc-pag.lua
@@ -6,8 +6,6 @@ if not modules then modules = { } end modules ['strc-pag'] = {
     license   = "see context related readme files"
 }
 
-local texcount = tex.count
-
 local allocate, mark = utilities.storage.allocate, utilities.storage.mark
 
 local trace_pages         = false  trackers.register("structures.pages", function(v) trace_pages = v end)
@@ -33,6 +31,9 @@ local applyprocessor      = processors.apply
 local startapplyprocessor = processors.startapply
 local stopapplyprocessor  = processors.stopapply
 
+local texsetcount         = tex.setcount
+local texgetcount         = tex.getcount
+
 -- storage
 
 local collected, tobesaved = allocate(), allocate()
@@ -50,7 +51,8 @@ job.register('structures.pages.collected', tobesaved, initializer)
 local specification = { } -- to be checked
 
 function pages.save(prefixdata,numberdata)
-    local realpage, userpage = texcount.realpageno, texcount.userpageno
+    local realpage = texgetcount("realpageno")
+    local userpage = texgetcount("userpageno")
     if realpage > 0 then
         if trace_pages then
             report_pages("saving page %s.%s",realpage,userpage)
@@ -74,24 +76,24 @@ end
 -- builder we have to make sure it starts at least at 1.
 
 function counters.specials.userpage()
-    local r = texcount.realpageno
+    local r = texgetcount("realpageno")
     if r > 0 then
         local t = tobesaved[r]
         if t then
-            t.number = texcount.userpageno
+            t.number = texgetcount("userpageno")
             if trace_pages then
                 report_pages("forcing pagenumber of realpage %s to %s",r,t.number)
             end
             return
         end
     end
-    local u = texcount.userpageno
+    local u = texgetcount("userpageno")
     if u == 0 then
         if trace_pages then
             report_pages("forcing pagenumber of realpage %s to %s (probably a bug)",r,1)
         end
         counters.setvalue("userpage",1)
-        texcount.userpageno = 1
+        texsetcount("userpageno",1) -- not global ?
     end
 end
 
@@ -253,8 +255,8 @@ function helpers.prefix(data,prefixspec)
 end
 
 function pages.is_odd(n)
-    n = n or texcount.realpageno
-    if texcount.pagenoshift % 2 == 0 then
+    n = n or texgetcount("realpageno")
+    if texgetcount("pagenoshift") % 2 == 0 then
         return n % 2 == 0
     else
         return n % 2 ~= 0
diff --git a/tex/context/base/strc-ref.lua b/tex/context/base/strc-ref.lua
index 8e65a6b19..4c4a0fe7f 100644
--- a/tex/context/base/strc-ref.lua
+++ b/tex/context/base/strc-ref.lua
@@ -15,7 +15,6 @@ if not modules then modules = { } end modules ['strc-ref'] = {
 -- todo: autoload components when :::
 
 local format, find, gmatch, match, concat = string.format, string.find, string.gmatch, string.match, table.concat
-local texcount, texsetcount = tex.count, tex.setcount
 local floor = math.floor
 local rawget, tonumber = rawget, tonumber
 local lpegmatch = lpeg.match
@@ -49,15 +48,15 @@ local constants          = interfaces.constants
 local context            = context
 local commands           = commands
 
+local texgetcount        = tex.getcount
+local texsetcount        = tex.setcount
+local texconditionals    = tex.conditionals
+
 local v_default          = variables.default
 local v_url              = variables.url
 local v_file             = variables.file
 local v_unknown          = variables.unknown
 local v_yes              = variables.yes
-
-local texcount           = tex.count
-local texconditionals    = tex.conditionals
-
 local productcomponent   = resolvers.jobs.productcomponent
 local justacomponent     = resolvers.jobs.justacomponent
 
@@ -212,7 +211,7 @@ local function referredpage(n)
         end
     end
     -- fallback
-    return texcount.realpageno
+    return texgetcount("realpageno")
 end
 
 -- setmetatableindex(referred,function(t,k) return referredpage(k) end )
@@ -224,7 +223,7 @@ function references.registerpage(n) -- called in the backend code
         if n > maxreferred then
             maxreferred = n
         end
-        tobereferred[n] = texcount.realpageno
+        tobereferred[n] = texgetcount("realpageno")
     end
 end
 
@@ -250,7 +249,7 @@ references.setnextorder = setnextorder
 
 function references.setnextinternal(kind,name)
     setnextorder(kind,name) -- always incremented with internal
-    local n = texcount.locationcount + 1
+    local n = texgetcount("locationcount") + 1
     texsetcount("global","locationcount",n)
     return n
 end
@@ -309,7 +308,7 @@ end
 function references.enhance(prefix,tag)
     local l = tobesaved[prefix][tag]
     if l then
-        l.references.realpage = texcount.realpageno
+        l.references.realpage = texgetcount("realpageno")
     end
 end
 
@@ -1545,7 +1544,7 @@ local function identify(prefix,reference)
     end
     local set = resolve(prefix,reference)
     local bug = false
-    texcount.referencehastexstate = set.has_tex and 1 or 0
+    texsetcount("referencehastexstate",set.has_tex and 1 or 0)
     nofidentified = nofidentified + 1
     set.n = nofidentified
     for i=1,#set do
@@ -1673,14 +1672,14 @@ function references.setinternalreference(prefix,tag,internal,view) -- needs chec
             t[tn] = "aut:" .. internal
         end
         local destination = references.mark(t,nil,nil,view) -- returns an attribute
-        texcount.lastdestinationattribute = destination
+        texsetcount("lastdestinationattribute",destination)
         return destination
     end
 end
 
 function references.setandgetattribute(kind,prefix,tag,data,view) -- maybe do internal automatically here
     local attr = references.set(kind,prefix,tag,data) and references.setinternalreference(prefix,tag,nil,view) or unsetvalue
-    texcount.lastdestinationattribute = attr
+    texsetcount("lastdestinationattribute",attr)
     return attr
 end
 
@@ -2039,7 +2038,7 @@ function references.checkedrealpage(r)
         realpageofpage(r) -- just initialize
     end
     if not r then
-        return texcount.realpageno
+        return texgetcount("realpageno")
     elseif r < 1 then
         return 1
     elseif r > nofrealpages then
@@ -2132,7 +2131,7 @@ end
 function specials.deltapage(var,actions)
     local p = tonumber(var.operation)
     if p then
-        p = references.checkedrealpage(p + texcount.realpageno)
+        p = references.checkedrealpage(p + texgetcount("realpageno"))
         var.r = p
         actions.realpage = actions.realpage or p -- first wins
     end
diff --git a/tex/context/base/strc-reg.lua b/tex/context/base/strc-reg.lua
index f4682c90d..2356acb9b 100644
--- a/tex/context/base/strc-reg.lua
+++ b/tex/context/base/strc-reg.lua
@@ -7,7 +7,6 @@ if not modules then modules = { } end modules ['strc-reg'] = {
 }
 
 local next, type = next, type
-local texcount = tex.count
 local format, gmatch = string.format, string.gmatch
 local equal, concat, remove = table.are_equal, table.concat, table.remove
 local utfchar = utf.char
@@ -33,6 +32,8 @@ local replacements    = sorters.replacements
 local processors      = typesetters.processors
 local splitprocessor  = processors.split
 
+local texgetcount     = tex.getcount
+
 local variables       = interfaces.variables
 local context         = context
 local commands        = commands
@@ -287,7 +288,7 @@ end
 function registers.enhance(name,n)
     local r = tobesaved[name].entries[n]
     if r then
-        r.references.realpage = texcount.realpageno
+        r.references.realpage = texgetcount("realpageno")
     end
 end
 
@@ -299,7 +300,7 @@ function registers.extend(name,tag,rawdata) -- maybe do lastsection internally
         local r = tobesaved[name].entries[tag]
         if r then
             local rr = r.references
-            rr.lastrealpage = texcount.realpageno
+            rr.lastrealpage = texgetcount("realpageno")
             rr.lastsection = sections.currentid()
             if rawdata then
                 if rawdata.entries then
diff --git a/tex/context/base/supp-box.lua b/tex/context/base/supp-box.lua
index 8e75b4f63..a8603ace3 100644
--- a/tex/context/base/supp-box.lua
+++ b/tex/context/base/supp-box.lua
@@ -6,7 +6,7 @@ if not modules then modules = { } end modules ['supp-box'] = {
     license   = "see context related readme files"
 }
 
--- this is preliminary code
+-- this is preliminary code, use insert_before etc
 
 local report_hyphenation = logs.reporter("languages","hyphenation")
 
@@ -30,7 +30,9 @@ local copy_list    = node.copy_list
 local copy_node    = node.copy
 local find_tail    = node.tail
 
-local texbox       = tex.box
+local texsetbox    = tex.setbox
+local texgetbox    = tex.getbox
+local texget       = tex.get
 
 local function hyphenatedlist(list)
     while list do
@@ -60,7 +62,7 @@ end
 
 local function checkedlist(list)
     if type(list) == "number" then
-        return texbox[list].list
+        return texgetbox(list).list
     else
         return list
     end
@@ -116,7 +118,7 @@ commands.applytochars = applytochars
 commands.applytowords = applytowords
 
 function commands.vboxlisttohbox(original,target,inbetween)
-    local current = texbox[original].list
+    local current = texgetbox(original).list
     local head = nil
     local tail = nil
     while current do
@@ -142,12 +144,12 @@ function commands.vboxlisttohbox(original,target,inbetween)
     end
     local result = new_hlist()
     result.list = head
-    texbox[target] = result
+    texsetbox(target,result)
 end
 
 function commands.hboxtovbox(original)
-    local b = texbox[original]
-    local factor = tex.baselineskip.width / tex.hsize
+    local b = texgetbox(original)
+    local factor = texget("baselineskip").width / texget("hsize")
     b.depth = 0
     b.height = b.width * factor
 end
diff --git a/tex/context/base/tabl-tbl.lua b/tex/context/base/tabl-tbl.lua
index 19548e7b3..21564a472 100644
--- a/tex/context/base/tabl-tbl.lua
+++ b/tex/context/base/tabl-tbl.lua
@@ -15,7 +15,7 @@ local tonumber = tonumber
 local gsub, rep, sub, find = string.gsub, string.rep, string.sub, string.find
 local P, C, Cc, Ct, lpegmatch = lpeg.P, lpeg.C, lpeg.Cc, lpeg.Ct, lpeg.match
 
-local settexcount = tex.setcount
+local texsetcount = tex.setcount
 
 local separator = P("|")
 local nested    = lpeg.patterns.nested
@@ -31,9 +31,9 @@ function commands.presettabulate(preamble)
     end
     local t = lpegmatch(pattern,preamble)
     local m = #t - 2
-    settexcount("global","c_tabl_tabulate_nofcolumns", m/2)
-    settexcount("global","c_tabl_tabulate_has_rule_spec_first", t[1] == "" and 0 or 1)
-    settexcount("global","c_tabl_tabulate_has_rule_spec_last", t[m+1] == "" and 0 or 1)
+    texsetcount("global","c_tabl_tabulate_nofcolumns", m/2)
+    texsetcount("global","c_tabl_tabulate_has_rule_spec_first", t[1] == "" and 0 or 1)
+    texsetcount("global","c_tabl_tabulate_has_rule_spec_last", t[m+1] == "" and 0 or 1)
     for i=1,m,2 do
         context.settabulateentry(t[i],t[i+1])
     end
diff --git a/tex/context/base/tabl-xtb.lua b/tex/context/base/tabl-xtb.lua
index 3ffe8a219..b0af0d5c8 100644
--- a/tex/context/base/tabl-xtb.lua
+++ b/tex/context/base/tabl-xtb.lua
@@ -27,11 +27,12 @@ this mechamism will be improved so that it can replace its older cousin.
 
 local commands, context, tex, node = commands, context, tex, node
 
-local texdimen    = tex.dimen
-local texcount    = tex.count
-local texbox      = tex.box
+local texgetcount = tex.getcount
 local texsetcount = tex.setcount
+local texgetbox   = tex.getbox
+local texgetdimen = tex.getdimen
 local texsetdimen = tex.setdimen
+local texget      = tex.get
 
 local format      = string.format
 local concat      = table.concat
@@ -171,11 +172,11 @@ function xtables.create(settings)
     settings.leftmargindistance = tonumber(settings.leftmargindistance) or 0
     settings.rightmargindistance = tonumber(settings.rightmargindistance) or 0
     settings.options = settings_to_hash(settings.option)
-    settings.textwidth = tonumber(settings.textwidth) or tex.hsize
-    settings.lineheight = tonumber(settings.lineheight) or texdimen.lineheight
+    settings.textwidth = tonumber(settings.textwidth) or texget("hsize")
+    settings.lineheight = tonumber(settings.lineheight) or texgetdimen("lineheight")
     settings.maxwidth = tonumber(settings.maxwidth) or settings.textwidth/8
  -- if #stack > 0 then
- --     settings.textwidth = tex.hsize
+ --     settings.textwidth = texget("hsize")
  -- end
     data.criterium_v =   2 * data.settings.lineheight
     data.criterium_h = .75 * data.settings.textwidth
@@ -186,10 +187,10 @@ function xtables.initialize_reflow_width(option)
     local r = data.currentrow
     local c = data.currentcolumn + 1
     local drc = data.rows[r][c]
-    drc.nx = texcount.c_tabl_x_nx
-    drc.ny = texcount.c_tabl_x_ny
+    drc.nx = texgetcount("c_tabl_x_nx")
+    drc.ny = texgetcount("c_tabl_x_ny")
     local distances = data.distances
-    local distance = texdimen.d_tabl_x_distance
+    local distance = texgetdimen("d_tabl_x_distance")
     if distance > distances[c] then
         distances[c] = distance
     end
@@ -214,7 +215,7 @@ function xtables.set_reflow_width()
     while row[c].span do -- can also be previous row ones
         c = c + 1
     end
-    local tb = texbox.b_tabl_x
+    local tb = texgetbox("b_tabl_x")
     local drc = row[c]
     --
     drc.list = true -- we don't need to keep the content around as we're in trial mode (no: copy_node_list(tb))
@@ -232,7 +233,7 @@ function xtables.set_reflow_width()
         depths[r] = depth
     end
     --
-    local dimensionstate = texcount.frameddimensionstate
+    local dimensionstate = texgetcount("frameddimensionstate")
     local fixedcolumns = data.fixedcolumns
     local fixedrows = data.fixedrows
     if dimensionstate == 1 then
@@ -294,19 +295,19 @@ function xtables.initialize_reflow_height()
     for x=1,drc.nx-1 do
         w = w + widths[c+x]
     end
-    texdimen.d_tabl_x_width = w
+    texsetdimen("d_tabl_x_width",w)
     local dimensionstate = drc.dimensionstate or 0
     if dimensionstate == 1 or dimensionstate == 3 then
         -- width was fixed so height is known
-        texcount.c_tabl_x_skip_mode = 1
+        texsetcount("c_tabl_x_skip_mode",1)
     elseif dimensionstate == 2 then
         -- height is enforced
-        texcount.c_tabl_x_skip_mode = 1
+        texsetcount("c_tabl_x_skip_mode",1)
     elseif data.autowidths[c] then
         -- width has changed so we need to recalculate the height
-        texcount.c_tabl_x_skip_mode = 0
+        texsetcount("c_tabl_x_skip_mode",0)
     else
-        texcount.c_tabl_x_skip_mode = 1
+        texsetcount("c_tabl_x_skip_mode",1)
     end
 end
 
@@ -315,10 +316,10 @@ function xtables.set_reflow_height()
     local c = data.currentcolumn
     local rows = data.rows
     local row = rows[r]
---     while row[c].span do -- we could adapt drc.nx instead
---         c = c + 1
---     end
-    local tb = texbox.b_tabl_x
+ -- while row[c].span do -- we could adapt drc.nx instead
+ --     c = c + 1
+ -- end
+    local tb = texgetbox("b_tabl_x")
     local drc = row[c]
     if data.fixedrows[r] == 0 then --  and drc.dimensionstate < 2
         local heights, height = data.heights, tb.height
@@ -330,8 +331,8 @@ function xtables.set_reflow_height()
             depths[r] = depth
         end
     end
---     c = c + drc.nx - 1
---     data.currentcolumn = c
+ -- c = c + drc.nx - 1
+ -- data.currentcolumn = c
 end
 
 function xtables.initialize_construct()
@@ -357,9 +358,9 @@ function xtables.initialize_construct()
         h = h + heights[r+y]
         d = d + depths[r+y]
     end
-    texdimen.d_tabl_x_width = w
-    texdimen.d_tabl_x_height = h + d
-    texdimen.d_tabl_x_depth = 0
+    texsetdimen("d_tabl_x_width",w)
+    texsetdimen("d_tabl_x_height",h + d)
+    texsetdimen("d_tabl_x_depth",0)
 end
 
 function xtables.set_construct()
@@ -367,14 +368,14 @@ function xtables.set_construct()
     local c = data.currentcolumn
     local rows = data.rows
     local row = rows[r]
---     while row[c].span do -- can also be previous row ones
---         c = c + 1
---     end
+ -- while row[c].span do -- can also be previous row ones
+ --     c = c + 1
+ -- end
     local drc = row[c]
     -- this will change as soon as in luatex we can reset a box list without freeing
-    drc.list = copy_node_list(texbox.b_tabl_x)
---     c = c + drc.nx - 1
---     data.currentcolumn = c
+    drc.list = copy_node_list(texgetbox("b_tabl_x"))
+ -- c = c + drc.nx - 1
+ -- data.currentcolumn = c
 end
 
 local function showwidths(where,widths,autowidths)
@@ -669,8 +670,8 @@ function xtables.construct()
             end
             local kern = new_kern(step)
             if stop then
-                stop.prev = kern
                 stop.next = kern
+                kern.prev = stop
             else -- can be first spanning next row (ny=...)
                 start = kern
             end
@@ -724,6 +725,8 @@ function xtables.construct()
     end
 end
 
+-- todo: join as that is as efficient as fushing multiple
+
 local function inject(row,copy,package)
     local list = row[1]
     if copy then
@@ -966,7 +969,7 @@ end
 
 function xtables.next_row()
     local r = data.currentrow + 1
-    data.modes[r] = texcount.c_tabl_x_mode
+    data.modes[r] = texgetcount("c_tabl_x_mode")
     data.currentrow = r
     data.currentcolumn = 0
 end
diff --git a/tex/context/base/task-ini.lua b/tex/context/base/task-ini.lua
index 0f477cb6e..107bfe226 100644
--- a/tex/context/base/task-ini.lua
+++ b/tex/context/base/task-ini.lua
@@ -103,7 +103,7 @@ appendaction("vboxbuilders", "normalizers", "builders.vspacing.vboxhandler")
 
 -- experimental too
 
-appendaction("mvlbuilders","normalizers","typesetters.checkers.handler")
+appendaction("mvlbuilders", "normalizers","typesetters.checkers.handler")
 appendaction("vboxbuilders","normalizers","typesetters.checkers.handler")
 
 -- speedup: only kick in when used
diff --git a/tex/context/base/trac-deb.lua b/tex/context/base/trac-deb.lua
index fe167c343..4cc48c4a5 100644
--- a/tex/context/base/trac-deb.lua
+++ b/tex/context/base/trac-deb.lua
@@ -11,22 +11,25 @@ local lpeg, status = lpeg, status
 local lpegmatch = lpeg.match
 local format, concat, match = string.format, table.concat, string.match
 local tonumber, tostring = tonumber, tostring
-local texdimen, textoks, texcount = tex.dimen, tex.toks, tex.count
 
 -- maybe tracers -> tracers.tex (and tracers.lua for current debugger)
 
 local report_system = logs.reporter("system","tex")
 
-tracers         = tracers or { }
-local tracers   = tracers
+tracers             = tracers or { }
+local tracers       = tracers
 
-tracers.lists   = { }
-local lists     = tracers.lists
+tracers.lists       = { }
+local lists         = tracers.lists
 
-tracers.strings = { }
-local strings   = tracers.strings
+tracers.strings     = { }
+local strings       = tracers.strings
 
-strings.undefined = "undefined"
+local texgetdimen   = tex.getdimen
+local texgettoks    = tex.gettoks
+local texgetcount   = tex.getcount
+
+strings.undefined   = "undefined"
 
 lists.scratch = {
     0, 2, 4, 6, 8
@@ -71,16 +74,16 @@ function tracers.cs(csname)
 end
 
 function tracers.dimen(name)
-    local d = texdimen[name]
+    local d = texgetdimen(name)
     return d and number.topoints(d) or strings.undefined
 end
 
 function tracers.count(name)
-    return texcount[name] or strings.undefined
+    return texgetcount(name) or strings.undefined
 end
 
 function tracers.toks(name,limit)
-    local t = textoks[name]
+    local t = texgettoks(name)
     return t and string.limit(t,tonumber(limit) or 40) or strings.undefined
 end
 
diff --git a/tex/context/base/trac-inf.lua b/tex/context/base/trac-inf.lua
index eefc15a6f..79cbdba3f 100644
--- a/tex/context/base/trac-inf.lua
+++ b/tex/context/base/trac-inf.lua
@@ -11,20 +11,24 @@ if not modules then modules = { } end modules ['trac-inf'] = {
 -- get warnings about assignments. This is more efficient than using rawset
 -- and rawget.
 
-local type, tonumber = type, tonumber
+local type, tonumber, select = type, tonumber, select
 local format, lower = string.format, string.lower
 local concat = table.concat
 local clock = os.gettimeofday or os.clock -- should go in environment
 
-statistics       = statistics or { }
-local statistics = statistics
+local setmetatableindex = table.setmetatableindex
+local serialize         = table.serialize
+local formatters        = string.formatters
 
-statistics.enable    = true
-statistics.threshold = 0.01
+statistics              = statistics or { }
+local statistics        = statistics
+
+statistics.enable       = true
+statistics.threshold    = 0.01
 
 local statusinfo, n, registered, timers = { }, 0, { }, { }
 
-table.setmetatableindex(timers,function(t,k)
+setmetatableindex(timers,function(t,k)
     local v = { timing = 0, loadtime = 0 }
     t[k] = v
     return v
@@ -178,6 +182,19 @@ function statistics.timed(action)
     report("total runtime: %s",elapsedtime("run"))
 end
 
+-- goodie
+
+function statistics.tracefunction(base,tag,...)
+    for i=1,select("#",...) do
+        local name = select(i,...)
+        local stat = { }
+        local func = base[name]
+        setmetatableindex(stat,function(t,k) t[k] = 0 return 0 end)
+        base[name] = function(n,k,v) stat[k] = stat[k] + 1 return func(n,k,v) end
+        statistics.register(formatters["%s.%s"](tag,name),function() return serialize(stat,"calls") end)
+    end
+end
+
 -- where, not really the best spot for this:
 
 commands = commands or { }
diff --git a/tex/context/base/trac-jus.lua b/tex/context/base/trac-jus.lua
index 9d99f059d..38220a752 100644
--- a/tex/context/base/trac-jus.lua
+++ b/tex/context/base/trac-jus.lua
@@ -15,19 +15,16 @@ local a_alignstate        = attributes.private("alignstate")
 local a_justification     = attributes.private("justification")
 
 local tracers             = nodes.tracers
-local setcolor            = tracers.colors.set
-local settransparency     = tracers.transparencies.set
+local tracedrule          = tracers.rule
 
 local new_rule            = nodes.pool.rule
+local new_hlist           = nodes.pool.hlist
 local new_glue            = nodes.pool.glue
 local new_kern            = nodes.pool.kern
-local concat_nodes        = nodes.concat
-local hpack_nodes         = node.hpack
-local copy_node           = node.copy
 local get_list_dimensions = node.dimensions
 local hlist_code          = nodes.nodecodes.hlist
 
-local tex_set_attribute   = tex.setattribute
+local texsetattribute     = tex.setattribute
 local unsetvalue          = attributes.unsetvalue
 
 local min_threshold = 0
@@ -36,14 +33,14 @@ local max_threshold = 0
 local function set(n)
     nodes.tasks.enableaction("mvlbuilders", "typesetters.checkers.handler")
     nodes.tasks.enableaction("vboxbuilders","typesetters.checkers.handler")
-    tex_set_attribute(a_justification,n or 1)
+    texsetattribute(a_justification,n or 1)
     function typesetters.checkers.set(n)
-        tex_set_attribute(a_justification,n or 1)
+        texsetattribute(a_justification,n or 1)
     end
 end
 
 local function reset()
-    tex_set_attribute(a_justification,unsetvalue)
+    texsetattribute(a_justification,unsetvalue)
 end
 
 checkers.set   = set
@@ -74,58 +71,22 @@ function checkers.handler(head)
                     if naturalwidth == 0 or delta == 0 then
                         -- special box
                     elseif delta >= max_threshold then
-                        local rule = new_rule(delta,naturalheight,naturaldepth)
-                        list = hpack_nodes(list,width,"exactly")
-                        if list.glue_set == 1 then
-                            setcolor(rule,"trace:dr")
-                            settransparency(rule,"trace:dr")
-                        else
-                            setcolor(rule,"trace:db")
-                            settransparency(rule,"trace:db")
-                        end
-                        rule = hpack_nodes(rule)
-                        rule.width = 0
-                        rule.height = 0
-                        rule.depth = 0
-                        current.list = concat_nodes { list, rule }
-                     -- current.list = concat_nodes { list, new_kern(-naturalwidth+width), rule }
+                        local rule = tracedrule(delta,naturalheight,naturaldepth,list.glue_set == 1 and "trace:dr"or "trace:db")
+                        current.list = list .. new_hlist(rule)
                     elseif delta <= min_threshold then
                         local alignstate = list[a_alignstate]
                         if alignstate == 1 then
-                            local rule = new_rule(-delta,naturalheight,naturaldepth)
-                            setcolor(rule,"trace:dc")
-                            settransparency(rule,"trace:dc")
-                            rule = hpack_nodes(rule)
-                            rule.height = 0
-                            rule.depth = 0
-                            rule.width = 0
-                            current.list = nodes.concat { rule, list }
+                            local rule = tracedrule(-delta,naturalheight,naturaldepth,"trace:dc")
+                            current.list = new_hlist(rule) .. list
                         elseif alignstate == 2 then
-                            local rule = new_rule(-delta/2,naturalheight,naturaldepth)
-                            setcolor(rule,"trace:dy")
-                            settransparency(rule,"trace:dy")
-                            rule = hpack_nodes(rule)
-                            rule.width = 0
-                            rule.height = 0
-                            rule.depth = 0
-                            current.list = concat_nodes { copy_node(rule), list, new_kern(delta/2), rule }
+                            local rule = tracedrule(-delta/2,naturalheight,naturaldepth,"trace:dy")
+                            current.list = new_hlist(rule^1) .. list .. new_kern(delta/2) .. new_hlist(rule)
                         elseif alignstate == 3 then
-                            local rule = new_rule(-delta,naturalheight,naturaldepth)
-                            setcolor(rule,"trace:dm")
-                            settransparency(rule,"trace:dm")
-                            rule = hpack_nodes(rule)
-                            rule.height = 0
-                            rule.depth = 0
-                            current.list = concat_nodes { list, new_kern(delta), rule }
+                            local rule = tracedrule(-delta,naturalheight,naturaldepth,"trace:dm")
+                            current.list = list .. new_kern(delta) .. new_hlist(rule)
                         else
-                            local rule = new_rule(-delta,naturalheight,naturaldepth)
-                            setcolor(rule,"trace:dg")
-                            settransparency(rule,"trace:dg")
-                            rule = hpack_nodes(rule)
-                            rule.height = 0
-                            rule.depth = 0
-                            rule.width = 0
-                            current.list = concat_nodes { list, new_kern(delta), rule }
+                            local rule = tracedrule(-delta,naturalheight,naturaldepth,"trace:dg")
+                            current.list = list .. new_kern(delta) .. new_hlist(rule)
                         end
                     end
                 end
diff --git a/tex/context/base/trac-log.lua b/tex/context/base/trac-log.lua
index 1f2520130..0ae5d87e3 100644
--- a/tex/context/base/trac-log.lua
+++ b/tex/context/base/trac-log.lua
@@ -68,13 +68,14 @@ local write_nl, write = texio and texio.write_nl or print, texio and texio.write
 local format, gmatch, find = string.format, string.gmatch, string.find
 local concat, insert, remove = table.concat, table.insert, table.remove
 local topattern = string.topattern
-local texcount = tex and tex.count
 local next, type, select = next, type, select
 local utfchar = utf.char
 
 local setmetatableindex = table.setmetatableindex
 local formatters        = string.formatters
 
+local texgetcount       = tex and tex.getcount
+
 --[[ldx--
 <p>This is a prelude to a more extensive logging module. We no longer
 provide <l n='xml'/> based logging as parsing is relatively easy anyway.</p>
@@ -129,7 +130,7 @@ setmetatableindex(logs, function(t,k) t[k] = ignore ; return ignore end)
 
 local report, subreport, status, settarget, setformats, settranslations
 
-local direct, subdirect, writer, pushtarget, poptarget
+local direct, subdirect, writer, pushtarget, poptarget, setlogfile
 
 if tex and (tex.jobname or tex.formatname) then
 
@@ -270,11 +271,15 @@ if tex and (tex.jobname or tex.formatname) then
         translations = t
     end
 
+    setlogfile = ignore
+
 else
 
     logs.flush = ignore
 
-    writer = write_nl
+    writer = function(s)
+        write_nl(s)
+    end
 
     newline = function()
         write_nl("\n")
@@ -334,6 +339,29 @@ else
     setformats      = ignore
     settranslations = ignore
 
+    local f_timed = formatters["[%S] "]
+
+    setlogfile = function(name,keepopen)
+        if name and name ~= "" then
+            local localtime = os.localtime
+            local writeline = write_nl
+            if keepopen then
+                local f = io.open(name,"ab")
+                write_nl = function(s)
+                    writeline(s)
+                    f:write(f_timed(localtime()),s,"\n")
+                end
+            else
+                write_nl = function(s)
+                    writeline(s)
+                    local f = io.open(name,"ab")
+                    f:write(f_timed(localtime()),s,"\n")
+                    f:close()
+                end
+            end
+        end
+    end
+
 end
 
 logs.report          = report
@@ -345,6 +373,8 @@ logs.poptarget       = poptarget
 logs.setformats      = setformats
 logs.settranslations = settranslations
 
+logs.setlogfile      = setlogfile
+
 logs.direct          = direct
 logs.subdirect       = subdirect
 logs.writer          = writer
@@ -529,8 +559,9 @@ local report_pages = logs.reporter("pages") -- not needed but saves checking whe
 local real, user, sub
 
 function logs.start_page_number()
-    real, user, sub = texcount.realpageno, texcount.userpageno, texcount.subpageno
---     real, user, sub = 0, 0, 0
+    real = texgetcount("realpageno")
+    user = texgetcount("userpageno")
+    sub  = texgetcount("subpageno")
 end
 
 local timing    = false
diff --git a/tex/context/base/trac-vis.lua b/tex/context/base/trac-vis.lua
index 762c71ab5..4a24dacf4 100644
--- a/tex/context/base/trac-vis.lua
+++ b/tex/context/base/trac-vis.lua
@@ -60,11 +60,9 @@ local rightskip_code      = gluecodes.rightskip
 
 local whatsitcodes        = nodes.whatsitcodes
 
-local concat_nodes        = nodes.concat
 local hpack_nodes         = node.hpack
 local vpack_nodes         = node.vpack
-local hpack_string        = typesetters.hpack
-local fast_hpack_string   = typesetters.fast_hpack
+local fast_hpack_string   = nodes.typesetters.fast_hpack
 local copy_node           = node.copy
 local copy_list           = node.copy_list
 local free_node           = node.free
@@ -74,8 +72,9 @@ local insert_node_after   = node.insert_after
 local fast_hpack          = nodes.fasthpack
 local traverse_nodes      = node.traverse
 
-local tex_attribute       = tex.attribute
-local tex_box             = tex.box
+local texgetattribute     = tex.getattribute
+local texsetattribute     = tex.setattribute
+local texgetbox           = tex.getbox
 local unsetvalue          = attributes.unsetvalue
 
 local current_font        = font.current
@@ -247,11 +246,11 @@ local function setvisual(n,a,what) -- this will become more efficient when we ha
 end
 
 function visualizers.setvisual(n)
-    tex_attribute[a_visual] = setvisual(n,tex_attribute[a_visual])
+    texsetattribute(a_visual,setvisual(n,texgetattribute(a_visual)))
 end
 
 function visualizers.setlayer(n)
-    tex_attribute[a_layer] = layers[n] or unsetvalue
+    texsetattribute(a_layer,layers[n] or unsetvalue)
 end
 
 commands.setvisual = visualizers.setvisual
@@ -262,7 +261,7 @@ function commands.visual(n)
 end
 
 local function set(mode,v)
-    tex_attribute[a_visual] = setvisual(mode,tex_attribute[a_visual],v)
+    texsetattribute(a_visual,setvisual(mode,texgetattribute(a_visual),v))
 end
 
 for mode, value in next, modes do
@@ -305,11 +304,7 @@ local function sometext(str,layer,color,textcolor) -- we can just paste verbatim
     if textcolor then
         setlistcolor(text.list,textcolor)
     end
-    local info = concat_nodes {
-        rule,
-        kern,
-        text,
-    }
+    local info = rule .. kern .. text
     setlisttransparency(info,c_zero)
     info = fast_hpack(info)
     if layer then
@@ -343,10 +338,7 @@ local function fontkern(head,current)
         setlisttransparency(list,c_text_d)
         settransparency(rule,c_text_d)
         text.shift = -5 * exheight
-        info = concat_nodes {
-            rule,
-            text,
-        }
+        info = rule .. text
         info = fast_hpack(info)
         info[a_layer] = l_fontkern
         info.width = 0
@@ -426,9 +418,12 @@ local b_cache = { }
 local function ruledbox(head,current,vertical,layer,what,simple)
     local wd = current.width
     if wd ~= 0 then
-        local ht, dp = current.height, current.depth
-        local next, prev = current.next, current.prev
-        current.next, current.prev = nil, nil
+        local ht = current.height
+        local dp = current.depth
+        local next = current.next
+        local prev = current.prev
+        current.next = nil
+        current.prev = nil
         local linewidth = emwidth/10
         local baseline, baseskip
         if dp ~= 0 and ht ~= 0 then
@@ -437,19 +432,16 @@ local function ruledbox(head,current,vertical,layer,what,simple)
                 if not baseline then
                     -- due to an optimized leader color/transparency we need to set the glue node in order
                     -- to trigger this mechanism
-                    local leader = concat_nodes {
-                        new_glue(2*linewidth),              -- 2.5
-                        new_rule(6*linewidth,linewidth,0),  -- 5.0
-                        new_glue(2*linewidth),              -- 2.5
-                    }
+                    local leader = new_glue(2*linewidth) .. new_rule(6*linewidth,linewidth,0) .. new_glue(2*linewidth)
                  -- setlisttransparency(leader,c_text)
                     leader = fast_hpack(leader)
                  -- setlisttransparency(leader,c_text)
                     baseline = new_glue(0)
                     baseline.leader = leader
                     baseline.subtype = cleaders_code
-                    baseline.spec.stretch = 65536
-                    baseline.spec.stretch_order = 2
+                    local spec = baseline.spec
+                    spec.stretch = 65536
+                    spec.stretch_order = 2
                     setlisttransparency(baseline,c_text)
                     b_cache.baseline = baseline
                 end
@@ -471,10 +463,7 @@ local function ruledbox(head,current,vertical,layer,what,simple)
             this = b_cache[what]
             if not this then
                 local text = fast_hpack_string(what,usedfont)
-                this = concat_nodes {
-                    new_kern(-text.width),
-                    text,
-                }
+                this = new_kern(-text.width) .. text
                 setlisttransparency(this,c_text)
                 this = fast_hpack(this)
                 this.width = 0
@@ -483,27 +472,24 @@ local function ruledbox(head,current,vertical,layer,what,simple)
                 b_cache[what] = this
             end
         end
-        local info = concat_nodes {
-            this and copy_list(this) or nil, -- this also triggers the right mode (else sometimes no whatits)
-            new_rule(linewidth,ht,dp),
-            new_rule(wd-2*linewidth,-dp+linewidth,dp),
-            new_rule(linewidth,ht,dp),
-            new_kern(-wd+linewidth),
-            new_rule(wd-2*linewidth,ht,-ht+linewidth),
-            baseskip,
-            baseline,
-        }
+        -- we need to trigger the right mode (else sometimes no whatits)
+        local info =
+            (this and copy_list(this) or nil) ..
+            new_rule(linewidth,ht,dp) ..
+            new_rule(wd-2*linewidth,-dp+linewidth,dp) ..
+            new_rule(linewidth,ht,dp) ..
+            new_kern(-wd+linewidth) ..
+            new_rule(wd-2*linewidth,ht,-ht+linewidth)
+        if baseskip then
+            info = info .. baseskip .. baseline
+        end
         setlisttransparency(info,c_text)
         info = fast_hpack(info)
         info.width = 0
         info.height = 0
         info.depth = 0
         info[a_layer] = layer
-        local info = concat_nodes {
-            current,
-            new_kern(-wd),
-            info,
-        }
+        local info = current .. new_kern(-wd) .. info
         info = fast_hpack(info,wd)
         if vertical then
             info = vpack_nodes(info)
@@ -533,25 +519,27 @@ end
 local function ruledglyph(head,current)
     local wd = current.width
     if wd ~= 0 then
-        local ht, dp = current.height, current.depth
-        local next, prev = current.next, current.prev
-        current.next, current.prev = nil, nil
+        local ht = current.height
+        local dp = current.depth
+        local next = current.next
+        local prev = current.prev
+        current.next = nil
+        current.prev = nil
         local linewidth = emwidth/20
         local baseline
         if dp ~= 0 and ht ~= 0 then
             baseline = new_rule(wd-2*linewidth,linewidth,0)
         end
         local doublelinewidth = 2*linewidth
-        local info = concat_nodes {
-            -- could be a pdf rule
-            new_rule(linewidth,ht,dp),
-            new_rule(wd-doublelinewidth,-dp+linewidth,dp),
-            new_rule(linewidth,ht,dp),
-            new_kern(-wd+linewidth),
-            new_rule(wd-doublelinewidth,ht,-ht+linewidth),
-            new_kern(-wd+doublelinewidth),
-            baseline,
-        }
+        -- could be a pdf rule
+        local info =
+            new_rule(linewidth,ht,dp) ..
+            new_rule(wd-doublelinewidth,-dp+linewidth,dp) ..
+            new_rule(linewidth,ht,dp) ..
+            new_kern(-wd+linewidth) ..
+            new_rule(wd-doublelinewidth,ht,-ht+linewidth) ..
+            new_kern(-wd+doublelinewidth) ..
+            baseline
         setlistcolor(info,c_glyph)
         setlisttransparency(info,c_glyph_d)
         info = fast_hpack(info)
@@ -559,11 +547,7 @@ local function ruledglyph(head,current)
         info.height = 0
         info.depth = 0
         info[a_layer] = l_glyph
-        local info = concat_nodes {
-            current,
-            new_kern(-wd),
-            info,
-        }
+        local info = current .. new_kern(-wd) .. info
         info = fast_hpack(info)
         info.width = wd
         if next then
@@ -857,13 +841,13 @@ end
 function visualizers.handler(head)
     if usedfont then
         starttiming(visualizers)
-     -- local l = tex_attribute[a_layer]
-     -- local v = tex_attribute[a_visual]
-     -- tex_attribute[a_layer] = unsetvalue
-     -- tex_attribute[a_visual] = unsetvalue
+     -- local l = texgetattribute(a_layer)
+     -- local v = texgetattribute(a_visual)
+     -- texsetattribute(a_layer,unsetvalue)
+     -- texsetattribute(a_visual,unsetvalue)
         head = visualize(head)
-     -- tex_attribute[a_layer] = l
-     -- tex_attribute[a_visual] = v
+     -- texsetattribute(a_layer,l)
+     -- texsetattribute(a_visual,v)
      -- -- cleanup()
         stoptiming(visualizers)
     end
@@ -871,7 +855,8 @@ function visualizers.handler(head)
 end
 
 function visualizers.box(n)
-    tex_box[n].list = visualizers.handler(tex_box[n].list)
+    local box = texgetbox(n)
+    box.list = visualizers.handler(box.list)
 end
 
 local last = nil
@@ -903,7 +888,7 @@ end
 
 function visualizers.markfonts(list)
     last, used = 0, { }
-    markfonts(type(n) == "number" and tex_box[n].list or n)
+    markfonts(type(n) == "number" and texgetbox(n).list or n)
 end
 
 function commands.markfonts(n)
diff --git a/tex/context/base/typo-bld.lua b/tex/context/base/typo-bld.lua
index ed700add7..bc9f66ee4 100644
--- a/tex/context/base/typo-bld.lua
+++ b/tex/context/base/typo-bld.lua
@@ -31,6 +31,7 @@ constructors.attribute   = a_parbuilder
 local unsetvalue         = attributes.unsetvalue
 local texsetattribute    = tex.setattribute
 local texnest            = tex.nest
+local texlists           = tex.lists
 
 local nodepool           = nodes.pool
 local new_baselineskip   = nodepool.baselineskip
@@ -183,3 +184,72 @@ commands.stopparbuilder    = constructors.stop
 commands.setparbuilder     = constructors.set
 commands.enableparbuilder  = constructors.enable
 commands.disableparbuilder = constructors.disable
+
+-- todo: move from nodes.builders to builders
+
+nodes.builders = nodes.builder or { }
+local builders = nodes.builders
+
+local actions = nodes.tasks.actions("vboxbuilders")
+
+function builders.vpack_filter(head,groupcode,size,packtype,maxdepth,direction)
+    local done = false
+    if head then
+        starttiming(builders)
+        if trace_vpacking then
+            local before = nodes.count(head)
+            head, done = actions(head,groupcode,size,packtype,maxdepth,direction)
+            local after = nodes.count(head)
+            if done then
+                nodes.processors.tracer("vpack","changed",head,groupcode,before,after,true)
+            else
+                nodes.processors.tracer("vpack","unchanged",head,groupcode,before,after,true)
+            end
+        else
+            head, done = actions(head,groupcode)
+        end
+        stoptiming(builders)
+    end
+    return head, done
+end
+
+-- This one is special in the sense that it has no head and we operate on the mlv. Also,
+-- we need to do the vspacing last as it removes items from the mvl.
+
+local actions = nodes.tasks.actions("mvlbuilders")
+
+local function report(groupcode,head)
+    report_page_builder("trigger: %s",groupcode)
+    report_page_builder("  vsize    : %p",tex.vsize)
+    report_page_builder("  pagegoal : %p",tex.pagegoal)
+    report_page_builder("  pagetotal: %p",tex.pagetotal)
+    report_page_builder("  list     : %s",head and nodeidstostring(head) or "<empty>")
+end
+
+function builders.buildpage_filter(groupcode)
+    local head, done = texlists.contrib_head, false
+    if head then
+        starttiming(builders)
+        if trace_page_builder then
+            report(groupcode,head)
+        end
+        head, done = actions(head,groupcode)
+        stoptiming(builders)
+     -- -- doesn't work here (not passed on?)
+     -- tex.pagegoal = tex.vsize - tex.dimen.d_page_floats_inserted_top - tex.dimen.d_page_floats_inserted_bottom
+        texlists.contrib_head = head
+        return done and head or true
+    else
+        if trace_page_builder then
+            report(groupcode)
+        end
+        return nil, false
+    end
+end
+
+callbacks.register('vpack_filter',     builders.vpack_filter,     "vertical spacing etc")
+callbacks.register('buildpage_filter', builders.buildpage_filter, "vertical spacing etc (mvl)")
+
+statistics.register("v-node processing time", function()
+    return statistics.elapsedseconds(builders)
+end)
diff --git a/tex/context/base/typo-brk.lua b/tex/context/base/typo-brk.lua
index d6326ebeb..a05cca25b 100644
--- a/tex/context/base/typo-brk.lua
+++ b/tex/context/base/typo-brk.lua
@@ -29,7 +29,7 @@ local remove_node        = nodes.remove -- ! nodes
 
 local tonodes            = nodes.tonodes
 
-local texattribute       = tex.attribute
+local texsetattribute    = tex.setattribute
 local unsetvalue         = attributes.unsetvalue
 
 local nodepool           = nodes.pool
@@ -156,7 +156,7 @@ methods[5] = function(head,start,settings) -- x => p q r
 end
 
 local function process(namespace,attribute,head)
-    local done, numbers = false,  languages.numbers
+    local done, numbers = false, languages.numbers
     local start, n = head, 0
     while start do
         local id = start.id
@@ -282,7 +282,7 @@ function breakpoints.set(n)
             n = n.number
         end
     end
-    texattribute[a_breakpoints] = n
+    texsetattribute(a_breakpoints,n)
 end
 
 breakpoints.handler = nodes.installattributehandler {
diff --git a/tex/context/base/typo-cap.lua b/tex/context/base/typo-cap.lua
index fdbf2e353..e9c98c846 100644
--- a/tex/context/base/typo-cap.lua
+++ b/tex/context/base/typo-cap.lua
@@ -20,7 +20,7 @@ local traverse_id     = node.traverse_id
 local copy_node       = node.copy
 local end_of_math     = node.end_of_math
 
-local texattribute    = tex.attribute
+local texsetattribute = tex.setattribute
 local unsetvalue      = attributes.unsetvalue
 
 local nodecodes       = nodes.nodecodes
@@ -167,7 +167,7 @@ local function Word(start,attribute,attr)
         prev = prev.prev
     end
     if not prev or prev.id ~= glyph_code then
-        --- only the first character is treated
+        -- only the first character is treated
         for n in traverse_id(glyph_code,start.next) do
             if n[attribute] == attr then
                 n[attribute] = unsetvalue
@@ -316,7 +316,7 @@ function cases.set(n)
             n = unsetvalue
         end
     end
-    texattribute[a_cases] = n
+    texsetattribute(a_cases,n)
  -- return n -- bonus
 end
 
diff --git a/tex/context/base/typo-cln.lua b/tex/context/base/typo-cln.lua
index be00ac10d..b4b682bff 100644
--- a/tex/context/base/typo-cln.lua
+++ b/tex/context/base/typo-cln.lua
@@ -7,7 +7,7 @@ if not modules then modules = { } end modules ['typo-cln'] = {
 }
 
 -- This quick and dirty hack took less time than listening to a CD (In
--- this case Dream Theaters' Octavium. Of course extensions will take
+-- this case Dream Theaters' Octavium). Of course extensions will take
 -- more time.
 
 local utfbyte = utf.byte
@@ -26,7 +26,7 @@ local variables       = interfaces.variables
 local nodecodes       = nodes.nodecodes
 local tasks           = nodes.tasks
 
-local texattribute    = tex.attribute
+local texsetattribute = tex.setattribute
 
 local traverse_id     = node.traverse_id
 
@@ -78,7 +78,7 @@ local enabled = false
 
 function cleaners.set(n)
     if n == variables.reset or not tonumber(n) or n == 0 then
-        texattribute[a_cleaner] = unsetvalue
+        texsetattribute(a_cleaner,unsetvalue)
     else
         if not enabled then
             tasks.enableaction("processors","typesetters.cleaners.handler")
@@ -87,7 +87,7 @@ function cleaners.set(n)
             end
             enabled = true
         end
-        texattribute[a_cleaner] = n
+        texsetattribute(a_cleaner,n)
     end
 end
 
diff --git a/tex/context/base/typo-dig.lua b/tex/context/base/typo-dig.lua
index 62d17fa3b..829e4cc7a 100644
--- a/tex/context/base/typo-dig.lua
+++ b/tex/context/base/typo-dig.lua
@@ -24,7 +24,7 @@ local traverse_id        = node.traverse_id
 local insert_node_before = node.insert_before
 local insert_node_after  = node.insert_after
 
-local texattribute       = tex.attribute
+local texsetattribute    = tex.setattribute
 local unsetvalue         = attributes.unsetvalue
 
 local nodecodes          = nodes.nodecodes
@@ -148,7 +148,7 @@ function digits.set(n) -- number or 'reset'
             n = unsetvalue
         end
     end
-    texattribute[a_digits] = n
+    texsetattribute(a_digits,n)
 end
 
 digits.handler = nodes.installattributehandler { -- we could avoid this wrapper
diff --git a/tex/context/base/typo-dir.lua b/tex/context/base/typo-dir.lua
index 7e5f8c2d3..fcf3e3acb 100644
--- a/tex/context/base/typo-dir.lua
+++ b/tex/context/base/typo-dir.lua
@@ -25,7 +25,7 @@ local insert_node_before = node.insert_before
 local insert_node_after  = node.insert_after
 local remove_node        = nodes.remove
 
-local texattribute       = tex.attribute
+local texsetattribute    = tex.setattribute
 local unsetvalue         = attributes.unsetvalue
 
 local nodecodes          = nodes.nodecodes
@@ -167,6 +167,8 @@ end
 
 -- todo: use new dir functions
 
+-- todo: use end_of_math
+
 local s_isol = fonts.analyzers.states.isol
 
 function directions.process(namespace,attribute,start) -- todo: make faster
@@ -451,7 +453,7 @@ function directions.set(n) -- todo: names and numbers
         n = unsetvalue
         -- maybe tracing
     end
-    texattribute[a_directions] = n
+    texsetattribute(a_directions,n)
 end
 
 commands.setdirection = directions.set
diff --git a/tex/context/base/typo-itc.lua b/tex/context/base/typo-itc.lua
index b39ea2f23..9ef0aaed3 100644
--- a/tex/context/base/typo-itc.lua
+++ b/tex/context/base/typo-itc.lua
@@ -28,7 +28,8 @@ local insert_node_after   = node.insert_after
 local delete_node         = nodes.delete
 local end_of_math         = node.end_of_math
 
-local texattribute        = tex.attribute
+local texgetattribute     = tex.getattribute
+local texsetattribute     = tex.setattribute
 local a_italics           = attributes.private("italics")
 local unsetvalue          = attributes.unsetvalue
 
@@ -199,14 +200,14 @@ function italics.set(n)
         enable()
     end
     if n == variables.reset then
-        texattribute[a_italics] = unsetvalue
+        texsetattribute(a_italics,unsetvalue)
     else
-        texattribute[a_italics] = tonumber(n) or unsetvalue
+        texsetattribute(a_italics,tonumber(n) or unsetvalue)
     end
 end
 
 function italics.reset()
-    texattribute[a_italics] = unsetvalue
+    texsetattribute(a_italics,unsetvalue)
 end
 
 italics.handler = nodes.installattributehandler {
@@ -231,10 +232,10 @@ function commands.setupitaliccorrection(option) -- no grouping !
     end
     if options[variables.global] then
         forcedvariant = variant
-        texattribute[a_italics] = unsetvalue
+        texsetattribute(a_italics,unsetvalue)
     else
         forcedvariant = false
-        texattribute[a_italics] = variant
+        texsetattribute(a_italics,variant)
     end
     if trace_italics then
         report_italics("forcing %a, variant %a",forcedvariant,variant ~= unsetvalue and variant)
@@ -246,11 +247,11 @@ end
 local stack = { }
 
 function commands.pushitaliccorrection()
-    table.insert(stack,{forcedvariant, texattribute[a_italics] })
+    table.insert(stack,{forcedvariant, texgetattribute(a_italics) })
 end
 
 function commands.popitaliccorrection()
     local top = table.remove(stack)
     forcedvariant = top[1]
-    texattribute[a_italics] = top[2]
+    texsetattribute(a_italics,top[2])
 end
diff --git a/tex/context/base/typo-krn.lua b/tex/context/base/typo-krn.lua
index fb28d3b2d..fb394044f 100644
--- a/tex/context/base/typo-krn.lua
+++ b/tex/context/base/typo-krn.lua
@@ -20,7 +20,7 @@ local insert_node_before = node.insert_before
 local insert_node_after  = node.insert_after
 local end_of_math        = node.end_of_math
 
-local texattribute       = tex.attribute
+local texsetattribute    = tex.setattribute
 local unsetvalue         = attributes.unsetvalue
 
 local nodepool           = nodes.pool
@@ -316,7 +316,7 @@ function kerns.set(factor)
     else
         factor = unsetvalue
     end
-    texattribute[a_kerns] = factor
+    texsetattribute(a_kerns,factor)
     return factor
 end
 
diff --git a/tex/context/base/typo-mar.lua b/tex/context/base/typo-mar.lua
index ec827883d..85d5c85a8 100644
--- a/tex/context/base/typo-mar.lua
+++ b/tex/context/base/typo-mar.lua
@@ -123,8 +123,6 @@ local free_node_list     = node.flush_list
 local insert_node_after  = node.insert_after
 local insert_node_before = node.insert_before
 
-local concat_nodes       = nodes.concat
-
 local nodecodes          = nodes.nodecodes
 local listcodes          = nodes.listcodes
 local gluecodes          = nodes.gluecodes
@@ -155,9 +153,10 @@ local new_stretch        = nodepool.stretch
 local new_usernumber     = nodepool.usernumber
 local new_latelua        = nodepool.latelua
 
-local texcount           = tex.count
-local texdimen           = tex.dimen
-local texbox             = tex.box
+local texgetcount        = tex.getcount
+local texgetdimen        = tex.getdimen
+local texgetbox          = tex.getbox
+local texget             = tex.get
 
 local points             = number.points
 
@@ -243,7 +242,7 @@ end
 
 function margins.save(t)
     setmetatable(t,defaults)
-    local content  = texbox[t.number]
+    local content  = texgetbox(t.number)
     local location = t.location
     local category = t.category
     local inline   = t.inline
@@ -309,23 +308,24 @@ function margins.save(t)
         t.n                   = nofsaved
         -- used later (we will clean up this natural mess later)
         -- nice is to make a special status table mechanism
-        local leftmargindistance  = texdimen.naturalleftmargindistance
-        local rightmargindistance = texdimen.naturalrightmargindistance
-        t.strutdepth          = texbox.strutbox.depth
-        t.strutheight         = texbox.strutbox.height
-        t.leftskip            = tex.leftskip.width  -- we're not in forgetall
-        t.rightskip           = tex.rightskip.width -- we're not in forgetall
+        local leftmargindistance  = texgetdimen("naturalleftmargindistance")
+        local rightmargindistance = texgetdimen("naturalrightmargindistance")
+        local strutbox        = texgetbox("strutbox")
+        t.strutdepth          = strutbox.depth
+        t.strutheight         = strutbox.height
+        t.leftskip            = texget("leftskip").width  -- we're not in forgetall
+        t.rightskip           = texget("rightskip").width -- we're not in forgetall
         t.leftmargindistance  = leftmargindistance -- todo:layoutstatus table
         t.rightmargindistance = rightmargindistance
-        t.leftedgedistance    = texdimen.naturalleftedgedistance
-                              + texdimen.leftmarginwidth
+        t.leftedgedistance    = texgetdimen("naturalleftedgedistance")
+                              + texgetdimen("leftmarginwidth")
                               + leftmargindistance
-        t.rightedgedistance   = texdimen.naturalrightedgedistance
-                              + texdimen.rightmarginwidth
+        t.rightedgedistance   = texgetdimen("naturalrightedgedistance")
+                              + texgetdimen("rightmarginwidth")
                               + rightmargindistance
-        t.lineheight          = texdimen.lineheight
+        t.lineheight          = texgetdimen("lineheight")
         --
-     -- t.realpageno          = texcount.realpageno
+     -- t.realpageno          = texgetcount("realpageno")
         if inline then
             context(new_usernumber(inline_mark,nofsaved))
             store[nofsaved] = t -- no insert
@@ -447,7 +447,7 @@ local function realign(current,candidate)
         end
     end
 
-    current.list = hpack_nodes(concat_nodes{anchornode,new_kern(-delta),current.list,new_kern(delta)})
+    current.list = hpack_nodes(anchornode .. new_kern(-delta) .. current.list .. new_kern(delta))
     current.width = 0
 end
 
@@ -490,7 +490,7 @@ local function markovershoot(current)
     v_anchors = v_anchors + 1
     cache[v_anchors] = stacked
     local anchor = new_latelua(format("typesetters.margins.ha(%s)",v_anchors)) -- todo: alleen als offset > line
-    current.list = hpack_nodes(concat_nodes{anchor,current.list})
+    current.list = hpack_nodes(anchor .. current.list)
 end
 
 local function getovershoot(location)
@@ -623,7 +623,7 @@ local function inject(parent,head,candidate)
     elseif head.id == whatsit_code and head.subtype == localpar_code then
         -- experimental
         if head.dir == "TRT" then
-            box.list = hpack_nodes(concat_nodes{new_kern(candidate.hsize),box.list,new_kern(-candidate.hsize)})
+            box.list = hpack_nodes(new_kern(candidate.hsize) .. box.list .. new_kern(-candidate.hsize))
         end
         insert_node_after(head,head,box)
     else
diff --git a/tex/context/base/typo-par.lua b/tex/context/base/typo-par.lua
index b25ae4a5b..5f8bd8d4a 100644
--- a/tex/context/base/typo-par.lua
+++ b/tex/context/base/typo-par.lua
@@ -8,6 +8,9 @@ if not modules then modules = { } end modules ['typo-par'] = {
 
 -- A playground for experiments.
 
+local tonumber, type, next = tonumber, type, next
+local ceil = math.ceil
+
 local utfbyte = utf.byte
 local utfchar = utf.char
 
@@ -26,7 +29,7 @@ local tasks             = nodes.tasks
 
 local variables         = interfaces.variables
 
-local texattribute      = tex.attribute
+local texsetattribute   = tex.setattribute
 local unsetvalue        = attributes.unsetvalue
 
 local glyph_code        = nodecodes.glyph
@@ -91,9 +94,9 @@ local function process(namespace,attribute,head)
                     first = first.next
                 end
                 if first and first.id == glyph_code then
--- if texattribute[a_paragraph] >= 0 then
---     texattribute[a_paragraph] = unsetvalue
--- end
+                 -- if texgetattribute(a_paragraph) >= 0 then
+                 --     texsetattribute(a_paragraph,unsetvalue)
+                 -- end
                     local char = first.char
                     local prev = first.prev
                     local next = first.next
@@ -103,7 +106,9 @@ local function process(namespace,attribute,head)
                     if next and next.id == kern_node then
                         next.kern = 0
                     end
-                    first.font = dropper.font or first.font
+                    if dropper.font then
+                        first.font = dropper.font
+                    end
                     -- can be a helper
                     local ma = dropper.ma or 0
                     local ca = dropper.ca
@@ -142,7 +147,7 @@ local function process(namespace,attribute,head)
                     else
                         local lines = tonumber(dropper.n) or 0
                         if lines == 0 then -- safeguard, not too precise
-                            lines = math.ceil((height+voffset) / tex.baselineskip.width)
+                            lines = ceil((height+voffset) / tex.baselineskip.width)
                         end
                         tex.hangafter  = - lines
                         tex.hangindent = width + distance
@@ -159,7 +164,7 @@ local enabled = false
 
 function paragraphs.set(n)
     if n == variables.reset or not tonumber(n) or n == 0 then
-        texattribute[a_paragraph] = unsetvalue
+        texsetattribute(a_paragraph,unsetvalue)
     else
         if not enabled then
             tasks.enableaction("processors","typesetters.paragraphs.handler")
@@ -168,7 +173,7 @@ function paragraphs.set(n)
             end
             enabled = true
         end
-        texattribute[a_paragraph] = n
+        texsetattribute(a_paragraph,n)
     end
 end
 
diff --git a/tex/context/base/typo-rep.lua b/tex/context/base/typo-rep.lua
index 8451ce52b..01868f490 100644
--- a/tex/context/base/typo-rep.lua
+++ b/tex/context/base/typo-rep.lua
@@ -17,23 +17,23 @@ local report_stripping = logs.reporter("fonts","stripping")
 
 local nodes, node = nodes, node
 
-local delete_node   = nodes.delete
-local replace_node  = nodes.replace
-local copy_node     = node.copy
+local delete_node     = nodes.delete
+local replace_node    = nodes.replace
+local copy_node       = node.copy
 
-local chardata      = characters.data
-local collected     = false
-local a_stripping   = attributes.private("stripping")
-local fontdata      = fonts.hashes.identifiers
-local tasks         = nodes.tasks
+local chardata        = characters.data
+local collected       = false
+local a_stripping     = attributes.private("stripping")
+local fontdata        = fonts.hashes.identifiers
+local tasks           = nodes.tasks
 
-local texattribute  = tex.attribute
-local unsetvalue    = attributes.unsetvalue
+local texsetattribute = tex.setattribute
+local unsetvalue      = attributes.unsetvalue
 
-local v_reset       = interfaces.variables.reset
+local v_reset         = interfaces.variables.reset
 
-local nodecodes     = nodes.nodecodes
-local glyph_code    = nodecodes.glyph
+local nodecodes       = nodes.nodecodes
+local glyph_code      = nodecodes.glyph
 
 -- todo: other namespace -> typesetters
 
@@ -115,7 +115,7 @@ function stripping.set(n) -- number or 'reset'
             n = unsetvalue
         end
     end
-    texattribute[a_stripping] = n
+    texsetattribute(a_stripping,n)
 end
 
 -- why not in task-ini?
diff --git a/tex/context/base/typo-spa.lua b/tex/context/base/typo-spa.lua
index 5eba22889..a8df6e1f8 100644
--- a/tex/context/base/typo-spa.lua
+++ b/tex/context/base/typo-spa.lua
@@ -24,7 +24,7 @@ local fonthashes         = fonts.hashes
 local fontdata           = fonthashes.identifiers
 local quaddata           = fonthashes.quads
 
-local texattribute       = tex.attribute
+local texsetattribute    = tex.setattribute
 local unsetvalue         = attributes.unsetvalue
 
 local v_reset            = interfaces.variables.reset
@@ -209,11 +209,11 @@ function spacings.set(name)
             n = data.number or unsetvalue
         end
     end
-    texattribute[a_spacings] = n
+    texsetattribute(a_spacings,n)
 end
 
 function spacings.reset()
-    texattribute[a_spacings] = unsetvalue
+    texsetattribute(a_spacings,unsetvalue)
 end
 
 spacings.handler = nodes.installattributehandler {
diff --git a/tex/context/base/util-dim.lua b/tex/context/base/util-dim.lua
index 47b2706b7..69061495f 100644
--- a/tex/context/base/util-dim.lua
+++ b/tex/context/base/util-dim.lua
@@ -22,6 +22,8 @@ local allocate          = utilities.storage.allocate
 local setmetatableindex = table.setmetatableindex
 local formatters        = string.formatters
 
+local texget            = tex and tex.get or function() return 65536*10*100 end
+
 --this might become another namespace
 
 number = number or { }
@@ -137,7 +139,7 @@ capture takes place.</p>
 --ldx]]--
 
 local amount = (S("+-")^0 * R("09")^0 * P(".")^0 * R("09")^0) + Cc("0")
-local unit   = R("az")^1
+local unit   = R("az")^1 + P("%")
 
 local dimenpair = amount/tonumber * (unit^1/dimenfactors + Cc(1)) -- tonumber is new
 
@@ -376,10 +378,10 @@ function dimen(a)
                 a = k
             else
                 local value, unit = lpegmatch(dimenpair,a)
-                if type(unit) == "function" then
-                    k = value/unit()
+                if value and unit then
+                    k = value/unit -- to be considered: round
                 else
-                    k = value/unit
+                    k = 0
                 end
                 known[a] = k
                 a = k
@@ -412,16 +414,16 @@ function string.todimen(str) -- maybe use tex.sp when available
     end
 end
 
---~ local known = { }
-
---~ function string.todimen(str) -- maybe use tex.sp
---~     local k = known[str]
---~     if not k then
---~         k = tex.sp(str)
---~         known[str] = k
---~     end
---~     return k
---~ end
+-- local known = { }
+--
+-- function string.todimen(str) -- maybe use tex.sp
+--     local k = known[str]
+--     if not k then
+--         k = tex.sp(str)
+--         known[str] = k
+--     end
+--     return k
+-- end
 
 stringtodimen = string.todimen -- local variable defined earlier
 
@@ -439,7 +441,7 @@ probably use a hash instead of a one-element table.</p>
 --ldx]]--
 
 function number.percent(n,d) -- will be cleaned up once luatex 0.30 is out
-    d = d or tex.hsize
+    d = d or texget("hsize")
     if type(d) == "string" then
         d = stringtodimen(d)
     end
diff --git a/tex/context/base/util-jsn.lua b/tex/context/base/util-jsn.lua
index 29587cd38..bbe25d89d 100644
--- a/tex/context/base/util-jsn.lua
+++ b/tex/context/base/util-jsn.lua
@@ -42,8 +42,20 @@ local dquote     = P('"')
 local whitespace = lpeg.patterns.whitespace
 local optionalws = whitespace^0
 
-local escape     = C(P("\\u") / "0x" * S("09","AF","af")) / function(s) return utfchar(tonumber(s)) end
-local jstring    = dquote * Cs((escape + (1-dquote))^0) * dquote
+local escapes    = {
+ -- ["\\"] = "\\",  -- lua will escape these
+ -- ["/"]  = "/",   -- no need to escape this one
+    ["b"]  = "\010",
+    ["f"]  = "\014",
+    ["n"]  = "\n",
+    ["r"]  = "\r",
+    ["t"]  = "\t",
+}
+
+local escape_un  = C(P("\\u") / "0x" * S("09","AF","af")) / function(s) return utfchar(tonumber(s)) end
+local escape_bs  = P([[\]]) / "" * (P(1) / escapes) -- if not found then P(1) is returned i.e. the to be escaped char
+
+local jstring    = dquote * Cs((escape_un + escape_bs + (1-dquote))^0) * dquote
 local jtrue      = P("true")  * Cc(true)
 local jfalse     = P("false") * Cc(false)
 local jnull      = P("null")  * Cc(nil)
diff --git a/tex/context/base/util-lua.lua b/tex/context/base/util-lua.lua
index 61d1190b2..e1dcdc94d 100644
--- a/tex/context/base/util-lua.lua
+++ b/tex/context/base/util-lua.lua
@@ -74,9 +74,16 @@ end
 function luautilities.loadedluacode(fullname,forcestrip,name)
     -- quite subtle ... doing this wrong incidentally can give more bytes
     name = name or fullname
-    local code = environment.loadpreprocessedfile and environment.loadpreprocessedfile(fullname) or loadfile(fullname)
+    local code, message
+    if environment.loadpreprocessedfile then
+        code, message = environment.loadpreprocessedfile(fullname)
+    else
+        code, message = loadfile(fullname)
+    end
     if code then
         code()
+    else
+        report_lua("loading of file %a failed:\n\t%s",fullname,message or "no message")
     end
     if forcestrip and luautilities.stripcode then
         if type(forcestrip) == "function" then
@@ -97,15 +104,16 @@ function luautilities.loadedluacode(fullname,forcestrip,name)
 end
 
 function luautilities.strippedloadstring(code,forcestrip,name) -- not executed
+    local code, message = load(code)
+    if not code then
+        report_lua("loading of file %a failed:\n\t%s",name,message or "no message")
+    end
     if forcestrip and luautilities.stripcode or luautilities.alwaysstripcode then
-        code = load(code)
-        if not code then
-            report_lua("fatal error %a in file %a",3,name)
-        end
         register(name)
-        code = dump(code,true)
+        return load(dump(code,true)), 0 -- not yet executes
+    else
+        return code, 0
     end
-    return load(code), 0
 end
 
 function luautilities.compile(luafile,lucfile,cleanup,strip,fallback) -- defaults: cleanup=false strip=true
diff --git a/tex/context/base/util-prs.lua b/tex/context/base/util-prs.lua
index 9b2a2b04c..7fe1e703b 100644
--- a/tex/context/base/util-prs.lua
+++ b/tex/context/base/util-prs.lua
@@ -410,7 +410,7 @@ function parsers.csvsplitter(specification)
         end
         whatever = quotedata + whatever
     end
-    local parser = Ct((Ct(whatever * (separator * whatever)^0) * S("\n\r"))^0 )
+    local parser = Ct((Ct(whatever * (separator * whatever)^0) * S("\n\r")^1)^0 )
     return function(data)
         return lpegmatch(parser,data)
     end
diff --git a/tex/context/base/util-seq.lua b/tex/context/base/util-seq.lua
index 4e042de59..35e693285 100644
--- a/tex/context/base/util-seq.lua
+++ b/tex/context/base/util-seq.lua
@@ -321,6 +321,8 @@ function sequencers.nodeprocessor(t,nofarguments) -- todo: handle 'kind' in plug
                     else
                         calls[n] = format("  head, ok = %s(head%s) done = done or ok",localized,args)
                     end
+-- local s = " print('" .. tostring(group) .. " " .. tostring(action) .. " : ' .. tostring(head)) "
+-- calls[n] = s .. calls[n] .. s
                 end
             end
         end
diff --git a/tex/context/base/util-str.lua b/tex/context/base/util-str.lua
index 0c8c0e2d2..13e1e0922 100644
--- a/tex/context/base/util-str.lua
+++ b/tex/context/base/util-str.lua
@@ -339,7 +339,7 @@ local format_i = function(f)
     if f and f ~= "" then
         return format("format('%%%si',a%s)",f,n)
     else
-        return format("a%s",n)
+        return format("format('%%i',a%s)",n)
     end
 end
 
diff --git a/tex/context/base/util-tab.lua b/tex/context/base/util-tab.lua
index b7db4fa84..f18c719e4 100644
--- a/tex/context/base/util-tab.lua
+++ b/tex/context/base/util-tab.lua
@@ -293,13 +293,12 @@ function tables.encapsulate(core,capsule,protect)
     end
 end
 
--- we no longer have %q in keys as for this kind of tables we can
--- assume sane keys
+-- best keep [%q] keys (as we have some in older applications i.e. saving user data
 
-local f_hashed_string   = formatters["[%s]=%q,"]
-local f_hashed_number   = formatters["[%s]=%s,"]
-local f_hashed_boolean  = formatters["[%s]=%l,"]
-local f_hashed_table    = formatters["[%s]="]
+local f_hashed_string   = formatters["[%q]=%q,"]
+local f_hashed_number   = formatters["[%q]=%s,"]
+local f_hashed_boolean  = formatters["[%q]=%l,"]
+local f_hashed_table    = formatters["[%q]="]
 
 local f_indexed_string  = formatters["%q,"]
 local f_indexed_number  = formatters["%s,"]
@@ -521,7 +520,7 @@ local serialize = table.serialize -- the extensive one, the one we started with
 
 function table.serialize(root,name,specification)
 
-    if specification then
+    if type(specification) == "table" then
         return serialize(root,name,specification) -- the original one
     end
 
diff --git a/tex/context/base/util-tpl.lua b/tex/context/base/util-tpl.lua
index dcc4c1216..e0c405a42 100644
--- a/tex/context/base/util-tpl.lua
+++ b/tex/context/base/util-tpl.lua
@@ -159,6 +159,14 @@ end
 
 templates.replace = replace
 
+function templates.replacer(str,how,recurse) -- reads nicer
+    return function(mapping)
+        return lpegmatch(replacer,str,1,mapping,how or "lua",recurse or false) or str
+    end
+end
+
+-- local cmd = templates.replacer([[foo %bar%]]) print(cmd { bar = "foo" })
+
 function templates.load(filename,mapping,how,recurse)
     local data = io.loaddata(filename) or ""
     if mapping and next(mapping) then
diff --git a/tex/context/base/x-asciimath.lua b/tex/context/base/x-asciimath.lua
index 5ef741ce3..992c37eae 100644
--- a/tex/context/base/x-asciimath.lua
+++ b/tex/context/base/x-asciimath.lua
@@ -140,6 +140,8 @@ local reserved = {
 
 }
 
+table.setmetatableindex(reserved,characters.entities)
+
 local postmapper = Cs ( (
 
     P("\\mathoptext ") * spaces * (P("\\bgroup ")/"{") * (1-P("\\egroup "))^1 * (P("\\egroup ")/"}") +
diff --git a/tex/context/fonts/ebgaramond.lfg b/tex/context/fonts/ebgaramond.lfg
new file mode 100644
index 000000000..43cc13c51
--- /dev/null
+++ b/tex/context/fonts/ebgaramond.lfg
@@ -0,0 +1,53 @@
+return {
+    name = "eb garamond",
+    version = "1.00",
+    comment = "Goodies that complement eb garamond.",
+    author = "Hans Hagen",
+    copyright = "ConTeXt development team",
+    designsizes = {
+        ["EBGaramond-Italic"] = {
+            ["8pt"]   = "file:EBGaramond08-Italic",
+            ["9pt"]   = "file:EBGaramond08-Italic",
+            ["9.5pt"] = "file:EBGaramond08-Italic",
+            ["10pt"]  = "file:EBGaramond12-Italic",
+            ["11pt"]  = "file:EBGaramond12-Italic",
+            ["12pt"]  = "file:EBGaramond12-Italic",
+            default   = "file:EBGaramond12-Italic",
+        },
+        ["EBGaramond-Regular"] = {
+            ["8pt"]   = "file:EBGaramond08-Regular",
+            ["9pt"]   = "file:EBGaramond08-Regular",
+            ["9.5pt"] = "file:EBGaramond08-Regular",
+            ["10pt"]  = "file:EBGaramond12-Regular",
+            ["11pt"]  = "file:EBGaramond12-Regular",
+            ["12pt"]  = "file:EBGaramond12-Regular",
+            default   = "file:EBGaramond12-Regular",
+        },
+        ["EBGaramond-SC"] = {
+            ["8pt"]   = "file:EBGaramond08-SC",
+            ["9pt"]   = "file:EBGaramond08-SC",
+            ["9.5pt"] = "file:EBGaramond08-SC",
+            ["10pt"]  = "file:EBGaramond12-SC",
+            ["11pt"]  = "file:EBGaramond12-SC",
+            ["12pt"]  = "file:EBGaramond12-SC",
+            default   = "file:EBGaramond12-SC",
+        },
+        ["EBGaramond-Bold"] = {
+            default  = "file:EBGaramond12-Bold",
+        },
+        ["EBGaramond-AllSC"] = {
+            default  = "file:EBGaramond12-AllSC",
+        },
+        ["EBGaramond-Initials"] = {
+            default  = "file:EBGaramondInitials",
+        },
+        ["EBGaramond-InitialsF1"] = {
+            default  = "file:EBGaramondInitialsF1",
+        },
+        ["EBGaramond-InitialsF2"] = {
+            default  = "file:EBGaramondInitialsF2",
+        },
+    }
+}
+
+
diff --git a/tex/context/fonts/lm.lfg b/tex/context/fonts/lm.lfg
index 7fad13afd..8d7614718 100644
--- a/tex/context/fonts/lm.lfg
+++ b/tex/context/fonts/lm.lfg
@@ -1,4 +1,4 @@
--- In order to be ale to use beta math fonts, we use our own file name and
+-- In order to be able to use beta math fonts, we use our own file name and
 -- always remap.
 
 return {
diff --git a/tex/context/interface/keys-cs.xml b/tex/context/interface/keys-cs.xml
index ad0cf2dca..2499186f3 100644
--- a/tex/context/interface/keys-cs.xml
+++ b/tex/context/interface/keys-cs.xml
@@ -807,6 +807,8 @@
 		<cd:constant name='marking' value='znaceni'/>
 		<cd:constant name='marstyle' value='stylsnacky'/>
 		<cd:constant name='mask' value='mask'/>
+		<cd:constant name='mathclass' value='mathclass'/>
+		<cd:constant name='mathlimits' value='mathlimits'/>
 		<cd:constant name='mathstyle' value='mathstyle'/>
 		<cd:constant name='max' value='max'/>
 		<cd:constant name='maxdepth' value='maxdepth'/>
diff --git a/tex/context/interface/keys-de.xml b/tex/context/interface/keys-de.xml
index 5d107ca40..6464bd44e 100644
--- a/tex/context/interface/keys-de.xml
+++ b/tex/context/interface/keys-de.xml
@@ -807,6 +807,8 @@
 		<cd:constant name='marking' value='beschriftung'/>
 		<cd:constant name='marstyle' value='beschrstil'/>
 		<cd:constant name='mask' value='mask'/>
+		<cd:constant name='mathclass' value='mathclass'/>
+		<cd:constant name='mathlimits' value='mathlimits'/>
 		<cd:constant name='mathstyle' value='mathstyle'/>
 		<cd:constant name='max' value='max'/>
 		<cd:constant name='maxdepth' value='maxdepth'/>
diff --git a/tex/context/interface/keys-en.xml b/tex/context/interface/keys-en.xml
index d9166d107..8a4eb55a9 100644
--- a/tex/context/interface/keys-en.xml
+++ b/tex/context/interface/keys-en.xml
@@ -807,6 +807,8 @@
 		<cd:constant name='marking' value='marking'/>
 		<cd:constant name='marstyle' value='marstyle'/>
 		<cd:constant name='mask' value='mask'/>
+		<cd:constant name='mathclass' value='mathclass'/>
+		<cd:constant name='mathlimits' value='mathlimits'/>
 		<cd:constant name='mathstyle' value='mathstyle'/>
 		<cd:constant name='max' value='max'/>
 		<cd:constant name='maxdepth' value='maxdepth'/>
diff --git a/tex/context/interface/keys-fr.xml b/tex/context/interface/keys-fr.xml
index c98826cf3..604e4560b 100644
--- a/tex/context/interface/keys-fr.xml
+++ b/tex/context/interface/keys-fr.xml
@@ -807,6 +807,8 @@
 		<cd:constant name='marking' value='marquage'/>
 		<cd:constant name='marstyle' value='stylemarquage'/>
 		<cd:constant name='mask' value='mask'/>
+		<cd:constant name='mathclass' value='mathclass'/>
+		<cd:constant name='mathlimits' value='mathlimits'/>
 		<cd:constant name='mathstyle' value='mathstyle'/>
 		<cd:constant name='max' value='max'/>
 		<cd:constant name='maxdepth' value='maxdepth'/>
diff --git a/tex/context/interface/keys-it.xml b/tex/context/interface/keys-it.xml
index afe3b8360..ff23d0973 100644
--- a/tex/context/interface/keys-it.xml
+++ b/tex/context/interface/keys-it.xml
@@ -807,6 +807,8 @@
 		<cd:constant name='marking' value='marcatura'/>
 		<cd:constant name='marstyle' value='stilemarcatura'/>
 		<cd:constant name='mask' value='mask'/>
+		<cd:constant name='mathclass' value='mathclass'/>
+		<cd:constant name='mathlimits' value='mathlimits'/>
 		<cd:constant name='mathstyle' value='mathstyle'/>
 		<cd:constant name='max' value='max'/>
 		<cd:constant name='maxdepth' value='maxdepth'/>
diff --git a/tex/context/interface/keys-nl.xml b/tex/context/interface/keys-nl.xml
index 226c96839..6597300b3 100644
--- a/tex/context/interface/keys-nl.xml
+++ b/tex/context/interface/keys-nl.xml
@@ -807,6 +807,8 @@
 		<cd:constant name='marking' value='markering'/>
 		<cd:constant name='marstyle' value='marletter'/>
 		<cd:constant name='mask' value='masker'/>
+		<cd:constant name='mathclass' value='mathclass'/>
+		<cd:constant name='mathlimits' value='mathlimits'/>
 		<cd:constant name='mathstyle' value='mathstyle'/>
 		<cd:constant name='max' value='max'/>
 		<cd:constant name='maxdepth' value='maxdepth'/>
diff --git a/tex/context/interface/keys-pe.xml b/tex/context/interface/keys-pe.xml
index 9303c29fd..212941015 100644
--- a/tex/context/interface/keys-pe.xml
+++ b/tex/context/interface/keys-pe.xml
@@ -807,6 +807,8 @@
 		<cd:constant name='marking' value='نشانه‌گذاری'/>
 		<cd:constant name='marstyle' value='سبک‌حاش'/>
 		<cd:constant name='mask' value='mask'/>
+		<cd:constant name='mathclass' value='mathclass'/>
+		<cd:constant name='mathlimits' value='mathlimits'/>
 		<cd:constant name='mathstyle' value='mathstyle'/>
 		<cd:constant name='max' value='بیشترین'/>
 		<cd:constant name='maxdepth' value='maxdepth'/>
diff --git a/tex/context/interface/keys-ro.xml b/tex/context/interface/keys-ro.xml
index 29368c9bc..deb7ad559 100644
--- a/tex/context/interface/keys-ro.xml
+++ b/tex/context/interface/keys-ro.xml
@@ -807,6 +807,8 @@
 		<cd:constant name='marking' value='marcaje'/>
 		<cd:constant name='marstyle' value='stilmarcaj'/>
 		<cd:constant name='mask' value='mask'/>
+		<cd:constant name='mathclass' value='mathclass'/>
+		<cd:constant name='mathlimits' value='mathlimits'/>
 		<cd:constant name='mathstyle' value='mathstyle'/>
 		<cd:constant name='max' value='max'/>
 		<cd:constant name='maxdepth' value='maxdepth'/>
diff --git a/tex/generic/context/luatex/luatex-basics-gen.lua b/tex/generic/context/luatex/luatex-basics-gen.lua
index 4a46fbb07..26c1edc72 100644
--- a/tex/generic/context/luatex/luatex-basics-gen.lua
+++ b/tex/generic/context/luatex/luatex-basics-gen.lua
@@ -149,19 +149,29 @@ do
 
     local cachepaths = kpse.expand_var('$TEXMFCACHE') or ""
 
-    -- quite like tex live or so
+    -- quite like tex live or so (the weird $TEXMFCACHE test seems to be needed on miktex)
 
-    if cachepaths == "" then
+    if cachepaths == "" or cachepaths == "$TEXMFCACHE" then
         cachepaths = kpse.expand_var('$TEXMFVAR') or ""
     end
 
-    -- this also happened to be used
+    -- this also happened to be used (the weird $TEXMFVAR test seems to be needed on miktex)
 
-    if cachepaths == "" then
+    if cachepaths == "" or cachepaths == "$TEXMFVAR" then
         cachepaths = kpse.expand_var('$VARTEXMF') or ""
     end
 
-    -- and this is a last resort
+    -- and this is a last resort (hm, we could use TEMP or TEMPDIR)
+
+    if cachepaths == "" then
+        local fallbacks = { "TMPDIR", "TEMPDIR", "TMP", "TEMP", "HOME", "HOMEPATH" }
+        for i=1,#fallbacks do
+            cachepaths = os.getenv(fallbacks[i]) or ""
+            if cachepath ~= "" and lfs.isdir(cachepath) then
+                break
+            end
+        end
+    end
 
     if cachepaths == "" then
         cachepaths = "."
@@ -267,7 +277,7 @@ function caches.savedata(path,name,data)
     local luaname, lucname = makefullname(path,name)
     if luaname then
         texio.write(string.format("(save: %s)",luaname))
-        table.tofile(luaname,data,true,{ reduce = true })
+        table.tofile(luaname,data,true)
         if lucname and type(caches.compile) == "function" then
             os.remove(lucname) -- better be safe
             texio.write(string.format("(save: %s)",lucname))
diff --git a/tex/generic/context/luatex/luatex-basics-nod.lua b/tex/generic/context/luatex/luatex-basics-nod.lua
index 5ab9df7f9..58b27897d 100644
--- a/tex/generic/context/luatex/luatex-basics-nod.lua
+++ b/tex/generic/context/luatex/luatex-basics-nod.lua
@@ -88,17 +88,32 @@ function nodes.delete(head,current)
     return nodes.remove(head,current,true)
 end
 
-nodes.before = node.insert_before
-nodes.after  = node.insert_after
-
 function nodes.pool.kern(k)
     local n = new_node("kern",1)
     n.kern = k
     return n
 end
 
-function nodes.endofmath(n)
-    for n in traverse_id(math_code,n.next) do
-        return n
-    end
-end
+-- experimental
+
+local getfield = node.getfield or function(n,tag)       return n[tag]  end end
+local setfield = node.setfield or function(n,tag,value) n[tag] = value end end
+
+nodes.getfield = getfield
+nodes.setfield = setfield
+
+nodes.getattr  = getfield
+nodes.setattr  = setfield
+
+if node.getid      then nodes.getid      = node.getid      else function nodes.getid     (n) return getfield(n,"id")      end end
+if node.getsubtype then nodes.getsubtype = node.getsubtype else function nodes.getsubtype(n) return getfield(n,"subtype") end end
+if node.getnext    then nodes.getnext    = node.getnext    else function nodes.getnext   (n) return getfield(n,"next")    end end
+if node.getprev    then nodes.getprev    = node.getprev    else function nodes.getprev   (n) return getfield(n,"prev")    end end
+if node.getchar    then nodes.getchar    = node.getchar    else function nodes.getchar   (n) return getfield(n,"char")    end end
+if node.getfont    then nodes.getfont    = node.getfont    else function nodes.getfont   (n) return getfield(n,"font")    end end
+if node.getlist    then nodes.getlist    = node.getlist    else function nodes.getlist   (n) return getfield(n,"list")    end end
+
+function nodes.tonut (n) return n end
+function nodes.tonode(n) return n end
+
+nodes.nuts = nodes -- we stay nodes
diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua
index 4cd0c4dc0..d7a1fab5b 100644
--- a/tex/generic/context/luatex/luatex-fonts-merged.lua
+++ b/tex/generic/context/luatex/luatex-fonts-merged.lua
@@ -1,6 +1,6 @@
 -- merged file : luatex-fonts-merged.lua
 -- parent file : luatex-fonts.lua
--- merge date  : 06/10/13 22:51:12
+-- merge date  : 07/12/13 19:10:11
 
 do -- begin closure to overcome local limits and interference
 
@@ -95,6 +95,7 @@ if not modules then modules={} end modules ['l-lpeg']={
   license="see context related readme files"
 }
 lpeg=require("lpeg")
+if not lpeg.print then function lpeg.print(...) print(lpeg.pcode(...)) end end
 local type,next,tostring=type,next,tostring
 local byte,char,gmatch,format=string.byte,string.char,string.gmatch,string.format
 local floor=math.floor
@@ -213,7 +214,7 @@ patterns.decafloat=sign^-1*(digit^0*period*digit^1+digit^1*period*digit^0+digit^
 patterns.propername=(uppercase+lowercase+underscore)*(uppercase+lowercase+underscore+digit)^0*endofstring
 patterns.somecontent=(anything-newline-space)^1 
 patterns.beginline=#(1-newline)
-patterns.longtostring=Cs(whitespace^0/""*nonwhitespace^0*((whitespace^0/" "*(patterns.quoted+nonwhitespace)^1)^0))
+patterns.longtostring=Cs(whitespace^0/""*((patterns.quoted+nonwhitespace^1+whitespace^1/""*(P(-1)+Cc(" ")))^0))
 local function anywhere(pattern) 
   return P { P(pattern)+1*V(1) }
 end
@@ -1066,6 +1067,7 @@ local noquotes,hexify,handle,reduce,compact,inline,functions
 local reserved=table.tohash { 
   'and','break','do','else','elseif','end','false','for','function','if',
   'in','local','nil','not','or','repeat','return','then','true','until','while',
+  'NaN','goto',
 }
 local function simple_table(t)
   if #t>0 then
@@ -1691,6 +1693,7 @@ local function readall(f)
     return f:read('*all')
   else
     local done=f:seek("set",0)
+    local step
     if size<1024*1024 then
       step=1024*1024
     elseif size>16*1024*1024 then
@@ -2214,17 +2217,24 @@ end
 function file.joinpath(tab,separator) 
   return tab and concat(tab,separator or io.pathseparator) 
 end
+local someslash=S("\\/")
 local stripper=Cs(P(fwslash)^0/""*reslasher)
-local isnetwork=fwslash*fwslash*(1-fwslash)+(1-fwslash-colon)^1*colon
+local isnetwork=someslash*someslash*(1-someslash)+(1-fwslash-colon)^1*colon
 local isroot=fwslash^1*-1
 local hasroot=fwslash^1
+local reslasher=lpeg.replacer(S("\\/"),"/")
 local deslasher=lpeg.replacer(S("\\/")^1,"/")
 function file.join(...)
   local lst={... }
   local one=lst[1]
   if lpegmatch(isnetwork,one) then
+    local one=lpegmatch(reslasher,one)
     local two=lpegmatch(deslasher,concat(lst,"/",2))
-    return one.."/"..two
+    if lpegmatch(hasroot,two) then
+      return one..two
+    else
+      return one.."/"..two
+    end
   elseif lpegmatch(isroot,one) then
     local two=lpegmatch(deslasher,concat(lst,"/",2))
     if lpegmatch(hasroot,two) then
@@ -2241,7 +2251,9 @@ end
 local drivespec=R("az","AZ")^1*colon
 local anchors=fwslash+drivespec
 local untouched=periods+(1-period)^1*P(-1)
-local splitstarter=(Cs(drivespec*(bwslash/"/"+fwslash)^0)+Cc(false))*Ct(lpeg.splitat(S("/\\")^1))
+local mswindrive=Cs(drivespec*(bwslash/"/"+fwslash)^0)
+local mswinuncpath=(bwslash+fwslash)*(bwslash+fwslash)*Cc("//")
+local splitstarter=(mswindrive+mswinuncpath+Cc(false))*Ct(lpeg.splitat(S("/\\")^1))
 local absolute=fwslash
 function file.collapsepath(str,anchor) 
   if not str then
@@ -2653,7 +2665,7 @@ local format_i=function(f)
   if f and f~="" then
     return format("format('%%%si',a%s)",f,n)
   else
-    return format("a%s",n)
+    return format("format('%%i',a%s)",n)
   end
 end
 local format_d=format_i
@@ -3044,12 +3056,21 @@ if not caches.namespace or caches.namespace=="" or caches.namespace=="context" t
 end
 do
   local cachepaths=kpse.expand_var('$TEXMFCACHE') or ""
-  if cachepaths=="" then
+  if cachepaths=="" or cachepaths=="$TEXMFCACHE" then
     cachepaths=kpse.expand_var('$TEXMFVAR') or ""
   end
-  if cachepaths=="" then
+  if cachepaths=="" or cachepaths=="$TEXMFVAR" then
     cachepaths=kpse.expand_var('$VARTEXMF') or ""
   end
+  if cachepaths=="" then
+    local fallbacks={ "TMPDIR","TEMPDIR","TMP","TEMP","HOME","HOMEPATH" }
+    for i=1,#fallbacks do
+      cachepaths=os.getenv(fallbacks[i]) or ""
+      if cachepath~="" and lfs.isdir(cachepath) then
+        break
+      end
+    end
+  end
   if cachepaths=="" then
     cachepaths="."
   end
@@ -3143,7 +3164,7 @@ function caches.savedata(path,name,data)
   local luaname,lucname=makefullname(path,name)
   if luaname then
     texio.write(string.format("(save: %s)",luaname))
-    table.tofile(luaname,data,true,{ reduce=true })
+    table.tofile(luaname,data,true)
     if lucname and type(caches.compile)=="function" then
       os.remove(lucname) 
       texio.write(string.format("(save: %s)",lucname))
@@ -3355,18 +3376,27 @@ end
 function nodes.delete(head,current)
   return nodes.remove(head,current,true)
 end
-nodes.before=node.insert_before
-nodes.after=node.insert_after
 function nodes.pool.kern(k)
   local n=new_node("kern",1)
   n.kern=k
   return n
 end
-function nodes.endofmath(n)
-  for n in traverse_id(math_code,n.next) do
-    return n
-  end
-end
+local getfield=node.getfield or function(n,tag)    return n[tag] end end
+local setfield=node.setfield or function(n,tag,value) n[tag]=value end end
+nodes.getfield=getfield
+nodes.setfield=setfield
+nodes.getattr=getfield
+nodes.setattr=setfield
+if node.getid   then nodes.getid=node.getid   else function nodes.getid   (n) return getfield(n,"id")   end end
+if node.getsubtype then nodes.getsubtype=node.getsubtype else function nodes.getsubtype(n) return getfield(n,"subtype") end end
+if node.getnext  then nodes.getnext=node.getnext  else function nodes.getnext  (n) return getfield(n,"next")  end end
+if node.getprev  then nodes.getprev=node.getprev  else function nodes.getprev  (n) return getfield(n,"prev")  end end
+if node.getchar  then nodes.getchar=node.getchar  else function nodes.getchar  (n) return getfield(n,"char")  end end
+if node.getfont  then nodes.getfont=node.getfont  else function nodes.getfont  (n) return getfield(n,"font")  end end
+if node.getlist  then nodes.getlist=node.getlist  else function nodes.getlist  (n) return getfield(n,"list")  end end
+function nodes.tonut (n) return n end
+function nodes.tonode(n) return n end
+nodes.nuts=nodes 
 
 end -- closure
 
@@ -4415,7 +4445,8 @@ function constructors.collectprocessors(what,tfmdata,features,trace,report)
     local whathandler=handlers[what]
     local whatfeatures=whathandler.features
     local whatprocessors=whatfeatures.processors
-    local processors=whatprocessors[properties.mode]
+    local mode=properties.mode
+    local processors=whatprocessors[mode]
     if processors then
       for i=1,#processors do
         local step=processors[i]
@@ -4432,7 +4463,7 @@ function constructors.collectprocessors(what,tfmdata,features,trace,report)
         end
       end
     elseif trace then
-      report("no feature processors for mode %a for font %a",mode,tfmdata.properties.fullname)
+      report("no feature processors for mode %a for font %a",mode,properties.fullname)
     end
   end
   return processes
@@ -4443,7 +4474,8 @@ function constructors.applymanipulators(what,tfmdata,features,trace,report)
     local whathandler=handlers[what]
     local whatfeatures=whathandler.features
     local whatmanipulators=whatfeatures.manipulators
-    local manipulators=whatmanipulators[properties.mode]
+    local mode=properties.mode
+    local manipulators=whatmanipulators[mode]
     if manipulators then
       for i=1,#manipulators do
         local step=manipulators[i]
@@ -4452,7 +4484,7 @@ function constructors.applymanipulators(what,tfmdata,features,trace,report)
         if value then
           local action=step.action
           if trace then
-            report("applying feature manipulator %a for mode %a for font %a",feature,mode,tfmdata.properties.fullname)
+            report("applying feature manipulator %a for mode %a for font %a",feature,mode,properties.fullname)
           end
           if action then
             action(tfmdata,feature,value)
@@ -7527,11 +7559,24 @@ local injections=nodes.injections
 local nodecodes=nodes.nodecodes
 local glyph_code=nodecodes.glyph
 local kern_code=nodecodes.kern
-local nodepool=nodes.pool
+local nuts=nodes.nuts
+local nodepool=nuts.pool
 local newkern=nodepool.kern
-local traverse_id=node.traverse_id
-local insert_node_before=node.insert_before
-local insert_node_after=node.insert_after
+local tonode=nuts.tonode
+local tonut=nuts.tonut
+local getfield=nuts.getfield
+local getnext=nuts.getnext
+local getprev=nuts.getprev
+local getid=nuts.getid
+local getattr=nuts.getattr
+local getfont=nuts.getfont
+local getsubtype=nuts.getsubtype
+local getchar=nuts.getchar
+local setfield=nuts.setfield
+local setattr=nuts.setattr
+local traverse_id=nuts.traverse_id
+local insert_node_before=nuts.insert_before
+local insert_node_after=nuts.insert_after
 local a_kernpair=attributes.private('kernpair')
 local a_ligacomp=attributes.private('ligacomp')
 local a_markbase=attributes.private('markbase')
@@ -7550,21 +7595,21 @@ function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmne
   local dx,dy=factor*(exit[1]-entry[1]),factor*(exit[2]-entry[2])
   local ws,wn=tfmstart.width,tfmnext.width
   local bound=#cursives+1
-  start[a_cursbase]=bound
-  nxt[a_curscurs]=bound
+  setattr(start,a_cursbase,bound)
+  setattr(nxt,a_curscurs,bound)
   cursives[bound]={ rlmode,dx,dy,ws,wn }
   return dx,dy,bound
 end
 function injections.setpair(current,factor,rlmode,r2lflag,spec,tfmchr)
   local x,y,w,h=factor*spec[1],factor*spec[2],factor*spec[3],factor*spec[4]
   if x~=0 or w~=0 or y~=0 or h~=0 then
-    local bound=current[a_kernpair]
+    local bound=getattr(current,a_kernpair)
     if bound then
       local kb=kerns[bound]
       kb[2],kb[3],kb[4],kb[5]=(kb[2] or 0)+x,(kb[3] or 0)+y,(kb[4] or 0)+w,(kb[5] or 0)+h
     else
       bound=#kerns+1
-      current[a_kernpair]=bound
+      setattr(current,a_kernpair,bound)
       kerns[bound]={ rlmode,x,y,w,h,r2lflag,tfmchr.width }
     end
     return x,y,w,h,bound
@@ -7575,7 +7620,7 @@ function injections.setkern(current,factor,rlmode,x,tfmchr)
   local dx=factor*x
   if dx~=0 then
     local bound=#kerns+1
-    current[a_kernpair]=bound
+    setattr(current,a_kernpair,bound)
     kerns[bound]={ rlmode,dx }
     return dx,bound
   else
@@ -7584,25 +7629,25 @@ function injections.setkern(current,factor,rlmode,x,tfmchr)
 end
 function injections.setmark(start,base,factor,rlmode,ba,ma,index) 
   local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2])   
-  local bound=base[a_markbase]          
+  local bound=getattr(base,a_markbase)          
   local index=1
   if bound then
     local mb=marks[bound]
     if mb then
       index=#mb+1
       mb[index]={ dx,dy,rlmode }
-      start[a_markmark]=bound
-      start[a_markdone]=index
+      setattr(start,a_markmark,bound)
+      setattr(start,a_markdone,index)
       return dx,dy,bound
     else
-      report_injections("possible problem, %U is base mark without data (id %a)",base.char,bound)
+      report_injections("possible problem, %U is base mark without data (id %a)",getchar(base),bound)
     end
   end
   index=index or 1
   bound=#marks+1
-  base[a_markbase]=bound
-  start[a_markmark]=bound
-  start[a_markdone]=index
+  setattr(base,a_markbase,bound)
+  setattr(start,a_markmark,bound)
+  setattr(start,a_markdone,index)
   marks[bound]={ [index]={ dx,dy,rlmode } }
   return dx,dy,bound
 end
@@ -7612,15 +7657,15 @@ end
 local function trace(head)
   report_injections("begin run")
   for n in traverse_id(glyph_code,head) do
-    if n.subtype<256 then
-      local kp=n[a_kernpair]
-      local mb=n[a_markbase]
-      local mm=n[a_markmark]
-      local md=n[a_markdone]
-      local cb=n[a_cursbase]
-      local cc=n[a_curscurs]
-      local char=n.char
-      report_injections("font %s, char %U, glyph %c",n.font,char,char)
+    if getsubtype(n)<256 then
+      local kp=getattr(n,a_kernpair)
+      local mb=getattr(n,a_markbase)
+      local mm=getattr(n,a_markmark)
+      local md=getattr(n,a_markdone)
+      local cb=getattr(n,a_cursbase)
+      local cc=getattr(n,a_curscurs)
+      local char=getchar(n)
+      report_injections("font %s, char %U, glyph %c",getfont(n),char,char)
       if kp then
         local k=kerns[kp]
         if k[3] then
@@ -7661,21 +7706,23 @@ local function show_result(head)
   local current=head
   local skipping=false
   while current do
-    local id=current.id
+    local id=getid(current)
     if id==glyph_code then
-      report_injections("char: %C, width %p, xoffset %p, yoffset %p",current.char,current.width,current.xoffset,current.yoffset)
+      report_injections("char: %C, width %p, xoffset %p, yoffset %p",
+        getchar(current),getfield(current,"width"),getfield(current,"xoffset"),getfield(current,"yoffset"))
       skipping=false
     elseif id==kern_code then
-      report_injections("kern: %p",current.kern)
+      report_injections("kern: %p",getfield(current,"kern"))
       skipping=false
     elseif not skipping then
       report_injections()
       skipping=true
     end
-    current=current.next
+    current=getnext(current)
   end
 end
 function injections.handler(head,where,keep)
+  head=tonut(head)
   local has_marks,has_cursives,has_kerns=next(marks),next(cursives),next(kerns)
   if has_marks or has_cursives then
     if trace_injections then
@@ -7685,17 +7732,18 @@ function injections.handler(head,where,keep)
     if has_kerns then 
       local nf,tm=nil,nil
       for n in traverse_id(glyph_code,head) do 
-        if n.subtype<256 then
+        if getsubtype(n)<256 then
           nofvalid=nofvalid+1
           valid[nofvalid]=n
-          if n.font~=nf then
-            nf=n.font
-            tm=fontdata[nf].resources.marks
+          local f=getfont(n)
+          if f~=nf then
+            nf=f
+            tm=fontdata[nf].resources.marks 
           end
           if tm then
-            mk[n]=tm[n.char]
+            mk[n]=tm[getchar(n)]
           end
-          local k=n[a_kernpair]
+          local k=getattr(n,a_kernpair)
           if k then
             local kk=kerns[k]
             if kk then
@@ -7715,15 +7763,16 @@ function injections.handler(head,where,keep)
     else
       local nf,tm=nil,nil
       for n in traverse_id(glyph_code,head) do
-        if n.subtype<256 then
+        if getsubtype(n)<256 then
           nofvalid=nofvalid+1
           valid[nofvalid]=n
-          if n.font~=nf then
-            nf=n.font
+          local f=getfont(n)
+          if f~=nf then
+            nf=f
             tm=fontdata[nf].resources.marks
           end
           if tm then
-            mk[n]=tm[n.char]
+            mk[n]=tm[getchar(n)]
           end
         end
       end
@@ -7732,7 +7781,7 @@ function injections.handler(head,where,keep)
       local cx={}
       if has_kerns and next(ky) then
         for n,k in next,ky do
-          n.yoffset=k
+          setfield(n,"yoffset",k)
         end
       end
       if has_cursives then
@@ -7741,9 +7790,9 @@ function injections.handler(head,where,keep)
         for i=1,nofvalid do 
           local n=valid[i]
           if not mk[n] then
-            local n_cursbase=n[a_cursbase]
+            local n_cursbase=getattr(n,a_cursbase)
             if p_cursbase then
-              local n_curscurs=n[a_curscurs]
+              local n_curscurs=getattr(n,a_curscurs)
               if p_cursbase==n_curscurs then
                 local c=cursives[n_curscurs]
                 if c then
@@ -7766,20 +7815,20 @@ function injections.handler(head,where,keep)
                 end
               end
             elseif maxt>0 then
-              local ny=n.yoffset
+              local ny=getfield(n,"yoffset")
               for i=maxt,1,-1 do
                 ny=ny+d[i]
                 local ti=t[i]
-                ti.yoffset=ti.yoffset+ny
+                setfield(ti,"yoffset",getfield(ti,"yoffset")+ny)
               end
               maxt=0
             end
             if not n_cursbase and maxt>0 then
-              local ny=n.yoffset
+              local ny=getfield(n,"yoffset")
               for i=maxt,1,-1 do
                 ny=ny+d[i]
                 local ti=t[i]
-                ti.yoffset=ny
+                setfield(ti,"yoffset",ny)
               end
               maxt=0
             end
@@ -7787,11 +7836,11 @@ function injections.handler(head,where,keep)
           end
         end
         if maxt>0 then
-          local ny=n.yoffset
+          local ny=getfield(n,"yoffset")
           for i=maxt,1,-1 do
             ny=ny+d[i]
             local ti=t[i]
-            ti.yoffset=ny
+            setfield(ti,"yoffset",ny)
           end
           maxt=0
         end
@@ -7802,46 +7851,52 @@ function injections.handler(head,where,keep)
       if has_marks then
         for i=1,nofvalid do
           local p=valid[i]
-          local p_markbase=p[a_markbase]
+          local p_markbase=getattr(p,a_markbase)
           if p_markbase then
             local mrks=marks[p_markbase]
             local nofmarks=#mrks
-            for n in traverse_id(glyph_code,p.next) do
-              local n_markmark=n[a_markmark]
+            for n in traverse_id(glyph_code,getnext(p)) do
+              local n_markmark=getattr(n,a_markmark)
               if p_markbase==n_markmark then
-                local index=n[a_markdone] or 1
+                local index=getattr(n,a_markdone) or 1
                 local d=mrks[index]
                 if d then
                   local rlmode=d[3]
                   local k=wx[p]
+                  local px=getfield(p,"xoffset")
+                  local ox=0
                   if k then
                     local x=k[2]
                     local w=k[4]
                     if w then
                       if rlmode and rlmode>=0 then
-                        n.xoffset=p.xoffset-p.width+d[1]-(w-x)
+                        ox=px-getfield(p,"width")+d[1]-(w-x)
                       else
-                        n.xoffset=p.xoffset-d[1]-x
+                        ox=px-d[1]-x
                       end
                     else
                       if rlmode and rlmode>=0 then
-                        n.xoffset=p.xoffset-p.width+d[1]
+                        ox=px-getfield(p,"width")+d[1]
                       else
-                        n.xoffset=p.xoffset-d[1]-x
+                        ox=px-d[1]-x
                       end
                     end
                   else
                     if rlmode and rlmode>=0 then
-                      n.xoffset=p.xoffset-p.width+d[1]
+                      ox=px-getfield(p,"width")+d[1]
                     else
-                      n.xoffset=p.xoffset-d[1]
+                      ox=px-d[1]
                     end
                   end
+                  setfield(n,"xoffset",ox)
+                  local py=getfield(p,"yoffset")
+                  local oy=0
                   if mk[p] then
-                    n.yoffset=p.yoffset+d[2]
+                    oy=py+d[2]
                   else
-                    n.yoffset=n.yoffset+p.yoffset+d[2]
+                    oy=getfield(n,"yoffset")+py+d[2]
                   end
+                  setfield(n,"yoffset",oy)
                   if nofmarks==1 then
                     break
                   else
@@ -7899,6 +7954,7 @@ function injections.handler(head,where,keep)
       if not keep then
         kerns={}
       end
+head=tonode(head)
       return head,true
     elseif not keep then
       kerns,cursives,marks={},{},{}
@@ -7908,14 +7964,14 @@ function injections.handler(head,where,keep)
       trace(head)
     end
     for n in traverse_id(glyph_code,head) do
-      if n.subtype<256 then
-        local k=n[a_kernpair]
+      if getsubtype(n)<256 then
+        local k=getattr(n,a_kernpair)
         if k then
           local kk=kerns[k]
           if kk then
             local rl,x,y,w=kk[1],kk[2] or 0,kk[3],kk[4]
             if y and y~=0 then
-              n.yoffset=y 
+              setfield(n,"yoffset",y) 
             end
             if w then
               local wx=w-x
@@ -7946,10 +8002,10 @@ function injections.handler(head,where,keep)
     if not keep then
       kerns={}
     end
-    return head,true
+    return tonode(head),true
   else
   end
-  return head,false
+  return tonode(head),false
 end
 
 end -- closure
@@ -7977,6 +8033,7 @@ analyzers.useunicodemarks=false
 local a_state=attributes.private('state')
 local nodecodes=nodes.nodecodes
 local glyph_code=nodecodes.glyph
+local disc_code=nodecodes.disc
 local math_code=nodecodes.math
 local traverse_id=node.traverse_id
 local traverse_node_list=node.traverse
@@ -8043,7 +8100,7 @@ function analyzers.setstate(head,font)
         first,last,n=nil,nil,0
       end
     elseif id==disc_code then
-      current[a_state]=s_midi
+      current[a_state]=s_medi
       last=current
     else 
       if first and first==last then
@@ -8095,7 +8152,7 @@ local function analyzeprocessor(head,font,attr)
 end
 registerotffeature {
   name="analyze",
-  description="analysis of (for instance) character classes",
+  description="analysis of character classes",
   default=true,
   initializers={
     node=analyzeinitializer,
@@ -8358,12 +8415,25 @@ registertracker("otf.positions","otf.marks,otf.kerns,otf.cursive")
 registertracker("otf.actions","otf.replacements,otf.positions")
 registertracker("otf.injections","nodes.injections")
 registertracker("*otf.sample","otf.steps,otf.actions,otf.analyzing")
-local insert_node_after=node.insert_after
-local delete_node=nodes.delete
-local copy_node=node.copy
-local find_node_tail=node.tail or node.slide
-local flush_node_list=node.flush_list
-local end_of_math=node.end_of_math
+local nuts=nodes.nuts
+local tonode=nuts.tonode
+local tonut=nuts.tonut
+local getfield=nuts.getfield
+local getnext=nuts.getnext
+local getprev=nuts.getprev
+local getid=nuts.getid
+local getattr=nuts.getattr
+local getfont=nuts.getfont
+local getsubtype=nuts.getsubtype
+local getchar=nuts.getchar
+local setfield=nuts.setfield
+local setattr=nuts.setattr
+local insert_node_after=nuts.insert_after
+local delete_node=nuts.delete
+local copy_node=nuts.copy
+local find_node_tail=nuts.tail
+local flush_node_list=nuts.flush_list
+local end_of_math=nuts.end_of_math
 local setmetatableindex=table.setmetatableindex
 local zwnj=0x200C
 local zwj=0x200D
@@ -8472,83 +8542,83 @@ local function pref(kind,lookupname)
   return formatters["feature %a, lookup %a"](kind,lookupname)
 end
 local function copy_glyph(g) 
-  local components=g.components
+  local components=getfield(g,"components")
   if components then
-    g.components=nil
+    setfield(g,"components",nil)
     local n=copy_node(g)
-    g.components=components
+    setfield(g,"components",components)
     return n
   else
     return copy_node(g)
   end
 end
 local function markstoligature(kind,lookupname,head,start,stop,char)
-  if start==stop and start.char==char then
+  if start==stop and getchar(start)==char then
     return head,start
   else
-    local prev=start.prev
-    local next=stop.next
-    start.prev=nil
-    stop.next=nil
+    local prev=getprev(start)
+    local next=getnext(stop)
+    setfield(start,"prev",nil)
+    setfield(stop,"next",nil)
     local base=copy_glyph(start)
     if head==start then
       head=base
     end
-    base.char=char
-    base.subtype=ligature_code
-    base.components=start
+    setfield(base,"char",char)
+    setfield(base,"subtype",ligature_code)
+    setfield(base,"components",start)
     if prev then
-      prev.next=base
+      setfield(prev,"next",base)
     end
     if next then
-      next.prev=base
+      setfield(next,"prev",base)
     end
-    base.next=next
-    base.prev=prev
+    setfield(base,"next",next)
+    setfield(base,"prev",prev)
     return head,base
   end
 end
 local function getcomponentindex(start)
-  if start.id~=glyph_code then
+  if getid(start)~=glyph_code then
     return 0
-  elseif start.subtype==ligature_code then
+  elseif getsubtype(start)==ligature_code then
     local i=0
-    local components=start.components
+    local components=getfield(start,"components")
     while components do
       i=i+getcomponentindex(components)
-      components=components.next
+      components=getnext(components)
     end
     return i
-  elseif not marks[start.char] then
+  elseif not marks[getchar(start)] then
     return 1
   else
     return 0
   end
 end
 local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound) 
-  if start==stop and start.char==char then
-    start.char=char
+  if start==stop and getchar(start)==char then
+    setfield(start,"char",char)
     return head,start
   end
-  local prev=start.prev
-  local next=stop.next
-  start.prev=nil
-  stop.next=nil
+  local prev=getprev(start)
+  local next=getnext(stop)
+  setfield(start,"prev",nil)
+  setfield(stop,"next",nil)
   local base=copy_glyph(start)
   if start==head then
     head=base
   end
-  base.char=char
-  base.subtype=ligature_code
-  base.components=start 
+  setfield(base,"char",char)
+  setfield(base,"subtype",ligature_code)
+  setfield(base,"components",start) 
   if prev then
-    prev.next=base
+    setfield(prev,"next",base)
   end
   if next then
-    next.prev=base
+    setfield(next,"prev",base)
   end
-  base.next=next
-  base.prev=prev
+  setfield(base,"next",next)
+  setfield(base,"prev",prev)
   if not discfound then
     local deletemarks=markflag~="mark"
     local components=start
@@ -8557,42 +8627,42 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun
     local head=base
     local current=base
     while start do
-      local char=start.char
+      local char=getchar(start)
       if not marks[char] then
         baseindex=baseindex+componentindex
         componentindex=getcomponentindex(start)
       elseif not deletemarks then 
-        start[a_ligacomp]=baseindex+(start[a_ligacomp] or componentindex)
+        setattr(start,a_ligacomp,baseindex+(getattr(start,a_ligacomp) or componentindex))
         if trace_marks then
-          logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp])
+          logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),getattr(start,a_ligacomp))
         end
         head,current=insert_node_after(head,current,copy_node(start)) 
       elseif trace_marks then
         logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char))
       end
-      start=start.next
+      start=getnext(start)
     end
-    local start=current.next
-    while start and start.id==glyph_code do
-      local char=start.char
+    local start=getnext(current)
+    while start and getid(start)==glyph_code do
+      local char=getchar(start)
       if marks[char] then
-        start[a_ligacomp]=baseindex+(start[a_ligacomp] or componentindex)
+        setattr(start,a_ligacomp,baseindex+(getattr(start,a_ligacomp) or componentindex))
         if trace_marks then
-          logwarning("%s: set mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp])
+          logwarning("%s: set mark %s, gets index %s",pref(kind,lookupname),gref(char),getattr(start,a_ligacomp))
         end
       else
         break
       end
-      start=start.next
+      start=getnext(start)
     end
   end
   return head,base
 end
 function handlers.gsub_single(head,start,kind,lookupname,replacement)
   if trace_singles then
-    logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(start.char),gref(replacement))
+    logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(getchar(start)),gref(replacement))
   end
-  start.char=replacement
+  setfield(start,"char",replacement)
   return head,start,true
 end
 local function get_alternative_glyph(start,alternatives,value,trace_alternatives)
@@ -8618,7 +8688,7 @@ local function get_alternative_glyph(start,alternatives,value,trace_alternatives
         return false,trace_alternatives and formatters["invalid value %a, %s"](value,"out of range")
       end
     elseif value==0 then
-      return start.char,trace_alternatives and formatters["invalid value %a, %s"](value,"no change")
+      return getchar(start),trace_alternatives and formatters["invalid value %a, %s"](value,"no change")
     elseif value<1 then
       return alternatives[1],trace_alternatives and formatters["invalid value %a, taking %a"](value,1)
     else
@@ -8629,25 +8699,25 @@ end
 local function multiple_glyphs(head,start,multiple) 
   local nofmultiples=#multiple
   if nofmultiples>0 then
-    start.char=multiple[1]
+    setfield(start,"char",multiple[1])
     if nofmultiples>1 then
-      local sn=start.next
+      local sn=getnext(start)
       for k=2,nofmultiples do 
         local n=copy_node(start) 
-        n.char=multiple[k]
-        n.next=sn
-        n.prev=start
+        setfield(n,"char",multiple[k])
+        setfield(n,"next",sn)
+        setfield(n,"prev",start)
         if sn then
-          sn.prev=n
+          setfield(sn,"prev",n)
         end
-        start.next=n
+        setfield(start,"next",n)
         start=n
       end
     end
     return head,start,true
   else
     if trace_multiples then
-      logprocess("no multiple for %s",gref(start.char))
+      logprocess("no multiple for %s",gref(getchar(start)))
     end
     return head,start,false
   end
@@ -8657,34 +8727,34 @@ function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence
   local choice,comment=get_alternative_glyph(start,alternative,value,trace_alternatives)
   if choice then
     if trace_alternatives then
-      logprocess("%s: replacing %s by alternative %a to %s, %s",pref(kind,lookupname),gref(start.char),choice,gref(choice),comment)
+      logprocess("%s: replacing %s by alternative %a to %s, %s",pref(kind,lookupname),gref(getchar(start)),choice,gref(choice),comment)
     end
-    start.char=choice
+    setfield(start,"char",choice)
   else
     if trace_alternatives then
-      logwarning("%s: no variant %a for %s, %s",pref(kind,lookupname),value,gref(start.char),comment)
+      logwarning("%s: no variant %a for %s, %s",pref(kind,lookupname),value,gref(getchar(start)),comment)
     end
   end
   return head,start,true
 end
 function handlers.gsub_multiple(head,start,kind,lookupname,multiple)
   if trace_multiples then
-    logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple))
+    logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(getchar(start)),gref(multiple))
   end
   return multiple_glyphs(head,start,multiple)
 end
 function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
-  local s,stop,discfound=start.next,nil,false
-  local startchar=start.char
+  local s,stop,discfound=getnext(start),nil,false
+  local startchar=getchar(start)
   if marks[startchar] then
     while s do
-      local id=s.id
-      if id==glyph_code and s.font==currentfont and s.subtype<256 then
-        local lg=ligature[s.char]
+      local id=getid(s)
+      if id==glyph_code and getfont(s)==currentfont and getsubtype(s)<256 then
+        local lg=ligature[getchar(s)]
         if lg then
           stop=s
           ligature=lg
-          s=s.next
+          s=getnext(s)
         else
           break
         end
@@ -8696,9 +8766,9 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
       local lig=ligature.ligature
       if lig then
         if trace_ligatures then
-          local stopchar=stop.char
+          local stopchar=getchar(stop)
           head,start=markstoligature(kind,lookupname,head,start,stop,lig)
-          logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char))
+          logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(getchar(start)))
         else
           head,start=markstoligature(kind,lookupname,head,start,stop,lig)
         end
@@ -8709,18 +8779,18 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
   else
     local skipmark=sequence.flags[1]
     while s do
-      local id=s.id
-      if id==glyph_code and s.subtype<256 then
-        if s.font==currentfont then
-          local char=s.char
+      local id=getid(s)
+      if id==glyph_code and getsubtype(s)<256 then
+        if getfont(s)==currentfont then
+          local char=getchar(s)
           if skipmark and marks[char] then
-            s=s.next
+            s=getnext(s)
           else
             local lg=ligature[char]
             if lg then
               stop=s
               ligature=lg
-              s=s.next
+              s=getnext(s)
             else
               break
             end
@@ -8730,7 +8800,7 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
         end
       elseif id==disc_code then
         discfound=true
-        s=s.next
+        s=getnext(s)
       else
         break
       end
@@ -8739,9 +8809,9 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
       local lig=ligature.ligature
       if lig then
         if trace_ligatures then
-          local stopchar=stop.char
+          local stopchar=getchar(stop)
           head,start=toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound)
-          logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char))
+          logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(getchar(start)))
         else
           head,start=toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound)
         end
@@ -8753,16 +8823,16 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
   return head,start,false
 end
 function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence)
-  local markchar=start.char
+  local markchar=getchar(start)
   if marks[markchar] then
-    local base=start.prev 
-    if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
-      local basechar=base.char
+    local base=getprev(start) 
+    if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
+      local basechar=getchar(base)
       if marks[basechar] then
         while true do
-          base=base.prev
-          if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
-            basechar=base.char
+          base=getprev(base)
+          if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
+            basechar=getchar(base)
             if not marks[basechar] then
               break
             end
@@ -8811,16 +8881,16 @@ function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence
   return head,start,false
 end
 function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequence)
-  local markchar=start.char
+  local markchar=getchar(start)
   if marks[markchar] then
-    local base=start.prev 
-    if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
-      local basechar=base.char
+    local base=getprev(start) 
+    if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
+      local basechar=getchar(base)
       if marks[basechar] then
         while true do
-          base=base.prev
-          if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
-            basechar=base.char
+          base=getprev(base)
+          if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
+            basechar=getchar(base)
             if not marks[basechar] then
               break
             end
@@ -8832,7 +8902,7 @@ function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequ
           end
         end
       end
-      local index=start[a_ligacomp]
+      local index=getattr(start,a_ligacomp)
       local baseanchors=descriptions[basechar]
       if baseanchors then
         baseanchors=baseanchors.anchors
@@ -8877,22 +8947,22 @@ function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequ
   return head,start,false
 end
 function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence)
-  local markchar=start.char
+  local markchar=getchar(start)
   if marks[markchar] then
-    local base=start.prev 
-    local slc=start[a_ligacomp]
+    local base=getprev(start) 
+    local slc=getattr(start,a_ligacomp)
     if slc then 
       while base do
-        local blc=base[a_ligacomp]
+        local blc=getattr(base,a_ligacomp)
         if blc and blc~=slc then
-          base=base.prev
+          base=getprev(base)
         else
           break
         end
       end
     end
-    if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then 
-      local basechar=base.char
+    if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then 
+      local basechar=getchar(base)
       local baseanchors=descriptions[basechar]
       if baseanchors then
         baseanchors=baseanchors.anchors
@@ -8930,20 +9000,20 @@ function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence
   return head,start,false
 end
 function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence) 
-  local alreadydone=cursonce and start[a_cursbase]
+  local alreadydone=cursonce and getattr(start,a_cursbase)
   if not alreadydone then
     local done=false
-    local startchar=start.char
+    local startchar=getchar(start)
     if marks[startchar] then
       if trace_cursive then
         logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar))
       end
     else
-      local nxt=start.next
-      while not done and nxt and nxt.id==glyph_code and nxt.font==currentfont and nxt.subtype<256 do
-        local nextchar=nxt.char
+      local nxt=getnext(start)
+      while not done and nxt and getid(nxt)==glyph_code and getfont(nxt)==currentfont and getsubtype(nxt)<256 do
+        local nextchar=getchar(nxt)
         if marks[nextchar] then
-          nxt=nxt.next
+          nxt=getnext(nxt)
         else
           local entryanchors=descriptions[nextchar]
           if entryanchors then
@@ -8977,13 +9047,13 @@ function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence)
     return head,start,done
   else
     if trace_cursive and trace_details then
-      logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone)
+      logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(getchar(start)),alreadydone)
     end
     return head,start,false
   end
 end
 function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence)
-  local startchar=start.char
+  local startchar=getchar(start)
   local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar])
   if trace_kerns then
     logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h)
@@ -8991,19 +9061,19 @@ function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence)
   return head,start,false
 end
 function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence)
-  local snext=start.next
+  local snext=getnext(start)
   if not snext then
     return head,start,false
   else
     local prev,done=start,false
     local factor=tfmdata.parameters.factor
     local lookuptype=lookuptypes[lookupname]
-    while snext and snext.id==glyph_code and snext.font==currentfont and snext.subtype<256 do
-      local nextchar=snext.char
+    while snext and getid(snext)==glyph_code and getfont(snext)==currentfont and getsubtype(snext)<256 do
+      local nextchar=getchar(snext)
       local krn=kerns[nextchar]
       if not krn and marks[nextchar] then
         prev=snext
-        snext=snext.next
+        snext=getnext(snext)
       else
         local krn=kerns[nextchar]
         if not krn then
@@ -9011,14 +9081,14 @@ function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence)
           if lookuptype=="pair" then 
             local a,b=krn[2],krn[3]
             if a and #a>0 then
-              local startchar=start.char
+              local startchar=getchar(start)
               local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar])
               if trace_kerns then
                 logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
               end
             end
             if b and #b>0 then
-              local startchar=start.char
+              local startchar=getchar(start)
               local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar])
               if trace_kerns then
                 logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)
@@ -9031,7 +9101,7 @@ function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence)
         elseif krn~=0 then
           local k=setkern(snext,factor,rlmode,krn)
           if trace_kerns then
-            logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar))
+            logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar))
           end
           done=true
         end
@@ -9066,13 +9136,13 @@ function chainmores.chainsub(head,start,stop,kind,chainname,currentcontext,looku
   return head,start,false
 end
 function chainprocs.reversesub(head,start,stop,kind,chainname,currentcontext,lookuphash,replacements)
-  local char=start.char
+  local char=getchar(start)
   local replacement=replacements[char]
   if replacement then
     if trace_singles then
       logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement))
     end
-    start.char=replacement
+    setfield(start,"char",replacement)
     return head,start,true
   else
     return head,start,false
@@ -9083,9 +9153,9 @@ local function delete_till_stop(start,stop,ignoremarks)
   if start==stop then
   elseif ignoremarks then
     repeat 
-      local next=start.next
-      if not marks[next.char] then
-        local components=next.components
+      local next=getnext(start)
+      if not marks[getchar(next)] then
+        local components=getfield(next,"components")
         if components then 
           flush_node_list(components)
         end
@@ -9095,8 +9165,8 @@ local function delete_till_stop(start,stop,ignoremarks)
     until next==stop
   else 
     repeat
-      local next=start.next
-      local components=next.components
+      local next=getnext(start)
+      local components=getfield(next,"components")
       if components then 
         flush_node_list(components)
       end
@@ -9113,8 +9183,8 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo
     logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," "))
   end
   while current do
-    if current.id==glyph_code then
-      local currentchar=current.char
+    if getid(current)==glyph_code then
+      local currentchar=getchar(current)
       local lookupname=subtables[1] 
       local replacement=lookuphash[lookupname]
       if not replacement then
@@ -9131,14 +9201,14 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo
           if trace_singles then
             logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement))
           end
-          current.char=replacement
+          setfield(current,"char",replacement)
         end
       end
       return head,start,true
     elseif current==stop then
       break
     else
-      current=current.next
+      current=getnext(current)
     end
   end
   return head,start,false
@@ -9146,7 +9216,7 @@ end
 chainmores.gsub_single=chainprocs.gsub_single
 function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
   delete_till_stop(start,stop) 
-  local startchar=start.char
+  local startchar=getchar(start)
   local subtables=currentlookup.subtables
   local lookupname=subtables[1]
   local replacements=lookuphash[lookupname]
@@ -9175,8 +9245,8 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext
   local subtables=currentlookup.subtables
   local value=featurevalue==true and tfmdata.shared.features[kind] or featurevalue
   while current do
-    if current.id==glyph_code then 
-      local currentchar=current.char
+    if getid(current)==glyph_code then 
+      local currentchar=getchar(current)
       local lookupname=subtables[1]
       local alternatives=lookuphash[lookupname]
       if not alternatives then
@@ -9191,7 +9261,7 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext
             if trace_alternatives then
               logprocess("%s: replacing %s by alternative %a to %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(char),choice,gref(choice),comment)
             end
-            start.char=choice
+            setfield(start,"char",choice)
           else
             if trace_alternatives then
               logwarning("%s: no variant %a for %s, %s",cref(kind,chainname,chainlookupname,lookupname),value,gref(char),comment)
@@ -9205,14 +9275,14 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext
     elseif current==stop then
       break
     else
-      current=current.next
+      current=getnext(current)
     end
   end
   return head,start,false
 end
 chainmores.gsub_alternate=chainprocs.gsub_alternate
 function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex)
-  local startchar=start.char
+  local startchar=getchar(start)
   local subtables=currentlookup.subtables
   local lookupname=subtables[1]
   local ligatures=lookuphash[lookupname]
@@ -9227,20 +9297,20 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
         logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))
       end
     else
-      local s=start.next
+      local s=getnext(start)
       local discfound=false
       local last=stop
       local nofreplacements=0
       local skipmark=currentlookup.flags[1]
       while s do
-        local id=s.id
+        local id=getid(s)
         if id==disc_code then
-          s=s.next
+          s=getnext(s)
           discfound=true
         else
-          local schar=s.char
+          local schar=getchar(s)
           if skipmark and marks[schar] then 
-            s=s.next
+            s=getnext(s)
           else
             local lg=ligatures[schar]
             if lg then
@@ -9248,7 +9318,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
               if s==stop then
                 break
               else
-                s=s.next
+                s=getnext(s)
               end
             else
               break
@@ -9265,7 +9335,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
           if start==stop then
             logprocess("%s: replacing character %s by ligature %s case 3",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2))
           else
-            logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2))
+            logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(getchar(stop)),gref(l2))
           end
         end
         head,start=toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound)
@@ -9274,7 +9344,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
         if start==stop then
           logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))
         else
-          logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char))
+          logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(getchar(stop)))
         end
       end
     end
@@ -9283,7 +9353,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
 end
 chainmores.gsub_ligature=chainprocs.gsub_ligature
 function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
-  local markchar=start.char
+  local markchar=getchar(start)
   if marks[markchar] then
     local subtables=currentlookup.subtables
     local lookupname=subtables[1]
@@ -9292,14 +9362,14 @@ function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext
       markanchors=markanchors[markchar]
     end
     if markanchors then
-      local base=start.prev 
-      if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
-        local basechar=base.char
+      local base=getprev(start) 
+      if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
+        local basechar=getchar(base)
         if marks[basechar] then
           while true do
-            base=base.prev
-            if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
-              basechar=base.char
+            base=getprev(base)
+            if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
+              basechar=getchar(base)
               if not marks[basechar] then
                 break
               end
@@ -9346,7 +9416,7 @@ function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext
   return head,start,false
 end
 function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
-  local markchar=start.char
+  local markchar=getchar(start)
   if marks[markchar] then
     local subtables=currentlookup.subtables
     local lookupname=subtables[1]
@@ -9355,14 +9425,14 @@ function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcon
       markanchors=markanchors[markchar]
     end
     if markanchors then
-      local base=start.prev 
-      if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
-        local basechar=base.char
+      local base=getprev(start) 
+      if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
+        local basechar=getchar(base)
         if marks[basechar] then
           while true do
-            base=base.prev
-            if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then
-              basechar=base.char
+            base=getprev(base)
+            if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then
+              basechar=getchar(base)
               if not marks[basechar] then
                 break
               end
@@ -9374,7 +9444,7 @@ function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcon
             end
           end
         end
-        local index=start[a_ligacomp]
+        local index=getattr(start,a_ligacomp)
         local baseanchors=descriptions[basechar].anchors
         if baseanchors then
           local baseanchors=baseanchors['baselig']
@@ -9413,7 +9483,7 @@ function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcon
   return head,start,false
 end
 function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
-  local markchar=start.char
+  local markchar=getchar(start)
   if marks[markchar] then
       local subtables=currentlookup.subtables
       local lookupname=subtables[1]
@@ -9422,20 +9492,20 @@ function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext
         markanchors=markanchors[markchar]
       end
       if markanchors then
-        local base=start.prev 
-        local slc=start[a_ligacomp]
+        local base=getprev(start) 
+        local slc=getattr(start,a_ligacomp)
         if slc then 
           while base do
-            local blc=base[a_ligacomp]
+            local blc=getattr(base,a_ligacomp)
             if blc and blc~=slc then
-              base=base.prev
+              base=getprev(base)
             else
               break
             end
           end
         end
-        if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then 
-          local basechar=base.char
+        if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then 
+          local basechar=getchar(base)
           local baseanchors=descriptions[basechar].anchors
           if baseanchors then
             baseanchors=baseanchors['basemark']
@@ -9471,9 +9541,9 @@ function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext
   return head,start,false
 end
 function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
-  local alreadydone=cursonce and start[a_cursbase]
+  local alreadydone=cursonce and getattr(start,a_cursbase)
   if not alreadydone then
-    local startchar=start.char
+    local startchar=getchar(start)
     local subtables=currentlookup.subtables
     local lookupname=subtables[1]
     local exitanchors=lookuphash[lookupname]
@@ -9487,11 +9557,11 @@ function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,l
           logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar))
         end
       else
-        local nxt=start.next
-        while not done and nxt and nxt.id==glyph_code and nxt.font==currentfont and nxt.subtype<256 do
-          local nextchar=nxt.char
+        local nxt=getnext(start)
+        while not done and nxt and getid(nxt)==glyph_code and getfont(nxt)==currentfont and getsubtype(nxt)<256 do
+          local nextchar=getchar(nxt)
           if marks[nextchar] then
-            nxt=nxt.next
+            nxt=getnext(nxt)
           else
             local entryanchors=descriptions[nextchar]
             if entryanchors then
@@ -9525,7 +9595,7 @@ function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,l
       return head,start,done
     else
       if trace_cursive and trace_details then
-        logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone)
+        logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(getchar(start)),alreadydone)
       end
       return head,start,false
     end
@@ -9533,7 +9603,7 @@ function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,l
   return head,start,false
 end
 function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
-  local startchar=start.char
+  local startchar=getchar(start)
   local subtables=currentlookup.subtables
   local lookupname=subtables[1]
   local kerns=lookuphash[lookupname]
@@ -9549,9 +9619,9 @@ function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lo
   return head,start,false
 end
 function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
-  local snext=start.next
+  local snext=getnext(start)
   if snext then
-    local startchar=start.char
+    local startchar=getchar(start)
     local subtables=currentlookup.subtables
     local lookupname=subtables[1]
     local kerns=lookuphash[lookupname]
@@ -9561,26 +9631,26 @@ function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,look
         local lookuptype=lookuptypes[lookupname]
         local prev,done=start,false
         local factor=tfmdata.parameters.factor
-        while snext and snext.id==glyph_code and snext.font==currentfont and snext.subtype<256 do
-          local nextchar=snext.char
+        while snext and getid(snext)==glyph_code and getfont(snext)==currentfont and getsubtype(snext)<256 do
+          local nextchar=getchar(snext)
           local krn=kerns[nextchar]
           if not krn and marks[nextchar] then
             prev=snext
-            snext=snext.next
+            snext=getnext(snext)
           else
             if not krn then
             elseif type(krn)=="table" then
               if lookuptype=="pair" then
                 local a,b=krn[2],krn[3]
                 if a and #a>0 then
-                  local startchar=start.char
+                  local startchar=getchar(start)
                   local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar])
                   if trace_kerns then
                     logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
                   end
                 end
                 if b and #b>0 then
-                  local startchar=start.char
+                  local startchar=getchar(start)
                   local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar])
                   if trace_kerns then
                     logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)
@@ -9592,7 +9662,7 @@ function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,look
                 if a and a~=0 then
                   local k=setkern(snext,factor,rlmode,a)
                   if trace_kerns then
-                    logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar))
+                    logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar))
                   end
                 end
                 if b and b~=0 then
@@ -9603,7 +9673,7 @@ function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,look
             elseif krn~=0 then
               local k=setkern(snext,factor,rlmode,krn)
               if trace_kerns then
-                logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar))
+                logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar))
               end
               done=true
             end
@@ -9640,7 +9710,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
     local seq=ck[3]
     local s=#seq
     if s==1 then
-      match=current.id==glyph_code and current.font==currentfont and current.subtype<256 and seq[1][current.char]
+      match=getid(current)==glyph_code and getfont(current)==currentfont and getsubtype(current)<256 and seq[1][getchar(current)]
     else
       local f,l=ck[4],ck[5]
       if f==1 and f==l then
@@ -9648,13 +9718,13 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
         if f==l then
         else
           local n=f+1
-          last=last.next
+          last=getnext(last)
           while n<=l do
             if last then
-              local id=last.id
+              local id=getid(last)
               if id==glyph_code then
-                if last.font==currentfont and last.subtype<256 then
-                  local char=last.char
+                if getfont(last)==currentfont and getsubtype(last)<256 then
+                  local char=getchar(last)
                   local ccd=descriptions[char]
                   if ccd then
                     local class=ccd.class
@@ -9663,10 +9733,10 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
                       if trace_skips then
                         show_skip(kind,chainname,char,ck,class)
                       end
-                      last=last.next
+                      last=getnext(last)
                     elseif seq[n][char] then
                       if n<l then
-                        last=last.next
+                        last=getnext(last)
                       end
                       n=n+1
                     else
@@ -9682,7 +9752,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
                   break
                 end
               elseif id==disc_code then
-                last=last.next
+                last=getnext(last)
               else
                 match=false
                 break
@@ -9695,15 +9765,15 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
         end
       end
       if match and f>1 then
-        local prev=start.prev
+        local prev=getprev(start)
         if prev then
           local n=f-1
           while n>=1 do
             if prev then
-              local id=prev.id
+              local id=getid(prev)
               if id==glyph_code then
-                if prev.font==currentfont and prev.subtype<256 then 
-                  local char=prev.char
+                if getfont(prev)==currentfont and getsubtype(prev)<256 then 
+                  local char=getchar(prev)
                   local ccd=descriptions[char]
                   if ccd then
                     local class=ccd.class
@@ -9733,7 +9803,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
                 match=false
                 break
               end
-              prev=prev.prev
+              prev=getprev(prev)
             elseif seq[n][32] then 
               n=n -1
             else
@@ -9753,15 +9823,15 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
         end
       end
       if match and s>l then
-        local current=last and last.next
+        local current=last and getnext(last)
         if current then
           local n=l+1
           while n<=s do
             if current then
-              local id=current.id
+              local id=getid(current)
               if id==glyph_code then
-                if current.font==currentfont and current.subtype<256 then 
-                  local char=current.char
+                if getfont(current)==currentfont and getsubtype(current)<256 then 
+                  local char=getchar(current)
                   local ccd=descriptions[char]
                   if ccd then
                     local class=ccd.class
@@ -9791,7 +9861,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
                 match=false
                 break
               end
-              current=current.next
+              current=getnext(current)
             elseif seq[n][32] then
               n=n+1
             else
@@ -9814,7 +9884,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
     if match then
       if trace_contexts then
         local rule,lookuptype,f,l=ck[1],ck[2],ck[4],ck[5]
-        local char=start.char
+        local char=getchar(start)
         if ck[9] then
           logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a, %a => %a",
             cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10])
@@ -9844,12 +9914,12 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
           repeat
             if skipped then
               while true do
-                local char=start.char
+                local char=getchar(start)
                 local ccd=descriptions[char]
                 if ccd then
                   local class=ccd.class
                   if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
-                    start=start.next
+                    start=getnext(start)
                   else
                     break
                   end
@@ -9874,7 +9944,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
               i=i+1
             end
             if start then
-              start=start.next
+              start=getnext(start)
             else
             end
           until i>nofchainlookups
@@ -10001,6 +10071,7 @@ local function featuresprocessor(head,font,attr)
   if not lookuphash then
     return head,false
   end
+  head=tonut(head)
   if trace_steps then
     checkstep(head)
   end
@@ -10033,10 +10104,10 @@ local function featuresprocessor(head,font,attr)
       local handler=handlers[typ]
       local start=find_node_tail(head) 
       while start do
-        local id=start.id
+        local id=getid(start)
         if id==glyph_code then
-          if start.font==font and start.subtype<256 then
-            local a=start[0]
+          if getfont(start)==font and getsubtype(start)<256 then
+            local a=getattr(start,0)
             if a then
               a=a==attr
             else
@@ -10047,7 +10118,7 @@ local function featuresprocessor(head,font,attr)
                 local lookupname=subtables[i]
                 local lookupcache=lookuphash[lookupname]
                 if lookupcache then
-                  local lookupmatch=lookupcache[start.char]
+                  local lookupmatch=lookupcache[getchar(start)]
                   if lookupmatch then
                     head,start,success=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
                     if success then
@@ -10058,15 +10129,15 @@ local function featuresprocessor(head,font,attr)
                   report_missing_cache(typ,lookupname)
                 end
               end
-              if start then start=start.prev end
+              if start then start=getprev(start) end
             else
-              start=start.prev
+              start=getprev(start)
             end
           else
-            start=start.prev
+            start=getprev(start)
           end
         else
-          start=start.prev
+          start=getprev(start)
         end
       end
     else
@@ -10081,17 +10152,17 @@ local function featuresprocessor(head,font,attr)
           report_missing_cache(typ,lookupname)
         else
           while start do
-            local id=start.id
+            local id=getid(start)
             if id==glyph_code then
-              if start.font==font and start.subtype<256 then
-                local a=start[0]
+              if getfont(start)==font and getsubtype(start)<256 then
+                local a=getattr(start,0)
                 if a then
-                  a=(a==attr) and (not attribute or start[a_state]==attribute)
+                  a=(a==attr) and (not attribute or getattr(start,a_state)==attribute)
                 else
-                  a=not attribute or start[a_state]==attribute
+                  a=not attribute or getattr(start,a_state)==attribute
                 end
                 if a then
-                  local lookupmatch=lookupcache[start.char]
+                  local lookupmatch=lookupcache[getchar(start)]
                   if lookupmatch then
                     local ok
                     head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
@@ -10099,17 +10170,17 @@ local function featuresprocessor(head,font,attr)
                       success=true
                     end
                   end
-                  if start then start=start.next end
+                  if start then start=getnext(start) end
                 else
-                  start=start.next
+                  start=getnext(start)
                 end
               else
-                start=start.next
+                start=getnext(start)
               end
             elseif id==whatsit_code then 
-              local subtype=start.subtype
+              local subtype=getsubtype(start)
               if subtype==dir_code then
-                local dir=start.dir
+                local dir=getfield(start,"dir")
                 if   dir=="+TRT" or dir=="+TLT" then
                   topstack=topstack+1
                   dirstack[topstack]=dir
@@ -10128,7 +10199,7 @@ local function featuresprocessor(head,font,attr)
                   report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir)
                 end
               elseif subtype==localpar_code then
-                local dir=start.dir
+                local dir=getfield(start,"dir")
                 if dir=="TRT" then
                   rlparmode=-1
                 elseif dir=="TLT" then
@@ -10141,31 +10212,31 @@ local function featuresprocessor(head,font,attr)
                   report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode)
                 end
               end
-              start=start.next
+              start=getnext(start)
             elseif id==math_code then
-              start=end_of_math(start).next
+              start=getnext(end_of_math(start))
             else
-              start=start.next
+              start=getnext(start)
             end
           end
         end
       else
         while start do
-          local id=start.id
+          local id=getid(start)
           if id==glyph_code then
-            if start.font==font and start.subtype<256 then
-              local a=start[0]
+            if getfont(start)==font and getsubtype(start)<256 then
+              local a=getattr(start,0)
               if a then
-                a=(a==attr) and (not attribute or start[a_state]==attribute)
+                a=(a==attr) and (not attribute or getattr(start,a_state)==attribute)
               else
-                a=not attribute or start[a_state]==attribute
+                a=not attribute or getattr(start,a_state)==attribute
               end
               if a then
                 for i=1,ns do
                   local lookupname=subtables[i]
                   local lookupcache=lookuphash[lookupname]
                   if lookupcache then
-                    local lookupmatch=lookupcache[start.char]
+                    local lookupmatch=lookupcache[getchar(start)]
                     if lookupmatch then
                       local ok
                       head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
@@ -10180,17 +10251,17 @@ local function featuresprocessor(head,font,attr)
                     report_missing_cache(typ,lookupname)
                   end
                 end
-                if start then start=start.next end
+                if start then start=getnext(start) end
               else
-                start=start.next
+                start=getnext(start)
               end
             else
-              start=start.next
+              start=getnext(start)
             end
           elseif id==whatsit_code then
-            local subtype=start.subtype
+            local subtype=getsubtype(start)
             if subtype==dir_code then
-              local dir=start.dir
+              local dir=getfield(start,"dir")
               if   dir=="+TRT" or dir=="+TLT" then
                 topstack=topstack+1
                 dirstack[topstack]=dir
@@ -10209,7 +10280,7 @@ local function featuresprocessor(head,font,attr)
                 report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir)
               end
             elseif subtype==localpar_code then
-              local dir=start.dir
+              local dir=getfield(start,"dir")
               if dir=="TRT" then
                 rlparmode=-1
               elseif dir=="TLT" then
@@ -10222,11 +10293,11 @@ local function featuresprocessor(head,font,attr)
                 report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode)
               end
             end
-            start=start.next
+            start=getnext(start)
           elseif id==math_code then
-            start=end_of_math(start).next
+            start=getnext(end_of_math(start))
           else
-            start=start.next
+            start=getnext(start)
           end
         end
       end
@@ -10238,6 +10309,7 @@ local function featuresprocessor(head,font,attr)
       registerstep(head)
     end
   end
+  head=tonode(head)
   return head,done
 end
 local function generic(lookupdata,lookupname,unicode,lookuphash)
diff --git a/tex/generic/context/luatex/luatex-fonts.lua b/tex/generic/context/luatex/luatex-fonts.lua
index 89592fcac..88d6a2f0e 100644
--- a/tex/generic/context/luatex/luatex-fonts.lua
+++ b/tex/generic/context/luatex/luatex-fonts.lua
@@ -192,7 +192,7 @@ if non_generic_context.luatex_fonts.skip_loading ~= true then
         -- with context. The mtx-fonts script can be used to genate this file (using the --names option).
 
         -- In 2013/14 I will merge/move some generic files into luatex-fonts-* files (copies) so that
-        -- intermediate updates of context not interfere. We can then also use the general merger and
+        -- intermediate updates of context don't interfere. We can then also use the general merger and
         -- consider stripping debug code.
 
         loadmodule('font-ini.lua')
@@ -209,7 +209,6 @@ if non_generic_context.luatex_fonts.skip_loading ~= true then
         loadmodule('font-ota.lua')
         loadmodule('font-otn.lua')
         loadmodule('font-otp.lua')         -- optional
-        ----------('luatex-fonts-chr.lua')
         loadmodule('luatex-fonts-lua.lua')
         loadmodule('font-def.lua')
         loadmodule('luatex-fonts-def.lua')
-- 
cgit v1.2.3