From 50476780411fb84f1f4d0dcb7cd865c1eba4141b Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Sat, 20 Aug 2016 21:31:35 +0200
Subject: [fontloader] sync with Context as of 2016-08-29

---
 src/fontloader/misc/fontloader-data-con.lua      |   2 +-
 src/fontloader/misc/fontloader-font-cff.lua      | 231 ++++--
 src/fontloader/misc/fontloader-font-dsp.lua      |   3 +-
 src/fontloader/misc/fontloader-font-onr.lua      | 197 +++--
 src/fontloader/misc/fontloader-font-osd.lua      | 156 ++--
 src/fontloader/misc/fontloader-font-ota.lua      |  54 +-
 src/fontloader/misc/fontloader-font-otj.lua      |  42 +-
 src/fontloader/misc/fontloader-font-otl.lua      |   2 +-
 src/fontloader/misc/fontloader-font-otr.lua      | 181 +----
 src/fontloader/misc/fontloader-font-ots.lua      |  68 +-
 src/fontloader/misc/fontloader-l-table.lua       |  15 +-
 src/fontloader/misc/fontloader-util-str.lua      |  88 ++-
 src/fontloader/runtime/fontloader-basics-gen.lua | 100 ++-
 src/fontloader/runtime/fontloader-reference.lua  | 915 ++++++++++++-----------
 14 files changed, 1145 insertions(+), 909 deletions(-)

(limited to 'src')

diff --git a/src/fontloader/misc/fontloader-data-con.lua b/src/fontloader/misc/fontloader-data-con.lua
index 240538d..c79fca7 100644
--- a/src/fontloader/misc/fontloader-data-con.lua
+++ b/src/fontloader/misc/fontloader-data-con.lua
@@ -91,7 +91,7 @@ function containers.read(container,name)
     local storage = container.storage
     local stored = storage[name]
     if not stored and container.enabled and caches and containers.usecache then
-        stored = caches.loaddata(container.readables,name)
+        stored = caches.loaddata(container.readables,name,container.writable)
         if stored and stored.cache_version == container.version then
             if trace_cache or trace_containers then
                 report_containers("action %a, category %a, name %a","load",container.subcategory,name)
diff --git a/src/fontloader/misc/fontloader-font-cff.lua b/src/fontloader/misc/fontloader-font-cff.lua
index 8c57b47..1c6bd56 100644
--- a/src/fontloader/misc/fontloader-font-cff.lua
+++ b/src/fontloader/misc/fontloader-font-cff.lua
@@ -26,6 +26,7 @@ local concat, remove = table.concat, table.remove
 local floor, abs, round, ceil = math.floor, math.abs, math.round, math.ceil
 local P, C, R, S, C, Cs, Ct = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Ct
 local lpegmatch = lpeg.match
+local formatters = string.formatters
 
 local readers           = fonts.handlers.otf.readers
 local streamreader      = readers.streamreader
@@ -594,6 +595,7 @@ do
     local ymax       = 0
     local checked    = false
     local keepcurve  = false
+    local version    = 2
 
     local function showstate(where)
         report("%w%-10s : [%s] n=%i",depth*2,where,concat(stack," ",1,top),top)
@@ -1052,13 +1054,114 @@ do
         end
     end
 
-    local function unsupported()
+    local function unsupported(t)
         if trace_charstrings then
-            showstate("unsupported")
+            showstate("unsupported " .. t)
         end
         top = 0
     end
 
+    local function unsupportedsub(t)
+        if trace_charstrings then
+            showstate("unsupported sub " .. t)
+        end
+        top = 0
+    end
+
+    -- type 1 (not used in type 2)
+
+    local function getstem3()
+        if trace_charstrings then
+            showstate("stem3")
+        end
+        top = 0
+    end
+
+    local function divide()
+        if version == 1 then
+            local d = stack[top]
+            top = top - 1
+            stack[top] = stack[top] / d
+        end
+    end
+
+    local function closepath()
+        if version == 1 then
+            if trace_charstrings then
+                showstate("closepath")
+            end
+        end
+        top = 0
+    end
+
+    local function hsbw()
+        if version == 1 then
+            if trace_charstrings then
+                showstate("dotsection")
+            end
+            width = stack[top]
+        end
+        top = 0
+    end
+
+    local function seac()
+        if version == 1 then
+            if trace_charstrings then
+                showstate("seac")
+            end
+        end
+        top = 0
+    end
+
+    local function sbw()
+        if version == 1 then
+            if trace_charstrings then
+                showstate("sbw")
+            end
+            width = stack[top-1]
+        end
+        top = 0
+    end
+
+    -- these are probably used for special cases i.e. call out to postscript
+
+    local function callothersubr()
+        if version == 1 then
+            -- we don't support this (ok, we could mimick these othersubs)
+            if trace_charstrings then
+                showstate("callothersubr (unsupported)")
+            end
+        end
+        top = 0
+    end
+
+    local function pop()
+        if version == 1 then
+            -- we don't support this
+            if trace_charstrings then
+                showstate("pop (unsupported)")
+            end
+            top = top + 1
+            stack[top] = 0 -- a dummy
+        else
+            top = 0
+        end
+    end
+
+    local function setcurrentpoint()
+        if version == 1 then
+            -- we don't support this
+            if trace_charstrings then
+                showstate("pop (unsupported)")
+            end
+            x = x + stack[top-1]
+            y = y + stack[top]
+        end
+        top = 0
+    end
+
+    -- so far for unsupported postscript
+
     -- Bah, we cannot use a fast lpeg because a hint has an unknown size and a
     -- runtime capture cannot handle that well.
 
@@ -1076,7 +1179,7 @@ do
         unsupported,  -- 10 -- calllocal,
         unsupported,  -- 11 -- callreturn,
         unsupported,  -- 12 -- elsewhere
-        unsupported,  -- 13 -- hsbw
+        hsbw,         -- 13 -- hsbw (type 1 cff)
         unsupported,  -- 14 -- endchar,
         unsupported,  -- 15
         unsupported,  -- 16
@@ -1098,6 +1201,17 @@ do
     }
 
     local subactions = {
+        -- cff 1
+        [000] = dotsection,
+        [001] = getstem3,
+        [002] = getstem3,
+        [006] = seac,
+        [007] = sbw,
+        [012] = divide,
+        [016] = callothersubr,
+        [017] = pop,
+        [033] = setcurrentpoint,
+        -- cff 2
         [034] = hflex,
         [035] = flex,
         [036] = hflex1,
@@ -1107,23 +1221,29 @@ do
     local p_bytes = Ct((P(1)/byte)^0)
 
     local function call(scope,list,bias,process)
-        local index = stack[top] + bias
-        top = top - 1
-        if trace_charstrings then
-            showvalue(scope,index,true)
-        end
-        local str = list[index]
-        if str then
-            if type(str) == "string" then
-                str = lpegmatch(p_bytes,str)
-                list[index] = str
-            end
-            depth = depth + 1
-            process(str)
-            depth = depth - 1
+        depth = depth + 1
+        if top == 0 then
+            showstate(formatters["unknown %s call"](scope))
+            top = 0
         else
-            report("unknown %s %i",scope,index)
+            local index = stack[top] + bias
+            top = top - 1
+            if trace_charstrings then
+                showvalue(scope,index,true)
+            end
+            local tab = list[index]
+            if tab then
+                if type(tab) == "string" then
+                    tab = lpegmatch(p_bytes,tab)
+                    list[index] = tab
+                end
+                process(tab)
+            else
+                showstate(formatters["unknown %s call %i"](scope,index))
+                top = 0
+            end
         end
+        depth = depth - 1
     end
 
     local function process(tab)
@@ -1131,7 +1251,7 @@ do
         local n = #tab
         while i <= n do
             local t = tab[i]
-            if t >= 32 and t<=246 then
+            if t >= 32 and t <= 246 then
                 -- -107 .. +107
                 top = top + 1
                 stack[top] = t - 139
@@ -1196,7 +1316,7 @@ do
                 local t = tab[i]
                 local a = subactions[t]
                 if a then
-                    a()
+                    a(t)
                 else
                     if trace_charstrings then
                         showvalue("<subaction>",t)
@@ -1207,7 +1327,7 @@ do
             else
                 local a = actions[t]
                 if a then
-                    local s = a()
+                    local s = a(t)
                     if s then
                         i = i + s
                     end
@@ -1260,27 +1380,44 @@ do
  --     end
  -- end
 
-    parsecharstrings = function(data,glyphs,doshapes)
+    local function setbias(globals,locals)
+        if version == 1 then
+            return
+                false,
+                false
+        else
+            local g, l = #globals, #locals
+            return
+                ((g <  1240 and 107) or (g < 33900 and 1131) or 32768) + 1,
+                ((l <  1240 and 107) or (l < 33900 and 1131) or 32768) + 1
+        end
+    end
+
+    parsecharstrings = function(data,glyphs,doshapes,tversion)
         -- for all charstrings
         local dictionary  = data.dictionaries[1]
         local charstrings = dictionary.charstrings
         local charset     = dictionary.charset
+        local private     = dictionary.private or { data = { } }
+
         keepcurve  = doshapes
+        version    = tversion
         stack      = { }
         glyphs     = glyphs or { }
         strings    = data.strings
-        locals     = dictionary.subroutines
-        globals    = data.routines
-        globalbias = #globals
-        localbias  = #locals
-        globalbias = ((globalbias <  1240 and 107) or (globalbias < 33900 and 1131) or 32768) + 1
-        localbias  = ((localbias  <  1240 and 107) or (localbias  < 33900 and 1131) or 32768) + 1
-        local nominalwidth = dictionary.private.data.nominalwidthx or 0
-        local defaultwidth = dictionary.private.data.defaultwidthx or 0
+        globals    = data.routines or { }
+        locals     = dictionary.subroutines or { }
+
+        globalbias, localbias = setbias(globals,locals)
+
+        local nominalwidth = private.data.nominalwidthx or 0
+        local defaultwidth = private.data.defaultwidthx or 0
 
         for i=1,#charstrings do
-            local str   = charstrings[i]
-            local tab   = lpegmatch(p_bytes,str)
+            local tab = charstrings[i]
+            if type(tab) == "string" then
+                tab = lpegmatch(p_bytes,tab)
+            end
             local index = i - 1
             x       = 0
             y       = 0
@@ -1341,20 +1478,23 @@ do
         return glyphs
     end
 
-    parsecharstring = function(data,dictionary,charstring,glyphs,index,doshapes)
+    parsecharstring = function(data,dictionary,tab,glyphs,index,doshapes,tversion)
         local private = dictionary.private
         keepcurve  = doshapes
+        version    = tversion
         strings    = data.strings -- or in dict?
         locals     = dictionary.subroutines or { }
         globals    = data.routines or { }
-        globalbias = #globals
-        localbias  = #locals
-        globalbias = ((globalbias <  1240 and 107) or (globalbias < 33900 and 1131) or 32768) + 1
-        localbias  = ((localbias  <  1240 and 107) or (localbias  < 33900 and 1131) or 32768) + 1
+
+        globalbias, localbias = setbias(globals,locals)
+
         local nominalwidth = private and private.data.nominalwidthx or 0
         local defaultwidth = private and private.data.defaultwidthx or 0
         --
-        local tab = lpegmatch(p_bytes,charstring)
+        if type(tab) == "string" then
+            tab = lpegmatch(p_bytes,tab)
+        end
+        --
         x         = 0
         y         = 0
         width     = false
@@ -1384,7 +1524,8 @@ do
             width = nominalwidth + width
         end
         --
-index = index - 1
+        index = index - 1
+        --
         local glyph = glyphs[index] -- can be autodefined in otr
         if not glyph then
             glyphs[index] = {
@@ -1410,8 +1551,6 @@ index = index - 1
             report("width: %s",tostring(width))
             report("boundingbox: % t",boundingbox)
         end
-        --
-        return charstring
     end
 
     resetcharstrings = function()
@@ -1542,7 +1681,7 @@ local function readcidprivates(f,data)
     parseprivates(data,dictionaries)
 end
 
-local function readnoselect(f,data,glyphs,doshapes)
+local function readnoselect(f,data,glyphs,doshapes,version)
     local dictionaries = data.dictionaries
     local dictionary   = dictionaries[1]
     readglobals(f,data)
@@ -1552,11 +1691,13 @@ local function readnoselect(f,data,glyphs,doshapes)
     readprivates(f,data)
     parseprivates(data,data.dictionaries)
     readlocals(f,data,dictionary)
-    parsecharstrings(data,glyphs,doshapes)
+    parsecharstrings(data,glyphs,doshapes,version)
     resetcharstrings()
 end
 
-local function readfdselect(f,data,glyphs,doshapes)
+readers.parsecharstrings = parsecharstrings
+
+local function readfdselect(f,data,glyphs,doshapes,version)
     local header       = data.header
     local dictionaries = data.dictionaries
     local dictionary   = dictionaries[1]
@@ -1615,7 +1756,7 @@ local function readfdselect(f,data,glyphs,doshapes)
             readlocals(f,data,dictionaries[i])
         end
         for i=1,#charstrings do
-            parsecharstring(data,dictionaries[fdindex[i]+1],charstrings[i],glyphs,i,doshapes)
+            parsecharstring(data,dictionaries[fdindex[i]+1],charstrings[i],glyphs,i,doshapes,version)
         end
         resetcharstrings()
     end
diff --git a/src/fontloader/misc/fontloader-font-dsp.lua b/src/fontloader/misc/fontloader-font-dsp.lua
index 49d5929..1c81e5e 100644
--- a/src/fontloader/misc/fontloader-font-dsp.lua
+++ b/src/fontloader/misc/fontloader-font-dsp.lua
@@ -53,6 +53,7 @@ local bittest = bit32.btest
 local rshift = bit32.rshift
 local concat = table.concat
 local lower = string.lower
+local copy = table.copy
 local sub = string.sub
 local strip = string.strip
 local tohash = table.tohash
@@ -1698,7 +1699,7 @@ do
                                                     flags     = d.flags,
                                                  -- chain     = d.chain,
                                                 }
-                                                sublookuplist[nofsublookups] = h
+                                                sublookuplist[nofsublookups] = copy(h) -- we repack later
                                                 sublookuphash[lookupid] = nofsublookups
                                                 sublookupcheck[lookupid] = 1
                                             else
diff --git a/src/fontloader/misc/fontloader-font-onr.lua b/src/fontloader/misc/fontloader-font-onr.lua
index dcf7445..6c33b24 100644
--- a/src/fontloader/misc/fontloader-font-onr.lua
+++ b/src/fontloader/misc/fontloader-font-onr.lua
@@ -50,31 +50,41 @@ and <l n='otf'/> reader.</p>
 and new vectors (we actually had one bad vector with the old loader).</p>
 --ldx]]--
 
