From 35b2a3dcf659cafd035db36393bd178bced1dabd Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Sat, 30 Jul 2016 11:08:03 +0200
Subject: [fontloader] sync with Context as of 2016-07-30

Hans appears to have heard our pleas =)
---
 src/fontloader/misc/fontloader-font-con.lua     | 358 +++++++++-----
 src/fontloader/misc/fontloader-font-one.lua     | 110 ++---
 src/fontloader/misc/fontloader-font-otl.lua     | 180 ++-----
 src/fontloader/misc/fontloader-font-ots.lua     | 158 ++++--
 src/fontloader/misc/fontloader-font-tfm.lua     |  44 +-
 src/fontloader/runtime/fontloader-reference.lua | 626 +++++++++++++-----------
 6 files changed, 799 insertions(+), 677 deletions(-)

(limited to 'src')

diff --git a/src/fontloader/misc/fontloader-font-con.lua b/src/fontloader/misc/fontloader-font-con.lua
index 9a6f3f8..931e4e7 100644
--- a/src/fontloader/misc/fontloader-font-con.lua
+++ b/src/fontloader/misc/fontloader-font-con.lua
@@ -12,9 +12,10 @@ local next, tostring, rawget = next, tostring, rawget
 local format, match, lower, gsub, find = string.format, string.match, string.lower, string.gsub, string.find
 local sort, insert, concat, sortedkeys, serialize, fastcopy = table.sort, table.insert, table.concat, table.sortedkeys, table.serialize, table.fastcopy
 local derivetable = table.derive
+local ioflush = io.flush
 
-local trace_defining = false  trackers.register("fonts.defining", function(v) trace_defining = v end)
-local trace_scaling  = false  trackers.register("fonts.scaling" , function(v) trace_scaling  = v end)
+local trace_defining  = false  trackers.register("fonts.defining",  function(v) trace_defining = v end)
+local trace_scaling   = false  trackers.register("fonts.scaling",   function(v) trace_scaling  = v end)
 
 local report_defining = logs.reporter("fonts","defining")
 
@@ -1086,146 +1087,269 @@ setmetatableindex(formats, function(t,k)
     return rawget(t,file.suffix(l))
 end)
 
-local locations = { }
+do
 
-local function setindeed(mode,target,group,name,action,position)
-    local t = target[mode]
-    if not t then
-        report_defining("fatal error in setting feature %a, group %a, mode %a",name,group,mode)
-        os.exit()
-    elseif position then
-        -- todo: remove existing
-        insert(t, position, { name = name, action = action })
-    else
-        for i=1,#t do
-            local ti = t[i]
-            if ti.name == name then
-                ti.action = action
-                return
+    local function setindeed(mode,target,group,name,action,position)
+        local t = target[mode]
+        if not t then
+            report_defining("fatal error in setting feature %a, group %a, mode %a",name,group,mode)
+            os.exit()
+        elseif position then
+            -- todo: remove existing
+            insert(t, position, { name = name, action = action })
+        else
+            for i=1,#t do
+                local ti = t[i]
+                if ti.name == name then
+                    ti.action = action
+                    return
+                end
             end
+            insert(t, { name = name, action = action })
         end
-        insert(t, { name = name, action = action })
-    end
-end
-
-local function set(group,name,target,source)
-    target = target[group]
-    if not target then
-        report_defining("fatal target error in setting feature %a, group %a",name,group)
-        os.exit()
-    end
-    local source = source[group]
-    if not source then
-        report_defining("fatal source error in setting feature %a, group %a",name,group)
-        os.exit()
-    end
-    local node     = source.node
-    local base     = source.base
-    local position = source.position
-    if node then
-        setindeed("node",target,group,name,node,position)
     end
-    if base then
-        setindeed("base",target,group,name,base,position)
-    end
-end
 
-local function register(where,specification)
-    local name = specification.name
-    if name and name ~= "" then
-        local default      = specification.default
-        local description  = specification.description
-        local initializers = specification.initializers
-        local processors   = specification.processors
-        local manipulators = specification.manipulators
-        local modechecker  = specification.modechecker
-        if default then
-            where.defaults[name] = default
+    local function set(group,name,target,source)
+        target = target[group]
+        if not target then
+            report_defining("fatal target error in setting feature %a, group %a",name,group)
+            os.exit()
         end
-        if description and description ~= "" then
-            where.descriptions[name] = description
+        local source = source[group]
+        if not source then
+            report_defining("fatal source error in setting feature %a, group %a",name,group)
+            os.exit()
         end
-        if initializers then
-            set('initializers',name,where,specification)
+        local node     = source.node
+        local base     = source.base
+        local position = source.position
+        if node then
+            setindeed("node",target,group,name,node,position)
         end
-        if processors then
-            set('processors',  name,where,specification)
-        end
-        if manipulators then
-            set('manipulators',name,where,specification)
+        if base then
+            setindeed("base",target,group,name,base,position)
         end
-        if modechecker then
-           where.modechecker = modechecker
+    end
+
+    local function register(where,specification)
+        local name = specification.name
+        if name and name ~= "" then
+            local default      = specification.default
+            local description  = specification.description
+            local initializers = specification.initializers
+            local processors   = specification.processors
+            local manipulators = specification.manipulators
+            local modechecker  = specification.modechecker
+            if default then
+                where.defaults[name] = default
+            end
+            if description and description ~= "" then
+                where.descriptions[name] = description
+            end
+            if initializers then
+                set('initializers',name,where,specification)
+            end
+            if processors then
+                set('processors',  name,where,specification)
+            end
+            if manipulators then
+                set('manipulators',name,where,specification)
+            end
+            if modechecker then
+               where.modechecker = modechecker
+            end
         end
     end
-end
 
-constructors.registerfeature = register
-
-function constructors.getfeatureaction(what,where,mode,name)
-    what = handlers[what].features
-    if what then
-        where = what[where]
-        if where then
-            mode = where[mode]
-            if mode then
-                for i=1,#mode do
-                    local m = mode[i]
-                    if m.name == name then
-                        return m.action
+    constructors.registerfeature = register
+
+    function constructors.getfeatureaction(what,where,mode,name)
+        what = handlers[what].features
+        if what then
+            where = what[where]
+            if where then
+                mode = where[mode]
+                if mode then
+                    for i=1,#mode do
+                        local m = mode[i]
+                        if m.name == name then
+                            return m.action
+                        end
                     end
                 end
             end
         end
     end
+
+    local newfeatures        = { }
+    constructors.newfeatures = newfeatures -- downward compatible
+    constructors.features    = newfeatures
+
+    local function setnewfeatures(what)
+        local handler  = handlers[what]
+        local features = handler.features
+        if not features then
+            local tables     = handler.tables     -- can be preloaded
+            local statistics = handler.statistics -- can be preloaded
+            features = allocate {
+                defaults     = { },
+                descriptions = tables and tables.features or { },
+                used         = statistics and statistics.usedfeatures or { },
+                initializers = { base = { }, node = { } },
+                processors   = { base = { }, node = { } },
+                manipulators = { base = { }, node = { } },
+            }
+            features.register = function(specification) return register(features,specification) end
+            handler.features = features -- will also become hidden
+        end
+        return features
+    end
+
+    setmetatable(newfeatures, {
+        __call  = function(t,k) local v = t[k] return v end,
+        __index = function(t,k) local v = setnewfeatures(k) t[k] = v return v end,
+    })
+
 end
 
-local newhandler        = { }
-constructors.handlers   = newhandler -- downward compatible
-constructors.newhandler = newhandler
+do
 
-local function setnewhandler(what) -- could be a metatable newindex
-    local handler = handlers[what]
-    if not handler then
-        handler = { }
-        handlers[what] = handler
+    local newhandler        = { }
+    constructors.handlers   = newhandler -- downward compatible
+    constructors.newhandler = newhandler
+
+    local function setnewhandler(what) -- could be a metatable newindex
+        local handler = handlers[what]
+        if not handler then
+            handler = { }
+            handlers[what] = handler
+        end
+        return handler
     end
-    return handler
+
+    setmetatable(newhandler, {
+        __call  = function(t,k) local v = t[k] return v end,
+        __index = function(t,k) local v = setnewhandler(k) t[k] = v return v end,
+    })
+
 end
 
