texlive[66179] Master/texmf-dist/tex/generic/context: ConTeXt LMTX:

commits+siepo at tug.org commits+siepo at tug.org
Sun Feb 26 15:47:58 CET 2023


Revision: 66179
          http://tug.org/svn/texlive?view=revision&revision=66179
Author:   siepo
Date:     2023-02-26 15:47:57 +0100 (Sun, 26 Feb 2023)
Log Message:
-----------
ConTeXt LMTX: tex/generic/context

Modified Paths:
--------------
    trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-basics-chr.lua
    trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-basics-nod.lua
    trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-basics.tex
    trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-core.lua
    trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-enc.lua
    trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-gbn.lua
    trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-merged.lua
    trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-math.lua
    trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-mplib.lua
    trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-pdf.tex

Removed Paths:
-------------
    trunk/Master/texmf-dist/tex/generic/context/ppchtex/

Modified: trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-basics-chr.lua
===================================================================
--- trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-basics-chr.lua	2023-02-26 14:47:05 UTC (rev 66178)
+++ trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-basics-chr.lua	2023-02-26 14:47:57 UTC (rev 66179)
@@ -206,6 +206,7 @@
  [1562]=5,
  [1563]=6,
  [1564]=6,
+ [1565]=6,
  [1566]=6,
  [1567]=6,
  [1568]=2,
@@ -672,9 +673,9 @@
  [2131]=2,
  [2132]=3,
  [2133]=2,
- [2134]=4,
- [2135]=4,
- [2136]=4,
+ [2134]=3,
+ [2135]=3,
+ [2136]=3,
  [2137]=5,
  [2138]=5,
  [2139]=5,
@@ -689,6 +690,47 @@
  [2152]=2,
  [2153]=3,
  [2154]=3,
+ [2160]=3,
+ [2161]=3,
+ [2162]=3,
+ [2163]=3,
+ [2164]=3,
+ [2165]=3,
+ [2166]=3,
+ [2167]=3,
+ [2168]=3,
+ [2169]=3,
+ [2170]=3,
+ [2171]=3,
+ [2172]=3,
+ [2173]=3,
+ [2174]=3,
+ [2175]=3,
+ [2176]=3,
+ [2177]=3,
+ [2178]=3,
+ [2179]=2,
+ [2180]=2,
+ [2181]=2,
+ [2182]=2,
+ [2183]=4,
+ [2184]=4,
+ [2185]=2,
+ [2186]=2,
+ [2187]=2,
+ [2188]=2,
+ [2189]=2,
+ [2190]=3,
+ [2192]=4,
+ [2193]=4,
+ [2200]=5,
+ [2201]=5,
+ [2202]=5,
+ [2203]=5,
+ [2204]=5,
+ [2205]=5,
+ [2206]=5,
+ [2207]=5,
  [2208]=2,
  [2209]=2,
  [2210]=2,
@@ -710,6 +752,7 @@
  [2226]=3,
  [2227]=2,
  [2228]=2,
+ [2229]=2,
  [2230]=2,
  [2231]=2,
  [2232]=2,
@@ -718,6 +761,26 @@
  [2235]=2,
  [2236]=2,
  [2237]=2,
+ [2238]=2,
+ [2239]=2,
+ [2240]=2,
+ [2241]=2,
+ [2242]=2,
+ [2243]=2,
+ [2244]=2,
+ [2245]=2,
+ [2246]=2,
+ [2247]=2,
+ [2248]=2,
+ [2250]=5,
+ [2251]=5,
+ [2252]=5,
+ [2253]=5,
+ [2254]=5,
+ [2255]=5,
+ [2256]=5,
+ [2257]=5,
+ [2258]=5,
  [2259]=5,
  [2260]=5,
  [2261]=5,
@@ -837,6 +900,7 @@
  [2883]=5,
  [2884]=5,
  [2893]=5,
+ [2901]=5,
  [2902]=5,
  [2914]=5,
  [2915]=5,
@@ -845,6 +909,7 @@
  [3021]=5,
  [3072]=5,
  [3076]=5,
+ [3132]=5,
  [3134]=5,
  [3135]=5,
  [3136]=5,
@@ -878,6 +943,7 @@
  [3405]=5,
  [3426]=5,
  [3427]=5,
+ [3457]=5,
  [3530]=5,
  [3538]=5,
  [3539]=5,
@@ -1057,6 +1123,7 @@
  [6156]=5,
  [6157]=5,
  [6158]=4,
+ [6159]=5,
  [6176]=2,
  [6177]=2,
  [6178]=2,
@@ -1241,6 +1308,22 @@
  [6843]=5,
  [6844]=5,
  [6845]=5,
+ [6847]=5,
+ [6848]=5,
+ [6849]=5,
+ [6850]=5,
+ [6851]=5,
+ [6852]=5,
+ [6853]=5,
+ [6854]=5,
+ [6855]=5,
+ [6856]=5,
+ [6857]=5,
+ [6858]=5,
+ [6859]=5,
+ [6860]=5,
+ [6861]=5,
+ [6862]=5,
  [6912]=5,
  [6913]=5,
  [6914]=5,
@@ -1373,6 +1456,7 @@
  [7671]=5,
  [7672]=5,
  [7673]=5,
+ [7674]=5,
  [7675]=5,
  [7676]=5,
  [7677]=5,
@@ -1474,6 +1558,7 @@
  [43019]=5,
  [43045]=5,
  [43046]=5,
+ [43052]=5,
  [43072]=2,
  [43073]=2,
  [43074]=2,
@@ -1750,6 +1835,8 @@
  [68901]=5,
  [68902]=5,
  [68903]=5,
+ [69291]=5,
+ [69292]=5,
  [69424]=2,
  [69425]=2,
  [69426]=2,
@@ -1787,6 +1874,56 @@
  [69458]=2,
  [69459]=2,
  [69460]=3,
+ [69488]=2,
+ [69489]=2,
+ [69490]=2,
+ [69491]=2,
+ [69492]=3,
+ [69493]=3,
+ [69494]=2,
+ [69495]=2,
+ [69496]=2,
+ [69497]=2,
+ [69498]=2,
+ [69499]=2,
+ [69500]=2,
+ [69501]=2,
+ [69502]=2,
+ [69503]=2,
+ [69504]=2,
+ [69505]=2,
+ [69506]=5,
+ [69507]=5,
+ [69508]=5,
+ [69509]=5,
+ [69552]=2,
+ [69553]=4,
+ [69554]=2,
+ [69555]=2,
+ [69556]=3,
+ [69557]=3,
+ [69558]=3,
+ [69559]=4,
+ [69560]=2,
+ [69561]=3,
+ [69562]=3,
+ [69563]=2,
+ [69564]=2,
+ [69565]=3,
+ [69566]=2,
+ [69567]=2,
+ [69568]=4,
+ [69569]=2,
+ [69570]=3,
+ [69571]=3,
+ [69572]=2,
+ [69573]=4,
+ [69574]=4,
+ [69575]=4,
+ [69576]=4,
+ [69577]=3,
+ [69578]=2,
+ [69579]=1,
  [69633]=5,
  [69688]=5,
  [69689]=5,
@@ -1803,6 +1940,9 @@
  [69700]=5,
  [69701]=5,
  [69702]=5,
+ [69744]=5,
+ [69747]=5,
+ [69748]=5,
  [69759]=5,
  [69760]=5,
  [69761]=5,
@@ -1813,6 +1953,7 @@
  [69817]=5,
  [69818]=5,
  [69821]=4,
+ [69826]=5,
  [69837]=4,
  [69888]=5,
  [69889]=5,
@@ -1845,6 +1986,7 @@
  [70090]=5,
  [70091]=5,
  [70092]=5,
+ [70095]=5,
  [70191]=5,
  [70192]=5,
  [70193]=5,
@@ -1955,6 +2097,10 @@
  [71735]=5,
  [71737]=5,
  [71738]=5,
+ [71995]=5,
+ [71996]=5,
+ [71998]=5,
+ [72003]=5,
  [72148]=5,
  [72149]=5,
  [72150]=5,
@@ -2092,8 +2238,78 @@
  [94096]=5,
  [94097]=5,
  [94098]=5,
+ [94180]=5,
  [113821]=5,
  [113822]=5,
+ [118528]=5,
+ [118529]=5,
+ [118530]=5,
+ [118531]=5,
+ [118532]=5,
+ [118533]=5,
+ [118534]=5,
+ [118535]=5,
+ [118536]=5,
+ [118537]=5,
+ [118538]=5,
+ [118539]=5,
+ [118540]=5,
+ [118541]=5,
+ [118542]=5,
+ [118543]=5,
+ [118544]=5,
+ [118545]=5,
+ [118546]=5,
+ [118547]=5,
+ [118548]=5,
+ [118549]=5,
+ [118550]=5,
+ [118551]=5,
+ [118552]=5,
+ [118553]=5,
+ [118554]=5,
+ [118555]=5,
+ [118556]=5,
+ [118557]=5,
+ [118558]=5,
+ [118559]=5,
+ [118560]=5,
+ [118561]=5,
+ [118562]=5,
+ [118563]=5,
+ [118564]=5,
+ [118565]=5,
+ [118566]=5,
+ [118567]=5,
+ [118568]=5,
+ [118569]=5,
+ [118570]=5,
+ [118571]=5,
+ [118572]=5,
+ [118573]=5,
+ [118576]=5,
+ [118577]=5,
+ [118578]=5,
+ [118579]=5,
+ [118580]=5,
+ [118581]=5,
+ [118582]=5,
+ [118583]=5,
+ [118584]=5,
+ [118585]=5,
+ [118586]=5,
+ [118587]=5,
+ [118588]=5,
+ [118589]=5,
+ [118590]=5,
+ [118591]=5,
+ [118592]=5,
+ [118593]=5,
+ [118594]=5,
+ [118595]=5,
+ [118596]=5,
+ [118597]=5,
+ [118598]=5,
  [119143]=5,
  [119144]=5,
  [119145]=5,
@@ -2291,6 +2507,7 @@
  [123188]=5,
  [123189]=5,
  [123190]=5,
+ [123566]=5,
  [123628]=5,
  [123629]=5,
  [123630]=5,
@@ -2459,6 +2676,7 @@
   [2879]=true,
   [2902]=true,
   [3376]=true,
+  [5901]=true,
  },
  ["after_postscript"]={
   [2433]=true,
@@ -2634,6 +2852,7 @@
   [2915]=true,
   [3009]=true,
   [3010]=true,
+  [3132]=true,
   [3170]=true,
   [3171]=true,
   [3260]=true,
@@ -3052,6 +3271,7 @@
   [4225]=true,
   [4226]=true,
   [4238]=true,
+  [5901]=true,
   [43488]=true,
   [43489]=true,
   [43490]=true,
@@ -3376,6 +3596,7 @@
   [3090]=true,
   [3091]=true,
   [3092]=true,
+  [3165]=true,
   [3168]=true,
   [3169]=true,
   [3205]=true,
@@ -3392,6 +3613,7 @@
   [3218]=true,
   [3219]=true,
   [3220]=true,
+  [3293]=true,
   [3296]=true,
   [3297]=true,
   [3333]=true,
@@ -3432,6 +3654,7 @@
   [2620]=true,
   [2748]=true,
   [2876]=true,
+  [3132]=true,
   [3260]=true,
  },
  ["post_mark"]={
@@ -3535,6 +3758,7 @@
   [3120]=true,
   [3248]=true,
   [3376]=true,
+  [5901]=true,
  },
  ["stress_tone_mark"]={
   [2385]=true,
@@ -3614,6 +3838,4 @@
  },
 }
 
--- done, uncomment for testing:
-
--- return characters.indicgroups
+-- done

Modified: trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-basics-nod.lua
===================================================================
--- trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-basics-nod.lua	2023-02-26 14:47:05 UTC (rev 66178)
+++ trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-basics-nod.lua	2023-02-26 14:47:57 UTC (rev 66179)
@@ -71,8 +71,8 @@
 nodes.disccodes  = disccodes
 nodes.dirvalues  = { lefttoright = 0, righttoleft = 1 }
 
-nodes.handlers.protectglyphs   = node.protect_glyphs   -- beware: nodes!
-nodes.handlers.unprotectglyphs = node.unprotect_glyphs -- beware: nodes!
+nodes.handlers.protectglyphs   = node.protectglyphs   or node.protect_glyphs   -- beware: nodes!
+nodes.handlers.unprotectglyphs = node.unprotectglyphs or node.unprotect_glyphs -- beware: nodes!
 
 -- in generic code, at least for some time, we stay nodes, while in context
 -- we can go nuts (e.g. experimental); this split permits us us keep code
@@ -81,84 +81,85 @@
 -- much of this will go away .. it's part of the context interface and not
 -- officially in luatex-*.lua
 
-local direct             = node.direct
-local nuts               = { }
-nodes.nuts               = nuts
+local direct       = node.direct
+local nuts         = { }
+nodes.nuts         = nuts
 
-local tonode             = direct.tonode
-local tonut              = direct.todirect
+local tonode       = direct.tonode
+local tonut        = direct.todirect
 
-nodes.tonode             = tonode
-nodes.tonut              = tonut
+nodes.tonode       = tonode
+nodes.tonut        = tonut
 
-nuts.tonode              = tonode
-nuts.tonut               = tonut
+nuts.tonode        = tonode
+nuts.tonut         = tonut
 
-nuts.getattr             = direct.get_attribute
-nuts.getboth             = direct.getboth
-nuts.getchar             = direct.getchar
-nuts.getdirection        = direct.getdirection
-nuts.getdisc             = direct.getdisc
-nuts.getreplace          = direct.getreplace
-nuts.getfield            = direct.getfield
-nuts.getfont             = direct.getfont
-nuts.getid               = direct.getid
-nuts.getkern             = direct.getkern
-nuts.getlist             = direct.getlist
-nuts.getnext             = direct.getnext
-nuts.getoffsets          = direct.getoffsets
-nuts.getoptions          = direct.getoptions or function() return 0 end
-nuts.getprev             = direct.getprev
-nuts.getsubtype          = direct.getsubtype
-nuts.getwidth            = direct.getwidth
-nuts.setattr             = direct.setfield
-nuts.setboth             = direct.setboth
-nuts.setchar             = direct.setchar
-nuts.setcomponents       = direct.setcomponents
-nuts.setdirection        = direct.setdirection
-nuts.setdisc             = direct.setdisc
-nuts.setreplace          = direct.setreplace
-nuts.setfield            = setfield
-nuts.setkern             = direct.setkern
-nuts.setlink             = direct.setlink
-nuts.setlist             = direct.setlist
-nuts.setnext             = direct.setnext
-nuts.setoffsets          = direct.setoffsets
-nuts.setprev             = direct.setprev
-nuts.setsplit            = direct.setsplit
-nuts.setsubtype          = direct.setsubtype
-nuts.setwidth            = direct.setwidth
+nuts.getattr       = direct.get_attribute
+nuts.getboth       = direct.getboth
+nuts.getchar       = direct.getchar
+nuts.getdirection  = direct.getdirection
+nuts.getdisc       = direct.getdisc
+nuts.getreplace    = direct.getreplace
+nuts.getfield      = direct.getfield
+nuts.getfont       = direct.getfont
+nuts.getid         = direct.getid
+nuts.getkern       = direct.getkern
+nuts.getlist       = direct.getlist
+nuts.getnext       = direct.getnext
+nuts.getoffsets    = direct.getoffsets
+nuts.getoptions    = direct.getoptions or function() return 0 end
+nuts.getprev       = direct.getprev
+nuts.getsubtype    = direct.getsubtype
+nuts.getwidth      = direct.getwidth
+nuts.setattr       = direct.setfield
+nuts.setboth       = direct.setboth
+nuts.setchar       = direct.setchar
+nuts.setcomponents = direct.setcomponents
+nuts.setdirection  = direct.setdirection
+nuts.setdisc       = direct.setdisc
+nuts.setreplace    = direct.setreplace
+nuts.setfield      = direct.setfield
+nuts.setkern       = direct.setkern
+nuts.setlink       = direct.setlink
+nuts.setlist       = direct.setlist
+nuts.setnext       = direct.setnext
+nuts.setoffsets    = direct.setoffsets
+nuts.setprev       = direct.setprev
+nuts.setsplit      = direct.setsplit
+nuts.setsubtype    = direct.setsubtype
+nuts.setwidth      = direct.setwidth
 
-nuts.getglyphdata        = nuts.getattr
-nuts.setglyphdata        = nuts.setattr
+nuts.getglyphdata  = nuts.getattribute    or nuts.getattr
+nuts.setglyphdata  = nuts.setattribute    or nuts.setattr
 
-nuts.ischar              = direct.is_char
-nuts.isglyph             = direct.is_glyph
+nuts.ischar        = direct.ischar        or direct.is_char
+nuts.isglyph       = direct.isglyph       or direct.is_glyph
+nuts.copy          = direct.copy
+nuts.copynode      = direct.copy
+nuts.copylist      = direct.copylist      or direct.copy_list
+nuts.endofmath     = direct.endofmath     or direct.end_of_math
+nuts.flush         = direct.flush
+nuts.flushlist     = direct.flushlist     or direct.flush_list
+nuts.flushnode     = direct.flushnode     or direct.flush_node
+nuts.free          = direct.free
+nuts.insertafter   = direct.insertafter   or direct.insert_after
+nuts.insertbefore  = direct.insertbefore  or direct.insert_before
+nuts.isnode        = direct.isnode        or direct.is_node
+nuts.isdirect      = direct.isdirect      or direct.is_direct
+nuts.isnut         = direct.isdirect      or direct.is_direct
+nuts.kerning       = direct.kerning
+nuts.ligaturing    = direct.ligaturing
+nuts.new           = direct.new
+nuts.remove        = direct.remove
+nuts.tail          = direct.tail
+nuts.traverse      = direct.traverse
+nuts.traversechar  = direct.traversechar  or direct.traverse_char
+nuts.traverseglyph = direct.traverseglyph or direct.traverse_glyph
+nuts.traverseid    = direct.traverseid    or direct.traverse_id
 
-nuts.copy                = direct.copy
-nuts.copy_list           = direct.copy_list
-nuts.copy_node           = direct.copy
-nuts.end_of_math         = direct.end_of_math
-nuts.flush               = direct.flush
-nuts.flush_list          = direct.flush_list
-nuts.flush_node          = direct.flush_node
-nuts.free                = direct.free
-nuts.insert_after        = direct.insert_after
-nuts.insert_before       = direct.insert_before
-nuts.is_node             = direct.is_node
-nuts.kerning             = direct.kerning
-nuts.ligaturing          = direct.ligaturing
-nuts.new                 = direct.new
-nuts.remove              = direct.remove
-nuts.tail                = direct.tail
-nuts.traverse            = direct.traverse
-nuts.traverse_char       = direct.traverse_char
-nuts.traverse_glyph      = direct.traverse_glyph
-nuts.traverse_id         = direct.traverse_id
-
 -- properties as used in the (new) injector:
 
-local propertydata = direct.get_properties_table()
+local propertydata = (direct.getpropertiestable or direct.get_properties_table)()
 nodes.properties   = { data = propertydata }
 
 if direct.set_properties_mode then
@@ -199,10 +200,10 @@
 local getsubtype    = nuts.getsubtype
 local isglyph       = nuts.isglyph
 local find_tail     = nuts.tail
-local flush_list    = nuts.flush_list
-local flush_node    = nuts.flush_node
-local traverse_id   = nuts.traverse_id
-local copy_node     = nuts.copy_node
+local flushlist     = nuts.flushlist
+local flushnode     = nuts.flushnode
+local traverseid    = nuts.traverseid
+local copynode      = nuts.copynode
 
 local glyph_code    = nodes.nodecodes.glyph
 local ligature_code = nodes.glyphcodes.ligature
@@ -222,22 +223,22 @@
 
 do
 
-    local get_components = node.direct.getcomponents
-    local set_components = node.direct.setcomponents
+    local getcomponents = node.direct.getcomponents
+    local setcomponents = node.direct.setcomponents
 
-    local function copy_no_components(g,copyinjection)
-        local components = get_components(g)
+    local function copynocomponents(g,copyinjection)
+        local components = getcomponents(g)
         if components then
-            set_components(g)
-            local n = copy_node(g)
+            setcomponents(g)
+            local n = copynode(g)
             if copyinjection then
                 copyinjection(n,g)
             end
-            set_components(g,components)
+            setcomponents(g,components)
             -- maybe also upgrade the subtype but we don't use it anyway
             return n
         else
-            local n = copy_node(g)
+            local n = copynode(g)
             if copyinjection then
                 copyinjection(n,g)
             end
@@ -245,11 +246,11 @@
         end
     end
 
-    local function copy_only_glyphs(current)
+    local function copyonlyglyphs(current)
         local head     = nil
         local previous = nil
-        for n in traverse_id(glyph_code,current) do
-            n = copy_node(n)
+        for n in traverseid(glyph_code,current) do
+            n = copynode(n)
             if head then
                 setlink(previous,n)
             else
@@ -260,14 +261,14 @@
         return head
     end
 
-    local function count_components(start,marks)
+    local function countcomponents(start,marks)
         local char = isglyph(start)
         if char then
             if getsubtype(start) == ligature_code then
                 local n = 0
-                local components = get_components(start)
+                local components = getcomponents(start)
                 while components do
-                    n = n + count_components(components,marks)
+                    n = n + countcomponents(components,marks)
                     components = getnext(components)
                 end
                 return n
@@ -278,20 +279,22 @@
         return 0
     end
 
-    local function flush_components()
+    local function flushcomponents()
         -- this is a no-op in mkiv / generic
     end
 
-    nuts.set_components     = set_components
-    nuts.get_components     = get_components
-    nuts.copy_only_glyphs   = copy_only_glyphs
-    nuts.copy_no_components = copy_no_components
-    nuts.count_components   = count_components
-    nuts.flush_components   = flush_components
+    nuts.components = {
+        set              = setcomponents,
+        get              = getcomponents,
+        copyonlyglyphs   = copyonlyglyphs,
+        copynocomponents = copynocomponents,
+        count            = countcomponents,
+        flush            = flushcomponents,
+    }
 
 end
 
-nuts.uses_font = direct.uses_font
+nuts.usesfont = direct.usesfont or direct.uses_font
 
 do
 
@@ -301,12 +304,12 @@
     local dummy = tonut(node.new("glyph"))
 
     nuts.traversers = {
-        glyph    = nuts.traverse_id(nodecodes.glyph,dummy),
-        glue     = nuts.traverse_id(nodecodes.glue,dummy),
-        disc     = nuts.traverse_id(nodecodes.disc,dummy),
-        boundary = nuts.traverse_id(nodecodes.boundary,dummy),
+        glyph    = nuts.traverseid(nodecodes.glyph,dummy),
+        glue     = nuts.traverseid(nodecodes.glue,dummy),
+        disc     = nuts.traverseid(nodecodes.disc,dummy),
+        boundary = nuts.traverseid(nodecodes.boundary,dummy),
 
-        char     = nuts.traverse_char(dummy),
+        char     = nuts.traversechar(dummy),
 
         node     = nuts.traverse(dummy),
     }
@@ -333,7 +336,7 @@
 
     local getsubtype = nuts.getsubtype
 
-    function nuts.start_of_par(n)
+    function nuts.startofpar(n)
         local s = getsubtype(n)
         return s == 0 or s == 2 -- sorry, hardcoded, won't change anyway
     end

Modified: trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-basics.tex
===================================================================
--- trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-basics.tex	2023-02-26 14:47:05 UTC (rev 66178)
+++ trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-basics.tex	2023-02-26 14:47:57 UTC (rev 66179)
@@ -31,8 +31,8 @@
     local registered  = {}
 
     function gadgets.functions.reverve()
-        local numb = newtoken.scan_int()
-        local name = newtoken.scan_string()
+        local numb = token.scan_int()
+        local name = token.scan_string()
         local okay = string.gsub(name,"[\string\\ ]","")
         registered[okay] = numb
         texio.write_nl("reserving lua function '"..okay.."' with number "..numb)
@@ -62,6 +62,9 @@
 
 % an example of usage (if we ever support it it will go to the plain gadgets module):
 %