-local get_indexes
+local get_indexes, get_shapes
 
 do
 
-    local n, m
+    local decrypt
 
-    local progress = function(str,position,name,size)
-        local forward = position + tonumber(size) + 3 + 2
-        n = n + 1
-        if n >= m then
-            return #str, name
-        elseif forward < #str then
-            return forward, name
-        else
-            return #str, name
+    do
+
+        local r, c1, c2, n = 0, 0, 0, 0
+
+        local function step(c)
+            local cipher = byte(c)
+            local plain  = bxor(cipher,rshift(r,8))
+            r = ((cipher + r) * c1 + c2) % 65536
+            return char(plain)
         end
-    end
 
-    local initialize = function(str,position,size)
-        n = 0
-        m = size -- % tonumber(size)
-        return position + 1
+        decrypt = function(binary,initial,seed)
+            r, c1, c2, n = initial, 52845, 22719, seed
+            binary       = gsub(binary,".",step)
+            return sub(binary,n+1)
+        end
+
+     -- local pattern = Cs((P(1) / step)^1)
+     --
+     -- decrypt = function(binary,initial,seed)
+     --     r, c1, c2, n = initial, 52845, 22719, seed
+     --     binary = lpegmatch(pattern,binary)
+     --     return sub(binary,n+1)
+     -- end
+
     end
 
     local charstrings   = P("/CharStrings")
+    local subroutines   = P("/Subrs")
     local encoding      = P("/Encoding")
     local dup           = P("dup")
     local put           = P("put")
@@ -85,9 +95,64 @@ do
     local spaces        = P(" ")^1
     local spacing       = patterns.whitespace^0
 
+    local routines, vector, chars, n, m
+
+    local initialize = function(str,position,size)
+        n = 0
+        m = size -- % tonumber(size)
+        return position + 1
+    end
+
+    local setroutine = function(str,position,index,size)
+        local forward = position + tonumber(size)
+        local stream  = sub(str,position+1,forward)
+        routines[index] = decrypt(stream,4330,4)
+        return forward
+    end
+
+    local setvector = function(str,position,name,size)
+        local forward = position + tonumber(size)
+        if n >= m then
+            return #str
+        elseif forward < #str then
+            vector[n] = name
+            n = n + 1 -- we compensate for notdef at the cff loader end
+            return forward
+        else
+            return #str
+        end
+    end
+
+    local setshapes = function(str,position,name,size)
+        local forward = position + tonumber(size)
+        local stream  = sub(str,position+1,forward)
+        if n > m then
+            return #str
+        elseif forward < #str then
+            vector[n] = name
+            n = n + 1
+            chars [n] = decrypt(stream,4330,4)
+            return forward
+        else
+            return #str
+        end
+    end
+
+    local p_rd = spacing * (P("RD") + P("-|"))
+    local p_np = spacing * (P("NP") + P( "|"))
+    local p_nd = spacing * (P("ND") + P( "|"))
+
+    local p_filterroutines = -- dup <i> <n> RD or -| <n encrypted bytes> NP or |
+        (1-subroutines)^0 * subroutines * spaces * Cmt(cardinal,initialize)
+      * (Cmt(cardinal * spaces * cardinal * p_rd, setroutine) * p_np + P(1))^1
+
+    local p_filtershapes = -- /foo <n> RD <n encrypted bytes> ND
+        (1-charstrings)^0 * charstrings * spaces * Cmt(cardinal,initialize)
+      * (Cmt(name * spaces * cardinal * p_rd, setshapes) * p_nd + P(1))^1
+
     local p_filternames = Ct (
         (1-charstrings)^0 * charstrings * spaces * Cmt(cardinal,initialize)
-      * (Cmt(name * spaces * cardinal, progress) + P(1))^1
+        * (Cmt(name * spaces * cardinal, setvector) + P(1))^1
     )
 
     -- /Encoding 256 array
@@ -102,36 +167,7 @@ do
 
     -- if one of first 4 not 0-9A-F then binary else hex
 
-    local decrypt
-
-    do
-
-        local r, c1, c2, n = 0, 0, 0, 0
-
-        local function step(c)
-            local cipher = byte(c)
-            local plain  = bxor(cipher,rshift(r,8))
-            r = ((cipher + r) * c1 + c2) % 65536
-            return char(plain)
-        end
-
-        decrypt = function(binary)
-            r, c1, c2, n = 55665, 52845, 22719, 4
-            binary       = gsub(binary,".",step)
-            return sub(binary,n+1)
-        end
-
-     -- local pattern = Cs((P(1) / step)^1)
-     --
-     -- decrypt = function(binary)
-     --     r, c1, c2, n = 55665, 52845, 22719, 4
-     --     binary = lpegmatch(pattern,binary)
-     --     return sub(binary,n+1)
-     -- end
-
-    end
-
-    local function loadpfbvector(filename)
+    local function loadpfbvector(filename,shapestoo)
         -- for the moment limited to encoding only
 
         local data = io.loaddata(resolvers.findfile(filename))
@@ -153,28 +189,36 @@ do
             return
         end
 
-        binary = decrypt(binary,4)
+        binary = decrypt(binary,55665,4)
 
-        local vector = lpegmatch(p_filternames,binary)
-
---         if vector[1] == ".notdef" then
---             -- tricky
---             vector[0] = table.remove(vector,1)
---         end
-
-        for i=1,#vector do
-            vector[i-1] = vector[i]
+        local names    = { }
+        local encoding = lpegmatch(p_filterencoding,ascii)
+        local glyphs   = { }
+
+        routines, vector, chars = { }, { }, { }
+
+        if shapestoo then
+            lpegmatch(p_filterroutines,binary)
+            lpegmatch(p_filtershapes,binary)
+            local data = {
+                dictionaries = {
+                    {
+                        charstrings = chars,
+                        charset     = vector,
+                        subroutines = routines,
+                    }
+                },
+            }
+            fonts.handlers.otf.readers.parsecharstrings(data,glyphs,true,true)
+        else
+            lpegmatch(p_filternames,binary)
         end