-setmetatable(newhandler, {
-    __call  = function(t,k) local v = t[k] return v end,
-    __index = function(t,k) local v = setnewhandler(k) t[k] = v return v end,
-})
-
-local newfeatures        = { }
-constructors.newfeatures = newfeatures -- downward compatible
-constructors.features    = newfeatures
-
-local function setnewfeatures(what)
-    local handler = handlers[what]
-    local features = handler.features
-    if not features then
-        local tables     = handler.tables     -- can be preloaded
-        local statistics = handler.statistics -- can be preloaded
-        features = allocate {
-            defaults     = { },
-            descriptions = tables and tables.features or { },
-            used         = statistics and statistics.usedfeatures or { },
-            initializers = { base = { }, node = { } },
-            processors   = { base = { }, node = { } },
-            manipulators = { base = { }, node = { } },
-        }
-        features.register = function(specification) return register(features,specification) end
-        handler.features = features -- will also become hidden
+do
+    -- a pitty that we need to be generic as we have nicer mechanisms for this ...
+
+    local newenhancer        = { }
+    constructors.enhancers   = newenhancer
+    constructors.newenhancer = newenhancer
+
+    local function setnewenhancer(format)
+
+        local handler   = handlers[format]
+        local enhancers = handler.enhancers
+
+        if not enhancers then
+
+            local actions = allocate()
+            local before  = allocate()
+            local after   = allocate()
+            local order   = allocate()
+            local patches = { before = before, after = after }
+
+            local trace   = false
+            local report  = logs.reporter("fonts",format .. " enhancing")
+
+            trackers.register(format .. ".loading", function(v) trace = v end)
+
+            local function enhance(name,data,filename,raw)
+                local enhancer = actions[name]
+                if enhancer then
+                    if trace then
+                        report("apply enhancement %a to file %a",name,filename)
+                        ioflush()
+                    end
+                    enhancer(data,filename,raw)
+                else
+                    -- no message as we can have private ones
+                end
+            end
+
+            local function apply(data,filename,raw)
+                local basename = file.basename(lower(filename))
+                if trace then
+                    report("%s enhancing file %a","start",filename)
+                end
+                ioflush() -- we want instant messages
+                for e=1,#order do
+                    local enhancer = order[e]
+                    local b = before[enhancer]
+                    if b then
+                        for pattern, action in next, b do
+                            if find(basename,pattern) then
+                                action(data,filename,raw)
+                            end
+                        end
+                    end
+                    enhance(enhancer,data,filename,raw)
+                    local a = after[enhancer]
+                    if a then
+                        for pattern, action in next, a do
+                            if find(basename,pattern) then
+                                action(data,filename,raw)
+                            end
+                        end
+                    end
+                    ioflush() -- we want instant messages
+                end
+                if trace then
+                    report("%s enhancing file %a","stop",filename)
+                end
+                ioflush() -- we want instant messages
+            end
+
+            local function register(what,action)
+                if action then
+                    if actions[what] then
+                        -- overloading, e.g."check extra features"
+                    else
+                        order[#order+1] = what
+                    end
+                    actions[what] = action
+                else
+                    report("bad enhancer %a",what)
+                end
+            end
+
+            -- fonts.constructors.otf.enhancers.patch("before","migrate metadata","cambria",function() end)
+
+            local function patch(what,where,pattern,action)
+                local pw = patches[what]
+                if pw then
+                    local ww = pw[where]
+                    if ww then
+                        ww[pattern] = action
+                    else
+                        pw[where] = { [pattern] = action}
+                    end
+                end
+            end
+
+            enhancers = {
+                register = register,
+                apply    = apply,
+                patch    = patch,
+                patches  = { register = patch }, -- for old times sake
+            }
+
+            handler.enhancers = enhancers
+        end
+        return enhancers
     end
-    return features
-end
 
-setmetatable(newfeatures, {
-    __call  = function(t,k) local v = t[k] return v end,
-    __index = function(t,k) local v = setnewfeatures(k) t[k] = v return v end,
-})
+    setmetatable(newenhancer, {
+        __call  = function(t,k) local v = t[k] return v end,
+        __index = function(t,k) local v = setnewenhancer(k) t[k] = v return v end,
+    })
+
+end
 
 --[[ldx--
 <p>We need to check for default features. For this we provide
diff --git a/src/fontloader/misc/fontloader-font-one.lua b/src/fontloader/misc/fontloader-font-one.lua
index 8629850..d9b9c65 100644
--- a/src/fontloader/misc/fontloader-font-one.lua
+++ b/src/fontloader/misc/fontloader-font-one.lua
@@ -29,42 +29,45 @@ local bxor, rshift = bit32.bxor, bit32.rshift
 local P, S, R, Cmt, C, Ct, Cs, Carg = lpeg.P, lpeg.S, lpeg.R, lpeg.Cmt, lpeg.C, lpeg.Ct, lpeg.Cs, lpeg.Carg
 local lpegmatch, patterns = lpeg.match, lpeg.patterns
 
-local trace_features     = false  trackers.register("afm.features",   function(v) trace_features = v end)
-local trace_indexing     = false  trackers.register("afm.indexing",   function(v) trace_indexing = v end)
-local trace_loading      = false  trackers.register("afm.loading",    function(v) trace_loading  = v end)
-local trace_defining     = false  trackers.register("fonts.defining", function(v) trace_defining = v end)
+local trace_features      = false  trackers.register("afm.features",   function(v) trace_features = v end)
+local trace_indexing      = false  trackers.register("afm.indexing",   function(v) trace_indexing = v end)
+local trace_loading       = false  trackers.register("afm.loading",    function(v) trace_loading  = v end)
+local trace_defining      = false  trackers.register("fonts.defining", function(v) trace_defining = v end)
 
-local report_afm         = logs.reporter("fonts","afm loading")
+local report_afm          = logs.reporter("fonts","afm loading")
 
-local setmetatableindex  = table.setmetatableindex
-local derivetable        = table.derive
+local setmetatableindex   = table.setmetatableindex
+local derivetable         = table.derive
 
-local findbinfile        = resolvers.findbinfile
+local findbinfile         = resolvers.findbinfile
 
-local definers           = fonts.definers
-local readers            = fonts.readers
-local constructors       = fonts.constructors
+local definers            = fonts.definers
+local readers             = fonts.readers
+local constructors        = fonts.constructors
 
-local afm                = constructors.handlers.afm
-local pfb                = constructors.handlers.pfb
-local otf                = fonts.handlers.otf
+local afm                 = constructors.handlers.afm
+local pfb                 = constructors.handlers.pfb
+local otf                 = fonts.handlers.otf
 
-local otfreaders         = otf.readers
-local otfenhancers       = otf.enhancers
+local otfreaders          = otf.readers
+local otfenhancers        = otf.enhancers
 
-local afmfeatures        = constructors.features.afm
-local registerafmfeature = afmfeatures.register
+local afmfeatures         = constructors.features.afm
+local registerafmfeature  = afmfeatures.register
 
-afm.version              = 1.512 -- incrementing this number one up will force a re-cache
-afm.cache                = containers.define("fonts", "afm", afm.version, true)
-afm.autoprefixed         = true -- this will become false some day (catches texnansi-blabla.*)
+local afmenhancers        = constructors.enhancers.afm
+local registerafmenhancer = afmenhancers.register
 
-afm.helpdata             = { }  -- set later on so no local for this
-afm.syncspace            = true -- when true, nicer stretch values
+afm.version               = 1.512 -- incrementing this number one up will force a re-cache
+afm.cache                 = containers.define("fonts", "one", afm.version, true)
+afm.autoprefixed          = true -- this will become false some day (catches texnansi-blabla.*)
 
-local overloads          = fonts.mappings.overloads
+afm.helpdata              = { }  -- set later on so no local for this
+afm.syncspace             = true -- when true, nicer stretch values
 
-local applyruntimefixes  = fonts.treatments and fonts.treatments.applyfixes
+local overloads           = fonts.mappings.overloads
+
+local applyruntimefixes   = fonts.treatments and fonts.treatments.applyfixes
 
 --[[ldx--
 <p>We cache files. Caching is taken care of in the loader. We cheat a bit by adding
@@ -76,36 +79,6 @@ fashion and later we transform it to sequences. Then we apply some methods also
 used in opentype fonts (like <t>tlig</t>).</p>
 --ldx]]--
 
-local enhancers = {
-    -- It's cleaner to implement them after we've seen what we are
-    -- dealing with.
-}
-
-local steps     = {
-    "unify names",
-    "add ligatures",
-    "add extra kerns",
-    "normalize features",
-    "check extra features",
-    "fix names", -- what a hack ...
---  "add tounicode data",
-}
-
-local function applyenhancers(data,filename)
-    for i=1,#steps do
-        local step     = steps[i]
-        local enhancer = enhancers[step]
-        if enhancer then
-            if trace_loading then
-                report_afm("applying enhancer %a",step)
-            end
-            enhancer(data,filename)
-        else
-            report_afm("invalid enhancer %a",step)
-        end
-    end
-end
-
 function afm.load(filename)
     filename = resolvers.findfile(filename,'afm') or ""
     if filename ~= "" and not fonts.names.ignoredfile(filename) then
@@ -129,7 +102,7 @@ function afm.load(filename)
             report_afm("reading %a",filename)
             data = afm.readers.loadfont(filename,pfbname)
             if data then
-                applyenhancers(data,filename)
+                afmenhancers.apply(data,filename)
              -- otfreaders.addunicodetable(data) -- only when not done yet
                 fonts.mappings.addtounicode(data,filename)
              -- otfreaders.extend(data)
@@ -162,7 +135,7 @@ end
 
 local uparser = fonts.mappings.makenameparser() -- each time
 
-enhancers["unify names"] = function(data, filename)
+local function enhance_unify_names(data, filename)
     local unicodevector = fonts.encodings.agl.unicodes -- loaded runtime in context
     local unicodes      = { }
     local names         = { }
@@ -218,7 +191,7 @@ end
 local everywhere = { ["*"] = { ["*"] = true } } -- or: { ["*"] = { "*" } }
 local noflags    = { false, false, false, false }
 
-enhancers["normalize features"] = function(data)
+local function enhance_normalize_features(data)
     local ligatures  = setmetatableindex("table")
     local kerns      = setmetatableindex("table")
     local extrakerns = setmetatableindex("table")
@@ -319,9 +292,7 @@ enhancers["normalize features"] = function(data)
     data.resources.sequences = sequences
 end
 
-enhancers["check extra features"] = otf.enhancers.enhance
-
-enhancers["fix names"] = function(data)
+local function enhance_fix_names(data)
     for k, v in next, data.descriptions do
         local n = v.name
         local r = overloads[n]
@@ -368,14 +339,10 @@ local addthem = function(rawdata,ligatures)
     end
 end
 
-enhancers["add ligatures"] = function(rawdata)
+local function enhance_add_ligatures(rawdata)
     addthem(rawdata,afm.helpdata.ligatures)
 end
 
--- enhancers["add tex ligatures"] = function(rawdata)
---     addthem(rawdata,afm.helpdata.texligatures)
--- end
-
 --[[ldx--
 <p>We keep the extra kerns in separate kerning tables so that we can use
 them selectively.</p>
@@ -388,7 +355,7 @@ them selectively.</p>
 -- we don't use the character database. (Ok, we can have a context specific
 -- variant).
 
-enhancers["add extra kerns"] = function(rawdata) -- using shcodes is not robust here
+local function enhance_add_extra_kerns(rawdata) -- using shcodes is not robust here
     local descriptions = rawdata.descriptions
     local resources    = rawdata.resources
     local unicodes     = resources.unicodes
@@ -851,3 +818,12 @@ function readers.pfb(specification,method) -- only called when forced
     swap("specification")
     return readers.afm(specification,method)
 end
+
+-- now we register them
+
+registerafmenhancer("unify names",          enhance_unify_names)
+registerafmenhancer("add ligatures",        enhance_add_ligatures)
+registerafmenhancer("add extra kerns",      enhance_add_extra_kerns)
+registerafmenhancer("normalize features",   enhance_normalize_features)
+registerafmenhancer("check extra features", otfenhancers.enhance)
+registerafmenhancer("fix names",            enhance_fix_names)
diff --git a/src/fontloader/misc/fontloader-font-otl.lua b/src/fontloader/misc/fontloader-font-otl.lua
index bdce80d..94f6a45 100644
--- a/src/fontloader/misc/fontloader-font-otl.lua
+++ b/src/fontloader/misc/fontloader-font-otl.lua
@@ -26,70 +26,67 @@ if not modules then modules = { } end modules ['font-otl'] = {
 local gmatch, find, match, lower, strip = string.gmatch, string.find, string.match, string.lower, string.strip
 local type, next, tonumber, tostring, unpack = type, next, tonumber, tostring, unpack
 local abs = math.abs
-local ioflush = io.flush
 local derivetable = table.derive
 local formatters = string.formatters
 
-local setmetatableindex  = table.setmetatableindex
-local allocate           = utilities.storage.allocate
-local registertracker    = trackers.register
-local registerdirective  = directives.register
-local starttiming        = statistics.starttiming
-local stoptiming         = statistics.stoptiming
-local elapsedtime        = statistics.elapsedtime
-local findbinfile        = resolvers.findbinfile
+local setmetatableindex   = table.setmetatableindex
+local allocate            = utilities.storage.allocate
+local registertracker     = trackers.register
+local registerdirective   = directives.register
+local starttiming         = statistics.starttiming
+local stoptiming          = statistics.stoptiming
+local elapsedtime         = statistics.elapsedtime
+local findbinfile         = resolvers.findbinfile
 
------ trace_private      = false  registertracker("otf.private",        function(v) trace_private   = v end)
------ trace_subfonts     = false  registertracker("otf.subfonts",       function(v) trace_subfonts  = v end)
-local trace_loading      = false  registertracker("otf.loading",        function(v) trace_loading   = v end)
-local trace_features     = false  registertracker("otf.features",       function(v) trace_features  = v end)
------ trace_dynamics     = false  registertracker("otf.dynamics",       function(v) trace_dynamics  = v end)
------ trace_sequences    = false  registertracker("otf.sequences",      function(v) trace_sequences = v end)
------ trace_markwidth    = false  registertracker("otf.markwidth",      function(v) trace_markwidth = v end)
-local trace_defining     = false  registertracker("fonts.defining",     function(v) trace_defining  = v end)
+----- trace_private       = false  registertracker("otf.private",        function(v) trace_private   = v end)
+----- trace_subfonts      = false  registertracker("otf.subfonts",       function(v) trace_subfonts  = v end)
+local trace_loading       = false  registertracker("otf.loading",        function(v) trace_loading   = v end)
+local trace_features      = false  registertracker("otf.features",       function(v) trace_features  = v end)
+----- trace_dynamics      = false  registertracker("otf.dynamics",       function(v) trace_dynamics  = v end)
+----- trace_sequences     = false  registertracker("otf.sequences",      function(v) trace_sequences = v end)
+----- trace_markwidth     = false  registertracker("otf.markwidth",      function(v) trace_markwidth = v end)
+local trace_defining      = false  registertracker("fonts.defining",     function(v) trace_defining  = v end)
 
-local report_otf         = logs.reporter("fonts","otf loading")
+local report_otf          = logs.reporter("fonts","otf loading")
 
-local fonts              = fonts
-local otf                = fonts.handlers.otf
+local fonts               = fonts
+local otf                 = fonts.handlers.otf
 
-otf.version              = 3.025 -- beware: also sync font-mis.lua and in mtx-fonts
-otf.cache                = containers.define("fonts", "otl", otf.version, true)
-otf.svgcache             = containers.define("fonts", "svg", otf.version, true)
-otf.pdfcache             = containers.define("fonts", "pdf", otf.version, true)
+otf.version               = 3.025 -- beware: also sync font-mis.lua and in mtx-fonts
+otf.cache                 = containers.define("fonts", "otl", otf.version, true)
+otf.svgcache              = containers.define("fonts", "svg", otf.version, true)
+otf.pdfcache              = containers.define("fonts", "pdf", otf.version, true)
 
-otf.svgenabled           = false
+otf.svgenabled            = false
 
-local otfreaders         = otf.readers
+local otfreaders          = otf.readers
 
-local hashes             = fonts.hashes
-local definers           = fonts.definers
-local readers            = fonts.readers
-local constructors       = fonts.constructors
+local hashes              = fonts.hashes
+local definers            = fonts.definers
+local readers             = fonts.readers
+local constructors        = fonts.constructors
 
-local otffeatures        = constructors.features.otf
-local registerotffeature = otffeatures.register
+local otffeatures         = constructors.features.otf
+local registerotffeature  = otffeatures.register
 
-local enhancers          = allocate()
-otf.enhancers            = enhancers
-local patches            = { }
-enhancers.patches        = patches
+local otfenhancers        = constructors.enhancers.otf
+local registerotfenhancer = otfenhancers.register
 
-local forceload          = false
-local cleanup            = 0     -- mk: 0=885M 1=765M 2=735M (regular run 730M)
-local syncspace          = true
-local forcenotdef        = false
+local forceload           = false
+local cleanup             = 0     -- mk: 0=885M 1=765M 2=735M (regular run 730M)
+local syncspace           = true
+local forcenotdef         = false
 
-local applyruntimefixes  = fonts.treatments and fonts.treatments.applyfixes
+local applyruntimefixes   = fonts.treatments and fonts.treatments.applyfixes
 
-local wildcard           = "*"
-local default            = "dflt"
+local wildcard            = "*"
+local default             = "dflt"
 
-local formats            = fonts.formats
+local formats             = fonts.formats
 
-formats.otf              = "opentype"
-formats.ttf              = "truetype"
-formats.ttc              = "truetype"
+formats.otf               = "opentype"
+formats.ttf               = "truetype"
+formats.ttc               = "truetype"
 
 registerdirective("fonts.otf.loader.cleanup",       function(v) cleanup       = tonumber(v) or (v and 1) or 0 end)
 registerdirective("fonts.otf.loader.force",         function(v) forceload     = v end)
@@ -105,92 +102,9 @@ registerdirective("fonts.otf.loader.forcenotdef",   function(v) forcenotdef   =
 --     end
 -- end
 
--- Enhancers are used to apply fixes and extensions to fonts. For instance, we use them
--- to implement tlig and trep features. They are not neccessarily bound to opentype
--- fonts but can also apply to type one fonts, given that they obey the structure of an
--- opentype font. They are not to be confused with format specific features but maybe
--- some are so generic that they might eventually move to this mechanism.
+-- otfenhancers.patch("before","migrate metadata","cambria",function() end)
 
-local ordered_enhancers = {
-    "check extra features",
-}
-
-local actions  = allocate()
-local before   = allocate()
-local after    = allocate()
-
-patches.before = before
-patches.after  = after
-
-local function enhance(name,data,filename,raw)
-    local enhancer = actions[name]
-    if enhancer then
-        if trace_loading then
-            report_otf("apply enhancement %a to file %a",name,filename)
-            ioflush()
-        end
-        enhancer(data,filename,raw)
-    else
-        -- no message as we can have private ones
-    end
-end
-
-function enhancers.apply(data,filename,raw)
-    local basename = file.basename(lower(filename))
-    if trace_loading then
-        report_otf("%s enhancing file %a","start",filename)
-    end
-    ioflush() -- we want instant messages
-    for e=1,#ordered_enhancers do
-        local enhancer = ordered_enhancers[e]
-        local b = before[enhancer]
-        if b then
-            for pattern, action in next, b do
-                if find(basename,pattern) then
-                    action(data,filename,raw)
-                end
-            end
-        end
-        enhance(enhancer,data,filename,raw)
-        local a = after[enhancer]
-        if a then
-            for pattern, action in next, a do
-                if find(basename,pattern) then
-                    action(data,filename,raw)
-                end
-            end
-        end
-        ioflush() -- we want instant messages
-    end
-    if trace_loading then
-        report_otf("%s enhancing file %a","stop",filename)
-    end
-    ioflush() -- we want instant messages
-end
-
--- patches.register("before","migrate metadata","cambria",function() end)
-
-function patches.register(what,where,pattern,action)
-    local pw = patches[what]
-    if pw then
-        local ww = pw[where]
-        if ww then
-            ww[pattern] = action
-        else
-            pw[where] = { [pattern] = action}
-        end
-    end
-end
-
-function patches.report(fmt,...)
-    if trace_loading then
-        report_otf("patching: %s",formatters[fmt](...))
-    end
-end
-
-function enhancers.register(what,action) -- only already registered can be overloaded
-    actions[what] = action
-end
+registerotfenhancer("check extra features", function() end) -- placeholder
 
 function otf.load(filename,sub,featurefile) -- second argument (format) is gone !
     --
@@ -329,7 +243,7 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone
         otfreaders.expand(data) -- inline tables
         otfreaders.addunicodetable(data) -- only when not done yet
         --
-        enhancers.apply(data,filename,data)
+        otfenhancers.apply(data,filename,data)
         --
      -- constructors.addcoreunicodes(data.resources.unicodes) -- still needed ?
         --
diff --git a/src/fontloader/misc/fontloader-font-ots.lua b/src/fontloader/misc/fontloader-font-ots.lua
index 10f0518..d371156 100644
--- a/src/fontloader/misc/fontloader-font-ots.lua
+++ b/src/fontloader/misc/fontloader-font-ots.lua
@@ -275,6 +275,13 @@ local checkstep       = (nodes and nodes.tracers and nodes.tracers.steppers.chec
 local registerstep    = (nodes and nodes.tracers and nodes.tracers.steppers.register) or function() end
 local registermessage = (nodes and nodes.tracers and nodes.tracers.steppers.message)  or function() end
 
+local function checkdisccontent(d)
+    local pre, post, replace = getdisc(d)
+    if pre     then for n in traverse_id(glue_code,pre)     do print("pre",nodes.idstostring(pre))     break end end
+    if post    then for n in traverse_id(glue_code,post)    do print("pos",nodes.idstostring(post))    break end end
+    if replace then for n in traverse_id(glue_code,replace) do print("rep",nodes.idstostring(replace)) break end end
+end
+
 local function logprocess(...)
     if trace_steps then
         registermessage(...)
@@ -351,7 +358,7 @@ end
 local function copy_glyph(g) -- next and prev are untouched !
     local components = getfield(g,"components")
     if components then
-        setfield(g,"components",nil)
+        setfield(g,"components")
         local n = copy_node(g)
         copyinjection(n,g) -- we need to preserve the lig indices
         setfield(g,"components",components)
@@ -364,11 +371,18 @@ local function copy_glyph(g) -- next and prev are untouched !
 end
 
 local function flattendisk(head,disc)
-    local _, _, replace, _, _, replacetail = getdisc(disc,true)
-    setfield(disc,"replace",nil)
+    local pre, post, replace, pretail, posttail, replacetail = getdisc(disc,true)
+    local prev, next = getboth(disc)
+    local ishead = head == disc
+    setdisc(disc)
     flush_node(disc)
-    if head == disc then
-        local next = getnext(disc)
+    if pre then
+        flush_node_list(pre)
+    end
+    if post then
+        flush_node_list(post)
+    end
+    if ishead then
         if replace then
             if next then
                 setlink(replacetail,next)
@@ -380,7 +394,6 @@ local function flattendisk(head,disc)
             return -- maybe warning
         end
     else
-        local prev, next = getboth(disc)
         if replace then
             if next then
                 setlink(replacetail,next)
@@ -419,8 +432,8 @@ local function markstoligature(head,start,stop,char)
     else
         local prev = getprev(start)
         local next = getnext(stop)
-        setprev(start,nil)
-        setnext(stop,nil)
+        setprev(start)
+        setnext(stop)
         local base = copy_glyph(start)
         if head == start then
             head = base
@@ -482,8 +495,8 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou
     local prev = getprev(start)
     local next = getnext(stop)
     local comp = start
-    setprev(start,nil)
-    setnext(stop,nil)
+    setprev(start)
+    setnext(stop)
     local base = copy_glyph(start)
     if start == head then
         head = base
@@ -553,38 +566,37 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou
             -- anyway
             local pre, post, replace, pretail, posttail, replacetail = getdisc(discfound,true)
             if not replace then -- todo: signal simple hyphen
-                local prev = getprev(base)
---                 local copied = copy_node_list(comp)
-local current  = comp
-local previous = nil
-local copied   = nil
-while current do
-    if getid(current) == glyph_code then
-        local n = copy_node(current)
-        if copied then
-            setlink(previous,n)
-        else
-            copied = n
-        end
-        previous = n
-    end
-    current = getnext(current)
-end
-                setprev(discnext,nil) -- also blocks funny assignments
-                setnext(discprev,nil) -- also blocks funny assignments
+                local prev     = getprev(base)
+                local current  = comp
+                local previous = nil
+                local copied   = nil
+                while current do
+                    if getid(current) == glyph_code then
+                        local n = copy_node(current)
+                        if copied then
+                            setlink(previous,n)
+                        else
+                            copied = n
+                        end
+                        previous = n
+                    end
+                    current = getnext(current)
+                end
+                setprev(discnext) -- also blocks funny assignments
+                setnext(discprev) -- also blocks funny assignments
                 if pre then
                     setlink(discprev,pre)
                 end
                 pre = comp
                 if post then
                     setlink(posttail,discnext)
-                    setprev(post,nil)
+                    setprev(post)
                 else
                     post = discnext
                 end
                 setlink(prev,discfound)
                 setlink(discfound,next)
-                setboth(base,nil,nil)
+                setboth(base)
                 setfield(base,"components",copied)
                 setdisc(discfound,pre,post,base,discretionary_code)
                 base = prev -- restart
@@ -1751,6 +1763,42 @@ end
 -- order to handle that we need more complex code which also slows down even more. The main
 -- loop variant could deal with that: test, collapse, backtrack.
 
+local new_kern = nuts.pool.kern
+
+local function checked(head)
+    local current = head
+    while current do
+        if getid(current) == glue_code then
+            local kern = new_kern(getfield(current,"width"))
+            if head == current then
+                local next = getnext(current)
+                if next then
+                    setlink(kern,next)
+                end
+                flush_node(current)
+                head    = kern
+                current = next
+            else
+                local prev, next = getboth(current)
+                setlink(prev,kern)
+                setlink(kern,next)
+                flush_node(current)
+                current = next
+            end
+        else
+            current = getnext(current)
+        end
+    end
+    return head
+end
+
+local function setdiscchecked(d,pre,post,replace)
+    if pre     then pre     = checked(pre)     end
+    if post    then post    = checked(post)    end
+    if replace then replace = checked(replace) end
+    setdisc(d,pre,post,replace)
+end
+
 local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,ck,chainproc)
 
     if not start then
@@ -1773,8 +1821,10 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c
     local current       = start
     local last          = start
     local prev          = getprev(start)
+    local hasglue       = false
 
     -- fishy: so we can overflow and then go on in the sweep?
+    -- todo : id can also be glue_code as we checked spaces
 
     local i = f
     while i <= l do
@@ -1783,6 +1833,11 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c
             i       = i + 1
             last    = current
             current = getnext(current)
+        elseif id == glue_code then
+            i       = i + 1
+            last    = current
+            current = getnext(current)
+            hasglue = true
         elseif id == disc_code then
             if keepdisc then
                 keepdisc = false
@@ -1835,8 +1890,8 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c
                 tail = find_node_tail(head)
             end
             setnext(sweepnode,current)
-            setprev(head,nil)
-            setnext(tail,nil)
+            setprev(head)
+            setnext(tail)
             appenddisc(sweepnode,head)
         end
     end
@@ -1849,6 +1904,10 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c
             if id == glyph_code then
                 i       = i + 1
                 current = getnext(current)
+            elseif id == glue_code then
+                i       = i + 1
+                current = getnext(current)
+                hasglue = true
             elseif id == disc_code then
                 if keepdisc then
                     keepdisc = false
@@ -1891,6 +1950,9 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c
             local id = getid(current)
             if id == glyph_code then
                 i = i - 1
+            elseif id == glue_code then
+                i       = i - 1
+                hasglue = true
             elseif id == disc_code then
                 if keepdisc then
                     keepdisc = false
@@ -1939,8 +2001,8 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c
         if cprev then
             setnext(cprev,lookaheaddisc)
         end
-        setprev(cf,nil)
-        setnext(cl,nil)
+        setprev(cf)
+        setnext(cl)
         if startishead then
             head = lookaheaddisc
         end
@@ -1967,7 +2029,11 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c
             local tail = find_node_tail(new)
             setlink(tail,replace)
         end
-        setdisc(lookaheaddisc,cf,post,new)
+        if hasglue then
+            setdiscchecked(lookaheaddisc,cf,post,new)
+        else
+            setdisc(lookaheaddisc,cf,post,new)
+        end
         start          = getprev(lookaheaddisc)
         sweephead[cf]  = getnext(clast)
         sweephead[new] = getnext(last)
@@ -1993,8 +2059,8 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c
             setprev(cnext,backtrackdisc)
         end
         setnext(backtrackdisc,cnext)
-        setprev(cf,nil)
-        setnext(cl,nil)
+        setprev(cf)
+        setnext(cl)
         local pre, post, replace, pretail, posttail, replacetail = getdisc(backtrackdisc,true)
         local new  = copy_node_list(cf)
         local cnew = find_node_tail(new)
@@ -2021,7 +2087,11 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c
         else
             replace = new
         end
-        setdisc(backtrackdisc,pre,post,replace)
+        if hasglue then
+            setdiscchecked(backtrackdisc,pre,post,replace)
+        else
+            setdisc(backtrackdisc,pre,post,replace)
+        end
         start              = getprev(backtrackdisc)
         sweephead[post]    = getnext(clast)
         sweephead[replace] = getnext(last)
@@ -2744,7 +2814,7 @@ local function kernrun(disc,k_run,font,attr,...)
                 done = true
             end
             setprev(pre,nest)
--- setprev(pre,nil)
+-- setprev(pre)
             setnext(prev,disc)
         end
     end
@@ -2758,7 +2828,7 @@ local function kernrun(disc,k_run,font,attr,...)
             if k_run(posttail,"postinjections",next,font,attr,...) then
                 done = true
             end
-            setnext(posttail,nil)
+            setnext(posttail)
             setprev(next,disc)
         end
     end
@@ -2774,7 +2844,7 @@ local function kernrun(disc,k_run,font,attr,...)
                 done = true
             end
             setprev(replace,nest)
--- setprev(replace,nil)
+-- setprev(replace)
             setnext(prev,disc)
         end
         if next then
@@ -2782,7 +2852,7 @@ local function kernrun(disc,k_run,font,attr,...)
             if k_run(replacetail,"replaceinjections",next,font,attr,...) then
                 done = true
             end
-            setnext(replacetail,nil)
+            setnext(replacetail)
             setprev(next,disc)
         end
     elseif prev and next then
@@ -2862,7 +2932,7 @@ local function testrun(disc,t_run,c_run,...)
         local ok, overflow = t_run(replace,next,...)
         if ok and overflow then
             -- so, we can have crossed the boundary
-            setfield(disc,"replace",nil)
+            setfield(disc,"replace")
             setlink(prev,replace)
          -- setlink(replacetail,next)
             setboth(disc)
diff --git a/src/fontloader/misc/fontloader-font-tfm.lua b/src/fontloader/misc/fontloader-font-tfm.lua
index 6565a0e..6584190 100644
--- a/src/fontloader/misc/fontloader-font-tfm.lua
+++ b/src/fontloader/misc/fontloader-font-tfm.lua
@@ -31,10 +31,14 @@ tfm.maxnestingdepth            = 5
 tfm.maxnestingsize             = 65536*1024
 
 local otf                      = fonts.handlers.otf
+local otfenhancers             = otf.enhancers
 
 local tfmfeatures              = constructors.features.tfm
 local registertfmfeature       = tfmfeatures.register
 
+local tfmenhancers             = constructors.enhancers.tfm
+local registertfmenhancer      = tfmenhancers.register
+
 constructors.resolvevirtualtoo = false -- wil be set in font-ctx.lua
 
 fonts.formats.tfm              = "type1" -- we need to have at least a value here
@@ -73,38 +77,7 @@ function tfm.setfeatures(tfmdata,features)
     end
 end
 
-local depth     = { } -- table.setmetatableindex("number")
-local enhancers = { }
-
-local steps     = {
-    "normalize features",
-    "check extra features"
-}
-
--- otf.enhancers.register("check extra features",enhance)
-
-enhancers["check extra features"] = otf.enhancers.enhance
-
---[[ PHG: begin hack for Luaotfload ]]--
-luaotfload_tfm_enhancers_reregister = function ()
-  enhancers["check extra features"]=otf.enhancers.enhance
-end
---[[ PHG: end hack for Luaotfload ]]--
-
-local function applyenhancers(data,filename)
-    for i=1,#steps do
-        local step     = steps[i]
-        local enhancer = enhancers[step]
-        if enhancer then
-            if trace_loading then
-                report_tfm("applying enhancer %a",step)
-            end
-            enhancer(data,filename)
-        else
-            report_tfm("invalid enhancer %a",step)
-        end
-    end
-end
+local depth = { } -- table.setmetatableindex("number")
 
 -- Normally we just load the tfm data and go on. However there was some demand for
 -- loading good old tfm /pfb files where afm files were lacking and even enc files
@@ -220,7 +193,7 @@ local function read_from_tfm(specification)
             --
             -- We make a pseudo opentype font, e.g. kerns and ligatures etc:
             --
-            applyenhancers(tfmdata,filename)
+            tfmenhancers.apply(tfmdata,filename)
             --
             -- Now user stuff can kick in.
             --
@@ -622,7 +595,7 @@ do
     local everywhere = { ["*"] = { ["*"] = true } } -- or: { ["*"] = { "*" } }
     local noflags    = { false, false, false, false }
 
-    enhancers["normalize features"] = function(data)
+    local function enhance_normalize_features(data)
         local ligatures  = setmetatableindex("table")
         local kerns      = setmetatableindex("table")
         local characters = data.characters
@@ -708,6 +681,9 @@ do
         data.shared.resources = data.shared.resources or resources
     end
 
+    registertfmenhancer("normalize features",   enhance_normalize_features)
+    registertfmenhancer("check extra features", otfenhancers.enhance)
+
 end
 
 -- As with type one (afm) loading, we just use the opentype ones:
diff --git a/src/fontloader/runtime/fontloader-reference.lua b/src/fontloader/runtime/fontloader-reference.lua
index 13c45aa..4cefe9a 100644
--- a/src/fontloader/runtime/fontloader-reference.lua
+++ b/src/fontloader/runtime/fontloader-reference.lua
@@ -1,6 +1,6 @@
 -- merged file : c:/data/develop/context/sources/luatex-fonts-merged.lua
 -- parent file : c:/data/develop/context/sources/luatex-fonts.lua
--- merge date  : 07/25/16 21:49:08
+-- merge date  : 07/30/16 00:26:47
 
 do -- begin closure to overcome local limits and interference
 
@@ -5722,6 +5722,7 @@ local next,tostring,rawget=next,tostring,rawget
 local format,match,lower,gsub,find=string.format,string.match,string.lower,string.gsub,string.find
 local sort,insert,concat,sortedkeys,serialize,fastcopy=table.sort,table.insert,table.concat,table.sortedkeys,table.serialize,table.fastcopy
 local derivetable=table.derive
+local ioflush=io.flush
 local trace_defining=false trackers.register("fonts.defining",function(v) trace_defining=v end)
 local trace_scaling=false trackers.register("fonts.scaling",function(v) trace_scaling=v end)
 local report_defining=logs.reporter("fonts","defining")
@@ -6547,134 +6548,233 @@ setmetatableindex(formats,function(t,k)
   end
   return rawget(t,file.suffix(l))
 end)
-local locations={}
-local function setindeed(mode,target,group,name,action,position)
-  local t=target[mode]
-  if not t then
-    report_defining("fatal error in setting feature %a, group %a, mode %a",name,group,mode)
-    os.exit()
-  elseif position then
-    insert(t,position,{ name=name,action=action })
-  else
-    for i=1,#t do
-      local ti=t[i]
-      if ti.name==name then
-        ti.action=action
-        return
+do
+  local function setindeed(mode,target,group,name,action,position)
+    local t=target[mode]
+    if not t then
+      report_defining("fatal error in setting feature %a, group %a, mode %a",name,group,mode)
+      os.exit()
+    elseif position then
+      insert(t,position,{ name=name,action=action })
+    else
+      for i=1,#t do
+        local ti=t[i]
+        if ti.name==name then
+          ti.action=action
+          return
+        end
       end
+      insert(t,{ name=name,action=action })
     end
-    insert(t,{ name=name,action=action })
   end
-end
-local function set(group,name,target,source)
-  target=target[group]
-  if not target then
-    report_defining("fatal target error in setting feature %a, group %a",name,group)
-    os.exit()
-  end
-  local source=source[group]
-  if not source then
-    report_defining("fatal source error in setting feature %a, group %a",name,group)
-    os.exit()
-  end
-  local node=source.node
-  local base=source.base
-  local position=source.position
-  if node then
-    setindeed("node",target,group,name,node,position)
-  end
-  if base then
-    setindeed("base",target,group,name,base,position)
-  end
-end
-local function register(where,specification)
-  local name=specification.name
-  if name and name~="" then
-    local default=specification.default
-    local description=specification.description
-    local initializers=specification.initializers
-    local processors=specification.processors
-    local manipulators=specification.manipulators
-    local modechecker=specification.modechecker
-    if default then
-      where.defaults[name]=default
+  local function set(group,name,target,source)
+    target=target[group]
+    if not target then
+      report_defining("fatal target error in setting feature %a, group %a",name,group)
+      os.exit()
     end
-    if description and description~="" then
-      where.descriptions[name]=description
+    local source=source[group]
+    if not source then
+      report_defining("fatal source error in setting feature %a, group %a",name,group)
+      os.exit()
     end
-    if initializers then
-      set('initializers',name,where,specification)
+    local node=source.node
+    local base=source.base
+    local position=source.position
+    if node then
+      setindeed("node",target,group,name,node,position)
     end
-    if processors then
-      set('processors',name,where,specification)
-    end
-    if manipulators then
-      set('manipulators',name,where,specification)
+    if base then
+      setindeed("base",target,group,name,base,position)
     end
-    if modechecker then
-      where.modechecker=modechecker
+  end
+  local function register(where,specification)
+    local name=specification.name
+    if name and name~="" then
+      local default=specification.default
+      local description=specification.description
+      local initializers=specification.initializers
+      local processors=specification.processors
+      local manipulators=specification.manipulators
+      local modechecker=specification.modechecker
+      if default then
+        where.defaults[name]=default
+      end
+      if description and description~="" then
+        where.descriptions[name]=description
+      end
+      if initializers then
+        set('initializers',name,where,specification)
+      end
+      if processors then
+        set('processors',name,where,specification)
+      end
+      if manipulators then
+        set('manipulators',name,where,specification)
+      end
+      if modechecker then
+        where.modechecker=modechecker
+      end
     end
   end
-end
-constructors.registerfeature=register
-function constructors.getfeatureaction(what,where,mode,name)
-  what=handlers[what].features
-  if what then
-    where=what[where]
-    if where then
-      mode=where[mode]
-      if mode then
-        for i=1,#mode do
-          local m=mode[i]
-          if m.name==name then
-            return m.action
+  constructors.registerfeature=register
+  function constructors.getfeatureaction(what,where,mode,name)
+    what=handlers[what].features
+    if what then
+      where=what[where]
+      if where then
+        mode=where[mode]
+        if mode then
+          for i=1,#mode do
+            local m=mode[i]
+            if m.name==name then
+              return m.action
+            end
           end
         end
       end
     end
   end
-end
-local newhandler={}
-constructors.handlers=newhandler 
-constructors.newhandler=newhandler
-local function setnewhandler(what) 
-  local handler=handlers[what]
-  if not handler then
-    handler={}
-    handlers[what]=handler
+  local newfeatures={}
+  constructors.newfeatures=newfeatures 
+  constructors.features=newfeatures
+  local function setnewfeatures(what)
+    local handler=handlers[what]
+    local features=handler.features
+    if not features then
+      local tables=handler.tables   
+      local statistics=handler.statistics 
+      features=allocate {
+        defaults={},
+        descriptions=tables and tables.features or {},
+        used=statistics and statistics.usedfeatures or {},
+        initializers={ base={},node={} },
+        processors={ base={},node={} },
+        manipulators={ base={},node={} },
+      }
+      features.register=function(specification) return register(features,specification) end
+      handler.features=features 
+    end
+    return features
   end
-  return handler
+  setmetatable(newfeatures,{
+    __call=function(t,k) local v=t[k] return v end,
+    __index=function(t,k) local v=setnewfeatures(k) t[k]=v return v end,
+  })
 end
-setmetatable(newhandler,{
-  __call=function(t,k) local v=t[k] return v end,
-  __index=function(t,k) local v=setnewhandler(k) t[k]=v return v end,
-})
-local newfeatures={}
-constructors.newfeatures=newfeatures 
-constructors.features=newfeatures
-local function setnewfeatures(what)
-  local handler=handlers[what]
-  local features=handler.features
-  if not features then
-    local tables=handler.tables   
-    local statistics=handler.statistics 
-    features=allocate {
-      defaults={},
-      descriptions=tables and tables.features or {},
-      used=statistics and statistics.usedfeatures or {},
-      initializers={ base={},node={} },
-      processors={ base={},node={} },
-      manipulators={ base={},node={} },
-    }
-    features.register=function(specification) return register(features,specification) end
-    handler.features=features 
+do
+  local newhandler={}
+  constructors.handlers=newhandler 
+  constructors.newhandler=newhandler
+  local function setnewhandler(what) 
+    local handler=handlers[what]
+    if not handler then
+      handler={}
+      handlers[what]=handler
+    end
+    return handler
+  end
+  setmetatable(newhandler,{
+    __call=function(t,k) local v=t[k] return v end,
+    __index=function(t,k) local v=setnewhandler(k) t[k]=v return v end,
+  })
+end
+do
+  local newenhancer={}
+  constructors.enhancers=newenhancer
+  constructors.newenhancer=newenhancer
+  local function setnewenhancer(format)
+    local handler=handlers[format]
+    local enhancers=handler.enhancers
+    if not enhancers then
+      local actions=allocate()
+      local before=allocate()
+      local after=allocate()
+      local order=allocate()
+      local patches={ before=before,after=after }
+      local trace=false
+      local report=logs.reporter("fonts",format.." enhancing")
+      trackers.register(format..".loading",function(v) trace=v end)
+      local function enhance(name,data,filename,raw)
+        local enhancer=actions[name]
+        if enhancer then
+          if trace then
+            report("apply enhancement %a to file %a",name,filename)
+            ioflush()
+          end
+          enhancer(data,filename,raw)
+        else
+        end
+      end
+      local function apply(data,filename,raw)
+        local basename=file.basename(lower(filename))
+        if trace then
+          report("%s enhancing file %a","start",filename)
+        end
+        ioflush() 
+        for e=1,#order do
+          local enhancer=order[e]
+          local b=before[enhancer]
+          if b then
+            for pattern,action in next,b do
+              if find(basename,pattern) then
+                action(data,filename,raw)
+              end
+            end
+          end
+          enhance(enhancer,data,filename,raw)
+          local a=after[enhancer]
+          if a then
+            for pattern,action in next,a do
+              if find(basename,pattern) then
+                action(data,filename,raw)
+              end
+            end
+          end
+          ioflush() 
+        end
+        if trace then
+          report("%s enhancing file %a","stop",filename)
+        end
+        ioflush() 
+      end
+      local function register(what,action)
+        if action then
+          if actions[what] then
+          else
+            order[#order+1]=what
+          end
+          actions[what]=action
+        else
+          report("bad enhancer %a",what)
+        end
+      end
+      local function patch(what,where,pattern,action)
+        local pw=patches[what]
+        if pw then
+          local ww=pw[where]
+          if ww then
+            ww[pattern]=action
+          else
+            pw[where]={ [pattern]=action}
+          end
+        end
+      end
+      enhancers={
+        register=register,
+        apply=apply,
+        patch=patch,
+        patches={ register=patch },
+      }
+      handler.enhancers=enhancers
+    end
+    return enhancers
   end
-  return features
+  setmetatable(newenhancer,{
+    __call=function(t,k) local v=t[k] return v end,
+    __index=function(t,k) local v=setnewenhancer(k) t[k]=v return v end,
+  })
 end
-setmetatable(newfeatures,{
-  __call=function(t,k) local v=t[k] return v end,
-  __index=function(t,k) local v=setnewfeatures(k) t[k]=v return v end,
-})
 function constructors.checkedfeatures(what,features)
   local defaults=handlers[what].features.defaults
   if features and next(features) then
@@ -15228,7 +15328,6 @@ if not modules then modules={} end modules ['font-otl']={
 local gmatch,find,match,lower,strip=string.gmatch,string.find,string.match,string.lower,string.strip
 local type,next,tonumber,tostring,unpack=type,next,tonumber,tostring,unpack
 local abs=math.abs
-local ioflush=io.flush
 local derivetable=table.derive
 local formatters=string.formatters
 local setmetatableindex=table.setmetatableindex
@@ -15257,10 +15356,8 @@ local readers=fonts.readers
 local constructors=fonts.constructors
 local otffeatures=constructors.features.otf
 local registerotffeature=otffeatures.register
-local enhancers=allocate()
-otf.enhancers=enhancers
-local patches={}
-enhancers.patches=patches
+local otfenhancers=constructors.enhancers.otf
+local registerotfenhancer=otfenhancers.register
 local forceload=false
 local cleanup=0   
 local syncspace=true
@@ -15276,76 +15373,7 @@ registerdirective("fonts.otf.loader.cleanup",function(v) cleanup=tonumber(v) or
 registerdirective("fonts.otf.loader.force",function(v) forceload=v end)
 registerdirective("fonts.otf.loader.syncspace",function(v) syncspace=v end)
 registerdirective("fonts.otf.loader.forcenotdef",function(v) forcenotdef=v end)
-local ordered_enhancers={
-  "check extra features",
-}
-local actions=allocate()
-local before=allocate()
-local after=allocate()
-patches.before=before
-patches.after=after
-local function enhance(name,data,filename,raw)
-  local enhancer=actions[name]
-  if enhancer then
-    if trace_loading then
-      report_otf("apply enhancement %a to file %a",name,filename)
-      ioflush()
-    end
-    enhancer(data,filename,raw)
-  else
-  end
-end
-function enhancers.apply(data,filename,raw)
-  local basename=file.basename(lower(filename))
-  if trace_loading then
-    report_otf("%s enhancing file %a","start",filename)
-  end
-  ioflush() 
-  for e=1,#ordered_enhancers do
-    local enhancer=ordered_enhancers[e]
-    local b=before[enhancer]
-    if b then
-      for pattern,action in next,b do
-        if find(basename,pattern) then
-          action(data,filename,raw)
-        end
-      end
-    end
-    enhance(enhancer,data,filename,raw)
-    local a=after[enhancer]
-    if a then
-      for pattern,action in next,a do
-        if find(basename,pattern) then
-          action(data,filename,raw)
-        end
-      end
-    end
-    ioflush() 
-  end
-  if trace_loading then
-    report_otf("%s enhancing file %a","stop",filename)
-  end
-  ioflush() 
-end
-function patches.register(what,where,pattern,action)
-  local pw=patches[what]
-  if pw then
-    local ww=pw[where]
-    if ww then
-      ww[pattern]=action
-    else
-      pw[where]={ [pattern]=action}
-    end
-  end
-end
-function patches.report(fmt,...)
-  if trace_loading then
-    report_otf("patching: %s",formatters[fmt](...))
-  end
-end
-function enhancers.register(what,action) 
-  actions[what]=action
-end
+registerotfenhancer("check extra features",function() end) 
 function otf.load(filename,sub,featurefile)
   local featurefile=nil
   local base=file.basename(file.removesuffix(filename))
@@ -15445,7 +15473,7 @@ function otf.load(filename,sub,featurefile)
     otfreaders.unpack(data)
     otfreaders.expand(data) 
     otfreaders.addunicodetable(data)
-    enhancers.apply(data,filename,data)
+    otfenhancers.apply(data,filename,data)
     if applyruntimefixes then
       applyruntimefixes(filename,data)
     end
@@ -18165,6 +18193,12 @@ local getthreshold=injections.getthreshold
 local checkstep=(nodes and nodes.tracers and nodes.tracers.steppers.check)  or function() end
 local registerstep=(nodes and nodes.tracers and nodes.tracers.steppers.register) or function() end
 local registermessage=(nodes and nodes.tracers and nodes.tracers.steppers.message) or function() end
+local function checkdisccontent(d)
+  local pre,post,replace=getdisc(d)
+  if pre   then for n in traverse_id(glue_code,pre)   do print("pre",nodes.idstostring(pre))   break end end
+  if post  then for n in traverse_id(glue_code,post)  do print("pos",nodes.idstostring(post))  break end end
+  if replace then for n in traverse_id(glue_code,replace) do print("rep",nodes.idstostring(replace)) break end end
+end
 local function logprocess(...)
   if trace_steps then
     registermessage(...)
@@ -18225,7 +18259,7 @@ end
 local function copy_glyph(g) 
   local components=getfield(g,"components")
   if components then
-    setfield(g,"components",nil)
+    setfield(g,"components")
     local n=copy_node(g)
     copyinjection(n,g) 
     setfield(g,"components",components)
@@ -18237,11 +18271,18 @@ local function copy_glyph(g)
   end
 end
 local function flattendisk(head,disc)
-  local _,_,replace,_,_,replacetail=getdisc(disc,true)
-  setfield(disc,"replace",nil)
+  local pre,post,replace,pretail,posttail,replacetail=getdisc(disc,true)
+  local prev,next=getboth(disc)
+  local ishead=head==disc
+  setdisc(disc)
   flush_node(disc)
-  if head==disc then
-    local next=getnext(disc)
+  if pre then
+    flush_node_list(pre)
+  end
+  if post then
+    flush_node_list(post)
+  end
+  if ishead then
     if replace then
       if next then
         setlink(replacetail,next)
@@ -18253,7 +18294,6 @@ local function flattendisk(head,disc)
       return 
     end
   else
-    local prev,next=getboth(disc)
     if replace then
       if next then
         setlink(replacetail,next)
@@ -18288,8 +18328,8 @@ local function markstoligature(head,start,stop,char)
   else
     local prev=getprev(start)
     local next=getnext(stop)
-    setprev(start,nil)
-    setnext(stop,nil)
+    setprev(start)
+    setnext(stop)
     local base=copy_glyph(start)
     if head==start then
       head=base
@@ -18336,8 +18376,8 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou
   local prev=getprev(start)
   local next=getnext(stop)
   local comp=start
-  setprev(start,nil)
-  setnext(stop,nil)
+  setprev(start)
+  setnext(stop)
   local base=copy_glyph(start)
   if start==head then
     head=base
@@ -18401,36 +18441,36 @@ local function toligature(head,start,stop,char,dataset,sequence,markflag,discfou
       local pre,post,replace,pretail,posttail,replacetail=getdisc(discfound,true)
       if not replace then 
         local prev=getprev(base)
-local current=comp
-local previous=nil
-local copied=nil
-while current do
-  if getid(current)==glyph_code then
-    local n=copy_node(current)
-    if copied then
-      setlink(previous,n)
-    else
-      copied=n
-    end
-    previous=n
-  end
-  current=getnext(current)
-end
-        setprev(discnext,nil) 
-        setnext(discprev,nil) 
+        local current=comp
+        local previous=nil
+        local copied=nil
+        while current do
+          if getid(current)==glyph_code then
+            local n=copy_node(current)
+            if copied then
+              setlink(previous,n)
+            else
+              copied=n
+            end
+            previous=n
+          end
+          current=getnext(current)
+        end
+        setprev(discnext) 
+        setnext(discprev) 
         if pre then
           setlink(discprev,pre)
         end
         pre=comp
         if post then
           setlink(posttail,discnext)
-          setprev(post,nil)
+          setprev(post)
         else
           post=discnext
         end
         setlink(prev,discfound)
         setlink(discfound,next)
-        setboth(base,nil,nil)
+        setboth(base)
         setfield(base,"components",copied)
         setdisc(discfound,pre,post,base,discretionary_code)
         base=prev 
@@ -19434,6 +19474,39 @@ end
 local function show_skip(dataset,sequence,char,ck,class)
   logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(dataset,sequence),gref(char),class,ck[1],ck[8] or ck[2])
 end
+local new_kern=nuts.pool.kern
+local function checked(head)
+  local current=head
+  while current do
+    if getid(current)==glue_code then
+      local kern=new_kern(getfield(current,"width"))
+      if head==current then
+        local next=getnext(current)
+        if next then
+          setlink(kern,next)
+        end
+        flush_node(current)
+        head=kern
+        current=next
+      else
+        local prev,next=getboth(current)
+        setlink(prev,kern)
+        setlink(kern,next)
+        flush_node(current)
+        current=next
+      end
+    else
+      current=getnext(current)
+    end
+  end
+  return head
+end
+local function setdiscchecked(d,pre,post,replace)
+  if pre   then pre=checked(pre)   end
+  if post  then post=checked(post)  end
+  if replace then replace=checked(replace) end
+  setdisc(d,pre,post,replace)
+end
 local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,ck,chainproc)
   if not start then
     return head,start,false
@@ -19454,6 +19527,7 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c
   local current=start
   local last=start
   local prev=getprev(start)
+  local hasglue=false
   local i=f
   while i<=l do
     local id=getid(current)
@@ -19461,6 +19535,11 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c
       i=i+1
       last=current
       current=getnext(current)
+    elseif id==glue_code then
+      i=i+1
+      last=current
+      current=getnext(current)
+      hasglue=true
     elseif id==disc_code then
       if keepdisc then
         keepdisc=false
@@ -19510,8 +19589,8 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c
         tail=find_node_tail(head)
       end
       setnext(sweepnode,current)
-      setprev(head,nil)
-      setnext(tail,nil)
+      setprev(head)
+      setnext(tail)
       appenddisc(sweepnode,head)
     end
   end
@@ -19523,6 +19602,10 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c
       if id==glyph_code then
         i=i+1
         current=getnext(current)
+      elseif id==glue_code then
+        i=i+1
+        current=getnext(current)
+        hasglue=true
       elseif id==disc_code then
         if keepdisc then
           keepdisc=false
@@ -19564,6 +19647,9 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c
       local id=getid(current)
       if id==glyph_code then
         i=i-1
+      elseif id==glue_code then
+        i=i-1
+        hasglue=true
       elseif id==disc_code then
         if keepdisc then
           keepdisc=false
@@ -19608,8 +19694,8 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c
     if cprev then
       setnext(cprev,lookaheaddisc)
     end
-    setprev(cf,nil)
-    setnext(cl,nil)
+    setprev(cf)
+    setnext(cl)
     if startishead then
       head=lookaheaddisc
     end
@@ -19636,7 +19722,11 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c
       local tail=find_node_tail(new)
       setlink(tail,replace)
     end
-    setdisc(lookaheaddisc,cf,post,new)
+    if hasglue then
+      setdiscchecked(lookaheaddisc,cf,post,new)
+    else
+      setdisc(lookaheaddisc,cf,post,new)
+    end
     start=getprev(lookaheaddisc)
     sweephead[cf]=getnext(clast)
     sweephead[new]=getnext(last)
@@ -19659,8 +19749,8 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c
       setprev(cnext,backtrackdisc)
     end
     setnext(backtrackdisc,cnext)
-    setprev(cf,nil)
-    setnext(cl,nil)
+    setprev(cf)
+    setnext(cl)
     local pre,post,replace,pretail,posttail,replacetail=getdisc(backtrackdisc,true)
     local new=copy_node_list(cf)
     local cnew=find_node_tail(new)
@@ -19687,7 +19777,11 @@ local function chaindisk(head,start,last,dataset,sequence,chainlookup,rlmode,k,c
     else
       replace=new
     end
-    setdisc(backtrackdisc,pre,post,replace)
+    if hasglue then
+      setdiscchecked(backtrackdisc,pre,post,replace)
+    else
+      setdisc(backtrackdisc,pre,post,replace)
+    end
     start=getprev(backtrackdisc)
     sweephead[post]=getnext(clast)
     sweephead[replace]=getnext(last)
@@ -20341,7 +20435,7 @@ local function kernrun(disc,k_run,font,attr,...)
       if k_run(posttail,"postinjections",next,font,attr,...) then
         done=true
       end
-      setnext(posttail,nil)
+      setnext(posttail)
       setprev(next,disc)
     end
   end
@@ -20363,7 +20457,7 @@ local function kernrun(disc,k_run,font,attr,...)
       if k_run(replacetail,"replaceinjections",next,font,attr,...) then
         done=true
       end
-      setnext(replacetail,nil)
+      setnext(replacetail)
       setprev(next,disc)
     end
   elseif prev and next then
@@ -20430,7 +20524,7 @@ local function testrun(disc,t_run,c_run,...)
     setlink(replacetail,next)
     local ok,overflow=t_run(replace,next,...)
     if ok and overflow then
-      setfield(disc,"replace",nil)
+      setfield(disc,"replace")
       setlink(prev,replace)
       setboth(disc)
       flush_node_list(disc)
@@ -23653,37 +23747,15 @@ local otfreaders=otf.readers
 local otfenhancers=otf.enhancers
 local afmfeatures=constructors.features.afm
 local registerafmfeature=afmfeatures.register
+local afmenhancers=constructors.enhancers.afm
+local registerafmenhancer=afmenhancers.register
 afm.version=1.512 
-afm.cache=containers.define("fonts","afm",afm.version,true)
+afm.cache=containers.define("fonts","one",afm.version,true)
 afm.autoprefixed=true 
 afm.helpdata={} 
 afm.syncspace=true 
 local overloads=fonts.mappings.overloads
 local applyruntimefixes=fonts.treatments and fonts.treatments.applyfixes
-local enhancers={
-}
-local steps={
-  "unify names",
-  "add ligatures",
-  "add extra kerns",
-  "normalize features",
-  "check extra features",
-  "fix names",
-}
-local function applyenhancers(data,filename)
-  for i=1,#steps do
-    local step=steps[i]
-    local enhancer=enhancers[step]
-    if enhancer then
-      if trace_loading then
-        report_afm("applying enhancer %a",step)
-      end
-      enhancer(data,filename)
-    else
-      report_afm("invalid enhancer %a",step)
-    end
-  end
-end
 function afm.load(filename)
   filename=resolvers.findfile(filename,'afm') or ""
   if filename~="" and not fonts.names.ignoredfile(filename) then
@@ -23706,7 +23778,7 @@ function afm.load(filename)
       report_afm("reading %a",filename)
       data=afm.readers.loadfont(filename,pfbname)
       if data then
-        applyenhancers(data,filename)
+        afmenhancers.apply(data,filename)
         fonts.mappings.addtounicode(data,filename)
         otfreaders.pack(data)
         data.size=size
@@ -23731,7 +23803,7 @@ function afm.load(filename)
   end
 end
 local uparser=fonts.mappings.makenameparser() 
-enhancers["unify names"]=function(data,filename)
+local function enhance_unify_names(data,filename)
   local unicodevector=fonts.encodings.agl.unicodes 
   local unicodes={}
   local names={}
@@ -23783,7 +23855,7 @@ enhancers["unify names"]=function(data,filename)
 end
 local everywhere={ ["*"]={ ["*"]=true } } 
 local noflags={ false,false,false,false }
-enhancers["normalize features"]=function(data)
+local function enhance_normalize_features(data)
   local ligatures=setmetatableindex("table")
   local kerns=setmetatableindex("table")
   local extrakerns=setmetatableindex("table")
@@ -23881,8 +23953,7 @@ enhancers["normalize features"]=function(data)
   data.resources.features=features
   data.resources.sequences=sequences
 end
-enhancers["check extra features"]=otf.enhancers.enhance
-enhancers["fix names"]=function(data)
+local function enhance_fix_names(data)
   for k,v in next,data.descriptions do
     local n=v.name
     local r=overloads[n]
@@ -23921,10 +23992,10 @@ local addthem=function(rawdata,ligatures)
     end
   end
 end
-enhancers["add ligatures"]=function(rawdata)
+local function enhance_add_ligatures(rawdata)
   addthem(rawdata,afm.helpdata.ligatures)
 end
-enhancers["add extra kerns"]=function(rawdata) 
+local function enhance_add_extra_kerns(rawdata) 
   local descriptions=rawdata.descriptions
   local resources=rawdata.resources
   local unicodes=resources.unicodes
@@ -24332,6 +24403,12 @@ function readers.pfb(specification,method)
   swap("specification")
   return readers.afm(specification,method)
 end
+registerafmenhancer("unify names",enhance_unify_names)
+registerafmenhancer("add ligatures",enhance_add_ligatures)
+registerafmenhancer("add extra kerns",enhance_add_extra_kerns)
+registerafmenhancer("normalize features",enhance_normalize_features)
+registerafmenhancer("check extra features",otfenhancers.enhance)
+registerafmenhancer("fix names",enhance_fix_names)
 
 end -- closure
 
@@ -24532,8 +24609,11 @@ tfm.version=1.000
 tfm.maxnestingdepth=5
 tfm.maxnestingsize=65536*1024
 local otf=fonts.handlers.otf
+local otfenhancers=otf.enhancers
 local tfmfeatures=constructors.features.tfm
 local registertfmfeature=tfmfeatures.register
+local tfmenhancers=constructors.enhancers.tfm
+local registertfmenhancer=tfmenhancers.register
 constructors.resolvevirtualtoo=false 
 fonts.formats.tfm="type1" 
 fonts.formats.ofm="type1"
@@ -24545,27 +24625,7 @@ function tfm.setfeatures(tfmdata,features)
     return {} 
   end
 end
-local depth={} 
-local enhancers={}
-local steps={
-  "normalize features",
-  "check extra features"
-}
-enhancers["check extra features"]=otf.enhancers.enhance
-local function applyenhancers(data,filename)
-  for i=1,#steps do
-    local step=steps[i]
-    local enhancer=enhancers[step]
-    if enhancer then
-      if trace_loading then
-        report_tfm("applying enhancer %a",step)
-      end
-      enhancer(data,filename)
-    else
-      report_tfm("invalid enhancer %a",step)
-    end
-  end
-end
+local depth={}
 local function read_from_tfm(specification)
   local filename=specification.filename
   local size=specification.size
@@ -24620,7 +24680,7 @@ local function read_from_tfm(specification)
         tfmdata.descriptions=tfmdata.characters
       end
       otf.readers.addunicodetable(tfmdata)
-      applyenhancers(tfmdata,filename)
+      tfmenhancers.apply(tfmdata,filename)
       constructors.applymanipulators("tfm",tfmdata,features,trace_features,report_tfm)
       otf.readers.unifymissing(tfmdata)
       fonts.mappings.addtounicode(tfmdata,filename)
@@ -24921,7 +24981,7 @@ end
 do
   local everywhere={ ["*"]={ ["*"]=true } } 
   local noflags={ false,false,false,false }
-  enhancers["normalize features"]=function(data)
+  local function enhance_normalize_features(data)
     local ligatures=setmetatableindex("table")
     local kerns=setmetatableindex("table")
     local characters=data.characters
@@ -25003,6 +25063,8 @@ do
     data.resources.sequences=sequences
     data.shared.resources=data.shared.resources or resources
   end
+  registertfmenhancer("normalize features",enhance_normalize_features)
+  registertfmenhancer("check extra features",otfenhancers.enhance)
 end
 registertfmfeature {
   name="mode",
-- 
cgit v1.2.3