+% \newluafunction\UcharcatLuaOne
+% \newluafunction\UcharcatLuaTwo
+%
 % \directlua {
 %
 %     local cct = nil
@@ -68,9 +71,9 @@
 %     local chr = nil
 %
 %     gadgets.functions.register("UcharcatLuaOne",function()
-%         chr = newtoken.scan_int()
+%         chr = token.scan_int()
 %         cct = tex.getcatcode(chr)
-%         tex.setcatcode(chr,newtoken.scan_int())
+%         tex.setcatcode(chr,token.scan_int())
 %         tex.sprint(unicode.utf8.char(chr))
 %     end)
 %
@@ -89,5 +92,4 @@
 % A:\the\catcode65:\Ucharcat 65  5:A:\the\catcode65\par
 % A:\the\catcode65:\Ucharcat 65 11:A:\the\catcode65\par
 
-
 \endinput

Modified: trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-core.lua
===================================================================
--- trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-core.lua	2023-02-26 14:47:05 UTC (rev 66178)
+++ trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-core.lua	2023-02-26 14:47:57 UTC (rev 66179)
@@ -210,6 +210,13 @@
 
     ffi = nil
 
+    -- A patch by LS for LaTeX per April 2022 but I'm, not sure if that breaks ConTeXt MKIV. I have
+    -- no time nor motivation to test that right now so we'll see where it breaks. We have different
+    -- loaders anyway and we don't test plain.
+
+    package.loaded .ffi = nil   -- Isn't that still nil then?
+    package.preload.ffi = error -- Do errors always go there?
+
 end
 
 if md5 then

Modified: trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-enc.lua
===================================================================
--- trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-enc.lua	2023-02-26 14:47:05 UTC (rev 66178)
+++ trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-enc.lua	2023-02-26 14:47:57 UTC (rev 66179)
@@ -16,10 +16,18 @@
 encodings.agl   = { }
 encodings.known = { }
 
+encodings.glyphlistfilename = "font-age.lua"
+
 setmetatable(encodings.agl, { __index = function(t,k)
     if k == "unicodes" then
         logs.report("fonts","loading (extended) adobe glyph list")
-        local unicodes = dofile(resolvers.findfile("font-age.lua"))
+        local foundname = resolvers.findfile(encodings.glyphlistfilename) or ""
+        local unicodes = foundname ~= "" and dofile(foundname)
+        if type(unicodes) ~= "table" then
+            logs.report("fonts","missing or invalid (extended) adobe glyph list")
+            -- no message
+            unicodes = { }
+        end
         encodings.agl = { unicodes = unicodes }
         return unicodes
     else

Modified: trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-gbn.lua
===================================================================
--- trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-gbn.lua	2023-02-26 14:47:05 UTC (rev 66178)
+++ trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-gbn.lua	2023-02-26 14:47:57 UTC (rev 66179)
@@ -17,27 +17,27 @@
 local fonts = fonts
 local nodes = nodes
 
-local nuts        = nodes.nuts -- context abstraction of direct nodes
+local nuts       = nodes.nuts -- context abstraction of direct nodes
 
-local traverse_id = nuts.traverse_id
-local flush_node  = nuts.flush_node
+local traverseid = nuts.traverseid
+local flushnode  = nuts.flushnode
 
-local glyph_code  = nodes.nodecodes.glyph
-local disc_code   = nodes.nodecodes.disc
+local glyph_code = nodes.nodecodes.glyph
+local disc_code  = nodes.nodecodes.disc
 
-local tonode      = nuts.tonode
-local tonut       = nuts.tonut
+local tonode     = nuts.tonode
+local tonut      = nuts.tonut
 
-local getfont     = nuts.getfont
-local getchar     = nuts.getchar
-local getid       = nuts.getid
-local getboth     = nuts.getboth
-local getprev     = nuts.getprev
-local getnext     = nuts.getnext
-local getdisc     = nuts.getdisc
-local setchar     = nuts.setchar
-local setlink     = nuts.setlink
-local setprev     = nuts.setprev
+local getfont    = nuts.getfont
+local getchar    = nuts.getchar
+local getid      = nuts.getid
+local getboth    = nuts.getboth
+local getprev    = nuts.getprev
+local getnext    = nuts.getnext
+local getdisc    = nuts.getdisc
+local setchar    = nuts.setchar
+local setlink    = nuts.setlink
+local setprev    = nuts.setprev
 
 -- from now on we apply ligaturing and kerning here because it might interfere with complex
 -- opentype discretionary handling where the base ligature pass expect some weird extra
@@ -99,7 +99,7 @@
         local variants  = nil
         local redundant = nil
         local nofused   = 0
-        for n in traverse_id(glyph_code,head) do
+        for n in traverseid(glyph_code,head) do
             local font = getfont(n)
             if font ~= prevfont then
                 if basefont then
@@ -178,13 +178,13 @@
                         end
                     end
                 end
-                flush_node(r)
+                flushnode(r)
             end
         end
-        for d in traverse_id(disc_code,head) do
+        for d in traverseid(disc_code,head) do
             local _, _, r = getdisc(d)
             if r then
-                for n in traverse_id(glyph_code,r) do
+                for n in traverseid(glyph_code,r) do
                     local font = getfont(n)
                     if font ~= prevfont then
                         prevfont = font
@@ -254,7 +254,7 @@
     return head
 end
 
-local protectpass = node.direct.protect_glyphs
+local protectpass = node.direct.protectglyphs or node.direct.protect_glyphs
 local injectpass  = nodes.injections.handler
 
 -- This is the only official public interface and this one can be hooked into a callback (chain) and

Modified: trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-merged.lua
===================================================================
--- trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-merged.lua	2023-02-26 14:47:05 UTC (rev 66178)
+++ trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-fonts-merged.lua	2023-02-26 14:47:57 UTC (rev 66179)
@@ -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  : 2021-03-05 19:11
+-- merge date  : 2023-02-23 21:26
 
 do -- begin closure to overcome local limits and interference
 
@@ -927,7 +927,7 @@
  license="see context related readme files"
 }
 local string=string
-local sub,gmatch,format,char,byte,rep,lower=string.sub,string.gmatch,string.format,string.char,string.byte,string.rep,string.lower
+local sub,gmatch,format,char,byte,rep,lower,find=string.sub,string.gmatch,string.format,string.char,string.byte,string.rep,string.lower,string.find
 local lpegmatch,patterns=lpeg.match,lpeg.patterns
 local P,S,C,Ct,Cc,Cs=lpeg.P,lpeg.S,lpeg.C,lpeg.Ct,lpeg.Cc,lpeg.Cs
 local unquoted=patterns.squote*C(patterns.nosquote)*patterns.squote+patterns.dquote*C(patterns.nodquote)*patterns.dquote
@@ -937,10 +937,18 @@
 function string.quoted(str)
  return format("%q",str) 
 end
-function string.count(str,pattern) 
+function string.count(str,pattern)
  local n=0
- for _ in gmatch(str,pattern) do 
-  n=n+1
+ local i=1
+ local l=#pattern
+ while true do
+  i=find(str,pattern,i)
+  if i then
+   n=n+1
+   i=i+l
+  else
+   break
+  end
  end
  return n
 end
@@ -2255,9 +2263,12 @@
   flush()
  end
 end
-function io.savedata(filename,data,joiner)
- local f=open(filename,"wb")
+function io.savedata(filename,data,joiner,append)
+ local f=open(filename,append and "ab" or "wb")
  if f then
+  if append and joiner and f:seek("end")>0 then
+   f:write(joiner)
+  end
   if type(data)=="table" then
    f:write(concat(data,joiner or ""))
   elseif type(data)=="function" then
@@ -2791,7 +2802,7 @@
  if not two then
   return one=="" and one or lpegmatch(reslasher,one)
  end
- if one=="" then
+ if not one or one=="" then
   return lpegmatch(stripper,three and concat({ two,three,... },"/") or two)
  end
  if lpegmatch(isnetwork,one) then
@@ -3028,8 +3039,14 @@
  math.ceiling=math.ceil
 end
 if not math.round then
- local floor=math.floor
- function math.round(x) return floor(x+0.5) end
+ if xmath then
+  math.round=xmath.round
+ else
+  local floor=math.floor
+  function math.round(x)
+   return x<0 and -floor(-x+0.5) or floor(x+0.5)
+  end
+ end
 end
 if not math.div then
  local floor=math.floor
@@ -3099,7 +3116,7 @@
 end
 if not math.ult then
  local floor=math.floor
- function math.tointeger(m,n)
+ function math.ult(m,n)
   return floor(m)<floor(n) 
  end
 end
@@ -3118,12 +3135,13 @@
 utilities=utilities or {}
 utilities.strings=utilities.strings or {}
 local strings=utilities.strings
-local format,gsub,rep,sub,find=string.format,string.gsub,string.rep,string.sub,string.find
+local format,gsub,rep,sub,find,char=string.format,string.gsub,string.rep,string.sub,string.find,string.char
 local load,dump=load,string.dump
 local tonumber,type,tostring,next,setmetatable=tonumber,type,tostring,next,setmetatable
 local unpack,concat=table.unpack,table.concat
 local P,V,C,S,R,Ct,Cs,Cp,Carg,Cc=lpeg.P,lpeg.V,lpeg.C,lpeg.S,lpeg.R,lpeg.Ct,lpeg.Cs,lpeg.Cp,lpeg.Carg,lpeg.Cc
 local patterns,lpegmatch=lpeg.patterns,lpeg.match
+local tsplitat=lpeg.tsplitat
 local utfchar,utfbyte,utflen=utf.char,utf.byte,utf.len
 local loadstripped=function(str,shortcuts)
  if shortcuts then
@@ -3477,6 +3495,14 @@
 function string.escapedquotes(s)
  return lpegmatch(pattern,s)
 end
+local pattern=(1-P("\\"))^1;pattern=Cs (
+ pattern*((P("\\")/""*(digit^-3/function(s) return char(tonumber(s)) end))+pattern )^1
+)
+patterns.unescapedquotes=pattern
+function string.unescapedquotes(s)
+ return lpegmatch(pattern,s) or s
+end
+string.texnewlines=lpeg.replacer(patterns.newline,"\r",true)
 local preamble=""
 local environment={
  global=global or _G,
@@ -4012,7 +4038,6 @@
  return f_16_16(n/65536.0)
 end
 if not string.explode then
- local tsplitat=lpeg.tsplitat
  local p_utf=patterns.utf8character
  local p_check=C(p_utf)*(P("+")*Cc(true))^0
  local p_split=Ct(C(p_utf)^0)
@@ -4032,6 +4057,20 @@
   end
  end
 end
+do
+ local p_whitespace=patterns.whitespace^1
+ local cache=setmetatable({},{ __index=function(t,k)
+  local p=tsplitat(p_whitespace*P(k)*p_whitespace)
+  local v=function(s)
+   return lpegmatch(p,s)
+  end
+  t[k]=v
+  return v
+ end })
+ function string.wordsplitter(s)
+  return cache[s]
+ end
+end
 
 end -- closure
 
@@ -4202,20 +4241,20 @@
  end
 end
 function files.readfixed2(f)
- local a,b=byte(f:read(2),1,2)
- if a>=0x80 then
-  tonumber((a-0x100).."."..b)
- else
-  tonumber((a    ).."."..b)
+ local n1,n2=byte(f:read(2),1,2)
+ if n1>=0x80 then
+  n1=n1-0x100
  end
+ return n1+n2/0xFF
 end
 function files.readfixed4(f)
  local a,b,c,d=byte(f:read(4),1,4)
- if a>=0x80 then
-  tonumber((0x100*a+b-0x10000).."."..(0x100*c+d))
- else
-  tonumber((0x100*a+b    ).."."..(0x100*c+d))
+ local n1=0x100*a+b
+ local n2=0x100*c+d
+ if n1>=0x8000 then
+  n1=n1-0x10000
  end
+ return n1+n2/0xFFFF
 end
 if bit32 then
  local extract=bit32.extract
@@ -4821,7 +4860,7 @@
  end,
  __storage__=true
 }
-function containers.define(category,subcategory,version,enabled)
+function containers.define(category,subcategory,version,enabled,reload)
  if category and subcategory then
   local c=allocated[category]
   if not c then
@@ -4835,6 +4874,7 @@
     subcategory=subcategory,
     storage={},
     enabled=enabled,
+    reload=reload,
     version=version or math.pi,
     trace=false,
    }
@@ -4857,7 +4897,8 @@
 end
 function containers.read(container,name)
  local storage=container.storage
- local stored=storage[name]
+ local reload=container.reload
+ local stored=not reload and storage[name]
  if not stored and container.enabled and caches and containers.usecache then
   stored=loaddatafromcache(container.readables,name,container.writable)
   if stored and stored.cache_version==container.version then
@@ -4961,8 +5002,8 @@
 nodes.glyphcodes=glyphcodes
 nodes.disccodes=disccodes
 nodes.dirvalues={ lefttoright=0,righttoleft=1 }
-nodes.handlers.protectglyphs=node.protect_glyphs   
-nodes.handlers.unprotectglyphs=node.unprotect_glyphs
+nodes.handlers.protectglyphs=node.protectglyphs   or node.protect_glyphs   
+nodes.handlers.unprotectglyphs=node.unprotectglyphs or node.unprotect_glyphs
 local direct=node.direct
 local nuts={}
 nodes.nuts=nuts
@@ -4996,7 +5037,7 @@
 nuts.setdirection=direct.setdirection
 nuts.setdisc=direct.setdisc
 nuts.setreplace=direct.setreplace
-nuts.setfield=setfield
+nuts.setfield=direct.setfield
 nuts.setkern=direct.setkern
 nuts.setlink=direct.setlink
 nuts.setlist=direct.setlist
@@ -5006,21 +5047,23 @@
 nuts.setsplit=direct.setsplit
 nuts.setsubtype=direct.setsubtype
 nuts.setwidth=direct.setwidth
-nuts.getglyphdata=nuts.getattr
-nuts.setglyphdata=nuts.setattr
-nuts.ischar=direct.is_char
-nuts.isglyph=direct.is_glyph
+nuts.getglyphdata=nuts.getattribute or nuts.getattr
+nuts.setglyphdata=nuts.setattribute or nuts.setattr
+nuts.ischar=direct.ischar  or direct.is_char
+nuts.isglyph=direct.isglyph    or direct.is_glyph
 nuts.copy=direct.copy
-nuts.copy_list=direct.copy_list
-nuts.copy_node=direct.copy
-nuts.end_of_math=direct.end_of_math
+nuts.copynode=direct.copy
+nuts.copylist=direct.copylist   or direct.copy_list
+nuts.endofmath=direct.endofmath  or direct.end_of_math
 nuts.flush=direct.flush
-nuts.flush_list=direct.flush_list
-nuts.flush_node=direct.flush_node
+nuts.flushlist=direct.flushlist  or direct.flush_list
+nuts.flushnode=direct.flushnode  or direct.flush_node
 nuts.free=direct.free
-nuts.insert_after=direct.insert_after
-nuts.insert_before=direct.insert_before
-nuts.is_node=direct.is_node
+nuts.insertafter=direct.insertafter   or direct.insert_after
+nuts.insertbefore=direct.insertbefore  or direct.insert_before
+nuts.isnode=direct.isnode  or direct.is_node
+nuts.isdirect=direct.isdirect   or direct.is_direct
+nuts.isnut=direct.isdirect   or direct.is_direct
 nuts.kerning=direct.kerning
 nuts.ligaturing=direct.ligaturing
 nuts.new=direct.new
@@ -5027,10 +5070,10 @@
 nuts.remove=direct.remove
 nuts.tail=direct.tail
 nuts.traverse=direct.traverse
-nuts.traverse_char=direct.traverse_char
-nuts.traverse_glyph=direct.traverse_glyph
-nuts.traverse_id=direct.traverse_id
-local propertydata=direct.get_properties_table()
+nuts.traversechar=direct.traversechar  or direct.traverse_char
+nuts.traverseglyph=direct.traverseglyph or direct.traverse_glyph
+nuts.traverseid=direct.traverseid or direct.traverse_id
+local propertydata=(direct.getpropertiestable or direct.get_properties_table)()
 nodes.properties={ data=propertydata }
 if direct.set_properties_mode then
  direct.set_properties_mode(true,true)
@@ -5063,10 +5106,10 @@
 local getsubtype=nuts.getsubtype
 local isglyph=nuts.isglyph
 local find_tail=nuts.tail
-local flush_list=nuts.flush_list
-local flush_node=nuts.flush_node
-local traverse_id=nuts.traverse_id
-local copy_node=nuts.copy_node
+local flushlist=nuts.flushlist
+local flushnode=nuts.flushnode
+local traverseid=nuts.traverseid
+local copynode=nuts.copynode
 local glyph_code=nodes.nodecodes.glyph
 local ligature_code=nodes.glyphcodes.ligature
 do 
@@ -5079,20 +5122,20 @@
  end
 end
 do
- local get_components=node.direct.getcomponents
- local set_components=node.direct.setcomponents
- local function copy_no_components(g,copyinjection)
-  local components=get_components(g)
+ local getcomponents=node.direct.getcomponents
+ local setcomponents=node.direct.setcomponents
+ local function copynocomponents(g,copyinjection)
+  local components=getcomponents(g)
   if components then
-   set_components(g)
-   local n=copy_node(g)
+   setcomponents(g)
+   local n=copynode(g)
    if copyinjection then
     copyinjection(n,g)
    end
-   set_components(g,components)
+   setcomponents(g,components)
    return n
   else
-   local n=copy_node(g)
+   local n=copynode(g)
    if copyinjection then
     copyinjection(n,g)
    end
@@ -5099,11 +5142,11 @@
    return n
   end
  end
- local function copy_only_glyphs(current)
+ local function copyonlyglyphs(current)
   local head=nil
   local previous=nil
-  for n in traverse_id(glyph_code,current) do
-   n=copy_node(n)
+  for n in traverseid(glyph_code,current) do
+   n=copynode(n)
    if head then
     setlink(previous,n)
    else
@@ -5113,14 +5156,14 @@
   end
   return head
  end
- local function count_components(start,marks)
+ local function countcomponents(start,marks)
   local char=isglyph(start)
   if char then
    if getsubtype(start)==ligature_code then
     local n=0
-    local components=get_components(start)
+    local components=getcomponents(start)
     while components do
-     n=n+count_components(components,marks)
+     n=n+countcomponents(components,marks)
      components=getnext(components)
     end
     return n
@@ -5130,24 +5173,26 @@
   end
   return 0
  end
- local function flush_components()
+ local function flushcomponents()
  end
- nuts.set_components=set_components
- nuts.get_components=get_components
- nuts.copy_only_glyphs=copy_only_glyphs
- nuts.copy_no_components=copy_no_components
- nuts.count_components=count_components
- nuts.flush_components=flush_components
+ nuts.components={
+  set=setcomponents,
+  get=getcomponents,
+  copyonlyglyphs=copyonlyglyphs,
+  copynocomponents=copynocomponents,
+  count=countcomponents,
+  flush=flushcomponents,
+ }
 end
-nuts.uses_font=direct.uses_font
+nuts.usesfont=direct.usesfont or direct.uses_font
 do
  local dummy=tonut(node.new("glyph"))
  nuts.traversers={
-  glyph=nuts.traverse_id(nodecodes.glyph,dummy),
-  glue=nuts.traverse_id(nodecodes.glue,dummy),
-  disc=nuts.traverse_id(nodecodes.disc,dummy),
-  boundary=nuts.traverse_id(nodecodes.boundary,dummy),
-  char=nuts.traverse_char(dummy),
+  glyph=nuts.traverseid(nodecodes.glyph,dummy),
+  glue=nuts.traverseid(nodecodes.glue,dummy),
+  disc=nuts.traverseid(nodecodes.disc,dummy),
+  boundary=nuts.traverseid(nodecodes.boundary,dummy),
+  char=nuts.traversechar(dummy),
   node=nuts.traverse(dummy),
  }
 end
@@ -5164,7 +5209,7 @@
 end
 do
  local getsubtype=nuts.getsubtype
- function nuts.start_of_par(n)
+ function nuts.startofpar(n)
   local s=getsubtype(n)
   return s==0 or s==2 
  end
@@ -5375,6 +5420,7 @@
  [1562]=5,
  [1563]=6,
  [1564]=6,
+ [1565]=6,
  [1566]=6,
  [1567]=6,
  [1568]=2,
@@ -5841,9 +5887,9 @@
  [2131]=2,
  [2132]=3,
  [2133]=2,
- [2134]=4,
- [2135]=4,
- [2136]=4,
+ [2134]=3,
+ [2135]=3,
+ [2136]=3,
  [2137]=5,
  [2138]=5,
  [2139]=5,
@@ -5858,6 +5904,47 @@
  [2152]=2,
  [2153]=3,
  [2154]=3,
+ [2160]=3,
+ [2161]=3,
+ [2162]=3,
+ [2163]=3,
+ [2164]=3,
+ [2165]=3,
+ [2166]=3,
+ [2167]=3,
+ [2168]=3,
+ [2169]=3,
+ [2170]=3,
+ [2171]=3,
+ [2172]=3,
+ [2173]=3,
+ [2174]=3,
+ [2175]=3,
+ [2176]=3,
+ [2177]=3,
+ [2178]=3,
+ [2179]=2,
+ [2180]=2,
+ [2181]=2,
+ [2182]=2,
+ [2183]=4,
+ [2184]=4,
+ [2185]=2,
+ [2186]=2,
+ [2187]=2,
+ [2188]=2,
+ [2189]=2,
+ [2190]=3,
+ [2192]=4,
+ [2193]=4,
+ [2200]=5,
+ [2201]=5,
+ [2202]=5,
+ [2203]=5,
+ [2204]=5,
+ [2205]=5,
+ [2206]=5,
+ [2207]=5,
  [2208]=2,
  [2209]=2,
  [2210]=2,
@@ -5879,6 +5966,7 @@
  [2226]=3,
  [2227]=2,
  [2228]=2,
+ [2229]=2,
  [2230]=2,
  [2231]=2,
  [2232]=2,
@@ -5887,6 +5975,26 @@
  [2235]=2,
  [2236]=2,
  [2237]=2,
+ [2238]=2,
+ [2239]=2,
+ [2240]=2,
+ [2241]=2,
+ [2242]=2,
+ [2243]=2,
+ [2244]=2,
+ [2245]=2,
+ [2246]=2,
+ [2247]=2,
+ [2248]=2,
+ [2250]=5,
+ [2251]=5,
+ [2252]=5,
+ [2253]=5,
+ [2254]=5,
+ [2255]=5,
+ [2256]=5,
+ [2257]=5,
+ [2258]=5,
  [2259]=5,
  [2260]=5,
  [2261]=5,
@@ -6006,6 +6114,7 @@
  [2883]=5,
  [2884]=5,
  [2893]=5,
+ [2901]=5,
  [2902]=5,
  [2914]=5,
  [2915]=5,
@@ -6014,6 +6123,7 @@
  [3021]=5,
  [3072]=5,
  [3076]=5,
+ [3132]=5,
  [3134]=5,
  [3135]=5,
  [3136]=5,
@@ -6047,6 +6157,7 @@
  [3405]=5,
  [3426]=5,
  [3427]=5,
+ [3457]=5,
  [3530]=5,
  [3538]=5,
  [3539]=5,
@@ -6226,6 +6337,7 @@
  [6156]=5,
  [6157]=5,
  [6158]=4,
+ [6159]=5,
  [6176]=2,
  [6177]=2,
  [6178]=2,
@@ -6410,6 +6522,22 @@
  [6843]=5,
  [6844]=5,
  [6845]=5,
+ [6847]=5,
+ [6848]=5,
+ [6849]=5,
+ [6850]=5,
+ [6851]=5,
+ [6852]=5,
+ [6853]=5,
+ [6854]=5,
+ [6855]=5,
+ [6856]=5,
+ [6857]=5,
+ [6858]=5,
+ [6859]=5,
+ [6860]=5,
+ [6861]=5,
+ [6862]=5,
  [6912]=5,
  [6913]=5,
  [6914]=5,
@@ -6542,6 +6670,7 @@
  [7671]=5,
  [7672]=5,
  [7673]=5,
+ [7674]=5,
  [7675]=5,
  [7676]=5,
  [7677]=5,
@@ -6643,6 +6772,7 @@
  [43019]=5,
  [43045]=5,
  [43046]=5,
+ [43052]=5,
  [43072]=2,
  [43073]=2,
  [43074]=2,
@@ -6919,6 +7049,8 @@
  [68901]=5,
  [68902]=5,
  [68903]=5,
+ [69291]=5,
+ [69292]=5,
  [69424]=2,
  [69425]=2,
  [69426]=2,
@@ -6956,6 +7088,56 @@
  [69458]=2,
  [69459]=2,
  [69460]=3,
+ [69488]=2,
+ [69489]=2,
+ [69490]=2,
+ [69491]=2,
+ [69492]=3,
+ [69493]=3,
+ [69494]=2,
+ [69495]=2,
+ [69496]=2,
+ [69497]=2,
+ [69498]=2,
+ [69499]=2,
+ [69500]=2,
+ [69501]=2,
+ [69502]=2,
+ [69503]=2,
+ [69504]=2,
+ [69505]=2,
+ [69506]=5,
+ [69507]=5,
+ [69508]=5,
+ [69509]=5,
+ [69552]=2,
+ [69553]=4,
+ [69554]=2,
+ [69555]=2,
+ [69556]=3,
+ [69557]=3,
+ [69558]=3,
+ [69559]=4,
+ [69560]=2,
+ [69561]=3,
+ [69562]=3,
+ [69563]=2,
+ [69564]=2,
+ [69565]=3,
+ [69566]=2,
+ [69567]=2,
+ [69568]=4,
+ [69569]=2,
+ [69570]=3,
+ [69571]=3,
+ [69572]=2,
+ [69573]=4,
+ [69574]=4,
+ [69575]=4,
+ [69576]=4,
+ [69577]=3,
+ [69578]=2,
+ [69579]=1,
  [69633]=5,
  [69688]=5,
  [69689]=5,
@@ -6972,6 +7154,9 @@
  [69700]=5,
  [69701]=5,
  [69702]=5,
+ [69744]=5,
+ [69747]=5,
+ [69748]=5,
  [69759]=5,
  [69760]=5,
  [69761]=5,
@@ -6982,6 +7167,7 @@
  [69817]=5,
  [69818]=5,
  [69821]=4,
+ [69826]=5,
  [69837]=4,
  [69888]=5,
  [69889]=5,
@@ -7014,6 +7200,7 @@
  [70090]=5,
  [70091]=5,
  [70092]=5,
+ [70095]=5,
  [70191]=5,
  [70192]=5,
  [70193]=5,
@@ -7124,6 +7311,10 @@
  [71735]=5,
  [71737]=5,
  [71738]=5,
+ [71995]=5,
+ [71996]=5,
+ [71998]=5,
+ [72003]=5,
  [72148]=5,
  [72149]=5,
  [72150]=5,
@@ -7261,8 +7452,78 @@
  [94096]=5,
  [94097]=5,
  [94098]=5,
+ [94180]=5,
  [113821]=5,
  [113822]=5,
+ [118528]=5,
+ [118529]=5,
+ [118530]=5,
+ [118531]=5,
+ [118532]=5,
+ [118533]=5,
+ [118534]=5,
+ [118535]=5,
+ [118536]=5,
+ [118537]=5,
+ [118538]=5,
+ [118539]=5,
+ [118540]=5,
+ [118541]=5,
+ [118542]=5,
+ [118543]=5,
+ [118544]=5,
+ [118545]=5,
+ [118546]=5,
+ [118547]=5,
+ [118548]=5,
+ [118549]=5,
+ [118550]=5,
+ [118551]=5,
+ [118552]=5,
+ [118553]=5,
+ [118554]=5,
+ [118555]=5,
+ [118556]=5,
+ [118557]=5,
+ [118558]=5,
+ [118559]=5,
+ [118560]=5,
+ [118561]=5,
+ [118562]=5,
+ [118563]=5,
+ [118564]=5,
+ [118565]=5,
+ [118566]=5,
+ [118567]=5,
+ [118568]=5,
+ [118569]=5,
+ [118570]=5,
+ [118571]=5,
+ [118572]=5,
+ [118573]=5,
+ [118576]=5,
+ [118577]=5,
+ [118578]=5,
+ [118579]=5,
+ [118580]=5,
+ [118581]=5,
+ [118582]=5,
+ [118583]=5,
+ [118584]=5,
+ [118585]=5,
+ [118586]=5,
+ [118587]=5,
+ [118588]=5,
+ [118589]=5,
+ [118590]=5,
+ [118591]=5,
+ [118592]=5,
+ [118593]=5,
+ [118594]=5,
+ [118595]=5,
+ [118596]=5,
+ [118597]=5,
+ [118598]=5,
  [119143]=5,
  [119144]=5,
  [119145]=5,
@@ -7460,6 +7721,7 @@
  [123188]=5,
  [123189]=5,
  [123190]=5,
+ [123566]=5,
  [123628]=5,
  [123629]=5,
  [123630]=5,
@@ -7627,6 +7889,7 @@
   [2879]=true,
   [2902]=true,
   [3376]=true,
+  [5901]=true,
  },
  ["after_postscript"]={
   [2433]=true,
@@ -7802,6 +8065,7 @@
   [2915]=true,
   [3009]=true,
   [3010]=true,
+  [3132]=true,
   [3170]=true,
   [3171]=true,
   [3260]=true,
@@ -8220,6 +8484,7 @@
   [4225]=true,
   [4226]=true,
   [4238]=true,
+  [5901]=true,
   [43488]=true,
   [43489]=true,
   [43490]=true,
@@ -8544,6 +8809,7 @@
   [3090]=true,
   [3091]=true,
   [3092]=true,
+  [3165]=true,
   [3168]=true,
   [3169]=true,
   [3205]=true,
@@ -8560,6 +8826,7 @@
   [3218]=true,
   [3219]=true,
   [3220]=true,
+  [3293]=true,
   [3296]=true,
   [3297]=true,
   [3333]=true,
@@ -8600,6 +8867,7 @@
   [2620]=true,
   [2748]=true,
   [2876]=true,
+  [3132]=true,
   [3260]=true,
  },
  ["post_mark"]={
@@ -8703,6 +8971,7 @@
   [3120]=true,
   [3248]=true,
   [3376]=true,
+  [5901]=true,
  },
  ["stress_tone_mark"]={
   [2385]=true,
@@ -9147,19 +9416,9 @@
  target.unscaled=tfmdata
  local mathsize=tonumber(specification.mathsize) or 0
  local textsize=tonumber(specification.textsize) or scaledpoints
- local forcedsize=tonumber(parameters.mathsize   ) or 0 
  local extrafactor=tonumber(specification.factor  ) or 1
- if (mathsize==2 or forcedsize==2) and parameters.scriptpercentage then
-  scaledpoints=parameters.scriptpercentage*textsize/100
- elseif (mathsize==3 or forcedsize==3) and parameters.scriptscriptpercentage then
-  scaledpoints=parameters.scriptscriptpercentage*textsize/100
- elseif forcedsize>1000 then 
-  scaledpoints=forcedsize
- else
- end
  targetparameters.mathsize=mathsize 
- targetparameters.textsize=textsize 
- targetparameters.forcedsize=forcedsize  
+ targetparameters.textsize=textsize
  targetparameters.extrafactor=extrafactor
  local addtounicode=constructors.addtounicode
  local tounicode=fonts.mappings.tounicode
@@ -10248,10 +10507,16 @@
 fonts.encodings=encodings
 encodings.agl={}
 encodings.known={}
+encodings.glyphlistfilename="font-age.lua"
 setmetatable(encodings.agl,{ __index=function(t,k)
  if k=="unicodes" then
   logs.report("fonts","loading (extended) adobe glyph list")
-  local unicodes=dofile(resolvers.findfile("font-age.lua"))
+  local foundname=resolvers.findfile(encodings.glyphlistfilename) or ""
+  local unicodes=foundname~="" and dofile(foundname)
+  if type(unicodes)~="table" then
+   logs.report("fonts","missing or invalid (extended) adobe glyph list")
+   unicodes={}
+  end
   encodings.agl={ unicodes=unicodes }
   return unicodes
  else
@@ -10643,9 +10908,9 @@
  local usedmap=cidinfo and fonts.cid.getmap(cidinfo)
  local uparser=makenameparser() 
  if usedmap then
-    oparser=usedmap and makenameparser(cidinfo.ordering)
-    cidnames=usedmap.names
-    cidcodes=usedmap.unicodes
+  oparser=usedmap and makenameparser(cidinfo.ordering)
+  cidnames=usedmap.names
+  cidcodes=usedmap.unicodes
  end
  local ns=0
  local nl=0
@@ -11051,6 +11316,7 @@
  copyright="PRAGMA ADE / ConTeXt Development Team",
  license="see context related readme files"
 }
+local number=number
 local next,type,tonumber,rawget=next,type,tonumber,rawget
 local byte,lower,char,gsub=string.byte,string.lower,string.char,string.gsub
 local fullstrip=string.fullstrip
@@ -11283,15 +11549,15 @@
  [900]="black",
 }
 local widths={
- [1]="ultracondensed",
- [2]="extracondensed",
- [3]="condensed",
- [4]="semicondensed",
- [5]="normal",
- [6]="semiexpanded",
- [7]="expanded",
- [8]="extraexpanded",
- [9]="ultraexpanded",
+ "ultracondensed",
+ "extracondensed",
+ "condensed",
+ "semicondensed",
+ "normal",
+ "semiexpanded",
+ "expanded",
+ "extraexpanded",
+ "ultraexpanded",
 }
 setmetatableindex(weights,function(t,k)
  local r=floor((k+50)/100)*100
@@ -11301,30 +11567,28 @@
 setmetatableindex(widths,function(t,k)
  return "normal"
 end)
-local panoseweights={
- [ 0]="normal",
- [ 1]="normal",
- [ 2]="verylight",
- [ 3]="light",
- [ 4]="thin",
- [ 5]="book",
- [ 6]="medium",
- [ 7]="demi",
- [ 8]="bold",
- [ 9]="heavy",
- [10]="black",
+local panoseweights={ [0]="normal",
+ "normal",
+ "verylight",
+ "light",
+ "thin",
+ "book",
+ "medium",
+ "demi",
+ "bold",
+ "heavy",
+ "black",
 }
-local panosewidths={
- [ 0]="normal",
- [ 1]="normal",
- [ 2]="normal",
- [ 3]="normal",
- [ 4]="normal",
- [ 5]="expanded",
- [ 6]="condensed",
- [ 7]="veryexpanded",
- [ 8]="verycondensed",
- [ 9]="monospaced",
+local panosewidths={ [0]="normal",
+ "normal",
+ "normal",
+ "normal",
+ "normal",
+ "expanded",
+ "condensed",
+ "veryexpanded",
+ "verycondensed",
+ "monospaced",
 }
 local helpers={}
 readers.helpers=helpers
@@ -11758,7 +12022,7 @@
   local version=readulong(f)
   fontdata.postscript={
    version=version,
-   italicangle=round(1000*readfixed(f))/1000,
+   italicangle=readfixed(f),
    underlineposition=readfword(f),
    underlinethickness=readfword(f),
    monospaced=readulong(f),
@@ -11798,8 +12062,6 @@
      if length>0 then
       glyphs[mapping].name=readstring(f,length)
      else
-      report("quit post name fetching at %a of %a: %s",i,maxnames,"overflow")
-      break
      end
     end
    end
@@ -12138,7 +12400,7 @@
 function readers.cmap(f,fontdata,specification)
  local tableoffset=gotodatatable(f,fontdata,"cmap",specification.glyphs)
  if tableoffset then
-  local version=readushort(f)
+  local version=readushort(f) 
   local noftables=readushort(f)
   local records={}
   local unicodecid=false
@@ -12377,6 +12639,7 @@
    pfmwidth=metrics.widthclass or 5,
    panosewidth=metrics.panosewidth,
    panoseweight=metrics.panoseweight,
+   fstype=metrics.fstype or 0,
    italicangle=postscript.italicangle or 0,
    units=fontheader.units or 0,
    designsize=fontdata.designsize,
@@ -12798,6 +13061,10 @@
     cidinfo=fontdata.cidinfo,
     mathconstants=fontdata.mathconstants,
     colorpalettes=fontdata.colorpalettes,
+    colorpaintdata=fontdata.colorpaintdata,
+    colorpaintlist=fontdata.colorpaintlist,
+    colorlinesdata=fontdata.colorlinesdata,
+    coloraffinedata=fontdata.coloraffinedata,
     svgshapes=fontdata.svgshapes,
     pngshapes=fontdata.pngshapes,
     variabledata=fontdata.variabledata,
@@ -12852,13 +13119,13 @@
   }
  end
 end
-function readers.rehash(fontdata,hashmethod)
+function readers.rehash() 
  report("the %a helper is not yet implemented","rehash")
 end
-function readers.checkhash(fontdata)
+function readers.checkhash() 
  report("the %a helper is not yet implemented","checkhash")
 end
-function readers.pack(fontdata,hashmethod)
+function readers.pack() 
  report("the %a helper is not yet implemented","pack")
 end
 function readers.unpack(fontdata)
@@ -12870,6 +13137,9 @@
 function readers.compact(fontdata)
  report("the %a helper is not yet implemented","compact")
 end
+function readers.condense(fontdata)
+ report("the %a helper is not yet implemented","condense")
+end
 local extenders={}
 function readers.registerextender(extender)
  extenders[#extenders+1]=extender
@@ -12899,7 +13169,7 @@
 }
 local next,type,tonumber,rawget=next,type,tonumber,rawget
 local byte,char,gmatch,sub=string.byte,string.char,string.gmatch,string.sub
-local concat,remove,unpack=table.concat,table.remove,table.unpack
+local concat,insert,remove,unpack=table.concat,table.insert,table.remove,table.unpack
 local floor,abs,round,ceil,min,max=math.floor,math.abs,math.round,math.ceil,math.min,math.max
 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
@@ -13153,10 +13423,10 @@
    result.familyotherblues={ unpack(stack,1,top) }
    top=0
   end+P("\10")/function()
-   result.strhw=stack[top]
+   result.stdhw=stack[top]
    top=0
   end+P("\11")/function()
-   result.strvw=stack[top]
+   result.stdvw=stack[top]
    top=0
   end+P("\13")/function()
    result.uniqueid=stack[top]
@@ -13228,7 +13498,7 @@
    result.bluescale=stack[top]
    top=0
   end+P("\10")/function()
-   result.bluesnap=stack[top]
+   result.blueshift=stack[top]
    top=0
   end+P("\11")/function()
    result.bluefuzz=stack[top]
@@ -13282,7 +13552,7 @@
    top=0
   end
  )
- local remap={
+ local remap_1={
   ["\x00"]="00",["\x01"]="01",["\x02"]="02",["\x03"]="03",["\x04"]="04",["\x05"]="05",["\x06"]="06",["\x07"]="07",["\x08"]="08",["\x09"]="09",["\x0A"]="0.",["\x0B"]="0E",["\x0C"]="0E-",["\x0D"]="0",["\x0E"]="0-",["\x0F"]="0",
   ["\x10"]="10",["\x11"]="11",["\x12"]="12",["\x13"]="13",["\x14"]="14",["\x15"]="15",["\x16"]="16",["\x17"]="17",["\x18"]="18",["\x19"]="19",["\x1A"]="1.",["\x1B"]="1E",["\x1C"]="1E-",["\x1D"]="1",["\x1E"]="1-",["\x1F"]="1",
   ["\x20"]="20",["\x21"]="21",["\x22"]="22",["\x23"]="23",["\x24"]="24",["\x25"]="25",["\x26"]="26",["\x27"]="27",["\x28"]="28",["\x29"]="29",["\x2A"]="2.",["\x2B"]="2E",["\x2C"]="2E-",["\x2D"]="2",["\x2E"]="2-",["\x2F"]="2",
@@ -13298,8 +13568,13 @@
   ["\xC0"]="E-0",["\xC1"]="E-1",["\xC2"]="E-2",["\xC3"]="E-3",["\xC4"]="E-4",["\xC5"]="E-5",["\xC6"]="E-6",["\xC7"]="E-7",["\xC8"]="E-8",["\xC9"]="E-9",["\xCA"]="E-.",["\xCB"]="E-E",["\xCC"]="E-E-",["\xCD"]="E-",["\xCE"]="E--",["\xCF"]="E-",
   ["\xD0"]="-0",["\xD1"]="-1",["\xD2"]="-2",["\xD3"]="-3",["\xD4"]="-4",["\xD5"]="-5",["\xD6"]="-6",["\xD7"]="-7",["\xD8"]="-8",["\xD9"]="-9",["\xDA"]="-.",["\xDB"]="-E",["\xDC"]="-E-",["\xDD"]="-",["\xDE"]="--",["\xDF"]="-",
  }
- local p_last=S("\x0F\x1F\x2F\x3F\x4F\x5F\x6F\x7F\x8F\x9F\xAF\xBF")+R("\xF0\xFF")
- local p_nibbles=P("\30")*Cs(((1-p_last)/remap)^0*(P(1)/remap))/function(n)
+ local remap_2={
+  ["\x0F"]="0",["\x1F"]="1",["\x2F"]="2",["\x3F"]="3",["\x4F"]="4",
+  ["\x5F"]="5",["\x6F"]="6",["\x7F"]="7",["\x8F"]="8",["\x9F"]="9",
+ }
+ local p_last_1=S("\x0F\x1F\x2F\x3F\x4F\x5F\x6F\x7F\x8F\x9F\xAF\xBF")
+ local p_last_2=R("\xF0\xFF")
+ local p_nibbles=P("\30")*Cs(((1-(p_last_1+p_last_2))/remap_1)^0*(p_last_1/remap_2+p_last_2/""))/function(n)
   top=top+1
   stack[top]=tonumber(n) or 0
  end
@@ -13337,7 +13612,8 @@
   top=0
  end
  local p_dictionary=(
-  p_byte+p_positive+p_negative+p_short+p_long+p_nibbles+p_single+p_double+p_unsupported
+  p_byte+p_positive+p_negative+p_short+p_long+p_nibbles+p_single+p_double
++p_unsupported
  )^1
  parsedictionaries=function(data,dictionaries,version)
   stack={}
@@ -13409,6 +13685,7 @@
  local y=0
  local width=false
  local lsb=0
+ local result={}
  local r=0
  local stems=0
  local globalbias=0
@@ -13435,8 +13712,13 @@
  local justpass=false
  local seacs={}
  local procidx=nil
- local function showstate(where)
-  report("%w%-10s : [%s] n=%i",depth*2,where,concat(stack," ",1,top),top)
+ local function showstate(where,i,n)
+  if i then
+   local j=i+n-1
+   report("%w%-10s : [%s] step",depth*2+2,where,concat(stack," ",i,j<=top and j or top))
+  else
+   report("%w%-10s : [%s] n=%i",depth*2,where,concat(stack," ",1,top),top)
+  end
  end
  local function showvalue(where,value,showstack)
   if showstack then
@@ -13552,9 +13834,9 @@
    ymin=y
   end
  end
- local function xycurveto(x1,y1,x2,y2,x3,y3) 
+ local function xycurveto(x1,y1,x2,y2,x3,y3,i,n) 
   if trace_charstrings then
-   showstate("curveto")
+   showstate("curveto",i,n)
   end
   if keepcurve then
    r=r+1
@@ -13691,6 +13973,15 @@
   if trace_charstrings then
    showstate("rrcurveto")
   end
+if top==6 then
+ local ax=x+stack[1] 
+ local ay=y+stack[2] 
+ local bx=ax+stack[3] 
+ local by=ay+stack[4] 
+ x=bx+stack[5]  
+ y=by+stack[6]  
+ xycurveto(ax,ay,bx,by,x,y,1,6)
+else
   for i=1,top,6 do
    local ax=x+stack[i]   
    local ay=y+stack[i+1] 
@@ -13698,8 +13989,9 @@
    local by=ay+stack[i+3] 
    x=bx+stack[i+4]  
    y=by+stack[i+5]  
-   xycurveto(ax,ay,bx,by,x,y)
+   xycurveto(ax,ay,bx,by,x,y,i,6)
   end
+end
   top=0
  end
  local function hhcurveto()
@@ -13711,6 +14003,15 @@
    y=y+stack[1]     
    s=2
   end
+if top==4 then
+   local ax=x+stack[1]  
+   local ay=y
+   local bx=ax+stack[2] 
+   local by=ay+stack[3] 
+   x=bx+stack[4]  
+   y=by
+   xycurveto(ax,ay,bx,by,x,y,1,4)
+else
   for i=s,top,4 do
    local ax=x+stack[i] 
    local ay=y
@@ -13718,8 +14019,9 @@
    local by=ay+stack[i+2] 
    x=bx+stack[i+3]  
    y=by
-   xycurveto(ax,ay,bx,by,x,y)
+   xycurveto(ax,ay,bx,by,x,y,i,4)
   end
+end
   top=0
  end
  local function vvcurveto()
@@ -13732,6 +14034,16 @@
    d=stack[1]      
    s=2
   end
+if top==4 then
+ local ax=x+d
+ local ay=y+stack[1]  
+ local bx=ax+stack[2] 
+ local by=ay+stack[3] 
+ x=bx
+ y=by+stack[4]  
+ xycurveto(ax,ay,bx,by,x,y,1,4)
+ d=0
+else
   for i=s,top,4 do
    local ax=x+d
    local ay=y+stack[i] 
@@ -13739,9 +14051,10 @@
    local by=ay+stack[i+2] 
    x=bx
    y=by+stack[i+3]  
-   xycurveto(ax,ay,bx,by,x,y)
+   xycurveto(ax,ay,bx,by,x,y,i,4)
    d=0
   end
+end
   top=0
  end
  local function xxcurveto(swap)
@@ -13749,6 +14062,33 @@
   if last then
    top=top-1
   end
+if top==4 then
+  local ax,ay,bx,by
+  if swap then
+   ax=x+stack[1]
+   ay=y
+   bx=ax+stack[2]
+   by=ay+stack[3]
+   y=by+stack[4]
+   if last then
+    x=bx+last
+   else
+    x=bx
+   end
+  else
+   ax=x
+   ay=y+stack[1]
+   bx=ax+stack[2]
+   by=ay+stack[3]
+   x=bx+stack[4]
+   if last then
+    y=by+last
+   else
+    y=by
+   end
+  end
+  xycurveto(ax,ay,bx,by,x,y,1,4)
+else
   for i=1,top,4 do
    local ax,ay,bx,by
    if swap then
@@ -13776,8 +14116,9 @@
     end
     swap=true
    end
-   xycurveto(ax,ay,bx,by,x,y)
+   xycurveto(ax,ay,bx,by,x,y,i,4)
   end
+end
   top=0
  end
  local function hvcurveto()
@@ -13803,7 +14144,7 @@
    local by=ay+stack[i+3] 
    x=bx+stack[i+4] 
    y=by+stack[i+5] 
-   xycurveto(ax,ay,bx,by,x,y)
+   xycurveto(ax,ay,bx,by,x,y,i,6)
   end
   x=x+stack[top-1] 
   y=y+stack[top]   
@@ -14237,6 +14578,7 @@
  end)
  local c_endchar=chars[14]
  local encode={}
+ local typeone=false
  setmetatableindex(encode,function(t,i)
   for i=-2048,-1130 do
    t[i]=char(28,band(rshift(i,8),0xFF),band(i,0xFF))
@@ -14362,13 +14704,20 @@
     elseif t<=254 then
      stack[top]=-t*256+64148-tab[i+1]
      i=i+2
+    elseif typeone then
+     local n=0x1000000*tab[i+1]+0x10000*tab[i+2]+0x100*tab[i+3]+tab[i+4]
+     if n>=0x8000000 then
+      n=n-0xFFFFFFFF-1
+     end
+     stack[top]=n
+     i=i+5
     else
-     local n=0x100*tab[i+1]+tab[i+2]
-     if n>=0x8000 then
-      stack[top]=n-0x10000+(0x100*tab[i+3]+tab[i+4])/0xFFFF
-     else
-      stack[top]=n+(0x100*tab[i+3]+tab[i+4])/0xFFFF
+     local n1=0x100*tab[i+1]+tab[i+2]
+     local n2=0x100*tab[i+3]+tab[i+4]
+     if n1>=0x8000 then
+      n1=n1-0x10000
      end
+     stack[top]=n1+n2/0xFFFF
      i=i+5
     end
    elseif t==28 then
@@ -14456,7 +14805,7 @@
      i=i+s+1
     elseif t==1 or t==3 or t==18 or operation==23 then
      p_getstem() 
-     if true then
+     if version=="cff" then
       if top>0 then
        for i=1,top do
         r=r+1;result[r]=encode[stack[i]]
@@ -14469,7 +14818,7 @@
      end
      i=i+1
     elseif t==19 or t==20 then
-     local s=p_getmask() or 0 
+     local s=p_getmask() or 0
      if true then
       if top>0 then
        for i=1,top do
@@ -14492,7 +14841,7 @@
      i=i+1
     elseif t==13 then
      hsbw()
-     if version=="cff" then
+     if true then
       r=r+1;result[r]=encode[lsb]
       r=r+1;result[r]=chars[22]
      else
@@ -14503,8 +14852,21 @@
       showstate(reverse[t] or "<action>")
      end
      if top>0 then
-      for i=1,top do
-       r=r+1;result[r]=encode[stack[i]]
+      if t==8 and top>48 then
+       local n=0
+       for i=1,top do
+        if n==48 then
+         r=r+1;result[r]=chars[t]
+         n=1
+        else
+         n=n+1
+        end
+        r=r+1;result[r]=encode[stack[i]]
+       end
+      else
+       for i=1,top do
+        r=r+1;result[r]=encode[stack[i]]
+       end
       end
       top=0
      end
@@ -14541,7 +14903,7 @@
     ((l<1240 and 107) or (l<33900 and 1131) or 32768)+1
   end
  end
- local function processshape(tab,index,hack)
+ local function processshape(glyphs,tab,index,hack)
   if not tab then
    glyphs[index]={
     boundingbox={ 0,0,0,0 },
@@ -14593,10 +14955,12 @@
    r=r+1
    result[r]=c_endchar
    local stream=concat(result)
+result=nil
    if glyph then
     glyph.stream=stream
+    glyph.width=width
    else
-    glyphs[index]={ stream=stream }
+    glyphs[index]={ stream=stream,width=width }
    end
   elseif glyph then
    glyph.segments=keepcurve~=false and result or nil
@@ -14614,6 +14978,7 @@
     width=width,
     name=charset and charset[index] or nil,
    }
+result=nil
   else
    glyphs[index]={
     boundingbox=boundingbox,
@@ -14663,23 +15028,24 @@
   end
   return privatedata.nominalwidthx or 0,privatedata.defaultwidthx or 0
  end
- parsecharstrings=function(fontdata,data,glphs,doshapes,tversion,streams,nobias)
+ parsecharstrings=function(fontdata,data,glphs,doshapes,tversion,streams,nobias,istypeone)
   local dictionary=data.dictionaries[1]
   local charstrings=dictionary.charstrings
   keepcurve=doshapes
   version=tversion
+  typeone=istypeone or false
   strings=data.strings
   globals=data.routines or {}
   locals=dictionary.subroutines or {}
   charset=dictionary.charset
   vsindex=dictionary.vsindex or 0
-  glyphs=glphs or {}
+  local glyphs=glphs or {}
   globalbias,localbias=setbias(globals,locals,nobias)
   nominalwidth,defaultwidth=setwidths(dictionary.private)
   if charstrings then
    startparsing(fontdata,data,streams)
    for index=1,#charstrings do
-    processshape(charstrings[index],index-1)
+    processshape(glyphs,charstrings[index],index-1)
    end
    if justpass and next(seacs) then
     local charset=data.dictionaries[1].charset
@@ -14693,7 +15059,7 @@
       if bglyph and aglyph then
        local jp=justpass
        justpass=false
-       local x,y=processshape(charstrings[bindex+1],bindex,true)
+       local x,y=processshape(glyphs,charstrings[bindex+1],bindex,true)
        justpass=jp
        local base=bglyph.stream
        local accent=aglyph.stream
@@ -14718,12 +15084,13 @@
   locals=dictionary.subroutines or {}
   charset=false
   vsindex=dictionary.vsindex or 0
-  glyphs=glphs or {}
+  local glyphs=glphs or {}
   justpass=streams==true
   seacs={}
   globalbias,localbias=setbias(globals,locals,nobias)
   nominalwidth,defaultwidth=setwidths(dictionary.private)
-  processshape(tab,index-1)
+  processshape(glyphs,tab,index-1)
+  return glyphs[index]
  end
 end
 local function readglobals(f,data,version)
@@ -14871,7 +15238,7 @@
  parseprivates(data,data.dictionaries)
  readlocals(f,data,dictionary,version)
  startparsing(fontdata,data,streams)
- parsecharstrings(fontdata,data,glyphs,doshapes,version,streams)
+ parsecharstrings(fontdata,data,glyphs,doshapes,version,streams,false)
  stopparsing(fontdata,data)
 end
 local function readfdselect(f,fontdata,data,glyphs,doshapes,version,streams)
@@ -14919,6 +15286,7 @@
    end
   end
  else
+  report("unsupported fd index format %i",format)
  end
  if maxindex>=0 then
   local cidarray=cid.fdarray
@@ -14925,20 +15293,28 @@
   if cidarray then
    setposition(f,header.offset+cidarray)
    local dictionaries=readlengths(f,version=="cff2")
-   for i=1,#dictionaries do
-    dictionaries[i]=readstring(f,dictionaries[i])
+   if #dictionaries>0 then
+    for i=1,#dictionaries do
+     dictionaries[i]=readstring(f,dictionaries[i])
+    end
+    parsedictionaries(data,dictionaries)
+    cid.dictionaries=dictionaries
+    readcidprivates(f,data)
+    for i=1,#dictionaries do
+     readlocals(f,data,dictionaries[i],version)
+    end
+    startparsing(fontdata,data,streams)
+    for i=1,#charstrings do
+     local dictionary=dictionaries[fdindex[i]+1]
+     if dictionary then
+      parsecharstring(fontdata,data,dictionary,charstrings[i],glyphs,i,doshapes,version,streams)
+     else
+     end
+    end
+    stopparsing(fontdata,data)
+   else
+    report("no cid dictionaries")
    end
-   parsedictionaries(data,dictionaries)
-   cid.dictionaries=dictionaries
-   readcidprivates(f,data)
-   for i=1,#dictionaries do
-    readlocals(f,data,dictionaries[i],version)
-   end
-   startparsing(fontdata,data,streams)
-   for i=1,#charstrings do
-    parsecharstring(fontdata,data,dictionaries[fdindex[i]+1],charstrings[i],glyphs,i,doshapes,version,streams)
-   end
-   stopparsing(fontdata,data)
   else
    report("no cid array")
   end
@@ -15010,6 +15386,8 @@
     cffinfo.bluefuzz=data.bluefuzz
     cffinfo.stdhw=data.stdhw
     cffinfo.stdvw=data.stdvw
+    cffinfo.stemsnaph=data.stemsnaph
+    cffinfo.stemsnapv=data.stemsnapv
    end
   end
   cleanup(data,dictionaries)
@@ -15052,39 +15430,6 @@
   cleanup(data,dictionaries)
  end
 end
-function readers.cffcheck(filename)
- local f=io.open(filename,"rb")
- if f then
-  local fontdata={
-   glyphs={},
-  }
-  local header=readheader(f)
-  if header.major~=1 then
-   report("only version %s is supported for table %a",1,"cff")
-   return
-  end
-  local names=readfontnames(f)
-  local dictionaries=readtopdictionaries(f)
-  local strings=readstrings(f)
-  local glyphs={}
-  local data={
-   header=header,
-   names=names,
-   dictionaries=dictionaries,
-   strings=strings,
-   glyphs=glyphs,
-   nofglyphs=0,
-  }
-  parsedictionaries(data,dictionaries,"cff")
-  local cid=data.dictionaries[1].cid
-  if cid and cid.fdselect then
-   readfdselect(f,fontdata,data,glyphs,false)
-  else
-   readnoselect(f,fontdata,data,glyphs,false)
-  end
-  return data
- end
-end
 
 end -- closure
 
@@ -15100,7 +15445,7 @@
 }
 local next,type,unpack=next,type,unpack
 local band,rshift=bit32.band,bit32.rshift
-local sqrt,round=math.sqrt,math.round
+local sqrt,round,abs,min,max=math.sqrt,math.round,math.abs,math.min,math.max
 local char,rep=string.char,string.rep
 local concat=table.concat
 local idiv=number.idiv
@@ -15234,12 +15579,12 @@
   r_x+2/3*(m_x-r_x),r_y+2/3*(m_y-r_y),
   r_x,r_y,"c"
 end
+local xv={} 
+local yv={} 
 local function applyaxis(glyph,shape,deltas,dowidth)
  local points=shape.points
  if points then
   local nofpoints=#points
-  local h=nofpoints+2 
-  local l=nofpoints+1
   local dw=0
   local dl=0
   for i=1,#deltas do
@@ -15246,57 +15591,170 @@
    local deltaset=deltas[i]
    local xvalues=deltaset.xvalues
    local yvalues=deltaset.yvalues
-   local dpoints=deltaset.points
-   local factor=deltaset.factor
-   if dpoints then
-    local nofdpoints=#dpoints
-    for i=1,nofdpoints do
-     local d=dpoints[i]
-     local p=points[d]
-     if p then
-      if xvalues then
-       local x=xvalues[i]
-       if x and x~=0 then
-        p[1]=p[1]+factor*x
+   if xvalues and yvalues then
+    local dpoints=deltaset.points
+    local factor=deltaset.factor
+    if dpoints then
+     local cnt=#dpoints
+     if dowidth then
+      cnt=cnt-4
+     end
+     if cnt>0 then
+      local contours=shape.contours
+      local nofcontours=#contours
+      local first=1
+      local firstindex=1
+      for contour=1,nofcontours do
+       local last=contours[contour]
+       if last>=first then
+        local lastindex=cnt
+        if firstindex<cnt then
+         for currentindex=firstindex,cnt do
+          local found=dpoints[currentindex]
+          if found<=first then
+           firstindex=currentindex
+          end
+          if found==last then
+           lastindex=currentindex
+           break
+          elseif found>last then
+           while lastindex>1 and dpoints[lastindex]>last do
+            lastindex=lastindex-1
+           end
+           break
+          end
+         end
+        end
+        local function find(i)
+         local prv=lastindex
+         for j=firstindex,lastindex do
+          local nxt=dpoints[j] 
+          if nxt==i then
+           return false,j,false
+          elseif nxt>i then
+           return prv,false,j
+          end
+          prv=j
+         end
+         return prv,false,firstindex
+        end
+        for point=first,last do
+         local d1,d2,d3=find(point)
+         local p2=points[point]
+         if d2 then
+          xv[point]=xvalues[d2]
+          yv[point]=yvalues[d2]
+         else
+          local n1=dpoints[d1]
+          local n3=dpoints[d3]
+          if n1>nofpoints then
+           n1=nofpoints
+          end
+          if n3>nofpoints then
+           n3=nofpoints
+          end
+          local p1=points[n1]
+          local p3=points[n3]
+          local p1x=p1[1]
+          local p2x=p2[1]
+          local p3x=p3[1]
+          local p1y=p1[2]
+          local p2y=p2[2]
+          local p3y=p3[2]
+          local x1=xvalues[d1]
+          local y1=yvalues[d1]
+          local x3=xvalues[d3]
+          local y3=yvalues[d3]
+          local fx
+          local fy
+          if p1x==p3x then
+           if x1==x3 then
+            fx=x1
+           else
+            fx=0
+           end
+          elseif p2x<=min(p1x,p3x) then
+           if p1x<p3x then
+            fx=x1
+           else
+            fx=x3
+           end
+          elseif p2x>=max(p1x,p3x) then
+           if p1x>p3x then
+            fx=x1
+           else
+            fx=x3
+           end
+          else
+           fx=(p2x-p1x)/(p3x-p1x)
+           fx=(1-fx)*x1+fx*x3
+          end
+          if p1y==p3y then
+           if y1==y3 then
+            fy=y1
+           else
+            fy=0
+           end
+          elseif p2y<=min(p1y,p3y) then
+           if p1y<p3y then
+            fy=y1
+           else
+            fy=y3
+           end
+          elseif p2y>=max(p1y,p3y) then
+           if p1y>p3y then
+            fy=y1
+           else
+            fy=y3
+           end
+          else
+           fy=(p2y-p1y)/(p3y-p1y)
+           fy=(1-fy)*y1+fy*y3
+          end
+          xv[point]=fx
+          yv[point]=fy
+         end
+        end
+        if lastindex<cnt then
+         firstindex=lastindex+1
+        end
        end
+       first=last+1
       end
-      if yvalues then
-       local y=yvalues[i]
-       if y and y~=0 then
-        p[2]=p[2]+factor*y
+      for i=1,nofpoints do
+       local pi=points[i]
+       local fx=xv[i]
+       local fy=yv[i]
+       if fx~=0 then
+        pi[1]=pi[1]+factor*fx
        end
-      end
-     elseif dowidth then
-      if d==h then
-       local x=xvalues[i]
-       if x then
-        dw=dw+factor*x
+       if fy~=0 then
+        pi[2]=pi[2]+factor*fy
        end
-      elseif d==l then
-       local x=xvalues[i]
-       if x then
-        dl=dl+factor*x
-       end
       end
+     else
+      report("bad deltapoint data, maybe a missing hvar table")
      end
-    end
-   else
-    for i=1,nofpoints do
-     local p=points[i]
-     if xvalues then
+    else
+     for i=1,nofpoints do
+      local p=points[i]
       local x=xvalues[i]
-      if x and x~=0 then
-       p[1]=p[1]+factor*x
+      if x then
+       local y=yvalues[i]
+       if x~=0 then
+        p[1]=p[1]+factor*x
+       end
+       if y~=0 then
+        p[2]=p[2]+factor*y
+       end
+      else
+       break
       end
      end
-     if yvalues then
-      local y=yvalues[i]
-      if y and y~=0 then
-       p[2]=p[2]+factor*y
-      end
-     end
     end
     if dowidth then
+     local h=nofpoints+2 
+     local l=nofpoints+1
      local x=xvalues[h]
      if x then
       dw=dw+factor*x
@@ -15618,6 +16076,12 @@
       end
       first=last+1
      end
+     xmin=glyph.boundingbox[1]
+     local dlsb=glyph.dlsb
+     if dlsb then
+      xmin=xmin+dlsb
+      glyph.dlsb=nil 
+     end
      glyph.boundingbox={ round(xmin),round(ymin),round(xmax),round(ymax) }
     end
    end
@@ -15875,13 +16339,13 @@
     yoffset=yoffset*yscale
    end
   elseif band(flags,0x0080)~=0 then 
-   xscale=read2dot14(f)
-   xrotate=read2dot14(f)
-   yrotate=read2dot14(f)
-   yscale=read2dot14(f)
+   xscale=read2dot14(f) 
+   xrotate=read2dot14(f) 
+   yrotate=read2dot14(f) 
+   yscale=read2dot14(f) 
    if f_xyarg and f_offset then
-    xoffset=xoffset*sqrt(xscale^2+xrotate^2)
-    yoffset=yoffset*sqrt(yrotate^2+yscale^2)
+    xoffset=xoffset*sqrt(xscale^2+yrotate^2) 
+    yoffset=yoffset*sqrt(xrotate^2+yscale^2) 
    end
   end
   nofcomponents=nofcomponents+1
@@ -16039,42 +16503,8 @@
 local function readdeltas(f,nofpoints)
  local deltas={}
  local p=0
- local z=0
  while nofpoints>0 do
   local control=readbyte(f)
-if not control then
- break
-end
-  local allzero=band(control,0x80)~=0
-  local runlength=band(control,0x3F)+1
-  if allzero then
-   z=z+runlength
-  else
-   local runreader=band(control,0x40)~=0 and readshort or readinteger
-   if z>0 then
-    for i=1,z do
-     p=p+1
-     deltas[p]=0
-    end
-    z=0
-   end
-   for i=1,runlength do
-    p=p+1
-    deltas[p]=runreader(f)
-   end
-  end
-  nofpoints=nofpoints-runlength
- end
- if p>0 then
-  return deltas
- else
- end
-end
-local function readdeltas(f,nofpoints)
- local deltas={}
- local p=0
- while nofpoints>0 do
-  local control=readbyte(f)
   if control then
    local allzero=band(control,0x80)~=0
    local runlength=band(control,0x3F)+1
@@ -16426,6 +16856,20 @@
  streamreader.readinteger3,
  streamreader.readinteger4,
 }
+directives.register("fonts.streamreader",function()
+ read_cardinal={
+  streamreader.readcardinal1,
+  streamreader.readcardinal2,
+  streamreader.readcardinal3,
+  streamreader.readcardinal4,
+ }
+ read_integer={
+  streamreader.readinteger1,
+  streamreader.readinteger2,
+  streamreader.readinteger3,
+  streamreader.readinteger4,
+ }
+end)
 local lookupnames={
  gsub={
   single="gsub_single",
@@ -16500,7 +16944,6 @@
  for i=1,#segments do
   local s=segments[i]
   if type(s)~="number" then
-   report("using default axis scale")
    return default
   elseif s[1]>=default then
    if s[2]==default then
@@ -18004,6 +18447,7 @@
   local noflookups=#lookups
   local lookupprefix=sub(what,2,2)
   local usedlookups=false
+  local allsteps={} 
   for lookupid=1,noflookups do
    local lookup=lookups[lookupid]
    local lookuptype=lookup.type
@@ -18038,6 +18482,7 @@
        steps[nofsteps]=step
        local rules=step.rules
        if rules then
+        allsteps[#allsteps+1]=step 
         for i=1,#rules do
          local rule=rules[i]
          local before=rule.before
@@ -18136,18 +18581,11 @@
    report("used %s lookups: % t",what,sortedkeys(usedlookups))
   end
   local reported={}
-  local function report_issue(i,what,sequence,kind)
-   local name=sequence.name
-   if not reported[name] then
-    report("rule %i in %s lookup %a has %s lookups",i,what,name,kind)
-    reported[name]=true
-   end
+  local function report_issue(i,what,step,kind)
+    report("rule %i in step %i of %s has %s lookups",i,step,what,kind)
   end
-  for i=lastsequence+1,nofsequences do
-   local sequence=sequences[i]
-   local steps=sequence.steps
-   for i=1,#steps do
-    local step=steps[i]
+   for s=1,#allsteps do    
+    local step=allsteps[s] 
     local rules=step.rules
     if rules then
      for i=1,#rules do
@@ -18154,7 +18592,7 @@
       local rule=rules[i]
       local rlookups=rule.lookups
       if not rlookups then
-       report_issue(i,what,sequence,"no")
+       report_issue(i,what,s,"no")
       elseif not next(rlookups) then
        rule.lookups=nil
       else
@@ -18190,12 +18628,12 @@
               sublookupcheck[lookupid]=1
               h=nofsublookups
              else
-              report_issue(i,what,sequence,"missing")
+              report_issue(i,what,s,"missing")
               rule.lookups=nil
               break
              end
             else
-             report_issue(i,what,sequence,"bad")
+             report_issue(i,what,s,"bad")
              rule.lookups=nil
              break
             end
@@ -18217,7 +18655,6 @@
      end
     end
    end
-  end
   for i,n in sortedhash(sublookupcheck) do
    local l=lookups[i]
    local t=l.type
@@ -18307,7 +18744,7 @@
    local scriptoffset=tableoffset+readushort(f)
    local featureoffset=tableoffset+readushort(f)
    local lookupoffset=tableoffset+readushort(f)
-   local variationsoffset=version>0x00010000 and (tableoffset+readulong(f)) or 0
+   local variationsoffset=version>0x00010000 and readulong(f) or 0
    if not scriptoffset then
     return
    end
@@ -18327,7 +18764,7 @@
     resolvelookups(f,lookupoffset,fontdata,lookups,lookuptypes,lookuphandlers,what,tableoffset)
    end
    if variationsoffset>0 then
-    loadvariations(f,fontdata,variationsoffset,lookuptypes,featurehash,featureorder)
+    loadvariations(f,fontdata,tableoffset+variationsoffset,lookuptypes,featurehash,featureorder)
    end
   end
  end
@@ -18640,7 +19077,7 @@
     if not math then
      glyph.math={ accent=accent }
     else
-     math.accent=accent
+     math.accent=accent 
     end
    end
   end
@@ -18721,7 +19158,7 @@
  local vconstruction=readcardinaltable(f,vnofglyphs,ushort)
  local hconstruction=readcardinaltable(f,hnofglyphs,ushort)
  fontdata.mathconstants.MinConnectorOverlap=minoverlap
- local function get(offset,coverage,nofglyphs,construction,kvariants,kparts,kitalic)
+ local function get(offset,coverage,nofglyphs,construction,kvariants,kparts,kitalic,korientation,orientation)
   if coverage~=0 and nofglyphs>0 then
    local coverage=readcoverage(f,offset+coverage,true)
    for i=1,nofglyphs do
@@ -18784,13 +19221,21 @@
       if italic and italic~=0 then
        math[kitalic]=italic
       end
+      if orientation then
+       math[korientation]=orientation
+      end
      end
     end
    end
   end
  end
- get(offset,vcoverage,vnofglyphs,vconstruction,"vvariants","vparts","vitalic")
- get(offset,hcoverage,hnofglyphs,hconstruction,"hvariants","hparts","hitalic")
+ if CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 then
+  get(offset,hcoverage,hnofglyphs,hconstruction,"variants","parts","partsitalic","partsorientation","horizontal")
+  get(offset,vcoverage,vnofglyphs,vconstruction,"variants","parts","partsitalic","partsorientation","vertical")
+ else
+  get(offset,vcoverage,vnofglyphs,vconstruction,"vvariants","vparts","vitalic")
+  get(offset,hcoverage,hnofglyphs,hconstruction,"hvariants","hparts","hitalic")
+ end
 end
 function readers.math(f,fontdata,specification)
  local tableoffset=gotodatatable(f,fontdata,"math",specification.glyphs)
@@ -18816,9 +19261,10 @@
  local tableoffset=gotodatatable(f,fontdata,"colr",specification.glyphs)
  if tableoffset then
   local version=readushort(f)
-  if version~=0 then
+  if version==0 or version==1 then
    report("table version %a of %a is not supported (yet), maybe font %s is bad",version,"colr",fontdata.filename)
    return
+  else
   end
   if not fontdata.tables.cpal then
    report("color table %a in font %a has no mandate %a table","colr",fontdata.filename,"cpal")
@@ -19441,6 +19887,7 @@
  local variations={}
  local innerindex={} 
  local outerindex={} 
+ local deltas={}
  if variationoffset>0 then
   regions,deltas=readvariationdata(f,variationoffset,factors)
  end
@@ -19733,16 +20180,20 @@
  ["cari"]="carian",
  ["cham"]="cham",
  ["cher"]="cherokee",
+ ["chrs"]="chorasmian",
  ["copt"]="coptic",
+ ["cpmn"]="cypro-minoan",
  ["cprt"]="cypriot syllabary",
  ["cyrl"]="cyrillic",
  ["dev2"]="devanagari variant 2",
  ["deva"]="devanagari",
+ ["diak"]="dives akuru",
  ["dogr"]="dogra",
  ["dsrt"]="deseret",
  ["dupl"]="duployan",
  ["egyp"]="egyptian heiroglyphs",
  ["elba"]="elbasan",
+ ["elym"]="elymaic",
  ["ethi"]="ethiopic",
  ["geor"]="georgian",
  ["gjr2"]="gujarati variant 2",
@@ -19762,6 +20213,7 @@
  ["hebr"]="hebrew",
  ["hluw"]="anatolian hieroglyphs",
  ["hmng"]="pahawh hmong",
+ ["hmnp"]="nyiakeng puachue hmong",
  ["hung"]="old hungarian",
  ["ital"]="old italic",
  ["jamo"]="hangul jamo",
@@ -19771,6 +20223,7 @@
  ["khar"]="kharosthi",
  ["khmr"]="khmer",
  ["khoj"]="khojki",
+ ["kits"]="khitan small script",
  ["knd2"]="kannada variant 2",
  ["knda"]="kannada",
  ["kthi"]="kaithi",
@@ -19804,6 +20257,7 @@
  ["musc"]="musical symbols",
  ["mym2"]="myanmar variant 2",
  ["mymr"]="myanmar",
+ ["nand"]="nandinagari",
  ["narb"]="old north arabian",
  ["nbat"]="nabataean",
  ["newa"]="newa",
@@ -19813,9 +20267,10 @@
  ["olck"]="ol chiki",
  ["orkh"]="old turkic and orkhon runic",
  ["ory2"]="odia variant 2",
- ["orya"]="oriya",
+ ["orya"]="odia",
  ["osge"]="osage",
  ["osma"]="osmanya",
+ ["ougr"]="old uyghur",
  ["palm"]="palmyrene",
  ["pauc"]="pau cin hau",
  ["perm"]="old permic",
@@ -19847,7 +20302,7 @@
  ["tagb"]="tagbanwa",
  ["takr"]="takri",
  ["tale"]="tai le",
- ["talu"]="tai lu",
+ ["talu"]="new tai lue",
  ["taml"]="tamil",
  ["tang"]="tangut",
  ["tavt"]="tai viet",
@@ -19859,12 +20314,16 @@
  ["thai"]="thai",
  ["tibt"]="tibetan",
  ["tirh"]="tirhuta",
+ ["tnsa"]="tangsa",
  ["tml2"]="tamil variant 2",
+ ["toto"]="toto",
  ["ugar"]="ugaritic cuneiform",
  ["vai" ]="vai",
  ["wara"]="warang citi",
+ ["wcho"]="wancho",
  ["xpeo"]="old persian cuneiform",
  ["xsux"]="sumero-akkadian cuneiform",
+ ["yezi"]="yezidi",
  ["yi"  ]="yi",
  ["zanb"]="zanabazar square",
 }
@@ -19879,6 +20338,7 @@
  ["agw" ]="agaw",
  ["aio" ]="aiton",
  ["aka" ]="akan",
+ ["akb" ]="batak angkola",
  ["als" ]="alsatian",
  ["alt" ]="altai",
  ["amh" ]="amharic",
@@ -19891,6 +20351,7 @@
  ["asm" ]="assamese",
  ["ast" ]="asturian",
  ["ath" ]="athapaskan",
+ ["avn" ]="avatime",
  ["avr" ]="avar",
  ["awa" ]="awadhi",
  ["aym" ]="aymara",
@@ -19936,8 +20397,12 @@
  ["brx" ]="bodo",
  ["bsh" ]="bashkir",
  ["bsk" ]="burushaski",
+ ["bta" ]="batak alas kluet",
+ ["btd" ]="batak dairi (pakpak)",
  ["bti" ]="beti",
+ ["btm" ]="batak mandailing",
  ["bts" ]="batak simalungun",
+ ["btx" ]="batak karo",
  ["bug" ]="bugis",
  ["byv" ]="medumba",
  ["cak" ]="kaqchikel",
@@ -19972,14 +20437,16 @@
  ["csl" ]="church slavonic",
  ["csy" ]="czech",
  ["ctg" ]="chittagonian",
+ ["ctt" ]="wayanad chetti",
  ["cuk" ]="san blas kuna",
+ ["dag" ]="dagbani",
  ["dan" ]="danish",
  ["dar" ]="dargwa",
  ["dax" ]="dayi",
  ["dcr" ]="woods cree",
  ["deu" ]="german",
- ["dgo" ]="dogri",
- ["dgr" ]="dogri",
+ ["dgo" ]="dogri (individual language)",
+ ["dgr" ]="dogri (macro language)",
  ["dhg" ]="dhangu",
  ["dhv" ]="divehi (dhivehi, maldivian)",
  ["diq" ]="dimli",
@@ -20051,6 +20518,7 @@
  ["guj" ]="gujarati",
  ["guz" ]="gusii",
  ["hai" ]="haitian (haitian creole)",
+ ["hai0"]="haida",
  ["hal" ]="halam",
  ["har" ]="harauti",
  ["hau" ]="hausa",
@@ -20057,7 +20525,9 @@
  ["haw" ]="hawaiian",
  ["hay" ]="haya",
  ["haz" ]="hazaragi",
+ ["hmz" ]="hmong shuat",
  ["hbn" ]="hammer-banna",
+ ["hei" ]="heiltsuk",
  ["her" ]="herero",
  ["hil" ]="hiligaynon",
  ["hin" ]="hindi",
@@ -20082,10 +20552,12 @@
  ["ind" ]="indonesian",
  ["ing" ]="ingush",
  ["inu" ]="inuktitut",
+ ["inuk"]="nunavik inuktitut",
  ["ipk" ]="inupiat",
  ["ipph"]="phonetic transcription—ipa conventions",
  ["iri" ]="irish",
  ["irt" ]="irish traditional",
+ ["uri" ]="irula",
  ["isl" ]="icelandic",
  ["ism" ]="inari sami",
  ["ita" ]="italian",
@@ -20105,6 +20577,7 @@
  ["kan" ]="kannada",
  ["kar" ]="karachay",
  ["kat" ]="georgian",
+ ["kaw" ]="kawi (old javanese)",
  ["kaz" ]="kazakh",
  ["kde" ]="makonde",
  ["kea" ]="kabuverdianu (crioulo)",
@@ -20162,6 +20635,7 @@
  ["kur" ]="kurdish",
  ["kuu" ]="kurukh",
  ["kuy" ]="kuy",
+ ["kwk" ]="kwakʼwala",
  ["kyk" ]="koryak",
  ["kyu" ]="western kayah",
  ["lad" ]="ladin",
@@ -20173,6 +20647,7 @@
  ["laz" ]="laz",
  ["lcr" ]="l-cree",
  ["ldk" ]="ladakhi",
+ ["lef" ]="lelemi",
  ["lez" ]="lezgi",
  ["lij" ]="ligurian",
  ["lim" ]="limburgish",
@@ -20185,6 +20660,7 @@
  ["lmo" ]="lombard",
  ["lmw" ]="lomwe",
  ["lom" ]="loma",
+ ["lpo" ]="lipo",
  ["lrc" ]="luri",
  ["lsb" ]="lower sorbian",
  ["lsm" ]="lule sami",
@@ -20201,7 +20677,7 @@
  ["mah" ]="marshallese",
  ["maj" ]="majang",
  ["mak" ]="makhuwa",
- ["mal" ]="malayalam reformed",
+ ["mal" ]="malayalam",
  ["mam" ]="mam",
  ["man" ]="mansi",
  ["map" ]="mapudungun",
@@ -20236,6 +20712,7 @@
  ["mok" ]="moksha",
  ["mol" ]="moldavian",
  ["mon" ]="mon",
+ ["mnw" ]="thailand mon",
  ["mor" ]="moroccan",
  ["mos" ]="mossi",
  ["mri" ]="maori",
@@ -20274,7 +20751,7 @@
  ["nor" ]="norwegian",
  ["nov" ]="novial",
  ["nsm" ]="northern sami",
- ["nso" ]="sotho, northern",
+ ["nso" ]="northern sotho",
  ["nta" ]="northern tai",
  ["nto" ]="esperanto",
  ["nym" ]="nyamwezi",
@@ -20322,6 +20799,7 @@
  ["rbu" ]="russian buriat",
  ["rcr" ]="r-cree",
  ["rej" ]="rejang",
+ ["rhg" ]="rohingya",
  ["ria" ]="riang",
  ["rif" ]="tarifit",
  ["rit" ]="ritarungo",
@@ -20346,6 +20824,7 @@
  ["scs" ]="north slavey",
  ["sek" ]="sekota",
  ["sel" ]="selkup",
+ ["sfm" ]="small flowery miao",
  ["sga" ]="old irish",
  ["sgo" ]="sango",
  ["sgs" ]="samogitian",
@@ -20367,7 +20846,7 @@
  ["snk" ]="soninke",
  ["sog" ]="sodo gurage",
  ["sop" ]="songe",
- ["sot" ]="sotho, southern",
+ ["sot" ]="southern sotho",
  ["sqi" ]="albanian",
  ["srb" ]="serbian",
  ["srd" ]="sardinian",
@@ -20408,7 +20887,9 @@
  ["tht" ]="tahitian",
  ["tib" ]="tibetan",
  ["tiv" ]="tiv",
+ ["tj;" ]="tai laing",
  ["tkm" ]="turkmen",
+ ["tli" ]="tlingit",
  ["tmh" ]="tamashek",
  ["tmn" ]="temne",
  ["tna" ]="tswana",
@@ -20422,7 +20903,7 @@
  ["tsj" ]="tshangla",
  ["tua" ]="turoyo aramaic",
  ["tul" ]="tulu",
- ["tum" ]="tulu",
+ ["tum" ]="tumbuka",
  ["tuv" ]="tuvin",
  ["tvl" ]="tuvalu",
  ["twi" ]="twi",
@@ -20444,6 +20925,7 @@
  ["wa"  ]="wa",
  ["wag" ]="wagdi",
  ["war" ]="waray-waray",
+ ["wci" ]="waci gbe",
  ["wcr" ]="west-cree",
  ["wel" ]="welsh",
  ["wlf" ]="wolof",
@@ -20455,17 +20937,23 @@
  ["xkf" ]="khengkha",
  ["xog" ]="soga",
  ["xpe" ]="kpelle (liberia)",
+ ["xub" ]="bette kuruma",
+ ["xuj" ]="jennu kuruma",
  ["yak" ]="sakha",
  ["yao" ]="yao",
  ["yap" ]="yapese",
  ["yba" ]="yoruba",
  ["ycr" ]="y-cree",
+ ["ygp" ]="gepo",
  ["yic" ]="yi classic",
  ["yim" ]="yi modern",
+ ["yna" ]="aluo",
+ ["ywq" ]="wuding-luquan",
  ["zea" ]="zealandic",
  ["zgh" ]="standard morrocan tamazigh",
  ["zha" ]="zhuang",
  ["zhh" ]="chinese, hong kong sar",
+ ["zho" ]="chinese traditional, macao",
  ["zhp" ]="chinese phonetic",
  ["zhs" ]="chinese simplified",
  ["zht" ]="chinese traditional",
@@ -20489,6 +20977,7 @@
  ["case"]="case-sensitive forms",
  ["ccmp"]="glyph composition/decomposition",
  ["cfar"]="conjunct form after ro",
+ ["chws"]="contextual half-width spacing",
  ["cjct"]="conjunct forms",
  ["clig"]="contextual ligatures",
  ["cpct"]="centered cjk punctuation",
@@ -20565,8 +21054,8 @@
  ["rtbd"]="right bounds",
  ["rtla"]="right-to-left alternates",
  ["rtlm"]="right to left mirrored forms",
+ ["ruby"]="ruby notation forms",
  ["rvrn"]="required variation alternates",
- ["ruby"]="ruby notation forms",
  ["salt"]="stylistic alternates",
  ["sinf"]="scientific inferiors",
  ["size"]="optical size",
@@ -20586,6 +21075,7 @@
  ["unic"]="unicase",
  ["valt"]="alternate vertical metrics",
  ["vatu"]="vattu variants",
+ ["vchw"]="vertical contextual half-width spacing",
  ["vert"]="vertical writing",
  ["vhal"]="alternate vertical half metrics",
  ["vjmo"]="vowel jamo forms",
@@ -20814,7 +21304,7 @@
 local lower=string.lower
 local type,next,tonumber,tostring,unpack=type,next,tonumber,tostring,unpack
 local abs=math.abs
-local derivetable=table.derive
+local derivetable,sortedhash=table.derive,table.sortedhash
 local formatters=string.formatters
 local setmetatableindex=table.setmetatableindex
 local allocate=utilities.storage.allocate
@@ -20830,7 +21320,7 @@
 local report_otf=logs.reporter("fonts","otf loading")
 local fonts=fonts
 local otf=fonts.handlers.otf
-otf.version=3.113 
+otf.version=3.132 
 otf.cache=containers.define("fonts","otl",otf.version,true)
 otf.svgcache=containers.define("fonts","svg",otf.version,true)
 otf.pngcache=containers.define("fonts","png",otf.version,true)
@@ -20965,6 +21455,9 @@
    if cleanup==0 then
     checkmemory(used,threshold,tracememory)
    end
+   if context then
+    otfreaders.condense(data)
+   end
    otfreaders.pack(data)
    report_otf("loading done")
    report_otf("saving %a in cache",filename)
@@ -21035,7 +21528,7 @@
   local properties=derivetable(data.properties)
   local descriptions=derivetable(data.descriptions)
   local goodies=derivetable(data.goodies)
-  local characters={}
+  local characters={} 
   local parameters={}
   local mathparameters={}
   local resources=data.resources
@@ -21203,7 +21696,25 @@
   properties.name=filename or fullname
   properties.subfont=subfont
 if not CONTEXTLMTXMODE or CONTEXTLMTXMODE==0 then
-  properties.encodingbytes=2
+ properties.encodingbytes=2
+elseif CONTEXTLMTXMODE then
+ local duplicates=resources and resources.duplicates
+ if duplicates then
+  local maxindex=data.nofglyphs or metadata.nofglyphs
+  if maxindex then
+   for u,d in sortedhash(duplicates) do
+    local du=descriptions[u]
+    if du then
+     for uu in sortedhash(d) do
+      maxindex=maxindex+1
+      descriptions[uu].dupindex=du.index
+      descriptions[uu].index=maxindex
+     end
+    else
+    end
+   end
+  end
+ end
 end
   properties.private=properties.private or data.private or privateoffset
   return {
@@ -21292,24 +21803,6 @@
  end
  return tfmdata
 end
-local function checkmathsize(tfmdata,mathsize)
- local mathdata=tfmdata.shared.rawdata.metadata.math
- local mathsize=tonumber(mathsize)
- if mathdata then 
-  local parameters=tfmdata.parameters
-  parameters.scriptpercentage=mathdata.ScriptPercentScaleDown
-  parameters.scriptscriptpercentage=mathdata.ScriptScriptPercentScaleDown
-  parameters.mathsize=mathsize 
- end
-end
-registerotffeature {
- name="mathsize",
- description="apply mathsize specified in the font",
- initializers={
-  base=checkmathsize,
-  node=checkmathsize,
- }
-}
 function otf.collectlookups(rawdata,kind,script,language)
  if not kind then
   return
@@ -21571,7 +22064,7 @@
  end
 end
 local function cref(feature,sequence)
- return formatters["feature %a, type %a, chain lookup %a"](feature,sequence.type,sequence.name)
+ return formatters["feature %a, type %a, (chain) lookup %a"](feature,sequence.type,sequence.name)
 end
 local function report_substitution(feature,sequence,descriptions,unicode,substitution)
  if unicode==substitution then
@@ -21632,7 +22125,7 @@
 local function makefake(tfmdata,name,present)
  local private=getprivate(tfmdata)
  local character={ intermediate=true,ligatures={} }
- resources.unicodes[name]=private
+ tfmdata.resources.unicodes[name]=private
  tfmdata.characters[private]=character
  tfmdata.descriptions[private]={ name=name }
  present[name]=private
@@ -21639,42 +22132,53 @@
  return character
 end
 local function make_1(present,tree,name)
- for k,v in next,tree do
-  if k=="ligature" then
-   present[name]=v
+ if tonumber(tree) then
+  present[name]=v
+ else
+  for k,v in next,tree do
+   if k=="ligature" then
+    present[name]=v
+   else
+    make_1(present,v,name.."_"..k)
+   end
+  end
+ end
+end
+local function make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,v)
+ local character=characters[preceding]
+ if not character then
+  if trace_baseinit then
+   report_prepare("weird ligature in lookup %a, current %C, preceding %C",sequence.name,v,preceding)
+  end
+  character=makefake(tfmdata,name,present)
+ end
+ local ligatures=character.ligatures
+ if ligatures then
+  ligatures[unicode]={ char=v }
+ else
+  character.ligatures={ [unicode]={ char=v } }
+ end
+ if done then
+  local d=done[name]
+  if not d then
+   done[name]={ "dummy",v }
   else
-   make_1(present,v,name.."_"..k)
+   d[#d+1]=v
   end
  end
 end
 local function make_2(present,tfmdata,characters,tree,name,preceding,unicode,done)
- for k,v in next,tree do
-  if k=="ligature" then
-   local character=characters[preceding]
-   if not character then
-    if trace_baseinit then
-     report_prepare("weird ligature in lookup %a, current %C, preceding %C",sequence.name,v,preceding)
-    end
-    character=makefake(tfmdata,name,present)
-   end
-   local ligatures=character.ligatures
-   if ligatures then
-    ligatures[unicode]={ char=v }
+ if tonumber(tree) then
+  make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,tree)
+ else
+  for k,v in next,tree do
+   if k=="ligature" then
+    make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,v)
    else
-    character.ligatures={ [unicode]={ char=v } }
+    local code=present[name] or unicode
+    local name=name.."_"..k
+    make_2(present,tfmdata,characters,v,name,code,k,done)
    end
-   if done then
-    local d=done[name]
-    if not d then
-     done[name]={ "dummy",v }
-    else
-     d[#d+1]=v
-    end
-   end
-  else
-   local code=present[name] or unicode
-   local name=name.."_"..k
-   make_2(present,tfmdata,characters,v,name,code,k,done)
   end
  end
 end
@@ -22021,6 +22525,8 @@
 local getboth=nuts.getboth
 local getdisc=nuts.getdisc
 local setdisc=nuts.setdisc
+local getreplace=nuts.getreplace
+local setreplace=nuts.setreplace
 local setoffsets=nuts.setoffsets
 local ischar=nuts.ischar
 local getkern=nuts.getkern
@@ -22030,8 +22536,8 @@
 local getwidth=nuts.getwidth
 local nextchar=nuts.traversers.char
 local nextglue=nuts.traversers.glue
-local insert_node_before=nuts.insert_before
-local insert_node_after=nuts.insert_after
+local insertnodebefore=nuts.insertbefore
+local insertnodeafter=nuts.insertafter
 local properties=nodes.properties.data
 local fontkern=nuts.pool and nuts.pool.fontkern   
 local italickern=nuts.pool and nuts.pool.italickern 
@@ -22045,7 +22551,7 @@
 if not fontkern then 
  local thekern=nuts.new("kern",0) 
  local setkern=nuts.setkern
- local copy_node=nuts.copy_node
+ local copy_node=nuts.copy
  fontkern=function(k)
   local n=copy_node(thekern)
   setkern(n,k)
@@ -22055,7 +22561,7 @@
 if not italickern then 
  local thekern=nuts.new("kern",3) 
  local setkern=nuts.setkern
- local copy_node=nuts.copy_node
+ local copy_node=nuts.copy
  italickern=function(k)
   local n=copy_node(thekern)
   setkern(n,k)
@@ -22516,12 +23022,12 @@
      if leftkern and leftkern~=0 then
       if prev and getid(prev)==glue_code then
        if useitalickerns then
-        head=insert_node_before(head,current,italickern(leftkern))
+        head=insertnodebefore(head,current,italickern(leftkern))
        else
         setwidth(prev,getwidth(prev)+leftkern)
        end
       else
-       head=insert_node_before(head,current,fontkern(leftkern))
+       head=insertnodebefore(head,current,fontkern(leftkern))
       end
      end
     end
@@ -22575,7 +23081,7 @@
       if i then
        local leftkern=i.leftkern
        if leftkern and leftkern~=0 then
-        pre=insert_node_before(pre,n,fontkern(leftkern))
+        pre=insertnodebefore(pre,n,fontkern(leftkern))
         done=true
        end
       end
@@ -22590,7 +23096,7 @@
       if i then
        local leftkern=i.leftkern
        if leftkern and leftkern~=0 then
-        post=insert_node_before(post,n,fontkern(leftkern))
+        post=insertnodebefore(post,n,fontkern(leftkern))
         done=true
        end
       end
@@ -22605,7 +23111,7 @@
       if i then
        local leftkern=i.leftkern
        if leftkern and leftkern~=0 then
-        replace=insert_node_before(replace,n,fontkern(leftkern))
+        replace=insertnodebefore(replace,n,fontkern(leftkern))
         done=true
        end
       end
@@ -22667,36 +23173,34 @@
        rightkern=0
       elseif prev and getid(prev)==glue_code then
        if useitalickerns then
-        head=insert_node_before(head,current,italickern(leftkern))
+        head=insertnodebefore(head,current,italickern(leftkern))
        else
         setwidth(prev,getwidth(prev)+leftkern)
        end
       else
-       head=insert_node_before(head,current,fontkern(leftkern))
+       head=insertnodebefore(head,current,fontkern(leftkern))
       end
      end
      if rightkern and rightkern~=0 then
       if next and getid(next)==glue_code then
        if useitalickerns then
-        insert_node_after(head,current,italickern(rightkern))
+        insertnodeafter(head,current,italickern(rightkern))
        else
         setwidth(next,getwidth(next)+rightkern)
        end
       else
-       insert_node_after(head,current,fontkern(rightkern))
+       insertnodeafter(head,current,fontkern(rightkern))
       end
      end
-    else
+    elseif next then
      local i=p.emptyinjections
      if i then
       local rightkern=i.rightkern
-      if rightkern and rightkern~=0 then
-       if next and getid(next)==disc_code then
-        if replace then
-        else
-         replace=fontkern(rightkern) 
-         done=true	
-        end
+      if rightkern and rightkern~=0 and getid(next)==disc_code then
+       local replace=getreplace(next)
+       if replace then
+       else
+        setreplace(next,fontkern(rightkern))
        end
       end
      end
@@ -22757,12 +23261,12 @@
        end
        local leftkern=i.leftkern
        if leftkern and leftkern~=0 then
-        pre=insert_node_before(pre,n,fontkern(leftkern))
+        pre=insertnodebefore(pre,n,fontkern(leftkern))
         done=true
        end
        local rightkern=i.rightkern
        if rightkern and rightkern~=0 then
-        insert_node_after(pre,n,fontkern(rightkern))
+        insertnodeafter(pre,n,fontkern(rightkern))
         done=true
        end
       end
@@ -22781,12 +23285,12 @@
        end
        local leftkern=i.leftkern
        if leftkern and leftkern~=0 then
-        post=insert_node_before(post,n,fontkern(leftkern))
+        post=insertnodebefore(post,n,fontkern(leftkern))
         done=true
        end
        local rightkern=i.rightkern
        if rightkern and rightkern~=0 then
-        insert_node_after(post,n,fontkern(rightkern))
+        insertnodeafter(post,n,fontkern(rightkern))
         done=true
        end
       end
@@ -22805,12 +23309,12 @@
        end
        local leftkern=i.leftkern
        if leftkern and leftkern~=0 then
-        replace=insert_node_before(replace,n,fontkern(leftkern))
+        replace=insertnodebefore(replace,n,fontkern(leftkern))
         done=true
        end
        local rightkern=i.rightkern
        if rightkern and rightkern~=0 then
-        insert_node_after(replace,n,fontkern(rightkern))
+        insertnodeafter(replace,n,fontkern(rightkern))
         done=true
        end
       end
@@ -22825,7 +23329,7 @@
       if i then
        local rightkern=i.rightkern
        if rightkern and rightkern~=0 then
-        pre=insert_node_before(pre,pre,fontkern(rightkern))
+        pre=insertnodebefore(pre,pre,fontkern(rightkern))
         done=true
        end
       end
@@ -22838,7 +23342,7 @@
       if i then
        local rightkern=i.rightkern
        if rightkern and rightkern~=0 then
-        replace=insert_node_before(replace,replace,fontkern(rightkern))
+        replace=insertnodebefore(replace,replace,fontkern(rightkern))
         done=true
        end
       end
@@ -22941,8 +23445,8 @@
      if trace_injections then
       report_injections("correcting non zero width mark %C",getchar(n))
      end
-     insert_node_before(n,n,fontkern(-wn))
-     insert_node_after(n,n,fontkern(-wn))
+     insertnodebefore(n,n,fontkern(-wn))
+     insertnodeafter(n,n,fontkern(-wn))
     end
    end
   end
@@ -23034,37 +23538,35 @@
         rightkern=0
        elseif prev and getid(prev)==glue_code then
         if useitalickerns then
-         head=insert_node_before(head,current,italickern(leftkern))
+         head=insertnodebefore(head,current,italickern(leftkern))
         else
          setwidth(prev,getwidth(prev)+leftkern)
         end
        else
-        head=insert_node_before(head,current,fontkern(leftkern))
+        head=insertnodebefore(head,current,fontkern(leftkern))
        end
       end
       if rightkern and rightkern~=0 then
        if next and getid(next)==glue_code then
         if useitalickerns then
-         insert_node_after(head,current,italickern(rightkern))
+         insertnodeafter(head,current,italickern(rightkern))
         else
          setwidth(next,getwidth(next)+rightkern)
         end
        else
-        insert_node_after(head,current,fontkern(rightkern))
+        insertnodeafter(head,current,fontkern(rightkern))
        end
       end
      end
-    else
+    elseif next then
      local i=p.emptyinjections
      if i then
       local rightkern=i.rightkern
-      if rightkern and rightkern~=0 then
-       if next and getid(next)==disc_code then
-        if replace then
-        else
-         replace=fontkern(rightkern)
-         done=true
-        end
+      if rightkern and rightkern~=0 and getid(next)==disc_code then
+       local replace=getreplace(next)
+       if replace then
+       else
+        setreplace(next,fontkern(rightkern))
        end
       end
      end
@@ -23139,12 +23641,12 @@
        end
        local leftkern=i.leftkern
        if leftkern and leftkern~=0 then
-        pre=insert_node_before(pre,n,fontkern(leftkern))
+        pre=insertnodebefore(pre,n,fontkern(leftkern))
         done=true
        end
        local rightkern=i.rightkern
        if rightkern and rightkern~=0 then
-        insert_node_after(pre,n,fontkern(rightkern))
+        insertnodeafter(pre,n,fontkern(rightkern))
         done=true
        end
        if hasmarks then
@@ -23169,12 +23671,12 @@
        end
        local leftkern=i.leftkern
        if leftkern and leftkern~=0 then
-        post=insert_node_before(post,n,fontkern(leftkern))
+        post=insertnodebefore(post,n,fontkern(leftkern))
         done=true
        end
        local rightkern=i.rightkern
        if rightkern and rightkern~=0 then
-        insert_node_after(post,n,fontkern(rightkern))
+        insertnodeafter(post,n,fontkern(rightkern))
         done=true
        end
        if hasmarks then
@@ -23199,12 +23701,12 @@
        end
        local leftkern=i.leftkern
        if leftkern and leftkern~=0 then
-        replace=insert_node_before(replace,n,fontkern(leftkern))
+        replace=insertnodebefore(replace,n,fontkern(leftkern))
         done=true
        end
        local rightkern=i.rightkern
        if rightkern and rightkern~=0 then
-        insert_node_after(replace,n,fontkern(rightkern))
+        insertnodeafter(replace,n,fontkern(rightkern))
         done=true
        end
        if hasmarks then
@@ -23225,7 +23727,7 @@
       if i then
        local rightkern=i.rightkern
        if rightkern and rightkern~=0 then
-        pre=insert_node_before(pre,pre,fontkern(rightkern))
+        pre=insertnodebefore(pre,pre,fontkern(rightkern))
         done=true
        end
       end
@@ -23238,7 +23740,7 @@
       if i then
        local rightkern=i.rightkern
        if rightkern and rightkern~=0 then
-        replace=insert_node_before(replace,replace,fontkern(rightkern))
+        replace=insertnodebefore(replace,replace,fontkern(rightkern))
         done=true
        end
       end
@@ -23384,8 +23886,8 @@
       if trace_spaces then
        report_spaces("%C [%p + %p + %p] %C",prevchar,lnew,old,rnew,nextchar)
       end
-      head=insert_node_before(head,n,italickern(lnew))
-      insert_node_after(head,n,italickern(rnew))
+      head=insertnodebefore(head,n,italickern(lnew))
+      insertnodeafter(head,n,italickern(rnew))
      else
       local new=old+(leftkern+rightkern)*factor
       if trace_spaces then
@@ -23400,7 +23902,7 @@
       if trace_spaces then
        report_spaces("%C [%p + %p]",prevchar,old,new)
       end
-      insert_node_after(head,n,italickern(new)) 
+      insertnodeafter(head,n,italickern(new)) 
      else
       local new=old+leftkern*factor
       if trace_spaces then
@@ -23419,7 +23921,7 @@
      if trace_spaces then
       report_spaces("[%p + %p] %C",old,new,nextchar)
      end
-     insert_node_after(head,n,italickern(new))
+     insertnodeafter(head,n,italickern(new))
     else
      local new=old+rightkern*factor
      if trace_spaces then
@@ -23474,6 +23976,7 @@
 local P,R,S=lpeg.P,lpeg.R,lpeg.S
 local lpegmatch=lpeg.match
 local insert,remove,copy,unpack=table.insert,table.remove,table.copy,table.unpack
+local find=string.find
 local formatters=string.formatters
 local sortedkeys=table.sortedkeys
 local sortedhash=table.sortedhash
@@ -23496,9 +23999,9 @@
 local f_character_y=formatters["%C"]
 local f_character_n=formatters["[ %C ]"]
 local check_duplicates=true 
-local check_soft_hyphen=true 
+local check_soft_hyphen=context 
 directives.register("otf.checksofthyphen",function(v)
- check_soft_hyphen=v
+ check_soft_hyphen=v 
 end)
 local function replaced(list,index,replacement)
  if type(list)=="number" then
@@ -23887,28 +24390,31 @@
   local resources=fontdata.resources
   local duplicates=resources.duplicates
   if check_soft_hyphen then
-   local ds=descriptions[0xAD]
-   if not ds or ds.width==0 then
-    if ds then
+   local dh=descriptions[0x2D]
+   if dh then
+    local ds=descriptions[0xAD]
+    if not ds or ds.width~=dh.width then
      descriptions[0xAD]=nil
-     if trace_unicodes then
-      report_unicodes("patching soft hyphen")
+     if ds then
+      if trace_unicodes then
+       report_unicodes("patching soft hyphen")
+      end
+     else
+      if trace_unicodes then
+       report_unicodes("adding soft hyphen")
+      end
      end
-    else
-     if trace_unicodes then
-      report_unicodes("adding soft hyphen")
+     if not duplicates then
+      duplicates={}
+      resources.duplicates=duplicates
      end
+     local d=duplicates[0x2D]
+     if d then
+      d[0xAD]=true
+     else
+      duplicates[0x2D]={ [0xAD]=true }
+     end
     end
-    if not duplicates then
-     duplicates={}
-     resources.duplicates=duplicates
-    end
-    local dh=duplicates[0x2D]
-    if dh then
-     dh[#dh+1]={ [0xAD]=true }
-    else
-     duplicates[0x2D]={ [0xAD]=true }
-    end
    end
   end
   if duplicates then
@@ -24173,6 +24679,7 @@
  local resources=fontdata.resources
  local zero=glyphs[0]
  local zerocode=zero.unicode
+ local nofglyphs=#glyphs
  if not zerocode then
   zerocode=private
   zero.unicode=zerocode
@@ -24187,7 +24694,7 @@
   indices[0]=zerocode
  end
  if names then
-  for index=1,#glyphs do
+  for index=1,nofglyphs do
    local glyph=glyphs[index]
    local unicode=glyph.unicode 
    if not unicode then
@@ -24220,7 +24727,7 @@
    descriptions[unicode]=glyph
   end
  elseif trace_unicodes then
-  for index=1,#glyphs do
+  for index=1,nofglyphs do
    local glyph=glyphs[index]
    local unicode=glyph.unicode 
    if not unicode then
@@ -24261,7 +24768,7 @@
    descriptions[unicode]=glyph
   end
  else
-  for index=1,#glyphs do
+  for index=1,nofglyphs do
    local glyph=glyphs[index]
    local unicode=glyph.unicode 
    if not unicode then
@@ -24287,30 +24794,46 @@
    descriptions[unicode]=glyph
   end
  end
- for index=1,#glyphs do
-  local math=glyphs[index].math
-  if math then
-   local list=math.vparts
-   if list then
-    for i=1,#list do local l=list[i] l.glyph=indices[l.glyph] end
+ if LUATEXENGINE=="luametatex" then
+  for index=1,nofglyphs do
+   local math=glyphs[index].math
+   if math then
+    local list=math.parts
+    if list then
+     for i=1,#list do local l=list[i] l.glyph=indices[l.glyph] end
+    end
+    local list=math.variants
+    if list then
+     for i=1,#list do list[i]=indices[list[i]] end
+    end
    end
-   local list=math.hparts
-   if list then
-    for i=1,#list do local l=list[i] l.glyph=indices[l.glyph] end
+  end
+ else
+  for index=1,nofglyphs do
+   local math=glyphs[index].math
+   if math then
+    local list=math.vparts
+    if list then
+     for i=1,#list do local l=list[i] l.glyph=indices[l.glyph] end
+    end
+    local list=math.hparts
+    if list then
+     for i=1,#list do local l=list[i] l.glyph=indices[l.glyph] end
+    end
+    local list=math.vvariants
+    if list then
+     for i=1,#list do list[i]=indices[list[i]] end
+    end
+    local list=math.hvariants
+    if list then
+     for i=1,#list do list[i]=indices[list[i]] end
+    end
    end
-   local list=math.vvariants
-   if list then
-    for i=1,#list do list[i]=indices[list[i]] end
-   end
-   local list=math.hvariants
-   if list then
-    for i=1,#list do list[i]=indices[list[i]] end
-   end
   end
  end
  local colorpalettes=resources.colorpalettes
  if colorpalettes then
-  for index=1,#glyphs do
+  for index=1,nofglyphs do
    local colors=glyphs[index].colors
    if colors then
     for i=1,#colors do
@@ -24325,15 +24848,17 @@
  fontdata.names=names
  fontdata.descriptions=descriptions
  fontdata.hashmethod=hashmethod
+ fontdata.nofglyphs=nofglyphs
  return indices,names
 end
-local p_crappyname  do
+local stripredundant  do
  local p_hex=R("af","AF","09")
  local p_digit=R("09")
  local p_done=S("._-")^0+P(-1)
+ local p_style=P(".")
  local p_alpha=R("az","AZ")
  local p_ALPHA=R("AZ")
- p_crappyname=(
+ local p_crappyname=(
   lpeg.utfchartabletopattern({ "uni","u" },true)*S("Xx_")^0*p_hex^1
 +lpeg.utfchartabletopattern({ "identity","glyph","jamo" },true)*p_hex^1
 +lpeg.utfchartabletopattern({ "index","afii" },true)*p_digit^1
@@ -24340,48 +24865,123 @@
 +p_digit*p_hex^3+p_alpha*p_digit^1
 +P("aj")*p_digit^1+P("eh_")*(p_digit^1+p_ALPHA*p_digit^1)+(1-P("_"))^1*P("_uni")*p_hex^1+P("_")*P(1)^1
  )*p_done
-end
-local forcekeep=false 
-directives.register("otf.keepnames",function(v)
- report_cleanup("keeping weird glyph names, expect larger files and more memory usage")
- forcekeep=v
-end)
-local function stripredundant(fontdata)
- local descriptions=fontdata.descriptions
- if descriptions then
-  local n=0
-  local c=0
-  if (not context and fonts.privateoffsets.keepnames) or forcekeep then
-   for unicode,d in next,descriptions do
-    if d.class=="base" then
-     d.class=nil
-     c=c+1
+ if context then
+  local forcekeep=false
+  directives.register("otf.keepnames",function(v)
+   report_cleanup("keeping weird glyph names, expect larger files and more memory usage")
+   forcekeep=v
+  end)
+  local function stripvariants(descriptions,list)
+   local n=list and #list or 0
+   if n>0 then
+    for i=1,n do
+     local g=list[i]
+     if g then
+      local d=descriptions[g]
+      if d and d.name then
+       d.name=nil
+       n=n+1
+      end
+     end
     end
    end
-  else
-   for unicode,d in next,descriptions do
-    local name=d.name
-    if name and lpegmatch(p_crappyname,name) then
-     d.name=nil
-     n=n+1
+   return n
+  end
+  local function stripparts(descriptions,list)
+   local n=list and #list or 0
+   if n>0 then
+    for i=1,n do
+     local g=list[i].glyph
+     if g then
+      local d=descriptions[g]
+      if d and d.name then
+       d.name=nil
+       n=n+1
+      end
+     end
     end
-    if d.class=="base" then
-     d.class=nil
-     c=c+1
+   end
+   return n
+  end
+  local function collectsimple(fontdata)
+   return nil
+  end
+  stripredundant=function(fontdata)
+   local descriptions=fontdata.descriptions
+   if descriptions then
+    local n=0
+    local c=0
+    for unicode,d in next,descriptions do
+     local m=d.math
+     if m then
+      n=n+stripvariants(descriptions,m.vvariants)
+      n=n+stripvariants(descriptions,m.hvariants)
+      n=n+stripparts   (descriptions,m.vparts)
+      n=n+stripparts   (descriptions,m.hparts)
+     end
     end
+    if forcekeep then
+     for unicode,d in next,descriptions do
+      if d.class=="base" then
+       d.class=nil
+       c=c+1
+      end
+     end
+    else
+     local keeplist=collectsimple(fontdata)
+     for unicode,d in next,descriptions do
+      local name=d.name
+      if name then
+       if keeplist and keeplist[name] then
+       elseif lpegmatch(p_crappyname,name) then
+        d.name=nil
+        n=n+1
+       end
+      end
+      if d.class=="base" then
+       d.class=nil
+       c=c+1
+      end
+     end
+    end
+    if trace_cleanup then
+     if n>0 then
+      report_cleanup("%s bogus names removed (verbose unicode)",n)
+     end
+     if c>0 then
+      report_cleanup("%s base class tags removed (default is base)",c)
+     end
+    end
    end
   end
-  if trace_cleanup then
-   if n>0 then
-    report_cleanup("%s bogus names removed (verbose unicode)",n)
+ else
+  stripredundant=function(fontdata)
+   local descriptions=fontdata.descriptions
+   if descriptions then
+    if fonts.privateoffsets.keepnames then
+     for unicode,d in next,descriptions do
+      if d.class=="base" then
+       d.class=nil
+      end
+     end
+    else
+     for unicode,d in next,descriptions do
+      local name=d.name
+      if name then
+       if lpegmatch(p_crappyname,name) then
+        d.name=nil
+       end
+      end
+      if d.class=="base" then
+       d.class=nil
+      end
+     end
+    end
    end
-   if c>0 then
-    report_cleanup("%s base class tags removed (default is base)",c)
-   end
   end
  end
+ readers.stripredundant=stripredundant
 end
-readers.stripredundant=stripredundant
 function readers.getcomponents(fontdata) 
  local resources=fontdata.resources
  if resources then
@@ -24397,6 +24997,10 @@
       local function traverse(p,k,v)
        if k=="ligature" then
         collected[v]={ unpack(l) }
+       elseif tonumber(v) then
+        insert(l,k)
+        collected[v]={ unpack(l) }
+        remove(l)
        else
         insert(l,k)
         for k,vv in next,v do
@@ -24461,8 +25065,7 @@
 function readers.rehash(fontdata,hashmethod) 
  if not (fontdata and fontdata.glyphs) then
   return
- end
- if hashmethod=="indices" then
+ elseif hashmethod=="indices" then
   fontdata.hashmethod="indices"
  elseif hashmethod=="names" then
   fontdata.hashmethod="names"
@@ -24557,7 +25160,6 @@
  end
 end
 local function tabstr_mixed(t) 
- local s={}
  local n=#t
  if n==0 then
   return ""
@@ -24571,6 +25173,7 @@
    return tostring(k) 
   end
  else
+  local s={}
   for i=1,n do
    local k=t[i]
    if k==true then
@@ -25705,6 +26308,28 @@
  end
  return kerned
 end
+local strip_pairs=true
+local compact_pairs=true
+local compact_singles=true
+local merge_pairs=true
+local merge_singles=true
+local merge_substitutions=true
+local merge_alternates=true
+local merge_multiples=true
+local merge_ligatures=true
+local merge_cursives=true
+local merge_marks=true
+directives.register("otf.strip.pairs",function(v) strip_pairs=v end)
+directives.register("otf.compact.pairs",function(v) compact_pairs=v end)
+directives.register("otf.compact.singles",function(v) compact_singles=v end)
+directives.register("otf.merge.pairs",function(v) merge_pairs=v end)
+directives.register("otf.merge.singles",function(v) merge_singles=v end)
+directives.register("otf.merge.substitutions",function(v) merge_substitutions=v end)
+directives.register("otf.merge.alternates",function(v) merge_alternates=v end)
+directives.register("otf.merge.multiples",function(v) merge_multiples=v end)
+directives.register("otf.merge.ligatures",function(v) merge_ligatures=v end)
+directives.register("otf.merge.cursives",function(v) merge_cursives=v end)
+directives.register("otf.merge.marks",function(v) merge_marks=v end)
 local function checkpairs(lookup)
  local steps=lookup.steps
  local nofsteps=lookup.nofsteps
@@ -25752,26 +26377,27 @@
  end
  return kerned
 end
-local compact_pairs=true
-local compact_singles=true
-local merge_pairs=true
-local merge_singles=true
-local merge_substitutions=true
-local merge_alternates=true
-local merge_multiples=true
-local merge_ligatures=true
-local merge_cursives=true
-local merge_marks=true
-directives.register("otf.compact.pairs",function(v) compact_pairs=v end)
-directives.register("otf.compact.singles",function(v) compact_singles=v end)
-directives.register("otf.merge.pairs",function(v) merge_pairs=v end)
-directives.register("otf.merge.singles",function(v) merge_singles=v end)
-directives.register("otf.merge.substitutions",function(v) merge_substitutions=v end)
-directives.register("otf.merge.alternates",function(v) merge_alternates=v end)
-directives.register("otf.merge.multiples",function(v) merge_multiples=v end)
-directives.register("otf.merge.ligatures",function(v) merge_ligatures=v end)
-directives.register("otf.merge.cursives",function(v) merge_cursives=v end)
-directives.register("otf.merge.marks",function(v) merge_marks=v end)
+local function strippairs(lookup)
+ local steps=lookup.steps
+ local nofsteps=lookup.nofsteps
+ local stripped=0
+ for i=1,nofsteps do
+  local step=steps[i]
+  if step.format=="pair" then
+   local coverage=step.coverage
+   for g1,d1 in next,coverage do
+    for g2,d2 in next,d1 do
+     if d2[2] then
+     elseif d2[1]==true then
+      d1[g2]=nil
+      stripped=stripped+1
+     end
+    end
+   end
+  end
+ end
+ return stripped
+end
 function readers.compact(data)
  if not data or data.compacted then
   return
@@ -25779,6 +26405,7 @@
   data.compacted=true
  end
  local resources=data.resources
+ local stripped=0
  local merged=0
  local kerned=0
  local allsteps=0
@@ -25816,6 +26443,9 @@
        kerned=kerned+checkkerns(lookup)
       end
      elseif kind=="gpos_pair" then
+      if strip_pairs then
+       stripped=stripped+strippairs(lookup) 
+      end
       if merge_pairs then
        merged=merged+mergesteps_2(lookup)
       end
@@ -25856,6 +26486,9 @@
  compact("sequences")
  compact("sublookups")
  if trace_optimizations then
+  if stripped>0 then
+   report_optimizations("%i zero positions stripped before merging",stripped)
+  end
   if merged>0 then
    report_optimizations("%i steps of %i removed due to merging",merged,allsteps)
   end
@@ -25864,6 +26497,85 @@
   end
  end
 end
+if CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 then
+ local done=0
+ local function condense_1(k,v,t)
+  if type(v)=="table" then
+   local u=false
+   local l=false
+   for k,v in next,v do
+    if k=="ligature" then
+     l=v
+     if u then
+      break
+     end
+    elseif u then
+     break
+    else
+     u=true
+    end
+   end
+   if l and not u then
+    t[k]=l
+    done=done+1
+   end
+   if u then
+    for k,vv in next,v do
+     if k~="ligature" then
+      condense_1(k,vv,v)
+     end
+    end
+   end
+  end
+ end
+ local function condensesteps_1(lookup)
+  done=0
+  if lookup.type=="gsub_ligature" then
+   local steps=lookup.steps
+   if steps then
+    for i=1,#steps do
+     local step=steps[i]
+     local coverage=step.coverage
+     if coverage then
+      for k,v in next,coverage do
+       if condense_1(k,v,coverage) then
+        coverage[k]=v.ligature
+        done=done+1
+       end
+      end
+     end
+    end
+   end
+  end
+  return done
+ end
+ function readers.condense(data)
+  if not data or data.condensed then
+   return
+  else
+   data.condensed=true
+  end
+  local resources=data.resources
+  local condensed=0
+  local function condense(what)
+   local lookups=resources[what]
+   if lookups then
+    for i=1,#lookups do
+     condensed=condensed+condensesteps_1(lookups[i])
+    end
+   elseif trace_optimizations then
+    report_optimizations("no lookups in %a",what)
+   end
+  end
+  condense("sequences")
+  condense("sublookups")
+  if trace_optimizations then
+   if condensed>0 then
+    report_optimizations("%i ligatures condensed",condensed)
+   end
+  end
+ end
+end
 local function mergesteps(t,k)
  if k=="merged" then
   local merged={}
@@ -25968,7 +26680,7 @@
    end
   end
  end
- local function expandlookups(sequences)
+ local function expandlookups(sequences,whatever)
   if sequences then
    for i=1,#sequences do
     local sequence=sequences[i]
@@ -26041,6 +26753,9 @@
           if lookups then
            for k,v in next,lookups do 
             local lookup=sublookups[v]
+if not lookup and whatever then
+ lookup=whatever[v]
+end
             if lookup then
              lookups[k]=lookup
              if not subtype then
@@ -26099,7 +26814,7 @@
   end
  end
  expandlookups(sequences)
- expandlookups(sublookups)
+ expandlookups(sublookups,sequences)
 end
 
 end -- closure
@@ -26134,7 +26849,7 @@
 local getsubtype=nuts.getsubtype
 local getchar=nuts.getchar
 local ischar=nuts.ischar
-local end_of_math=nuts.end_of_math
+local endofmath=nuts.endofmath
 local nodecodes=nodes.nodecodes
 local disc_code=nodecodes.disc
 local math_code=nodecodes.math
@@ -26240,7 +26955,7 @@
    end
    first,last,n=nil,nil,0
    if id==math_code then
-    current=end_of_math(current)
+    current=endofmath(current)
    end
   elseif id==disc_code then
    setstate(current,s_medi)
@@ -26253,7 +26968,7 @@
    end
    first,last,n=nil,nil,0
    if id==math_code then
-    current=end_of_math(current)
+    current=endofmath(current)
    end
   end
   current=getnext(current)
@@ -26469,7 +27184,7 @@
     first=nil
    end
    if id==math_code then 
-    current=end_of_math(current)
+    current=endofmath(current)
    end
   end
   current=getnext(current)
@@ -26583,7 +27298,7 @@
      wrapup()
     end
     if id==math_code then 
-     current=end_of_math(current)
+     current=endofmath(current)
     end
    end
    current=getnext(current)
@@ -26642,12 +27357,9 @@
 local trace_testruns=false  registertracker("otf.testruns",function(v) trace_testruns=v end)
 local forcediscretionaries=false
 local forcepairadvance=false 
-directives.register("otf.forcediscretionaries",function(v)
- forcediscretionaries=v
-end)
-directives.register("otf.forcepairadvance",function(v)
- forcepairadvance=v
-end)
+local repeatablemultiples=context or false
+directives.register("otf.forcediscretionaries",function(v) forcediscretionaries=v end)
+directives.register("otf.forcepairadvance",function(v) forcepairadvance=v end)
 local report_direct=logs.reporter("fonts","otf direct")
 local report_subchain=logs.reporter("fonts","otf subchain")
 local report_chain=logs.reporter("fonts","otf chain")
@@ -26669,7 +27381,6 @@
 local getid=nuts.getid
 local getstate=nuts.getstate
 local getsubtype=nuts.getsubtype
-local setsubtype=nuts.setsubtype
 local getchar=nuts.getchar
 local setchar=nuts.setchar
 local getdisc=nuts.getdisc
@@ -26679,29 +27390,29 @@
 local getwidth=nuts.getwidth
 local getattr=nuts.getattr
 local getglyphdata=nuts.getglyphdata
-local copy_no_components=nuts.copy_no_components
-local copy_only_glyphs=nuts.copy_only_glyphs
-local count_components=nuts.count_components
-local set_components=nuts.set_components
-local get_components=nuts.get_components
-local flush_components=nuts.flush_components
+local components=nuts.components
+local copynocomponents=components.copynocomponents
+local copyonlyglyphs=components.copyonlyglyphs
+local countcomponents=components.count
+local setcomponents=components.set
+local getcomponents=components.get
+local flushcomponents=components.flush
 local ischar=nuts.ischar
-local usesfont=nuts.uses_font
-local insert_node_after=nuts.insert_after
-local copy_node=nuts.copy
-local copy_node_list=nuts.copy_list
-local remove_node=nuts.remove
-local find_node_tail=nuts.tail
-local flush_node_list=nuts.flush_list
-local flush_node=nuts.flush_node
-local end_of_math=nuts.end_of_math
-local start_of_par=nuts.start_of_par
+local usesfont=nuts.usesfont
+local insertnodeafter=nuts.insertafter
+local copynode=nuts.copy
+local copynodelist=nuts.copylist
+local removenode=nuts.remove
+local findnodetail=nuts.tail
+local flushnodelist=nuts.flushlist
+local flushnode=nuts.flushnode
+local endofmath=nuts.endofmath
+local startofpar=nuts.startofpar
 local setmetatable=setmetatable
 local setmetatableindex=table.setmetatableindex
 local nextnode=nuts.traversers.node
 local nodecodes=nodes.nodecodes
 local glyphcodes=nodes.glyphcodes
-local disccodes=nodes.disccodes
 local glyph_code=nodecodes.glyph
 local glue_code=nodecodes.glue
 local disc_code=nodecodes.disc
@@ -26710,8 +27421,7 @@
 local par_code=nodecodes.par
 local lefttoright_code=nodes.dirvalues.lefttoright
 local righttoleft_code=nodes.dirvalues.righttoleft
-local discretionarydisc_code=disccodes.discretionary
-local ligatureglyph_code=glyphcodes.ligature
+local discretionarydisc_code=nodes.disccodes.discretionary
 local a_noligature=attributes.private("noligature")
 local injections=nodes.injections
 local setmark=injections.setmark
@@ -26802,7 +27512,7 @@
   return "no valid dataset"
  end
  local merged=sequence.merged and "merged " or ""
- if index then
+ if index and index>1 then
   return formatters["feature %a, type %a, %schain lookup %a, index %a"](
    dataset[4],sequence.type,merged,sequence.name,index)
  else
@@ -26826,12 +27536,12 @@
  local prev,next=getboth(disc)
  local ishead=head==disc
  setdisc(disc)
- flush_node(disc)
+ flushnode(disc)
  if pre then
-  flush_node_list(pre)
+  flushnodelist(pre)
  end
  if post then
-  flush_node_list(post)
+  flushnodelist(post)
  end
  if ishead then
   if replace then
@@ -26859,7 +27569,7 @@
 local function appenddisc(disc,list)
  local pre,post,replace,pretail,posttail,replacetail=getdisc(disc,true)
  local posthead=list
- local replacehead=copy_node_list(list)
+ local replacehead=copynodelist(list)
  if post then
   setlink(posttail,posthead)
  else
@@ -26880,16 +27590,15 @@
   local next=getnext(stop)
   setprev(start)
   setnext(stop)
-  local base=copy_no_components(start,copyinjection)
+  local base=copynocomponents(start,copyinjection)
   if head==start then
    head=base
   end
   resetinjection(base)
   setchar(base,char)
-  setsubtype(base,ligatureglyph_code)
-  set_components(base,start)
+  setcomponents(base,start)
   setlink(prev,base,next)
-  flush_components(start)
+  flushcomponents(start)
   return head,base
  end
 end
@@ -26897,7 +27606,7 @@
 local no_right_ligature_code=2
 local no_left_kern_code=4
 local no_right_kern_code=8
-local has_glyph_option=node.direct.has_glyph_option or function(n,c)
+local hasglyphoption=function(n,c)
  if c==no_left_ligature_code or c==no_right_ligature_code then
   return getattr(n,a_noligature)==1
  else
@@ -26905,7 +27614,7 @@
  end
 end
 local function toligature(head,start,stop,char,dataset,sequence,skiphash,discfound,hasmarks) 
- if has_glyph_option(start,no_right_ligature_code) then
+ if hasglyphoption(start,no_right_ligature_code) then
   return head,start
  end
  if start==stop and getchar(start)==char then
@@ -26918,14 +27627,13 @@
  local comp=start
  setprev(start)
  setnext(stop)
- local base=copy_no_components(start,copyinjection)
+ local base=copynocomponents(start,copyinjection)
  if start==head then
   head=base
  end
  resetinjection(base)
  setchar(base,char)
- setsubtype(base,ligatureglyph_code)
- set_components(base,comp)
+ setcomponents(base,comp)
  setlink(prev,base,next)
  if not discfound then
   local deletemarks=not skiphash or hasmarks
@@ -26938,15 +27646,15 @@
    local char=getchar(start)
    if not marks[char] then
     baseindex=baseindex+componentindex
-    componentindex=count_components(start,marks)
+    componentindex=countcomponents(start,marks)
    elseif not deletemarks then
     setligaindex(start,baseindex+getligaindex(start,componentindex))
     if trace_marks then
      logwarning("%s: keep ligature mark %s, gets index %s",pref(dataset,sequence),gref(char),getligaindex(start))
     end
-    local n=copy_node(start)
+    local n=copynode(start)
     copyinjection(n,start) 
-    head,current=insert_node_after(head,current,n) 
+    head,current=insertnodeafter(head,current,n) 
    elseif trace_marks then
     logwarning("%s: delete ligature mark %s",pref(dataset,sequence),gref(char))
    end
@@ -26969,7 +27677,7 @@
     break
    end
   end
-  flush_components(components)
+  flushcomponents(components)
  else
   local discprev,discnext=getboth(discfound)
   if discprev and discnext then
@@ -26976,7 +27684,7 @@
    local pre,post,replace,pretail,posttail,replacetail=getdisc(discfound,true)
    if not replace then
     local prev=getprev(base)
-    local copied=copy_only_glyphs(comp)
+    local copied=copyonlyglyphs(comp)
     if pre then
      setlink(discprev,pre)
     else
@@ -26992,7 +27700,7 @@
     end
     setlink(prev,discfound,next)
     setboth(base)
-    set_components(base,copied)
+    setcomponents(base,copied)
     replace=base
     if forcediscretionaries then
      setdisc(discfound,pre,post,replace,discretionarydisc_code)
@@ -27008,28 +27716,60 @@
 local function multiple_glyphs(head,start,multiple,skiphash,what,stop) 
  local nofmultiples=#multiple
  if nofmultiples>0 then
+  local first=start
   resetinjection(start)
   setchar(start,multiple[1])
   if nofmultiples>1 then
-   local sn=getnext(start)
-   for k=2,nofmultiples do
-    local n=copy_node(start) 
+   for i=2,nofmultiples do
+    local n=copynode(start) 
     resetinjection(n)
-    setchar(n,multiple[k])
-    insert_node_after(head,start,n)
+    setchar(n,multiple[i])
+    insertnodeafter(head,start,n)
     start=n
    end
-   if what==true then
-   elseif what>1 then
-    local m=multiple[nofmultiples]
-    for i=2,what do
-     local n=copy_node(start) 
-     resetinjection(n)
-     setchar(n,m)
-     insert_node_after(head,start,n)
-     start=n
+  end
+  if what~=true and repeatablemultiples then
+   local kind=type(what)
+   local m,f,l
+   if kind=="string" then
+    local what,n=string.match(what,"^repeat(.-)[:=](%d+)$")
+    if what=="middle" then
+     m=tonumber(n)
+    elseif what=="first" then
+     f=tonumber(n)
+    elseif what=="last" then
+     l=tonumber(n)
     end
+   elseif kind=="table" then
+      m=what.middle
+      f=what.first
+      l=what.last
    end
+   if f or m or l then
+    if m and m>1 and nofmultiples==3 then
+     local middle=getnext(first)
+     for i=2,m do
+      local n=copynode(middle) 
+      resetinjection(n)
+      insertnodeafter(head,first,n)
+     end
+    end
+    if f and f>1 then
+     for i=2,f do
+      local n=copynode(first) 
+      resetinjection(n)
+      insertnodeafter(head,first,n)
+     end
+    end
+    if l and l>1 then
+     for i=2,l do
+      local n=copynode(start) 
+      resetinjection(n)
+      insertnodeafter(head,start,n)
+      start=n
+     end
+    end
+   end
   end
   return head,start,true
  else
@@ -27115,7 +27855,7 @@
   while current do
    local char=ischar(current,currentfont)
    if char then
-    local lg=ligature[char]
+    local lg=not tonumber(ligature) and ligature[char]
     if lg then
      stop=current
      ligature=lg
@@ -27128,14 +27868,14 @@
    end
   end
   if stop then
-   local lig=ligature.ligature
-   if lig then
+   local ligature=tonumber(ligature) or ligature.ligature
+   if ligature then
     if trace_ligatures then
      local stopchar=getchar(stop)
-     head,start=markstoligature(head,start,stop,lig)
+     head,start=markstoligature(head,start,stop,ligature)
      logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(getchar(start)))
     else
-     head,start=markstoligature(head,start,stop,lig)
+     head,start=markstoligature(head,start,stop,ligature)
     end
     return head,start,true,false
    else
@@ -27150,7 +27890,7 @@
     if skiphash and skiphash[char] then
      current=getnext(current)
     else
-     local lg=ligature[char]
+     local lg=not tonumber(ligature) and ligature[char]
      if lg then
       if marks[char] then
        hasmarks=true
@@ -27176,13 +27916,13 @@
    local match
    if replace then
     local char=ischar(replace,currentfont)
-    if char and ligature[char] then
+    if char and (not tonumber(ligature) and ligature[char]) then
      match=true
     end
    end
    if not match and pre then
     local char=ischar(pre,currentfont)
-    if char and ligature[char] then
+    if char and (not tonumber(ligature) and ligature[char]) then
      match=true
     end
    end
@@ -27189,7 +27929,7 @@
    if not match and not pre or not replace then
     local n=getnext(discfound)
     local char=ischar(n,currentfont)
-    if char and ligature[char] then
+    if char and (not tonumber(ligature) and ligature[char]) then
      match=true
     end
    end
@@ -27198,9 +27938,9 @@
     local prev=getprev(start)
     if stop then
      setnext(stop)
-     local copy=copy_node_list(start)
+     local copy=copynodelist(start)
      local tail=stop 
-     local liat=find_node_tail(copy)
+     local liat=findnodetail(copy)
      if pre then
       setlink(liat,pre)
      end
@@ -27211,7 +27951,7 @@
      replace=start
     else
      setnext(start)
-     local copy=copy_node(start)
+     local copy=copynode(start)
      if pre then
       setlink(copy,pre)
      end
@@ -27232,21 +27972,21 @@
     return head,start,true,true
    end
   end
-  local lig=ligature.ligature
-  if lig then
+  local ligature=tonumber(ligature) or ligature.ligature
+  if ligature then
    if stop then
     if trace_ligatures then
      local stopchar=getchar(stop)
-     head,start=toligature(head,start,stop,lig,dataset,sequence,skiphash,false,hasmarks)
-     logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(lig))
+     head,start=toligature(head,start,stop,ligature,dataset,sequence,skiphash,false,hasmarks)
+     logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(ligature))
     else
-     head,start=toligature(head,start,stop,lig,dataset,sequence,skiphash,false,hasmarks)
+     head,start=toligature(head,start,stop,ligature,dataset,sequence,skiphash,false,hasmarks)
     end
    else
     resetinjection(start)
-    setchar(start,lig)
+    setchar(start,ligature)
     if trace_ligatures then
-     logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(dataset,sequence),gref(startchar),gref(lig))
+     logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(dataset,sequence),gref(startchar),gref(ligature))
     end
    end
    return head,start,true,false
@@ -27256,7 +27996,7 @@
  return head,start,false,false
 end
 function handlers.gpos_single(head,start,dataset,sequence,kerns,rlmode,skiphash,step,injection)
- if has_glyph_option(start,no_right_kern_code) then
+ if hasglyphoption(start,no_right_kern_code) then
   return head,start,false
  else
   local startchar=getchar(start)
@@ -27276,7 +28016,7 @@
  end
 end
 function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,skiphash,step,injection)
- if has_glyph_option(start,no_right_kern_code) then
+ if hasglyphoption(start,no_right_kern_code) then
   return head,start,false
  else
   local snext=getnext(start)
@@ -27441,7 +28181,7 @@
       end
      end
     elseif trace_bugs then
-     onetimemessage(currentfont,basechar,"no base anchors",report_fonts)
+     onetimemessage(currentfont,basechar,"no base anchors")
     end
    elseif trace_bugs then
     logwarning("%s: prev node is no char, case %i",pref(dataset,sequence),1)
@@ -27588,7 +28328,7 @@
  if trace_chains then
   logprocess("%s: removing character %s",cref(dataset,sequence,chainindex),gref(getchar(start)))
  end
- head,start=remove_node(head,start,true)
+ head,start=removenode(head,start,true)
  return head,getprev(start),true
 end
 function chainprocs.gsub_single(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
@@ -27720,7 +28460,7 @@
      if skiphash and skiphash[schar] then
        current=getnext(current)
      else
-      local lg=ligatures[schar]
+      local lg=not tonumber(ligatures) and ligatures[schar]
       if lg then
        ligatures=lg
        last=current
@@ -27739,7 +28479,7 @@
      end
     end
    end
-   local ligature=ligatures.ligature
+   local ligature=tonumber(ligatures) or ligatures.ligature
    if ligature then
     if chainindex then
      stop=last
@@ -27765,7 +28505,7 @@
  return head,start,false,0,false
 end
 function chainprocs.gpos_single(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
- if not has_glyph_option(start,no_right_kern_code) then
+ if not hasglyphoption(start,no_right_kern_code) then
   local mapping=currentlookup.mapping
   if mapping==nil then
    mapping=getmapping(dataset,sequence,currentlookup)
@@ -27793,7 +28533,7 @@
  return head,start,false
 end
 function chainprocs.gpos_pair(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash,chainindex)
- if not has_glyph_option(start,no_right_kern_code) then
+ if not hasglyphoption(start,no_right_kern_code) then
   local mapping=currentlookup.mapping
   if mapping==nil then
    mapping=getmapping(dataset,sequence,currentlookup)
@@ -28081,7 +28821,7 @@
         end
        end
       elseif trace_bugs then
-       onetimemessage(currentfont,startchar,"no entry anchors",report_fonts)
+       onetimemessage(currentfont,startchar,"no entry anchors")
       end
       break
      end
@@ -28101,7 +28841,7 @@
  local thekern=nuts.new("kern",1) 
  local setkern=nuts.setkern    
  userkern=function(k)
-  local n=copy_node(thekern)
+  local n=copynode(thekern)
   setkern(n,k)
   return n
  end
@@ -28116,13 +28856,13 @@
     if next then
      setlink(kern,next)
     end
-    flush_node(current)
+    flushnode(current)
     head=kern
     current=next
    else
     local prev,next=getboth(current)
     setlink(prev,kern,next)
-    flush_node(current)
+    flushnode(current)
     current=next
    end
   else
@@ -28138,7 +28878,7 @@
  setdisc(d,pre,post,replace)
 end
 local noflags={ false,false,false,false }
-local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck)
+local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck,where)
  local size=ck[5]-ck[4]+1
  local chainlookups=ck[6]
  local done=false
@@ -28145,19 +28885,26 @@
  if chainlookups then
   if size==1 then
    local chainlookup=chainlookups[1]
-   for j=1,#chainlookup do
-    local chainstep=chainlookup[j]
-    local chainkind=chainstep.type
-    local chainproc=chainprocs[chainkind]
-    if chainproc then
-     local ok
-     head,start,ok=chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash)
-     if ok then
-      done=true
+   if chainlookup then
+    for j=1,#chainlookup do
+     local chainstep=chainlookup[j]
+     if chainstep then
+      local chainkind=chainstep.type
+      local chainproc=chainprocs[chainkind]
+      if chainproc then
+       local ok
+       head,start,ok=chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash,1)
+       if ok then
+        done=true
+       end
+      else
+       logprocess("%s: %s is not yet supported (1)",cref(dataset,sequence),chainkind)
+      end
+     else
+      logprocess("%s: has an issue (1)",cref(dataset,sequence))
      end