-        vector[#vector] = nil
 
-        if not vector then
-            report_pfb("no vector in %a",filename)
-            return
-        end
+        names = vector
 
-        local encoding = lpegmatch(p_filterencoding,ascii)
+        routines, vector, chars = nil, nil, nil
 
-        return vector, encoding
+        return names, encoding, glyphs
 
     end
 
@@ -202,6 +246,11 @@ do
         end
     end
 
+    get_shapes = function(pfbname)
+        local vector, encoding, glyphs = loadpfbvector(pfbname,true)
+        return glyphs
+    end
+
 end
 
 --[[ldx--
@@ -428,6 +477,26 @@ function readers.loadfont(afmname,pfbname)
     end
 end
 
+-- for now, todo: n and check with otf (no afm needed here)
+
+function readers.loadshapes(filename)
+    local fullname = resolvers.findfile(filename) or ""
+    if fullname == "" then
+        return {
+            filename = "not found: " .. filename,
+            glyphs   = { }
+        }
+    else
+        return {
+            filename = fullname,
+            format   = "opentype",
+            glyphs   = get_shapes(fullname) or { },
+            units    = 1000,
+        }
+    end
+end
+
+
 function readers.getinfo(filename)
     local data = read(resolvers.findfile(filename),infoparser)
     if data then
diff --git a/src/fontloader/misc/fontloader-font-osd.lua b/src/fontloader/misc/fontloader-font-osd.lua
index 26af691..b67cc92 100644
--- a/src/fontloader/misc/fontloader-font-osd.lua
+++ b/src/fontloader/misc/fontloader-font-osd.lua
@@ -613,13 +613,12 @@ local function initializedevanagi(tfmdata)
                 local steps    = sequence.steps
                 local nofsteps = sequence.nofsteps
                 local features = sequence.features
-                if features["rphf"] then
-                    -- deva
+                local has_rphf = features.rphf
+                local has_blwf = features.blwf
+                if has_rphf and has_rphf.deva then
                     devanagari.reph = true
-                elseif features["blwf"] then
-                    -- deva
+                elseif has_blwf and has_blwf.deva then
                     devanagari.vattu = true
-                    -- dev2
                     for i=1,nofsteps do
                         local step     = steps[i]
                         local coverage = step.coverage
@@ -632,59 +631,71 @@ local function initializedevanagi(tfmdata)
                         end
                     end
                 end
-                if valid[kind] then
-                    for i=1,nofsteps do
-                        local step     = steps[i]
-                        local coverage = step.coverage
-                        if coverage then
-                            local reph = false
-                            if step.osdstep then
-                                -- rphf acts on consonant + halant
-                                for k, v in next, ra do
-                                    local r = coverage[k]
-                                    if r then
-                                        local h = false
-                                        for k, v in next, halant do
-                                            local h = r[k]
-                                            if h then
-                                                reph = h.ligature or false
-                                                break
+                for kind, spec in next, features do -- beware, this is
+                    if spec.dev2 and valid[kind] then
+                        for i=1,nofsteps do
+                            local step     = steps[i]
+                            local coverage = step.coverage
+                            if coverage then
+                                local reph = false
+                                if kind == "rphf" then
+                                 --
+                                 -- KE: I don't understand the rationale behind osdstep. The original if
+                                 --     statement checked whether coverage is contextual chaining.
+                                 --
+                                 -- HH: The osdstep signals that we deal with our own feature here, not
+                                 --     one in the font itself so it was just a safeguard against us overloading
+                                 --     something driven by the font.
+                                 --
+                                 -- if step.osdstep then -- selective
+                                    if true then -- always
+                                        -- rphf acts on consonant + halant
+                                        for k, v in next, ra do
+                                            local r = coverage[k]
+                                            if r then
+                                                local h = false
+                                                for k, v in next, halant do
+                                                    local h = r[k]
+                                                    if h then
+                                                        reph = h.ligature or false
+                                                        break
+                                                    end
+                                                end
+                                                if reph then
+                                                    break
+                                                end
                                             end
                                         end
-                                        if reph then
-                                            break
-                                        end
+                                    else
+                                        -- rphf might be result of other handler/chainproc
                                     end
                                 end
-                            else
-                                -- rphf might be result of other handler/chainproc
+                                seqsubset[#seqsubset+1] = { kind, coverage, reph }
                             end
-                            seqsubset[#seqsubset+1] = { kind, coverage, reph }
                         end
                     end
-                end
-                if kind == "pref" then
-                    local sequence = dataset[3] -- was [5]
-                    local steps    = sequence.steps
-                    local nofsteps = sequence.nofsteps
-                    for i=1,nofsteps do
-                        local step     = steps[i]
-                        local coverage = step.coverage
-                        if coverage then
-                            for k, v in next, halant do
-                                local h = coverage[k]
-                                if h then
-                                    local found = false
-                                    for k, v in next, h do
-                                        found = v and v.ligature
+                    if kind == "pref" then
+                        local steps    = sequence.steps
+                        local nofsteps = sequence.nofsteps
+                        for i=1,nofsteps do
+                            local step     = steps[i]
+                            local coverage = step.coverage
+                            if coverage then
+                                for k, v in next, halant do
+                                    local h = coverage[k]
+                                    if h then
+                                        local found = false
+                                        for k, v in next, h do
+                                            found = v and v.ligature
+                                            if found then
+                                                pre_base_reordering_consonants[k] = found
+                                                break
+                                            end
+                                        end
                                         if found then
-                                            pre_base_reordering_consonants[k] = found
                                             break
                                         end
                                     end
-                                    if found then
-                                        break
-                                    end
                                 end
                             end
                         end
@@ -1132,6 +1143,8 @@ function handlers.devanagari_reorder_matras(head,start) -- no leak
                 start = startnext
                 break
             end
+        else
+            break
         end
         current = next
     end
@@ -1171,12 +1184,12 @@ function handlers.devanagari_reorder_reph(head,start)
     local startfont = getfont(start)
     local startattr = getprop(start,a_syllabe)
     while current do
-        local char = ischar(current,font)
+        local char = ischar(current,startfont)
         if char and getprop(current,a_syllabe) == startattr then -- step 2
             if halant[char] and not getprop(current,a_state) then
                 local next = getnext(current)
                 if next then
-                    local nextchar = ischar(next,font)
+                    local nextchar = ischar(next,startfont)
                     if nextchar and zw_char[nextchar] and getprop(next,a_syllabe) == startattr then
                         current = next
                         next    = getnext(current)
@@ -1198,7 +1211,7 @@ function handlers.devanagari_reorder_reph(head,start)
     if not startnext then
         current = getnext(start)
         while current do
-            local char = ischar(current,font)
+            local char = ischar(current,startfont)
             if char and getprop(current,a_syllabe) == startattr then -- step 4
                 if getprop(current,a_state) == s_pstf then -- post-base
                     startnext = getnext(start)
@@ -1223,7 +1236,7 @@ function handlers.devanagari_reorder_reph(head,start)
         current = getnext(start)
         local c = nil
         while current do
-            local char = ischar(current,font)
+            local char = ischar(current,startfont)
             if char and getprop(current,a_syllabe) == startattr then -- step 5
                 if not c and mark_above_below_post[char] and reorder_class[char] ~= "after subscript" then
                     c = current
@@ -1250,7 +1263,7 @@ function handlers.devanagari_reorder_reph(head,start)
         current = start
         local next = getnext(current)
         while next do
-            local nextchar = ischar(next,font)
+            local nextchar = ischar(next,startfont)
             if nextchar and getprop(next,a_syllabe) == startattr then --step 6
                 current = next
                 next = getnext(current)
@@ -1263,7 +1276,7 @@ function handlers.devanagari_reorder_reph(head,start)
             head = remove_node(head,start)
             local next = getnext(current)
             setlink(start,next)
-            setlink(current,"next",start)
+            setlink(current,start)
             start = startnext
         end
     end
@@ -1290,12 +1303,12 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start)
     local startattr = getprop(start,a_syllabe)
     -- can be fast for loop + caching state
     while current do
-        local char = ischar(current,font)
+        local char = ischar(current,startfont)
         if char and getprop(current,a_syllabe) == startattr then
             local next = getnext(current)
             if halant[char] and not getprop(current,a_state) then
                 if next then
-                    local nextchar = ischar(next,font)
+                    local nextchar = ischar(next,startfont)
                     if nextchar and getprop(next,a_syllabe) == startattr then
                         if nextchar == c_zwnj or nextchar == c_zwj then
                             current = next
@@ -1319,13 +1332,13 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start)
         current   = getnext(start)
         startattr = getprop(start,a_syllabe)
         while current do
-            local char = ischar(current,font)
+            local char = ischar(current,startfont)
             if char and getprop(current,a_syllabe) == startattr then
                 if not consonant[char] and getprop(current,a_state) then -- main
                     startnext = getnext(start)
                     removenode(start,start)
                     local prev = getprev(current)
-                    setlink(start,prev)
+                    setlink(prev,start)
                     setlink(start,current)
                     start = startnext
                     break
@@ -1426,21 +1439,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa
         local kind        = subset[1]
         local lookupcache = subset[2]
         if kind == "rphf" then
-            for k, v in next, ra do
-                local r = lookupcache[k]
-                if r then
-                    for k, v in next, halant do
-                        local h = r[k]
-                        if h then
-                            reph = h.ligature or false
-                            break
-                        end
-                    end
-                    if reph then
-                        break
-                    end
-                end
-            end
+            reph = subset[3]
             local current = start
             local last = getnext(stop)
             while current ~= last do
@@ -1473,7 +1472,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa
                 if current ~= stop then
                     local c = locl[current] or getchar(current)
                     local found = lookupcache[c]
-                    if found then
+                    if found then -- pre-base: pref	Halant + Consonant
                         local next = getnext(current)
                         local n = locl[next] or getchar(next)
                         if found[n] then
@@ -1718,7 +1717,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa
                 local prev = getprev(current)
                 if prev ~= target then
                     local next = getnext(current)
-                    setlink(next,prev)
+                    setlink(prev,next)
                     if current == stop then
                         stop = prev
                     end
@@ -1752,7 +1751,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa
                 stop = current
             end
             local prev = getprev(c)
-            setlink(next,prev)
+            setlink(prev,next)
             local nextnext = getnext(next)
             setnext(current,nextnext)
             local nextnextnext = getnext(nextnext)
@@ -1766,6 +1765,9 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa
     end
 
     if getchar(base) == c_nbsp then
+        if base == stop then
+            stop = getprev(stop)
+        end
         nbspaces = nbspaces - 1
         head = remove_node(head, base)
         flush_node(base)
@@ -1815,7 +1817,7 @@ local function analyze_next_chars_one(c,font,variant) -- skip one dependent vowe
                             elseif (vv == c_zwnj or vv == c_zwj) and halant[vvv] then
                                 local nnnn = getnext(nnn)
                                 if nnnn then
-                                    local vvvv = ischar(nnnn)
+                                    local vvvv = ischar(nnnn,font)
                                     if vvvv and consonant[vvvv] then
                                         c = nnnn
                                     end
@@ -1838,7 +1840,7 @@ local function analyze_next_chars_one(c,font,variant) -- skip one dependent vowe
                 local nn = getnext(n)
                 if nn then
                     local vv = ischar(nn,font)
-                    if vv and zw_char[vv] then
+                    if vv and zw_char[v] then
                         n = nn
                         v = vv
                         nn = getnext(nn)
diff --git a/src/fontloader/misc/fontloader-font-ota.lua b/src/fontloader/misc/fontloader-font-ota.lua
index 4ddb831..b8944e0 100644
--- a/src/fontloader/misc/fontloader-font-ota.lua
+++ b/src/fontloader/misc/fontloader-font-ota.lua
@@ -262,36 +262,44 @@ local classifiers = characters.classifiers
 
 if not classifiers then
 
-    local first_arabic,  last_arabic  = characters.blockrange("arabic")
-    local first_syriac,  last_syriac  = characters.blockrange("syriac")
-    local first_mandiac, last_mandiac = characters.blockrange("mandiac")
-    local first_nko,     last_nko     = characters.blockrange("nko")
+    local f_arabic,  l_arabic  = characters.blockrange("arabic")
+    local f_syriac,  l_syriac  = characters.blockrange("syriac")
+    local f_mandiac, l_mandiac = characters.blockrange("mandiac")
+    local f_nko,     l_nko     = characters.blockrange("nko")
+    local f_ext_a,   l_ext_a   = characters.blockrange("arabicextendeda")
 
     classifiers = table.setmetatableindex(function(t,k)
-        local c = chardata[k]
-        local v = false
-        if c then
-            local arabic = c.arabic
-            if arabic then
-                v = mappers[arabic]
-                if not v then
-                    log.report("analyze","error in mapping arabic %C",k)
-                    --  error
-                    v = false
-                end
-            elseif k >= first_arabic  and k <= last_arabic  or k >= first_syriac  and k <= last_syriac  or
-                   k >= first_mandiac and k <= last_mandiac or k >= first_nko     and k <= last_nko     then
-                if categories[k] == "mn" then
-                    v = s_mark
-                else
-                    v = s_rest
+        if type(k) == "number" then
+            local c = chardata[k]
+            local v = false
+            if c then
+                local arabic = c.arabic
+                if arabic then
+                    v = mappers[arabic]
+                    if not v then
+                        log.report("analyze","error in mapping arabic %C",k)
+                        --  error
+                        v = false
+                    end
+                elseif (k >= f_arabic  and k <= l_arabic)  or
+                       (k >= f_syriac  and k <= l_syriac)  or
+                       (k >= f_mandiac and k <= l_mandiac) or
+                       (k >= f_nko     and k <= l_nko)     or
+                       (k >= f_ext_a   and k <= l_ext_a)   then
+                    if categories[k] == "mn" then
+                        v = s_mark
+                    else
+                        v = s_rest
+                    end
                 end
             end
+            t[k] = v
+            return v
         end
-        t[k] = v
-        return v
     end)
 
+    characters.classifiers = classifiers
+
 end
 
 function methods.arab(head,font,attr)
diff --git a/src/fontloader/misc/fontloader-font-otj.lua b/src/fontloader/misc/fontloader-font-otj.lua
index 46b2ca8..68cf608 100644
--- a/src/fontloader/misc/fontloader-font-otj.lua
+++ b/src/fontloader/misc/fontloader-font-otj.lua
@@ -377,7 +377,8 @@ function injections.setkern(current,factor,rlmode,x,injection)
     end
 end
 
-function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk) -- ba=baseanchor, ma=markanchor
+function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk,checkmark) -- ba=baseanchor, ma=markanchor
+
     local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2])
     nofregisteredmarks = nofregisteredmarks + 1
     if rlmode >= 0 then
@@ -398,6 +399,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk) -- ba=b
                 i.markbase     = nofregisteredmarks
                 i.markbasenode = base
                 i.markmark     = mkmk
+                i.checkmark    = checkmark
             end
         else
             p.injections = {
@@ -407,6 +409,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk) -- ba=b
                 markbase     = nofregisteredmarks,
                 markbasenode = base,
                 markmark     = mkmk,
+                checkmark    = checkmark,
             }
         end
     else
@@ -418,6 +421,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk) -- ba=b
                 markbase     = nofregisteredmarks,
                 markbasenode = base,
                 markmark     = mkmk,
+                checkmark    = checkmark,
             },
         }
     end
@@ -1062,11 +1066,22 @@ local function inject_everything(head,where)
                 ox = px - pn.markx
              -- report_injections("l2r case 3: %p",ox)
          -- end
-            local wn = getfield(n,"width") -- in arial marks have widths
-            if wn ~= 0 then
-                -- bad: we should center
-                pn.leftkern  = -wn/2
-                pn.rightkern = -wn/2
+            if pn.checkmark then
+                local wn = getfield(n,"width") -- in arial marks have widths
+                if wn ~= 0 then
+                    wn = wn/2
+                    if trace_injections then
+                        report_injections("correcting non zero width mark %C",getchar(n))
+                    end
+                    -- -- bad: we should center
+                    -- pn.leftkern  = -wn
+                    -- pn.rightkern = -wn
+                    -- -- we're too late anyway as kerns are already injected so
+                    -- -- we do it the ugly way (no checking if the previous is
+                    -- -- already a kern) .. maybe we should fix the font instead
+                    insert_node_before(n,n,newkern(-wn))
+                    insert_node_after(n,n,newkern(-wn))
+                end
             end
         end
         local oy = getfield(n,"yoffset") + getfield(p,"yoffset") + pn.marky
@@ -1092,10 +1107,10 @@ local function inject_everything(head,where)
                             nofmarks = nofmarks + 1
                             marks[nofmarks] = current
                         else
-local yoffset = i.yoffset
-if yoffset and yoffset ~= 0 then
-    setfield(current,"yoffset",yoffset)
-end
+                            local yoffset = i.yoffset
+                            if yoffset and yoffset ~= 0 then
+                                setfield(current,"yoffset",yoffset)
+                            end
                             if hascursives then
                                 local cursivex = i.cursivex
                                 if cursivex then
@@ -1148,10 +1163,6 @@ end
                                 end
                             end
                             -- left|glyph|right
---                             local yoffset = i.yoffset
---                             if yoffset and yoffset ~= 0 then
---                                 setfield(current,"yoffset",yoffset)
---                             end
                             local leftkern = i.leftkern
                             if leftkern and leftkern ~= 0 then
                                 insert_node_before(head,current,newkern(leftkern))
@@ -1166,7 +1177,7 @@ end
                         local i = p.emptyinjections
                         if i then
                             -- glyph|disc|glyph (special case)
--- okay?
+                            -- okay?
                             local rightkern = i.rightkern
                             if rightkern and rightkern ~= 0 then
                                 if next and getid(next) == disc_code then
@@ -1561,6 +1572,7 @@ function injections.handler(head,where)
     if triggers then
         head = injectspaces(head)
     end
+    -- todo: marks only run too
     if nofregisteredmarks > 0 or nofregisteredcursives > 0 then
         if trace_injections then
             report_injections("injection variant %a","everything")
diff --git a/src/fontloader/misc/fontloader-font-otl.lua b/src/fontloader/misc/fontloader-font-otl.lua
index 94f6a45..0662290 100644
--- a/src/fontloader/misc/fontloader-font-otl.lua
+++ b/src/fontloader/misc/fontloader-font-otl.lua
@@ -52,7 +52,7 @@ local report_otf          = logs.reporter("fonts","otf loading")
 local fonts               = fonts
 local otf                 = fonts.handlers.otf
 
-otf.version               = 3.025 -- beware: also sync font-mis.lua and in mtx-fonts
+otf.version               = 3.026 -- beware: also sync font-mis.lua and in mtx-fonts
 otf.cache                 = containers.define("fonts", "otl", otf.version, true)
 otf.svgcache              = containers.define("fonts", "svg", otf.version, true)
 otf.pdfcache              = containers.define("fonts", "pdf", otf.version, true)
diff --git a/src/fontloader/misc/fontloader-font-otr.lua b/src/fontloader/misc/fontloader-font-otr.lua
index 7c81285..9cdbc3d 100644
--- a/src/fontloader/misc/fontloader-font-otr.lua
+++ b/src/fontloader/misc/fontloader-font-otr.lua
@@ -1747,118 +1747,6 @@ function readers.math(f,fontdata,specification)
     end
 end
 
