From d8dcfd9af18a15af70ec86d8e01e6d7a0f43c35c Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Thu, 30 Jul 2015 07:53:30 +0200
Subject: [aux] do not rely on the AGL being loaded at initialization time

---
 src/luaotfload-auxiliary.lua | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-auxiliary.lua b/src/luaotfload-auxiliary.lua
index 1ef581e..dce0d60 100644
--- a/src/luaotfload-auxiliary.lua
+++ b/src/luaotfload-auxiliary.lua
@@ -19,6 +19,7 @@ local aux                   = luaotfload.aux
 local log                   = luaotfload.log
 local report                = log.report
 local fonthashes            = fonts.hashes
+local encodings             = fonts.encodings
 local identifiers           = fonthashes.identifiers
 local fontnames             = fonts.names
 
@@ -214,8 +215,6 @@ luatexbase.add_to_callback(
 ---                      glyphs and characters
 -----------------------------------------------------------------------
 
-local agl = fonts.encodings.agl
-
 --- int -> int -> bool
 local font_has_glyph = function (font_id, codepoint)
   local fontdata = fonts.hashes.identifiers[font_id]
@@ -232,7 +231,7 @@ aux.font_has_glyph = font_has_glyph
 local raw_slot_of_name = function (font_id, glyphname)
   local fontdata = font.fonts[font_id]
   if fontdata.type == "virtual" then --- get base font for glyph idx
-    local codepoint  = agl.unicodes[glyphname]
+    local codepoint  = encodings.agl.unicodes[glyphname]
     local glyph      = fontdata.characters[codepoint]
     if fontdata.characters[codepoint] then
       return codepoint
@@ -293,7 +292,7 @@ local indices
 --- int -> (string | false)
 local name_of_slot = function (codepoint)
   if not indices then --- this will load the glyph list
-    local unicodes = agl.unicodes
+    local unicodes = encodings.agl.unicodes
     indices = table.swapped(unicodes)
   end
   local glyphname = indices[codepoint]
-- 
cgit v1.2.3


From e05c2e2c2f72788e63da7334e054fd887bd22e70 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Thu, 27 Aug 2015 23:08:11 +0200
Subject: [main] install stub for main initialization hook

---
 src/luaotfload-main.lua | 8 ++++++++
 src/luaotfload.sty      | 1 +
 2 files changed, 9 insertions(+)

(limited to 'src')

diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua
index b633ed7..36db2c3 100644
--- a/src/luaotfload-main.lua
+++ b/src/luaotfload-main.lua
@@ -266,4 +266,12 @@ load_luaotfload_module "auxiliary"    --- additional high-level functionality
 
 luaotfload.aux.start_rewrite_fontname () --- to be migrated to fontspec
 
+luaotfload.main = function ()
+    local starttime = os.gettimeofday ()
+    --- XXX stub
+    logreport ("both", 0, "main",
+               "initialization completed in %0.3f seconds",
+               os.gettimeofday() - starttime)
+end
+
 -- vim:tw=79:sw=4:ts=4:et
diff --git a/src/luaotfload.sty b/src/luaotfload.sty
index c9c9864..ec62dad 100644
--- a/src/luaotfload.sty
+++ b/src/luaotfload.sty
@@ -45,4 +45,5 @@
   \RequirePackage{luatexbase}
 \fi
 \RequireLuaModule{luaotfload-main}
+\directlua{local _void = luaotfload.main ()}
 
-- 
cgit v1.2.3


From 7cd218b920814322d002f915ed5c8bb0132cf40a Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Thu, 27 Aug 2015 23:28:32 +0200
Subject: [main,loaders] regroup callback handling code with loaders
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

... changing the meaning of the file’s designation instead of adding yet
another file.

All the callback manipulation is now contained inside that module which
will inject most of its functionality only when its main ``.install()``
method is called.
---
 src/luaotfload-loaders.lua | 147 ++++++++++++++++++++++++++++++++++++---------
 src/luaotfload-main.lua    | 116 +++++------------------------------
 2 files changed, 134 insertions(+), 129 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-loaders.lua b/src/luaotfload-loaders.lua
index 901d4d8..061789c 100644
--- a/src/luaotfload-loaders.lua
+++ b/src/luaotfload-loaders.lua
@@ -1,34 +1,127 @@
-if not modules then modules = { } end modules ["loaders"] = {
-    version   = "2.5",
-    comment   = "companion to luaotfload-main.lua",
-    author    = "Hans Hagen, Khaled Hosny, Elie Roux, Philipp Gesang",
-    copyright = "PRAGMA ADE / ConTeXt Development Team",
-    license   = "see context related readme files"
-}
+#!/usr/bin/env texlua
+-----------------------------------------------------------------------
+--         FILE:  luaotfload-loaders.lua
+--  DESCRIPTION:  Luaotfload callback handling
+-- REQUIREMENTS:  luatex v.0.80 or later; packages lualibs, luatexbase
+--       AUTHOR:  Philipp Gesang (Phg), <phg@phi-gamma.net>, Hans Hagen, Khaled Hosny, Elie Roux
+--      VERSION:  just consult Git, okay?
+-----------------------------------------------------------------------
+--
+--- Contains parts of the earlier main script.
+
+if not lualibs    then error "this module requires Luaotfload" end
+if not luaotfload then error "this module requires Luaotfload" end
+
+local logreport        = luaotfload.log and luaotfload.log.report or print
+local add_to_callback  = luatexbase.add_to_callback
+local create_callback  = luatexbase.create_callback
+local reset_callback   = luatexbase.reset_callback
+local call_callback    = luatexbase.call_callback
+local dummy_function   = function () end
+
+local install_formats = function ()
+  local fonts = fonts
+  if not fonts then return false end
+
+  local readers  = fonts.readers
+  local handlers = fonts.handlers
+  local formats  = fonts.formats
+  if not readers or not handlers or not formats then return false end
+
+  local aux = function (which, reader)
+    if not which or type (which) ~= "string" then return false end
+    formats  [which] = "type1"
+    readers  [which] = reader
+    handlers [which] = { }
+    return true
+  end
+
+  return aux ("pfa", function (spec) return readers.opentype (spec, "pfa", "type1") end)
+     and aux ("pfb", function (spec) return readers.opentype (spec, "pfb", "type1") end)
+     and aux ("ofm", readers.tfm)
+end
+
+--[[doc--
 
-local fonts           = fonts
-local readers         = fonts.readers
-local handlers        = fonts.handlers
-local formats         = fonts.formats
-
-local pfb_reader = function (specification)
-  return readers.opentype (specification, "pfb", "type1")
-end 
- 
-local pfa_reader = function (specification)
-  return readers.opentype (specification, "pfa", "type1")
+    \subsection{\CONTEXT override}
+    \label{define-font}
+    We provide a simplified version of the original font definition
+    callback.
+
+--doc]]--
+
+
+local definers = { } --- (string, spec -> size -> id -> tmfdata) hash_t
+do
+  local read = fonts.definers.read
+
+  local patch = function (specification, size, id)
+    local fontdata = read (specification, size, id)
+    if type (fontdata) == "table" and fontdata.shared then
+      --- We need to test for the “shared” field here
+      --- or else the fontspec capheight callback will
+      --- operate on tfm fonts.
+      call_callback ("luaotfload.patch_font", fontdata, specification)
+    else
+      call_callback ("luaotfload.patch_font_unsafe", fontdata, specification)
+    end
+    return fontdata
+  end
+
+  local mk_info = function (name)
+    local definer = name == "patch" and patch or read
+    return function (specification, size, id)
+      logreport ("both", 0, "main", "defining font no. %d", id)
+      logreport ("both", 0, "main", "   > active font definer: %q", name)
+      logreport ("both", 0, "main", "   > spec %q", specification)
+      logreport ("both", 0, "main", "   > at size %.2f pt", size / 2^16)
+      local result = definer (specification, size, id)
+      if not result then
+        logreport ("both", 0, "main", "   > font definition failed")
+        return
+      elseif type (result) == "number" then
+        logreport ("both", 0, "main", "   > font definition yielded id %d", result)
+        return result
+      end
+      logreport ("both", 0, "main", "   > font definition successful")
+      logreport ("both", 0, "main", "   > name %q",     result.name     or "<nil>")
+      logreport ("both", 0, "main", "   > fontname %q", result.fontname or "<nil>")
+      logreport ("both", 0, "main", "   > fullname %q", result.fullname or "<nil>")
+      return result
+    end
+  end
+
+  definers.patch          = patch
+  definers.generic        = read
+  definers.info_patch     = mk_info "patch"
+  definers.info_generic   = mk_info "generic"
 end
 
-formats.pfa  = "type1"
-readers.pfa  = pfa_reader
-handlers.pfa = { }
+--[[doc--
+
+  We create callbacks for patching fonts on the fly, to be used by
+  other packages. In addition to the regular \identifier{patch_font}
+  callback there is an unsafe variant \identifier{patch_font_unsafe}
+  that will be invoked even if the target font lacks certain essential
+  tfmdata tables.
 
-formats.pfb  = "type1"
-readers.pfb  = pfb_reader
-handlers.pfb = { }
+  The callbacks initially contain the empty function that we are going
+  to override below.
 
-formats.ofm  = "type1"
-readers.ofm  = readers.tfm
-handlers.ofm = { }
+--doc]]--
 
+return {
+  install = function ()
+    if not install_formats () then
+      logreport ("both", 0, "main", "error initializing OFM/PF{A,B} loaders")
+    end
+    create_callback("luaotfload.patch_font",        "simple", dummy_function)
+    create_callback("luaotfload.patch_font_unsafe", "simple", dummy_function)
+
+    reset_callback "define_font"
+    local definer = config.luaotfload.run.definer
+    add_to_callback ("define_font", definers[definer or "patch"],
+                     "luaotfload.define_font", 1)
+  end
+}
 -- vim:tw=71:sw=2:ts=2:expandtab
diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua
index 36db2c3..fe27215 100644
--- a/src/luaotfload-main.lua
+++ b/src/luaotfload-main.lua
@@ -67,12 +67,6 @@ luaotfload.module = {
 local luatexbase       = luatexbase
 local require          = require
 local type             = type
-local add_to_callback  = luatexbase.add_to_callback
-local create_callback  = luatexbase.create_callback
-local reset_callback   = luatexbase.reset_callback
-local call_callback    = luatexbase.call_callback
-
-local dummy_function = function () end --- XXX this will be moved to the luaotfload namespace when we have the init module
 
 local error, warning, info, log =
     luatexbase.provides_module(luaotfload.module)
@@ -165,110 +159,28 @@ end
 
 luaotfload.init.main (store)
 
-load_luaotfload_module "loaders"         --- Type1 font wrappers
-load_luaotfload_module "database"        --- Font management.
-load_luaotfload_module "colors"          --- Per-font colors.
-
-luaotfload.resolvers = load_luaotfload_module "resolvers" --- Font lookup
-
-luaotfload.resolvers.install ()
-
-if not config.actions.reconfigure () then
-    logreport ("log", 0, "load", "Post-configuration hooks failed.")
-end
-
---[[doc--
-
-    We create callbacks for patching fonts on the fly, to be used by
-    other packages. In addition to the regular \identifier{patch_font}
-    callback there is an unsafe variant \identifier{patch_font_unsafe}
-    that will be invoked even if the target font lacks certain essential
-    tfmdata tables.
-
-    The callbacks initially contain the empty function that we are going to
-    override below.
-
---doc]]--
-
-create_callback("luaotfload.patch_font",        "simple", dummy_function)
-create_callback("luaotfload.patch_font_unsafe", "simple", dummy_function)
-
---[[doc--
-
-    \subsection{\CONTEXT override}
-    \label{define-font}
-    We provide a simplified version of the original font definition
-    callback.
+luaotfload.main = function ()
+    local starttime = os.gettimeofday ()
 
---doc]]--
+    luaotfload.loaders = load_luaotfload_module "loaders" --- Font loading; callbacks
+    luaotfload.loaders.install ()
 
+    load_luaotfload_module "database"        --- Font management.
+    load_luaotfload_module "colors"          --- Per-font colors.
 
-local definers = { } --- (string, spec -> size -> id -> tmfdata) hash_t
-do
-    local read = fonts.definers.read
-
-    local patch = function (specification, size, id)
-        local fontdata = read (specification, size, id)
-        if type (fontdata) == "table" and fontdata.shared then
-            --- We need to test for the “shared” field here
-            --- or else the fontspec capheight callback will
-            --- operate on tfm fonts.
-            call_callback ("luaotfload.patch_font", fontdata, specification)
-        else
-            call_callback ("luaotfload.patch_font_unsafe", fontdata, specification)
-        end
-        return fontdata
-    end
+    luaotfload.resolvers = load_luaotfload_module "resolvers" --- Font lookup
+    luaotfload.resolvers.install ()
 
-    local mk_info = function (name)
-        local definer = name == "patch" and patch or read
-        return function (specification, size, id)
-            logreport ("both", 0, "main", "defining font no. %d", id)
-            logreport ("both", 0, "main", "   > active font definer: %q", name)
-            logreport ("both", 0, "main", "   > spec %q", specification)
-            logreport ("both", 0, "main", "   > at size %.2f pt", size / 2^16)
-            local result = definer (specification, size, id)
-            if not result then
-                logreport ("both", 0, "main", "   > font definition failed")
-                return
-            elseif type (result) == "number" then
-                logreport ("both", 0, "main", "   > font definition yielded id %d", result)
-                return result
-            end
-            logreport ("both", 0, "main", "   > font definition successful")
-            logreport ("both", 0, "main", "   > name %q",     result.name     or "<nil>")
-            logreport ("both", 0, "main", "   > fontname %q", result.fontname or "<nil>")
-            logreport ("both", 0, "main", "   > fullname %q", result.fullname or "<nil>")
-            return result
-        end
+    if not config.actions.reconfigure () then
+        logreport ("log", 0, "load", "Post-configuration hooks failed.")
     end
 
-    definers.patch          = patch
-    definers.generic        = read
-    definers.info_patch     = mk_info "patch"
-    definers.info_generic   = mk_info "generic"
-end
-
-reset_callback "define_font"
-
---[[doc--
+    load_luaotfload_module "features"     --- font request and feature handling
+    load_luaotfload_module "letterspace"  --- extra character kerning
+    load_luaotfload_module "auxiliary"    --- additional high-level functionality
 
-    Finally we register the callbacks.
+    luaotfload.aux.start_rewrite_fontname () --- to be migrated to fontspec
 
---doc]]--
-
-local definer = config.luaotfload.run.definer
-add_to_callback ("define_font", definers[definer], "luaotfload.define_font", 1)
-
-load_luaotfload_module "features"     --- font request and feature handling
-load_luaotfload_module "letterspace"  --- extra character kerning
-load_luaotfload_module "auxiliary"    --- additional high-level functionality
-
-luaotfload.aux.start_rewrite_fontname () --- to be migrated to fontspec
-
-luaotfload.main = function ()
-    local starttime = os.gettimeofday ()
-    --- XXX stub
     logreport ("both", 0, "main",
                "initialization completed in %0.3f seconds",
                os.gettimeofday() - starttime)
-- 
cgit v1.2.3


From c6fdf62a060ad9e77c461d5eb7a55699fa6c7249 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Thu, 27 Aug 2015 23:39:46 +0200
Subject: [loaders] {re,un}-scope locals for economy

---
 src/luaotfload-loaders.lua | 57 +++++++++++++++++++++++++++++-----------------
 1 file changed, 36 insertions(+), 21 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-loaders.lua b/src/luaotfload-loaders.lua
index 061789c..f722d85 100644
--- a/src/luaotfload-loaders.lua
+++ b/src/luaotfload-loaders.lua
@@ -12,12 +12,7 @@
 if not lualibs    then error "this module requires Luaotfload" end
 if not luaotfload then error "this module requires Luaotfload" end
 
-local logreport        = luaotfload.log and luaotfload.log.report or print
-local add_to_callback  = luatexbase.add_to_callback
-local create_callback  = luatexbase.create_callback
-local reset_callback   = luatexbase.reset_callback
-local call_callback    = luatexbase.call_callback
-local dummy_function   = function () end
+local logreport = luaotfload.log and luaotfload.log.report or print
 
 local install_formats = function ()
   local fonts = fonts
@@ -29,7 +24,11 @@ local install_formats = function ()
   if not readers or not handlers or not formats then return false end
 
   local aux = function (which, reader)
-    if not which or type (which) ~= "string" then return false end
+    if   not which  or type (which) ~= "string"
+      or not reader or type (reader) ~= "function" then
+      logreport ("both", 2, "main", "error installing reader for “%s”", which)
+      return false
+    end
     formats  [which] = "type1"
     readers  [which] = reader
     handlers [which] = { }
@@ -51,7 +50,7 @@ end
 --doc]]--
 
 
-local definers = { } --- (string, spec -> size -> id -> tmfdata) hash_t
+local definers --- (string, spec -> size -> id -> tmfdata) hash_t
 do
   local read = fonts.definers.read
 
@@ -61,7 +60,7 @@ do
       --- We need to test for the “shared” field here
       --- or else the fontspec capheight callback will
       --- operate on tfm fonts.
-      call_callback ("luaotfload.patch_font", fontdata, specification)
+      luatexbase.call_callback ("luaotfload.patch_font", fontdata, specification)
     else
       call_callback ("luaotfload.patch_font_unsafe", fontdata, specification)
     end
@@ -91,10 +90,12 @@ do
     end
   end
 
-  definers.patch          = patch
-  definers.generic        = read
-  definers.info_patch     = mk_info "patch"
-  definers.info_generic   = mk_info "generic"
+  definers = {
+    patch          = patch,
+    generic        = read,
+    info_patch     = mk_info "patch",
+    info_generic   = mk_info "generic",
+  }
 end
 
 --[[doc--
@@ -110,18 +111,32 @@ end
 
 --doc]]--
 
+local install_callbacks = function ()
+  local create_callback  = luatexbase.create_callback
+  local dummy_function   = function () end
+  create_callback ("luaotfload.patch_font",        "simple", dummy_function)
+  create_callback ("luaotfload.patch_font_unsafe", "simple", dummy_function)
+  luatexbase.reset_callback "define_font"
+  local definer = config.luaotfload.run.definer
+  luatexbase.add_to_callback ("define_font",
+                              definers[definer or "patch"],
+                              "luaotfload.define_font",
+                              1)
+  return true
+end
+
 return {
   install = function ()
+    local ret = true
     if not install_formats () then
       logreport ("both", 0, "main", "error initializing OFM/PF{A,B} loaders")
+      ret = false
     end
-    create_callback("luaotfload.patch_font",        "simple", dummy_function)
-    create_callback("luaotfload.patch_font_unsafe", "simple", dummy_function)
-
-    reset_callback "define_font"
-    local definer = config.luaotfload.run.definer
-    add_to_callback ("define_font", definers[definer or "patch"],
-                     "luaotfload.define_font", 1)
+    if not install_callbacks () then
+      logreport ("both", 0, "main", "error installing font loader callbacks")
+      ret = false
+    end
+    return ret
   end
 }
--- vim:tw=71:sw=2:ts=2:expandtab
+-- vim:tw=79:sw=2:ts=2:expandtab
-- 
cgit v1.2.3


From c3db46b819beebb892698513bd51341b9a691786 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Thu, 27 Aug 2015 23:42:44 +0200
Subject: [main,loaders] adjust noise and check status of loader init

---
 src/luaotfload-loaders.lua | 26 +++++++++++++-------------
 src/luaotfload-main.lua    |  4 +++-
 2 files changed, 16 insertions(+), 14 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-loaders.lua b/src/luaotfload-loaders.lua
index f722d85..44216d7 100644
--- a/src/luaotfload-loaders.lua
+++ b/src/luaotfload-loaders.lua
@@ -26,7 +26,7 @@ local install_formats = function ()
   local aux = function (which, reader)
     if   not which  or type (which) ~= "string"
       or not reader or type (reader) ~= "function" then
-      logreport ("both", 2, "main", "error installing reader for “%s”", which)
+      logreport ("both", 2, "loaders", "Error installing reader for “%s”.", which)
       return false
     end
     formats  [which] = "type1"
@@ -70,22 +70,22 @@ do
   local mk_info = function (name)
     local definer = name == "patch" and patch or read
     return function (specification, size, id)
-      logreport ("both", 0, "main", "defining font no. %d", id)
-      logreport ("both", 0, "main", "   > active font definer: %q", name)
-      logreport ("both", 0, "main", "   > spec %q", specification)
-      logreport ("both", 0, "main", "   > at size %.2f pt", size / 2^16)
+      logreport ("both", 0, "loaders", "defining font no. %d", id)
+      logreport ("both", 0, "loaders", "   > active font definer: %q", name)
+      logreport ("both", 0, "loaders", "   > spec %q", specification)
+      logreport ("both", 0, "loaders", "   > at size %.2f pt", size / 2^16)
       local result = definer (specification, size, id)
       if not result then
-        logreport ("both", 0, "main", "   > font definition failed")
+        logreport ("both", 0, "loaders", "   > font definition failed")
         return
       elseif type (result) == "number" then
-        logreport ("both", 0, "main", "   > font definition yielded id %d", result)
+        logreport ("both", 0, "loaders", "   > font definition yielded id %d", result)
         return result
       end
-      logreport ("both", 0, "main", "   > font definition successful")
-      logreport ("both", 0, "main", "   > name %q",     result.name     or "<nil>")
-      logreport ("both", 0, "main", "   > fontname %q", result.fontname or "<nil>")
-      logreport ("both", 0, "main", "   > fullname %q", result.fullname or "<nil>")
+      logreport ("both", 0, "loaders", "   > font definition successful")
+      logreport ("both", 0, "loaders", "   > name %q",     result.name     or "<nil>")
+      logreport ("both", 0, "loaders", "   > fontname %q", result.fontname or "<nil>")
+      logreport ("both", 0, "loaders", "   > fullname %q", result.fullname or "<nil>")
       return result
     end
   end
@@ -129,11 +129,11 @@ return {
   install = function ()
     local ret = true
     if not install_formats () then
-      logreport ("both", 0, "main", "error initializing OFM/PF{A,B} loaders")
+      logreport ("log", 0, "loaders", "Error initializing OFM/PF{A,B} loaders.")
       ret = false
     end
     if not install_callbacks () then
-      logreport ("both", 0, "main", "error installing font loader callbacks")
+      logreport ("log", 0, "loaders", "Error installing font loader callbacks.")
       ret = false
     end
     return ret
diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua
index fe27215..32eb04d 100644
--- a/src/luaotfload-main.lua
+++ b/src/luaotfload-main.lua
@@ -163,7 +163,9 @@ luaotfload.main = function ()
     local starttime = os.gettimeofday ()
 
     luaotfload.loaders = load_luaotfload_module "loaders" --- Font loading; callbacks
-    luaotfload.loaders.install ()
+    if not luaotfload.loaders.install () then
+        logreport ("log", 0, "load", "Callback and loader initialization failed.")
+    end
 
     load_luaotfload_module "database"        --- Font management.
     load_luaotfload_module "colors"          --- Per-font colors.
-- 
cgit v1.2.3


From c7967b7a69bc5cf4e0c2e38b4e552c2587a7502d Mon Sep 17 00:00:00 2001
From: Dohyun Kim <nomosnomos@gmail.com>
Date: Fri, 25 Sep 2015 14:01:40 +0900
Subject: [colors] support displayed math

---
 src/luaotfload-colors.lua | 52 ++++++++++++++++++++++++++++-------------------
 1 file changed, 31 insertions(+), 21 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-colors.lua b/src/luaotfload-colors.lua
index 89884b6..2171a68 100644
--- a/src/luaotfload-colors.lua
+++ b/src/luaotfload-colors.lua
@@ -192,6 +192,7 @@ local whatsit_t         = nodetype("whatsit")
 local disc_t            = nodetype("disc")
 local pdfliteral_t      = node.subtype("pdf_literal")
 local colorstack_t      = node.subtype("pdf_colorstack")
+local mlist_to_hlist    = node.mlist_to_hlist
 
 local color_callback
 local color_attr        = luatexbase.new_attribute("luaotfload_color_attribute")
@@ -230,8 +231,6 @@ local get_font_color = function (font_id)
     return font_color
 end
 
-local cnt = 0
-
 --[[doc--
 While the second argument and second returned value are apparently
 always nil when the function is called, they temporarily take string
@@ -240,26 +239,24 @@ values during the node list traversal.
 
 --- (node * (string | nil)) -> (node * (string | nil))
 local node_colorize
-node_colorize = function (head, current_color)
+node_colorize = function (head, toplevel, current_color)
     local n = head
     while n do
         local n_id = getid(n)
 
         if n_id == hlist_t or n_id == vlist_t then
-            cnt = cnt + 1
             local n_list = getlist(n)
             if getattribute(n_list, color_attr) then
                 if current_color then
                     head, n, current_color = color_whatsit(head, n, current_color, false)
                 end
             else
-                n_list, current_color = node_colorize(n_list, current_color)
+                n_list, current_color = node_colorize(n_list, false, current_color)
                 if current_color and getsubtype(n) == 1 then -- created by linebreak
                     n_list, _, current_color = color_whatsit(n_list, nodetail(n_list), current_color, false, true)
                 end
                 setfield(n, "head", n_list)
             end
-            cnt = cnt - 1
 
         elseif n_id == glyph_t then
             --- colorization is restricted to those fonts
@@ -303,7 +300,7 @@ node_colorize = function (head, current_color)
         n = getnext(n)
     end
 
-    if cnt == 0 and current_color then
+    if toplevel and current_color then
         head, _, current_color = color_whatsit(head, nodetail(head), current_color, false, true)
     end
 
@@ -314,7 +311,7 @@ end
 --- node -> node
 local color_handler = function (head)
     head = todirect(head)
-    head = node_colorize(head)
+    head = node_colorize(head, true)
     head = tonode(head)
 
     -- now append our page resources
@@ -351,6 +348,8 @@ local color_handler = function (head)
 end
 
 local color_callback_activated = 0
+local add_to_callback          = luatexbase.add_to_callback
+local priority_in_callback     = luatexbase.priority_in_callback
 
 --- unit -> unit
 add_color_callback = function ( )
@@ -360,19 +359,30 @@ add_color_callback = function ( )
     end
 
     if color_callback_activated == 0 then
-        luatexbase.add_to_callback(color_callback,
-                                   color_handler,
-                                   "luaotfload.color_handler")
-        luatexbase.add_to_callback("hpack_filter",
-                                   function (head, groupcode)
-                                       if  groupcode == "hbox"          or
-                                           groupcode == "adjusted_hbox" or
-                                           groupcode == "align_set"     then
-                                           head = color_handler(head)
-                                       end
-                                       return head
-                                   end,
-                                   "luaotfload.color_handler")
+        add_to_callback(color_callback,
+                        color_handler,
+                        "luaotfload.color_handler")
+        add_to_callback("hpack_filter",
+                        function (head, groupcode)
+                            if  groupcode == "hbox"          or
+                                groupcode == "adjusted_hbox" or
+                                groupcode == "align_set"     then
+                                head = color_handler(head)
+                            end
+                            return head
+                        end,
+                        "luaotfload.color_handler")
+        add_to_callback("mlist_to_hlist",
+                        function (head, display_type, need_penalties)
+                            if priority_in_callback("mlist_to_hlist","luaotfload.color_handler") == 1 then
+                                head = mlist_to_hlist(head, display_type, need_penalties)
+                            end
+                            if display_type == "text" then
+                                return head
+                            end
+                            return color_handler(head)
+                        end,
+                        "luaotfload.color_handler")
         color_callback_activated = 1
     end
 end
-- 
cgit v1.2.3


From 4a76c3abcdf750cbf1e825d0fce637e35f63b10a Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Sun, 27 Sep 2015 18:28:14 +0200
Subject: [main, parsers] prepare for deferred initialization

---
 src/luaotfload-init.lua    |  4 ++--
 src/luaotfload-main.lua    |  6 +++++-
 src/luaotfload-parsers.lua | 49 +++++++++++++++++++++-------------------------
 3 files changed, 29 insertions(+), 30 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-init.lua b/src/luaotfload-init.lua
index a493cc1..a5e5bec 100644
--- a/src/luaotfload-init.lua
+++ b/src/luaotfload-init.lua
@@ -38,8 +38,8 @@ local logreport  --- filled in after loading the log module
 --[[doc--
 
     \subsection{Preparing the Font Loader}
-    We treat the fontloader as a black box so behavior is consistent
-    between formats.
+    We treat the fontloader as a semi-black box so behavior is
+    consistent between formats.
     We load the fontloader code directly in the same fashion as the
     Plain format \identifier{luatex-fonts} that is part of Context.
     How this is executed depends on the presence on the
diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua
index 32eb04d..3005f5a 100644
--- a/src/luaotfload-main.lua
+++ b/src/luaotfload-main.lua
@@ -150,7 +150,11 @@ local logreport       = log.report
 
 --doc]]--
 
-load_luaotfload_module "parsers"         --- fonts.conf and syntax
+local tmp = load_luaotfload_module "parsers" --- fonts.conf and syntax
+if not tmp.init () then
+    logreport ("log", 0, "load", "Failed to install the parsers.")
+end
+
 load_luaotfload_module "configuration"   --- configuration options
 
 if not config.actions.apply_defaults () then
diff --git a/src/luaotfload-parsers.lua b/src/luaotfload-parsers.lua
index a52b5d4..151cb9e 100644
--- a/src/luaotfload-parsers.lua
+++ b/src/luaotfload-parsers.lua
@@ -2,26 +2,14 @@
 -------------------------------------------------------------------------------
 --         FILE:  luaotfload-parsers.lua
 --  DESCRIPTION:  various lpeg-based parsers used in Luaotfload
--- REQUIREMENTS:  Luaotfload > 2.4
---       AUTHOR:  Philipp Gesang (Phg), <phg42.2a@gmail.com>
+-- REQUIREMENTS:  Luaotfload > 2.6
+--       AUTHOR:  Philipp Gesang (Phg), <phg@phi-gamma.net>
 --      VERSION:  same as Luaotfload
 --      CREATED:  2014-01-14 10:15:20+0100
 -------------------------------------------------------------------------------
 --
 
-if not modules then modules = { } end modules ['luaotfload-parsers'] = {
-  version   = "2.5",
-  comment   = "companion to luaotfload-main.lua",
-  author    = "Philipp Gesang",
-  copyright = "Luaotfload Development Team",
-  license   = "GNU GPL v2.0"
-}
-
-luaotfload                 = luaotfload or { }
-luaotfload.parsers         = luaotfload.parsers or { }
-local parsers              = luaotfload.parsers
-parsers.traversal_maxdepth = 42 --- prevent stack overflows
-local traversal_maxdepth   = parsers.traversal_maxdepth --- TODO could be an option
+local traversal_maxdepth  = 42 --- prevent stack overflows
 
 local rawset            = rawset
 
@@ -42,8 +30,7 @@ local filedirname       = file.dirname
 local io                = io
 local ioopen            = io.open
 
-local log               = luaotfload.log
-local logreport         = log.report
+local logreport         = print
 
 local string            = string
 local stringsub         = string.sub
@@ -399,10 +386,6 @@ local read_fonts_conf = function (path_list, find_files)
   return acc
 end
 
-luaotfload.parsers.read_fonts_conf = read_fonts_conf
-
-
-
 -------------------------------------------------------------------------------
 ---                               MISC PARSERS
 -------------------------------------------------------------------------------
@@ -410,10 +393,8 @@ luaotfload.parsers.read_fonts_conf = read_fonts_conf
 
 local trailingslashes   = slash^1 * P(-1)
 local stripslashes      = C((1 - trailingslashes)^0)
-parsers.stripslashes    = stripslashes
 
 local splitcomma        = Ct((C(noncomma^1) + comma)^1)
-parsers.splitcomma      = splitcomma
 
 
 
@@ -653,8 +634,6 @@ local font_request      = Ct(path_lookup   * (colon^-1 * features)^-1
 --- v2.5 parser: 1065 rules
 --- v1.2 parser:  230 rules
 
-luaotfload.parsers.font_request = font_request
-
 -------------------------------------------------------------------------------
 ---                                INI FILES
 -------------------------------------------------------------------------------
@@ -731,7 +710,7 @@ local ini_variables     = Cg (Cf (Ct "" * ini_variable^0, rawset), "variables")
 
 local ini_section       = Ct (ini_heading * ini_variables)
 local ini_sections      = skip_line^0 * ini_section^0
-local config            = Ct (ini_sections)
+local parse_config      = Ct (ini_sections)
 
 --[=[doc--
 
@@ -763,6 +742,22 @@ local config            = Ct (ini_sections)
 
 --doc]=]--
 
-luaotfload.parsers.config = config
+return {
+  init = function ()
+    logreport = luaotfload.log.report
+    luaotfload.parsers = {
+      --- parameters
+      traversal_maxdepth    = traversal_maxdepth,
+      --- main parsers
+      read_fonts_conf       = read_fonts_conf,
+      font_request          = font_request,
+      config                = parse_config,
+      --- common patterns
+      stripslashes          = stripslashes,
+      splitcomma            = splitcomma,
+    }
+    return true
+  end
+}
 
 -- vim:ft=lua:tw=71:et:sw=2:sts=4:ts=8
-- 
cgit v1.2.3


From 974d9c6a280e42d01eb4a7c810900f9b4855e919 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Sun, 27 Sep 2015 18:43:41 +0200
Subject: [main, conf] prepare for deferred loading

---
 src/luaotfload-configuration.lua | 57 ++++++++++++++++++++--------------------
 src/luaotfload-main.lua          | 22 ++++++++--------
 2 files changed, 40 insertions(+), 39 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua
index e2cfbd8..263c8ad 100644
--- a/src/luaotfload-configuration.lua
+++ b/src/luaotfload-configuration.lua
@@ -6,7 +6,6 @@
 --       AUTHOR:  Philipp Gesang (Phg), <phg42.2a@gmail.com>
 --       AUTHOR:  Dohyun Kim <nomosnomos@gmail.com>
 --      VERSION:  same as Luaotfload
---     MODIFIED:  2015-05-05
 -------------------------------------------------------------------------------
 --
 
@@ -18,17 +17,12 @@ if not modules then modules = { } end modules ["luaotfload-configuration"] = {
   license   = "GNU GPL v2.0"
 }
 
-luaotfload                    = luaotfload or { }
-config                        = config or { }
-config.luaotfload             = { }
-
 local status_file             = "luaotfload-status"
 local luaotfloadstatus        = require (status_file)
 
-local stringexplode           = string.explode
+local string                  = string
 local stringfind              = string.find
 local stringformat            = string.format
-local string                  = string
 local stringstrip             = string.strip
 local stringsub               = string.sub
 
@@ -55,9 +49,7 @@ local lpegmatch               = lpeg.match
 local commasplitter           = lpeg.splitat ","
 local equalssplitter          = lpeg.splitat "="
 
-local kpse                    = kpse
 local kpseexpand_path         = kpse.expand_path
-local kpselookup              = kpse.lookup
 
 local lfs                     = lfs
 local lfsisfile               = lfs.isfile
@@ -67,16 +59,12 @@ local file                    = file
 local filejoin                = file.join
 local filereplacesuffix       = file.replacesuffix
 
+local logreport               = print -- overloaded later
+local getwritablepath         = caches.getwritablepath
 
-local parsers                 = luaotfload.parsers
-
-local log                     = luaotfload.log
-local logreport               = log.report
-
-local config_parser           = parsers.config
-local stripslashes            = parsers.stripslashes
 
-local getwritablepath         = caches.getwritablepath
+local config_parser -- set later during init
+local stripslashes  -- set later during init
 
 -------------------------------------------------------------------------------
 ---                                SETTINGS
@@ -301,8 +289,11 @@ local set_name_resolver = function ()
 end
 
 local set_loglevel = function ()
-  log.set_loglevel (config.luaotfload.run.log_level)
-  return true
+  if luaotfload then
+    luaotfload.log.set_loglevel (config.luaotfload.run.log_level)
+    return true
+  end
+  return false
 end
 
 local build_cache_paths = function ()
@@ -846,7 +837,7 @@ local read = function (extra)
     return false
   end
 
-  local parsed = lpegmatch (parsers.config, raw)
+  local parsed = lpegmatch (config_parser, raw)
   if not parsed then
     logreport ("both", 2, "conf", "Error parsing configuration file %q.", readme)
     return false
@@ -912,13 +903,23 @@ end
 ---                                 EXPORTS
 -------------------------------------------------------------------------------
 
-luaotfload.default_config = default_config
-
-config.actions = {
-  read             = read,
-  apply            = apply,
-  apply_defaults   = apply_defaults,
-  reconfigure      = reconfigure,
-  dump             = dump,
+return {
+  init = function ()
+    config.luaotfload = { }
+    logreport         = luaotfload.log.report
+    local parsers     = luaotfload.parsers
+    config_parser     = parsers.config
+    stripslashes      = parsers.stripslashes
+
+    luaotfload.default_config = default_config
+    config.actions = {
+      read             = read,
+      apply            = apply,
+      apply_defaults   = apply_defaults,
+      reconfigure      = reconfigure,
+      dump             = dump,
+    }
+    return true
+  end
 }
 
diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua
index 3005f5a..73f9a75 100644
--- a/src/luaotfload-main.lua
+++ b/src/luaotfload-main.lua
@@ -15,6 +15,7 @@
 
 local initial_log_level           = 0
 luaotfload                        = luaotfload or { }
+config                            = config     or { }
 local luaotfload                  = luaotfload
 luaotfload.log                    = luaotfload.log or { }
 luaotfload.version                = "2.6"
@@ -150,22 +151,21 @@ local logreport       = log.report
 
 --doc]]--
 
-local tmp = load_luaotfload_module "parsers" --- fonts.conf and syntax
-if not tmp.init () then
-    logreport ("log", 0, "load", "Failed to install the parsers.")
-end
-
-load_luaotfload_module "configuration"   --- configuration options
-
-if not config.actions.apply_defaults () then
-    logreport ("log", 0, "load", "Configuration unsuccessful.")
-end
-
 luaotfload.init.main (store)
 
 luaotfload.main = function ()
     local starttime = os.gettimeofday ()
 
+    local tmp = load_luaotfload_module "parsers" --- fonts.conf and syntax
+    if not tmp.init () then
+        logreport ("log", 0, "load", "Failed to install the parsers.")
+    end
+
+    local tmp = load_luaotfload_module "configuration"   --- configuration options
+    if not tmp.init() or not config.actions.apply_defaults () then
+        logreport ("log", 0, "load", "Configuration unsuccessful.")
+    end
+
     luaotfload.loaders = load_luaotfload_module "loaders" --- Font loading; callbacks
     if not luaotfload.loaders.install () then
         logreport ("log", 0, "load", "Callback and loader initialization failed.")
-- 
cgit v1.2.3


From 145203842d83591fc9e67322472994c481d5aadc Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Sun, 27 Sep 2015 18:50:22 +0200
Subject: [main] move toplevel statements into init routine

---
 src/luaotfload-main.lua | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua
index 73f9a75..9e57dbf 100644
--- a/src/luaotfload-main.lua
+++ b/src/luaotfload-main.lua
@@ -139,26 +139,21 @@ local load_fontloader_module = make_loader "fontloader"
 luaotfload.loaders.luaotfload = load_luaotfload_module
 luaotfload.loaders.fontloader = load_fontloader_module
 
-luaotfload.init = load_luaotfload_module "init" --- fontloader initialization
-
-local store           = luaotfload.init.early ()
-local log             = luaotfload.log
-local logreport       = log.report
-
 --[[doc--
 
     Now we load the modules written for \identifier{luaotfload}.
 
 --doc]]--
 
-luaotfload.init.main (store)
-
 luaotfload.main = function ()
     local starttime = os.gettimeofday ()
+    local init      = load_luaotfload_module "init" --- fontloader initialization
+    local store     = init.early ()                 --- injects the log module too
+    local logreport = luaotfload.log.report
 
     local tmp = load_luaotfload_module "parsers" --- fonts.conf and syntax
     if not tmp.init () then
-        logreport ("log", 0, "load", "Failed to install the parsers.")
+        logreport ("log", 0, "load", "Failed to install the parsers module.")
     end
 
     local tmp = load_luaotfload_module "configuration"   --- configuration options
@@ -166,6 +161,10 @@ luaotfload.main = function ()
         logreport ("log", 0, "load", "Configuration unsuccessful.")
     end
 
+    if not init.main (store) then
+        logreport ("log", 0, "load", "Main fontloader initialization failed.")
+    end
+
     luaotfload.loaders = load_luaotfload_module "loaders" --- Font loading; callbacks
     if not luaotfload.loaders.install () then
         logreport ("log", 0, "load", "Callback and loader initialization failed.")
-- 
cgit v1.2.3


From 1497cfdbee7193ab84f87daa6229b54be3209cd4 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Sun, 27 Sep 2015 19:18:06 +0200
Subject: [main, db] convert for deferred initialization

Also clean up the local name of the logger for consistency with the rest
of the code base.
---
 src/luaotfload-database.lua | 440 +++++++++++++++++++++++---------------------
 1 file changed, 226 insertions(+), 214 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua
index f4aab16..d9d7594 100644
--- a/src/luaotfload-database.lua
+++ b/src/luaotfload-database.lua
@@ -45,7 +45,7 @@ local stripslashes             = parsers.stripslashes
 local splitcomma               = parsers.splitcomma
 
 local log                      = luaotfload.log
-local report                   = log.report
+local logreport                = log and log.report or print -- overriden later on
 local report_status            = log.names_status
 local report_status_start      = log.names_status_start
 local report_status_stop       = log.names_status_stop
@@ -119,19 +119,10 @@ local tablefastcopy            = table.fastcopy
 local tabletofile              = table.tofile
 local tabletohash              = table.tohash
 local tableserialize           = table.serialize
---- the font loader namespace is “fonts”, same as in Context
---- we need to put some fallbacks into place for when running
---- as a script
-fonts                          = fonts          or { }
-fonts.names                    = fonts.names    or { }
-fonts.definers                 = fonts.definers or { }
-
-local names                    = fonts.names
+local names                    = fonts and fonts.names or { }
+
 local name_index               = nil --> upvalue for names.data
 local lookup_cache             = nil --> for names.lookups
-names.version                  = 2.51
-names.data                     = nil      --- contains the loaded database
-names.lookups                  = nil      --- contains the lookup cache
 
 --- string -> (string * string)
 local make_luanames = function (path)
@@ -485,12 +476,12 @@ load_names = function (dry_run, no_rebuild)
     local foundname, data = load_lua_file (config.luaotfload.paths.index_path_lua)
 
     if data then
-        report ("log", 0, "db",
-                "Font names database loaded from %s", foundname)
-        report ("term", 3, "db",
-                "Font names database loaded from %s", foundname)
-        report ("info", 3, "db", "Loading took %0.f ms.",
-                1000 * (osgettimeofday () - starttime))
+        logreport ("log", 0, "db",
+                   "Font names database loaded from %s", foundname)
+        logreport ("term", 3, "db",
+                   "Font names database loaded from %s", foundname)
+        logreport ("info", 3, "db", "Loading took %0.f ms.",
+                   1000 * (osgettimeofday () - starttime))
 
         local db_version, names_version
         if data.meta then
@@ -503,32 +494,32 @@ load_names = function (dry_run, no_rebuild)
         end
         names_version = names.version
         if db_version ~= names_version then
-            report ("both", 0, "db",
-                    [[Version mismatch; expected %4.3f, got %4.3f.]],
-                    names_version, db_version)
+            logreport ("both", 0, "db",
+                       [[Version mismatch; expected %4.3f, got %4.3f.]],
+                       names_version, db_version)
             if not fonts_reloaded then
-                report ("both", 0, "db", [[Force rebuild.]])
+                logreport ("both", 0, "db", [[Force rebuild.]])
                 data = update_names ({ }, true, false)
                 if not data then
-                    report ("both", 0, "db",
-                            "Database creation unsuccessful.")
+                    logreport ("both", 0, "db",
+                               "Database creation unsuccessful.")
                 end
             end
         end
     else
         if no_rebuild == true then
-            report ("both", 2, "db",
-                    [[Database does not exist, skipping rebuild though.]])
+            logreport ("both", 2, "db",
+                       [[Database does not exist, skipping rebuild though.]])
             return false
         end
-        report ("both", 0, "db",
-                [[Font names database not found, generating new one.]])
-        report ("both", 0, "db",
-                [[This can take several minutes; please be patient.]])
+        logreport ("both", 0, "db",
+                   [[Font names database not found, generating new one.]])
+        logreport ("both", 0, "db",
+                   [[This can take several minutes; please be patient.]])
         data = update_names (initialize_namedata (get_font_filter ()),
                              nil, dry_run)
         if not data then
-            report ("both", 0, "db", "Database creation unsuccessful.")
+            logreport ("both", 0, "db", "Database creation unsuccessful.")
         end
     end
     return data
@@ -559,12 +550,12 @@ local load_lookups
 load_lookups = function ( )
     local foundname, data = load_lua_file(config.luaotfload.paths.lookup_path_lua)
     if data then
-        report("log", 0, "cache", "Lookup cache loaded from %s.", foundname)
-        report("term", 3, "cache",
-               "Lookup cache loaded from %s.", foundname)
+        logreport ("log", 0, "cache", "Lookup cache loaded from %s.", foundname)
+        logreport ("term", 3, "cache",
+                   "Lookup cache loaded from %s.", foundname)
     else
-        report("both", 1, "cache",
-               "No lookup cache, creating empty.")
+        logreport ("both", 1, "cache",
+                   "No lookup cache, creating empty.")
         data = { }
     end
     lookup_cache = data
@@ -780,23 +771,24 @@ local lookup_font_name_cached
 lookup_font_name_cached = function (specification)
     if not lookup_cache then load_lookups () end
     local request = hash_request(specification)
-    report("both", 4, "cache", "Looking for %q in cache ...",
-           request)
+    logreport ("both", 4, "cache", "Looking for %q in cache ...",
+               request)
 
     local found = lookup_cache [request]
 
     --- case 1) cache positive ----------------------------------------
     if found then --- replay fields from cache hit
-        report("info", 4, "cache", "Found!")
+        logreport ("info", 4, "cache", "Found!")
         local basename = found[1]
         --- check the presence of the file in case it’s been removed
         local success = verify_font_file (basename)
         if success == true then
             return basename, found[2], true
         end
-        report("both", 4, "cache", "Cached file not found; resolving again.")
+        logreport ("both", 4, "cache",
+                   "Cached file not found; resolving again.")
     else
-        report("both", 4, "cache", "Not cached; resolving.")
+        logreport ("both", 4, "cache", "Not cached; resolving.")
     end
 
     --- case 2) cache negative ----------------------------------------
@@ -807,16 +799,16 @@ lookup_font_name_cached = function (specification)
     end
     --- ... then we add the fields to the cache ... ...
     local entry = { filename, subfont }
-    report("both", 4, "cache", "New entry: %s.", request)
+    logreport ("both", 4, "cache", "New entry: %s.", request)
     lookup_cache [request] = entry
 
     --- obviously, the updated cache needs to be stored.
     --- TODO this should trigger a save only once the
     ---      document is compiled (finish_pdffile callback?)
-    report("both", 5, "cache", "Saving updated cache.")
+    logreport ("both", 5, "cache", "Saving updated cache.")
     local success = save_lookups ()
     if not success then --- sad, but not critical
-        report("both", 0, "cache", "Error writing cache.")
+        logreport ("both", 0, "cache", "Error writing cache.")
     end
     return filename, subfont
 end
@@ -973,8 +965,8 @@ local lookup_familyname = function (specification, name, style, askedsize)
     if not success then
         return nil, nil
     end
-    report ("info", 2, "db", "Match found: %s(%d).",
-            resolved, subfont or 0)
+    logreport ("info", 2, "db", "Match found: %s(%d).",
+               resolved, subfont or 0)
     return resolved, subfont
 end
 
@@ -1140,9 +1132,9 @@ reload_db = function (why, caller, ...)
     local namedata  = name_index
     local formats   = tableconcat (namedata.meta.formats, ",")
 
-    report ("both", 0, "db",
-            "Reload initiated (formats: %s); reason: %q.",
-            formats, why)
+    logreport ("both", 0, "db",
+               "Reload initiated (formats: %s); reason: %q.",
+               formats, why)
 
     set_font_filter (formats)
     namedata = update_names (namedata, false, false)
@@ -1153,7 +1145,7 @@ reload_db = function (why, caller, ...)
         return caller (...)
     end
 
-    report ("both", 0, "db", "Database update unsuccessful.")
+    logreport ("both", 0, "db", "Database update unsuccessful.")
 end
 
 --- string -> string -> int
@@ -1237,15 +1229,15 @@ find_closest = function (name, limit)
     if n_distances > 0 then --- got some data
         tablesort(distances)
         limit = mathmin(n_distances, limit)
-        report(false, 1, "query",
-               "Displaying %d distance levels.", limit)
+        logreport (false, 1, "query",
+                   "Displaying %d distance levels.", limit)
 
         for i = 1, limit do
             local dist     = distances[i]
             local namelst  = by_distance[dist]
-            report(false, 0, "query",
-                   "Distance from \"%s\": %s\n    "
-                   .. tableconcat (namelst, "\n    "),
+            logreport (false, 0, "query",
+                       "Distance from \"%s\": %s\n    "
+                       .. tableconcat (namelst, "\n    "),
                    name, dist)
         end
 
@@ -1273,7 +1265,7 @@ local load_font_file = function (filename, subfont)
     local rawfont, _msg = fontloaderopen (filename, subfont)
     --local rawfont, _msg = fontloaderinfo (filename, subfont)
     if not rawfont then
-        report ("log", 1, "db", "ERROR: failed to open %s.", filename)
+        logreport ("log", 1, "db", "ERROR: failed to open %s.", filename)
         return
     end
     return rawfont
@@ -1316,8 +1308,8 @@ local get_english_names = function (metadata)
     end
 
     -- no (English) names table, probably a broken font
-    report("both", 3, "db",
-            "%s: missing or broken English names table.", basename)
+    logreport ("both", 3, "db",
+               "%s: missing or broken English names table.", basename)
     return { fontname = metadata.fontname,
              fullname = metadata.fullname, }
 end
@@ -1343,9 +1335,9 @@ local get_raw_info = function (metadata, basename)
         --- Broken names table, e.g. avkv.ttf with UTF-16 strings;
         --- we put some dummies in place like the fontloader
         --- (font-otf.lua) does.
-        report("both", 3, "db",
-               "%s has invalid postscript font names, using dummies.",
-               basename)
+        logreport ("both", 3, "db",
+                   "%s has invalid postscript font names, using dummies.",
+                   basename)
         fontname = "bad-fontname-" .. basename
         fullname = "bad-fullname-" .. basename
     end
@@ -1619,7 +1611,7 @@ local compare_timestamps = function (fullname,
 
     if targetentrystatus ~= nil
     and targetentrystatus.timestamp == targettimestamp then
-        report ("log", 3, "db", "Font %q already read.", fullname)
+        logreport ("log", 3, "db", "Font %q already read.", fullname)
         return false
     end
 
@@ -1641,7 +1633,7 @@ local compare_timestamps = function (fullname,
             targetentrystatus.index [targetindex + 1] = location
         end
 
-        report ("log", 3, "db", "Font %q already indexed.", fullname)
+        logreport ("log", 3, "db", "Font %q already indexed.", fullname)
 
         return false
     end
@@ -1721,8 +1713,8 @@ local read_font_names = function (fullname,
     --- 1) skip if blacklisted
 
     if names.blacklist[fullname] or names.blacklist[basename] then
-        report("log", 2, "db",
-               "Ignoring blacklisted font %q.", fullname)
+        logreport ("log", 2, "db",
+                   "Ignoring blacklisted font %q.", fullname)
         return false
     end
 
@@ -1745,8 +1737,8 @@ local read_font_names = function (fullname,
     local loader    = loaders [format] --- ot_fullinfo, t1_fullinfo
 
     if not loader then
-        report ("both", 0, "db",
-                "Unknown format: %q, skipping.", format)
+        logreport ("both", 0, "db",
+                   "Unknown format: %q, skipping.", format)
         return false
     end
 
@@ -1755,8 +1747,8 @@ local read_font_names = function (fullname,
     local info = fontloaderinfo (fullname)
 
     if not info then
-        report ("log", 1, "db",
-                "Failed to read basic information from %q", basename)
+        logreport ("log", 1, "db",
+                   "Failed to read basic information from %q", basename)
         return false
     end
 
@@ -1830,9 +1822,7 @@ end
 
 fonts.path_normalize = path_normalize
 
-names.blacklist = { }
-
-local blacklist   = names.blacklist
+local blacklist = { }
 local p_blacklist --- prefixes of dirs
 
 --- string list -> string list
@@ -1861,8 +1851,8 @@ local create_blacklist = function (blacklist, whitelist)
     local result = { }
     local dirs   = { }
 
-    report("info", 2, "db", "Blacklisting %d files and directories.",
-           #blacklist)
+    logreport ("info", 2, "db", "Blacklisting %d files and directories.",
+               #blacklist)
     for i=1, #blacklist do
         local entry = blacklist[i]
         if lfsisdir(entry) then
@@ -1872,7 +1862,7 @@ local create_blacklist = function (blacklist, whitelist)
         end
     end
 
-    report("info", 2, "db", "Whitelisting %d files.", #whitelist)
+    logreport ("info", 2, "db", "Whitelisting %d files.", #whitelist)
     for i=1, #whitelist do
         result[whitelist[i]] = nil
     end
@@ -1914,9 +1904,9 @@ read_blacklist = function ()
                 if first_chr == "%" or stringis_empty(line) then
                     -- comment or empty line
                 elseif first_chr == "-" then
-                    report ("both", 3, "db",
-                            "Whitelisted file %q via %q.",
-                            line, path)
+                    logreport ("both", 3, "db",
+                               "Whitelisted file %q via %q.",
+                               line, path)
                     whitelist[#whitelist+1] = stringsub(line, 2, -1)
                 else
                     local cmt = stringfind(line, "%%")
@@ -1924,9 +1914,9 @@ read_blacklist = function ()
                         line = stringsub(line, 1, cmt - 1)
                     end
                     line = stringstrip(line)
-                    report ("both", 3, "db",
-                            "Blacklisted file %q via %q.",
-                            line, path)
+                    logreport ("both", 3, "db",
+                               "Blacklisted file %q via %q.",
+                               line, path)
                     blacklist[#blacklist+1] = line
                 end
             end
@@ -2123,24 +2113,24 @@ end
 --- string -> string -> string * string list
 local collect_font_filenames_dir = function (dirname, location)
     if lpegmatch (p_blacklist, dirname) then
-        report ("both", 4, "db",
-                "Skipping blacklisted directory %s.", dirname)
+        logreport ("both", 4, "db",
+                   "Skipping blacklisted directory %s.", dirname)
         --- ignore
         return { }
     end
     local found = find_font_files (dirname, location ~= "texmf" and location ~= "local")
     if not found then
-        report ("both", 4, "db",
-                "No such directory: %q; skipping.", dirname)
+        logreport ("both", 4, "db",
+                   "No such directory: %q; skipping.", dirname)
         return { }
     end
 
     local nfound = #found
     local files  = { }
 
-    report ("both", 4, "db",
-            "%d font files detected in %s.",
-            nfound, dirname)
+    logreport ("both", 4, "db",
+               "%d font files detected in %s.",
+               nfound, dirname)
     for j = 1, nfound do
         local fullname = found[j]
         files[#files + 1] = { path_normalize (fullname), location }
@@ -2184,14 +2174,14 @@ local collect_font_filenames_texmf = function ()
     local osfontdir = kpseexpand_path "$OSFONTDIR"
 
     if stringis_empty (osfontdir) then
-        report ("info", 1, "db", "Scanning TEXMF for fonts...")
+        logreport ("info", 1, "db", "Scanning TEXMF for fonts...")
     else
-        report ("info", 1, "db", "Scanning TEXMF and $OSFONTDIR for fonts...")
+        logreport ("info", 1, "db", "Scanning TEXMF and $OSFONTDIR for fonts...")
         if log.get_loglevel () > 3 then
             local osdirs = filesplitpath (osfontdir)
-            report ("info", 0, "db", "$OSFONTDIR has %d entries:", #osdirs)
+            logreport ("info", 0, "db", "$OSFONTDIR has %d entries:", #osdirs)
             for i = 1, #osdirs do
-                report ("info", 0, "db", "[%d] %s", i, osdirs[i])
+                logreport ("info", 0, "db", "[%d] %s", i, osdirs[i])
             end
         end
     end
@@ -2205,14 +2195,14 @@ local collect_font_filenames_texmf = function ()
     end
 
     local tasks = filter_out_pwd (filesplitpath (fontdirs))
-    report ("info", 3, "db",
-            "Initiating scan of %d directories.", #tasks)
+    logreport ("info", 3, "db",
+               "Initiating scan of %d directories.", #tasks)
 
     local files = { }
     for _, dir in next, tasks do
         files = tableappend (files, collect_font_filenames_dir (dir, "texmf"))
     end
-    report ("term", 3, "db", "Collected %d files.", #files)
+    logreport ("term", 3, "db", "Collected %d files.", #files)
     return files
 end
 
@@ -2247,14 +2237,14 @@ end
 
 --- string list -> size_t
 local count_removed = function (old)
-    report("log", 4, "db", "Checking removed files.")
+    logreport ("log", 4, "db", "Checking removed files.")
     local nrem = 0
     local nold = #old
     for i = 1, nold do
         local f = old[i]
         if not kpsereadable_file (f) then
-            report("log", 2, "db",
-                   "File %s does not exist in file system.")
+            logreport ("log", 2, "db",
+                      "File %s does not exist in file system.")
             nrem = nrem + 1
         end
     end
@@ -2281,7 +2271,7 @@ local retrieve_namedata = function (files, currentnames, targetnames, dry_run)
     local nfiles    = #files
     local nnew      = 0
 
-    report ("info", 1, "db", "Scanning %d collected font files ...", nfiles)
+    logreport ("info", 1, "db", "Scanning %d collected font files ...", nfiles)
 
     local bylocation = { texmf     = { 0, 0 }
                        , ["local"] = { 0, 0 }
@@ -2294,12 +2284,12 @@ local retrieve_namedata = function (files, currentnames, targetnames, dry_run)
         count[1]                   = count[1] + 1
         if dry_run == true then
             local truncated = truncate_string (fullname, 43)
-            report ("log", 2, "db", "Would have been loading %s.", fullname)
+            logreport ("log", 2, "db", "Would have been loading %s.", fullname)
             report_status ("term", "db", "Would have been loading %s", truncated)
             --- skip the read_font_names part
         else
             local truncated = truncate_string (fullname, 32)
-            report ("log", 2, "db", "Loading font %s.", fullname)
+            logreport ("log", 2, "db", "Loading font %s.", fullname)
             report_status ("term", "db", "Loading font %s", truncated)
             local new = read_font_names (fullname, currentnames,
                                          targetnames, location)
@@ -2311,8 +2301,8 @@ local retrieve_namedata = function (files, currentnames, targetnames, dry_run)
     end
     report_status_stop ("term", "db", "Scanned %d files, %d new.", nfiles, nnew)
     for location, count in next, bylocation do
-        report ("term", 4, "db", "   * %s: %d files, %d new",
-                location, count[1], count[2])
+        logreport ("term", 4, "db", "   * %s: %d files, %d new",
+                   location, count[1], count[2])
     end
     return nnew
 end
@@ -2321,15 +2311,15 @@ end
 local collect_font_filenames_system = function ()
 
     local n_scanned, n_new = 0, 0
-    report ("info", 1, "db", "Scanning system fonts...")
-    report ("info", 2, "db",
-            "Searching in static system directories...")
+    logreport ("info", 1, "db", "Scanning system fonts...")
+    logreport ("info", 2, "db",
+               "Searching in static system directories...")
 
     local files = { }
     for _, dir in next, get_os_dirs () do
         tableappend (files, collect_font_filenames_dir (dir, "system"))
     end
-    report ("term", 3, "db", "Collected %d files.", #files)
+    logreport ("term", 3, "db", "Collected %d files.", #files)
     return files
 end
 
@@ -2355,25 +2345,25 @@ end
 --- unit -> string * string list
 local collect_font_filenames_local = function ()
     local pwd = lfscurrentdir ()
-    report ("both", 1, "db", "Scanning for fonts in $PWD (%q) ...", pwd)
+    logreport ("both", 1, "db", "Scanning for fonts in $PWD (%q) ...", pwd)
 
     local files  = collect_font_filenames_dir (pwd, "local")
     local nfiles = #files
     if nfiles > 0 then
         targetnames.meta["local"] = true --- prevent saving to disk
-        report ("term", 1, "db", "Found %d files.", pwd)
+        logreport ("term", 1, "db", "Found %d files.", pwd)
     else
-        report ("term", 1, "db",
-                "Couldn’t find a thing here. What a waste.", pwd)
+        logreport ("term", 1, "db",
+                   "Couldn’t find a thing here. What a waste.", pwd)
     end
-    report ("term", 3, "db", "Collected %d files.", #files)
+    logreport ("term", 3, "db", "Collected %d files.", #files)
     return files
 end
 
 --- fontentry list -> filemap
 generate_filedata = function (mappings)
 
-    report ("both", 2, "db", "Creating filename map.")
+    logreport ("both", 2, "db", "Creating filename map.")
 
     local nmappings  = #mappings
 
@@ -2435,10 +2425,10 @@ generate_filedata = function (mappings)
         if inbase then
             local present = inbase [basename]
             if present then
-                report ("both", 4, "db",
-                        "Conflicting basename: %q already indexed \z
-                         in category %s, ignoring.",
-                        barename, location)
+                logreport ("both", 4, "db",
+                           "Conflicting basename: %q already indexed \z
+                            in category %s, ignoring.",
+                           barename, location)
                 conflicts.basenames = conflicts.basenames + 1
 
                 --- track conflicts per font
@@ -2465,10 +2455,10 @@ generate_filedata = function (mappings)
         if inbare then
             local present = inbare [barename]
             if present then
-                report ("both", 4, "db",
-                        "Conflicting barename: %q already indexed \z
-                         in category %s/%s, ignoring.",
-                        barename, location, format)
+                logreport ("both", 4, "db",
+                           "Conflicting barename: %q already indexed \z
+                            in category %s/%s, ignoring.",
+                           barename, location, format)
                 conflicts.barenames = conflicts.barenames + 1
 
                 --- track conflicts per font
@@ -2650,7 +2640,7 @@ end
 
 collect_families = function (mappings)
 
-    report ("info", 2, "db", "Analyzing families.")
+    logreport ("info", 2, "db", "Analyzing families.")
 
     local families = {
         ["local"]  = { },
@@ -2746,7 +2736,7 @@ local style_categories   = { "r", "b", "i", "bi" }
 local bold_categories    = {      "b",      "bi" }
 
 group_modifiers = function (mappings, families)
-    report ("info", 2, "db", "Analyzing shapes, weights, and styles.")
+    logreport ("info", 2, "db", "Analyzing shapes, weights, and styles.")
     for location, location_data in next, families do
         for format, format_data in next, location_data do
             for familyname, collected in next, format_data do
@@ -2845,7 +2835,7 @@ end
 
 order_design_sizes = function (families)
 
-    report ("info", 2, "db", "Ordering design sizes.")
+    logreport ("info", 2, "db", "Ordering design sizes.")
 
     for location, data in next, families do
         for format, data in next, data do
@@ -2870,7 +2860,7 @@ end
 --- unit -> string * string list
 local collect_font_filenames = function ()
 
-    report ("info", 4, "db", "Scanning the filesystem for font files.")
+    logreport ("info", 4, "db", "Scanning the filesystem for font files.")
 
     local filenames = { }
     local bisect    = config.luaotfload.misc.bisect
@@ -2900,7 +2890,7 @@ end
 
 --- int -> string
 local nth_font_filename = function (n)
-    report ("info", 4, "db", "Picking font file no. %d.", n)
+    logreport ("info", 4, "db", "Picking font file no. %d.", n)
     if not p_blacklist then
         read_blacklist ()
     end
@@ -2915,7 +2905,7 @@ end
 --doc]]--
 
 local font_slice = function (lo, hi)
-    report ("info", 4, "db", "Retrieving font files nos. %d--%d.", lo, hi)
+    logreport ("info", 4, "db", "Retrieving font files nos. %d--%d.", lo, hi)
     if not p_blacklist then
         read_blacklist ()
     end
@@ -2937,7 +2927,7 @@ end
 
 --- unit -> int
 local count_font_files = function ()
-    report ("info", 4, "db", "Counting font files.")
+    logreport ("info", 4, "db", "Counting font files.")
     if not p_blacklist then
         read_blacklist ()
     end
@@ -3063,31 +3053,31 @@ local collect_statistics = function (mappings)
                     itemlist = tableconcat (itemlist, ", ")
                 end
 
-                report ("both", 0, "db",
-                        "       · %4d × %s.",
-                        freq, itemlist)
+                logreport ("both", 0, "db",
+                           "       · %4d × %s.",
+                           freq, itemlist)
             end
         end
 
-        report ("both", 0, "", "~~~~ font index statistics ~~~~")
-        report ("both", 0, "db",
-                "   · Collected %d fonts (%d names) in %d families.",
-                #mappings, n_fullname, n_family)
+        logreport ("both", 0, "", "~~~~ font index statistics ~~~~")
+        logreport ("both", 0, "db",
+                   "   · Collected %d fonts (%d names) in %d families.",
+                   #mappings, n_fullname, n_family)
         pprint_top (families, 4, true)
 
-        report ("both", 0, "db",
-                "   · %d different “subfamily” kinds.",
-                setsize (subfamily))
+        logreport ("both", 0, "db",
+                   "   · %d different “subfamily” kinds.",
+                   setsize (subfamily))
         pprint_top (subfamily, 4)
 
-        report ("both", 0, "db",
-                "   · %d different “prefmodifiers” kinds.",
-                setsize (prefmodifiers))
+        logreport ("both", 0, "db",
+                   "   · %d different “prefmodifiers” kinds.",
+                   setsize (prefmodifiers))
         pprint_top (prefmodifiers, 4)
 
-        report ("both", 0, "db",
-                "   · %d different “fontstyle_name” kinds.",
-                setsize (fontstyle_name))
+        logreport ("both", 0, "db",
+                   "   · %d different “fontstyle_name” kinds.",
+                   setsize (fontstyle_name))
         pprint_top (fontstyle_name, 4)
     end
 
@@ -3121,7 +3111,7 @@ update_names = function (currentnames, force, dry_run)
 
     local conf = config.luaotfload
     if conf.run.live ~= false and conf.db.update_live == false then
-        report ("info", 2, "db", "Skipping database update.")
+        logreport ("info", 2, "db", "Skipping database update.")
         --- skip all db updates
         return currentnames or name_index
     end
@@ -3133,15 +3123,16 @@ update_names = function (currentnames, force, dry_run)
     - “targetnames” is the final table to return
     - force is whether we rebuild it from scratch or not
     ]]
-    report("both", 1, "db", "Updating the font names database"
-                         .. (force and " forcefully." or "."))
+    logreport ("both", 1, "db",
+               "Updating the font names database"
+               .. (force and " forcefully." or "."))
 
     if config.luaotfload.db.skip_read == true then
         --- the difference to a “dry run” is that we don’t search
         --- for font files entirely. we also ignore the “force”
         --- parameter since it concerns only the font files.
-        report ("info", 2, "db",
-                "Ignoring font files, reusing old data.")
+        logreport ("info", 2, "db",
+                   "Ignoring font files, reusing old data.")
         currentnames = load_names (false)
         targetnames  = currentnames
     else
@@ -3152,8 +3143,9 @@ update_names = function (currentnames, force, dry_run)
                 currentnames = load_names (dry_run)
             end
             if currentnames.meta.version ~= names.version then
-                report ("both", 1, "db", "No font names database or old "
-                                    .. "one found; generating new one.")
+                logreport ("both", 1, "db",
+                           "No font names database or old \z
+                            one found; generating new one.")
                 currentnames = initialize_namedata (get_font_filter ())
             end
         end
@@ -3176,9 +3168,9 @@ update_names = function (currentnames, force, dry_run)
                                    targetnames,
                                    dry_run)
 
-        report ("info", 3, "db",
-                "Found %d font files; %d new, %d stale entries.",
-                #font_filenames, n_new, n_rem)
+        logreport ("info", 3, "db",
+                   "Found %d font files; %d new, %d stale entries.",
+                   #font_filenames, n_new, n_rem)
     end
 
     --- pass 3 (optional): collect some stats about the raw font info
@@ -3204,27 +3196,27 @@ update_names = function (currentnames, force, dry_run)
     --- pass 7: order design size tables
     targetnames.families    = order_design_sizes (targetnames.families)
 
-    report ("info", 3, "db",
-            "Rebuilt in %0.f ms.",
-            1000 * (osgettimeofday () - starttime))
+    logreport ("info", 3, "db",
+               "Rebuilt in %0.f ms.",
+               1000 * (osgettimeofday () - starttime))
     name_index = targetnames
 
     if dry_run ~= true then
 
         if n_new + n_rem == 0 then
-            report ("info", 2, "db",
-                    "No new or removed fonts, skip saving to disk.")
+            logreport ("info", 2, "db",
+                       "No new or removed fonts, skip saving to disk.")
         else
             local success, reason = save_names ()
             if not success then
-                report ("both", 0, "db",
-                        "Failed to save database to disk: %s",
-                        reason)
+                logreport ("both", 0, "db",
+                           "Failed to save database to disk: %s",
+                           reason)
             end
         end
 
         if flush_lookup_cache () and save_lookups () then
-            report ("both", 2, "cache", "Lookup cache emptied.")
+            logreport ("both", 2, "cache", "Lookup cache emptied.")
             return targetnames
         end
     end
@@ -3241,18 +3233,18 @@ save_lookups = function ( )
         caches.compile (lookup_cache, luaname, lucname)
         --- double check ...
         if lfsisfile (luaname) and lfsisfile (lucname) then
-            report ("both", 3, "cache", "Lookup cache saved.")
+            logreport ("both", 3, "cache", "Lookup cache saved.")
             return true
         end
-        report ("info", 0, "cache", "Could not compile lookup cache.")
+        logreport ("info", 0, "cache", "Could not compile lookup cache.")
         return false
     end
-    report ("info", 0, "cache", "Lookup cache file not writable.")
+    logreport ("info", 0, "cache", "Lookup cache file not writable.")
     if not fileiswritable (luaname) then
-        report ("info", 0, "cache", "Failed to write %s.", luaname)
+        logreport ("info", 0, "cache", "Failed to write %s.", luaname)
     end
     if not fileiswritable (lucname) then
-        report ("info", 0, "cache", "Failed to write %s.", lucname)
+        logreport ("info", 0, "cache", "Failed to write %s.", lucname)
     end
     return false
 end
@@ -3281,33 +3273,33 @@ save_names = function (currentnames)
             tabletofile (luaname, currentnames, true)
             caches.compile (currentnames, luaname, lucname)
         end
-        report ("info", 2, "db", "Font index saved at ...")
+        logreport ("info", 2, "db", "Font index saved at ...")
         local success = false
         if lfsisfile (luaname) then
-            report ("info", 2, "db", "Text: " .. luaname)
+            logreport ("info", 2, "db", "Text: " .. luaname)
             success = true
         end
         if lfsisfile (gzname) then
-            report ("info", 2, "db", "Gzip: " .. gzname)
+            logreport ("info", 2, "db", "Gzip: " .. gzname)
             success = true
         end
         if lfsisfile (lucname) then
-            report ("info", 2, "db", "Byte: " .. lucname)
+            logreport ("info", 2, "db", "Byte: " .. lucname)
             success = true
         end
         if success then
             return true
         else
-            report ("info", 0, "db", "Could not compile font index.")
+            logreport ("info", 0, "db", "Could not compile font index.")
             return false
         end
     end
-    report ("info", 0, "db", "Index file not writable")
+    logreport ("info", 0, "db", "Index file not writable")
     if not fileiswritable (luaname) then
-        report ("info", 0, "db", "Failed to write %s.", luaname)
+        logreport ("info", 0, "db", "Failed to write %s.", luaname)
     end
     if not fileiswritable (lucname) then
-        report ("info", 0, "db", "Failed to write %s.", lucname)
+        logreport ("info", 0, "db", "Failed to write %s.", lucname)
     end
     return false
 end
@@ -3321,7 +3313,7 @@ end
 --- string -> string -> string list -> string list -> string list -> unit
 local print_cache = function (category, path, luanames, lucnames, rest)
     local report_indeed = function (...)
-        report("info", 0, "cache", ...)
+        logreport ("info", 0, "cache", ...)
     end
     report_indeed("Luaotfload cache: %s", category)
     report_indeed("location: %s", path)
@@ -3333,15 +3325,15 @@ end
 
 --- string -> string -> string list -> bool -> bool
 local purge_from_cache = function (category, path, list, all)
-    report("info", 1, "cache", "Luaotfload cache: %s %s",
-        (all and "erase" or "purge"), category)
-    report("info", 1, "cache", "location: %s",path)
+    logreport ("info", 1, "cache", "Luaotfload cache: %s %s",
+               (all and "erase" or "purge"), category)
+    logreport ("info", 1, "cache", "location: %s", path)
     local n = 0
     for i=1,#list do
         local filename = list[i]
         if stringfind(filename,"luatex%-cache") then -- safeguard
             if all then
-                report("info", 5, "cache", "Removing %s.", filename)
+                logreport ("info", 5, "cache", "Removing %s.", filename)
                 osremove(filename)
                 n = n + 1
             else
@@ -3350,7 +3342,7 @@ local purge_from_cache = function (category, path, list, all)
                     local checkname = file.replacesuffix(
                         filename, "lua", "luc")
                     if lfsisfile(checkname) then
-                        report("info", 5, "cache", "Removing %s.", filename)
+                        logreport ("info", 5, "cache", "Removing %s.", filename)
                         osremove(filename)
                         n = n + 1
                     end
@@ -3358,7 +3350,7 @@ local purge_from_cache = function (category, path, list, all)
             end
         end
     end
-    report("info", 1, "cache", "Removed lua files : %i", n)
+    logreport ("info", 1, "cache", "Removed lua files : %i", n)
     return true
 end
 
@@ -3435,7 +3427,7 @@ local erase_cache = function ( )
 end
 
 local separator = function ( )
-    report("info", 0, string.rep("-", 67))
+    logreport ("info", 0, string.rep("-", 67))
 end
 
 --- unit -> unit
@@ -3464,35 +3456,55 @@ end
 --- export functionality to the namespace “fonts.names”
 -----------------------------------------------------------------------
 
-names.set_font_filter             = set_font_filter
-names.flush_lookup_cache          = flush_lookup_cache
-names.save_lookups                = save_lookups
-names.load                        = load_names
-names.access_font_index           = access_font_index
-names.data                        = function () return name_index end
-names.save                        = save_names
-names.update                      = update_names
-names.lookup_font_file            = lookup_font_file
-names.lookup_font_name            = lookup_font_name
-names.lookup_font_name_cached     = lookup_font_name_cached
-names.getfilename                 = lookup_fullpath
-names.lookup_fullpath             = lookup_fullpath
-names.read_blacklist              = read_blacklist
-names.sanitize_fontname           = sanitize_fontname
-names.getmetadata                 = getmetadata
-names.set_location_precedence     = set_location_precedence
-names.count_font_files            = count_font_files
-names.nth_font_filename           = nth_font_filename
-names.font_slice                  = font_slice
-
---- font cache
-names.purge_cache    = purge_cache
-names.erase_cache    = erase_cache
-names.show_cache     = show_cache
-
-names.find_closest = find_closest
-
--- for testing purpose
-names.read_fonts_conf = read_fonts_conf
+local export = {
+    set_font_filter             = set_font_filter,
+    flush_lookup_cache          = flush_lookup_cache,
+    save_lookups                = save_lookups,
+    load                        = load_names,
+    access_font_index           = access_font_index,
+    data                        = function () return name_index end,
+    save                        = save_names,
+    update                      = update_names,
+    lookup_font_file            = lookup_font_file,
+    lookup_font_name            = lookup_font_name,
+    lookup_font_name_cached     = lookup_font_name_cached,
+    getfilename                 = lookup_fullpath,
+    lookup_fullpath             = lookup_fullpath,
+    read_blacklist              = read_blacklist,
+    sanitize_fontname           = sanitize_fontname,
+    getmetadata                 = getmetadata,
+    set_location_precedence     = set_location_precedence,
+    count_font_files            = count_font_files,
+    nth_font_filename           = nth_font_filename,
+    font_slice                  = font_slice,
+    --- font cache
+    purge_cache                 = purge_cache,
+    erase_cache                 = erase_cache,
+    show_cache                  = show_cache,
+    find_closest                = find_closest,
+    -- for testing purpose
+    read_fonts_conf             = read_fonts_conf,
+}
+
+return {
+    init = function ()
+        --- the font loader namespace is “fonts”, same as in Context
+        --- we need to put some fallbacks into place for when running
+        --- as a script
+        log             = luaotfload.log
+        logreport       = log.report
+        fonts           = fonts or { }
+        local fonts     = fonts
+        fonts.names     = fonts.names or names
+        fonts.definers  = fonts.definers or { }
+
+        names.blacklist = blacklist
+        names.version   = 2.51
+        names.data      = nil      --- contains the loaded database
+        names.lookups   = nil      --- contains the lookup cache
+
+        for sym, ref in next, export do names[sym] = ref end
+    end
+}
 
 -- vim:tw=71:sw=4:ts=4:expandtab
-- 
cgit v1.2.3


From 780113609ffbb146c1b5a825cb25025246cdba2a Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Sun, 27 Sep 2015 20:17:02 +0200
Subject: [main, *] convert for centralized initialization routine

---
 src/luaotfload-colors.lua        | 78 +++++++++++++++++---------------
 src/luaotfload-configuration.lua |  4 ++
 src/luaotfload-database.lua      |  6 +--
 src/luaotfload-features.lua      | 81 +++++++++++++++++----------------
 src/luaotfload-init.lua          |  2 +
 src/luaotfload-loaders.lua       |  2 +-
 src/luaotfload-main.lua          | 96 ++++++++++++++++++++++++++--------------
 7 files changed, 157 insertions(+), 112 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-colors.lua b/src/luaotfload-colors.lua
index 89884b6..a0b80bd 100644
--- a/src/luaotfload-colors.lua
+++ b/src/luaotfload-colors.lua
@@ -19,8 +19,7 @@ explanation: http://tug.org/pipermail/luatex/2013-May/004305.html
 
 --doc]]--
 
-local log                   = luaotfload.log
-local logreport             = log.report
+local logreport             = luaotfload and luaotfload.log.report or print
 
 local nodedirect            = node.direct
 local newnode               = nodedirect.new
@@ -44,10 +43,7 @@ local texsettoks            = tex.settoks
 local texgettoks            = tex.gettoks
 
 local stringformat          = string.format
-
-local otffeatures           = fonts.constructors.newfeatures("otf")
 local identifiers           = fonts.hashes.identifiers
-local registerotffeature    = otffeatures.register
 
 local add_color_callback --[[ this used to be a global‽ ]]
 
@@ -101,36 +97,6 @@ local sanitize_color_expression = function (digits)
     return sanitized
 end
 
---[[doc--
-``setcolor`` modifies tfmdata.properties.color in place
---doc]]--
-
---- fontobj -> string -> unit
----
----         (where “string” is a rgb value as three octet
----         hexadecimal, with an optional fourth transparency
----         value)
----
-local setcolor = function (tfmdata, value)
-    local sanitized  = sanitize_color_expression(value)
-    local properties = tfmdata.properties
-
-    if sanitized then
-        properties.color = sanitized
-        add_color_callback()
-    end
-end
-
-registerotffeature {
-    name        = "color",
-    description = "color",
-    initializers = {
-        base = setcolor,
-        node = setcolor,
-    }
-}
-
-
 --- something is carried around in ``res``
 --- for later use by color_handler() --- but what?
 
@@ -377,5 +343,47 @@ add_color_callback = function ( )
     end
 end
 
+--[[doc--
+``setcolor`` modifies tfmdata.properties.color in place
+--doc]]--
+
+--- fontobj -> string -> unit
+---
+---         (where “string” is a rgb value as three octet
+---         hexadecimal, with an optional fourth transparency
+---         value)
+---
+local setcolor = function (tfmdata, value)
+    local sanitized  = sanitize_color_expression(value)
+    local properties = tfmdata.properties
+
+    if sanitized then
+        properties.color = sanitized
+        add_color_callback()
+    end
+end
+
+return {
+    init = function ()
+        logreport = luaotfload.log.report
+        if not fonts then
+            logreport ("log", 0, "color",
+                       "OTF mechanisms missing -- did you forget to \z
+                       load a font loader?")
+            return false
+        end
+        fonts.handlers.otf.features.register {
+            name        = "color",
+            description = "color",
+            initializers = {
+                base = setcolor,
+                node = setcolor,
+            }
+        }
+        return true
+    end
+}
+
+
 -- vim:tw=71:sw=4:ts=4:expandtab
 
diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua
index 263c8ad..86a302e 100644
--- a/src/luaotfload-configuration.lua
+++ b/src/luaotfload-configuration.lua
@@ -919,6 +919,10 @@ return {
       reconfigure      = reconfigure,
       dump             = dump,
     }
+    if not apply_defaults () then
+      logreport ("log", 0, "load",
+                 "Configuration unsuccessful: error loading default settings.")
+    end
     return true
   end
 }
diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua
index d9d7594..4af2451 100644
--- a/src/luaotfload-database.lua
+++ b/src/luaotfload-database.lua
@@ -3491,9 +3491,8 @@ return {
         --- the font loader namespace is “fonts”, same as in Context
         --- we need to put some fallbacks into place for when running
         --- as a script
-        log             = luaotfload.log
-        logreport       = log.report
-        fonts           = fonts or { }
+        if not fonts then return false end
+        logreport       = luaotfload.log.report
         local fonts     = fonts
         fonts.names     = fonts.names or names
         fonts.definers  = fonts.definers or { }
@@ -3504,6 +3503,7 @@ return {
         names.lookups   = nil      --- contains the lookup cache
 
         for sym, ref in next, export do names[sym] = ref end
+        return true
     end
 }
 
diff --git a/src/luaotfload-features.lua b/src/luaotfload-features.lua
index 9b895ce..6fb2114 100644
--- a/src/luaotfload-features.lua
+++ b/src/luaotfload-features.lua
@@ -921,24 +921,13 @@ end
 
 ---[[ end included font-ltx.lua ]]
 
---[[doc--
-This uses the code from luatex-fonts-merged (<- font-otc.lua) instead
-of the removed luaotfload-font-otc.lua.
-
-TODO find out how far we get setting features without these lines,
-relying on luatex-fonts only (it *does* handle features somehow, after
-all).
---doc]]--
-
--- we assume that the other otf stuff is loaded already
+-- We assume that the other otf stuff is loaded already; though there’s
+-- another check below during the initialization phase.
 
 ---[[ begin snippet from font-otc.lua ]]
 local trace_loading       = false  trackers.register("otf.loading", function(v) trace_loading = v end)
 local report_otf          = logs.reporter("fonts","otf loading")
 
-local otf                 = fonts.handlers.otf
-local registerotffeature  = otf.features.register
-
 --[[HH--
 
    In the userdata interface we can not longer tweak the loaded font as
@@ -960,7 +949,7 @@ setmetatableindex(types, function(t,k) t[k] = k return k end) -- "key"
 local everywhere = { ["*"] = { ["*"] = true } } -- or: { ["*"] = { "*" } }
 local noflags    = { }
 
-local function addfeature(data,feature,specifications)
+local function addfeature (data, feature, specifications)
     local descriptions = data.descriptions
     local resources    = data.resources
     local lookups      = resources.lookups
@@ -1100,26 +1089,9 @@ local function addfeature(data,feature,specifications)
     end
 end
 
-
-otf.enhancers.addfeature = addfeature
-
-local extrafeatures = { }
-
-function otf.addfeature(name,specification)
-    extrafeatures[name] = specification
-end
-
-local function enhance(data,filename,raw)
-    for feature, specification in next, extrafeatures do
-        addfeature(data,feature,specification)
-    end
-end
-
-otf.enhancers.register("check extra features",enhance)
-
 ---[[ end snippet from font-otc.lua ]]
 
-local tlig = {
+local tlig_specification = {
     {
         type      = "substitution",
         features  = everywhere,
@@ -1167,9 +1139,6 @@ local tlig = {
     },
 }
 
-otf.addfeature ("tlig", tlig)
-otf.addfeature ("trep", { })
-
 local anum_arabic = { --- these are the same as in font-otc
     [0x0030] = 0x0660,
     [0x0031] = 0x0661,
@@ -1228,11 +1197,45 @@ local anum_specification = {
     },
 }
 
-otf.addfeature ("anum", anum_specification)
+return {
+    init = function ()
+
+        if not fonts and fonts.handlers then
+            logreport ("log", 0, "color",
+                       "OTF mechanisms missing -- did you forget to \z
+                       load a font loader?")
+            return false
+        end
+
+        local otf = fonts.handlers.otf
 
-registerotffeature {
-    name        = "anum",
-    description = "arabic digits",
+        local extrafeatures = {
+            tlig = tlig_specification,
+            trep = { },
+            anum = anum_specification,
+        }
+
+        otf.enhancers.register ("check extra features",
+                                function (data,filename, raw)
+                                    for feature, specification in next, extrafeatures do
+                                        addfeature (data, feature, specification)
+                                    end
+                                end)
+
+        logreport = luaotfload.log.report
+        if not fonts then
+            logreport ("log", 0, "color",
+                       "OTF mechanisms missing -- did you forget to \z
+                       load a font loader?")
+            return false
+        end
+
+        otf.features.register {
+            name        = "anum",
+            description = "arabic digits",
+        }
+        return true
+    end
 }
 
 -- vim:tw=71:sw=4:ts=4:expandtab
diff --git a/src/luaotfload-init.lua b/src/luaotfload-init.lua
index a5e5bec..af71cb2 100644
--- a/src/luaotfload-init.lua
+++ b/src/luaotfload-init.lua
@@ -431,6 +431,8 @@ return {
                os.gettimeofday() - starttime)
     local n = init_post ()
     logreport ("both", 5, "init", "post hook terminated, %d actions performed", n)
+    return true
   end
 }
 
+-- vim:tw=79:sw=2:ts=2:expandtab
diff --git a/src/luaotfload-loaders.lua b/src/luaotfload-loaders.lua
index 44216d7..89a9fff 100644
--- a/src/luaotfload-loaders.lua
+++ b/src/luaotfload-loaders.lua
@@ -126,7 +126,7 @@ local install_callbacks = function ()
 end
 
 return {
-  install = function ()
+  init = function ()
     local ret = true
     if not install_formats () then
       logreport ("log", 0, "loaders", "Error initializing OFM/PF{A,B} loaders.")
diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua
index 9e57dbf..815a2f0 100644
--- a/src/luaotfload-main.lua
+++ b/src/luaotfload-main.lua
@@ -13,6 +13,9 @@
 --- version 2.4 to 2.5. Thus, the comments are still in TeX (Latex)
 --- markup.
 
+local os                          = os
+local osgettimeofday              = os.gettimeofday
+
 local initial_log_level           = 0
 luaotfload                        = luaotfload or { }
 config                            = config     or { }
@@ -125,70 +128,95 @@ local make_loader_name = function (prefix, name)
     return name
 end
 
+local timing_info = {
+    t_load = { },
+    t_init = { },
+}
+
 local make_loader = function (prefix)
     return function (name)
+        local t_0 = osgettimeofday ()
         local modname = make_loader_name (prefix, name)
-        return require (modname)
+        local data = require (modname)
+        local t_end = osgettimeofday ()
+        timing_info.t_load [name] = t_end - t_0
+        return data
     end
 end
 
-local load_luaotfload_module = make_loader "luaotfload"
------ load_luaotfload_module = make_loader "luatex" --=> for Luatex-Plain
-local load_fontloader_module = make_loader "fontloader"
+local install_loaders = function ()
+    local loaders      = { }
+    local loadmodule   = make_loader "luaotfload"
+    loaders.luaotfload = loadmodule
+    loaders.fontloader = make_loader "fontloader"
+----loaders.plaintex   = make_loader "luatex" --=> for Luatex-Plain
+
+    loaders.initialize = function (name)
+        local tmp       = loadmodule (name)
+        local logreport = luaotfload.log.report
+        if type (tmp) == "table" then
+            local init = tmp.init
+            if init and type (init) == "function" then
+                local t_0 = osgettimeofday ()
+                if not init () then
+                    logreport ("log", 0, "load",
+                               "Failed to load module “%s”.", name)
+                    return
+                end
+                local t_end = osgettimeofday ()
+                local d_t = t_end - t_0
+                logreport ("log", 4, "load",
+                           "Module “%s” loaded in %d ms.",
+                           d_t)
+                timing_info.t_init [name] = d_t
+            end
+        end
+    end
 
-luaotfload.loaders.luaotfload = load_luaotfload_module
-luaotfload.loaders.fontloader = load_fontloader_module
+    return loaders
+end
 
---[[doc--
 
-    Now we load the modules written for \identifier{luaotfload}.
+luaotfload.main = function ()
 
---doc]]--
+    luaotfload.loaders = install_loaders ()
+    local loaders    = luaotfload.loaders
+    local loadmodule = loaders.luaotfload
+    local initialize = loaders.initialize
 
-luaotfload.main = function ()
-    local starttime = os.gettimeofday ()
-    local init      = load_luaotfload_module "init" --- fontloader initialization
-    local store     = init.early ()                 --- injects the log module too
+    local starttime = osgettimeofday ()
+    local init      = loadmodule "init" --- fontloader initialization
+    local store     = init.early ()     --- injects the log module too
     local logreport = luaotfload.log.report
 
-    local tmp = load_luaotfload_module "parsers" --- fonts.conf and syntax
-    if not tmp.init () then
-        logreport ("log", 0, "load", "Failed to install the parsers module.")
-    end
-
-    local tmp = load_luaotfload_module "configuration"   --- configuration options
-    if not tmp.init() or not config.actions.apply_defaults () then
-        logreport ("log", 0, "load", "Configuration unsuccessful.")
-    end
+    initialize "parsers"         --- fonts.conf and syntax
+    initialize "configuration"   --- configuration options
 
     if not init.main (store) then
         logreport ("log", 0, "load", "Main fontloader initialization failed.")
     end
 
-    luaotfload.loaders = load_luaotfload_module "loaders" --- Font loading; callbacks
-    if not luaotfload.loaders.install () then
-        logreport ("log", 0, "load", "Callback and loader initialization failed.")
-    end
-
-    load_luaotfload_module "database"        --- Font management.
-    load_luaotfload_module "colors"          --- Per-font colors.
+    initialize "loaders"         --- Font loading; callbacks
+    initialize "database"        --- Font management.
+    initialize "colors"          --- Per-font colors.
 
-    luaotfload.resolvers = load_luaotfload_module "resolvers" --- Font lookup
+    luaotfload.resolvers = loadmodule "resolvers" --- Font lookup
     luaotfload.resolvers.install ()
 
     if not config.actions.reconfigure () then
         logreport ("log", 0, "load", "Post-configuration hooks failed.")
     end
 
-    load_luaotfload_module "features"     --- font request and feature handling
-    load_luaotfload_module "letterspace"  --- extra character kerning
-    load_luaotfload_module "auxiliary"    --- additional high-level functionality
+    initialize "features"     --- font request and feature handling
+    loadmodule "letterspace"  --- extra character kerning
+    loadmodule "auxiliary"    --- additional high-level functionality
 
     luaotfload.aux.start_rewrite_fontname () --- to be migrated to fontspec
 
     logreport ("both", 0, "main",
                "initialization completed in %0.3f seconds",
-               os.gettimeofday() - starttime)
+               osgettimeofday() - starttime)
+----inspect (timing_info)
 end
 
 -- vim:tw=79:sw=4:ts=4:et
-- 
cgit v1.2.3


From 120c852ccd7a1579310ed855a4d8b1322a1f9d09 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Wed, 28 Oct 2015 06:48:50 +0100
Subject: main: fix module insertion and throw away luatexbase-provided
 resources

---
 src/luaotfload-main.lua | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua
index 815a2f0..3d68a17 100644
--- a/src/luaotfload-main.lua
+++ b/src/luaotfload-main.lua
@@ -72,16 +72,9 @@ local luatexbase       = luatexbase
 local require          = require
 local type             = type
 
-local error, warning, info, log =
+local _error, _warning, _info, _log =
     luatexbase.provides_module(luaotfload.module)
 
-luaotfload.log.tex        = {
-    error        = error,
-    warning      = warning,
-    info         = info,
-    log          = log,
-}
-
 --[[doc--
 
      We set the minimum version requirement for \LUATEX to v0.76,
@@ -167,7 +160,7 @@ local install_loaders = function ()
                 local d_t = t_end - t_0
                 logreport ("log", 4, "load",
                            "Module “%s” loaded in %d ms.",
-                           d_t)
+                           name, d_t)
                 timing_info.t_init [name] = d_t
             end
         end
-- 
cgit v1.2.3


From d2e389383564c7f7faf9dd93976e5d548924e1e4 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Wed, 28 Oct 2015 06:54:43 +0100
Subject: [db] fix behavior when starting with no data

---
 src/luaotfload-database.lua | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua
index 4af2451..1845643 100644
--- a/src/luaotfload-database.lua
+++ b/src/luaotfload-database.lua
@@ -354,7 +354,7 @@ local initialize_namedata = function (formats, created)
         status          = { }, -- was: status; map abspath -> mapping
         mappings        = { }, -- TODO: check if still necessary after rewrite
         names           = { },
---      files           = { }, -- created later
+        files           = { }, -- created later
         meta            = {
             created    = created or now,
             formats    = formats,
@@ -2236,7 +2236,12 @@ end
 --doc]]--
 
 --- string list -> size_t
-local count_removed = function (old)
+local count_removed = function (files)
+    if not files or not files.full then
+        logreport ("log", 4, "db", "Empty file store; no data to work with.")
+        return 0
+    end
+    local old = files.full
     logreport ("log", 4, "db", "Checking removed files.")
     local nrem = 0
     local nold = #old
@@ -3161,7 +3166,7 @@ update_names = function (currentnames, force, dry_run)
         --- pass 2: read font files (normal case) or reuse information
         --- present in index
 
-        n_rem = count_removed (currentnames.files.full)
+        n_rem = count_removed (currentnames.files)
 
         n_new = retrieve_namedata (font_filenames,
                                    currentnames,
-- 
cgit v1.2.3


From 811d385a2c903f001ceb719ae1a29ce4586b16b4 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Wed, 28 Oct 2015 06:56:07 +0100
Subject: [loaders] fix call to missing local

---
 src/luaotfload-loaders.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/luaotfload-loaders.lua b/src/luaotfload-loaders.lua
index 89a9fff..b644266 100644
--- a/src/luaotfload-loaders.lua
+++ b/src/luaotfload-loaders.lua
@@ -62,7 +62,7 @@ do
       --- operate on tfm fonts.
       luatexbase.call_callback ("luaotfload.patch_font", fontdata, specification)
     else
-      call_callback ("luaotfload.patch_font_unsafe", fontdata, specification)
+      luatexbase.call_callback ("luaotfload.patch_font_unsafe", fontdata, specification)
     end
     return fontdata
   end
-- 
cgit v1.2.3


From 5907d3909cdc508f7804434d4a88d3977530ed64 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Wed, 28 Oct 2015 07:03:11 +0100
Subject: [db] fix access to restructured globals

---
 src/luaotfload-database.lua | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua
index 1845643..b871954 100644
--- a/src/luaotfload-database.lua
+++ b/src/luaotfload-database.lua
@@ -39,11 +39,6 @@ if not modules then modules = { } end modules ['luaotfload-database'] = {
 local lpeg                     = require "lpeg"
 local P, Cc, lpegmatch         = lpeg.P, lpeg.Cc, lpeg.match
 
-local parsers                  = luaotfload.parsers
-local read_fonts_conf          = parsers.read_fonts_conf
-local stripslashes             = parsers.stripslashes
-local splitcomma               = parsers.splitcomma
-
 local log                      = luaotfload.log
 local logreport                = log and log.report or print -- overriden later on
 local report_status            = log.names_status
@@ -1820,8 +1815,6 @@ do
     end
 end
 
-fonts.path_normalize = path_normalize
-
 local blacklist = { }
 local p_blacklist --- prefixes of dirs
 
@@ -1953,6 +1946,9 @@ do
             return
         end
 
+        if splitcomma == nil then
+            splitcomma = luaotfload.parsers and luaotfload.parsers.splitcomma
+        end
         if stringsub (formats, 1, 1) == "+" then -- add
             formats = lpegmatch (splitcomma, stringsub (formats, 2))
             if formats then
@@ -2138,10 +2134,12 @@ local collect_font_filenames_dir = function (dirname, location)
     return files
 end
 
-
 --- string list -> string list
 local filter_out_pwd = function (dirs)
     local result = { }
+    if stripslashes == nil then
+        stripslashes = luaotfload.parsers and luaotfload.parsers.stripslashes
+    end
     local pwd = path_normalize (lpegmatch (stripslashes,
                                            lfscurrentdir ()))
     for i = 1, #dirs do
@@ -2223,7 +2221,10 @@ local function get_os_dirs ()
             "/usr/local/etc/fonts/fonts.conf",
             "/etc/fonts/fonts.conf",
         }
-        local os_dirs = read_fonts_conf(fonts_conves, find_files)
+        if not luaotfload.parsers then
+            logreport ("log", 0, "db", "Fatal: no fonts.conf parser.")
+        end
+        local os_dirs = luaotfload.parsers.read_fonts_conf(fonts_conves, find_files)
         return os_dirs
     end
     return {}
@@ -3488,7 +3489,6 @@ local export = {
     show_cache                  = show_cache,
     find_closest                = find_closest,
     -- for testing purpose
-    read_fonts_conf             = read_fonts_conf,
 }
 
 return {
-- 
cgit v1.2.3


From a29b5562c526a9079f09ea022db5fa4951c18c58 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Wed, 28 Oct 2015 07:09:22 +0100
Subject: [tool] fix interfacing with database

---
 src/luaotfload-tool.lua | 53 ++++++++++++++++++++++++-------------------------
 1 file changed, 26 insertions(+), 27 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua
index e240e4c..fdc6c62 100755
--- a/src/luaotfload-tool.lua
+++ b/src/luaotfload-tool.lua
@@ -143,15 +143,14 @@ require"luaotfload-basics-gen.lua"
 texio.write, texio.write_nl          = backup.write, backup.write_nl
 utilities                            = backup.utilities
 
+fonts = { names = { } } -- for db; normally provided by the fontloaders
+
 require "luaotfload-log.lua"       --- this populates the luaotfload.log.* namespace
 require "luaotfload-parsers"       --- fonts.conf, configuration, and request syntax
 require "luaotfload-configuration" --- configuration file handling
 require "luaotfload-database"
 require "alt_getopt"
 
-local names                          = fonts.names
-local sanitize_fontname              = names.sanitize_fontname
-
 local log                            = luaotfload.log
 local report                         = log.report
 
@@ -272,7 +271,7 @@ local about = [[
 local version_msg = function ( )
     local out   = function (...) texiowrite_nl (stringformat (...)) end
     local uname = os.uname ()
-    local meta  = names.getmetadata ()
+    local meta  = fonts.names.getmetadata ()
     out (about, luaotfload.self)
     out ("%s version: %q", luaotfload.self, version)
     out ("Revision: %q", config.luaotfload.status.notes.revision)
@@ -673,7 +672,7 @@ subfont_by_name = function (lst, askedname, n)
 
     local font = lst[n]
     if font then
-        if sanitize_fontname (font.fullname) == askedname then
+        if fonts.names.sanitize_fontname (font.fullname) == askedname then
             return font
         end
         return subfont_by_name (lst, askedname, n+1)
@@ -690,10 +689,10 @@ The font info knows two levels of detail:
 --doc]]--
 
 local show_font_info = function (basename, askedname, detail, warnings)
-    local filenames = names.data().files
+    local filenames = fonts.names.data().files
     local index     = filenames.base[basename]
     local fullname  = filenames.full[index]
-    askedname = sanitize_fontname (askedname)
+    askedname = fonts.names.sanitize_fontname (askedname)
     if not fullname then -- texmf
         fullname = resolvers.findfile(basename)
     end
@@ -797,17 +796,17 @@ actions.help = function (job)
 end
 
 actions.blacklist = function (job)
-    names.read_blacklist()
+    fonts.names.read_blacklist()
     local n = 0
-    for n, entry in next, tablesortedkeys(names.blacklist) do
+    for n, entry in next, tablesortedkeys(fonts.names.blacklist) do
         iowrite (stringformat("(%d %s)\n", n, entry))
     end
     return true, false
 end
 
 actions.generate = function (job)
-    local _ = names.update (fontnames, job.force_reload, job.dry_run)
-    local namedata = names.data ()
+    local _ = fonts.names.update (fontnames, job.force_reload, job.dry_run)
+    local namedata = fonts.names.data ()
     if namedata then
         report ("info", 2, "db", "Fonts in the database: %i", #namedata.mappings)
         return true, true
@@ -895,7 +894,7 @@ local bisect_start = function ()
     end
     report ("info", 2, "bisect",
             "Starting bisection of font database %q.", bisect_status_file)
-    local n     = names.count_font_files ()
+    local n     = fonts.names.count_font_files ()
     local pivot = mathfloor (n / 2)
     local data  = { { 1, n, pivot } }
     report ("info", 0, "bisect", "Initializing pivot to %d.", pivot)
@@ -946,7 +945,7 @@ local bisect_terminate = function (nsteps, culprit)
     report ("info", 1, "bisect",
             "Bisection completed after %d steps.", nsteps)
     report ("info", 0, "bisect",
-            "Bad file: %s.", names.nth_font_filename (culprit))
+            "Bad file: %s.", fonts.names.nth_font_filename (culprit))
     report ("info", 0, "bisect",
             "Run with --bisect=stop to finish bisection.")
     return true, false
@@ -959,7 +958,7 @@ end
 --doc]]--
 
 local list_remainder = function (lo, hi)
-    local fonts = names.font_slice (lo, hi)
+    local fonts = fonts.names.font_slice (lo, hi)
     report ("info", 0, "bisect", "%d fonts left.", hi - lo + 1)
     for i = 1, #fonts do
         report ("info", 1, "bisect", "   · %2d: %s", lo, fonts[i])
@@ -1110,9 +1109,9 @@ actions.bisect = function (job)
 end
 
 actions.flush = function (job)
-    local success = names.flush_lookup_cache()
+    local success = fonts.names.flush_lookup_cache()
     if success then
-        local success = names.save_lookups()
+        local success = fonts.names.save_lookups()
         if success then
             report ("info", 2, "cache", "Lookup cache emptied")
             return true, true
@@ -1122,9 +1121,9 @@ actions.flush = function (job)
 end
 
 local cache_directives = {
-    ["purge"] = names.purge_cache,
-    ["erase"] = names.erase_cache,
-    ["show"]  = names.show_cache,
+    ["purge"] = fonts.names.purge_cache,
+    ["erase"] = fonts.names.erase_cache,
+    ["show"]  = fonts.names.show_cache,
 }
 
 actions.cache = function (job)
@@ -1154,7 +1153,7 @@ actions.query = function (job)
         features      = { },
     }
 
-    tmpspec = names.handle_request (tmpspec)
+    tmpspec = fonts.names.handle_request (tmpspec)
 
     if not tmpspec.size then
         tmpspec.size = 655360 --- assume 10pt
@@ -1165,13 +1164,13 @@ actions.query = function (job)
     if tmpspec.lookup == "name"
     or tmpspec.lookup == "anon" --- not *exactly* as resolvers.anon
     then
-        foundname, subfont = names.resolve_name (tmpspec)
+        foundname, subfont = fonts.names.resolve_name (tmpspec)
         if foundname then
-            foundname, _, success = names.font_file_lookup (foundname)
+            foundname, _, success = fonts.names.font_file_lookup (foundname)
         end
     elseif tmpspec.lookup == "file" then
         foundname, _, success =
-            names.font_file_lookup (tmpspec.name)
+            fonts.names.font_file_lookup (tmpspec.name)
     end
 
     if success then
@@ -1196,7 +1195,7 @@ actions.query = function (job)
         if job.fuzzy == true then
             report (false, 0, "resolve",
                     "Looking for close matches, this may take a while ...")
-            local _success = names.find_closest(query, job.fuzzy_limit)
+            local _success = fonts.names.find_closest(query, job.fuzzy_limit)
         end
     end
     return true, true
@@ -1274,7 +1273,7 @@ local splitcomma = luaotfload.parsers.splitcomma
 actions.list = function (job)
     local criterion     = job.criterion
     local asked_fields  = job.asked_fields
-    local name_index    = names.data ()
+    local name_index    = fonts.names.data ()
 
     if asked_fields then
         asked_fields = lpegmatch(splitcomma, asked_fields)
@@ -1286,7 +1285,7 @@ actions.list = function (job)
     end
 
     if not name_index then
-        name_index = names.load()
+        name_index = fonts.names.load()
     end
 
     local mappings  = name_index.mappings
@@ -1528,7 +1527,7 @@ local process_cmdline = function ( ) -- unit -> jobspec
         elseif v == "D" then
             result.dry_run = true
         elseif v == "p" then
-            names.set_location_precedence {
+            fonts.names.set_location_precedence {
                 "local", "texmf", "system"
             }
         elseif v == "b" then
-- 
cgit v1.2.3


From 3b2349244c00df1393f3aedb910e7e458a52a7ea Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Wed, 28 Oct 2015 07:17:04 +0100
Subject: [tool] defer import of logger

---
 src/luaotfload-tool.lua | 207 ++++++++++++++++++++++++++----------------------
 1 file changed, 114 insertions(+), 93 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua
index fdc6c62..6b84a6d 100755
--- a/src/luaotfload-tool.lua
+++ b/src/luaotfload-tool.lua
@@ -151,8 +151,12 @@ require "luaotfload-configuration" --- configuration file handling
 require "luaotfload-database"
 require "alt_getopt"
 
-local log                            = luaotfload.log
-local report                         = log.report
+local logreport
+
+local init_modules = function ()
+    logreport = luaotfload.log.report
+end
+
 
 local help_messages = {
     ["luaotfload-tool"] = [[
@@ -702,9 +706,9 @@ local show_font_info = function (basename, askedname, detail, warnings)
         if nfonts > 0 then -- true type collection
             local subfont
             if askedname then
-                report (true, 1, "resolve",
-                        [[%s is part of the font collection %s]],
-                        askedname, basename)
+                logreport (true, 1, "resolve",
+                           [[%s is part of the font collection %s]],
+                           askedname, basename)
                 subfont = subfont_by_name(shortinfo, askedname)
             end
             if subfont then
@@ -713,11 +717,11 @@ local show_font_info = function (basename, askedname, detail, warnings)
                     show_full_info(fullname, subfont, warnings)
                 end
             else -- list all subfonts
-                report (true, 1, "resolve",
-                        [[%s is a font collection]], basename)
+                logreport (true, 1, "resolve",
+                           [[%s is a font collection]], basename)
                 for subfont = 1, nfonts do
-                    report (true, 1, "resolve",
-                            [[Showing info for font no. %d]], n)
+                    logreport (true, 1, "resolve",
+                               [[Showing info for font no. %d]], n)
                     show_info_items(shortinfo[subfont])
                     if detail == true then
                         show_full_info(fullname, subfont, warnings)
@@ -731,7 +735,7 @@ local show_font_info = function (basename, askedname, detail, warnings)
             end
         end
     else
-        report (true, 1, "resolve", "Font %s not found", filename)
+        logreport (true, 1, "resolve", "Font %s not found", filename)
     end
 end
 
@@ -759,9 +763,9 @@ local actions = { } --- (jobspec -> (bool * bool)) list
 actions.loglevel = function (job)
     local lvl = job.log_level
     if lvl then
-        log.set_loglevel(lvl)
-        report ("info", 3, "util", "Setting the log level to %d.", lvl)
-        report ("log", 2, "util", "Lua=%q", _VERSION)
+        luaotfload.log.set_loglevel(lvl)
+        logreport ("info", 3, "util", "Setting the log level to %d.", lvl)
+        logreport ("log", 2, "util", "Lua=%q", _VERSION)
     end
     return true, true
 end
@@ -808,7 +812,9 @@ actions.generate = function (job)
     local _ = fonts.names.update (fontnames, job.force_reload, job.dry_run)
     local namedata = fonts.names.data ()
     if namedata then
-        report ("info", 2, "db", "Fonts in the database: %i", #namedata.mappings)
+        logreport ("info", 2, "db",
+                   "Fonts in the database: %i",
+                   #namedata.mappings)
         return true, true
     end
     return false, false
@@ -844,12 +850,14 @@ local write_bisect_status = function (data)
                                   osdate ("%Y-%m-d %H:%M:%S", os.time ()),
                                   payload)
     if status and iosavedata (bisect_status_file, status) then
-        report ("info", 4, "bisect",
-                "Bisection state written to %s.", bisect_status_file)
+        logreport ("info", 4, "bisect",
+                   "Bisection state written to %s.",
+                   bisect_status_file)
         return true
     end
-    report ("info", 0, "bisect",
-            "Failed to write bisection state to %s.", bisect_status_file)
+    logreport ("info", 0, "bisect",
+               "Failed to write bisection state to %s.",
+               bisect_status_file)
     return false
 end
 
@@ -861,16 +869,22 @@ end
 
 --- unit -> state list
 local read_bisect_status = function ()
-    report ("info", 4, "bisect", "Testing for status file: %q.", bisect_status_file)
+    logreport ("info", 4, "bisect",
+               "Testing for status file: %q.",
+               bisect_status_file)
     if not lfsisfile (bisect_status_file) then
-        report ("info", 2, "bisect", "No such file: %q.", bisect_status_file)
-        report ("info", 0, "bisect", "Not in bisect mode.")
+        logreport ("info", 2, "bisect",
+                   "No such file: %q.", bisect_status_file)
+        logreport ("info", 0, "bisect",
+                   "Not in bisect mode.")
         return false
     end
-    report ("info", 4, "bisect", "Reading status file: %q.", bisect_status_file)
+    logreport ("info", 4, "bisect",
+               "Reading status file: %q.", bisect_status_file)
     local success, status = pcall (dofile, bisect_status_file)
     if not success then
-        report ("info", 0, "bisect", "Could not read status file.")
+        logreport ("info", 0, "bisect",
+                   "Could not read status file.")
         return false
     end
     return status
@@ -885,19 +899,21 @@ end
 
 local bisect_start = function ()
     if lfsisfile (bisect_status_file) then
-        report ("info", 0, "bisect",
-                "Bisect session in progress.",
-                bisect_status_file)
-        report ("info", 0, "bisect",
-                "Use --bisect=stop to erase it before starting over.")
+        logreport ("info", 0, "bisect",
+                   "Bisect session in progress.",
+                   bisect_status_file)
+        logreport ("info", 0, "bisect",
+                   "Use --bisect=stop to erase it before starting over.")
         return false, false
     end
-    report ("info", 2, "bisect",
-            "Starting bisection of font database %q.", bisect_status_file)
+    logreport ("info", 2, "bisect",
+               "Starting bisection of font database %q.",
+               bisect_status_file)
     local n     = fonts.names.count_font_files ()
     local pivot = mathfloor (n / 2)
     local data  = { { 1, n, pivot } }
-    report ("info", 0, "bisect", "Initializing pivot to %d.", pivot)
+    logreport ("info", 0, "bisect",
+               "Initializing pivot to %d.", pivot)
     if write_bisect_status (data) then
         return true, false
     end
@@ -911,21 +927,23 @@ end
 --doc]]--
 
 local bisect_stop = function ()
-    report ("info", 3, "bisect", "Erasing bisection state at %s.", bisect_status_file)
+    logreport ("info", 3, "bisect",
+               "Erasing bisection state at %s.",
+               bisect_status_file)
     if lfsisfile (bisect_status_file) then
         local success, msg = os.remove (bisect_status_file)
         if not success then
-            report ("info", 2, "bisect",
-                    "Failed to erase file %s (%s).",
-                     bisect_status_file, msg)
+            logreport ("info", 2, "bisect",
+                       "Failed to erase file %s (%s).",
+                        bisect_status_file, msg)
         end
     end
     if lfsisdir (bisect_status_path) then
         local success, msg = os.remove (bisect_status_path)
         if not success then
-            report ("info", 2, "bisect",
-                    "Failed to erase directory %s (%s).",
-                     bisect_status_path, msg)
+            logreport ("info", 2, "bisect",
+                       "Failed to erase directory %s (%s).",
+                       bisect_status_path, msg)
         end
     end
     if lfsisfile (bisect_status_file) then
@@ -942,12 +960,12 @@ end
 --doc]]--
 
 local bisect_terminate = function (nsteps, culprit)
-    report ("info", 1, "bisect",
-            "Bisection completed after %d steps.", nsteps)
-    report ("info", 0, "bisect",
-            "Bad file: %s.", fonts.names.nth_font_filename (culprit))
-    report ("info", 0, "bisect",
-            "Run with --bisect=stop to finish bisection.")
+    logreport ("info", 1, "bisect",
+               "Bisection completed after %d steps.", nsteps)
+    logreport ("info", 0, "bisect",
+               "Bad file: %s.", fonts.names.nth_font_filename (culprit))
+    logreport ("info", 0, "bisect",
+               "Run with --bisect=stop to finish bisection.")
     return true, false
 end
 
@@ -959,9 +977,9 @@ end
 
 local list_remainder = function (lo, hi)
     local fonts = fonts.names.font_slice (lo, hi)
-    report ("info", 0, "bisect", "%d fonts left.", hi - lo + 1)
+    logreport ("info", 0, "bisect", "%d fonts left.", hi - lo + 1)
     for i = 1, #fonts do
-        report ("info", 1, "bisect", "   · %2d: %s", lo, fonts[i])
+        logreport ("info", 1, "bisect", "   · %2d: %s", lo, fonts[i])
         lo = lo + 1
     end
 end
@@ -994,8 +1012,9 @@ local bisect_set = function (outcome)
 
     local lo, hi, pivot = unpack (previous)
 
-    report ("info", 3, "bisect", "Previous step %d: lo=%d, hi=%d, pivot=%d.",
-            nsteps, lo, hi, pivot)
+    logreport ("info", 3, "bisect",
+               "Previous step %d: lo=%d, hi=%d, pivot=%d.",
+               nsteps, lo, hi, pivot)
 
     if outcome == "bad" then
         hi = pivot
@@ -1006,9 +1025,9 @@ local bisect_set = function (outcome)
             return bisect_terminate (nsteps, lo)
         end
         pivot = mathfloor ((lo + hi) / 2)
-        report ("info", 0, "bisect",
-                "Continuing with the lower segment: lo=%d, hi=%d, pivot=%d.",
-                lo, hi, pivot)
+        logreport ("info", 0, "bisect",
+                   "Continuing with the lower segment: lo=%d, hi=%d, pivot=%d.",
+                   lo, hi, pivot)
     elseif outcome == "good" then
         lo = pivot + 1
         if lo >= hi then --- complete
@@ -1018,11 +1037,12 @@ local bisect_set = function (outcome)
             return bisect_terminate (nsteps, lo)
         end
         pivot = mathfloor ((lo + hi) / 2)
-        report ("info", 0, "bisect",
-                "Continuing with the upper segment: lo=%d, hi=%d, pivot=%d.",
-                lo, hi, pivot)
+        logreport ("info", 0, "bisect",
+                   "Continuing with the upper segment: lo=%d, hi=%d, pivot=%d.",
+                   lo, hi, pivot)
     else -- can’t happen
-        report ("info", 0, "bisect", "What the hell?", lo, hi, pivot)
+        logreport ("info", 0, "bisect",
+                   "What the hell?", lo, hi, pivot)
         return false, false
     end
 
@@ -1049,13 +1069,13 @@ local bisect_status = function ()
     if nsteps > 1 then
         for i = nsteps - 1, 1, -1 do
             local step = status[i]
-            report ("info", 2, "bisect", "Step %d: lo=%d, hi=%d, pivot=%d.",
-                    i, unpack (step))
+            logreport ("info", 2, "bisect", "Step %d: lo=%d, hi=%d, pivot=%d.",
+                       i, unpack (step))
         end
     end
     local current = status[nsteps]
-    report ("info", 0, "bisect", "Step %d: lo=%d, hi=%d, pivot=%d.",
-            nsteps, unpack (current))
+    logreport ("info", 0, "bisect", "Step %d: lo=%d, hi=%d, pivot=%d.",
+               nsteps, unpack (current))
     return true, false
 end
 
@@ -1081,10 +1101,10 @@ local bisect_run = function ()
         current = status[nsteps - 1]
     end
     local lo, hi, pivot = unpack (current)
-    report ("info", 3, "bisect", "Previous step %d: lo=%d, hi=%d, pivot=%d.",
-            nsteps, lo, hi, pivot)
-    report ("info", 1, "bisect", "Step %d: Testing fonts from %d to %d.",
-            currentstep, lo, pivot)
+    logreport ("info", 3, "bisect", "Previous step %d: lo=%d, hi=%d, pivot=%d.",
+               nsteps, lo, hi, pivot)
+    logreport ("info", 1, "bisect", "Step %d: Testing fonts from %d to %d.",
+               currentstep, lo, pivot)
     config.luaotfload.misc.bisect = { lo, pivot }
     return true, true
 end
@@ -1102,7 +1122,7 @@ actions.bisect = function (job)
     local mode   = job.bisect
     local runner = bisect_modes[mode]
     if not runner then
-        report ("info", 0, "bisect", "Unknown directive %q.", mode)
+        logreport ("info", 0, "bisect", "Unknown directive %q.", mode)
         return false, false
     end
     return runner (job)
@@ -1113,7 +1133,7 @@ actions.flush = function (job)
     if success then
         local success = fonts.names.save_lookups()
         if success then
-            report ("info", 2, "cache", "Lookup cache emptied")
+            logreport ("info", 2, "cache", "Lookup cache emptied")
             return true, true
         end
     end
@@ -1129,8 +1149,8 @@ local cache_directives = {
 actions.cache = function (job)
     local directive = cache_directives[job.cache]
     if not directive or type(directive) ~= "function" then
-        report ("info", 2, "cache",
-                "Invalid font cache directive %s.", job.cache)
+        logreport ("info", 2, "cache",
+                   "Invalid font cache directive %s.", job.cache)
         return false, false
     end
     if directive() then
@@ -1174,27 +1194,27 @@ actions.query = function (job)
     end
 
     if success then
-        report (false, 0, "resolve", "Font %q found!", query)
+        logreport (false, 0, "resolve", "Font %q found!", query)
         if subfont then
-            report (false, 0, "resolve",
-                    "Resolved file name %q, subfont nr. %q",
-                foundname, subfont)
+            logreport (false, 0, "resolve",
+                       "Resolved file name %q, subfont nr. %q",
+                       foundname, subfont)
         else
-            report (false, 0, "resolve",
-                    "Resolved file name %q", foundname)
+            logreport (false, 0, "resolve",
+                       "Resolved file name %q", foundname)
         end
         if job.show_info then
             show_font_info (foundname, query, job.full_info, job.warnings)
             iowrite "\n"
         end
     else
-        report (false, 0, "resolve", "Cannot find %q in index.", query)
-        report (false, 0, "resolve",
-                "Hint: use the --fuzzy option to display suggestions.",
-                query)
+        logreport (false, 0, "resolve", "Cannot find %q in index.", query)
+        logreport (false, 0, "resolve",
+                   "Hint: use the --fuzzy option to display suggestions.",
+                   query)
         if job.fuzzy == true then
-            report (false, 0, "resolve",
-                    "Looking for close matches, this may take a while ...")
+            logreport (false, 0, "resolve",
+                       "Looking for close matches, this may take a while ...")
             local _success = fonts.names.find_closest(query, job.fuzzy_limit)
         end
     end
@@ -1268,14 +1288,13 @@ set_primary_field = function (fields, addme, acc, n)
     return acc
 end
 
-local splitcomma = luaotfload.parsers.splitcomma
-
 actions.list = function (job)
     local criterion     = job.criterion
     local asked_fields  = job.asked_fields
     local name_index    = fonts.names.data ()
 
     if asked_fields then
+        local splitcomma = luaotfload.parsers.splitcomma
         asked_fields = lpegmatch(splitcomma, asked_fields)
     end
 
@@ -1292,7 +1311,7 @@ actions.list = function (job)
     local nmappings = #mappings
 
     if criterion == "*" then
-        report (false, 1, "list", "All %d entries", nmappings)
+        logreport (false, 1, "list", "All %d entries", nmappings)
         for i=1, nmappings do
             local entry     = mappings[i]
             local fields    = get_fields(entry, asked_fields)
@@ -1307,12 +1326,12 @@ actions.list = function (job)
         criterion          = criterion[1]
         asked_fields       = set_primary_field(asked_fields, criterion)
 
-        report (false, 1, "list", "By %s", criterion)
+        logreport (false, 1, "list", "By %s", criterion)
 
         --- firstly, build a list of fonts to operate on
         local targets = { }
         if asked_value then --- only those whose value matches
-            report (false, 2, "list", "Restricting to value %s", asked_value)
+            logreport (false, 2, "list", "Restricting to value %s", asked_value)
             for i=1, nmappings do
                 local entry = mappings[i]
                 if  entry[criterion]
@@ -1357,7 +1376,7 @@ actions.list = function (job)
             end
         end
         local ntargets = #targets
-        report (false, 2, "list", "%d entries", ntargets)
+        logreport (false, 2, "list", "%d entries", ntargets)
 
         --- now, output the collection
         for i=1, ntargets do
@@ -1494,7 +1513,7 @@ local process_cmdline = function ( ) -- unit -> jobspec
         elseif v == "log" then
             local str = optarg[n]
             if str then
-                finalizers = log.set_logout(str, finalizers)
+                finalizers = luaotfload.log.set_logout(str, finalizers)
             end
         elseif v == "find" then
             action_pending["query"] = true
@@ -1588,6 +1607,8 @@ local process_cmdline = function ( ) -- unit -> jobspec
 end
 
 local main = function ( ) -- unit -> int
+    if init_modules () == false then return -42 end
+
     local retval    = 0
     local job       = process_cmdline()
 
@@ -1598,23 +1619,23 @@ local main = function ( ) -- unit -> int
         local actionname = action_sequence[i]
         local exit       = false
         if action_pending[actionname] then
-            report ("log", 3, "util", "Preparing for task", "%s", actionname)
+            logreport ("log", 3, "util", "Preparing for task", "%s", actionname)
 
             local action             = actions[actionname]
             local success, continue  = action(job)
 
             if not success then
-                report (false, 0, "util",
-                        "Failed to execute task.", "%s", actionname)
+                logreport (false, 0, "util",
+                           "Failed to execute task.", "%s", actionname)
                 retval = -1
                 exit   = true
             elseif not continue then
-                report (false, 3, "util",
-                        "Task completed, exiting.", "%s", actionname)
+                logreport (false, 3, "util",
+                           "Task completed, exiting.", "%s", actionname)
                 exit = true
             else
-                report (false, 3, "util",
-                        "Task completed successfully.", "%s", actionname)
+                logreport (false, 3, "util",
+                           "Task completed successfully.", "%s", actionname)
             end
         end
         if exit then break end
-- 
cgit v1.2.3


From c810fcae82effa6c73b7da19e0b94d735be9f767 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Wed, 28 Oct 2015 07:31:42 +0100
Subject: [tool] adapt to current initialization

---
 src/luaotfload-tool.lua | 51 +++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 47 insertions(+), 4 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua
index 6b84a6d..181bd5c 100755
--- a/src/luaotfload-tool.lua
+++ b/src/luaotfload-tool.lua
@@ -145,16 +145,59 @@ utilities                            = backup.utilities
 
 fonts = { names = { } } -- for db; normally provided by the fontloaders
 
-require "luaotfload-log.lua"       --- this populates the luaotfload.log.* namespace
-require "luaotfload-parsers"       --- fonts.conf, configuration, and request syntax
-require "luaotfload-configuration" --- configuration file handling
-require "luaotfload-database"
+local require_init = { }
+
+local loadmodule = function (name)
+    local v = require ("luaotfload-" .. name)
+    if v then
+        local mod = { }
+        local tv  = type (v)
+        if tv == "table" then
+            mod.name = name
+            mod.init = v.init
+            require_init [#require_init + 1] = mod
+        elseif tv == "function" then
+            mod.name = name
+            mod.init = v
+            require_init [#require_init + 1] = mod
+        end
+    end
+end
+
 require "alt_getopt"
 
+loadmodule "log.lua"       --- this populates the luaotfload.log.* namespace
+loadmodule "parsers"       --- fonts.conf, configuration, and request syntax
+loadmodule "configuration" --- configuration file handling
+loadmodule "database"
+
 local logreport
 
 local init_modules = function ()
+    --- NB we don’t command the logger at this point.
+    local todo = #require_init
+    local ret  = true
+    for i = 1, todo do
+        local mod  = require_init[i]
+        local name = mod.name
+        local init = mod.init
+        if type (init) ~= "function" then
+            error ("luaotfload broken; module "
+                   .. name .. " missing initializers!")
+        end
+        local v = mod.init ()
+        if v == true then
+            --- evaluated well
+        elseif type (v) == "table" then
+            luaotfload[name] = v
+        else
+            error ("luaotfload broken; initialization of module "
+                   .. name .. " returned " .. tostring (v) .. ".")
+            return false
+        end
+    end
     logreport = luaotfload.log.report
+    return ret
 end
 
 
-- 
cgit v1.2.3


From 4f053696e1813fde4bd6cebbb77ff2a1e1f6800b Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Thu, 29 Oct 2015 07:14:40 +0100
Subject: [fontloader] sync with Context as of 2015-10-29

---
 src/fontloader/misc/fontloader-basics-nod.lua   |    3 +
 src/fontloader/misc/fontloader-font-afm.lua     |   18 +-
 src/fontloader/misc/fontloader-font-con.lua     |  114 +-
 src/fontloader/misc/fontloader-font-def.lua     |    6 +-
 src/fontloader/misc/fontloader-font-map.lua     |  473 ++--
 src/fontloader/misc/fontloader-font-otb.lua     |   10 +-
 src/fontloader/misc/fontloader-font-otf.lua     |  583 ++++-
 src/fontloader/misc/fontloader-font-otp.lua     |    5 +-
 src/fontloader/misc/fontloader-font-tfm.lua     |    1 +
 src/fontloader/misc/fontloader-fonts-cbk.lua    |   33 +-
 src/fontloader/misc/fontloader-fonts-inj.lua    |  375 +--
 src/fontloader/misc/fontloader-fonts-otn.lua    | 2125 +++++++++++-----
 src/fontloader/misc/fontloader-fonts.lua        |   18 +-
 src/fontloader/misc/fontloader-l-lpeg.lua       |    2 +-
 src/fontloader/misc/fontloader-l-lua.lua        |   34 +-
 src/fontloader/misc/fontloader-l-string.lua     |    7 +-
 src/fontloader/misc/fontloader-l-table.lua      |    2 +-
 src/fontloader/misc/fontloader-mplib.lua        |  114 +-
 src/fontloader/misc/fontloader-mplib.tex        |   21 +-
 src/fontloader/misc/fontloader-plain.tex        |   25 +-
 src/fontloader/misc/fontloader-test.tex         |   26 +-
 src/fontloader/misc/fontloader-util-str.lua     |   13 +-
 src/fontloader/runtime/fontloader-reference.lua | 2947 ++++++++++++++++-------
 23 files changed, 4803 insertions(+), 2152 deletions(-)

(limited to 'src')

diff --git a/src/fontloader/misc/fontloader-basics-nod.lua b/src/fontloader/misc/fontloader-basics-nod.lua
index 1ec2895..39400a3 100644
--- a/src/fontloader/misc/fontloader-basics-nod.lua
+++ b/src/fontloader/misc/fontloader-basics-nod.lua
@@ -56,6 +56,9 @@ local whatcodes    = { } for k,v in next, node.whatsits() do whatcodes[string.gs
 local glyphcodes   = { [0] = "character", "glyph", "ligature", "ghost", "left", "right" }
 local disccodes    = { [0] = "discretionary", "explicit", "automatic", "regular", "first", "second" }
 
+for i=0,#glyphcodes do glyphcodes[glyphcodes[i]] = i end
+for i=0,#disccodes  do disccodes [disccodes [i]] = i end
+
 nodes.nodecodes    = nodecodes
 nodes.whatcodes    = whatcodes
 nodes.whatsitcodes = whatcodes
diff --git a/src/fontloader/misc/fontloader-font-afm.lua b/src/fontloader/misc/fontloader-font-afm.lua
index a96c668..329639b 100644
--- a/src/fontloader/misc/fontloader-font-afm.lua
+++ b/src/fontloader/misc/fontloader-font-afm.lua
@@ -152,14 +152,14 @@ end
 
 local keys = { }
 
-function keys.FontName    (data,line) data.metadata.fontname     = strip    (line) -- get rid of spaces
-                                      data.metadata.fullname     = strip    (line) end
-function keys.ItalicAngle (data,line) data.metadata.italicangle  = tonumber (line) end
-function keys.IsFixedPitch(data,line) data.metadata.isfixedpitch = toboolean(line,true) end
-function keys.CharWidth   (data,line) data.metadata.charwidth    = tonumber (line) end
-function keys.XHeight     (data,line) data.metadata.xheight      = tonumber (line) end
-function keys.Descender   (data,line) data.metadata.descender    = tonumber (line) end
-function keys.Ascender    (data,line) data.metadata.ascender     = tonumber (line) end
+function keys.FontName    (data,line) data.metadata.fontname    = strip    (line) -- get rid of spaces
+                                      data.metadata.fullname    = strip    (line) end
+function keys.ItalicAngle (data,line) data.metadata.italicangle = tonumber (line) end
+function keys.IsFixedPitch(data,line) data.metadata.monospaced  = toboolean(line,true) end
+function keys.CharWidth   (data,line) data.metadata.charwidth   = tonumber (line) end
+function keys.XHeight     (data,line) data.metadata.xheight     = tonumber (line) end
+function keys.Descender   (data,line) data.metadata.descender   = tonumber (line) end
+function keys.Ascender    (data,line) data.metadata.ascender    = tonumber (line) end
 function keys.Comment     (data,line)
  -- Comment DesignSize 12 (pts)
  -- Comment TFM designsize: 12 (in points)
@@ -640,7 +640,7 @@ local function copytotfm(data)
         local spacer     = "space"
         local spaceunits = 500
         --
-        local monospaced  = metadata.isfixedpitch
+        local monospaced  = metadata.monospaced
         local charwidth   = metadata.charwidth
         local italicangle = metadata.italicangle
         local charxheight = metadata.xheight and metadata.xheight > 0 and metadata.xheight
diff --git a/src/fontloader/misc/fontloader-font-con.lua b/src/fontloader/misc/fontloader-font-con.lua
index 72fbb5c..383a403 100644
--- a/src/fontloader/misc/fontloader-font-con.lua
+++ b/src/fontloader/misc/fontloader-font-con.lua
@@ -191,10 +191,9 @@ in the process; numbers are of course copies. Here 65536 equals 1pt. (Due to
 excessive memory usage in CJK fonts, we no longer pass the boundingbox.)</p>
 --ldx]]--
 
--- The scaler is only used for otf and afm and virtual fonts. If
--- a virtual font has italic correction make sure to set the
--- hasitalics flag. Some more flags will be added in
--- the future.
+-- The scaler is only used for otf and afm and virtual fonts. If a virtual font has italic
+-- correction make sure to set the hasitalics flag. Some more flags will be added in the
+-- future.
 
 --[[ldx--
 <p>The reason why the scaler was originally split, is that for a while we experimented
@@ -426,6 +425,7 @@ function constructors.scale(tfmdata,specification)
     local vdelta         = delta
     --
     target.designsize    = parameters.designsize -- not really needed so it might become obsolete
+    target.units         = units
     target.units_per_em  = units                 -- just a trigger for the backend
     --
     local direction      = properties.direction or tfmdata.direction or 0 -- pointless, as we don't use omf fonts at all
@@ -562,26 +562,27 @@ function constructors.scale(tfmdata,specification)
         target.mathparameters = nil -- nop
     end
     --
-    local italickey  = "italic"
-    local useitalics = true -- something context
-    --
-    -- some context specific trickery (this will move to a plugin)
+    -- Here we support some context specific trickery (this might move to a plugin). During the
+    -- transition to opentype the engine had troubles with italics so we had some additional code
+    -- for fixing that. In node mode (text) we don't care much if italics gets passed because
+    -- the engine does nothign with them then.
     --
     if hasmath then
-     -- the latest luatex can deal with it itself so we now disable this
-     -- mechanism here
-     --
-     -- if properties.mathitalics then
-     --     italickey = "italic_correction"
-     --     if trace_defining then
-     --         report_defining("math italics disabled for font %a, fullname %a, filename %a",name,fullname,filename)
-     --     end
-     -- end
-        autoitalicamount = false -- new
-    elseif properties.textitalics then
-        italickey = "italic_correction"
-        useitalics = false
-        if properties.delaytextitalics then
+        local mathitalics = properties.mathitalics
+        if mathitalics == false then
+            if trace_defining then
+                report_defining("%s italics %s for font %a, fullname %a, filename %a","math",hasitalics and "ignored" or "disabled",name,fullname,filename)
+            end
+            hasitalics       = false
+            autoitalicamount = false
+        end
+    else
+        local textitalics = properties.textitalics
+        if textitalics == false then
+            if trace_defining then
+                report_defining("%s italics %s for font %a, fullname %a, filename %a","text",hasitalics and "ignored" or "disabled",name,fullname,filename)
+            end
+            hasitalics       = false
             autoitalicamount = false
         end
     end
@@ -590,8 +591,7 @@ function constructors.scale(tfmdata,specification)
     --
     if trace_defining then
         report_defining("defining tfm, name %a, fullname %a, filename %a, hscale %a, vscale %a, math %a, italics %a",
-            name,fullname,filename,hdelta,vdelta,
-            hasmath and "enabled" or "disabled",useitalics and "enabled" or "disabled")
+            name,fullname,filename,hdelta,vdelta,hasmath and "enabled" or "disabled",hasitalics and "enabled" or "disabled")
     end
     --
     constructors.beforecopyingcharacters(target,tfmdata)
@@ -606,15 +606,15 @@ function constructors.scale(tfmdata,specification)
             local c = changed[unicode]
             if c then
                 description = descriptions[c] or descriptions[unicode] or character
-                character = characters[c] or character
-                index = description.index or c
+                character   = characters[c] or character
+                index       = description.index or c
             else
                 description = descriptions[unicode] or character
-                index = description.index or unicode
+                index       = description.index or unicode
             end
         else
             description = descriptions[unicode] or character
-            index = description.index or unicode
+            index       = description.index or unicode
         end
         local width  = description.width
         local height = description.height
@@ -699,23 +699,6 @@ function constructors.scale(tfmdata,specification)
             end
         end
         --
-        if autoitalicamount then
-            local vi = description.italic
-            if not vi then
-                local vi = description.boundingbox[3] - description.width + autoitalicamount
-                if vi > 0 then -- < 0 indicates no overshoot or a very small auto italic
-                    chr[italickey] = vi*hdelta
-                end
-            elseif vi ~= 0 then
-                chr[italickey] = vi*hdelta
-            end
-        elseif hasitalics then
-            local vi = description.italic
-            if vi and vi ~= 0 then
-                chr[italickey] = vi*hdelta
-            end
-        end
-        -- to be tested
         if hasmath then
             -- todo, just operate on descriptions.math
             local vn = character.next
@@ -754,7 +737,7 @@ function constructors.scale(tfmdata,specification)
                     end
                 end
             end
-            local va = character.top_accent
+            local va = character.accent
             if va then
                 chr.top_accent = vdelta*va
             end
@@ -777,6 +760,27 @@ function constructors.scale(tfmdata,specification)
                     chr.mathkern = kerns -- singular -> should be patched in luatex !
                 end
             end
+            if hasitalics then
+                local vi = character.italic
+                if vi and vi ~= 0 then
+                    chr.italic = vi*hdelta
+                end
+            end
+        elseif autoitalicamount then -- itlc feature
+            local vi = description.italic
+            if not vi then
+                local vi = description.boundingbox[3] - description.width + autoitalicamount
+                if vi > 0 then -- < 0 indicates no overshoot or a very small auto italic
+                    chr.italic = vi*hdelta
+                end
+            elseif vi ~= 0 then
+                chr.italic = vi*hdelta
+            end
+        elseif hasitalics then -- unlikely
+            local vi = character.italic
+            if vi and vi ~= 0 then
+                chr.italic = vi*hdelta
+            end
         end
         if haskerns then
             local vk = character.kerns
@@ -843,6 +847,8 @@ function constructors.scale(tfmdata,specification)
         targetcharacters[unicode] = chr
     end
     --
+    properties.setitalics = hasitalics -- for postprocessing
+    --
     constructors.aftercopyingcharacters(target,tfmdata)
     --
     constructors.trytosharefont(target,tfmdata)
@@ -895,12 +901,21 @@ function constructors.finalize(tfmdata)
         parameters.slantfactor = tfmdata.slant or 0
     end
     --
-    if not parameters.designsize then
-        parameters.designsize = tfmdata.designsize or (factors.pt * 10)
+    local designsize = parameters.designsize
+    if designsize then
+        parameters.minsize = tfmdata.minsize or designsize
+        parameters.maxsize = tfmdata.maxsize or designsize
+    else
+        designsize = factors.pt * 10
+        parameters.designsize = designsize
+        parameters.minsize    = designsize
+        parameters.maxsize    = designsize
     end
+    parameters.minsize = tfmdata.minsize or parameters.designsize
+    parameters.maxsize = tfmdata.maxsize or parameters.designsize
     --
     if not parameters.units then
-        parameters.units = tfmdata.units_per_em or 1000
+        parameters.units = tfmdata.units or tfmdata.units_per_em or 1000
     end
     --
     if not tfmdata.descriptions then
@@ -976,6 +991,7 @@ function constructors.finalize(tfmdata)
     tfmdata.auto_protrude  = nil
     tfmdata.extend         = nil
     tfmdata.slant          = nil
+    tfmdata.units          = nil
     tfmdata.units_per_em   = nil
     --
     tfmdata.cache          = nil
diff --git a/src/fontloader/misc/fontloader-font-def.lua b/src/fontloader/misc/fontloader-font-def.lua
index fdded3c..add42ee 100644
--- a/src/fontloader/misc/fontloader-font-def.lua
+++ b/src/fontloader/misc/fontloader-font-def.lua
@@ -183,10 +183,11 @@ end
 function resolvers.name(specification)
     local resolve = fonts.names.resolve
     if resolve then
-        local resolved, sub = resolve(specification.name,specification.sub,specification) -- we pass specification for overloaded versions
+        local resolved, sub, subindex = resolve(specification.name,specification.sub,specification) -- we pass specification for overloaded versions
         if resolved then
             specification.resolved = resolved
             specification.sub      = sub
+            specification.subindex = subindex
             local suffix = lower(suffixonly(resolved))
             if fonts.formats[suffix] then
                 specification.forced     = suffix
@@ -204,10 +205,11 @@ end
 function resolvers.spec(specification)
     local resolvespec = fonts.names.resolvespec
     if resolvespec then
-        local resolved, sub = resolvespec(specification.name,specification.sub,specification) -- we pass specification for overloaded versions
+        local resolved, sub, subindex = resolvespec(specification.name,specification.sub,specification) -- we pass specification for overloaded versions
         if resolved then
             specification.resolved   = resolved
             specification.sub        = sub
+            specification.subindex   = subindex
             specification.forced     = lower(suffixonly(resolved))
             specification.forcedname = resolved
             specification.name       = removesuffix(resolved)
diff --git a/src/fontloader/misc/fontloader-font-map.lua b/src/fontloader/misc/fontloader-font-map.lua
index 69474ba..b645d9a 100644
--- a/src/fontloader/misc/fontloader-font-map.lua
+++ b/src/fontloader/misc/fontloader-font-map.lua
@@ -31,25 +31,27 @@ of obsolete. Some code may move to runtime or auxiliary modules.</p>
 <p>The name to unciode related code will stay of course.</p>
 --ldx]]--
 
-local function loadlumtable(filename) -- will move to font goodies
-    local lumname = file.replacesuffix(file.basename(filename),"lum")
-    local lumfile = resolvers.findfile(lumname,"map") or ""
-    if lumfile ~= "" and lfs.isfile(lumfile) then
-        if trace_loading or trace_mapping then
-            report_fonts("loading map table %a",lumfile)
-        end
-        lumunic = dofile(lumfile)
-        return lumunic, lumfile
-    end
-end
+-- local function loadlumtable(filename) -- will move to font goodies
+--     local lumname = file.replacesuffix(file.basename(filename),"lum")
+--     local lumfile = resolvers.findfile(lumname,"map") or ""
+--     if lumfile ~= "" and lfs.isfile(lumfile) then
+--         if trace_loading or trace_mapping then
+--             report_fonts("loading map table %a",lumfile)
+--         end
+--         lumunic = dofile(lumfile)
+--         return lumunic, lumfile
+--     end
+-- end
 
 local hex     = R("AF","09")
-local hexfour = (hex*hex*hex*hex)         / function(s) return tonumber(s,16) end
-local hexsix  = (hex*hex*hex*hex*hex*hex) / function(s) return tonumber(s,16) end
+----- hexfour = (hex*hex*hex*hex)         / function(s) return tonumber(s,16) end
+----- hexsix  = (hex*hex*hex*hex*hex*hex) / function(s) return tonumber(s,16) end
+local hexfour = (hex*hex*hex^-2) / function(s) return tonumber(s,16) end
+local hexsix  = (hex*hex*hex^-4) / function(s) return tonumber(s,16) end
 local dec     = (R("09")^1) / tonumber
 local period  = P(".")
-local unicode = P("uni")   * (hexfour * (period + P(-1)) * Cc(false) + Ct(hexfour^1) * Cc(true))
-local ucode   = P("u")     * (hexsix  * (period + P(-1)) * Cc(false) + Ct(hexsix ^1) * Cc(true))
+local unicode = (P("uni") + P("UNI")) * (hexfour * (period + P(-1)) * Cc(false) + Ct(hexfour^1) * Cc(true)) -- base planes
+local ucode   = (P("u")   + P("U")  ) * (hexsix  * (period + P(-1)) * Cc(false) + Ct(hexsix ^1) * Cc(true)) -- extended
 local index   = P("index") * dec * Cc(false)
 
 local parser  = unicode + ucode + index
@@ -168,7 +170,6 @@ end
 --     return s
 -- end
 
-mappings.loadlumtable        = loadlumtable
 mappings.makenameparser      = makenameparser
 mappings.tounicode           = tounicode
 mappings.tounicode16         = tounicode16
@@ -179,13 +180,13 @@ local ligseparator = P("_")
 local varseparator = P(".")
 local namesplitter = Ct(C((1 - ligseparator - varseparator)^1) * (ligseparator * C((1 - ligseparator - varseparator)^1))^0)
 
+-- maybe: ff fi fl ffi ffl => f_f f_i f_l f_f_i f_f_l
+
 -- local function test(name)
 --     local split = lpegmatch(namesplitter,name)
 --     print(string.formatters["%s: [% t]"](name,split))
 -- end
 
--- maybe: ff fi fl ffi ffl => f_f f_i f_l f_f_i f_f_l
-
 -- test("i.f_")
 -- test("this")
 -- test("this.that")
@@ -221,332 +222,184 @@ end
 
 mappings.overloads = overloads
 
-function mappings.addtounicode(data,filename)
-    local resources    = data.resources
-    local properties   = data.properties
-    local descriptions = data.descriptions
-    local unicodes     = resources.unicodes
-    local lookuptypes  = resources.lookuptypes
+function mappings.addtounicode(data,filename,checklookups)
+    local resources = data.resources
+    local unicodes  = resources.unicodes
     if not unicodes then
         return
     end
+    local properties    = data.properties
+    local descriptions  = data.descriptions
     -- we need to move this code
     unicodes['space']   = unicodes['space']  or 32
     unicodes['hyphen']  = unicodes['hyphen'] or 45
     unicodes['zwj']     = unicodes['zwj']    or 0x200D
     unicodes['zwnj']    = unicodes['zwnj']   or 0x200C
-    local private       = fonts.constructors.privateoffset
-    local unicodevector = fonts.encodings.agl.unicodes -- loaded runtime in context
-    ----- namevector    = fonts.encodings.agl.names    -- loaded runtime in context
-    local missing       = { }
-    local lumunic, uparser, oparser
-    local cidinfo, cidnames, cidcodes, usedmap
-    --
-    cidinfo = properties.cidinfo
-    usedmap = cidinfo and fonts.cid.getmap(cidinfo)
     --
+    local private       = fonts.constructors and fonts.constructors.privateoffset or 0xF0000 -- 0x10FFFF
+    local unicodevector = fonts.encodings.agl.unicodes or { } -- loaded runtime in context
+    local contextvector = fonts.encodings.agl.ctxcodes or { } -- loaded runtime in context
+    local missing       = { }
+    local nofmissing    = 0
+    local oparser       = nil
+    local cidnames      = nil
+    local cidcodes      = nil
+    local cidinfo       = properties.cidinfo
+    local usedmap       = cidinfo and fonts.cid.getmap(cidinfo)
+    local uparser       = makenameparser() -- hm, every time?
     if usedmap then
-        oparser  = usedmap and makenameparser(cidinfo.ordering)
-        cidnames = usedmap.names
-        cidcodes = usedmap.unicodes
+          oparser       = usedmap and makenameparser(cidinfo.ordering)
+          cidnames      = usedmap.names
+          cidcodes      = usedmap.unicodes
     end
-    uparser = makenameparser()
-    local ns, nl = 0, 0
+    local ns            = 0
+    local nl            = 0
+    --
     for unic, glyph in next, descriptions do
-        local index = glyph.index
-        local name  = glyph.name
-        local r = overloads[name]
-        if r then
-            -- get rid of weird ligatures
-         -- glyph.name    = r.name
-            glyph.unicode = r.unicode
-        elseif unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then
-            local unicode = lumunic and lumunic[name] or unicodevector[name]
-            if unicode then
-                glyph.unicode = unicode
-                ns            = ns + 1
-            end
-            -- cidmap heuristics, beware, there is no guarantee for a match unless
-            -- the chain resolves
-            if (not unicode) and usedmap then
-                local foundindex = lpegmatch(oparser,name)
-                if foundindex then
-                    unicode = cidcodes[foundindex] -- name to number
-                    if unicode then
-                        glyph.unicode = unicode
-                        ns            = ns + 1
-                    else
-                        local reference = cidnames[foundindex] -- number to name
-                        if reference then
-                            local foundindex = lpegmatch(oparser,reference)
-                            if foundindex then
-                                unicode = cidcodes[foundindex]
-                                if unicode then
-                                    glyph.unicode = unicode
-                                    ns            = ns + 1
+        local name = glyph.name
+        if name then
+            local index = glyph.index
+            local r = overloads[name]
+            if r then
+                -- get rid of weird ligatures
+             -- glyph.name    = r.name
+                glyph.unicode = r.unicode
+            elseif not unic or unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then
+                local unicode = unicodevector[name] or contextvector[name]
+                if unicode then
+                    glyph.unicode = unicode
+                    ns            = ns + 1
+                end
+                -- cidmap heuristics, beware, there is no guarantee for a match unless
+                -- the chain resolves
+                if (not unicode) and usedmap then
+                    local foundindex = lpegmatch(oparser,name)
+                    if foundindex then
+                        unicode = cidcodes[foundindex] -- name to number
+                        if unicode then
+                            glyph.unicode = unicode
+                            ns            = ns + 1
+                        else
+                            local reference = cidnames[foundindex] -- number to name
+                            if reference then
+                                local foundindex = lpegmatch(oparser,reference)
+                                if foundindex then
+                                    unicode = cidcodes[foundindex]
+                                    if unicode then
+                                        glyph.unicode = unicode
+                                        ns            = ns + 1
+                                    end
                                 end
-                            end
-                            if not unicode or unicode == "" then
-                                local foundcodes, multiple = lpegmatch(uparser,reference)
-                                if foundcodes then
-                                    glyph.unicode = foundcodes
-                                    if multiple then
-                                        nl      = nl + 1
-                                        unicode = true
-                                    else
-                                        ns      = ns + 1
-                                        unicode = foundcodes
+                                if not unicode or unicode == "" then
+                                    local foundcodes, multiple = lpegmatch(uparser,reference)
+                                    if foundcodes then
+                                        glyph.unicode = foundcodes
+                                        if multiple then
+                                            nl      = nl + 1
+                                            unicode = true
+                                        else
+                                            ns      = ns + 1
+                                            unicode = foundcodes
+                                        end
                                     end
                                 end
                             end
                         end
                     end
                 end
-            end
-            -- a.whatever or a_b_c.whatever or a_b_c (no numbers) a.b_
-            --
-            -- It is not trivial to find a solution that suits all fonts. We tried several alternatives
-            -- and this one seems to work reasonable also with fonts that use less standardized naming
-            -- schemes. The extra private test is tested by KE and seems to work okay with non-typical
-            -- fonts as well.
-            --
-            -- The next time I look into this, I'll add an extra analysis step to the otf loader (we can
-            -- resolve some tounicodes by looking into the gsub data tables that are bound to glyphs.
-            --
--- a real tricky last resort:
---
---             local lookups = glyph.lookups
---             if lookups then
---                 for _, lookup in next, lookups do -- assume consistency else we need to sort
---                     for i=1,#lookup do
---                         local l = lookup[i]
---                         if l.type == "ligature" then
---                             local s = l.specification
---                             if s.char == glyph.name then
---                                 local components = s.components
---                                 if components then
---                                     local t, n = { }, 0
---                                     unicode = true
---                                     for l=1,#components do
---                                         local base = components[l]
---                                         local u = unicodes[base] or unicodevector[base]
---                                         if not u then
---                                             break
---                                         elseif type(u) == "table" then
---                                             if u[1] >= private then
---                                                 unicode = false
---                                                 break
---                                             end
---                                             n = n + 1
---                                             t[n] = u[1]
---                                         else
---                                             if u >= private then
---                                                 unicode = false
---                                                 break
---                                             end
---                                             n = n + 1
---                                             t[n] = u
---                                         end
---                                     end
---                                     if n == 0 then -- done then
---                                         -- nothing
---                                     elseif n == 1 then
---                                         glyph.unicode = t[1]
---                                     else
---                                         glyph.unicode = t
---                                     end
---                                     nl = nl + 1
---                                     break
---                                 end
---                             end
---                         end
---                     end
---                     if unicode then
---                         break
---                     end
---                 end
---             end
-            if not unicode or unicode == "" then
-                local split = lpegmatch(namesplitter,name)
-                local nsplit = split and #split or 0
-                local t, n = { }, 0
-                unicode = true
-                for l=1,nsplit do
-                    local base = split[l]
-                    local u = unicodes[base] or unicodevector[base]
-                    if not u then
-                        break
-                    elseif type(u) == "table" then
-                        if u[1] >= private then
-                            unicode = false
-                            break
+                -- a.whatever or a_b_c.whatever or a_b_c (no numbers) a.b_
+                --
+                -- It is not trivial to find a solution that suits all fonts. We tried several alternatives
+                -- and this one seems to work reasonable also with fonts that use less standardized naming
+                -- schemes. The extra private test is tested by KE and seems to work okay with non-typical
+                -- fonts as well.
+                --
+                if not unicode or unicode == "" then
+                    local split  = lpegmatch(namesplitter,name)
+                    local nsplit = split and #split or 0 -- add if
+                    if nsplit == 0 then
+                        -- skip
+                    elseif nsplit == 1 then
+                        local base = split[1]
+                        local u = unicodes[base] or unicodevector[base] or contextvector[name]
+                        if not u then
+                            -- skip
+                        elseif type(u) == "table" then
+                            -- unlikely
+                            if u[1] < private then
+                                unicode = u
+                                glyph.unicode = unicode
+                            end
+                        elseif u < private then
+                            unicode = u
+                            glyph.unicode = unicode
                         end
-                        n = n + 1
-                        t[n] = u[1]
                     else
-                        if u >= private then
-                            unicode = false
-                            break
-                        end
-                        n = n + 1
-                        t[n] = u
-                    end
-                end
-                if n == 0 then -- done then
-                    -- nothing
-                elseif n == 1 then
-                    glyph.unicode = t[1]
-                else
-                    glyph.unicode = t
-                end
-                nl = nl + 1
-            end
-            -- last resort (we might need to catch private here as well)
-            if not unicode or unicode == "" then
-                local foundcodes, multiple = lpegmatch(uparser,name)
-                if foundcodes then
-                    glyph.unicode = foundcodes
-                    if multiple then
-                        nl      = nl + 1
-                        unicode = true
-                    else
-                        ns      = ns + 1
-                        unicode = foundcodes
-                    end
-                end
-            end
-            -- check using substitutes and alternates
-            local r = overloads[unicode]
-            if r then
-                unicode = r.unicode
-                glyph.unicode = unicode
-            end
-            --
-            if not unicode then
-                missing[name] = true
-            end
-        end
-    end
-    if next(missing) then
-        local guess  = { }
-        -- helper
-        local function check(gname,code,unicode)
-            local description = descriptions[code]
-            -- no need to add a self reference
-            local variant = description.name
-            if variant == gname then
-                return
-            end
-            -- the variant already has a unicode (normally that resultrs in a default tounicode to self)
-            local unic = unicodes[variant]
-            if unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then
-                -- no default mapping and therefore maybe no tounicode yet
-            else
-                return
-            end
-            -- the variant already has a tounicode
-            if descriptions[code].unicode then
-                return
-            end
-            -- add to the list
-            local g = guess[variant]
-         -- local r = overloads[unicode]
-         -- if r then
-         --     unicode = r.unicode
-         -- end
-            if g then
-                g[gname] = unicode
-            else
-                guess[variant] = { [gname] = unicode }
-            end
-        end
-        --
-        for unicode, description in next, descriptions do
-            local slookups = description.slookups
-            if slookups then
-                local gname = description.name
-                for tag, data in next, slookups do
-                    local lookuptype = lookuptypes[tag]
-                    if lookuptype == "alternate" then
-                        for i=1,#data do
-                            check(gname,data[i],unicode)
-                        end
-                    elseif lookuptype == "substitution" then
-                        check(gname,data,unicode)
-                    end
-                end
-            end
-            local mlookups = description.mlookups
-            if mlookups then
-                local gname = description.name
-                for tag, list in next, mlookups do
-                    local lookuptype = lookuptypes[tag]
-                    if lookuptype == "alternate" then
-                        for i=1,#list do
-                            local data = list[i]
-                            for i=1,#data do
-                                check(gname,data[i],unicode)
+                        local t, n = { }, 0
+                        for l=1,nsplit do
+                            local base = split[l]
+                            local u = unicodes[base] or unicodevector[base] or contextvector[name]
+                            if not u then
+                                break
+                            elseif type(u) == "table" then
+                                if u[1] >= private then
+                                    break
+                                end
+                                n = n + 1
+                                t[n] = u[1]
+                            else
+                                if u >= private then
+                                    break
+                                end
+                                n = n + 1
+                                t[n] = u
                             end
                         end
-                    elseif lookuptype == "substitution" then
-                        for i=1,#list do
-                            check(gname,list[i],unicode)
+                        if n > 0 then
+                            if n == 1 then
+                                unicode = t[1]
+                            else
+                                unicode = t
+                            end
+                            glyph.unicode = unicode
                         end
                     end
+                    nl = nl + 1
                 end
-            end
-        end
-        -- resolve references
-        local done = true
-        while done do
-            done = false
-            for k, v in next, guess do
-                if type(v) ~= "number" then
-                    for kk, vv in next, v do
-                        if vv == -1 or vv >= private or (vv >= 0xE000 and vv <= 0xF8FF) or vv == 0xFFFE or vv == 0xFFFF then
-                            local uu = guess[kk]
-                            if type(uu) == "number" then
-                                guess[k] = uu
-                                done = true
-                            end
+                -- last resort (we might need to catch private here as well)
+                if not unicode or unicode == "" then
+                    local foundcodes, multiple = lpegmatch(uparser,name)
+                    if foundcodes then
+                        glyph.unicode = foundcodes
+                        if multiple then
+                            nl      = nl + 1
+                            unicode = true
                         else
-                            guess[k] = vv
-                            done = true
+                            ns      = ns + 1
+                            unicode = foundcodes
                         end
                     end
                 end
-            end
-        end
-        -- wrap up
-        local orphans = 0
-        local guessed = 0
-        for k, v in next, guess do
-            if type(v) == "number" then
-                descriptions[unicodes[k]].unicode = descriptions[v].unicode or v -- can also be a table
-                guessed = guessed + 1
-            else
-                local t = nil
-                local l = lower(k)
-                local u = unicodes[l]
-                if not u then
-                    orphans = orphans + 1
-                elseif u == -1 or u >= private or (u >= 0xE000 and u <= 0xF8FF) or u == 0xFFFE or u == 0xFFFF then
-                    local unicode = descriptions[u].unicode
-                    if unicode then
-                        descriptions[unicodes[k]].unicode = unicode
-                        guessed = guessed + 1
-                    else
-                        orphans = orphans + 1
-                    end
-                else
-                    orphans = orphans + 1
+                -- check using substitutes and alternates
+                local r = overloads[unicode]
+                if r then
+                    unicode = r.unicode
+                    glyph.unicode = unicode
+                end
+                --
+                if not unicode then
+                    missing[unic] = true
+                    nofmissing    = nofmissing + 1
                 end
             end
+        else
+            -- no name
         end
-        if trace_loading and orphans > 0 or guessed > 0 then
-            report_fonts("%s glyphs with no related unicode, %s guessed, %s orphans",guessed+orphans,guessed,orphans)
-        end
     end
+    if type(checklookups) == "function" then
+        checklookups(data,missing,nofmissing)
+    end
+    -- todo: go lowercase
     if trace_mapping then
         for unic, glyph in table.sortedhash(descriptions) do
             local name    = glyph.name
diff --git a/src/fontloader/misc/fontloader-font-otb.lua b/src/fontloader/misc/fontloader-font-otb.lua
index 4e955a1..c9f5d4a 100644
--- a/src/fontloader/misc/fontloader-font-otb.lua
+++ b/src/fontloader/misc/fontloader-font-otb.lua
@@ -321,14 +321,14 @@ local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplis
 
     for unicode, character in next, characters do
         local description = descriptions[unicode]
-        local lookups = description.slookups
+        local lookups     = description.slookups
         if lookups then
             for l=1,#lookuplist do
                 local lookupname = lookuplist[l]
                 local lookupdata = lookups[lookupname]
                 if lookupdata then
                     local lookuptype = lookuptypes[lookupname]
-                    local action = actions[lookuptype]
+                    local action     = actions[lookuptype]
                     if action then
                         action(lookupdata,lookuptags,lookupname,description,unicode)
                     end
@@ -342,7 +342,7 @@ local function preparesubstitutions(tfmdata,feature,value,validlookups,lookuplis
                 local lookuplist = lookups[lookupname]
                 if lookuplist then
                     local lookuptype = lookuptypes[lookupname]
-                    local action = actions[lookuptype]
+                    local action     = actions[lookuptype]
                     if action then
                         for i=1,#lookuplist do
                             action(lookuplist[i],lookuptags,lookupname,description,unicode)
@@ -614,8 +614,8 @@ local function featuresinitializer(tfmdata,value)
             local collectlookups    = otf.collectlookups
             local rawdata           = tfmdata.shared.rawdata
             local properties        = tfmdata.properties
-            local script            = properties.script
-            local language          = properties.language
+            local script            = properties.script   -- or "dflt" -- can be nil
+            local language          = properties.language -- or "dflt" -- can be nil
             local basesubstitutions = rawdata.resources.features.gsub
             local basepositionings  = rawdata.resources.features.gpos
             --
diff --git a/src/fontloader/misc/fontloader-font-otf.lua b/src/fontloader/misc/fontloader-font-otf.lua
index e7a97c6..0ca1e98 100644
--- a/src/fontloader/misc/fontloader-font-otf.lua
+++ b/src/fontloader/misc/fontloader-font-otf.lua
@@ -12,15 +12,19 @@ if not modules then modules = { } end modules ['font-otf'] = {
 -- to_table -> totable
 -- ascent descent
 
+-- to be checked: combinations like:
+--
+-- current="ABCD" with [A]=nothing, [BC]=ligature, [D]=single (applied to result of BC so funny index)
+--
+-- unlikely but possible
+
 -- more checking against low level calls of functions
 
 local utfbyte = utf.byte
-local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip
+local gmatch, gsub, find, match, lower, strip = string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip
 local type, next, tonumber, tostring = type, next, tonumber, tostring
 local abs = math.abs
-local insert = table.insert
-local lpegmatch = lpeg.match
-local reversed, concat, remove, sortedkeys = table.reversed, table.concat, table.remove, table.sortedkeys
+local reversed, concat, insert, remove, sortedkeys = table.reversed, table.concat, table.insert, table.remove, table.sortedkeys
 local ioflush = io.flush
 local fastcopy, tohash, derivetable = table.fastcopy, table.tohash, table.derive
 local formatters = string.formatters
@@ -54,7 +58,7 @@ local otf                = fonts.handlers.otf
 
 otf.glists               = { "gsub", "gpos" }
 
-otf.version              = 2.812 -- beware: also sync font-mis.lua
+otf.version              = 2.819 -- beware: also sync font-mis.lua and in mtx-fonts
 otf.cache                = containers.define("fonts", "otf", otf.version, true)
 
 local hashes             = fonts.hashes
@@ -283,13 +287,17 @@ local ordered_enhancers = {
 
     "check glyphs",
     "check metadata",
-    "check extra features", -- after metadata
+--     "check extra features", -- after metadata
 
     "prepare tounicode",
 
     "check encoding", -- moved
     "add duplicates",
 
+    "expand lookups", -- a temp hack awaiting the lua loader
+
+--     "check extra features", -- after metadata and duplicates
+
     "cleanup tables",
 
     "compact lookups",
@@ -386,6 +394,7 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone
     if featurefile then
         name = name .. "@" .. file.removesuffix(file.basename(featurefile))
     end
+    -- or: sub = tonumber(sub)
     if sub == "" then
         sub = false
     end
@@ -442,6 +451,7 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone
         end
      end
      if reload then
+        starttiming("fontloader")
         report_otf("loading %a, hash %a",filename,hash)
         local fontdata, messages
         if sub then
@@ -476,6 +486,7 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone
             data = {
                 size        = size,
                 time        = time,
+                subfont     = sub,
                 format      = otf_format(filename),
                 featuredata = featurefiles,
                 resources   = {
@@ -512,7 +523,6 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone
                     tounicodetable = Ct(splitter),
                 },
             }
-            starttiming(data)
             report_otf("file size: %s", size)
             enhancers.apply(data,filename,fontdata)
             local packtime = { }
@@ -529,10 +539,10 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone
             if cleanup > 1 then
                 collectgarbage("collect")
             end
-            stoptiming(data)
+            stoptiming("fontloader")
             if elapsedtime then -- not in generic
-                report_otf("preprocessing and caching time %s, packtime %s",
-                    elapsedtime(data),packdata and elapsedtime(packtime) or 0)
+                report_otf("loading, optimizing, packing and caching time %s, pack time %s",
+                    elapsedtime("fontloader"),packdata and elapsedtime(packtime) or 0)
             end
             close_font(fontdata) -- free memory
             if cleanup > 3 then
@@ -543,6 +553,7 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone
                 collectgarbage("collect")
             end
         else
+            stoptiming("fontloader")
             data = nil
             report_otf("loading failed due to read error")
         end
@@ -589,6 +600,7 @@ function otf.load(filename,sub,featurefile) -- second argument (format) is gone
             applyruntimefixes(filename,data)
         end
         enhance("add dimensions",data,filename,nil,false)
+enhance("check extra features",data,filename)
         if trace_sequences then
             showfeatureorder(data,filename)
         end
@@ -697,7 +709,7 @@ local function somecopy(old) -- fast one
     end
 end
 
--- not setting hasitalics and class (when nil) during table cronstruction can save some mem
+-- not setting hasitalics and class (when nil) during table construction can save some mem
 
 actions["prepare glyphs"] = function(data,filename,raw)
     local tableversion = tonumber(raw.table_version) or 0
@@ -780,7 +792,7 @@ actions["prepare glyphs"] = function(data,filename,raw)
                                 end
                                 if not unicode or unicode == -1 then -- or unicode >= criterium then
                                     if not name then
-                                        name = format("u%06X.ctx",private)
+                                        name = formatters["u%06X.ctx"](private)
                                     end
                                     unicode = private
                                     unicodes[name] = private
@@ -803,7 +815,7 @@ actions["prepare glyphs"] = function(data,filename,raw)
                                  --     end
                                  -- end
                                     if not name then
-                                        name = format("u%06X.ctx",unicode)
+                                        name = formatters["u%06X.ctx"](unicode)
                                     end
                                     unicodes[name] = unicode
                                     nofunicodes = nofunicodes + 1
@@ -819,35 +831,35 @@ actions["prepare glyphs"] = function(data,filename,raw)
                                     glyph       = glyph,
                                 }
                                 descriptions[unicode] = description
-local altuni = glyph.altuni
-if altuni then
- -- local d
-    for i=1,#altuni do
-        local a = altuni[i]
-        local u = a.unicode
-        if u ~= unicode then
-            local v = a.variant
-            if v then
-                -- tricky: no addition to d? needs checking but in practice such dups are either very simple
-                -- shapes or e.g cjk with not that many features
-                local vv = variants[v]
-                if vv then
-                    vv[u] = unicode
-                else -- xits-math has some:
-                    vv = { [u] = unicode }
-                    variants[v] = vv
-                end
-         -- elseif d then
-         --     d[#d+1] = u
-         -- else
-         --     d = { u }
-            end
-        end
-    end
- -- if d then
- --     duplicates[unicode] = d -- is this needed ?
- -- end
-end
+                                local altuni = glyph.altuni
+                                if altuni then
+                                 -- local d
+                                    for i=1,#altuni do
+                                        local a = altuni[i]
+                                        local u = a.unicode
+                                        if u ~= unicode then
+                                            local v = a.variant
+                                            if v then
+                                                -- tricky: no addition to d? needs checking but in practice such dups are either very simple
+                                                -- shapes or e.g cjk with not that many features
+                                                local vv = variants[v]
+                                                if vv then
+                                                    vv[u] = unicode
+                                                else -- xits-math has some:
+                                                    vv = { [u] = unicode }
+                                                    variants[v] = vv
+                                                end
+                                         -- elseif d then
+                                         --     d[#d+1] = u
+                                         -- else
+                                         --     d = { u }
+                                            end
+                                        end
+                                    end
+                                 -- if d then
+                                 --     duplicates[unicode] = d -- is this needed ?
+                                 -- end
+                                end
                             end
                         end
                     else
@@ -916,7 +928,7 @@ end
                     end
                     indices[index] = unicode
                  -- if not name then
-                 --     name = format("u%06X",unicode) -- u%06X.ctx
+                 --     name = formatters["u%06X"](unicode) -- u%06X.ctx
                  -- end
                     descriptions[unicode] = {
                      -- width       = glyph.width,
@@ -1089,7 +1101,7 @@ actions["add duplicates"] = function(data,filename,raw)
                     end
                     if u > 0 then -- and
                         local duplicate = table.copy(description) -- else packing problem
-                        duplicate.comment = format("copy of U+%05X", unicode)
+                        duplicate.comment = formatters["copy of %U"](unicode)
                         descriptions[u] = duplicate
                      -- validduplicates[#validduplicates+1] = u
                         if trace_loading then
@@ -1107,16 +1119,16 @@ end
 -- boundingbox: split into ht/dp takes more memory (larger tables and less sharing)
 
 actions["analyze glyphs"] = function(data,filename,raw) -- maybe integrate this in the previous
-    local descriptions      = data.descriptions
-    local resources         = data.resources
-    local metadata          = data.metadata
-    local properties        = data.properties
-    local hasitalics        = false
-    local widths            = { }
-    local marks             = { } -- always present (saves checking)
+    local descriptions = data.descriptions
+    local resources    = data.resources
+    local metadata     = data.metadata
+    local properties   = data.properties
+    local hasitalics   = false
+    local widths       = { }
+    local marks        = { } -- always present (saves checking)
     for unicode, description in next, descriptions do
-        local glyph = description.glyph
-        local italic = glyph.italic_correction
+        local glyph  = description.glyph
+        local italic = glyph.italic_correction -- only in a math font (we also have vert/horiz)
         if not italic then
             -- skip
         elseif italic == 0 then
@@ -1185,7 +1197,8 @@ end
 actions["reorganize features"] = function(data,filename,raw) -- combine with other
     local features = { }
     data.resources.features = features
-    for k, what in next, otf.glists do
+    for k=1,#otf.glists do
+        local what = otf.glists[k]
         local dw = raw[what]
         if dw then
             local f = { }
@@ -1254,6 +1267,140 @@ actions["reorganize anchor classes"] = function(data,filename,raw)
     end
 end
 
+-- local function checklookups(data,missing,nofmissing)
+--     local resources    = data.resources
+--     local unicodes     = resources.unicodes
+--     local lookuptypes  = resources.lookuptypes
+--     if not unicodes or not lookuptypes then
+--         return
+--     elseif nofmissing <= 0 then
+--         return
+--     end
+--     local descriptions = data.descriptions
+--     local private      = fonts.constructors and fonts.constructors.privateoffset or 0xF0000 -- 0x10FFFF
+--     --
+--     local ns, nl = 0, 0
+
+--     local guess  = { }
+--     -- helper
+--     local function check(gname,code,unicode)
+--         local description = descriptions[code]
+--         -- no need to add a self reference
+--         local variant = description.name
+--         if variant == gname then
+--             return
+--         end
+--         -- the variant already has a unicode (normally that results in a default tounicode to self)
+--         local unic = unicodes[variant]
+--         if unic == -1 or unic >= private or (unic >= 0xE000 and unic <= 0xF8FF) or unic == 0xFFFE or unic == 0xFFFF then
+--             -- no default mapping and therefore maybe no tounicode yet
+--         else
+--             return
+--         end
+--         -- the variant already has a tounicode
+--         if descriptions[code].unicode then
+--             return
+--         end
+--         -- add to the list
+--         local g = guess[variant]
+--      -- local r = overloads[unicode]
+--      -- if r then
+--      --     unicode = r.unicode
+--      -- end
+--         if g then
+--             g[gname] = unicode
+--         else
+--             guess[variant] = { [gname] = unicode }
+--         end
+--     end
+--     --
+--     for unicode, description in next, descriptions do
+--         local slookups = description.slookups
+--         if slookups then
+--             local gname = description.name
+--             for tag, data in next, slookups do
+--                 local lookuptype = lookuptypes[tag]
+--                 if lookuptype == "alternate" then
+--                     for i=1,#data do
+--                         check(gname,data[i],unicode)
+--                     end
+--                 elseif lookuptype == "substitution" then
+--                     check(gname,data,unicode)
+--                 end
+--             end
+--         end
+--         local mlookups = description.mlookups
+--         if mlookups then
+--             local gname = description.name
+--             for tag, list in next, mlookups do
+--                 local lookuptype = lookuptypes[tag]
+--                 if lookuptype == "alternate" then
+--                     for i=1,#list do
+--                         local data = list[i]
+--                         for i=1,#data do
+--                             check(gname,data[i],unicode)
+--                         end
+--                     end
+--                 elseif lookuptype == "substitution" then
+--                     for i=1,#list do
+--                         check(gname,list[i],unicode)
+--                     end
+--                 end
+--             end
+--         end
+--     end
+--     -- resolve references
+--     local done = true
+--     while done do
+--         done = false
+--         for k, v in next, guess do
+--             if type(v) ~= "number" then
+--                 for kk, vv in next, v do
+--                     if vv == -1 or vv >= private or (vv >= 0xE000 and vv <= 0xF8FF) or vv == 0xFFFE or vv == 0xFFFF then
+--                         local uu = guess[kk]
+--                         if type(uu) == "number" then
+--                             guess[k] = uu
+--                             done = true
+--                         end
+--                     else
+--                         guess[k] = vv
+--                         done = true
+--                     end
+--                 end
+--             end
+--         end
+--     end
+--     -- wrap up
+--     local orphans = 0
+--     local guessed = 0
+--     for k, v in next, guess do
+--         if type(v) == "number" then
+--             descriptions[unicodes[k]].unicode = descriptions[v].unicode or v -- can also be a table
+--             guessed = guessed + 1
+--         else
+--             local t = nil
+--             local l = lower(k)
+--             local u = unicodes[l]
+--             if not u then
+--                 orphans = orphans + 1
+--             elseif u == -1 or u >= private or (u >= 0xE000 and u <= 0xF8FF) or u == 0xFFFE or u == 0xFFFF then
+--                 local unicode = descriptions[u].unicode
+--                 if unicode then
+--                     descriptions[unicodes[k]].unicode = unicode
+--                     guessed = guessed + 1
+--                 else
+--                     orphans = orphans + 1
+--                 end
+--             else
+--                 orphans = orphans + 1
+--             end
+--         end
+--     end
+--     if trace_loading and orphans > 0 or guessed > 0 then
+--         report_otf("%s glyphs with no related unicode, %s guessed, %s orphans",guessed+orphans,guessed,orphans)
+--     end
+-- end
+
 actions["prepare tounicode"] = function(data,filename,raw)
     fonts.mappings.addtounicode(data,filename)
 end
@@ -1288,8 +1435,9 @@ actions["reorganize subtables"] = function(data,filename,raw)
     local lookups         = { }
     local chainedfeatures = { }
     resources.sequences   = sequences
-    resources.lookups     = lookups
-    for _, what in next, otf.glists do
+    resources.lookups     = lookups -- we also have lookups in data itself
+    for k=1,#otf.glists do
+        local what = otf.glists[k]
         local dw = raw[what]
         if dw then
             for k=1,#dw do
@@ -1375,12 +1523,6 @@ actions["reorganize subtables"] = function(data,filename,raw)
     end
 end
 
--- test this:
---
---    for _, what in next, otf.glists do
---        raw[what] = nil
---    end
-
 actions["prepare lookups"] = function(data,filename,raw)
     local lookups = raw.lookups
     if lookups then
@@ -1494,12 +1636,16 @@ end
 actions["reorganize lookups"] = function(data,filename,raw) -- we could check for "" and n == 0
     -- we prefer the before lookups in a normal order
     if data.lookups then
-        local splitter = data.helpers.tounicodetable
-        local t_u_cache = { }
-        local s_u_cache = t_u_cache -- string keys
-        local t_h_cache = { }
-        local s_h_cache = t_h_cache -- table keys (so we could use one cache)
-        local r_u_cache = { } -- maybe shared
+        local helpers      = data.helpers
+        local duplicates   = data.resources.duplicates
+        local splitter     = helpers.tounicodetable
+        local t_u_cache    = { }
+        local s_u_cache    = t_u_cache -- string keys
+        local t_h_cache    = { }
+        local s_h_cache    = t_h_cache -- table keys (so we could use one cache)
+        local r_u_cache    = { } -- maybe shared
+        helpers.matchcache = t_h_cache -- so that we can add duplicates
+        --
         for _, lookup in next, data.lookups do
             local rules = lookup.rules
             if rules then
@@ -1653,6 +1799,50 @@ actions["reorganize lookups"] = function(data,filename,raw) -- we could check fo
     end
 end
 
+actions["expand lookups"] = function(data,filename,raw) -- we could check for "" and n == 0
+    if data.lookups then
+        local cache = data.helpers.matchcache
+        if cache then
+            local duplicates = data.resources.duplicates
+            for key, hash in next, cache do
+                local done = nil
+                for key in next, hash do
+                    local unicode = duplicates[key]
+                    if not unicode then
+                        -- no duplicate
+                    elseif type(unicode) == "table" then
+                        -- multiple duplicates
+                        for i=1,#unicode do
+                            local u = unicode[i]
+                            if hash[u] then
+                                -- already in set
+                            elseif done then
+                                done[u] = key
+                            else
+                                done = { [u] = key }
+                            end
+                        end
+                    else
+                        -- one duplicate
+                        if hash[unicode] then
+                            -- already in set
+                        elseif done then
+                            done[unicode] = key
+                        else
+                            done = { [unicode] = key }
+                        end
+                    end
+                end
+                if done then
+                    for u in next, done do
+                        hash[u] = true
+                    end
+                end
+            end
+        end
+    end
+end
+
 local function check_variants(unicode,the_variants,splitter,unicodes)
     local variants = the_variants.variants
     if variants then -- use splitter
@@ -1693,11 +1883,11 @@ local function check_variants(unicode,the_variants,splitter,unicodes)
             parts = nil
         end
     end
-    local italic_correction = the_variants.italic_correction
-    if italic_correction and italic_correction == 0 then
-        italic_correction = nil
+    local italic = the_variants.italic
+    if italic and italic == 0 then
+        italic = nil
     end
-    return variants, parts, italic_correction
+    return variants, parts, italic
 end
 
 actions["analyze math"] = function(data,filename,raw)
@@ -1706,15 +1896,16 @@ actions["analyze math"] = function(data,filename,raw)
         local unicodes = data.resources.unicodes
         local splitter = data.helpers.tounicodetable
         for unicode, description in next, data.descriptions do
-            local glyph          = description.glyph
-            local mathkerns      = glyph.mathkern -- singular
-            local horiz_variants = glyph.horiz_variants
-            local vert_variants  = glyph.vert_variants
-            local top_accent     = glyph.top_accent
-            if mathkerns or horiz_variants or vert_variants or top_accent then
+            local glyph        = description.glyph
+            local mathkerns    = glyph.mathkern -- singular
+            local hvariants    = glyph.horiz_variants
+            local vvariants    = glyph.vert_variants
+            local accent       = glyph.top_accent
+            local italic       = glyph.italic_correction
+            if mathkerns or hvariants or vvariants or accent or italic then
                 local math = { }
-                if top_accent then
-                    math.top_accent = top_accent
+                if accent then
+                    math.accent = accent
                 end
                 if mathkerns then
                     for k, v in next, mathkerns do
@@ -1730,15 +1921,14 @@ actions["analyze math"] = function(data,filename,raw)
                     end
                     math.kerns = mathkerns
                 end
-                if horiz_variants then
-                    math.horiz_variants, math.horiz_parts, math.horiz_italic_correction = check_variants(unicode,horiz_variants,splitter,unicodes)
+                if hvariants then
+                    math.hvariants, math.hparts, math.hitalic = check_variants(unicode,hvariants,splitter,unicodes)
                 end
-                if vert_variants then
-                    math.vert_variants, math.vert_parts, math.vert_italic_correction = check_variants(unicode,vert_variants,splitter,unicodes)
+                if vvariants then
+                    math.vvariants, math.vparts, math.vitalic = check_variants(unicode,vvariants,splitter,unicodes)
                 end
-                local italic_correction = description.italic
-                if italic_correction and italic_correction ~= 0 then
-                    math.italic_correction = italic_correction
+                if italic and italic ~= 0 then
+                    math.italic = italic
                 end
                 description.math = math
             end
@@ -1903,7 +2093,7 @@ actions["merge kern classes"] = function(data,filename,raw)
             report_otf("%s kern overloads ignored",ignored)
         end
         if blocked > 0 then
-            report_otf("%s succesive kerns blocked",blocked)
+            report_otf("%s successive kerns blocked",blocked)
         end
     end
 end
@@ -1940,18 +2130,21 @@ actions["check metadata"] = function(data,filename,raw)
         end
     end
     --
+    local names = raw.names
+    --
     if metadata.validation_state and table.contains(metadata.validation_state,"bad_ps_fontname") then
         -- the ff library does a bit too much (and wrong) checking ... so we need to catch this
         -- at least for now
         local function valid(what)
-            local names = raw.names
-            for i=1,#names do
-                local list = names[i]
-                local names = list.names
-                if names then
-                    local name = names[what]
-                    if name and valid_ps_name(name) then
-                        return name
+            if names then
+                for i=1,#names do
+                    local list = names[i]
+                    local names = list.names
+                    if names then
+                        local name = names[what]
+                        if name and valid_ps_name(name) then
+                            return name
+                        end
                     end
                 end
             end
@@ -1975,6 +2168,33 @@ actions["check metadata"] = function(data,filename,raw)
         check("fullname")
     end
     --
+    if names then
+        local psname = metadata.psname
+        if not psname or psname == "" then
+            for i=1,#names do
+                local name = names[i]
+                -- Currently we use the same restricted search as in the new context (specific) font loader
+                -- but we might add more lang checks (it worked ok in the new loaded so now we're in sync)
+                -- This check here is also because there are (esp) cjk fonts out there with psnames different
+                -- from fontnames (gives a bad lookup in backend).
+                if lower(name.lang) == "english (us)" then
+                    local specification = name.names
+                    if specification then
+                        local postscriptname = specification.postscriptname
+                        if postscriptname then
+                            psname = postscriptname
+                        end
+                    end
+                end
+                break
+            end
+        end
+        if psname ~= metadata.fontname then
+            report_otf("fontname %a, fullname %a, psname %a",metadata.fontname,metadata.fullname,psname)
+        end
+        metadata.psname = psname
+    end
+    --
 end
 
 actions["cleanup tables"] = function(data,filename,raw)
@@ -2000,7 +2220,9 @@ end
 
 -- we can share { } as it is never set
 
---- ligatures have an extra specification.char entry that we don't use
+-- ligatures have an extra specification.char entry that we don't use
+
+-- mlookups only with pairs and ligatures
 
 actions["reorganize glyph lookups"] = function(data,filename,raw)
     local resources    = data.resources
@@ -2081,14 +2303,14 @@ actions["reorganize glyph lookups"] = function(data,filename,raw)
             if mlookups then
                 description.mlookups = mlookups
             end
+         -- description.lookups = nil
         end
     end
-
 end
 
 local zero = { 0, 0 }
 
-actions["reorganize glyph anchors"] = function(data,filename,raw) -- when we replace inplace we safe entries
+actions["reorganize glyph anchors"] = function(data,filename,raw)
     local descriptions = data.descriptions
     for unicode, description in next, descriptions do
         local anchors = description.glyph.anchors
@@ -2311,7 +2533,7 @@ end
 -- we cannot share descriptions as virtual fonts might extend them (ok,
 -- we could use a cache with a hash
 --
--- we already assing an empty tabel to characters as we can add for
+-- we already assign an empty tabel to characters as we can add for
 -- instance protruding info and loop over characters; one is not supposed
 -- to change descriptions and if one does so one should make a copy!
 
@@ -2334,10 +2556,14 @@ local function copytotfm(data,cache_id)
         local spaceunits     = 500
         local spacer         = "space"
         local designsize     = metadata.designsize or metadata.design_size or 100
+        local minsize        = metadata.minsize or metadata.design_range_bottom or designsize
+        local maxsize        = metadata.maxsize or metadata.design_range_top    or designsize
         local mathspecs      = metadata.math
         --
         if designsize == 0 then
             designsize = 100
+            minsize    = 100
+            maxsize    = 100
         end
         if mathspecs then
             for name, value in next, mathspecs do
@@ -2355,8 +2581,11 @@ local function copytotfm(data,cache_id)
                 local m = d.math
                 if m then
                     -- watch out: luatex uses horiz_variants for the parts
-                    local variants = m.horiz_variants
-                    local parts    = m.horiz_parts
+                    --
+                    local italic   = m.italic
+                    --
+                    local variants = m.hvariants
+                    local parts    = m.hparts
                  -- local done     = { [unicode] = true }
                     if variants then
                         local c = character
@@ -2373,9 +2602,11 @@ local function copytotfm(data,cache_id)
                         c.horiz_variants = parts
                     elseif parts then
                         character.horiz_variants = parts
+                        italic = m.hitalic
                     end
-                    local variants = m.vert_variants
-                    local parts    = m.vert_parts
+                    --
+                    local variants = m.vvariants
+                    local parts    = m.vparts
                  -- local done     = { [unicode] = true }
                     if variants then
                         local c = character
@@ -2392,15 +2623,18 @@ local function copytotfm(data,cache_id)
                         c.vert_variants = parts
                     elseif parts then
                         character.vert_variants = parts
+                        italic = m.vitalic
                     end
-                    local italic_correction = m.vert_italic_correction
-                    if italic_correction then
-                        character.vert_italic_correction = italic_correction -- was c.
+                    --
+                    if italic and italic ~= 0 then
+                        character.italic = italic -- overload
                     end
-                    local top_accent = m.top_accent
-                    if top_accent then
-                        character.top_accent = top_accent
+                    --
+                    local accent = m.accent
+                    if accent then
+                        character.accent = accent
                     end
+                    --
                     local kerns = m.kerns
                     if kerns then
                         character.mathkerns = kerns
@@ -2413,16 +2647,16 @@ local function copytotfm(data,cache_id)
         local filename = constructors.checkedfilename(resources)
         local fontname = metadata.fontname
         local fullname = metadata.fullname or fontname
-        local psname   = fontname or fullname
-        local units    = metadata.units_per_em or 1000
+        local psname   = metadata.psname or fontname or fullname
+        local units    = metadata.units or metadata.units_per_em or 1000
         --
         if units == 0 then -- catch bugs in fonts
             units = 1000 -- maybe 2000 when ttf
-            metadata.units_per_em = 1000
+            metadata.units = 1000
             report_otf("changing %a units to %a",0,units)
         end
         --
-        local monospaced  = metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion == "Monospaced")
+        local monospaced  = metadata.monospaced or metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion == "Monospaced")
         local charwidth   = pfminfo.avgwidth -- or unset
         local charxheight = pfminfo.os2_xheight and pfminfo.os2_xheight > 0 and pfminfo.os2_xheight
 -- charwidth = charwidth * units/1000
@@ -2492,17 +2726,16 @@ local function copytotfm(data,cache_id)
             end
         end
         --
-        parameters.designsize = (designsize/10)*65536
-        parameters.ascender   = abs(metadata.ascent  or 0)
-        parameters.descender  = abs(metadata.descent or 0)
-        parameters.units      = units
+        parameters.designsize    = (designsize/10)*65536
+        parameters.minsize       = (minsize   /10)*65536
+        parameters.maxsize       = (maxsize   /10)*65536
+        parameters.ascender      = abs(metadata.ascender  or metadata.ascent  or 0)
+        parameters.descender     = abs(metadata.descender or metadata.descent or 0)
+        parameters.units         = units
         --
         properties.space         = spacer
         properties.encodingbytes = 2
         properties.format        = data.format or otf_format(filename) or formats.otf
--- if units ~= 1000 and format ~= "truetype" then
---     properties.format = "truetype"
--- end
         properties.noglyphnames  = true
         properties.filename      = filename
         properties.fontname      = fontname
@@ -2703,3 +2936,111 @@ function otf.scriptandlanguage(tfmdata,attr)
     local properties = tfmdata.properties
     return properties.script or "dflt", properties.language or "dflt"
 end
+
+-- a little bit of abstraction
+
+local function justset(coverage,unicode,replacement)
+    coverage[unicode] = replacement
+end
+
+otf.coverup = {
+    stepkey = "subtables",
+    actions = {
+        substitution = justset,
+        alternate    = justset,
+        multiple     = justset,
+        ligature     = justset,
+        kern         = justset,
+    },
+    register = function(coverage,lookuptype,format,feature,n,descriptions,resources)
+        local name = formatters["ctx_%s_%s"](feature,n)
+        if lookuptype == "kern" then
+            resources.lookuptypes[name] = "position"
+        else
+            resources.lookuptypes[name] = lookuptype
+        end
+        for u, c in next, coverage do
+            local description = descriptions[u]
+            local slookups = description.slookups
+            if slookups then
+                slookups[name] = c
+            else
+                description.slookups = { [name] = c }
+            end
+-- inspect(feature,description)
+        end
+        return name
+    end
+}
+
+-- moved from font-oth.lua
+
+local function getgsub(tfmdata,k,kind)
+    local description = tfmdata.descriptions[k]
+    if description then
+        local slookups = description.slookups -- we assume only slookups (we can always extend)
+        if slookups then
+            local shared = tfmdata.shared
+            local rawdata = shared and shared.rawdata
+            if rawdata then
+                local lookuptypes = rawdata.resources.lookuptypes
+                if lookuptypes then
+                    local properties = tfmdata.properties
+                    -- we could cache these
+                    local validlookups, lookuplist = otf.collectlookups(rawdata,kind,properties.script,properties.language)
+                    if validlookups then
+                        for l=1,#lookuplist do
+                            local lookup = lookuplist[l]
+                            local found  = slookups[lookup]
+                            if found then
+                                return found, lookuptypes[lookup]
+                            end
+                        end
+                    end
+                end
+            end
+        end
+    end
+end
+
+otf.getgsub = getgsub -- returns value, gsub_kind
+
+function otf.getsubstitution(tfmdata,k,kind,value)
+    local found, kind = getgsub(tfmdata,k,kind)
+    if not found then
+        --
+    elseif kind == "substitution" then
+        return found
+    elseif kind == "alternate" then
+        local choice = tonumber(value) or 1 -- no random here (yet)
+        return found[choice] or found[1] or k
+    end
+    return k
+end
+
+otf.getalternate = otf.getsubstitution
+
+function otf.getmultiple(tfmdata,k,kind)
+    local found, kind = getgsub(tfmdata,k,kind)
+    if found and kind == "multiple" then
+        return found
+    end
+    return { k }
+end
+
+function otf.getkern(tfmdata,left,right,kind)
+    local kerns = getgsub(tfmdata,left,kind or "kern",true) -- for now we use getsub
+    if kerns then
+        local found = kerns[right]
+        local kind  = type(found)
+        if kind == "table" then
+            found = found[1][3] -- can be more clever
+        elseif kind ~= "number" then
+            found = false
+        end
+        if found then
+            return found * tfmdata.parameters.factor
+        end
+    end
+    return 0
+end
diff --git a/src/fontloader/misc/fontloader-font-otp.lua b/src/fontloader/misc/fontloader-font-otp.lua
index ebf36ed..91bd05b 100644
--- a/src/fontloader/misc/fontloader-font-otp.lua
+++ b/src/fontloader/misc/fontloader-font-otp.lua
@@ -12,9 +12,8 @@ if not modules then modules = { } end modules ['font-otp'] = {
 --
 -- unless we sort all hashes we can get a different pack order (no big deal but size can differ)
 
-local next, type = next, type
+local next, type, tostring = next, type, tostring
 local sort, concat = table.sort, table.concat
-local sortedhash = table.sortedhash
 
 local trace_packing = false  trackers.register("otf.packing", function(v) trace_packing = v end)
 local trace_loading = false  trackers.register("otf.loading", function(v) trace_loading = v end)
@@ -148,6 +147,7 @@ end
 -- we then need to sort more thanks to random hashing
 
 local function packdata(data)
+
     if data then
      -- stripdata(data)
         local h, t, c = { }, { }, { }
@@ -537,6 +537,7 @@ local unpacked_mt = {
 }
 
 local function unpackdata(data)
+
     if data then
         local tables = data.tables
         if tables then
diff --git a/src/fontloader/misc/fontloader-font-tfm.lua b/src/fontloader/misc/fontloader-font-tfm.lua
index ab03788..401dc83 100644
--- a/src/fontloader/misc/fontloader-font-tfm.lua
+++ b/src/fontloader/misc/fontloader-font-tfm.lua
@@ -24,6 +24,7 @@ local constructors             = fonts.constructors
 local encodings                = fonts.encodings
 
 local tfm                      = constructors.newhandler("tfm")
+tfm.version                    = 1.000
 
 local tfmfeatures              = constructors.newfeatures("tfm")
 local registertfmfeature       = tfmfeatures.register
diff --git a/src/fontloader/misc/fontloader-fonts-cbk.lua b/src/fontloader/misc/fontloader-fonts-cbk.lua
index 81b5b6e..9da8151 100644
--- a/src/fontloader/misc/fontloader-fonts-cbk.lua
+++ b/src/fontloader/misc/fontloader-fonts-cbk.lua
@@ -160,12 +160,31 @@ function nodes.handlers.nodepass(head)
                 local range = basefonts[i]
                 local start = range[1]
                 local stop  = range[2]
-                if stop then
-                    start, stop = ligaturing(start,stop)
-                    start, stop = kerning(start,stop)
-                elseif start then
-                    start = ligaturing(start)
-                    start = kerning(start)
+                -- maybe even: if start and start ~= stop then
+                if start or stop then
+                    local prev  = nil
+                    local next  = nil
+                    local front = start == head
+                    if stop then
+                        next  = stop.next
+                        start, stop = ligaturing(start,stop)
+                        start, stop = kerning(start,stop)
+                    elseif start then
+                        prev  = start.prev
+                        start = ligaturing(start)
+                        start = kerning(start)
+                    end
+                    if prev then
+                        start.prev = prev
+                        prev.next  = start
+                    end
+                    if next then
+                        stop.next  = next
+                        next.prev  = stop
+                    end
+                    if front then
+                        head = start
+                    end
                 end
             end
         end
@@ -176,7 +195,7 @@ function nodes.handlers.nodepass(head)
 end
 
 function nodes.handlers.basepass(head)
-    if not basepass then
+    if basepass then
         head = ligaturing(head)
         head = kerning(head)
     end
diff --git a/src/fontloader/misc/fontloader-fonts-inj.lua b/src/fontloader/misc/fontloader-fonts-inj.lua
index 332e920..36781f7 100644
--- a/src/fontloader/misc/fontloader-fonts-inj.lua
+++ b/src/fontloader/misc/fontloader-fonts-inj.lua
@@ -8,7 +8,16 @@ if not modules then modules = { } end modules ['font-inj'] = {
 
 -- This property based variant is not faster but looks nicer than the attribute one. We
 -- need to use rawget (which is apbout 4 times slower than a direct access but we cannot
--- get/set that one for our purpose!
+-- get/set that one for our purpose! This version does a bit more with discretionaries
+-- (and Kai has tested it with his collection of weird fonts.)
+
+-- There is some duplicate code here (especially in the the pre/post/replace branches) but
+-- we go for speed. We could store a list of glyph and mark nodes when registering but it's
+-- cleaner to have an identification pass here. Also, I need to keep tracing in mind so
+-- being too clever here is dangerous.
+
+-- The subtype test is not needed as there will be no (new) properties set, given that we
+-- reset the properties.
 
 if not nodes.properties then return end
 
@@ -80,8 +89,7 @@ function injections.resetcounts()
     keepregisteredcounts  = false
 end
 
--- We need to make sure that a possible metatable will not kick in
--- unexpectedly.
+-- We need to make sure that a possible metatable will not kick in unexpectedly.
 
 function injections.reset(n)
     local p = rawget(properties,n)
@@ -146,7 +154,8 @@ end
 function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) -- hm: nuts or nodes
     local dx =  factor*(exit[1]-entry[1])
     local dy = -factor*(exit[2]-entry[2])
-    local ws, wn = tfmstart.width, tfmnext.width
+    local ws = tfmstart.width
+    local wn = tfmnext.width
     nofregisteredcursives = nofregisteredcursives + 1
     if rlmode < 0 then
         dx = -(dx + wn)
@@ -195,7 +204,10 @@ function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmne
 end
 
 function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2lflag & tfmchr not used
-    local x, y, w, h = factor*spec[1], factor*spec[2], factor*spec[3], factor*spec[4]
+    local x = factor*spec[1]
+    local y = factor*spec[2]
+    local w = factor*spec[3]
+    local h = factor*spec[4]
     if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then -- okay?
         local yoffset   = y - h
         local leftkern  = x      -- both kerns are set in a pair kern compared
@@ -205,9 +217,12 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2l
             if rlmode and rlmode < 0 then
                 leftkern, rightkern = rightkern, leftkern
             end
+            if not injection then
+                injection = "injections"
+            end
             local p = rawget(properties,current)
             if p then
-                local i = rawget(p,"injections")
+                local i = rawget(p,injection)
                 if i then
                     if leftkern ~= 0 then
                         i.leftkern  = (i.leftkern  or 0) + leftkern
@@ -219,19 +234,19 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2l
                         i.yoffset = (i.yoffset or 0) + yoffset
                     end
                 elseif leftkern ~= 0 or rightkern ~= 0 then
-                    p.injections = {
+                    p[injection] = {
                         leftkern  = leftkern,
                         rightkern = rightkern,
                         yoffset   = yoffset,
                     }
                 else
-                    p.injections = {
+                    p[injection] = {
                         yoffset = yoffset,
                     }
                 end
             elseif leftkern ~= 0 or rightkern ~= 0 then
                 properties[current] = {
-                    injections = {
+                    [injection] = {
                         leftkern  = leftkern,
                         rightkern = rightkern,
                         yoffset   = yoffset,
@@ -239,7 +254,7 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2l
                 }
             else
                 properties[current] = {
-                    injections = {
+                    [injection] = {
                         yoffset = yoffset,
                     },
                 }
@@ -250,10 +265,9 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) -- r2l
     return x, y, w, h -- no bound
 end
 
--- this needs checking for rl < 0 but it is unlikely that a r2l script
--- uses kernclasses between glyphs so we're probably safe (KE has a
--- problematic font where marks interfere with rl < 0 in the previous
--- case)
+-- This needs checking for rl < 0 but it is unlikely that a r2l script uses kernclasses between
+-- glyphs so we're probably safe (KE has a problematic font where marks interfere with rl < 0 in
+-- the previous case)
 
 function injections.setkern(current,factor,rlmode,x,injection)
     local dx = factor * x
@@ -285,7 +299,7 @@ function injections.setkern(current,factor,rlmode,x,injection)
     end
 end
 
-function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) -- ba=baseanchor, ma=markanchor
+function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk) -- ba=baseanchor, ma=markanchor
     local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2])
     nofregisteredmarks = nofregisteredmarks + 1
  -- markanchors[nofregisteredmarks] = base
@@ -293,14 +307,20 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) -- ba=basean
         dx = tfmbase.width - dx -- see later commented ox
     end
     local p = rawget(properties,start)
+    -- hm, dejavu serif does a sloppy mark2mark before mark2base
     if p then
         local i = rawget(p,"injections")
         if i then
-            i.markx        = dx
-            i.marky        = dy
-            i.markdir      = rlmode or 0
-            i.markbase     = nofregisteredmarks
-            i.markbasenode = base
+            if i.markmark then
+                -- out of order mkmk: yes or no or option
+            else
+                i.markx        = dx
+                i.marky        = dy
+                i.markdir      = rlmode or 0
+                i.markbase     = nofregisteredmarks
+                i.markbasenode = base
+                i.markmark     = mkmk
+            end
         else
             p.injections = {
                 markx        = dx,
@@ -308,6 +328,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) -- ba=basean
                 markdir      = rlmode or 0,
                 markbase     = nofregisteredmarks,
                 markbasenode = base,
+                markmark     = mkmk,
             }
         end
     else
@@ -318,6 +339,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) -- ba=basean
                 markdir      = rlmode or 0,
                 markbase     = nofregisteredmarks,
                 markbasenode = base,
+                markmark     = mkmk,
             },
         }
     end
@@ -430,30 +452,36 @@ local function show_result(head)
     end
 end
 
--- we could also check for marks here but maybe not all are registered (needs checking)
-
-local function collect_glyphs_1(head)
-    local glyphs, nofglyphs = { }, 0
-    local marks, nofmarks = { }, 0
+local function collect_glyphs(head,offsets)
+    local glyphs, glyphi, nofglyphs = { }, { }, 0
+    local marks, marki, nofmarks = { }, { }, 0
     local nf, tm = nil, nil
-    for n in traverse_id(glyph_code,head) do -- only needed for relevant fonts
-        if getsubtype(n) < 256 then
-            local f = getfont(n)
-            if f ~= nf then
-                nf = f
-                tm = fontdata[nf].resources.marks -- other hash in ctx
-            end
-            if tm and tm[getchar(n)] then
-                nofmarks = nofmarks + 1
-                marks[nofmarks] = n
-            else
-                nofglyphs = nofglyphs + 1
-                glyphs[nofglyphs] = n
+    local n = head
+
+    local function identify(n,what)
+        local f = getfont(n)
+        if f ~= nf then
+            nf = f
+            -- other hash in ctx:
+            tm = fontdata[nf].resources
+            if tm then
+                tm = tm.marks
             end
+        end
+        if tm and tm[getchar(n)] then
+            nofmarks = nofmarks + 1
+            marks[nofmarks] = n
+            marki[nofmarks] = "injections"
+        else
+            nofglyphs = nofglyphs + 1
+            glyphs[nofglyphs] = n
+            glyphi[nofglyphs] = what
+        end
+        if offsets then
             -- yoffsets can influence curs steps
             local p = rawget(properties,n)
             if p then
-                local i = rawget(p,"injections")
+                local i = rawget(p,what)
                 if i then
                     local yoffset = i.yoffset
                     if yoffset and yoffset ~= 0 then
@@ -463,38 +491,50 @@ local function collect_glyphs_1(head)
             end
         end
     end
-    return glyphs, nofglyphs, marks, nofmarks
-end
 
-local function collect_glyphs_2(head)
-    local glyphs, nofglyphs = { }, 0
-    local marks, nofmarks = { }, 0
-    local nf, tm = nil, nil
-    for n in traverse_id(glyph_code,head) do
-        if getsubtype(n) < 256 then
-            local f = getfont(n)
-            if f ~= nf then
-                nf = f
-                tm = fontdata[nf].resources.marks -- other hash in ctx
-            end
-            if tm and tm[getchar(n)] then
-                nofmarks = nofmarks + 1
-                marks[nofmarks] = n
-            else
-                nofglyphs = nofglyphs + 1
-                glyphs[nofglyphs] = n
-            end
+    while n do -- only needed for relevant fonts
+        local id = getid(n)
+        if id == glyph_code then
+            identify(n,"injections")
+        elseif id == disc_code then
+            local d = getfield(n,"pre")
+            if d then
+                for n in traverse_id(glyph_code,d) do
+                    if getsubtype(n) < 256 then
+                        identify(n,"preinjections")
+                    end
+                end
+			end
+            local d = getfield(n,"post")
+            if d then
+                for n in traverse_id(glyph_code,d) do
+                    if getsubtype(n) < 256 then
+                        identify(n,"postinjections")
+                    end
+                end
+			end
+            local d = getfield(n,"replace")
+            if d then
+                for n in traverse_id(glyph_code,d) do
+                    if getsubtype(n) < 256 then
+                        identify(n,"replaceinjections")
+                    end
+                end
+			end
         end
+		n = getnext(n)
     end
-    return glyphs, nofglyphs, marks, nofmarks
+
+    return glyphs, glyphi, nofglyphs, marks, marki, nofmarks
 end
 
-local function inject_marks(marks,nofmarks)
+local function inject_marks(marks,marki,nofmarks)
     for i=1,nofmarks do
-        local n = marks[i]
+        local n  = marks[i]
         local pn = rawget(properties,n)
         if pn then
-            pn = rawget(pn,"injections")
+            local ni = marki[i]
+            local pn = rawget(pn,ni)
             if pn then
                 local p = pn.markbasenode
                 if p then
@@ -503,7 +543,7 @@ local function inject_marks(marks,nofmarks)
                     local rightkern = nil
                     local pp = rawget(properties,p)
                     if pp then
-                        pp = rawget(pp,"injections")
+                        pp = rawget(pp,ni)
                         if pp then
                             rightkern = pp.rightkern
                         end
@@ -516,13 +556,22 @@ local function inject_marks(marks,nofmarks)
                         else
                             -- kern(x) glyph(p) kern(w-x) mark(n)
                          -- ox = px - getfield(p,"width") + pn.markx - pp.leftkern
-                            local leftkern = pp.leftkern
-                            if leftkern then
-                                ox = px - pn.markx
+                            --
+							-- According to Kai we don't need to handle leftkern here but I'm
+                            -- pretty sure I've run into a case where it was needed so maybe
+	                        -- some day we need something more clever here.
+                            --
+							if false then
+                                -- a mark with kerning
+                                local leftkern = pp.leftkern
+                                if leftkern then
+                                    ox = px - pn.markx - leftkern
+                                else
+                                    ox = px - pn.markx
+                                end
                             else
-                                ox = px - pn.markx - leftkern
+                                ox = px - pn.markx
                             end
--- report_injections("l2r case 1: %p",ox)
                         end
                     else
                         -- we need to deal with fonts that have marks with width
@@ -548,12 +597,13 @@ local function inject_marks(marks,nofmarks)
                     setfield(n,"xoffset",ox)
                     --
                     local py = getfield(p,"yoffset")
-                    local oy = 0
-                    if marks[p] then
-                        oy = py + pn.marky
-                    else
-                        oy = getfield(n,"yoffset") + py + pn.marky
-                    end
+--                     local oy = 0
+--                     if marks[p] then
+--                         oy = py + pn.marky
+--                     else
+--                         oy = getfield(n,"yoffset") + py + pn.marky
+--                     end
+                    local oy = getfield(n,"yoffset") + py + pn.marky
                     setfield(n,"yoffset",oy)
                 else
                     -- normally this can't happen (only when in trace mode which is a special case anyway)
@@ -564,14 +614,14 @@ local function inject_marks(marks,nofmarks)
     end
 end
 
-local function inject_cursives(glyphs,nofglyphs)
+local function inject_cursives(glyphs,glyphi,nofglyphs)
     local cursiveanchor, lastanchor = nil, nil
     local minc, maxc, last = 0, 0, nil
     for i=1,nofglyphs do
-        local n = glyphs[i]
+        local n  = glyphs[i]
         local pn = rawget(properties,n)
         if pn then
-            pn = rawget(pn,"injections")
+            pn = rawget(pn,glyphi[i])
         end
         if pn then
             local cursivex = pn.cursivex
@@ -630,7 +680,7 @@ local function inject_cursives(glyphs,nofglyphs)
      -- if maxc > 0 and not cursiveanchor then
      --     local ny = getfield(n,"yoffset")
      --     for i=maxc,minc,-1 do
-     --         local ti = glyphs[i]
+     --         local ti = glyphs[i][1]
      --         ny = ny + properties[ti].cursivedy
      --         setfield(ti,"yoffset",ny) -- why not add ?
      --     end
@@ -647,23 +697,67 @@ local function inject_cursives(glyphs,nofglyphs)
     end
 end
 
-local function inject_kerns(head,list,length)
- -- todo: pre/post/replace
+-- G  +D-pre        G
+--     D-post+
+--    +D-replace+
+--
+-- G  +D-pre       +D-pre
+--     D-post      +D-post
+--    +D-replace   +D-replace
+
+local function inject_kerns(head,glist,ilist,length) -- not complete ! compare with inject_kerns_only (but unlikely disc here)
     for i=1,length do
-        local n  = list[i]
+        local n  = glist[i]
         local pn = rawget(properties,n)
         if pn then
-            local i = rawget(pn,"injections")
-            if i then
-                local leftkern = i.leftkern
-                if leftkern and leftkern ~= 0 then
-                    insert_node_before(head,n,newkern(leftkern)) -- type 0/2
-                end
-                local rightkern = i.rightkern
-                if rightkern and rightkern ~= 0 then
-                    insert_node_after(head,n,newkern(rightkern)) -- type 0/2
-                end
-            end
+			local dp = nil
+			local dr = nil
+            local ni = ilist[i]
+            local p  = nil
+			if ni == "injections" then
+				p = getprev(n)
+				if p then
+					local id = getid(p)
+					if id == disc_code then
+						dp = getfield(p,"post")
+						dr = getfield(p,"replace")
+					end
+				end
+			end
+			if dp then
+				local i = rawget(pn,"postinjections")
+				if i then
+					local leftkern = i.leftkern
+					if leftkern and leftkern ~= 0 then
+						local t = find_tail(dp)
+						insert_node_after(dp,t,newkern(leftkern))
+                        setfield(p,"post",dp) -- currently we need to force a tail refresh
+					end
+				end
+			end
+			if dr then
+				local i = rawget(pn,"replaceinjections")
+				if i then
+					local leftkern = i.leftkern
+					if leftkern and leftkern ~= 0 then
+						local t = find_tail(dr)
+						insert_node_after(dr,t,newkern(leftkern))
+                        setfield(p,"replace",dr) -- currently we need to force a tail refresh
+					end
+				end
+			else
+				local i = rawget(pn,ni)
+				if i then
+					local leftkern = i.leftkern
+					if leftkern and leftkern ~= 0 then
+						insert_node_before(head,n,newkern(leftkern)) -- type 0/2
+					end
+					local rightkern = i.rightkern
+					if rightkern and rightkern ~= 0 then
+						insert_node_after(head,n,newkern(rightkern)) -- type 0/2
+					end
+				end
+			end
         end
     end
 end
@@ -673,23 +767,18 @@ local function inject_everything(head,where)
     if trace_injections then
         trace(head,"everything")
     end
-    local glyphs, nofglyphs, marks, nofmarks
-    if nofregisteredpairs > 0 then
-        glyphs, nofglyphs, marks, nofmarks = collect_glyphs_1(head)
-    else
-        glyphs, nofglyphs, marks, nofmarks = collect_glyphs_2(head)
-    end
+    local glyphs, glyphi, nofglyphs, marks, marki, nofmarks = collect_glyphs(head,nofregisteredpairs > 0)
     if nofglyphs > 0 then
         if nofregisteredcursives > 0 then
-            inject_cursives(glyphs,nofglyphs)
+            inject_cursives(glyphs,glyphi,nofglyphs)
         end
         if nofregisteredmarks > 0 then -- and nofmarks > 0
-            inject_marks(marks,nofmarks)
+            inject_marks(marks,marki,nofmarks)
         end
-        inject_kerns(head,glyphs,nofglyphs)
+        inject_kerns(head,glyphs,glyphi,nofglyphs)
     end
     if nofmarks > 0 then
-        inject_kerns(head,marks,nofmarks)
+        inject_kerns(head,marks,marki,nofmarks)
 	end
     if keepregisteredcounts then
         keepregisteredcounts  = false
@@ -702,13 +791,21 @@ local function inject_everything(head,where)
     return tonode(head), true
 end
 
+-- G  +D-pre        G
+--     D-post+
+--    +D-replace+
+--
+-- G  +D-pre       +D-pre
+--     D-post      +D-post
+--    +D-replace   +D-replace
+
 local function inject_kerns_only(head,where)
     head = tonut(head)
     if trace_injections then
         trace(head,"kerns")
     end
     local n = head
-    local p = nil
+    local p = nil -- disc node when non-nil
     while n do
         local id = getid(n)
         if id == glyph_code then
@@ -724,6 +821,7 @@ local function inject_kerns_only(head,where)
                                 if leftkern and leftkern ~= 0 then
                                     local t = find_tail(d)
                                     insert_node_after(d,t,newkern(leftkern))
+                                    setfield(p,"post",d) -- currently we need to force a tail refresh
                                 end
                             end
                         end
@@ -735,6 +833,7 @@ local function inject_kerns_only(head,where)
                                 if leftkern and leftkern ~= 0 then
                                     local t = find_tail(d)
                                     insert_node_after(d,t,newkern(leftkern))
+                                    setfield(p,"replace",d) -- currently we need to force a tail refresh
                                 end
                             end
                         else
@@ -747,6 +846,7 @@ local function inject_kerns_only(head,where)
                             end
                         end
                     else
+                        -- this is the most common case
                         local i = rawget(pn,"injections")
                         if i then
                             local leftkern = i.leftkern
@@ -756,8 +856,6 @@ local function inject_kerns_only(head,where)
                         end
                     end
                 end
-            else
-                break
             end
             p = nil
         elseif id == disc_code then
@@ -812,7 +910,7 @@ local function inject_kerns_only(head,where)
                 local h = d
                 for n in traverse_id(glyph_code,d) do
                     if getsubtype(n) < 256 then
-                        local pn = rawget(properties,n) -- why can it be empty { }
+                        local pn = rawget(properties,n)
                         if pn then
                             local i = rawget(pn,"replaceinjections")
                             if i then
@@ -850,9 +948,8 @@ local function inject_pairs_only(head,where)
     if trace_injections then
         trace(head,"pairs")
     end
-    --
     local n = head
-    local p = nil
+    local p = nil -- disc node when non-nil
     while n do
         local id = getid(n)
         if id == glyph_code then
@@ -868,6 +965,7 @@ local function inject_pairs_only(head,where)
                                 if leftkern and leftkern ~= 0 then
                                     local t = find_tail(d)
                                     insert_node_after(d,t,newkern(leftkern))
+                                    setfield(p,"post",d) -- currently we need to force a tail refresh
                                 end
                              -- local rightkern = i.rightkern
                              -- if rightkern and rightkern ~= 0 then
@@ -884,6 +982,7 @@ local function inject_pairs_only(head,where)
                                 if leftkern and leftkern ~= 0 then
                                     local t = find_tail(d)
                                     insert_node_after(d,t,newkern(leftkern))
+                                    setfield(p,"replace",d) -- currently we need to force a tail refresh
                                 end
                              -- local rightkern = i.rightkern
                              -- if rightkern and rightkern ~= 0 then
@@ -909,24 +1008,22 @@ local function inject_pairs_only(head,where)
                         -- this is the most common case
                         local i = rawget(pn,"injections")
                         if i then
-                            local yoffset = i.yoffset
-                            if yoffset and yoffset ~= 0 then
-                                setfield(n,"yoffset",yoffset)
-                            end
                             local leftkern = i.leftkern
                             if leftkern and leftkern ~= 0 then
-                                insert_node_before(head,n,newkern(leftkern))
+                                head = insert_node_before(head,n,newkern(leftkern))
                             end
                             local rightkern = i.rightkern
                             if rightkern and rightkern ~= 0 then
                                 insert_node_after(head,n,newkern(rightkern))
                                 n = getnext(n) -- to be checked
                             end
+                            local yoffset = i.yoffset
+                            if yoffset and yoffset ~= 0 then
+                                setfield(n,"yoffset",yoffset)
+                            end
                         end
                     end
                 end
-            else
-                break
             end
             p = nil
         elseif id == disc_code then
@@ -935,16 +1032,12 @@ local function inject_pairs_only(head,where)
                 local h = d
                 for n in traverse_id(glyph_code,d) do
                     if getsubtype(n) < 256 then
-                        local p = rawget(properties,n)
-                        if p then
-                            local i = rawget(p,"preinjections")
+                        local pn = rawget(properties,n)
+                        if pn then
+                            local i = rawget(pn,"preinjections")
                             if i then
-                                local yoffset = i.yoffset
-                                if yoffset and yoffset ~= 0 then
-                                    setfield(n,"yoffset",yoffset)
-                                end
                                 local leftkern = i.leftkern
-                                if leftkern ~= 0 then
+                                if leftkern and leftkern ~= 0 then
                                     h = insert_node_before(h,n,newkern(leftkern))
                                 end
                                 local rightkern = i.rightkern
@@ -952,6 +1045,10 @@ local function inject_pairs_only(head,where)
                                     insert_node_after(head,n,newkern(rightkern))
                                     n = getnext(n) -- to be checked
                                 end
+                                local yoffset = i.yoffset
+                                if yoffset and yoffset ~= 0 then
+                                    setfield(n,"yoffset",yoffset)
+                                end
                             end
                         end
                     else
@@ -967,14 +1064,10 @@ local function inject_pairs_only(head,where)
                 local h = d
                 for n in traverse_id(glyph_code,d) do
                     if getsubtype(n) < 256 then
-                        local p = rawget(properties,n)
-                        if p then
-                            local i = rawget(p,"postinjections")
+                        local pn = rawget(properties,n)
+                        if pn then
+                            local i = rawget(pn,"postinjections")
                             if i then
-                                local yoffset = i.yoffset
-                                if yoffset and yoffset ~= 0 then
-                                    setfield(n,"yoffset",yoffset)
-                                end
                                 local leftkern = i.leftkern
                                 if leftkern and leftkern ~= 0 then
                                     h = insert_node_before(h,n,newkern(leftkern))
@@ -984,6 +1077,10 @@ local function inject_pairs_only(head,where)
                                     insert_node_after(head,n,newkern(rightkern))
                                     n = getnext(n) -- to be checked
                                 end
+                                local yoffset = i.yoffset
+                                if yoffset and yoffset ~= 0 then
+                                    setfield(n,"yoffset",yoffset)
+                                end
                             end
                         end
                     else
@@ -999,14 +1096,10 @@ local function inject_pairs_only(head,where)
                 local h = d
                 for n in traverse_id(glyph_code,d) do
                     if getsubtype(n) < 256 then
-                        local p = rawget(properties,n)
-                        if p then
-                            local i = rawget(p,"replaceinjections")
+                        local pn = rawget(properties,n)
+                        if pn then
+                            local i = rawget(pn,"replaceinjections")
                             if i then
-                                local yoffset = i.yoffset
-                                if yoffset and yoffset ~= 0 then
-                                    setfield(n,"yoffset",yoffset)
-                                end
                                 local leftkern = i.leftkern
                                 if leftkern and leftkern ~= 0 then
                                     h = insert_node_before(h,n,newkern(leftkern))
@@ -1016,6 +1109,10 @@ local function inject_pairs_only(head,where)
                                     insert_node_after(head,n,newkern(rightkern))
                                     n = getnext(n) -- to be checked
                                 end
+                                local yoffset = i.yoffset
+                                if yoffset and yoffset ~= 0 then
+                                    setfield(n,"yoffset",yoffset)
+                                end
                             end
                         end
                     else
@@ -1042,7 +1139,7 @@ local function inject_pairs_only(head,where)
     return tonode(head), true
 end
 
-function injections.handler(head,where) -- optimize for n=1 ?
+function injections.handler(head,where)
     if nofregisteredmarks > 0 or nofregisteredcursives > 0 then
         return inject_everything(head,where)
     elseif nofregisteredpairs > 0 then
diff --git a/src/fontloader/misc/fontloader-fonts-otn.lua b/src/fontloader/misc/fontloader-fonts-otn.lua
index dd3aa61..1b99c56 100644
--- a/src/fontloader/misc/fontloader-fonts-otn.lua
+++ b/src/fontloader/misc/fontloader-fonts-otn.lua
@@ -6,12 +6,16 @@ if not modules then modules = { } end modules ['font-otn'] = {
     license   = "see context related readme files",
 }
 
--- todo: looks like we have a leak somewhere (probably in ligatures)
--- todo: copy attributes to disc
-
 -- this is a context version which can contain experimental code, but when we
 -- have serious patches we also need to change the other two font-otn files
 
+-- at some point i might decide to convert the whole list into a table and then
+-- run over that instead (but it has some drawbacks as we also need to deal with
+-- attributes and such so we need to keep a lot of track - which is why i rejected
+-- that method - although it has become a bit easier in the meantime so it might
+-- become an alternative (by that time i probably have gone completely lua) .. the
+-- usual chicken-egg issues ... maybe mkix as it's no real tex any more then
+
 -- preprocessors = { "nodes" }
 
 -- anchor class : mark, mkmk, curs, mklg (todo)
@@ -40,7 +44,18 @@ if not modules then modules = { } end modules ['font-otn'] = {
 -- mark (to mark) code is still not what it should be (too messy but we need some more extreem husayni tests)
 -- remove some optimizations (when I have a faster machine)
 --
--- maybe redo the lot some way (more context specific)
+-- beware:
+--
+-- we do some disc jugling where we need to keep in mind that the
+-- pre, post and replace fields can have prev pointers to a nesting
+-- node ... i wonder if that is still needed
+--
+-- not possible:
+--
+-- \discretionary {alpha-} {betagammadelta}
+--   {\discretionary {alphabeta-} {gammadelta}
+--      {\discretionary {alphabetagamma-} {delta}
+--         {alphabetagammadelta}}}
 
 --[[ldx--
 <p>This module is a bit more split up that I'd like but since we also want to test
@@ -65,9 +80,12 @@ is currently acceptable. Not all functions are implemented yet, often because I
 lack the fonts for testing. Many scripts are not yet supported either, but I will
 look into them as soon as <l n='context'/> users ask for it.</p>
 
-<p>Because there are different interpretations possible, I will extend the code
-with more (configureable) variants. I can also add hooks for users so that they can
-write their own extensions.</p>
+<p>The specification leaves room for interpretation. In case of doubt the microsoft
+implementation is the reference as it is the most complete one. As they deal with
+lots of scripts and fonts, Kai and Ivo did a lot of testing of the generic code and
+their suggestions help improve the code. I'm aware that not all border cases can be
+taken care of, unless we accept excessive runtime, and even then the interference
+with other mechanisms (like hyphenation) are not trivial.</p>
 
 <p>Glyphs are indexed not by unicode but in their own way. This is because there is no
 relationship with unicode at all, apart from the fact that a font might cover certain
@@ -94,12 +112,12 @@ when there's a fix in the <l n='fontforge'/> library or <l n='lua'/> code that
 results in different tables.</p>
 --ldx]]--
 
--- action                    handler     chainproc             chainmore              comment
+-- action                    handler     chainproc
 --
--- gsub_single               ok          ok                    ok
--- gsub_multiple             ok          ok                    not implemented yet
--- gsub_alternate            ok          ok                    not implemented yet
--- gsub_ligature             ok          ok                    ok
+-- gsub_single               ok          ok
+-- gsub_multiple             ok          ok
+-- gsub_alternate            ok          ok
+-- gsub_ligature             ok          ok
 -- gsub_context              ok          --
 -- gsub_contextchain         ok          --
 -- gsub_reversecontextchain  ok          --
@@ -123,7 +141,6 @@ results in different tables.</p>
 -- chainmore : multiple substitutions triggered by contextual lookup (e.g. fij -> f + ij)
 --
 -- remark: the 'not implemented yet' variants will be done when we have fonts that use them
--- remark: we need to check what to do with discretionaries
 
 -- We used to have independent hashes for lookups but as the tags are unique
 -- we now use only one hash. If needed we can have multiple again but in that
@@ -131,16 +148,14 @@ results in different tables.</p>
 
 -- Todo: make plugin feature that operates on char/glyphnode arrays
 
-local concat, insert, remove = table.concat, table.insert, table.remove
-local gmatch, gsub, find, match, lower, strip = string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip
-local type, next, tonumber, tostring = type, next, tonumber, tostring
-local lpegmatch = lpeg.match
+local type, next, tonumber = type, next, tonumber
 local random = math.random
 local formatters = string.formatters
 
 local logs, trackers, nodes, attributes = logs, trackers, nodes, attributes
 
-local registertracker = trackers.register
+local registertracker   = trackers.register
+local registerdirective = directives.register
 
 local fonts = fonts
 local otf   = fonts.handlers.otf
@@ -162,6 +177,16 @@ local trace_steps        = false  registertracker("otf.steps",        function(v
 local trace_skips        = false  registertracker("otf.skips",        function(v) trace_skips        = v end)
 local trace_directions   = false  registertracker("otf.directions",   function(v) trace_directions   = v end)
 
+local trace_kernruns     = false  registertracker("otf.kernruns",     function(v) trace_kernruns     = v end)
+local trace_discruns     = false  registertracker("otf.discruns",     function(v) trace_discruns     = v end)
+local trace_compruns     = false  registertracker("otf.compruns",     function(v) trace_compruns     = v end)
+
+local quit_on_no_replacement = true  -- maybe per font
+local zwnjruns               = true
+
+registerdirective("otf.zwnjruns",                 function(v) zwnjruns = v end)
+registerdirective("otf.chain.quitonnoreplacement",function(value) quit_on_no_replacement = value end)
+
 local report_direct   = logs.reporter("fonts","otf direct")
 local report_subchain = logs.reporter("fonts","otf subchain")
 local report_chain    = logs.reporter("fonts","otf chain")
@@ -230,11 +255,7 @@ local math_code          = nodecodes.math
 
 local dir_code           = whatcodes.dir
 local localpar_code      = whatcodes.localpar
-
 local discretionary_code = disccodes.discretionary
-local regular_code       = disccodes.regular
-local automatic_code     = disccodes.automatic
-
 local ligature_code      = glyphcodes.ligature
 
 local privateattribute   = attributes.private
@@ -286,6 +307,15 @@ local handlers            = { }
 local rlmode              = 0
 local featurevalue        = false
 
+local sweephead           = { }
+local sweepnode           = nil
+local sweepprev           = nil
+local sweepnext           = nil
+
+local notmatchpre         = { }
+local notmatchpost        = { }
+local notmatchreplace     = { }
+
 -- head is always a whatsit so we can safely assume that head is not changed
 
 -- we use this for special testing and documentation
@@ -376,8 +406,66 @@ local function copy_glyph(g) -- next and prev are untouched !
     end
 end
 
---
+local function flattendisk(head,disc)
+    local replace = getfield(disc,"replace")
+    setfield(disc,"replace",nil)
+    free_node(disc)
+    if head == disc then
+        local next = getnext(disc)
+        if replace then
+            if next then
+                local tail = find_node_tail(replace)
+                setfield(tail,"next",next)
+                setfield(next,"prev",tail)
+            end
+            return replace, replace
+        elseif next then
+            return next, next
+        else
+            return -- maybe warning
+        end
+    else
+        local next = getnext(disc)
+        local prev = getprev(disc)
+        if replace then
+            local tail = find_node_tail(replace)
+            if next then
+                setfield(tail,"next",next)
+                setfield(next,"prev",tail)
+            end
+            setfield(prev,"next",replace)
+            setfield(replace,"prev",prev)
+            return head, replace
+        else
+            if next then
+                setfield(next,"prev",prev)
+            end
+            setfield(prev,"next",next)
+            return head, next
+        end
+    end
+end
 
+local function appenddisc(disc,list)
+    local post    = getfield(disc,"post")
+    local replace = getfield(disc,"replace")
+    local phead   = list
+    local rhead   = copy_node_list(list)
+    local ptail   = find_node_tail(post)
+    local rtail   = find_node_tail(replace)
+    if post then
+        setfield(ptail,"next",phead)
+        setfield(phead,"prev",ptail)
+    else
+        setfield(disc,"post",phead)
+    end
+    if replace then
+        setfield(rtail,"next",rhead)
+        setfield(rhead,"prev",rtail)
+    else
+        setfield(disc,"replace",rhead)
+    end
+end
 
 -- start is a mark and we need to keep that one
 
@@ -416,8 +504,8 @@ end
 -- iteration this becomes a KAF-LAM-ALEF with a SHADDA on the second and a FATHA on the
 -- third component.
 
-local function getcomponentindex(start)
-    if getid(start) ~= glyph_code then
+local function getcomponentindex(start) -- we could store this offset in the glyph (nofcomponents)
+    if getid(start) ~= glyph_code then  -- and then get rid of all components
         return 0
     elseif getsubtype(start) == ligature_code then
         local i = 0
@@ -434,16 +522,28 @@ local function getcomponentindex(start)
     end
 end
 
--- eventually we will do positioning in an other way (needs addional w/h/d fields)
+local a_noligature = attributes.private("noligature")
 
 local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound) -- brr head
+    if getattr(start,a_noligature) == 1 then
+        -- so we can do: e\noligature{ff}e e\noligature{f}fie (we only look at the first)
+        return head, start
+    end
     if start == stop and getchar(start) == char then
         resetinjection(start)
         setfield(start,"char",char)
         return head, start
     end
+    -- needs testing (side effects):
+    local components = getfield(start,"components")
+    if components then
+     -- we get a double free .. needs checking
+     -- flush_node_list(components)
+    end
+    --
     local prev = getprev(start)
     local next = getnext(stop)
+    local comp = start
     setfield(start,"prev",nil)
     setfield(stop,"next",nil)
     local base = copy_glyph(start)
@@ -453,15 +553,15 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun
     resetinjection(base)
     setfield(base,"char",char)
     setfield(base,"subtype",ligature_code)
-    setfield(base,"components",start) -- start can have components
+    setfield(base,"components",comp) -- start can have components ... do we need to flush?
     if prev then
         setfield(prev,"next",base)
     end
     if next then
         setfield(next,"prev",base)
     end
-    setfield(base,"next",next)
     setfield(base,"prev",prev)
+    setfield(base,"next",next)
     if not discfound then
         local deletemarks = markflag ~= "mark"
         local components = start
@@ -480,7 +580,9 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun
                 if trace_marks then
                     logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),getligaindex(start))
                 end
-                head, current = insert_node_after(head,current,copy_node(start)) -- unlikely that mark has components
+                local n = copy_node(start)
+                copyinjection(n,start)
+                head, current = insert_node_after(head,current,n) -- unlikely that mark has components
             elseif trace_marks then
                 logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char))
             end
@@ -501,17 +603,85 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun
             end
             start = getnext(start)
         end
+    else
+        -- discfound ... forget about marks .. probably no scripts that hyphenate and have marks
+        local discprev = getfield(discfound,"prev")
+        local discnext = getfield(discfound,"next")
+        if discprev and discnext then
+            -- we assume normalization in context, and don't care about generic ... especially
+            -- \- can give problems as there we can have a negative char but that won't match
+            -- anyway
+            local pre     = getfield(discfound,"pre")
+            local post    = getfield(discfound,"post")
+            local replace = getfield(discfound,"replace")
+            if not replace then -- todo: signal simple hyphen
+                local prev = getfield(base,"prev")
+                local copied = copy_node_list(comp)
+                setfield(discnext,"prev",nil) -- also blocks funny assignments
+                setfield(discprev,"next",nil) -- also blocks funny assignments
+                if pre then
+                    setfield(discprev,"next",pre)
+                    setfield(pre,"prev",discprev)
+                end
+                pre = comp
+                if post then
+                    local tail = find_node_tail(post)
+                    setfield(tail,"next",discnext)
+                    setfield(discnext,"prev",tail)
+                    setfield(post,"prev",nil)
+                else
+                    post = discnext
+                end
+                setfield(prev,"next",discfound)
+                setfield(discfound,"prev",prev)
+                setfield(discfound,"next",next)
+                setfield(next,"prev",discfound)
+                setfield(base,"next",nil)
+                setfield(base,"prev",nil)
+                setfield(base,"components",copied)
+                setfield(discfound,"pre",pre)
+                setfield(discfound,"post",post)
+                setfield(discfound,"replace",base)
+                setfield(discfound,"subtype",discretionary_code)
+                base = prev -- restart
+            end
+        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(getchar(start)),gref(replacement))
+local function multiple_glyphs(head,start,multiple,ignoremarks)
+    local nofmultiples = #multiple
+    if nofmultiples > 0 then
+        resetinjection(start)
+        setfield(start,"char",multiple[1])
+        if nofmultiples > 1 then
+            local sn = getnext(start)
+            for k=2,nofmultiples do -- todo: use insert_node
+-- untested:
+--
+-- while ignoremarks and marks[getchar(sn)] then
+--     local sn = getnext(sn)
+-- end
+                local n = copy_node(start) -- ignore components
+                resetinjection(n)
+                setfield(n,"char",multiple[k])
+                setfield(n,"prev",start)
+                setfield(n,"next",sn)
+                if sn then
+                    setfield(sn,"prev",n)
+                end
+                setfield(start,"next",n)
+                start = n
+            end
+        end
+        return head, start, true
+    else
+        if trace_multiples then
+            logprocess("no multiple for %s",gref(getchar(start)))
+        end
+        return head, start, false
     end
-    resetinjection(start)
-    setfield(start,"char",replacement)
-    return head, start, true
 end
 
 local function get_alternative_glyph(start,alternatives,value,trace_alternatives)
@@ -546,38 +716,15 @@ local function get_alternative_glyph(start,alternatives,value,trace_alternatives
     end
 end
 
-local function multiple_glyphs(head,start,multiple,ignoremarks)
-    local nofmultiples = #multiple
-    if nofmultiples > 0 then
-        resetinjection(start)
-        setfield(start,"char",multiple[1])
-        if nofmultiples > 1 then
-            local sn = getnext(start)
-            for k=2,nofmultiples do -- todo: use insert_node
--- untested:
---
--- while ignoremarks and marks[getchar(sn)] then
---     local sn = getnext(sn)
--- end
-                local n = copy_node(start) -- ignore components
-                resetinjection(n)
-                setfield(n,"char",multiple[k])
-                setfield(n,"next",sn)
-                setfield(n,"prev",start)
-                if sn then
-                    setfield(sn,"prev",n)
-                end
-                setfield(start,"next",n)
-                start = n
-            end
-        end
-        return head, start, true
-    else
-        if trace_multiples then
-            logprocess("no multiple for %s",gref(getchar(start)))
-        end
-        return head, start, false
+-- handlers
+
+function handlers.gsub_single(head,start,kind,lookupname,replacement)
+    if trace_singles then
+        logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(getchar(start)),gref(replacement))
     end
+    resetinjection(start)
+    setfield(start,"char",replacement)
+    return head, start, true
 end
 
 function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence)
@@ -605,7 +752,7 @@ function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence)
 end
 
 function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
-    local s, stop, discfound = getnext(start), nil, false
+    local s, stop = getnext(start), nil
     local startchar = getchar(start)
     if marks[startchar] then
         while s do
@@ -633,24 +780,30 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
                 else
                     head, start = markstoligature(kind,lookupname,head,start,stop,lig)
                 end
-                return head, start, true
+                return head, start, true, false
             else
                 -- ok, goto next lookup
             end
         end
     else
-        local skipmark = sequence.flags[1]
+        local skipmark  = sequence.flags[1]
+        local discfound = false
+        local lastdisc  = nil
         while s do
             local id = getid(s)
-            if id == glyph_code and getsubtype(s)<256 then
-                if getfont(s) == currentfont then
+            if id == glyph_code and getsubtype(s)<256 then -- not needed
+                if getfont(s) == currentfont then          -- also not needed only when mark
                     local char = getchar(s)
                     if skipmark and marks[char] then
                         s = getnext(s)
-                    else
-                        local lg = ligature[char]
+                    else -- ligature is a tree
+                        local lg = ligature[char] -- can there be multiple in a row? maybe in a bad font
                         if lg then
-                            stop = s
+                            if not discfound and lastdisc then
+                                discfound = lastdisc
+                                lastdisc  = nil
+                            end
+                            stop = s -- needed for fake so outside then
                             ligature = lg
                             s = getnext(s)
                         else
@@ -661,13 +814,13 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
                     break
                 end
             elseif id == disc_code then
-                discfound = true
+                lastdisc = s
                 s = getnext(s)
             else
                 break
             end
         end
-        local lig = ligature.ligature
+        local lig = ligature.ligature -- can't we get rid of this .ligature?
         if lig then
             if stop then
                 if trace_ligatures then
@@ -685,14 +838,88 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
                     logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig))
                 end
             end
-            return head, start, true
+            return head, start, true, discfound
         else
-            -- weird but happens
+            -- weird but happens, pseudo ligatures ... just the components
         end
     end
+    return head, start, false, discfound
+end
+
+function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence,injection)
+    local startchar = getchar(start)
+    local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,injection) -- ,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)
+    end
     return head, start, false
 end
 
+function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence,lookuphash,i,injection)
+    -- todo: kerns in disc nodes: pre, post, replace -> loop over disc too
+    -- todo: kerns in components of ligatures
+    local snext = getnext(start)
+    if not snext then
+        return head, start, false
+    else
+        local prev   = start
+        local done   = false
+        local factor = tfmdata.parameters.factor
+        local lookuptype = lookuptypes[lookupname]
+        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 = getnext(snext)
+            else
+                if not krn then
+                    -- skip
+                elseif type(krn) == "table" then
+                    if lookuptype == "pair" then -- probably not needed
+                        local a, b = krn[2], krn[3]
+                        if a and #a > 0 then
+                            local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,injection) -- characters[startchar])
+                            if trace_kerns then
+                                local startchar = getchar(start)
+                                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 x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,injection) -- characters[nextchar])
+                            if trace_kerns then
+                                local startchar = getchar(start)
+                                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)
+                            end
+                        end
+                    else -- wrong ... position has different entries
+                        report_process("%s: check this out (old kern stuff)",pref(kind,lookupname))
+                     -- local a, b = krn[2], krn[6]
+                     -- 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",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar))
+                     --     end
+                     -- end
+                     -- if b and b ~= 0 then
+                     --     logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor)
+                     -- end
+                    end
+                    done = true
+                elseif krn ~= 0 then
+                    local k = setkern(snext,factor,rlmode,krn,injection)
+                    if trace_kerns then
+                        logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar)) -- prev?
+                    end
+                    done = true
+                end
+                break
+            end
+        end
+        return head, start, done
+    end
+end
+
 --[[ldx--
 <p>We get hits on a mark, but we're not sure if the it has to be applied so
 we need to explicitly test for basechar, baselig and basemark entries.</p>
@@ -855,7 +1082,7 @@ function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence
                             if al[anchor] then
                                 local ma = markanchors[anchor]
                                 if ma then
-                                    local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar])
+                                    local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar],true)
                                     if trace_marks then
                                         logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
                                             pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
@@ -938,85 +1165,11 @@ function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence)
     end
 end
 
-function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence)
-    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)
-    end
-    return head, start, false
-end
-
-function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence)
-    -- todo: kerns in disc nodes: pre, post, replace -> loop over disc too
-    -- todo: kerns in components of ligatures
-    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 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 = getnext(snext)
-            else
-                if not krn then
-                    -- skip
-                elseif type(krn) == "table" then
-                    if lookuptype == "pair" then -- probably not needed
-                        local a, b = krn[2], krn[3]
-                        if a and #a > 0 then
-                            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 = 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)
-                            end
-                        end
-                    else -- wrong ... position has different entries
-                        report_process("%s: check this out (old kern stuff)",pref(kind,lookupname))
-                     -- local a, b = krn[2], krn[6]
-                     -- 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",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar))
-                     --     end
-                     -- end
-                     -- if b and b ~= 0 then
-                     --     logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor)
-                     -- end
-                    end
-                    done = true
-                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(getchar(prev)),gref(nextchar))
-                    end
-                    done = true
-                end
-                break
-            end
-        end
-        return head, start, done
-    end
-end
-
 --[[ldx--
 <p>I will implement multiple chain replacements once I run into a font that uses
 it. It's not that complex to handle.</p>
 --ldx]]--
 
-local chainmores = { }
 local chainprocs = { }
 
 local function logprocess(...)
@@ -1045,11 +1198,6 @@ function chainprocs.chainsub(head,start,stop,kind,chainname,currentcontext,looku
     return head, start, false
 end
 
-function chainmores.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n)
-    logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname))
-    return head, start, false
-end
-
 -- The reversesub is a special case, which is why we need to store the replacements
 -- in a bit weird way. There is no lookup and the replacement comes from the lookup
 -- itself. It is meant mostly for dealing with Urdu.
@@ -1116,8 +1264,7 @@ as less as needed but that would also make the code even more messy.</p>
 -- end
 
 --[[ldx--
-<p>Here we replace start by a single variant, First we delete the rest of the
-match.</p>
+<p>Here we replace start by a single variant.</p>
 --ldx]]--
 
 function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex)
@@ -1125,7 +1272,7 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo
     local current = start
     local subtables = currentlookup.subtables
     if #subtables > 1 then
-        logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," "))
+        logwarning("todo: check if we need to loop over the replacements: % t",subtables)
     end
     while current do
         if getid(current) == glyph_code then
@@ -1160,11 +1307,8 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo
     return head, start, false
 end
 
-chainmores.gsub_single = chainprocs.gsub_single
-
 --[[ldx--
-<p>Here we replace start by a sequence of new glyphs. First we delete the rest of
-the match.</p>
+<p>Here we replace start by a sequence of new glyphs.</p>
 --ldx]]--
 
 function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
@@ -1193,8 +1337,6 @@ function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,
     return head, start, false
 end
 
-chainmores.gsub_multiple = chainprocs.gsub_multiple
-
 --[[ldx--
 <p>Here we replace start by new glyph. First we delete the rest of the match.</p>
 --ldx]]--
@@ -1249,8 +1391,6 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext
     return head, start, false
 end
 
-chainmores.gsub_alternate = chainprocs.gsub_alternate
-
 --[[ldx--
 <p>When we replace ligatures we use a helper that handles the marks. I might change
 this function (move code inline and handle the marks by a separate function). We
@@ -1276,13 +1416,19 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
             local s = getnext(start)
             local discfound = false
             local last = stop
-            local nofreplacements = 0
+            local nofreplacements = 1
             local skipmark = currentlookup.flags[1]
             while s do
                 local id = getid(s)
                 if id == disc_code then
-                    s = getnext(s)
-                    discfound = true
+                    if not discfound then
+                        discfound = s
+                    end
+                    if s == stop then
+                        break -- okay? or before the disc
+                    else
+                        s = getnext(s)
+                    end
                 else
                     local schar = getchar(s)
                     if skipmark and marks[schar] then -- marks
@@ -1315,7 +1461,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
                     end
                 end
                 head, start = toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound)
-                return head, start, true, nofreplacements
+                return head, start, true, nofreplacements, discfound
             elseif trace_bugs then
                 if start == stop then
                     logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))
@@ -1325,11 +1471,97 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
             end
         end
     end
-    return head, start, false, 0
+    return head, start, false, 0, false
+end
+
+function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
+    -- untested .. needs checking for the new model
+    local startchar = getchar(start)
+    local subtables = currentlookup.subtables
+    local lookupname = subtables[1]
+    local kerns = lookuphash[lookupname]
+    if kerns then
+        kerns = kerns[startchar] -- needed ?
+        if kerns then
+            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)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h)
+            end
+        end
+    end
+    return head, start, false
+end
+
+function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
+    local snext = getnext(start)
+    if snext then
+        local startchar = getchar(start)
+        local subtables = currentlookup.subtables
+        local lookupname = subtables[1]
+        local kerns = lookuphash[lookupname]
+        if kerns then
+            kerns = kerns[startchar]
+            if kerns then
+                local lookuptype = lookuptypes[lookupname]
+                local prev, done = start, false
+                local factor = tfmdata.parameters.factor
+                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 = getnext(snext)
+                    else
+                        if not krn then
+                            -- skip
+                        elseif type(krn) == "table" then
+                            if lookuptype == "pair" then
+                                local a, b = krn[2], krn[3]
+                                if a and #a > 0 then
+                                    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 = 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)
+                                    end
+                                end
+                            else
+                                report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname))
+                             -- local a, b = krn[2], krn[6]
+                             -- 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(getchar(prev)),gref(nextchar))
+                             --     end
+                             -- end
+                             -- if b and b ~= 0 then
+                             --     logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor)
+                             -- end
+                            end
+                            done = true
+                        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(getchar(prev)),gref(nextchar))
+                            end
+                            done = true
+                        end
+                        break
+                    end
+                end
+                return head, start, done
+            end
+        end
+    end
+    return head, start, false
 end
 
-chainmores.gsub_ligature = chainprocs.gsub_ligature
-
 function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
     local markchar = getchar(start)
     if marks[markchar] then
@@ -1497,7 +1729,7 @@ function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext
                             if al[anchor] then
                                 local ma = markanchors[anchor]
                                 if ma then
-                                    local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar])
+                                    local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar],true)
                                     if trace_marks then
                                         logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
                                             cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
@@ -1588,133 +1820,346 @@ 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)
-    -- untested .. needs checking for the new model
-    local startchar = getchar(start)
-    local subtables = currentlookup.subtables
-    local lookupname = subtables[1]
-    local kerns = lookuphash[lookupname]
-    if kerns then
-        kerns = kerns[startchar] -- needed ?
-        if kerns then
-            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)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h)
+-- what pointer to return, spec says stop
+-- to be discussed ... is bidi changer a space?
+-- elseif char == zwnj and sequence[n][32] then -- brrr
+
+-- somehow l or f is global
+-- we don't need to pass the currentcontext, saves a bit
+-- make a slow variant then can be activated but with more tracing
+
+local function show_skip(kind,chainname,char,ck,class)
+    if ck[9] then
+        logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10])
+    else
+        logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2])
+    end
+end
+
+-- A previous version had disc collapsing code in the (single sub) handler plus some
+-- checking in the main loop, but that left the pre/post sequences undone. The best
+-- solution is to add some checking there and backtrack when a replace/post matches
+-- but it takes a bit of work to figure out an efficient way (this is what the sweep*
+-- names refer to). I might look into that variant one day again as it can replace
+-- some other code too. In that approach we can have a special version for gub and pos
+-- which gains some speed. This method does the test and passes info to the handlers
+-- (sweepnode, sweepmode, sweepprev, sweepnext, etc). Here collapsing is handled in the
+-- main loop which also makes code elsewhere simpler (i.e. no need for the other special
+-- runners and disc code in ligature building). I also experimented with pushing preceding
+-- glyphs sequences in the replace/pre fields beforehand which saves checking afterwards
+-- but at the cost of duplicate glyphs (memory) but it's too much overhead (runtime).
+--
+-- In the meantime Kai had moved the code from the single chain into a more general handler
+-- and this one (renamed to chaindisk) is used now. I optimized the code a bit and brought
+-- it in sycn with the other code. Hopefully I didn't introduce errors. Note: this somewhat
+-- complex approach is meant for fonts that implement (for instance) ligatures by character
+-- replacement which to some extend is not that suitable for hyphenation. I also use some
+-- helpers. This method passes some states but reparses the list. There is room for a bit of
+-- speed up but that will be done in the context version. (In fact a partial rewrite of all
+-- code can bring some more efficientry.)
+--
+-- I didn't test it with extremes but successive disc nodes still can give issues but in
+-- 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 function chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,chainindex,sequence,chainproc)
+
+    if not start then
+        return head, start, false
+    end
+
+    local startishead   = start == head
+    local seq           = ck[3]
+    local f             = ck[4]
+    local l             = ck[5]
+    local s             = #seq
+    local done          = false
+    local sweepnode     = sweepnode
+    local sweeptype     = sweeptype
+    local sweepoverflow = false
+    local checkdisc     = getprev(head) -- hm bad name head
+    local keepdisc      = not sweepnode
+    local lookaheaddisc = nil
+    local backtrackdisc = nil
+    local current       = start
+    local last          = start
+    local prev          = getprev(start)
+
+    -- fishy: so we can overflow and then go on in the sweep?
+
+    local i = f
+    while i <= l do
+        local id = getid(current)
+        if id == glyph_code then
+            i       = i + 1
+            last    = current
+            current = getnext(current)
+        elseif id == disc_code then
+            if keepdisc then
+                keepdisc = false
+                if notmatchpre[current] ~= notmatchreplace[current] then
+                    lookaheaddisc = current
+                end
+                local replace = getfield(current,"replace")
+                while replace and i <= l do
+                    if getid(replace) == glyph_code then
+                        i = i + 1
+                    end
+                    replace = getnext(replace)
+                end
+                last    = current
+                current = getnext(c)
+            else
+                head, current = flattendisk(head,current)
+            end
+        else
+            last    = current
+            current = getnext(current)
+        end
+        if current then
+            -- go on
+        elseif sweepoverflow then
+            -- we already are folling up on sweepnode
+            break
+        elseif sweeptype == "post" or sweeptype == "replace" then
+            current = getnext(sweepnode)
+            if current then
+                sweeptype     = nil
+                sweepoverflow = true
+            else
+                break
             end
         end
     end
-    return head, start, false
-end
 
-chainmores.gpos_single = chainprocs.gpos_single -- okay?
+    if sweepoverflow then
+        local prev = current and getprev(current)
+        if not current or prev ~= sweepnode then
+            local head = getnext(sweepnode)
+            local tail = nil
+            if prev then
+                tail = prev
+                setfield(current,"prev",sweepnode)
+            else
+                tail = find_node_tail(head)
+            end
+            setfield(sweepnode,"next",current)
+            setfield(head,"prev",nil)
+            setfield(tail,"next",nil)
+            appenddisc(sweepnode,head)
+        end
+    end
 
--- when machines become faster i will make a shared function
+    if l < s then
+        local i = l
+        local t = sweeptype == "post" or sweeptype == "replace"
+        while current and i < s do
+            local id = getid(current)
+            if id == glyph_code then
+                i       = i + 1
+                current = getnext(current)
+            elseif id == disc_code then
+                if keepdisc then
+                    keepdisc = false
+                    if notmatchpre[current] ~= notmatchreplace[current] then
+                        lookaheaddisc = current
+                    end
+                    local replace = getfield(c,"replace")
+                    while replace and i < s do
+                        if getid(replace) == glyph_code then
+                            i = i + 1
+                        end
+                        replace = getnext(replace)
+                    end
+                    current = getnext(current)
+                elseif notmatchpre[current] ~= notmatchreplace[current] then
+                    head, current = flattendisk(head,current)
+                else
+                    current = getnext(current) -- HH
+                end
+            else
+                current = getnext(current)
+            end
+            if not current and t then
+                current = getnext(sweepnode)
+                if current then
+                    sweeptype = nil
+                end
+            end
+        end
+    end
 
-function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
-    local snext = getnext(start)
-    if snext then
-        local startchar = getchar(start)
-        local subtables = currentlookup.subtables
-        local lookupname = subtables[1]
-        local kerns = lookuphash[lookupname]
-        if kerns then
-            kerns = kerns[startchar]
-            if kerns then
-                local lookuptype = lookuptypes[lookupname]
-                local prev, done = start, false
-                local factor = tfmdata.parameters.factor
-                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 = getnext(snext)
-                    else
-                        if not krn then
-                            -- skip
-                        elseif type(krn) == "table" then
-                            if lookuptype == "pair" then
-                                local a, b = krn[2], krn[3]
-                                if a and #a > 0 then
-                                    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 = 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)
-                                    end
-                                end
-                            else
-                                report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname))
-                                local a, b = krn[2], krn[6]
-                                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(getchar(prev)),gref(nextchar))
-                                    end
-                                end
-                                if b and b ~= 0 then
-                                    logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor)
-                                end
-                            end
-                            done = true
-                        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(getchar(prev)),gref(nextchar))
-                            end
-                            done = true
+    if f > 1 then
+        local current = prev
+        local i       = f
+        local t       = sweeptype == "pre" or sweeptype == "replace"
+        if not current and t and current == checkdisk then
+            current = getprev(sweepnode)
+        end
+        while current and i > 1 do -- missing getprev added / moved outside
+            local id = getid(current)
+            if id == glyph_code then
+                i = i - 1
+            elseif id == disc_code then
+                if keepdisc then
+                    keepdisc = false
+                    if notmatchpost[current] ~= notmatchreplace[current] then
+                        backtrackdisc = current
+                    end
+                    local replace = getfield(current,"replace")
+                    while replace and i > 1 do
+                        if getid(replace) == glyph_code then
+                            i = i - 1
                         end
-                        break
+                        replace = getnext(replace)
                     end
+                elseif notmatchpost[current] ~= notmatchreplace[current] then
+                    head, current = flattendisk(head,current)
                 end
-                return head, start, done
+            end
+            current = getprev(current)
+            if t and current == checkdisk then
+                current = getprev(sweepnode)
             end
         end
     end
-    return head, start, false
-end
 
-chainmores.gpos_pair = chainprocs.gpos_pair -- okay?
+    local ok = false
+    if lookaheaddisc then
 
--- what pointer to return, spec says stop
--- to be discussed ... is bidi changer a space?
--- elseif char == zwnj and sequence[n][32] then -- brrr
+        local cf            = start
+        local cl            = getprev(lookaheaddisc)
+        local cprev         = getprev(start)
+        local insertedmarks = 0
 
--- somehow l or f is global
--- we don't need to pass the currentcontext, saves a bit
--- make a slow variant then can be activated but with more tracing
+        while cprev and getid(cf) == glyph_code and getfont(cf) == currentfont and getsubtype(cf) < 256 and marks[getchar(cf)] do
+            insertedmarks = insertedmarks + 1
+            cf            = cprev
+            startishead   = cf == head
+            cprev         = getprev(cprev)
+        end
+
+        setfield(lookaheaddisc,"prev",cprev)
+        if cprev then
+            setfield(cprev,"next",lookaheaddisc)
+        end
+        setfield(cf,"prev",nil)
+        setfield(cl,"next",nil)
+        if startishead then
+            head = lookaheaddisc
+        end
+
+        local replace = getfield(lookaheaddisc,"replace")
+        local pre     = getfield(lookaheaddisc,"pre")
+        local new     = copy_node_list(cf)
+        local cnew = new
+        for i=1,insertedmarks do
+            cnew = getnext(cnew)
+        end
+        local clast = cnew
+        for i=f,l do
+            clast = getnext(clast)
+        end
+        if not notmatchpre[lookaheaddisc] then
+            cf, start, ok = chainproc(cf,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+        end
+        if not notmatchreplace[lookaheaddisc] then
+            new, cnew, ok = chainproc(new,cnew,clast,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+        end
+        if pre then
+            setfield(cl,"next",pre)
+            setfield(pre,"prev",cl)
+        end
+        if replace then
+            local tail = find_node_tail(new)
+            setfield(tail,"next",replace)
+            setfield(replace,"prev",tail)
+        end
+        setfield(lookaheaddisc,"pre",cf)      -- also updates tail
+        setfield(lookaheaddisc,"replace",new) -- also updates tail
+
+        start          = getprev(lookaheaddisc)
+        sweephead[cf]  = getnext(clast)
+        sweephead[new] = getnext(last)
+
+    elseif backtrackdisc then
+
+        local cf            = getnext(backtrackdisc)
+        local cl            = start
+        local cnext         = getnext(start)
+        local insertedmarks = 0
+
+        while cnext and getid(cnext) == glyph_code and getfont(cnext) == currentfont and getsubtype(cnext) < 256 and marks[getchar(cnext)] do
+            insertedmarks = insertedmarks + 1
+            cl            = cnext
+            cnext         = getnext(cnext)
+        end
+        if cnext then
+            setfield(cnext,"prev",backtrackdisc)
+        end
+        setfield(backtrackdisc,"next",cnext)
+        setfield(cf,"prev",nil)
+        setfield(cl,"next",nil)
+        local replace = getfield(backtrackdisc,"replace")
+        local post    = getfield(backtrackdisc,"post")
+        local new     = copy_node_list(cf)
+        local cnew    = find_node_tail(new)
+        for i=1,insertedmarks do
+            cnew = getprev(cnew)
+        end
+        local clast = cnew
+        for i=f,l do
+            clast = getnext(clast)
+        end
+        if not notmatchpost[backtrackdisc] then
+            cf, start, ok = chainproc(cf,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+        end
+        if not notmatchreplace[backtrackdisc] then
+            new, cnew, ok = chainproc(new,cnew,clast,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+        end
+        if post then
+            local tail = find_node_tail(post)
+            setfield(tail,"next",cf)
+            setfield(cf,"prev",tail)
+        else
+            post = cf
+        end
+        if replace then
+            local tail = find_node_tail(replace)
+            setfield(tail,"next",new)
+            setfield(new,"prev",tail)
+        else
+            replace = new
+        end
+        setfield(backtrackdisc,"post",post)       -- also updates tail
+        setfield(backtrackdisc,"replace",replace) -- also updates tail
+        start              = getprev(backtrackdisc)
+        sweephead[post]    = getnext(clast)
+        sweephead[replace] = getnext(last)
 
-local function show_skip(kind,chainname,char,ck,class)
-    if ck[9] then
-        logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10])
     else
-        logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2])
-    end
-end
 
-local quit_on_no_replacement = true
+        head, start, ok = chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
 
-directives.register("otf.chain.quitonnoreplacement",function(value) -- maybe per font
-    quit_on_no_replacement = value
-end)
+    end
+
+    return head, start, ok
+end
 
 local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash)
-    --  local rule, lookuptype, sequence, f, l, lookups = ck[1], ck[2] ,ck[3], ck[4], ck[5], ck[6]
+    local sweepnode    = sweepnode
+    local sweeptype    = sweeptype
+    local diskseen     = false
+    local checkdisc    = getprev(head)
     local flags        = sequence.flags
     local done         = false
     local skipmark     = flags[1]
     local skipligature = flags[2]
     local skipbase     = flags[3]
-    local someskip     = skipmark or skipligature or skipbase -- could be stored in flags for a fast test (hm, flags could be false !)
-    local markclass    = sequence.markclass                   -- todo, first we need a proper test
+    local markclass    = sequence.markclass
     local skipped      = false
-    for k=1,#contexts do
+
+    for k=1,#contexts do -- i've only seen ccmp having > 1 (e.g. dejavu)
         local match   = true
         local current = start
         local last    = start
@@ -1728,7 +2173,8 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
         else
             -- maybe we need a better space check (maybe check for glue or category or combination)
             -- we cannot optimize for n=2 because there can be disc nodes
-            local f, l = ck[4], ck[5]
+            local f = ck[4]
+            local l = ck[5]
             -- current match
             if f == 1 and f == l then -- current only
                 -- already a hit
@@ -1738,9 +2184,14 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
                 if f == l then -- new, else last out of sync (f is > 1)
                  -- match = true
                 else
+                    local discfound = nil
                     local n = f + 1
                     last = getnext(last)
                     while n <= l do
+                        if not last and (sweeptype == "post" or sweeptype == "replace") then
+                            last      = getnext(sweepnode)
+                            sweeptype = nil
+                        end
                         if last then
                             local id = getid(last)
                             if id == glyph_code then
@@ -1748,7 +2199,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
                                     local char = getchar(last)
                                     local ccd = descriptions[char]
                                     if ccd then
-                                        local class = ccd.class
+                                        local class = ccd.class or "base"
                                         if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then
                                             skipped = true
                                             if trace_skips then
@@ -1761,18 +2212,77 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
                                             end
                                             n = n + 1
                                         else
-                                            match = false
+                                            if discfound then
+                                                notmatchreplace[discfound] = true
+                                                match = not notmatchpre[discfound]
+                                            else
+                                                match = false
+                                            end
                                             break
                                         end
                                     else
-                                        match = false
+                                        if discfound then
+                                            notmatchreplace[discfound] = true
+                                            match = not notmatchpre[discfound]
+                                        else
+                                            match = false
+                                        end
                                         break
                                     end
                                 else
-                                    match = false
+                                    if discfound then
+                                        notmatchreplace[discfound] = true
+                                        match = not notmatchpre[discfound]
+                                    else
+                                        match = false
+                                    end
                                     break
                                 end
                             elseif id == disc_code then
+                                diskseen              = true
+                                discfound             = last
+                                notmatchpre[last]     = nil
+                                notmatchpost[last]    = true
+                                notmatchreplace[last] = nil
+                                local pre     = getfield(last,"pre")
+                                local replace = getfield(last,"replace")
+                                if pre then
+                                    local n = n
+                                    while pre do
+                                        if seq[n][getchar(pre)] then
+                                            n = n + 1
+                                            pre = getnext(pre)
+                                            if n > l then
+                                                break
+                                            end
+                                        else
+                                            notmatchpre[last] = true
+                                            break
+                                        end
+                                    end
+                                    if n <= l then
+                                        notmatchpre[last] = true
+                                    end
+                                else
+                                    notmatchpre[last] = true
+                                end
+                                if replace then
+                                    -- so far we never entered this branch
+                                    while replace do
+                                        if seq[n][getchar(replace)] then
+                                            n = n + 1
+                                            replace = getnext(replace)
+                                            if n > l then
+                                                break
+                                            end
+                                        else
+                                            notmatchreplace[last] = true
+                                            match = not notmatchpre[last]
+                                            break
+                                        end
+                                    end
+                                    match = not notmatchpre[last]
+                                end
                                 last = getnext(last)
                             else
                                 match = false
@@ -1789,50 +2299,137 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
             if match and f > 1 then
                 local prev = getprev(start)
                 if prev then
-                    local n = f-1
-                    while n >= 1 do
-                        if prev then
-                            local id = getid(prev)
-                            if id == glyph_code then
-                                if getfont(prev) == currentfont and getsubtype(prev)<256 then -- normal char
-                                    local char = getchar(prev)
-                                    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
-                                            skipped = true
-                                            if trace_skips then
-                                                show_skip(kind,chainname,char,ck,class)
+                    if prev == checkdisc and (sweeptype == "pre" or sweeptype == "replace") then
+                        prev      = getprev(sweepnode)
+                     -- sweeptype = nil
+                    end
+                    if prev then
+                        local discfound = nil
+                        local n = f - 1
+                        while n >= 1 do
+                            if prev then
+                                local id = getid(prev)
+                                if id == glyph_code then
+                                    if getfont(prev) == currentfont and getsubtype(prev)<256 then -- normal char
+                                        local char = getchar(prev)
+                                        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
+                                                skipped = true
+                                                if trace_skips then
+                                                    show_skip(kind,chainname,char,ck,class)
+                                                end
+                                            elseif seq[n][char] then
+                                                n = n -1
+                                            else
+                                                if discfound then
+                                                    notmatchreplace[discfound] = true
+                                                    match = not notmatchpost[discfound]
+                                                else
+                                                    match = false
+                                                end
+                                                break
                                             end
-                                        elseif seq[n][char] then
-                                            n = n -1
                                         else
-                                            match = false
+                                            if discfound then
+                                                notmatchreplace[discfound] = true
+                                                match = not notmatchpost[discfound]
+                                            else
+                                                match = false
+                                            end
                                             break
                                         end
                                     else
-                                        match = false
+                                        if discfound then
+                                            notmatchreplace[discfound] = true
+                                            match = not notmatchpost[discfound]
+                                        else
+                                            match = false
+                                        end
                                         break
                                     end
+                                elseif id == disc_code then
+                                    -- the special case: f i where i becomes dottless i ..
+                                    diskseen              = true
+                                    discfound             = prev
+                                    notmatchpre[prev]     = true
+                                    notmatchpost[prev]    = nil
+                                    notmatchreplace[prev] = nil
+                                    local pre     = getfield(prev,"pre")
+                                    local post    = getfield(prev,"post")
+                                    local replace = getfield(prev,"replace")
+                                    if pre ~= start and post ~= start and replace ~= start then
+                                        if post then
+                                            local n = n
+                                            local posttail = find_node_tail(post)
+                                            while posttail do
+                                                if seq[n][getchar(posttail)] then
+                                                    n = n - 1
+                                                    if posttail == post then
+                                                        break
+                                                    else
+                                                        posttail = getprev(posttail)
+                                                        if n < 1 then
+                                                            break
+                                                        end
+                                                    end
+                                                else
+                                                    notmatchpost[prev] = true
+                                                    break
+                                                end
+                                            end
+                                            if n >= 1 then
+                                                notmatchpost[prev] = true
+                                            end
+                                        else
+                                            notmatchpost[prev] = true
+                                        end
+                                        if replace then
+                                            -- we seldom enter this branch (e.g. on brill efficient)
+                                            local replacetail = find_node_tail(replace)
+                                            while replacetail do
+                                                if seq[n][getchar(replacetail)] then
+                                                    n = n - 1
+                                                    if replacetail == replace then
+                                                        break
+                                                    else
+                                                        replacetail = getprev(replacetail)
+                                                        if n < 1 then
+                                                            break
+                                                        end
+                                                    end
+                                                else
+                                                    notmatchreplace[prev] = true
+                                                    match = not notmatchpost[prev]
+                                                    break
+                                                end
+                                            end
+                                            if not match then
+                                                break
+                                            end
+                                        else
+                                            -- skip 'm
+                                        end
+                                    else
+                                        -- skip 'm
+                                    end
+                                elseif seq[n][32] then
+                                    n = n -1
                                 else
                                     match = false
                                     break
                                 end
-                            elseif id == disc_code then
-                                -- skip 'm
-                            elseif seq[n][32] then
-                                n = n -1
+                                prev = getprev(prev)
+                            elseif seq[n][32] then -- somewhat special, as zapfino can have many preceding spaces
+                                n = n - 1
                             else
                                 match = false
                                 break
                             end
-                            prev = getprev(prev)
-                        elseif seq[n][32] then -- somewhat special, as zapfino can have many preceding spaces
-                            n = n -1
-                        else
-                            match = false
-                            break
                         end
+                    else
+                        match = false
                     end
                 else
                     match = false
@@ -1841,7 +2438,14 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
             -- after
             if match and s > l then
                 local current = last and getnext(last)
+                if not current then
+                    if sweeptype == "post" or sweeptype == "replace" then
+                        current   = getnext(sweepnode)
+                     -- sweeptype = nil
+                    end
+                end
                 if current then
+                    local discfound = nil
                     -- removed optimization for s-l == 1, we have to deal with marks anyway
                     local n = l + 1
                     while n <= s do
@@ -1861,19 +2465,81 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
                                         elseif seq[n][char] then
                                             n = n + 1
                                         else
-                                            match = false
+                                            if discfound then
+                                                notmatchreplace[discfound] = true
+                                                match = not notmatchpre[discfound]
+                                            else
+                                                match = false
+                                            end
                                             break
                                         end
+                                    else
+                                        if discfound then
+                                            notmatchreplace[discfound] = true
+                                            match = not notmatchpre[discfound]
+                                        else
+                                            match = false
+                                        end
+                                        break
+                                    end
+                                else
+                                    if discfound then
+                                        notmatchreplace[discfound] = true
+                                        match = not notmatchpre[discfound]
                                     else
                                         match = false
+                                    end
+                                    break
+                                end
+                            elseif id == disc_code then
+                                diskseen                 = true
+                                discfound                = current
+                                notmatchpre[current]     = nil
+                                notmatchpost[current]    = true
+                                notmatchreplace[current] = nil
+                                local pre     = getfield(current,"pre")
+                                local replace = getfield(current,"replace")
+                                if pre then
+                                    local n = n
+                                    while pre do
+                                        if seq[n][getchar(pre)] then
+                                            n = n + 1
+                                            pre = getnext(pre)
+                                            if n > s then
+                                                break
+                                            end
+                                        else
+                                            notmatchpre[current] = true
+                                            break
+                                        end
+                                    end
+                                    if n <= s then
+                                        notmatchpre[current] = true
+                                    end
+                                else
+                                    notmatchpre[current] = true
+                                end
+                                if replace then
+                                    -- so far we never entered this branch
+                                    while replace do
+                                        if seq[n][getchar(replace)] then
+                                            n = n + 1
+                                            replace = getnext(replace)
+                                            if n > s then
+                                                break
+                                            end
+                                        else
+                                            notmatchreplace[current] = true
+                                            match = notmatchpre[current]
+                                            break
+                                        end
+                                    end
+                                    if not match then
                                         break
                                     end
                                 else
-                                    match = false
-                                    break
+                                    -- skip 'm
                                 end
-                            elseif id == disc_code then
-                                -- skip 'm
                             elseif seq[n][32] then -- brrr
                                 n = n + 1
                             else
@@ -1894,7 +2560,8 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
             end
         end
         if match then
-            -- ck == currentcontext
+            -- can lookups be of a different type ?
+            local diskchain = diskseen or sweepnode
             if trace_contexts then
                 local rule, lookuptype, f, l = ck[1], ck[2], ck[4], ck[5]
                 local char = getchar(start)
@@ -1914,10 +2581,14 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
                     local chainlookupname = chainlookups[1]
                     local chainlookup = lookuptable[chainlookupname]
                     if chainlookup then
-                        local cp = chainprocs[chainlookup.type]
-                        if cp then
+                        local chainproc = chainprocs[chainlookup.type]
+                        if chainproc then
                             local ok
-                            head, start, ok = cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+                            if diskchain then
+                                head, start, ok = chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence,chainproc)
+                            else
+                                head, start, ok = chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+                            end
                             if ok then
                                 done = true
                             end
@@ -1929,13 +2600,13 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
                     end
                  else
                     local i = 1
-                    while true do
+                    while start and true do
                         if skipped then
-                            while true do
+                            while true do -- todo: use properties
                                 local char = getchar(start)
                                 local ccd = descriptions[char]
                                 if ccd then
-                                    local class = ccd.class
+                                    local class = ccd.class or "base"
                                     if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then
                                         start = getnext(start)
                                     else
@@ -1946,36 +2617,51 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
                                 end
                             end
                         end
+                        -- see remark in ms standard under : LookupType 5: Contextual Substitution Subtable
                         local chainlookupname = chainlookups[i]
                         local chainlookup = lookuptable[chainlookupname]
                         if not chainlookup then
-                            -- okay, n matches, < n replacements
+                            -- we just advance
                             i = i + 1
                         else
-                            local cp = chainmores[chainlookup.type]
-                            if not cp then
+                            local chainproc = chainprocs[chainlookup.type]
+                            if not chainproc then
                                 -- actually an error
                                 logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type)
                                 i = i + 1
                             else
                                 local ok, n
-                                head, start, ok, n = cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence)
+                                if diskchain then
+                                    head, start, ok    = chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence,chainproc)
+                                else
+                                    head, start, ok, n = chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence)
+                                end
                                 -- messy since last can be changed !
                                 if ok then
                                     done = true
-                                    -- skip next one(s) if ligature
-                                    i = i + (n or 1)
-                                else
-                                    i = i + 1
+                                    if n and n > 1 then
+                                        -- we have a ligature (cf the spec we advance one but we really need to test it
+                                        -- as there are fonts out there that are fuzzy and have too many lookups:
+                                        --
+                                        -- U+1105 U+119E U+1105 U+119E : sourcehansansklight: script=hang ccmp=yes
+                                        --
+                                        if i + n > nofchainlookups then
+                                         -- if trace_contexts then
+                                         --     logprocess("%s: quitting lookups",cref(kind,chainname))
+                                         -- end
+                                            break
+                                        else
+                                            -- we need to carry one
+                                        end
+                                    end
                                 end
+                                i = i + 1
                             end
                         end
-                        if i > nofchainlookups then
+                        if i > nofchainlookups or not start then
                             break
                         elseif start then
                             start = getnext(start)
-                        else
-                            -- weird
                         end
                     end
                 end
@@ -1990,8 +2676,16 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
                     end
                 end
             end
+            if done then
+                break -- out of contexts (new, needs checking)
+            end
         end
     end
+    if diskseen then -- maybe move up so that we can turn checking on/off
+        notmatchpre     = { }
+        notmatchpost    = { }
+        notmatchreplace = { }
+    end
     return head, start, done
 end
 
@@ -2076,13 +2770,13 @@ local function initialize(sequence,script,language,enabled)
         local order = sequence.order
         if order then
             for i=1,#order do --
-                local kind = order[i] --
+                local kind  = order[i] --
                 local valid = enabled[kind]
                 if valid then
                     local scripts = features[kind] --
                     local languages = scripts[script] or scripts[wildcard]
                     if languages and (languages[language] or languages[wildcard]) then
-                        return { valid, autofeatures[kind] or false, sequence.chain or 0, kind, sequence }
+                        return { valid, autofeatures[kind] or false, sequence, kind }
                     end
                 end
             end
@@ -2126,32 +2820,216 @@ function otf.dataset(tfmdata,font) -- generic variant, overloaded in context
     return rl
 end
 
--- elseif id == glue_code then
---     if p[5] then -- chain
---         local pc = pp[32]
---         if pc then
---             start, ok = start, false -- p[1](start,kind,p[2],pc,p[3],p[4])
---             if ok then
---                 done = true
---             end
---             if start then start = getnext(start) end
---         else
---             start = getnext(start)
---         end
---     else
---         start = getnext(start)
---     end
+-- assumptions:
+--
+-- * languages that use complex disc nodes
+
+local function kernrun(disc,run)
+    --
+    -- we catch <font 1><disc font 2>
+    --
+    if trace_kernruns then
+        report_run("kern") -- will be more detailed
+    end
+    --
+    local prev      = getprev(disc) -- todo, keep these in the main loop
+    local next      = getnext(disc) -- todo, keep these in the main loop
+    --
+    local pre       = getfield(disc,"pre")
+    local post      = getfield(disc,"post")
+    local replace   = getfield(disc,"replace")
+    --
+    local prevmarks = prev
+    --
+    -- can be optional, because why on earth do we get a disc after a mark (okay, maybe when a ccmp
+    -- has happened but then it should be in the disc so basically this test indicates an error)
+    --
+    while prevmarks and getid(prevmarks) == glyph_code and marks[getchar(prevmarks)] and getfont(prevmarks) == currentfont and getsubtype(prevmarks) < 256 do
+        prevmarks = getprev(prevmarks)
+    end
+    --
+    if prev and (pre or replace) and not (getid(prev) == glyph_code and getfont(prev) == currentfont and getsubtype(prev)<256) then
+        prev = false
+    end
+    if next and (post or replace) and not (getid(next) == glyph_code and getfont(next) == currentfont and getsubtype(next)<256) then
+        next = false
+    end
+    --
+    if not pre then
+        -- go on
+    elseif prev then
+        local nest = getprev(pre)
+        setfield(pre,"prev",prev)
+        setfield(prev,"next",pre)
+        run(prevmarks,"preinjections")
+        setfield(pre,"prev",nest)
+        setfield(prev,"next",disc)
+    else
+        run(pre,"preinjections")
+    end
+    --
+    if not post then
+        -- go on
+    elseif next then
+        local tail = find_node_tail(post)
+        setfield(tail,"next",next)
+        setfield(next,"prev",tail)
+        run(post,"postinjections",next)
+        setfield(tail,"next",nil)
+        setfield(next,"prev",disc)
+    else
+        run(post,"postinjections")
+    end
+    --
+    if not replace and prev and next then
+        -- this should be already done by discfound
+        setfield(prev,"next",next)
+        setfield(next,"prev",prev)
+        run(prevmarks,"injections",next)
+        setfield(prev,"next",disc)
+        setfield(next,"prev",disc)
+    elseif prev and next then
+        local tail = find_node_tail(replace)
+        local nest = getprev(replace)
+        setfield(replace,"prev",prev)
+        setfield(prev,"next",replace)
+        setfield(tail,"next",next)
+        setfield(next,"prev",tail)
+        run(prevmarks,"replaceinjections",next)
+        setfield(replace,"prev",nest)
+        setfield(prev,"next",disc)
+        setfield(tail,"next",nil)
+        setfield(next,"prev",disc)
+    elseif prev then
+        local nest = getprev(replace)
+        setfield(replace,"prev",prev)
+        setfield(prev,"next",replace)
+        run(prevmarks,"replaceinjections")
+        setfield(replace,"prev",nest)
+        setfield(prev,"next",disc)
+    elseif next then
+        local tail = find_node_tail(replace)
+        setfield(tail,"next",next)
+        setfield(next,"prev",tail)
+        run(replace,"replaceinjections",next)
+        setfield(tail,"next",nil)
+        setfield(next,"prev",disc)
+    else
+        run(replace,"replaceinjections")
+    end
+end
 
--- there will be a new direction parser (pre-parsed etc)
+-- the if new test might be dangerous as luatex will check / set some tail stuff
+-- in a temp node
 
--- less bytecode: 290 -> 254
---
--- attr = attr or false
---
--- local a = getattr(start,0)
--- if (a == attr and (not attribute or getprop(start,a_state) == attribute)) or (not attribute or getprop(start,a_state) == attribute) then
---     -- the action
--- end
+local function comprun(disc,run)
+    if trace_compruns then
+        report_run("comp: %s",languages.serializediscretionary(disc))
+    end
+    --
+    local pre = getfield(disc,"pre")
+    if pre then
+        sweepnode = disc
+        sweeptype = "pre" -- in alternative code preinjections is used (also used then for proeprties, saves a variable)
+        local new, done = run(pre)
+        if done then
+            setfield(disc,"pre",new)
+        end
+    end
+    --
+    local post = getfield(disc,"post")
+    if post then
+        sweepnode = disc
+        sweeptype = "post"
+        local new, done = run(post)
+        if done then
+            setfield(disc,"post",new)
+        end
+    end
+    --
+    local replace = getfield(disc,"replace")
+    if replace then
+        sweepnode = disc
+        sweeptype = "replace"
+        local new, done = run(replace)
+        if done then
+            setfield(disc,"replace",new)
+        end
+    end
+    sweepnode = nil
+    sweeptype = nil
+end
+
+local function testrun(disc,trun,crun) -- use helper
+    local next = getnext(disc)
+    if next then
+        local replace = getfield(disc,"replace")
+        if replace then
+            local prev = getprev(disc)
+            if prev then
+                -- only look ahead
+                local tail = find_node_tail(replace)
+             -- local nest = getprev(replace)
+                setfield(tail,"next",next)
+                setfield(next,"prev",tail)
+                if trun(replace,next) then
+                    setfield(disc,"replace",nil) -- beware, side effects of nest so first
+                    setfield(prev,"next",replace)
+                    setfield(replace,"prev",prev)
+                    setfield(next,"prev",tail)
+                    setfield(tail,"next",next)
+                    setfield(disc,"prev",nil)
+                    setfield(disc,"next",nil)
+                    flush_node_list(disc)
+                    return replace -- restart
+                else
+                    setfield(tail,"next",nil)
+                    setfield(next,"prev",disc)
+                end
+            else
+                -- weird case
+            end
+        else
+            -- no need
+        end
+    else
+        -- weird case
+    end
+    comprun(disc,crun)
+    return next
+end
+
+local function discrun(disc,drun,krun)
+    local next = getnext(disc)
+    local prev = getprev(disc)
+    if trace_discruns then
+        report_run("disc") -- will be more detailed
+    end
+    if next and prev then
+        setfield(prev,"next",next)
+     -- setfield(next,"prev",prev)
+        drun(prev)
+        setfield(prev,"next",disc)
+     -- setfield(next,"prev",disc)
+    end
+    --
+    local pre = getfield(disc,"pre")
+    if not pre then
+        -- go on
+    elseif prev then
+        local nest = getprev(pre)
+        setfield(pre,"prev",prev)
+        setfield(prev,"next",pre)
+        krun(prev,"preinjections")
+        setfield(pre,"prev",nest)
+        setfield(prev,"next",disc)
+    else
+        krun(pre,"preinjections")
+    end
+    return next
+end
+
+-- todo: maybe run lr and rl stretches
 
 local function featuresprocessor(head,font,attr)
 
@@ -2180,6 +3058,7 @@ local function featuresprocessor(head,font,attr)
 
     currentfont     = font
     rlmode          = 0
+    sweephead       = { }
 
     local sequences = resources.sequences
     local done      = false
@@ -2195,23 +3074,27 @@ local function featuresprocessor(head,font,attr)
     -- Keeping track of the headnode is needed for devanagari (I generalized it a bit
     -- so that multiple cases are also covered.)
 
-    -- todo: retain prev
+    -- We don't goto the next node of a disc node is created so that we can then treat
+    -- the pre, post and replace. It's abit of a hack but works out ok for most cases.
+
+    -- there can be less subtype and attr checking in the comprun etc helpers
 
     for s=1,#datasets do
-        local dataset = datasets[s]
-        featurevalue = dataset[1] -- todo: pass to function instead of using a global
-
-        local sequence  = dataset[5] -- sequences[s] -- also dataset[5]
-        local rlparmode = 0
-        local topstack  = 0
-        local success   = false
-        local attribute = dataset[2]
-        local chain     = dataset[3] -- sequence.chain or 0
-        local typ       = sequence.type
-        local subtables = sequence.subtables
-        if chain < 0 then
+        local dataset      = datasets[s]
+              featurevalue = dataset[1] -- todo: pass to function instead of using a global
+        local attribute    = dataset[2]
+        local sequence     = dataset[3] -- sequences[s] -- also dataset[5]
+        local kind         = dataset[4]
+        ----- chain        = dataset[5] -- sequence.chain or 0
+        local rlparmode    = 0
+        local topstack     = 0
+        local success      = false
+        local typ          = sequence.type
+        local gpossing     = typ == "gpos_single" or typ == "gpos_pair" -- maybe all of them
+        local subtables    = sequence.subtables
+        local handler      = handlers[typ]
+        if typ == "gsub_reversecontextchain" then -- chain < 0
             -- this is a limited case, no special treatments like 'init' etc
-            local handler = handlers[typ]
             -- we need to get rid of this slide! probably no longer needed in latest luatex
             local start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo
             while start do
@@ -2225,13 +3108,15 @@ local function featuresprocessor(head,font,attr)
                             a = true
                         end
                         if a then
+                            local char = getchar(start)
                             for i=1,#subtables do
                                 local lookupname = subtables[i]
                                 local lookupcache = lookuphash[lookupname]
                                 if lookupcache then
-                                    local lookupmatch = lookupcache[getchar(start)]
+                                    local lookupmatch = lookupcache[char]
                                     if lookupmatch then
-                                        head, start, success = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
+                                        -- todo: disc?
+                                        head, start, success = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)
                                         if success then
                                             break
                                         end
@@ -2252,24 +3137,30 @@ local function featuresprocessor(head,font,attr)
                 end
             end
         else
-            local handler = handlers[typ]
             local ns = #subtables
             local start = head -- local ?
             rlmode = 0 -- to be checked ?
             if ns == 1 then -- happens often
-                local lookupname = subtables[1]
+                local lookupname  = subtables[1]
                 local lookupcache = lookuphash[lookupname]
                 if not lookupcache then -- also check for empty cache
                     report_missing_cache(typ,lookupname)
                 else
 
-                    local function subrun(start)
-                        -- mostly for gsub, gpos would demand a more clever approach
-                        local head = start
-                        local done = false
+                    local function c_run(head) -- no need to check for 256 and attr probably also the same
+                        local done  = false
+                        local start = sweephead[head]
+                        if start then
+                            sweephead[head] = nil
+                        else
+                            start = head
+                        end
                         while start do
                             local id = getid(start)
-                            if id == glyph_code and getfont(start) == font and getsubtype(start) < 256 then
+                            if id ~= glyph_code then
+                                -- very unlikely
+                                start = getnext(start)
+                            elseif getfont(start) == font and getsubtype(start) < 256 then
                                 local a = getattr(start,0)
                                 if a then
                                     a = (a == attr) and (not attribute or getprop(start,a_state) == attribute)
@@ -2281,7 +3172,7 @@ local function featuresprocessor(head,font,attr)
                                     if lookupmatch then
                                         -- sequence kan weg
                                         local ok
-                                        head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
+                                        head, start, ok = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,1)
                                         if ok then
                                             done = true
                                         end
@@ -2291,48 +3182,106 @@ local function featuresprocessor(head,font,attr)
                                     start = getnext(start)
                                 end
                             else
-                                start = getnext(start)
+                                return head, false
                             end
                         end
                         if done then
-                            success = true
-                            return head
+                            success = true -- needed in this subrun?
                         end
+                        return head, done
                     end
 
-                    local function kerndisc(disc) -- we can assume that prev and next are glyphs
-                        local prev = getprev(disc)
-                        local next = getnext(disc)
-                        if prev and next then
-                            setfield(prev,"next",next)
-                         -- setfield(next,"prev",prev)
-                            local a = getattr(prev,0)
-                            if a then
-                                a = (a == attr) and (not attribute or getprop(prev,a_state) == attribute)
+                    local function t_run(start,stop)
+                        while start ~= stop do
+                            local id = getid(start)
+                            if id == glyph_code and getfont(start) == font and getsubtype(start) < 256 then
+                                local a = getattr(start,0)
+                                if a then
+                                    a = (a == attr) and (not attribute or getprop(start,a_state) == attribute)
+                                else
+                                    a = not attribute or getprop(start,a_state) == attribute
+                                end
+                                if a then
+                                    local lookupmatch = lookupcache[getchar(start)]
+                                    if lookupmatch then -- hm, hyphens can match (tlig) so we need to really check
+                                        -- if we need more than ligatures we can outline the code and use functions
+                                        local s = getnext(start)
+                                        local l = nil
+                                        while s do
+                                            local lg = lookupmatch[getchar(s)]
+                                            if lg then
+                                                l = lg
+                                                s = getnext(s)
+                                            else
+                                                break
+                                            end
+                                        end
+                                        if l and l.ligature then
+                                            return true
+                                        end
+                                    end
+                                end
+                                start = getnext(start)
                             else
-                                a = not attribute or getprop(prev,a_state) == attribute
+                                break
                             end
-                            if a then
-                                local lookupmatch = lookupcache[getchar(prev)]
-                                if lookupmatch then
-                                    -- sequence kan weg
-                                    local h, d, ok = handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
-                                    if ok then
-                                        done = true
-                                        success = true
+                        end
+                    end
+
+                    local function d_run(prev) -- we can assume that prev and next are glyphs
+                        local a = getattr(prev,0)
+                        if a then
+                            a = (a == attr) and (not attribute or getprop(prev,a_state) == attribute)
+                        else
+                            a = not attribute or getprop(prev,a_state) == attribute
+                        end
+                        if a then
+                            local lookupmatch = lookupcache[getchar(prev)]
+                            if lookupmatch then
+                                -- sequence kan weg
+                                local h, d, ok = handler(head,prev,kind,lookupname,lookupmatch,sequence,lookuphash,1)
+                                if ok then
+                                    done    = true
+                                    success = true
+                                end
+                            end
+                        end
+                    end
+
+                    local function k_run(sub,injection,last)
+                        local a = getattr(sub,0)
+                        if a then
+                            a = (a == attr) and (not attribute or getprop(sub,a_state) == attribute)
+                        else
+                            a = not attribute or getprop(sub,a_state) == attribute
+                        end
+                        if a then
+                            -- sequence kan weg
+                            for n in traverse_nodes(sub) do -- only gpos
+                                if n == last then
+                                    break
+                                end
+                                local id = getid(n)
+                                if id == glyph_code then
+                                    local lookupmatch = lookupcache[getchar(n)]
+                                    if lookupmatch then
+                                        local h, d, ok = handler(sub,n,kind,lookupname,lookupmatch,sequence,lookuphash,1,injection)
+                                        if ok then
+                                            done    = true
+                                            success = true
+                                        end
                                     end
+                                else
+                                    -- message
                                 end
                             end
-                            setfield(prev,"next",disc)
-                         -- setfield(next,"prev",disc)
                         end
-                        return next
                     end
 
                     while start do
                         local id = getid(start)
                         if id == glyph_code then
-                            if getfont(start) == font and getsubtype(start) < 256 then
+                            if getfont(start) == font and getsubtype(start) < 256 then -- why a 256 test ...
                                 local a = getattr(start,0)
                                 if a then
                                     a = (a == attr) and (not attribute or getprop(start,a_state) == attribute)
@@ -2340,59 +3289,52 @@ local function featuresprocessor(head,font,attr)
                                     a = not attribute or getprop(start,a_state) == attribute
                                 end
                                 if a then
-                                    local lookupmatch = lookupcache[getchar(start)]
+                                    local char        = getchar(start)
+                                    local lookupmatch = lookupcache[char]
                                     if lookupmatch then
                                         -- sequence kan weg
                                         local ok
-                                        head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
+                                        head, start, ok = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,1)
                                         if ok then
                                             success = true
+                                        elseif gpossing and zwnjruns and char == zwnj then
+                                            discrun(start,d_run)
                                         end
+                                    elseif gpossing and zwnjruns and char == zwnj then
+                                        discrun(start,d_run)
                                     end
                                     if start then start = getnext(start) end
                                 else
-                                    start = getnext(start)
+                                   start = getnext(start)
                                 end
                             else
                                 start = getnext(start)
                             end
                         elseif id == disc_code then
-                            -- mostly for gsub
-                            if getsubtype(start) == discretionary_code then
-                                local pre = getfield(start,"pre")
-                                if pre then
-                                    local new = subrun(pre)
-                                    if new then setfield(start,"pre",new) end
-                                end
-                                local post = getfield(start,"post")
-                                if post then
-                                    local new = subrun(post)
-                                    if new then setfield(start,"post",new) end
-                                end
-                                local replace = getfield(start,"replace")
-                                if replace then
-                                    local new = subrun(replace)
-                                    if new then setfield(start,"replace",new) end
-                                end
-elseif typ == "gpos_single" or typ == "gpos_pair" then
-    kerndisc(start)
+                            if gpossing then
+                                kernrun(start,k_run)
+                                start = getnext(start)
+                            elseif typ == "gsub_ligature" then
+                                start = testrun(start,t_run,c_run)
+                            else
+                                comprun(start,c_run)
+                                start = getnext(start)
                             end
-                            start = getnext(start)
                         elseif id == whatsit_code then -- will be function
                             local subtype = getsubtype(start)
                             if subtype == dir_code then
                                 local dir = getfield(start,"dir")
-                                if     dir == "+TRT" or dir == "+TLT" then
+                                if dir == "+TLT" then
                                     topstack = topstack + 1
                                     dirstack[topstack] = dir
-                                elseif dir == "-TRT" or dir == "-TLT" then
-                                    topstack = topstack - 1
-                                end
-                                local newdir = dirstack[topstack]
-                                if newdir == "+TRT" then
-                                    rlmode = -1
-                                elseif newdir == "+TLT" then
                                     rlmode = 1
+                                elseif dir == "+TRT" then
+                                    topstack = topstack + 1
+                                    dirstack[topstack] = dir
+                                    rlmode = -1
+                                elseif dir == "-TLT" or dir == "-TRT" then
+                                    topstack = topstack - 1
+                                    rlmode = dirstack[topstack] == "+TRT" and -1 or 1
                                 else
                                     rlmode = rlparmode
                                 end
@@ -2422,15 +3364,23 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then
                         end
                     end
                 end
+
             else
 
-                local function subrun(start)
-                    -- mostly for gsub, gpos would demand a more clever approach
-                    local head = start
-                    local done = false
+                local function c_run(head)
+                    local done  = false
+                    local start = sweephead[head]
+                    if start then
+                        sweephead[head] = nil
+                    else
+                        start = head
+                    end
                     while start do
                         local id = getid(start)
-                        if id == glyph_code and getfont(start) == font and getsubtype(start) < 256 then
+                        if id ~= glyph_code then
+                            -- very unlikely
+                            start = getnext(start)
+                        elseif getfont(start) == font and getsubtype(start) < 256 then
                             local a = getattr(start,0)
                             if a then
                                 a = (a == attr) and (not attribute or getprop(start,a_state) == attribute)
@@ -2438,15 +3388,16 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then
                                 a = not attribute or getprop(start,a_state) == attribute
                             end
                             if a then
+                                local char = getchar(start)
                                 for i=1,ns do
                                     local lookupname = subtables[i]
                                     local lookupcache = lookuphash[lookupname]
                                     if lookupcache then
-                                        local lookupmatch = lookupcache[getchar(start)]
+                                        local lookupmatch = lookupcache[char]
                                         if lookupmatch then
                                             -- we could move all code inline but that makes things even more unreadable
                                             local ok
-                                            head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
+                                            head, start, ok = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)
                                             if ok then
                                                 done = true
                                                 break
@@ -2464,50 +3415,127 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then
                                 start = getnext(start)
                             end
                         else
-                            start = getnext(start)
+                            return head, false
                         end
                     end
                     if done then
                         success = true
-                        return head
                     end
+                    return head, done
                 end
 
-                local function kerndisc(disc) -- we can assume that prev and next are glyphs
-                    local prev = getprev(disc)
-                    local next = getnext(disc)
-                    if prev and next then
-                        setfield(prev,"next",next)
-                     -- setfield(next,"prev",prev)
-                        local a = getattr(prev,0)
-                        if a then
-                            a = (a == attr) and (not attribute or getprop(prev,a_state) == attribute)
-                        else
-                            a = not attribute or getprop(prev,a_state) == attribute
+                local function d_run(prev)
+                    local a = getattr(prev,0)
+                    if a then
+                        a = (a == attr) and (not attribute or getprop(prev,a_state) == attribute)
+                    else
+                        a = not attribute or getprop(prev,a_state) == attribute
+                    end
+                    if a then
+                        -- brr prev can be disc
+                        local char = getchar(prev)
+                        for i=1,ns do
+                            local lookupname  = subtables[i]
+                            local lookupcache = lookuphash[lookupname]
+                            if lookupcache then
+                                local lookupmatch = lookupcache[char]
+                                if lookupmatch then
+                                    -- we could move all code inline but that makes things even more unreadable
+                                    local h, d, ok = handler(head,prev,kind,lookupname,lookupmatch,sequence,lookuphash,i)
+                                    if ok then
+                                        done = true
+                                        break
+                                    end
+                                end
+                            else
+                                report_missing_cache(typ,lookupname)
+                            end
                         end
-                        if a then
-                            for i=1,ns do
-                                local lookupname = subtables[i]
-                                local lookupcache = lookuphash[lookupname]
-                                if lookupcache then
-                                    local lookupmatch = lookupcache[getchar(prev)]
-                                    if lookupmatch then
-                                        -- we could move all code inline but that makes things even more unreadable
-                                        local h, d, ok = handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
-                                        if ok then
-                                            done = true
-                                            break
+                    end
+                end
+
+               local function k_run(sub,injection,last)
+                    local a = getattr(sub,0)
+                    if a then
+                        a = (a == attr) and (not attribute or getprop(sub,a_state) == attribute)
+                    else
+                        a = not attribute or getprop(sub,a_state) == attribute
+                    end
+                    if a then
+                        for n in traverse_nodes(sub) do -- only gpos
+                            if n == last then
+                                break
+                            end
+                            local id = getid(n)
+                            if id == glyph_code then
+                                local char = getchar(n)
+                                for i=1,ns do
+                                    local lookupname  = subtables[i]
+                                    local lookupcache = lookuphash[lookupname]
+                                    if lookupcache then
+                                        local lookupmatch = lookupcache[char]
+                                        if lookupmatch then
+                                            local h, d, ok = handler(head,n,kind,lookupname,lookupmatch,sequence,lookuphash,i,injection)
+                                            if ok then
+                                                done = true
+                                                break
+                                            end
                                         end
+                                    else
+                                        report_missing_cache(typ,lookupname)
+                                    end
+                                end
+                            else
+                                -- message
+                            end
+                        end
+                    end
+                end
+
+                local function t_run(start,stop)
+                    while start ~= stop do
+                        local id = getid(start)
+                        if id == glyph_code and getfont(start) == font and getsubtype(start) < 256 then
+                            local a = getattr(start,0)
+                            if a then
+                                a = (a == attr) and (not attribute or getprop(start,a_state) == attribute)
+                            else
+                                a = not attribute or getprop(start,a_state) == attribute
+                            end
+                            if a then
+                                local char = getchar(start)
+                                for i=1,ns do
+                                    local lookupname  = subtables[i]
+                                    local lookupcache = lookuphash[lookupname]
+                                    if lookupcache then
+                                        local lookupmatch = lookupcache[char]
+                                        if lookupmatch then
+                                            -- if we need more than ligatures we can outline the code and use functions
+                                            local s = getnext(start)
+                                            local l = nil
+                                            while s do
+                                                local lg = lookupmatch[getchar(s)]
+                                                if lg then
+                                                    l = lg
+                                                    s = getnext(s)
+                                                else
+                                                    break
+                                                end
+                                            end
+                                            if l and l.ligature then
+                                                return true
+                                            end
+                                        end
+                                    else
+                                        report_missing_cache(typ,lookupname)
                                     end
-                                else
-                                    report_missing_cache(typ,lookupname)
                                 end
                             end
+                            start = getnext(start)
+                        else
+                            break
                         end
-                        setfield(prev,"next",disc)
-                     -- setfield(next,"prev",disc)
                     end
-                    return next
                 end
 
                 while start do
@@ -2522,21 +3550,26 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then
                             end
                             if a then
                                 for i=1,ns do
-                                    local lookupname = subtables[i]
+                                    local lookupname  = subtables[i]
                                     local lookupcache = lookuphash[lookupname]
                                     if lookupcache then
-                                        local lookupmatch = lookupcache[getchar(start)]
+                                        local char = getchar(start)
+                                        local lookupmatch = lookupcache[char]
                                         if lookupmatch then
                                             -- we could move all code inline but that makes things even more unreadable
                                             local ok
-                                            head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
+                                            head, start, ok = handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)
                                             if ok then
                                                 success = true
                                                 break
                                             elseif not start then
                                                 -- don't ask why ... shouldn't happen
                                                 break
+                                            elseif gpossing and zwnjruns and char == zwnj then
+                                                discrun(start,d_run)
                                             end
+                                        elseif gpossing and zwnjruns and char == zwnj then
+                                            discrun(start,d_run)
                                         end
                                     else
                                         report_missing_cache(typ,lookupname)
@@ -2550,42 +3583,30 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then
                             start = getnext(start)
                         end
                     elseif id == disc_code then
-                        -- mostly for gsub
-                        if getsubtype(start) == discretionary_code then
-                            local pre = getfield(start,"pre")
-                            if pre then
-                                local new = subrun(pre)
-                                if new then setfield(start,"pre",new) end
-                            end
-                            local post = getfield(start,"post")
-                            if post then
-                                local new = subrun(post)
-                                if new then setfield(start,"post",new) end
-                            end
-                            local replace = getfield(start,"replace")
-                            if replace then
-                                local new = subrun(replace)
-                                if new then setfield(start,"replace",new) end
-                            end
-elseif typ == "gpos_single" or typ == "gpos_pair" then
-    kerndisc(start)
+                        if gpossing then
+                            kernrun(start,k_run)
+                            start = getnext(start)
+                        elseif typ == "gsub_ligature" then
+                            start = testrun(start,t_run,c_run)
+                        else
+                            comprun(start,c_run)
+                            start = getnext(start)
                         end
-                        start = getnext(start)
                     elseif id == whatsit_code then
                         local subtype = getsubtype(start)
                         if subtype == dir_code then
                             local dir = getfield(start,"dir")
-                            if     dir == "+TRT" or dir == "+TLT" then
+                            if dir == "+TLT" then
                                 topstack = topstack + 1
                                 dirstack[topstack] = dir
-                            elseif dir == "-TRT" or dir == "-TLT" then
-                                topstack = topstack - 1
-                            end
-                            local newdir = dirstack[topstack]
-                            if newdir == "+TRT" then
-                                rlmode = -1
-                            elseif newdir == "+TLT" then
                                 rlmode = 1
+                            elseif dir == "+TRT" then
+                                topstack = topstack + 1
+                                dirstack[topstack] = dir
+                                rlmode = -1
+                            elseif dir == "-TLT" or dir == "-TRT" then
+                                topstack = topstack - 1
+                                rlmode = dirstack[topstack] == "+TRT" and -1 or 1
                             else
                                 rlmode = rlparmode
                             end
@@ -2629,6 +3650,8 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then
     return head, done
 end
 
+-- this might move to the loader
+
 local function generic(lookupdata,lookupname,unicode,lookuphash)
     local target = lookuphash[lookupname]
     if target then
@@ -2638,47 +3661,48 @@ local function generic(lookupdata,lookupname,unicode,lookuphash)
     end
 end
 
-local action = {
+local function ligature(lookupdata,lookupname,unicode,lookuphash)
+    local target = lookuphash[lookupname]
+    if not target then
+        target = { }
+        lookuphash[lookupname] = target
+    end
+    for i=1,#lookupdata do
+        local li = lookupdata[i]
+        local tu = target[li]
+        if not tu then
+            tu = { }
+            target[li] = tu
+        end
+        target = tu
+    end
+    target.ligature = unicode
+end
+
+local function pair(lookupdata,lookupname,unicode,lookuphash)
+    local target = lookuphash[lookupname]
+    if not target then
+        target = { }
+        lookuphash[lookupname] = target
+    end
+    local others = target[unicode]
+    local paired = lookupdata[1]
+    if others then
+        others[paired] = lookupdata
+    else
+        others = { [paired] = lookupdata }
+        target[unicode] = others
+    end
+end
 
+local action = {
     substitution = generic,
     multiple     = generic,
     alternate    = generic,
     position     = generic,
-
-    ligature = function(lookupdata,lookupname,unicode,lookuphash)
-        local target = lookuphash[lookupname]
-        if not target then
-            target = { }
-            lookuphash[lookupname] = target
-        end
-        for i=1,#lookupdata do
-            local li = lookupdata[i]
-            local tu = target[li]
-            if not tu then
-                tu = { }
-                target[li] = tu
-            end
-            target = tu
-        end
-        target.ligature = unicode
-    end,
-
-    pair = function(lookupdata,lookupname,unicode,lookuphash)
-        local target = lookuphash[lookupname]
-        if not target then
-            target = { }
-            lookuphash[lookupname] = target
-        end
-        local others = target[unicode]
-        local paired = lookupdata[1]
-        if others then
-            others[paired] = lookupdata
-        else
-            others = { [paired] = lookupdata }
-            target[unicode] = others
-        end
-    end,
-
+    ligature     = ligature,
+    pair         = pair,
+    kern         = pair,
 }
 
 local function prepare_lookups(tfmdata)
@@ -2691,12 +3715,17 @@ local function prepare_lookups(tfmdata)
     local lookuptypes      = resources.lookuptypes
     local characters       = tfmdata.characters
     local descriptions     = tfmdata.descriptions
+    local duplicates       = resources.duplicates
 
     -- we cannot free the entries in the descriptions as sometimes we access
     -- then directly (for instance anchors) ... selectively freeing does save
     -- much memory as it's only a reference to a table and the slot in the
     -- description hash is not freed anyway
 
+    -- we can delay this using metatables so that we don't make the hashes for
+    -- features we don't use but then we need to loop over the characters
+    -- many times so we gain nothing
+
     for unicode, character in next, characters do -- we cannot loop over descriptions !
 
         local description = descriptions[unicode]
@@ -2706,7 +3735,7 @@ local function prepare_lookups(tfmdata)
             local lookups = description.slookups
             if lookups then
                 for lookupname, lookupdata in next, lookups do
-                    action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash)
+                    action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash,duplicates)
                 end
             end
 
@@ -2716,7 +3745,7 @@ local function prepare_lookups(tfmdata)
                     local lookuptype = lookuptypes[lookupname]
                     for l=1,#lookuplist do
                         local lookupdata = lookuplist[l]
-                        action[lookuptype](lookupdata,lookupname,unicode,lookuphash)
+                        action[lookuptype](lookupdata,lookupname,unicode,lookuphash,duplicates)
                     end
                 end
             end
@@ -2740,7 +3769,7 @@ local function prepare_lookups(tfmdata)
                         for name, anchor in next, anchors do
                             local lookups = anchor_to_lookup[name]
                             if lookups then
-                                for lookup, _ in next, lookups do
+                                for lookup in next, lookups do
                                     local target = lookuphash[lookup]
                                     if target then
                                         target[unicode] = anchors
@@ -2760,6 +3789,8 @@ local function prepare_lookups(tfmdata)
 
 end
 
+-- so far
+
 local function split(replacement,original)
     local result = { }
     for i=1,#replacement do
@@ -2835,7 +3866,7 @@ local function prepare_contextchains(tfmdata)
                                 -- use sequence[start] instead but it's somewhat ugly.
                                 nt = nt + 1
                                 t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements }
-                                for unic, _  in next, sequence[start] do
+                                for unic in next, sequence[start] do
                                     local cu = contexts[unic]
                                     if not cu then
                                         contexts[unic] = t
diff --git a/src/fontloader/misc/fontloader-fonts.lua b/src/fontloader/misc/fontloader-fonts.lua
index c81e8cd..f18ba35 100644
--- a/src/fontloader/misc/fontloader-fonts.lua
+++ b/src/fontloader/misc/fontloader-fonts.lua
@@ -27,16 +27,10 @@ if not modules then modules = { } end modules ['luatex-fonts'] = {
 -- also add more helper code here, but that depends to what extend metatex (sidetrack of context)
 -- evolves into a low level layer (depends on time, as usual).
 
-texio.write_nl("")
-texio.write_nl("--------------------------------------------------------------------------------")
-texio.write_nl("The font code has been brought in sync with the context version of 2014.12.21 so")
-texio.write_nl("if things don't work out as expected the interfacing needs to be checked. When")
-texio.write_nl("this works as expected a second upgrade will happen that gives a more complete")
-texio.write_nl("support and another sync with the context code (that new code is currently being")
-texio.write_nl("tested. The base pass is now integrated in the main pass. The results can differ")
-texio.write_nl("from those in context because there we integrate some mechanisms differently.")
-texio.write_nl("--------------------------------------------------------------------------------")
-texio.write_nl("")
+-- The code here is the same as in context version 2015.09.11 but the rendering in context can be
+-- different from generic. This can be a side effect of additional callbacks, additional features
+-- and interferences between mechanisms between macro packages. We use the rendering in context
+-- and luatex-plain as reference for issues.
 
 utf = utf or unicode.utf8
 
@@ -221,9 +215,9 @@ if non_generic_context.luatex_fonts.skip_loading ~= true then
         loadmodule('font-oti.lua')
         loadmodule('font-otf.lua')
         loadmodule('font-otb.lua')
-        loadmodule('luatex-fonts-inj.lua')
+        loadmodule('luatex-fonts-inj.lua') -- normally the same as font-inj.lua
         loadmodule('luatex-fonts-ota.lua')
-        loadmodule('luatex-fonts-otn.lua')
+        loadmodule('luatex-fonts-otn.lua') -- normally the same as font-otn.lua
         loadmodule('font-otp.lua')
         loadmodule('luatex-fonts-lua.lua')
         loadmodule('font-def.lua')         -- this code (stripped) might end up in luatex-fonts-def.lua
diff --git a/src/fontloader/misc/fontloader-l-lpeg.lua b/src/fontloader/misc/fontloader-l-lpeg.lua
index 55a0d89..5be1246 100644
--- a/src/fontloader/misc/fontloader-l-lpeg.lua
+++ b/src/fontloader/misc/fontloader-l-lpeg.lua
@@ -82,7 +82,7 @@ local lpegtype, lpegmatch, lpegprint = lpeg.type, lpeg.match, lpeg.print
 -- let's start with an inspector:
 
 if setinspector then
-    setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end)
+    setinspector("lpeg",function(v) if lpegtype(v) then lpegprint(v) return true end end)
 end
 
 -- Beware, we predefine a bunch of patterns here and one reason for doing so
diff --git a/src/fontloader/misc/fontloader-l-lua.lua b/src/fontloader/misc/fontloader-l-lua.lua
index 1a2a987..cb61829 100644
--- a/src/fontloader/misc/fontloader-l-lua.lua
+++ b/src/fontloader/misc/fontloader-l-lua.lua
@@ -129,22 +129,36 @@ local print, select, tostring = print, select, tostring
 
 local inspectors = { }
 
-function setinspector(inspector) -- global function
-    inspectors[#inspectors+1] = inspector
+function setinspector(kind,inspector) -- global function
+    inspectors[kind] = inspector
 end
 
 function inspect(...) -- global function
     for s=1,select("#",...) do
         local value = select(s,...)
-        local done = false
-        for i=1,#inspectors do
-            done = inspectors[i](value)
-            if done then
-                break
+        if value == nil then
+            print("nil")
+        else
+            local done  = false
+            -- type driven (table)
+            local kind      = type(value)
+            local inspector = inspectors[kind]
+            if inspector then
+                done = inspector(value)
+                if done then
+                    break
+                end
+            end
+            -- whatever driven (token, node, ...)
+            for kind, inspector in next, inspectors do
+                done = inspector(value)
+                if done then
+                    break
+                end
+            end
+            if not done then
+                print(tostring(value))
             end
-        end
-        if not done then
-            print(tostring(value))
         end
     end
 end
diff --git a/src/fontloader/misc/fontloader-l-string.lua b/src/fontloader/misc/fontloader-l-string.lua
index 70c66f6..e9dc2bb 100644
--- a/src/fontloader/misc/fontloader-l-string.lua
+++ b/src/fontloader/misc/fontloader-l-string.lua
@@ -192,10 +192,11 @@ string.itself  = function(s) return s end
 
 -- also handy (see utf variant)
 
-local pattern = Ct(C(1)^0) -- string and not utf !
+local pattern_c = Ct( C(1)      ^0) -- string and not utf !
+local pattern_b = Ct((C(1)/byte)^0)
 
-function string.totable(str)
-    return lpegmatch(pattern,str)
+function string.totable(str,bytes)
+    return lpegmatch(bytes and pattern_b or pattern_c,str)
 end
 
 -- handy from within tex:
diff --git a/src/fontloader/misc/fontloader-l-table.lua b/src/fontloader/misc/fontloader-l-table.lua
index b02f210..552097e 100644
--- a/src/fontloader/misc/fontloader-l-table.lua
+++ b/src/fontloader/misc/fontloader-l-table.lua
@@ -1144,7 +1144,7 @@ function table.print(t,...)
 end
 
 if setinspector then
-    setinspector(function(v) if type(v) == "table" then serialize(print,v,"table") return true end end)
+    setinspector("table",function(v) if type(v) == "table" then serialize(print,v,"table") return true end end)
 end
 
 -- -- -- obsolete but we keep them for a while and might comment them later -- -- --
diff --git a/src/fontloader/misc/fontloader-mplib.lua b/src/fontloader/misc/fontloader-mplib.lua
index c6628ac..fd6eb97 100644
--- a/src/fontloader/misc/fontloader-mplib.lua
+++ b/src/fontloader/misc/fontloader-mplib.lua
@@ -22,7 +22,9 @@ if metapost and metapost.version then
 
 else
 
-    local format, concat, abs, match = string.format, table.concat, math.abs, string.match
+    local format, match, gsub = string.format, string.match, string.gsub
+    local concat = table.concat
+    local abs = math.abs
 
     local mplib = require ('mplib')
     local kpse  = require ('kpse')
@@ -144,10 +146,101 @@ else
         metapost.make = metapost.make or function()
         end
 
+        local template = [[
+            \pdfoutput=1
+            \pdfpkresolution600
+            \pdfcompresslevel=9
+            %s\relax
+            \hsize=100in
+            \vsize=\hsize
+            \hoffset=-1in
+            \voffset=\hoffset
+            \topskip=0pt
+            \setbox0=\hbox{%s}\relax
+            \pageheight=\ht0
+            \pagewidth=\wd0
+            \box0
+            \bye
+        ]]
+
+        metapost.texrunner = "mtxrun --script plain"
+
+        local texruns = 0   -- per document
+        local texhash = { } -- per document
+
+        function metapost.maketext(mpd,str,what)
+            -- inefficient but one can always use metafun .. it's more a test
+            -- feature
+            local verbatimtex = mpd.verbatimtex
+            if not verbatimtex then
+                verbatimtex = { }
+                mpd.verbatimtex = verbatimtex
+            end
+            if what == 1 then
+                table.insert(verbatimtex,str)
+            else
+                local texcode = format(template,concat(verbatimtex,"\n"),str)
+                local texdone = texhash[texcode]
+                local jobname = tex.jobname
+                if not texdone then
+                    texruns = texruns + 1
+                    texdone = texruns
+                    texhash[texcode] = texdone
+                    local texname = format("%s-mplib-%s.tmp",jobname,texdone)
+                    local logname = format("%s-mplib-%s.log",jobname,texdone)
+                    local pdfname = format("%s-mplib-%s.pdf",jobname,texdone)
+                    io.savedata(texname,texcode)
+                    os.execute(format("%s %s",metapost.texrunner,texname))
+                    os.remove(texname)
+                    os.remove(logname)
+                end
+                return format('"image::%s-mplib-%s.pdf" infont defaultfont',jobname,texdone)
+            end
+        end
+
+        local function mpprint(buffer,...)
+            for i=1,select("#",...) do
+                local value = select(i,...)
+                if value ~= nil then
+                    local t = type(value)
+                    if t == "number" then
+                        buffer[#buffer+1] = format("%.16f",value)
+                    elseif t == "string" then
+                        buffer[#buffer+1] = value
+                    elseif t == "table" then
+                        buffer[#buffer+1] = "(" .. concat(value,",") .. ")"
+                    else -- boolean or whatever
+                        buffer[#buffer+1] = tostring(value)
+                    end
+                end
+            end
+        end
+
+        function metapost.runscript(mpd,code)
+            local code = loadstring(code)
+            if type(code) == "function" then
+                local buffer = { }
+                function metapost.print(...)
+                    mpprint(buffer,...)
+                end
+                code()
+             -- mpd.buffer = buffer -- for tracing
+                return concat(buffer,"")
+            end
+            return ""
+        end
+
         function metapost.load(name)
+            local mpd = {
+                buffer   = { },
+                verbatim = { }
+            }
             local mpx = mplib.new {
                 ini_version = true,
-                find_file = metapost.finder,
+                find_file   = metapost.finder,
+                make_text   = function(...) return metapost.maketext (mpd,...) end,
+                run_script  = function(...) return metapost.runscript(mpd,...) end,
+                extensions  = 1,
             }
             local result
             if not mpx then
@@ -217,8 +310,8 @@ else
         return figure:objects()
     end
 
-    function metapost.convert(result, flusher)
-        metapost.flush(result, flusher)
+    function metapost.convert(result,flusher)
+        metapost.flush(result,flusher)
         return true -- done
     end
 
@@ -239,8 +332,13 @@ else
     end
 
     function pdf_textfigure(font,size,text,width,height,depth)
-        text = text:gsub(".","\\hbox{%1}") -- kerning happens in metapost
-        tex.sprint(format("\\MPLIBtextext{%s}{%s}{%s}{%s}{%s}",font,size,text,0,-( 7200/ 7227)/65536*depth))
+        local how, what = match(text,"^(.-)::(.+)$")
+        if how == "image" then
+            tex.sprint(format("\\MPLIBpdftext{%s}{%s}",what,depth))
+        else
+            text = gsub(text,".","\\hbox{%1}") -- kerning happens in metapost
+            tex.sprint(format("\\MPLIBtextext{%s}{%s}{%s}{%s}",font,size,text,depth))
+        end
     end
 
     local bend_tolerance = 131/65536
@@ -375,8 +473,10 @@ else
                                     pdf_literalcode("Q")
                                 else
                                     local cs = object.color
+                                    local cr = false
                                     if cs and #cs > 0 then
-                                        pdf_literalcode(metapost.colorconverter(cs))
+                                        cs, cr = metapost.colorconverter(cs)
+                                        pdf_literalcode(cs)
                                     end
                                     local ml = object.miterlimit
                                     if ml and ml ~= miterlimit then
diff --git a/src/fontloader/misc/fontloader-mplib.tex b/src/fontloader/misc/fontloader-mplib.tex
index 09dd179..f9de4b2 100644
--- a/src/fontloader/misc/fontloader-mplib.tex
+++ b/src/fontloader/misc/fontloader-mplib.tex
@@ -106,15 +106,14 @@
 
 %D Text items have a special handler:
 
-\def\MPLIBtextext#1#2#3#4#5%
+\def\MPLIBtextext#1#2#3#4%
   {\begingroup
    \setbox\mplibscratchbox\hbox
      {\font\temp=#1 at #2bp%
       \temp
       #3}%
    \setbox\mplibscratchbox\hbox
-     {\hskip#4 bp%
-      \raise#5 bp%
+     {\raise#4sp%
       \box\mplibscratchbox}%
    \wd\mplibscratchbox0pt%
    \ht\mplibscratchbox0pt%
@@ -122,4 +121,20 @@
    \box\mplibscratchbox
    \endgroup}
 
+\def\MPLIBpdftext#1#2%
+  {\ifcsname mplib::#1\endcsname
+     % already done, forgotten outside convert group
+     \message{<reusing mplib: #1>}%
+   \else
+     \message{<embedding mplib: #1>}%
+     \immediate\pdfximage{#1}% we cannot remove the file as it is included last
+     \expandafter\edef\csname mplib::#1\endcsname{\the\pdflastximage}%
+   \fi
+   \setbox\mplibscratchbox\hbox
+     {\raise#2sp\hbox{\pdfrefximage\csname mplib::#1\endcsname}}%
+   \wd\mplibscratchbox0pt%
+   \ht\mplibscratchbox0pt%
+   \dp\mplibscratchbox0pt%
+   \box\mplibscratchbox}
+
 \endinput
diff --git a/src/fontloader/misc/fontloader-plain.tex b/src/fontloader/misc/fontloader-plain.tex
index c9a9e36..9902c49 100644
--- a/src/fontloader/misc/fontloader-plain.tex
+++ b/src/fontloader/misc/fontloader-plain.tex
@@ -11,7 +11,26 @@
 
 \directlua {tex.enableprimitives('', tex.extraprimitives())}
 
-\pdfoutput=1
+% We assume that pdf is used.
+
+\pdfoutput 1
+
+% We set the page dimensions because otherwise the backend does weird things
+% when we have for instance this on a line of its own:
+%
+%   \hbox to 100cm {\hss wide indeed\hss}
+%
+% The page dimension calculation is a fuzzy one as there are some compensations
+% for the \hoffset and \voffset and such. I remember long discussions and much
+% trial and error in figuring this out during pdftex development times. Where
+% a dvi driver will project on a papersize (and thereby clip) the pdf backend
+% has to deal with the lack of a page concept on tex by some guessing. Normally
+% a macro package will set the dimensions to something reasonable anyway.
+
+\pagewidth   8.5in
+\pageheight 11.0in
+
+% We load some code at runtime:
 
 \everyjob \expandafter {%
     \the\everyjob
@@ -20,9 +39,11 @@
     \input {luatex-math}%
     \input {luatex-languages}%
     \input {luatex-mplib}%
-  % \input {luatex-gadgets}%
+    \input {luatex-gadgets}%
 }
 
+% We also patch the version number:
+
 \edef\fmtversion{\fmtversion+luatex}
 
 \dump
diff --git a/src/fontloader/misc/fontloader-test.tex b/src/fontloader/misc/fontloader-test.tex
index 6f48e0c..f851aab 100644
--- a/src/fontloader/misc/fontloader-test.tex
+++ b/src/fontloader/misc/fontloader-test.tex
@@ -1,3 +1,5 @@
+% texformat=luatex-plain
+
 %D \module
 %D   [       file=luatex-test,
 %D        version=2009.12.01,
@@ -33,12 +35,12 @@
 
 \font\mathtest=cambria(math) {\mathtest 123}
 
-\font\gothic=msgothic(ms-gothic) {\gothic whatever}
+% \font\gothic=msgothic(ms-gothic) {\gothic whatever} % no longer in windows 10
 
 \bgroup
 
-    \pdfprotrudechars2
-    \pdfadjustspacing2
+    \ifdefined\pdfprotrudechars \pdfprotrudechars \else \protrudechars \fi 2 \relax
+    \ifdefined\pdfadjustspacing \pdfadjustspacing \else \adjustspacing \fi 2 \relax
 
     \font\testb=file:lmroman12-regular:+liga;extend=1.5         at 12pt \testb \input tufte \par
     \font\testb=file:lmroman12-regular:+liga;slant=0.8          at 12pt \testb \input tufte \par
@@ -48,12 +50,30 @@
 
 \setmplibformat{plain}
 
+\directlua {
+    function MpTest()
+        metapost.print("fullcircle scaled 3cm")
+    end
+}
+
 \mplibcode
     beginfig(1) ;
         draw fullcircle
             scaled 10cm
             withcolor red
             withpen pencircle xscaled 4mm yscaled 2mm rotated 30 ;
+        draw "test" infont defaultfont scaled 4 ;
+        verbatimtex \sl etex;
+        draw btex some more test etex scaled 2 ;
+        currentpicture := currentpicture shifted (0,1cm) ;
+        verbatimtex \bf etex;
+        draw btex another test etex scaled 2 ;
+        currentpicture := currentpicture shifted (0,1cm) ;
+        draw btex another test etex scaled 2 ;
+        draw
+            runscript("MpTest()")
+            withcolor green
+            withpen pencircle xscaled 2mm yscaled 1mm rotated 20 ;
     endfig ;
 \endmplibcode
 
diff --git a/src/fontloader/misc/fontloader-util-str.lua b/src/fontloader/misc/fontloader-util-str.lua
index c2139b1..95534c8 100644
--- a/src/fontloader/misc/fontloader-util-str.lua
+++ b/src/fontloader/misc/fontloader-util-str.lua
@@ -6,7 +6,7 @@ if not modules then modules = { } end modules ['util-str'] = {
     license   = "see context related readme files"
 }
 
-utilities         = utilities or {}
+utilities         = utilities or { }
 utilities.strings = utilities.strings or { }
 local strings     = utilities.strings
 
@@ -354,7 +354,16 @@ function string.autosingle(s,sep)
     return ("'" .. tostring(s) .. "'")
 end
 
-local tracedchars  = { }
+local tracedchars  = { [0] =
+    -- the regular bunch
+    "[null]", "[soh]", "[stx]", "[etx]", "[eot]", "[enq]", "[ack]", "[bel]",
+    "[bs]",   "[ht]",  "[lf]",  "[vt]",  "[ff]",  "[cr]",  "[so]",  "[si]",
+    "[dle]",  "[dc1]", "[dc2]", "[dc3]", "[dc4]", "[nak]", "[syn]", "[etb]",
+    "[can]",  "[em]",  "[sub]", "[esc]", "[fs]",  "[gs]",  "[rs]",  "[us]",
+    -- plus space
+    "[space]", -- 0x20
+}
+
 string.tracedchars = tracedchars
 strings.tracers    = tracedchars
 
diff --git a/src/fontloader/runtime/fontloader-reference.lua b/src/fontloader/runtime/fontloader-reference.lua
index d8095a2..a2a598b 100644
--- a/src/fontloader/runtime/fontloader-reference.lua
+++ b/src/fontloader/runtime/fontloader-reference.lua
@@ -1,6 +1,6 @@
 -- merged file : luatex-fonts-merged.lua
 -- parent file : luatex-fonts.lua
--- merge date  : 05/24/15 12:42:55
+-- merge date  : 10/09/15 21:28:28
 
 do -- begin closure to overcome local limits and interference
 
@@ -57,21 +57,33 @@ if not package.loaders then
 end
 local print,select,tostring=print,select,tostring
 local inspectors={}
-function setinspector(inspector) 
-  inspectors[#inspectors+1]=inspector
+function setinspector(kind,inspector) 
+  inspectors[kind]=inspector
 end
 function inspect(...) 
   for s=1,select("#",...) do
     local value=select(s,...)
-    local done=false
-    for i=1,#inspectors do
-      done=inspectors[i](value)
-      if done then
-        break
+    if value==nil then
+      print("nil")
+    else
+      local done=false
+      local kind=type(value)
+      local inspector=inspectors[kind]
+      if inspector then
+        done=inspector(value)
+        if done then
+          break
+        end
+      end
+      for kind,inspector in next,inspectors do
+        done=inspector(value)
+        if done then
+          break
+        end
+      end
+      if not done then
+        print(tostring(value))
       end
-    end
-    if not done then
-      print(tostring(value))
     end
   end
 end
@@ -112,7 +124,7 @@ local floor=math.floor
 local P,R,S,V,Ct,C,Cs,Cc,Cp,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.V,lpeg.Ct,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Cp,lpeg.Cmt
 local lpegtype,lpegmatch,lpegprint=lpeg.type,lpeg.match,lpeg.print
 if setinspector then
-  setinspector(function(v) if lpegtype(v) then lpegprint(v) return true end end)
+  setinspector("lpeg",function(v) if lpegtype(v) then lpegprint(v) return true end end)
 end
 lpeg.patterns=lpeg.patterns or {} 
 local patterns=lpeg.patterns
@@ -995,9 +1007,10 @@ function string.valid(str,default)
   return (type(str)=="string" and str~="" and str) or default or nil
 end
 string.itself=function(s) return s end
-local pattern=Ct(C(1)^0) 
-function string.totable(str)
-  return lpegmatch(pattern,str)
+local pattern_c=Ct(C(1)^0) 
+local pattern_b=Ct((C(1)/byte)^0)
+function string.totable(str,bytes)
+  return lpegmatch(bytes and pattern_b or pattern_c,str)
 end
 local replacer=lpeg.replacer("@","%%") 
 function string.tformat(fmt,...)
@@ -1884,7 +1897,7 @@ function table.print(t,...)
   end
 end
 if setinspector then
-  setinspector(function(v) if type(v)=="table" then serialize(print,v,"table") return true end end)
+  setinspector("table",function(v) if type(v)=="table" then serialize(print,v,"table") return true end end)
 end
 function table.sub(t,i,j)
   return { unpack(t,i,j) }
@@ -2937,7 +2950,13 @@ function string.autosingle(s,sep)
   end
   return ("'"..tostring(s).."'")
 end
-local tracedchars={}
+local tracedchars={ [0]=
+  "[null]","[soh]","[stx]","[etx]","[eot]","[enq]","[ack]","[bel]",
+  "[bs]","[ht]","[lf]","[vt]","[ff]","[cr]","[so]","[si]",
+  "[dle]","[dc1]","[dc2]","[dc3]","[dc4]","[nak]","[syn]","[etb]",
+  "[can]","[em]","[sub]","[esc]","[fs]","[gs]","[rs]","[us]",
+  "[space]",
+}
 string.tracedchars=tracedchars
 strings.tracers=tracedchars
 function string.tracedchar(b)
@@ -3886,6 +3905,8 @@ local nodecodes={} for k,v in next,node.types  () do nodecodes[string.gsub(v,"_"
 local whatcodes={} for k,v in next,node.whatsits() do whatcodes[string.gsub(v,"_","")]=k end
 local glyphcodes={ [0]="character","glyph","ligature","ghost","left","right" }
 local disccodes={ [0]="discretionary","explicit","automatic","regular","first","second" }
+for i=0,#glyphcodes do glyphcodes[glyphcodes[i]]=i end
+for i=0,#disccodes do disccodes [disccodes [i]]=i end
 nodes.nodecodes=nodecodes
 nodes.whatcodes=whatcodes
 nodes.whatsitcodes=whatcodes
@@ -4361,6 +4382,7 @@ function constructors.scale(tfmdata,specification)
   local hdelta=delta
   local vdelta=delta
   target.designsize=parameters.designsize 
+  target.units=units
   target.units_per_em=units
   local direction=properties.direction or tfmdata.direction or 0 
   target.direction=direction
@@ -4472,21 +4494,28 @@ function constructors.scale(tfmdata,specification)
     target.nomath=true
     target.mathparameters=nil 
   end
-  local italickey="italic"
-  local useitalics=true
   if hasmath then
-    autoitalicamount=false 
-  elseif properties.textitalics then
-    italickey="italic_correction"
-    useitalics=false
-    if properties.delaytextitalics then
+    local mathitalics=properties.mathitalics
+    if mathitalics==false then
+      if trace_defining then
+        report_defining("%s italics %s for font %a, fullname %a, filename %a","math",hasitalics and "ignored" or "disabled",name,fullname,filename)
+      end
+      hasitalics=false
+      autoitalicamount=false
+    end
+  else
+    local textitalics=properties.textitalics
+    if textitalics==false then
+      if trace_defining then
+        report_defining("%s italics %s for font %a, fullname %a, filename %a","text",hasitalics and "ignored" or "disabled",name,fullname,filename)
+      end
+      hasitalics=false
       autoitalicamount=false
     end
   end
   if trace_defining then
     report_defining("defining tfm, name %a, fullname %a, filename %a, hscale %a, vscale %a, math %a, italics %a",
-      name,fullname,filename,hdelta,vdelta,
-      hasmath and "enabled" or "disabled",useitalics and "enabled" or "disabled")
+      name,fullname,filename,hdelta,vdelta,hasmath and "enabled" or "disabled",hasitalics and "enabled" or "disabled")
   end
   constructors.beforecopyingcharacters(target,tfmdata)
   local sharedkerns={}
@@ -4584,22 +4613,6 @@ function constructors.scale(tfmdata,specification)
         chr.right_protruding=protrusionfactor*width*vr
       end
     end
-    if autoitalicamount then
-      local vi=description.italic
-      if not vi then
-        local vi=description.boundingbox[3]-description.width+autoitalicamount
-        if vi>0 then 
-          chr[italickey]=vi*hdelta
-        end
-      elseif vi~=0 then
-        chr[italickey]=vi*hdelta
-      end
-    elseif hasitalics then
-      local vi=description.italic
-      if vi and vi~=0 then
-        chr[italickey]=vi*hdelta
-      end
-    end
     if hasmath then
       local vn=character.next
       if vn then
@@ -4637,7 +4650,7 @@ function constructors.scale(tfmdata,specification)
           end
         end
       end
-      local va=character.top_accent
+      local va=character.accent
       if va then
         chr.top_accent=vdelta*va
       end
@@ -4660,6 +4673,27 @@ function constructors.scale(tfmdata,specification)
           chr.mathkern=kerns 
         end
       end
+      if hasitalics then
+        local vi=character.italic
+        if vi and vi~=0 then
+          chr.italic=vi*hdelta
+        end
+      end
+    elseif autoitalicamount then 
+      local vi=description.italic
+      if not vi then
+        local vi=description.boundingbox[3]-description.width+autoitalicamount
+        if vi>0 then 
+          chr.italic=vi*hdelta
+        end
+      elseif vi~=0 then
+        chr.italic=vi*hdelta
+      end
+    elseif hasitalics then 
+      local vi=character.italic
+      if vi and vi~=0 then
+        chr.italic=vi*hdelta
+      end
     end
     if haskerns then
       local vk=character.kerns
@@ -4722,6 +4756,7 @@ function constructors.scale(tfmdata,specification)
     end
     targetcharacters[unicode]=chr
   end
+  properties.setitalics=hasitalics
   constructors.aftercopyingcharacters(target,tfmdata)
   constructors.trytosharefont(target,tfmdata)
   return target
@@ -4762,11 +4797,20 @@ function constructors.finalize(tfmdata)
   if not parameters.slantfactor then
     parameters.slantfactor=tfmdata.slant or 0
   end
-  if not parameters.designsize then
-    parameters.designsize=tfmdata.designsize or (factors.pt*10)
+  local designsize=parameters.designsize
+  if designsize then
+    parameters.minsize=tfmdata.minsize or designsize
+    parameters.maxsize=tfmdata.maxsize or designsize
+  else
+    designsize=factors.pt*10
+    parameters.designsize=designsize
+    parameters.minsize=designsize
+    parameters.maxsize=designsize
   end
+  parameters.minsize=tfmdata.minsize or parameters.designsize
+  parameters.maxsize=tfmdata.maxsize or parameters.designsize
   if not parameters.units then
-    parameters.units=tfmdata.units_per_em or 1000
+    parameters.units=tfmdata.units or tfmdata.units_per_em or 1000
   end
   if not tfmdata.descriptions then
     local descriptions={} 
@@ -4829,6 +4873,7 @@ function constructors.finalize(tfmdata)
   tfmdata.auto_protrude=nil
   tfmdata.extend=nil
   tfmdata.slant=nil
+  tfmdata.units=nil
   tfmdata.units_per_em=nil
   tfmdata.cache=nil
   properties.finalized=true
@@ -5393,24 +5438,13 @@ local fonts=fonts or {}
 local mappings=fonts.mappings or {}
 fonts.mappings=mappings
 local allocate=utilities.storage.allocate
-local function loadlumtable(filename) 
-  local lumname=file.replacesuffix(file.basename(filename),"lum")
-  local lumfile=resolvers.findfile(lumname,"map") or ""
-  if lumfile~="" and lfs.isfile(lumfile) then
-    if trace_loading or trace_mapping then
-      report_fonts("loading map table %a",lumfile)
-    end
-    lumunic=dofile(lumfile)
-    return lumunic,lumfile
-  end
-end
 local hex=R("AF","09")
-local hexfour=(hex*hex*hex*hex)/function(s) return tonumber(s,16) end
-local hexsix=(hex*hex*hex*hex*hex*hex)/function(s) return tonumber(s,16) end
+local hexfour=(hex*hex*hex^-2)/function(s) return tonumber(s,16) end
+local hexsix=(hex*hex*hex^-4)/function(s) return tonumber(s,16) end
 local dec=(R("09")^1)/tonumber
 local period=P(".")
-local unicode=P("uni")*(hexfour*(period+P(-1))*Cc(false)+Ct(hexfour^1)*Cc(true))
-local ucode=P("u")*(hexsix*(period+P(-1))*Cc(false)+Ct(hexsix^1)*Cc(true))
+local unicode=(P("uni")+P("UNI"))*(hexfour*(period+P(-1))*Cc(false)+Ct(hexfour^1)*Cc(true)) 
+local ucode=(P("u")+P("U") )*(hexsix*(period+P(-1))*Cc(false)+Ct(hexsix^1)*Cc(true)) 
 local index=P("index")*dec*Cc(false)
 local parser=unicode+ucode+index
 local parsers={}
@@ -5485,7 +5519,6 @@ local function fromunicode16(str)
     return (tonumber(l,16))*0x400+tonumber(r,16)-0xDC00
   end
 end
-mappings.loadlumtable=loadlumtable
 mappings.makenameparser=makenameparser
 mappings.tounicode=tounicode
 mappings.tounicode16=tounicode16
@@ -5516,244 +5549,162 @@ for k,v in next,overloads do
   end
 end
 mappings.overloads=overloads
-function mappings.addtounicode(data,filename)
+function mappings.addtounicode(data,filename,checklookups)
   local resources=data.resources
-  local properties=data.properties
-  local descriptions=data.descriptions
   local unicodes=resources.unicodes
-  local lookuptypes=resources.lookuptypes
   if not unicodes then
     return
   end
+  local properties=data.properties
+  local descriptions=data.descriptions
   unicodes['space']=unicodes['space'] or 32
   unicodes['hyphen']=unicodes['hyphen'] or 45
   unicodes['zwj']=unicodes['zwj']  or 0x200D
   unicodes['zwnj']=unicodes['zwnj']  or 0x200C
-  local private=fonts.constructors.privateoffset
-  local unicodevector=fonts.encodings.agl.unicodes
+  local private=fonts.constructors and fonts.constructors.privateoffset or 0xF0000 
+  local unicodevector=fonts.encodings.agl.unicodes or {} 
+  local contextvector=fonts.encodings.agl.ctxcodes or {} 
   local missing={}
-  local lumunic,uparser,oparser
-  local cidinfo,cidnames,cidcodes,usedmap
-  cidinfo=properties.cidinfo
-  usedmap=cidinfo and fonts.cid.getmap(cidinfo)
+  local nofmissing=0
+  local oparser=nil
+  local cidnames=nil
+  local cidcodes=nil
+  local cidinfo=properties.cidinfo
+  local usedmap=cidinfo and fonts.cid.getmap(cidinfo)
+  local uparser=makenameparser() 
   if usedmap then
-    oparser=usedmap and makenameparser(cidinfo.ordering)
-    cidnames=usedmap.names
-    cidcodes=usedmap.unicodes
+     oparser=usedmap and makenameparser(cidinfo.ordering)
+     cidnames=usedmap.names
+     cidcodes=usedmap.unicodes
   end
-  uparser=makenameparser()
-  local ns,nl=0,0
+  local ns=0
+  local nl=0
   for unic,glyph in next,descriptions do
-    local index=glyph.index
     local name=glyph.name
-    local r=overloads[name]
-    if r then
-      glyph.unicode=r.unicode
-    elseif unic==-1 or unic>=private or (unic>=0xE000 and unic<=0xF8FF) or unic==0xFFFE or unic==0xFFFF then
-      local unicode=lumunic and lumunic[name] or unicodevector[name]
-      if unicode then
-        glyph.unicode=unicode
-        ns=ns+1
-      end
-      if (not unicode) and usedmap then
-        local foundindex=lpegmatch(oparser,name)
-        if foundindex then
-          unicode=cidcodes[foundindex] 
-          if unicode then
-            glyph.unicode=unicode
-            ns=ns+1
-          else
-            local reference=cidnames[foundindex] 
-            if reference then
-              local foundindex=lpegmatch(oparser,reference)
-              if foundindex then
-                unicode=cidcodes[foundindex]
-                if unicode then
-                  glyph.unicode=unicode
-                  ns=ns+1
-                end
-              end
-              if not unicode or unicode=="" then
-                local foundcodes,multiple=lpegmatch(uparser,reference)
-                if foundcodes then
-                  glyph.unicode=foundcodes
-                  if multiple then
-                    nl=nl+1
-                    unicode=true
-                  else
+    if name then
+      local index=glyph.index
+      local r=overloads[name]
+      if r then
+        glyph.unicode=r.unicode
+      elseif not unic or unic==-1 or unic>=private or (unic>=0xE000 and unic<=0xF8FF) or unic==0xFFFE or unic==0xFFFF then
+        local unicode=unicodevector[name] or contextvector[name]
+        if unicode then
+          glyph.unicode=unicode
+          ns=ns+1
+        end
+        if (not unicode) and usedmap then
+          local foundindex=lpegmatch(oparser,name)
+          if foundindex then
+            unicode=cidcodes[foundindex] 
+            if unicode then
+              glyph.unicode=unicode
+              ns=ns+1
+            else
+              local reference=cidnames[foundindex] 
+              if reference then
+                local foundindex=lpegmatch(oparser,reference)
+                if foundindex then
+                  unicode=cidcodes[foundindex]
+                  if unicode then
+                    glyph.unicode=unicode
                     ns=ns+1
-                    unicode=foundcodes
+                  end
+                end
+                if not unicode or unicode=="" then
+                  local foundcodes,multiple=lpegmatch(uparser,reference)
+                  if foundcodes then
+                    glyph.unicode=foundcodes
+                    if multiple then
+                      nl=nl+1
+                      unicode=true
+                    else
+                      ns=ns+1
+                      unicode=foundcodes
+                    end
                   end
                 end
               end
             end
           end
         end
-      end
-      if not unicode or unicode=="" then
-        local split=lpegmatch(namesplitter,name)
-        local nsplit=split and #split or 0
-        local t,n={},0
-        unicode=true
-        for l=1,nsplit do
-          local base=split[l]
-          local u=unicodes[base] or unicodevector[base]
-          if not u then
-            break
-          elseif type(u)=="table" then
-            if u[1]>=private then
-              unicode=false
-              break
-            end
-            n=n+1
-            t[n]=u[1]
-          else
-            if u>=private then
-              unicode=false
-              break
+        if not unicode or unicode=="" then
+          local split=lpegmatch(namesplitter,name)
+          local nsplit=split and #split or 0 
+          if nsplit==0 then
+          elseif nsplit==1 then
+            local base=split[1]
+            local u=unicodes[base] or unicodevector[base] or contextvector[name]
+            if not u then
+            elseif type(u)=="table" then
+              if u[1]<private then
+                unicode=u
+                glyph.unicode=unicode
+              end
+            elseif u<private then
+              unicode=u
+              glyph.unicode=unicode
             end
-            n=n+1
-            t[n]=u
-          end
-        end
-        if n==0 then
-        elseif n==1 then
-          glyph.unicode=t[1]
-        else
-          glyph.unicode=t
-        end
-        nl=nl+1
-      end
-      if not unicode or unicode=="" then
-        local foundcodes,multiple=lpegmatch(uparser,name)
-        if foundcodes then
-          glyph.unicode=foundcodes
-          if multiple then
-            nl=nl+1
-            unicode=true
           else
-            ns=ns+1
-            unicode=foundcodes
-          end
-        end
-      end
-      local r=overloads[unicode]
-      if r then
-        unicode=r.unicode
-        glyph.unicode=unicode
-      end
-      if not unicode then
-        missing[name]=true
-      end
-    end
-  end
-  if next(missing) then
-    local guess={}
-    local function check(gname,code,unicode)
-      local description=descriptions[code]
-      local variant=description.name
-      if variant==gname then
-        return
-      end
-      local unic=unicodes[variant]
-      if unic==-1 or unic>=private or (unic>=0xE000 and unic<=0xF8FF) or unic==0xFFFE or unic==0xFFFF then
-      else
-        return
-      end
-      if descriptions[code].unicode then
-        return
-      end
-      local g=guess[variant]
-      if g then
-        g[gname]=unicode
-      else
-        guess[variant]={ [gname]=unicode }
-      end
-    end
-    for unicode,description in next,descriptions do
-      local slookups=description.slookups
-      if slookups then
-        local gname=description.name
-        for tag,data in next,slookups do
-          local lookuptype=lookuptypes[tag]
-          if lookuptype=="alternate" then
-            for i=1,#data do
-              check(gname,data[i],unicode)
-            end
-          elseif lookuptype=="substitution" then
-            check(gname,data,unicode)
-          end
-        end
-      end
-      local mlookups=description.mlookups
-      if mlookups then
-        local gname=description.name
-        for tag,list in next,mlookups do
-          local lookuptype=lookuptypes[tag]
-          if lookuptype=="alternate" then
-            for i=1,#list do
-              local data=list[i]
-              for i=1,#data do
-                check(gname,data[i],unicode)
+            local t,n={},0
+            for l=1,nsplit do
+              local base=split[l]
+              local u=unicodes[base] or unicodevector[base] or contextvector[name]
+              if not u then
+                break
+              elseif type(u)=="table" then
+                if u[1]>=private then
+                  break
+                end
+                n=n+1
+                t[n]=u[1]
+              else
+                if u>=private then
+                  break
+                end
+                n=n+1
+                t[n]=u
               end
             end
-          elseif lookuptype=="substitution" then
-            for i=1,#list do
-              check(gname,list[i],unicode)
+            if n>0 then
+              if n==1 then
+                unicode=t[1]
+              else
+                unicode=t
+              end
+              glyph.unicode=unicode
             end
           end
-        end
-      end
-    end
-    local done=true
-    while done do
-      done=false
-      for k,v in next,guess do
-        if type(v)~="number" then
-          for kk,vv in next,v do
-            if vv==-1 or vv>=private or (vv>=0xE000 and vv<=0xF8FF) or vv==0xFFFE or vv==0xFFFF then
-              local uu=guess[kk]
-              if type(uu)=="number" then
-                guess[k]=uu
-                done=true
-              end
+          nl=nl+1
+        end
+        if not unicode or unicode=="" then
+          local foundcodes,multiple=lpegmatch(uparser,name)
+          if foundcodes then
+            glyph.unicode=foundcodes
+            if multiple then
+              nl=nl+1
+              unicode=true
             else
-              guess[k]=vv
-              done=true
+              ns=ns+1
+              unicode=foundcodes
             end
           end
         end
-      end
-    end
-    local orphans=0
-    local guessed=0
-    for k,v in next,guess do
-      if type(v)=="number" then
-        descriptions[unicodes[k]].unicode=descriptions[v].unicode or v 
-        guessed=guessed+1
-      else
-        local t=nil
-        local l=lower(k)
-        local u=unicodes[l]
-        if not u then
-          orphans=orphans+1
-        elseif u==-1 or u>=private or (u>=0xE000 and u<=0xF8FF) or u==0xFFFE or u==0xFFFF then
-          local unicode=descriptions[u].unicode
-          if unicode then
-            descriptions[unicodes[k]].unicode=unicode
-            guessed=guessed+1
-          else
-            orphans=orphans+1
-          end
-        else
-          orphans=orphans+1
+        local r=overloads[unicode]
+        if r then
+          unicode=r.unicode
+          glyph.unicode=unicode
+        end
+        if not unicode then
+          missing[unic]=true
+          nofmissing=nofmissing+1
         end
       end
-    end
-    if trace_loading and orphans>0 or guessed>0 then
-      report_fonts("%s glyphs with no related unicode, %s guessed, %s orphans",guessed+orphans,guessed,orphans)
+    else
     end
   end
+  if type(checklookups)=="function" then
+    checklookups(data,missing,nofmissing)
+  end
   if trace_mapping then
     for unic,glyph in table.sortedhash(descriptions) do
       local name=glyph.name
@@ -5881,6 +5832,7 @@ local readers=fonts.readers
 local constructors=fonts.constructors
 local encodings=fonts.encodings
 local tfm=constructors.newhandler("tfm")
+tfm.version=1.000
 local tfmfeatures=constructors.newfeatures("tfm")
 local registertfmfeature=tfmfeatures.register
 constructors.resolvevirtualtoo=false 
@@ -6067,7 +6019,7 @@ local keys={}
 function keys.FontName  (data,line) data.metadata.fontname=strip  (line) 
                    data.metadata.fullname=strip  (line) end
 function keys.ItalicAngle (data,line) data.metadata.italicangle=tonumber (line) end
-function keys.IsFixedPitch(data,line) data.metadata.isfixedpitch=toboolean(line,true) end
+function keys.IsFixedPitch(data,line) data.metadata.monospaced=toboolean(line,true) end
 function keys.CharWidth  (data,line) data.metadata.charwidth=tonumber (line) end
 function keys.XHeight   (data,line) data.metadata.xheight=tonumber (line) end
 function keys.Descender  (data,line) data.metadata.descender=tonumber (line) end
@@ -6489,7 +6441,7 @@ local function copytotfm(data)
     local emdash=0x2014
     local spacer="space"
     local spaceunits=500
-    local monospaced=metadata.isfixedpitch
+    local monospaced=metadata.monospaced
     local charwidth=metadata.charwidth
     local italicangle=metadata.italicangle
     local charxheight=metadata.xheight and metadata.xheight>0 and metadata.xheight
@@ -7144,12 +7096,10 @@ if not modules then modules={} end modules ['font-otf']={
   license="see context related readme files"
 }
 local utfbyte=utf.byte
-local format,gmatch,gsub,find,match,lower,strip=string.format,string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip
+local gmatch,gsub,find,match,lower,strip=string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip
 local type,next,tonumber,tostring=type,next,tonumber,tostring
 local abs=math.abs
-local insert=table.insert
-local lpegmatch=lpeg.match
-local reversed,concat,remove,sortedkeys=table.reversed,table.concat,table.remove,table.sortedkeys
+local reversed,concat,insert,remove,sortedkeys=table.reversed,table.concat,table.insert,table.remove,table.sortedkeys
 local ioflush=io.flush
 local fastcopy,tohash,derivetable=table.fastcopy,table.tohash,table.derive
 local formatters=string.formatters
@@ -7176,7 +7126,7 @@ local report_otf=logs.reporter("fonts","otf loading")
 local fonts=fonts
 local otf=fonts.handlers.otf
 otf.glists={ "gsub","gpos" }
-otf.version=2.812 
+otf.version=2.819 
 otf.cache=containers.define("fonts","otf",otf.version,true)
 local hashes=fonts.hashes
 local definers=fonts.definers
@@ -7353,10 +7303,10 @@ local ordered_enhancers={
   "reorganize subtables",
   "check glyphs",
   "check metadata",
-  "check extra features",
   "prepare tounicode",
   "check encoding",
   "add duplicates",
+  "expand lookups",
   "cleanup tables",
   "compact lookups",
   "purge names",
@@ -7493,6 +7443,7 @@ function otf.load(filename,sub,featurefile)
     end
    end
    if reload then
+    starttiming("fontloader")
     report_otf("loading %a, hash %a",filename,hash)
     local fontdata,messages
     if sub then
@@ -7526,6 +7477,7 @@ function otf.load(filename,sub,featurefile)
       data={
         size=size,
         time=time,
+        subfont=sub,
         format=otf_format(filename),
         featuredata=featurefiles,
         resources={
@@ -7553,7 +7505,6 @@ function otf.load(filename,sub,featurefile)
           tounicodetable=Ct(splitter),
         },
       }
-      starttiming(data)
       report_otf("file size: %s",size)
       enhancers.apply(data,filename,fontdata)
       local packtime={}
@@ -7570,10 +7521,10 @@ function otf.load(filename,sub,featurefile)
       if cleanup>1 then
         collectgarbage("collect")
       end
-      stoptiming(data)
+      stoptiming("fontloader")
       if elapsedtime then 
-        report_otf("preprocessing and caching time %s, packtime %s",
-          elapsedtime(data),packdata and elapsedtime(packtime) or 0)
+        report_otf("loading, optimizing, packing and caching time %s, pack time %s",
+          elapsedtime("fontloader"),packdata and elapsedtime(packtime) or 0)
       end
       close_font(fontdata) 
       if cleanup>3 then
@@ -7584,6 +7535,7 @@ function otf.load(filename,sub,featurefile)
         collectgarbage("collect")
       end
     else
+      stoptiming("fontloader")
       data=nil
       report_otf("loading failed due to read error")
     end
@@ -7625,6 +7577,7 @@ function otf.load(filename,sub,featurefile)
       applyruntimefixes(filename,data)
     end
     enhance("add dimensions",data,filename,nil,false)
+enhance("check extra features",data,filename)
     if trace_sequences then
       showfeatureorder(data,filename)
     end
@@ -7785,7 +7738,7 @@ actions["prepare glyphs"]=function(data,filename,raw)
                 end
                 if not unicode or unicode==-1 then 
                   if not name then
-                    name=format("u%06X.ctx",private)
+                    name=formatters["u%06X.ctx"](private)
                   end
                   unicode=private
                   unicodes[name]=private
@@ -7796,7 +7749,7 @@ actions["prepare glyphs"]=function(data,filename,raw)
                   nofnames=nofnames+1
                 else
                   if not name then
-                    name=format("u%06X.ctx",unicode)
+                    name=formatters["u%06X.ctx"](unicode)
                   end
                   unicodes[name]=unicode
                   nofunicodes=nofunicodes+1
@@ -7810,25 +7763,25 @@ actions["prepare glyphs"]=function(data,filename,raw)
                   glyph=glyph,
                 }
                 descriptions[unicode]=description
-local altuni=glyph.altuni
-if altuni then
-  for i=1,#altuni do
-    local a=altuni[i]
-    local u=a.unicode
-    if u~=unicode then
-      local v=a.variant
-      if v then
-        local vv=variants[v]
-        if vv then
-          vv[u]=unicode
-        else 
-          vv={ [u]=unicode }
-          variants[v]=vv
-        end
-      end
-    end
-  end
-end
+                local altuni=glyph.altuni
+                if altuni then
+                  for i=1,#altuni do
+                    local a=altuni[i]
+                    local u=a.unicode
+                    if u~=unicode then
+                      local v=a.variant
+                      if v then
+                        local vv=variants[v]
+                        if vv then
+                          vv[u]=unicode
+                        else 
+                          vv={ [u]=unicode }
+                          variants[v]=vv
+                        end
+                      end
+                    end
+                  end
+                end
               end
             end
           else
@@ -8014,7 +7967,7 @@ actions["add duplicates"]=function(data,filename,raw)
           end
           if u>0 then 
             local duplicate=table.copy(description) 
-            duplicate.comment=format("copy of U+%05X",unicode)
+            duplicate.comment=formatters["copy of %U"](unicode)
             descriptions[u]=duplicate
             if trace_loading then
               report_otf("duplicating %U to %U with index %H (%s kerns)",unicode,u,description.index,n)
@@ -8035,7 +7988,7 @@ actions["analyze glyphs"]=function(data,filename,raw)
   local marks={} 
   for unicode,description in next,descriptions do
     local glyph=description.glyph
-    local italic=glyph.italic_correction
+    local italic=glyph.italic_correction 
     if not italic then
     elseif italic==0 then
     else
@@ -8096,7 +8049,8 @@ end
 actions["reorganize features"]=function(data,filename,raw) 
   local features={}
   data.resources.features=features
-  for k,what in next,otf.glists do
+  for k=1,#otf.glists do
+    local what=otf.glists[k]
     local dw=raw[what]
     if dw then
       local f={}
@@ -8178,8 +8132,9 @@ actions["reorganize subtables"]=function(data,filename,raw)
   local lookups={}
   local chainedfeatures={}
   resources.sequences=sequences
-  resources.lookups=lookups
-  for _,what in next,otf.glists do
+  resources.lookups=lookups 
+  for k=1,#otf.glists do
+    local what=otf.glists[k]
     local dw=raw[what]
     if dw then
       for k=1,#dw do
@@ -8353,12 +8308,15 @@ local function r_uncover(splitter,cache,cover,replacements)
 end
 actions["reorganize lookups"]=function(data,filename,raw)
   if data.lookups then
-    local splitter=data.helpers.tounicodetable
+    local helpers=data.helpers
+    local duplicates=data.resources.duplicates
+    local splitter=helpers.tounicodetable
     local t_u_cache={}
     local s_u_cache=t_u_cache 
     local t_h_cache={}
     local s_h_cache=t_h_cache 
     local r_u_cache={} 
+    helpers.matchcache=t_h_cache
     for _,lookup in next,data.lookups do
       local rules=lookup.rules
       if rules then
@@ -8504,6 +8462,44 @@ actions["reorganize lookups"]=function(data,filename,raw)
     end
   end
 end
+actions["expand lookups"]=function(data,filename,raw) 
+  if data.lookups then
+    local cache=data.helpers.matchcache
+    if cache then
+      local duplicates=data.resources.duplicates
+      for key,hash in next,cache do
+        local done=nil
+        for key in next,hash do
+          local unicode=duplicates[key]
+          if not unicode then
+          elseif type(unicode)=="table" then
+            for i=1,#unicode do
+              local u=unicode[i]
+              if hash[u] then
+              elseif done then
+                done[u]=key
+              else
+                done={ [u]=key }
+              end
+            end
+          else
+            if hash[unicode] then
+            elseif done then
+              done[unicode]=key
+            else
+              done={ [unicode]=key }
+            end
+          end
+        end
+        if done then
+          for u in next,done do
+            hash[u]=true
+          end
+        end
+      end
+    end
+  end
+end
 local function check_variants(unicode,the_variants,splitter,unicodes)
   local variants=the_variants.variants
   if variants then 
@@ -8544,11 +8540,11 @@ local function check_variants(unicode,the_variants,splitter,unicodes)
       parts=nil
     end
   end
-  local italic_correction=the_variants.italic_correction
-  if italic_correction and italic_correction==0 then
-    italic_correction=nil
+  local italic=the_variants.italic
+  if italic and italic==0 then
+    italic=nil
   end
-  return variants,parts,italic_correction
+  return variants,parts,italic
 end
 actions["analyze math"]=function(data,filename,raw)
   if raw.math then
@@ -8558,13 +8554,14 @@ actions["analyze math"]=function(data,filename,raw)
     for unicode,description in next,data.descriptions do
       local glyph=description.glyph
       local mathkerns=glyph.mathkern 
-      local horiz_variants=glyph.horiz_variants
-      local vert_variants=glyph.vert_variants
-      local top_accent=glyph.top_accent
-      if mathkerns or horiz_variants or vert_variants or top_accent then
+      local hvariants=glyph.horiz_variants
+      local vvariants=glyph.vert_variants
+      local accent=glyph.top_accent
+      local italic=glyph.italic_correction
+      if mathkerns or hvariants or vvariants or accent or italic then
         local math={}
-        if top_accent then
-          math.top_accent=top_accent
+        if accent then
+          math.accent=accent
         end
         if mathkerns then
           for k,v in next,mathkerns do
@@ -8580,15 +8577,14 @@ actions["analyze math"]=function(data,filename,raw)
           end
           math.kerns=mathkerns
         end
-        if horiz_variants then
-          math.horiz_variants,math.horiz_parts,math.horiz_italic_correction=check_variants(unicode,horiz_variants,splitter,unicodes)
+        if hvariants then
+          math.hvariants,math.hparts,math.hitalic=check_variants(unicode,hvariants,splitter,unicodes)
         end
-        if vert_variants then
-          math.vert_variants,math.vert_parts,math.vert_italic_correction=check_variants(unicode,vert_variants,splitter,unicodes)
+        if vvariants then
+          math.vvariants,math.vparts,math.vitalic=check_variants(unicode,vvariants,splitter,unicodes)
         end
-        local italic_correction=description.italic
-        if italic_correction and italic_correction~=0 then
-          math.italic_correction=italic_correction
+        if italic and italic~=0 then
+          math.italic=italic
         end
         description.math=math
       end
@@ -8745,7 +8741,7 @@ actions["merge kern classes"]=function(data,filename,raw)
       report_otf("%s kern overloads ignored",ignored)
     end
     if blocked>0 then
-      report_otf("%s succesive kerns blocked",blocked)
+      report_otf("%s successive kerns blocked",blocked)
     end
   end
 end
@@ -8774,16 +8770,18 @@ actions["check metadata"]=function(data,filename,raw)
       ttftables[i].data="deleted"
     end
   end
+  local names=raw.names
   if metadata.validation_state and table.contains(metadata.validation_state,"bad_ps_fontname") then
     local function valid(what)
-      local names=raw.names
-      for i=1,#names do
-        local list=names[i]
-        local names=list.names
-        if names then
-          local name=names[what]
-          if name and valid_ps_name(name) then
-            return name
+      if names then
+        for i=1,#names do
+          local list=names[i]
+          local names=list.names
+          if names then
+            local name=names[what]
+            if name and valid_ps_name(name) then
+              return name
+            end
           end
         end
       end
@@ -8806,6 +8804,28 @@ actions["check metadata"]=function(data,filename,raw)
     check("fontname")
     check("fullname")
   end
+  if names then
+    local psname=metadata.psname
+    if not psname or psname=="" then
+      for i=1,#names do
+        local name=names[i]
+        if lower(name.lang)=="english (us)" then
+          local specification=name.names
+          if specification then
+            local postscriptname=specification.postscriptname
+            if postscriptname then
+              psname=postscriptname
+            end
+          end
+        end
+        break
+      end
+    end
+    if psname~=metadata.fontname then
+      report_otf("fontname %a, fullname %a, psname %a",metadata.fontname,metadata.fullname,psname)
+    end
+    metadata.psname=psname
+  end
 end
 actions["cleanup tables"]=function(data,filename,raw)
   local duplicates=data.resources.duplicates
@@ -8901,7 +8921,7 @@ actions["reorganize glyph lookups"]=function(data,filename,raw)
   end
 end
 local zero={ 0,0 }
-actions["reorganize glyph anchors"]=function(data,filename,raw) 
+actions["reorganize glyph anchors"]=function(data,filename,raw)
   local descriptions=data.descriptions
   for unicode,description in next,descriptions do
     local anchors=description.glyph.anchors
@@ -9103,9 +9123,13 @@ local function copytotfm(data,cache_id)
     local spaceunits=500
     local spacer="space"
     local designsize=metadata.designsize or metadata.design_size or 100
+    local minsize=metadata.minsize or metadata.design_range_bottom or designsize
+    local maxsize=metadata.maxsize or metadata.design_range_top  or designsize
     local mathspecs=metadata.math
     if designsize==0 then
       designsize=100
+      minsize=100
+      maxsize=100
     end
     if mathspecs then
       for name,value in next,mathspecs do
@@ -9120,8 +9144,9 @@ local function copytotfm(data,cache_id)
         local d=descriptions[unicode]
         local m=d.math
         if m then
-          local variants=m.horiz_variants
-          local parts=m.horiz_parts
+          local italic=m.italic
+          local variants=m.hvariants
+          local parts=m.hparts
           if variants then
             local c=character
             for i=1,#variants do
@@ -9132,9 +9157,10 @@ local function copytotfm(data,cache_id)
             c.horiz_variants=parts
           elseif parts then
             character.horiz_variants=parts
+            italic=m.hitalic
           end
-          local variants=m.vert_variants
-          local parts=m.vert_parts
+          local variants=m.vvariants
+          local parts=m.vparts
           if variants then
             local c=character
             for i=1,#variants do
@@ -9145,14 +9171,14 @@ local function copytotfm(data,cache_id)
             c.vert_variants=parts
           elseif parts then
             character.vert_variants=parts
+            italic=m.vitalic
           end
-          local italic_correction=m.vert_italic_correction
-          if italic_correction then
-            character.vert_italic_correction=italic_correction 
+          if italic and italic~=0 then
+            character.italic=italic 
           end
-          local top_accent=m.top_accent
-          if top_accent then
-            character.top_accent=top_accent
+          local accent=m.accent
+          if accent then
+            character.accent=accent
           end
           local kerns=m.kerns
           if kerns then
@@ -9164,14 +9190,14 @@ local function copytotfm(data,cache_id)
     local filename=constructors.checkedfilename(resources)
     local fontname=metadata.fontname
     local fullname=metadata.fullname or fontname
-    local psname=fontname or fullname
-    local units=metadata.units_per_em or 1000
+    local psname=metadata.psname or fontname or fullname
+    local units=metadata.units or metadata.units_per_em or 1000
     if units==0 then 
       units=1000 
-      metadata.units_per_em=1000
+      metadata.units=1000
       report_otf("changing %a units to %a",0,units)
     end
-    local monospaced=metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion=="Monospaced")
+    local monospaced=metadata.monospaced or metadata.isfixedpitch or (pfminfo.panose and pfminfo.panose.proportion=="Monospaced")
     local charwidth=pfminfo.avgwidth 
     local charxheight=pfminfo.os2_xheight and pfminfo.os2_xheight>0 and pfminfo.os2_xheight
     local italicangle=metadata.italicangle
@@ -9236,8 +9262,10 @@ local function copytotfm(data,cache_id)
       end
     end
     parameters.designsize=(designsize/10)*65536
-    parameters.ascender=abs(metadata.ascent or 0)
-    parameters.descender=abs(metadata.descent or 0)
+    parameters.minsize=(minsize/10)*65536
+    parameters.maxsize=(maxsize/10)*65536
+    parameters.ascender=abs(metadata.ascender or metadata.ascent or 0)
+    parameters.descender=abs(metadata.descender or metadata.descent or 0)
     parameters.units=units
     properties.space=spacer
     properties.encodingbytes=2
@@ -9416,6 +9444,99 @@ function otf.scriptandlanguage(tfmdata,attr)
   local properties=tfmdata.properties
   return properties.script or "dflt",properties.language or "dflt"
 end
+local function justset(coverage,unicode,replacement)
+  coverage[unicode]=replacement
+end
+otf.coverup={
+  stepkey="subtables",
+  actions={
+    substitution=justset,
+    alternate=justset,
+    multiple=justset,
+    ligature=justset,
+    kern=justset,
+  },
+  register=function(coverage,lookuptype,format,feature,n,descriptions,resources)
+    local name=formatters["ctx_%s_%s"](feature,n)
+    if lookuptype=="kern" then
+      resources.lookuptypes[name]="position"
+    else
+      resources.lookuptypes[name]=lookuptype
+    end
+    for u,c in next,coverage do
+      local description=descriptions[u]
+      local slookups=description.slookups
+      if slookups then
+        slookups[name]=c
+      else
+        description.slookups={ [name]=c }
+      end
+    end
+    return name
+  end
+}
+local function getgsub(tfmdata,k,kind)
+  local description=tfmdata.descriptions[k]
+  if description then
+    local slookups=description.slookups 
+    if slookups then
+      local shared=tfmdata.shared
+      local rawdata=shared and shared.rawdata
+      if rawdata then
+        local lookuptypes=rawdata.resources.lookuptypes
+        if lookuptypes then
+          local properties=tfmdata.properties
+          local validlookups,lookuplist=otf.collectlookups(rawdata,kind,properties.script,properties.language)
+          if validlookups then
+            for l=1,#lookuplist do
+              local lookup=lookuplist[l]
+              local found=slookups[lookup]
+              if found then
+                return found,lookuptypes[lookup]
+              end
+            end
+          end
+        end
+      end
+    end
+  end
+end
+otf.getgsub=getgsub 
+function otf.getsubstitution(tfmdata,k,kind,value)
+  local found,kind=getgsub(tfmdata,k,kind)
+  if not found then
+  elseif kind=="substitution" then
+    return found
+  elseif kind=="alternate" then
+    local choice=tonumber(value) or 1 
+    return found[choice] or found[1] or k
+  end
+  return k
+end
+otf.getalternate=otf.getsubstitution
+function otf.getmultiple(tfmdata,k,kind)
+  local found,kind=getgsub(tfmdata,k,kind)
+  if found and kind=="multiple" then
+    return found
+  end
+  return { k }
+end
+function otf.getkern(tfmdata,left,right,kind)
+  local kerns=getgsub(tfmdata,left,kind or "kern",true) 
+  if kerns then
+    local found=kerns[right]
+    local kind=type(found)
+    if kind=="table" then
+      found=found[1][3] 
+    elseif kind~="number" then
+      found=false
+    end
+    if found then
+      return found*tfmdata.parameters.factor
+    end
+  end
+  return 0
+end
 
 end -- closure
 
@@ -9946,8 +10067,8 @@ local function featuresinitializer(tfmdata,value)
       local collectlookups=otf.collectlookups
       local rawdata=tfmdata.shared.rawdata
       local properties=tfmdata.properties
-      local script=properties.script
-      local language=properties.language
+      local script=properties.script  
+      local language=properties.language 
       local basesubstitutions=rawdata.resources.features.gsub
       local basepositionings=rawdata.resources.features.gpos
       if basesubstitutions or basepositionings then
@@ -10125,7 +10246,8 @@ end
 function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) 
   local dx=factor*(exit[1]-entry[1])
   local dy=-factor*(exit[2]-entry[2])
-  local ws,wn=tfmstart.width,tfmnext.width
+  local ws=tfmstart.width
+  local wn=tfmnext.width
   nofregisteredcursives=nofregisteredcursives+1
   if rlmode<0 then
     dx=-(dx+wn)
@@ -10172,7 +10294,10 @@ function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmne
   return dx,dy,nofregisteredcursives
 end
 function injections.setpair(current,factor,rlmode,r2lflag,spec,injection) 
-  local x,y,w,h=factor*spec[1],factor*spec[2],factor*spec[3],factor*spec[4]
+  local x=factor*spec[1]
+  local y=factor*spec[2]
+  local w=factor*spec[3]
+  local h=factor*spec[4]
   if x~=0 or w~=0 or y~=0 or h~=0 then 
     local yoffset=y-h
     local leftkern=x   
@@ -10182,9 +10307,12 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection)
       if rlmode and rlmode<0 then
         leftkern,rightkern=rightkern,leftkern
       end
+      if not injection then
+        injection="injections"
+      end
       local p=rawget(properties,current)
       if p then
-        local i=rawget(p,"injections")
+        local i=rawget(p,injection)
         if i then
           if leftkern~=0 then
             i.leftkern=(i.leftkern or 0)+leftkern
@@ -10196,19 +10324,19 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection)
             i.yoffset=(i.yoffset or 0)+yoffset
           end
         elseif leftkern~=0 or rightkern~=0 then
-          p.injections={
+          p[injection]={
             leftkern=leftkern,
             rightkern=rightkern,
             yoffset=yoffset,
           }
         else
-          p.injections={
+          p[injection]={
             yoffset=yoffset,
           }
         end
       elseif leftkern~=0 or rightkern~=0 then
         properties[current]={
-          injections={
+          [injection]={
             leftkern=leftkern,
             rightkern=rightkern,
             yoffset=yoffset,
@@ -10216,7 +10344,7 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,injection)
         }
       else
         properties[current]={
-          injections={
+          [injection]={
             yoffset=yoffset,
           },
         }
@@ -10255,7 +10383,7 @@ function injections.setkern(current,factor,rlmode,x,injection)
     return 0,0
   end
 end
-function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase) 
+function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk) 
   local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2])
   nofregisteredmarks=nofregisteredmarks+1
   if rlmode>=0 then
@@ -10265,11 +10393,15 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase)
   if p then
     local i=rawget(p,"injections")
     if i then
-      i.markx=dx
-      i.marky=dy
-      i.markdir=rlmode or 0
-      i.markbase=nofregisteredmarks
-      i.markbasenode=base
+      if i.markmark then
+      else
+        i.markx=dx
+        i.marky=dy
+        i.markdir=rlmode or 0
+        i.markbase=nofregisteredmarks
+        i.markbasenode=base
+        i.markmark=mkmk
+      end
     else
       p.injections={
         markx=dx,
@@ -10277,6 +10409,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase)
         markdir=rlmode or 0,
         markbase=nofregisteredmarks,
         markbasenode=base,
+        markmark=mkmk,
       }
     end
   else
@@ -10287,6 +10420,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase)
         markdir=rlmode or 0,
         markbase=nofregisteredmarks,
         markbasenode=base,
+        markmark=mkmk,
       },
     }
   end
@@ -10391,27 +10525,33 @@ local function show_result(head)
     current=getnext(current)
   end
 end
-local function collect_glyphs_1(head)
-  local glyphs,nofglyphs={},0
-  local marks,nofmarks={},0
+local function collect_glyphs(head,offsets)
+  local glyphs,glyphi,nofglyphs={},{},0
+  local marks,marki,nofmarks={},{},0
   local nf,tm=nil,nil
-  for n in traverse_id(glyph_code,head) do 
-    if getsubtype(n)<256 then
-      local f=getfont(n)
-      if f~=nf then
-        nf=f
-        tm=fontdata[nf].resources.marks 
-      end
-      if tm and tm[getchar(n)] then
-        nofmarks=nofmarks+1
-        marks[nofmarks]=n
-      else
-        nofglyphs=nofglyphs+1
-        glyphs[nofglyphs]=n
-      end
+  local n=head
+  local function identify(n,what)
+    local f=getfont(n)
+    if f~=nf then
+      nf=f
+      tm=fontdata[nf].resources
+      if tm then
+        tm=tm.marks
+      end
+    end
+    if tm and tm[getchar(n)] then
+      nofmarks=nofmarks+1
+      marks[nofmarks]=n
+      marki[nofmarks]="injections"
+    else
+      nofglyphs=nofglyphs+1
+      glyphs[nofglyphs]=n
+      glyphi[nofglyphs]=what
+    end
+    if offsets then
       local p=rawget(properties,n)
       if p then
-        local i=rawget(p,"injections")
+        local i=rawget(p,what)
         if i then
           local yoffset=i.yoffset
           if yoffset and yoffset~=0 then
@@ -10421,36 +10561,47 @@ local function collect_glyphs_1(head)
       end
     end
   end
-  return glyphs,nofglyphs,marks,nofmarks
-end
-local function collect_glyphs_2(head)
-  local glyphs,nofglyphs={},0
-  local marks,nofmarks={},0
-  local nf,tm=nil,nil
-  for n in traverse_id(glyph_code,head) do
-    if getsubtype(n)<256 then
-      local f=getfont(n)
-      if f~=nf then
-        nf=f
-        tm=fontdata[nf].resources.marks 
-      end
-      if tm and tm[getchar(n)] then
-        nofmarks=nofmarks+1
-        marks[nofmarks]=n
-      else
-        nofglyphs=nofglyphs+1
-        glyphs[nofglyphs]=n
-      end
+  while n do 
+    local id=getid(n)
+    if id==glyph_code then
+      identify(n,"injections")
+    elseif id==disc_code then
+      local d=getfield(n,"pre")
+      if d then
+        for n in traverse_id(glyph_code,d) do
+          if getsubtype(n)<256 then
+            identify(n,"preinjections")
+          end
+        end
+			end
+      local d=getfield(n,"post")
+      if d then
+        for n in traverse_id(glyph_code,d) do
+          if getsubtype(n)<256 then
+            identify(n,"postinjections")
+          end
+        end
+			end
+      local d=getfield(n,"replace")
+      if d then
+        for n in traverse_id(glyph_code,d) do
+          if getsubtype(n)<256 then
+            identify(n,"replaceinjections")
+          end
+        end
+			end
     end
+		n=getnext(n)
   end
-  return glyphs,nofglyphs,marks,nofmarks
+  return glyphs,glyphi,nofglyphs,marks,marki,nofmarks
 end
-local function inject_marks(marks,nofmarks)
+local function inject_marks(marks,marki,nofmarks)
   for i=1,nofmarks do
     local n=marks[i]
     local pn=rawget(properties,n)
     if pn then
-      pn=rawget(pn,"injections")
+      local ni=marki[i]
+      local pn=rawget(pn,ni)
       if pn then
         local p=pn.markbasenode
         if p then
@@ -10459,7 +10610,7 @@ local function inject_marks(marks,nofmarks)
           local rightkern=nil
           local pp=rawget(properties,p)
           if pp then
-            pp=rawget(pp,"injections")
+            pp=rawget(pp,ni)
             if pp then
               rightkern=pp.rightkern
             end
@@ -10468,11 +10619,17 @@ local function inject_marks(marks,nofmarks)
             if pn.markdir<0 then
               ox=px-pn.markx-rightkern
             else
-              local leftkern=pp.leftkern
-              if leftkern then
-                ox=px-pn.markx
+							
+	
+							if false then
+                local leftkern=pp.leftkern
+                if leftkern then
+                  ox=px-pn.markx-leftkern
+                else
+                  ox=px-pn.markx
+                end
               else
-                ox=px-pn.markx-leftkern
+                ox=px-pn.markx
               end
             end
           else
@@ -10485,12 +10642,7 @@ local function inject_marks(marks,nofmarks)
           end
           setfield(n,"xoffset",ox)
           local py=getfield(p,"yoffset")
-          local oy=0
-          if marks[p] then
-            oy=py+pn.marky
-          else
-            oy=getfield(n,"yoffset")+py+pn.marky
-          end
+          local oy=getfield(n,"yoffset")+py+pn.marky
           setfield(n,"yoffset",oy)
         else
         end
@@ -10498,14 +10650,14 @@ local function inject_marks(marks,nofmarks)
     end
   end
 end
-local function inject_cursives(glyphs,nofglyphs)
+local function inject_cursives(glyphs,glyphi,nofglyphs)
   local cursiveanchor,lastanchor=nil,nil
   local minc,maxc,last=0,0,nil
   for i=1,nofglyphs do
     local n=glyphs[i]
     local pn=rawget(properties,n)
     if pn then
-      pn=rawget(pn,"injections")
+      pn=rawget(pn,glyphi[i])
     end
     if pn then
       local cursivex=pn.cursivex
@@ -10571,22 +10723,59 @@ local function inject_cursives(glyphs,nofglyphs)
     end
   end
 end
-local function inject_kerns(head,list,length)
+local function inject_kerns(head,glist,ilist,length) 
   for i=1,length do
-    local n=list[i]
+    local n=glist[i]
     local pn=rawget(properties,n)
     if pn then
-      local i=rawget(pn,"injections")
-      if i then
-        local leftkern=i.leftkern
-        if leftkern and leftkern~=0 then
-          insert_node_before(head,n,newkern(leftkern)) 
-        end
-        local rightkern=i.rightkern
-        if rightkern and rightkern~=0 then
-          insert_node_after(head,n,newkern(rightkern)) 
-        end
-      end
+			local dp=nil
+			local dr=nil
+      local ni=ilist[i]
+      local p=nil
+			if ni=="injections" then
+				p=getprev(n)
+				if p then
+					local id=getid(p)
+					if id==disc_code then
+						dp=getfield(p,"post")
+						dr=getfield(p,"replace")
+					end
+				end
+			end
+			if dp then
+				local i=rawget(pn,"postinjections")
+				if i then
+					local leftkern=i.leftkern
+					if leftkern and leftkern~=0 then
+						local t=find_tail(dp)
+						insert_node_after(dp,t,newkern(leftkern))
+            setfield(p,"post",dp) 
+					end
+				end
+			end
+			if dr then
+				local i=rawget(pn,"replaceinjections")
+				if i then
+					local leftkern=i.leftkern
+					if leftkern and leftkern~=0 then
+						local t=find_tail(dr)
+						insert_node_after(dr,t,newkern(leftkern))
+            setfield(p,"replace",dr) 
+					end
+				end
+			else
+				local i=rawget(pn,ni)
+				if i then
+					local leftkern=i.leftkern
+					if leftkern and leftkern~=0 then
+						insert_node_before(head,n,newkern(leftkern)) 
+					end
+					local rightkern=i.rightkern
+					if rightkern and rightkern~=0 then
+						insert_node_after(head,n,newkern(rightkern)) 
+					end
+				end
+			end
     end
   end
 end
@@ -10595,23 +10784,18 @@ local function inject_everything(head,where)
   if trace_injections then
     trace(head,"everything")
   end
-  local glyphs,nofglyphs,marks,nofmarks
-  if nofregisteredpairs>0 then
-    glyphs,nofglyphs,marks,nofmarks=collect_glyphs_1(head)
-  else
-    glyphs,nofglyphs,marks,nofmarks=collect_glyphs_2(head)
-  end
+  local glyphs,glyphi,nofglyphs,marks,marki,nofmarks=collect_glyphs(head,nofregisteredpairs>0)
   if nofglyphs>0 then
     if nofregisteredcursives>0 then
-      inject_cursives(glyphs,nofglyphs)
+      inject_cursives(glyphs,glyphi,nofglyphs)
     end
     if nofregisteredmarks>0 then 
-      inject_marks(marks,nofmarks)
+      inject_marks(marks,marki,nofmarks)
     end
-    inject_kerns(head,glyphs,nofglyphs)
+    inject_kerns(head,glyphs,glyphi,nofglyphs)
   end
   if nofmarks>0 then
-    inject_kerns(head,marks,nofmarks)
+    inject_kerns(head,marks,marki,nofmarks)
 	end
   if keepregisteredcounts then
     keepregisteredcounts=false
@@ -10629,7 +10813,7 @@ local function inject_kerns_only(head,where)
     trace(head,"kerns")
   end
   local n=head
-  local p=nil
+  local p=nil 
   while n do
     local id=getid(n)
     if id==glyph_code then
@@ -10645,6 +10829,7 @@ local function inject_kerns_only(head,where)
                 if leftkern and leftkern~=0 then
                   local t=find_tail(d)
                   insert_node_after(d,t,newkern(leftkern))
+                  setfield(p,"post",d) 
                 end
               end
             end
@@ -10656,6 +10841,7 @@ local function inject_kerns_only(head,where)
                 if leftkern and leftkern~=0 then
                   local t=find_tail(d)
                   insert_node_after(d,t,newkern(leftkern))
+                  setfield(p,"replace",d) 
                 end
               end
             else
@@ -10677,8 +10863,6 @@ local function inject_kerns_only(head,where)
             end
           end
         end
-      else
-        break
       end
       p=nil
     elseif id==disc_code then
@@ -10733,7 +10917,7 @@ local function inject_kerns_only(head,where)
         local h=d
         for n in traverse_id(glyph_code,d) do
           if getsubtype(n)<256 then
-            local pn=rawget(properties,n) 
+            local pn=rawget(properties,n)
             if pn then
               local i=rawget(pn,"replaceinjections")
               if i then
@@ -10770,7 +10954,7 @@ local function inject_pairs_only(head,where)
     trace(head,"pairs")
   end
   local n=head
-  local p=nil
+  local p=nil 
   while n do
     local id=getid(n)
     if id==glyph_code then
@@ -10786,6 +10970,7 @@ local function inject_pairs_only(head,where)
                 if leftkern and leftkern~=0 then
                   local t=find_tail(d)
                   insert_node_after(d,t,newkern(leftkern))
+                  setfield(p,"post",d) 
                 end
               end
             end
@@ -10797,6 +10982,7 @@ local function inject_pairs_only(head,where)
                 if leftkern and leftkern~=0 then
                   local t=find_tail(d)
                   insert_node_after(d,t,newkern(leftkern))
+                  setfield(p,"replace",d) 
                 end
               end
             else
@@ -10811,24 +10997,22 @@ local function inject_pairs_only(head,where)
           else
             local i=rawget(pn,"injections")
             if i then
-              local yoffset=i.yoffset
-              if yoffset and yoffset~=0 then
-                setfield(n,"yoffset",yoffset)
-              end
               local leftkern=i.leftkern
               if leftkern and leftkern~=0 then
-                insert_node_before(head,n,newkern(leftkern))
+                head=insert_node_before(head,n,newkern(leftkern))
               end
               local rightkern=i.rightkern
               if rightkern and rightkern~=0 then
                 insert_node_after(head,n,newkern(rightkern))
                 n=getnext(n) 
               end
+              local yoffset=i.yoffset
+              if yoffset and yoffset~=0 then
+                setfield(n,"yoffset",yoffset)
+              end
             end
           end
         end
-      else
-        break
       end
       p=nil
     elseif id==disc_code then
@@ -10837,16 +11021,12 @@ local function inject_pairs_only(head,where)
         local h=d
         for n in traverse_id(glyph_code,d) do
           if getsubtype(n)<256 then
-            local p=rawget(properties,n)
-            if p then
-              local i=rawget(p,"preinjections")
+            local pn=rawget(properties,n)
+            if pn then
+              local i=rawget(pn,"preinjections")
               if i then
-                local yoffset=i.yoffset
-                if yoffset and yoffset~=0 then
-                  setfield(n,"yoffset",yoffset)
-                end
                 local leftkern=i.leftkern
-                if leftkern~=0 then
+                if leftkern and leftkern~=0 then
                   h=insert_node_before(h,n,newkern(leftkern))
                 end
                 local rightkern=i.rightkern
@@ -10854,6 +11034,10 @@ local function inject_pairs_only(head,where)
                   insert_node_after(head,n,newkern(rightkern))
                   n=getnext(n) 
                 end
+                local yoffset=i.yoffset
+                if yoffset and yoffset~=0 then
+                  setfield(n,"yoffset",yoffset)
+                end
               end
             end
           else
@@ -10869,14 +11053,10 @@ local function inject_pairs_only(head,where)
         local h=d
         for n in traverse_id(glyph_code,d) do
           if getsubtype(n)<256 then
-            local p=rawget(properties,n)
-            if p then
-              local i=rawget(p,"postinjections")
+            local pn=rawget(properties,n)
+            if pn then
+              local i=rawget(pn,"postinjections")
               if i then
-                local yoffset=i.yoffset
-                if yoffset and yoffset~=0 then
-                  setfield(n,"yoffset",yoffset)
-                end
                 local leftkern=i.leftkern
                 if leftkern and leftkern~=0 then
                   h=insert_node_before(h,n,newkern(leftkern))
@@ -10886,6 +11066,10 @@ local function inject_pairs_only(head,where)
                   insert_node_after(head,n,newkern(rightkern))
                   n=getnext(n) 
                 end
+                local yoffset=i.yoffset
+                if yoffset and yoffset~=0 then
+                  setfield(n,"yoffset",yoffset)
+                end
               end
             end
           else
@@ -10901,14 +11085,10 @@ local function inject_pairs_only(head,where)
         local h=d
         for n in traverse_id(glyph_code,d) do
           if getsubtype(n)<256 then
-            local p=rawget(properties,n)
-            if p then
-              local i=rawget(p,"replaceinjections")
+            local pn=rawget(properties,n)
+            if pn then
+              local i=rawget(pn,"replaceinjections")
               if i then
-                local yoffset=i.yoffset
-                if yoffset and yoffset~=0 then
-                  setfield(n,"yoffset",yoffset)
-                end
                 local leftkern=i.leftkern
                 if leftkern and leftkern~=0 then
                   h=insert_node_before(h,n,newkern(leftkern))
@@ -10918,6 +11098,10 @@ local function inject_pairs_only(head,where)
                   insert_node_after(head,n,newkern(rightkern))
                   n=getnext(n) 
                 end
+                local yoffset=i.yoffset
+                if yoffset and yoffset~=0 then
+                  setfield(n,"yoffset",yoffset)
+                end
               end
             end
           else
@@ -10942,7 +11126,7 @@ local function inject_pairs_only(head,where)
   end
   return tonode(head),true
 end
-function injections.handler(head,where) 
+function injections.handler(head,where)
   if nofregisteredmarks>0 or nofregisteredcursives>0 then
     return inject_everything(head,where)
   elseif nofregisteredpairs>0 then
@@ -11342,14 +11526,12 @@ if not modules then modules={} end modules ['font-otn']={
   copyright="PRAGMA ADE / ConTeXt Development Team",
   license="see context related readme files",
 }
-local concat,insert,remove=table.concat,table.insert,table.remove
-local gmatch,gsub,find,match,lower,strip=string.gmatch,string.gsub,string.find,string.match,string.lower,string.strip
-local type,next,tonumber,tostring=type,next,tonumber,tostring
-local lpegmatch=lpeg.match
+local type,next,tonumber=type,next,tonumber
 local random=math.random
 local formatters=string.formatters
 local logs,trackers,nodes,attributes=logs,trackers,nodes,attributes
 local registertracker=trackers.register
+local registerdirective=directives.register
 local fonts=fonts
 local otf=fonts.handlers.otf
 local trace_lookups=false registertracker("otf.lookups",function(v) trace_lookups=v end)
@@ -11368,6 +11550,13 @@ local trace_applied=false registertracker("otf.applied",function(v) trace_applie
 local trace_steps=false registertracker("otf.steps",function(v) trace_steps=v end)
 local trace_skips=false registertracker("otf.skips",function(v) trace_skips=v end)
 local trace_directions=false registertracker("otf.directions",function(v) trace_directions=v end)
+local trace_kernruns=false registertracker("otf.kernruns",function(v) trace_kernruns=v end)
+local trace_discruns=false registertracker("otf.discruns",function(v) trace_discruns=v end)
+local trace_compruns=false registertracker("otf.compruns",function(v) trace_compruns=v end)
+local quit_on_no_replacement=true 
+local zwnjruns=true
+registerdirective("otf.zwnjruns",function(v) zwnjruns=v end)
+registerdirective("otf.chain.quitonnoreplacement",function(value) quit_on_no_replacement=value end)
 local report_direct=logs.reporter("fonts","otf direct")
 local report_subchain=logs.reporter("fonts","otf subchain")
 local report_chain=logs.reporter("fonts","otf chain")
@@ -11426,8 +11615,6 @@ local math_code=nodecodes.math
 local dir_code=whatcodes.dir
 local localpar_code=whatcodes.localpar
 local discretionary_code=disccodes.discretionary
-local regular_code=disccodes.regular
-local automatic_code=disccodes.automatic
 local ligature_code=glyphcodes.ligature
 local privateattribute=attributes.private
 local a_state=privateattribute('state')
@@ -11461,6 +11648,13 @@ local lookuptags=false
 local handlers={}
 local rlmode=0
 local featurevalue=false
+local sweephead={}
+local sweepnode=nil
+local sweepprev=nil
+local sweepnext=nil
+local notmatchpre={}
+local notmatchpost={}
+local notmatchreplace={}
 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
@@ -11530,6 +11724,65 @@ local function copy_glyph(g)
     return n
   end
 end
+local function flattendisk(head,disc)
+  local replace=getfield(disc,"replace")
+  setfield(disc,"replace",nil)
+  free_node(disc)
+  if head==disc then
+    local next=getnext(disc)
+    if replace then
+      if next then
+        local tail=find_node_tail(replace)
+        setfield(tail,"next",next)
+        setfield(next,"prev",tail)
+      end
+      return replace,replace
+    elseif next then
+      return next,next
+    else
+      return 
+    end
+  else
+    local next=getnext(disc)
+    local prev=getprev(disc)
+    if replace then
+      local tail=find_node_tail(replace)
+      if next then
+        setfield(tail,"next",next)
+        setfield(next,"prev",tail)
+      end
+      setfield(prev,"next",replace)
+      setfield(replace,"prev",prev)
+      return head,replace
+    else
+      if next then
+        setfield(next,"prev",prev)
+      end
+      setfield(prev,"next",next)
+      return head,next
+    end
+  end
+end
+local function appenddisc(disc,list)
+  local post=getfield(disc,"post")
+  local replace=getfield(disc,"replace")
+  local phead=list
+  local rhead=copy_node_list(list)
+  local ptail=find_node_tail(post)
+  local rtail=find_node_tail(replace)
+  if post then
+    setfield(ptail,"next",phead)
+    setfield(phead,"prev",ptail)
+  else
+    setfield(disc,"post",phead)
+  end
+  if replace then
+    setfield(rtail,"next",rhead)
+    setfield(rhead,"prev",rtail)
+  else
+    setfield(disc,"replace",rhead)
+  end
+end
 local function markstoligature(kind,lookupname,head,start,stop,char)
   if start==stop and getchar(start)==char then
     return head,start
@@ -11557,8 +11810,8 @@ local function markstoligature(kind,lookupname,head,start,stop,char)
     return head,base
   end
 end
-local function getcomponentindex(start)
-  if getid(start)~=glyph_code then
+local function getcomponentindex(start) 
+  if getid(start)~=glyph_code then 
     return 0
   elseif getsubtype(start)==ligature_code then
     local i=0
@@ -11574,14 +11827,22 @@ local function getcomponentindex(start)
     return 0
   end
 end
+local a_noligature=attributes.private("noligature")
 local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound) 
+  if getattr(start,a_noligature)==1 then
+    return head,start
+  end
   if start==stop and getchar(start)==char then
     resetinjection(start)
     setfield(start,"char",char)
     return head,start
   end
+  local components=getfield(start,"components")
+  if components then
+  end
   local prev=getprev(start)
   local next=getnext(stop)
+  local comp=start
   setfield(start,"prev",nil)
   setfield(stop,"next",nil)
   local base=copy_glyph(start)
@@ -11591,15 +11852,15 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun
   resetinjection(base)
   setfield(base,"char",char)
   setfield(base,"subtype",ligature_code)
-  setfield(base,"components",start) 
+  setfield(base,"components",comp) 
   if prev then
     setfield(prev,"next",base)
   end
   if next then
     setfield(next,"prev",base)
   end
-  setfield(base,"next",next)
   setfield(base,"prev",prev)
+  setfield(base,"next",next)
   if not discfound then
     local deletemarks=markflag~="mark"
     local components=start
@@ -11617,7 +11878,9 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun
         if trace_marks then
           logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),getligaindex(start))
         end
-        head,current=insert_node_after(head,current,copy_node(start)) 
+        local n=copy_node(start)
+        copyinjection(n,start)
+        head,current=insert_node_after(head,current,n) 
       elseif trace_marks then
         logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char))
       end
@@ -11636,16 +11899,75 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun
       end
       start=getnext(start)
     end
+  else
+    local discprev=getfield(discfound,"prev")
+    local discnext=getfield(discfound,"next")
+    if discprev and discnext then
+      local pre=getfield(discfound,"pre")
+      local post=getfield(discfound,"post")
+      local replace=getfield(discfound,"replace")
+      if not replace then 
+        local prev=getfield(base,"prev")
+        local copied=copy_node_list(comp)
+        setfield(discnext,"prev",nil) 
+        setfield(discprev,"next",nil) 
+        if pre then
+          setfield(discprev,"next",pre)
+          setfield(pre,"prev",discprev)
+        end
+        pre=comp
+        if post then
+          local tail=find_node_tail(post)
+          setfield(tail,"next",discnext)
+          setfield(discnext,"prev",tail)
+          setfield(post,"prev",nil)
+        else
+          post=discnext
+        end
+        setfield(prev,"next",discfound)
+        setfield(discfound,"prev",prev)
+        setfield(discfound,"next",next)
+        setfield(next,"prev",discfound)
+        setfield(base,"next",nil)
+        setfield(base,"prev",nil)
+        setfield(base,"components",copied)
+        setfield(discfound,"pre",pre)
+        setfield(discfound,"post",post)
+        setfield(discfound,"replace",base)
+        setfield(discfound,"subtype",discretionary_code)
+        base=prev 
+      end
+    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(getchar(start)),gref(replacement))
+local function multiple_glyphs(head,start,multiple,ignoremarks)
+  local nofmultiples=#multiple
+  if nofmultiples>0 then
+    resetinjection(start)
+    setfield(start,"char",multiple[1])
+    if nofmultiples>1 then
+      local sn=getnext(start)
+      for k=2,nofmultiples do
+        local n=copy_node(start) 
+        resetinjection(n)
+        setfield(n,"char",multiple[k])
+        setfield(n,"prev",start)
+        setfield(n,"next",sn)
+        if sn then
+          setfield(sn,"prev",n)
+        end
+        setfield(start,"next",n)
+        start=n
+      end
+    end
+    return head,start,true
+  else
+    if trace_multiples then
+      logprocess("no multiple for %s",gref(getchar(start)))
+    end
+    return head,start,false
   end
-  resetinjection(start)
-  setfield(start,"char",replacement)
-  return head,start,true
 end
 local function get_alternative_glyph(start,alternatives,value,trace_alternatives)
   local n=#alternatives
@@ -11678,33 +12000,13 @@ local function get_alternative_glyph(start,alternatives,value,trace_alternatives
     end
   end
 end
-local function multiple_glyphs(head,start,multiple,ignoremarks)
-  local nofmultiples=#multiple
-  if nofmultiples>0 then
-    resetinjection(start)
-    setfield(start,"char",multiple[1])
-    if nofmultiples>1 then
-      local sn=getnext(start)
-      for k=2,nofmultiples do
-        local n=copy_node(start) 
-        resetinjection(n)
-        setfield(n,"char",multiple[k])
-        setfield(n,"next",sn)
-        setfield(n,"prev",start)
-        if sn then
-          setfield(sn,"prev",n)
-        end
-        setfield(start,"next",n)
-        start=n
-      end
-    end
-    return head,start,true
-  else
-    if trace_multiples then
-      logprocess("no multiple for %s",gref(getchar(start)))
-    end
-    return head,start,false
+function handlers.gsub_single(head,start,kind,lookupname,replacement)
+  if trace_singles then
+    logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(getchar(start)),gref(replacement))
   end
+  resetinjection(start)
+  setfield(start,"char",replacement)
+  return head,start,true
 end
 function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence)
   local value=featurevalue==true and tfmdata.shared.features[kind] or featurevalue
@@ -11729,7 +12031,7 @@ function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence)
   return multiple_glyphs(head,start,multiple,sequence.flags[1])
 end
 function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
-  local s,stop,discfound=getnext(start),nil,false
+  local s,stop=getnext(start),nil
   local startchar=getchar(start)
   if marks[startchar] then
     while s do
@@ -11757,23 +12059,29 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
         else
           head,start=markstoligature(kind,lookupname,head,start,stop,lig)
         end
-        return head,start,true
+        return head,start,true,false
       else
       end
     end
   else
     local skipmark=sequence.flags[1]
+    local discfound=false
+    local lastdisc=nil
     while s do
       local id=getid(s)
-      if id==glyph_code and getsubtype(s)<256 then
-        if getfont(s)==currentfont then
+      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=getnext(s)
-          else
-            local lg=ligature[char]
+          else 
+            local lg=ligature[char] 
             if lg then
-              stop=s
+              if not discfound and lastdisc then
+                discfound=lastdisc
+                lastdisc=nil
+              end
+              stop=s 
               ligature=lg
               s=getnext(s)
             else
@@ -11784,13 +12092,13 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
           break
         end
       elseif id==disc_code then
-        discfound=true
+        lastdisc=s
         s=getnext(s)
       else
         break
       end
     end
-    local lig=ligature.ligature
+    local lig=ligature.ligature 
     if lig then
       if stop then
         if trace_ligatures then
@@ -11807,12 +12115,71 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)
           logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig))
         end
       end
-      return head,start,true
+      return head,start,true,discfound
     else
     end
   end
+  return head,start,false,discfound
+end
+function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence,injection)
+  local startchar=getchar(start)
+  local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,injection) 
+  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)
+  end
   return head,start,false
 end
+function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence,lookuphash,i,injection)
+  local snext=getnext(start)
+  if not snext then
+    return head,start,false
+  else
+    local prev=start
+    local done=false
+    local factor=tfmdata.parameters.factor
+    local lookuptype=lookuptypes[lookupname]
+    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=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 x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,injection) 
+              if trace_kerns then
+                local startchar=getchar(start)
+                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 x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,injection) 
+              if trace_kerns then
+                local startchar=getchar(start)
+                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)
+              end
+            end
+          else 
+            report_process("%s: check this out (old kern stuff)",pref(kind,lookupname))
+          end
+          done=true
+        elseif krn~=0 then
+          local k=setkern(snext,factor,rlmode,krn,injection)
+          if trace_kerns then
+            logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar)) 
+          end
+          done=true
+        end
+        break
+      end
+    end
+    return head,start,done
+  end
+end
 function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence)
   local markchar=getchar(start)
   if marks[markchar] then
@@ -11965,7 +12332,7 @@ function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence
               if al[anchor] then
                 local ma=markanchors[anchor]
                 if ma then
-                  local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar])
+                  local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar],true)
                   if trace_marks then
                     logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
                       pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
@@ -12043,65 +12410,6 @@ function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence)
     return head,start,false
   end
 end
-function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence)
-  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)
-  end
-  return head,start,false
-end
-function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence)
-  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 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=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=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=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)
-              end
-            end
-          else 
-            report_process("%s: check this out (old kern stuff)",pref(kind,lookupname))
-          end
-          done=true
-        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(getchar(prev)),gref(nextchar))
-          end
-          done=true
-        end
-        break
-      end
-    end
-    return head,start,done
-  end
-end
-local chainmores={}
 local chainprocs={}
 local function logprocess(...)
   if trace_steps then
@@ -12121,10 +12429,6 @@ function chainprocs.chainsub(head,start,stop,kind,chainname,currentcontext,looku
   logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname))
   return head,start,false
 end
-function chainmores.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n)
-  logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname))
-  return head,start,false
-end
 function chainprocs.reversesub(head,start,stop,kind,chainname,currentcontext,lookuphash,replacements)
   local char=getchar(start)
   local replacement=replacements[char]
@@ -12143,7 +12447,7 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo
   local current=start
   local subtables=currentlookup.subtables
   if #subtables>1 then
-    logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," "))
+    logwarning("todo: check if we need to loop over the replacements: % t",subtables)
   end
   while current do
     if getid(current)==glyph_code then
@@ -12177,7 +12481,6 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo
   end
   return head,start,false
 end
-chainmores.gsub_single=chainprocs.gsub_single
 function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
   local startchar=getchar(start)
   local subtables=currentlookup.subtables
@@ -12202,7 +12505,6 @@ function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,
   end
   return head,start,false
 end
-chainmores.gsub_multiple=chainprocs.gsub_multiple
 function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
   local current=start
   local subtables=currentlookup.subtables
@@ -12244,7 +12546,6 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext
   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=getchar(start)
   local subtables=currentlookup.subtables
@@ -12264,13 +12565,19 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
       local s=getnext(start)
       local discfound=false
       local last=stop
-      local nofreplacements=0
+      local nofreplacements=1
       local skipmark=currentlookup.flags[1]
       while s do
         local id=getid(s)
         if id==disc_code then
-          s=getnext(s)
-          discfound=true
+          if not discfound then
+            discfound=s
+          end
+          if s==stop then
+            break 
+          else
+            s=getnext(s)
+          end
         else
           local schar=getchar(s)
           if skipmark and marks[schar] then 
@@ -12303,7 +12610,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
           end
         end
         head,start=toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound)
-        return head,start,true,nofreplacements
+        return head,start,true,nofreplacements,discfound
       elseif trace_bugs then
         if start==stop then
           logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))
@@ -12313,10 +12620,83 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,
       end
     end
   end
-  return head,start,false,0
+  return head,start,false,0,false
 end
-chainmores.gsub_ligature=chainprocs.gsub_ligature
-function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
+function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
+  local startchar=getchar(start)
+  local subtables=currentlookup.subtables
+  local lookupname=subtables[1]
+  local kerns=lookuphash[lookupname]
+  if kerns then
+    kerns=kerns[startchar] 
+    if kerns then
+      local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns) 
+      if trace_kerns then
+        logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h)
+      end
+    end
+  end
+  return head,start,false
+end
+function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
+  local snext=getnext(start)
+  if snext then
+    local startchar=getchar(start)
+    local subtables=currentlookup.subtables
+    local lookupname=subtables[1]
+    local kerns=lookuphash[lookupname]
+    if kerns then
+      kerns=kerns[startchar]
+      if kerns then
+        local lookuptype=lookuptypes[lookupname]
+        local prev,done=start,false
+        local factor=tfmdata.parameters.factor
+        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=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=getchar(start)
+                  local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a) 
+                  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=getchar(start)
+                  local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b) 
+                  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)
+                  end
+                end
+              else
+                report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname))
+              end
+              done=true
+            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(getchar(prev)),gref(nextchar))
+              end
+              done=true
+            end
+            break
+          end
+        end
+        return head,start,done
+      end
+    end
+  end
+  return head,start,false
+end
+function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)
   local markchar=getchar(start)
   if marks[markchar] then
     local subtables=currentlookup.subtables
@@ -12479,7 +12859,7 @@ function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext
               if al[anchor] then
                 local ma=markanchors[anchor]
                 if ma then
-                  local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar])
+                  local dx,dy,bound=setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,characters[basechar],true)
                   if trace_marks then
                     logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
                       cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy)
@@ -12566,113 +12946,286 @@ function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,l
   end
   return head,start,false
 end
-function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
-  local startchar=getchar(start)
-  local subtables=currentlookup.subtables
-  local lookupname=subtables[1]
-  local kerns=lookuphash[lookupname]
-  if kerns then
-    kerns=kerns[startchar] 
-    if kerns then
-      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)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h)
+local function show_skip(kind,chainname,char,ck,class)
+  if ck[9] then
+    logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10])
+  else
+    logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2])
+  end
+end
+local function chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,chainindex,sequence,chainproc)
+  if not start then
+    return head,start,false
+  end
+  local startishead=start==head
+  local seq=ck[3]
+  local f=ck[4]
+  local l=ck[5]
+  local s=#seq
+  local done=false
+  local sweepnode=sweepnode
+  local sweeptype=sweeptype
+  local sweepoverflow=false
+  local checkdisc=getprev(head) 
+  local keepdisc=not sweepnode
+  local lookaheaddisc=nil
+  local backtrackdisc=nil
+  local current=start
+  local last=start
+  local prev=getprev(start)
+  local i=f
+  while i<=l do
+    local id=getid(current)
+    if id==glyph_code then
+      i=i+1
+      last=current
+      current=getnext(current)
+    elseif id==disc_code then
+      if keepdisc then
+        keepdisc=false
+        if notmatchpre[current]~=notmatchreplace[current] then
+          lookaheaddisc=current
+        end
+        local replace=getfield(current,"replace")
+        while replace and i<=l do
+          if getid(replace)==glyph_code then
+            i=i+1
+          end
+          replace=getnext(replace)
+        end
+        last=current
+        current=getnext(c)
+      else
+        head,current=flattendisk(head,current)
+      end
+    else
+      last=current
+      current=getnext(current)
+    end
+    if current then
+    elseif sweepoverflow then
+      break
+    elseif sweeptype=="post" or sweeptype=="replace" then
+      current=getnext(sweepnode)
+      if current then
+        sweeptype=nil
+        sweepoverflow=true
+      else
+        break
       end
     end
   end
-  return head,start,false
-end
-chainmores.gpos_single=chainprocs.gpos_single
-function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)
-  local snext=getnext(start)
-  if snext then
-    local startchar=getchar(start)
-    local subtables=currentlookup.subtables
-    local lookupname=subtables[1]
-    local kerns=lookuphash[lookupname]
-    if kerns then
-      kerns=kerns[startchar]
-      if kerns then
-        local lookuptype=lookuptypes[lookupname]
-        local prev,done=start,false
-        local factor=tfmdata.parameters.factor
-        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=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=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=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)
-                  end
-                end
-              else
-                report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname))
-                local a,b=krn[2],krn[6]
-                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(getchar(prev)),gref(nextchar))
-                  end
-                end
-                if b and b~=0 then
-                  logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor)
-                end
-              end
-              done=true
-            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(getchar(prev)),gref(nextchar))
-              end
-              done=true
+  if sweepoverflow then
+    local prev=current and getprev(current)
+    if not current or prev~=sweepnode then
+      local head=getnext(sweepnode)
+      local tail=nil
+      if prev then
+        tail=prev
+        setfield(current,"prev",sweepnode)
+      else
+        tail=find_node_tail(head)
+      end
+      setfield(sweepnode,"next",current)
+      setfield(head,"prev",nil)
+      setfield(tail,"next",nil)
+      appenddisc(sweepnode,head)
+    end
+  end
+  if l<s then
+    local i=l
+    local t=sweeptype=="post" or sweeptype=="replace"
+    while current and i<s do
+      local id=getid(current)
+      if id==glyph_code then
+        i=i+1
+        current=getnext(current)
+      elseif id==disc_code then
+        if keepdisc then
+          keepdisc=false
+          if notmatchpre[current]~=notmatchreplace[current] then
+            lookaheaddisc=current
+          end
+          local replace=getfield(c,"replace")
+          while replace and i<s do
+            if getid(replace)==glyph_code then
+              i=i+1
             end
-            break
+            replace=getnext(replace)
           end
+          current=getnext(current)
+        elseif notmatchpre[current]~=notmatchreplace[current] then
+          head,current=flattendisk(head,current)
+        else
+          current=getnext(current) 
+        end
+      else
+        current=getnext(current)
+      end
+      if not current and t then
+        current=getnext(sweepnode)
+        if current then
+          sweeptype=nil
         end
-        return head,start,done
       end
     end
   end
-  return head,start,false
-end
-chainmores.gpos_pair=chainprocs.gpos_pair
-local function show_skip(kind,chainname,char,ck,class)
-  if ck[9] then
-    logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10])
+  if f>1 then
+    local current=prev
+    local i=f
+    local t=sweeptype=="pre" or sweeptype=="replace"
+    if not current and t and current==checkdisk then
+      current=getprev(sweepnode)
+    end
+    while current and i>1 do 
+      local id=getid(current)
+      if id==glyph_code then
+        i=i-1
+      elseif id==disc_code then
+        if keepdisc then
+          keepdisc=false
+          if notmatchpost[current]~=notmatchreplace[current] then
+            backtrackdisc=current
+          end
+          local replace=getfield(current,"replace")
+          while replace and i>1 do
+            if getid(replace)==glyph_code then
+              i=i-1
+            end
+            replace=getnext(replace)
+          end
+        elseif notmatchpost[current]~=notmatchreplace[current] then
+          head,current=flattendisk(head,current)
+        end
+      end
+      current=getprev(current)
+      if t and current==checkdisk then
+        current=getprev(sweepnode)
+      end
+    end
+  end
+  local ok=false
+  if lookaheaddisc then
+    local cf=start
+    local cl=getprev(lookaheaddisc)
+    local cprev=getprev(start)
+    local insertedmarks=0
+    while cprev and getid(cf)==glyph_code and getfont(cf)==currentfont and getsubtype(cf)<256 and marks[getchar(cf)] do
+      insertedmarks=insertedmarks+1
+      cf=cprev
+      startishead=cf==head
+      cprev=getprev(cprev)
+    end
+    setfield(lookaheaddisc,"prev",cprev)
+    if cprev then
+      setfield(cprev,"next",lookaheaddisc)
+    end
+    setfield(cf,"prev",nil)
+    setfield(cl,"next",nil)
+    if startishead then
+      head=lookaheaddisc
+    end
+    local replace=getfield(lookaheaddisc,"replace")
+    local pre=getfield(lookaheaddisc,"pre")
+    local new=copy_node_list(cf)
+    local cnew=new
+    for i=1,insertedmarks do
+      cnew=getnext(cnew)
+    end
+    local clast=cnew
+    for i=f,l do
+      clast=getnext(clast)
+    end
+    if not notmatchpre[lookaheaddisc] then
+      cf,start,ok=chainproc(cf,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+    end
+    if not notmatchreplace[lookaheaddisc] then
+      new,cnew,ok=chainproc(new,cnew,clast,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+    end
+    if pre then
+      setfield(cl,"next",pre)
+      setfield(pre,"prev",cl)
+    end
+    if replace then
+      local tail=find_node_tail(new)
+      setfield(tail,"next",replace)
+      setfield(replace,"prev",tail)
+    end
+    setfield(lookaheaddisc,"pre",cf)   
+    setfield(lookaheaddisc,"replace",new) 
+    start=getprev(lookaheaddisc)
+    sweephead[cf]=getnext(clast)
+    sweephead[new]=getnext(last)
+  elseif backtrackdisc then
+    local cf=getnext(backtrackdisc)
+    local cl=start
+    local cnext=getnext(start)
+    local insertedmarks=0
+    while cnext and getid(cnext)==glyph_code and getfont(cnext)==currentfont and getsubtype(cnext)<256 and marks[getchar(cnext)] do
+      insertedmarks=insertedmarks+1
+      cl=cnext
+      cnext=getnext(cnext)
+    end
+    if cnext then
+      setfield(cnext,"prev",backtrackdisc)
+    end
+    setfield(backtrackdisc,"next",cnext)
+    setfield(cf,"prev",nil)
+    setfield(cl,"next",nil)
+    local replace=getfield(backtrackdisc,"replace")
+    local post=getfield(backtrackdisc,"post")
+    local new=copy_node_list(cf)
+    local cnew=find_node_tail(new)
+    for i=1,insertedmarks do
+      cnew=getprev(cnew)
+    end
+    local clast=cnew
+    for i=f,l do
+      clast=getnext(clast)
+    end
+    if not notmatchpost[backtrackdisc] then
+      cf,start,ok=chainproc(cf,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+    end
+    if not notmatchreplace[backtrackdisc] then
+      new,cnew,ok=chainproc(new,cnew,clast,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+    end
+    if post then
+      local tail=find_node_tail(post)
+      setfield(tail,"next",cf)
+      setfield(cf,"prev",tail)
+    else
+      post=cf
+    end
+    if replace then
+      local tail=find_node_tail(replace)
+      setfield(tail,"next",new)
+      setfield(new,"prev",tail)
+    else
+      replace=new
+    end
+    setfield(backtrackdisc,"post",post)    
+    setfield(backtrackdisc,"replace",replace) 
+    start=getprev(backtrackdisc)
+    sweephead[post]=getnext(clast)
+    sweephead[replace]=getnext(last)
   else
-    logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2])
+    head,start,ok=chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
   end
+  return head,start,ok
 end
-local quit_on_no_replacement=true
-directives.register("otf.chain.quitonnoreplacement",function(value) 
-  quit_on_no_replacement=value
-end)
 local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash)
+  local sweepnode=sweepnode
+  local sweeptype=sweeptype
+  local diskseen=false
+  local checkdisc=getprev(head)
   local flags=sequence.flags
   local done=false
   local skipmark=flags[1]
   local skipligature=flags[2]
   local skipbase=flags[3]
-  local someskip=skipmark or skipligature or skipbase 
-  local markclass=sequence.markclass          
+  local markclass=sequence.markclass
   local skipped=false
-  for k=1,#contexts do
+  for k=1,#contexts do 
     local match=true
     local current=start
     local last=start
@@ -12682,14 +13235,20 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
     if s==1 then
       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]
+      local f=ck[4]
+      local l=ck[5]
       if f==1 and f==l then
       else
         if f==l then
         else
+          local discfound=nil
           local n=f+1
           last=getnext(last)
           while n<=l do
+            if not last and (sweeptype=="post" or sweeptype=="replace") then
+              last=getnext(sweepnode)
+              sweeptype=nil
+            end
             if last then
               local id=getid(last)
               if id==glyph_code then
@@ -12697,7 +13256,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
                   local char=getchar(last)
                   local ccd=descriptions[char]
                   if ccd then
-                    local class=ccd.class
+                    local class=ccd.class or "base"
                     if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
                       skipped=true
                       if trace_skips then
@@ -12710,18 +13269,76 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
                       end
                       n=n+1
                     else
-                      match=false
+                      if discfound then
+                        notmatchreplace[discfound]=true
+                        match=not notmatchpre[discfound]
+                      else
+                        match=false
+                      end
                       break
                     end
                   else
-                    match=false
+                    if discfound then
+                      notmatchreplace[discfound]=true
+                      match=not notmatchpre[discfound]
+                    else
+                      match=false
+                    end
                     break
                   end
                 else
-                  match=false
+                  if discfound then
+                    notmatchreplace[discfound]=true
+                    match=not notmatchpre[discfound]
+                  else
+                    match=false
+                  end
                   break
                 end
               elseif id==disc_code then
+                diskseen=true
+                discfound=last
+                notmatchpre[last]=nil
+                notmatchpost[last]=true
+                notmatchreplace[last]=nil
+                local pre=getfield(last,"pre")
+                local replace=getfield(last,"replace")
+                if pre then
+                  local n=n
+                  while pre do
+                    if seq[n][getchar(pre)] then
+                      n=n+1
+                      pre=getnext(pre)
+                      if n>l then
+                        break
+                      end
+                    else
+                      notmatchpre[last]=true
+                      break
+                    end
+                  end
+                  if n<=l then
+                    notmatchpre[last]=true
+                  end
+                else
+                  notmatchpre[last]=true
+                end
+                if replace then
+                  while replace do
+                    if seq[n][getchar(replace)] then
+                      n=n+1
+                      replace=getnext(replace)
+                      if n>l then
+                        break
+                      end
+                    else
+                      notmatchreplace[last]=true
+                      match=not notmatchpre[last]
+                      break
+                    end
+                  end
+                  match=not notmatchpre[last]
+                end
                 last=getnext(last)
               else
                 match=false
@@ -12737,49 +13354,132 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
       if match and f>1 then
         local prev=getprev(start)
         if prev then
-          local n=f-1
-          while n>=1 do
-            if prev then
-              local id=getid(prev)
-              if id==glyph_code then
-                if getfont(prev)==currentfont and getsubtype(prev)<256 then 
-                  local char=getchar(prev)
-                  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
-                      skipped=true
-                      if trace_skips then
-                        show_skip(kind,chainname,char,ck,class)
+          if prev==checkdisc and (sweeptype=="pre" or sweeptype=="replace") then
+            prev=getprev(sweepnode)
+          end
+          if prev then
+            local discfound=nil
+            local n=f-1
+            while n>=1 do
+              if prev then
+                local id=getid(prev)
+                if id==glyph_code then
+                  if getfont(prev)==currentfont and getsubtype(prev)<256 then 
+                    local char=getchar(prev)
+                    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
+                        skipped=true
+                        if trace_skips then
+                          show_skip(kind,chainname,char,ck,class)
+                        end
+                      elseif seq[n][char] then
+                        n=n -1
+                      else
+                        if discfound then
+                          notmatchreplace[discfound]=true
+                          match=not notmatchpost[discfound]
+                        else
+                          match=false
+                        end
+                        break
                       end
-                    elseif seq[n][char] then
-                      n=n -1
                     else
-                      match=false
+                      if discfound then
+                        notmatchreplace[discfound]=true
+                        match=not notmatchpost[discfound]
+                      else
+                        match=false
+                      end
                       break
                     end
                   else
-                    match=false
+                    if discfound then
+                      notmatchreplace[discfound]=true
+                      match=not notmatchpost[discfound]
+                    else
+                      match=false
+                    end
                     break
                   end
+                elseif id==disc_code then
+                  diskseen=true
+                  discfound=prev
+                  notmatchpre[prev]=true
+                  notmatchpost[prev]=nil
+                  notmatchreplace[prev]=nil
+                  local pre=getfield(prev,"pre")
+                  local post=getfield(prev,"post")
+                  local replace=getfield(prev,"replace")
+                  if pre~=start and post~=start and replace~=start then
+                    if post then
+                      local n=n
+                      local posttail=find_node_tail(post)
+                      while posttail do
+                        if seq[n][getchar(posttail)] then
+                          n=n-1
+                          if posttail==post then
+                            break
+                          else
+                            posttail=getprev(posttail)
+                            if n<1 then
+                              break
+                            end
+                          end
+                        else
+                          notmatchpost[prev]=true
+                          break
+                        end
+                      end
+                      if n>=1 then
+                        notmatchpost[prev]=true
+                      end
+                    else
+                      notmatchpost[prev]=true
+                    end
+                    if replace then
+                      local replacetail=find_node_tail(replace)
+                      while replacetail do
+                        if seq[n][getchar(replacetail)] then
+                          n=n-1
+                          if replacetail==replace then
+                            break
+                          else
+                            replacetail=getprev(replacetail)
+                            if n<1 then
+                              break
+                            end
+                          end
+                        else
+                          notmatchreplace[prev]=true
+                          match=not notmatchpost[prev]
+                          break
+                        end
+                      end
+                      if not match then
+                        break
+                      end
+                    else
+                    end
+                  else
+                  end
+                elseif seq[n][32] then
+                  n=n -1
                 else
                   match=false
                   break
                 end
-              elseif id==disc_code then
-              elseif seq[n][32] then
-                n=n -1
+                prev=getprev(prev)
+              elseif seq[n][32] then 
+                n=n-1
               else
                 match=false
                 break
               end
-              prev=getprev(prev)
-            elseif seq[n][32] then 
-              n=n -1
-            else
-              match=false
-              break
             end
+          else
+            match=false
           end
         else
           match=false
@@ -12787,7 +13487,13 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
       end
       if match and s>l then
         local current=last and getnext(last)
+        if not current then
+          if sweeptype=="post" or sweeptype=="replace" then
+            current=getnext(sweepnode)
+          end
+        end
         if current then
+          local discfound=nil
           local n=l+1
           while n<=s do
             if current then
@@ -12803,21 +13509,82 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
                       if trace_skips then
                         show_skip(kind,chainname,char,ck,class)
                       end
-                    elseif seq[n][char] then
+                    elseif seq[n][char] then
+                      n=n+1
+                    else
+                      if discfound then
+                        notmatchreplace[discfound]=true
+                        match=not notmatchpre[discfound]
+                      else
+                        match=false
+                      end
+                      break
+                    end
+                  else
+                    if discfound then
+                      notmatchreplace[discfound]=true
+                      match=not notmatchpre[discfound]
+                    else
+                      match=false
+                    end
+                    break
+                  end
+                else
+                  if discfound then
+                    notmatchreplace[discfound]=true
+                    match=not notmatchpre[discfound]
+                  else
+                    match=false
+                  end
+                  break
+                end
+              elseif id==disc_code then
+                diskseen=true
+                discfound=current
+                notmatchpre[current]=nil
+                notmatchpost[current]=true
+                notmatchreplace[current]=nil
+                local pre=getfield(current,"pre")
+                local replace=getfield(current,"replace")
+                if pre then
+                  local n=n
+                  while pre do
+                    if seq[n][getchar(pre)] then
+                      n=n+1
+                      pre=getnext(pre)
+                      if n>s then
+                        break
+                      end
+                    else
+                      notmatchpre[current]=true
+                      break
+                    end
+                  end
+                  if n<=s then
+                    notmatchpre[current]=true
+                  end
+                else
+                  notmatchpre[current]=true
+                end
+                if replace then
+                  while replace do
+                    if seq[n][getchar(replace)] then
                       n=n+1
+                      replace=getnext(replace)
+                      if n>s then
+                        break
+                      end
                     else
-                      match=false
+                      notmatchreplace[current]=true
+                      match=notmatchpre[current]
                       break
                     end
-                  else
-                    match=false
+                  end
+                  if not match then
                     break
                   end
                 else
-                  match=false
-                  break
                 end
-              elseif id==disc_code then
               elseif seq[n][32] then 
                 n=n+1
               else
@@ -12838,6 +13605,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
       end
     end
     if match then
+      local diskchain=diskseen or sweepnode
       if trace_contexts then
         local rule,lookuptype,f,l=ck[1],ck[2],ck[4],ck[5]
         local char=getchar(start)
@@ -12856,10 +13624,14 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
           local chainlookupname=chainlookups[1]
           local chainlookup=lookuptable[chainlookupname]
           if chainlookup then
-            local cp=chainprocs[chainlookup.type]
-            if cp then
+            local chainproc=chainprocs[chainlookup.type]
+            if chainproc then
               local ok
-              head,start,ok=cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+              if diskchain then
+                head,start,ok=chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence,chainproc)
+              else
+                head,start,ok=chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence)
+              end
               if ok then
                 done=true
               end
@@ -12871,13 +13643,13 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
           end
          else
           local i=1
-          while true do
+          while start and true do
             if skipped then
-              while true do
+              while true do 
                 local char=getchar(start)
                 local ccd=descriptions[char]
                 if ccd then
-                  local class=ccd.class
+                  local class=ccd.class or "base"
                   if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then
                     start=getnext(start)
                   else
@@ -12893,26 +13665,33 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
             if not chainlookup then
               i=i+1
             else
-              local cp=chainmores[chainlookup.type]
-              if not cp then
+              local chainproc=chainprocs[chainlookup.type]
+              if not chainproc then
                 logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type)
                 i=i+1
               else
                 local ok,n
-                head,start,ok,n=cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence)
+                if diskchain then
+                  head,start,ok=chaindisk(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence,chainproc)
+                else
+                  head,start,ok,n=chainproc(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence)
+                end
                 if ok then
                   done=true
-                  i=i+(n or 1)
-                else
-                  i=i+1
+                  if n and n>1 then
+                    if i+n>nofchainlookups then
+                      break
+                    else
+                    end
+                  end
                 end
+                i=i+1
               end
             end
-            if i>nofchainlookups then
+            if i>nofchainlookups or not start then
               break
             elseif start then
               start=getnext(start)
-            else
             end
           end
         end
@@ -12927,8 +13706,16 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq
           end
         end
       end
+      if done then
+        break 
+      end
     end
   end
+  if diskseen then 
+    notmatchpre={}
+    notmatchpost={}
+    notmatchreplace={}
+  end
   return head,start,done
 end
 local verbose_handle_contextchain=function(font,...)
@@ -12999,7 +13786,7 @@ local function initialize(sequence,script,language,enabled)
           local scripts=features[kind] 
           local languages=scripts[script] or scripts[wildcard]
           if languages and (languages[language] or languages[wildcard]) then
-            return { valid,autofeatures[kind] or false,sequence.chain or 0,kind,sequence }
+            return { valid,autofeatures[kind] or false,sequence,kind }
           end
         end
       end
@@ -13039,6 +13826,175 @@ function otf.dataset(tfmdata,font)
   end
   return rl
 end
+local function kernrun(disc,run)
+  if trace_kernruns then
+    report_run("kern") 
+  end
+  local prev=getprev(disc) 
+  local next=getnext(disc)
+  local pre=getfield(disc,"pre")
+  local post=getfield(disc,"post")
+  local replace=getfield(disc,"replace")
+  local prevmarks=prev
+  while prevmarks and getid(prevmarks)==glyph_code and marks[getchar(prevmarks)] and getfont(prevmarks)==currentfont and getsubtype(prevmarks)<256 do
+    prevmarks=getprev(prevmarks)
+  end
+  if prev and (pre or replace) and not (getid(prev)==glyph_code and getfont(prev)==currentfont and getsubtype(prev)<256) then
+    prev=false
+  end
+  if next and (post or replace) and not (getid(next)==glyph_code and getfont(next)==currentfont and getsubtype(next)<256) then
+    next=false
+  end
+  if not pre then
+  elseif prev then
+    local nest=getprev(pre)
+    setfield(pre,"prev",prev)
+    setfield(prev,"next",pre)
+    run(prevmarks,"preinjections")
+    setfield(pre,"prev",nest)
+    setfield(prev,"next",disc)
+  else
+    run(pre,"preinjections")
+  end
+  if not post then
+  elseif next then
+    local tail=find_node_tail(post)
+    setfield(tail,"next",next)
+    setfield(next,"prev",tail)
+    run(post,"postinjections",next)
+    setfield(tail,"next",nil)
+    setfield(next,"prev",disc)
+  else
+    run(post,"postinjections")
+  end
+  if not replace and prev and next then
+    setfield(prev,"next",next)
+    setfield(next,"prev",prev)
+    run(prevmarks,"injections",next)
+    setfield(prev,"next",disc)
+    setfield(next,"prev",disc)
+  elseif prev and next then
+    local tail=find_node_tail(replace)
+    local nest=getprev(replace)
+    setfield(replace,"prev",prev)
+    setfield(prev,"next",replace)
+    setfield(tail,"next",next)
+    setfield(next,"prev",tail)
+    run(prevmarks,"replaceinjections",next)
+    setfield(replace,"prev",nest)
+    setfield(prev,"next",disc)
+    setfield(tail,"next",nil)
+    setfield(next,"prev",disc)
+  elseif prev then
+    local nest=getprev(replace)
+    setfield(replace,"prev",prev)
+    setfield(prev,"next",replace)
+    run(prevmarks,"replaceinjections")
+    setfield(replace,"prev",nest)
+    setfield(prev,"next",disc)
+  elseif next then
+    local tail=find_node_tail(replace)
+    setfield(tail,"next",next)
+    setfield(next,"prev",tail)
+    run(replace,"replaceinjections",next)
+    setfield(tail,"next",nil)
+    setfield(next,"prev",disc)
+  else
+    run(replace,"replaceinjections")
+  end
+end
+local function comprun(disc,run)
+  if trace_compruns then
+    report_run("comp: %s",languages.serializediscretionary(disc))
+  end
+  local pre=getfield(disc,"pre")
+  if pre then
+    sweepnode=disc
+    sweeptype="pre" 
+    local new,done=run(pre)
+    if done then
+      setfield(disc,"pre",new)
+    end
+  end
+  local post=getfield(disc,"post")
+  if post then
+    sweepnode=disc
+    sweeptype="post"
+    local new,done=run(post)
+    if done then
+      setfield(disc,"post",new)
+    end
+  end
+  local replace=getfield(disc,"replace")
+  if replace then
+    sweepnode=disc
+    sweeptype="replace"
+    local new,done=run(replace)
+    if done then
+      setfield(disc,"replace",new)
+    end
+  end
+  sweepnode=nil
+  sweeptype=nil
+end
+local function testrun(disc,trun,crun) 
+  local next=getnext(disc)
+  if next then
+    local replace=getfield(disc,"replace")
+    if replace then
+      local prev=getprev(disc)
+      if prev then
+        local tail=find_node_tail(replace)
+        setfield(tail,"next",next)
+        setfield(next,"prev",tail)
+        if trun(replace,next) then
+          setfield(disc,"replace",nil) 
+          setfield(prev,"next",replace)
+          setfield(replace,"prev",prev)
+          setfield(next,"prev",tail)
+          setfield(tail,"next",next)
+          setfield(disc,"prev",nil)
+          setfield(disc,"next",nil)
+          flush_node_list(disc)
+          return replace 
+        else
+          setfield(tail,"next",nil)
+          setfield(next,"prev",disc)
+        end
+      else
+      end
+    else
+    end
+  else
+  end
+  comprun(disc,crun)
+  return next
+end
+local function discrun(disc,drun,krun)
+  local next=getnext(disc)
+  local prev=getprev(disc)
+  if trace_discruns then
+    report_run("disc") 
+  end
+  if next and prev then
+    setfield(prev,"next",next)
+    drun(prev)
+    setfield(prev,"next",disc)
+  end
+  local pre=getfield(disc,"pre")
+  if not pre then
+  elseif prev then
+    local nest=getprev(pre)
+    setfield(pre,"prev",prev)
+    setfield(prev,"next",pre)
+    krun(prev,"preinjections")
+    setfield(pre,"prev",nest)
+    setfield(prev,"next",disc)
+  else
+    krun(pre,"preinjections")
+  end
+  return next
+end
 local function featuresprocessor(head,font,attr)
   local lookuphash=lookuphashes[font] 
   if not lookuphash then
@@ -13059,23 +14015,25 @@ local function featuresprocessor(head,font,attr)
   lookuptags=resources.lookuptags
   currentfont=font
   rlmode=0
+  sweephead={}
   local sequences=resources.sequences
   local done=false
   local datasets=otf.dataset(tfmdata,font,attr)
   local dirstack={}
   for s=1,#datasets do
     local dataset=datasets[s]
-    featurevalue=dataset[1] 
-    local sequence=dataset[5] 
+       featurevalue=dataset[1] 
+    local attribute=dataset[2]
+    local sequence=dataset[3] 
+    local kind=dataset[4]
     local rlparmode=0
     local topstack=0
     local success=false
-    local attribute=dataset[2]
-    local chain=dataset[3] 
     local typ=sequence.type
+    local gpossing=typ=="gpos_single" or typ=="gpos_pair" 
     local subtables=sequence.subtables
-    if chain<0 then
-      local handler=handlers[typ]
+    local handler=handlers[typ]
+    if typ=="gsub_reversecontextchain" then
       local start=find_node_tail(head) 
       while start do
         local id=getid(start)
@@ -13088,13 +14046,14 @@ local function featuresprocessor(head,font,attr)
               a=true
             end
             if a then
+              local char=getchar(start)
               for i=1,#subtables do
                 local lookupname=subtables[i]
                 local lookupcache=lookuphash[lookupname]
                 if lookupcache then
-                  local lookupmatch=lookupcache[getchar(start)]
+                  local lookupmatch=lookupcache[char]
                   if lookupmatch then
-                    head,start,success=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
+                    head,start,success=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)
                     if success then
                       break
                     end
@@ -13115,7 +14074,6 @@ local function featuresprocessor(head,font,attr)
         end
       end
     else
-      local handler=handlers[typ]
       local ns=#subtables
       local start=head 
       rlmode=0 
@@ -13125,12 +14083,19 @@ local function featuresprocessor(head,font,attr)
         if not lookupcache then 
           report_missing_cache(typ,lookupname)
         else
-          local function subrun(start)
-            local head=start
+          local function c_run(head) 
             local done=false
+            local start=sweephead[head]
+            if start then
+              sweephead[head]=nil
+            else
+              start=head
+            end
             while start do
               local id=getid(start)
-              if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then
+              if id~=glyph_code then
+                start=getnext(start)
+              elseif getfont(start)==font and getsubtype(start)<256 then
                 local a=getattr(start,0)
                 if a then
                   a=(a==attr) and (not attribute or getprop(start,a_state)==attribute)
@@ -13141,7 +14106,7 @@ local function featuresprocessor(head,font,attr)
                   local lookupmatch=lookupcache[getchar(start)]
                   if lookupmatch then
                     local ok
-                    head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
+                    head,start,ok=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,1)
                     if ok then
                       done=true
                     end
@@ -13151,43 +14116,98 @@ local function featuresprocessor(head,font,attr)
                   start=getnext(start)
                 end
               else
-                start=getnext(start)
+                return head,false
               end
             end
             if done then
-              success=true
-              return head
+              success=true 
             end
+            return head,done
           end
-          local function kerndisc(disc) 
-            local prev=getprev(disc)
-            local next=getnext(disc)
-            if prev and next then
-              setfield(prev,"next",next)
-              local a=getattr(prev,0)
-              if a then
-                a=(a==attr) and (not attribute or getprop(prev,a_state)==attribute)
+          local function t_run(start,stop)
+            while start~=stop do
+              local id=getid(start)
+              if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then
+                local a=getattr(start,0)
+                if a then
+                  a=(a==attr) and (not attribute or getprop(start,a_state)==attribute)
+                else
+                  a=not attribute or getprop(start,a_state)==attribute
+                end
+                if a then
+                  local lookupmatch=lookupcache[getchar(start)]
+                  if lookupmatch then
+                    local s=getnext(start)
+                    local l=nil
+                    while s do
+                      local lg=lookupmatch[getchar(s)]
+                      if lg then
+                        l=lg
+                        s=getnext(s)
+                      else
+                        break
+                      end
+                    end
+                    if l and l.ligature then
+                      return true
+                    end
+                  end
+                end
+                start=getnext(start)
               else
-                a=not attribute or getprop(prev,a_state)==attribute
+                break
               end
-              if a then
-                local lookupmatch=lookupcache[getchar(prev)]
-                if lookupmatch then
-                  local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
-                  if ok then
-                    done=true
-                    success=true
+            end
+          end
+          local function d_run(prev) 
+            local a=getattr(prev,0)
+            if a then
+              a=(a==attr) and (not attribute or getprop(prev,a_state)==attribute)
+            else
+              a=not attribute or getprop(prev,a_state)==attribute
+            end
+            if a then
+              local lookupmatch=lookupcache[getchar(prev)]
+              if lookupmatch then
+                local h,d,ok=handler(head,prev,kind,lookupname,lookupmatch,sequence,lookuphash,1)
+                if ok then
+                  done=true
+                  success=true
+                end
+              end
+            end
+          end
+          local function k_run(sub,injection,last)
+            local a=getattr(sub,0)
+            if a then
+              a=(a==attr) and (not attribute or getprop(sub,a_state)==attribute)
+            else
+              a=not attribute or getprop(sub,a_state)==attribute
+            end
+            if a then
+              for n in traverse_nodes(sub) do 
+                if n==last then
+                  break
+                end
+                local id=getid(n)
+                if id==glyph_code then
+                  local lookupmatch=lookupcache[getchar(n)]
+                  if lookupmatch then
+                    local h,d,ok=handler(sub,n,kind,lookupname,lookupmatch,sequence,lookuphash,1,injection)
+                    if ok then
+                      done=true
+                      success=true
+                    end
                   end
+                else
                 end
               end
-              setfield(prev,"next",disc)
             end
-            return next
           end
           while start do
             local id=getid(start)
             if id==glyph_code then
-              if getfont(start)==font and getsubtype(start)<256 then
+              if getfont(start)==font and getsubtype(start)<256 then 
                 local a=getattr(start,0)
                 if a then
                   a=(a==attr) and (not attribute or getprop(start,a_state)==attribute)
@@ -13195,13 +14215,18 @@ local function featuresprocessor(head,font,attr)
                   a=not attribute or getprop(start,a_state)==attribute
                 end
                 if a then
-                  local lookupmatch=lookupcache[getchar(start)]
+                  local char=getchar(start)
+                  local lookupmatch=lookupcache[char]
                   if lookupmatch then
                     local ok
-                    head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)
+                    head,start,ok=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,1)
                     if ok then
                       success=true
+                    elseif gpossing and zwnjruns and char==zwnj then
+                      discrun(start,d_run)
                     end
+                  elseif gpossing and zwnjruns and char==zwnj then
+                    discrun(start,d_run)
                   end
                   if start then start=getnext(start) end
                 else
@@ -13211,41 +14236,30 @@ local function featuresprocessor(head,font,attr)
                 start=getnext(start)
               end
             elseif id==disc_code then
-              if getsubtype(start)==discretionary_code then
-                local pre=getfield(start,"pre")
-                if pre then
-                  local new=subrun(pre)
-                  if new then setfield(start,"pre",new) end
-                end
-                local post=getfield(start,"post")
-                if post then
-                  local new=subrun(post)
-                  if new then setfield(start,"post",new) end
-                end
-                local replace=getfield(start,"replace")
-                if replace then
-                  local new=subrun(replace)
-                  if new then setfield(start,"replace",new) end
-                end
-elseif typ=="gpos_single" or typ=="gpos_pair" then
-  kerndisc(start)
+              if gpossing then
+                kernrun(start,k_run)
+                start=getnext(start)
+              elseif typ=="gsub_ligature" then
+                start=testrun(start,t_run,c_run)
+              else
+                comprun(start,c_run)
+                start=getnext(start)
               end
-              start=getnext(start)
             elseif id==whatsit_code then 
               local subtype=getsubtype(start)
               if subtype==dir_code then
                 local dir=getfield(start,"dir")
-                if   dir=="+TRT" or dir=="+TLT" then
+                if dir=="+TLT" then
                   topstack=topstack+1
                   dirstack[topstack]=dir
-                elseif dir=="-TRT" or dir=="-TLT" then
-                  topstack=topstack-1
-                end
-                local newdir=dirstack[topstack]
-                if newdir=="+TRT" then
-                  rlmode=-1
-                elseif newdir=="+TLT" then
                   rlmode=1
+                elseif dir=="+TRT" then
+                  topstack=topstack+1
+                  dirstack[topstack]=dir
+                  rlmode=-1
+                elseif dir=="-TLT" or dir=="-TRT" then
+                  topstack=topstack-1
+                  rlmode=dirstack[topstack]=="+TRT" and -1 or 1
                 else
                   rlmode=rlparmode
                 end
@@ -13275,12 +14289,19 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then
           end
         end
       else
-        local function subrun(start)
-          local head=start
+        local function c_run(head)
           local done=false
+          local start=sweephead[head]
+          if start then
+            sweephead[head]=nil
+          else
+            start=head
+          end
           while start do
             local id=getid(start)
-            if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then
+            if id~=glyph_code then
+              start=getnext(start)
+            elseif getfont(start)==font and getsubtype(start)<256 then
               local a=getattr(start,0)
               if a then
                 a=(a==attr) and (not attribute or getprop(start,a_state)==attribute)
@@ -13288,14 +14309,15 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then
                 a=not attribute or getprop(start,a_state)==attribute
               end
               if a then
+                local char=getchar(start)
                 for i=1,ns do
                   local lookupname=subtables[i]
                   local lookupcache=lookuphash[lookupname]
                   if lookupcache then
-                    local lookupmatch=lookupcache[getchar(start)]
+                    local lookupmatch=lookupcache[char]
                     if lookupmatch then
                       local ok
-                      head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
+                      head,start,ok=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)
                       if ok then
                         done=true
                         break
@@ -13312,46 +14334,120 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then
                 start=getnext(start)
               end
             else
-              start=getnext(start)
+              return head,false
             end
           end
           if done then
             success=true
-            return head
           end
+          return head,done
         end
-        local function kerndisc(disc) 
-          local prev=getprev(disc)
-          local next=getnext(disc)
-          if prev and next then
-            setfield(prev,"next",next)
-            local a=getattr(prev,0)
-            if a then
-              a=(a==attr) and (not attribute or getprop(prev,a_state)==attribute)
-            else
-              a=not attribute or getprop(prev,a_state)==attribute
+        local function d_run(prev)
+          local a=getattr(prev,0)
+          if a then
+            a=(a==attr) and (not attribute or getprop(prev,a_state)==attribute)
+          else
+            a=not attribute or getprop(prev,a_state)==attribute
+          end
+          if a then
+            local char=getchar(prev)
+            for i=1,ns do
+              local lookupname=subtables[i]
+              local lookupcache=lookuphash[lookupname]
+              if lookupcache then
+                local lookupmatch=lookupcache[char]
+                if lookupmatch then
+                  local h,d,ok=handler(head,prev,kind,lookupname,lookupmatch,sequence,lookuphash,i)
+                  if ok then
+                    done=true
+                    break
+                  end
+                end
+              else
+                report_missing_cache(typ,lookupname)
+              end
             end
-            if a then
-              for i=1,ns do
-                local lookupname=subtables[i]
-                local lookupcache=lookuphash[lookupname]
-                if lookupcache then
-                  local lookupmatch=lookupcache[getchar(prev)]
-                  if lookupmatch then
-                    local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
-                    if ok then
-                      done=true
-                      break
+          end
+        end
+        local function k_run(sub,injection,last)
+          local a=getattr(sub,0)
+          if a then
+            a=(a==attr) and (not attribute or getprop(sub,a_state)==attribute)
+          else
+            a=not attribute or getprop(sub,a_state)==attribute
+          end
+          if a then
+            for n in traverse_nodes(sub) do 
+              if n==last then
+                break
+              end
+              local id=getid(n)
+              if id==glyph_code then
+                local char=getchar(n)
+                for i=1,ns do
+                  local lookupname=subtables[i]
+                  local lookupcache=lookuphash[lookupname]
+                  if lookupcache then
+                    local lookupmatch=lookupcache[char]
+                    if lookupmatch then
+                      local h,d,ok=handler(head,n,kind,lookupname,lookupmatch,sequence,lookuphash,i,injection)
+                      if ok then
+                        done=true
+                        break
+                      end
                     end
+                  else
+                    report_missing_cache(typ,lookupname)
+                  end
+                end
+              else
+              end
+            end
+          end
+        end
+        local function t_run(start,stop)
+          while start~=stop do
+            local id=getid(start)
+            if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then
+              local a=getattr(start,0)
+              if a then
+                a=(a==attr) and (not attribute or getprop(start,a_state)==attribute)
+              else
+                a=not attribute or getprop(start,a_state)==attribute
+              end
+              if a then
+                local char=getchar(start)
+                for i=1,ns do
+                  local lookupname=subtables[i]
+                  local lookupcache=lookuphash[lookupname]
+                  if lookupcache then
+                    local lookupmatch=lookupcache[char]
+                    if lookupmatch then
+                      local s=getnext(start)
+                      local l=nil
+                      while s do
+                        local lg=lookupmatch[getchar(s)]
+                        if lg then
+                          l=lg
+                          s=getnext(s)
+                        else
+                          break
+                        end
+                      end
+                      if l and l.ligature then
+                        return true
+                      end
+                    end
+                  else
+                    report_missing_cache(typ,lookupname)
                   end
-                else
-                  report_missing_cache(typ,lookupname)
                 end
               end
+              start=getnext(start)
+            else
+              break
             end
-            setfield(prev,"next",disc)
           end
-          return next
         end
         while start do
           local id=getid(start)
@@ -13368,16 +14464,21 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then
                   local lookupname=subtables[i]
                   local lookupcache=lookuphash[lookupname]
                   if lookupcache then
-                    local lookupmatch=lookupcache[getchar(start)]
+                    local char=getchar(start)
+                    local lookupmatch=lookupcache[char]
                     if lookupmatch then
                       local ok
-                      head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)
+                      head,start,ok=handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,i)
                       if ok then
                         success=true
                         break
                       elseif not start then
                         break
+                      elseif gpossing and zwnjruns and char==zwnj then
+                        discrun(start,d_run)
                       end
+                    elseif gpossing and zwnjruns and char==zwnj then
+                      discrun(start,d_run)
                     end
                   else
                     report_missing_cache(typ,lookupname)
@@ -13391,41 +14492,30 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then
               start=getnext(start)
             end
           elseif id==disc_code then
-            if getsubtype(start)==discretionary_code then
-              local pre=getfield(start,"pre")
-              if pre then
-                local new=subrun(pre)
-                if new then setfield(start,"pre",new) end
-              end
-              local post=getfield(start,"post")
-              if post then
-                local new=subrun(post)
-                if new then setfield(start,"post",new) end
-              end
-              local replace=getfield(start,"replace")
-              if replace then
-                local new=subrun(replace)
-                if new then setfield(start,"replace",new) end
-              end
-elseif typ=="gpos_single" or typ=="gpos_pair" then
-  kerndisc(start)
+            if gpossing then
+              kernrun(start,k_run)
+              start=getnext(start)
+            elseif typ=="gsub_ligature" then
+              start=testrun(start,t_run,c_run)
+            else
+              comprun(start,c_run)
+              start=getnext(start)
             end
-            start=getnext(start)
           elseif id==whatsit_code then
             local subtype=getsubtype(start)
             if subtype==dir_code then
               local dir=getfield(start,"dir")
-              if   dir=="+TRT" or dir=="+TLT" then
+              if dir=="+TLT" then
                 topstack=topstack+1
                 dirstack[topstack]=dir
-              elseif dir=="-TRT" or dir=="-TLT" then
-                topstack=topstack-1
-              end
-              local newdir=dirstack[topstack]
-              if newdir=="+TRT" then
-                rlmode=-1
-              elseif newdir=="+TLT" then
                 rlmode=1
+              elseif dir=="+TRT" then
+                topstack=topstack+1
+                dirstack[topstack]=dir
+                rlmode=-1
+              elseif dir=="-TLT" or dir=="-TRT" then
+                topstack=topstack-1
+                rlmode=dirstack[topstack]=="+TRT" and -1 or 1
               else
                 rlmode=rlparmode
               end
@@ -13473,43 +14563,46 @@ local function generic(lookupdata,lookupname,unicode,lookuphash)
     lookuphash[lookupname]={ [unicode]=lookupdata }
   end
 end
+local function ligature(lookupdata,lookupname,unicode,lookuphash)
+  local target=lookuphash[lookupname]
+  if not target then
+    target={}
+    lookuphash[lookupname]=target
+  end
+  for i=1,#lookupdata do
+    local li=lookupdata[i]
+    local tu=target[li]
+    if not tu then
+      tu={}
+      target[li]=tu
+    end
+    target=tu
+  end
+  target.ligature=unicode
+end
+local function pair(lookupdata,lookupname,unicode,lookuphash)
+  local target=lookuphash[lookupname]
+  if not target then
+    target={}
+    lookuphash[lookupname]=target
+  end
+  local others=target[unicode]
+  local paired=lookupdata[1]
+  if others then
+    others[paired]=lookupdata
+  else
+    others={ [paired]=lookupdata }
+    target[unicode]=others
+  end
+end
 local action={
   substitution=generic,
   multiple=generic,
   alternate=generic,
   position=generic,
-  ligature=function(lookupdata,lookupname,unicode,lookuphash)
-    local target=lookuphash[lookupname]
-    if not target then
-      target={}
-      lookuphash[lookupname]=target
-    end
-    for i=1,#lookupdata do
-      local li=lookupdata[i]
-      local tu=target[li]
-      if not tu then
-        tu={}
-        target[li]=tu
-      end
-      target=tu
-    end
-    target.ligature=unicode
-  end,
-  pair=function(lookupdata,lookupname,unicode,lookuphash)
-    local target=lookuphash[lookupname]
-    if not target then
-      target={}
-      lookuphash[lookupname]=target
-    end
-    local others=target[unicode]
-    local paired=lookupdata[1]
-    if others then
-      others[paired]=lookupdata
-    else
-      others={ [paired]=lookupdata }
-      target[unicode]=others
-    end
-  end,
+  ligature=ligature,
+  pair=pair,
+  kern=pair,
 }
 local function prepare_lookups(tfmdata)
   local rawdata=tfmdata.shared.rawdata
@@ -13520,13 +14613,14 @@ local function prepare_lookups(tfmdata)
   local lookuptypes=resources.lookuptypes
   local characters=tfmdata.characters
   local descriptions=tfmdata.descriptions
+  local duplicates=resources.duplicates
   for unicode,character in next,characters do 
     local description=descriptions[unicode]
     if description then
       local lookups=description.slookups
       if lookups then
         for lookupname,lookupdata in next,lookups do
-          action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash)
+          action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash,duplicates)
         end
       end
       local lookups=description.mlookups
@@ -13535,7 +14629,7 @@ local function prepare_lookups(tfmdata)
           local lookuptype=lookuptypes[lookupname]
           for l=1,#lookuplist do
             local lookupdata=lookuplist[l]
-            action[lookuptype](lookupdata,lookupname,unicode,lookuphash)
+            action[lookuptype](lookupdata,lookupname,unicode,lookuphash,duplicates)
           end
         end
       end
@@ -13557,7 +14651,7 @@ local function prepare_lookups(tfmdata)
             for name,anchor in next,anchors do
               local lookups=anchor_to_lookup[name]
               if lookups then
-                for lookup,_ in next,lookups do
+                for lookup in next,lookups do
                   local target=lookuphash[lookup]
                   if target then
                     target[unicode]=anchors
@@ -13639,7 +14733,7 @@ local function prepare_contextchains(tfmdata)
               if sequence[1] then
                 nt=nt+1
                 t[nt]={ nofrules,lookuptype,sequence,start,stop,rule.lookups,replacements }
-                for unic,_ in next,sequence[start] do
+                for unic in next,sequence[start] do
                   local cu=contexts[unic]
                   if not cu then
                     contexts[unic]=t
@@ -13698,9 +14792,8 @@ if not modules then modules={} end modules ['font-otp']={
   copyright="PRAGMA ADE / ConTeXt Development Team",
   license="see context related readme files"
 }
-local next,type=next,type
+local next,type,tostring=next,type,tostring
 local sort,concat=table.sort,table.concat
-local sortedhash=table.sortedhash
 local trace_packing=false trackers.register("otf.packing",function(v) trace_packing=v end)
 local trace_loading=false trackers.register("otf.loading",function(v) trace_loading=v end)
 local report_otf=logs.reporter("fonts","otf loading")
@@ -14676,10 +15769,11 @@ end
 function resolvers.name(specification)
   local resolve=fonts.names.resolve
   if resolve then
-    local resolved,sub=resolve(specification.name,specification.sub,specification) 
+    local resolved,sub,subindex=resolve(specification.name,specification.sub,specification) 
     if resolved then
       specification.resolved=resolved
       specification.sub=sub
+      specification.subindex=subindex
       local suffix=lower(suffixonly(resolved))
       if fonts.formats[suffix] then
         specification.forced=suffix
@@ -14696,10 +15790,11 @@ end
 function resolvers.spec(specification)
   local resolvespec=fonts.names.resolvespec
   if resolvespec then
-    local resolved,sub=resolvespec(specification.name,specification.sub,specification) 
+    local resolved,sub,subindex=resolvespec(specification.name,specification.sub,specification) 
     if resolved then
       specification.resolved=resolved
       specification.sub=sub
+      specification.subindex=subindex
       specification.forced=lower(suffixonly(resolved))
       specification.forcedname=resolved
       specification.name=removesuffix(resolved)
@@ -15341,12 +16436,30 @@ function nodes.handlers.nodepass(head)
         local range=basefonts[i]
         local start=range[1]
         local stop=range[2]
-        if stop then
-          start,stop=ligaturing(start,stop)
-          start,stop=kerning(start,stop)
-        elseif start then
-          start=ligaturing(start)
-          start=kerning(start)
+        if start or stop then
+          local prev=nil
+          local next=nil
+          local front=start==head
+          if stop then
+            next=stop.next
+            start,stop=ligaturing(start,stop)
+            start,stop=kerning(start,stop)
+          elseif start then
+            prev=start.prev
+            start=ligaturing(start)
+            start=kerning(start)
+          end
+          if prev then
+            start.prev=prev
+            prev.next=start
+          end
+          if next then
+            stop.next=next
+            next.prev=stop
+          end
+          if front then
+            head=start
+          end
         end
       end
     end
@@ -15356,7 +16469,7 @@ function nodes.handlers.nodepass(head)
   end
 end
 function nodes.handlers.basepass(head)
-  if not basepass then
+  if basepass then
     head=ligaturing(head)
     head=kerning(head)
   end
-- 
cgit v1.2.3


From 7fd7d54817edbf75ff4e0a574769a27a851a5240 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Thu, 29 Oct 2015 07:21:18 +0100
Subject: [tool] adapt outdated usage info

---
 src/luaotfload-tool.lua | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua
index 181bd5c..8b7bc33 100755
--- a/src/luaotfload-tool.lua
+++ b/src/luaotfload-tool.lua
@@ -109,6 +109,7 @@ config.lualibs.prefer_merged    = true
 config.lualibs.load_extended    = true
 
 require "lualibs"
+
 local iosavedata                = io.savedata
 local lfsisdir                  = lfs.isdir
 local lfsisfile                 = lfs.isfile
@@ -138,7 +139,7 @@ local backup = {
 }
 
 texio.write, texio.write_nl          = dummy_function, dummy_function
-require"luaotfload-basics-gen.lua"
+require "luaotfload-basics-gen.lua"
 
 texio.write, texio.write_nl          = backup.write, backup.write_nl
 utilities                            = backup.utilities
@@ -216,8 +217,7 @@ Usage: %s [OPTIONS...]
 
   -q --quiet                   don't output anything
   -v --verbose=LEVEL           be more verbose (print the searched directories)
-  -vv                          print the loaded fonts
-  -vvv                         print all steps of directory searching
+  -v, -vv .. -vvvvvvvvv        set loglevel in unary
   --log=stdout                 redirect log output to stdout
 
   -V --version                 print version and exit
-- 
cgit v1.2.3


From 1bffc1c923412efa9b45f370d771d1ebacbcbaef Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Thu, 29 Oct 2015 07:26:34 +0100
Subject: [log] fix format strings passed to os.date()

    In the name of MS compiler compatibility
        bow before the Middle Ages.
---
 src/luaotfload-log.lua | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-log.lua b/src/luaotfload-log.lua
index 7c012f4..e5db310 100644
--- a/src/luaotfload-log.lua
+++ b/src/luaotfload-log.lua
@@ -91,7 +91,7 @@ local set_logout = function (s, finalizers)
         logout = "redirect"
         local chan = choose_logfile ()
         chan:write (stringformat ("logging initiated at %s",
-                                  osdate ("%Y-%m-%d %h:%m:%s", --- i. e. osdate "%F %T"
+                                  osdate ("%Y-%m-%d %H:%M:%S", --- i. e. osdate "%F %T"
                                           ostime ())))
         local writefile = function (...)
             if select ("#", ...) == 2 then
@@ -118,7 +118,7 @@ local set_logout = function (s, finalizers)
 
         finalizers[#finalizers+1] = function ()
             chan:write (stringformat ("\nlogging finished at %s\n",
-                                      osdate ("%Y-%m-%d %h:%m:%s", --- i. e. osdate "%F %T"
+                                      osdate ("%Y-%m-%d %H:%M:%S", --- i. e. osdate "%F %T"
                                               ostime ())))
             chan:close ()
             texiowrite    = texio.write
-- 
cgit v1.2.3


From e52df63570da80bf121fd9b9294de5a2d037f1b6 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Thu, 29 Oct 2015 07:37:39 +0100
Subject: [main] dejumble module loader message

---
 src/luaotfload-main.lua | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua
index 3d68a17..ff2b48d 100644
--- a/src/luaotfload-main.lua
+++ b/src/luaotfload-main.lua
@@ -109,15 +109,15 @@ end
 
 local make_loader_name = function (prefix, name)
     local msg = luaotfload.log and luaotfload.log.report or print
-    if prefix then
+    if prefix and name then
         msg ("log", 7, "load",
-             "Composing fontloader name from constitutents %s, %s",
+             "Composing module name from constituents %s, %s",
              prefix, name)
         return prefix .. "-" .. name .. ".lua"
     end
     msg ("log", 7, "load",
-         "Loading fontloader file %s literally.",
-         name)
+         "Loading module %s literally.",
+         tostring (name))
     return name
 end
 
-- 
cgit v1.2.3


From 4ab63d7a670d90f9ffcbd2c35b363c432c043d5d Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Thu, 29 Oct 2015 07:39:59 +0100
Subject: [diagnose] fix indentation of status message

---
 src/luaotfload-diagnostics.lua | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-diagnostics.lua b/src/luaotfload-diagnostics.lua
index 80e461c..255b5a5 100644
--- a/src/luaotfload-diagnostics.lua
+++ b/src/luaotfload-diagnostics.lua
@@ -655,27 +655,27 @@ local diagnose = function (job)
               you may sleep well.")
         return true, false
     end
-    out (         [[===============================================
-                                        WARNING
-                    ===============================================
+    out (             [[===============================================
+                                            WARNING
+                        ===============================================
 
-                    The diagnostic detected %d errors.
+                        The diagnostic detected %d errors.
 
-                    This version of luaotfload may have been
-                    tampered with. Modified versions of the
-                    luaotfload source are unsupported. Read the log
-                    carefully and get a clean version from CTAN or
-                    github:
+                        This version of luaotfload may have been
+                        tampered with. Modified versions of the
+                        luaotfload source are unsupported. Read the log
+                        carefully and get a clean version from CTAN or
+                        github:
 
-                        × http://www.ctan.org/pkg/luaotfload 
-                        × https://github.com/lualatex/luaotfload/releases
+                            × http://www.ctan.org/pkg/luaotfload 
+                            × https://github.com/lualatex/luaotfload/releases
 
-                    If you are uncertain as to how to proceed, then
-                    ask on the lualatex mailing list:
+                        If you are uncertain as to how to proceed, then
+                        ask on the lualatex mailing list:
 
-                        http://www.tug.org/mailman/listinfo/lualatex-dev
+                            http://www.tug.org/mailman/listinfo/lualatex-dev
 
-                    ===============================================
+                        ===============================================
 ]],          errcnt)
     return true, false
 end
-- 
cgit v1.2.3


From dfdee4a9f3e968c5493f9e4166503624edfe5eb0 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Thu, 29 Oct 2015 08:03:33 +0100
Subject: [db] fix passing inverse format list

---
 src/luaotfload-database.lua | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua
index b871954..086f46f 100644
--- a/src/luaotfload-database.lua
+++ b/src/luaotfload-database.lua
@@ -1921,9 +1921,8 @@ end
 local p_font_filter
 
 do
-    local current_formats = { }
-
     local extension_pattern = function (list)
+        if type (list) ~= "table" or #list == 0 then return P(-1) end
         local pat
         for i=#list, 1, -1 do
             local e = list[i]
@@ -1940,6 +1939,8 @@ do
     --- small helper to adjust the font filter pattern (--formats
     --- option)
 
+    local current_formats = { }
+
     set_font_filter = function (formats)
 
         if not formats or type (formats) ~= "string" then
-- 
cgit v1.2.3


From 6c0c403a3b6fa01604935960a46eb1e10a4586af Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Thu, 29 Oct 2015 08:22:36 +0100
Subject: [tool,resolvers,db] fix references to the fonts table

This makes the ``--find`` option to luaotfload-too work again.
---
 src/luaotfload-database.lua  |  3 ++-
 src/luaotfload-main.lua      |  2 +-
 src/luaotfload-resolvers.lua | 32 ++++++++++++++++----------------
 src/luaotfload-tool.lua      |  7 +++----
 4 files changed, 22 insertions(+), 22 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua
index 086f46f..b84b48d 100644
--- a/src/luaotfload-database.lua
+++ b/src/luaotfload-database.lua
@@ -3501,7 +3501,8 @@ return {
         logreport       = luaotfload.log.report
         local fonts     = fonts
         fonts.names     = fonts.names or names
-        fonts.definers  = fonts.definers or { }
+        fonts.formats   = fonts.formats or { }
+        fonts.definers  = fonts.definers or { resolvers = { } }
 
         names.blacklist = blacklist
         names.version   = 2.51
diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua
index ff2b48d..98afee0 100644
--- a/src/luaotfload-main.lua
+++ b/src/luaotfload-main.lua
@@ -194,7 +194,7 @@ luaotfload.main = function ()
     initialize "colors"          --- Per-font colors.
 
     luaotfload.resolvers = loadmodule "resolvers" --- Font lookup
-    luaotfload.resolvers.install ()
+    luaotfload.resolvers.init ()
 
     if not config.actions.reconfigure () then
         logreport ("log", 0, "load", "Post-configuration hooks failed.")
diff --git a/src/luaotfload-resolvers.lua b/src/luaotfload-resolvers.lua
index 42ea2fd..2e34a4e 100644
--- a/src/luaotfload-resolvers.lua
+++ b/src/luaotfload-resolvers.lua
@@ -37,15 +37,9 @@ local stringlower         = string.lower
 local stringformat        = string.format
 local filesuffix          = file.suffix
 local fileremovesuffix    = file.removesuffix
-local formats             = fonts.formats
-local names               = fonts.names
-local encodings           = fonts.encodings
 local luatexbase          = luatexbase
 local logreport           = luaotfload.log.report
 
-formats.ofm               = "type1"
-encodings.known           = encodings.known or { }
-
 --[[doc--
 
     \identifier{luaotfload} promises easy access to system fonts.
@@ -70,9 +64,9 @@ encodings.known           = encodings.known or { }
 
 local resolve_file
 resolve_file = function (specification)
-    local name   = names.lookup_font_file (specification.name)
+    local name   = fonts.names.lookup_font_file (specification.name)
     local suffix = filesuffix (name)
-    if formats[suffix] then
+    if fonts.formats[suffix] then
         specification.forced      = stringlower (suffix)
         specification.forcedname  = fileremovesuffix(name)
     else
@@ -101,7 +95,7 @@ resolve_path = function (specification)
         resolve_file (specification)
     else
         local suffix = filesuffix (name)
-        if formats[suffix] then
+        if fonts.formats[suffix] then
             specification.forced      = stringlower (suffix)
             specification.name        = fileremovesuffix(name)
             specification.forcedname  = name
@@ -122,9 +116,9 @@ end
 
 local resolve_name
 resolve_name = function (specification)
-    local resolver = names.lookup_font_name_cached
+    local resolver = fonts.names.lookup_font_name_cached
     if config.luaotfload.run.resolver == "normal" then
-        resolver = names.lookup_font_name
+        resolver = fonts.names.lookup_font_name
     end
     local resolved, subfont = resolver (specification)
     if resolved then
@@ -210,7 +204,7 @@ local resolve_kpse
 resolve_kpse = function (specification)
     local name       = specification.name
     local suffix     = filesuffix (name)
-    if suffix and formats[suffix] then
+    if suffix and fonts.formats[suffix] then
         name = fileremovesuffix (name)
         if resolvers.findfile (name, suffix) then
             specification.forced       = stringlower (suffix)
@@ -218,7 +212,7 @@ resolve_kpse = function (specification)
             return
         end
     end
-    for t, format in next, formats do --- brute force
+    for t, format in next, fonts.formats do --- brute force
         if kpsefind_file (name, format) then
             specification.forced = t
             specification.name   = name
@@ -238,8 +232,11 @@ local resolve_my = function (specification)
 end
 
 return {
-    install = function ( )
-        luatexbase.create_callback ("luaotfload.resolve_font", "simple", function () end)
+    init = function ( )
+        if luatexbase and luatexbase.create_callback then
+            luatexbase.create_callback ("luaotfload.resolve_font",
+                                        "simple", function () end)
+        end
         logreport ("log", 5, "resolvers", "installing font resolvers", name)
         local request_resolvers = fonts.definers.resolvers
         request_resolvers.file = resolve_file
@@ -248,8 +245,11 @@ return {
         request_resolvers.path = resolve_path
         request_resolvers.kpse = resolve_kpse
         request_resolvers.my   = resolve_my
+        fonts.formats.ofm      = "type1"
+        fonts.encodings        = fonts.encodings       or { }
+        fonts.encodings.known  = fonts.encodings.known or { }
         return true
-    end, --- [.install]
+    end, --- [.init]
 }
 
 --- vim:ft=lua:ts=8:sw=4:et
diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua
index 8b7bc33..e92f5fb 100755
--- a/src/luaotfload-tool.lua
+++ b/src/luaotfload-tool.lua
@@ -76,7 +76,6 @@ else -- 5.2
     runtime = { "stock", _VERSION }
 end
 
-
 local C, Ct, P, S  = lpeg.C, lpeg.Ct, lpeg.P, lpeg.S
 local lpegmatch    = lpeg.match
 
@@ -84,7 +83,6 @@ local loader_file = "luatexbase.loader.lua"
 local loader_path = assert(kpsefind_file(loader_file, "lua"),
                            "File '"..loader_file.."' not found")
 
-
 string.quoted = string.quoted or function (str)
   return string.format("%q",str) 
 end
@@ -171,6 +169,7 @@ loadmodule "log.lua"       --- this populates the luaotfload.log.* namespace
 loadmodule "parsers"       --- fonts.conf, configuration, and request syntax
 loadmodule "configuration" --- configuration file handling
 loadmodule "database"
+loadmodule "resolvers"     --- Font lookup
 
 local logreport
 
@@ -237,7 +236,7 @@ Usage: %s [OPTIONS...]
   -c --no-compress             do not gzip index file (text version only)
   -l --flush-lookups           empty lookup cache of font requests
   -D --dry-run                 skip loading of fonts, just scan
-  --formats=[+|-]EXTENSIONS    set, add, or subtract formats to index
+  --formats=[+|-]EXTENSIONS    set, add, or subtract file formats
   -p --prefer-texmf            prefer fonts in the TEXMF over system fonts
   --max-fonts=N                process at most N font files
 
@@ -1227,7 +1226,7 @@ actions.query = function (job)
     if tmpspec.lookup == "name"
     or tmpspec.lookup == "anon" --- not *exactly* as resolvers.anon
     then
-        foundname, subfont = fonts.names.resolve_name (tmpspec)
+        foundname, subfont = fonts.definers.resolvers.name (tmpspec)
         if foundname then
             foundname, _, success = fonts.names.font_file_lookup (foundname)
         end
-- 
cgit v1.2.3


From 72a9a971cb229656e021ab56894ac73baa62c0c0 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Fri, 30 Oct 2015 00:15:55 +0100
Subject: [tool] fix --file lookup

---
 src/luaotfload-tool.lua | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua
index e92f5fb..5508ffc 100755
--- a/src/luaotfload-tool.lua
+++ b/src/luaotfload-tool.lua
@@ -1226,13 +1226,12 @@ actions.query = function (job)
     if tmpspec.lookup == "name"
     or tmpspec.lookup == "anon" --- not *exactly* as resolvers.anon
     then
-        foundname, subfont = fonts.definers.resolvers.name (tmpspec)
+        foundname, _, success = fonts.names.lookup_font_name (tmpspec)
         if foundname then
-            foundname, _, success = fonts.names.font_file_lookup (foundname)
+            foundname, _, success = fonts.names.lookup_font_file (foundname)
         end
     elseif tmpspec.lookup == "file" then
-        foundname, _, success =
-            fonts.names.font_file_lookup (tmpspec.name)
+        foundname, _, success = fonts.names.lookup_font_file (tmpspec.name)
     end
 
     if success then
-- 
cgit v1.2.3


From 32d25b33ee109f1979db85d7ddf563246d6bcb7f Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Fri, 30 Oct 2015 07:42:58 +0100
Subject: [tool] display fuzzy hint only when not fuzzing

---
 src/luaotfload-tool.lua | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua
index 5508ffc..022b659 100755
--- a/src/luaotfload-tool.lua
+++ b/src/luaotfload-tool.lua
@@ -1250,13 +1250,14 @@ actions.query = function (job)
         end
     else
         logreport (false, 0, "resolve", "Cannot find %q in index.", query)
-        logreport (false, 0, "resolve",
-                   "Hint: use the --fuzzy option to display suggestions.",
-                   query)
         if job.fuzzy == true then
             logreport (false, 0, "resolve",
                        "Looking for close matches, this may take a while ...")
             local _success = fonts.names.find_closest(query, job.fuzzy_limit)
+        else
+            logreport (false, 0, "resolve",
+                       "Hint: use the --fuzzy option to display suggestions.",
+                       query)
         end
     end
     return true, true
-- 
cgit v1.2.3


From e1a19fbda5bb54a1378620c8df9c2d42ed087fee Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Fri, 30 Oct 2015 07:53:08 +0100
Subject: [db] omit duplicate entries from fuzzed lookup results

---
 src/luaotfload-database.lua | 24 ++++++++++++++++++++++--
 1 file changed, 22 insertions(+), 2 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua
index b84b48d..1bc2768 100644
--- a/src/luaotfload-database.lua
+++ b/src/luaotfload-database.lua
@@ -1171,6 +1171,25 @@ local iterative_levenshtein = function (s1, s2)
   return costs[len2]--- lower right has the distance
 end
 
+--- string list -> string list
+local delete_dupes = function (lst)
+    local n0 = #lst
+    if n0 == 0 then return lst end
+    tablesort (lst)
+    local ret = { }
+    local last
+    for i = 1, n0 do
+        local cur = lst[i]
+        if cur ~= last then
+            last = cur
+            ret[#ret + 1] = cur
+        end
+    end
+    logreport (false, 8, "query",
+               "Removed %d duplicate names.", n0 - #ret)
+    return ret
+end
+
 --- string -> int -> bool
 find_closest = function (name, limit)
     local name     = sanitize_fontname (name)
@@ -1216,6 +1235,7 @@ find_closest = function (name, limit)
         else --- append
             namelst[#namelst+1] = fullname
         end
+
         by_distance[dist] = namelst
     end
 
@@ -1229,11 +1249,11 @@ find_closest = function (name, limit)
 
         for i = 1, limit do
             local dist     = distances[i]
-            local namelst  = by_distance[dist]
+            local namelst  = delete_dupes (by_distance[dist])
             logreport (false, 0, "query",
                        "Distance from \"%s\": %s\n    "
                        .. tableconcat (namelst, "\n    "),
-                   name, dist)
+                       name, dist)
         end
 
         return true
-- 
cgit v1.2.3


From 330440f784cb12b0902fd80271e568c59da64771 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Thu, 5 Nov 2015 22:05:00 +0100
Subject: [main] clean up unused and irrelevant pieces

---
 src/luaotfload-main.lua | 14 +++-----------
 1 file changed, 3 insertions(+), 11 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua
index 98afee0..5fa316d 100644
--- a/src/luaotfload-main.lua
+++ b/src/luaotfload-main.lua
@@ -7,23 +7,15 @@
 --     MODIFIED:  2015-06-09 23:08:18+0200
 -----------------------------------------------------------------------
 --
---- Note:
---- This file was part of the original luaotfload.dtx and has been
---- converted to a pure Lua file during the transition from Luaotfload
---- version 2.4 to 2.5. Thus, the comments are still in TeX (Latex)
---- markup.
 
-local os                          = os
 local osgettimeofday              = os.gettimeofday
-
-local initial_log_level           = 0
-luaotfload                        = luaotfload or { }
 config                            = config     or { }
+luaotfload                        = luaotfload or { }
 local luaotfload                  = luaotfload
 luaotfload.log                    = luaotfload.log or { }
 luaotfload.version                = "2.6"
 luaotfload.loaders                = { }
-luaotfload.min_luatex_version     = 79             --- i. e. 0.79
+luaotfload.min_luatex_version     = 80             --- i. e. 0.79
 luaotfload.fontloader_package     = "reference"    --- default: from current Context
 
 local authors = "\z
@@ -40,7 +32,7 @@ local authors = "\z
 luaotfload.module = {
     name          = "luaotfload-main",
     version       = 2.60001,
-    date          = "2015/05/26",
+    date          = "2015/11/05",
     description   = "OpenType layout system.",
     author        = authors,
     copyright     = authors,
-- 
cgit v1.2.3


From 8395eb8efec1ac9e311541d9834f1a939b91e710 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Thu, 5 Nov 2015 22:08:47 +0100
Subject: [*] kill off file headers

We have the VCS info in the status file; these things are just silly.
---
 src/luaotfload-auxiliary.lua     | 2 --
 src/luaotfload-configuration.lua | 1 -
 src/luaotfload-diagnostics.lua   | 2 --
 src/luaotfload-init.lua          | 2 --
 src/luaotfload-loaders.lua       | 1 -
 src/luaotfload-main.lua          | 2 --
 src/luaotfload-parsers.lua       | 4 +---
 src/luaotfload-resolvers.lua     | 2 --
 src/luaotfload-tool.lua          | 2 --
 9 files changed, 1 insertion(+), 17 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-auxiliary.lua b/src/luaotfload-auxiliary.lua
index dce0d60..15541af 100644
--- a/src/luaotfload-auxiliary.lua
+++ b/src/luaotfload-auxiliary.lua
@@ -4,8 +4,6 @@
 --  DESCRIPTION:  part of luaotfload
 -- REQUIREMENTS:  luaotfload 2.6
 --       AUTHOR:  Khaled Hosny, Élie Roux, Philipp Gesang
---      VERSION:  2.6
---     MODIFIED:  2015-03-29 12:43:26+0200
 -----------------------------------------------------------------------
 --
 
diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua
index 86a302e..6f94195 100644
--- a/src/luaotfload-configuration.lua
+++ b/src/luaotfload-configuration.lua
@@ -5,7 +5,6 @@
 -- REQUIREMENTS:  Luaotfload 2.6 or above
 --       AUTHOR:  Philipp Gesang (Phg), <phg42.2a@gmail.com>
 --       AUTHOR:  Dohyun Kim <nomosnomos@gmail.com>
---      VERSION:  same as Luaotfload
 -------------------------------------------------------------------------------
 --
 
diff --git a/src/luaotfload-diagnostics.lua b/src/luaotfload-diagnostics.lua
index 255b5a5..582105a 100644
--- a/src/luaotfload-diagnostics.lua
+++ b/src/luaotfload-diagnostics.lua
@@ -4,8 +4,6 @@
 --  DESCRIPTION:  functionality accessible by the --diagnose option
 -- REQUIREMENTS:  luaotfload-tool.lua
 --       AUTHOR:  Philipp Gesang (Phg), <phg42.2a@gmail.com>
---      VERSION:  2.5
---     MODIFIED:  2014-01-02 21:23:06+0100
 -----------------------------------------------------------------------
 --
 local names                    = fonts.names
diff --git a/src/luaotfload-init.lua b/src/luaotfload-init.lua
index af71cb2..1c2046d 100644
--- a/src/luaotfload-init.lua
+++ b/src/luaotfload-init.lua
@@ -4,8 +4,6 @@
 --  DESCRIPTION:  Luaotfload font loader initialization
 -- REQUIREMENTS:  luatex v.0.80 or later; packages lualibs, luatexbase
 --       AUTHOR:  Philipp Gesang (Phg), <phg@phi-gamma.net>
---      VERSION:  1.0
---      CREATED:  2015-05-26 07:50:54+0200
 -----------------------------------------------------------------------
 --
 
diff --git a/src/luaotfload-loaders.lua b/src/luaotfload-loaders.lua
index b644266..38062f6 100644
--- a/src/luaotfload-loaders.lua
+++ b/src/luaotfload-loaders.lua
@@ -4,7 +4,6 @@
 --  DESCRIPTION:  Luaotfload callback handling
 -- REQUIREMENTS:  luatex v.0.80 or later; packages lualibs, luatexbase
 --       AUTHOR:  Philipp Gesang (Phg), <phg@phi-gamma.net>, Hans Hagen, Khaled Hosny, Elie Roux
---      VERSION:  just consult Git, okay?
 -----------------------------------------------------------------------
 --
 --- Contains parts of the earlier main script.
diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua
index 5fa316d..85c9cee 100644
--- a/src/luaotfload-main.lua
+++ b/src/luaotfload-main.lua
@@ -3,8 +3,6 @@
 --  DESCRIPTION:  Luaotfload entry point
 -- REQUIREMENTS:  luatex v.0.80 or later; packages lualibs, luatexbase
 --       AUTHOR:  Élie Roux, Khaled Hosny, Philipp Gesang
---      VERSION:  same as Luaotfload
---     MODIFIED:  2015-06-09 23:08:18+0200
 -----------------------------------------------------------------------
 --
 
diff --git a/src/luaotfload-parsers.lua b/src/luaotfload-parsers.lua
index 151cb9e..0349cdc 100644
--- a/src/luaotfload-parsers.lua
+++ b/src/luaotfload-parsers.lua
@@ -2,10 +2,8 @@
 -------------------------------------------------------------------------------
 --         FILE:  luaotfload-parsers.lua
 --  DESCRIPTION:  various lpeg-based parsers used in Luaotfload
--- REQUIREMENTS:  Luaotfload > 2.6
+-- REQUIREMENTS:  Luaotfload >= 2.6
 --       AUTHOR:  Philipp Gesang (Phg), <phg@phi-gamma.net>
---      VERSION:  same as Luaotfload
---      CREATED:  2014-01-14 10:15:20+0100
 -------------------------------------------------------------------------------
 --
 
diff --git a/src/luaotfload-resolvers.lua b/src/luaotfload-resolvers.lua
index 2e34a4e..3d7f6b0 100644
--- a/src/luaotfload-resolvers.lua
+++ b/src/luaotfload-resolvers.lua
@@ -5,8 +5,6 @@
 --  DESCRIPTION:  Resolvers for hooking into the fontloader
 -- REQUIREMENTS:  Luaotfload and a decent bit of courage
 --       AUTHOR:  Philipp Gesang (Phg), <phg@phi-gamma.net>
---      VERSION:  1.0
---      CREATED:  2015-07-23 07:31:50+0200
 -----------------------------------------------------------------------
 --
 --- The bare fontloader uses a set of simplistic file name resolvers
diff --git a/src/luaotfload-tool.lua b/src/luaotfload-tool.lua
index 6c54f29..e9a434a 100755
--- a/src/luaotfload-tool.lua
+++ b/src/luaotfload-tool.lua
@@ -4,9 +4,7 @@
 --  DESCRIPTION:  database functionality
 -- REQUIREMENTS:  luaotfload 2.6
 --       AUTHOR:  Khaled Hosny, Élie Roux, Philipp Gesang
---      VERSION:  2.6
 --      LICENSE:  GPL v2.0
---     MODIFIED:  2015-03-29 12:43:00+0200
 -----------------------------------------------------------------------
 
 luaotfload          = luaotfload or { }
-- 
cgit v1.2.3


From 389fe13c73832215d8486e43ed3c52f6c96f45ca Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Mon, 9 Nov 2015 08:03:29 +0100
Subject: [main,init] add no-op loader for fontloader files

---
 src/luaotfload-init.lua | 38 +++++++++++++++++++++++---------------
 src/luaotfload-main.lua | 12 ++++++++++++
 2 files changed, 35 insertions(+), 15 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-init.lua b/src/luaotfload-init.lua
index 1c2046d..1d6d1a5 100644
--- a/src/luaotfload-init.lua
+++ b/src/luaotfload-init.lua
@@ -203,6 +203,7 @@ end --- [init_adapt]
 local init_main = function ()
 
   local load_fontloader_module = luaotfload.loaders.fontloader
+  local ignore_module          = luaotfload.loaders.ignore
 
   --[[doc--
 
@@ -214,26 +215,33 @@ local init_main = function ()
 
   --doc]]--
 
+  logreport ("log", 4, "init",
+             "Loading fontloader as merged package.")
   load_fontloader_module (luaotfload.fontloader_package)
 
   ---load_fontloader_module "font-odv.lua" --- <= Devanagari support from Context
 
   if not fonts then
-    --- the loading sequence is known to change, so this might have to
-    --- be updated with future updates!
-    --- do not modify it though unless there is a change to the merged
-    --- package!
-    load_fontloader_module "l-lua"
-    load_fontloader_module "l-lpeg"
-    load_fontloader_module "l-function"
-    load_fontloader_module "l-string"
-    load_fontloader_module "l-table"
-    load_fontloader_module "l-io"
-    load_fontloader_module "l-file"
-    load_fontloader_module "l-boolean"
-    load_fontloader_module "l-math"
-    load_fontloader_module "util-str"
-    load_fontloader_module "luatex-basics-gen"
+    logreport ("log", 4, "init",
+               "Loading fontloader components individually.")
+    --- The loading sequence is known to change, so this might have to be
+    --- updated with future updates. Do not modify it though unless there is
+    --- a change to the upstream package!
+
+    --- Since 2.6 those are directly provided by the Lualibs package.
+    ignore_module "l-lua"
+    ignore_module "l-lpeg"
+    ignore_module "l-function"
+    ignore_module "l-string"
+    ignore_module "l-table"
+    ignore_module "l-io"
+    ignore_module "l-file"
+    ignore_module "l-boolean"
+    ignore_module "l-math"
+    ignore_module "util-str"
+    ignore_module "luatex-basics-gen"
+
+    --- These constitute the fontloader proper.
     load_fontloader_module "data-con"
     load_fontloader_module "luatex-basics-nod"
     load_fontloader_module "font-ini"
diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua
index 85c9cee..686ce0e 100644
--- a/src/luaotfload-main.lua
+++ b/src/luaotfload-main.lua
@@ -127,11 +127,23 @@ local make_loader = function (prefix)
     end
 end
 
+--[[doc--
+    Certain files are kept around that aren’t loaded because they are part of
+    the imported fontloader. In order to keep the initialization structure
+    intact we also provide a no-op version of the module loader that can be
+    called in the expected places.
+--doc]]--
+
+local dummy_loader = function (name)
+    luaotfload.log.report("log", 3, "load", "Skipping module “%s”.", name)
+end
+
 local install_loaders = function ()
     local loaders      = { }
     local loadmodule   = make_loader "luaotfload"
     loaders.luaotfload = loadmodule
     loaders.fontloader = make_loader "fontloader"
+    loaders.ignore     = dummy_loader
 ----loaders.plaintex   = make_loader "luatex" --=> for Luatex-Plain
 
     loaders.initialize = function (name)
-- 
cgit v1.2.3


From 60f41dfbc13f06a1b43a4e66798a0d043d210eee Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Mon, 9 Nov 2015 08:16:19 +0100
Subject: [init,conf] enable direct loading of fontloader components
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

For now, this pertains only to the modules we ship in Luaotfload. For
loading from Context we still need a step to distinguish between the
namespaced versions of the files, the pure ones in texmf/…/base, and the
Luatex-Fonts ones.

But yeah, setting

    [run]
        fontloader = unpackaged

in the luaotfloadrc now works splendidly.
---
 src/luaotfload-configuration.lua |  8 +++---
 src/luaotfload-init.lua          | 53 +++++++++++++++++++++++++---------------
 2 files changed, 38 insertions(+), 23 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua
index 6f94195..1fe30fe 100644
--- a/src/luaotfload-configuration.lua
+++ b/src/luaotfload-configuration.lua
@@ -127,9 +127,11 @@ local feature_presets = {
 --doc]]--
 
 local registered_loaders = {
-  default   = luaotfloadstatus and luaotfloadstatus.notes.loader or "reference",
-  reference = "reference",
-  tl2014    = "tl2014",
+  default    = luaotfloadstatus and luaotfloadstatus.notes.loader or "reference",
+  reference  = "reference",
+  unpackaged = "unpackaged",
+  context    = "context",
+  tl2014     = "tl2014",
 }
 
 --[[doc--
diff --git a/src/luaotfload-init.lua b/src/luaotfload-init.lua
index 1d6d1a5..c2899d7 100644
--- a/src/luaotfload-init.lua
+++ b/src/luaotfload-init.lua
@@ -215,14 +215,11 @@ local init_main = function ()
 
   --doc]]--
 
-  logreport ("log", 4, "init",
-             "Loading fontloader as merged package.")
-  load_fontloader_module (luaotfload.fontloader_package)
-
-  ---load_fontloader_module "font-odv.lua" --- <= Devanagari support from Context
+  local fontloader = config.luaotfload and config.luaotfload.run.fontloader
+                                        or "reference"
 
-  if not fonts then
-    logreport ("log", 4, "init",
+  if fontloader == "unpackaged" then
+    logreport ("both", 4, "init",
                "Loading fontloader components individually.")
     --- The loading sequence is known to change, so this might have to be
     --- updated with future updates. Do not modify it though unless there is
@@ -243,27 +240,43 @@ local init_main = function ()
 
     --- These constitute the fontloader proper.
     load_fontloader_module "data-con"
-    load_fontloader_module "luatex-basics-nod"
+    load_fontloader_module "basics-nod"
     load_fontloader_module "font-ini"
     load_fontloader_module "font-con"
-    load_fontloader_module "luatex-fonts-enc"
+    load_fontloader_module "fonts-enc"
     load_fontloader_module "font-cid"
     load_fontloader_module "font-map"
-    load_fontloader_module "luatex-fonts-syn"
-    load_fontloader_module "luatex-fonts-tfm"
+    load_fontloader_module "fonts-syn"
+    load_fontloader_module "fonts-tfm"
     load_fontloader_module "font-oti"
     load_fontloader_module "font-otf"
     load_fontloader_module "font-otb"
-    load_fontloader_module "luatex-fonts-inj"  --> since 2014-01-07, replaces node-inj.lua
-    load_fontloader_module "luatex-fonts-ota"
-    load_fontloader_module "luatex-fonts-otn"  --> since 2014-01-07, replaces font-otn.lua
-    load_fontloader_module "font-otp"          --> since 2013-04-23
-    load_fontloader_module "luatex-fonts-lua"
+    load_fontloader_module "fonts-inj"  --> since 2014-01-07, replaces node-inj.lua
+    load_fontloader_module "fonts-ota"
+    load_fontloader_module "fonts-otn"  --> since 2014-01-07, replaces font-otn.lua
+    load_fontloader_module "font-otp"   --> since 2013-04-23
+    load_fontloader_module "fonts-lua"
     load_fontloader_module "font-def"
-    load_fontloader_module "luatex-fonts-def"
-    load_fontloader_module "luatex-fonts-ext"
-    load_fontloader_module "luatex-fonts-cbk"
-  end --- non-merge fallback scope
+    load_fontloader_module "fonts-def"
+    load_fontloader_module "fonts-ext"
+    load_fontloader_module "fonts-cbk"
+
+  elseif fontloader == "context" then
+    logreport ("both", 4, "init",
+               "Loading fontloader components from context.")
+    logreport ("both", 0, "init", "NOT IMPLEMENTED YET.")
+    os.exit(-42)
+
+  elseif fontloader then
+    fontloader = tostring (fontloader)
+    --- “reference”, “default”
+    logreport ("both", 4, "init",
+               "Attempting to load fontloader “%s”.",
+               fontloader)
+    load_fontloader_module (luaotfload.fontloader_package)
+  end
+
+  ---load_fontloader_module "font-odv.lua" --- <= Devanagari support from Context
 
 end --- [init_main]
 
-- 
cgit v1.2.3


From a67b8e37ab846a79c69ee26761835934680e41b4 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Tue, 10 Nov 2015 08:02:36 +0100
Subject: [main] implement a loader for Context files

---
 src/luaotfload-main.lua | 39 +++++++++++++++++++++++++++++----------
 1 file changed, 29 insertions(+), 10 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua
index 686ce0e..0ed57da 100644
--- a/src/luaotfload-main.lua
+++ b/src/luaotfload-main.lua
@@ -99,16 +99,24 @@ end
 
 local make_loader_name = function (prefix, name)
     local msg = luaotfload.log and luaotfload.log.report or print
-    if prefix and name then
-        msg ("log", 7, "load",
-             "Composing module name from constituents %s, %s",
-             prefix, name)
-        return prefix .. "-" .. name .. ".lua"
+    if not name then
+        msg ("both", 0, "load",
+             "Fatal error: make_loader_name (“%s”, “%s”).",
+             tostring (prefix), tostring (name))
+        return "dummy-name"
     end
-    msg ("log", 7, "load",
-         "Loading module %s literally.",
-         tostring (name))
-    return name
+    name = tostring (name)
+    if prefix == false then
+        msg ("log", 9, "load",
+             "No prefix requested, passing module name “%s” unmodified.",
+             name)
+        return tostring (name) .. ".lua"
+    end
+    prefix = tostring (prefix)
+    msg ("log", 9, "load",
+         "Composing module name from constituents %s, %s.",
+         prefix, name)
+    return prefix .. "-" .. name .. ".lua"
 end
 
 local timing_info = {
@@ -135,7 +143,17 @@ end
 --doc]]--
 
 local dummy_loader = function (name)
-    luaotfload.log.report("log", 3, "load", "Skipping module “%s”.", name)
+    luaotfload.log.report("log", 3, "load", "Skipping module “%s” on purpose.", name)
+end
+
+local context_loader = function (name)
+    luaotfload.log.report("log", 3, "load", "Loading module “%s” from Context.", name)
+    local t_0 = osgettimeofday ()
+    local modname = make_loader_name (false, name)
+    local data = require (modname)
+    local t_end = osgettimeofday ()
+    timing_info.t_load [name] = t_end - t_0
+    return data
 end
 
 local install_loaders = function ()
@@ -143,6 +161,7 @@ local install_loaders = function ()
     local loadmodule   = make_loader "luaotfload"
     loaders.luaotfload = loadmodule
     loaders.fontloader = make_loader "fontloader"
+    loaders.context    = context_loader
     loaders.ignore     = dummy_loader
 ----loaders.plaintex   = make_loader "luatex" --=> for Luatex-Plain
 
-- 
cgit v1.2.3


From 43f18a389a1380e950cea104f3ada32c88c47863 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Tue, 10 Nov 2015 08:06:55 +0100
Subject: [init] access the context loader from init
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Works fine now by choosing the “context” fontloader in luaotfloadrc.
---
 src/luaotfload-init.lua | 37 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 36 insertions(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/luaotfload-init.lua b/src/luaotfload-init.lua
index c2899d7..ca5d9ec 100644
--- a/src/luaotfload-init.lua
+++ b/src/luaotfload-init.lua
@@ -203,6 +203,7 @@ end --- [init_adapt]
 local init_main = function ()
 
   local load_fontloader_module = luaotfload.loaders.fontloader
+  local load_context_module    = luaotfload.loaders.context
   local ignore_module          = luaotfload.loaders.ignore
 
   --[[doc--
@@ -265,7 +266,41 @@ local init_main = function ()
     logreport ("both", 4, "init",
                "Loading fontloader components from context.")
     logreport ("both", 0, "init", "NOT IMPLEMENTED YET.")
-    os.exit(-42)
+    --- Since 2.6 those are directly provided by the Lualibs package.
+    ignore_module "l-lua"
+    ignore_module "l-lpeg"
+    ignore_module "l-function"
+    ignore_module "l-string"
+    ignore_module "l-table"
+    ignore_module "l-io"
+    ignore_module "l-file"
+    ignore_module "l-boolean"
+    ignore_module "l-math"
+    ignore_module "util-str"
+
+    --- These constitute the fontloader proper.
+    load_context_module "luatex-basics-gen"
+    load_context_module "data-con"
+    load_context_module "luatex-basics-nod"
+    load_context_module "font-ini"
+    load_context_module "font-con"
+    load_context_module "luatex-fonts-enc"
+    load_context_module "font-cid"
+    load_context_module "font-map"
+    load_context_module "luatex-fonts-syn"
+    load_context_module "luatex-fonts-tfm"
+    load_context_module "font-oti"
+    load_context_module "font-otf"
+    load_context_module "font-otb"
+    load_context_module "luatex-fonts-inj"  --> since 2014-01-07, replaces node-inj.lua
+    load_context_module "luatex-fonts-ota"
+    load_context_module "luatex-fonts-otn"  --> since 2014-01-07, replaces font-otn.lua
+    load_context_module "font-otp"   --> since 2013-04-23
+    load_context_module "luatex-fonts-lua"
+    load_context_module "font-def"
+    load_context_module "luatex-fonts-def"
+    load_context_module "luatex-fonts-ext"
+    load_context_module "luatex-fonts-cbk"
 
   elseif fontloader then
     fontloader = tostring (fontloader)
-- 
cgit v1.2.3


From 54e74772fab02405bf90362d76884e88b63eb381 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Thu, 12 Nov 2015 08:15:06 +0100
Subject: [conf] implement path-based fontloader specification
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Makes the config test for paths specified as “context:/path/to/texmf”
and return a ("context", path) pair that is intended for use as the base
path of the fontloader files.
---
 src/luaotfload-configuration.lua | 31 ++++++++++++++++++++++++++++---
 1 file changed, 28 insertions(+), 3 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua
index 1fe30fe..4ac8c50 100644
--- a/src/luaotfload-configuration.lua
+++ b/src/luaotfload-configuration.lua
@@ -134,6 +134,33 @@ local registered_loaders = {
   tl2014     = "tl2014",
 }
 
+local pick_fontloader = function (s)
+  local ldr = registered_loaders[s]
+  if ldr ~= nil and type (ldr) == "string" then
+    logreport ("log", 2, "conf", "Using predefined fontloader \"%s\".", ldr)
+    return ldr
+  end
+  local idx = stringfind (s, ":")
+  if idx and idx > 2 then
+    if stringsub (s, 1, idx - 1) == "context" then
+      local pth = stringsub (s, idx + 1)
+      pth = stringstrip (pth)
+      logreport ("log", 2, "conf", "Context base path specified at \"%s\".", pth)
+      if lfsisdir (pth) then
+        logreport ("log", 5, "conf", "Context base path exists at \"%s\".", pth)
+        return { "context", pth }
+      end
+      pth = kpseexpand_path (pth)
+      if lfsisdir (pth) then
+        logreport ("log", 5, "conf", "Context base path exists at \"%s\".", pth)
+        return { "context", pth }
+      end
+      logreport ("both", 0, "conf", "Context base path not found at \"%s\".", pth)
+    end
+  end
+  return nil
+end
+
 --[[doc--
 
   The ``post_linebreak_filter`` has been made the default callback for
@@ -468,10 +495,8 @@ local option_spec = {
       in_t      = string_t,
       out_t     = string_t,
       transform = function (id)
-        local ldr = registered_loaders[id]
+        local ldr = pick_fontloader (id)
         if ldr ~= nil then
-          logreport ("log", 2, "conf",
-                     "Using predefined fontloader \"%s\".", ldr)
           return ldr
         end
         logreport ("log", 0, "conf",
-- 
cgit v1.2.3


From d8f160fc0518155a9ae995dfb16fc0d29937aa7c Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Tue, 17 Nov 2015 22:29:41 +0100
Subject: [conf] correctly set default fontloader

---
 src/luaotfload-configuration.lua | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua
index 4ac8c50..0ce9467 100644
--- a/src/luaotfload-configuration.lua
+++ b/src/luaotfload-configuration.lua
@@ -198,7 +198,7 @@ local default_config = {
     definer        = "patch",
     log_level      = 0,
     color_callback = "post_linebreak_filter",
-    live           = true,
+    fontloader     = "default",
   },
   misc = {
     bisect         = false,
-- 
cgit v1.2.3


From 6e2184e0226de1e76e7195493b781b99f56eb879 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Tue, 17 Nov 2015 22:33:04 +0100
Subject: [conf] return path instead of pair to prevent type-mismatch

The (fontloader, path) return value cannot pass the post-config
validation because the value of ``run.fontloader`` is of type string.
Since the path is always checked during configuration we can just
forward it as-is and attempt a path load later on.
---
 src/luaotfload-configuration.lua | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua
index 0ce9467..efed573 100644
--- a/src/luaotfload-configuration.lua
+++ b/src/luaotfload-configuration.lua
@@ -148,12 +148,12 @@ local pick_fontloader = function (s)
       logreport ("log", 2, "conf", "Context base path specified at \"%s\".", pth)
       if lfsisdir (pth) then
         logreport ("log", 5, "conf", "Context base path exists at \"%s\".", pth)
-        return { "context", pth }
+        return pth
       end
       pth = kpseexpand_path (pth)
       if lfsisdir (pth) then
         logreport ("log", 5, "conf", "Context base path exists at \"%s\".", pth)
-        return { "context", pth }
+        return pth
       end
       logreport ("both", 0, "conf", "Context base path not found at \"%s\".", pth)
     end
-- 
cgit v1.2.3


From ec1984766b941087425acca1b4d4fa8201c86639 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Tue, 17 Nov 2015 22:51:30 +0100
Subject: [init] rework fontloader choices

---
 src/luaotfload-init.lua | 35 +++++++++++++++++++++++++++++------
 1 file changed, 29 insertions(+), 6 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-init.lua b/src/luaotfload-init.lua
index ca5d9ec..2676ec7 100644
--- a/src/luaotfload-init.lua
+++ b/src/luaotfload-init.lua
@@ -8,6 +8,7 @@
 --
 
 local setmetatable = setmetatable
+local kpselookup   = kpse.lookup
 
 --[[doc--
 
@@ -218,6 +219,7 @@ local init_main = function ()
 
   local fontloader = config.luaotfload and config.luaotfload.run.fontloader
                                         or "reference"
+  fontloader = tostring (fontloader)
 
   if fontloader == "unpackaged" then
     logreport ("both", 4, "init",
@@ -302,13 +304,34 @@ local init_main = function ()
     load_context_module "luatex-fonts-ext"
     load_context_module "luatex-fonts-cbk"
 
-  elseif fontloader then
-    fontloader = tostring (fontloader)
-    --- “reference”, “default”
-    logreport ("both", 4, "init",
-               "Attempting to load fontloader “%s”.",
+  elseif lfs.isdir (fontloader) then
+    logreport ("both", 2, "init",
+               "Attempting to load Context files under prefix “%s”.",
+               fontloader)
+    TODO()
+
+  elseif lfs.isfile (fontloader) then
+    logreport ("both", 2, "init",
+               "Attempting to load fontloader from absolute path “%s”.",
+               fontloader)
+    local _void = require (fontloader)
+
+  elseif kpselookup (fontloader) then
+    local pth = kpselookup (fontloader)
+    logreport ("both", 2, "init",
+               "Attempting to load fontloader “%s” from kpse-resolved path “%s”.",
+               fontloader, path)
+    local _void = require (path)
+
+  else --- “reference”, “default”
+    logreport ("log", 6, "init",
+               "No match for fontloader spec “%s”.",
+               fontloader)
+    fontloader = luaotfload.fontloader_package
+    logreport ("log", 4, "init",
+               "Using predefined fontloader “%s”.",
                fontloader)
-    load_fontloader_module (luaotfload.fontloader_package)
+    load_fontloader_module (fontloader)
   end
 
   ---load_fontloader_module "font-odv.lua" --- <= Devanagari support from Context
-- 
cgit v1.2.3


From 2bcb46cfec8b8d988e12f44d6297f4fb5f9e879b Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Tue, 17 Nov 2015 23:08:58 +0100
Subject: [init] unify both Context load branches

---
 src/luaotfload-init.lua | 103 +++++++++++++++++++++++++++++-------------------
 1 file changed, 63 insertions(+), 40 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-init.lua b/src/luaotfload-init.lua
index 2676ec7..a42801f 100644
--- a/src/luaotfload-init.lua
+++ b/src/luaotfload-init.lua
@@ -182,6 +182,65 @@ local pop_namespaces = function (normalglobal,
     end
 end
 
+local context_modules = {
+
+  --- Since 2.6 those are directly provided by the Lualibs package.
+  { false, "l-lua"      },
+  { false, "l-lpeg"     },
+  { false, "l-function" },
+  { false, "l-string"   },
+  { false, "l-table"    },
+  { false, "l-io"       },
+  { false, "l-file"     },
+  { false, "l-boolean"  },
+  { false, "l-math"     },
+  { false, "util-str"   },
+
+  --- These constitute the fontloader proper.
+  { true,  "luatex-basics-gen" },
+  { true,  "data-con"          },
+  { true,  "luatex-basics-nod" },
+  { true,  "font-ini"          },
+  { true,  "font-con"          },
+  { true,  "luatex-fonts-enc"  },
+  { true,  "font-cid"          },
+  { true,  "font-map"          },
+  { true,  "luatex-fonts-syn"  },
+  { true,  "luatex-fonts-tfm"  },
+  { true,  "font-oti"          },
+  { true,  "font-otf"          },
+  { true,  "font-otb"          },
+  { true,  "luatex-fonts-inj"  }, --> since 2014-01-07, replaces node-inj.lua
+  { true,  "luatex-fonts-ota"  },
+  { true,  "luatex-fonts-otn"  }, --> since 2014-01-07, replaces font-otn.lua
+  { true,  "font-otp"          }, --> since 2013-04-23
+  { true,  "luatex-fonts-lua"  },
+  { true,  "font-def"          },
+  { true,  "luatex-fonts-def"  },
+  { true,  "luatex-fonts-ext"  },
+  { true,  "luatex-fonts-cbk"  },
+
+} --[[context_modules]]
+
+local load_context_modules = function (pth)
+
+  local load_context_module    = luaotfload.loaders.context
+  local ignore_module          = luaotfload.loaders.ignore
+
+  logreport ("both", 2, "init",
+             "Loading fontloader components from context.")
+  local n = #context_modules
+  for i = 1, n do
+    local state, spec = unpack (context_modules [i])
+    if state == false then
+      ignore_module (spec)
+    elseif state == true then
+      load_context_module (spec)
+    end
+  end
+
+end
+
 local init_adapt = function ()
 
   local context_environment  = { }
@@ -204,7 +263,6 @@ end --- [init_adapt]
 local init_main = function ()
 
   local load_fontloader_module = luaotfload.loaders.fontloader
-  local load_context_module    = luaotfload.loaders.context
   local ignore_module          = luaotfload.loaders.ignore
 
   --[[doc--
@@ -265,50 +323,15 @@ local init_main = function ()
     load_fontloader_module "fonts-cbk"
 
   elseif fontloader == "context" then
-    logreport ("both", 4, "init",
-               "Loading fontloader components from context.")
-    logreport ("both", 0, "init", "NOT IMPLEMENTED YET.")
-    --- Since 2.6 those are directly provided by the Lualibs package.
-    ignore_module "l-lua"
-    ignore_module "l-lpeg"
-    ignore_module "l-function"
-    ignore_module "l-string"
-    ignore_module "l-table"
-    ignore_module "l-io"
-    ignore_module "l-file"
-    ignore_module "l-boolean"
-    ignore_module "l-math"
-    ignore_module "util-str"
-
-    --- These constitute the fontloader proper.
-    load_context_module "luatex-basics-gen"
-    load_context_module "data-con"
-    load_context_module "luatex-basics-nod"
-    load_context_module "font-ini"
-    load_context_module "font-con"
-    load_context_module "luatex-fonts-enc"
-    load_context_module "font-cid"
-    load_context_module "font-map"
-    load_context_module "luatex-fonts-syn"
-    load_context_module "luatex-fonts-tfm"
-    load_context_module "font-oti"
-    load_context_module "font-otf"
-    load_context_module "font-otb"
-    load_context_module "luatex-fonts-inj"  --> since 2014-01-07, replaces node-inj.lua
-    load_context_module "luatex-fonts-ota"
-    load_context_module "luatex-fonts-otn"  --> since 2014-01-07, replaces font-otn.lua
-    load_context_module "font-otp"   --> since 2013-04-23
-    load_context_module "luatex-fonts-lua"
-    load_context_module "font-def"
-    load_context_module "luatex-fonts-def"
-    load_context_module "luatex-fonts-ext"
-    load_context_module "luatex-fonts-cbk"
+    logreport ("both", 2, "init",
+               "Attempting to load Context modules in lookup path.")
+    load_context_modules ()
 
   elseif lfs.isdir (fontloader) then
     logreport ("both", 2, "init",
                "Attempting to load Context files under prefix “%s”.",
                fontloader)
-    TODO()
+    load_context_modules (fontloader)
 
   elseif lfs.isfile (fontloader) then
     logreport ("both", 2, "init",
-- 
cgit v1.2.3


From 0bfd03b4fcdf1cbf3f1fb8e8afa7a2c4d917715a Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Tue, 17 Nov 2015 23:56:44 +0100
Subject: [main,init] implement path dependent loading of context modules

---
 src/luaotfload-init.lua | 61 ++++++++++++++++++++++++++++---------------------
 src/luaotfload-main.lua | 39 ++++++++++++++++++++++++++-----
 2 files changed, 68 insertions(+), 32 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-init.lua b/src/luaotfload-init.lua
index a42801f..2755f78 100644
--- a/src/luaotfload-init.lua
+++ b/src/luaotfload-init.lua
@@ -182,6 +182,10 @@ local pop_namespaces = function (normalglobal,
     end
 end
 
+--- below paths are relative to the texmf-context
+local ltx = "tex/generic/context/luatex"
+local ctx = "tex/context/base"
+
 local context_modules = {
 
   --- Since 2.6 those are directly provided by the Lualibs package.
@@ -197,28 +201,28 @@ local context_modules = {
   { false, "util-str"   },
 
   --- These constitute the fontloader proper.
-  { true,  "luatex-basics-gen" },
-  { true,  "data-con"          },
-  { true,  "luatex-basics-nod" },
-  { true,  "font-ini"          },
-  { true,  "font-con"          },
-  { true,  "luatex-fonts-enc"  },
-  { true,  "font-cid"          },
-  { true,  "font-map"          },
-  { true,  "luatex-fonts-syn"  },
-  { true,  "luatex-fonts-tfm"  },
-  { true,  "font-oti"          },
-  { true,  "font-otf"          },
-  { true,  "font-otb"          },
-  { true,  "luatex-fonts-inj"  }, --> since 2014-01-07, replaces node-inj.lua
-  { true,  "luatex-fonts-ota"  },
-  { true,  "luatex-fonts-otn"  }, --> since 2014-01-07, replaces font-otn.lua
-  { true,  "font-otp"          }, --> since 2013-04-23
-  { true,  "luatex-fonts-lua"  },
-  { true,  "font-def"          },
-  { true,  "luatex-fonts-def"  },
-  { true,  "luatex-fonts-ext"  },
-  { true,  "luatex-fonts-cbk"  },
+  { ltx,   "luatex-basics-gen" },
+  { ctx,   "data-con"          },
+  { ltx,   "luatex-basics-nod" },
+  { ctx,   "font-ini"          },
+  { ctx,   "font-con"          },
+  { ltx,   "luatex-fonts-enc"  },
+  { ctx,   "font-cid"          },
+  { ctx,   "font-map"          },
+  { ltx,   "luatex-fonts-syn"  },
+  { ltx,   "luatex-fonts-tfm"  },
+  { ctx,   "font-oti"          },
+  { ctx,   "font-otf"          },
+  { ctx,   "font-otb"          },
+  { ltx,   "luatex-fonts-inj"  }, --> since 2014-01-07, replaces node-inj.lua
+  { ltx,   "luatex-fonts-ota"  },
+  { ltx,   "luatex-fonts-otn"  }, --> since 2014-01-07, replaces font-otn.lua
+  { ctx,   "font-otp"          }, --> since 2013-04-23
+  { ltx,   "luatex-fonts-lua"  },
+  { ctx,   "font-def"          },
+  { ltx,   "luatex-fonts-def"  },
+  { ltx,   "luatex-fonts-ext"  },
+  { ltx,   "luatex-fonts-cbk"  },
 
 } --[[context_modules]]
 
@@ -231,11 +235,16 @@ local load_context_modules = function (pth)
              "Loading fontloader components from context.")
   local n = #context_modules
   for i = 1, n do
-    local state, spec = unpack (context_modules [i])
-    if state == false then
+    local sub, spec = unpack (context_modules [i])
+    if sub == false then
       ignore_module (spec)
-    elseif state == true then
-      load_context_module (spec)
+    elseif type (sub) == "string" then
+      load_context_module (spec, file.join (pth, sub))
+    else
+      logreport ("both", 0, "init",
+                 "Internal error, please report. \z
+                  This is not your fault.")
+      os.exit (-1)
     end
   end
 
diff --git a/src/luaotfload-main.lua b/src/luaotfload-main.lua
index 0ed57da..62765e4 100644
--- a/src/luaotfload-main.lua
+++ b/src/luaotfload-main.lua
@@ -98,7 +98,8 @@ end
 --doc]]--
 
 local make_loader_name = function (prefix, name)
-    local msg = luaotfload.log and luaotfload.log.report or print
+    local msg = luaotfload.log and luaotfload.log.report
+             or function (...) texio.write_nl ("log", ...) end
     if not name then
         msg ("both", 0, "load",
              "Fatal error: make_loader_name (“%s”, “%s”).",
@@ -143,17 +144,43 @@ end
 --doc]]--
 
 local dummy_loader = function (name)
-    luaotfload.log.report("log", 3, "load", "Skipping module “%s” on purpose.", name)
+    luaotfload.log.report ("log", 3, "load",
+                           "Skipping module “%s” on purpose.",
+                           name)
 end
 
-local context_loader = function (name)
-    luaotfload.log.report("log", 3, "load", "Loading module “%s” from Context.", name)
+local context_loader = function (name, path)
+    luaotfload.log.report ("log", 3, "load",
+                           "Loading module “%s” from Context.",
+                           name)
     local t_0 = osgettimeofday ()
     local modname = make_loader_name (false, name)
-    local data = require (modname)
+    local modpath = modname
+    if path then
+        if lfs.isdir (path) then
+            luaotfload.log.report ("log", 3, "load",
+                                   "Prepending path “%s”.",
+                                   path)
+            modpath = file.join (path, modname)
+        else
+            luaotfload.log.report ("both", 0, "load",
+                                   "Non-existant path “%s” specified, ignoring.",
+                                   path)
+        end
+    end
+    local ret = require (modpath)
     local t_end = osgettimeofday ()
     timing_info.t_load [name] = t_end - t_0
-    return data
+
+    if ret ~= true then
+        --- require () returns “true” upon success unless the loaded file
+        --- yields a non-zero exit code. This isn’t per se indicating that
+        --- something isn’t right, but against HH’s coding practices. We’ll
+        --- silently ignore this ever happening on lower log levels.
+        luaotfload.log.report ("log", 4, "load",
+                               "Module “%s” returned “%s”.", ret)
+    end
+    return ret
 end
 
 local install_loaders = function ()
-- 
cgit v1.2.3


From 17b8126f260a4f80a041ab179ceb68abf425c359 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Wed, 18 Nov 2015 07:47:47 +0100
Subject: [init] fix pathless Context module loading

---
 src/luaotfload-init.lua | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-init.lua b/src/luaotfload-init.lua
index 2755f78..f1d1a8e 100644
--- a/src/luaotfload-init.lua
+++ b/src/luaotfload-init.lua
@@ -228,8 +228,8 @@ local context_modules = {
 
 local load_context_modules = function (pth)
 
-  local load_context_module    = luaotfload.loaders.context
-  local ignore_module          = luaotfload.loaders.ignore
+  local load_module   = luaotfload.loaders.context
+  local ignore_module = luaotfload.loaders.ignore
 
   logreport ("both", 2, "init",
              "Loading fontloader components from context.")
@@ -239,7 +239,11 @@ local load_context_modules = function (pth)
     if sub == false then
       ignore_module (spec)
     elseif type (sub) == "string" then
-      load_context_module (spec, file.join (pth, sub))
+      if pth then
+        load_module (spec, file.join (pth, sub))
+      else
+        load_module (spec)
+      end
     else
       logreport ("both", 0, "init",
                  "Internal error, please report. \z
-- 
cgit v1.2.3


From 85bc85c1f116f759c59ef06f27bb37c0d2bb80f6 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Wed, 18 Nov 2015 07:52:24 +0100
Subject: [init] handle case for reference loader first
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Since it’s the trivial case, expected in 99 % of runs.
---
 src/luaotfload-init.lua | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-init.lua b/src/luaotfload-init.lua
index f1d1a8e..5b21d70 100644
--- a/src/luaotfload-init.lua
+++ b/src/luaotfload-init.lua
@@ -292,7 +292,11 @@ local init_main = function ()
                                         or "reference"
   fontloader = tostring (fontloader)
 
-  if fontloader == "unpackaged" then
+  if fontloader == "reference" or fontloader == "default" then
+    logreport ("log", 4, "init", "Using reference fontloader.")
+    load_fontloader_module (luaotfload.fontloader_package)
+
+  elseif fontloader == "unpackaged" then
     logreport ("both", 4, "init",
                "Loading fontloader components individually.")
     --- The loading sequence is known to change, so this might have to be
@@ -359,7 +363,7 @@ local init_main = function ()
                fontloader, path)
     local _void = require (path)
 
-  else --- “reference”, “default”
+  else
     logreport ("log", 6, "init",
                "No match for fontloader spec “%s”.",
                fontloader)
@@ -372,6 +376,9 @@ local init_main = function ()
 
   ---load_fontloader_module "font-odv.lua" --- <= Devanagari support from Context
 
+  logreport ("both", 0, "init",
+             "Context OpenType loader version “%s”",
+             fonts.handlers.otf.version)
 end --- [init_main]
 
 local init_cleanup = function (store)
-- 
cgit v1.2.3


From 671f654e671cc2b255b21b937bbfd7f63cf43c22 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Wed, 18 Nov 2015 08:04:17 +0100
Subject: [init] treat known fontloaders as special case

Defaulting too soon will prevent loading other loaders like the TL 2014
one we ship for backwards compatibility.
---
 src/luaotfload-init.lua | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-init.lua b/src/luaotfload-init.lua
index 5b21d70..0f7464a 100644
--- a/src/luaotfload-init.lua
+++ b/src/luaotfload-init.lua
@@ -363,13 +363,19 @@ local init_main = function ()
                fontloader, path)
     local _void = require (path)
 
+  elseif fontloader then
+    logreport ("log", 4, "init",
+               "Using predefined fontloader “%s”.",
+               fontloader)
+    load_fontloader_module (fontloader)
+
   else
-    logreport ("log", 6, "init",
-               "No match for fontloader spec “%s”.",
+    logreport ("log", 4, "init",
+               "No match for requested fontloader “%s”.",
                fontloader)
     fontloader = luaotfload.fontloader_package
     logreport ("log", 4, "init",
-               "Using predefined fontloader “%s”.",
+               "Defaulting to predefined fontloader “%s”.",
                fontloader)
     load_fontloader_module (fontloader)
   end
-- 
cgit v1.2.3


From 3a12cad2b229d501ef6835d27797a1a18fb981ef Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Fri, 20 Nov 2015 00:23:45 +0100
Subject: [conf] plug in the correct resolvers (fallout from db reorganization)

---
 src/luaotfload-configuration.lua | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-configuration.lua b/src/luaotfload-configuration.lua
index efed573..57311dc 100644
--- a/src/luaotfload-configuration.lua
+++ b/src/luaotfload-configuration.lua
@@ -308,9 +308,9 @@ local set_name_resolver = function ()
     --- replace the resolver from luatex-fonts
     if config.luaotfload.db.resolver == "cached" then
         logreport ("both", 2, "cache", "Caching of name: lookups active.")
-        names.resolvespec  = names.resolve_cached
+        names.resolvespec  = fonts.names.lookup_font_name_cached
     else
-        names.resolvespec  = names.resolve_name
+        names.resolvespec  = fonts.names.lookup_font_name
     end
   end
   return true
-- 
cgit v1.2.3