-    else
-     logprocess("%s: %s is not yet supported (1)",cref(dataset,sequence),chainkind)
     end
+   else
    end
    else
    local i=1
@@ -28182,20 +28929,24 @@
     if chainlookup then
      for j=1,#chainlookup do
       local chainstep=chainlookup[j]
-      local chainkind=chainstep.type
-      local chainproc=chainprocs[chainkind]
-      if chainproc then
-       local ok,n
-       head,start,ok,n=chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash,i)
-       if ok then
-        done=true
-        if n and n>1 and i+n>nofchainlookups then
-         i=size 
-         break
+      if chainstep then
+       local chainkind=chainstep.type
+       local chainproc=chainprocs[chainkind]
+       if chainproc then
+        local ok,n
+        head,start,ok,n=chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash,i)
+        if ok then
+         done=true
+         if n and n>1 and i+n>nofchainlookups then
+          i=size 
+          break
+         end
         end
+       else
+        logprocess("%s: %s is not yet supported (2)",cref(dataset,sequence),chainkind)
        end
       else
-       logprocess("%s: %s is not yet supported (2)",cref(dataset,sequence),chainkind)
+       logprocess("%s: has an issue (2)",cref(dataset,sequence))
       end
      end
     else
@@ -28219,7 +28970,7 @@
   else
    done=true
    if trace_contexts then