--- Goodie. A sequence instead of segments costs a bit more memory, some 300K on a
--- dejavu serif and about the same on a pagella regular.
-
-local function packoutlines(data,makesequence)
-    local subfonts = data.subfonts
-    if subfonts then
-        for i=1,#subfonts do
-            packoutlines(subfonts[i],makesequence)
-        end
-        return
-    end
-    local common = data.segments
-    if common then
-        return
-    end
-    local glyphs = data.glyphs
-    if not glyphs then
-        return
-    end
-    if makesequence then
-        for index=1,#glyphs do
-            local glyph = glyphs[index]
-            local segments = glyph.segments
-            if segments then
-                local sequence    = { }
-                local nofsequence = 0
-                for i=1,#segments do
-                    local segment    = segments[i]
-                    local nofsegment = #segment
-                    nofsequence = nofsequence + 1
-                    sequence[nofsequence] = segment[nofsegment]
-                    for i=1,nofsegment-1 do
-                        nofsequence = nofsequence + 1
-                        sequence[nofsequence] = segment[i]
-                    end
-                end
-                glyph.sequence = sequence
-                glyph.segments = nil
-            end
-        end
-    else
-        local hash    = { }
-        local common  = { }
-        local reverse = { }
-        local last    = 0
-        for index=1,#glyphs do
-            local segments = glyphs[index].segments
-            if segments then
-                for i=1,#segments do
-                    local h = concat(segments[i]," ")
-                    hash[h] = (hash[h] or 0) + 1
-                end
-            end
-        end
-        for index=1,#glyphs do
-            local segments = glyphs[index].segments
-            if segments then
-                for i=1,#segments do
-                    local segment = segments[i]
-                    local h = concat(segment," ")
-                    if hash[h] > 1 then -- minimal one shared in order to hash
-                        local idx = reverse[h]
-                        if not idx then
-                            last = last + 1
-                            reverse[h] = last
-                            common[last] = segment
-                            idx = last
-                        end
-                        segments[i] = idx
-                    end
-                end
-            end
-        end
-        if last > 0 then
-            data.segments = common
-        end
-    end
-end
-
-local function unpackoutlines(data)
-    local subfonts = data.subfonts
-    if subfonts then
-        for i=1,#subfonts do
-            unpackoutlines(subfonts[i])
-        end
-        return
-    end
-    local common = data.segments
-    if not common then
-        return
-    end
-    local glyphs = data.glyphs
-    if not glyphs then
-        return
-    end
-    for index=1,#glyphs do
-        local segments = glyphs[index].segments
-        if segments then
-            for i=1,#segments do
-                local c = common[segments[i]]
-                if c then
-                    segments[i] = c
-                end
-            end
-        end
-    end
-    data.segments = nil
-end
-
-otf.packoutlines   = packoutlines
-otf.unpackoutlines = unpackoutlines
-
 -- Now comes the loader. The order of reading these matters as we need to know
 -- some properties in order to read following tables. When details is true we also
 -- initialize the glyphs data.
@@ -2152,6 +2040,15 @@ function readers.loadshapes(filename,n)
         shapes   = true,
         subfont  = n,
     }
+    if fontdata then
+        -- easier on luajit but still we can hit the 64 K stack constants issue
+        for k, v in next, fontdata.glyphs do
+            v.class = nil
+            v.index = nil
+            v.math  = nil
+         -- v.name  = nil
+        end
+    end
     return fontdata and {
      -- version  = 0.123 -- todo
         filename = filename,
@@ -2302,63 +2199,3 @@ function readers.extend(fontdata)
         end
     end
 end
-
--- for now .. this will move to a context specific file
-
-if fonts.hashes then
-
-    local identifiers = fonts.hashes.identifiers
-    local loadshapes  = readers.loadshapes
-
-    readers.version  = 0.006
-    readers.cache    = containers.define("fonts", "shapes", readers.version, true)
-
-    -- todo: loaders per format
-
-    local function load(filename,sub)
-        local base = file.basename(filename)
-        local name = file.removesuffix(base)
-        local kind = file.suffix(filename)
-        local attr = lfs.attributes(filename)
-        local size = attr and attr.size or 0
-        local time = attr and attr.modification or 0
-        local sub  = tonumber(sub)
-        if size > 0 and (kind == "otf" or kind == "ttf" or kind == "tcc") then
-            local hash = containers.cleanname(base) -- including suffix
-            if sub then
-                hash = hash .. "-" .. sub
-            end
-            data = containers.read(readers.cache,hash)
-            if not data or data.time ~= time or data.size  ~= size then
-                data = loadshapes(filename,sub)
-                if data then
-                    data.size   = size
-                    data.format = data.format or (kind == "otf" and "opentype") or "truetype"
-                    data.time   = time
-                    packoutlines(data)
-                    containers.write(readers.cache,hash,data)
-                    data = containers.read(readers.cache,hash) -- frees old mem
-                end
-            end
-            unpackoutlines(data)
-        else
-            data = {
-                filename = filename,
-                size     = 0,
-                time     = time,
-                format   = "unknown",
-                units    = 1000,
-                glyphs   = { }
-            }
-        end
-        return data
-    end
-
-    fonts.hashes.shapes = table.setmetatableindex(function(t,k)
-        local d = identifiers[k]
-        local v = load(d.properties.filename,d.subindex)
-        t[k] = v
-        return v
-    end)
-
-end
diff --git a/src/fontloader/misc/fontloader-font-ots.lua b/src/fontloader/misc/fontloader-font-ots.lua
index d371156..da5d50a 100644
--- a/src/fontloader/misc/fontloader-font-ots.lua
+++ b/src/fontloader/misc/fontloader-font-ots.lua
@@ -254,6 +254,7 @@ local marks           = false
 local currentfont     = false
 local factor          = 0
 local threshold       = 0
+local checkmarks      = false
 
 local sweepnode       = nil
 local sweepprev       = nil
@@ -959,7 +960,7 @@ function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode)
                 local ba = markanchors[1][basechar]
                 if ba then
                     local ma = markanchors[2]
-                    local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar])
+                    local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks)
                     if trace_marks then
                         logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)",
                             pref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy)
@@ -1015,7 +1016,7 @@ function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlm
                         local index = getligaindex(start)
                         ba = ba[index]
                         if ba then
-                            local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar]) -- index
+                            local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks)
                             if trace_marks then
                                 logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)",
                                     pref(dataset,sequence),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy)
@@ -1064,7 +1065,7 @@ function handlers.gpos_mark2mark(head,start,dataset,sequence,markanchors,rlmode)
                 local ba = markanchors[1][basechar] -- slot 1 has been made copy of the class hash
                 if ba then
                     local ma = markanchors[2]
-                    local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true)
+                    local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true,checkmarks)
                     if trace_marks then
                         logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
                             pref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy)
@@ -1532,7 +1533,7 @@ function chainprocs.gpos_mark2base(head,start,stop,dataset,sequence,currentlooku
                     if ba then
                         local ma = markanchors[2]
                         if ma then
-                            local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar])
+                            local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks)
                             if trace_marks then
                                 logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)",
                                     cref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy)
@@ -1599,7 +1600,7 @@ function chainprocs.gpos_mark2ligature(head,start,stop,dataset,sequence,currentl
                             local index = getligaindex(start)
                             ba = ba[index]
                             if ba then
-                                local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar])
+                                local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks)
                                 if trace_marks then
                                     logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)",
                                         cref(dataset,sequence),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy)
@@ -1652,7 +1653,7 @@ function chainprocs.gpos_mark2mark(head,start,stop,dataset,sequence,currentlooku
                     if ba then
                         local ma = markanchors[2]
                         if ma then
-                            local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true)
+                            local dx, dy, bound = setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true,checkmarks)
                             if trace_marks then
                                 logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
                                     cref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy)
@@ -3381,6 +3382,7 @@ local function featuresprocessor(head,font,attr)
         marks         = tfmdata.resources.marks
         threshold,
         factor        = getthreshold(font)
+        checkmarks    = tfmdata.properties.checkmarks
 
     elseif currentfont ~= font then
 
@@ -3665,7 +3667,7 @@ if fontfeatures then
 
     function otf.handlers.trigger_space_kerns(head,start,dataset,sequence,_,_,_,_,font,attr)
         local features = fontfeatures[font]
-        local enabled  = features.spacekern == true and features.kern == true
+        local enabled  = features and features.spacekern and features.kern
         if enabled then
             setspacekerns(font,sequence)
         end
@@ -3677,7 +3679,7 @@ else -- generic (no hashes)
     function otf.handlers.trigger_space_kerns(head,start,dataset,sequence,_,_,_,_,font,attr)
         local shared   = fontdata[font].shared
         local features = shared and shared.features
-        local enabled  = features and features.spacekern == true and features.kern == true
+        local enabled  = features and features.spacekern and features.kern
         if enabled then
             setspacekerns(font,sequence)
         end
@@ -3739,7 +3741,7 @@ local function spaceinitializer(tfmdata,value) -- attr
                     if kern then
                         if feat then
                             for script, languages in next, kern do
-                                local f = feat[k]
+                                local f = feat[script]
                                 if f then
                                     for l in next, languages do
                                         f[l] = true
@@ -3752,26 +3754,42 @@ local function spaceinitializer(tfmdata,value) -- attr
                             feat = kern
                         end
                         for i=1,#steps do
-                            local step = steps[i]
+                            local step     = steps[i]
                             local coverage = step.coverage
-                            if coverage then
-                                local kerns = coverage[32]
+                            local rules    = step.rules
+                            local format   = step.format
+                            if rules then
+                                -- not now: analyze (simple) rules
+                            elseif coverage then
+                                -- what to do if we have no [1] but only [2]
+                                local single = format == gpos_single
+                                local kerns  = coverage[32]
                                 if kerns then
                                     for k, v in next, kerns do
-                                        if type(v) == "table" then
-                                            right[k] = v[3] -- needs checking
-                                        else
+                                        if type(v) ~= "table" then
                                             right[k] = v
+                                        elseif single then
+                                            right[k] = v[3]
+                                        else
+                                            local one = v[1]
+                                            if one then
+                                                right[k] = one[3]
+                                            end
                                         end
                                     end
                                 end
                                 for k, v in next, coverage do
                                     local kern = v[32]
                                     if kern then
-                                        if type(kern) == "table" then
-                                            left[k] = kern[3] -- needs checking
-                                        else
+                                        if type(kern) ~= "table" then
                                             left[k] = kern
+                                        elseif single then
+                                            left[k] = v[3]
+                                        else
+                                            local one = v[1]
+                                            if one then
+                                                left[k] = one[3]
+                                            end
                                         end
                                     end
                                 end
@@ -3822,3 +3840,17 @@ registerotffeature {
         node     = spaceinitializer,
     },
 }
+
+local function markinitializer(tfmdata,value)
+    local properties = tfmdata.properties
+    properties.checkmarks = value
+end
+
+registerotffeature {
+    name         = "checkmarks",
+    description  = "check mark widths",
+    default      = true,
+    initializers = {
+        node     = markinitializer,
+    },
+}
diff --git a/src/fontloader/misc/fontloader-l-table.lua b/src/fontloader/misc/fontloader-l-table.lua
index d1e0592..498f518 100644
--- a/src/fontloader/misc/fontloader-l-table.lua
+++ b/src/fontloader/misc/fontloader-l-table.lua
@@ -478,7 +478,7 @@ function table.fromhash(t)
     return hsh
 end
 
-local noquotes, hexify, handle, compact, inline, functions
+local noquotes, hexify, handle, compact, inline, functions, metacheck
 
 local reserved = table.tohash { -- intercept a language inconvenience: no reserved words as key
     'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 'function', 'if',
@@ -608,7 +608,8 @@ local function do_serialize(root,name,depth,level,indexed)
         if compact then
             last = #root
             for k=1,last do
-                if root[k] == nil then
+             -- if root[k] == nil then
+                if rawget(root,k) == nil then
                     last = k - 1
                     break
                 end
@@ -817,6 +818,7 @@ local function serialize(_handle,root,name,specification) -- handle wins
         functions = specification.functions
         compact   = specification.compact
         inline    = specification.inline and compact
+        metacheck = specification.metacheck
         if functions == nil then
             functions = true
         end
@@ -826,6 +828,9 @@ local function serialize(_handle,root,name,specification) -- handle wins
         if inline == nil then
             inline = compact
         end
+        if metacheck == nil then
+            metacheck = true
+        end
     else
         noquotes  = false
         hexify    = false
@@ -833,6 +838,7 @@ local function serialize(_handle,root,name,specification) -- handle wins
         compact   = true
         inline    = true
         functions = true
+        metacheck = true
     end
     if tname == "string" then
         if name == "return" then
@@ -857,8 +863,9 @@ local function serialize(_handle,root,name,specification) -- handle wins
     end
     if root then
         -- The dummy access will initialize a table that has a delayed initialization
-        -- using a metatable. (maybe explicitly test for metatable)
-        if getmetatable(root) then -- todo: make this an option, maybe even per subtable
+        -- using a metatable. (maybe explicitly test for metatable). This can crash on
+        -- metatables that check the index against a number.
+        if metacheck and getmetatable(root) then
             local dummy = root._w_h_a_t_e_v_e_r_
             root._w_h_a_t_e_v_e_r_ = nil
         end
diff --git a/src/fontloader/misc/fontloader-util-str.lua b/src/fontloader/misc/fontloader-util-str.lua
index a54a4aa..42dbf16 100644
--- a/src/fontloader/misc/fontloader-util-str.lua
+++ b/src/fontloader/misc/fontloader-util-str.lua
@@ -10,7 +10,7 @@ utilities         = utilities or { }
 utilities.strings = utilities.strings or { }
 local strings     = utilities.strings
 
-local format, gsub, rep, sub = string.format, string.gsub, string.rep, string.sub
+local format, gsub, rep, sub, find = string.format, string.gsub, string.rep, string.sub, string.find
 local load, dump = load, string.dump
 local tonumber, type, tostring = tonumber, type, tostring
 local unpack, concat = table.unpack, table.concat
@@ -385,6 +385,43 @@ function number.signed(i)
     end
 end
 
+-- maybe to util-num
+
+local digit  = patterns.digit
+local period = patterns.period
+local three  = digit * digit * digit
+
+local splitter = Cs (
+    (((1 - (three^1 * period))^1 + C(three)) * (Carg(1) * three)^1 + C((1-period)^1))
+  * (P(1)/"" * Carg(2)) * C(2)
+)
+
+patterns.formattednumber = splitter
+
+function number.formatted(n,sep1,sep2)
+    local s = type(s) == "string" and n or format("%0.2f",n)
+    if sep1 == true then
+        return lpegmatch(splitter,s,1,".",",")
+    elseif sep1 == "." then
+        return lpegmatch(splitter,s,1,sep1,sep2 or ",")
+    elseif sep1 == "," then
+        return lpegmatch(splitter,s,1,sep1,sep2 or ".")
+    else
+        return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".")
+    end
+end
+
+-- print(number.formatted(1))
+-- print(number.formatted(12))
+-- print(number.formatted(123))
+-- print(number.formatted(1234))
+-- print(number.formatted(12345))
+-- print(number.formatted(123456))
+-- print(number.formatted(1234567))
+-- print(number.formatted(12345678))
+-- print(number.formatted(12345678,true))
+-- print(number.formatted(1234.56,"!","?"))
+
 local zero      = P("0")^1 / ""
 local plus      = P("+")   / ""
 local minus     = P("-")
