diff options
81 files changed, 3396 insertions, 1443 deletions
diff --git a/doc/context/documents/general/manuals/luatex.pdf b/doc/context/documents/general/manuals/luatex.pdf Binary files differindex 32d761a5e..b139a1456 100644 --- a/doc/context/documents/general/manuals/luatex.pdf +++ b/doc/context/documents/general/manuals/luatex.pdf diff --git a/doc/context/documents/general/qrcs/setup-cs.pdf b/doc/context/documents/general/qrcs/setup-cs.pdf Binary files differindex 27503d751..aaae6e780 100644 --- a/doc/context/documents/general/qrcs/setup-cs.pdf +++ b/doc/context/documents/general/qrcs/setup-cs.pdf diff --git a/doc/context/documents/general/qrcs/setup-de.pdf b/doc/context/documents/general/qrcs/setup-de.pdf Binary files differindex 60ed45aed..37b3f2968 100644 --- a/doc/context/documents/general/qrcs/setup-de.pdf +++ b/doc/context/documents/general/qrcs/setup-de.pdf diff --git a/doc/context/documents/general/qrcs/setup-en.pdf b/doc/context/documents/general/qrcs/setup-en.pdf Binary files differindex 42aaf874c..22a0fdd48 100644 --- a/doc/context/documents/general/qrcs/setup-en.pdf +++ b/doc/context/documents/general/qrcs/setup-en.pdf diff --git a/doc/context/documents/general/qrcs/setup-fr.pdf b/doc/context/documents/general/qrcs/setup-fr.pdf Binary files differindex 14c3836a6..dc28d50ae 100644 --- a/doc/context/documents/general/qrcs/setup-fr.pdf +++ b/doc/context/documents/general/qrcs/setup-fr.pdf diff --git a/doc/context/documents/general/qrcs/setup-it.pdf b/doc/context/documents/general/qrcs/setup-it.pdf Binary files differindex ae7ac6615..821a84b03 100644 --- a/doc/context/documents/general/qrcs/setup-it.pdf +++ b/doc/context/documents/general/qrcs/setup-it.pdf diff --git a/doc/context/documents/general/qrcs/setup-nl.pdf b/doc/context/documents/general/qrcs/setup-nl.pdf Binary files differindex 4c758ad3c..1b7c303ea 100644 --- a/doc/context/documents/general/qrcs/setup-nl.pdf +++ b/doc/context/documents/general/qrcs/setup-nl.pdf diff --git a/doc/context/documents/general/qrcs/setup-ro.pdf b/doc/context/documents/general/qrcs/setup-ro.pdf Binary files differindex 617df4672..e8e100c87 100644 --- a/doc/context/documents/general/qrcs/setup-ro.pdf +++ b/doc/context/documents/general/qrcs/setup-ro.pdf diff --git a/doc/context/sources/general/manuals/luatex/luatex-nodes.tex b/doc/context/sources/general/manuals/luatex/luatex-nodes.tex index 0947bb58b..f238a58db 100644 --- a/doc/context/sources/general/manuals/luatex/luatex-nodes.tex +++ b/doc/context/sources/general/manuals/luatex/luatex-nodes.tex @@ -1560,26 +1560,29 @@ optional. The two returned nodes \type {h} and \type {t} are the head and tail  (either one of these can be an inserted kern node, because special kernings with  word boundaries are possible). -\subsubsection{\type {node.unprotect_glyphs}} +\subsubsection{\type {node.unprotect_glyphs} and \type {node.unprotect_glyph}}  \startfunctioncall -node.unprotect_glyphs(<node> n) +node.unprotect_glyph(<node> n) +node.unprotect_glyphs(<node> n,[<node> n])  \stopfunctioncall  Subtracts 256 from all glyph node subtypes. This and the next function are  helpers to convert from \type {characters} to \type {glyphs} during node -processing. +processing. The second argument is option and indicates the end of a range.  \subsubsection{\type {node.protect_glyphs} and \type {node.protect_glyph}}  \startfunctioncall -node.protect_glyphs(<node> n) +node.protect_glyph(<node> n) +node.protect_glyphs(<node> n,[<node> n])  \stopfunctioncall  Adds 256 to all glyph node subtypes in the node list starting at \type {n},  except that if the value is 1, it adds only 255. The special handling of 1 means  that \type {characters} will become \type {glyphs} after subtraction of 256. A -single character can be marked by the singular call. +single character can be marked by the singular call. The second argument is +option and indicates the end of a range.  \subsubsection{\type {node.last_node}} @@ -1752,6 +1755,15 @@ node.check_discretionaries(<node> head)  The plural variant runs over all disc nodes in a list, the singular variant  checks one node only (it also checks if the node is a disc node). +\subsubsection{\type {node.flatten_discretionaries}} + +This function will remove the discretionaries in the list and inject the replace +field when set. + +\startfunctioncall +<node> head, count = node.flatten_discretionaries(<node> n) +\stopfunctioncall +  \subsubsection{\type {node.family_font}}  When you pass it a proper family identifier the next helper will return the font diff --git a/doc/context/sources/general/manuals/luatex/luatex.tex b/doc/context/sources/general/manuals/luatex/luatex.tex index 79f3ba186..cc0d5fc9f 100644 --- a/doc/context/sources/general/manuals/luatex/luatex.tex +++ b/doc/context/sources/general/manuals/luatex/luatex.tex @@ -1,6 +1,6 @@  % macros=mkvi -% author    : Hans Hagen +% author    : Hans Hagen with Taco Hoekwater, Luigi Scarso & Hartmut Henkel  % copyright : PRAGMA ADE & ConTeXt Development Team  % license   : Creative Commons Attribution ShareAlike 4.0 International  % reference : pragma-ade.nl | contextgarden.net | texlive (related) distributions @@ -13,9 +13,13 @@  %             the manuals contain characteristic graphics and personal notes or examples that  %             make no sense when used out-of-context.  % -% comment   : Some chapters might have been published in TugBoat, the NTG Maps, the ConTeXt -%             Group journal or otherwise. Thanks to the editors for corrections. Also thanks -%             to users for testing, feedback and corrections. +% comment   : Some (parts of) chapters might have been published in TugBoat, the NTG Maps, the +%             ConTeXt Group journal or otherwise. Thanks to the editors for corrections. Also +%             thanks to users for testing, feedback and corrections. +% +% 238 pages : 2017-07-06 +%             luatex 9.5 sec / luajittex 7.0 sec +%             Dell 7600 / i7 3840QM / passmark 1.922 / Windows 10 64 bit  % \tex vs \type vs \syntax vs. \luatex  % \em \it \/ diff --git a/doc/context/sources/general/manuals/workflows/workflows-synctex.tex b/doc/context/sources/general/manuals/workflows/workflows-synctex.tex index d5b73a8e1..8f1fac632 100644 --- a/doc/context/sources/general/manuals/workflows/workflows-synctex.tex +++ b/doc/context/sources/general/manuals/workflows/workflows-synctex.tex @@ -172,6 +172,7 @@ In case you want to see what gets synced you can enable a tracker:  \starttyping  \enabletrackers[system.synctex.visualize] +\enabletrackers[system.synctex.visualize=real]  \stoptyping  The following tracker outputs some status information about \XML\ flushing. Such diff --git a/scripts/context/lua/mtx-fonts.lua b/scripts/context/lua/mtx-fonts.lua index d077e0198..a24c044e3 100644 --- a/scripts/context/lua/mtx-fonts.lua +++ b/scripts/context/lua/mtx-fonts.lua @@ -16,7 +16,7 @@ local lower = string.lower  local concat = table.concat  local write_nl = texio.write_nl -local otlversion  = 3.031 +local otlversion  = 3.032  local helpinfo = [[  <?xml version="1.0"?> @@ -286,6 +286,7 @@ local function showfeatures(tag,specification)          report("no features")      end      report() +    collectgarbage("collect")  end  local function reloadbase(reload) @@ -338,6 +339,7 @@ local function list_matches(t,info)              for k=1,#s do                  local v = s[k]                  showfeatures(v,t[v]) +                collectgarbage("collect") -- in case we load a lot              end          else              for k=1,#s do diff --git a/scripts/context/lua/mtxrun.lua b/scripts/context/lua/mtxrun.lua index e27b3712c..02447ad3a 100644 --- a/scripts/context/lua/mtxrun.lua +++ b/scripts/context/lua/mtxrun.lua @@ -447,7 +447,7 @@ do -- create closure to overcome 200 locals limit  package.loaded["l-package"] = package.loaded["l-package"] or true --- original size: 10587, stripped down to: 7815 +-- original size: 11455, stripped down to: 8625  if not modules then modules={} end modules ['l-package']={    version=1.001, @@ -461,6 +461,7 @@ local gsub,format,find=string.gsub,string.format,string.find  local P,S,Cs,lpegmatch=lpeg.P,lpeg.S,lpeg.Cs,lpeg.match  local package=package  local searchers=package.searchers or package.loaders +local insert,remove=table.insert,table.remove  local filejoin=file and file.join    or function(path,name)  return path.."/"..name end  local isreadable=file and file.is_readable or function(name)    local f=io.open(name) if f then f:close() return true end end  local addsuffix=file and file.addsuffix  or function(name,suffix) return name.."."..suffix end @@ -594,15 +595,43 @@ local function registerpath(tag,what,target,...)        add(path)      end    end -  return paths +end +local function pushpath(tag,what,target,path) +  local path=helpers.cleanpath(path) +  insert(target,1,path) +  if helpers.trace then +    helpers.report("pushing %s path in front: %s",tag,path) +  end +end +local function poppath(tag,what,target) +  local path=remove(target,1) +  if helpers.trace then +    if path then +      helpers.report("popping %s path from front: %s",tag,path) +    else +      helpers.report("no %s path to pop",tag) +    end +  end  end  helpers.registerpath=registerpath  function package.extraluapath(...)    registerpath("extra lua","lua",extraluapaths,...)  end +function package.pushluapath(path) +  pushpath("extra lua","lua",extraluapaths,path) +end +function package.popluapath() +  poppath("extra lua","lua",extraluapaths) +end  function package.extralibpath(...)    registerpath("extra lib","lib",extralibpaths,...)  end +function package.pushlibpath(path) +  pushpath("extra lib","lib",extralibpaths,path) +end +function package.poplibpath() +  poppath("extra lib","lua",extralibpaths) +end  local function loadedaslib(resolved,rawname)     local base=gsub(rawname,"%.","_")    local init="luaopen_"..gsub(base,"%.","_") @@ -5678,7 +5707,7 @@ do -- create closure to overcome 200 locals limit  package.loaded["util-str"] = package.loaded["util-str"] or true --- original size: 36148, stripped down to: 20179 +-- original size: 37003, stripped down to: 20839  if not modules then modules={} end modules ['util-str']={    version=1.001, @@ -5892,6 +5921,21 @@ function number.formatted(n,sep1,sep2)      return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".")    end  end +local p=Cs( +    P("-")^0*(P("0")^1/"")^0*(1-P("."))^0*(P(".")*P("0")^1*P(-1)/""+P(".")^0)*P(1-P("0")^1*P(-1))^0 +  ) +function number.compactfloat(n,fmt) +  if n==0 then +    return "0" +  elseif n==1 then +    return "1" +  end +  n=lpegmatch(p,format(fmt or "%0.3f",n)) +  if n=="." or n=="" or n=="-" then +    return "0" +  end +  return n +end  local zero=P("0")^1/""  local plus=P("+")/""  local minus=P("-") @@ -6404,6 +6448,21 @@ local pattern=Cs((newline/(os.newline or "\r")+1)^0)  function string.replacenewlines(str)    return lpegmatch(pattern,str)  end +function strings.newcollector() +  local result,r={},0 +  return +    function(fmt,str,...)  +      r=r+1 +      result[r]=str==nil and fmt or formatters[fmt](str,...) +    end, +    function(connector)  +      if result then +        local str=concat(result,connector) +        result,r={},0 +        return str +      end +    end +end  end -- of closure @@ -6412,7 +6471,7 @@ do -- create closure to overcome 200 locals limit  package.loaded["util-tab"] = package.loaded["util-tab"] or true --- original size: 27407, stripped down to: 17116 +-- original size: 27638, stripped down to: 17116  if not modules then modules={} end modules ['util-tab']={    version=1.001, @@ -10425,7 +10484,7 @@ do -- create closure to overcome 200 locals limit  package.loaded["util-sbx"] = package.loaded["util-sbx"] or true --- original size: 20309, stripped down to: 13848 +-- original size: 20388, stripped down to: 13919  if not modules then modules={} end modules ['util-sbx']={    version=1.001, @@ -10450,6 +10509,7 @@ local concat=string.concat  local unquoted=string.unquoted  local optionalquoted=string.optionalquoted  local basename=file.basename +local nameonly=file.nameonly  local sandbox=sandbox  local validroots={}  local validrunners={} @@ -10529,9 +10589,9 @@ local function registerlibrary(name)        return      end      if validlibraries==true then -      validlibraries={ [name]=true } +      validlibraries={ [nameonly(name)]=true }      else -      validlibraries[name]=true +      validlibraries[nameonly(name)]=true      end    elseif name==true then      validlibraries={} @@ -10856,15 +10916,15 @@ if FFISUPPORTED and ffi then        end      end    end -  local load=ffi.load -  if load then +  local fiiload=ffi.load +  if fiiload then      local reported={}      function ffi.load(name,...)        if validlibraries==false then        elseif validlibraries==true then -        return load(name,...) -      elseif validlibraries[name] then -        return load(name,...) +        return fiiload(name,...) +      elseif validlibraries[nameonly(name)] then +        return fiiload(name,...)        else        end        if not reported[name] then @@ -19967,7 +20027,7 @@ do -- create closure to overcome 200 locals limit  package.loaded["util-lib"] = package.loaded["util-lib"] or true --- original size: 14131, stripped down to: 7859 +-- original size: 14333, stripped down to: 7859  if not modules then modules={} end modules ['util-lib']={    version=1.001, @@ -20571,8 +20631,8 @@ end -- of closure  -- used libraries    : l-lua.lua l-sandbox.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-fil.lua util-sac.lua util-sto.lua util-prs.lua util-fmt.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-tpl.lua util-sbx.lua util-mrg.lua util-env.lua luat-env.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.lua data-ini.lua data-exp.lua data-env.lua data-tmp.lua data-met.lua data-res.lua data-pre.lua data-inp.lua data-out.lua data-fil.lua data-con.lua data-use.lua data-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua util-lib.lua luat-sta.lua luat-fmt.lua  -- skipped libraries : - --- original bytes    : 846363 --- stripped bytes    : 306226 +-- original bytes    : 848598 +-- stripped bytes    : 306920  -- end library merge diff --git a/scripts/context/stubs/mswin/mtxrun.lua b/scripts/context/stubs/mswin/mtxrun.lua index e27b3712c..02447ad3a 100644 --- a/scripts/context/stubs/mswin/mtxrun.lua +++ b/scripts/context/stubs/mswin/mtxrun.lua @@ -447,7 +447,7 @@ do -- create closure to overcome 200 locals limit  package.loaded["l-package"] = package.loaded["l-package"] or true --- original size: 10587, stripped down to: 7815 +-- original size: 11455, stripped down to: 8625  if not modules then modules={} end modules ['l-package']={    version=1.001, @@ -461,6 +461,7 @@ local gsub,format,find=string.gsub,string.format,string.find  local P,S,Cs,lpegmatch=lpeg.P,lpeg.S,lpeg.Cs,lpeg.match  local package=package  local searchers=package.searchers or package.loaders +local insert,remove=table.insert,table.remove  local filejoin=file and file.join    or function(path,name)  return path.."/"..name end  local isreadable=file and file.is_readable or function(name)    local f=io.open(name) if f then f:close() return true end end  local addsuffix=file and file.addsuffix  or function(name,suffix) return name.."."..suffix end @@ -594,15 +595,43 @@ local function registerpath(tag,what,target,...)        add(path)      end    end -  return paths +end +local function pushpath(tag,what,target,path) +  local path=helpers.cleanpath(path) +  insert(target,1,path) +  if helpers.trace then +    helpers.report("pushing %s path in front: %s",tag,path) +  end +end +local function poppath(tag,what,target) +  local path=remove(target,1) +  if helpers.trace then +    if path then +      helpers.report("popping %s path from front: %s",tag,path) +    else +      helpers.report("no %s path to pop",tag) +    end +  end  end  helpers.registerpath=registerpath  function package.extraluapath(...)    registerpath("extra lua","lua",extraluapaths,...)  end +function package.pushluapath(path) +  pushpath("extra lua","lua",extraluapaths,path) +end +function package.popluapath() +  poppath("extra lua","lua",extraluapaths) +end  function package.extralibpath(...)    registerpath("extra lib","lib",extralibpaths,...)  end +function package.pushlibpath(path) +  pushpath("extra lib","lib",extralibpaths,path) +end +function package.poplibpath() +  poppath("extra lib","lua",extralibpaths) +end  local function loadedaslib(resolved,rawname)     local base=gsub(rawname,"%.","_")    local init="luaopen_"..gsub(base,"%.","_") @@ -5678,7 +5707,7 @@ do -- create closure to overcome 200 locals limit  package.loaded["util-str"] = package.loaded["util-str"] or true --- original size: 36148, stripped down to: 20179 +-- original size: 37003, stripped down to: 20839  if not modules then modules={} end modules ['util-str']={    version=1.001, @@ -5892,6 +5921,21 @@ function number.formatted(n,sep1,sep2)      return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".")    end  end +local p=Cs( +    P("-")^0*(P("0")^1/"")^0*(1-P("."))^0*(P(".")*P("0")^1*P(-1)/""+P(".")^0)*P(1-P("0")^1*P(-1))^0 +  ) +function number.compactfloat(n,fmt) +  if n==0 then +    return "0" +  elseif n==1 then +    return "1" +  end +  n=lpegmatch(p,format(fmt or "%0.3f",n)) +  if n=="." or n=="" or n=="-" then +    return "0" +  end +  return n +end  local zero=P("0")^1/""  local plus=P("+")/""  local minus=P("-") @@ -6404,6 +6448,21 @@ local pattern=Cs((newline/(os.newline or "\r")+1)^0)  function string.replacenewlines(str)    return lpegmatch(pattern,str)  end +function strings.newcollector() +  local result,r={},0 +  return +    function(fmt,str,...)  +      r=r+1 +      result[r]=str==nil and fmt or formatters[fmt](str,...) +    end, +    function(connector)  +      if result then +        local str=concat(result,connector) +        result,r={},0 +        return str +      end +    end +end  end -- of closure @@ -6412,7 +6471,7 @@ do -- create closure to overcome 200 locals limit  package.loaded["util-tab"] = package.loaded["util-tab"] or true --- original size: 27407, stripped down to: 17116 +-- original size: 27638, stripped down to: 17116  if not modules then modules={} end modules ['util-tab']={    version=1.001, @@ -10425,7 +10484,7 @@ do -- create closure to overcome 200 locals limit  package.loaded["util-sbx"] = package.loaded["util-sbx"] or true --- original size: 20309, stripped down to: 13848 +-- original size: 20388, stripped down to: 13919  if not modules then modules={} end modules ['util-sbx']={    version=1.001, @@ -10450,6 +10509,7 @@ local concat=string.concat  local unquoted=string.unquoted  local optionalquoted=string.optionalquoted  local basename=file.basename +local nameonly=file.nameonly  local sandbox=sandbox  local validroots={}  local validrunners={} @@ -10529,9 +10589,9 @@ local function registerlibrary(name)        return      end      if validlibraries==true then -      validlibraries={ [name]=true } +      validlibraries={ [nameonly(name)]=true }      else -      validlibraries[name]=true +      validlibraries[nameonly(name)]=true      end    elseif name==true then      validlibraries={} @@ -10856,15 +10916,15 @@ if FFISUPPORTED and ffi then        end      end    end -  local load=ffi.load -  if load then +  local fiiload=ffi.load +  if fiiload then      local reported={}      function ffi.load(name,...)        if validlibraries==false then        elseif validlibraries==true then -        return load(name,...) -      elseif validlibraries[name] then -        return load(name,...) +        return fiiload(name,...) +      elseif validlibraries[nameonly(name)] then +        return fiiload(name,...)        else        end        if not reported[name] then @@ -19967,7 +20027,7 @@ do -- create closure to overcome 200 locals limit  package.loaded["util-lib"] = package.loaded["util-lib"] or true --- original size: 14131, stripped down to: 7859 +-- original size: 14333, stripped down to: 7859  if not modules then modules={} end modules ['util-lib']={    version=1.001, @@ -20571,8 +20631,8 @@ end -- of closure  -- used libraries    : l-lua.lua l-sandbox.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-fil.lua util-sac.lua util-sto.lua util-prs.lua util-fmt.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-tpl.lua util-sbx.lua util-mrg.lua util-env.lua luat-env.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.lua data-ini.lua data-exp.lua data-env.lua data-tmp.lua data-met.lua data-res.lua data-pre.lua data-inp.lua data-out.lua data-fil.lua data-con.lua data-use.lua data-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua util-lib.lua luat-sta.lua luat-fmt.lua  -- skipped libraries : - --- original bytes    : 846363 --- stripped bytes    : 306226 +-- original bytes    : 848598 +-- stripped bytes    : 306920  -- end library merge diff --git a/scripts/context/stubs/unix/mtxrun b/scripts/context/stubs/unix/mtxrun index e27b3712c..02447ad3a 100644 --- a/scripts/context/stubs/unix/mtxrun +++ b/scripts/context/stubs/unix/mtxrun @@ -447,7 +447,7 @@ do -- create closure to overcome 200 locals limit  package.loaded["l-package"] = package.loaded["l-package"] or true --- original size: 10587, stripped down to: 7815 +-- original size: 11455, stripped down to: 8625  if not modules then modules={} end modules ['l-package']={    version=1.001, @@ -461,6 +461,7 @@ local gsub,format,find=string.gsub,string.format,string.find  local P,S,Cs,lpegmatch=lpeg.P,lpeg.S,lpeg.Cs,lpeg.match  local package=package  local searchers=package.searchers or package.loaders +local insert,remove=table.insert,table.remove  local filejoin=file and file.join    or function(path,name)  return path.."/"..name end  local isreadable=file and file.is_readable or function(name)    local f=io.open(name) if f then f:close() return true end end  local addsuffix=file and file.addsuffix  or function(name,suffix) return name.."."..suffix end @@ -594,15 +595,43 @@ local function registerpath(tag,what,target,...)        add(path)      end    end -  return paths +end +local function pushpath(tag,what,target,path) +  local path=helpers.cleanpath(path) +  insert(target,1,path) +  if helpers.trace then +    helpers.report("pushing %s path in front: %s",tag,path) +  end +end +local function poppath(tag,what,target) +  local path=remove(target,1) +  if helpers.trace then +    if path then +      helpers.report("popping %s path from front: %s",tag,path) +    else +      helpers.report("no %s path to pop",tag) +    end +  end  end  helpers.registerpath=registerpath  function package.extraluapath(...)    registerpath("extra lua","lua",extraluapaths,...)  end +function package.pushluapath(path) +  pushpath("extra lua","lua",extraluapaths,path) +end +function package.popluapath() +  poppath("extra lua","lua",extraluapaths) +end  function package.extralibpath(...)    registerpath("extra lib","lib",extralibpaths,...)  end +function package.pushlibpath(path) +  pushpath("extra lib","lib",extralibpaths,path) +end +function package.poplibpath() +  poppath("extra lib","lua",extralibpaths) +end  local function loadedaslib(resolved,rawname)     local base=gsub(rawname,"%.","_")    local init="luaopen_"..gsub(base,"%.","_") @@ -5678,7 +5707,7 @@ do -- create closure to overcome 200 locals limit  package.loaded["util-str"] = package.loaded["util-str"] or true --- original size: 36148, stripped down to: 20179 +-- original size: 37003, stripped down to: 20839  if not modules then modules={} end modules ['util-str']={    version=1.001, @@ -5892,6 +5921,21 @@ function number.formatted(n,sep1,sep2)      return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".")    end  end +local p=Cs( +    P("-")^0*(P("0")^1/"")^0*(1-P("."))^0*(P(".")*P("0")^1*P(-1)/""+P(".")^0)*P(1-P("0")^1*P(-1))^0 +  ) +function number.compactfloat(n,fmt) +  if n==0 then +    return "0" +  elseif n==1 then +    return "1" +  end +  n=lpegmatch(p,format(fmt or "%0.3f",n)) +  if n=="." or n=="" or n=="-" then +    return "0" +  end +  return n +end  local zero=P("0")^1/""  local plus=P("+")/""  local minus=P("-") @@ -6404,6 +6448,21 @@ local pattern=Cs((newline/(os.newline or "\r")+1)^0)  function string.replacenewlines(str)    return lpegmatch(pattern,str)  end +function strings.newcollector() +  local result,r={},0 +  return +    function(fmt,str,...)  +      r=r+1 +      result[r]=str==nil and fmt or formatters[fmt](str,...) +    end, +    function(connector)  +      if result then +        local str=concat(result,connector) +        result,r={},0 +        return str +      end +    end +end  end -- of closure @@ -6412,7 +6471,7 @@ do -- create closure to overcome 200 locals limit  package.loaded["util-tab"] = package.loaded["util-tab"] or true --- original size: 27407, stripped down to: 17116 +-- original size: 27638, stripped down to: 17116  if not modules then modules={} end modules ['util-tab']={    version=1.001, @@ -10425,7 +10484,7 @@ do -- create closure to overcome 200 locals limit  package.loaded["util-sbx"] = package.loaded["util-sbx"] or true --- original size: 20309, stripped down to: 13848 +-- original size: 20388, stripped down to: 13919  if not modules then modules={} end modules ['util-sbx']={    version=1.001, @@ -10450,6 +10509,7 @@ local concat=string.concat  local unquoted=string.unquoted  local optionalquoted=string.optionalquoted  local basename=file.basename +local nameonly=file.nameonly  local sandbox=sandbox  local validroots={}  local validrunners={} @@ -10529,9 +10589,9 @@ local function registerlibrary(name)        return      end      if validlibraries==true then -      validlibraries={ [name]=true } +      validlibraries={ [nameonly(name)]=true }      else -      validlibraries[name]=true +      validlibraries[nameonly(name)]=true      end    elseif name==true then      validlibraries={} @@ -10856,15 +10916,15 @@ if FFISUPPORTED and ffi then        end      end    end -  local load=ffi.load -  if load then +  local fiiload=ffi.load +  if fiiload then      local reported={}      function ffi.load(name,...)        if validlibraries==false then        elseif validlibraries==true then -        return load(name,...) -      elseif validlibraries[name] then -        return load(name,...) +        return fiiload(name,...) +      elseif validlibraries[nameonly(name)] then +        return fiiload(name,...)        else        end        if not reported[name] then @@ -19967,7 +20027,7 @@ do -- create closure to overcome 200 locals limit  package.loaded["util-lib"] = package.loaded["util-lib"] or true --- original size: 14131, stripped down to: 7859 +-- original size: 14333, stripped down to: 7859  if not modules then modules={} end modules ['util-lib']={    version=1.001, @@ -20571,8 +20631,8 @@ end -- of closure  -- used libraries    : l-lua.lua l-sandbox.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-fil.lua util-sac.lua util-sto.lua util-prs.lua util-fmt.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-tpl.lua util-sbx.lua util-mrg.lua util-env.lua luat-env.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.lua data-ini.lua data-exp.lua data-env.lua data-tmp.lua data-met.lua data-res.lua data-pre.lua data-inp.lua data-out.lua data-fil.lua data-con.lua data-use.lua data-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua util-lib.lua luat-sta.lua luat-fmt.lua  -- skipped libraries : - --- original bytes    : 846363 --- stripped bytes    : 306226 +-- original bytes    : 848598 +-- stripped bytes    : 306920  -- end library merge diff --git a/scripts/context/stubs/win64/mtxrun.lua b/scripts/context/stubs/win64/mtxrun.lua index e27b3712c..02447ad3a 100644 --- a/scripts/context/stubs/win64/mtxrun.lua +++ b/scripts/context/stubs/win64/mtxrun.lua @@ -447,7 +447,7 @@ do -- create closure to overcome 200 locals limit  package.loaded["l-package"] = package.loaded["l-package"] or true --- original size: 10587, stripped down to: 7815 +-- original size: 11455, stripped down to: 8625  if not modules then modules={} end modules ['l-package']={    version=1.001, @@ -461,6 +461,7 @@ local gsub,format,find=string.gsub,string.format,string.find  local P,S,Cs,lpegmatch=lpeg.P,lpeg.S,lpeg.Cs,lpeg.match  local package=package  local searchers=package.searchers or package.loaders +local insert,remove=table.insert,table.remove  local filejoin=file and file.join    or function(path,name)  return path.."/"..name end  local isreadable=file and file.is_readable or function(name)    local f=io.open(name) if f then f:close() return true end end  local addsuffix=file and file.addsuffix  or function(name,suffix) return name.."."..suffix end @@ -594,15 +595,43 @@ local function registerpath(tag,what,target,...)        add(path)      end    end -  return paths +end +local function pushpath(tag,what,target,path) +  local path=helpers.cleanpath(path) +  insert(target,1,path) +  if helpers.trace then +    helpers.report("pushing %s path in front: %s",tag,path) +  end +end +local function poppath(tag,what,target) +  local path=remove(target,1) +  if helpers.trace then +    if path then +      helpers.report("popping %s path from front: %s",tag,path) +    else +      helpers.report("no %s path to pop",tag) +    end +  end  end  helpers.registerpath=registerpath  function package.extraluapath(...)    registerpath("extra lua","lua",extraluapaths,...)  end +function package.pushluapath(path) +  pushpath("extra lua","lua",extraluapaths,path) +end +function package.popluapath() +  poppath("extra lua","lua",extraluapaths) +end  function package.extralibpath(...)    registerpath("extra lib","lib",extralibpaths,...)  end +function package.pushlibpath(path) +  pushpath("extra lib","lib",extralibpaths,path) +end +function package.poplibpath() +  poppath("extra lib","lua",extralibpaths) +end  local function loadedaslib(resolved,rawname)     local base=gsub(rawname,"%.","_")    local init="luaopen_"..gsub(base,"%.","_") @@ -5678,7 +5707,7 @@ do -- create closure to overcome 200 locals limit  package.loaded["util-str"] = package.loaded["util-str"] or true --- original size: 36148, stripped down to: 20179 +-- original size: 37003, stripped down to: 20839  if not modules then modules={} end modules ['util-str']={    version=1.001, @@ -5892,6 +5921,21 @@ function number.formatted(n,sep1,sep2)      return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".")    end  end +local p=Cs( +    P("-")^0*(P("0")^1/"")^0*(1-P("."))^0*(P(".")*P("0")^1*P(-1)/""+P(".")^0)*P(1-P("0")^1*P(-1))^0 +  ) +function number.compactfloat(n,fmt) +  if n==0 then +    return "0" +  elseif n==1 then +    return "1" +  end +  n=lpegmatch(p,format(fmt or "%0.3f",n)) +  if n=="." or n=="" or n=="-" then +    return "0" +  end +  return n +end  local zero=P("0")^1/""  local plus=P("+")/""  local minus=P("-") @@ -6404,6 +6448,21 @@ local pattern=Cs((newline/(os.newline or "\r")+1)^0)  function string.replacenewlines(str)    return lpegmatch(pattern,str)  end +function strings.newcollector() +  local result,r={},0 +  return +    function(fmt,str,...)  +      r=r+1 +      result[r]=str==nil and fmt or formatters[fmt](str,...) +    end, +    function(connector)  +      if result then +        local str=concat(result,connector) +        result,r={},0 +        return str +      end +    end +end  end -- of closure @@ -6412,7 +6471,7 @@ do -- create closure to overcome 200 locals limit  package.loaded["util-tab"] = package.loaded["util-tab"] or true --- original size: 27407, stripped down to: 17116 +-- original size: 27638, stripped down to: 17116  if not modules then modules={} end modules ['util-tab']={    version=1.001, @@ -10425,7 +10484,7 @@ do -- create closure to overcome 200 locals limit  package.loaded["util-sbx"] = package.loaded["util-sbx"] or true --- original size: 20309, stripped down to: 13848 +-- original size: 20388, stripped down to: 13919  if not modules then modules={} end modules ['util-sbx']={    version=1.001, @@ -10450,6 +10509,7 @@ local concat=string.concat  local unquoted=string.unquoted  local optionalquoted=string.optionalquoted  local basename=file.basename +local nameonly=file.nameonly  local sandbox=sandbox  local validroots={}  local validrunners={} @@ -10529,9 +10589,9 @@ local function registerlibrary(name)        return      end      if validlibraries==true then -      validlibraries={ [name]=true } +      validlibraries={ [nameonly(name)]=true }      else -      validlibraries[name]=true +      validlibraries[nameonly(name)]=true      end    elseif name==true then      validlibraries={} @@ -10856,15 +10916,15 @@ if FFISUPPORTED and ffi then        end      end    end -  local load=ffi.load -  if load then +  local fiiload=ffi.load +  if fiiload then      local reported={}      function ffi.load(name,...)        if validlibraries==false then        elseif validlibraries==true then -        return load(name,...) -      elseif validlibraries[name] then -        return load(name,...) +        return fiiload(name,...) +      elseif validlibraries[nameonly(name)] then +        return fiiload(name,...)        else        end        if not reported[name] then @@ -19967,7 +20027,7 @@ do -- create closure to overcome 200 locals limit  package.loaded["util-lib"] = package.loaded["util-lib"] or true --- original size: 14131, stripped down to: 7859 +-- original size: 14333, stripped down to: 7859  if not modules then modules={} end modules ['util-lib']={    version=1.001, @@ -20571,8 +20631,8 @@ end -- of closure  -- used libraries    : l-lua.lua l-sandbox.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-fil.lua util-sac.lua util-sto.lua util-prs.lua util-fmt.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-tpl.lua util-sbx.lua util-mrg.lua util-env.lua luat-env.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.lua data-ini.lua data-exp.lua data-env.lua data-tmp.lua data-met.lua data-res.lua data-pre.lua data-inp.lua data-out.lua data-fil.lua data-con.lua data-use.lua data-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua util-lib.lua luat-sta.lua luat-fmt.lua  -- skipped libraries : - --- original bytes    : 846363 --- stripped bytes    : 306226 +-- original bytes    : 848598 +-- stripped bytes    : 306920  -- end library merge diff --git a/tex/context/base/mkii/cont-new.mkii b/tex/context/base/mkii/cont-new.mkii index 4e4b70b10..e22f6c377 100644 --- a/tex/context/base/mkii/cont-new.mkii +++ b/tex/context/base/mkii/cont-new.mkii @@ -11,7 +11,7 @@  %C therefore copyrighted by \PRAGMA. See mreadme.pdf for  %C details. -\newcontextversion{2017.07.05 23:01} +\newcontextversion{2017.07.14 19:35}  %D This file is loaded at runtime, thereby providing an  %D excellent place for hacks, patches, extensions and new diff --git a/tex/context/base/mkii/context.mkii b/tex/context/base/mkii/context.mkii index b80487453..9b7f9c968 100644 --- a/tex/context/base/mkii/context.mkii +++ b/tex/context/base/mkii/context.mkii @@ -20,7 +20,7 @@  %D your styles an modules.  \edef\contextformat {\jobname} -\edef\contextversion{2017.07.05 23:01} +\edef\contextversion{2017.07.14 19:35}  %D For those who want to use this: diff --git a/tex/context/base/mkii/mult-en.mkii b/tex/context/base/mkii/mult-en.mkii index a4838128b..e1f59ed93 100644 --- a/tex/context/base/mkii/mult-en.mkii +++ b/tex/context/base/mkii/mult-en.mkii @@ -687,6 +687,7 @@  \setinterfaceconstant{bottomoffset}{bottomoffset}  \setinterfaceconstant{bottomspace}{bottomspace}  \setinterfaceconstant{bottomstate}{bottomstate} +\setinterfaceconstant{break}{break}  \setinterfaceconstant{buffer}{buffer}  \setinterfaceconstant{cache}{cache}  \setinterfaceconstant{calculate}{calculate} diff --git a/tex/context/base/mkii/mult-pe.mkii b/tex/context/base/mkii/mult-pe.mkii index 8b300ae73..46a715d2e 100644 --- a/tex/context/base/mkii/mult-pe.mkii +++ b/tex/context/base/mkii/mult-pe.mkii @@ -687,6 +687,7 @@  \setinterfaceconstant{bottomoffset}{آفستپایین}  \setinterfaceconstant{bottomspace}{فضایپایین}  \setinterfaceconstant{bottomstate}{وضعیتپایین} +\setinterfaceconstant{break}{break}  \setinterfaceconstant{buffer}{buffer}  \setinterfaceconstant{cache}{میانگیر}  \setinterfaceconstant{calculate}{محاسبه} diff --git a/tex/context/base/mkiv/cont-new.mkiv b/tex/context/base/mkiv/cont-new.mkiv index da2b24760..92d217d35 100644 --- a/tex/context/base/mkiv/cont-new.mkiv +++ b/tex/context/base/mkiv/cont-new.mkiv @@ -11,7 +11,7 @@  %C therefore copyrighted by \PRAGMA. See mreadme.pdf for  %C details. -\newcontextversion{2017.07.05 23:01} +\newcontextversion{2017.07.14 19:35}  %D This file is loaded at runtime, thereby providing an excellent place for  %D hacks, patches, extensions and new features. diff --git a/tex/context/base/mkiv/cont-run.lua b/tex/context/base/mkiv/cont-run.lua index ee86c6a18..f18d06d66 100644 --- a/tex/context/base/mkiv/cont-run.lua +++ b/tex/context/base/mkiv/cont-run.lua @@ -181,7 +181,10 @@ local function processjob()      local filename  = environment.filename -- hm, not inputfilename !      if arguments.synctex then -        directives.enable("system.synctex") +        luatex.synctex.setup { +            state  = interfaces.variables.start, +            method = interfaces.variables.max, +        }      end      if not filename or filename == "" then diff --git a/tex/context/base/mkiv/cont-run.mkiv b/tex/context/base/mkiv/cont-run.mkiv index ef4992c69..8a6d49d7d 100644 --- a/tex/context/base/mkiv/cont-run.mkiv +++ b/tex/context/base/mkiv/cont-run.mkiv @@ -13,7 +13,6 @@  \writestatus{loading}{ConTeXt Core Macros / Runner} -\registerctxluafile{node-syn}{1.001}  \registerctxluafile{cont-run}{1.001}  % \enabletrackers[system.synctex.visualize] diff --git a/tex/context/base/mkiv/context.mkiv b/tex/context/base/mkiv/context.mkiv index c142bbc64..056ba3d32 100644 --- a/tex/context/base/mkiv/context.mkiv +++ b/tex/context/base/mkiv/context.mkiv @@ -41,7 +41,7 @@  %D up and the dependencies are more consistent.  \edef\contextformat {\jobname} -\edef\contextversion{2017.07.05 23:01} +\edef\contextversion{2017.07.14 19:35}  \edef\contextkind   {beta}  %D For those who want to use this: diff --git a/tex/context/base/mkiv/font-con.lua b/tex/context/base/mkiv/font-con.lua index fd3b28612..1b986b6a6 100644 --- a/tex/context/base/mkiv/font-con.lua +++ b/tex/context/base/mkiv/font-con.lua @@ -555,22 +555,6 @@ function constructors.scale(tfmdata,specification)          if changed then              local c = changed[unicode]              if c and c ~= unicode then ---                 while true do ---                     local cc = changed[c] ---                     if not cc then ---                         -- we're done, no (futher) chain ---                         break ---                     elseif cc == unicode then ---                         -- we probably have a bidi swap ---                         break ---                     elseif cc == c then ---                         -- we have a self reference, shouldn't happen ---                         c = nil ---                         break ---                     else ---                         c = cc ---                     end ---                 end                  if c then                      description = descriptions[c] or descriptions[unicode] or character                      character   = characters[c] or character diff --git a/tex/context/base/mkiv/font-ctx.lua b/tex/context/base/mkiv/font-ctx.lua index 8f7eed4b9..eabd1d435 100644 --- a/tex/context/base/mkiv/font-ctx.lua +++ b/tex/context/base/mkiv/font-ctx.lua @@ -2543,7 +2543,7 @@ end  -- a fontkern plug: -nodes.injections.installnewkern(nuts.pool.fontkern) +-- nodes.injections.installnewkern(nuts.pool.fontkern)  do @@ -2599,7 +2599,7 @@ do      local unsetvalue      = attributes.unsetvalue -    local traverse_id     = nuts.traverse_id +    local traverse_char   = nuts.traverse_char      local a_color         = attributes.private('color')      local a_colormodel    = attributes.private('colormodel') @@ -2628,7 +2628,7 @@ do          if head then              head = tonut(head)              local model = getattr(head,a_colormodel) or 1 -            for glyph in traverse_id(glyph_code,head) do +            for glyph in traverse_char(head) do                  local a = getprop(glyph,a_state)                  if a then                      local name = colornames[a] @@ -2679,7 +2679,7 @@ do      function methods.nocolor(head,font,attr) -        for n in traverse_id(glyph_code,head) do +        for n in traverse_char(head) do              if not font or getfont(n) == font then                  setattr(n,a_color,unsetvalue)              end diff --git a/tex/context/base/mkiv/font-dsp.lua b/tex/context/base/mkiv/font-dsp.lua index 2e85c2438..2d3dce2fb 100644 --- a/tex/context/base/mkiv/font-dsp.lua +++ b/tex/context/base/mkiv/font-dsp.lua @@ -690,13 +690,13 @@ end  local function readposition(f,format,mainoffset,getdelta)      if format == 0 then -        return +        return false      end      -- a few happen often      if format == 0x04 then          local h = readshort(f)          if h == 0 then -            return +            return true -- all zero          else              return { 0, 0, h, 0 }          end @@ -705,7 +705,7 @@ local function readposition(f,format,mainoffset,getdelta)          local x = readshort(f)          local h = readshort(f)          if x == 0 and h == 0 then -            return +            return true -- all zero          else              return { x, 0, h, 0 }          end @@ -724,7 +724,7 @@ local function readposition(f,format,mainoffset,getdelta)              skipshort(f,1)          end          if h == 0 then -            return +            return true -- all zero          else              return { 0, 0, h, 0 }          end @@ -779,7 +779,7 @@ local function readposition(f,format,mainoffset,getdelta)          end          return { x, y, h, v }      elseif x == 0 and y == 0 and h == 0 and v == 0 then -        return +        return true -- all zero      else          return { x, y, h, v }      end @@ -1521,7 +1521,7 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly                      local first  = value[2]                      local second = value[3]                      if first or second then -                        hash[other] = { first, second } -- needs checking +                        hash[other] = { first, second or nil } -- needs checking                      else                          hash[other] = nil                      end @@ -1557,7 +1557,7 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly                              local first  = offsets[1]                              local second = offsets[2]                              if first or second then -                                hash[paired] = { first, second } +                                hash[paired] = { first, second or nil }                              else                                  -- upto the next lookup for this combination                              end @@ -1591,18 +1591,27 @@ function gposhandlers.cursive(f,fontdata,lookupid,lookupoffset,offset,glyphs,nof              local entry = readushort(f)              local exit  = readushort(f)              records[i] = { -                entry = entry ~= 0 and (tableoffset + entry) or false, -                exit  = exit  ~= 0 and (tableoffset + exit ) or false, +             -- entry = entry ~= 0 and (tableoffset + entry) or false, +             -- exit  = exit  ~= 0 and (tableoffset + exit ) or nil, +                entry ~= 0 and (tableoffset + entry) or false, +                exit  ~= 0 and (tableoffset + exit ) or nil,              }          end +        -- slot 1 will become hash after loading an dit must be unique because we +        -- pack the tables (packed we turn the cc-* into a zero) +        local cc = (fontdata.temporary.cursivecount or 0) + 1 +        fontdata.temporary.cursivecount = cc +        cc = "cc-" .. cc          coverage = readcoverage(f,coverage)          for i=1,nofrecords do              local r = records[i] -            -- slot 1 will become hash after loading (must be unique per lookup when packed)              records[i] = { -                1, -                readanchor(f,r.entry,getdelta) or nil, -                readanchor(f,r.exit, getdelta) or nil, +             -- 1, +                cc, +             -- readanchor(f,r.entry,getdelta) or false, +             -- readanchor(f,r.exit, getdelta) or nil, +                readanchor(f,r[1],getdelta) or false, +                readanchor(f,r[2],getdelta) or nil,              }          end          for index, newindex in next, coverage do diff --git a/tex/context/base/mkiv/font-ext.lua b/tex/context/base/mkiv/font-ext.lua index cf77ac4eb..d541521fb 100644 --- a/tex/context/base/mkiv/font-ext.lua +++ b/tex/context/base/mkiv/font-ext.lua @@ -749,15 +749,15 @@ registerafmfeature(dimensions_specification)  --  -- \definecolor[DummyColor][s=.75,t=.5,a=1] {\DummyColor test} \nopdfcompression  -- --- local gray  = { "pdf", "/Tr1 gs .75 g" } --- local black = { "pdf", "/Tr0 gs 0 g" } +-- local gray  = { "pdf", "origin", "/Tr1 gs .75 g" } +-- local black = { "pdf", "origin", "/Tr0 gs 0 g" }  -- sort of obsolete as we now have \showglyphs  local push  = { "push" }  local pop   = { "pop" } -local gray  = { "pdf", ".75 g" } -local black = { "pdf", "0 g"   } +local gray  = { "pdf", "origin", ".75 g" } +local black = { "pdf", "origin", "0 g"   }  local downcache = { } -- handy for huge cjk fonts  local rulecache = { } -- handy for huge cjk fonts @@ -1486,7 +1486,8 @@ do                      report_reorder("stop swapping lookups, %i lookups swapped",swapped)                      report_reorder()                  end -                tfmdata.resources.sequences = sequences +--                 tfmdata.resources.sequences = sequences +                tfmdata.shared.reorderedsequences = sequences              end          end      end diff --git a/tex/context/base/mkiv/font-fbk.lua b/tex/context/base/mkiv/font-fbk.lua index 8a5c1ebb7..5008887b8 100644 --- a/tex/context/base/mkiv/font-fbk.lua +++ b/tex/context/base/mkiv/font-fbk.lua @@ -300,11 +300,11 @@ end  -- vf builder --- { "pdf", "q " .. s .. " 0 0 " .. s .. " 0 0 cm" }, --- { "pdf", "q 1 0 0 1 " .. -w .. " " .. -h .. " cm" }, --- { "pdf", "/Fm\XX\space Do" }, --- { "pdf", "Q" }, --- { "pdf", "Q" }, +-- { "pdf", "origin", "q " .. s .. " 0 0 " .. s .. " 0 0 cm" }, +-- { "pdf", "origin", "q 1 0 0 1 " .. -w .. " " .. -h .. " cm" }, +-- { "pdf", "origin", "/Fm\XX\space Do" }, +-- { "pdf", "origin", "Q" }, +-- { "pdf", "origin", "Q" },  -- new and experimental diff --git a/tex/context/base/mkiv/font-lib.mkvi b/tex/context/base/mkiv/font-lib.mkvi index 4ba7a7de4..4dd15812f 100644 --- a/tex/context/base/mkiv/font-lib.mkvi +++ b/tex/context/base/mkiv/font-lib.mkvi @@ -81,7 +81,7 @@  \registerctxluafile{font-lua}{1.001} -\registerctxluafile{font-vf} {1.001} +\registerctxluafile{font-vir}{1.001}  \registerctxluafile{font-enh}{1.001}  \registerctxluafile{good-ini}{1.001} diff --git a/tex/context/base/mkiv/font-mis.lua b/tex/context/base/mkiv/font-mis.lua index 024e32831..3325264fc 100644 --- a/tex/context/base/mkiv/font-mis.lua +++ b/tex/context/base/mkiv/font-mis.lua @@ -21,7 +21,7 @@ local readers  = otf.readers  if readers then -    otf.version = otf.version or 3.031 +    otf.version = otf.version or 3.032      otf.cache   = otf.cache   or containers.define("fonts", "otl", otf.version, true)      function fonts.helpers.getfeatures(name,save) @@ -33,10 +33,17 @@ if readers then              if data then                  readers.unpack(data)              else -                data = readers.loadfont(filename) -                if data and save then -                    containers.write(otf.cache,cleanname,data) -                end +                data = readers.loadfont(filename) -- we can do a more minimal load +             -- if data and save then +             --     -- keep this in sync with font-otl +             --     readers.compact(data) +             --     readers.rehash(data,"unicodes") +             --     readers.addunicodetable(data) +             --     readers.extend(data) +             --     readers.pack(data) +             --     -- till here +             --     containers.write(otf.cache,cleanname,data) +             -- end              end              return data and data.resources and data.resources.features          end diff --git a/tex/context/base/mkiv/font-nod.lua b/tex/context/base/mkiv/font-nod.lua index b629a51d1..c70763e6b 100644 --- a/tex/context/base/mkiv/font-nod.lua +++ b/tex/context/base/mkiv/font-nod.lua @@ -78,7 +78,7 @@ local copy_node_list   = nuts.copy_list  local hpack_node_list  = nuts.hpack  local flush_node_list  = nuts.flush_list  local traverse_nodes   = nuts.traverse -local traverse_id      = nuts.traverse_id +----- traverse_id      = nuts.traverse_id  local protect_glyphs   = nuts.protect_glyphs  local nodepool         = nuts.pool @@ -135,22 +135,22 @@ function char_tracers.collect(head,list,tag,n)              l[#l+1] = { c, f }          elseif id == disc_code then              -- skip ---             local pre, post, replace = getdisc(head) ---             if replace then ---                 for n in traverse_id(glyph_code,replace) do ---                     l[#l+1] = { c, f } ---                 end ---             end ---             if pre then ---                 for n in traverse_id(glyph_code,pre) do ---                     l[#l+1] = { c, f } ---                 end ---             end ---             if post then ---                 for n in traverse_id(glyph_code,post) do ---                     l[#l+1] = { c, f } ---                 end ---             end +         -- local pre, post, replace = getdisc(head) +         -- if replace then +         --     for n in traverse_id(glyph_code,replace) do +         --         l[#l+1] = { c, f } +         --     end +         -- end +         -- if pre then +         --     for n in traverse_id(glyph_code,pre) do +         --         l[#l+1] = { c, f } +         --     end +         -- end +         -- if post then +         --     for n in traverse_id(glyph_code,post) do +         --         l[#l+1] = { c, f } +         --     end +         -- end          else              ok = false          end diff --git a/tex/context/base/mkiv/font-otc.lua b/tex/context/base/mkiv/font-otc.lua index 0af588b74..b7c296809 100644 --- a/tex/context/base/mkiv/font-otc.lua +++ b/tex/context/base/mkiv/font-otc.lua @@ -412,11 +412,9 @@ local function addfeature(data,feature,specifications)          local rules    = list.rules          local coverage = { }          if rules then -            local rulehash     = { } -            local rulesize     = 0 -            local sequence     = { } -            local nofsequences = 0 -            local lookuptype   = types[featuretype] +            local rulehash   = { } +            local rulesize   = 0 +            local lookuptype = types[featuretype]              for nofrules=1,#rules do                  local rule         = rules[nofrules]                  local current      = rule.current @@ -504,8 +502,10 @@ local function addfeature(data,feature,specifications)                              coverage[unic] = rulehash -- can now be done cleaner i think                          end                      end +                    sequence.n = nofsequences                  end              end +            rulehash.n = rulesize          end          return coverage      end diff --git a/tex/context/base/mkiv/font-otd.lua b/tex/context/base/mkiv/font-otd.lua index c45278826..2633c6cae 100644 --- a/tex/context/base/mkiv/font-otd.lua +++ b/tex/context/base/mkiv/font-otd.lua @@ -268,7 +268,7 @@ function otf.dataset(tfmdata,font,attr) -- attr only when explicit (as in specia              -- indexed but we can also add specific data by key in:          }          rl[attr] = ra -        local sequences = tfmdata.resources.sequences +        local sequences = tfmdata.shared.reorderedsequences or tfmdata.resources.sequences          if sequences then              local autoscript   = (s_enabled and s_enabled.autoscript  ) or (a_enabled and a_enabled.autoscript  )              local autolanguage = (s_enabled and s_enabled.autolanguage) or (a_enabled and a_enabled.autolanguage) diff --git a/tex/context/base/mkiv/font-otj.lua b/tex/context/base/mkiv/font-otj.lua index 6275f81b8..a6e60a3f5 100644 --- a/tex/context/base/mkiv/font-otj.lua +++ b/tex/context/base/mkiv/font-otj.lua @@ -515,7 +515,7 @@ end  local function showsub(n,what,where)      report_injections("begin subrun: %s",where) -    for n in traverse_id(glyph_code,n) do +    for n in traverse_char(n) do          showchar(n,where)          show(n,what,where," ")      end @@ -593,7 +593,7 @@ local function inject_kerns_only(head,where)      local prev        = nil      local next        = nil      local prevdisc    = nil -    local prevglyph   = nil + -- local prevglyph   = nil      local pre         = nil -- saves a lookup      local post        = nil -- saves a lookup      local replace     = nil -- saves a lookup @@ -655,11 +655,11 @@ local function inject_kerns_only(head,where)                  end              end              prevdisc  = nil -            prevglyph = current +         -- prevglyph = current          elseif char == false then              -- other font              prevdisc  = nil -            prevglyph = current +         -- prevglyph = current          elseif id == disc_code then              pre, post, replace, pretail, posttail, replacetail = getdisc(current,true)              local done = false @@ -717,10 +717,10 @@ local function inject_kerns_only(head,where)              if done then                  setdisc(current,pre,post,replace)              end -            prevglyph = nil +         -- prevglyph = nil              prevdisc  = current          else -            prevglyph = nil +         -- prevglyph = nil              prevdisc  = nil          end          prev    = current @@ -966,7 +966,7 @@ local function inject_pairs_only(head,where)      if keepregisteredcounts then          keepregisteredcounts = false      else -        nofregisteredkerns   = 0 +        nofregisteredpairs = 0      end      if trace_injections then          show_result(head) @@ -1548,7 +1548,8 @@ local function injectspaces(head)          factor     = getthreshold(font)      end -    for n in traverse_id(glue_code,tonut(head)) do + -- for n in traverse_id(glue_code,tonut(head)) do +    for n in traverse_char(tonut(head)) do          local prev, next = getspaceboth(n)          local prevchar = prev and ischar(prev)          local nextchar = next and ischar(next) diff --git a/tex/context/base/mkiv/font-otl.lua b/tex/context/base/mkiv/font-otl.lua index 2661ac5c1..1cdbb450a 100644 --- a/tex/context/base/mkiv/font-otl.lua +++ b/tex/context/base/mkiv/font-otl.lua @@ -52,7 +52,7 @@ local report_otf          = logs.reporter("fonts","otf loading")  local fonts               = fonts  local otf                 = fonts.handlers.otf -otf.version               = 3.031 -- beware: also sync font-mis.lua and in mtx-fonts +otf.version               = 3.032 -- beware: also sync font-mis.lua and in mtx-fonts  otf.cache                 = containers.define("fonts", "otl",  otf.version, true)  otf.svgcache              = containers.define("fonts", "svg",  otf.version, true)  otf.sbixcache             = containers.define("fonts", "sbix", otf.version, true) diff --git a/tex/context/base/mkiv/font-oto.lua b/tex/context/base/mkiv/font-oto.lua index 22bb1d74e..0009d7f5f 100644 --- a/tex/context/base/mkiv/font-oto.lua +++ b/tex/context/base/mkiv/font-oto.lua @@ -368,9 +368,13 @@ local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist                          local character = characters[unicode]                          local kerns     = character.kerns                          for otherunicode, kern in next, data do -                            if not kern[2] and not (kerns and kerns[otherunicode]) then +                            -- kern[2] is true (all zero) or a table +                            local other = kern[2] +                            if other == true or (not other and not (kerns and kerns[otherunicode])) then                                  local kern = kern[1] -                                if kern[1] ~= 0 or kern[2] ~= 0 or kern[4] ~= 0 then +                                if kern == true then +                                    -- all zero +                                elseif kern[1] ~= 0 or kern[2] ~= 0 or kern[4] ~= 0 then                                      -- a complex pair not suitable for basemode                                  else                                      kern = kern[3] @@ -400,6 +404,43 @@ local function initializehashes(tfmdata)      -- already done  end +local function checkmathreplacements(tfmdata,fullname) +    if tfmdata.mathparameters then +        local characters = tfmdata.characters +        local changed    = tfmdata.changed +        if next(changed) then +            if trace_preparing or trace_baseinit then +                report_prepare("checking math replacements for %a",fullname) +            end +            for unicode, replacement in next, changed do +                local u = characters[unicode] +                local r = characters[replacement] +                local n = u.next +                local v = u.vert_variants +                local h = u.horiz_variants +                if n and not r.next then +                    if trace_preparing then +                        report_prepare("forcing %s for %C substituted by %U","incremental step",unicode,replacement) +                    end +                    r.next = n +                end +                if v and not r.vert_variants then +                    if trace_preparing then +                        report_prepare("forcing %s for %C substituted by %U","vertical variants",unicode,replacement) +                    end +                    r.vert_variants = v +                end +                if h and not r.horiz_variants then +                    if trace_preparing then +                        report_prepare("forcing %s for %C substituted by %U","horizontal variants",unicode,replacement) +                    end +                    r.horiz_variants = h +                end +            end +        end +    end +end +  local function featuresinitializer(tfmdata,value)      if true then -- value then          local starttime = trace_preparing and os.clock() @@ -416,6 +457,8 @@ local function featuresinitializer(tfmdata,value)              local rawfeatures       = rawresources and rawresources.features              local basesubstitutions = rawfeatures and rawfeatures.gsub              local basepositionings  = rawfeatures and rawfeatures.gpos +            local substitutionsdone = false +            local positioningsdone  = false              --              if basesubstitutions or basepositionings then                  local sequences = tfmdata.resources.sequences @@ -438,12 +481,14 @@ local function featuresinitializer(tfmdata,value)                                          end                                          preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist)                                          registerbasefeature(feature,value) +                                        substitutionsdone = true                                      elseif basepositionings and basepositionings[feature] then                                          if trace_preparing then                                              report_prepare("filtering base %a feature %a for %a with value %a","pos",feature,fullname,value)                                          end                                          preparepositionings(tfmdata,feature,value,validlookups,lookuplist)                                          registerbasefeature(feature,value) +                                        positioningsdone = true                                      end                                  end                              end @@ -452,6 +497,10 @@ local function featuresinitializer(tfmdata,value)                  end              end              -- +            if substitutionsdone then +                checkmathreplacements(tfmdata,fullname) +            end +            --              registerbasehash(tfmdata)          end          if trace_preparing then diff --git a/tex/context/base/mkiv/font-ots.lua b/tex/context/base/mkiv/font-ots.lua index a5a039525..26659721f 100644 --- a/tex/context/base/mkiv/font-ots.lua +++ b/tex/context/base/mkiv/font-ots.lua @@ -148,7 +148,6 @@ local trace_compruns       = false  registertracker("otf.compruns",     function  local trace_testruns       = false  registertracker("otf.testruns",     function(v) trace_testruns     = v end)  local forcediscretionaries = false -local optimizekerns        = true  directives.register("otf.forcediscretionaries",function(v)      forcediscretionaries = v @@ -207,7 +206,7 @@ local flush_node_list    = nuts.flush_list  local flush_node         = nuts.flush_node  local end_of_math        = nuts.end_of_math  local traverse_nodes     = nuts.traverse -local traverse_id        = nuts.traverse_id +----- traverse_id        = nuts.traverse_id  local set_components     = nuts.set_components  local take_components    = nuts.take_components  local count_components   = nuts.count_components @@ -271,6 +270,8 @@ local factor             = 0  local threshold          = 0  local checkmarks         = false +local discs              = false +  local sweepnode          = nil  local sweephead          = { } -- we don't nil entries but false them (no collection and such) @@ -305,47 +306,56 @@ local function logwarning(...)      report_direct(...)  end -local f_unicode = formatters["U+%X"]      -- was ["%U"] -local f_uniname = formatters["U+%X (%s)"] -- was ["%U (%s)"] -local f_unilist = formatters["% t (% t)"] +local gref  do -local function gref(n) -- currently the same as in font-otb -    if type(n) == "number" then -        local description = descriptions[n] -        local name = description and description.name -        if name then -            return f_uniname(n,name) -        else -            return f_unicode(n) -        end -    elseif n then -        local num, nam = { }, { } -        for i=1,#n do -            local ni = n[i] -            if tonumber(ni) then -- later we will start at 2 -                local di = descriptions[ni] -                num[i] = f_unicode(ni) -                nam[i] = di and di.name or "-" +    local f_unicode = formatters["U+%X"]      -- was ["%U"] +    local f_uniname = formatters["U+%X (%s)"] -- was ["%U (%s)"] +    local f_unilist = formatters["% t (% t)"] + +    gref = function(n) -- currently the same as in font-otb +        if type(n) == "number" then +            local description = descriptions[n] +            local name = description and description.name +            if name then +                return f_uniname(n,name) +            else +                return f_unicode(n)              end +        elseif n then +            local num, nam = { }, { } +            for i=1,#n do +                local ni = n[i] +                if tonumber(ni) then -- later we will start at 2 +                    local di = descriptions[ni] +                    num[i] = f_unicode(ni) +                    nam[i] = di and di.name or "-" +                end +            end +            return f_unilist(num,nam) +        else +            return "<error in node mode tracing>"          end -        return f_unilist(num,nam) -    else -        return "<error in node mode tracing>"      end +  end  local function cref(dataset,sequence,index)      if not dataset then          return "no valid dataset" -    elseif index then -        return formatters["feature %a, type %a, chain lookup %a, index %a"](dataset[4],sequence.type,sequence.name,index) +    end +    local merged = sequence.merged and "merged " or "" +    if index then +        return formatters["feature %a, type %a, %schain lookup %a, index %a"]( +            dataset[4],sequence.type,merged,sequence.name,index)      else -        return formatters["feature %a, type %a, chain lookup %a"](dataset[4],sequence.type,sequence.name) +        return formatters["feature %a, type %a, %schain lookup %a"]( +            dataset[4],sequence.type,merged,sequence.name)      end  end  local function pref(dataset,sequence) -    return formatters["feature %a, type %a, lookup %a"](dataset[4],sequence.type,sequence.name) +    return formatters["feature %a, type %a, %slookup %a"]( +        dataset[4],sequence.type,sequence.merged and "merged " or "",sequence.name)  end  local function mref(rlmode) @@ -689,7 +699,7 @@ function handlers.gsub_alternate(head,start,dataset,sequence,alternative)          setchar(start,choice)      else          if trace_alternatives then -                logwarning("%s: no variant %a for %s, %s",pref(dataset,sequence),value,gref(getchar(start)),comment) +            logwarning("%s: no variant %a for %s, %s",pref(dataset,sequence),value,gref(getchar(start)),comment)          end      end      return head, start, true @@ -850,36 +860,32 @@ function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,step,i,inje              if nextchar then                  local krn = kerns[nextchar]                  if not krn and marks[nextchar] then +                    -- hm, needs checking i guess                      prev = snext                      snext = getnext(snext)                  elseif not krn then                      break                  elseif step.format == "pair" then                      local a, b = krn[1], krn[2] -                    if optimizekerns then -                        -- this permits a mixed table, but we could also decide to optimize this -                        -- in the loader and use format 'kern' -                        if not b and a[1] == 0 and a[2] == 0 and a[4] == 0 then -                            local k = setkern(snext,factor,rlmode,a[3],injection) -                            if trace_kerns then -                                logprocess("%s: shifting single %s by %p",pref(dataset,sequence),gref(nextchar),k) -                            end -                            return head, start, true -                        end -                    end -                    if a and #a > 0 then +                    if a == true then +                        -- zero +                    elseif a then -- #a > 0                          local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,injection)                          if trace_kerns then                              local startchar = getchar(start)                              logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections")                          end                      end -                    if b and #b > 0 then +                    if b == true then +                        -- zero +                        start = snext -- cf spec +                    elseif b then -- #b > 0                          local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,injection)                          if trace_kerns then                              local startchar = getchar(snext)                              logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections")                          end +                        start = snext -- cf spec                      end                      return head, start, true                  elseif krn ~= 0 then @@ -1073,8 +1079,7 @@ function handlers.gpos_cursive(head,start,dataset,sequence,exitanchors,rlmode,st              if not nextchar then                  break              elseif marks[nextchar] then -                -- should not happen (maybe warning) -                nxt = getnext(nxt) +                nxt      = getnext(nxt)              else                  local exit = exitanchors[3]                  if exit then @@ -1458,30 +1463,25 @@ function chainprocs.gpos_pair(head,start,stop,dataset,sequence,currentlookup,rlm                          break                      elseif step.format == "pair" then                          local a, b = krn[1], krn[2] -                        if optimizekerns then -                            -- this permits a mixed table, but we could also decide to optimize this -                            -- in the loader and use format 'kern' -                            if not b and a[1] == 0 and a[2] == 0 and a[4] == 0 then -                                local k = setkern(snext,factor,rlmode,a[3],"injections") -                                if trace_kerns then -                                    logprocess("%s: shifting single %s by %p",cref(dataset,sequence),gref(startchar),k) -                                end -                                return head, start, true -                            end -                        end -                        if a and #a > 0 then -                            local startchar = getchar(start) +                        if a then +                            -- zero +                        elseif a then                              local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,"injections") -- currentlookups flags?                              if trace_kerns then +                                local startchar = getchar(start)                                  logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h)                              end                          end -                        if b and #b > 0 then -                            local startchar = getchar(start) +                        if b == true then +                            -- zero +                            start = snext -- cf spec +                        elseif b then -- #b > 0                              local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,"injections")                              if trace_kerns then +                                local startchar = getchar(start)                                  logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h)                              end +                            start = snext -- cf spec                          end                          return head, start, true                      elseif krn ~= 0 then @@ -1836,20 +1836,21 @@ local noflags = { false, false, false, false }  local function chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped)      local size         = ck[5] - ck[4] + 1 -    local flags        = sequence.flags or noflags -    local done         = false -    local skipmark     = flags[1]      local chainlookups = ck[6] +    local done         = false      -- current match      if chainlookups then -        local nofchainlookups = #chainlookups +          -- Lookups can be like { 1, false, 3 } or { false, 2 } or basically anything and          -- #lookups can be less than #current +          if size == 1 then +           -- if nofchainlookups > size then           --     -- bad rules           -- end +              local chainlookup = chainlookups[1]              for j=1,#chainlookup do                  local chainstep = chainlookup[j] @@ -1865,7 +1866,9 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped)                      logprocess("%s: %s is not yet supported (1)",cref(dataset,sequence),chainkind)                  end              end +           else +              -- See LookupType 5: Contextual Substitution Subtable. Now it becomes messy. The              -- easiest case is where #current maps on #lookups i.e. one-to-one. But what if              -- we have a ligature. Cf the spec we then need to advance one character but we @@ -1876,10 +1879,27 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped)              --              -- Even worse are these family emoji shapes as they can have multiple lookups              -- per slot (probably only for gpos). + +            -- It's very unlikely that we will have skip classes here but still ... we seldom +            -- enter this branch anyway. + +            local skipmark +            local skipligature +            local skipbase +            local markclass +            if skipped then +                local flags  = sequence.flags or noflags +                skipmark     = flags[1] +                skipligature = flags[2] +                skipbase     = flags[3] +                markclass    = sequence.markclass +            end +              local i = 1              local laststart = start +            local nofchainlookups = #chainlookups -- useful?              while start do -                if skipped then +                if skipped then -- hm, so we know we skip some                      while start do                          local char, id = ischar(start,currentfont)                          if char then @@ -1932,6 +1952,7 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped)              if not start then                  start = laststart              end +          end      else          -- todo: needs checking for holes in the replacements @@ -2762,11 +2783,12 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode)      local checkdisc    = sweeptype and getprev(head)      local flags        = sequence.flags or noflags      local done         = false +    local markclass    = sequence.markclass      local skipmark     = flags[1]      local skipligature = flags[2]      local skipbase     = flags[3] -    local markclass    = sequence.markclass -    local skipsome     = skipmark ~= false or skipligature ~= false or skipbase ~= false or markclass + -- local skipsome     = skipmark ~= false or skipligature ~= false or skipbase ~= false or markclass +    local skipsome     = flags[5]      local skipped      = false      local startprev,            startnext    = getboth(start) @@ -2778,7 +2800,6 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode)          local ck      = contexts[k]          local seq     = ck[3]          local s       = #seq -        local size    = 1          -- f..l = mid string          if s == 1 then              -- this seldom happens as it makes no sense (bril, ebgaramond, husayni, minion) @@ -2793,12 +2814,10 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode)              local l = ck[5]              -- current match              -- seq[f][ischar(current,currentfont)] is not nil -            size = l - f + 1 -            if size > 1 then +            if l > f then                  -- before/current/after | before/current | current/after                  local discfound -- = nil                  local n = f + 1 -             -- last = getnext(last) -- the second in current (first already matched)                  last = startnext -- the second in current (first already matched)                  while n <= l do                      if not last and (sweeptype == "post" or sweeptype == "replace") then @@ -2923,13 +2942,10 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode)              end              -- before              if match and f > 1 then -             -- local prev = getprev(start) -             -- if prev then                  if startprev then                      local prev = startprev                      if prev == checkdisc and (sweeptype == "pre" or sweeptype == "replace") then -                        prev      = getprev(sweepnode) -                     -- sweeptype = nil +                        prev = getprev(sweepnode)                      end                      if prev then                          local discfound -- = nil @@ -3074,8 +3090,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode)              if match and s > l then                  local current = last and getnext(last)                  if not current and (sweeptype == "post" or sweeptype == "replace") then -                    current   = getnext(sweepnode) -                 -- sweeptype = nil +                    current = getnext(sweepnode)                  end                  if current then                      local discfound -- = nil @@ -3232,6 +3247,503 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode)      return head, start, done  end +-- only a bit faster but probably also a bit cleaner ... so ... + +local function optimized_handle_contextchain(head,start,dataset,sequence,contexts,rlmode) +    -- optimizing for rlmode gains nothing +    local sweepnode    = sweepnode +    local sweeptype    = sweeptype +    local postreplace +    local prereplace +    local checkdisc +    local diskseen  -- = false +    if sweeptype then +        if sweeptype == "replace" then +            postreplace = true +            prereplace  = true +        else +            postreplace = sweeptype == "post" +            prereplace  = sweeptype == "pre" +        end +        checkdisc = getprev(head) +    end +    local currentfont  = currentfont +    local flags        = sequence.flags or noflags +    local skipsome     = flags[5] +    local skipmark +    local skipligature +    local skipbase +    local markclass +    if skipsome then +        skipmark     = flags[1] +        skipligature = flags[2] +        skipbase     = flags[3] +        markclass    = sequence.markclass +    end +    local skipped   -- = false +    local startprev, +          startnext    = getboth(start) +    local done      -- = false + +    for k=1,contexts.n do -- or #contexts do +        local current = start +        local last    = start +        local ck      = contexts[k] +        local seq     = ck[3] +     -- local s       = #seq +        local s       = seq.n -- or #seq +        -- f..l = mid string +        if s == 1 then +            -- this seldom happens as it makes no sense (bril, ebgaramond, husayni, minion) +            local char = ischar(current,currentfont) +            if char and not seq[1][char] then +                goto next +            end +        else +            -- maybe we need a better space check (maybe check for glue or category or combination) +            local f = ck[4] +            local l = ck[5] +            -- current match +            -- seq[f][ischar(current,currentfont)] is not nil +            if l > f then +                -- before/current/after | before/current | current/after +                local discfound -- = nil +                local n = f + 1 +                last = startnext -- the second in current (first already matched) +                while n <= l do +                    if not last and postreplace then +                        last      = getnext(sweepnode) +                        sweeptype = nil +                    end +                    if last then +                        local char, id = ischar(last,currentfont) +                        if char then +                            if skipsome then +                                local class = classes[char] +                             -- if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then +                                if class == skipmark or (markclass and class == "mark" and not markclass[char]) or class == skipligature or class == skipbase then +                                    skipped = true +                                    if trace_skips then +                                        show_skip(dataset,sequence,char,ck,class) +                                    end +                                    last = getnext(last) +                                elseif seq[n][char] then +                                    if n < l then +                                        last = getnext(last) +                                    end +                                    n = n + 1 +                                else +                                    if discfound then +                                        notmatchreplace[discfound] = true +                                        if notmatchpre[discfound] then +                                            goto next +                                        end +                                    else +                                        goto next +                                    end +                                    break +                                end +                            else +                                if seq[n][char] then +                                    if n < l then +                                        last = getnext(last) +                                    end +                                    n = n + 1 +                                else +                                    if discfound then +                                        notmatchreplace[discfound] = true +                                        if notmatchpre[discfound] then +                                            goto next +                                        end +                                    else +                                        goto next +                                    end +                                    break +                                end +                            end +                        elseif char == false then +                            if discfound then +                                notmatchreplace[discfound] = true +                                if notmatchpre[discfound] then +                                    goto next +                                end +                            else +                                goto next +                            end +                            break +                        elseif id == disc_code then +                     -- elseif id == disc_code and (not discs or discs[last]) then +                            diskseen              = 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 +                                end +                                if n <= l then +                                    notmatchpre[last] = true +                                end +                            else +                                notmatchpre[last] = true +                            end +                            if replace then +                                -- so far we never entered this branch +                                while replace do +                                    if seq[n][getchar(replace)] then +                                        n = n + 1 +                                        if n > l then +                                            break +                                        end +                                        replace = getnext(replace) +                                    else +                                        notmatchreplace[last] = true +                                        if notmatchpre[last] then +                                            goto next +                                        end +                                        break +                                    end +                                end +                                -- why here again +                                if notmatchpre[last] then +                                    goto next +                                end +                            end +                            -- maybe only if match +                            last = getnext(last) +                        else +                            goto next +                        end +                    else +                        goto next +                    end +                end +            end +            -- before +            if f > 1 then +                if startprev then +                    local prev = startprev +                    if prereplace and prev == checkdisc then +                        prev = getprev(sweepnode) +                    end +                    if prev then +                        local discfound -- = nil +                        local n = f - 1 +                        while n >= 1 do +                            if prev then +                                local char, id = ischar(prev,currentfont) +                                if char then +                                    if skipsome then +                                        local class = classes[char] +                                     -- if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then +                                        if class == skipmark or (markclass and class == "mark" and not markclass[char]) or class == skipligature or class == skipbase then +                                            skipped = true +                                            if trace_skips then +                                                show_skip(dataset,sequence,char,ck,class) +                                            end +                                            prev = getprev(prev) +                                        elseif seq[n][char] then +                                            if n > 1 then +                                                prev = getprev(prev) +                                            end +                                            n = n - 1 +                                        else +                                            if discfound then +                                                notmatchreplace[discfound] = true +                                                if notmatchpost[discfound] then +                                                    goto next +                                                end +                                            else +                                                goto next +                                            end +                                            break +                                        end +                                    else +                                        if seq[n][char] then +                                            if n > 1 then +                                                prev = getprev(prev) +                                            end +                                            n = n - 1 +                                        else +                                            if discfound then +                                                notmatchreplace[discfound] = true +                                                if notmatchpost[discfound] then +                                                    goto next +                                                end +                                            else +                                                goto next +                                            end +                                            break +                                        end +                                    end +                                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 +                             -- elseif id == disc_code and (not discs or discs[prev]) then +                                    -- the special case: f i where i becomes dottless i .. +                                    diskseen              = true +                                    discfound             = prev +                                    notmatchpre[prev]     = true +                                    notmatchpost[prev]    = nil +                                    notmatchreplace[prev] = nil +                                    local pre, 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 then +                                                        break +                                                    else +                                                        if n < 1 then +                                                            break +                                                        end +                                                        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 +                                            -- we seldom enter this branch (e.g. on brill efficient) +                                            while replacetail do +                                                if seq[n][getchar(replacetail)] then +                                                    n = n - 1 +                                                    if replacetail == replace then +                                                        break +                                                    else +                                                        if n < 1 then +                                                            break +                                                        end +                                                        replacetail = getprev(replacetail) +                                                    end +                                                else +                                                    notmatchreplace[prev] = true +                                                    if notmatchpost[prev] then +                                                        goto next +                                                    end +                                                    break +                                                end +                                            end +                                        end +                                    end +                                    prev = getprev(prev) +                                elseif id == glue_code and seq[n][32] and isspace(prev,threshold,id) then +                                    n = n - 1 +                                    prev = getprev(prev) +                                else +                                    goto next +                                end +                            else +                                goto next +                            end +                        end +                    else +                        goto next +                    end +                else +                    goto next +                end +            end +            -- after +            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 -- = nil +                    local n = l + 1 +                    while n <= s do +                        if current then +                            local char, id = ischar(current,currentfont) +                            if char then +                                if skipsome then +                                    local class = classes[char] +                                 -- if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then +                                    if class == skipmark or (markclass and class == "mark" and not markclass[char]) or class == skipligature or class == skipbase then +                                        skipped = true +                                        if trace_skips then +                                            show_skip(dataset,sequence,char,ck,class) +                                        end +                                        current = getnext(current) -- was absent +                                    elseif seq[n][char] then +                                        if n < s then -- new test +                                            current = getnext(current) -- was absent +                                        end +                                        n = n + 1 +                                    else +                                        if discfound then +                                            notmatchreplace[discfound] = true +                                            if notmatchpre[discfound] then +                                                goto next +                                            end +                                        else +                                            goto next +                                        end +                                        break +                                    end +                                else +                                    if seq[n][char] then +                                        if n < s then -- new test +                                            current = getnext(current) -- was absent +                                        end +                                        n = n + 1 +                                    else +                                        if discfound then +                                            notmatchreplace[discfound] = true +                                            if notmatchpre[discfound] then +                                                goto next +                                            end +                                        else +                                            goto next +                                        end +                                        break +                                    end +                                end +                            elseif char == false then +                                if discfound then +                                    notmatchreplace[discfound] = true +                                    if notmatchpre[discfound] then +                                        goto next +                                    end +                                else +                                    goto next +                                end +                                break +                            elseif id == disc_code then +                         -- elseif id == disc_code and (not discs or discs[current]) then +                                diskseen                 = 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 +                                            end +                                            pre = getnext(pre) +                                        else +                                            notmatchpre[current] = true +                                            break +                                        end +                                    end +                                    if n <= s then +                                        notmatchpre[current] = true +                                    end +                                else +                                    notmatchpre[current] = true +                                end +                                if replace then +                                    -- so far we never entered this branch +                                    while replace do +                                        if seq[n][getchar(replace)] then +                                            n = n + 1 +                                            if n > s then +                                                break +                                            end +                                            replace = getnext(replace) +                                        else +                                            notmatchreplace[current] = true +                                            -- different than others, needs checking if "not" is okay +                                            if not notmatchpre[current] then +                                                goto next +                                            end +                                            break +                                        end +                                    end +                                else +                                    -- skip 'm +                                end +                                current = getnext(current) +                            elseif id == glue_code and seq[n][32] and isspace(current,threshold,id) then +                                n = n + 1 +                                current = getnext(current) +                            else +                                goto next +                            end +                        else +                            goto next +                        end +                    end +                else +                    goto next +                end +            end +        end +        if trace_contexts then +            chaintrac(head,start,dataset,sequence,rlmode,ck,skipped,true) +        end +        if diskseen or sweepnode then +            head, start, done = chaindisk(head,start,dataset,sequence,rlmode,ck,skipped) +        else +            head, start, done = chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped) +        end +        if done then +            break +     -- else +            -- next context +        end +        ::next:: +     -- if trace_chains then +     --     chaintrac(head,start,dataset,sequence,rlmode,ck,skipped,match) +     -- end +    end +    if diskseen then +        notmatchpre     = { } +        notmatchpost    = { } +        notmatchreplace = { } +    end +    return head, start, done +end + +directives.register("otf.optimizechains",function(v) +    if v then +        report_chain() +        report_chain("using experimental optimized code") +        report_chain() +    end +    local handle = v and optimized_handle_contextchain or handle_contextchain +    handlers.gsub_context             = handle +    handlers.gsub_contextchain        = handle +    handlers.gsub_reversecontextchain = handle +    handlers.gpos_contextchain        = handle +    handlers.gpos_context             = handle +end) +  ------------------------------  handlers.gsub_context             = handle_contextchain @@ -3275,7 +3787,9 @@ chainprocs.gpos_context             = chained_contextchain  --     end  -- end -local missing = setmetatableindex("table") +local missing    = setmetatableindex("table") +local logwarning = report_process +local resolved   = { } -- we only resolve a font,script,language pair once  local function logprocess(...)      if trace_steps then @@ -3284,19 +3798,6 @@ local function logprocess(...)      report_process(...)  end -local logwarning = report_process - -local function report_missing_coverage(dataset,sequence) -    local t = missing[currentfont] -    if not t[sequence] then -        t[sequence] = true -        logwarning("missing coverage for feature %a, lookup %a, type %a, font %a, name %a", -            dataset[4],sequence.name,sequence.type,currentfont,tfmdata.properties.fullname) -    end -end - -local resolved = { } -- we only resolve a font,script,language pair once -  -- todo: pass all these 'locals' in a table  local sequencelists = setmetatableindex(function(t,font) @@ -3582,12 +4083,11 @@ local function testrun(disc,t_run,c_run,...)          end          local d_post    = t_run(post,next,...)          local d_replace = t_run(replace,next,...) -        if (d_post and d_post > 0) or (d_replace and d_replace > 0) then -            local d = d_replace or d_post -            if d_post and d < d_post then -                d = d_post -            end -            local head, tail = getnext(disc), disc +        if d_post > 0 or d_replace > 0 then +            local d = d_replace > d_post and d_replace or d_post +            local head = getnext(disc) +         -- local tail = disc -- bug +            local tail = head              for i=1,d do                  tail = getnext(tail)                  if getid(tail) == disc_code then @@ -3696,7 +4196,6 @@ end  --  1{2{\oldstyle\discretionary{3}{4}{5}}6}7\par  --  1{2\discretionary{3{\oldstyle3}}{{\oldstyle4}4}{5{\oldstyle5}5}6}7\par -  local nesting = 0  local function c_run_single(head,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) @@ -3818,6 +4317,7 @@ local function t_run_single(start,stop,font,attr,lookupcache)              break          end      end +    return 0  end  local function k_run_single(sub,injection,last,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) @@ -3865,22 +4365,18 @@ local function c_run_multiple(head,font,attr,steps,nofsteps,dataset,sequence,rlm                  for i=1,nofsteps do                      local step        = steps[i]                      local lookupcache = step.coverage -                    if lookupcache then -                        local lookupmatch = lookupcache[char] -                        if lookupmatch then -                            -- we could move all code inline but that makes things even more unreadable -                            local ok -                            head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) -                            if ok then -                                done = true -                                break -                            elseif not start then -                                -- don't ask why ... shouldn't happen -                                break -                            end +                    local lookupmatch = lookupcache[char] +                    if lookupmatch then +                        -- we could move all code inline but that makes things even more unreadable +                        local ok +                        head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) +                        if ok then +                            done = true +                            break +                        elseif not start then +                            -- don't ask why ... shouldn't happen +                            break                          end -                    else -                        report_missing_coverage(dataset,sequence)                      end                  end                  if start then @@ -3918,60 +4414,56 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps)                  for i=1,nofsteps do                      local step = steps[i]                      local lookupcache = step.coverage -                    if lookupcache then -                        local lookupmatch = lookupcache[char] -                        if lookupmatch then -                            -- if we need more than ligatures we can outline the code and use functions -                            local s     = startnext -                            local ss    = nil -                            local sstop = s == stop +                    local lookupmatch = lookupcache[char] +                    if lookupmatch then +                        -- if we need more than ligatures we can outline the code and use functions +                        local s     = startnext +                        local ss    = nil +                        local sstop = s == stop +                        if not s then +                            s  = ss +                            ss = nil +                        end +                        while getid(s) == disc_code do +                            ss = getnext(s) +                            s  = getfield(s,"replace")                              if not s then                                  s  = ss                                  ss = nil                              end -                            while getid(s) == disc_code do -                                ss = getnext(s) -                                s  = getfield(s,"replace") +                        end +                        local l = nil +                        local d = 0 +                        while s do +                            local lg = lookupmatch[getchar(s)] +                            if lg then +                                if sstop then +                                    d = 1 +                                elseif d > 0 then +                                    d = d + 1 +                                end +                                l = lg +                                s = getnext(s) +                                sstop = s == stop                                  if not s then                                      s  = ss                                      ss = nil                                  end -                            end -                            local l = nil -                            local d = 0 -                            while s do -                                local lg = lookupmatch[getchar(s)] -                                if lg then -                                    if sstop then -                                        d = 1 -                                    elseif d > 0 then -                                        d = d + 1 -                                    end -                                    l = lg -                                    s = getnext(s) -                                    sstop = s == stop +                                while getid(s) == disc_code do +                                    ss = getnext(s) +                                    s  = getfield(s,"replace")                                      if not s then                                          s  = ss                                          ss = nil                                      end -                                    while getid(s) == disc_code do -                                        ss = getnext(s) -                                        s  = getfield(s,"replace") -                                        if not s then -                                            s  = ss -                                            ss = nil -                                        end -                                    end -                                else -                                    break                                  end -                            end -                            if l and l.ligature then -                                lastd = d +                            else +                                break                              end                          end -                    else -                        report_missing_coverage(dataset,sequence) +                        if l and l.ligature then +                            lastd = d +                        end                      end                  end              else @@ -3985,6 +4477,7 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps)              break          end      end +    return 0  end  local function k_run_multiple(sub,injection,last,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler) @@ -4002,16 +4495,12 @@ local function k_run_multiple(sub,injection,last,font,attr,steps,nofsteps,datase                  for i=1,nofsteps do                      local step        = steps[i]                      local lookupcache = step.coverage -                    if lookupcache then -                        local lookupmatch = lookupcache[char] -                        if lookupmatch then -                            local h, d, ok = handler(head,n,dataset,sequence,lookupmatch,rlmode,step,i,injection) -                            if ok then -                                return true -                            end +                    local lookupmatch = lookupcache[char] +                    if lookupmatch then +                        local h, d, ok = handler(head,n,dataset,sequence,lookupmatch,rlmode,step,i,injection) +                        if ok then +                            return true                          end -                    else -                        report_missing_coverage(dataset,sequence)                      end                  end              end @@ -4074,6 +4563,26 @@ otf.helpers.pardirstate = pardirstate  do +    -- reference: +    -- +    --  local a = attr and getattr(start,0) +    --  if a then +    --      a = (a == attr) and (not attribute or getprop(start,a_state) == attribute) +    --  else +    --      a = not attribute or getprop(start,a_state) == attribute +    --  end +    -- +    -- used: +    -- +    --  local a -- happens often so no assignment is faster +    --  if attr then +    --      if getattr(start,0) == attr and (not attribute or getprop(start,a_state) == attribute) then +    --          a = true +    --      end +    --  elseif not attribute or getprop(start,a_state) == attribute then +    --      a = true +    --  end +      -- This is a measurable experimental speedup (only with hyphenated text and multiple      -- fonts per processor call), especially for fonts with lots of contextual lookups. @@ -4184,19 +4693,15 @@ do   --                         for i=1,nofsteps do   --                             local step = steps[i]   --                             local lookupcache = step.coverage - --                             if lookupcache then - --                                 local lookupmatch = lookupcache[char] - --                                 if lookupmatch then - --                                     -- todo: disc? - --                                     local ok - --                                     head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) - --                                     if ok then - --                                         done = true - --                                         break - --                                     end + --                             local lookupmatch = lookupcache[char] + --                             if lookupmatch then + --                                 -- todo: disc? + --                                 local ok + --                                 head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) + --                                 if ok then + --                                     done = true + --                                     break   --                                 end - --                             else - --                                 report_missing_coverage(dataset,sequence)   --                             end   --                         end   --                         if start then @@ -4215,72 +4720,62 @@ do   --             if nofsteps == 1 then -- happens often   --                 local step = steps[1]   --                 local lookupcache = step.coverage - --                 if not lookupcache then - --                     report_missing_coverage(dataset,sequence) - --                 else - --                     while start do - --                         local char, id = ischar(start,font) - --                         if char then - --                          -- local a = attr and getattr(start,0) - --                          -- if a then - --                          --     a = (a == attr) and (not attribute or getprop(start,a_state) == attribute) - --                          -- else - --                          --     a = not attribute or getprop(start,a_state) == attribute - --                          -- end - --                             local a -- happens often so no assignment is faster - --                             if attr then - --                                 if getattr(start,0) == attr and (not attribute or getprop(start,a_state) == attribute) then - --                                     a = true - --                                 end - --                             elseif not attribute or getprop(start,a_state) == attribute then + --                 while start do + --                     local char, id = ischar(start,font) + --                     if char then + --                         local a -- happens often so no assignment is faster + --                         if attr then + --                             if getattr(start,0) == attr and (not attribute or getprop(start,a_state) == attribute) then   --                                 a = true   --                             end - --                             if a then - --                                 local lookupmatch = lookupcache[char] - --                                 if lookupmatch then - --                                     local ok - --                                     head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1) - --                                     if ok then - --                                         done = true - --                                     end - --                                 end - --                                 if start then - --                                     start = getnext(start) - --                                 end - --                             else - --                                start = getnext(start) - --                             end - --                         elseif char == false then - --                            -- whatever glyph - --                            start = getnext(start) - --                         elseif id == glue_code then - --                             -- happens often - --                            start = getnext(start) - --                         elseif id == disc_code then - --                             if not discs or discs[start] == true then + --                         elseif not attribute or getprop(start,a_state) == attribute then + --                             a = true + --                         end + --                         if a then + --                             local lookupmatch = lookupcache[char] + --                             if lookupmatch then   --                                 local ok - --                                 if gpossing then - --                                     start, ok = kernrun(start,k_run_single,             font,attr,lookupcache,step,dataset,sequence,rlmode,handler) - --                                 elseif typ == "gsub_ligature" then - --                                     start, ok = testrun(start,t_run_single,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) - --                                 else - --                                     start, ok = comprun(start,c_run_single,             font,attr,lookupcache,step,dataset,sequence,rlmode,handler) - --                                 end + --                                 head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1)   --                                 if ok then   --                                     done = true   --                                 end - --                             else + --                             end + --                             if start then   --                                 start = getnext(start)   --                             end - --                         elseif id == math_code then - --                             start = getnext(end_of_math(start)) - --                         elseif id == dir_code then - --                             start, topstack, rlmode = txtdirstate(start,dirstack,topstack,rlparmode) - --                         elseif id == localpar_code then - --                             start, rlparmode, rlmode = pardirstate(start) + --                         else + --                            start = getnext(start) + --                         end + --                     elseif char == false then + --                        -- whatever glyph + --                        start = getnext(start) + --                     elseif id == glue_code then + --                         -- happens often + --                        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,handler) + --                             elseif typ == "gsub_ligature" then + --                                 start, ok = testrun(start,t_run_single,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) + --                             else + --                                 start, ok = comprun(start,c_run_single,             font,attr,lookupcache,step,dataset,sequence,rlmode,handler) + --                             end + --                             if ok then + --                                 done = true + --                             end   --                         else   --                             start = getnext(start)   --                         end + --                     elseif id == math_code then + --                         start = getnext(end_of_math(start)) + --                     elseif id == dir_code then + --                         start, topstack, rlmode = txtdirstate(start,dirstack,topstack,rlparmode) + --                     elseif id == localpar_code then + --                         start, rlparmode, rlmode = pardirstate(start) + --                     else + --                         start = getnext(start)   --                     end   --                 end   -- @@ -4288,12 +4783,6 @@ do   --                 while start do   --                     local char, id = ischar(start,font)   --                     if char then - --                      -- local a = attr and getattr(start,0) - --                      -- if a then - --                      --     a = (a == attr) and (not attribute or getprop(start,a_state) == attribute) - --                      -- else - --                      --     a = not attribute or getprop(start,a_state) == attribute - --                      -- end   --                         local a -- happens often so no assignment is faster   --                         if attr then   --                             if getattr(start,0) == attr and (not attribute or getprop(start,a_state) == attribute) then @@ -4306,22 +4795,18 @@ do   --                             for i=1,nofsteps do   --                                 local step        = steps[i]   --                                 local lookupcache = step.coverage - --                                 if lookupcache then - --                                     local lookupmatch = lookupcache[char] - --                                     if lookupmatch then - --                                         -- we could move all code inline but that makes things even more unreadable - --                                         local ok - --                                         head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) - --                                         if ok then - --                                             done = true - --                                             break - --                                         elseif not start then - --                                             -- don't ask why ... shouldn't happen - --                                             break - --                                         end + --                                 local lookupmatch = lookupcache[char] + --                                 if lookupmatch then + --                                     -- we could move all code inline but that makes things even more unreadable + --                                     local ok + --                                     head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) + --                                     if ok then + --                                         done = true + --                                         break + --                                     elseif not start then + --                                         -- don't ask why ... shouldn't happen + --                                         break   --                                     end - --                                 else - --                                     report_missing_coverage(dataset,sequence)   --                                 end   --                             end   --                             if start then @@ -4381,6 +4866,14 @@ do      -- about 15% on arabtype .. then moving the a test also saves a bit (even when      -- often a is not set at all so that one is a bit debatable +    local otfdataset = nil -- todo: make an installer + +    local getfastdics = function(t,k) +        local v = usesfont(k,currentfont) +        t[k] = v +        return v +    end +      function otf.featuresprocessor(head,font,attr,direction,n)          local sequences = sequencelists[font] -- temp hack @@ -4403,6 +4896,12 @@ do              factor       = getthreshold(font)              checkmarks   = tfmdata.properties.checkmarks +            if not otfdataset then +                otfdataset = otf.dataset +            end + +            discs = fastdisc and n and n > 1 and setmetatableindex(getfastdisc) -- maybe inline +          elseif currentfont ~= font then              report_warning("nested call with a different font, level %s, quitting",nesting) @@ -4419,7 +4918,7 @@ do          --     attr = false          -- end -        head = tonut(head) +        local head = tonut(head)          if trace_steps then              checkstep(head) @@ -4428,7 +4927,8 @@ do          local initialrl = direction == "TRT" and -1 or 0          local done      = false -        local datasets  = otf.dataset(tfmdata,font,attr) +     -- local datasets  = otf.dataset(tfmdata,font,attr) +        local datasets  = otfdataset(tfmdata,font,attr)          local dirstack  = { } -- could move outside function but we can have local runs          sweephead       = { } @@ -4438,12 +4938,6 @@ do          -- We don't goto the next node when a disc node is created so that we can then treat          -- the pre, post and replace. It's a bit of a hack but works out ok for most cases. -        local discs = fastdisc and n and n > 1 and setmetatableindex(function(t,k) -            local v = usesfont(k,font) -            t[k] = v -            return v -        end) -          for s=1,#datasets do              local dataset      = datasets[s]              ----- featurevalue = dataset[1] -- todo: pass to function instead of using a global @@ -4453,7 +4947,7 @@ do              local topstack     = 0              local typ          = sequence.type              local gpossing     = typ == "gpos_single" or typ == "gpos_pair" -- store in dataset -            local handler      = handlers[typ] +            local handler      = handlers[typ] -- store in dataset              local steps        = sequence.steps              local nofsteps     = sequence.nofsteps              if not steps then @@ -4489,19 +4983,15 @@ do                               -- for i=1,#m do                               --     local step = m[i]                                      local lookupcache = step.coverage -                                    if lookupcache then -                                        local lookupmatch = lookupcache[char] -                                        if lookupmatch then -                                            -- todo: disc? -                                            local ok -                                            head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) -                                            if ok then -                                                done = true -                                                break -                                            end +                                    local lookupmatch = lookupcache[char] +                                    if lookupmatch then +                                        -- todo: disc? +                                        local ok +                                        head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) +                                        if ok then +                                            done = true +                                            break                                          end -                                    else -                                        report_missing_coverage(dataset,sequence)                                      end                                  end                                  if start then @@ -4523,74 +5013,64 @@ do                  if nofsteps == 1 then -- happens often                      local step = steps[1]                      local lookupcache = step.coverage -                    if not lookupcache then -                        report_missing_coverage(dataset,sequence) -                    else -                        while start do -                            local char, id = ischar(start,font) -                            if char then -                                local lookupmatch = lookupcache[char] -                                if lookupmatch then -                                 -- local a = attr and getattr(start,0) -                                 -- if a then -                                 --     a = (a == attr) and (not attribute or getprop(start,a_state) == attribute) -                                 -- else -                                 --     a = not attribute or getprop(start,a_state) == attribute -                                 -- end -                                    local a -- happens often so no assignment is faster -                                    if attr then -                                        if getattr(start,0) == attr and (not attribute or getprop(start,a_state) == attribute) then -                                            a = true -                                        end -                                    elseif not attribute or getprop(start,a_state) == attribute then +                    while start do +                        local char, id = ischar(start,font) +                        if char then +                            local lookupmatch = lookupcache[char] +                            if lookupmatch then +                                local a -- happens often so no assignment is faster +                                if attr then +                                    if getattr(start,0) == attr and (not attribute or getprop(start,a_state) == attribute) then                                          a = true                                      end -                                    if a then -                                        local ok -                                        head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1) -                                        if ok then -                                            done = true -                                        end -                                        if start then -                                            start = getnext(start) -                                        end -                                    else -                                        start = getnext(start) -                                    end -                                else -                                   start = getnext(start) +                                elseif not attribute or getprop(start,a_state) == attribute then +                                    a = true                                  end -                            elseif char == false then -                               -- whatever glyph -                               start = getnext(start) -                            elseif id == glue_code then -                                -- happens often -                               start = getnext(start) -                            elseif id == disc_code then -                                if not discs or discs[start] == true then +                                if a then                                      local ok -                                    if gpossing then -                                        start, ok = kernrun(start,k_run_single,             font,attr,lookupcache,step,dataset,sequence,rlmode,handler) -                                    elseif typ == "gsub_ligature" then -                                        start, ok = testrun(start,t_run_single,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) -                                    else -                                        start, ok = comprun(start,c_run_single,             font,attr,lookupcache,step,dataset,sequence,rlmode,handler) -                                    end +                                    head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1)                                      if ok then                                          done = true                                      end +                                    if start then +                                        start = getnext(start) +                                    end                                  else                                      start = getnext(start)                                  end -                            elseif id == math_code then -                                start = getnext(end_of_math(start)) -                            elseif id == dir_code then -                                start, topstack, rlmode = txtdirstate(start,dirstack,topstack,rlparmode) -                            elseif id == localpar_code then -                                start, rlparmode, rlmode = pardirstate(start) +                            else +                               start = getnext(start) +                            end +                        elseif char == false then +                           -- whatever glyph +                           start = getnext(start) +                        elseif id == glue_code then +                            -- happens often +                           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,handler) +                                elseif typ == "gsub_ligature" then +                                    start, ok = testrun(start,t_run_single,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) +                                else +                                    start, ok = comprun(start,c_run_single,             font,attr,lookupcache,step,dataset,sequence,rlmode,handler) +                                end +                                if ok then +                                    done = true +                                end                              else                                  start = getnext(start)                              end +                        elseif id == math_code then +                            start = getnext(end_of_math(start)) +                        elseif id == dir_code then +                            start, topstack, rlmode = txtdirstate(start,dirstack,topstack,rlparmode) +                        elseif id == localpar_code then +                            start, rlparmode, rlmode = pardirstate(start) +                        else +                            start = getnext(start)                          end                      end                  else @@ -4600,12 +5080,6 @@ do                          if char then                              local m = merged[char]                              if m then -                             -- local a = attr and getattr(start,0) -                             -- if a then -                             --     a = (a == attr) and (not attribute or getprop(start,a_state) == attribute) -                             -- else -                             --     a = not attribute or getprop(start,a_state) == attribute -                             -- end                                  local a -- happens often so no assignment is faster                                  if attr then                                      if getattr(start,0) == attr and (not attribute or getprop(start,a_state) == attribute) then @@ -4620,22 +5094,18 @@ do                                   -- for i=1,#m do                                   --     local step = m[i]                                          local lookupcache = step.coverage -                                        if lookupcache then -                                            local lookupmatch = lookupcache[char] -                                            if lookupmatch then -                                                -- we could move all code inline but that makes things even more unreadable -                                                local ok -                                                head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) -                                                if ok then -                                                    done = true -                                                    break -                                                elseif not start then -                                                    -- don't ask why ... shouldn't happen -                                                    break -                                                end +                                        local lookupmatch = lookupcache[char] +                                        if lookupmatch then +                                            -- we could move all code inline but that makes things even more unreadable +                                            local ok +                                            head, start, ok = handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) +                                            if ok then +                                                done = true +                                                break +                                            elseif not start then +                                                -- don't ask why ... shouldn't happen +                                                break                                              end -                                        else -                                            report_missing_coverage(dataset,sequence)                                          end                                      end                                      if start then @@ -4787,7 +5257,7 @@ local function hasspacekerns(data)              for i=1,#steps do                  local coverage = steps[i].coverage                  if not coverage then -                    -- maybe an issue +                    -- maybe an issue, can't happen                  elseif coverage[32] then                      return true                  else @@ -4852,7 +5322,7 @@ local function spaceinitializer(tfmdata,value) -- attr                                  -- not now: analyze (simple) rules                              elseif coverage then                                  -- what to do if we have no [1] but only [2] -                                local single = format == gpos_single +                                local single = format == "gpos_single"                                  local kerns  = coverage[32]                                  if kerns then                                      for k, v in next, kerns do @@ -4877,12 +5347,14 @@ local function spaceinitializer(tfmdata,value) -- attr                                              left[k] = kern[3]                                          else                                              local one = kern[1] -                                            if one then +                                            if one and one ~= true then                                                  left[k] = one[3]                                              end                                          end                                      end                                  end +                            else +                                -- can't happen                              end                          end                          last = i diff --git a/tex/context/base/mkiv/font-oup.lua b/tex/context/base/mkiv/font-oup.lua index 5ee6df505..9f4ded5b5 100644 --- a/tex/context/base/mkiv/font-oup.lua +++ b/tex/context/base/mkiv/font-oup.lua @@ -622,7 +622,7 @@ local function checklookups(fontdata,missing,nofmissing)              end          end          if next(done) then -            report("not unicoded: % t",table.sortedkeys(done)) +            report("not unicoded: % t",sortedkeys(done))          end      end  end @@ -1067,13 +1067,14 @@ function readers.pack(data)              end          end -        local function pack_flat(v) -            local tag = tabstr_flat(v) +        local function pack_normal_cc(v) +            local tag = tabstr_normal(v)              local ht = h[tag]              if ht then                  c[ht] = c[ht] + 1                  return ht              else +                v[1] = 0                  nt = nt + 1                  t[nt] = v                  h[tag] = nt @@ -1082,8 +1083,8 @@ function readers.pack(data)              end          end -        local function pack_boolean(v) -            local tag = tabstr_boolean(v) +        local function pack_flat(v) +            local tag = tabstr_flat(v)              local ht = h[tag]              if ht then                  c[ht] = c[ht] + 1 @@ -1127,6 +1128,84 @@ function readers.pack(data)              end          end +        -- saves a lot on noto sans + +        -- can be made more clever + +        local function pack_boolean(v) +            local tag = tabstr_boolean(v) +            local ht = h[tag] +            if ht then +                c[ht] = c[ht] + 1 +                return ht +            else +                nt = nt + 1 +                t[nt] = v +                h[tag] = nt +                c[nt] = 1 +                return nt +            end +        end + +     -- -- This was an experiment to see if we can bypass the luajit limits but loading is +     -- -- still an issue due to other limits so we don't use this ... actually it can +     -- -- prevent a luajittex crash but i don't care too much about that as we can't use +     -- -- that engine anyway then. +     -- +     -- local function check(t) +     --     if type(t) == "table" then +     --         local s = sortedkeys(t) +     --         local n = #s +     --         if n <= 10 then +     --             return +     --         end +     --         local ranges = { } +     --         local first, last +     --         for i=1,#s do +     --             local ti = s[i] +     --             if not first then +     --                 first = ti +     --                 last  = ti +     --             elseif ti == last + 1 then +     --                 last = ti +     --             elseif last - first < 10 then +     --                 -- we could permits a few exceptions +     --                 return +     --             else +     --                 ranges[#ranges+1] = { first, last } +     --                 first, last = nil, nil +     --             end +     --         end +     --         if #ranges > 0 then +     --             return { +     --                 ranges = ranges +     --             } +     --         end +     --     end +     -- end +     -- +     -- local function pack_boolean(v) +     --     local tag +     --     local r = check(v) +     --     if r then +     --         v = r +     --         tag = tabstr_normal(v) +     --     else +     --         tag = tabstr_boolean(v) +     --     end +     --     local ht = h[tag] +     --     if ht then +     --         c[ht] = c[ht] + 1 +     --         return ht +     --     else +     --         nt = nt + 1 +     --         t[nt] = v +     --         h[tag] = nt +     --         c[nt] = 1 +     --         return nt +     --     end +     -- end +          local function pack_final(v)              -- v == number              if c[v] <= criterium then @@ -1146,6 +1225,25 @@ function readers.pack(data)              end          end +        local function pack_final_cc(v) +            -- v == number +            if c[v] <= criterium then +                return t[v] +            else +                -- compact hash +                local hv = hh[v] +                if hv then +                    return hv +                else +                    ntt = ntt + 1 +                    tt[ntt] = t[v] +                    hh[v] = ntt +                    cc[ntt] = c[v] +                    return ntt +                end +            end +        end +          local function success(stage,pass)              if nt == 0 then                  if trace_loading or trace_packing then @@ -1192,9 +1290,9 @@ function readers.pack(data)          local function packers(pass)              if pass == 1 then -                return pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed +                return pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed, pack_normal_cc              else -                return pack_final, pack_final, pack_final, pack_final, pack_final +                return pack_final, pack_final, pack_final, pack_final, pack_final, pack_final_cc              end          end @@ -1212,15 +1310,13 @@ function readers.pack(data)              return          end -        -- -          for pass=1,2 do              if trace_packing then                  report_otf("start packing: stage 1, pass %s",pass)              end -            local pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed = packers(pass) +            local pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed, pack_normal_cc = packers(pass)              for unicode, description in next, descriptions do                  local boundingbox = description.boundingbox @@ -1267,8 +1363,8 @@ function readers.pack(data)                                      else                                          for g1, d1 in next, c do                                              for g2, d2 in next, d1 do -                                                local f = d2[1] if f then d2[1] = pack_indexed(f) end -                                                local s = d2[2] if s then d2[2] = pack_indexed(s) end +                                                local f = d2[1] if f and f ~= true then d2[1] = pack_indexed(f) end +                                                local s = d2[2] if s and s ~= true then d2[2] = pack_indexed(s) end                                              end                                          end                                      end @@ -1280,7 +1376,9 @@ function readers.pack(data)                                          step.coverage = pack_normal(c)                                      else                                          for g1, d1 in next, c do -                                            c[g1] = pack_indexed(d1) +                                            if d1 ~= true then +                                                c[g1] = pack_indexed(d1) +                                            end                                          end                                      end                                  end @@ -1400,7 +1498,7 @@ function readers.pack(data)                              for i=1,#deltas do                                  local di = deltas[i]                                  local d  = di.deltas -                                local r  = di.regions +                             -- local r  = di.regions                                  for j=1,#d do                                      d[j] = pack_indexed(d[j])                                  end @@ -1440,7 +1538,7 @@ function readers.pack(data)                      report_otf("start packing: stage 2, pass %s",pass)                  end -                local pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed = packers(pass) +                local pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed, pack_normal_cc = packers(pass)                  for unicode, description in next, descriptions do                      local math = description.math @@ -1464,9 +1562,7 @@ function readers.pack(data)                                  if kind == "gpos_pair" then                                      local c = step.coverage                                      if c then -                                        if step.format == "kern" then -                                            -- todo ! -                                        else +                                        if step.format ~= "kern" then                                              for g1, d1 in next, c do                                                  for g2, d2 in next, d1 do                                                      d1[g2] = pack_normal(d2) @@ -1474,11 +1570,22 @@ function readers.pack(data)                                              end                                          end                                      end ---                                 elseif kind == "gpos_mark2base" or kind == "gpos_mark2mark" or kind == "gpos_mark2ligature" then --- local c = step.baseclasses --- for k, v in next, c do ---     c[k] = pack_normal(v) --- end +                             -- elseif kind == "gpos_cursive" then +                             --     local c = step.coverage -- new +                             --     if c then +                             --         for g1, d1 in next, c do +                             --             c[g1] = pack_normal_cc(d1) +                             --         end +                             --     end +                                elseif kind == "gpos_mark2ligature" then +                                    local c = step.baseclasses -- new +                                    if c then +                                        for g1, d1 in next, c do +                                            for g2, d2 in next, d1 do +                                                d1[g2] = pack_normal(d2) +                                            end +                                        end +                                    end                                  end                                  local rules = step.rules                                  if rules then @@ -1526,7 +1633,7 @@ function readers.pack(data)                      report_otf("start packing: stage 3, pass %s",pass)                  end -                local pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed = packers(pass) +                local pack_normal, pack_indexed, pack_flat, pack_boolean, pack_mixed, pack_normal_cc = packers(pass)                  local function packthem(sequences)                      for i=1,#sequences do @@ -1540,18 +1647,23 @@ function readers.pack(data)                                  if kind == "gpos_pair" then                                      local c = step.coverage                                      if c then -                                        if step.format == "kern" then -                                            -- todo ! -                                        else +                                        if step.format ~= "kern" then                                              for g1, d1 in next, c do                                                  c[g1] = pack_normal(d1)                                              end                                          end                                      end +                                elseif kind == "gpos_cursive" then +                                    local c = step.coverage +                                    if c then +                                        for g1, d1 in next, c do +                                            c[g1] = pack_normal_cc(d1) +                                        end +                                    end                                  end                              end                          end -                   end +                    end                  end                  if sequences then @@ -1627,6 +1739,15 @@ function readers.unpack(data)               -- end              end +         -- local function expandranges(t,ranges) +         --     for i=1,#ranges do +         --         local r = ranges[i] +         --         for k=r[1],r[2] do +         --             t[k] = true +         --         end +         --     end +         -- end +              local function unpackthem(sequences)                  for i=1,#sequences do                      local sequence  = sequences[i] @@ -1636,6 +1757,19 @@ function readers.unpack(data)                      local features  = sequence.features                      local flags     = sequence.flags                      local markclass = sequence.markclass +                    if features then +                        local tv = tables[features] +                        if tv then +                            sequence.features = tv +                            features = tv +                        end +                        for script, feature in next, features do +                            local tv = tables[feature] +                            if tv then +                                features[script] = tv +                            end +                        end +                    end                      if steps then                          for i=1,#steps do                              local step = steps[i] @@ -1689,6 +1823,11 @@ function readers.unpack(data)                                  local c = step.coverage                                  if c then                                      for g1, d1 in next, c do +                                        local tv = tables[d1] +                                        if tv then +                                            d1 = tv +                                            c[g1] = d1 +                                        end                                          local f = tables[d1[2]] if f then d1[2] = f end                                          local s = tables[d1[3]] if s then d1[3] = s end                                      end @@ -1696,12 +1835,6 @@ function readers.unpack(data)                              elseif kind == "gpos_mark2base" or kind == "gpos_mark2mark" then                                  local c = step.baseclasses                                  if c then --- for k, v in next, c do ---     local tv = tables[v] ---     if tv then ---         c[k] = tv ---     end --- end                                      for g1, d1 in next, c do                                          for g2, d2 in next, d1 do                                              local tv = tables[d2] @@ -1723,14 +1856,13 @@ function readers.unpack(data)                              elseif kind == "gpos_mark2ligature" then                                  local c = step.baseclasses                                  if c then --- for k, v in next, c do ---     local tv = tables[v] ---     if tv then ---         c[k] = tv ---     end --- end                                      for g1, d1 in next, c do                                          for g2, d2 in next, d1 do +                                            local tv = tables[d2] -- new +                                            if tv then +                                                d2 = tv +                                                d1[g2] = d2 +                                            end                                              for g3, d3 in next, d2 do                                                  local tv = tables[d2[g3]]                                                  if tv then @@ -1767,6 +1899,18 @@ function readers.unpack(data)                                                  before[i] = tv                                              end                                          end +                                     -- for i=1,#before do +                                     --     local bi = before[i] +                                     --     local tv = tables[bi] +                                     --     if tv then +                                     --         bi = tv +                                     --         before[i] = bi +                                     --     end +                                     --     local ranges = bi.ranges +                                     --     if ranges then +                                     --         expandranges(bi,ranges) +                                     --     end +                                     -- end                                      end                                      local after = rule.after                                      if after then @@ -1781,6 +1925,18 @@ function readers.unpack(data)                                                  after[i] = tv                                              end                                          end +                                     -- for i=1,#after do +                                     --     local ai = after[i] +                                     --     local tv = tables[ai] +                                     --     if tv then +                                     --         ai = tv +                                     --         after[i] = ai +                                     --     end +                                     --     local ranges = ai.ranges +                                     --     if ranges then +                                     --         expandranges(ai,ranges) +                                     --     end +                                     -- end                                      end                                      local current = rule.current                                      if current then @@ -1795,6 +1951,18 @@ function readers.unpack(data)                                                  current[i] = tv                                              end                                          end +                                     -- for i=1,#current do +                                     --     local ci = current[i] +                                     --     local tv = tables[ci] +                                     --     if tv then +                                     --         ci = tv +                                     --         current[i] = ci +                                     --     end +                                     --     local ranges = ci.ranges +                                     --     if ranges then +                                     --         expandranges(ci,ranges) +                                     --     end +                                     -- end                                      end                                   -- local lookups = rule.lookups                                   -- if lookups then @@ -1814,19 +1982,6 @@ function readers.unpack(data)                              end                          end                      end -                    if features then -                        local tv = tables[features] -                        if tv then -                            sequence.features = tv -                            features = tv -                        end -                        for script, feature in next, features do -                            local tv = tables[feature] -                            if tv then -                                features[script] = tv -                            end -                        end -                    end                      if order then                          local tv = tables[order]                          if tv then @@ -2140,11 +2295,14 @@ local function checkpairs(lookup)              for g1, d1 in next, coverage do                  for g2, d2 in next, d1 do                      if d2[2] then +                        --- true or { a, b, c, d }                          kerns = false                          break                      else                          local v = d2[1] -                        if v[1] ~= 0 or v[2] ~= 0 or v[4] ~= 0 then +                        if v == true then +                            -- all zero +                        elseif v and (v[1] ~= 0 or v[2] ~= 0 or v[4] ~= 0) then                              kerns = false                              break                          end @@ -2155,7 +2313,12 @@ local function checkpairs(lookup)                  report("turning pairs of step %a of %a lookup %a into kerns",i,lookup.type,lookup.name)                  for g1, d1 in next, coverage do                      for g2, d2 in next, d1 do -                        d1[g2] = d2[1][3] +                        local v = d2[1] +                        if v == true then +                            d1[g2] = nil +                        elseif v then +                            d1[g2] = v[3] +                        end                      end                  end                  step.format = "kern" @@ -2185,6 +2348,7 @@ function readers.compact(data)                  allsteps = allsteps + nofsteps                  if nofsteps > 1 then                      local kind = lookup.type +                    local merg = merged                      if kind == "gsub_single" or kind == "gsub_alternate" or kind == "gsub_multiple" then                          merged = merged + mergesteps_1(lookup)                      elseif kind == "gsub_ligature" then @@ -2200,6 +2364,9 @@ function readers.compact(data)                      elseif kind == "gpos_mark2mark" or kind == "gpos_mark2base" or kind == "gpos_mark2ligature" then                          merged = merged + mergesteps_3(lookup)                      end +                    if merg ~= merged then +                        lookup.merged = true +                    end                  end              end          else @@ -2268,7 +2435,7 @@ function readers.expand(data)              for i=1,#t do                  local step     = t[i]                  local coverage = step.coverage -                for k, v in next, coverage do +                for k in next, coverage do                      local m = merged[k]                      if m then                          m[2] = i @@ -2291,6 +2458,7 @@ function readers.expand(data)                  local sequence = sequences[i]                  local steps    = sequence.steps                  if steps then +                    local nofsteps = sequence.nofsteps                      setmetatableindex(steps,mergesteps) @@ -2304,7 +2472,14 @@ function readers.expand(data)                              sequence.markclass = markclasses[markclass]                          end                      end -                    local nofsteps = sequence.nofsteps +                    local flags = sequence.flags +                    if flags then +                        flags[5] = flags[1] ~= false  -- otherwise "mark" +                                or flags[2] ~= false  -- otherwise "base" +                                or flags[3] ~= false  -- otherwise "ligature" +                                or sequence.markclass +                                or false +                    end                      for i=1,nofsteps do                          local step = steps[i]                          local baseclasses = step.baseclasses @@ -2373,6 +2548,7 @@ function readers.expand(data)                                      end                                  end                                  if sequence[1] then -- we merge coverage into one +                                    sequence.n = #sequence -- tiny speedup                                      rulesize = rulesize + 1                                      rulehash[rulesize] = {                                          nofrules,     -- 1 @@ -2393,6 +2569,7 @@ function readers.expand(data)                                          end                                      end                                  end +                                rulehash.n = #rulehash -- tiny speedup                              end                          end                      end diff --git a/tex/context/base/mkiv/font-shp.lua b/tex/context/base/mkiv/font-shp.lua index c465ec91b..918304a0b 100644 --- a/tex/context/base/mkiv/font-shp.lua +++ b/tex/context/base/mkiv/font-shp.lua @@ -362,7 +362,7 @@ local function addvariableshapes(tfmdata,key,value)                       -- we need inline in order to support color                          local bt, et = getactualtext(char.tounicode or char.unicode or unicode)                          char.commands = { -                            { "pdf", segmentstopdf(segments,factor,bt,et) } +                            { "pdf", "origin", segmentstopdf(segments,factor,bt,et) }                          }                      end                  end diff --git a/tex/context/base/mkiv/font-vf.lua b/tex/context/base/mkiv/font-vir.lua index ded992850..03ad7fc85 100644 --- a/tex/context/base/mkiv/font-vf.lua +++ b/tex/context/base/mkiv/font-vir.lua @@ -1,4 +1,4 @@ -if not modules then modules = { } end modules ['font-vf'] = { +if not modules then modules = { } end modules ['font-vir'] = {      version   = 1.001,      comment   = "companion to font-ini.mkiv",      author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL", diff --git a/tex/context/base/mkiv/hand-ini.mkiv b/tex/context/base/mkiv/hand-ini.mkiv index d4bffdb14..f678a88da 100644 --- a/tex/context/base/mkiv/hand-ini.mkiv +++ b/tex/context/base/mkiv/hand-ini.mkiv @@ -60,6 +60,7 @@  \def\font_expansion_enable  {\normaladjustspacing\plusthree}  \def\font_expansion_enable_k{\normaladjustspacing\plustwo} +\def\font_expansion_enable_n{\normaladjustspacing\plusone}  \def\font_expansion_disable {\normaladjustspacing\zerocount}  \def\font_protruding_enable {\normalprotrudechars\plustwo  } diff --git a/tex/context/base/mkiv/l-package.lua b/tex/context/base/mkiv/l-package.lua index 075fcde25..5f72dd6b2 100644 --- a/tex/context/base/mkiv/l-package.lua +++ b/tex/context/base/mkiv/l-package.lua @@ -23,6 +23,7 @@ local P, S, Cs, lpegmatch = lpeg.P, lpeg.S, lpeg.Cs, lpeg.match  local package    = package  local searchers  = package.searchers or package.loaders +local insert, remove = table.insert, table.remove  -- dummies @@ -191,7 +192,25 @@ local function registerpath(tag,what,target,...)              add(path)          end      end -    return paths +end + +local function pushpath(tag,what,target,path) +    local path = helpers.cleanpath(path) +    insert(target,1,path) +    if helpers.trace then +        helpers.report("pushing %s path in front: %s",tag,path) +    end +end + +local function poppath(tag,what,target) +    local path = remove(target,1) +    if helpers.trace then +        if path then +            helpers.report("popping %s path from front: %s",tag,path) +        else +            helpers.report("no %s path to pop",tag) +        end +    end  end  helpers.registerpath = registerpath @@ -199,10 +218,22 @@ helpers.registerpath = registerpath  function package.extraluapath(...)      registerpath("extra lua","lua",extraluapaths,...)  end +function package.pushluapath(path) +    pushpath("extra lua","lua",extraluapaths,path) +end +function package.popluapath() +    poppath("extra lua","lua",extraluapaths) +end  function package.extralibpath(...)      registerpath("extra lib","lib",extralibpaths,...)  end +function package.pushlibpath(path) +    pushpath("extra lib","lib",extralibpaths,path) +end +function package.poplibpath() +    poppath("extra lib","lua",extralibpaths) +end  -- lib loader (used elsewhere) diff --git a/tex/context/base/mkiv/lpdf-col.lua b/tex/context/base/mkiv/lpdf-col.lua index af01c7dd2..a1006f773 100644 --- a/tex/context/base/mkiv/lpdf-col.lua +++ b/tex/context/base/mkiv/lpdf-col.lua @@ -22,7 +22,7 @@ local nodeinjections          = backends.pdf.nodeinjections  local codeinjections          = backends.pdf.codeinjections  local registrations           = backends.pdf.registrations -local nodepool                = nodes.pool +local nodepool                = nodes.nuts.pool  local register                = nodepool.register  local pdfliteral              = nodepool.pdfliteral @@ -707,25 +707,25 @@ local f_slant = formatters["q 1 0 %F 1 0 0 cm"]  backends.pdf.tables.vfspecials = allocate { -- todo: distinguish between glyph and rule color -    red        = { "pdf", "1 0 0 rg 1 0 0 RG" }, -    green      = { "pdf", "0 1 0 rg 0 1 0 RG" }, -    blue       = { "pdf", "0 0 1 rg 0 0 1 RG" }, -    gray       = { "pdf", ".75 g .75 G" }, -    black      = { "pdf", "0 g 0 G" }, +    red        = { "pdf", "origin", "1 0 0 rg 1 0 0 RG" }, +    green      = { "pdf", "origin", "0 1 0 rg 0 1 0 RG" }, +    blue       = { "pdf", "origin", "0 0 1 rg 0 0 1 RG" }, +    gray       = { "pdf", "origin", ".75 g .75 G" }, +    black      = { "pdf", "origin", "0 g 0 G" },      rulecolors = { -            red        = { "pdf", '1 0 0 rg' }, -            green      = { "pdf", '0 1 0 rg' }, -            blue       = { "pdf", '0 0 1 rg' }, -            gray       = { "pdf", '.5 g' }, -            black      = { "pdf", '0 g' }, -            palered    = { "pdf", '1 .75 .75 rg' }, -            palegreen  = { "pdf", '.75 1 .75 rg' }, -            paleblue   = { "pdf", '.75 .75 1 rg' }, -            palegray   = { "pdf", '.75 g' }, +            red        = { "pdf", "origin", '1 0 0 rg' }, +            green      = { "pdf", "origin", '0 1 0 rg' }, +            blue       = { "pdf", "origin", '0 0 1 rg' }, +            gray       = { "pdf", "origin", '.5 g' }, +            black      = { "pdf", "origin", '0 g' }, +            palered    = { "pdf", "origin", '1 .75 .75 rg' }, +            palegreen  = { "pdf", "origin", '.75 1 .75 rg' }, +            paleblue   = { "pdf", "origin", '.75 .75 1 rg' }, +            palegray   = { "pdf", "origin", '.75 g' },      }, -    startslant = function(a) return { "pdf", f_slant(a) } end, -    stopslant  = { "pdf", "Q" }, +    startslant = function(a) return { "pdf", "origin", f_slant(a) } end, +    stopslant  = { "pdf", "origin", "Q" },  } diff --git a/tex/context/base/mkiv/lpdf-mis.lua b/tex/context/base/mkiv/lpdf-mis.lua index 91dd08afd..9d903ce96 100644 --- a/tex/context/base/mkiv/lpdf-mis.lua +++ b/tex/context/base/mkiv/lpdf-mis.lua @@ -25,9 +25,10 @@ local nodeinjections       = backends.pdf.nodeinjections  local codeinjections       = backends.pdf.codeinjections  local registrations        = backends.pdf.registrations -local copy_node            = node.copy +local nuts                 = nodes.nuts +local copy_node            = nuts.copy -local nodepool             = nodes.pool +local nodepool             = nuts.pool  local pdfliteral           = nodepool.pdfliteral  local register             = nodepool.register diff --git a/tex/context/base/mkiv/lpdf-ren.lua b/tex/context/base/mkiv/lpdf-ren.lua index 47075ee08..a57c550fd 100644 --- a/tex/context/base/mkiv/lpdf-ren.lua +++ b/tex/context/base/mkiv/lpdf-ren.lua @@ -49,7 +49,10 @@ local addtocatalog        = lpdf.addtocatalog  local escaped             = lpdf.escaped -local nodepool            = nodes.pool +local nuts                = nodes.nuts +local copy_node           = nuts.copy + +local nodepool            = nuts.pool  local register            = nodepool.register  local pdfliteral          = nodepool.pdfliteral @@ -62,8 +65,6 @@ local pdf_design          = pdfconstant("Design")  local pdf_toggle          = pdfconstant("Toggle")  local pdf_setocgstate     = pdfconstant("SetOCGState") -local copy_node           = node.copy -  local pdf_print = {      [v_yes] = pdfdictionary { PrintState = pdf_on  },      [v_no ] = pdfdictionary { PrintState = pdf_off }, diff --git a/tex/context/base/mkiv/lpdf-xmp.lua b/tex/context/base/mkiv/lpdf-xmp.lua index 26ced4aee..a0005eb8a 100644 --- a/tex/context/base/mkiv/lpdf-xmp.lua +++ b/tex/context/base/mkiv/lpdf-xmp.lua @@ -241,6 +241,8 @@ end  -- flushing +local add_xmp_blob = true  directives.register("backend.xmp",function(v) add_xmp_blob = v end) +  local function flushxmpinfo()      commands.pushrandomseed()      commands.setrandomseed(os.time()) @@ -261,42 +263,45 @@ local function flushxmpinfo()          instanceid = "uuid:" .. os.uuid()      end -    pdfaddxmpinfo("DocumentID",      documentid) -    pdfaddxmpinfo("InstanceID",      instanceid) -    pdfaddxmpinfo("Producer",        producer) -    pdfaddxmpinfo("CreatorTool",     creator) -    pdfaddxmpinfo("CreateDate",      time) -    pdfaddxmpinfo("ModifyDate",      time) -    pdfaddxmpinfo("MetadataDate",    time) -    pdfaddxmpinfo("PTEX.Fullbanner", fullbanner) -      pdfaddtoinfo("Producer",         producer)      pdfaddtoinfo("Creator",          creator)      pdfaddtoinfo("CreationDate",     time)      pdfaddtoinfo("ModDate",          time)   -- pdfaddtoinfo("PTEX.Fullbanner",  fullbanner) -- no checking done on existence -    local blob = xml.tostring(xml.first(xmp or valid_xmp(),"/x:xmpmeta")) -    local md = pdfdictionary { -        Subtype = pdfconstant("XML"), -        Type    = pdfconstant("Metadata"), -    } -    if trace_xmp then -        report_xmp("data flushed, see log file") -        logs.pushtarget("logfile") -        report_xmp("start xmp blob") -        logs.newline() -        logs.writer(blob) -        logs.newline() -        report_xmp("stop xmp blob") -        logs.poptarget() -    end -    blob = format(xpacket,blob) -    if not verbose and pdf.getcompresslevel() > 0 then -        blob = gsub(blob,">%s+<","><") +    if add_xmp_blob then + +        pdfaddxmpinfo("DocumentID",      documentid) +        pdfaddxmpinfo("InstanceID",      instanceid) +        pdfaddxmpinfo("Producer",        producer) +        pdfaddxmpinfo("CreatorTool",     creator) +        pdfaddxmpinfo("CreateDate",      time) +        pdfaddxmpinfo("ModifyDate",      time) +        pdfaddxmpinfo("MetadataDate",    time) +        pdfaddxmpinfo("PTEX.Fullbanner", fullbanner) + +        local blob = xml.tostring(xml.first(xmp or valid_xmp(),"/x:xmpmeta")) +        local md = pdfdictionary { +            Subtype = pdfconstant("XML"), +            Type    = pdfconstant("Metadata"), +        } +        if trace_xmp then +            report_xmp("data flushed, see log file") +            logs.pushtarget("logfile") +            report_xmp("start xmp blob") +            logs.newline() +            logs.writer(blob) +            logs.newline() +            report_xmp("stop xmp blob") +            logs.poptarget() +        end +        blob = format(xpacket,blob) +        if not verbose and pdf.getcompresslevel() > 0 then +            blob = gsub(blob,">%s+<","><") +        end +        local r = pdfflushstreamobject(blob,md,false) -- uncompressed +        lpdf.addtocatalog("Metadata",pdfreference(r))      end -    local r = pdfflushstreamobject(blob,md,false) -- uncompressed -    lpdf.addtocatalog("Metadata",pdfreference(r))      commands.poprandomseed() -- hack  end diff --git a/tex/context/base/mkiv/lxml-tex.lua b/tex/context/base/mkiv/lxml-tex.lua index f79aa708f..76a20fbef 100644 --- a/tex/context/base/mkiv/lxml-tex.lua +++ b/tex/context/base/mkiv/lxml-tex.lua @@ -34,6 +34,8 @@ local commands          = commands  local context           = context  local contextsprint     = context.sprint             -- with catcodes (here we use fast variants, but with option for tracing) +local synctex           = luatex.synctex +  local implement         = interfaces.implement  local xmlelements       = xml.elements @@ -486,13 +488,8 @@ local noffiles     = 0  local nofconverted = 0  local linenumbers  = false --- directives.register("lxml.linenumbers", function(v) ---     linenumbers = v --- end) - -directives.register("system.synctex.xml",function(v) -    linenumbers = v -end) +synctex.registerenabler (function() linenumbers = true  end) +synctex.registerdisabler(function() linenumbers = false end)  function xml.load(filename,settings)      noffiles, nofconverted = noffiles + 1, nofconverted + 1 @@ -713,13 +710,8 @@ local setfilename = false  local trace_name  = false  local report_name = logs.reporter("lxml") -directives.register("system.synctex.xml",function(v) -    if v then -        setfilename = luatex.synctex.setfilename -    else -        setfilename = false -    end -end) +synctex.registerenabler (function() setfilename = synctex.setfilename end) +synctex.registerdisabler(function() setfilename = false end)  local function syncfilename(e,where)      local cf = e.cf @@ -1050,9 +1042,6 @@ xml.cprint = cprint local xmlcprint = cprint  -- calls ct  mathml  -> will be re  function lxml.main(id)      local root = getid(id) ---     if setfilename then ---         syncfilename(root,"main") ---     end      xmlserialize(root,xmltexhandler) -- the real root (@rt@)  end diff --git a/tex/context/base/mkiv/m-fonts-plugins.mkiv b/tex/context/base/mkiv/m-fonts-plugins.mkiv index 7678f820c..85411025f 100644 --- a/tex/context/base/mkiv/m-fonts-plugins.mkiv +++ b/tex/context/base/mkiv/m-fonts-plugins.mkiv @@ -19,6 +19,8 @@  %D below should work in the generic loader too. It's anyhow an illustration of  %D how \type {ffi} can work be used in a practical application. +% \enabletrackers[resolvers.ffilib] +  \registerctxluafile{font-txt}{1.001} % generic text handler  \registerctxluafile{font-phb}{1.001} % harfbuzz plugin @@ -201,10 +203,11 @@    [arabic-uniscribe]    [mode=plug,     features=harfbuzz, -   script=arab,language=dflt,ccmp=yes, -   init=yes,medi=yes,fina=yes,isol=yes, -   liga=yes,dlig=yes,rlig=yes,clig=yes,calt=yes, -   mark=yes,mkmk=yes,kern=yes,curs=yes, +   script=arab,language=dflt, +%    ccmp=yes, +%    init=yes,medi=yes,fina=yes,isol=yes, +%    liga=yes,dlig=yes,rlig=yes,clig=yes,calt=yes, +%    mark=yes,mkmk=yes,kern=yes,curs=yes,     shaper=uniscribe]  \starttexdefinition RunLatinTest #1#2#3#4#5 @@ -320,7 +323,7 @@          context.RunLatinTest (name, 100, "context",  "base",      "latin")          context.RunLatinTest (name, 100, "context",  "node",      "latin")          context.RunLatinTest (name, 100, "harfbuzz", "native",    "latin") -     -- context.RunLatinTest (name, 100, "harfbuzz", "uniscribe", "latin") +        context.RunLatinTest (name, 100, "harfbuzz", "uniscribe", "latin")       -- context.RunLatinTest (name,   1, "context",  "text",      "latin")       -- context.RunLatinTest (name,   1, "harfbuzz", "binary",    "latin") @@ -336,7 +339,8 @@      -- ARABIC      local list = { -        "arabtype" +        "arabtype", +        "husayni",      }      data.timings["arabic"] = { @@ -353,7 +357,7 @@          context.RunArabicTest (name, 100, "context",  "base",      "arabic")          context.RunArabicTest (name, 100, "context",  "node",      "arabic")          context.RunArabicTest (name, 100, "harfbuzz", "native",    "arabic") -     -- context.RunArabicTest (name, 100, "harfbuzz", "uniscribe", "arabic") +        context.RunArabicTest (name, 100, "harfbuzz", "uniscribe", "arabic")       -- context.RunArabicTest (name,   1, "context",  "text",      "arabic")       -- context.RunArabicTest (name,   1, "harfbuzz", "binary",    "arabic") @@ -369,7 +373,8 @@      -- MIXED      local list = { -        "arabtype" +        "arabtype", +        "husayni"      }      data.timings["mixed"] = { @@ -386,7 +391,7 @@          context.RunArabicTest (name, 100, "context",  "base",      "mixed")          context.RunArabicTest (name, 100, "context",  "node",      "mixed")          context.RunArabicTest (name, 100, "harfbuzz", "native",    "mixed") -     -- context.RunArabicTest (name, 100, "harfbuzz", "uniscribe", "mixed") +        context.RunArabicTest (name, 100, "harfbuzz", "uniscribe", "mixed")       -- context.RunArabicTest (name,   1, "context",  "text",      "mixed")       -- context.RunArabicTest (name,   1, "harfbuzz", "binary",    "mixed") diff --git a/tex/context/base/mkiv/meta-fnt.lua b/tex/context/base/mkiv/meta-fnt.lua index ea7f6f2bc..e7e83c694 100644 --- a/tex/context/base/mkiv/meta-fnt.lua +++ b/tex/context/base/mkiv/meta-fnt.lua @@ -66,7 +66,7 @@ local flusher = {          if inline then              characters[slot] = {                  commands = { -                    { "pdf", code }, +                    { "pdf", "origin", code },                  }              }          else diff --git a/tex/context/base/mkiv/node-fin.lua b/tex/context/base/mkiv/node-fin.lua index ffb2ae49e..5d01f6e5a 100644 --- a/tex/context/base/mkiv/node-fin.lua +++ b/tex/context/base/mkiv/node-fin.lua @@ -124,12 +124,6 @@ function nodes.installattributehandler(plugin)      return loadstripped(template)()  end --- for the moment: - -local function copied(n) -    return copy_node(tonut(n)) -end -  -- the injectors  local nsdata, nsnone, nslistwise, nsforced, nsselector, nstrigger @@ -163,13 +157,13 @@ function states.finalize(namespace,attribute,head) -- is this one ok?          if id == hlist_code or id == vlist_code then              local content = getlist(head)              if content then -                local list = insert_node_before(content,content,copied(nsnone)) -- two return values +                local list = insert_node_before(content,content,copy_node(nsnone)) -- two return values                  if list ~= content then                      setlist(head,list)                  end              end          else -            head = insert_node_before(head,head,copied(nsnone)) +            head = insert_node_before(head,head,copy_node(nsnone))          end          return tonode(head), true, true      end @@ -178,7 +172,7 @@ end  -- we need to deal with literals too (reset as well as oval) -local function process(namespace,attribute,head,inheritance,default) -- one attribute +local function process(attribute,head,inheritance,default) -- one attribute      local stack  = head      local done   = false      local check  = false @@ -201,7 +195,7 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr                  if nstrigger and getattr(stack,nstrigger) then                      local outer = getattr(stack,attribute)                      if outer ~= inheritance then -                        local list, ok = process(namespace,attribute,content,inheritance,outer) +                        local list, ok = process(attribute,content,inheritance,outer)                          if content ~= list then                              setlist(stack,list)                          end @@ -209,7 +203,7 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr                              done = true                          end                      else -                        local list, ok = process(namespace,attribute,content,inheritance,default) +                        local list, ok = process(attribute,content,inheritance,default)                          if content ~= list then                              setlist(stack,list)                          end @@ -218,7 +212,7 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr                          end                      end                  else -                    local list, ok = process(namespace,attribute,content,inheritance,default) +                    local list, ok = process(attribute,content,inheritance,default)                      if content ~= list then                          setlist(stack,list)                      end @@ -237,12 +231,12 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr              if c then                  if default and c == inheritance then                      if current ~= default then -                        head    = insert_node_before(head,stack,copied(nsdata[default])) +                        head    = insert_node_before(head,stack,copy_node(nsdata[default]))                          current = default                          done    = true                      end                  elseif current ~= c then -                    head    = insert_node_before(head,stack,copied(nsdata[c])) +                    head    = insert_node_before(head,stack,copy_node(nsdata[c]))                      current = c                      done    = true                  end @@ -259,7 +253,7 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr                      if nstrigger and getattr(stack,nstrigger) then                          local outer = getattr(stack,attribute)                          if outer ~= inheritance then -                            local list, ok = process(namespace,attribute,leader,inheritance,outer) +                            local list, ok = process(attribute,leader,inheritance,outer)                              if leader ~= list then                                  setleader(stack,list)                              end @@ -267,7 +261,7 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr                                  done = true                              end                          else -                            local list, ok = process(namespace,attribute,leader,inheritance,default) +                            local list, ok = process(attribute,leader,inheritance,default)                              if leader ~= list then                                  setleader(stack,list)                              end @@ -276,7 +270,7 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr                              end                          end                      else -                        local list, ok = process(namespace,attribute,leader,inheritance,default) +                        local list, ok = process(attribute,leader,inheritance,default)                          if leader ~= list then                              setleader(stack,list)                          end @@ -290,12 +284,12 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr                  end              elseif default and inheritance then                  if current ~= default then -                    head    = insert_node_before(head,stack,copied(nsdata[default])) +                    head    = insert_node_before(head,stack,copy_node(nsdata[default]))                      current = default                      done    = true                  end              elseif current > 0 then -                head    = insert_node_before(head,stack,copied(nsnone)) +                head    = insert_node_before(head,stack,copy_node(nsnone))                  current = 0                  done    = true              end @@ -307,7 +301,7 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr  end  states.process = function(namespace,attribute,head,default) -    local head, done = process(namespace,attribute,tonut(head),default) +    local head, done = process(attribute,tonut(head),default)      return tonode(head), done  end @@ -317,7 +311,8 @@ end  -- state changes while the main state stays the same (like two glyphs following  -- each other with the same color but different color spaces e.g. \showcolor) -local function selective(namespace,attribute,head,inheritance,default) -- two attributes +local function selective(attribute,head,inheritance,default) -- two attributes + -- local head   = head      local stack  = head      local done   = false      local check  = false @@ -340,7 +335,7 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at                  if nstrigger and getattr(stack,nstrigger) then                      local outer = getattr(stack,attribute)                      if outer ~= inheritance then -                        local list, ok = selective(namespace,attribute,content,inheritance,outer) +                        local list, ok = selective(attribute,content,inheritance,outer)                          if content ~= list then                              setlist(stack,list)                          end @@ -348,7 +343,7 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at                              done = true                          end                      else -                        local list, ok = selective(namespace,attribute,content,inheritance,default) +                        local list, ok = selective(attribute,content,inheritance,default)                          if content ~= list then                              setlist(stack,list)                          end @@ -357,7 +352,7 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at                          end                      end                  else -                    local list, ok = selective(namespace,attribute,content,inheritance,default) +                    local list, ok = selective(attribute,content,inheritance,default)                      if content ~= list then                          setlist(stack,list)                      end @@ -377,7 +372,7 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at                  if default and c == inheritance then                      if current ~= default then                          local data = nsdata[default] -                        head = insert_node_before(head,stack,copied(data[nsforced or getattr(stack,nsselector) or nsselector])) +                        head = insert_node_before(head,stack,copy_node(data[nsforced or getattr(stack,nsselector) or nsselector]))                          current = default                          if ok then                              done = true @@ -385,9 +380,11 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at                      end                  else                      local s = getattr(stack,nsselector) +                 -- local s = nsforced or getattr(stack,nsselector)                      if current ~= c or current_selector ~= s then                          local data = nsdata[c] -                        head = insert_node_before(head,stack,copied(data[nsforced or getattr(stack,nsselector) or nsselector])) +                        head = insert_node_before(head,stack,copy_node(data[nsforced or s or nsselector])) +                     -- head = insert_node_before(head,stack,copy_node(data[s or nsselector]))                          current = c                          current_selector = s                          if ok then @@ -400,7 +397,7 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at                      if nstrigger and getattr(stack,nstrigger) then                          local outer = getatribute(stack,attribute)                          if outer ~= inheritance then -                            local list, ok = selective(namespace,attribute,leader,inheritance,outer) +                            local list, ok = selective(attribute,leader,inheritance,outer)                              if leader ~= list then                                  setleader(stack,list)                              end @@ -408,7 +405,7 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at                                  done = true                              end                          else -                            local list, ok = selective(namespace,attribute,leader,inheritance,default) +                            local list, ok = selective(attribute,leader,inheritance,default)                              if leader ~= list then                                  setleader(stack,list)                              end @@ -417,7 +414,7 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at                              end                          end                      else -                        local list, ok = selective(namespace,attribute,leader,inheritance,default) +                        local list, ok = selective(attribute,leader,inheritance,default)                          if leader ~= list then                              setleader(stack,list)                          end @@ -431,12 +428,12 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at              elseif default and inheritance then                  if current ~= default then                      local data = nsdata[default] -                    head    = insert_node_before(head,stack,copied(data[nsforced or getattr(stack,nsselector) or nsselector])) +                    head    = insert_node_before(head,stack,copy_node(data[nsforced or getattr(stack,nsselector) or nsselector]))                      current = default                      done    = true                  end              elseif current > 0 then -                head = insert_node_before(head,stack,copied(nsnone)) +                head = insert_node_before(head,stack,copy_node(nsnone))                  current, current_selector, done = 0, 0, true              end              check = false @@ -447,8 +444,8 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at  end  states.selective = function(namespace,attribute,head,default) -    local head, done = selective(namespace,attribute,tonut(head),default) -    return tonode(head), done +    local head = selective(attribute,tonut(head),default) +    return tonode(head), true  end  -- Ideally the next one should be merged with the previous but keeping it separate is @@ -460,7 +457,7 @@ end  -- Todo: make a better stacker. Keep track (in attribute) about nesting level. Not  -- entirely trivial and a generic solution is nicer (compares to the exporter). -local function stacked(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise +local function stacked(attribute,head,default) -- no triggering, no inheritance, but list-wise      local stack   = head      local done    = false      local current = default or 0 @@ -485,16 +482,16 @@ local function stacked(namespace,attribute,head,default) -- no triggering, no in                      if a and current ~= a and nslistwise[a] then -- viewerlayer / needs checking, see below                          local p = current                          current = a -                        head    = insert_node_before(head,stack,copied(nsdata[a])) -                        local list = stacked(namespace,attribute,content,current) -- two return values +                        head    = insert_node_before(head,stack,copy_node(nsdata[a])) +                        local list = stacked(attribute,content,current) -- two return values                          if content ~= list then                              setlist(stack,list)                          end -                        head, stack = insert_node_after(head,stack,copied(nsnone)) +                        head, stack = insert_node_after(head,stack,copy_node(nsnone))                          current = p                          done    = true                      else -                        local list, ok = stacked(namespace,attribute,content,current) +                        local list, ok = stacked(attribute,content,current)                          if content ~= list then                              setlist(stack,list) -- only if ok                          end @@ -503,7 +500,7 @@ local function stacked(namespace,attribute,head,default) -- no triggering, no in                          end                      end                  else -                    local list, ok = stacked(namespace,attribute,content,current) +                    local list, ok = stacked(attribute,content,current)                      if content ~= list then                          setlist(stack,list) -- only if ok                      end @@ -520,13 +517,13 @@ local function stacked(namespace,attribute,head,default) -- no triggering, no in              local a = getattr(stack,attribute)              if a then                  if current ~= a then -                    head    = insert_node_before(head,stack,copied(nsdata[a])) +                    head    = insert_node_before(head,stack,copy_node(nsdata[a]))                      depth   = depth + 1                      current = a                      done    = true                  end                  if leader then -                    local list, ok = stacked(namespace,attribute,content,current) +                    local list, ok = stacked(attribute,content,current)                      if leader ~= list then                          setleader(stack,list) -- only if ok                      end @@ -538,7 +535,7 @@ local function stacked(namespace,attribute,head,default) -- no triggering, no in              elseif default > 0 then                  --              elseif current > 0 then -                head    = insert_node_before(head,stack,copied(nsnone)) +                head    = insert_node_before(head,stack,copy_node(nsnone))                  depth   = depth - 1                  current = 0                  done    = true @@ -548,20 +545,20 @@ local function stacked(namespace,attribute,head,default) -- no triggering, no in          stack = getnext(stack)      end      while depth > 0 do -        head = insert_node_after(head,stack,copied(nsnone)) +        head = insert_node_after(head,stack,copy_node(nsnone))          depth = depth - 1      end      return head, done  end  states.stacked = function(namespace,attribute,head,default) -    local head, done = stacked(namespace,attribute,tonut(head),default) +    local head, done = stacked(attribute,tonut(head),default)      return tonode(head), done  end  -- experimental -local function stacker(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise +local function stacker(attribute,head,default) -- no triggering, no inheritance, but list-wise  --     nsbegin()      local stacked  = false @@ -589,15 +586,15 @@ local function stacker(namespace,attribute,head,default) -- no triggering, no in              elseif nslistwise then                  local a = getattr(current,attribute)                  if a and attrib ~= a and nslistwise[a] then -- viewerlayer -                    head = insert_node_before(head,current,copied(nsdata[a])) -                    local list = stacker(namespace,attribute,content,a) +                    head = insert_node_before(head,current,copy_node(nsdata[a])) +                    local list = stacker(attribute,content,a)                      if list ~= content then                          setlist(current,list)                      end                      done = true -                    head, current = insert_node_after(head,current,copied(nsnone)) +                    head, current = insert_node_after(head,current,copy_node(nsnone))                  else -                    local list, ok = stacker(namespace,attribute,content,attrib) +                    local list, ok = stacker(attribute,content,attrib)                      if content ~= list then                          setlist(current,list)                      end @@ -606,7 +603,7 @@ local function stacker(namespace,attribute,head,default) -- no triggering, no in                      end                  end              else -                local list, ok = stacker(namespace,attribute,content,default) +                local list, ok = stacker(attribute,content,default)                  if list ~= content then                      setlist(current,list)                  end @@ -633,7 +630,7 @@ local function stacker(namespace,attribute,head,default) -- no triggering, no in                  done   = true                  if leader then                      -- tricky as a leader has to be a list so we cannot inject before -                    local list, ok = stacker(namespace,attribute,leader,attrib) +                    local list, ok = stacker(attribute,leader,attrib)                      if ok then                          done = true                      end @@ -647,21 +644,19 @@ local function stacker(namespace,attribute,head,default) -- no triggering, no in          current = getnext(current)      end -if stacked then - -    local n = nsend() -    while n do -        head = insert_node_after(head,previous,tonut(n)) -        n = nsend() +    if stacked then +        local n = nsend() +        while n do +            head = insert_node_after(head,previous,tonut(n)) +            n = nsend() +        end      end -end -      return head, done  end  states.stacker = function(namespace,attribute,head,default) -    local head, done = stacker(namespace,attribute,tonut(head),default) +    local head, done = stacker(attribute,tonut(head),default)      nsreset()      return tonode(head), done  end diff --git a/tex/context/base/mkiv/node-fnt.lua b/tex/context/base/mkiv/node-fnt.lua index 0f89e581f..c0d7eecc8 100644 --- a/tex/context/base/mkiv/node-fnt.lua +++ b/tex/context/base/mkiv/node-fnt.lua @@ -56,6 +56,7 @@ local getfield          = nuts.getfield  ----- getdisc           = nuts.getdisc  local setchar           = nuts.setchar  local setlink           = nuts.setlink +local setnext           = nuts.setnext  local setfield          = nuts.setfield  local setprev           = nuts.setprev @@ -71,6 +72,9 @@ local disc_code         = nodecodes.disc  local boundary_code     = nodecodes.boundary  local word_boundary     = nodes.boundarycodes.word +local protect_glyphs    = nuts.protect_glyphs +local unprotect_glyphs  = nuts.unprotect_glyphs +  local setmetatableindex = table.setmetatableindex  -- some tests with using an array of dynamics[id] and processes[id] demonstrated @@ -212,8 +216,10 @@ function handlers.characters(head,groupcode,size,packtype,direction)      local done      = false      local variants  = nil      local redundant = nil -    local none      = false      local nuthead   = tonut(head) +    local lastfont  = nil +    local lastproc  = nil +    local lastnone  = nil      local a, u, b, r, e = 0, 0, 0, 0, 0 @@ -224,54 +230,91 @@ function handlers.characters(head,groupcode,size,packtype,direction)      -- There is no gain in checking for a single glyph and then having a fast path. On the      -- metafun manual (with some 2500 single char lists) the difference is just noise. +    local function protectnone() +        protect_glyphs(firstnone,lastnone) +        firstnone = nil +    end + +    local function setnone(n) +        if firstnone then +            protectnone() +        end +        if basefont then +            basefont[2] = getprev(n) +            basefont = false +        end +        if not firstnone then +            firstnone = n +        end +        lastnone = n +    end + +    local function setbase(n) +        if firstnone then +            protectnone() +        end +        if force_basepass then +            if basefont then +                basefont[2] = getprev(n) +            end +            b = b + 1 +            basefont = { n, false } +            basefonts[b] = basefont +        end +    end + +    local function setnode(n,font,attr) -- we could use prevfont and prevattr when we set then first +        if firstnone then +            protectnone() +        end +        if basefont then +            basefont[2] = getprev(n) +            basefont = false +        end +        if attr > 0 then +            local used = attrfonts[font] +            if not used then +                used = { } +                attrfonts[font] = used +            end +            if not used[attr] then +                local fd = setfontdynamics[font] +                if fd then +                    used[attr] = fd[attr] +                    a = a + 1 +                end +            end +        else +            local used = usedfonts[font] +            if not used then +                lastfont = font +                lastproc = fontprocesses[font] +                if lastproc then +                    usedfonts[font] = lastproc +                    u = u + 1 +                end +            end +        end +    end +      for n in traverse_char(nuthead) do          local font = getfont(n) -        local attr = (none and prevattr) or getattr(n,0) or 0 -- zero attribute is reserved for fonts in context +     -- local attr = (none and prevattr) or getattr(n,0) or 0 -- zero attribute is reserved for fonts in context +        local attr = getattr(n,0) or 0 -- zero attribute is reserved for fonts in context          if font ~= prevfont or attr ~= prevattr then              prevfont = font              prevattr = attr              variants = fontvariants[font] -            none     = fontmodes[font] == "none" -            if none then -                -- skip -             -- variants = false -                protect_glyph(n) +            local fontmode = fontmodes[font] +            if fontmode == "none" then +                setnone(n) +            elseif fontmode == "base" then +                setbase(n)              else -                if basefont then -                    basefont[2] = getprev(n) -                end -                if attr > 0 then -                    local used = attrfonts[font] -                    if not used then -                        used = { } -                        attrfonts[font] = used -                    end -                    if not used[attr] then -                        local fd = setfontdynamics[font] -                        if fd then -                            used[attr] = fd[attr] -                            a = a + 1 -                        elseif force_basepass then -                            b = b + 1 -                            basefont = { n, false } -                            basefonts[b] = basefont -                        end -                    end -                else -                    local used = usedfonts[font] -                    if not used then -                        local fp = fontprocesses[font] -                        if fp then -                            usedfonts[font] = fp -                            u = u + 1 -                        elseif force_basepass then -                            b = b + 1 -                            basefont = { n, false } -                            basefonts[b] = basefont -                        end -                    end -                end +                setnode(n,font,attr)              end +        elseif firstnone then +            lastnone = n          end          if variants then              local char = getchar(n) @@ -309,6 +352,10 @@ function handlers.characters(head,groupcode,size,packtype,direction)          end      end +    if firstnone then +        protectnone() +    end +      if force_boundaryrun then          -- we can inject wordboundaries and then let the hyphenator do its work @@ -376,43 +423,29 @@ function handlers.characters(head,groupcode,size,packtype,direction)                  local none     = false                  for n in traverse_char(r) do                      local font = getfont(n) -                    local attr = (none and prevattr) or getattr(n,0) or 0 -- zero attribute is reserved for fonts in context +                    local attr = getattr(n,0) or 0 -- zero attribute is reserved for fonts in context                      if font ~= prevfont or attr ~= prevattr then                          prevfont = font                          prevattr = attr -                        none     = fontmodes[font] == "none"  -- very unlikely that we run into disc nodes in none mode -                        if none then -                            -- skip -                         -- variants = false -                            protect_glyph(n) -                        elseif attr > 0 then -                            local used = attrfonts[font] -                            if not used then -                                used = { } -                                attrfonts[font] = used -                            end -                            if not used[attr] then -                                local fd = setfontdynamics[font] -                                if fd then -                                    used[attr] = fd[attr] -                                    a = a + 1 -                                end -                            end +                        local fontmode = fontmodes[font] +                        if fontmode == "none" then +                            setnone(n) +                        elseif fontmode == "base" then +                            setbase(n)                          else -                            local used = usedfonts[font] -                            if not used then -                                local fp = fontprocesses[font] -                                if fp then -                                    usedfonts[font] = fp -                                    u = u + 1 -                                end -                            end +                            setnode(n,font,attr)                          end +                    elseif firstnone then +                     -- lastnone = n +                        lastnone = nil                      end                      -- we assume one font for now (and if there are more and we get into issues then                      -- we can always remove the break)                      break                  end +                if firstnone then +                    protectnone() +                end              elseif expanders then                  local subtype = getsubtype(d)                  if subtype == automatic_code or subtype == explicit_code then @@ -432,11 +465,9 @@ function handlers.characters(head,groupcode,size,packtype,direction)      if u == 0 then          -- skip      elseif u == 1 then -        local font, processors = next(usedfonts) -     -- local attr = a == 0 and false or 0 -- 0 is the savest way          local attr = a > 0 and 0 or false -- 0 is the savest way -        for i=1,#processors do -            local h, d = processors[i](head,font,attr,direction) +        for i=1,#lastproc do +            local h, d = lastproc[i](head,lastfont,attr,direction)              if d then                  if h then                      head = h @@ -549,19 +580,5 @@ function handlers.characters(head,groupcode,size,packtype,direction)      return head, true  end -local d_protect_glyphs   = nuts.protect_glyphs -local d_unprotect_glyphs = nuts.unprotect_glyphs - -handlers.protectglyphs   = function(n) return d_protect_glyphs  (tonut(n)) end -handlers.unprotectglyphs = function(n) return d_unprotect_glyphs(tonut(n)) end - --- function handlers.protectglyphs(h) ---     local h = tonut(h) ---     for n in traverse_id(disc_code,h) do ---         local pre, post, replace = getdisc(n) ---         if pre     then d_protect_glyphs(pre)     end ---         if post    then d_protect_glyphs(post)    end ---         if replace then d_protect_glyphs(replace) end ---     end ---     return d_protect_glyphs(h) --- end +handlers.protectglyphs   = function(n) protect_glyphs  (tonut(n)) return n, true end +handlers.unprotectglyphs = function(n) unprotect_glyphs(tonut(n)) return n, true end diff --git a/tex/context/base/mkiv/node-ini.mkiv b/tex/context/base/mkiv/node-ini.mkiv index 369b06ab2..0f5a2df7e 100644 --- a/tex/context/base/mkiv/node-ini.mkiv +++ b/tex/context/base/mkiv/node-ini.mkiv @@ -36,6 +36,7 @@  %registerctxluafile{node-prp}{1.001} % makes no sense (yet)  \registerctxluafile{node-ppt}{1.001}  \registerctxluafile{node-scn}{1.001} +\registerctxluafile{node-syn}{1.001}  \newcount\c_node_tracers_show_box % box number diff --git a/tex/context/base/mkiv/node-met.lua b/tex/context/base/mkiv/node-met.lua index e6d0f4689..2686aa990 100644 --- a/tex/context/base/mkiv/node-met.lua +++ b/tex/context/base/mkiv/node-met.lua @@ -110,8 +110,9 @@ nodes.set_attribute         = node.set_attribute  nodes.find_attribute        = node.find_attribute  nodes.unset_attribute       = node.unset_attribute -nodes.protect_glyphs        = node.protect_glyphs  nodes.protect_glyph         = node.protect_glyph +nodes.protect_glyphs        = node.protect_glyphs +nodes.unprotect_glyph       = node.unprotect_glyph  nodes.unprotect_glyphs      = node.unprotect_glyphs  nodes.kerning               = node.kerning  nodes.ligaturing            = node.ligaturing @@ -669,3 +670,46 @@ end  nodes.keys   = keys       -- [id][subtype]  nodes.fields = nodefields -- (n) + +-- for the moment (pre 6380) + +if not nodes.unprotect_glyph then + +    local protect_glyph    = nodes.protect_glyph +    local protect_glyphs   = nodes.protect_glyphs +    local unprotect_glyph  = nodes.unprotect_glyph +    local unprotect_glyphs = nodes.unprotect_glyphs + +    local getnext          = nodes.getnext +    local setnext          = nodes.setnext + +    function nodes.protectglyphs(first,last) +        if first == last then +            return protect_glyph(first) +        elseif last then +            local nxt = getnext(last) +            setnext(last) +            local f, b = protect_glyphs(first) +            setnext(last,nxt) +            return f, b +        else +            return protect_glyphs(first) +        end +    end + +    function nodes.unprotectglyphs(first,last) +        if first == last then +            return unprotect_glyph(first) +        elseif last then +            local nxt = getnext(last) +            setnext(last) +            local f, b = unprotect_glyphs(first) +            setnext(last,nxt) +            return f, b +        else +            return unprotect_glyphs(first) +        end +    end + +end + diff --git a/tex/context/base/mkiv/node-nut.lua b/tex/context/base/mkiv/node-nut.lua index 787afd888..87df5d5b2 100644 --- a/tex/context/base/mkiv/node-nut.lua +++ b/tex/context/base/mkiv/node-nut.lua @@ -283,8 +283,9 @@ nuts.has_attribute         = direct.has_attribute  nuts.set_attribute         = direct.set_attribute  nuts.unset_attribute       = direct.unset_attribute -nuts.protect_glyphs        = direct.protect_glyphs  nuts.protect_glyph         = direct.protect_glyph +nuts.protect_glyphs        = direct.protect_glyphs +nuts.unprotect_glyph       = direct.unprotect_glyph  nuts.unprotect_glyphs      = direct.unprotect_glyphs  nuts.ligaturing            = direct.ligaturing  nuts.kerning               = direct.kerning @@ -409,18 +410,32 @@ local d_setlink            = direct.setlink  local d_setboth            = direct.setboth  local d_getboth            = direct.getboth +-- local function remove(head,current,free_too) +--     local t = current +--     head, current = d_remove_node(head,current) +--     if not t then +--         -- forget about it +--     elseif free_too then +--         d_flush_node(t) +--         t = nil +--     else +--         d_setboth(t) -- (t,nil,nil) +--     end +--     return head, current, t +-- end +  local function remove(head,current,free_too) -    local t = current -    head, current = d_remove_node(head,current) -    if not t then -        -- forget about it -    elseif free_too then -        d_flush_node(t) -        t = nil -    else -        d_setboth(t) -- (t,nil,nil) +    if current then +        local h, c = d_remove_node(head,current) +        if free_too then +            d_flush_node(current) +            return h, c +        else +            d_setboth(current) +            return h, c, current +        end      end -    return head, current, t +    return head, current  end  -- alias @@ -902,3 +917,45 @@ if not nuts.uses_font then  end +-- for the moment (pre 6380) + +if not nuts.unprotect_glyph then + +    local protect_glyph    = nuts.protect_glyph +    local protect_glyphs   = nuts.protect_glyphs +    local unprotect_glyph  = nuts.unprotect_glyph +    local unprotect_glyphs = nuts.unprotect_glyphs + +    local getnext          = nuts.getnext +    local setnext          = nuts.setnext + +    function nuts.protectglyphs(first,last) +        if first == last then +            return protect_glyph(first) +        elseif last then +            local nxt = getnext(last) +            setnext(last) +            local f, b = protect_glyphs(first) +            setnext(last,nxt) +            return f, b +        else +            return protect_glyphs(first) +        end +    end + +    function nuts.unprotectglyphs(first,last) +        if first == last then +            return unprotect_glyph(first) +        elseif last then +            local nxt = getnext(last) +            setnext(last) +            local f, b = unprotect_glyphs(first) +            setnext(last,nxt) +            return f, b +        else +            return unprotect_glyphs(first) +        end +    end + +end + diff --git a/tex/context/base/mkiv/node-ref.lua b/tex/context/base/mkiv/node-ref.lua index b313a00b6..f2f660e67 100644 --- a/tex/context/base/mkiv/node-ref.lua +++ b/tex/context/base/mkiv/node-ref.lua @@ -371,61 +371,30 @@ end  -- we need to do vlists differently  local function inject_areas(head,attribute,make,stack,done,skip,parent,pardir,txtdir)  -- main -    if head then -        local current, first, last, firstdir, reference = head, nil, nil, nil, nil -        pardir = pardir or "===" -        txtdir = txtdir or "===" -        while current do -            local id = getid(current) -            if id == hlist_code or id == vlist_code then -                local r = getattr(current,attribute) -                -- test \goto{test}[page(2)] test \gotobox{test}[page(2)] -                -- test \goto{\TeX}[page(2)] test \gotobox{\hbox {x} \hbox {x}}[page(2)] -             -- if r and (not skip or r >) skip then -- maybe no > test -             --     inject_list(id,current,r,make,stack,pardir,txtdir) -             -- end -                if r then -                    if not reference then -                        reference, first, last, firstdir = r, current, current, txtdir -                    elseif r == reference then -                        -- same link -                        last = current -                    elseif (done[reference] or 0) == 0 then -                        if not skip or r > skip then -- maybe no > test -                            head, current = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir) -                            reference, first, last, firstdir = nil, nil, nil, nil -                        end -                    else -                        reference, first, last, firstdir = r, current, current, txtdir -                    end -                    done[r] = (done[r] or 0) + 1 -                end -                local list = getlist(current) -                if list then -                    local h, ok -                    h, ok , pardir, txtdir = inject_areas(list,attribute,make,stack,done,r or skip or 0,current,pardir,txtdir) -                    if h ~= current then -                        setlist(current,h) -                    end -                end -                if r then -                    done[r] = done[r] - 1 -                end -            elseif id == dir_code then -                txtdir = getdir(current) -            elseif id == localpar_code then -                pardir = getdir(current) -            elseif id == glue_code and getsubtype(current) == leftskip_code then -- any glue at the left? -                -- -            else -                local r = getattr(current,attribute) -                if not r then -                    -- just go on, can be kerns -                elseif not reference then +    local first, last, firstdir, reference +    if not pardir then +        pardir = "===" +    end +    if not texdir then +        txtdir = "===" +    end +    local current = head +    while current do +        local id = getid(current) +        if id == hlist_code or id == vlist_code then +            local r = getattr(current,attribute) +            -- test \goto{test}[page(2)] test \gotobox{test}[page(2)] +            -- test \goto{\TeX}[page(2)] test \gotobox{\hbox {x} \hbox {x}}[page(2)] +         -- if r and (not skip or r >) skip then -- maybe no > test +         --     inject_list(id,current,r,make,stack,pardir,txtdir) +         -- end +            if r then +                if not reference then                      reference, first, last, firstdir = r, current, current, txtdir                  elseif r == reference then +                    -- same link                      last = current -                elseif (done[reference] or 0) == 0 then -- or id == glue_code and getsubtype(current) == right_skip_code +                elseif (done[reference] or 0) == 0 then                      if not skip or r > skip then -- maybe no > test                          head, current = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir)                          reference, first, last, firstdir = nil, nil, nil, nil @@ -433,53 +402,89 @@ local function inject_areas(head,attribute,make,stack,done,skip,parent,pardir,tx                  else                      reference, first, last, firstdir = r, current, current, txtdir                  end +                done[r] = (done[r] or 0) + 1              end -            current = getnext(current) -        end -        if reference and (done[reference] or 0) == 0 then -            head = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir) -        end -    end -    return head, true, pardir, txtdir -end - -local function inject_area(head,attribute,make,stack,done,parent,pardir,txtdir) -- singular  ! -    if head then -        pardir = pardir or "===" -        txtdir = txtdir or "===" -        local current = head -        while current do -            local id = getid(current) -            if id == hlist_code or id == vlist_code then -                local r = getattr(current,attribute) -                if r and not done[r] then -                    done[r] = true -                    inject_list(id,current,r,make,stack,pardir,txtdir) +            local list = getlist(current) +            if list then +                local h, ok +                h, ok, pardir, txtdir = inject_areas(list,attribute,make,stack,done,r or skip or 0,current,pardir,txtdir) +                if h ~= current then +                    setlist(current,h)                  end -                local list = getlist(current) -                if list then -                    local h = inject_area(list,attribute,make,stack,done,current,pardir,txtdir) -                    if h ~= current then -                        setlist(current,h) -                    end +            end +            if r then +                done[r] = done[r] - 1 +            end +        elseif id == glue_code and getsubtype(current) == leftskip_code then -- any glue at the left? +            -- +        elseif id == dir_code then +            txtdir = getdir(current) +        elseif id == localpar_code then -- only test at begin +            pardir = getdir(current) +        else +            local r = getattr(current,attribute) +            if not r then +                -- just go on, can be kerns +            elseif not reference then +                reference, first, last, firstdir = r, current, current, txtdir +            elseif r == reference then +                last = current +            elseif (done[reference] or 0) == 0 then -- or id == glue_code and getsubtype(current) == right_skip_code +                if not skip or r > skip then -- maybe no > test +                    head, current = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir) +                    reference, first, last, firstdir = nil, nil, nil, nil                  end -            elseif id == dir_code then -                txtdir = getdir(current) -            elseif id == localpar_code then -                pardir = getdir(current)              else -                local r = getattr(current,attribute) -                if r and not done[r] then -                    done[r] = true -                    head, current = inject_range(head,current,current,r,make,stack,parent,pardir,txtdir) -                end +                reference, first, last, firstdir = r, current, current, txtdir              end -            current = getnext(current)          end +        current = getnext(current)      end -    return head, true +    if reference and (done[reference] or 0) == 0 then +        head = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir) +    end +    return head, true, pardir, txtdir  end +-- local function inject_area(head,attribute,make,stack,done,parent,pardir,txtdir) -- singular  ! +--     if not pardir then +--         pardir = "===" +--     end +--     if not texdir then +--         txtdir = "===" +--     end +--     local current = head +--     while current do +--         local id = getid(current) +--         if id == hlist_code or id == vlist_code then +--             local r = getattr(current,attribute) +--             if r and not done[r] then +--                 done[r] = true +--                 inject_list(id,current,r,make,stack,pardir,txtdir) +--             end +--             local list = getlist(current) +--             if list then +--                 local h = inject_area(list,attribute,make,stack,done,current,pardir,txtdir) +--                 if h ~= current then +--                     setlist(current,h) +--                 end +--             end +--         elseif id == dir_code then +--             txtdir = getdir(current) +--         elseif id == localpar_code then +--             pardir = getdir(current) +--         else +--             local r = getattr(current,attribute) +--             if r and not done[r] then +--                 done[r] = true +--                 head, current = inject_range(head,current,current,r,make,stack,parent,pardir,txtdir) +--             end +--         end +--         current = getnext(current) +--     end +--     return head, true +-- end +  -- tracing: todo: use predefined colors  local register_color = colors.register @@ -669,17 +674,9 @@ local function makereference(width,height,depth,reference) -- height and depth a      end  end --- function nodes.references.handler(head) ---     if topofstack > 0 then ---         return inject_areas(head,attribute,makereference,stack,done) ---     else ---         return head, false ---     end --- end -  function nodes.references.handler(head) -    if topofstack > 0 then -        head = tonut(head) +    if head and topofstack > 0 then +        local head = tonut(head)          local head, done = inject_areas(head,attribute,makereference,stack,done)          return tonode(head), done      else @@ -791,7 +788,7 @@ local function makedestination(width,height,depth,reference)  end  -- function nodes.destinations.handler(head) ---     if topofstack > 0 then +--     if head and topofstack > 0 then  --         return inject_area(head,attribute,makedestination,stack,done) -- singular  --     else  --         return head, false @@ -799,8 +796,8 @@ end  -- end  function nodes.destinations.handler(head) -    if topofstack > 0 then -        head = tonut(head) +    if head and topofstack > 0 then +        local head = tonut(head)          local head, done = inject_areas(head,attribute,makedestination,stack,done)          return tonode(head), done      else @@ -808,7 +805,6 @@ function nodes.destinations.handler(head)      end  end -  -- will move  function references.mark(reference,h,d,view) diff --git a/tex/context/base/mkiv/node-syn.lua b/tex/context/base/mkiv/node-syn.lua index bd8cc7964..1c070c45f 100644 --- a/tex/context/base/mkiv/node-syn.lua +++ b/tex/context/base/mkiv/node-syn.lua @@ -69,6 +69,7 @@ local kerncodes          = nodes.kerncodes  local glyph_code         = nodecodes.glyph  local disc_code          = nodecodes.disc  local glue_code          = nodecodes.glue +local penalty_code       = nodecodes.penalty  local kern_code          = nodecodes.kern  ----- rule_code          = nodecodes.rule  local hlist_code         = nodecodes.hlist @@ -81,7 +82,7 @@ local insert_after       = nuts.insert_after  local nodepool           = nuts.pool  local new_latelua        = nodepool.latelua  local new_rule           = nodepool.rule -local new_hlist          = nodepool.hlist +local new_kern           = nodepool.kern  local getdimensions      = nuts.dimensions  local getrangedimensions = nuts.rangedimensions @@ -321,7 +322,8 @@ local function inject(head,first,last,tag,line)          d = depth      end      if trace then -        head = insert_before(head,first,new_hlist(new_rule(w,fulltrace and h or traceheight,fulltrace and d or tracedepth))) +        head = insert_before(head,first,new_rule(w,fulltrace and h or traceheight,fulltrace and d or tracedepth)) +        head = insert_before(head,first,new_kern(-w))      end      head = x_hlist(head,first,tag,line,w,h,d)      return head @@ -389,7 +391,8 @@ local function inject(parent,head,first,last,tag,line)          d = depth      end      if trace then -        head = insert_before(head,first,new_hlist(new_rule(w,fulltrace and h or traceheight,fulltrace and d or tracedepth))) +        head = insert_before(head,first,new_rule(w,fulltrace and h or traceheight,fulltrace and d or tracedepth)) +        head = insert_before(head,first,new_kern(-w))      end      head = x_hlist(head,first,tag,line,w,h,d)      return head @@ -451,6 +454,8 @@ local function collect_max(head,parent)                          break                      end                      id = nil -- so no test later on +                elseif id == penalty_code then +                    -- go on (and be nice for math)                  else                      if tag > 0 then                          head = inject(parent,head,first,last,tag,line) @@ -516,16 +521,28 @@ function synctex.stop()      end  end +local enablers  = { } +local disablers = { } + +function synctex.registerenabler(f) +    enablers[#enablers+1] = f +end +function synctex.registerdisabler(f) +    disablers[#disablers+1] = f +end +  function synctex.enable()      if not enabled then          enabled = true          set_synctex_mode(3) -- we want details          if not used then -            directives.enable("system.synctex.xml")              nodes.tasks.appendaction("shipouts", "after", "luatex.synctex.collect")              report_system("synctex functionality is enabled, expect 5-10 pct runtime overhead!")              used = true          end +        for i=1,#enablers do +            enablers[i](true) +        end      end  end @@ -534,6 +551,9 @@ function synctex.disable()          set_synctex_mode(0)          report_system("synctex functionality is disabled!")          enabled = false +        for i=1,#disablers do +            disablers[i](false) +        end      end  end @@ -563,14 +583,6 @@ end  luatex.registerstopactions(synctex.finish) -directives.register("system.synctex", function(v) -    if v then -        synctex.enable() -    else -        synctex.disable() -    end -end) -  statistics.register("synctex tracing",function()      if used then          return string.format("%i referenced files, %i files ignored, %i objects flushed, logfile: %s", @@ -595,26 +607,28 @@ interfaces.implement {      actions   = synctex.resetfilename,  } +function synctex.setup(t) +    if t.method == interfaces.variables.max then +        collect = collect_max +    else +        collect = collect_min +    end +    if t.state == interfaces.variables.start then +        synctex.enable() +    else +        synctex.disable() +    end +end +  interfaces.implement {      name      = "setupsynctex", +    actions   = synctex.setup,      arguments = {          {              { "state" },              { "method" },          },      }, -    actions   = function(t) -        if t.method == interfaces.variables.max then -            collect = collect_max -        else -            collect = collect_min -        end -        if t.state == interfaces.variables.start then -            synctex.enable() -        else -            synctex.disable() -        end -    end  }  interfaces.implement { diff --git a/tex/context/base/mkiv/spac-chr.lua b/tex/context/base/mkiv/spac-chr.lua index fe402ed87..b8a53d2a1 100644 --- a/tex/context/base/mkiv/spac-chr.lua +++ b/tex/context/base/mkiv/spac-chr.lua @@ -48,6 +48,7 @@ local insert_node_before = nuts.insert_before  local insert_node_after  = nuts.insert_after  local remove_node        = nuts.remove  local traverse_id        = nuts.traverse_id +local traverse_char      = nuts.traverse_char  local tasks              = nodes.tasks @@ -148,16 +149,37 @@ function characters.replacenbsp(head,original)      return head, current  end +-- function characters.replacenbspaces(head) +--     for current in traverse_id(glyph_code,head) do +--         if getchar(current) == 0x00A0 then +--             local h = nbsp(head,current) +--             if h then +--                 head = remove_node(h,current,true) +--             end +--         end +--     end +--     return head +-- end +  function characters.replacenbspaces(head) -    for current in traverse_id(glyph_code,head) do +    local head = tonut(head) +    local wipe = false +    for current in traverse_id(glyph_code,head) do -- can be anytiem so no traverse_char          if getchar(current) == 0x00A0 then +            if wipe then +                head = remove_node(h,current,true) +                wipe = false +            end              local h = nbsp(head,current)              if h then -                head = remove_node(h,current,true) +                wipe = current              end          end      end -    return head +    if wipe then +        head = remove_node(h,current,true) +    end +    return tonode(head)  end  -- This initialization might move someplace else if we need more of it. The problem is that @@ -283,29 +305,60 @@ local methods = {  characters.methods = methods -function characters.handler(head) -- todo: use traverse_id -    head = tonut(head) -    local current = head -    local done = false -    while current do -        local char, id = isglyph(current) +-- function characters.handler(head) -- todo: use traverse_id +--     head = tonut(head) +--     local current = head +--     local done = false +--     while current do +--         local char, id = isglyph(current) +--         if char then +--             local next   = getnext(current) +--             local method = methods[char] +--             if method then +--                 if trace_characters then +--                     report_characters("replacing character %C, description %a",char,lower(chardata[char].description)) +--                 end +--                 local h = method(head,current) +--                 if h then +--                     head = remove_node(h,current,true) +--                 end +--                 done = true +--             end +--             current = next +--         else +--             current = getnext(current) +--         end +--     end +--     return tonode(head), done +-- end + +-- for current, char, font in traverse_char_data(head) will save 0.015 on a 300 page doc + +function characters.handler(head) +    local head = tonut(head) +    local wipe = false +    for current in traverse_char(head) do +        local char = getchar(current)          if char then -            local next   = getnext(current)              local method = methods[char]              if method then +                if wipe then +                    head = remove_node(head,wipe,true) +                    wipe = false +                end                  if trace_characters then                      report_characters("replacing character %C, description %a",char,lower(chardata[char].description))                  end                  local h = method(head,current)                  if h then -                    head = remove_node(h,current,true) +                    wipe = current                  end                  done = true              end -            current = next -        else -            current = getnext(current)          end      end +    if wipe then +        head = remove_node(head,wipe,true) +    end      return tonode(head), done  end diff --git a/tex/context/base/mkiv/status-files.pdf b/tex/context/base/mkiv/status-files.pdf Binary files differindex 72baf08cc..83c6b914d 100644 --- a/tex/context/base/mkiv/status-files.pdf +++ b/tex/context/base/mkiv/status-files.pdf diff --git a/tex/context/base/mkiv/status-lua.pdf b/tex/context/base/mkiv/status-lua.pdf Binary files differindex b33293a23..9950ca3a6 100644 --- a/tex/context/base/mkiv/status-lua.pdf +++ b/tex/context/base/mkiv/status-lua.pdf diff --git a/tex/context/base/mkiv/strc-con.mkvi b/tex/context/base/mkiv/strc-con.mkvi index 18ce17355..522026556 100644 --- a/tex/context/base/mkiv/strc-con.mkvi +++ b/tex/context/base/mkiv/strc-con.mkvi @@ -73,6 +73,8 @@      \c!display=\v!yes,      \c!width=7\emwidth,      \c!distance=\emwidth, +    \c!stretch=.50, +    \c!shrink=.25,      \c!titledistance=.5\emwidth,      %c!hang=,      %c!sample=, @@ -410,8 +412,12 @@  \installcommandhandler \??constructionalternative {constructionalternative} \??constructionalternative  \setupconstructionalternative -  [\c!width=\constructionparameter\c!width, -   \c!distance=\constructionparameter\c!distance] +  [%c!left=, % no inheritance +   %c!right=,% no inheritance +   \c!width=\constructionparameter\c!width, +   \c!distance=\constructionparameter\c!distance, +   \c!stretch=\constructionparameter\c!stretch, +   \c!shrink=\constructionparameter\c!shrink]  \defineconstructionalternative    [\v!left] @@ -463,7 +469,7 @@  \defineconstructionalternative    [\v!hanging] -  [%\c!width=\v!fit, +  [%\c!width=.75\emwidth     \c!renderingsetup=\??constructionrenderings:\v!hanging]  \defineconstructionalternative @@ -778,7 +784,10 @@      \flushconstructionheadbox      \ifconditional\c_strc_constructions_distance_none \else          \nobreak -        \hskip\constructionsheaddistance \s!plus .5\constructionsheaddistance \s!minus .25\constructionsheaddistance\relax +        \hskip\constructionsheaddistance +          \s!plus \constructionalternativeparameter\c!stretch\constructionsheaddistance +          \s!minus\constructionalternativeparameter\c!shrink \constructionsheaddistance +        \relax      \fi      \useconstructionstyleandcolor\c!style\c!color      \ignorespaces @@ -822,9 +831,9 @@      \ifconditional\c_strc_constructions_distance_none \else        % we used to have just a kern, but long lines look bad then        \scratchdistance\ifdim\constructionsheaddistance=\zeropoint .75\emwidth\else\constructionsheaddistance\fi -      \hskip     \scratchdistance -      \s!plus .25\scratchdistance -      \s!minus.25\scratchdistance +      \hskip \scratchdistance +        \s!plus \constructionalternativeparameter\c!stretch\scratchdistance +        \s!minus\constructionalternativeparameter\c!shrink \scratchdistance      \fi      \ifhmode          \ifx\p_strc_constructions_width\v!line diff --git a/tex/context/base/mkiv/supp-box.lua b/tex/context/base/mkiv/supp-box.lua index b9bf0ccf0..fc206b6d2 100644 --- a/tex/context/base/mkiv/supp-box.lua +++ b/tex/context/base/mkiv/supp-box.lua @@ -350,6 +350,7 @@ implement {          local result = new_hlist()          setlist(result,head)          setbox(target,result) +     -- setbox(target,new_hlist(head))      end  } diff --git a/tex/context/base/mkiv/trac-vis.lua b/tex/context/base/mkiv/trac-vis.lua index d58114d71..38bad03e2 100644 --- a/tex/context/base/mkiv/trac-vis.lua +++ b/tex/context/base/mkiv/trac-vis.lua @@ -6,11 +6,11 @@ if not modules then modules = { } end modules ['trac-vis'] = {      license   = "see context related readme files"  } -local string, number, table = string, number, table  local node, nodes, attributes, fonts, tex = node, nodes, attributes, fonts, tex  local type = type  local gmatch = string.gmatch  local formatters = string.formatters +local compactfloat = number.compactfloat  -- This module started out in the early days of mkiv and luatex with  -- visualizing kerns related to fonts. In the process of cleaning up the @@ -166,6 +166,7 @@ local trace_user  local trace_math  local trace_italic  local trace_discretionary +local trace_expansion  local report_visualize = logs.reporter("visualize") @@ -190,10 +191,11 @@ local modes = {      italic        =  8192,      origin        = 16384,      discretionary = 32768, +    expansion     = 65536,  }  local usedfont, exheight, emwidth -local l_penalty, l_glue, l_kern, l_fontkern, l_hbox, l_vbox, l_vtop, l_strut, l_whatsit, l_glyph, l_user, l_math, l_italic, l_origin, l_discretionary +local l_penalty, l_glue, l_kern, l_fontkern, l_hbox, l_vbox, l_vtop, l_strut, l_whatsit, l_glyph, l_user, l_math, l_italic, l_origin, l_discretionary, l_expansion  local enabled = false  local layers  = { } @@ -243,6 +245,7 @@ local function enable()      l_italic        = layers.italic      l_origin        = layers.origin      l_discretionary = layers.discretionary +    l_expansion     = layers.expansion      enableaction("shipouts","nodes.visualizers.handler")      report_visualize("enabled")      enabled = true @@ -355,6 +358,7 @@ local c_white           = "trace:w"  local c_math            = "trace:r"  local c_origin          = "trace:o"  local c_discretionary   = "trace:o" +local c_expansion       = "trace:o"  local c_positive_d      = "trace:db"  local c_negative_d      = "trace:dr" @@ -369,6 +373,7 @@ local c_white_d         = "trace:dw"  local c_math_d          = "trace:dr"  local c_origin_d        = "trace:do"  local c_discretionary_d = "trace:do" +local c_expansion_d     = "trace:do"  local function sometext(str,layer,color,textcolor,lap) -- we can just paste verbatim together .. no typesteting needed      local text = hpack_string(str,usedfont) @@ -435,6 +440,78 @@ local fontkern do  end +local glyphexpansion do + +    local f_cache = caches["glyphexpansion"] + +    glyphexpansion = function(head,current) +        local extra = getfield(current,"expansion_factor") +        if extra ~= 0 then +            extra = extra / 1000 +            local info = f_cache[extra] +            if info then +                -- print("hit fontkern") +            else +                local text = hpack_string(compactfloat(extra,"%.1f"),usedfont) +                local rule = new_rule(emwidth/fraction,exheight,2*exheight) +                local list = getlist(text) +                if extra > 0 then +                    setlistcolor(list,c_positive_d) +                elseif extra < 0 then +                    setlistcolor(list,c_negative_d) +                end +                setlisttransparency(list,c_text_d) +                setcolor(rule,c_text_d) +                settransparency(rule,c_text_d) +                setshift(text,1.5 * exheight) +                info = new_hlist(setlink(rule,text)) +                setattr(info,a_layer,l_expansion) +                f_cache[extra] = info +            end +            head = insert_node_before(head,current,copy_list(info)) +            return head, current +        end +        return head, current +    end + +end + +local kernexpansion do + +    local f_cache = caches["kernexpansion"] + +    kernexpansion = function(head,current) +        local extra = getfield(current,"expansion_factor") +        if extra ~= 0 then +            extra = extra / 1000 +            local info = f_cache[extra] +            if info then +                -- print("hit fontkern") +            else +                local text = hpack_string(compactfloat(extra,"%.1f"),usedfont) +                local rule = new_rule(emwidth/fraction,exheight,4*exheight) +                local list = getlist(text) +                if extra > 0 then +                    setlistcolor(list,c_positive_d) +                elseif extra < 0 then +                    setlistcolor(list,c_negative_d) +                end +                setlisttransparency(list,c_text_d) +                setcolor(rule,c_text_d) +                settransparency(rule,c_text_d) +                setshift(text,3.5 * exheight) +                info = new_hlist(setlink(rule,text)) +                setattr(info,a_layer,l_expansion) +                f_cache[extra] = info +            end +            head = insert_node_before(head,current,copy_list(info)) +            return head, current +        end +        return head, current +    end + +end +  local whatsit do      local w_cache = caches["whatsit"] @@ -929,151 +1006,161 @@ local ruledpenalty do  end -local function visualize(head,vertical,forced,parent) -    local trace_hbox     = false -    local trace_vbox     = false -    local trace_vtop     = false -    local trace_kern     = false -    local trace_glue     = false -    local trace_penalty  = false -    local trace_fontkern = false -    local trace_strut    = false -    local trace_whatsit  = false -    local trace_glyph    = false -    local trace_simple   = false -    local trace_user     = false -    local trace_math     = false -    local trace_italic   = false -    local trace_origin   = false -    local current        = head -    local previous       = nil -    local attr           = unsetvalue -    local prev_trace_fontkern = nil -    while current do -        local id = getid(current) -        local a = forced or getattr(current,a_visual) or unsetvalue -        if a ~= attr then -            prev_trace_fontkern = trace_fontkern -            if a == unsetvalue then -                trace_hbox          = false -                trace_vbox          = false -                trace_vtop          = false -                trace_kern          = false -                trace_glue          = false -                trace_penalty       = false -                trace_fontkern      = false -                trace_strut         = false -                trace_whatsit       = false -                trace_glyph         = false -                trace_simple        = false -                trace_user          = false -                trace_math          = false -                trace_italic        = false -                trace_origin        = false -                trace_discretionary = false -            else -- dead slow: -                trace_hbox          = hasbit(a,    1) -                trace_vbox          = hasbit(a,    2) -                trace_vtop          = hasbit(a,    4) -                trace_kern          = hasbit(a,    8) -                trace_glue          = hasbit(a,   16) -                trace_penalty       = hasbit(a,   32) -                trace_fontkern      = hasbit(a,   64) -                trace_strut         = hasbit(a,  128) -                trace_whatsit       = hasbit(a,  256) -                trace_glyph         = hasbit(a,  512) -                trace_simple        = hasbit(a, 1024) -                trace_user          = hasbit(a, 2048) -                trace_math          = hasbit(a, 4096) -                trace_italic        = hasbit(a, 8192) -                trace_origin        = hasbit(a,16384) -                trace_discretionary = hasbit(a,32768) -            end -            attr = a -        end -        if trace_strut then -            setattr(current,a_layer,l_strut) -        elseif id == glyph_code then -            if trace_glyph then -                head, current = ruledglyph(head,current,previous) -            end -        elseif id == disc_code then -            if trace_discretionary then -                head, current = ruleddiscretionary(head,current) -            end -            local pre, post, replace = getdisc(current) -            if pre then -                pre = visualize(pre,false,a,parent) -            end -            if post then -                post = visualize(post,false,a,parent) -            end -            if replace then -                replace = visualize(replace,false,a,parent) +do + +    local function visualize(head,vertical,forced,parent) +        local trace_hbox     = false +        local trace_vbox     = false +        local trace_vtop     = false +        local trace_kern     = false +        local trace_glue     = false +        local trace_penalty  = false +        local trace_fontkern = false +        local trace_strut    = false +        local trace_whatsit  = false +        local trace_glyph    = false +        local trace_simple   = false +        local trace_user     = false +        local trace_math     = false +        local trace_italic   = false +        local trace_origin   = false +        local current        = head +        local previous       = nil +        local attr           = unsetvalue +        local prev_trace_fontkern  = nil +        local prev_trace_expansion = nil +        while current do +            local id = getid(current) +            local a = forced or getattr(current,a_visual) or unsetvalue +            if a ~= attr then +                prev_trace_fontkern  = trace_fontkern +                prev_trace_expansion = trace_expansion +                if a == unsetvalue then +                    trace_hbox          = false +                    trace_vbox          = false +                    trace_vtop          = false +                    trace_kern          = false +                    trace_glue          = false +                    trace_penalty       = false +                    trace_fontkern      = false +                    trace_strut         = false +                    trace_whatsit       = false +                    trace_glyph         = false +                    trace_simple        = false +                    trace_user          = false +                    trace_math          = false +                    trace_italic        = false +                    trace_origin        = false +                    trace_discretionary = false +                    trace_expansion     = false +                else -- dead slow: +                    trace_hbox          = hasbit(a,    1) +                    trace_vbox          = hasbit(a,    2) +                    trace_vtop          = hasbit(a,    4) +                    trace_kern          = hasbit(a,    8) +                    trace_glue          = hasbit(a,   16) +                    trace_penalty       = hasbit(a,   32) +                    trace_fontkern      = hasbit(a,   64) +                    trace_strut         = hasbit(a,  128) +                    trace_whatsit       = hasbit(a,  256) +                    trace_glyph         = hasbit(a,  512) +                    trace_simple        = hasbit(a, 1024) +                    trace_user          = hasbit(a, 2048) +                    trace_math          = hasbit(a, 4096) +                    trace_italic        = hasbit(a, 8192) +                    trace_origin        = hasbit(a,16384) +                    trace_discretionary = hasbit(a,32768) +                    trace_expansion     = hasbit(a,65536) +                end +                attr = a              end -            setdisc(current,pre,post,replace) -        elseif id == kern_code then -            local subtype = getsubtype(current) -            if subtype == font_kern_code then -                if trace_fontkern or prev_trace_fontkern then -                    head, current = fontkern(head,current) +            if trace_strut then +                setattr(current,a_layer,l_strut) +            elseif id == glyph_code then +                if trace_glyph then +                    head, current = ruledglyph(head,current,previous)                  end -            else -- if subtype == user_kern_code then -                if trace_italic then -                    head, current = ruleditalic(head,current) -                elseif trace_kern then -                    head, current = ruledkern(head,current,vertical) +                if trace_expansion then +                    head, current = glyphexpansion(head,current) +                end +            elseif id == disc_code then +                if trace_discretionary then +                    head, current = ruleddiscretionary(head,current) +                end +                local pre, post, replace = getdisc(current) +                if pre then +                    pre = visualize(pre,false,a,parent) +                end +                if post then +                    post = visualize(post,false,a,parent) +                end +                if replace then +                    replace = visualize(replace,false,a,parent) +                end +                setdisc(current,pre,post,replace) +            elseif id == kern_code then +                local subtype = getsubtype(current) +                if subtype == font_kern_code then +                    if trace_fontkern or prev_trace_fontkern then +                        head, current = fontkern(head,current) +                    end +                    if trace_expansion or prev_trace_expansion then +                        head, current = kernexpansion(head,current) +                    end +                else -- if subtype == user_kern_code then +                    if trace_italic then +                        head, current = ruleditalic(head,current) +                    elseif trace_kern then +                        head, current = ruledkern(head,current,vertical) +                    end +                end +            elseif id == glue_code then +                local content = getleader(current) +                if content then +                    setleader(current,visualize(content,false,nil,parent)) +                elseif trace_glue then +                    head, current = ruledglue(head,current,vertical,parent) +                end +            elseif id == penalty_code then +                if trace_penalty then +                    head, current = ruledpenalty(head,current,vertical) +                end +            elseif id == hlist_code then +                local content = getlist(current) +                if content then +                    setlist(current,visualize(content,false,nil,current)) +                end +                if trace_hbox then +                    head, current = ruledbox(head,current,false,l_hbox,"H__",trace_simple,previous,trace_origin,parent) +                end +            elseif id == vlist_code then +                local content = getlist(current) +                if content then +                    setlist(current,visualize(content,true,nil,current)) +                end +                if trace_vtop then +                    head, current = ruledbox(head,current,true,l_vtop,"_T_",trace_simple,previous,trace_origin,parent) +                elseif trace_vbox then +                    head, current = ruledbox(head,current,true,l_vbox,"__V",trace_simple,previous,trace_origin,parent) +                end +            elseif id == whatsit_code then +                if trace_whatsit then +                    head, current = whatsit(head,current) +                end +            elseif id == user_code then +                if trace_user then +                    head, current = user(head,current) +                end +            elseif id == math_code then +                if trace_math then +                    head, current = math(head,current)                  end              end -        elseif id == glue_code then -            local content = getleader(current) -            if content then -                setleader(current,visualize(content,false,nil,parent)) -            elseif trace_glue then -                head, current = ruledglue(head,current,vertical,parent) -            end -        elseif id == penalty_code then -            if trace_penalty then -                head, current = ruledpenalty(head,current,vertical) -            end -        elseif id == hlist_code then -            local content = getlist(current) -            if content then -                setlist(current,visualize(content,false,nil,current)) -            end -            if trace_hbox then -                head, current = ruledbox(head,current,false,l_hbox,"H__",trace_simple,previous,trace_origin,parent) -            end -        elseif id == vlist_code then -            local content = getlist(current) -            if content then -                setlist(current,visualize(content,true,nil,current)) -            end -            if trace_vtop then -                head, current = ruledbox(head,current,true,l_vtop,"_T_",trace_simple,previous,trace_origin,parent) -            elseif trace_vbox then -                head, current = ruledbox(head,current,true,l_vbox,"__V",trace_simple,previous,trace_origin,parent) -            end -        elseif id == whatsit_code then -            if trace_whatsit then -                head, current = whatsit(head,current) -            end -        elseif id == user_code then -            if trace_user then -                head, current = user(head,current) -            end -        elseif id == math_code then -            if trace_math then -                head, current = math(head,current) -            end +            previous = current +            current  = getnext(current)          end -        previous = current -        current  = getnext(current) +        return head      end -    return head -end - -do      local function cleanup()          for tag, cache in next, caches do @@ -1101,20 +1188,20 @@ do      luatex.registerstopactions(cleanup) -end - -function visualizers.box(n) -    if usedfont then -        starttiming(visualizers) -        local box = getbox(n) -        if box then -            setlist(box,visualize(getlist(box),getid(box) == vlist_code)) +    function visualizers.box(n) +        if usedfont then +            starttiming(visualizers) +            local box = getbox(n) +            if box then +                setlist(box,visualize(getlist(box),getid(box) == vlist_code)) +            end +            stoptiming(visualizers) +            return head, true +        else +            return head, false          end -        stoptiming(visualizers) -        return head, true -    else -        return head, false      end +  end  do @@ -1162,40 +1249,44 @@ end)  -- interface -local implement = interfaces.implement +do -implement { -    name      = "setvisual", -    arguments = "string", -    actions   = visualizers.setvisual -} +    local implement = interfaces.implement -implement { -    name      = "setvisuals", -    arguments = "string", -    actions   = visualizers.setvisual -} +    implement { +        name      = "setvisual", +        arguments = "string", +        actions   = visualizers.setvisual +    } -implement { -    name      = "getvisual", -    arguments = "string", -    actions   = { setvisual, context } -} +    implement { +        name      = "setvisuals", +        arguments = "string", +        actions   = visualizers.setvisual +    }      implement { -    name      = "setvisuallayer", -    arguments = "string", -    actions   = visualizers.setlayer -} +        name      = "getvisual", +        arguments = "string", +        actions   = { setvisual, context } +    } -implement { -    name      = "markvisualfonts", -    arguments = "integer", -    actions   = visualizers.markfonts -} +        implement { +        name      = "setvisuallayer", +        arguments = "string", +        actions   = visualizers.setlayer +    } -implement { -    name      = "setvisualfont", -    arguments = "integer", -    actions   = visualizers.setfont -} +    implement { +        name      = "markvisualfonts", +        arguments = "integer", +        actions   = visualizers.markfonts +    } + +    implement { +        name      = "setvisualfont", +        arguments = "integer", +        actions   = visualizers.setfont +    } + +end diff --git a/tex/context/base/mkiv/trac-vis.mkiv b/tex/context/base/mkiv/trac-vis.mkiv index 894408222..fbe59a875 100644 --- a/tex/context/base/mkiv/trac-vis.mkiv +++ b/tex/context/base/mkiv/trac-vis.mkiv @@ -119,6 +119,9 @@  \unexpanded\def\showfontkerns    {\clf_setvisual{fontkern}} +\unexpanded\def\showfontexpansion +  {\clf_setvisual{expansion}} +  \unexpanded\def\setvisualizerfont    {\dosingleempty\syst_visualizers_setfont} diff --git a/tex/context/base/mkiv/typo-del.mkiv b/tex/context/base/mkiv/typo-del.mkiv index 84fe2c469..b7ebb28d9 100644 --- a/tex/context/base/mkiv/typo-del.mkiv +++ b/tex/context/base/mkiv/typo-del.mkiv @@ -369,7 +369,7 @@    %\c!language=\v!local,     \c!repeat=\v!no] -\def\typo_delimited_repeat_ideed +\def\typo_delimited_repeat_indeed    {\relax\ifcase\delimitedtextlevel\else       \typo_delimited_handle_middle\c!middle     \fi} diff --git a/tex/context/base/mkiv/util-lib.lua b/tex/context/base/mkiv/util-lib.lua index ab26afe46..5c3f5f3cf 100644 --- a/tex/context/base/mkiv/util-lib.lua +++ b/tex/context/base/mkiv/util-lib.lua @@ -343,10 +343,17 @@ We use the same lookup logic for ffi loading.      local trace_ffilib  = false      local savedffiload  = ffi.load + -- local pushlibpath = package.pushlibpath + -- local poplibpath  = package.poplibpath +   -- ffi.savedload = savedffiload      trackers.register("resolvers.ffilib", function(v) trace_ffilib = v end) + -- pushlibpath(pathpart(name)) + -- local message, library = pcall(savedffiload,nameonly(name)) + -- poplibpath() +      local function locateindeed(name)          local message, library = pcall(savedffiload,removesuffix(name))          if type(message) == "userdata" then diff --git a/tex/context/base/mkiv/util-sbx.lua b/tex/context/base/mkiv/util-sbx.lua index 66a650875..9318ee38a 100644 --- a/tex/context/base/mkiv/util-sbx.lua +++ b/tex/context/base/mkiv/util-sbx.lua @@ -28,6 +28,7 @@ local concat         = string.concat  local unquoted       = string.unquoted  local optionalquoted = string.optionalquoted  local basename       = file.basename +local nameonly       = file.nameonly  local sandbox        = sandbox  local validroots     = { } @@ -122,9 +123,9 @@ local function registerlibrary(name)              return          end          if validlibraries == true then -            validlibraries = { [name] = true } +            validlibraries = { [nameonly(name)] = true }          else -            validlibraries[name] = true +            validlibraries[nameonly(name)] = true          end      elseif name == true then          validlibraries = { } @@ -562,9 +563,9 @@ if FFISUPPORTED and ffi then          end      end -    local load = ffi.load +    local fiiload = ffi.load -    if load then +    if fiiload then          local reported = { } @@ -573,10 +574,10 @@ if FFISUPPORTED and ffi then                  -- all blocked              elseif validlibraries == true then                  -- all permitted -                return load(name,...) -            elseif validlibraries[name] then +                return fiiload(name,...) +            elseif validlibraries[nameonly(name)] then                  -- 'name' permitted -                return load(name,...) +                return fiiload(name,...)              else                  -- 'name' not permitted              end diff --git a/tex/context/base/mkiv/util-seq.lua b/tex/context/base/mkiv/util-seq.lua index 5836f5eca..d302ff276 100644 --- a/tex/context/base/mkiv/util-seq.lua +++ b/tex/context/base/mkiv/util-seq.lua @@ -361,6 +361,6 @@ function sequencers.nodeprocessor(t,nofarguments) -- todo: handle 'kind' in plug          end      end      local processor = #calls > 0 and formatters[nostate and template_yes_nostate or template_yes_state](concat(vars,"\n"),args,concat(calls,"\n")) or template_nop --- print(processor) + -- print(processor)      return processor  end diff --git a/tex/context/base/mkiv/util-str.lua b/tex/context/base/mkiv/util-str.lua index cebbc6be2..6d8f198ab 100644 --- a/tex/context/base/mkiv/util-str.lua +++ b/tex/context/base/mkiv/util-str.lua @@ -423,6 +423,27 @@ end  -- print(number.formatted(12345678,true))  -- print(number.formatted(1234.56,"!","?")) +local p = Cs( +        P("-")^0 +      * (P("0")^1/"")^0 +      * (1-P("."))^0 +      * (P(".") * P("0")^1 * P(-1)/"" + P(".")^0) +      * P(1-P("0")^1*P(-1))^0 +    ) + +function number.compactfloat(n,fmt) +    if n == 0 then +        return "0" +    elseif n == 1 then +        return "1" +    end +    n = lpegmatch(p,format(fmt or "%0.3f",n)) +    if n == "." or n == "" or n == "-" then +        return "0" +    end +    return n +end +  local zero      = P("0")^1 / ""  local plus      = P("+")   / ""  local minus     = P("-") @@ -1183,3 +1204,21 @@ local pattern = Cs((newline / (os.newline or "\r") + 1)^0)  function string.replacenewlines(str)      return lpegmatch(pattern,str)  end + +-- + +function strings.newcollector() +    local result, r = { }, 0 +    return +        function(fmt,str,...) -- write +            r = r + 1 +            result[r] = str == nil and fmt or formatters[fmt](str,...) +        end, +        function(connector) -- flush +            if result then +                local str = concat(result,connector) +                result, r = { }, 0 +                return str +            end +        end +end diff --git a/tex/context/base/mkiv/util-tab.lua b/tex/context/base/mkiv/util-tab.lua index fb2702228..05038f2fb 100644 --- a/tex/context/base/mkiv/util-tab.lua +++ b/tex/context/base/mkiv/util-tab.lua @@ -616,6 +616,10 @@ local is_simple_table = table.is_simple_table  --     return nil  -- end +-- In order to overcome the luajit (65K constant) limitation I tried a split approach, +-- i.e. outputting the first level tables as locals but that failed with large cjk +-- fonts too so I removed that ... just use luatex instead. +  local function serialize(root,name,specification)      if type(specification) == "table" then diff --git a/tex/context/interface/mkii/keys-en.xml b/tex/context/interface/mkii/keys-en.xml index a1c935db8..be455e9fc 100644 --- a/tex/context/interface/mkii/keys-en.xml +++ b/tex/context/interface/mkii/keys-en.xml @@ -693,6 +693,7 @@  		<cd:constant name='bottomoffset' value='bottomoffset'/>  		<cd:constant name='bottomspace' value='bottomspace'/>  		<cd:constant name='bottomstate' value='bottomstate'/> +		<cd:constant name='break' value='break'/>  		<cd:constant name='buffer' value='buffer'/>  		<cd:constant name='cache' value='cache'/>  		<cd:constant name='calculate' value='calculate'/> diff --git a/tex/context/interface/mkii/keys-pe.xml b/tex/context/interface/mkii/keys-pe.xml index 91f778c5e..4033b45f8 100644 --- a/tex/context/interface/mkii/keys-pe.xml +++ b/tex/context/interface/mkii/keys-pe.xml @@ -693,6 +693,7 @@  		<cd:constant name='bottomoffset' value='آفستپایین'/>  		<cd:constant name='bottomspace' value='فضایپایین'/>  		<cd:constant name='bottomstate' value='وضعیتپایین'/> +		<cd:constant name='break' value='break'/>  		<cd:constant name='buffer' value='buffer'/>  		<cd:constant name='cache' value='میانگیر'/>  		<cd:constant name='calculate' value='محاسبه'/> diff --git a/tex/context/interface/mkiv/context-en.xml b/tex/context/interface/mkiv/context-en.xml index b6308269d..ce9a81b5a 100644 --- a/tex/context/interface/mkiv/context-en.xml +++ b/tex/context/interface/mkiv/context-en.xml @@ -7361,6 +7361,12 @@        <cd:constant type="none"/>        <cd:constant type="cd:dimension"/>       </cd:parameter> +     <cd:parameter name="stretch"> +      <cd:constant type="cd:number"/> +     </cd:parameter> +     <cd:parameter name="shrink"> +      <cd:constant type="cd:number"/> +     </cd:parameter>       <cd:parameter name="hang">        <cd:constant type="fit"/>        <cd:constant type="broad"/> @@ -8081,6 +8087,12 @@        <cd:constant type="none"/>        <cd:constant type="cd:dimension"/>       </cd:parameter> +     <cd:parameter name="stretch"> +      <cd:constant type="cd:number"/> +     </cd:parameter> +     <cd:parameter name="shrink"> +      <cd:constant type="cd:number"/> +     </cd:parameter>       <cd:parameter name="hang">        <cd:constant type="fit"/>        <cd:constant type="broad"/> @@ -25269,6 +25281,12 @@        <cd:constant type="none"/>        <cd:constant type="cd:dimension"/>       </cd:parameter> +     <cd:parameter name="stretch"> +      <cd:constant type="cd:number"/> +     </cd:parameter> +     <cd:parameter name="shrink"> +      <cd:constant type="cd:number"/> +     </cd:parameter>       <cd:parameter name="hang">        <cd:constant type="fit"/>        <cd:constant type="broad"/> diff --git a/tex/context/interface/mkiv/i-context.pdf b/tex/context/interface/mkiv/i-context.pdf Binary files differindex 42aaf874c..22a0fdd48 100644 --- a/tex/context/interface/mkiv/i-context.pdf +++ b/tex/context/interface/mkiv/i-context.pdf diff --git a/tex/context/interface/mkiv/i-description.xml b/tex/context/interface/mkiv/i-description.xml index 6f836f326..66da76acc 100644 --- a/tex/context/interface/mkiv/i-description.xml +++ b/tex/context/interface/mkiv/i-description.xml @@ -84,6 +84,12 @@                      <cd:constant type="none"/>                      <cd:constant type="cd:dimension"/>                  </cd:parameter> +                <cd:parameter name="stretch"> +                    <cd:constant type="cd:number"/> +                </cd:parameter> +                <cd:parameter name="shrink"> +                    <cd:constant type="cd:number"/> +                </cd:parameter>                  <cd:parameter name="hang">                      <cd:constant type="fit"/>                      <cd:constant type="broad"/> diff --git a/tex/context/interface/mkiv/i-enumeration.xml b/tex/context/interface/mkiv/i-enumeration.xml index 2469a9713..575d192a5 100644 --- a/tex/context/interface/mkiv/i-enumeration.xml +++ b/tex/context/interface/mkiv/i-enumeration.xml @@ -130,6 +130,12 @@                      <cd:constant type="none"/>                      <cd:constant type="cd:dimension"/>                  </cd:parameter> +                <cd:parameter name="stretch"> +                    <cd:constant type="cd:number"/> +                </cd:parameter> +                <cd:parameter name="shrink"> +                    <cd:constant type="cd:number"/> +                </cd:parameter>                  <cd:parameter name="hang">                      <cd:constant type="fit"/>                      <cd:constant type="broad"/> diff --git a/tex/context/interface/mkiv/i-note.xml b/tex/context/interface/mkiv/i-note.xml index 339bb58ad..734702264 100644 --- a/tex/context/interface/mkiv/i-note.xml +++ b/tex/context/interface/mkiv/i-note.xml @@ -138,6 +138,12 @@                      <cd:constant type="none"/>                      <cd:constant type="cd:dimension"/>                  </cd:parameter> +                <cd:parameter name="stretch"> +                    <cd:constant type="cd:number"/> +                </cd:parameter> +                <cd:parameter name="shrink"> +                    <cd:constant type="cd:number"/> +                </cd:parameter>                  <cd:parameter name="hang">                      <cd:constant type="fit"/>                      <cd:constant type="broad"/> diff --git a/tex/context/interface/mkiv/i-readme.pdf b/tex/context/interface/mkiv/i-readme.pdf Binary files differindex 1ad38df61..3c36718a9 100644 --- a/tex/context/interface/mkiv/i-readme.pdf +++ b/tex/context/interface/mkiv/i-readme.pdf diff --git a/tex/context/test/mkiv/pdf-ua.mkiv b/tex/context/test/mkiv/pdf-ua.mkiv index e048b9717..e83bd88b9 100644 --- a/tex/context/test/mkiv/pdf-ua.mkiv +++ b/tex/context/test/mkiv/pdf-ua.mkiv @@ -13,7 +13,7 @@  \setuptagging[state=start] % should be set automatically, if 'tagging = true'  \setuplanguage[en] -   +  % DisplayDocTitle key is not set to true  %  % [from PDFUA-Reference-09_(English-invoice).pdf] @@ -21,6 +21,7 @@  % <</Type/Catalog/Pages 1 0 R/ViewerPreferences<</DisplayDocTitle true>>/PageMode/UseOutlines/Outlines 43 0 R/Lang(en)/StructTreeRoot 2 0 R/MarkInfo<</Marked true/Suspects false>>/OutputIntents[ 128 0 R]/Metadata 127 0 R>>  % endobj  % this may interfer with other ViewerPreferences settings? +  \startluacode      lpdf.addtocatalog("ViewerPreferences",lpdf.dictionary{DisplayDocTitle = true})  \stopluacode @@ -29,13 +30,13 @@  \starttext    Warning: This document may contain errors and traces of hazelnuts. -   +    \blank[big]    \startMPcode      fill fullcircle scaled 3cm withcolor red;    \stopMPcode -   +    \blank[big]    The \quote{Matterhorn Protocol} gives a condensed overview of the possible failure conditions diff --git a/tex/generic/context/luatex/luatex-fonts-demo-vf-1.lua b/tex/generic/context/luatex/luatex-fonts-demo-vf-1.lua index 1fc1c79f7..1e4256003 100644 --- a/tex/generic/context/luatex/luatex-fonts-demo-vf-1.lua +++ b/tex/generic/context/luatex/luatex-fonts-demo-vf-1.lua @@ -8,12 +8,12 @@ if not modules then modules = { } end modules ['luatex-fonts-demo-vf-1'] = {  local identifiers = fonts.hashes.identifiers -local defaults = { -    { "pdf", "0 g" }, -    { "pdf", "1 0 0 rg" }, -    { "pdf", "0 1 0 rg" }, -    { "pdf", "0 0 1 rg" }, -    { "pdf", "0 0 1 rg" }, +local defaults = { [0] = +    { "pdf", "origin", "0 g" }, +    { "pdf", "origin", "1 0 0 rg" }, +    { "pdf", "origin", "0 1 0 rg" }, +    { "pdf", "origin", "0 0 1 rg" }, +    { "pdf", "origin", "0 0 1 rg" },  }  return function(specification) @@ -28,9 +28,6 @@ return function(specification)              { id = id2 },              { id = id3 },          } -        local color = { -            [0] = defaults, -        }          local chars = {              identifiers[id1].characters,              identifiers[id2].characters, @@ -40,9 +37,9 @@ return function(specification)              local n = math.floor(math.random(1,3)+0.5)              local c = chars[n][u] or v              v.commands = { -                color[n], +                defaults[n] or defaults[0],                  { 'slot', n, u }, -                color[0], +                defaults[0],                  { 'nop' }              }              v.kerns    = nil diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua index 51e10df43..725d23268 100644 --- a/tex/generic/context/luatex/luatex-fonts-merged.lua +++ b/tex/generic/context/luatex/luatex-fonts-merged.lua @@ -1,6 +1,6 @@  -- merged file : c:/data/develop/context/sources/luatex-fonts-merged.lua  -- parent file : c:/data/develop/context/sources/luatex-fonts.lua --- merge date  : 07/05/17 23:01:18 +-- merge date  : 07/14/17 19:35:39  do -- begin closure to overcome local limits and interference @@ -3762,6 +3762,21 @@ function number.formatted(n,sep1,sep2)      return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".")    end  end +local p=Cs( +    P("-")^0*(P("0")^1/"")^0*(1-P("."))^0*(P(".")*P("0")^1*P(-1)/""+P(".")^0)*P(1-P("0")^1*P(-1))^0 +  ) +function number.compactfloat(n,fmt) +  if n==0 then +    return "0" +  elseif n==1 then +    return "1" +  end +  n=lpegmatch(p,format(fmt or "%0.3f",n)) +  if n=="." or n=="" or n=="-" then +    return "0" +  end +  return n +end  local zero=P("0")^1/""  local plus=P("+")/""  local minus=P("-") @@ -4274,6 +4289,21 @@ local pattern=Cs((newline/(os.newline or "\r")+1)^0)  function string.replacenewlines(str)    return lpegmatch(pattern,str)  end +function strings.newcollector() +  local result,r={},0 +  return +    function(fmt,str,...)  +      r=r+1 +      result[r]=str==nil and fmt or formatters[fmt](str,...) +    end, +    function(connector)  +      if result then +        local str=concat(result,connector) +        result,r={},0 +        return str +      end +    end +end  end -- closure @@ -14796,12 +14826,12 @@ local function readvariation(f,offset)  end  local function readposition(f,format,mainoffset,getdelta)    if format==0 then -    return +    return false    end    if format==0x04 then      local h=readshort(f)      if h==0 then -      return +      return true       else        return { 0,0,h,0 }      end @@ -14810,7 +14840,7 @@ local function readposition(f,format,mainoffset,getdelta)      local x=readshort(f)      local h=readshort(f)      if x==0 and h==0 then -      return +      return true       else        return { x,0,h,0 }      end @@ -14829,7 +14859,7 @@ local function readposition(f,format,mainoffset,getdelta)        skipshort(f,1)      end      if h==0 then -      return +      return true       else        return { 0,0,h,0 }      end @@ -14875,7 +14905,7 @@ local function readposition(f,format,mainoffset,getdelta)      end      return { x,y,h,v }    elseif x==0 and y==0 and h==0 and v==0 then -    return +    return true     else      return { x,y,h,v }    end @@ -15537,7 +15567,7 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly            local first=value[2]            local second=value[3]            if first or second then -            hash[other]={ first,second }  +            hash[other]={ first,second or nil }             else              hash[other]=nil            end @@ -15573,7 +15603,7 @@ function gposhandlers.pair(f,fontdata,lookupid,lookupoffset,offset,glyphs,nofgly                local first=offsets[1]                local second=offsets[2]                if first or second then -                hash[paired]={ first,second } +                hash[paired]={ first,second or nil }                else                end              end @@ -15605,17 +15635,20 @@ function gposhandlers.cursive(f,fontdata,lookupid,lookupoffset,offset,glyphs,nof        local entry=readushort(f)        local exit=readushort(f)        records[i]={ -        entry=entry~=0 and (tableoffset+entry) or false, -        exit=exit~=0 and (tableoffset+exit ) or false, +        entry~=0 and (tableoffset+entry) or false, +        exit~=0 and (tableoffset+exit ) or nil,        }      end +    local cc=(fontdata.temporary.cursivecount or 0)+1 +    fontdata.temporary.cursivecount=cc +    cc="cc-"..cc      coverage=readcoverage(f,coverage)      for i=1,nofrecords do        local r=records[i]        records[i]={ -        1, -        readanchor(f,r.entry,getdelta) or nil, -        readanchor(f,r.exit,getdelta) or nil, +        cc, +        readanchor(f,r[1],getdelta) or false, +        readanchor(f,r[2],getdelta) or nil,        }      end      for index,newindex in next,coverage do @@ -17881,7 +17914,7 @@ local function checklookups(fontdata,missing,nofmissing)        end      end      if next(done) then -      report("not unicoded: % t",table.sortedkeys(done)) +      report("not unicoded: % t",sortedkeys(done))      end    end  end @@ -18270,13 +18303,14 @@ function readers.pack(data)          return nt        end      end -    local function pack_flat(v) -      local tag=tabstr_flat(v) +    local function pack_normal_cc(v) +      local tag=tabstr_normal(v)        local ht=h[tag]        if ht then          c[ht]=c[ht]+1          return ht        else +        v[1]=0          nt=nt+1          t[nt]=v          h[tag]=nt @@ -18284,8 +18318,8 @@ function readers.pack(data)          return nt        end      end -    local function pack_boolean(v) -      local tag=tabstr_boolean(v) +    local function pack_flat(v) +      local tag=tabstr_flat(v)        local ht=h[tag]        if ht then          c[ht]=c[ht]+1 @@ -18326,6 +18360,20 @@ function readers.pack(data)          return nt        end      end +    local function pack_boolean(v) +      local tag=tabstr_boolean(v) +      local ht=h[tag] +      if ht then +        c[ht]=c[ht]+1 +        return ht +      else +        nt=nt+1 +        t[nt]=v +        h[tag]=nt +        c[nt]=1 +        return nt +      end +    end      local function pack_final(v)        if c[v]<=criterium then          return t[v] @@ -18342,6 +18390,22 @@ function readers.pack(data)          end        end      end +    local function pack_final_cc(v) +      if c[v]<=criterium then +        return t[v] +      else +        local hv=hh[v] +        if hv then +          return hv +        else +          ntt=ntt+1 +          tt[ntt]=t[v] +          hh[v]=ntt +          cc[ntt]=c[v] +          return ntt +        end +      end +    end      local function success(stage,pass)        if nt==0 then          if trace_loading or trace_packing then @@ -18387,9 +18451,9 @@ function readers.pack(data)      end      local function packers(pass)        if pass==1 then -        return pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed +        return pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed,pack_normal_cc        else -        return pack_final,pack_final,pack_final,pack_final,pack_final +        return pack_final,pack_final,pack_final,pack_final,pack_final,pack_final_cc        end      end      local resources=data.resources @@ -18407,7 +18471,7 @@ function readers.pack(data)        if trace_packing then          report_otf("start packing: stage 1, pass %s",pass)        end -      local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass) +      local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed,pack_normal_cc=packers(pass)        for unicode,description in next,descriptions do          local boundingbox=description.boundingbox          if boundingbox then @@ -18444,8 +18508,8 @@ function readers.pack(data)                    else                      for g1,d1 in next,c do                        for g2,d2 in next,d1 do -                        local f=d2[1] if f then d2[1]=pack_indexed(f) end -                        local s=d2[2] if s then d2[2]=pack_indexed(s) end +                        local f=d2[1] if f and f~=true then d2[1]=pack_indexed(f) end +                        local s=d2[2] if s and s~=true then d2[2]=pack_indexed(s) end                        end                      end                    end @@ -18457,7 +18521,9 @@ function readers.pack(data)                      step.coverage=pack_normal(c)                    else                      for g1,d1 in next,c do -                      c[g1]=pack_indexed(d1) +                      if d1~=true then +                        c[g1]=pack_indexed(d1) +                      end                      end                    end                  end @@ -18565,7 +18631,6 @@ function readers.pack(data)                for i=1,#deltas do                  local di=deltas[i]                  local d=di.deltas -                local r=di.regions                  for j=1,#d do                    d[j]=pack_indexed(d[j])                  end @@ -18597,7 +18662,7 @@ function readers.pack(data)          if trace_packing then            report_otf("start packing: stage 2, pass %s",pass)          end -        local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass) +        local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed,pack_normal_cc=packers(pass)          for unicode,description in next,descriptions do            local math=description.math            if math then @@ -18619,8 +18684,7 @@ function readers.pack(data)                  if kind=="gpos_pair" then                    local c=step.coverage                    if c then -                    if step.format=="kern" then -                    else +                    if step.format~="kern" then                        for g1,d1 in next,c do                          for g2,d2 in next,d1 do                            d1[g2]=pack_normal(d2) @@ -18628,6 +18692,15 @@ function readers.pack(data)                        end                      end                    end +                elseif kind=="gpos_mark2ligature" then +                  local c=step.baseclasses  +                  if c then +                    for g1,d1 in next,c do +                      for g2,d2 in next,d1 do +                        d1[g2]=pack_normal(d2) +                      end +                    end +                  end                  end                  local rules=step.rules                  if rules then @@ -18670,7 +18743,7 @@ function readers.pack(data)          if trace_packing then            report_otf("start packing: stage 3, pass %s",pass)          end -        local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed=packers(pass) +        local pack_normal,pack_indexed,pack_flat,pack_boolean,pack_mixed,pack_normal_cc=packers(pass)          local function packthem(sequences)            for i=1,#sequences do              local sequence=sequences[i] @@ -18683,13 +18756,19 @@ function readers.pack(data)                  if kind=="gpos_pair" then                    local c=step.coverage                    if c then -                    if step.format=="kern" then -                    else +                    if step.format~="kern" then                        for g1,d1 in next,c do                          c[g1]=pack_normal(d1)                        end                      end                    end +                elseif kind=="gpos_cursive" then +                  local c=step.coverage +                  if c then +                    for g1,d1 in next,c do +                      c[g1]=pack_normal_cc(d1) +                    end +                  end                  end                end              end @@ -18758,6 +18837,19 @@ function readers.unpack(data)            local features=sequence.features            local flags=sequence.flags            local markclass=sequence.markclass +          if features then +            local tv=tables[features] +            if tv then +              sequence.features=tv +              features=tv +            end +            for script,feature in next,features do +              local tv=tables[feature] +              if tv then +                features[script]=tv +              end +            end +          end            if steps then              for i=1,#steps do                local step=steps[i] @@ -18811,6 +18903,11 @@ function readers.unpack(data)                  local c=step.coverage                  if c then                    for g1,d1 in next,c do +                    local tv=tables[d1] +                    if tv then +                      d1=tv +                      c[g1]=d1 +                    end                      local f=tables[d1[2]] if f then d1[2]=f end                      local s=tables[d1[3]] if s then d1[3]=s end                    end @@ -18841,6 +18938,11 @@ function readers.unpack(data)                  if c then                    for g1,d1 in next,c do                      for g2,d2 in next,d1 do +                      local tv=tables[d2]  +                      if tv then +                        d2=tv +                        d1[g2]=d2 +                      end                        for g3,d3 in next,d2 do                          local tv=tables[d2[g3]]                          if tv then @@ -18917,19 +19019,6 @@ function readers.unpack(data)                end              end            end -          if features then -            local tv=tables[features] -            if tv then -              sequence.features=tv -              features=tv -            end -            for script,feature in next,features do -              local tv=tables[feature] -              if tv then -                features[script]=tv -              end -            end -          end            if order then              local tv=tables[order]              if tv then @@ -19220,7 +19309,8 @@ local function checkpairs(lookup)              break            else              local v=d2[1] -            if v[1]~=0 or v[2]~=0 or v[4]~=0 then +            if v==true then +            elseif v and (v[1]~=0 or v[2]~=0 or v[4]~=0) then                kerns=false                break              end @@ -19231,7 +19321,12 @@ local function checkpairs(lookup)          report("turning pairs of step %a of %a lookup %a into kerns",i,lookup.type,lookup.name)          for g1,d1 in next,coverage do            for g2,d2 in next,d1 do -            d1[g2]=d2[1][3] +            local v=d2[1] +            if v==true then +              d1[g2]=nil +            elseif v then +              d1[g2]=v[3] +            end            end          end          step.format="kern" @@ -19260,6 +19355,7 @@ function readers.compact(data)          allsteps=allsteps+nofsteps          if nofsteps>1 then            local kind=lookup.type +          local merg=merged            if kind=="gsub_single" or kind=="gsub_alternate" or kind=="gsub_multiple" then              merged=merged+mergesteps_1(lookup)            elseif kind=="gsub_ligature" then @@ -19275,6 +19371,9 @@ function readers.compact(data)            elseif kind=="gpos_mark2mark" or kind=="gpos_mark2base" or kind=="gpos_mark2ligature" then              merged=merged+mergesteps_3(lookup)            end +          if merg~=merged then +            lookup.merged=true +          end          end        end      else @@ -19334,7 +19433,7 @@ function readers.expand(data)        for i=1,#t do          local step=t[i]          local coverage=step.coverage -        for k,v in next,coverage do +        for k in next,coverage do            local m=merged[k]            if m then              m[2]=i @@ -19353,6 +19452,7 @@ function readers.expand(data)          local sequence=sequences[i]          local steps=sequence.steps          if steps then +          local nofsteps=sequence.nofsteps            setmetatableindex(steps,mergesteps)            local kind=sequence.type            local markclass=sequence.markclass @@ -19364,7 +19464,14 @@ function readers.expand(data)                sequence.markclass=markclasses[markclass]              end            end -          local nofsteps=sequence.nofsteps +          local flags=sequence.flags +          if flags then +            flags[5]=flags[1]~=false  +                or flags[2]~=false  +                or flags[3]~=false  +                or sequence.markclass +                or false +          end            for i=1,nofsteps do              local step=steps[i]              local baseclasses=step.baseclasses @@ -19432,6 +19539,7 @@ function readers.expand(data)                    end                  end                  if sequence[1] then  +                  sequence.n=#sequence                     rulesize=rulesize+1                    rulehash[rulesize]={                      nofrules, @@ -19451,6 +19559,7 @@ function readers.expand(data)                      end                    end                  end +                rulehash.n=#rulehash                 end              end            end @@ -19492,7 +19601,7 @@ local trace_defining=false registertracker("fonts.defining",function(v) trace_de  local report_otf=logs.reporter("fonts","otf loading")  local fonts=fonts  local otf=fonts.handlers.otf -otf.version=3.031  +otf.version=3.032   otf.cache=containers.define("fonts","otl",otf.version,true)  otf.svgcache=containers.define("fonts","svg",otf.version,true)  otf.sbixcache=containers.define("fonts","sbix",otf.version,true) @@ -20420,9 +20529,11 @@ local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist              local character=characters[unicode]              local kerns=character.kerns              for otherunicode,kern in next,data do -              if not kern[2] and not (kerns and kerns[otherunicode]) then +              local other=kern[2] +              if other==true or (not other and not (kerns and kerns[otherunicode])) then                  local kern=kern[1] -                if kern[1]~=0 or kern[2]~=0 or kern[4]~=0 then +                if kern==true then +                elseif kern[1]~=0 or kern[2]~=0 or kern[4]~=0 then                  else                    kern=kern[3]                    if kern~=0 then @@ -20447,6 +20558,42 @@ local function preparepositionings(tfmdata,feature,value,validlookups,lookuplist  end  local function initializehashes(tfmdata)  end +local function checkmathreplacements(tfmdata,fullname) +  if tfmdata.mathparameters then +    local characters=tfmdata.characters +    local changed=tfmdata.changed +    if next(changed) then +      if trace_preparing or trace_baseinit then +        report_prepare("checking math replacements for %a",fullname) +      end +      for unicode,replacement in next,changed do +        local u=characters[unicode] +        local r=characters[replacement] +        local n=u.next +        local v=u.vert_variants +        local h=u.horiz_variants +        if n and not r.next then +          if trace_preparing then +            report_prepare("forcing %s for %C substituted by %U","incremental step",unicode,replacement) +          end +          r.next=n +        end +        if v and not r.vert_variants then +          if trace_preparing then +            report_prepare("forcing %s for %C substituted by %U","vertical variants",unicode,replacement) +          end +          r.vert_variants=v +        end +        if h and not r.horiz_variants then +          if trace_preparing then +            report_prepare("forcing %s for %C substituted by %U","horizontal variants",unicode,replacement) +          end +          r.horiz_variants=h +        end +      end +    end +  end +end  local function featuresinitializer(tfmdata,value)    if true then       local starttime=trace_preparing and os.clock() @@ -20463,6 +20610,8 @@ local function featuresinitializer(tfmdata,value)        local rawfeatures=rawresources and rawresources.features        local basesubstitutions=rawfeatures and rawfeatures.gsub        local basepositionings=rawfeatures and rawfeatures.gpos +      local substitutionsdone=false +      local positioningsdone=false        if basesubstitutions or basepositionings then          local sequences=tfmdata.resources.sequences          for s=1,#sequences do @@ -20483,12 +20632,14 @@ local function featuresinitializer(tfmdata,value)                      end                      preparesubstitutions(tfmdata,feature,value,validlookups,lookuplist)                      registerbasefeature(feature,value) +                    substitutionsdone=true                    elseif basepositionings and basepositionings[feature] then                      if trace_preparing then                        report_prepare("filtering base %a feature %a for %a with value %a","pos",feature,fullname,value)                      end                      preparepositionings(tfmdata,feature,value,validlookups,lookuplist)                      registerbasefeature(feature,value) +                    positioningsdone=true                    end                  end                end @@ -20496,6 +20647,9 @@ local function featuresinitializer(tfmdata,value)            end          end        end +      if substitutionsdone then +        checkmathreplacements(tfmdata,fullname) +      end        registerbasehash(tfmdata)      end      if trace_preparing then @@ -20926,7 +21080,7 @@ local function show(n,what,nested,symbol)  end  local function showsub(n,what,where)    report_injections("begin subrun: %s",where) -  for n in traverse_id(glyph_code,n) do +  for n in traverse_char(n) do      showchar(n,where)      show(n,what,where," ")    end @@ -20993,7 +21147,6 @@ local function inject_kerns_only(head,where)    local prev=nil    local next=nil    local prevdisc=nil -  local prevglyph=nil    local pre=nil     local post=nil     local replace=nil  @@ -21049,10 +21202,8 @@ local function inject_kerns_only(head,where)          end        end        prevdisc=nil -      prevglyph=current      elseif char==false then        prevdisc=nil -      prevglyph=current      elseif id==disc_code then        pre,post,replace,pretail,posttail,replacetail=getdisc(current,true)        local done=false @@ -21104,10 +21255,8 @@ local function inject_kerns_only(head,where)        if done then          setdisc(current,pre,post,replace)        end -      prevglyph=nil        prevdisc=current      else -      prevglyph=nil        prevdisc=nil      end      prev=current @@ -21331,7 +21480,7 @@ local function inject_pairs_only(head,where)    if keepregisteredcounts then      keepregisteredcounts=false    else -    nofregisteredkerns=0 +    nofregisteredpairs=0    end    if trace_injections then      show_result(head) @@ -21802,7 +21951,7 @@ local function injectspaces(head)      threshold,      factor=getthreshold(font)    end -  for n in traverse_id(glue_code,tonut(head)) do +  for n in traverse_char(tonut(head)) do      local prev,next=getspaceboth(n)      local prevchar=prev and ischar(prev)      local nextchar=next and ischar(next) @@ -22313,7 +22462,6 @@ local trace_discruns=false registertracker("otf.discruns",function(v) trace_disc  local trace_compruns=false registertracker("otf.compruns",function(v) trace_compruns=v end)  local trace_testruns=false registertracker("otf.testruns",function(v) trace_testruns=v end)  local forcediscretionaries=false -local optimizekerns=true  directives.register("otf.forcediscretionaries",function(v)    forcediscretionaries=v  end) @@ -22365,7 +22513,6 @@ local flush_node_list=nuts.flush_list  local flush_node=nuts.flush_node  local end_of_math=nuts.end_of_math  local traverse_nodes=nuts.traverse -local traverse_id=nuts.traverse_id  local set_components=nuts.set_components  local take_components=nuts.take_components  local count_components=nuts.count_components @@ -22410,6 +22557,7 @@ local currentfont=false  local factor=0  local threshold=0  local checkmarks=false +local discs=false  local sweepnode=nil  local sweephead={}   local notmatchpre={}  @@ -22430,44 +22578,51 @@ end  local function logwarning(...)    report_direct(...)  end -local f_unicode=formatters["U+%X"]    -local f_uniname=formatters["U+%X (%s)"]  -local f_unilist=formatters["% t (% t)"] -local function gref(n)  -  if type(n)=="number" then -    local description=descriptions[n] -    local name=description and description.name -    if name then -      return f_uniname(n,name) -    else -      return f_unicode(n) -    end -  elseif n then -    local num,nam={},{} -    for i=1,#n do -      local ni=n[i] -      if tonumber(ni) then  -        local di=descriptions[ni] -        num[i]=f_unicode(ni) -        nam[i]=di and di.name or "-" +local gref do +  local f_unicode=formatters["U+%X"]    +  local f_uniname=formatters["U+%X (%s)"]  +  local f_unilist=formatters["% t (% t)"] +  gref=function(n)  +    if type(n)=="number" then +      local description=descriptions[n] +      local name=description and description.name +      if name then +        return f_uniname(n,name) +      else +        return f_unicode(n)        end +    elseif n then +      local num,nam={},{} +      for i=1,#n do +        local ni=n[i] +        if tonumber(ni) then  +          local di=descriptions[ni] +          num[i]=f_unicode(ni) +          nam[i]=di and di.name or "-" +        end +      end +      return f_unilist(num,nam) +    else +      return "<error in node mode tracing>"      end -    return f_unilist(num,nam) -  else -    return "<error in node mode tracing>"    end  end  local function cref(dataset,sequence,index)    if not dataset then      return "no valid dataset" -  elseif index then -    return formatters["feature %a, type %a, chain lookup %a, index %a"](dataset[4],sequence.type,sequence.name,index) +  end +  local merged=sequence.merged and "merged " or "" +  if index then +    return formatters["feature %a, type %a, %schain lookup %a, index %a"]( +      dataset[4],sequence.type,merged,sequence.name,index)    else -    return formatters["feature %a, type %a, chain lookup %a"](dataset[4],sequence.type,sequence.name) +    return formatters["feature %a, type %a, %schain lookup %a"]( +      dataset[4],sequence.type,merged,sequence.name)    end  end  local function pref(dataset,sequence) -  return formatters["feature %a, type %a, lookup %a"](dataset[4],sequence.type,sequence.name) +  return formatters["feature %a, type %a, %slookup %a"]( +    dataset[4],sequence.type,sequence.merged and "merged " or "",sequence.name)  end  local function mref(rlmode)    if not rlmode or rlmode==0 then @@ -22757,7 +22912,7 @@ function handlers.gsub_alternate(head,start,dataset,sequence,alternative)      setchar(start,choice)    else      if trace_alternatives then -        logwarning("%s: no variant %a for %s, %s",pref(dataset,sequence),value,gref(getchar(start)),comment) +      logwarning("%s: no variant %a for %s, %s",pref(dataset,sequence),value,gref(getchar(start)),comment)      end    end    return head,start,true @@ -22910,28 +23065,23 @@ function handlers.gpos_pair(head,start,dataset,sequence,kerns,rlmode,step,i,inje            break          elseif step.format=="pair" then            local a,b=krn[1],krn[2] -          if optimizekerns then -            if not b and a[1]==0 and a[2]==0 and a[4]==0 then -              local k=setkern(snext,factor,rlmode,a[3],injection) -              if trace_kerns then -                logprocess("%s: shifting single %s by %p",pref(dataset,sequence),gref(nextchar),k) -              end -              return head,start,true -            end -          end -          if a and #a>0 then +          if a==true then +          elseif a then               local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,injection)              if trace_kerns then                local startchar=getchar(start)                logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections")              end            end -          if b and #b>0 then +          if b==true then +            start=snext  +          elseif b then               local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,injection)              if trace_kerns then                local startchar=getchar(snext)                logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p) as %s",pref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h,injection or "injections")              end +            start=snext             end            return head,start,true          elseif krn~=0 then @@ -23412,28 +23562,23 @@ function chainprocs.gpos_pair(head,start,stop,dataset,sequence,currentlookup,rlm              break            elseif step.format=="pair" then              local a,b=krn[1],krn[2] -            if optimizekerns then -              if not b and a[1]==0 and a[2]==0 and a[4]==0 then -                local k=setkern(snext,factor,rlmode,a[3],"injections") -                if trace_kerns then -                  logprocess("%s: shifting single %s by %p",cref(dataset,sequence),gref(startchar),k) -                end -                return head,start,true -              end -            end -            if a and #a>0 then -              local startchar=getchar(start) +            if a then +            elseif a then                local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,"injections")                 if trace_kerns then +                local startchar=getchar(start)                  logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h)                end              end -            if b and #b>0 then -              local startchar=getchar(start) +            if b==true then +              start=snext  +            elseif b then                 local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,"injections")                if trace_kerns then +                local startchar=getchar(start)                  logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(dataset,sequence),gref(startchar),gref(nextchar),x,y,w,h)                end +              start=snext               end              return head,start,true            elseif krn~=0 then @@ -23742,12 +23887,9 @@ end  local noflags={ false,false,false,false }  local function chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped)    local size=ck[5]-ck[4]+1 -  local flags=sequence.flags or noflags -  local done=false -  local skipmark=flags[1]    local chainlookups=ck[6] +  local done=false    if chainlookups then -    local nofchainlookups=#chainlookups      if size==1 then        local chainlookup=chainlookups[1]        for j=1,#chainlookup do @@ -23765,10 +23907,22 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped)          end        end       else +      local skipmark +      local skipligature +      local skipbase +      local markclass +      if skipped then +        local flags=sequence.flags or noflags +        skipmark=flags[1] +        skipligature=flags[2] +        skipbase=flags[3] +        markclass=sequence.markclass +      end        local i=1        local laststart=start +      local nofchainlookups=#chainlookups         while start do -        if skipped then +        if skipped then             while start do              local char,id=ischar(start,currentfont)              if char then @@ -24155,11 +24309,11 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode)    local checkdisc=sweeptype and getprev(head)    local flags=sequence.flags or noflags    local done=false +  local markclass=sequence.markclass    local skipmark=flags[1]    local skipligature=flags[2]    local skipbase=flags[3] -  local markclass=sequence.markclass -  local skipsome=skipmark~=false or skipligature~=false or skipbase~=false or markclass +  local skipsome=flags[5]    local skipped=false    local startprev,       startnext=getboth(start) @@ -24170,7 +24324,6 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode)      local ck=contexts[k]      local seq=ck[3]      local s=#seq -    local size=1      if s==1 then        local char=ischar(current,currentfont)        if char and not seq[1][char] then @@ -24179,8 +24332,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode)      else        local f=ck[4]        local l=ck[5] -      size=l-f+1 -      if size>1 then +      if l>f then          local discfound           local n=f+1          last=startnext  @@ -24596,6 +24748,469 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode)    end    return head,start,done  end +local function optimized_handle_contextchain(head,start,dataset,sequence,contexts,rlmode) +  local sweepnode=sweepnode +  local sweeptype=sweeptype +  local postreplace +  local prereplace +  local checkdisc +  local diskseen  +  if sweeptype then +    if sweeptype=="replace" then +      postreplace=true +      prereplace=true +    else +      postreplace=sweeptype=="post" +      prereplace=sweeptype=="pre" +    end +    checkdisc=getprev(head) +  end +  local currentfont=currentfont +  local flags=sequence.flags or noflags +  local skipsome=flags[5] +  local skipmark +  local skipligature +  local skipbase +  local markclass +  if skipsome then +    skipmark=flags[1] +    skipligature=flags[2] +    skipbase=flags[3] +    markclass=sequence.markclass +  end +  local skipped   +  local startprev, +     startnext=getboth(start) +  local done    +  for k=1,contexts.n do  +    local current=start +    local last=start +    local ck=contexts[k] +    local seq=ck[3] +    local s=seq.n +    if s==1 then +      local char=ischar(current,currentfont) +      if char and not seq[1][char] then +        goto next +      end +    else +      local f=ck[4] +      local l=ck[5] +      if l>f then +        local discfound  +        local n=f+1 +        last=startnext  +        while n<=l do +          if not last and postreplace then +            last=getnext(sweepnode) +            sweeptype=nil +          end +          if last then +            local char,id=ischar(last,currentfont) +            if char then +              if skipsome then +                local class=classes[char] +                if class==skipmark or (markclass and class=="mark" and not markclass[char]) or class==skipligature or class==skipbase then +                  skipped=true +                  if trace_skips then +                    show_skip(dataset,sequence,char,ck,class) +                  end +                  last=getnext(last) +                elseif seq[n][char] then +                  if n<l then +                    last=getnext(last) +                  end +                  n=n+1 +                else +                  if discfound then +                    notmatchreplace[discfound]=true +                    if notmatchpre[discfound] then +                      goto next +                    end +                  else +                    goto next +                  end +                  break +                end +              else +                if seq[n][char] then +                  if n<l then +                    last=getnext(last) +                  end +                  n=n+1 +                else +                  if discfound then +                    notmatchreplace[discfound]=true +                    if notmatchpre[discfound] then +                      goto next +                    end +                  else +                    goto next +                  end +                  break +                end +              end +            elseif char==false then +              if discfound then +                notmatchreplace[discfound]=true +                if notmatchpre[discfound] then +                  goto next +                end +              else +                goto next +              end +              break +            elseif id==disc_code then +              diskseen=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 +                end +                if n<=l then +                  notmatchpre[last]=true +                end +              else +                notmatchpre[last]=true +              end +              if replace then +                while replace do +                  if seq[n][getchar(replace)] then +                    n=n+1 +                    if n>l then +                      break +                    end +                    replace=getnext(replace) +                  else +                    notmatchreplace[last]=true +                    if notmatchpre[last] then +                      goto next +                    end +                    break +                  end +                end +                if notmatchpre[last] then +                  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) +          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 skipsome then +                    local class=classes[char] +                    if class==skipmark or (markclass and class=="mark" and not markclass[char]) or class==skipligature or class==skipbase then +                      skipped=true +                      if trace_skips then +                        show_skip(dataset,sequence,char,ck,class) +                      end +                      prev=getprev(prev) +                    elseif seq[n][char] then +                      if n>1 then +                        prev=getprev(prev) +                      end +                      n=n-1 +                    else +                      if discfound then +                        notmatchreplace[discfound]=true +                        if notmatchpost[discfound] then +                          goto next +                        end +                      else +                        goto next +                      end +                      break +                    end +                  else +                    if seq[n][char] then +                      if n>1 then +                        prev=getprev(prev) +                      end +                      n=n-1 +                    else +                      if discfound then +                        notmatchreplace[discfound]=true +                        if notmatchpost[discfound] then +                          goto next +                        end +                      else +                        goto next +                      end +                      break +                    end +                  end +                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 +                  diskseen=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 then +                            break +                          else +                            if n<1 then +                              break +                            end +                            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 then +                            break +                          else +                            if n<1 then +                              break +                            end +                            replacetail=getprev(replacetail) +                          end +                        else +                          notmatchreplace[prev]=true +                          if notmatchpost[prev] then +                            goto next +                          end +                          break +                        end +                      end +                    end +                  end +                  prev=getprev(prev) +                elseif id==glue_code and seq[n][32] and isspace(prev,threshold,id) then +                  n=n-1 +                  prev=getprev(prev) +                else +                  goto next +                end +              else +                goto next +              end +            end +          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 skipsome then +                  local class=classes[char] +                  if class==skipmark or (markclass and class=="mark" and not markclass[char]) or class==skipligature or class==skipbase then +                    skipped=true +                    if trace_skips then +                      show_skip(dataset,sequence,char,ck,class) +                    end +                    current=getnext(current)  +                  elseif seq[n][char] then +                    if n<s then  +                      current=getnext(current)  +                    end +                    n=n+1 +                  else +                    if discfound then +                      notmatchreplace[discfound]=true +                      if notmatchpre[discfound] then +                        goto next +                      end +                    else +                      goto next +                    end +                    break +                  end +                else +                  if seq[n][char] then +                    if n<s then  +                      current=getnext(current)  +                    end +                    n=n+1 +                  else +                    if discfound then +                      notmatchreplace[discfound]=true +                      if notmatchpre[discfound] then +                        goto next +                      end +                    else +                      goto next +                    end +                    break +                  end +                end +              elseif char==false then +                if discfound then +                  notmatchreplace[discfound]=true +                  if notmatchpre[discfound] then +                    goto next +                  end +                else +                  goto next +                end +                break +              elseif id==disc_code then +                diskseen=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 +                      end +                      pre=getnext(pre) +                    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 +                      end +                      replace=getnext(replace) +                    else +                      notmatchreplace[current]=true +                      if not notmatchpre[current] then +                        goto next +                      end +                      break +                    end +                  end +                else +                end +                current=getnext(current) +              elseif id==glue_code and seq[n][32] and isspace(current,threshold,id) then +                n=n+1 +                current=getnext(current) +              else +                goto next +              end +            else +              goto next +            end +          end +        else +          goto next +        end +      end +    end +    if trace_contexts then +      chaintrac(head,start,dataset,sequence,rlmode,ck,skipped,true) +    end +    if diskseen or sweepnode then +      head,start,done=chaindisk(head,start,dataset,sequence,rlmode,ck,skipped) +    else +      head,start,done=chainrun(head,start,last,dataset,sequence,rlmode,ck,skipped) +    end +    if done then +      break +    end +    ::next:: +  end +  if diskseen then +    notmatchpre={} +    notmatchpost={} +    notmatchreplace={} +  end +  return head,start,done +end +directives.register("otf.optimizechains",function(v) +  if v then +    report_chain() +    report_chain("using experimental optimized code") +    report_chain() +  end +  local handle=v and optimized_handle_contextchain or handle_contextchain +  handlers.gsub_context=handle +  handlers.gsub_contextchain=handle +  handlers.gsub_reversecontextchain=handle +  handlers.gpos_contextchain=handle +  handlers.gpos_context=handle +end)  handlers.gsub_context=handle_contextchain  handlers.gsub_contextchain=handle_contextchain  handlers.gsub_reversecontextchain=handle_contextchain @@ -24615,22 +25230,14 @@ chainprocs.gsub_reversecontextchain=chained_contextchain  chainprocs.gpos_contextchain=chained_contextchain  chainprocs.gpos_context=chained_contextchain  local missing=setmetatableindex("table") +local logwarning=report_process +local resolved={}   local function logprocess(...)    if trace_steps then      registermessage(...)    end    report_process(...)  end -local logwarning=report_process -local function report_missing_coverage(dataset,sequence) -  local t=missing[currentfont] -  if not t[sequence] then -    t[sequence]=true -    logwarning("missing coverage for feature %a, lookup %a, type %a, font %a, name %a", -      dataset[4],sequence.name,sequence.type,currentfont,tfmdata.properties.fullname) -  end -end -local resolved={}  local sequencelists=setmetatableindex(function(t,font)    local sequences=fontdata[font].resources.sequences    if not sequences or not next(sequences) then @@ -24857,12 +25464,10 @@ local function testrun(disc,t_run,c_run,...)      end      local d_post=t_run(post,next,...)      local d_replace=t_run(replace,next,...) -    if (d_post and d_post>0) or (d_replace and d_replace>0) then -      local d=d_replace or d_post -      if d_post and d<d_post then -        d=d_post -      end -      local head,tail=getnext(disc),disc +    if d_post>0 or d_replace>0 then +      local d=d_replace>d_post and d_replace or d_post +      local head=getnext(disc) +      local tail=head        for i=1,d do          tail=getnext(tail)          if getid(tail)==disc_code then @@ -25049,6 +25654,7 @@ local function t_run_single(start,stop,font,attr,lookupcache)        break      end    end +  return 0  end  local function k_run_single(sub,injection,last,font,attr,lookupcache,step,dataset,sequence,rlmode,handler)    local a  @@ -25093,20 +25699,16 @@ local function c_run_multiple(head,font,attr,steps,nofsteps,dataset,sequence,rlm          for i=1,nofsteps do            local step=steps[i]            local lookupcache=step.coverage -          if lookupcache then -            local lookupmatch=lookupcache[char] -            if lookupmatch then -              local ok -              head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) -              if ok then -                done=true -                break -              elseif not start then -                break -              end +          local lookupmatch=lookupcache[char] +          if lookupmatch then +            local ok +            head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) +            if ok then +              done=true +              break +            elseif not start then +              break              end -          else -            report_missing_coverage(dataset,sequence)            end          end          if start then @@ -25139,59 +25741,55 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps)          for i=1,nofsteps do            local step=steps[i]            local lookupcache=step.coverage -          if lookupcache then -            local lookupmatch=lookupcache[char] -            if lookupmatch then -              local s=startnext -              local ss=nil -              local sstop=s==stop +          local lookupmatch=lookupcache[char] +          if lookupmatch then +            local s=startnext +            local ss=nil +            local sstop=s==stop +            if not s then +              s=ss +              ss=nil +            end +            while getid(s)==disc_code do +              ss=getnext(s) +              s=getfield(s,"replace")                if not s then                  s=ss                  ss=nil                end -              while getid(s)==disc_code do -                ss=getnext(s) -                s=getfield(s,"replace") +            end +            local l=nil +            local d=0 +            while s do +              local lg=lookupmatch[getchar(s)] +              if lg then +                if sstop then +                  d=1 +                elseif d>0 then +                  d=d+1 +                end +                l=lg +                s=getnext(s) +                sstop=s==stop                  if not s then                    s=ss                    ss=nil                  end -              end -              local l=nil -              local d=0 -              while s do -                local lg=lookupmatch[getchar(s)] -                if lg then -                  if sstop then -                    d=1 -                  elseif d>0 then -                    d=d+1 -                  end -                  l=lg -                  s=getnext(s) -                  sstop=s==stop +                while getid(s)==disc_code do +                  ss=getnext(s) +                  s=getfield(s,"replace")                    if not s then                      s=ss                      ss=nil                    end -                  while getid(s)==disc_code do -                    ss=getnext(s) -                    s=getfield(s,"replace") -                    if not s then -                      s=ss -                      ss=nil -                    end -                  end -                else -                  break                  end -              end -              if l and l.ligature then -                lastd=d +              else +                break                end              end -          else -            report_missing_coverage(dataset,sequence) +            if l and l.ligature then +              lastd=d +            end            end          end        else @@ -25204,6 +25802,7 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps)        break      end    end +  return 0  end  local function k_run_multiple(sub,injection,last,font,attr,steps,nofsteps,dataset,sequence,rlmode,handler)    local a  @@ -25220,16 +25819,12 @@ local function k_run_multiple(sub,injection,last,font,attr,steps,nofsteps,datase          for i=1,nofsteps do            local step=steps[i]            local lookupcache=step.coverage -          if lookupcache then -            local lookupmatch=lookupcache[char] -            if lookupmatch then -              local h,d,ok=handler(head,n,dataset,sequence,lookupmatch,rlmode,step,i,injection) -              if ok then -                return true -              end +          local lookupmatch=lookupcache[char] +          if lookupmatch then +            local h,d,ok=handler(head,n,dataset,sequence,lookupmatch,rlmode,step,i,injection) +            if ok then +              return true              end -          else -            report_missing_coverage(dataset,sequence)            end          end        end @@ -25278,6 +25873,12 @@ otf.helpers.pardirstate=pardirstate  do    local fastdisc=true    directives.register("otf.fastdisc",function(v) fastdisc=v end) +  local otfdataset=nil  +  local getfastdics=function(t,k) +    local v=usesfont(k,currentfont) +    t[k]=v +    return v +  end    function otf.featuresprocessor(head,font,attr,direction,n)      local sequences=sequencelists[font]       if not sequencelists then @@ -25295,25 +25896,24 @@ do        threshold,        factor=getthreshold(font)        checkmarks=tfmdata.properties.checkmarks +      if not otfdataset then +        otfdataset=otf.dataset +      end +      discs=fastdisc and n and n>1 and setmetatableindex(getfastdisc)       elseif currentfont~=font then        report_warning("nested call with a different font, level %s, quitting",nesting)        nesting=nesting-1        return head,false      end -    head=tonut(head) +    local head=tonut(head)      if trace_steps then        checkstep(head)      end      local initialrl=direction=="TRT" and -1 or 0      local done=false -    local datasets=otf.dataset(tfmdata,font,attr) +    local datasets=otfdataset(tfmdata,font,attr)      local dirstack={}       sweephead={} -    local discs=fastdisc and n and n>1 and setmetatableindex(function(t,k) -      local v=usesfont(k,font) -      t[k]=v -      return v -    end)      for s=1,#datasets do        local dataset=datasets[s]        local attribute=dataset[2] @@ -25322,7 +25922,7 @@ do        local topstack=0        local typ=sequence.type        local gpossing=typ=="gpos_single" or typ=="gpos_pair"  -      local handler=handlers[typ] +      local handler=handlers[typ]         local steps=sequence.steps        local nofsteps=sequence.nofsteps        if not steps then @@ -25350,18 +25950,14 @@ do                  for i=m[1],m[2] do                    local step=steps[i]                    local lookupcache=step.coverage -                  if lookupcache then -                    local lookupmatch=lookupcache[char] -                    if lookupmatch then -                      local ok -                      head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) -                      if ok then -                        done=true -                        break -                      end +                  local lookupmatch=lookupcache[char] +                  if lookupmatch then +                    local ok +                    head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) +                    if ok then +                      done=true +                      break                      end -                  else -                    report_missing_coverage(dataset,sequence)                    end                  end                  if start then @@ -25383,66 +25979,62 @@ do          if nofsteps==1 then             local step=steps[1]            local lookupcache=step.coverage -          if not lookupcache then -            report_missing_coverage(dataset,sequence) -          else -            while start do -              local char,id=ischar(start,font) -              if char then -                local lookupmatch=lookupcache[char] -                if lookupmatch then -                  local a  -                  if attr then -                    if getattr(start,0)==attr and (not attribute or getprop(start,a_state)==attribute) then -                      a=true -                    end -                  elseif not attribute or getprop(start,a_state)==attribute then +          while start do +            local char,id=ischar(start,font) +            if char then +              local lookupmatch=lookupcache[char] +              if lookupmatch then +                local a  +                if attr then +                  if getattr(start,0)==attr and (not attribute or getprop(start,a_state)==attribute) then                      a=true                    end -                  if a then -                    local ok -                    head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1) -                    if ok then -                      done=true -                    end -                    if start then -                      start=getnext(start) -                    end -                  else -                    start=getnext(start) -                  end -                else -                  start=getnext(start) +                elseif not attribute or getprop(start,a_state)==attribute then +                  a=true                  end -              elseif char==false then -                start=getnext(start) -              elseif id==glue_code then -                start=getnext(start) -              elseif id==disc_code then -                if not discs or discs[start]==true then +                if a then                    local ok -                  if gpossing then -                    start,ok=kernrun(start,k_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) -                  elseif typ=="gsub_ligature" then -                    start,ok=testrun(start,t_run_single,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) -                  else -                    start,ok=comprun(start,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) -                  end +                  head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,1)                    if ok then                      done=true                    end +                  if start then +                    start=getnext(start) +                  end                  else                    start=getnext(start)                  end -              elseif id==math_code then -                start=getnext(end_of_math(start)) -              elseif id==dir_code then -                start,topstack,rlmode=txtdirstate(start,dirstack,topstack,rlparmode) -              elseif id==localpar_code then -                start,rlparmode,rlmode=pardirstate(start)                else                  start=getnext(start)                end +            elseif char==false then +              start=getnext(start) +            elseif id==glue_code then +              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,handler) +                elseif typ=="gsub_ligature" then +                  start,ok=testrun(start,t_run_single,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) +                else +                  start,ok=comprun(start,c_run_single,font,attr,lookupcache,step,dataset,sequence,rlmode,handler) +                end +                if ok then +                  done=true +                end +              else +                start=getnext(start) +              end +            elseif id==math_code then +              start=getnext(end_of_math(start)) +            elseif id==dir_code then +              start,topstack,rlmode=txtdirstate(start,dirstack,topstack,rlparmode) +            elseif id==localpar_code then +              start,rlparmode,rlmode=pardirstate(start) +            else +              start=getnext(start)              end            end          else @@ -25464,20 +26056,16 @@ do                    for i=m[1],m[2] do                      local step=steps[i]                      local lookupcache=step.coverage -                    if lookupcache then -                      local lookupmatch=lookupcache[char] -                      if lookupmatch then -                        local ok -                        head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) -                        if ok then -                          done=true -                          break -                        elseif not start then -                          break -                        end +                    local lookupmatch=lookupcache[char] +                    if lookupmatch then +                      local ok +                      head,start,ok=handler(head,start,dataset,sequence,lookupmatch,rlmode,step,i) +                      if ok then +                        done=true +                        break +                      elseif not start then +                        break                        end -                    else -                      report_missing_coverage(dataset,sequence)                      end                    end                    if start then @@ -25659,7 +26247,7 @@ local function spaceinitializer(tfmdata,value)                local format=step.format                if rules then                elseif coverage then -                local single=format==gpos_single +                local single=format=="gpos_single"                  local kerns=coverage[32]                  if kerns then                    for k,v in next,kerns do @@ -25684,12 +26272,13 @@ local function spaceinitializer(tfmdata,value)                        left[k]=kern[3]                      else                        local one=kern[1] -                      if one then +                      if one and one~=true then                          left[k]=one[3]                        end                      end                    end                  end +              else                end              end              last=i @@ -28530,8 +29119,6 @@ local function addfeature(data,feature,specifications)      if rules then        local rulehash={}        local rulesize=0 -      local sequence={} -      local nofsequences=0        local lookuptype=types[featuretype]        for nofrules=1,#rules do          local rule=rules[nofrules] @@ -28617,8 +29204,10 @@ local function addfeature(data,feature,specifications)                coverage[unic]=rulehash               end            end +          sequence.n=nofsequences          end        end +      rulehash.n=rulesize      end      return coverage    end  | 