-    logprocess("%s: skipping match",cref(dataset,sequence))
+    logprocess("%s: skipping match @ %i",cref(dataset,sequence),where)
    end
   end
  end
@@ -28246,6 +28997,8 @@
  local last=start
  local prev=getprev(start)
  local hasglue=false
+ local useddisc=nil   
+ local usedstart=start
  local i=f
  while i<=l do
   local id=getid(current)
@@ -28308,7 +29061,7 @@
     tail=prev
     setprev(current,sweepnode)
    else
-    tail=find_node_tail(head)
+    tail=findnodetail(head)
    end
    setnext(sweepnode,current)
    setprev(head)
@@ -28419,13 +29172,13 @@
    head=lookaheaddisc
   end
   local pre,post,replace=getdisc(lookaheaddisc)
-  local new=copy_node_list(cf) 
+  local new=copynodelist(cf) 
   local cnew=new
   if pre then
-   setlink(find_node_tail(cf),pre)
+   setlink(findnodetail(cf),pre)
   end
   if replace then
-   local tail=find_node_tail(new)
+   local tail=findnodetail(new)
    setlink(tail,replace)
   end
   for i=1,insertedmarks do
@@ -28439,7 +29192,7 @@
   end
   if not notmatchpre[lookaheaddisc] then
    local ok=false