@@ -732,43 +769,6 @@ local format_W = function(f) -- handy when doing depth related indent
     return format("nspaces[%s]",tonumber(f) or 0)
 end
 
--- maybe to util-num
-
-local digit  = patterns.digit
-local period = patterns.period
-local three  = digit * digit * digit
-
-local splitter = Cs (
-    (((1 - (three^1 * period))^1 + C(three)) * (Carg(1) * three)^1 + C((1-period)^1))
-  * (P(1)/"" * Carg(2)) * C(2)
-)
-
-patterns.formattednumber = splitter
-
-function number.formatted(n,sep1,sep2)
-    local s = type(s) == "string" and n or format("%0.2f",n)
-    if sep1 == true then
-        return lpegmatch(splitter,s,1,".",",")
-    elseif sep1 == "." then
-        return lpegmatch(splitter,s,1,sep1,sep2 or ",")
-    elseif sep1 == "," then
-        return lpegmatch(splitter,s,1,sep1,sep2 or ".")
-    else
-        return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".")
-    end
-end
-
--- print(number.formatted(1))
--- print(number.formatted(12))
--- print(number.formatted(123))
--- print(number.formatted(1234))
--- print(number.formatted(12345))
--- print(number.formatted(123456))
--- print(number.formatted(1234567))
--- print(number.formatted(12345678))
--- print(number.formatted(12345678,true))
--- print(number.formatted(1234.56,"!","?"))
-
 local format_m = function(f)
     n = n + 1
     if not f or f == "" then
@@ -801,9 +801,16 @@ end
 local format_extension = function(extensions,f,name)
     local extension = extensions[name] or "tostring(%s)"
     local f = tonumber(f) or 1
+    local w = find(extension,"%.%.%.")
     if f == 0 then
+        if w then
+            extension = gsub(extension,"%.%.%.","")
+        end
         return extension
     elseif f == 1 then
+        if w then
+            extension = gsub(extension,"%.%.%.","%%s")
+        end
         n = n + 1
         local a = "a" .. n
         return format(extension,a,a) -- maybe more times?
@@ -811,6 +818,11 @@ local format_extension = function(extensions,f,name)
         local a = "a" .. (n + f + 1)
         return format(extension,a,a)
     else
+        if w then
+            extension = gsub(extension,"%.%.%.",rep("%%s,",f-1).."%%s")
+        end
+        -- we could fill an array and then n = n + 1 unpack(t,n,n+f) but as we
+        -- cache we don't save much and there are hardly any extensions anyway
         local t = { }
         for i=1,f do
             n = n + 1
diff --git a/src/fontloader/runtime/fontloader-basics-gen.lua b/src/fontloader/runtime/fontloader-basics-gen.lua
index 871e548..8ea93cc 100644
--- a/src/fontloader/runtime/fontloader-basics-gen.lua
+++ b/src/fontloader/runtime/fontloader-basics-gen.lua
@@ -258,45 +258,83 @@ function caches.is_writable(path,name)
     return fullname and file.is_writable(fullname)
 end
 
-function caches.loaddata(paths,name)
-    for i=1,#paths do
-        local data = false
-        local luaname, lucname = makefullname(paths[i],name)
-        if lucname and not lfs.isfile(lucname) and type(caches.compile) == "function" then
-            -- in case we used luatex and luajittex mixed ... lub or luc file
-            texio.write(string.format("(compiling luc: %s)",lucname))
-            data = loadfile(luaname)
-            if data then
-                data = data()
-            end
-            if data then
-                caches.compile(data,luaname,lucname)
-                return data
-            end
+-- function caches.loaddata(paths,name)
+--     for i=1,#paths do
+--         local data = false
+--         local luaname, lucname = makefullname(paths[i],name)
+--         if lucname and not lfs.isfile(lucname) and type(caches.compile) == "function" then
+--             -- in case we used luatex and luajittex mixed ... lub or luc file
+--             texio.write(string.format("(compiling luc: %s)",lucname))
+--             data = loadfile(luaname)
+--             if data then
+--                 data = data()
+--             end
+--             if data then
+--                 caches.compile(data,luaname,lucname)
+--                 return data
+--             end
+--         end
+--         if lucname and lfs.isfile(lucname) then -- maybe also check for size
+--             texio.write(string.format("(load luc: %s)",lucname))
+--             data = loadfile(lucname)
+--             if data then
+--                 data = data()
+--             end
+--             if data then
+--                 return data
+--             else
+--                 texio.write(string.format("(loading failed: %s)",lucname))
+--             end
+--         end
+--         if luaname and lfs.isfile(luaname) then
+--             texio.write(string.format("(load lua: %s)",luaname))
+--             data = loadfile(luaname)
+--             if data then
+--                 data = data()
+--             end
+--             if data then
+--                 return data
+--             end
+--         end
+--     end
+-- end
+
+function caches.loaddata(readables,name,writable)
+    for i=1,#readables do
+        local path   = readables[i]
+        local loader = false
+        local luaname, lucname = makefullname(path,name)
+        if lfs.isfile(lucname) then
+            loader = loadfile(lucname)
         end
-        if lucname and lfs.isfile(lucname) then -- maybe also check for size
-            texio.write(string.format("(load luc: %s)",lucname))
-            data = loadfile(lucname)
-            if data then
-                data = data()
+        if not loader and lfs.isfile(luaname) then
+            -- can be different paths when we read a file database from disk
+            local luacrap, lucname = makefullname(writable,name)
+            texio.write(string.format("(compiling luc: %s)",lucname))
+            if lfs.isfile(lucname) then
+                loader = loadfile(lucname)
             end
-            if data then
-                return data
+            caches.compile(data,luaname,lucname)
+            if lfs.isfile(lucname) then
+                texio.write(string.format("(load luc: %s)",lucname))
+                loader = loadfile(lucname)
             else
                 texio.write(string.format("(loading failed: %s)",lucname))
             end
-        end
-        if luaname and lfs.isfile(luaname) then
-            texio.write(string.format("(load lua: %s)",luaname))
-            data = loadfile(luaname)
-            if data then
-                data = data()
-            end
-            if data then
-                return data
+            if not loader then
+                texio.write(string.format("(load lua: %s)",luaname))
+                loader = loadfile(luaname)
+            else
+                texio.write(string.format("(loading failed: %s)",luaname))
             end
         end
+        if loader then
+            loader = loader()
+            collectgarbage("step")
+            return loader
+        end
     end
+    return false
 end
 
 function caches.savedata(path,name,data)
diff --git a/src/fontloader/runtime/fontloader-reference.lua b/src/fontloader/runtime/fontloader-reference.lua
index 6ef9430..6b899a1 100644
--- a/src/fontloader/runtime/fontloader-reference.lua
+++ b/src/fontloader/runtime/fontloader-reference.lua
@@ -1,6 +1,6 @@
 -- merged file : c:/data/develop/context/sources/luatex-fonts-merged.lua
 -- parent file : c:/data/develop/context/sources/luatex-fonts.lua
--- merge date  : 08/11/16 13:56:03
+-- merge date  : 08/27/16 13:35:36
 
 do -- begin closure to overcome local limits and interference
 
@@ -1352,7 +1352,7 @@ function table.fromhash(t)
   end
   return hsh
 end
-local noquotes,hexify,handle,compact,inline,functions
+local noquotes,hexify,handle,compact,inline,functions,metacheck
 local reserved=table.tohash { 
   'and','break','do','else','elseif','end','false','for','function','if',
   'in','local','nil','not','or','repeat','return','then','true','until','while',
@@ -1422,7 +1422,7 @@ local function do_serialize(root,name,depth,level,indexed)
     if compact then
       last=#root
       for k=1,last do
-        if root[k]==nil then
+        if rawget(root,k)==nil then
           last=k-1
           break
         end
@@ -1619,6 +1619,7 @@ local function serialize(_handle,root,name,specification)
     functions=specification.functions
     compact=specification.compact
     inline=specification.inline and compact
+    metacheck=specification.metacheck
     if functions==nil then
       functions=true
     end
@@ -1628,6 +1629,9 @@ local function serialize(_handle,root,name,specification)
     if inline==nil then
       inline=compact
     end
+    if metacheck==nil then
+      metacheck=true
+    end
   else
     noquotes=false
     hexify=false
@@ -1635,6 +1639,7 @@ local function serialize(_handle,root,name,specification)
     compact=true
     inline=true
     functions=true
+    metacheck=true
   end
   if tname=="string" then
     if name=="return" then
@@ -1658,7 +1663,7 @@ local function serialize(_handle,root,name,specification)
     handle("t={")
   end
   if root then
-    if getmetatable(root) then 
+    if metacheck and getmetatable(root) then
       local dummy=root._w_h_a_t_e_v_e_r_
       root._w_h_a_t_e_v_e_r_=nil
     end
@@ -3416,7 +3421,7 @@ if not modules then modules={} end modules ['util-str']={
 utilities=utilities or {}
 utilities.strings=utilities.strings or {}
 local strings=utilities.strings
-local format,gsub,rep,sub=string.format,string.gsub,string.rep,string.sub
+local format,gsub,rep,sub,find=string.format,string.gsub,string.rep,string.sub,string.find
 local load,dump=load,string.dump
 local tonumber,type,tostring=tonumber,type,tostring
 local unpack,concat=table.unpack,table.concat
@@ -3599,6 +3604,25 @@ function number.signed(i)
     return "-",-i
   end
 end
+local digit=patterns.digit
+local period=patterns.period
+local three=digit*digit*digit
+local splitter=Cs (
+  (((1-(three^1*period))^1+C(three))*(Carg(1)*three)^1+C((1-period)^1))*(P(1)/""*Carg(2))*C(2)
+)
+patterns.formattednumber=splitter
+function number.formatted(n,sep1,sep2)
+  local s=type(s)=="string" and n or format("%0.2f",n)
+  if sep1==true then
+    return lpegmatch(splitter,s,1,".",",")
+  elseif sep1=="." then
+    return lpegmatch(splitter,s,1,sep1,sep2 or ",")
+  elseif sep1=="," then
+    return lpegmatch(splitter,s,1,sep1,sep2 or ".")
+  else
+    return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".")
+  end
+end
 local zero=P("0")^1/""
 local plus=P("+")/""
 local minus=P("-")
@@ -3882,25 +3906,6 @@ end
 local format_W=function(f) 
   return format("nspaces[%s]",tonumber(f) or 0)
 end
-local digit=patterns.digit
-local period=patterns.period
-local three=digit*digit*digit
-local splitter=Cs (
-  (((1-(three^1*period))^1+C(three))*(Carg(1)*three)^1+C((1-period)^1))*(P(1)/""*Carg(2))*C(2)
-)
-patterns.formattednumber=splitter
-function number.formatted(n,sep1,sep2)
-  local s=type(s)=="string" and n or format("%0.2f",n)
-  if sep1==true then
-    return lpegmatch(splitter,s,1,".",",")
-  elseif sep1=="." then
-    return lpegmatch(splitter,s,1,sep1,sep2 or ",")
-  elseif sep1=="," then
-    return lpegmatch(splitter,s,1,sep1,sep2 or ".")
-  else
-    return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".")
-  end
-end
 local format_m=function(f)
   n=n+1
   if not f or f=="" then
@@ -3925,9 +3930,16 @@ end
 local format_extension=function(extensions,f,name)
   local extension=extensions[name] or "tostring(%s)"
   local f=tonumber(f) or 1
+  local w=find(extension,"%.%.%.")
   if f==0 then
+    if w then
+      extension=gsub(extension,"%.%.%.","")
+    end
     return extension
   elseif f==1 then
+    if w then
+      extension=gsub(extension,"%.%.%.","%%s")
+    end
     n=n+1
     local a="a"..n
     return format(extension,a,a) 
@@ -3935,6 +3947,9 @@ local format_extension=function(extensions,f,name)
     local a="a"..(n+f+1)
     return format(extension,a,a)
   else
+    if w then
+      extension=gsub(extension,"%.%.%.",rep("%%s,",f-1).."%%s")
+    end
     local t={}
     for i=1,f do
       n=n+1
@@ -4466,44 +4481,41 @@ function caches.is_writable(path,name)
   local fullname=makefullname(path,name)
   return fullname and file.is_writable(fullname)
 end
-function caches.loaddata(paths,name)
-  for i=1,#paths do
-    local data=false
-    local luaname,lucname=makefullname(paths[i],name)
-    if lucname and not lfs.isfile(lucname) and type(caches.compile)=="function" then
+function caches.loaddata(readables,name,writable)
+  for i=1,#readables do
+    local path=readables[i]
+    local loader=false
+    local luaname,lucname=makefullname(path,name)
+    if lfs.isfile(lucname) then
+      loader=loadfile(lucname)
+    end
+    if not loader and lfs.isfile(luaname) then
+      local luacrap,lucname=makefullname(writable,name)
       texio.write(string.format("(compiling luc: %s)",lucname))
-      data=loadfile(luaname)
-      if data then
-        data=data()
-      end
-      if data then
-        caches.compile(data,luaname,lucname)
-        return data
-      end
-    end
-    if lucname and lfs.isfile(lucname) then 
-      texio.write(string.format("(load luc: %s)",lucname))
-      data=loadfile(lucname)
-      if data then
-        data=data()
+      if lfs.isfile(lucname) then
+        loader=loadfile(lucname)
       end
-      if data then
-        return data
+      caches.compile(data,luaname,lucname)
+      if lfs.isfile(lucname) then
+        texio.write(string.format("(load luc: %s)",lucname))
+        loader=loadfile(lucname)
       else
         texio.write(string.format("(loading failed: %s)",lucname))
       end
-    end
-    if luaname and lfs.isfile(luaname) then
-      texio.write(string.format("(load lua: %s)",luaname))
-      data=loadfile(luaname)
-      if data then
-        data=data()
-      end
-      if data then
-        return data
+      if not loader then
+        texio.write(string.format("(load lua: %s)",luaname))
+        loader=loadfile(luaname)
+      else
+        texio.write(string.format("(loading failed: %s)",luaname))
       end
     end
+    if loader then
+      loader=loader()
+      collectgarbage("step")
+      return loader
+    end
   end
+  return false
 end
 function caches.savedata(path,name,data)
   local luaname,lucname=makefullname(path,name)
@@ -4630,7 +4642,7 @@ function containers.read(container,name)
   local storage=container.storage
   local stored=storage[name]
   if not stored and container.enabled and caches and containers.usecache then
-    stored=caches.loaddata(container.readables,name)
+    stored=caches.loaddata(container.readables,name,container.writable)
     if stored and stored.cache_version==container.version then
       if trace_cache or trace_containers then
         report_containers("action %a, category %a, name %a","load",container.subcategory,name)
@@ -8845,112 +8857,6 @@ function readers.math(f,fontdata,specification)
     reportskippedtable("math")
   end
 end
-local function packoutlines(data,makesequence)
-  local subfonts=data.subfonts
-  if subfonts then
-    for i=1,#subfonts do
-      packoutlines(subfonts[i],makesequence)
-    end
-    return
-  end
-  local common=data.segments
-  if common then
-    return
-  end
-  local glyphs=data.glyphs
-  if not glyphs then
-    return
-  end
-  if makesequence then
-    for index=1,#glyphs do
-      local glyph=glyphs[index]
-      local segments=glyph.segments
-      if segments then
-        local sequence={}
-        local nofsequence=0
-        for i=1,#segments do
-          local segment=segments[i]
-          local nofsegment=#segment
-          nofsequence=nofsequence+1
-          sequence[nofsequence]=segment[nofsegment]
-          for i=1,nofsegment-1 do
-            nofsequence=nofsequence+1
-            sequence[nofsequence]=segment[i]
-          end
-        end
-        glyph.sequence=sequence
-        glyph.segments=nil
-      end
-    end
-  else
-    local hash={}
-    local common={}
-    local reverse={}
-    local last=0
-    for index=1,#glyphs do
-      local segments=glyphs[index].segments
-      if segments then
-        for i=1,#segments do
-          local h=concat(segments[i]," ")
-          hash[h]=(hash[h] or 0)+1
-        end
-      end
-    end
-    for index=1,#glyphs do
-      local segments=glyphs[index].segments
-      if segments then
-        for i=1,#segments do
-          local segment=segments[i]
-          local h=concat(segment," ")
-          if hash[h]>1 then 
-            local idx=reverse[h]
-            if not idx then
-              last=last+1
-              reverse[h]=last
-              common[last]=segment
-              idx=last
-            end
-            segments[i]=idx
-          end
-        end
-      end
-    end
-    if last>0 then
-      data.segments=common
-    end
-  end
-end
-local function unpackoutlines(data)
-  local subfonts=data.subfonts
-  if subfonts then
-    for i=1,#subfonts do
-      unpackoutlines(subfonts[i])
-    end
-    return
-  end
-  local common=data.segments
-  if not common then
-    return
-  end
-  local glyphs=data.glyphs
-  if not glyphs then
-    return
-  end
-  for index=1,#glyphs do
-    local segments=glyphs[index].segments
-    if segments then
-      for i=1,#segments do
-        local c=common[segments[i]]
-        if c then
-          segments[i]=c
-        end
-      end
-    end
-  end
-  data.segments=nil
-end
-otf.packoutlines=packoutlines
-otf.unpackoutlines=unpackoutlines
 local function getinfo(maindata,sub,platformnames,rawfamilynames)
   local fontdata=sub and maindata.subfonts and maindata.subfonts[sub] or maindata
   local names=fontdata.names
@@ -9218,6 +9124,13 @@ function readers.loadshapes(filename,n)
     shapes=true,
     subfont=n,
   }
+  if fontdata then
+    for k,v in next,fontdata.glyphs do
+      v.class=nil
+      v.index=nil
+      v.math=nil
+    end
+  end
   return fontdata and {
     filename=filename,
     format=fontdata.format,
@@ -9347,56 +9260,6 @@ function readers.extend(fontdata)
     end
   end
 end
-if fonts.hashes then
-  local identifiers=fonts.hashes.identifiers
-  local loadshapes=readers.loadshapes
-  readers.version=0.006
-  readers.cache=containers.define("fonts","shapes",readers.version,true)
-  local function load(filename,sub)
-    local base=file.basename(filename)
-    local name=file.removesuffix(base)
-    local kind=file.suffix(filename)
-    local attr=lfs.attributes(filename)
-    local size=attr and attr.size or 0
-    local time=attr and attr.modification or 0
-    local sub=tonumber(sub)
-    if size>0 and (kind=="otf" or kind=="ttf" or kind=="tcc") then
-      local hash=containers.cleanname(base) 
-      if sub then
-        hash=hash.."-"..sub
-      end
-      data=containers.read(readers.cache,hash)
-      if not data or data.time~=time or data.size~=size then
-        data=loadshapes(filename,sub)
-        if data then
-          data.size=size
-          data.format=data.format or (kind=="otf" and "opentype") or "truetype"
-          data.time=time
-          packoutlines(data)
-          containers.write(readers.cache,hash,data)
-          data=containers.read(readers.cache,hash) 
-        end
-      end
-      unpackoutlines(data)
-    else
-      data={
-        filename=filename,
-        size=0,
-        time=time,
-        format="unknown",
-        units=1000,
-        glyphs={}
-      }
-    end
-    return data
-  end
-  fonts.hashes.shapes=table.setmetatableindex(function(t,k)
-    local d=identifiers[k]
-    local v=load(d.properties.filename,d.subindex)
-    t[k]=v
-    return v
-  end)
-end
 
 end -- closure
 
@@ -9415,6 +9278,7 @@ local concat,remove=table.concat,table.remove
 local floor,abs,round,ceil=math.floor,math.abs,math.round,math.ceil
 local P,C,R,S,C,Cs,Ct=lpeg.P,lpeg.C,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Ct
 local lpegmatch=lpeg.match
+local formatters=string.formatters
 local readers=fonts.handlers.otf.readers
 local streamreader=readers.streamreader
 local readbytes=streamreader.readbytes
@@ -9815,6 +9679,7 @@ do
   local ymax=0
   local checked=false
   local keepcurve=false
+  local version=2
   local function showstate(where)
     report("%w%-10s : [%s] n=%i",depth*2,where,concat(stack," ",1,top),top)
   end
@@ -10237,9 +10102,91 @@ do
       return floor((stems+7)/8)
     end
   end
-  local function unsupported()
+  local function unsupported(t)
     if trace_charstrings then
-      showstate("unsupported")
+      showstate("unsupported "..t)
+    end
+    top=0
+  end
+  local function unsupportedsub(t)
+    if trace_charstrings then
+      showstate("unsupported sub "..t)
+    end
+    top=0
+  end
+  local function getstem3()
+    if trace_charstrings then
+      showstate("stem3")
+    end
+    top=0
+  end
+  local function divide()
+    if version==1 then
+      local d=stack[top]
+      top=top-1
+      stack[top]=stack[top]/d
+    end
+  end
+  local function closepath()
+    if version==1 then
+      if trace_charstrings then
+        showstate("closepath")
+      end
+    end
+    top=0
+  end
+  local function hsbw()
+    if version==1 then
+      if trace_charstrings then
+        showstate("dotsection")
+      end
+      width=stack[top]
+    end
+    top=0
+  end
+  local function seac()
+    if version==1 then
+      if trace_charstrings then
+        showstate("seac")
+      end
+    end
+    top=0
+  end
+  local function sbw()
+    if version==1 then
+      if trace_charstrings then
+        showstate("sbw")
+      end
+      width=stack[top-1]
+    end
+    top=0
+  end
+  local function callothersubr()
+    if version==1 then
+      if trace_charstrings then
+        showstate("callothersubr (unsupported)")
+      end
+    end
+    top=0
+  end
+  local function pop()
+    if version==1 then
+      if trace_charstrings then
+        showstate("pop (unsupported)")
+      end
+      top=top+1
+      stack[top]=0 
+    else
+      top=0
+    end
+  end
+  local function setcurrentpoint()
+    if version==1 then
+      if trace_charstrings then
+        showstate("pop (unsupported)")
+      end
+      x=x+stack[top-1]
+      y=y+stack[top]
     end
     top=0
   end
@@ -10256,7 +10203,7 @@ do
     unsupported,
     unsupported,
     unsupported,
-    unsupported,
+    hsbw,
     unsupported,
     unsupported,
     unsupported,
@@ -10277,6 +10224,15 @@ do
     hvcurveto,
   }
   local subactions={
+    [000]=dotsection,
+    [001]=getstem3,
+    [002]=getstem3,
+    [006]=seac,
+    [007]=sbw,
+    [012]=divide,
+    [016]=callothersubr,
+    [017]=pop,
+    [033]=setcurrentpoint,
     [034]=hflex,
     [035]=flex,
     [036]=hflex1,
@@ -10284,23 +10240,29 @@ do
   }
   local p_bytes=Ct((P(1)/byte)^0)
   local function call(scope,list,bias,process)
-    local index=stack[top]+bias
-    top=top-1
-    if trace_charstrings then
-      showvalue(scope,index,true)
-    end
-    local str=list[index]
-    if str then
-      if type(str)=="string" then
-        str=lpegmatch(p_bytes,str)
-        list[index]=str
-      end
-      depth=depth+1
-      process(str)
-      depth=depth-1
+    depth=depth+1
+    if top==0 then
+      showstate(formatters["unknown %s call"](scope))
+      top=0
     else
-      report("unknown %s %i",scope,index)
+      local index=stack[top]+bias
+      top=top-1
+      if trace_charstrings then
+        showvalue(scope,index,true)
+      end
+      local tab=list[index]
+      if tab then
+        if type(tab)=="string" then
+          tab=lpegmatch(p_bytes,tab)
+          list[index]=tab
+        end
+        process(tab)
+      else
+        showstate(formatters["unknown %s call %i"](scope,index))
+        top=0
+      end
     end
+    depth=depth-1
   end
   local function process(tab)
     local i=1
@@ -10367,7 +10329,7 @@ do
         local t=tab[i]
         local a=subactions[t]
         if a then
-          a()
+          a(t)
         else
           if trace_charstrings then
             showvalue("<subaction>",t)
@@ -10378,7 +10340,7 @@ do
       else
         local a=actions[t]
         if a then
-          local s=a()
+          local s=a(t)
           if s then
             i=i+s
           end
@@ -10392,25 +10354,38 @@ do
       end
     end
   end
-  parsecharstrings=function(data,glyphs,doshapes)
+  local function setbias(globals,locals)
+    if version==1 then
+      return
+        false,
+        false
+    else
+      local g,l=#globals,#locals
+      return
+        ((g<1240 and 107) or (g<33900 and 1131) or 32768)+1,
+        ((l<1240 and 107) or (l<33900 and 1131) or 32768)+1
+    end
+  end
+  parsecharstrings=function(data,glyphs,doshapes,tversion)
     local dictionary=data.dictionaries[1]
     local charstrings=dictionary.charstrings
     local charset=dictionary.charset
+    local private=dictionary.private or { data={} }
     keepcurve=doshapes
+    version=tversion
     stack={}
     glyphs=glyphs or {}
     strings=data.strings
-    locals=dictionary.subroutines
-    globals=data.routines
-    globalbias=#globals
-    localbias=#locals
-    globalbias=((globalbias<1240 and 107) or (globalbias<33900 and 1131) or 32768)+1
-    localbias=((localbias<1240 and 107) or (localbias<33900 and 1131) or 32768)+1
-    local nominalwidth=dictionary.private.data.nominalwidthx or 0
-    local defaultwidth=dictionary.private.data.defaultwidthx or 0
+    globals=data.routines or {}
+    locals=dictionary.subroutines or {}
+    globalbias,localbias=setbias(globals,locals)
+    local nominalwidth=private.data.nominalwidthx or 0
+    local defaultwidth=private.data.defaultwidthx or 0
     for i=1,#charstrings do
-      local str=charstrings[i]
-      local tab=lpegmatch(p_bytes,str)
+      local tab=charstrings[i]
+      if type(tab)=="string" then
+        tab=lpegmatch(p_bytes,tab)
+      end
       local index=i-1
       x=0
       y=0
@@ -10461,19 +10436,19 @@ do
     end
     return glyphs
   end
-  parsecharstring=function(data,dictionary,charstring,glyphs,index,doshapes)
+  parsecharstring=function(data,dictionary,tab,glyphs,index,doshapes,tversion)
     local private=dictionary.private
     keepcurve=doshapes
+    version=tversion
     strings=data.strings 
     locals=dictionary.subroutines or {}
     globals=data.routines or {}
-    globalbias=#globals
-    localbias=#locals
-    globalbias=((globalbias<1240 and 107) or (globalbias<33900 and 1131) or 32768)+1
-    localbias=((localbias<1240 and 107) or (localbias<33900 and 1131) or 32768)+1
+    globalbias,localbias=setbias(globals,locals)
     local nominalwidth=private and private.data.nominalwidthx or 0
     local defaultwidth=private and private.data.defaultwidthx or 0
-    local tab=lpegmatch(p_bytes,charstring)
+    if type(tab)=="string" then
+      tab=lpegmatch(p_bytes,tab)
+    end
     x=0
     y=0
     width=false