-   cf,start,ok=chainrun(cf,start,cl,dataset,sequence,rlmode,skiphash,ck)
+   cf,start,ok=chainrun(cf,start,cl,dataset,sequence,rlmode,skiphash,ck,1)
    if ok then
     done=true
    end
@@ -28446,7 +29199,7 @@
   end
   if not notmatchreplace[lookaheaddisc] then
    local ok=false
-   new,cnew,ok=chainrun(new,cnew,clast,dataset,sequence,rlmode,skiphash,ck)
+   new,cnew,ok=chainrun(new,cnew,clast,dataset,sequence,rlmode,skiphash,ck,2)
    if ok then
     done=true
    end
@@ -28457,6 +29210,7 @@
    setdisc(lookaheaddisc,cf,post,new)
   end
   start=getprev(lookaheaddisc)
+  useddisc=lookaheaddisc 
   sweephead[cf]=getnext(clast) or false
   sweephead[new]=getnext(cl) or false
  elseif backtrackdisc then
@@ -28478,8 +29232,8 @@
   setprev(cf)
   setnext(cl)
   local pre,post,replace,pretail,posttail,replacetail=getdisc(backtrackdisc,true)
-  local new=copy_node_list(cf)
-  local cnew=find_node_tail(new)
+  local new=copynodelist(cf)
+  local cnew=findnodetail(new)
   for i=1,insertedmarks do
    cnew=getprev(cnew)
   end