@@ -10497,7 +10472,7 @@ do
     else
       width=nominalwidth+width
     end
-index=index-1
+    index=index-1
     local glyph=glyphs[index] 
     if not glyph then
       glyphs[index]={
@@ -10520,7 +10495,6 @@ index=index-1
       report("width: %s",tostring(width))
       report("boundingbox: % t",boundingbox)
     end
-    return charstring
   end
   resetcharstrings=function()
     result={}
@@ -10634,7 +10608,7 @@ local function readcidprivates(f,data)
   end
   parseprivates(data,dictionaries)
 end
-local function readnoselect(f,data,glyphs,doshapes)
+local function readnoselect(f,data,glyphs,doshapes,version)
   local dictionaries=data.dictionaries
   local dictionary=dictionaries[1]
   readglobals(f,data)
@@ -10644,10 +10618,11 @@ local function readnoselect(f,data,glyphs,doshapes)
   readprivates(f,data)
   parseprivates(data,data.dictionaries)
   readlocals(f,data,dictionary)
-  parsecharstrings(data,glyphs,doshapes)
+  parsecharstrings(data,glyphs,doshapes,version)
   resetcharstrings()
 end
-local function readfdselect(f,data,glyphs,doshapes)
+readers.parsecharstrings=parsecharstrings
+local function readfdselect(f,data,glyphs,doshapes,version)
   local header=data.header
   local dictionaries=data.dictionaries
   local dictionary=dictionaries[1]
@@ -10705,7 +10680,7 @@ local function readfdselect(f,data,glyphs,doshapes)
       readlocals(f,data,dictionaries[i])
     end
     for i=1,#charstrings do
-      parsecharstring(data,dictionaries[fdindex[i]+1],charstrings[i],glyphs,i,doshapes)
+      parsecharstring(data,dictionaries[fdindex[i]+1],charstrings[i],glyphs,i,doshapes,version)
     end
     resetcharstrings()
   end
@@ -11199,6 +11174,7 @@ local bittest=bit32.btest
 local rshift=bit32.rshift
 local concat=table.concat
 local lower=string.lower
+local copy=table.copy
 local sub=string.sub
 local strip=string.strip
 local tohash=table.tohash
@@ -12632,7 +12608,7 @@ do
                           markclass=d.markclass or nil,
                           flags=d.flags,
                         }
-                        sublookuplist[nofsublookups]=h
+                        sublookuplist[nofsublookups]=copy(h) 
                         sublookuphash[lookupid]=nofsublookups
                         sublookupcheck[lookupid]=1
                       else
@@ -15344,7 +15320,7 @@ local trace_defining=false registertracker("fonts.defining",function(v) trace_de
 local report_otf=logs.reporter("fonts","otf loading")
 local fonts=fonts
 local otf=fonts.handlers.otf
-otf.version=3.025 
+otf.version=3.026 
 otf.cache=containers.define("fonts","otl",otf.version,true)
 otf.svgcache=containers.define("fonts","svg",otf.version,true)
 otf.pdfcache=containers.define("fonts","pdf",otf.version,true)
@@ -16624,7 +16600,7 @@ function injections.setkern(current,factor,rlmode,x,injection)
     return 0,0
   end
 end
-function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk) 
+function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk,checkmark) 
   local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2])
   nofregisteredmarks=nofregisteredmarks+1
   if rlmode>=0 then
@@ -16642,6 +16618,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk)
         i.markbase=nofregisteredmarks
         i.markbasenode=base
         i.markmark=mkmk
+        i.checkmark=checkmark
       end
     else
       p.injections={
@@ -16651,6 +16628,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk)
         markbase=nofregisteredmarks,
         markbasenode=base,
         markmark=mkmk,
+        checkmark=checkmark,
       }
     end
   else
@@ -16662,6 +16640,7 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,tfmbase,mkmk)
         markbase=nofregisteredmarks,
         markbasenode=base,
         markmark=mkmk,
+        checkmark=checkmark,
       },
     }
   end
@@ -17213,10 +17192,16 @@ local function inject_everything(head,where)
       end
     else
         ox=px-pn.markx
-      local wn=getfield(n,"width") 
-      if wn~=0 then
-        pn.leftkern=-wn/2
-        pn.rightkern=-wn/2
+      if pn.checkmark then
+        local wn=getfield(n,"width") 
+        if wn~=0 then
+          wn=wn/2
+          if trace_injections then
+            report_injections("correcting non zero width mark %C",getchar(n))
+          end
+          insert_node_before(n,n,newkern(-wn))
+          insert_node_after(n,n,newkern(-wn))
+        end
       end
     end
     local oy=getfield(n,"yoffset")+getfield(p,"yoffset")+pn.marky
@@ -17240,10 +17225,10 @@ local function inject_everything(head,where)
               nofmarks=nofmarks+1
               marks[nofmarks]=current
             else
-local yoffset=i.yoffset
-if yoffset and yoffset~=0 then
-  setfield(current,"yoffset",yoffset)
-end
+              local yoffset=i.yoffset
+              if yoffset and yoffset~=0 then
+                setfield(current,"yoffset",yoffset)
+              end
               if hascursives then
                 local cursivex=i.cursivex
                 if cursivex then
@@ -17884,33 +17869,40 @@ local mappers={
 }
 local classifiers=characters.classifiers
 if not classifiers then
-  local first_arabic,last_arabic=characters.blockrange("arabic")
-  local first_syriac,last_syriac=characters.blockrange("syriac")
-  local first_mandiac,last_mandiac=characters.blockrange("mandiac")
-  local first_nko,last_nko=characters.blockrange("nko")
+  local f_arabic,l_arabic=characters.blockrange("arabic")
+  local f_syriac,l_syriac=characters.blockrange("syriac")
+  local f_mandiac,l_mandiac=characters.blockrange("mandiac")
+  local f_nko,l_nko=characters.blockrange("nko")
+  local f_ext_a,l_ext_a=characters.blockrange("arabicextendeda")
   classifiers=table.setmetatableindex(function(t,k)
-    local c=chardata[k]
-    local v=false
-    if c then
-      local arabic=c.arabic
-      if arabic then
-        v=mappers[arabic]
-        if not v then
-          log.report("analyze","error in mapping arabic %C",k)
-          v=false
-        end
-      elseif k>=first_arabic and k<=last_arabic or k>=first_syriac and k<=last_syriac or
-          k>=first_mandiac and k<=last_mandiac or k>=first_nko   and k<=last_nko   then
-        if categories[k]=="mn" then
-          v=s_mark
-        else
-          v=s_rest
+    if type(k)=="number" then
+      local c=chardata[k]
+      local v=false
+      if c then
+        local arabic=c.arabic
+        if arabic then
+          v=mappers[arabic]
+          if not v then
+            log.report("analyze","error in mapping arabic %C",k)
+            v=false
+          end
+        elseif (k>=f_arabic and k<=l_arabic) or
+            (k>=f_syriac and k<=l_syriac) or
+            (k>=f_mandiac and k<=l_mandiac) or
+            (k>=f_nko   and k<=l_nko)   or
+            (k>=f_ext_a  and k<=l_ext_a)  then
+          if categories[k]=="mn" then
+            v=s_mark
+          else
+            v=s_rest
+          end
         end
       end
+      t[k]=v
+      return v
     end
-    t[k]=v
-    return v
   end)
+  characters.classifiers=classifiers
 end
 function methods.arab(head,font,attr)
   local first,last=nil,nil
@@ -18180,6 +18172,7 @@ local marks=false
 local currentfont=false
 local factor=0
 local threshold=0
+local checkmarks=false
 local sweepnode=nil
 local sweepprev=nil
 local sweepnext=nil
@@ -18795,7 +18788,7 @@ function handlers.gpos_mark2base(head,start,dataset,sequence,markanchors,rlmode)
         local ba=markanchors[1][basechar]
         if ba then
           local ma=markanchors[2]
-          local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar])
+          local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks)
           if trace_marks then
             logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)",
               pref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy)
@@ -18850,7 +18843,7 @@ function handlers.gpos_mark2ligature(head,start,dataset,sequence,markanchors,rlm
             local index=getligaindex(start)
             ba=ba[index]
             if ba then
-              local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar]) 
+              local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks)
               if trace_marks then
                 logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)",
                   pref(dataset,sequence),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy)
@@ -18897,7 +18890,7 @@ function handlers.gpos_mark2mark(head,start,dataset,sequence,markanchors,rlmode)
         local ba=markanchors[1][basechar] 
         if ba then
           local ma=markanchors[2]
-          local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true)
+          local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true,checkmarks)
           if trace_marks then
             logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
               pref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy)
@@ -19279,7 +19272,7 @@ function chainprocs.gpos_mark2base(head,start,stop,dataset,sequence,currentlooku
           if ba then
             local ma=markanchors[2]
             if ma then
-              local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar])
+              local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks)
               if trace_marks then
                 logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)",
                   cref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy)
@@ -19345,7 +19338,7 @@ function chainprocs.gpos_mark2ligature(head,start,stop,dataset,sequence,currentl
               local index=getligaindex(start)
               ba=ba[index]
               if ba then
-                local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar])
+                local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],false,checkmarks)
                 if trace_marks then
                   logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)",
                     cref(dataset,sequence),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy)
@@ -19397,7 +19390,7 @@ function chainprocs.gpos_mark2mark(head,start,stop,dataset,sequence,currentlooku
           if ba then
             local ma=markanchors[2]
             if ma then
-              local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true)
+              local dx,dy,bound=setmark(start,base,factor,rlmode,ba,ma,characters[basechar],true,checkmarks)
               if trace_marks then
                 logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)",
                   cref(dataset,sequence),anchor,bound,gref(markchar),gref(basechar),dx,dy)
@@ -20837,6 +20830,7 @@ local function featuresprocessor(head,font,attr)
     marks=tfmdata.resources.marks
     threshold,
     factor=getthreshold(font)
+    checkmarks=tfmdata.properties.checkmarks
   elseif currentfont~=font then
     report_warning("nested call with a different font, level %s, quitting",nesting)
     nesting=nesting-1
@@ -21057,7 +21051,7 @@ local setspacekerns=nodes.injections.setspacekerns if not setspacekerns then os.
 if fontfeatures then
   function otf.handlers.trigger_space_kerns(head,start,dataset,sequence,_,_,_,_,font,attr)
     local features=fontfeatures[font]
-    local enabled=features.spacekern==true and features.kern==true
+    local enabled=features and features.spacekern and features.kern
     if enabled then
       setspacekerns(font,sequence)
     end
@@ -21067,7 +21061,7 @@ else
   function otf.handlers.trigger_space_kerns(head,start,dataset,sequence,_,_,_,_,font,attr)
     local shared=fontdata[font].shared
     local features=shared and shared.features
-    local enabled=features and features.spacekern==true and features.kern==true
+    local enabled=features and features.spacekern and features.kern
     if enabled then
       setspacekerns(font,sequence)
     end
@@ -21122,7 +21116,7 @@ local function spaceinitializer(tfmdata,value)
           if kern then
             if feat then
               for script,languages in next,kern do
-                local f=feat[k]
+                local f=feat[script]
                 if f then
                   for l in next,languages do
                     f[l]=true
@@ -21137,24 +21131,38 @@ local function spaceinitializer(tfmdata,value)
             for i=1,#steps do
               local step=steps[i]
               local coverage=step.coverage
-              if coverage then
+              local rules=step.rules
+              local format=step.format
+              if rules then
+              elseif coverage then
+                local single=format==gpos_single
                 local kerns=coverage[32]
                 if kerns then
                   for k,v in next,kerns do
-                    if type(v)=="table" then
-                      right[k]=v[3] 
-                    else
+                    if type(v)~="table" then
                       right[k]=v
+                    elseif single then
+                      right[k]=v[3]
+                    else
+                      local one=v[1]
+                      if one then
+                        right[k]=one[3]
+                      end
                     end
                   end
                 end
                 for k,v in next,coverage do
                   local kern=v[32]
                   if kern then
-                    if type(kern)=="table" then
-                      left[k]=kern[3] 
-                    else
+                    if type(kern)~="table" then
                       left[k]=kern
+                    elseif single then
+                      left[k]=v[3]
+                    else
+                      local one=v[1]
+                      if one then
+                        left[k]=one[3]
+                      end
                     end
                   end
                 end
@@ -21202,6 +21210,18 @@ registerotffeature {
     node=spaceinitializer,
   },
 }
+local function markinitializer(tfmdata,value)
+  local properties=tfmdata.properties
+  properties.checkmarks=value
+end
+registerotffeature {
+  name="checkmarks",
+  description="check mark widths",
+  default=true,
+  initializers={
+    node=markinitializer,
+  },
+}
 
 end -- closure
 
@@ -21605,9 +21625,11 @@ local function initializedevanagi(tfmdata)
         local steps=sequence.steps
         local nofsteps=sequence.nofsteps
         local features=sequence.features
-        if features["rphf"] then
+        local has_rphf=features.rphf
+        local has_blwf=features.blwf
+        if has_rphf and has_rphf.deva then
           devanagari.reph=true
-        elseif features["blwf"] then
+        elseif has_blwf and has_blwf.deva then
           devanagari.vattu=true
           for i=1,nofsteps do
             local step=steps[i]
@@ -21621,57 +21643,60 @@ local function initializedevanagi(tfmdata)
             end
           end
         end
-        if valid[kind] then
-          for i=1,nofsteps do
-            local step=steps[i]
-            local coverage=step.coverage
-            if coverage then
-              local reph=false
-              if step.osdstep then
-                for k,v in next,ra do
-                  local r=coverage[k]
-                  if r then
-                    local h=false
-                    for k,v in next,halant do
-                      local h=r[k]
-                      if h then
-                        reph=h.ligature or false
-                        break
+        for kind,spec in next,features do 
+          if spec.dev2 and valid[kind] then
+            for i=1,nofsteps do
+              local step=steps[i]
+              local coverage=step.coverage
+              if coverage then
+                local reph=false
+                if kind=="rphf" then
+                  if true then
+                    for k,v in next,ra do
+                      local r=coverage[k]
+                      if r then
+                        local h=false
+                        for k,v in next,halant do
+                          local h=r[k]
+                          if h then
+                            reph=h.ligature or false
+                            break
+                          end
+                        end
+                        if reph then
+                          break
+                        end
                       end
                     end
-                    if reph then
-                      break
-                    end
+                  else
                   end
                 end
-              else
+                seqsubset[#seqsubset+1]={ kind,coverage,reph }
               end
-              seqsubset[#seqsubset+1]={ kind,coverage,reph }
             end
           end
-        end
-        if kind=="pref" then
-          local sequence=dataset[3] 
-          local steps=sequence.steps
-          local nofsteps=sequence.nofsteps
-          for i=1,nofsteps do
-            local step=steps[i]
-            local coverage=step.coverage
-            if coverage then
-              for k,v in next,halant do
-                local h=coverage[k]
-                if h then
-                  local found=false
-                  for k,v in next,h do
-                    found=v and v.ligature
+          if kind=="pref" then
+            local steps=sequence.steps
+            local nofsteps=sequence.nofsteps
+            for i=1,nofsteps do
+              local step=steps[i]
+              local coverage=step.coverage
+              if coverage then
+                for k,v in next,halant do
+                  local h=coverage[k]
+                  if h then
+                    local found=false
+                    for k,v in next,h do
+                      found=v and v.ligature
+                      if found then
+                        pre_base_reordering_consonants[k]=found
+                        break
+                      end
+                    end
                     if found then
-                      pre_base_reordering_consonants[k]=found
                       break
                     end
                   end
-                  if found then
-                    break
-                  end
                 end
               end
             end
@@ -22058,6 +22083,8 @@ function handlers.devanagari_reorder_matras(head,start)
         start=startnext
         break
       end
+    else
+      break
     end
     current=next
   end
@@ -22070,12 +22097,12 @@ function handlers.devanagari_reorder_reph(head,start)
   local startfont=getfont(start)
   local startattr=getprop(start,a_syllabe)
   while current do
-    local char=ischar(current,font)
+    local char=ischar(current,startfont)
     if char and getprop(current,a_syllabe)==startattr then 
       if halant[char] and not getprop(current,a_state) then
         local next=getnext(current)
         if next then
-          local nextchar=ischar(next,font)
+          local nextchar=ischar(next,startfont)
           if nextchar and zw_char[nextchar] and getprop(next,a_syllabe)==startattr then
             current=next
             next=getnext(current)
@@ -22097,7 +22124,7 @@ function handlers.devanagari_reorder_reph(head,start)
   if not startnext then
     current=getnext(start)
     while current do
-      local char=ischar(current,font)
+      local char=ischar(current,startfont)
       if char and getprop(current,a_syllabe)==startattr then 
         if getprop(current,a_state)==s_pstf then 
           startnext=getnext(start)
@@ -22119,7 +22146,7 @@ function handlers.devanagari_reorder_reph(head,start)
     current=getnext(start)
     local c=nil
     while current do
-      local char=ischar(current,font)
+      local char=ischar(current,startfont)
       if char and getprop(current,a_syllabe)==startattr then 
         if not c and mark_above_below_post[char] and reorder_class[char]~="after subscript" then
           c=current
@@ -22143,7 +22170,7 @@ function handlers.devanagari_reorder_reph(head,start)
     current=start
     local next=getnext(current)
     while next do
-      local nextchar=ischar(next,font)
+      local nextchar=ischar(next,startfont)
       if nextchar and getprop(next,a_syllabe)==startattr then 
         current=next
         next=getnext(current)
@@ -22156,7 +22183,7 @@ function handlers.devanagari_reorder_reph(head,start)
       head=remove_node(head,start)
       local next=getnext(current)
       setlink(start,next)
-      setlink(current,"next",start)
+      setlink(current,start)
       start=startnext
     end
   end
@@ -22169,12 +22196,12 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start)
   local startfont=getfont(start)
   local startattr=getprop(start,a_syllabe)
   while current do
-    local char=ischar(current,font)
+    local char=ischar(current,startfont)
     if char and getprop(current,a_syllabe)==startattr then
       local next=getnext(current)
       if halant[char] and not getprop(current,a_state) then
         if next then
-          local nextchar=ischar(next,font)
+          local nextchar=ischar(next,startfont)
           if nextchar and getprop(next,a_syllabe)==startattr then
             if nextchar==c_zwnj or nextchar==c_zwj then
               current=next
@@ -22198,13 +22225,13 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start)
     current=getnext(start)
     startattr=getprop(start,a_syllabe)
     while current do
-      local char=ischar(current,font)
+      local char=ischar(current,startfont)
       if char and getprop(current,a_syllabe)==startattr then
         if not consonant[char] and getprop(current,a_state) then 
           startnext=getnext(start)
           removenode(start,start)
           local prev=getprev(current)
-          setlink(start,prev)
+          setlink(prev,start)
           setlink(start,current)
           start=startnext
           break
@@ -22264,21 +22291,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces)
     local kind=subset[1]
     local lookupcache=subset[2]
     if kind=="rphf" then
-      for k,v in next,ra do
-        local r=lookupcache[k]
-        if r then
-          for k,v in next,halant do
-            local h=r[k]
-            if h then
-              reph=h.ligature or false
-              break
-            end
-          end
-          if reph then
-            break
-          end
-        end
-      end
+      reph=subset[3]
       local current=start
       local last=getnext(stop)
       while current~=last do
@@ -22311,7 +22324,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces)
         if current~=stop then
           local c=locl[current] or getchar(current)
           local found=lookupcache[c]
-          if found then
+          if found then 
             local next=getnext(current)
             local n=locl[next] or getchar(next)
             if found[n] then
@@ -22538,7 +22551,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces)
         local prev=getprev(current)
         if prev~=target then
           local next=getnext(current)
-          setlink(next,prev)
+          setlink(prev,next)
           if current==stop then
             stop=prev
           end
@@ -22569,7 +22582,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces)
         stop=current
       end
       local prev=getprev(c)
-      setlink(next,prev)
+      setlink(prev,next)
       local nextnext=getnext(next)
       setnext(current,nextnext)
       local nextnextnext=getnext(nextnext)
@@ -22582,6 +22595,9 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces)
     current=getnext(current)
   end
   if getchar(base)==c_nbsp then
+    if base==stop then
+      stop=getprev(stop)
+    end
     nbspaces=nbspaces-1
     head=remove_node(head,base)
     flush_node(base)
@@ -22623,7 +22639,7 @@ local function analyze_next_chars_one(c,font,variant)
               elseif (vv==c_zwnj or vv==c_zwj) and halant[vvv] then
                 local nnnn=getnext(nnn)
                 if nnnn then
-                  local vvvv=ischar(nnnn)
+                  local vvvv=ischar(nnnn,font)
                   if vvvv and consonant[vvvv] then
                     c=nnnn
                   end
@@ -22646,7 +22662,7 @@ local function analyze_next_chars_one(c,font,variant)
         local nn=getnext(n)
         if nn then
           local vv=ischar(nn,font)
-          if vv and zw_char[vv] then
+          if vv and zw_char[v] then
             n=nn
             v=vv
             nn=getnext(nn)
@@ -24226,26 +24242,25 @@ handlers.afm=afm
 local readers=afm.readers or {}
 afm.readers=readers
 afm.version=1.512
-local get_indexes
+local get_indexes,get_shapes
 do
-  local n,m
-  local progress=function(str,position,name,size)
-    local forward=position+tonumber(size)+3+2
-    n=n+1
-    if n>=m then
-      return #str,name
-    elseif forward<#str then
-      return forward,name
-    else
-      return #str,name
+  local decrypt
+  do
+    local r,c1,c2,n=0,0,0,0
+    local function step(c)
+      local cipher=byte(c)
+      local plain=bxor(cipher,rshift(r,8))
+      r=((cipher+r)*c1+c2)%65536
+      return char(plain)
+    end
+    decrypt=function(binary,initial,seed)
+      r,c1,c2,n=initial,52845,22719,seed
+      binary=gsub(binary,".",step)
+      return sub(binary,n+1)
     end
-  end
-  local initialize=function(str,position,size)
-    n=0
-    m=size 
-    return position+1
   end
   local charstrings=P("/CharStrings")
+  local subroutines=P("/Subrs")
   local encoding=P("/Encoding")
   local dup=P("dup")
   local put=P("put")
@@ -24255,28 +24270,58 @@ do
   local cardinal=digits/tonumber
   local spaces=P(" ")^1
   local spacing=patterns.whitespace^0
+  local routines,vector,chars,n,m
+  local initialize=function(str,position,size)
+    n=0
+    m=size 
+    return position+1
+  end
+  local setroutine=function(str,position,index,size)
+    local forward=position+tonumber(size)
+    local stream=sub(str,position+1,forward)
+    routines[index]=decrypt(stream,4330,4)
+    return forward
+  end
+  local setvector=function(str,position,name,size)
+    local forward=position+tonumber(size)
+    if n>=m then
+      return #str
+    elseif forward<#str then
+      vector[n]=name
+      n=n+1 
+      return forward
+    else
+      return #str
+    end
+  end
+  local setshapes=function(str,position,name,size)
+    local forward=position+tonumber(size)
+    local stream=sub(str,position+1,forward)
+    if n>m then
+      return #str
+    elseif forward<#str then
+      vector[n]=name
+      n=n+1
+      chars [n]=decrypt(stream,4330,4)
+      return forward
+    else
+      return #str
+    end
+  end
+  local p_rd=spacing*(P("RD")+P("-|"))
+  local p_np=spacing*(P("NP")+P("|"))
+  local p_nd=spacing*(P("ND")+P("|"))
+  local p_filterroutines=
+    (1-subroutines)^0*subroutines*spaces*Cmt(cardinal,initialize)*(Cmt(cardinal*spaces*cardinal*p_rd,setroutine)*p_np+P(1))^1
+  local p_filtershapes=
+    (1-charstrings)^0*charstrings*spaces*Cmt(cardinal,initialize)*(Cmt(name*spaces*cardinal*p_rd,setshapes)*p_nd+P(1))^1
   local p_filternames=Ct (
-    (1-charstrings)^0*charstrings*spaces*Cmt(cardinal,initialize)*(Cmt(name*spaces*cardinal,progress)+P(1))^1
+    (1-charstrings)^0*charstrings*spaces*Cmt(cardinal,initialize)*(Cmt(name*spaces*cardinal,setvector)+P(1))^1
   )
   local p_filterencoding=(1-encoding)^0*encoding*spaces*digits*spaces*array*(1-dup)^0*Cf(
       Ct("")*Cg(spacing*dup*spaces*cardinal*spaces*name*spaces*put)^1
 ,rawset)
-  local decrypt
-  do
-    local r,c1,c2,n=0,0,0,0
-    local function step(c)
-      local cipher=byte(c)
-      local plain=bxor(cipher,rshift(r,8))
-      r=((cipher+r)*c1+c2)%65536
-      return char(plain)
-    end
-    decrypt=function(binary)
-      r,c1,c2,n=55665,52845,22719,4
-      binary=gsub(binary,".",step)
-      return sub(binary,n+1)
-    end
-  end
-  local function loadpfbvector(filename)
+  local function loadpfbvector(filename,shapestoo)
     local data=io.loaddata(resolvers.findfile(filename))
     if not data then
       report_pfb("no data in %a",filename)
@@ -24291,18 +24336,30 @@ do
       report_pfb("no binary data in %a",filename)
       return
     end
-    binary=decrypt(binary,4)
-    local vector=lpegmatch(p_filternames,binary)
-    for i=1,#vector do
-      vector[i-1]=vector[i]
-    end
-    vector[#vector]=nil
-    if not vector then
-      report_pfb("no vector in %a",filename)
-      return
-    end
+    binary=decrypt(binary,55665,4)
+    local names={}
     local encoding=lpegmatch(p_filterencoding,ascii)
-    return vector,encoding
+    local glyphs={}
+    routines,vector,chars={},{},{}
+    if shapestoo then
+      lpegmatch(p_filterroutines,binary)
+      lpegmatch(p_filtershapes,binary)
+      local data={
+        dictionaries={
+          {
+            charstrings=chars,
+            charset=vector,
+            subroutines=routines,
+          }
+        },
+      }
+      fonts.handlers.otf.readers.parsecharstrings(data,glyphs,true,true)
+    else
+      lpegmatch(p_filternames,binary)
+    end
+    names=vector
+    routines,vector,chars=nil,nil,nil
+    return names,encoding,glyphs
   end
   local pfb=handlers.pfb or {}
   handlers.pfb=pfb
@@ -24326,6 +24383,10 @@ do
       end
     end
   end
+  get_shapes=function(pfbname)
+    local vector,encoding,glyphs=loadpfbvector(pfbname,true)
+    return glyphs
+  end
 end
 local spacer=patterns.spacer
 local whitespace=patterns.whitespace
@@ -24474,6 +24535,22 @@ function readers.loadfont(afmname,pfbname)
     return data
   end
 end
+function readers.loadshapes(filename)
+  local fullname=resolvers.findfile(filename) or ""
+  if fullname=="" then
+    return {
+      filename="not found: "..filename,
+      glyphs={}
+    }
+  else
+    return {
+      filename=fullname,
+      format="opentype",
+      glyphs=get_shapes(fullname) or {},
+      units=1000,
+    }
+  end
+end
 function readers.getinfo(filename)
   local data=read(resolvers.findfile(filename),infoparser)
   if data then
-- 
cgit v1.2.3