@@ -28489,7 +29243,7 @@
   end
   if not notmatchpost[backtrackdisc] then
    local ok=false
-   cf,start,ok=chainrun(cf,start,last,dataset,sequence,rlmode,skiphash,ck)
+   cf,start,ok=chainrun(cf,start,last,dataset,sequence,rlmode,skiphash,ck,3)
    if ok then
     done=true
    end
@@ -28496,7 +29250,7 @@
   end
   if not notmatchreplace[backtrackdisc] then
    local ok=false
-   new,cnew,ok=chainrun(new,cnew,clast,dataset,sequence,rlmode,skiphash,ck)
+   new,cnew,ok=chainrun(new,cnew,clast,dataset,sequence,rlmode,skiphash,ck,4)
    if ok then
     done=true
    end
@@ -28517,30 +29271,55 @@
    setdisc(backtrackdisc,pre,post,replace)
   end
   start=getprev(backtrackdisc)
+  useddisc=backtrackdisc 
   sweephead[post]=getnext(clast) or false
   sweephead[replace]=getnext(last) or false
  else
   local ok=false
-  head,start,ok=chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck)
+  head,start,ok=chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck,5)
   if ok then
    done=true
   end
  end
- return head,start,done
+ if useddisc and start~=usedstart then 
+    start=getnext(start)           
+ end                  
+ return head,start,done,useddisc           
 end
-local function chaintrac(head,start,dataset,sequence,rlmode,skiphash,ck,match,discseen,sweepnode)
- local rule=ck[1]
- local lookuptype=ck[8] or ck[2]
- local nofseq=#ck[3]
- local first=ck[4]
- local last=ck[5]
- local char=getchar(start)
- logwarning("%s: rule %s %s at char %s for (%s,%s,%s) chars, lookuptype %a, %sdisc seen, %ssweeping",
-  cref(dataset,sequence),rule,match and "matches" or "nomatch",
-  gref(char),first-1,last-first+1,nofseq-last,lookuptype,
-  discseen and "" or "no ",sweepnode and "" or "not ")
+local chaintrac do
+ local level=0
+ local last={}
+ chaintrac=function(head,start,dataset,sequence,rlmode,skiphash,ck,match,discseen,sweepnode)
+  if dataset then
+   level=level+1
+   last[level]=start
+   local rule=ck[1]
+   local lookuptype=ck[8] or ck[2]
+   local nofseq=#ck[3] 
+   local first=ck[4]
+   local last=ck[5]
+   local char=getchar(start)
+   logwarning("+ %i : %s: rule %s %s at char %s for (%s,%s,%s) chars, lookuptype %a, %sdisc seen, %ssweeping",
+    level,cref(dataset,sequence),rule,match and "matches" or "nomatch",
+    gref(char),first-1,last-first+1,nofseq-last,lookuptype,
+    discseen and "" or "no ",sweepnode and "" or "not ")
+  else
+   local what=start and "done" or "continue"
+   local where=head==last[level] and "same" or "different"
+   local char=getchar(head)
+   if char then
+    logwarning("- %i : %s at char %s, %s node",level,what,gref(char),where)
+   else
+    logwarning("- %i : %s, %s node",level,what,where)
+   end
+   level=level-1
+  end
+ end
 end
 local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,skiphash)
+ if not contexts then
+  return head,start,false
+ end
  local sweepnode=sweepnode
  local sweeptype=sweeptype
  local postreplace
@@ -28568,129 +29347,243 @@
   local ck=contexts[k]
   local seq=ck[3]
   local f=ck[4] 
+  local last=start
   if not startchar or not seq[f][startchar] then
    goto next
   end
   local s=seq.n 
-  local l=ck[5] 
-  local current=start
-  local last=start
   if s==1 then
-   goto next
-  end
-  if l>f then
-   local discfound 
-   local n=f+1
-   last=startnext 
-   while n<=l do
-    if postreplace and not last then
-     last=getnext(sweepnode)
-     sweeptype=nil
-    end
-    if last then
-     local char,id=ischar(last,currentfont)
-     if char then
-      if skiphash and skiphash[char] then
-       skipped=true
-       if trace_skips then
-        show_skip(dataset,sequence,char,ck,classes[char])
-       end
-       last=getnext(last)
-      elseif seq[n][char] then
-       if n<l then
+  else
+   local l=ck[5] 
+   local current=start
+   if l>f then
+    local discfound 
+    local n=f+1
+    last=startnext 
+    while n<=l do
+     if postreplace and not last then
+      last=getnext(sweepnode)
+      sweeptype=nil
+     end
+     if last then
+      local char,id=ischar(last,currentfont)
+      if char then
+       if skiphash and skiphash[char] then
+        skipped=true
+        if trace_skips then
+         show_skip(dataset,sequence,char,ck,classes[char])
+        end
         last=getnext(last)
+       elseif seq[n][char] then
+        if n<l then
+         last=getnext(last)
+        end
+        n=n+1
+       elseif discfound then
+        notmatchreplace[discfound]=true
+        if notmatchpre[discfound] then
+         goto next
+        else
+         break
+        end
+       else
+        goto next
        end
-       n=n+1
-      elseif discfound then
-       notmatchreplace[discfound]=true
-       if notmatchpre[discfound] then
-        goto next
+      elseif char==false then
+       if discfound then
+        notmatchreplace[discfound]=true
+        if notmatchpre[discfound] then
+         goto next
+        else
+         break
+        end
        else
-        break
-       end
-      else
-       goto next
-      end
-     elseif char==false then
-      if discfound then
-       notmatchreplace[discfound]=true
-       if notmatchpre[discfound] then
         goto next
-       else
-        break
        end
-      else
-       goto next
-      end
-     elseif id==disc_code then
-      discseen=true
-      discfound=last
-      notmatchpre[last]=nil
-      notmatchpost[last]=true
-      notmatchreplace[last]=nil
-      local pre,post,replace=getdisc(last)
-      if pre then
-       local n=n
-       while pre do
-        if seq[n][getchar(pre)] then
-         n=n+1
-         if n>l then
+      elseif id==disc_code then
+       discseen=true
+       discfound=last
+       notmatchpre[last]=nil
+       notmatchpost[last]=true
+       notmatchreplace[last]=nil
+       local pre,post,replace=getdisc(last)
+       if pre then
+        local n=n
+        while pre do
+         if seq[n][getchar(pre)] then
+          n=n+1
+          if n>l then
+           break
+          end
+          pre=getnext(pre)
+         else
+          notmatchpre[last]=true
           break
          end
-         pre=getnext(pre)
-        else
+        end
+        if n<=l then
          notmatchpre[last]=true
-         break
         end
-       end
-       if n<=l then
+       else
         notmatchpre[last]=true
        end
+       if replace then
+        while replace do
+         if seq[n][getchar(replace)] then
+          n=n+1
+          if n>l then
+           break
+          end
+          replace=getnext(replace)
+         else
+          notmatchreplace[last]=true
+          if notmatchpre[last] then
+           goto next
+          else
+           break
+          end
+         end
+        end
+        if notmatchpre[last] then
+         goto next
+        end
+       end
+       last=getnext(last)
       else
-       notmatchpre[last]=true
+       goto next
       end
-      if replace then
-       while replace do
-        if seq[n][getchar(replace)] then
-         n=n+1
-         if n>l then
-          break
+     else
+      goto next
+     end
+    end
+   end
+   if f>1 then
+     local prev=startprev
+     if prereplace and prev==checkdisc then
+      prev=getprev(sweepnode)
+     end
+     if prev then
+      local discfound 
+      local n=f-1
+      while n>=1 do
+       if prev then
+        local char,id=ischar(prev,currentfont)
+        if char then
+         if skiphash and skiphash[char] then
+          skipped=true
+          if trace_skips then
+           show_skip(dataset,sequence,char,ck,classes[char])
+          end
+          prev=getprev(prev)
+         elseif seq[n][char] then
+          if n>1 then
+           prev=getprev(prev)
+          end
+          n=n-1
+         elseif discfound then
+          notmatchreplace[discfound]=true
+          if notmatchpost[discfound] then
+           goto next
+          else
+           break
+          end
+         else
+          goto next
          end
-         replace=getnext(replace)
-        else
-         notmatchreplace[last]=true
-         if notmatchpre[last] then
+        elseif char==false then
+         if discfound then
+          notmatchreplace[discfound]=true
+          if notmatchpost[discfound] then
+           goto next
+          end
+         else
           goto next
+         end
+         break
+        elseif id==disc_code then
+         discseen=true
+         discfound=prev
+         notmatchpre[prev]=true
+         notmatchpost[prev]=nil
+         notmatchreplace[prev]=nil
+         local pre,post,replace,pretail,posttail,replacetail=getdisc(prev,true)
+         if pre~=start and post~=start and replace~=start then
+          if post then
+           local n=n
+           while posttail do
+            if seq[n][getchar(posttail)] then
+             n=n-1
+             if posttail==post or n<1 then
+              break
+             else
+              posttail=getprev(posttail)
+             end
+            else
+             notmatchpost[prev]=true
+             break
+            end
+           end
+           if n>=1 then
+            notmatchpost[prev]=true
+           end
+          else
+           notmatchpost[prev]=true
+          end
+          if replace then
+           while replacetail do
+            if seq[n][getchar(replacetail)] then
+             n=n-1
+             if replacetail==replace or n<1 then
+              break
+             else
+              replacetail=getprev(replacetail)
+             end
+            else
+             notmatchreplace[prev]=true
+             if notmatchpost[prev] then
+              goto next
+             else
+              break
+             end
+            end
+           end
+          else
+          end
+         end
+         prev=getprev(prev)
+        elseif id==glue_code then
+         local sn=seq[n]
+         if (sn[32] and spaces[prev]) or sn[0xFFFC] then
+          n=n-1
+          prev=getprev(prev)
          else
-          break
+          goto next
          end
+        elseif seq[n][0xFFFC] then
+         n=n-1
+         prev=getprev(prev)
+        else
+         goto next
         end
-       end
-       if notmatchpre[last] then
+       else
         goto next
        end
       end
-      last=getnext(last)
      else
       goto next
      end
-    else
-     goto next
-    end
    end
-  end
-  if f>1 then
-   if startprev then
-    local prev=startprev
-    if prereplace and prev==checkdisc then
-     prev=getprev(sweepnode)
+   if s>l then
+    local current=last and getnext(last)
+    if not current and postreplace then
+     current=getnext(sweepnode)
     end
-    if prev then
+    if current then
      local discfound 
-     local n=f-1
-     while n>=1 do
-      if prev then
-       local char,id=ischar(prev,currentfont)
+     local n=l+1
+     while n<=s do
+      if current then
+       local char,id=ischar(current,currentfont)
        if char then
         if skiphash and skiphash[char] then
          skipped=true
@@ -28697,15 +29590,15 @@
          if trace_skips then
           show_skip(dataset,sequence,char,ck,classes[char])
          end
-         prev=getprev(prev)
+         current=getnext(current) 
         elseif seq[n][char] then
-         if n>1 then
-          prev=getprev(prev)
+         if n<s then 
+          current=getnext(current) 
          end
-         n=n-1
+         n=n+1
         elseif discfound then
          notmatchreplace[discfound]=true
-         if notmatchpost[discfound] then
+         if notmatchpre[discfound] then
           goto next
          else
           break
@@ -28716,75 +29609,74 @@
        elseif char==false then
         if discfound then
          notmatchreplace[discfound]=true
-         if notmatchpost[discfound] then
+         if notmatchpre[discfound] then
           goto next
+         else
+          break
          end
         else
          goto next
         end
-        break
        elseif id==disc_code then
         discseen=true
-        discfound=prev
-        notmatchpre[prev]=true
-        notmatchpost[prev]=nil
-        notmatchreplace[prev]=nil
-        local pre,post,replace,pretail,posttail,replacetail=getdisc(prev,true)
-        if pre~=start and post~=start and replace~=start then
-         if post then
-          local n=n
-          while posttail do
-           if seq[n][getchar(posttail)] then
-            n=n-1
-            if posttail==post or n<1 then
-             break
-            else
-             posttail=getprev(posttail)
-            end
+        discfound=current
+        notmatchpre[current]=nil
+        notmatchpost[current]=true
+        notmatchreplace[current]=nil
+        local pre,post,replace=getdisc(current)
+        if pre then
+         local n=n
+         while pre do
+          if seq[n][getchar(pre)] then
+           n=n+1
+           if n>s then
+            break
            else
-            notmatchpost[prev]=true
-            break
+            pre=getnext(pre)
            end
+          else
+           notmatchpre[current]=true
+           break
           end
-          if n>=1 then
-           notmatchpost[prev]=true
-          end
-         else
-          notmatchpost[prev]=true
          end
-         if replace then
-          while replacetail do
-           if seq[n][getchar(replacetail)] then
-            n=n-1
-            if replacetail==replace or n<1 then
-             break
-            else
-             replacetail=getprev(replacetail)
-            end
+         if n<=s then
+          notmatchpre[current]=true
+         end
+        else
+         notmatchpre[current]=true
+        end
+        if replace then
+         while replace do
+          if seq[n][getchar(replace)] then
+           n=n+1
+           if n>s then
+            break
            else
-            notmatchreplace[prev]=true
-            if notmatchpost[prev] then
-             goto next
-            else
-             break
-            end
+            replace=getnext(replace)
            end
+          else
+           notmatchreplace[current]=true
+           if notmatchpre[current] then
+            goto next
+           else
+            break
+           end
           end
-         else
          end
+        else
         end
-        prev=getprev(prev)
+        current=getnext(current)
        elseif id==glue_code then
         local sn=seq[n]
-        if (sn[32] and spaces[prev]) or sn[0xFFFC] then
-         n=n-1
-         prev=getprev(prev)
+        if (sn[32] and spaces[current]) or sn[0xFFFC] then
+         n=n+1
+         current=getnext(current)
         else
          goto next
         end
        elseif seq[n][0xFFFC] then
-        n=n-1
-        prev=getprev(prev)
+        n=n+1
+        current=getnext(current)
        else
         goto next
        end
@@ -28795,125 +29687,8 @@
     else
      goto next
     end
-   else
-    goto next
    end
   end
-  if s>l then
-   local current=last and getnext(last)
-   if not current and postreplace then
-    current=getnext(sweepnode)
-   end
-   if current then
-    local discfound 
-    local n=l+1
-    while n<=s do
-     if current then
-      local char,id=ischar(current,currentfont)
-      if char then
-       if skiphash and skiphash[char] then
-        skipped=true
-        if trace_skips then
-         show_skip(dataset,sequence,char,ck,classes[char])
-        end
-        current=getnext(current) 
-       elseif seq[n][char] then
-        if n<s then 
-         current=getnext(current) 
-        end
-        n=n+1
-       elseif discfound then
-        notmatchreplace[discfound]=true
-        if notmatchpre[discfound] then
-         goto next
-        else
-         break
-        end
-       else
-        goto next
-       end
-      elseif char==false then
-       if discfound then
-        notmatchreplace[discfound]=true
-        if notmatchpre[discfound] then
-         goto next
-        else
-         break
-        end
-       else
-        goto next
-       end
-      elseif id==disc_code then
-       discseen=true
-       discfound=current
-       notmatchpre[current]=nil
-       notmatchpost[current]=true
-       notmatchreplace[current]=nil
-       local pre,post,replace=getdisc(current)
-       if pre then
-        local n=n
-        while pre do
-         if seq[n][getchar(pre)] then
-          n=n+1
-          if n>s then
-           break
-          else
-           pre=getnext(pre)
-          end
-         else
-          notmatchpre[current]=true
-          break
-         end
-        end
-        if n<=s then
-         notmatchpre[current]=true
-        end
-       else
-        notmatchpre[current]=true
-       end
-       if replace then
-        while replace do
-         if seq[n][getchar(replace)] then
-          n=n+1
-          if n>s then
-           break
-          else
-           replace=getnext(replace)
-          end
-         else
-          notmatchreplace[current]=true
-          if notmatchpre[current] then
-           goto next
-          else
-           break
-          end
-         end
-        end
-       else
-       end
-       current=getnext(current)
-      elseif id==glue_code then
-       local sn=seq[n]
-       if (sn[32] and spaces[current]) or sn[0xFFFC] then
-        n=n+1
-        current=getnext(current)
-       else
-        goto next
-       end
-      elseif seq[n][0xFFFC] then
-       n=n+1
-       current=getnext(current)
-      else
-       goto next
-      end
-     else
-      goto next
-     end
-    end
-   else
-    goto next
-   end
-  end
   if trace_contexts then
    chaintrac(head,start,dataset,sequence,rlmode,skipped and skiphash,ck,true,discseen,sweepnode)
   end
@@ -28920,12 +29695,15 @@
   if discseen or sweepnode then
    head,start,done=chaindisk(head,start,dataset,sequence,rlmode,skipped and skiphash,ck)
   else
-   head,start,done=chainrun(head,start,last,dataset,sequence,rlmode,skipped and skiphash,ck)
+   head,start,done=chainrun(head,start,last,dataset,sequence,rlmode,skipped and skiphash,ck,6)
   end
+  if trace_contexts then
+   chaintrac(start,done)
+  end
   if done then
    break
   end
-  ::next::
+   ::next::
  end
  if discseen then
   notmatchpre={}
@@ -28942,15 +29720,26 @@
 local function chained_contextchain(head,start,stop,dataset,sequence,currentlookup,rlmode,skiphash)
  local steps=currentlookup.steps
  local nofsteps=currentlookup.nofsteps
- if nofsteps>1 then
-  reportmoresteps(dataset,sequence)
- end
- local l=steps[1].coverage[getchar(start)]
- if l then
-  return handle_contextchain(head,start,dataset,sequence,l,rlmode,skiphash)
+ local char=getchar(start)
+ if nofsteps==1 then
+  local s=steps[1]
+  local l=s.coverage[char]
+  if l then
+   return handle_contextchain(head,start,dataset,sequence,l,rlmode,skiphash)
+  end
  else
-  return head,start,false
+  for i=1,nofsteps do
+   local s=steps[i]
+   local l=s.coverage[char]
+   if l then
+    local h,s,d=handle_contextchain(head,start,dataset,sequence,l,rlmode,skiphash)
+    if d then
+     return h,s,d
+    end
+   end
+  end
  end
+ return head,start,false
 end
 chainprocs.gsub_context=chained_contextchain
 chainprocs.gsub_contextchain=chained_contextchain
@@ -29133,7 +29922,7 @@
  if done and trace_testruns then
   report_disc("done",disc)
  end
- return nextstart,done
+ return nextstart
 end
 local function comprun(disc,c_run,...) 
  if trace_compruns then
@@ -29176,8 +29965,12 @@
   end
   setdisc(disc,pre,post,replace)
  end
- return getnext(disc),renewed
+ return getnext(disc)
 end
+local test_flatten_start=2 
+directives.register("otf.testrun.forceflatten",function(v)
+ test_flatten_start=v and 1 or 2
+end)
 local function testrun(disc,t_run,c_run,...)
  if trace_testruns then
   report_disc("test",disc)
@@ -29205,7 +29998,7 @@
    local d=d_replace>d_post and d_replace or d_post
    local head=getnext(disc) 
    local tail=head
-   for i=2,d do 
+   for i=test_flatten_start,d do
     local nx=getnext(tail)
     local id=getid(nx)
     if id==disc_code then
@@ -29219,7 +30012,7 @@
    next=getnext(tail)
    setnext(tail)
    setprev(head)
-   local new=copy_node_list(head)
+   local new=copynodelist(head)
    if posttail then
     setlink(posttail,head)
    else
@@ -29282,7 +30075,7 @@
    report_disc("done",disc)
   end
  end
- return getnext(disc),renewed
+ return getnext(disc)
 end
 local nesting=0
 local function c_run_single(head,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler)
@@ -29360,7 +30153,7 @@
      while s do
       local char=ischar(s,font)
       if char then
-       local lg=lookupmatch[char]
+       local lg=not tonumber(lookupmatch) and lookupmatch[char]
        if lg then
         if sstop then
          d=1
@@ -29390,7 +30183,7 @@
        break
       end
      end
-     if l and l.ligature then 
+     if l and (tonumber(l) or l.ligature) then 
       lastd=d
      end
     else
@@ -29515,7 +30308,7 @@
       while s do
        local char=ischar(s)
        if char then
-        local lg=lookupmatch[char]
+        local lg=not tonumber(lookupmatch) and lookupmatch[char]
         if lg then
          if sstop then
           d=1
@@ -29545,7 +30338,7 @@
         break
        end
       end
-      if l and l.ligature then
+      if l and (tonumber(l) or l.ligature) then
        lastd=d
       end
      end
@@ -29673,7 +30466,7 @@
    checkstep(head)
   end
   local initialrl=0
-  if getid(head)==par_code and start_of_par(head) then
+  if getid(head)==par_code and startofpar(head) then
    initialrl=pardirstate(head)
   elseif direction==righttoleft_code then
    initialrl=-1
@@ -29700,7 +30493,7 @@
      head=h
     end
    elseif typ=="gsub_reversecontextchain" then
-    local start=find_node_tail(head)
+    local start=findnodetail(head)
     local rlmode=0 
     local merged=steps.merged
     while start do
@@ -29778,19 +30571,18 @@
        start=getnext(start)
       elseif id==disc_code then
        if not discs or discs[start]==true then
-        local ok
         if gpossing then
-         start,ok=kernrun(start,k_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler)
+         start=kernrun(start,k_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler)
         elseif forcetestrun then
-         start,ok=testrun(start,t_run_single,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler)
+         start=testrun(start,t_run_single,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler)
         else
-         start,ok=comprun(start,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler)
+         start=comprun(start,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,skiphash,handler)
         end
        else
         start=getnext(start)
        end
       elseif id==math_code then
-       start=getnext(end_of_math(start))
+       start=getnext(endofmath(start))
       elseif id==dir_code then
        topstack,rlmode=txtdirstate(start,dirstack,topstack,rlparmode)
        start=getnext(start)
@@ -29848,19 +30640,18 @@
        start=getnext(start)
       elseif id==disc_code then
        if not discs or discs[start]==true then
-        local ok
         if gpossing then
-         start,ok=kernrun(start,k_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler)
+         start=kernrun(start,k_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler)
         elseif forcetestrun then
-         start,ok=testrun(start,t_run_multiple,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler)
+         start=testrun(start,t_run_multiple,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler)
         else
-         start,ok=comprun(start,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler)
+         start=comprun(start,c_run_multiple,font,attr,steps,nofsteps,dataset,sequence,rlmode,skiphash,handler)
         end
        else
         start=getnext(start)
        end
       elseif id==math_code then
-       start=getnext(end_of_math(start))
+       start=getnext(endofmath(start))
       elseif id==dir_code then
        topstack,rlmode=txtdirstate(start,dirstack,topstack,rlparmode)
        start=getnext(start)
@@ -29938,7 +30729,7 @@
    elseif char==false or id==glue_code then
     start=getnext(start)
    elseif id==math_code then
-    start=getnext(end_of_math(start))
+    start=getnext(endofmath(start))
    elseif id==dir_code then
     topstack,rlmode=txtdirstate(start,dirstack,topstack,rlparmode)
     start=getnext(start)
@@ -29949,44 +30740,52 @@
   return head
  end
 end
-local plugins={}
-otf.plugins=plugins
-local report=logs.reporter("fonts")
-function otf.registerplugin(name,f)
- if type(name)=="string" and type(f)=="function" then
-  plugins[name]={ name,f }
-  report()
-  report("plugin %a has been loaded, please be aware of possible side effects",name)
-  report()
-  if logs.pushtarget then
-   logs.pushtarget("log")
+do
+ local plugins={}
+ otf.plugins=plugins
+ local report=logs.reporter("fonts")
+ local warned=false
+ local okay={ text=true }
+ function otf.registerplugin(name,f)
+  if type(name)=="string" and type(f)=="function" then
+   plugins[name]={ name,f }
+   if okay[name] then
+   else
+    report("plugin %a has been loaded, please be aware of possible side effects",name)
+    if not warned then
+     if logs.pushtarget then
+      logs.pushtarget("log")
+     end
+     report("Plugins are not officially supported unless stated otherwise. This is because")
+     report("they bypass the regular font handling and therefore some features in ConTeXt")
+     report("(especially those related to fonts) might not work as expected or might not work")
+     report("at all. Some plugins are for testing and development only and might change")
+     report("whenever we feel the need for it.")
+     report()
+     if logs.poptarget then
+      logs.poptarget()
+     end
+     warned=true
+    end
+   end
   end
-  report("Plugins are not officially supported unless stated otherwise. This is because")
-  report("they bypass the regular font handling and therefore some features in ConTeXt")
-  report("(especially those related to fonts) might not work as expected or might not work")
-  report("at all. Some plugins are for testing and development only and might change")
-  report("whenever we feel the need for it.")
-  report()
-  if logs.poptarget then
-   logs.poptarget()
+ end
+ function otf.plugininitializer(tfmdata,value)
+  if type(value)=="string" then
+   tfmdata.shared.plugin=plugins[value]
   end
  end
-end
-function otf.plugininitializer(tfmdata,value)
- if type(value)=="string" then
-  tfmdata.shared.plugin=plugins[value]
- end
-end
-function otf.pluginprocessor(head,font,attr,direction) 
- local s=fontdata[font].shared
- local p=s and s.plugin
- if p then
-  if trace_plugins then
-   report_process("applying plugin %a",p[1])
+ function otf.pluginprocessor(head,font,dynamic,direction) 
+  local s=fontdata[font].shared
+  local p=s and s.plugin
+  if p then
+   if trace_plugins then
+    report_process("applying plugin %a",p[1])
+   end
+   return p[2](head,font,dynamic,direction)
+  else
+   return head,false
   end
-  return p[2](head,font,attr,direction)
- else
-  return head,false
  end
 end
 function otf.featuresinitializer(tfmdata,value)
@@ -30313,6 +31112,7 @@
  else
   dataset={ { data=specification.data } }
   specification.data=nil
+  specification.coverage=dataset
   specification.dataset=dataset
  end
  local first=dataset[1]
@@ -30338,7 +31138,7 @@
   return specification,name
  end
 end
-local function addfeature(data,feature,specifications)
+local function addfeature(data,feature,specifications,prepareonly)
  if not specifications then
   report_otf("missing specification")
   return
@@ -30345,6 +31145,10 @@
  end
  local descriptions=data.descriptions
  local resources=data.resources
+ if not descriptions or not resources then
+  report_otf("missing specification")
+  return
+ end
  local features=resources.features
  local sequences=resources.sequences
  if not features or not sequences then
@@ -30612,12 +31416,31 @@
   return coverage
  end
  local prepare_single=prepare_pair 
- local function prepare_chain(list,featuretype,sublookups)
+ local function hassteps(lookups)
+  if lookups then
+   for i=1,#lookups do
+    local l=lookups[i]
+    if l then
+     for j=1,#l do
+      local l=l[j]
+      if l then
+       local n=l.nofsteps
+       if not n then
+        return true
+       elseif n>0 then
+        return true
+       end
+      end
+     end
+    end
+   end
+  end
+  return false
+ end
+ local function prepare_chain(list,featuretype,sublookups,nocheck)
   local rules=list.rules
   local coverage={}
   if rules then
-   local rulehash={}
-   local rulesize=0
    local lookuptype=types[featuretype]
    for nofrules=1,#rules do
     local rule=rules[nofrules]
@@ -30648,6 +31471,15 @@
     local lookups=rule.lookups or false
     local subtype=nil
     if lookups and sublookups then
+     if #lookups>0 then
+      local ns=stop-start+1
+      for i=1,ns do
+       if lookups[i]==nil then
+        lookups[i]=0
+       end
+      end
+     end
+     local l={}
      for k,v in sortedhash(lookups) do
       local t=type(v)
       if t=="table" then
@@ -30657,58 +31489,71 @@
          v[i]={ vi }
         end
        end
+       l[k]=v
       elseif t=="number" then
        local lookup=sublookups[v]
        if lookup then
-        lookups[k]={ lookup }
+        l[k]={ lookup }
         if not subtype then
          subtype=lookup.type
         end
        elseif v==0 then
-        lookups[k]={ { type="gsub_remove" } }
+        l[k]={ { type="gsub_remove",nosteps=true } }
        else
-        lookups[k]=false 
+        l[k]=false 
        end
       else
-       lookups[k]=false 
+       l[k]=false 
       end
      end
+     if nocheck then
+      rule.lookups=l 
+     end
+     lookups=l
     end
     if nofsequences>0 then
-     local hashed={}
-     for i=1,nofsequences do
-      local t={}
-      local s=sequence[i]
-      for i=1,#s do
-       local u=tounicode(s[i])
-       if u then
-        t[u]=true
+     if hassteps(lookups) then
+      local hashed={}
+      for i=1,nofsequences do
+       local t={}
+       local s=sequence[i]
+       for i=1,#s do
+        local u=tounicode(s[i])
+        if u then
+         t[u]=true
+        end
        end
+       hashed[i]=t
       end
-      hashed[i]=t
-     end
-     sequence=hashed
-     rulesize=rulesize+1
-     rulehash[rulesize]={
-      nofrules,
-      lookuptype,
-      sequence,
-      start,
-      stop,
-      lookups,
-      replacements,
-      subtype,
-     }
-     for unic in sortedhash(sequence[start]) do
-      local cu=coverage[unic]
-      if not cu then
-       coverage[unic]=rulehash 
+      sequence=hashed
+      local ruleset={
+       nofrules,
+       lookuptype,
+       sequence,
+       start,
+       stop,
+       lookups,
+       replacements,
+       subtype,
+      }
+      for unic in sortedhash(sequence[start]) do
+       local cu=coverage[unic]
+       if cu then
+        local n=cu.n+1
+        cu[n]=ruleset
+        cu.n=n
+       else
+        coverage[unic]={
+         ruleset,
+         n=1,
+        }
+       end
       end
+      sequence.n=nofsequences
+     else
      end
-     sequence.n=nofsequences
     end
    end
-   rulehash.n=rulesize
   end
   return coverage
  end
@@ -30792,9 +31637,11 @@
    end
    local askedfeatures=specification.features or everywhere
    local askedsteps=specification.steps or specification.subtables or { specification.data } or {}
-   local featuretype=normalized[specification.type or "substitution"] or "substitution"
+   local featuretype=specification.type or "substitution"
+   local featureaction=false
    local featureflags=specification.flags or noflags
    local nocheck=specification.nocheck
+   local mapping=specification.mapping
    local featureorder=specification.order or { feature }
    local featurechain=(featuretype=="chainsubstitution" or featuretype=="chainposition") and 1 or 0
    local nofsteps=0
@@ -30801,117 +31648,140 @@
    local steps={}
    local sublookups=specification.lookups
    local category=nil
+   local steptype=nil
+   local sequence=nil
+   if fonts.handlers.otf.handlers[featuretype] then
+    featureaction=true 
+   else
+    featuretype=normalized[specification.type or "substitution"] or "substitution"
+   end
    checkflags(specification,resources)
-   if sublookups then
-    local s={}
-    for i=1,#sublookups do
-     local specification=sublookups[i]
-     local askedsteps=specification.steps or specification.subtables or { specification.data } or {}
-     local featuretype=normalized[specification.type or "substitution"] or "substitution"
-     local featureflags=specification.flags or noflags
-     local nofsteps=0
-     local steps={}
-     for i=1,#askedsteps do
-      local list=askedsteps[i]
-      local coverage=nil
-      local format=nil
-      if featuretype=="substitution" then
-       coverage=prepare_substitution(list,featuretype,nocheck)
-      elseif featuretype=="ligature" then
-       coverage=prepare_ligature(list,featuretype,nocheck)
-      elseif featuretype=="alternate" then
-       coverage=prepare_alternate(list,featuretype,nocheck)
-      elseif featuretype=="multiple" then
-       coverage=prepare_multiple(list,featuretype,nocheck)
-      elseif featuretype=="kern" or featuretype=="move" then
-       format=featuretype
-       coverage=prepare_kern(list,featuretype)
-      elseif featuretype=="pair" then
-       format="pair"
-       coverage=prepare_pair(list,featuretype)
-      elseif featuretype=="single" then
-       format="single"
-       coverage=prepare_single(list,featuretype)
+   for k,v in next,askedfeatures do
+    if v[1] then
+     askedfeatures[k]=tohash(v)
+    end
+   end
+   if featureflags[1] then featureflags[1]="mark" end
+   if featureflags[2] then featureflags[2]="ligature" end
+   if featureflags[3] then featureflags[3]="base" end
+   if featureaction then
+    category="gsub"
+    sequence={
+     features={ [feature]=askedfeatures },
+     flags=featureflags,
+     name=feature,
+     order=featureorder,
+     type=featuretype,
+     nofsteps=0,
+    }
+   else
+    if sublookups then
+     local s={}
+     for i=1,#sublookups do
+      local specification=sublookups[i]
+      local askedsteps=specification.steps or specification.subtables or { specification.data } or {}
+      local featuretype=normalized[specification.type or "substitution"] or "substitution"
+      local featureflags=specification.flags or noflags
+      local nofsteps=0
+      local steps={}
+      for i=1,#askedsteps do
+       local list=askedsteps[i]
+       local coverage=nil
+       local format=nil
+       if featuretype=="substitution" then
+        coverage=prepare_substitution(list,featuretype,nocheck)
+       elseif featuretype=="ligature" then
+        coverage=prepare_ligature(list,featuretype,nocheck)
+       elseif featuretype=="alternate" then
+        coverage=prepare_alternate(list,featuretype,nocheck)
+       elseif featuretype=="multiple" then
+        coverage=prepare_multiple(list,featuretype,nocheck)
+       elseif featuretype=="kern" or featuretype=="move" then
+        format=featuretype
+        coverage=prepare_kern(list,featuretype)
+       elseif featuretype=="pair" then
+        format="pair"
+        coverage=prepare_pair(list,featuretype)
+       elseif featuretype=="single" then
+        format="single"
+        coverage=prepare_single(list,featuretype)
+       end
+       if coverage and next(coverage) then
+        nofsteps=nofsteps+1
+        steps[nofsteps]=register(coverage,featuretype,format,feature,nofsteps,descriptions,resources)
+       end
       end
-      if coverage and next(coverage) then
-       nofsteps=nofsteps+1
-       steps[nofsteps]=register(coverage,featuretype,format,feature,nofsteps,descriptions,resources)
-      end
+      checkmerge(specification)
+      checksteps(specification)
+      s[i]={
+       [stepkey]=steps,
+       nofsteps=nofsteps,
+       flags=featureflags,
+       type=types[featuretype],
+      }
      end
-     checkmerge(specification)
-     checksteps(specification)
-     s[i]={
+     sublookups=s
+    end
+    for i=1,#askedsteps do
+     local list=askedsteps[i]
+     local coverage=nil
+     local format=nil
+     if featuretype=="substitution" then
+      category="gsub"
+      coverage=(mapping and list) or prepare_substitution(list,featuretype,nocheck)
+     elseif featuretype=="ligature" then
+      category="gsub"
+      coverage=prepare_ligature(list,featuretype,nocheck)
+     elseif featuretype=="alternate" then
+      category="gsub"
+      coverage=prepare_alternate(list,featuretype,nocheck)
+     elseif featuretype=="multiple" then
+      category="gsub"
+      coverage=prepare_multiple(list,featuretype,nocheck)
+     elseif featuretype=="kern" or featuretype=="move" then
+      category="gpos"
+      format=featuretype
+      coverage=prepare_kern(list,featuretype)
+     elseif featuretype=="pair" then
+      category="gpos"
+      format="pair"
+      coverage=prepare_pair(list,featuretype)
+     elseif featuretype=="single" then
+      category="gpos"
+      format="single"
+      coverage=prepare_single(list,featuretype)
+     elseif featuretype=="chainsubstitution" then
+      category="gsub"
+      coverage=prepare_chain(list,featuretype,sublookups,nocheck)
+     elseif featuretype=="chainposition" then
+      category="gpos"
+      coverage=prepare_chain(list,featuretype,sublookups,nocheck)
+     else
+      report_otf("not registering feature %a, unknown category",feature)
+      return
+     end
+     if coverage and next(coverage) then
+      nofsteps=nofsteps+1
+      steps[nofsteps]=register(coverage,featuretype,format,feature,nofsteps,descriptions,resources)
+     end
+    end
+    if nofsteps>0 then
+     sequence={
+      chain=featurechain,
+      features={ [feature]=askedfeatures },
+      flags=featureflags,
+      name=feature,
+      order=featureorder,
       [stepkey]=steps,
       nofsteps=nofsteps,
-      flags=featureflags,
-      type=types[featuretype],
+      type=specification.handler or types[featuretype],
      }
+     if prepareonly then
+      return sequence
+     end
     end
-    sublookups=s
    end
-   for i=1,#askedsteps do
-    local list=askedsteps[i]
-    local coverage=nil
-    local format=nil
-    if featuretype=="substitution" then
-     category="gsub"
-     coverage=prepare_substitution(list,featuretype,nocheck)
-    elseif featuretype=="ligature" then
-     category="gsub"
-     coverage=prepare_ligature(list,featuretype,nocheck)
-    elseif featuretype=="alternate" then
-     category="gsub"
-     coverage=prepare_alternate(list,featuretype,nocheck)
-    elseif featuretype=="multiple" then
-     category="gsub"
-     coverage=prepare_multiple(list,featuretype,nocheck)
-    elseif featuretype=="kern" or featuretype=="move" then
-     category="gpos"
-     format=featuretype
-     coverage=prepare_kern(list,featuretype)
-    elseif featuretype=="pair" then
-     category="gpos"
-     format="pair"
-     coverage=prepare_pair(list,featuretype)
-    elseif featuretype=="single" then
-     category="gpos"
-     format="single"
-     coverage=prepare_single(list,featuretype)
-    elseif featuretype=="chainsubstitution" then
-     category="gsub"
-     coverage=prepare_chain(list,featuretype,sublookups)
-    elseif featuretype=="chainposition" then
-     category="gpos"
-     coverage=prepare_chain(list,featuretype,sublookups)
-    else
-     report_otf("not registering feature %a, unknown category",feature)
-     return
-    end
-    if coverage and next(coverage) then
-     nofsteps=nofsteps+1
-     steps[nofsteps]=register(coverage,featuretype,format,feature,nofsteps,descriptions,resources)
-    end
-   end
-   if nofsteps>0 then
-    for k,v in next,askedfeatures do
-     if v[1] then
-      askedfeatures[k]=tohash(v)
-     end
-    end
-    if featureflags[1] then featureflags[1]="mark" end
-    if featureflags[2] then featureflags[2]="ligature" end
-    if featureflags[3] then featureflags[3]="base" end
-    local steptype=types[featuretype]
-    local sequence={
-     chain=featurechain,
-     features={ [feature]=askedfeatures },
-     flags=featureflags,
-     name=feature,
-     order=featureorder,
-     [stepkey]=steps,
-     nofsteps=nofsteps,
-     type=steptype,
-    }
+   if sequence then
     checkflags(sequence,resources)
     checkmerge(sequence)
     checksteps(sequence)
@@ -30990,8 +31860,10 @@
  copyright="TAT Zetwerk / PRAGMA ADE / ConTeXt Development Team",
  license="see context related readme files"
 }
-local insert,imerge,copy,tohash=table.insert,table.imerge,table.copy,table.tohash
-local next,type=next,type
+local insert,remove,imerge,copy,tohash=table.insert,table.remove,table.imerge,table.copy,table.tohash
+local next,type,rawget=next,type,rawget
+local formatters=string.formatters
+local settings_to_hash=utilities.parsers.settings_to_hash
 local report=logs.reporter("otf","devanagari")
 fonts=fonts       or {}
 fonts.analyzers=fonts.analyzers   or {}
@@ -31001,6 +31873,7 @@
 local methods=fonts.analyzers.methods
 local otffeatures=fonts.constructors.features.otf
 local registerotffeature=otffeatures.register
+local trace_steps=false
 local nuts=nodes.nuts
 local getnext=nuts.getnext
 local getprev=nuts.getprev
@@ -31018,15 +31891,16 @@
 local getstate=nuts.getstate
 local setstate=nuts.setstate
 local ischar=nuts.ischar
-local insert_node_after=nuts.insert_after
+local insertnodeafter=nuts.insertafter
 local copy_node=nuts.copy
 local remove_node=nuts.remove
-local flush_list=nuts.flush_list
-local flush_node=nuts.flush_node
+local flushlist=nuts.flushlist
+local flushnode=nuts.flushnode
 local copyinjection=nodes.injections.copy 
 local unsetvalue=attributes.unsetvalue
 local fontdata=fonts.hashes.identifiers
-local a_syllabe=attributes.private('syllabe')
+local a_syllabe="syllable"  
+local a_reordered="reordered" 
 local dotted_circle=0x25CC
 local c_nbsp=0x00A0
 local c_zwnj=0x200C
@@ -31046,6 +31920,7 @@
  return replace_all_nbsp(head)
 end
 local processcharacters=nil
+local logprocess=nil
 if context then
 
 --removed
@@ -31058,6 +31933,8 @@
   end
   return head
  end
+ logprocess=function(str)
+ end
 end
 local indicgroups=characters and characters.indicgroups
 if not indicgroups and characters then
@@ -31167,7 +32044,7 @@
 local after_subscript=indicgroups.after_subscript
 local before_main=indicgroups.before_main
 local after_main=indicgroups.after_main
-local mark_four=table.merged (
+local mark_pre_above_below_post=table.merged (
  pre_mark,
  above_mark,
  below_mark,
@@ -31178,6 +32055,11 @@
  below_mark,
  post_mark
 )
+local devanagarihash=table.setmetatableindex(function(t,k)
+ local v=fontdata[k].resources.devanagari or false
+ t[k]=v
+ return v
+end)
 local zw_char={ 
  [c_zwnj]=true,
  [c_zwj ]=true,
@@ -31298,7 +32180,7 @@
   local resources=tfmdata.resources
   local devanagari=resources.devanagari
   if not devanagari then
-   report("adding devanagari features to font")
+   report("adding features to font")
    local gsubfeatures=resources.features.gsub
    local sequences=resources.sequences
    local sharedfeatures=tfmdata.shared.features
@@ -31325,7 +32207,7 @@
          for k,v in next,pre_mark do
           local locl=coverage[k]
           if locl then
-           if #locl>0 then	
+           if #locl>0 then
             for j=1,#locl do
              local ck=locl[j]
              local f=ck[4]
@@ -31346,6 +32228,7 @@
               end
              end
             end
+           else
            end
            if locl then
             reorder_matras.steps[1].coverage[locl]=true
@@ -31358,7 +32241,7 @@
       if basic_shaping_forms[k] then
        lastmatch=lastmatch+1
        if s~=lastmatch then
-        table.insert(sequences,lastmatch,table.remove(sequences,s))
+        insert(sequences,lastmatch,remove(sequences,s))
        end
       end
      end
@@ -31376,9 +32259,7 @@
    local vatucache={}
    local pstfcache={}
    local seqsubset={}
-   local rephstep={
-    coverage={} 
-   }
+   local rephstep={ coverage={} } 
    local devanagari={
     reph=false,
     vattu=false,
@@ -31403,7 +32284,7 @@
     local has_pstf=features.pstf
     if has_rphf and has_rphf[script] then
      devanagari.reph=true
-    elseif (has_blwf and has_blwf[script] ) or (has_vatu and has_vatu[script] ) then
+    elseif (has_blwf and has_blwf[script]) or (has_vatu and has_vatu[script]) then
      devanagari.vattu=true
      for i=1,nofsteps do
       local step=steps[i]
@@ -31411,10 +32292,8 @@
       if coverage then
        for k,v in next,coverage do
         for h,w in next,halant do
-         if v[h] then
-          if not blwfcache[k] then
-           blwfcache[k]=v
-          end
+         if v[h] and not blwfcache[k] then
+          blwfcache[k]=v
          end
          if has_vatu and has_vatu[script] and not vatucache[k] then
           vatucache[k]=v
@@ -31437,33 +32316,37 @@
         local r=coverage[k]
         if r then
          local found=false
-         if #r>0 then  
+         if #r>0 then
           for j=1,#r do
            local ck=r[j]
            local f=ck[4]
            local chainlookups=ck[6]
-           if chainlookups and chainlookups[f] then	
+           if chainlookups then
             local chainlookup=chainlookups[f]
-            for j=1,#chainlookup do
-             local chainstep=chainlookup[j]
-             local steps=chainstep.steps
-             local nofsteps=chainstep.nofsteps
-             for i=1,nofsteps do
-              local step=steps[i]
-              local coverage=step.coverage
-              if coverage then
-               local h=coverage[k]
-               if h then
-                for k,v in next,h do
-                 found=v and v.ligature
+            if chainlookup then
+             for j=1,#chainlookup do
+              local chainstep=chainlookup[j]
+              local steps=chainstep.steps
+              local nofsteps=chainstep.nofsteps
+              for i=1,nofsteps do
+               local step=steps[i]
+               local coverage=step.coverage
+               if coverage then
+                local h=coverage[k]
+                if h then
+                 for k,v in next,h do
+                  if v then
+                   found=tonumber(v) or v.ligature
+                   if found then
+                    pre_base_reordering_consonants[found]=true
+                    break
+                   end
+                  end
+                 end
                  if found then
-                  pre_base_reordering_consonants[found]=true
                   break
                  end
                 end
-                if found then
-                 break
-                end
                end
               end
              end
@@ -31472,10 +32355,12 @@
           end
          else
           for k,v in next,r do
-           found=v and v.ligature
-           if found then
-            pre_base_reordering_consonants[found]=true
-            break
+           if v then
+            found=tonumber(v) or v.ligature
+            if found then
+             pre_base_reordering_consonants[found]=true
+             break
+            end
            end
           end
          end
@@ -31493,14 +32378,15 @@
        local step=steps[i]
        local coverage=step.coverage
        if coverage then
-        local reph,rephbase=false,false
+        local reph=false
+        local base=false
         if kind=="rphf" then
          for k,v in next,ra do
           local r=coverage[k]
           if r then
-           rephbase=k
+           base=k
            local h=false
-           if #r>0 then	
+           if #r>0 then
             for j=1,#r do
              local ck=r[j]
              local f=ck[4]
@@ -31520,7 +32406,7 @@
                   for k,v in next,halant do
                    local h=r[k]
                    if h then
-                    reph=h.ligature or false
+                    reph=tonumber(h) or h.ligature or false
                     break
                    end
                   end
@@ -31537,7 +32423,7 @@
             for k,v in next,halant do
              local h=r[k]
              if h then
-              reph=h.ligature or false
+              reph=tonumber(h) or h.ligature or false
               break
              end
             end
@@ -31548,7 +32434,7 @@
           end
          end
         end
-        seqsubset[#seqsubset+1]={ kind,coverage,reph,rephbase }
+         seqsubset[#seqsubset+1]={ kind,coverage,reph,base }
        end
       end
      end
@@ -31563,7 +32449,7 @@
          local h=coverage[k]
          if h then
           local found=false
-          if #h>0 then 
+          if #h>0 then
            for j=1,#h do
             local ck=h[j]
             local f=ck[4]
@@ -31581,10 +32467,12 @@
                 local h=coverage[k]
                 if h then
                  for k,v in next,h do
-                  found=v and v.ligature
-                  if found then
-                   pre_base_reordering_consonants[found]=true
-                   break
+                  if v then
+                   found=tonumber(v) or v.ligature
+                   if found then
+                    pre_base_reordering_consonants[found]=true
+                    break
+                   end
                   end
                  end
                  if found then
@@ -31598,7 +32486,7 @@
            end
           else
            for k,v in next,h do
-            found=v and v.ligature
+            found=v and (tonumber(v) or v.ligature)
             if found then
              pre_base_reordering_consonants[found]=true
              break
@@ -31624,9 +32512,6 @@
     sharedfeatures["dv03"]=true 
     sharedfeatures["dv04"]=true 
    end
-   if script=="mlym" or script=="taml" then
-    devanagari.left_matra_before_base=true
-   end
   end
  end
 end
@@ -31638,6 +32523,44 @@
   node=initializedevanagi,
  },
 }
+local function initializeconjuncts(tfmdata,value)
+ if value then
+  local resources=tfmdata.resources
+  local devanagari=resources.devanagari
+  if devanagari then
+   local conjuncts="auto" 
+   local movematra="auto" 
+   if type(value)=="string" and value~="auto" then
+    value=settings_to_hash(value)
+    conjuncts=rawget(value,"conjuncts") or conjuncts
+    movematra=rawget(value,"movematra") or movematra
+   end
+   if conjuncts=="auto" then
+    conjuncts="mixed" 
+   end
+   if movematra=="auto" and
+      script=="mlym" or
+      script=="taml" then
+    movematra="leftbeforebase"
+   else
+    movematra="default"
+   end
+   devanagari.conjuncts=conjuncts
+   devanagari.movematra=movematra
+   if trace_steps then
+    report("conjuncts %a, movematra %a",conjuncts,movematra)
+   end
+  end
+ end
+end
+registerotffeature {
+ name="indic",
+ description="control indic",
+ default="auto",
+ initializers={
+  node=initializeconjuncts,
+ },
+}
 local show_syntax_errors=false
 local function inject_syntax_error(head,current,char)
  local signal=copy_node(current)
@@ -31647,7 +32570,7 @@
  else
   setchar(current,dotted_circle)
  end
- return insert_node_after(head,current,signal)
+ return insertnodeafter(head,current,signal)
 end
 local function initialize_one(font,attr) 
  local tfmdata=fontdata[font]
@@ -31683,36 +32606,40 @@
 end
 local function contextchain(contexts,n)
  local char=getchar(n)
- for k=1,#contexts do
-  local ck=contexts[k]
-  local seq=ck[3]
-  local f=ck[4]
-  local l=ck[5]
-  if (l-f)==1 and seq[f+1][char] then
-   local ok=true
-   local c=n
-   for i=l+1,#seq do
-    c=getnext(c)
-    if not c or not seq[i][ischar(c)] then
-     ok=false
-     break
-    end
-   end
-   if ok then
-    c=getprev(n)
-    for i=1,f-1 do
-     c=getprev(c)
-     if not c or not seq[f-i][ischar(c)] then
+ if not contexts.n then
+  return contexts[char]
+ else
+  for k=1,#contexts do
+   local ck=contexts[k]
+   local seq=ck[3]
+   local f=ck[4]
+   local l=ck[5]
+   if (l-f)==1 and seq[f+1][char] then
+    local ok=true
+    local c=n
+    for i=l+1,#seq do
+     c=getnext(c)
+     if not c or not seq[i][ischar(c)] then
       ok=false
+      break
      end
     end
+    if ok then
+     c=getprev(n)
+     for i=1,f-1 do
+      c=getprev(c)
+      if not c or not seq[f-i][ischar(c)] then
+       ok=false
+      end
+     end
+    end
+    if ok then
+     return true
+    end
    end
-   if ok then
-    return true
-   end
   end
+  return false
  end
- return false
 end
 local function order_matras(c)
  local cn=getnext(c)
@@ -31741,9 +32668,9 @@
   char=getchar(cn)
  end
 end
+local swapped=table.swapped(states)
 local function reorder_one(head,start,stop,font,attr,nbspaces)
- local reph,vattu,blwfcache,vatucache,pstfcache=initialize_one(font,attr) 
- local devanagari=fontdata[font].resources.devanagari
+ local reph,vattu,blwfcache,vatucache,pstfcache=initialize_one(font,attr)
  local current=start
  local n=getnext(start)
  local base=nil
@@ -31765,7 +32692,10 @@
   if current==stop then
    stop=getprev(stop)
    head=remove_node(head,current)
-   flush_node(current)
+   flushnode(current)
+   if trace_steps then
+    logprocess("reorder one, remove nbsp")
+   end
    return head,stop,nbspaces
   else
    nbspaces=nbspaces+1
@@ -31795,7 +32725,7 @@
       tempcurrent=processcharacters(tempcurrent,font)
       setstate(tempcurrent,unsetvalue)
       if getchar(next)==getchar(tempcurrent) then
-       flush_list(tempcurrent)
+       flushlist(tempcurrent)
        if show_syntax_errors then
         head,current=inject_syntax_error(head,current,char)
        end
@@ -31803,12 +32733,15 @@
        setchar(current,getchar(tempcurrent)) 
        local freenode=getnext(current)
        setlink(current,tmp)
-       flush_node(freenode)
-       flush_list(tempcurrent)
+       flushnode(freenode)
+       flushlist(tempcurrent)
        if changestop then
         stop=current
        end
       end
+      if trace_steps then
+       logprocess("reorder one, handle nbsp")
+      end
      end
     end
    end
@@ -31863,6 +32796,9 @@
    if lastcons==stop then
     stop=n
    end
+   if trace_steps then
+    logprocess("reorder one, handle halant")
+   end
   end
  end
  n=getnext(start)
@@ -31887,6 +32823,9 @@
   if matra==stop then
    stop=n
   end
+  if trace_steps then
+   logprocess("reorder one, handle matra")
+  end
  end
  local current=start
  while current~=stop do
@@ -31925,14 +32864,19 @@
      ch=getchar(n)
     end
     local tpm=twopart_mark[ch]
-    while tpm do
-     local extra=copy_node(n)
-     copyinjection(extra,n)
-     ch=tpm[1]
-     setchar(n,ch)
-     setchar(extra,tpm[2])
-     head=insert_node_after(head,current,extra)
-     tpm=twopart_mark[ch]
+    if tpm then
+     while tpm do
+      local extra=copy_node(n)
+      copyinjection(extra,n)
+      ch=tpm[1]
+      setchar(n,ch)
+      setchar(extra,tpm[2])
+      head=insertnodeafter(head,current,extra)
+      tpm=twopart_mark[ch]
+     end
+     if trace_steps then
+      logprocess("reorder one, handle mark")
+     end
     end
     while c~=stop and dependent_vowel[ch] do
      c=n
@@ -31955,9 +32899,10 @@
   local bp=getprev(firstcons)
   local cn=getnext(current)
   local last=getnext(c)
+  local done=false
   while cn~=last do
    if pre_mark[getchar(cn)] then
-    if devanagari.left_matra_before_base then
+    if devanagarihash[font].movematra=="leftbeforebase" then
      local prev,next=getboth(cn)
      setlink(prev,next)
      if cn==stop then
@@ -31994,6 +32939,7 @@
      end
      cn=next
     end
+    done=true
    elseif current~=base and dependent_vowel[getchar(cn)] then
     local prev,next=getboth(cn)
     if next then
@@ -32006,6 +32952,7 @@
     setlink(b,cn,getnext(b))
     order_matras(cn)
     cn=next
+    done=true
    elseif current==base and dependent_vowel[getchar(cn)] then
     local cnn=getnext(cn)
     order_matras(cn)
@@ -32019,9 +32966,14 @@
   end
   allreordered=c==stop
   current=getnext(c)
+  if done and trace_steps then
+   logprocess("reorder one, matra")
+  end
  end
  if reph or vattu then
-  local current,cns=start,nil
+  local current=start
+  local cns=nil
+  local done=false
   while current~=stop do
    local c=current
    local n=getnext(current)
@@ -32052,6 +33004,7 @@
       local next=getnext(b)
       setlink(c,next)
       setlink(b,current)
+      done=true
      end
     elseif cns and getnext(cns)~=current then
      local cp=getprev(current)
@@ -32059,6 +33012,7 @@
      setlink(cp,n)
      setlink(cns,current) 
      setlink(c,cnsn)
+     done=true
      if c==stop then
       stop=cp
       break
@@ -32098,14 +33052,17 @@
    end
    current=getnext(current)
   end
+  if done and trace_steps then
+   logprocess("reorder one, handle reph and vata") 
+  end
  end
  if getchar(base)==c_nbsp then
   nbspaces=nbspaces-1
   if base==stop then
-  	stop=getprev(stop)
+   stop=getprev(stop)
   end
   head=remove_node(head,base)
-  flush_node(base)
+  flushnode(base)
  end
  return head,stop,nbspaces
 end
@@ -32130,6 +33087,9 @@
     setlink(start,next)
     setlink(current,start)
     start=startnext
+    if trace_steps then
+     logprocess("reorder matra")
+    end
     break
    end
   else
@@ -32158,6 +33118,9 @@
    local char=ischar(current,startfont)
    if char and getprop(current,a_syllabe)==startattr then
     if halant[char] then
+     if trace_steps then
+      logprocess("reorder reph, handling halant")
+     end
      local next=getnext(current)
      if next then
       local nextchar=ischar(next,startfont)
@@ -32188,6 +33151,9 @@
     local char=ischar(current,startfont)
     if char and getprop(current,a_syllabe)==startattr then
      if consonant[char] and not getstate(current,s_pref) then
+      if trace_steps then
+       logprocess("reorder reph, handling consonant")
+      end
       startnext=getnext(start)
       head=remove_node(head,start)
       setlink(current,start)
@@ -32212,6 +33178,9 @@
     local char=ischar(current,startfont)
     if char and getprop(current,a_syllabe)==startattr then
      if getstate(current,s_pstf) then 
+      if trace_steps then
+       logprocess("reorder reph, before postscript, post base")
+      end
       startnext=getnext(start)
       head=remove_node(head,start)
       setlink(getprev(current),start)
@@ -32219,12 +33188,15 @@
       start=startnext
       startattr=getprop(start,a_syllabe)
       break
-     elseif not c and (vowel_modifier[char] or stress_tone_mark[char] ) then
+     elseif not c and (vowel_modifier[char] or stress_tone_mark[char]) then
       c=current
      end
      current=getnext(current)
     else
      if c then
+      if trace_steps then
+       logprocess("reorder reph, before postscript")
+      end
       startnext=getnext(start)
       head=remove_node(head,start)
       setlink(getprev(c),start)
@@ -32247,7 +33219,13 @@
     local state=getstate(current)
     if before_subscript[rephbase] and (state==s_blwf or state==s_pstf) then
      c=current
+     if trace_steps then
+      logprocess("reorder reph, before subscript")
+     end
     elseif after_subscript[rephbase] and (state==s_pstf) then
+     if trace_steps then
+      logprocess("reorder reph, after subscript")
+     end
      c=current
     end
     current=getnext(current)
@@ -32278,6 +33256,9 @@
    end
   end
   if start~=current then
+   if trace_steps then
+    logprocess("reorder reph, to end")
+   end
    startnext=getnext(start)
    head=remove_node(head,start)
    setlink(start,getnext(current))
@@ -32287,9 +33268,8 @@
  end
  return head,start,true
 end
-local reordered_pre_base_reordering_consonants={} 
 function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start)
- if reordered_pre_base_reordering_consonants[start] then
+ if getprop(start,a_reordered) then
   return head,start,true
  end
  local current=start 
@@ -32300,6 +33280,9 @@
   local next=getnext(current)
   if char and getprop(current,a_syllabe)==startattr then
    if halant[char] then 
+    if trace_steps then
+     logprocess("reorder pre base consonants, handle halant")
+    end
     if next then
      local char=ischar(next,startfont)
      if char and zw_char[char] and getprop(next,a_syllabe)==startattr then
@@ -32311,7 +33294,7 @@
     head=remove_node(head,start)
     setlink(start,next)
     setlink(current,start)
-    reordered_pre_base_reordering_consonants[start]=true
+    setprop(start,"reordered",true)
     start=startnext
     return head,start,true
    end
@@ -32325,6 +33308,9 @@
  while current and getprop(current,a_syllabe)==startattr do
   local char=ischar(current)
   if (not dependent_vowel[char] and (not getstate(current) or getstate(current,s_init))) then
+   if trace_steps then
+    logprocess("reorder pre base consonants, handle vowel or initial")
+   end
    startnext=getnext(start)
    head=remove_node(head,start)
    if current==head then
@@ -32334,7 +33320,7 @@
     setlink(getprev(current),start)
     setlink(start,current)
    end
-   reordered_pre_base_reordering_consonants[start]=true
+   setprop(start,"reordered",true)
    start=startnext
    break
   end
@@ -32365,7 +33351,10 @@
  if head==start then
   head=stop
  end
- flush_list(start)
+ flushlist(start)
+ if trace_steps then
+  logprocess("remove joiners")
+ end
  return head,stop,true
 end
 local function initialize_two(font,attr)
@@ -32382,15 +33371,17 @@
  local basepos=nil
  local subpos=nil
  local postpos=nil
- reorderreph.coverage={}
- rephbase[font]={}
+ reorderreph.coverage={} 
+ rephbase[font]={} 
  for i=1,#seqsubset do
   local subset=seqsubset[i]
   local kind=subset[1]
   local lookupcache=subset[2]
   if kind=="rphf" then
-   reorderreph.coverage[subset[3]]=true 
-   rephbase[font][subset[3]]=subset[4]
+   local reph=subset[3]
+   local base=subset[4]
+   reorderreph.coverage[reph]=true 
+   rephbase[font][reph]=base
    local current=start
    local last=getnext(stop)
    while current~=last do
@@ -32399,15 +33390,15 @@
      local found=lookupcache[c]
      if found then
       local next=getnext(current)
-      if found[getchar(next)] or contextchain(found,next) then 
+      if contextchain(found,next) then 
        local afternext=next~=stop and getnext(next)
        if afternext and zw_char[getchar(afternext)] then 
         current=afternext 
        elseif current==start then
         setstate(current,s_rphf)
-        current=next
+        current=next 
        else
-        current=next
+        current=next 
        end
       end
      end
@@ -32423,8 +33414,8 @@
      local found=lookupcache[c]
      if found then 
       local next=getnext(current)
-      if found[getchar(next)] or contextchain(found,next) then
-       if (not getstate(current) and not getstate(next)) then	
+      if contextchain(found,next) then
+       if not getstate(current) and not getstate(next) then 
         setstate(current,s_pref)
         setstate(next,s_pref)
         current=next
@@ -32443,10 +33434,10 @@
      local found=lookupcache[c]
      if found then
       local next=getnext(current)
-      if found[getchar(next)] or contextchain(found,next) then
+      if contextchain(found,next) then
        if next~=stop and getchar(getnext(next))==c_zwnj then 
         current=next
-       elseif (not getstate(current)) then	
+       elseif not getstate(current) then 
         setstate(current,s_half)
         if not halfpos then
          halfpos=current
@@ -32467,8 +33458,8 @@
      local found=lookupcache[c]
      if found then
       local next=getnext(current)
-      if found[getchar(next)] or contextchain(found,next) then
-       if (not getstate(current) and not getstate(next)) then 
+      if contextchain(found,next) then
+       if not getstate(current) and not getstate(next) then 
         setstate(current,s_blwf)
         setstate(next,s_blwf)
         current=next
@@ -32488,8 +33479,8 @@
      local found=lookupcache[c]
      if found then
       local next=getnext(current)
-      if found[getchar(next)] or contextchain(found,next) then
-       if (not getstate(current) and not getstate(next)) then 
+      if contextchain(found,next) then
+       if not getstate(current) and not getstate(next) then 
         setstate(current,s_pstf)
         setstate(next,s_pstf)
         current=next
@@ -32502,7 +33493,7 @@
    end
   end
  end
- local current,base,firstcons=start,nil,nil
+ local current,base,firstcons,subnotafterbase,postnotafterbase=start,nil,nil,nil,nil
  if getstate(start,s_rphf) then
   current=getnext(getnext(start))
  end
@@ -32510,7 +33501,10 @@
   if current==stop then
    stop=getprev(stop)
    head=remove_node(head,current)
-   flush_node(current)
+   flushnode(current)
+   if trace_steps then
+    logprocess("reorder two, remove nbsp")
+   end
    return head,stop,nbspaces
   else
    nbspaces=nbspaces+1
@@ -32551,6 +33545,9 @@
      end
     end
    end
+   if trace_steps then
+    logprocess("reorder two, handle nbsp")
+   end
   end
  else 
   local last=getnext(stop)
@@ -32580,7 +33577,7 @@
   return head,stop,nbspaces
  else
   if getstate(base) then 
-   setstate(base,unsetvalue)
+   setstate(base,unsetvalue)  
   end
   basepos=base
  end
@@ -32601,17 +33598,22 @@
   local target=nil
   local cn=getnext(current)
   local tpm=twopart_mark[char]
-  while tpm do
-   local extra=copy_node(current)
-   copyinjection(extra,current)
-   char=tpm[1]
-   setchar(current,char)
-   setchar(extra,tpm[2])
-   head=insert_node_after(head,current,extra)
-   tpm=twopart_mark[char]
+  if tpm then
+   while tpm do
+    local extra=copy_node(current)
+    copyinjection(extra,current)
+    char=tpm[1]
+    setchar(current,char)
+    setchar(extra,tpm[2])
+    head=insertnodeafter(head,current,extra)
+    tpm=twopart_mark[char]
+   end
+   if tpm and trace_steps then
+    logprocess("reorder two, handle matra")
+   end
   end
-  if not moved[current] and dependent_vowel[char] then
-   if pre_mark[char] then 
+   if not moved[current] and dependent_vowel[char] then
+   if pre_mark[char] then
     moved[current]=true
     local prev,next=getboth(current)
     setlink(prev,next)
@@ -32649,6 +33651,9 @@
     end
     setlink(getprev(pos),current)
     setlink(current,pos)
+    if trace_steps then
+     logprocess("reorder two, handle pre mark")
+    end
    elseif above_mark[char] then
     target=basepos
     if subpos==basepos then
@@ -32688,6 +33693,9 @@
      end
      setlink(current,getnext(target))
      setlink(target,current)
+     if trace_steps then
+      logprocess("reorder two, handle mark")
+     end
     end
    end
   end
@@ -32715,6 +33723,9 @@
       stop=prev
      end
      cn=next
+     if trace_steps then
+      logprocess("reorder two, handle halant and ra")
+     end
     end
    end
   end
@@ -32747,6 +33758,9 @@
     setprev(nextnextnext,current)
    end
    setlink(nextnext,c)
+   if trace_steps then
+    logprocess("reorder two, handle nukta")
+   end
   end
   if stop==current then break end
   current=getnext(current)
@@ -32757,7 +33771,10 @@
   end
   nbspaces=nbspaces-1
   head=remove_node(head,base)
-  flush_node(base)
+  flushnode(base)
+  if trace_steps then
+   logprocess("reorder two, handle nbsp")
+  end
  end
  return head,stop,nbspaces
 end
@@ -32774,8 +33791,8 @@
  if not n then
   return c
  end
+ local v=ischar(n,font)
  if variant==1 then
-  local v=ischar(n,font)
   if v and nukta[v] then
    n=getnext(n)
    if n then
@@ -32808,7 +33825,6 @@
    end
   end
  elseif variant==2 then
-  local v=ischar(n,font)
   if v and nukta[v] then
    c=n
   end
@@ -32832,11 +33848,11 @@
    end
   end
  end
- local n=getnext(c)
+ n=getnext(c)
  if not n then
   return c
  end
- local v=ischar(n,font)
+ v=ischar(n,font)
  if not v then
   return c
  end
@@ -32845,21 +33861,42 @@
  local already_below_mark 
  local already_post_mark  
  while dependent_vowel[v] do
-	 local vowels=twopart_mark[v] or { v }
-	 for k,v in next,vowels do
-			if pre_mark[v] and not already_pre_mark then
-				already_pre_mark=true
-			elseif above_mark[v] and not already_above_mark then
-				already_above_mark=true
-			elseif below_mark[v] and not already_below_mark then
-				already_below_mark=true
-			elseif post_mark[v] and not already_post_mark then
-				already_post_mark=true
-			else
-				return c
-			end
-	 end
-  c=getnext(c)
+  local vowels=twopart_mark[v]
+  if vowels then
+   for k=1,#vowels do
+    local v=vowels[k]
+    if pre_mark[v] and not already_pre_mark then
+     already_pre_mark=true
+    elseif above_mark[v] and not already_above_mark then
+     already_above_mark=true
+    elseif below_mark[v] and not already_below_mark then
+     already_below_mark=true
+    elseif post_mark[v] and not already_post_mark then
+     already_post_mark=true
+    elseif devanagarihash[font].conjuncts=="continue" then
+    else
+     return c
+    end
+   end
+  else
+   if pre_mark[v] and not already_pre_mark then
+    already_pre_mark=true
+   elseif post_mark[v] and not already_post_mark then
+    if devanagarihash[font].conjuncts=="mixed" then
+     return c
+    else
+     already_post_mark=true
+    end
+   elseif below_mark[v] and not already_below_mark then
+    already_below_mark=true
+   elseif above_mark[v] and not already_above_mark then
+    already_above_mark=true
+   elseif devanagarihash[font].conjuncts=="continue" then
+   else
+    return c
+   end
+  end
+  c=n
   n=getnext(c)
   if not n then
    return c
@@ -32870,7 +33907,7 @@
   end
  end
  if nukta[v] then
-  c=getnext(c)
+  c=n
   n=getnext(c)
   if not n then
    return c
@@ -32881,7 +33918,7 @@
   end
  end
  if halant[v] then
-  c=getnext(c)
+  c=n
   n=getnext(c)
   if not n then
    return c
@@ -32892,7 +33929,7 @@
   end
  end
  if vowel_modifier[v] then
-  c=getnext(c)
+  c=n
   n=getnext(c)
   if not n then
    return c
@@ -32903,7 +33940,7 @@
   end
  end
  if stress_tone_mark[v] then
-  c=getnext(c)
+  c=n
   n=getnext(c)
   if not n then
    return c
@@ -32983,11 +34020,11 @@
  if not c then
   return
  end
- local n=getnext(c)
+ n=getnext(c)
  if not n then
   return c
  end
- local v=ischar(n,font)
+ v=ischar(n,font)
  if not v then
   return c
  end
@@ -33027,22 +34064,43 @@
   local already_pre_mark   
   local already_above_mark 
   local already_below_mark 
-  local already_post_mark  
-		while dependent_vowel[v] do
-			local vowels=twopart_mark[v] or { v }
-			for k,v in next,vowels do
-				if pre_mark[v] and not already_pre_mark then
-					already_pre_mark=true
-				elseif above_mark[v] and not already_above_mark then
-					already_above_mark=true
-				elseif below_mark[v] and not already_below_mark then
-					already_below_mark=true
-				elseif post_mark[v] and not already_post_mark then
-					already_post_mark=true
-				else
-					return c
-				end
-			end
+  local already_post_mark
+  while dependent_vowel[v] do
+   local vowels=twopart_mark[v]
+   if vowels then
+    for k=1,#vowels do
+     local v=vowels[k]
+     if pre_mark[v] and not already_pre_mark then
+      already_pre_mark=true
+     elseif above_mark[v] and not already_above_mark then
+      already_above_mark=true
+     elseif below_mark[v] and not already_below_mark then
+      already_below_mark=true
+     elseif post_mark[v] and not already_post_mark then
+      already_post_mark=true
+     elseif devanagarihash[font].conjuncts=="continue" then
+     else
+      return c
+     end
+    end
+   else
+    if pre_mark[v] and not already_pre_mark then
+     already_pre_mark=true
+    elseif post_mark[v] and not already_post_mark then
+       if devanagarihash[font].conjuncts=="mixed" then
+        return c
+       else
+        already_post_mark=true
+       end
+    elseif below_mark[v] and not already_below_mark then
+     already_below_mark=true
+    elseif above_mark[v] and not already_above_mark then
+     already_above_mark=true
+    elseif devanagarihash[font].conjuncts=="continue" then
+    else
+     return c
+    end
+   end
    c=n
    n=getnext(c)
    if not n then
@@ -33277,7 +34335,7 @@
      end
     else
      if show_syntax_errors then
-      local mark=mark_four[char]
+      local mark=mark_pre_above_below_post[char]
       if mark then
        head,current=inject_syntax_error(head,current,char)
       end
@@ -33298,15 +34356,15 @@
  while current do
   local char=ischar(current,font)
   if char then
-			if n==0 and not getstate(current) then
-				setstate(current,s_init)
-			end
-			n=n+1
-		else
-			n=0
-		end
-		current=getnext(current)
-	end
+   if n==0 and not getstate(current) then
+    setstate(current,s_init)
+   end
+   n=n+1
+  else
+   n=0
+  end
+  current=getnext(current)
+ end
  return head,done
 end
 local function method_two(head,font,attr)
@@ -33376,7 +34434,7 @@
   if not syllableend and show_syntax_errors then
    local char=ischar(current,font)
    if char and not getstate(current) then 
-    local mark=mark_four[char]
+    local mark=mark_pre_above_below_post[char]
     if mark then
      head,current=inject_syntax_error(head,current,char)
     end
@@ -33393,15 +34451,15 @@
  while current do
   local char=ischar(current,font)
   if char then
-			if n==0 and not getstate(current) then 
-				setstate(current,s_init)
-			end
-			n=n+1
-		else
-			n=0
-		end
-		current=getnext(current)
-	end
+   if n==0 and not getstate(current) then 
+    setstate(current,s_init)
+   end
+   n=n+1
+  else
+   n=0
+  end
+  current=getnext(current)
+ end
  return head,done
 end
 for i=1,nofscripts do
@@ -33691,9 +34749,10 @@
   if inkscape then
    local descriptions=tfmdata.descriptions
    local nofshapes=#svgshapes
+   local s_format=inkscapeformat("pdf") 
    local f_svgfile=formatters["temp-otf-svg-shape-%i.svg"]
    local f_pdffile=formatters["temp-otf-svg-shape-%i.pdf"]
-   local f_convert=formatters["%s --export-%s=%s\n"]
+   local f_convert=formatters[new and "file-open:%s; export-%s:%s; export-do\n" or "%s --export-%s=%s\n"]
    local filterglyph=otfsvg.filterglyph
    local nofdone=0
    local processed={}
@@ -33707,7 +34766,7 @@
       local svgfile=f_svgfile(index)
       local pdffile=f_pdffile(index)
       savedata(svgfile,data)
-      inkscape:write(f_convert(svgfile,inkscapeformat("pdf"),pdffile))
+      inkscape:write(f_convert(svgfile,s_format,pdffile))
       processed[index]=true
       nofdone=nofdone+1
       if nofdone%25==0 then
@@ -36095,7 +37154,7 @@
 local pfb=fonts.handlers.pfb
 local hashes=fonts.hashes
 local identifiers=hashes.identifiers
-local version=0.009
+local version=otf.version or 0.015
 local shapescache=containers.define("fonts","shapes",version,true)
 local streamscache=containers.define("fonts","streams",version,true)
 local compact_streams=false
@@ -36321,7 +37380,8 @@
    if streams then
     local fontbbox=metadata.fontbbox or { 0,0,0,0 }
     for i=0,#streams do
-     streams[i]=streams[i].stream or "\14"
+     local s=streams[i]
+     streams[i]=s.stream or "\14"
     end
     data={
      filename=filename,
@@ -36403,7 +37463,8 @@
  local fontdata=identifiers[fontid]
  if fontdata then
   local properties=fontdata.properties
-  return makehash(properties.filename,properties.subfont,properties.instance)
+  local fonthash=makehash(properties.filename,properties.subfont,properties.instance)
+  return fonthash,fontdata
  end
 end
 local function loadstreamdata(fontdata)
@@ -36914,12 +37975,12 @@
     before=before,
     current={ one,two },
     after=after,
-    lookups={ 1 },
+    lookups={ 1,false },
    }
    revert[new]={
     current={ one,zwj },
     after={ two },
-    lookups={ 1 },
+    lookups={ 1,false },
    }
   end
  end
@@ -36942,30 +38003,11 @@
  copyright="PRAGMA ADE / ConTeXt Development Team",
  license="see context related readme files"
 }
-local next=next
+local next,tonumber=next,tonumber
 local fonts=fonts
 local handlers=fonts.handlers
 local registerotffeature=handlers.otf.features.register
 local registerafmfeature=handlers.afm.features.register
-local function initialize(tfmdata,key,value)
- for unicode,character in next,tfmdata.characters do
-  local olditalic=character.italic
-  if olditalic and olditalic~=0 then
-   character.width=character.width+olditalic
-   character.italic=0
-  end
- end
-end
-local specification={
- name="italicwidths",
- description="add italic to width",
- manipulators={
-  base=initialize,
-  node=initialize,
- }
-}
-registerotffeature(specification)
-registerafmfeature(specification)
 local function initialize(tfmdata,value) 
  if value then
   local parameters=tfmdata.parameters
@@ -37180,74 +38222,55 @@
  "FractionRuleThickness",
  "UnderbarRuleThickness",
 }
-local setmathparameters
-local setmathcharacters
-if CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 then
- setmathparameters=function(tfmdata,characters,mathparameters,dx,dy,squeeze,multiplier)
-  if delta~=0 then
-   for i=1,#rules do
-    local name=rules[i]
-    local value=mathparameters[name]
-    if value then
-       mathparameters[name]=(squeeze or 1)*(value+dy)
-    end
+local function setmathparameters(tfmdata,characters,mathparameters,dx,dy,squeeze,multiplier)
+ if dy~=0 then
+  for i=1,#rules do
+   local name=rules[i]
+   local value=mathparameters[name]
+   if value then
+      mathparameters[name]=(squeeze or 1)*(value+dy)
    end
   end
  end
- setmathcharacters=function()
+end
+local function setmathcharacters(tfmdata,characters,mathparameters,dx,dy,squeeze,wdelta,hdelta,ddelta)
+ local function wdpatch(char)
+  if wsnap~=0 then
+   char.width=char.width+wdelta/2
+  end
  end
-else
- setmathparameters=function(tfmdata,characters,mathparameters,dx,dy,squeeze,multiplier)
-  if delta~=0 then
-   for i=1,#rules do
-    local name=rules[i]
-    local value=mathparameters[name]
-    if value then
-       mathparameters[name]=(squeeze or 1)*(value+dy)
-    end
+ local function htpatch(char)
+  if hsnap~=0 then
+   local height=char.height
+   if height then
+    char.height=char.height+2*dy
    end
   end
  end
- setmathcharacters=function(tfmdata,characters,mathparameters,dx,dy,squeeze,wdelta,hdelta,ddelta)
-  local function wdpatch(char)
-   if wsnap~=0 then
-    char.width=char.width+wdelta/2
-   end
-  end
-  local function htpatch(char)
-   if hsnap~=0 then
-    local height=char.height
-    if height then
-     char.height=char.height+2*dy
-    end
-   end
-  end
-  local character=characters[0x221A]
-  if character and character.next then
-   local char=character
-   local next=character.next
+ local character=characters[0x221A]
+ if character and character.next then
+  local char=character
+  local next=character.next
+  wdpatch(char)
+  htpatch(char)
+  while next do
+   char=characters[next]
    wdpatch(char)
    htpatch(char)
-   while next do
-    char=characters[next]
-    wdpatch(char)
-    htpatch(char)
-    next=char.next
-   end
-   if char then
-    local v=char.vert_variants
-    if v then
-     local top=v[#v]
-     if top then
-      local char=characters[top.glyph]
-      htpatch(char)
-     end
+   next=char.next
+  end
+  if char then
+   local v=char.vert_variants
+   if v then
+    local top=v[#v]
+    if top then
+     local char=characters[top.glyph]
+     htpatch(char)
     end
    end
   end
  end
 end
-local shiftmode=CONTEXTLMTXMODE and CONTEXTLMTXMODE>0
 local function manipulateeffect(tfmdata)
  local effect=tfmdata.properties.effect
  if effect then
@@ -37269,13 +38292,8 @@
   local factor=(1+effect.factor)*factor
   local hfactor=(1+effect.hfactor)*hfactor
   local vfactor=(1+effect.vfactor)*vfactor
-  if shiftmode then
-   parameters.hshift=hshift
-   parameters.vshift=vshift
-  else
-   vshift=vshift~=0 and upcommand[vshift] or false
-   hshift=rightcommand[hshift]
-  end
+  vshift=vshift~=0 and upcommand[vshift] or false
+  hshift=rightcommand[hshift]
   for unicode,character in next,characters do
    local oldwidth=character.width
    local oldheight=character.height
@@ -37282,32 +38300,30 @@
    local olddepth=character.depth
    if oldwidth and oldwidth>0 then
     character.width=oldwidth+wdelta
-    if not shiftmode then
-     local commands=character.commands
-     if vshift then
-      if commands then
+    local commands=character.commands
+    if vshift then
+     if commands then
+      prependcommands (commands,
+       hshift,
+       vshift
+      )
+     else
+      character.commands={
+       hshift,
+       vshift,
+       charcommand[unicode]
+      }
+     end
+    else
+     if commands then
        prependcommands (commands,
-        hshift,
-        vshift
+        hshift
        )
-      else
-       character.commands={
-        hshift,
-        vshift,
-        charcommand[unicode]
+     else
+      character.commands={
+       hshift,
+       charcommand[unicode]
        }
-      end
-     else
-      if commands then
-        prependcommands (commands,
-         hshift
-        )
-      else
-       character.commands={
-        hshift,
-        charcommand[unicode]
-        }
-      end
      end
     end
    end
@@ -39469,8 +40485,8 @@
 local fonts=fonts
 local nodes=nodes
 local nuts=nodes.nuts 
-local traverse_id=nuts.traverse_id
-local flush_node=nuts.flush_node
+local traverseid=nuts.traverseid
+local flushnode=nuts.flushnode
 local glyph_code=nodes.nodecodes.glyph
 local disc_code=nodes.nodecodes.disc
 local tonode=nuts.tonode
@@ -39529,7 +40545,7 @@
   local variants=nil
   local redundant=nil
   local nofused=0
-  for n in traverse_id(glyph_code,head) do
+  for n in traverseid(glyph_code,head) do
    local font=getfont(n)
    if font~=prevfont then
     if basefont then
@@ -39607,13 +40623,13 @@
       end
      end
     end
-    flush_node(r)
+    flushnode(r)
    end
   end
-  for d in traverse_id(disc_code,head) do
+  for d in traverseid(disc_code,head) do
    local _,_,r=getdisc(d)
    if r then
-    for n in traverse_id(glyph_code,r) do
+    for n in traverseid(glyph_code,r) do
      local font=getfont(n)
      if font~=prevfont then
       prevfont=font
@@ -39681,7 +40697,7 @@
  end
  return head
 end
-local protectpass=node.direct.protect_glyphs
+local protectpass=node.direct.protectglyphs or node.direct.protect_glyphs
 local injectpass=nodes.injections.handler
 function nodes.handlers.nodepass(head,...)
  if head then

Modified: trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-math.lua
===================================================================
--- trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-math.lua	2023-02-26 14:47:05 UTC (rev 66178)
+++ trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-math.lua	2023-02-26 14:47:57 UTC (rev 66179)
@@ -33,7 +33,6 @@
     [0x1D551] = 0x02124,
 }
 
-
 local function fixmath(tfmdata,key,value)
     if value then
         local characters = tfmdata.characters
@@ -51,3 +50,156 @@
         node = fixmath,
     }
 }
+
+-- This emulation is experimental and work in progress. This plain support is
+-- for testing only anyway. If needed disable the feature which is there mostly
+-- for MS and HH (a side effect of their math project).
+
+local emulate = true
+
+local integrals = table.tohash { 8747, 8748, 8749, 8750, 8751, 8752, 8753, 8754,
+8755, 8992, 8993, 10763, 10764, 10765, 10766, 10767, 10768, 10769, 10770, 10771,
+10772, 10773, 10774, 10775, 10776, 10777, 10778, 10779, 10780 }
+
+local italics = table.tohash { 8458, 8459, 8462, 8464, 8466, 8475, 8492, 8495,
+8496, 8497, 8499, 8500, 119860, 119861, 119862, 119863, 119864, 119865, 119866,
+119867, 119868, 119869, 119870, 119871, 119872, 119873, 119874, 119875, 119876,
+119877, 119878, 119879, 119880, 119881, 119882, 119883, 119884, 119885, 119886,
+119887, 119888, 119889, 119890, 119891, 119892, 119893, 119894, 119895, 119896,
+119897, 119898, 119899, 119900, 119901, 119902, 119903, 119904, 119905, 119906,
+119907, 119908, 119909, 119910, 119911, 119912, 119913, 119914, 119915, 119916,
+119917, 119918, 119919, 119920, 119921, 119922, 119923, 119924, 119925, 119926,
+119927, 119928, 119929, 119930, 119931, 119932, 119933, 119934, 119935, 119936,
+119937, 119938, 119939, 119940, 119941, 119942, 119943, 119944, 119945, 119946,
+119947, 119948, 119949, 119950, 119951, 119952, 119953, 119954, 119955, 119956,
+119957, 119958, 119959, 119960, 119961, 119962, 119963, 119964, 119965, 119966,
+119967, 119968, 119969, 119970, 119971, 119972, 119973, 119974, 119975, 119976,
+119977, 119978, 119979, 119980, 119981, 119982, 119983, 119984, 119985, 119986,
+119987, 119988, 119989, 119990, 119991, 119992, 119993, 119994, 119995, 119996,
+119997, 119998, 119999, 120000, 120001, 120002, 120003, 120004, 120005, 120006,
+120007, 120008, 120009, 120010, 120011, 120012, 120013, 120014, 120015, 120016,
+120017, 120018, 120019, 120020, 120021, 120022, 120023, 120024, 120025, 120026,
+120027, 120028, 120029, 120030, 120031, 120032, 120033, 120034, 120035, 120036,
+120037, 120038, 120039, 120040, 120041, 120042, 120043, 120044, 120045, 120046,
+120047, 120048, 120049, 120050, 120051, 120052, 120053, 120054, 120055, 120056,
+120057, 120058, 120059, 120060, 120061, 120062, 120063, 120064, 120065, 120066,
+120067, 120328, 120329, 120330, 120331, 120332, 120333, 120334, 120335, 120336,
+120337, 120338, 120339, 120340, 120341, 120342, 120343, 120344, 120345, 120346,
+120347, 120348, 120349, 120350, 120351, 120352, 120353, 120354, 120355, 120356,
+120357, 120358, 120359, 120360, 120361, 120362, 120363, 120364, 120365, 120366,
+120367, 120368, 120369, 120370, 120371, 120372, 120373, 120374, 120375, 120376,
+120377, 120378, 120379, 120380, 120381, 120382, 120383, 120384, 120385, 120386,
+120387, 120388, 120389, 120390, 120391, 120392, 120393, 120394, 120395, 120396,
+120397, 120398, 120399, 120400, 120401, 120402, 120403, 120404, 120405, 120406,
+120407, 120408, 120409, 120410, 120411, 120412, 120413, 120414, 120415, 120416,
+120417, 120418, 120419, 120420, 120421, 120422, 120423, 120424, 120425, 120426,
+120427, 120428, 120429, 120430, 120431, 120546, 120547, 120548, 120549, 120550,
+120551, 120552, 120553, 120554, 120555, 120556, 120557, 120558, 120559, 120560,
+120561, 120562, 120563, 120564, 120565, 120566, 120567, 120568, 120569, 120570,
+120571, 120572, 120573, 120574, 120575, 120576, 120577, 120578, 120579, 120580,
+120581, 120582, 120583, 120584, 120585, 120586, 120587, 120588, 120589, 120590,
+120591, 120592, 120593, 120594, 120595, 120596, 120597, 120604, 120605, 120606,
+120607, 120608, 120609, 120610, 120611, 120612, 120613, 120614, 120615, 120616,
+120617, 120618, 120619, 120620, 120621, 120622, 120623, 120624, 120625, 120626,
+120627, 120628, 120629, 120630, 120631, 120632, 120633, 120634, 120635, 120636,
+120637, 120638, 120639, 120640, 120641, 120642, 120643, 120644, 120645, 120646,
+120647, 120648, 120649, 120650, 120651, 120652, 120653, 120654, 120655, 120720,
+120721, 120722, 120723, 120724, 120725, 120726, 120727, 120728, 120729, 120730,
+120731, 120732, 120733, 120734, 120735, 120736, 120737, 120738, 120739, 120740,
+120741, 120742, 120743, 120744, 120745, 120746, 120747, 120748, 120749, 120750,
+120751, 120752, 120753, 120754, 120755, 120756, 120757, 120758, 120759, 120760,
+120761, 120762, 120763, 120764, 120765, 120766, 120767, 120768, 120769, 120770,
+120771 }
+
+local function emulatelmtx(tfmdata,key,value)
+    if tfmdata.mathparameters and not tfmdata.emulatedlmtx then
+        tfmdata.fonts = { { id = 0 } }
+        tfmdata.type = "virtual"
+        tfmdata.properties.virtualized = true
+    end
+end
+
+fonts.handlers.otf.features.register {
+    name         = "emulate lmtx",
+    description  = "emulate lmtx mode",
+    default      = emulate,
+    manipulators = { base = emulatelmtx },
+}
+
+local function emulatelmtx(tfmdata,key,value)
+    if tfmdata.mathparameters and not tfmdata.emulatedlmtx then
+        local targetcharacters   = tfmdata.characters
+        local targetdescriptions = tfmdata.descriptions
+        local factor             = tfmdata.parameters.factor
+        local function getllx(u)
+            local d = targetdescriptions[u]
+            if d then
+                local b = d.boundingbox
+                if b then
+                    local llx = b[1]
+                    if llx < 0 then
+                        return - llx
+                    end
+                end
+            end
+            return false
+        end
+        for u, c in next, targetcharacters do
+            local uc = c.unicode or u
+            if integrals[uc] then
+                -- skip this one
+            else
+                local accent = c.top_accent
+                local italic = c.italic
+                local width  = c.width  or 0
+                local llx    = getllx(u)
+                local bl, br, tl, tr
+                if llx then
+                    llx   = llx * factor
+                    width = width + llx
+                    bl    = - llx
+                    tl    = bl
+                    c.commands = { { "right", llx }, { "slot", 0, u } }
+                    if accent then
+                        accent = accent + llx
+                    end
+                end
+                if accent then
+                    if italics[uc] then
+                        c.top_accent = accent
+                    else
+                        c.top_accent = nil
+                    end
+                end
+                if italic and italic ~= 0 then
+                    width = width + italic
+                    br    = - italic
+                end
+                c.width = width
+                if italic then
+                    c.italic = nil
+                end
+                if bl or br or tl or tr then
+                    -- watch out: singular and _ because we are post copying / scaling
+                    c.mathkern = {
+                        bottom_left  = bl and { { height = 0,             kern = bl } } or nil,
+                        bottom_right = br and { { height = 0,             kern = br } } or nil,
+                        top_left     = tl and { { height = c.height or 0, kern = tl } } or nil,
+                        top_right    = tr and { { height = c.height or 0, kern = tr } } or nil,
+                    }
+                end
+            end
+        end
+        tfmdata.fonts = { { id = 0 } }
+        tfmdata.type = "virtual"
+        tfmdata.properties.virtualized = true
+        tfmdata.emulatedlmtx = true
+    end
+end
+
+fonts.handlers.otf.features.register {
+    name         = "emulate lmtx",
+    description  = "emulate lmtx mode",
+    default      = emulate,
+    manipulators = { base = emulatelmtx },
+}

Modified: trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-mplib.lua
===================================================================
--- trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-mplib.lua	2023-02-26 14:47:05 UTC (rev 66178)
+++ trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-mplib.lua	2023-02-26 14:47:57 UTC (rev 66179)
@@ -350,8 +350,9 @@
         if how == "image" then
             tex.sprint(format("\\MPLIBpdftext{%s}{%s}",what,depth))
         else
-            text = gsub(text,".","\\hbox{%1}") -- kerning happens in metapost
-            tex.sprint(format("\\MPLIBtextext{%s}{%s}{%s}{%s}",font,size,text,depth))
+         -- text = gsub(text,".","\\hbox{%1}") -- kerning happens in metapost
+         -- tex.sprint(format("\\MPLIBtextext{%s}{%s}{%s}{%s}",font,size,text,depth))
+            tex.sprint(format("\\MPLIBtextext{%s}{%s}{\\hpack{\\detokenize{%s}}}{%s}",font,size,text,depth))
         end
     end
 

Modified: trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-pdf.tex
===================================================================
--- trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-pdf.tex	2023-02-26 14:47:05 UTC (rev 66178)
+++ trunk/Master/texmf-dist/tex/generic/context/luatex/luatex-pdf.tex	2023-02-26 14:47:57 UTC (rev 66179)
@@ -137,6 +137,7 @@
     \xdef\pdfgentounicode             {\pdfvariable gentounicode}
     \xdef\pdfomitcidset               {\pdfvariable omitcidset}
     \xdef\pdfomitcharset              {\pdfvariable omitcharset}
+    \xdef\pdfomitinfodict             {\pdfvariable omitinfodict}
     \xdef\pdfpagebox                  {\pdfvariable pagebox}
     \xdef\pdfmajorversion             {\pdfvariable majorversion}
     \xdef\pdfminorversion             {\pdfvariable minorversion}
@@ -182,6 +183,7 @@
     \global\pdfgentounicode          0
   % \global\pdfomitcidset            0
   % \global\pdfomitcharset           0
+  % \global\pdfomitinfodict          0
     \global\pdfpagebox               0
   % \global\pdfmajorversion          1
     \global\pdfminorversion          4



More information about the tex-live-commits mailing list.