diff options
| author | Hans Hagen <pragma@wxs.nl> | 2021-08-19 20:32:31 +0200 | 
|---|---|---|
| committer | Context Git Mirror Bot <phg@phi-gamma.net> | 2021-08-19 20:32:31 +0200 | 
| commit | af60125ab3fa9e482720f0f46c2143fa08512113 (patch) | |
| tree | 3e85c8a8a5979ebd05b891f8ecfb93d1b69ac41b /tex | |
| parent | d3d93bc4f0d21a259fdafee5ba1a744999474c28 (diff) | |
| download | context-af60125ab3fa9e482720f0f46c2143fa08512113.tar.gz | |
2021-08-19 19:43:00
Diffstat (limited to 'tex')
102 files changed, 3212 insertions, 1898 deletions
diff --git a/tex/context/base/mkii/cont-new.mkii b/tex/context/base/mkii/cont-new.mkii index aee36ce61..25e2ed4d7 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{2021.08.10 12:37} +\newcontextversion{2021.08.19 19:40}  %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 ec8a50d33..7d3289b97 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{2021.08.10 12:37} +\edef\contextversion{2021.08.19 19:40}  %D For those who want to use this: diff --git a/tex/context/base/mkii/mult-ro.mkii b/tex/context/base/mkii/mult-ro.mkii index 50a9ea08f..9aede3730 100644 --- a/tex/context/base/mkii/mult-ro.mkii +++ b/tex/context/base/mkii/mult-ro.mkii @@ -151,6 +151,7 @@  \setinterfacevariable{commands}{comenzi}  \setinterfacevariable{comment}{comentariu}  \setinterfacevariable{component}{componenta} +\setinterfacevariable{compress}{compress}  \setinterfacevariable{compressseparator}{compressseparator}  \setinterfacevariable{compressstopper}{compressstopper}  \setinterfacevariable{concept}{concept} diff --git a/tex/context/base/mkiv/back-exp.lua b/tex/context/base/mkiv/back-exp.lua index 51c32ec1d..4f29e09d6 100644 --- a/tex/context/base/mkiv/back-exp.lua +++ b/tex/context/base/mkiv/back-exp.lua @@ -325,9 +325,9 @@ local styletemplate = [[      local numbertoallign = {          [0] = "justify", ["0"] = "justify", [variables.normal    ] = "justify", -        [1] = "right",   ["1"] = "right",   [variables.flushright] = "right", -        [2] = "center",  ["2"] = "center",  [variables.middle    ] = "center", -        [3] = "left",    ["3"] = "left",    [variables.flushleft ] = "left", +              "right",   ["1"] = "right",   [variables.flushright] = "right", +              "center",  ["2"] = "center",  [variables.middle    ] = "center", +              "left",    ["3"] = "left",    [variables.flushleft ] = "left",      }      function wrapups.allusedstyles(filename) diff --git a/tex/context/base/mkiv/buff-ini.lua b/tex/context/base/mkiv/buff-ini.lua index 8a8ec67c7..6c844342a 100644 --- a/tex/context/base/mkiv/buff-ini.lua +++ b/tex/context/base/mkiv/buff-ini.lua @@ -121,6 +121,11 @@ local function exists(name)      return cache[name]  end +local function getcontent(name) +    local buffer = name and cache[name] +    return buffer and buffer.data or "" +end +  local function empty(name)      if find(getcontent(name),"%S") then          return false @@ -129,11 +134,6 @@ local function empty(name)      end  end -local function getcontent(name) -    local buffer = name and cache[name] -    return buffer and buffer.data or "" -end -  local function getlines(name)      local buffer = name and cache[name]      return buffer and splitlines(buffer.data) diff --git a/tex/context/base/mkiv/cont-new.mkiv b/tex/context/base/mkiv/cont-new.mkiv index 61325b615..da7aa9dc0 100644 --- a/tex/context/base/mkiv/cont-new.mkiv +++ b/tex/context/base/mkiv/cont-new.mkiv @@ -13,7 +13,7 @@  % \normalend % uncomment this to get the real base runtime -\newcontextversion{2021.08.10 12:37} +\newcontextversion{2021.08.19 19:40}  %D This file is loaded at runtime, thereby providing an excellent place for hacks,  %D patches, extensions and new features. There can be local overloads in cont-loc diff --git a/tex/context/base/mkiv/context.mkiv b/tex/context/base/mkiv/context.mkiv index 74e3cea56..d837240b4 100644 --- a/tex/context/base/mkiv/context.mkiv +++ b/tex/context/base/mkiv/context.mkiv @@ -45,7 +45,7 @@  %D {YYYY.MM.DD HH:MM} format.  \edef\contextformat {\jobname} -\edef\contextversion{2021.08.10 12:37} +\edef\contextversion{2021.08.19 19:40}  %D Kind of special: diff --git a/tex/context/base/mkiv/data-tmp.lua b/tex/context/base/mkiv/data-tmp.lua index 9a8a586cd..1948f1ea5 100644 --- a/tex/context/base/mkiv/data-tmp.lua +++ b/tex/context/base/mkiv/data-tmp.lua @@ -74,7 +74,7 @@ local usedreadables = { }  local compilelua    = luautilities.compile  local luasuffixes   = luautilities.suffixes -caches.base         = caches.base or "luatex-cache"  -- can be local +caches.base         = caches.base or (LUATEXENGINE and LUATEXENGINE .. "-cache") or "luatex-cache"  -- can be local  caches.more         = caches.more or "context"       -- can be local  caches.defaults     = { "TMPDIR", "TEMPDIR", "TMP", "TEMP", "HOME", "HOMEPATH" } diff --git a/tex/context/base/mkiv/font-cft.lua b/tex/context/base/mkiv/font-cft.lua index cf6c232a6..267c63f7a 100644 --- a/tex/context/base/mkiv/font-cft.lua +++ b/tex/context/base/mkiv/font-cft.lua @@ -439,7 +439,7 @@ do          },          colorschemes = {              default = { -                [1] = { t_string }, +                { t_string },              }          },          files = { diff --git a/tex/context/base/mkiv/font-mis.lua b/tex/context/base/mkiv/font-mis.lua index a72f1bf42..9bb8d4cc1 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.118 +    otf.version = otf.version or 3.119      otf.cache   = otf.cache   or containers.define("fonts", "otl", otf.version, true)      function fonts.helpers.getfeatures(name,save) diff --git a/tex/context/base/mkiv/font-ocm.lua b/tex/context/base/mkiv/font-ocm.lua index ef0b02a14..e54799b23 100644 --- a/tex/context/base/mkiv/font-ocm.lua +++ b/tex/context/base/mkiv/font-ocm.lua @@ -40,16 +40,16 @@ callback.register("provide_charproc_data",function(action,f,...)  end)  local defaults = { -    [1] = function() return 0, 0 end, -    [2] = function() return 0, 0 end, -    [3] = function() return 0.001, "" end, +    function() return 0, 0 end, +    function() return 0, 0 end, +    function() return 0.001, "" end,  }  local function registeractions(t)      return { -        [1] = t.preroll or defaults[1], -        [2] = t.collect or defaults[2], -        [3] = t.wrapup  or defaults[3], +        t.preroll or defaults[1], +        t.collect or defaults[2], +        t.wrapup  or defaults[3],      }  end diff --git a/tex/context/base/mkiv/font-osd.lua b/tex/context/base/mkiv/font-osd.lua index a9ea878c0..b4e753182 100644 --- a/tex/context/base/mkiv/font-osd.lua +++ b/tex/context/base/mkiv/font-osd.lua @@ -630,7 +630,8 @@ local function initializedevanagi(tfmdata)                                                              local h = coverage[k]                                                              if h then                                                                  for k, v in next, h do -                                                                    found = v and v.ligature +                                                                 -- found = v and v.ligature +                                                                    found = v and (tonumber(v) or v.ligature)                                                                      if found then                                                                          pre_base_reordering_consonants[found] = true                                                                          break @@ -647,7 +648,8 @@ local function initializedevanagi(tfmdata)                                          end                                      else                                          for k, v in next, r do -                                            found = v and v.ligature +                                         -- found = v and v.ligature +                                            found = v and (tonumber(v) or v.ligature)                                              if found then                                                  pre_base_reordering_consonants[found] = true                                                  break @@ -696,7 +698,8 @@ local function initializedevanagi(tfmdata)                                                                          for k, v in next, halant do                                                                              local h = r[k]                                                                              if h then -                                                                                reph = h.ligature or false +                                                                             -- reph = h.ligature or false +                                                                                reph = tonumber(h) or h.ligature or false                                                                                  break                                                                              end                                                                          end @@ -713,7 +716,8 @@ local function initializedevanagi(tfmdata)                                                  for k, v in next, halant do                                                      local h = r[k]                                                      if h then -                                                        reph = h.ligature or false +                                                     -- reph = h.ligature or false +                                                        reph = tonumber(h) or h.ligature or false                                                          break                                                      end                                                  end @@ -757,7 +761,8 @@ local function initializedevanagi(tfmdata)                                                                  local h = coverage[k]                                                                  if h then                                                                      for k, v in next, h do -                                                                        found = v and v.ligature +                                                                     -- found = v and v.ligature +                                                                        found = v and (tonumber(v) or v.ligature)                                                                          if found then                                                                              pre_base_reordering_consonants[found] = true                                                                              break @@ -774,7 +779,8 @@ local function initializedevanagi(tfmdata)                                              end                                          else                                              for k, v in next, h do -                                                found = v and v.ligature +                                             -- found = v and v.ligature +                                                found = v and (tonumber(v) or v.ligature)                                                  if found then                                                      pre_base_reordering_consonants[found] = true                                                      break diff --git a/tex/context/base/mkiv/font-otl.lua b/tex/context/base/mkiv/font-otl.lua index 74f209b0a..ae2dda4f3 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.118 -- beware: also sync font-mis.lua and in mtx-fonts +otf.version               = 3.119 -- 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.pngcache              = containers.define("fonts", "png", otf.version, true) @@ -218,6 +218,9 @@ function otf.load(filename,sub,instance)              if cleanup == 0 then                  checkmemory(used,threshold,tracememory)              end +            if context then +                otfreaders.condense(data) +            end              otfreaders.pack(data)              report_otf("loading done")              report_otf("saving %a in cache",filename) diff --git a/tex/context/base/mkiv/font-oto.lua b/tex/context/base/mkiv/font-oto.lua index 6f5f2fb41..0f667bcc2 100644 --- a/tex/context/base/mkiv/font-oto.lua +++ b/tex/context/base/mkiv/font-oto.lua @@ -170,7 +170,7 @@ end  local function makefake(tfmdata,name,present)      local private   = getprivate(tfmdata)      local character = { intermediate = true, ligatures = { } } -    resources.unicodes[name] = private +    tfmdata.resources.unicodes[name] = private      tfmdata.characters[private] = character      tfmdata.descriptions[private] = { name = name }      present[name] = private @@ -178,43 +178,55 @@ local function makefake(tfmdata,name,present)  end  local function make_1(present,tree,name) -    for k, v in next, tree do -        if k == "ligature" then -            present[name] = v +    if tonumber(tree) then +        present[name] = v +    else +        for k, v in next, tree do +            if k == "ligature" then +                present[name] = v +            else +                make_1(present,v,name .. "_" .. k) +            end +        end +    end +end + +local function make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,v) +    local character = characters[preceding] +    if not character then +        if trace_baseinit then +            report_prepare("weird ligature in lookup %a, current %C, preceding %C",sequence.name,v,preceding) +        end +        character = makefake(tfmdata,name,present) +    end +    local ligatures = character.ligatures +    if ligatures then +        ligatures[unicode] = { char = v } +    else +        character.ligatures = { [unicode] = { char = v } } +    end +    if done then +        local d = done[name] +        if not d then +            done[name] = { "dummy", v }          else -            make_1(present,v,name .. "_" .. k) +            d[#d+1] = v          end      end  end  local function make_2(present,tfmdata,characters,tree,name,preceding,unicode,done) -    for k, v in next, tree do -        if k == "ligature" then -            local character = characters[preceding] -            if not character then -                if trace_baseinit then -                    report_prepare("weird ligature in lookup %a, current %C, preceding %C",sequence.name,v,preceding) -                end -                character = makefake(tfmdata,name,present) -            end -            local ligatures = character.ligatures -            if ligatures then -                ligatures[unicode] = { char = v } +    if tonumber(tree) then +        make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,tree) +    else +        for k, v in next, tree do +            if k == "ligature" then +                make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,v)              else -                character.ligatures = { [unicode] = { char = v } } +                local code = present[name] or unicode +                local name = name .. "_" .. k +                make_2(present,tfmdata,characters,v,name,code,k,done)              end -            if done then -                local d = done[name] -                if not d then -                    done[name] = { "dummy", v } -                else -                    d[#d+1] = v -                end -            end -        else -            local code = present[name] or unicode -            local name = name .. "_" .. k -            make_2(present,tfmdata,characters,v,name,code,k,done)          end      end  end diff --git a/tex/context/base/mkiv/font-otr.lua b/tex/context/base/mkiv/font-otr.lua index 2f8934508..3b5edebc4 100644 --- a/tex/context/base/mkiv/font-otr.lua +++ b/tex/context/base/mkiv/font-otr.lua @@ -682,15 +682,15 @@ local weights = {  }  local widths = { -    [1] = "ultracondensed", -    [2] = "extracondensed", -    [3] = "condensed", -    [4] = "semicondensed", -    [5] = "normal", -    [6] = "semiexpanded", -    [7] = "expanded", -    [8] = "extraexpanded", -    [9] = "ultraexpanded", +    "ultracondensed", +    "extracondensed", +    "condensed", +    "semicondensed", +    "normal", +    "semiexpanded", +    "expanded", +    "extraexpanded", +    "ultraexpanded",  }  setmetatableindex(weights, function(t,k) @@ -703,31 +703,31 @@ setmetatableindex(widths,function(t,k)      return "normal"  end) -local panoseweights = { -    [ 0] = "normal", -    [ 1] = "normal", -    [ 2] = "verylight", -    [ 3] = "light", -    [ 4] = "thin", -    [ 5] = "book", -    [ 6] = "medium", -    [ 7] = "demi", -    [ 8] = "bold", -    [ 9] = "heavy", -    [10] = "black", +local panoseweights = { [0] = +    "normal", +    "normal", +    "verylight", +    "light", +    "thin", +    "book", +    "medium", +    "demi", +    "bold", +    "heavy", +    "black",  } -local panosewidths = { -    [ 0] = "normal", -    [ 1] = "normal", -    [ 2] = "normal", -    [ 3] = "normal", -    [ 4] = "normal", -    [ 5] = "expanded", -    [ 6] = "condensed", -    [ 7] = "veryexpanded", -    [ 8] = "verycondensed", -    [ 9] = "monospaced", +local panosewidths = { [0] = +    "normal", +    "normal", +    "normal", +    "normal", +    "normal", +    "expanded", +    "condensed", +    "veryexpanded", +    "verycondensed", +    "monospaced",  }  -- We implement a reader per table. @@ -2522,6 +2522,10 @@ function readers.compact(fontdata)      report("the %a helper is not yet implemented","compact")  end +function readers.condense(fontdata) +    report("the %a helper is not yet implemented","condense") +end +  -- plug in  local extenders = { } diff --git a/tex/context/base/mkiv/font-ots.lua b/tex/context/base/mkiv/font-ots.lua index 30d79c407..8e8be6f95 100644 --- a/tex/context/base/mkiv/font-ots.lua +++ b/tex/context/base/mkiv/font-ots.lua @@ -763,7 +763,7 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip          while current do              local char = ischar(current,currentfont)              if char then -                local lg = ligature[char] +                local lg = not tonumber(ligature) and ligature[char]                  if lg then                      stop     = current                      ligature = lg @@ -776,14 +776,14 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip              end          end          if stop then -            local lig = ligature.ligature -            if lig then +            local ligature = tonumber(ligature) or ligature.ligature +            if ligature then                  if trace_ligatures then                      local stopchar = getchar(stop) -                    head, start = markstoligature(head,start,stop,lig) +                    head, start = markstoligature(head,start,stop,ligature)                      logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(getchar(start)))                  else -                    head, start = markstoligature(head,start,stop,lig) +                    head, start = markstoligature(head,start,stop,ligature)                  end                  return head, start, true, false              else @@ -799,7 +799,7 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip                  if skiphash and skiphash[char] then                      current = getnext(current)                  else -                    local lg = ligature[char] +                    local lg = not tonumber(ligature) and ligature[char]                      if lg then                          if marks[char] then                              hasmarks = true @@ -833,20 +833,20 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip              local match              if replace then                  local char = ischar(replace,currentfont) -                if char and ligature[char] then +                if char and (not tonumber(ligature) and ligature[char]) then                      match = true                  end              end              if not match and pre then                  local char = ischar(pre,currentfont) -                if char and ligature[char] then +                if char and (not tonumber(ligature) and ligature[char]) then                      match = true                  end              end              if not match and not pre or not replace then                  local n    = getnext(discfound)                  local char = ischar(n,currentfont) -                if char and ligature[char] then +                if char and (not tonumber(ligature) and ligature[char]) then                      match = true                  end              end @@ -890,24 +890,26 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip                  return head, start, true, true              end          end -        local lig = ligature.ligature -        if lig then +        local ligature = tonumber(ligature) or ligature.ligature +        if ligature then              if stop then                  if trace_ligatures then                      local stopchar = getchar(stop) -                 -- head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,discfound,hasmarks) -                    head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,false,hasmarks) -                    logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(lig)) +                 -- head, start = toligature(head,start,stop,ligature,dataset,sequence,skiphash,discfound,hasmarks) +                    head, start = toligature(head,start,stop,ligature,dataset,sequence,skiphash,false,hasmarks) +                    logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(ligature)) +                 -- we can have a rare case of multiple disc in a lig but that makes no sense language wise but if really +                 -- needed we could backtrack if we're in a disc node                  else -                 -- head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,discfound,hasmarks) -                    head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,false,hasmarks) +                 -- head, start = toligature(head,start,stop,ligature,dataset,sequence,skiphash,discfound,hasmarks) +                    head, start = toligature(head,start,stop,ligature,dataset,sequence,skiphash,false,hasmarks)                  end              else                  -- weird but happens (in some arabic font)                  resetinjection(start) -                setchar(start,lig) +                setchar(start,ligature)                  if trace_ligatures then -                    logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(dataset,sequence),gref(startchar),gref(lig)) +                    logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(dataset,sequence),gref(startchar),gref(ligature))                  end              end              return head, start, true, false @@ -1484,7 +1486,7 @@ function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup                              current = getnext(current)                          -- end                      else -                        local lg = ligatures[schar] +                        local lg = not tonumber(ligatures) and ligatures[schar]                          if lg then                              ligatures       = lg                              last            = current @@ -1503,7 +1505,7 @@ function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup                      end                  end              end -            local ligature = ligatures.ligature +            local ligature = tonumber(ligatures) or ligatures.ligature              if ligature then                  if chainindex then                      stop = last @@ -1978,7 +1980,9 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck)                  local chainproc = chainprocs[chainkind]                  if chainproc then                      local ok -                    head, start, ok = chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash) +                    -- HH: chainindex 1 added here (for KAI to check too), there are weird ligatures e.g. +                    -- char + mark -> char where mark has to disappear +                    head, start, ok = chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash,1)                      if ok then                          done = true                      end @@ -2471,6 +2475,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s          local ck  = contexts[k]          local seq = ck[3]          local f   = ck[4] -- first current +local last    = start          if not startchar or not seq[f][startchar] then              -- report("no hit in %a at %i of %i contexts",sequence.type,k,nofcontexts)              goto next @@ -2481,7 +2486,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s          else              local l       = ck[5] -- last current              local current = start -            local last    = start +--             local last    = start              -- current match @@ -3409,7 +3414,7 @@ local function t_run_single(start,stop,font,attr,lookupcache)                      while s do                          local char = ischar(s,font)                          if char then -                            local lg = lookupmatch[char] +                            local lg = not tonumber(lookupmatch) and lookupmatch[char]                              if lg then                                  if sstop then                                      d = 1 @@ -3439,7 +3444,7 @@ local function t_run_single(start,stop,font,attr,lookupcache)                              break                          end                      end -                    if l and l.ligature then -- so we test for ligature +                    if l and (tonumber(l) or l.ligature) then -- so we test for ligature                          lastd = d                      end                      -- why not: if not l then break elseif l.ligature then return d end @@ -3580,7 +3585,7 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps)                          while s do                              local char = ischar(s)                              if char then -                                local lg = lookupmatch[char] +                                local lg = not tonumber(lookupmatch) and lookupmatch[char]                                  if lg then                                      if sstop then                                          d = 1 @@ -3610,7 +3615,7 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps)                                  break                              end                          end -                        if l and l.ligature then +                        if l and (tonumber(l) or l.ligature) then                              lastd = d                          end                      end diff --git a/tex/context/base/mkiv/font-oup.lua b/tex/context/base/mkiv/font-oup.lua index 4e70bf936..4c3d562c2 100644 --- a/tex/context/base/mkiv/font-oup.lua +++ b/tex/context/base/mkiv/font-oup.lua @@ -44,6 +44,9 @@ directives.register("otf.checksofthyphen",function(v)      check_soft_hyphen = v -- only for testing  end) +-- After (!) the unicodes have been resolved we compact ligature tables so before that happens +-- we don't need to check for numbers. +  local function replaced(list,index,replacement)      if type(list) == "number" then          return replacement @@ -1043,6 +1046,10 @@ function readers.getcomponents(fontdata) -- handy for resolving ligatures when n                          local function traverse(p,k,v)                              if k == "ligature" then                                  collected[v] = { unpack(l) } +                            elseif tonumber(v) then +                                insert(l,k) +                                collected[v] = { unpack(l) } +                                remove(l)                              else                                  insert(l,k)                                  for k, vv in next, v do @@ -1230,7 +1237,6 @@ local function tabstr_flat(t)  end  local function tabstr_mixed(t) -- indexed -    local s = { }      local n = #t      if n == 0 then          return "" @@ -1244,6 +1250,7 @@ local function tabstr_mixed(t) -- indexed              return tostring(k) -- number or string          end      else +        local s = { }          for i=1,n do              local k = t[i]              if k == true then @@ -1353,6 +1360,21 @@ function readers.pack(data)              end          end +     -- local function pack_indexed(v) -- less code +     --     local tag = concat(v," ") +     --     local ht = h[tag] +     --     if ht then +     --         c[ht] = c[ht] + 1 +     --     else +     --         ht = nt + 1 +     --         t[ht] = v +     --         c[ht] = 1 +     --         h[tag] = ht +     --         nt = ht +     --     end +     --     return ht +     -- end +          local function pack_mixed(v)              local tag = tabstr_mixed(v)              local ht = h[tag] @@ -2801,6 +2823,91 @@ function readers.compact(data)      end  end +if CONTEXTLMTXMODE and CONTEXTLMTXMODE > 0 then + +    local done = 0 + +    local function condense_1(k,v,t) +        if type(v) == "table" then +            local u = false +            local l = false +            for k, v in next, v do +                if k == "ligature" then +                    l = v +                    if u then +                        break +                    end +                elseif u then +                    break +                else +                    u = true +                end +            end +            if l and not u then +                t[k] = l +                done = done + 1 +            end +            if u then +                for k, vv in next, v do +                    if k ~= "ligature" then +                        condense_1(k,vv,v) +                    end +                end +            end +        end +    end + +    local function condensesteps_1(lookup) +        done = 0 +        if lookup.type == "gsub_ligature" then +            local steps = lookup.steps +            if steps then +                for i=1,#steps do +                    local step     = steps[i] +                    local coverage = step.coverage +                    if coverage then +                        for k, v in next, coverage do +                            if condense_1(k,v,coverage) then +                                coverage[k] = v.ligature +                                done = done + 1 +                            end +                        end +                    end +                end +            end +        end +        return done +    end + +    function readers.condense(data) +        if not data or data.condensed then +            return +        else +            data.condensed = true +        end +        local resources = data.resources +        local condensed = 0 +        local function condense(what) +            local lookups = resources[what] +            if lookups then +                for i=1,#lookups do +                    condensed = condensed + condensesteps_1(lookups[i]) +                end +            elseif trace_optimizations then +                report_optimizations("no lookups in %a",what) +            end +        end +        condense("sequences") +        condense("sublookups") +        if trace_optimizations then +            if condensed > 0 then +                report_optimizations("%i ligatures condensed",condensed) +            end +        end +    end + +end +  local function mergesteps(t,k)      if k == "merged" then          local merged = { } diff --git a/tex/context/base/mkiv/font-ttf.lua b/tex/context/base/mkiv/font-ttf.lua index d8abed329..fc987d74c 100644 --- a/tex/context/base/mkiv/font-ttf.lua +++ b/tex/context/base/mkiv/font-ttf.lua @@ -269,6 +269,16 @@ local function applyaxis(glyph,shape,deltas,dowidth)                              else                                  local n1 = dpoints[d1]                                  local n3 = dpoints[d3] +                                -- Some day I need to figure out these extra points but +                                -- I'll wait till the standard is more clear and fonts +                                -- become better (ntg-context: fraunces.ttf > abcdef). +                                if n1 > nofpoints then +                                    n1 = nofpoints +                                end +                                if n3 > nofpoints then +                                    n3 = nofpoints +                                end +                                --                                  local p1 = points[n1]                                  local p3 = points[n3]                                  local p1x = p1[1] diff --git a/tex/context/base/mkiv/good-gen.lua b/tex/context/base/mkiv/good-gen.lua index cee6b3172..1747727c1 100644 --- a/tex/context/base/mkiv/good-gen.lua +++ b/tex/context/base/mkiv/good-gen.lua @@ -10,7 +10,7 @@ if not modules then modules = { } end modules ['good-gen'] = {  local type, next = type, next  local lower = string.lower - +local filesuffix, replacesuffix = file.suffix, file.replacesuffix  local fonts = fonts  ----- trace_goodies  = false  trackers.register("fonts.goodies", function(v) trace_goodies = v end) @@ -134,6 +134,16 @@ function fontgoodies.filenames.resolve(name)                  return fn              end          end +    elseif filesuffix(name) == "any" then +        -- This is a bit weird place but it's a kind of fallback option in case +        -- we can't resolve due to a name conflict. +        local sequence = fonts.readers.sequence +        for i=1,#sequence do +            local fn = replacesuffix(name,sequence[i]) +            if findfile(fn) ~= "" then +                return fn +            end +        end      else          -- no lookup, just use the regular mechanism      end diff --git a/tex/context/base/mkiv/l-dir.lua b/tex/context/base/mkiv/l-dir.lua index 325039cb1..ac8e2f4e8 100644 --- a/tex/context/base/mkiv/l-dir.lua +++ b/tex/context/base/mkiv/l-dir.lua @@ -230,18 +230,18 @@ if onwindows then -- we could sanitize here  --     pattern = Ct {      pattern = { -        [1] = (Cs(P(".") + slash^1) + Cs(R("az","AZ") * P(":") * slash^0) + Cc("./")) * V(2) * V(3), -        [2] = Cs(((1-S("*?/\\"))^0 * slash)^0), -        [3] = Cs(P(1)^0) +        (Cs(P(".") + slash^1) + Cs(R("az","AZ") * P(":") * slash^0) + Cc("./")) * V(2) * V(3), +        Cs(((1-S("*?/\\"))^0 * slash)^0), +        Cs(P(1)^0)      }  else -- assume unix  --     pattern = Ct {      pattern = { -        [1] = (C(P(".") + P("/")^1) + Cc("./")) * V(2) * V(3), -        [2] = C(((1-S("*?/"))^0 * P("/"))^0), -        [3] = C(P(1)^0) +        (C(P(".") + P("/")^1) + Cc("./")) * V(2) * V(3), +        C(((1-S("*?/"))^0 * P("/"))^0), +        C(P(1)^0)      }  end diff --git a/tex/context/base/mkiv/l-io.lua b/tex/context/base/mkiv/l-io.lua index f72995abd..6bf7a97bd 100644 --- a/tex/context/base/mkiv/l-io.lua +++ b/tex/context/base/mkiv/l-io.lua @@ -291,7 +291,8 @@ end  io.noflines = noflines --- inlined is faster ... beware, better use util-fil +-- inlined is faster ... beware, better use util-fil so these are obsolete +-- and will go  local nextchar = {      [ 4] = function(f) diff --git a/tex/context/base/mkiv/l-lpeg.lua b/tex/context/base/mkiv/l-lpeg.lua index 77e5cf12d..5f3bea08c 100644 --- a/tex/context/base/mkiv/l-lpeg.lua +++ b/tex/context/base/mkiv/l-lpeg.lua @@ -665,12 +665,12 @@ end  -- lpeg.print(lpeg.P("a","b","c"))  -- lpeg.print(lpeg.S("a","b","c")) --- print(lpeg.counter("äáàa",lpeg.P("á") + lpeg.P("à"))) --- print(lpeg.counter("äáàa",lpeg.UP("áà"))) --- print(lpeg.counter("äáàa",lpeg.US("àá"))) --- print(lpeg.counter("äáàa",lpeg.UR("aá"))) --- print(lpeg.counter("äáàa",lpeg.UR("àá"))) --- print(lpeg.counter("äáàa",lpeg.UR(0x0000,0xFFFF))) +-- print(lpeg.counter(lpeg.P("á") + lpeg.P("à"))("äáàa")) +-- print(lpeg.counter(lpeg.UP("áà"))("äáàa")) +-- print(lpeg.counter(lpeg.US("àá"))("äáàa")) +-- print(lpeg.counter(lpeg.UR("aá"))("äáàa")) +-- print(lpeg.counter(lpeg.UR("àá"))("äáàa")) +-- print(lpeg.counter(lpeg.UR(0x0000,0xFFFF)))  function lpeg.is_lpeg(p)      return p and lpegtype(p) == "pattern" diff --git a/tex/context/base/mkiv/lpdf-wid.lua b/tex/context/base/mkiv/lpdf-wid.lua index 1e91ecd56..e856ddaf4 100644 --- a/tex/context/base/mkiv/lpdf-wid.lua +++ b/tex/context/base/mkiv/lpdf-wid.lua @@ -25,7 +25,7 @@ if not modules then modules = { } end modules ['lpdf-wid'] = {  -- html5 media in pdf then.  --  -- See mail by Michal Vlasák to the mailing list that discusses current support in --- viewers and also mentions a few fixes wrt embedding media. +-- viewers and also mentions (and submitted) a few fixes wrt embedding media.  local tonumber, next = tonumber, next  local gmatch, gsub, find, lower = string.gmatch, string.gsub, string.find, string.lower @@ -60,6 +60,7 @@ local v_auto                   = variables.auto  local v_embed                  = variables.embed  local v_max                    = variables.max  local v_yes                    = variables.yes +local v_compress               = variables.compress  local pdfconstant              = lpdf.constant  local pdfnull                  = lpdf.null @@ -649,6 +650,7 @@ local function insertrenderingwindow(specification)          Subtype = pdfconstant("Screen"),          P       = pdfreference(pdfpagereference(page)),          A       = a, -- needed in order to make the annotation clickable (i.e. don't bark) +        T       = pdfunicode(label), -- for JS          Border  = bs,          C       = bc,          AA      = actions, @@ -662,7 +664,7 @@ end  -- some dictionaries can have a MH (must honor) or BE (best effort) capsule  local function insertrendering(specification) -    local label = specification.label +    local label  = specification.label      local option = settings_to_hash(specification.option)      if not mf[label] then          local filename = specification.filename @@ -693,7 +695,7 @@ local function insertrendering(specification)       --     }       -- }          local parameters = pdfdictionary { -            Type = pdfconstant(MediaPermissions), +            Type = pdfconstant("MediaPermissions"),              TF   = pdfstring("TEMPALWAYS"), -- TEMPNEVER TEMPEXTRACT TEMPACCESS TEMPALWAYS / needed for acrobat/wmp          }          local descriptor = pdfdictionary { @@ -707,7 +709,7 @@ local function insertrendering(specification)              descriptor = codeinjections.embedfile {                  file           = filename,                  mimetype       = mimetype, -- yes or no -                compress       = false, +                compress       = option[v_compress] or false,                  forcereference = true,              }          end @@ -723,7 +725,7 @@ local function insertrendering(specification)          local rendition = pdfdictionary {              Type = pdfconstant("Rendition"),              S    = pdfconstant("MR"), -            N    = label, +            N    = pdfunicode(label),              C    = pdfreference(pdfflushobject(clip)),          }          mf[label] = pdfreference(pdfflushobject(rendition)) @@ -761,6 +763,21 @@ function codeinjections.processrendering(label)      end  end +-- needed mapping for access from JS + +local function flushrenderings() +    if next(mf) then +        local r = pdfarray() +        for label, reference in sortedhash(mf) do +            r[#r+1] = pdfunicode(label) +            r[#r+1] = reference -- already a reference +        end +        lpdf.addtonames("Renditions",pdfreference(pdfflushobject(pdfdictionary{ Names = r }))) +    end +end + +lpdf.registerdocumentfinalizer(flushrenderings,"renderings") +  function codeinjections.insertrenderingwindow(specification)      local label = specification.label      codeinjections.processrendering(label) diff --git a/tex/context/base/mkiv/math-ini.lua b/tex/context/base/mkiv/math-ini.lua index 19bf86802..2a8129ca7 100644 --- a/tex/context/base/mkiv/math-ini.lua +++ b/tex/context/base/mkiv/math-ini.lua @@ -120,13 +120,13 @@ local accents = allocate {  local codes = allocate {      ordinary       = 0, [0] = "ordinary", -    largeoperator  = 1, [1] = "largeoperator", -    binaryoperator = 2, [2] = "binaryoperator", -    relation       = 3, [3] = "relation", -    openingsymbol  = 4, [4] = "openingsymbol", -    closingsymbol  = 5, [5] = "closingsymbol", -    punctuation    = 6, [6] = "punctuation", -    variable       = 7, [7] = "variable", +    largeoperator  = 1,       "largeoperator", +    binaryoperator = 2,       "binaryoperator", +    relation       = 3,       "relation", +    openingsymbol  = 4,       "openingsymbol", +    closingsymbol  = 5,       "closingsymbol", +    punctuation    = 6,       "punctuation", +    variable       = 7,       "variable",  }  local extensibles = allocate { diff --git a/tex/context/base/mkiv/math-map.lua b/tex/context/base/mkiv/math-map.lua index 97860b923..5f93b43fc 100644 --- a/tex/context/base/mkiv/math-map.lua +++ b/tex/context/base/mkiv/math-map.lua @@ -689,9 +689,9 @@ local issygreek = regular_tf.symbols  local isgreek   = merged(islcgreek,isucgreek,issygreek)  local greekremapping = { -    [1] = { what = "unchanged" }, -- upright -    [2] = { what = "upright", it = "tf", bi = "bf" }, -- upright -    [3] = { what = "italic",  tf = "it", bf = "bi" }, -- italic +    { what = "unchanged" }, -- upright +    { what = "upright", it = "tf", bi = "bf" }, -- upright +    { what = "italic",  tf = "it", bf = "bi" }, -- italic  }  local usedremap = { } diff --git a/tex/context/base/mkiv/mlib-mpf.lua b/tex/context/base/mkiv/mlib-mpf.lua index 7452c0111..7e2a01ffb 100644 --- a/tex/context/base/mkiv/mlib-mpf.lua +++ b/tex/context/base/mkiv/mlib-mpf.lua @@ -247,7 +247,7 @@ do      -- writers -    local function mpp(value) +    local function rawmpp(value)          n = n + 1          local t = type(value)          if t == "number" then @@ -268,13 +268,13 @@ do      local function mpprint(first,second,...)          if second == nil then              if first ~= nil then -                mpp(first) +                rawmpp(first)              end          else              for i=1,select("#",first,second,...) do                  local value = (select(i,first,second,...))                  if value ~= nil then -                    mpp(value) +                    rawmpp(value)                  end              end          end @@ -653,6 +653,25 @@ do      for k, v in next, aux do mp[k] = v end + -- mp.print = table.setmetatablecall(aux, function(t,...) + --     mpprint(...) + -- end) + +    mp.print = table.setmetatablecall(aux, function(t,first,second,...) +        if second == nil then +            if first ~= nil then +                rawmpp(first) +            end +        else +            for i=1,select("#",first,second,...) do +                local value = (select(i,first,second,...)) +                if value ~= nil then +                    rawmpp(value) +                end +            end +        end +    end) +  end  do diff --git a/tex/context/base/mkiv/mult-def.lua b/tex/context/base/mkiv/mult-def.lua index af7804506..a06deffe5 100644 --- a/tex/context/base/mkiv/mult-def.lua +++ b/tex/context/base/mkiv/mult-def.lua @@ -15390,6 +15390,10 @@ return {     ["pe"]="eight",     ["ro"]="eight",    }, +  ["compress"]={ +   ["en"]="compress", +   ["nl"]="comprimeer", +  },    ["embed"]={     ["en"]="embed",     ["fr"]="integrer", diff --git a/tex/context/base/mkiv/mult-fun.lua b/tex/context/base/mkiv/mult-fun.lua index 50ced6ead..9f6c8456c 100644 --- a/tex/context/base/mkiv/mult-fun.lua +++ b/tex/context/base/mkiv/mult-fun.lua @@ -189,10 +189,17 @@ return {          "setmacro", "setdimen", "setcount", "settoks",          "setglobalmacro", "setglobaldimen", "setglobalcount", "setglobaltoks",          -- -        "positionpath", "positioncurve", "positionxy", "positionpxy", -        "positionwhd", "positionpage", "positionregion", "positionbox", +        "positionpath", "positioncurve", "positionxy", "positionparagraph", "positioncolumn", +        "positionwhd", "positionpage", "positionregion", "positionbox", "positionx", "positiony",          "positionanchor", "positioninregion", "positionatanchor",          -- +        "getposboxesv, getmultipars", +        "getpospage", "getposparagraph", "getposcolumn", "getposregion", +        "getposx", "getposy", "getposwidth", "getposheight", "getposdepth", +        "getposleftskip", "getposrightskip", "getposhsize", "getposparindent", "getposhangindent", "getposhangafter", +        "getposxy", "getposupperleft", "getposlowerleft", "getposupperright", "getposlowerright". +        "getposllx", "getposlly", "getposurx", "getposury" ; +        --          "wdpart", "htpart", "dppart",          --          "texvar", "texstr", @@ -201,7 +208,7 @@ return {          --          "utfnum", "utflen", "utfsub",          -- -        "newhash", "disposehash", "inhash", "tohash", +        "newhash", "disposehash", "inhash", "tohash", "fromhash",          --          "isarray", "prefix", "isobject",          -- diff --git a/tex/context/base/mkiv/mult-low.lua b/tex/context/base/mkiv/mult-low.lua index 5985faf73..59320507a 100644 --- a/tex/context/base/mkiv/mult-low.lua +++ b/tex/context/base/mkiv/mult-low.lua @@ -357,7 +357,7 @@ return {          --          "quitcondition", "truecondition", "falsecondition",          -- -        "tracingall", "tracingnone", "loggingall", +        "tracingall", "tracingnone", "loggingall", "tracingcatcodes",          "showluatokens",          --          "aliasmacro", diff --git a/tex/context/base/mkiv/page-mix.lua b/tex/context/base/mkiv/page-mix.lua index 374ba8866..7828b7e56 100644 --- a/tex/context/base/mkiv/page-mix.lua +++ b/tex/context/base/mkiv/page-mix.lua @@ -59,7 +59,7 @@ local getnext             = nuts.getnext  local getprev             = nuts.getprev  local getid               = nuts.getid  local getlist             = nuts.getlist -local getindex            = nuts.getindex +local getindex            = nuts.getindex or nuts.getsubtype -- luatex catch  local getbox              = nuts.getbox  local getattr             = nuts.getattr  local getwhd              = nuts.getwhd @@ -79,6 +79,8 @@ local new_glue            = nodepool.glue  local points              = number.points +local setinsertcontent    = tex.setinsertcontent or tex.setbox +  local settings_to_hash    = utilities.parsers.settings_to_hash  local variables           = interfaces.variables @@ -986,9 +988,8 @@ local function getsplit(result,n)          for i=1,#list-1 do              setdepth(list[i],0)          end -        local b = vpack(l)    -- multiple arguments, todo: fastvpack ---         setbox("global",c,b)  -- when we wrap in a box -        tex.setinsertcontent(c,tonode(b))  -- when we wrap in a box +        local b = vpack(l)            -- multiple arguments, todo: fastvpack +        setinsertcontent(c,tonode(b)) -- when we wrap in a box          r.inserts[c] = nil      end diff --git a/tex/context/base/mkiv/publ-dat.lua b/tex/context/base/mkiv/publ-dat.lua index f09e97a8d..382f70471 100644 --- a/tex/context/base/mkiv/publ-dat.lua +++ b/tex/context/base/mkiv/publ-dat.lua @@ -632,13 +632,13 @@ do      local r_value    = reference * Carg(1) / resolve      local balanced   = P { -        [1] = ((escape * (left+right)) + (collapsed + r_value + 1 - (left+right))^1 + V(2))^0, -        [2] = left * V(1) * right, +        ((escape * (left+right)) + (collapsed + r_value + 1 - (left+right))^1 + V(2))^0, +        left * V(1) * right,      }   -- local unbalanced = P { - --     [1] = left * V(2) * right, - --     [2] = ((escape * (left+right)) + (collapsed + 1 - (left+right))^1 + V(1))^0, + --     left * V(2) * right, + --     ((escape * (left+right)) + (collapsed + 1 - (left+right))^1 + V(1))^0,   -- }      local unbalanced = (left/"") * balanced * (right/"") * P(-1) diff --git a/tex/context/base/mkiv/publ-fnd.lua b/tex/context/base/mkiv/publ-fnd.lua index 32d0c11be..5ba173365 100644 --- a/tex/context/base/mkiv/publ-fnd.lua +++ b/tex/context/base/mkiv/publ-fnd.lua @@ -147,8 +147,8 @@ local p_expression = P("match")/"" * Cs(p_compare)                       ) / test_key_value  local pattern = Cs { -    [1] = V(2) * (p_combine * V(2))^0, -    [2] = p_expression, +    V(2) * (p_combine * V(2))^0, +    p_expression,  }  -- -- -- -- -- -- -- -- -- -- -- -- -- diff --git a/tex/context/base/mkiv/spac-ver.lua b/tex/context/base/mkiv/spac-ver.lua index 5ab8196c8..efc157a2b 100644 --- a/tex/context/base/mkiv/spac-ver.lua +++ b/tex/context/base/mkiv/spac-ver.lua @@ -710,19 +710,19 @@ local function snap_topskip(current,method)      return w, 0  end -local categories = { -     [0] = "discard", -     [1] = "largest", -     [2] = "force", -     [3] = "penalty", -     [4] = "add", -     [5] = "disable", -     [6] = "nowhite", -     [7] = "goback", -     [8] = "packed", -     [9] = "overlay", -    [10] = "enable", -    [11] = "notopskip", +local categories = { [0] = +    "discard", +    "largest", +    "force", +    "penalty", +    "add", +    "disable", +    "nowhite", +    "goback", +    "packed", +    "overlay", +    "enable", +    "notopskip",  }  categories          = allocate(table.swapped(categories,categories)) diff --git a/tex/context/base/mkiv/status-files.pdf b/tex/context/base/mkiv/status-files.pdf Binary files differindex 67e5b7a47..61569f5d1 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 42d1b8d7b..163af8eee 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-syn.lua b/tex/context/base/mkiv/strc-syn.lua index 6b50da4ac..f8b78b940 100644 --- a/tex/context/base/mkiv/strc-syn.lua +++ b/tex/context/base/mkiv/strc-syn.lua @@ -86,12 +86,12 @@ function synonyms.register(class,kind,spec)      local tag        = definition.tag or ""      data.metadata.kind = kind -- runtime, not saved in format (yet)      if not hash[tag] then -        if definition.used == nil then -            definition.used = false -        end -        if definition.shown == nil then -            definition.shown = false -        end +--         if definition.used == nil then +--             definition.used = false +--         end +--         if definition.shown == nil then +--             definition.shown = false +--         end          local entries = data.entries          entries[#entries+1] = spec          hash[tag] = spec @@ -121,24 +121,26 @@ end  function synonyms.isused(class,tag)      local data = tobesaved[class]      local okay = data.hash[tag] -    return okay and okay.definition.used +    return okay and okay.definition.used or false  end  function synonyms.isshown(class,tag)      local data = tobesaved[class]      local okay = data.hash[tag] -    return okay and okay.definition.shown +    return okay and okay.definition.shown or false  end  function synonyms.resetused(class)      for tag, data in next, tobesaved[class].hash do -        data.definition.used = false +--         data.definition.used = false +        data.definition.used = nil      end  end  function synonyms.resetshown(class)      for tag, data in next, tobesaved[class].hash do -        data.definition.shown = false +--         data.definition.shown = false +        data.definition.shown = nil      end  end diff --git a/tex/context/base/mkiv/util-prs.lua b/tex/context/base/mkiv/util-prs.lua index 7e2bf0110..6fcc66082 100644 --- a/tex/context/base/mkiv/util-prs.lua +++ b/tex/context/base/mkiv/util-prs.lua @@ -55,9 +55,13 @@ local nobracket   = 1 - (lbracket + rbracket)  local escape, left, right = P("\\"), P('{'), P('}') +-- lpegpatterns.balanced = P { +--     [1] = ((escape * (left+right)) + (1 - (left+right)) + V(2))^0, +--     [2] = left * V(1) * right +-- }  lpegpatterns.balanced = P { -    [1] = ((escape * (left+right)) + (1 - (left+right)) + V(2))^0, -    [2] = left * V(1) * right +    ((escape * (left+right)) + (1 - (left+right)) + V(2))^0, +    left * V(1) * right  }  local nestedbraces   = P { lbrace   * (nobrace   + V(1))^0 * rbrace } diff --git a/tex/context/base/mkiv/util-tab.lua b/tex/context/base/mkiv/util-tab.lua index 9f7112eb9..a92b47b3f 100644 --- a/tex/context/base/mkiv/util-tab.lua +++ b/tex/context/base/mkiv/util-tab.lua @@ -417,9 +417,9 @@ if JITSUPPORTED then  else -    local f_v = formatters["[%q]=%q,"] -    local f_t = formatters["[%q]="] -    local f_q = formatters["%q,"] + -- local f_v = formatters["[%q]=%q,"] + -- local f_t = formatters["[%q]="] + -- local f_q = formatters["%q,"]      function table.fastserialize(t,prefix) -- todo, move local function out          local r = { type(prefix) == "string" and prefix or "return" } @@ -720,6 +720,7 @@ local function serialize(root,name,specification)      local t    -- = { }      local n       = 1 + -- local m       = 0 -- no gain      local unknown = false      local function do_serialize(root,name,depth,level,indexed) @@ -850,6 +851,12 @@ local function serialize(root,name,specification)                          n = n + 1 t[n] = f_key_str_value_str(depth,tostring(k),tostring(v))                      end                  end +             -- if n > 100000 then -- no gain +             --     local k = m + 1 +             --     t[k] = concat(t,"\n",k,n) +             --     n = k +             --     m = k +             -- end              end          end          if level > 0 then @@ -898,6 +905,7 @@ local function serialize(root,name,specification)      n = n + 1      t[n] = f_table_finish()      return concat(t,"\n") + -- return concat(t,"\n",1,n) -- no gain  end  table.serialize = serialize diff --git a/tex/context/base/mkxl/anch-bar.mkxl b/tex/context/base/mkxl/anch-bar.mkxl index 71cc7f163..9fa9d6bf2 100644 --- a/tex/context/base/mkxl/anch-bar.mkxl +++ b/tex/context/base/mkxl/anch-bar.mkxl @@ -15,8 +15,6 @@  \unprotect -%D This can be done better now ... -  %D We will implement a sidebar mechanism using the functionality from \type  %D {core-pos}.  %D @@ -54,6 +52,8 @@  %D   \input tufte  %D \stopsidebar  %D \stoptyping +%D +%D This can be done better now ... maybe also by using the syncpos features.  \installcorenamespace{sidebar} diff --git a/tex/context/base/mkxl/anch-bck.mklx b/tex/context/base/mkxl/anch-bck.mklx index 740ed693b..5c9382a7b 100644 --- a/tex/context/base/mkxl/anch-bck.mklx +++ b/tex/context/base/mkxl/anch-bck.mklx @@ -97,9 +97,9 @@  \installcommandhandler \??textbackground {textbackground} \??textbackground  \appendtoks -    \frozen\instance\setuevalue{\currenttextbackground}{\groupedcommand{\starttextbackground[\currenttextbackground]}{\stoptextbackground}}% -    \frozen\instance\setuevalue{\e!start\currenttextbackground}{\starttextbackground[\currenttextbackground]}% -    \frozen\instance\setuevalue{\e!stop \currenttextbackground}{\stoptextbackground}% +    \frozen\instance\protected\edefcsname        \currenttextbackground\endcsname{\groupedcommand{\starttextbackground[\currenttextbackground]}{\stoptextbackground}}% +    \frozen\instance\protected\edefcsname\e!start\currenttextbackground\endcsname{\starttextbackground[\currenttextbackground]}% +    \frozen\instance\protected\edefcsname\e!stop \currenttextbackground\endcsname{\stoptextbackground}%  \to \everydefinetextbackground  \newconstant   \c_anch_backgrounds_pos_state @@ -134,9 +134,11 @@     \ifparameter#settings\or       \setupcurrenttextbackground[#settings]%     \fi -   \doifelse{\textbackgroundparameter\c!state}\v!start +   \ifcstok{\textbackgroundparameter\c!state}\v!start       \anch_backgrounds_text_preset_yes +   \else       \anch_backgrounds_text_preset_nop +   \fi     \anch_backgrounds_text_start_indeed}  \def\anch_backgrounds_text_level_start @@ -292,10 +294,8 @@          \kern\textbackgroundskip\nobreak       \fi     \fi -   \dosetleftskipadaption{\textbackgroundparameter\c!leftoffset}% -   \advance\leftskip\leftskipadaption -   \dosetleftskipadaption{\textbackgroundparameter\c!rightoffset}% -   \advance\rightskip\leftskipadaption +   \doadaptleftskip {\textbackgroundparameter\c!leftoffset}% +   \doadaptrightskip{\textbackgroundparameter\c!rightoffset}%     % new     \dosetraggedcommand{\textbackgroundparameter\c!align}%     \raggedcommand diff --git a/tex/context/base/mkxl/anch-pgr.mkxl b/tex/context/base/mkxl/anch-pgr.mkxl index 3c4dcf680..4ffedbfc3 100644 --- a/tex/context/base/mkxl/anch-pgr.mkxl +++ b/tex/context/base/mkxl/anch-pgr.mkxl @@ -245,7 +245,7 @@          {\dopositionaction{\currentpositionoverlay::\MPanchoridentifier}\hss}%        \ht\scratchbox\d_overlay_height        \dp\scratchbox\zeropoint -      \anch_mark_tagged_box\scratchbox\MPanchorid % needs an hbox +      \anch_mark_tagged_box\scratchbox\MPanchorid\zerocount % needs an hbox        \box\scratchbox        \vfill}} @@ -427,7 +427,7 @@  \def\anch_positions_meta_graphic_cleanup_range#1#2#3#4% pos tag setups    {\ifnum\MPp{#2}<\realpageno \else -     \noexpand \anch_positions_meta_graphic_handle_range_indeed{#1}{#2}{#3}{#4}% +     \noexpand\anch_positions_meta_graphic_handle_range_indeed{#1}{#2}{#3}{#4}%     \fi}  \appendtoks diff --git a/tex/context/base/mkxl/anch-pos.lmt b/tex/context/base/mkxl/anch-pos.lmt index 94eb443c6..5e53406da 100644 --- a/tex/context/base/mkxl/anch-pos.lmt +++ b/tex/context/base/mkxl/anch-pos.lmt @@ -6,33 +6,46 @@ if not modules then modules = { } end modules ['anch-pos'] = {      license   = "see context related readme files"  } ---[[ldx-- -<p>We save positional information in the main utility table. Not only -can we store much more information in <l n='lua'/> but it's also -more efficient.</p> ---ldx]]-- - --- plus (extra) is obsolete but we will keep it for a while +-- We save positional information in the main utility table. Not only can we store +-- much more information in Lua but it's also more efficient. In the meantime these +-- files have become quite large. In some cases that get noticed by a hickup in the +-- start and/or finish, but that is the price we pay for progress. +-- +-- This was the last module that got rid of directly setting scanners, with a little +-- performance degradation but not that noticeable. It is also a module that has been +-- on the (partial) redo list for a while.  -- --- maybe replace texsp by our own converter (stay at the lua end) --- eventually mp will have large numbers so we can use sp there too +-- We can gain a little when we group positions but then we still have to deal with +-- regions and cells so we either end up with lots of extra small tables pointing to +-- them and/or assembling/disassembling. I played with that and rejected the idea +-- until I ran into a test case where we had 50.000 one line paragraphs in an eight +-- columns setup, and there we save 25 M on a 75 M tuc file. So, I played a bit more +-- and we can have a solution that is of similar performance for regular documents +-- (in spite of the extra overhead) but also works ok for the large files. In normal +-- documents it is never a problem, but there are always exceptions to the normal and +-- often these are also cases where positions are not really used but end up in the +-- tuc file anyway.  -- --- this is one of the first modules using scanners and we need to replace it by --- implement and friends +-- Currently (because we never had split tags) we do splitting at access time, which +-- is sort of inefficient but still ok. Much of this mechanism comes from MkII where +-- TeX was the bottleneck.  -- --- we could have namespaces, like p, page, region, columnarea, textarea but then --- we need virtual table accessors as well as have tag/id accessors ... we don't --- save much here (at least not now) +-- By grouping we need to set more metatable so in the end that is also overhead +-- but on the average we're okay because excessive serialization also comes at a price +-- and this way we also delay some activities till the moment it is realy used (which +-- is not always the case with positional information. We will make this transition +-- stepwise so till we're done there will be inefficiencies and overhead.  -- --- This was the last module that got rid of directly setting scanners, with a little --- performance degradation but not that noticeable. +-- The pure hash based variant combined with filtering is in anch-pos.lua and previous +-- lmt versions! That is the reference. -local tostring, next, setmetatable, tonumber = tostring, next, setmetatable, tonumber -local sort = table.sort +local tostring, next, setmetatable, tonumber, rawget, rawset = tostring, next, setmetatable, tonumber, rawget, rawset +local sort, sortedhash = table.sort, table.sortedhash  local format, gmatch = string.format, string.gmatch -local lpegmatch = lpeg.match +local P, R, C, Cc, lpegmatch = lpeg.P, lpeg.R, lpeg.C, lpeg.Cc, lpeg.match  local insert, remove = table.insert, table.remove  local allocate = utilities.storage.allocate +local setmetatableindex, setmetatablenewindex = table.setmetatableindex, table.setmetatablenewindex  local report            = logs.reporter("positions") @@ -55,11 +68,11 @@ local texsetcount       = tex.setcount  local texget            = tex.get  local texsp             = tex.sp  ----- texsp             = string.todimen -- because we cache this is much faster but no rounding - -local setmetatableindex    = table.setmetatableindex -local setmetatablenewindex = table.setmetatablenewindex +local texgetnest        = tex.getnest +local texgetparstate    = tex.getparstate  local nuts              = nodes.nuts +local tonut             = nodes.tonut  local setlink           = nuts.setlink  local getlist           = nuts.getlist @@ -67,8 +80,12 @@ local setlist           = nuts.setlist  local getbox            = nuts.getbox  local getid             = nuts.getid  local getwhd            = nuts.getwhd +local setprop           = nuts.setprop + +local getparstate       = nuts.getparstate  local hlist_code        = nodes.nodecodes.hlist +local par_code          = nodes.nodecodes.par  local find_tail         = nuts.tail  ----- hpack             = nuts.hpack @@ -85,6 +102,7 @@ local formatters        = string.formatters  local collected         = allocate()  local tobesaved         = allocate() +local positionsused     = false  local jobpositions = {      collected = collected, @@ -109,138 +127,707 @@ local default = { -- not r and paragraphs etc          hs  = 0,     -- hsize          pi  = 0,     -- parindent          ps  = false, -- parshape -        dir = 0, +        dir = 0,     -- obsolete +        r2l = false, -- righttoleft      }  }  local f_b_tag     = formatters["b:%s"]  local f_e_tag     = formatters["e:%s"]  local f_p_tag     = formatters["p:%s"] -local f_w_tag     = formatters["w:%s"] +----- f_w_tag     = formatters["w:%s"]  local f_region    = formatters["region:%s"]  local f_tag_three = formatters["%s:%s:%s"]  local f_tag_two   = formatters["%s:%s"] -local nofregular  = 0 -local nofspecial  = 0 -local splitter    = lpeg.splitat(":",true) - -local pagedata    = { } -local columndata  = setmetatableindex("table") -- per page -local freedata    = setmetatableindex("table") -- per page - -local function initializer() -    tobesaved = jobpositions.tobesaved -    collected = jobpositions.collected -    for tag, data in next, collected do -        local prefix, rest = lpegmatch(splitter,tag) -        if prefix == "p" then -            nofregular = nofregular + 1 -        elseif prefix == "page" then -            nofregular = nofregular + 1 -            pagedata[tonumber(rest) or 0] = data -        elseif prefix == "free" then -            nofspecial = nofspecial + 1 -            local t = freedata[data.p or 0] -            t[#t+1] = data -        elseif prefix == "columnarea" then -            columndata[data.p or 0][data.c or 0] = data -        end -        setmetatable(data,default) -    end -    -- -    local pages = structures.pages.collected -    if pages then -        local last = nil -        for p=1,#pages do -            local region = "page:" .. p -            local data   = pagedata[p] -            local free   = freedata[p] -            if free then -                sort(free,function(a,b) return b.y < a.y end) -- order matters ! -            end -            if data then -                last      = data -                last.free = free -            elseif last then -                local t = setmetatableindex({ free = free, p = p },last) -                if not collected[region] then -                    collected[region] = t +-- Because positions are set with a delay we cannot yet make the tree -- so that +-- is a finalizer step. But, we already have a dual split. + +-- local treemode = false +local treemode = true + +local function checkshapes(s) +    for p, data in next, s do +        local n = #data +        if n > 1 then +            local d1 = data[1] +            local ph = d1[2] +            local pd = d1[3] +            local xl = d1[4] +            local xr = d1[5] +            for i=2,n do +                local di = data[i] +                local h = di[2] +                local d = di[3] +                local l = di[4] +                local r = di[5] +                if r == xr then +                    di[5] = nil +                    if l == xl then +                        di[4] = nil +                        if d == pd then +                            di[3] = nil +                            if h == ph then +                                di[2] = nil +                            else +                                ph = h +                            end +                        else +                            pd, ph = d, h +                        end +                    else +                        ph, pd, xl = h, d, l +                    end                  else -                    -- something is wrong +                    ph, pd, xl, xr = h, d, l, r                  end -                pagedata[p] = t              end          end      end -    jobpositions.pagedata   = pagedata  end -function jobpositions.used() -    return next(collected) -- we can safe it -end +local columndata = { } +local freedata   = { }       -- we can make these weak +local syncdata   = { }       -- we can make these weak +local columndone = false -function jobpositions.getfree(page) -    return freedata[page] -end +if treemode then --- we can gain a little when we group positions but then we still have to --- deal with regions and cells so we either end up with lots of extra small --- tables pointing to them and/or assembling/disassembling so in the end --- it makes no sense to do it (now) and still have such a mix --- --- proof of concept code removed ... see archive - -local function finalizer() -    -- We make the (possible extensive) shape lists sparse working -    -- from the end. We could also drop entries here that have l and -    -- r the same which saves testing later on. -    for k, v in next, tobesaved do -        local s = v.s -        if s then -            for p, data in next, s do -                local n = #data -                if n > 1 then -                    local ph = data[1][2] -                    local pd = data[1][3] -                    local xl = data[1][4] -                    local xr = data[1][5] -                    for i=2,n do -                        local di = data[i] -                        local h = di[2] -                        local d = di[3] -                        local l = di[4] -                        local r = di[5] -                        if r == xr then -                            di[5] = nil -                            if l == xl then -                                di[4] = nil -                                if d == pd then -                                    di[3] = nil -                                    if h == ph then -                                        di[2] = nil -                                    else -                                        ph = h +    -- At some point we can install extra ones. I actually was halfway making a more +    -- general installer but we have quite some distinct handling down here and it +    -- became messy. So I rolled that back. Also, users and modules will quite likely +    -- stay in the "user" namespace. + +    -- syncpos    : indirect access via helper, todo after we switch: direct setters +    -- free       : indirect access via helper, todo after we switch: direct setters +    -- columnarea : indirect access via helper, todo after we switch: direct setters + +    -- todo: keep track of total and check that against # (sanity check) + +    local prefix_number       = { "text", "textarea", "page", "p", "free", "columnarea" } +    local prefix_label_number = { "syncpos" } +    local prefix_number_rest  = { "region", "b", "e" } + +    -- no need to split: syncpos free columnarea (textarea?) + +    local function splitter_pattern() +        local p_number = R("09")^1/tonumber +        local p_colon  = P(":") +        local p_label  = C(P(1 - p_colon)^0) +        local p_rest   = C(P(1)^0) +        return +            C(lpeg.utfchartabletopattern(prefix_number      )) * p_colon * p_number * P(-1) +          + C(lpeg.utfchartabletopattern(prefix_label_number)) * p_colon * (p_number + p_label) * p_colon * p_number * P(-1) +          + C(lpeg.utfchartabletopattern(prefix_number_rest )) * p_colon * (p_number + p_rest) +          + Cc("user")                                         * p_rest +    end + +    -- In the end these metatable entries are not more efficient than copying +    -- but it's all about making sure that the tuc file doesn't explode. + +    columndata = { } +    columndone = false + +    local deltapacking = true -- so we can see the difference + +    local function checkcommondata(v,common) +        if common then +            local i = v.i +            local t = common[i] +            if t then +                local m = t.mt +                if not m then +                    setmetatable(t,default) +                    m = { __index = t } +                    t.mt = m +                end +                setmetatable(v,m) +                return +            end +        end +        setmetatable(v,default) +    end + +    local function initializer() +        tobesaved = jobpositions.tobesaved +        collected = jobpositions.collected +        -- +        local p_splitter = splitter_pattern() +        -- +        local list = nil +        -- +        local shared        = setmetatableindex(rawget(collected,"shared"),"table") +        local x_y_w_h_list  = shared.x_y_w_h +        local y_w_h_d_list  = shared.y_w_h_d +        local x_h_d_list    = shared.x_h_d +        local x_h_d_hs_list = shared.x_h_d_hs +        -- +        columndata = setmetatableindex(function(t,k) +            setmetatableindex(t,"table") +            list = rawget(collected,"columnarea") +            if list then +             -- for tag, data in next, list do +                for i=1,#list do +                    local data = list[i] +                    columndata[data.p or 0][data.c or 0] = data +                    checkcommondata(data,y_w_h_d_list) +                end +            end +            columndone = true +            return t[k] +        end) +        -- +        -- todo: use a raw collected and a weak proxy +        -- +        setmetatableindex(collected,function(t,k) +            local prefix, one, two = lpegmatch(p_splitter,k) +            local list = rawget(t,prefix) +            if list and type(list) == "table" then +                v = list[one] or false +                if v then +                    if prefix == "p" then +                        if deltapacking and type(v) == "number" then +                            for i=one,1,-1 do +                                local l = list[i] +                                if type(l) ~= "number" then +                                    if not getmetatable(l) then +                                        checkcommondata(l,x_h_d_hs_list)                                      end -                                else -                                    pd, ph = d, h +                                    v = setmetatable({ y = v }, { __index = l }) +                                    list[one] = v +                                    break +                                end +                            end +                        else +                            checkcommondata(v,x_h_d_hs_list) +                        end +                    elseif prefix == "text" or prefix == "textarea" then +                        if type(v) == "number" then +                            for i=one,1,-1 do +                                local l = list[i] +                                if type(l) ~= "number" then +                                    if not getmetatable(l) then +                                        checkcommondata(l,x_y_w_h_list) +                                    end +                                    v = setmetatable({ p = p }, { __index = l }) +                                    list[one] = v +                                    break                                  end -                            else -                                ph, pd, xl = h, d, l                              end                          else -                            ph, pd, xl, xr = h, d, l, r +                            checkcommondata(v,x_y_w_h_list) +                        end +                    elseif prefix == "columnarea" then +                        if not columndone then +                            checkcommondata(v,y_w_h_d_list) +                        end +                    elseif prefix == "syncpos" then +                        -- will become an error +                        if two then +                         -- v = syncdata[one][two] or { } +                            v = v[two] or { } +                        else +                            v = { } +                        end +                     -- for j=1,#v do +                     --     checkcommondata(v[j],x_h_d_list) +                     -- end +                    elseif prefix == "free" then +                        -- will become an error +                    elseif prefix == "page" then +                        checkcommondata(v) +                    else +                        checkcommondata(v) +                    end +                else +                    if prefix == "page" then +                        for i=one,1,-1 do +                            local data = list[i] +                            if data then +                                v = setmetatableindex({ free = free or false, p = p },last) +                                list[one] = v +                                break +                            end +                        end +                    end +                end +                t[k] = v +                return v +            else +                t[k] = false +                return false +            end +        end) +        -- +        setmetatableindex(tobesaved,function(t,k) +            local prefix, one, two = lpegmatch(p_splitter,k) +            local v = rawget(t,prefix) +            if v and type(v) == "table" then +                v = v[one] +                if v and two then +                    v = v[two] +                end +                return v -- or default +            else +             -- return default +            end +        end) +        -- +        setmetatablenewindex(tobesaved,function(t,k,v) +            local prefix, one, two = lpegmatch(p_splitter,k) +            local p = rawget(t,prefix) +            if not p then +                p = { } +                rawset(t,prefix,p) +            end +            if type(one) == "number" then -- maybe Cc(0 1 2) +                if #p < one then +                    for i=#p+1,one-1 do +                        p[i] = { } -- false +                    end +                end +            end +            if two then +                local pone = p[one] +                if not pone then +                    pone = { } +                    p[one] = pone +                end +                if type(two) == "number" then -- maybe Cc(0 1 2) +                    if #pone < two then +                        for i=#pone+1,two-1 do +                            pone[i] = { } -- false +                        end +                    end +                end +                pone[two] = v +            else +                p[one] = v +            end +        end) +        -- +        syncdata = setmetatableindex(function(t,category) +            -- p's and y's are not shared so no need to resolve +            local list = rawget(collected,"syncpos") +            local tc = list and rawget(list,category) +            if tc then +                sort(tc,function(a,b) +                    local ap = a.p +                    local bp = b.p +                    if ap == bp then +                        return b.y < a.y +                    else +                        return ap < bp +                    end +                end) +                tc.start = 1 +                for i=1,#tc do +                    checkcommondata(tc[i],x_h_d_list) +                end +            else +                tc = { } +            end +            t[category] = tc +            return tc +        end) +        -- +        for k, v in next, collected do +            if k ~= "shared" and next(v) then +                positionsused = true +                break +            end +        end +    end + +    function jobpositions.used() +        return positionsused +    end + +    local function finalizer() + +        -- We make the (possible extensive) shape lists sparse working from the end. We +        -- could also drop entries here that have l and r the same which saves testing +        -- later on. + +        local nofpositions = 0 +        local nofpartials  = 0 +        local nofdeltas    = 0 +        -- +        local x_y_w_h_size = 0 +        local x_y_w_h_list = { } +        local x_y_w_h_hash = setmetatableindex(function(t,x) +            local y = setmetatableindex(function(t,y) +                local w = setmetatableindex(function(t,w) +                    local h = setmetatableindex(function(t,h) +                        x_y_w_h_size = x_y_w_h_size + 1 +                        t[h] = x_y_w_h_size +                        x_y_w_h_list[x_y_w_h_size] = { x = x, y = y, w = w, h = h } +                        return x_y_w_h_size +                    end) +                    t[w] = h +                    return h +                end) +                t[y] = w +                return w +            end) +            t[x] = y +            return y +        end) +        -- +        local y_w_h_d_size = 0 +        local y_w_h_d_list = { } +        local y_w_h_d_hash = setmetatableindex(function(t,y) +            local w = setmetatableindex(function(t,w) +                local h = setmetatableindex(function(t,h) +                    local d = setmetatableindex(function(t,d) +                        y_w_h_d_size = y_w_h_d_size + 1 +                        t[d] = y_w_h_d_size +                        y_w_h_d_list[y_w_h_d_size] = { y = y, w = w, h = h, d = d } +                        return y_w_h_d_size +                    end) +                    t[h] = d +                    return d +                end) +                t[w] = h +                return h +            end) +            t[y] = w +            return w +        end) +        -- +        local x_h_d_size = 0 +        local x_h_d_list = { } +        local x_h_d_hash = setmetatableindex(function(t,x) +            local h = setmetatableindex(function(t,h) +                local d = setmetatableindex(function(t,d) +                    x_h_d_size = x_h_d_size + 1 +                    t[d] = x_h_d_size +                    x_h_d_list[x_h_d_size] = { x = x, h = h, d = d } +                    return x_h_d_size +                end) +                t[h] = d +                return d +            end) +            t[x] = h +            return h +        end) +        -- +        local x_h_d_hs_size = 0 +        local x_h_d_hs_list = { } +        local x_h_d_hs_hash = setmetatableindex(function(t,x) +            local h = setmetatableindex(function(t,h) +                local d = setmetatableindex(function(t,d) +                    local hs = setmetatableindex(function(t,hs) +                        x_h_d_hs_size = x_h_d_hs_size + 1 +                        t[hs] = x_h_d_hs_size +                        x_h_d_hs_list[x_h_d_hs_size] = { x = x, h = h, d = d, hs = hs } +                        return x_h_d_hs_size +                    end) +                    t[d] = hs +                    return hs +                end) +                t[h] = d +                return d +            end) +            t[x] = h +            return h +        end) +        -- +        rawset(tobesaved,"shared", { +            x_y_w_h  = x_y_w_h_list, +            y_w_h_d  = y_w_h_d_list, +            x_h_d    = x_h_d_list, +            x_h_d_hs = x_h_d_hs_list, +        }) +        -- +        -- If fonts can use crazy and hard to grasp packing tricks so can we. The "i" field +        -- refers to a shared set of values. In addition we pack some sequences. +        -- +        -- how about free +        -- +        for k, v in sortedhash(tobesaved) do +            if k == "p" then +                -- numeric +                local n = #v +                for i=1,n do +                    local t = v[i] +                    local hsh = x_h_d_hs_hash[t.x or 0][t.h or 0][t.d or 0][t.hs or 0] +                    t.x = nil +                    t.h = nil +                    t.d = nil +                    t.hs = nil -- not in syncpos +                    t.i = hsh +                    local s = t.s +                    if s then +                        checkshapes(s) +                    end +                end +                if deltapacking then +                    -- delta packing (y) +                    local last +                    local current +                    for i=1,n do +                        current = v[i] +                        if last then +                            for k, v in next, last do +                                if k ~= "y" and v ~= current[k] then +                                    goto DIFFERENT +                                end +                            end +                            for k, v in next, current do +                                if k ~= "y" and v ~= last[k] then +                                    goto DIFFERENT +                                end +                            end +                            v[i] = current.y or 0 +                            nofdeltas = nofdeltas + 1 +                            goto CONTINUE +                        end +                      ::DIFFERENT:: +                        last = current +                      ::CONTINUE:: +                    end +                end +                -- +                nofpositions = nofpositions + n +                nofpartials  = nofpartials  + n +            elseif k == "syncpos" then +                -- hash +                for k, t in next, v do +                    -- numeric +                    local n = #t +                    for j=1,n do +                        local t = t[j] +                        local hsh = x_h_d_hash[t.x or 0][t.h or 0][t.d or 0] +                        t.x = nil +                        t.h = nil +                        t.d = nil +                        t.i = hsh +                    end +                    nofpositions = nofpositions + n +                    nofpartials  = nofpartials  + n +                end +            elseif k == "text" or k == "textarea" then +                -- numeric +                local n = #v +                for i=1,n do +                    local t = v[i] +                    local hsh = x_y_w_h_hash[t.x or 0][t.y or 0][t.w or 0][t.h or 0] +                    t.x = nil +                    t.y = nil +                    t.w = nil +                    t.h = nil +                    t.i = hsh +                end +                nofpositions = nofpositions + n +                nofpartials  = nofpartials  + n +                if deltapacking then +                    -- delta packing (p) +                    local last +                    local current +                    for i=1,n do +                        current = v[i] +                        if last then +                            for k, v in next, last do +                                if k ~= "p" and v ~= current[k] then +                                    goto DIFFERENT +                                end +                            end +                            for k, v in next, current do +                                if k ~= "p" and v ~= last[k] then +                                    goto DIFFERENT +                                end +                            end +                            v[i] = current.p or 0 +                            nofdeltas = nofdeltas + 1 +                            goto CONTINUE                          end +                      ::DIFFERENT:: +                        last = current +                      ::CONTINUE::                      end                  end +            elseif k == "columnarea" then +                -- numeric +                local n = #v +                for i=1,n do +                    local t = v[i] +                    local hsh = y_w_h_d_hash[t.y or 0][t.w or 0][t.h or 0][t.d or 0] +                    t.y = nil +                    t.w = nil +                    t.h = nil +                    t.d = nil +                    t.i = hsh +                end +                nofpositions = nofpositions + n +                nofpartials  = nofpartials  + n +            else -- probably only b has shapes +                for k, t in next, v do -- no need to sort +                    local s = t.s +                    if s then +                        checkshapes(s) +                    end +                    nofpositions = nofpositions + 1 +                end              end          end + +        statistics.register("positions", function() +            if nofpositions > 0 then +                return format("%s collected, %i deltas, %i shared partials, %i partial entries", +                    nofpositions, nofdeltas, nofpartials, +                    x_y_w_h_size + y_w_h_d_size + x_h_d_size + x_h_d_hs_size +                ) +            else +                return nil +            end +        end) +      end + +    freedata = setmetatableindex(function(t,page) +        local list = rawget(collected,"free") +        local free = { } +        if list then +            local size = 0 +            for i=1,#list do +                local l = list[i] +                if l.p == page then +                    size = size + 1 +                    free[size] = l +                    checkcommondata(l) +                end +            end +            sort(free,function(a,b) return b.y < a.y end) -- order matters ! +        end +        t[page] = free +        return free +    end) + +    job.register('job.positions.collected', tobesaved, initializer, finalizer) + +else + +    columndata = setmetatableindex("table") -- per page +    freedata   = setmetatableindex("table") -- per page + +    local function initializer() +        tobesaved = jobpositions.tobesaved +        collected = jobpositions.collected +        -- +        local pagedata   = { } +        local p_splitter = lpeg.splitat(":",true) + +        for tag, data in next, collected do +            local prefix, rest = lpegmatch(p_splitter,tag) +            if prefix == "page" then +                pagedata[tonumber(rest) or 0] = data +            elseif prefix == "free" then +                local t = freedata[data.p or 0] +                t[#t+1] = data +            elseif prefix == "columnarea" then +                columndata[data.p or 0][data.c or 0] = data +            end +            setmetatable(data,default) +        end +        local pages = structures.pages.collected +        if pages then +            local last = nil +            for p=1,#pages do +                local region = "page:" .. p +                local data   = pagedata[p] +                local free   = freedata[p] +                if free then +                    sort(free,function(a,b) return b.y < a.y end) -- order matters ! +                end +                if data then +                    last      = data +                    last.free = free +                elseif last then +                    local t = setmetatableindex({ free = free, p = p },last) +                    if not collected[region] then +                        collected[region] = t +                    else +                        -- something is wrong +                    end +                    pagedata[p] = t +                end +            end +        end +        jobpositions.pagedata = pagedata -- never used + +    end + +    local function finalizer() + +        -- We make the (possible extensive) shape lists sparse working from the end. We +        -- could also drop entries here that have l and r the same which saves testing +        -- later on. + +        local nofpositions = 0 + +        for k, v in next, tobesaved do +            local s = v.s +            if s then +                checkshapes(s) +            end +            nofpositions = nofpositions + 1 +        end + +        statistics.register("positions", function() +            if nofpositions > 0 then +                return format("%s collected",nofpositions) +            else +                return nil +            end +        end) + +    end + +    local p_number = lpeg.patterns.cardinal/tonumber +    local p_tag    = P("syncpos:") * p_number * P(":") * p_number + +    syncdata = setmetatableindex(function(t,category) +        setmetatable(t,nil) +        for tag, pos in next, collected do +            local c, n = lpegmatch(p_tag,tag) +            if c then +                local tc = t[c] +                if tc then +                    tc[n] = pos +                else +                    t[c] = { [n] = pos } +                end +            end +        end +        for k, list in next, t do +            sort(list,function(a,b) +                local ap = a.p +                local bp = b.p +                if ap == bp then +                    return b.y < a.y +                else +                    return ap < bp +                end +            end) +            list.start = 1 +        end +        return t[category] +    end) + +    job.register('job.positions.collected', tobesaved, initializer, finalizer) + +end + +function jobpositions.getfree(page) +    return freedata[page]  end -job.register('job.positions.collected', tobesaved, initializer, finalizer) +function jobpositions.getsync(category) +    return syncdata[category] or { } +end  local regions    = { }  local nofregions = 0 @@ -491,7 +1078,6 @@ local function e_region(specification)      local last = tobesaved[region]      if last then          local y = getvpos() -        local x, y = getpos()          if specification.correct then              local h = (last.y or 0) - y              last.h = h ~= 0 and h or nil @@ -507,13 +1093,22 @@ jobpositions.e_region = e_region  local lastregion -local function setregionbox(n,tag,k,lo,ro,to,bo,column) -- kind +local function setregionbox(n,tag,index,k,lo,ro,to,bo,column) -- kind      if not tag or tag == "" then          nofregions = nofregions + 1 -        tag = f_region(nofregions) +        tag   = "region" +        index = nofregions +    elseif index ~= 0 then +        -- So we can cheat and pass a zero index and enforce tag as is needed in +        -- cases where we fallback on automated region tagging (framed). +        tag = tag .. ":" .. index      end      local box = getbox(n)      local w, h, d = getwhd(box) +    -- We could set directly but then we also need to check for gaps but as this +    -- is direct is is unlikely that we get a gap. We then also need to intecept +    -- these auto regions (comning from framed). Too messy and the split in the +    -- setter is fast enough.      tobesaved[tag] = {       -- p  = texgetcount("realpageno"), -- we copy them          x  = 0, @@ -532,8 +1127,18 @@ local function setregionbox(n,tag,k,lo,ro,to,bo,column) -- kind      return tag, box  end -local function markregionbox(n,tag,correct,...) -- correct needs checking -    local tag, box = setregionbox(n,tag,...) +-- we can have a finalizer property that we catch in the backend but that demands +-- a check for property for each list .. what is the impact + +-- textarea operates *inside* a box so experiments with pre/post hooks in the +-- backend driver didn't work out (because a box can be larger) +-- +-- it also gives no gain to split prefix and number here because in the end we +-- push and pop tags as strings, but it save a little on expansion so we do it +-- in the interface + +local function markregionbox(n,tag,index,correct,...) -- correct needs checking +    local tag, box = setregionbox(n,tag,index,...)       -- todo: check if tostring is needed with formatter      local push = new_latelua { action = b_region, tag = tag }      local pop  = new_latelua { action = e_region, correct = correct } @@ -577,195 +1182,290 @@ function jobpositions.settobesaved(name,tag,data)      end  end -local nofparagraphs = 0 +do -implement { -    name    = "parpos", -    actions = function() -        nofparagraphs = nofparagraphs + 1 -        texsetcount("global","c_anch_positions_paragraph",nofparagraphs) -        local box = getbox("strutbox") -        local w, h, d = getwhd(box) -        local t = { -            p  = true, -            c  = true, -            r  = true, -            x  = true, -            y  = true, -            h  = h, -            d  = d, -            hs = texget("hsize"), -- never 0 -        } -        local leftskip   = texget("leftskip",false) -        local rightskip  = texget("rightskip",false) -        local hangindent = texget("hangindent") -        local hangafter  = texget("hangafter") -        local parindent  = texget("parindent") -        local parshape   = texget("parshape") -        if leftskip ~= 0 then -            t.ls = leftskip -        end -        if rightskip ~= 0 then -            t.rs = rightskip -        end -        if hangindent ~= 0 then -            t.hi = hangindent +    local nofparagraphs = 0 + +    local function enhancepar_1(data) +        if data then +            local par   = data.par -- we can pass twice when we copy +            local state = par and getparstate(data.par,true) +            if state then +                local x, y = getpos() +                if x ~= 0 then +                    data.x  = x +                end +                if y ~= 0 then +                    data.y  = y +                end +                data.p = texgetcount("realpageno") -- we should use a variable set in otr +                if column then +                    data.c = column +                end +                if region then +                    data.r = region +                end +                -- +                data.par         = nil +                local leftskip   = state.leftskip +                local rightskip  = state.rightskip +                local hangindent = state.hangindent +                local hangafter  = state.hangafter +                local parindent  = state.parindent +                local parshape   = state.parshape +                if hangafter ~= 0 and hangafter ~= 1 then +                    data.ha = hangafter +                end +                if hangindent ~= 0 then +                    data.hi = hangindent +                end +                data.hs = state.hsize +                if leftskip ~= 0 then +                    data.ls = leftskip +                end +                if parindent ~= 0 then +                    data.pi = parindent +                end +                if rightskip ~= 0 then +                    data.rs = rightskip +                end +                if parshape and #parshape > 0 then +                    data.ps = parshape +                end +            end          end -        if hangafter ~= 1 and hangafter ~= 0 then -- can not be zero .. so it needs to be 1 if zero -            t.ha = hangafter +        return data +    end + +    local function enhancepar_2(data) +        if data then +            local x, y = getpos() +            if x ~= 0 then +                data.x  = x +            end +            if y ~= 0 then +                data.y  = y +            end +            data.p = texgetcount("realpageno") -- we should use a variable set in otr +            if column then +                data.c = column +            end +            if region then +                data.r = region +            end          end -        if parindent ~= 0 then -            t.pi = parindent +        return data +    end + +    implement { +        name    = "parpos", +        actions = function() +            nofparagraphs = nofparagraphs + 1 +            texsetcount("global","c_anch_positions_paragraph",nofparagraphs) +            local name = f_p_tag(nofparagraphs) +            local box = getbox("strutbox") +            local w, h, d = getwhd(box) +            -- +            local top = texgetnest("top","head") +            local nxt = top.next +            if nxt then +                nxt = tonut(nxt) +            end +            local data +            if nxt and getid(nxt) == par_code then -- todo: check node type +                local t = { +                    h   = h, +                    d   = d, +                    par = nxt, +                } +                tobesaved[name] = t +                ctx_latelua { action = enhancepar_1, specification = t } +            else +                -- This is kind of weird but it happens in tables (rows) so we probably +                -- need less. +                local state      = texgetparstate() +                local leftskip   = state.leftskip +                local rightskip  = state.rightskip +                local hangindent = state.hangindent +                local hangafter  = state.hangafter +                local parindent  = state.parindent +                local parshape   = state.parshape +                local t = { +                    p  = true, +                    c  = true, +                    r  = true, +                    x  = true, +                    y  = true, +                    h  = h, +                    d  = d, +                    hs = state.hsize, -- never 0 +                } +                if leftskip ~= 0 then +                    t.ls = leftskip +                end +                if rightskip ~= 0 then +                    t.rs = rightskip +                end +                if hangindent ~= 0 then +                    t.hi = hangindent +                end +                if hangafter ~= 1 and hangafter ~= 0 then -- can not be zero .. so it needs to be 1 if zero +                    t.ha = hangafter +                end +                if parindent ~= 0 then +                    t.pi = parindent +                end +                if parshape and #parshape > 0 then +                    t.ps = parshape +                end +                tobesaved[name] = t +                ctx_latelua { action = enhancepar_2, specification = t } +            end          end -        if parshape and #parshape > 0 then -            t.ps = parshape +    } + +    implement { +        name      = "dosetposition", +        arguments = "argument", +        public    = true, +        protected = true, +        actions   = function(name) +            local spec = { +                p   = true, +                c   = column, +                r   = true, +                x   = true, +                y   = true, +                n   = nofparagraphs > 0 and nofparagraphs or nil, +                r2l = texgetinteger("inlinelefttoright") == 1 or nil, +            } +            tobesaved[name] = spec +            ctx_latelua { action = enhance, specification = spec }          end -        local name = f_p_tag(nofparagraphs) -        tobesaved[name] = t -        ctx_latelua { action = enhance, specification = t } -    end -} +    } -implement { -    name      = "dosetposition", -    arguments = "argument", -    public    = true, -    protected = true, -    actions   = function(name) -        local spec = { -            p   = true, -            c   = column, -            r   = true, -            x   = true, -            y   = true, -            n   = nofparagraphs > 0 and nofparagraphs or nil, -            r2l = texgetinteger("inlinelefttoright") == 1 or nil, -        } -        tobesaved[name] = spec -        ctx_latelua { action = enhance, specification = spec } -    end -} +    implement { +        name      = "dosetpositionwhd", +        arguments = { "argument", "dimenargument", "dimenargument", "dimenargument" }, +        public    = true, +        protected = true, +        actions   = function(name,w,h,d) +            local spec = { +                p   = true, +                c   = column, +                r   = true, +                x   = true, +                y   = true, +                w   = w ~= 0 and w or nil, +                h   = h ~= 0 and h or nil, +                d   = d ~= 0 and d or nil, +                n   = nofparagraphs > 0 and nofparagraphs or nil, +                r2l = texgetinteger("inlinelefttoright") == 1 or nil, +            } +            tobesaved[name] = spec +            ctx_latelua { action = enhance, specification = spec } +        end +    } -implement { -    name      = "dosetpositionwhd", -    arguments = { "argument", "dimenargument", "dimenargument", "dimenargument" }, -    public    = true, -    protected = true, -    actions   = function(name,w,h,d) -        local spec = { -            p   = true, -            c   = column, -            r   = true, -            x   = true, -            y   = true, -            w   = w ~= 0 and w or nil, -            h   = h ~= 0 and h or nil, -            d   = d ~= 0 and d or nil, -            n   = nofparagraphs > 0 and nofparagraphs or nil, -            r2l = texgetinteger("inlinelefttoright") == 1 or nil, -        } -        tobesaved[name] = spec -        ctx_latelua { action = enhance, specification = spec } -    end -} +    implement { +        name      = "dosetpositionbox", +        arguments = { "argument", "integerargument" }, +        public    = true, +        protected = true, +        actions   = function(name,n) +            local box  = getbox(n) +            local w, h, d = getwhd(box) +            local spec = { +                p = true, +                c = column, +                r = true, +                x = true, +                y = true, +                w = w ~= 0 and w or nil, +                h = h ~= 0 and h or nil, +                d = d ~= 0 and d or nil, +                n = nofparagraphs > 0 and nofparagraphs or nil, +                r2l = texgetinteger("inlinelefttoright") == 1 or nil, +            } +            tobesaved[name] = spec +            ctx_latelua { action = enhance, specification = spec } +        end +    } -implement { -    name      = "dosetpositionbox", -    arguments = { "argument", "integerargument" }, -    public    = true, -    protected = true, -    actions   = function(name,n) -        local box  = getbox(n) -        local w, h, d = getwhd(box) -        local spec = { -            p = true, -            c = column, -            r = true, -            x = true, -            y = true, -            w = w ~= 0 and w or nil, -            h = h ~= 0 and h or nil, -            d = d ~= 0 and d or nil, -            n = nofparagraphs > 0 and nofparagraphs or nil, -            r2l = texgetinteger("inlinelefttoright") == 1 or nil, -        } -        tobesaved[name] = spec -        ctx_latelua { action = enhance, specification = spec } -    end -} +    implement { +        name      = "dosetpositionplus", +        arguments = { "argument", "dimenargument", "dimenargument", "dimenargument" }, +        public    = true, +        protected = true, +        actions   = function(name,w,h,d) +            local spec = { +                p   = true, +                c   = column, +                r   = true, +                x   = true, +                y   = true, +                w   = w ~= 0 and w or nil, +                h   = h ~= 0 and h or nil, +                d   = d ~= 0 and d or nil, +                n   = nofparagraphs > 0 and nofparagraphs or nil, +                e   = scanstring(), +                r2l = texgetinteger("inlinelefttoright") == 1 or nil, +            } +            tobesaved[name] = spec +            ctx_latelua { action = enhance, specification = spec } +        end +    } -implement { -    name      = "dosetpositionplus", -    arguments = { "argument", "dimenargument", "dimenargument", "dimenargument" }, -    public    = true, -    protected = true, -    actions   = function(name,w,h,d) -        local spec = { -            p   = true, -            c   = column, -            r   = true, -            x   = true, -            y   = true, -            w   = w ~= 0 and w or nil, -            h   = h ~= 0 and h or nil, -            d   = d ~= 0 and d or nil, -            n   = nofparagraphs > 0 and nofparagraphs or nil, -            e   = scanstring(), -            r2l = texgetinteger("inlinelefttoright") == 1 or nil, -        } -        tobesaved[name] = spec -        ctx_latelua { action = enhance, specification = spec } -    end -} +    implement { +        name      = "dosetpositionstrut", +        arguments = "argument", +        public    = true, +        protected = true, +        actions   = function(name) +            local box = getbox("strutbox") +            local w, h, d = getwhd(box) +            local spec = { +                p   = true, +                c   = column, +                r   = true, +                x   = true, +                y   = true, +                h   = h ~= 0 and h or nil, +                d   = d ~= 0 and d or nil, +                n   = nofparagraphs > 0 and nofparagraphs or nil, +                r2l = texgetinteger("inlinelefttoright") == 1 or nil, +            } +            tobesaved[name] = spec +            ctx_latelua { action = enhance, specification = spec } +        end +    } -implement { -    name      = "dosetpositionstrut", -    arguments = "argument", -    public    = true, -    protected = true, -    actions   = function(name) -        local box = getbox("strutbox") -        local w, h, d = getwhd(box) -        local spec = { -            p   = true, -            c   = column, -            r   = true, -            x   = true, -            y   = true, -            h   = h ~= 0 and h or nil, -            d   = d ~= 0 and d or nil, -            n   = nofparagraphs > 0 and nofparagraphs or nil, -            r2l = texgetinteger("inlinelefttoright") == 1 or nil, -        } -        tobesaved[name] = spec -        ctx_latelua { action = enhance, specification = spec } -    end -} +    implement { +        name      = "dosetpositionstrutkind", +        arguments = { "argument", "integerargument" }, +        public    = true, +        protected = true, +        actions   = function(name,kind) +            local box = getbox("strutbox") +            local w, h, d = getwhd(box) +            local spec = { +                k   = kind, +                p   = true, +                c   = column, +                r   = true, +                x   = true, +                y   = true, +                h   = h ~= 0 and h or nil, +                d   = d ~= 0 and d or nil, +                n   = nofparagraphs > 0 and nofparagraphs or nil, +                r2l = texgetinteger("inlinelefttoright") == 1 or nil, +            } +            tobesaved[name] = spec +            ctx_latelua { action = enhance, specification = spec } +        end +    } -implement { -    name      = "dosetpositionstrutkind", -    arguments = { "argument", "integerargument" }, -    public    = true, -    protected = true, -    actions   = function(name,kind) -        local box = getbox("strutbox") -        local w, h, d = getwhd(box) -        local spec = { -            k   = kind, -            p   = true, -            c   = column, -            r   = true, -            x   = true, -            y   = true, -            h   = h ~= 0 and h or nil, -            d   = d ~= 0 and d or nil, -            n   = nofparagraphs > 0 and nofparagraphs or nil, -            r2l = texgetinteger("inlinelefttoright") == 1 or nil, -        } -        tobesaved[name] = spec -        ctx_latelua { action = enhance, specification = spec } -    end -} +end  function jobpositions.getreserved(tag,n)      if tag == v_column then @@ -1384,18 +2084,18 @@ implement {      actions   = MPpardata  } -implement { -    name      = "MPposset", -    arguments = "argument", -    public    = true, -    actions   = function(name) -        local b = f_b_tag(name) -        local e = f_e_tag(name) -        local w = f_w_tag(name) -        local p = f_p_tag(getparagraph(b)) -        MPpos(b) context(",") MPpos(e) context(",") MPpos(w) context(",") MPpos(p) context(",") MPpardata(p) -    end -} +-- implement { +--     name      = "MPposset", +--     arguments = "argument", +--     public    = true, +--     actions   = function(name) +--         local b = f_b_tag(name) +--         local e = f_e_tag(name) +--         local w = f_w_tag(name) +--         local p = f_p_tag(getparagraph(b)) +--         MPpos(b) context(",") MPpos(e) context(",") MPpos(w) context(",") MPpos(p) context(",") MPpardata(p) +--     end +-- }  implement {      name      = "MPls", @@ -1451,7 +2151,7 @@ implement {  implement {      name      = "MPrest", -    arguments = { "argument", "argument" }, +    arguments = "2 arguments",      public    = true,      actions   = function(name,default)          local jpi = collected[name] @@ -1515,7 +2215,7 @@ implement {  implement {      name      = "doifelseoverlapping", -    arguments = { "argument", "argument" }, +    arguments = "2 arguments",      public    = true,      protected = true,      actions   = function(one,two) @@ -1548,53 +2248,53 @@ implement {      public    = true,      protected = true,      actions   = function() -        doifelse(next(collected)) +        doifelse(positionsused)      end  }  implement {      name      = "markregionbox", -    arguments = "integer", +    arguments = { "integer", "integer" },      actions   = markregionbox  }  implement {      name      = "setregionbox", -    arguments = "integer", +    arguments = { "integer", "integer" },      actions   = setregionbox  }  implement {      name      = "markregionboxtagged", -    arguments = { "integer", "string" }, +    arguments = { "integer", "string", "integer" },      actions   = markregionbox  }  implement {      name      = "markregionboxtaggedn", -    arguments = { "integer", "string", "integer" }, -    actions   = function(box,tag,n) -        markregionbox(box,tag,nil,nil,nil,nil,nil,nil,n) +    arguments = { "integer", "string", "integer", "integer" }, +    actions   = function(box,tag,index,n) +        markregionbox(box,tag,index,nil,nil,nil,nil,nil,nil,n)      end  }  implement {      name      = "setregionboxtagged", -    arguments = { "integer", "string" }, +    arguments = { "integer", "string", "integer" },      actions   = setregionbox  }  implement {      name      = "markregionboxcorrected", -    arguments = { "integer", "string", true }, +    arguments = { "integer", "string", "integer", true },      actions   = markregionbox  }  implement {      name      = "markregionboxtaggedkind", -    arguments = { "integer", "string", "integer", "dimen", "dimen", "dimen", "dimen" }, -    actions   = function(box,tag,n,d1,d2,d3,d4) -        markregionbox(box,tag,nil,n,d1,d2,d3,d4) +    arguments = { "integer", "string", "integer", "integer", "dimen", "dimen", "dimen", "dimen" }, +    actions   = function(box,tag,index,n,d1,d2,d3,d4) +        markregionbox(box,tag,index,nil,n,d1,d2,d3,d4)      end  } @@ -1607,27 +2307,6 @@ implement {      end  } --- statistics (at least for the moment, when testing) - --- statistics.register("positions", function() ---     local total = nofregular + nofusedregions + nofmissingregions ---     if total > 0 then ---         return format("%s collected, %s regulars, %s regions, %s unresolved regions", ---             total, nofregular, nofusedregions, nofmissingregions) ---     else ---         return nil ---     end --- end) - -statistics.register("positions", function() -    local total = nofregular + nofspecial -    if total > 0 then -        return format("%s collected, %s regular, %s special",total,nofregular,nofspecial) -    else -        return nil -    end -end) -  -- We support the low level positional commands too:  local newsavepos = nodes.pool.savepos diff --git a/tex/context/base/mkxl/anch-pos.mkxl b/tex/context/base/mkxl/anch-pos.mkxl index 2bf7fab6f..70197e93e 100644 --- a/tex/context/base/mkxl/anch-pos.mkxl +++ b/tex/context/base/mkxl/anch-pos.mkxl @@ -164,7 +164,7 @@    {\anch_positions_initialize     \hbox % \hpack       {\edef\currentposition{#1}% -      \dosetpositionwhd\currentposition{#2}{#3}{#4}% already \the\dimexpr +      \dosetpositionwhd\currentposition{#2}{#3}{#4}%        \anch_positions_trace_left        \dopositionaction\currentposition        \hss}} @@ -184,7 +184,8 @@  \def\anch_positions_set_box_finish#1%    {\anch_positions_initialize -   \hbox to \wd\nextbox % \hpack +  %\hbox to \wd\nextbox +   \hpack to \wd\nextbox       {\edef\currentposition{#1}%        \dosetpositionbox\currentposition\nextbox        \anch_positions_trace_left @@ -244,7 +245,7 @@    {\anch_positions_initialize     \hbox % \hpack       {\edef\currentposition{#1}% -      \dosetpositionplus\currentposition{#2}{#3}{#4}{#5}% already \the\dimexpr +      \dosetpositionplus\currentposition{#2}{#3}{#4}{#5}%        \anch_positions_trace_right        \dopositionaction\currentposition        \hss}} @@ -288,12 +289,15 @@  % Is this really always needed? We use \enabletextarearegistration for page areas so why  % not also for this. +% +% At some point we can switch to dedicated markers because there are not +% that many variants: text, page, textarea, columnarea, free.  \protected\def\anch_mark_column_box#1#2% box n    {\global\advance\c_anch_column\plusone -   \clf_markregionboxtaggedn#1{columnarea:\the\c_anch_column}#2\relax} % extra height +   \clf_markregionboxtaggedn#1{columnarea}\c_anch_column#2\relax} % extra height -\protected\def\anch_mark_region_box +\protected\def\anch_mark_region_box % auto region:index    {\iftrialtypesetting       \expandafter\gobbleoneargument     \orelse\ifpositioning @@ -308,38 +312,45 @@  \protected\def\anch_mark_flow_box#1% will be extended / renamed    {\hpack\bgroup     \global\advance\c_anch_text\plusone -   \clf_markregionboxtagged#1{textarea:\the\c_anch_text}% +   \clf_markregionboxtagged#1{textarea}\c_anch_text % will become flow:     \box#1%     \egroup} -\protected\def\anch_mark_tagged_box#1#2% -  {\clf_markregionboxtagged#1{#2}} +\protected\def\anch_mark_tagged_box#1#2#3% +  {\clf_markregionboxtagged#1{#2}#3\relax}  \protected\def\anch_mark_flow_only#1% will be extended / renamed    {\global\advance\c_anch_text\plusone -   \clf_markregionboxcorrected#1{textarea:\the\c_anch_text}} +   \clf_markregionboxcorrected#1{textarea}\c_anch_text}% will become flow:  \protected\def\anch_make_page_box#1% maybe like text -  {\clf_setregionboxtagged#1{page:\the\realpageno}} +  {\clf_setregionboxtagged#1{page}\realpageno}  \protected\def\anch_mark_text_box#1% -  {\clf_markregionboxtagged#1{text:\the\realpageno}} % needs an hbox +  {\clf_markregionboxtagged#1{text}\realpageno} % needs an hbox -\newcount\c_anch_free - -\protected\def\anch_mark_tagged_box_free#1#2#3#4#5#6% only needed when positions +\protected\def\anch_mark_tagged_box_free    {\ifpositioning -     \global\advance\c_anch_free\plusone % could be done at the lua end -     \clf_markregionboxtaggedkind -       #1% -       {free:\number\c_anch_free}% -       #2\space % kind -       #3\space % leftoffset -       #4\space % rightoffset -       #5\space % topoffset -       #6\relax % bottomoffset +     \expandafter\anch_mark_tagged_box_free_yes +   \else +     \expandafter\gobblesixarguments     \fi} +\newcount\c_anch_free + +\protected\def\anch_mark_tagged_box_free_yes#1#2#3#4#5#6% only needed when positions +  {\global\advance\c_anch_free\plusone % could be done at the lua end +   \clf_markregionboxtaggedkind +     #1% +     {free}% +     \c_anch_free +     #2% kind         % single token value +     #3% leftoffset   % single token value +     #4% rightoffset  % single token value +     #5% topoffset    % single token value +     #6% bottomoffset % single token value +   \relax} +  % \reservedautoregiontag % define at lua end  %D We can copy a position with: diff --git a/tex/context/base/mkxl/anch-snc.lmt b/tex/context/base/mkxl/anch-snc.lmt index 57789a5a0..c97be2b39 100644 --- a/tex/context/base/mkxl/anch-snc.lmt +++ b/tex/context/base/mkxl/anch-snc.lmt @@ -6,7 +6,7 @@ if not modules then modules = { } end modules ['anch-snc'] = {      license   = "see context related readme files"  } -local tonumber, next, setmetatable = tonumber, next, setmetatable +local tonumber, next, setmetatable, rawget = tonumber, next, setmetatable, rawget  local concat, sort, remove, copy = table.concat, table.sort, table.remove, table.copy  local match, find = string.match, string.find  local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns @@ -17,9 +17,10 @@ graphics                = graphics or { }  local synchronizers     = { }  graphics.synchronizers  = synchronizers +local jobpositions      = job.positions +  local p_number = lpegpatterns.cardinal/tonumber  local p_space  = lpegpatterns.whitespace^0 -local p_tag    = P("syncpos:") * p_number * P(":") * p_number  local p_option = p_number * ((P(",") * p_space * P("reset") * Cc(true)) + Cc(false)) -- for now  local list     = { } @@ -31,39 +32,10 @@ local kinds    = {      below    = 5,  } -local allentries = setmetatableindex(function(t,category) -    setmetatable(t,nil) -    for tag, pos in next, job.positions.collected do -        local c, n = lpegmatch(p_tag,tag) -        if c then -            local tc = t[c] -            if tc then -                tc[n] = pos -            else -                t[c] = { [n] = pos } -            end -        end -    end -    for k, list in next, t do -        sort(list,function(a,b) -            local ap = a.p -            local bp = b.p -            if ap == bp then -                return b.y < a.y -            else -                return ap < bp -            end -        end) -        list.start = 1 -    end -    setmetatableindex(t,"table") -    return t[category] -end) -  local lastdone = { }  function synchronizers.collect(category,realpage,region) -    local all = allentries[category] +    local all = jobpositions.getsync(category)      local m   = 0      local n   = #all      list = { } @@ -78,7 +50,7 @@ function synchronizers.collect(category,realpage,region)              local r = pos.r              if r == region then                  if not done then -                    local region = job.positions.collected[r] +                    local region = jobpositions.collected[r]                      list.region  = region                      list.page    = region                      rtop = (region.y or 0) + (region.h or 0) @@ -106,8 +78,8 @@ function synchronizers.collect(category,realpage,region)              local p = pos.p              if p == realpage then                  if not done then -                    local region = job.positions.collected[pos.r] -                    local page   = job.positions.collected["page:"..realpage] or region +                    local region = jobpositions.collected[pos.r] +                    local page   = jobpositions.collected["page:"..realpage] or region                      list.region  = region                      list.page    = page                      rtop = (region.y or 0) + (region.h or 0) @@ -224,6 +196,8 @@ function synchronizers.collapse()      return n  end +-- These operate on the currently set list: +  function synchronizers.getsize  ()  return #list               end  function synchronizers.gettop   (n) return list[n].top         end  function synchronizers.getbottom(n) return list[n].bottom      end diff --git a/tex/context/base/mkxl/anch-snc.mkxl b/tex/context/base/mkxl/anch-snc.mkxl index 436305634..dcf2c86cb 100644 --- a/tex/context/base/mkxl/anch-snc.mkxl +++ b/tex/context/base/mkxl/anch-snc.mkxl @@ -125,10 +125,12 @@  %D \stoptext  %D \stoptyping +%D We could actually use par positions but in \LMTX\ we have syncpos entries +%D somewhat optimized. +  \unprotect -\ifdefined\??syncposcounter \else \installcorenamespace{syncposcounter} \fi -\ifdefined\s!syncpos        \else \def\s!syncpos       {syncpos}        \fi +\installcorenamespace{syncposcounter}  \let\c_sync_n\relax @@ -137,7 +139,7 @@       \expandafter\newcount\csname\??syncposcounter:#1\endcsname     \fi} -\permanent\tolerant\protected\def\syncposition[#1]#*[#2]% we could actually use par positions +\permanent\tolerant\protected\def\syncposition[#1]#*[#2]%    {\dontleavehmode     \ifcsname\??syncposcounter:#1\endcsname       \let\c_sync_n\lastnamedcs diff --git a/tex/context/base/mkxl/back-exp.lmt b/tex/context/base/mkxl/back-exp.lmt index a465cee92..de1a1830e 100644 --- a/tex/context/base/mkxl/back-exp.lmt +++ b/tex/context/base/mkxl/back-exp.lmt @@ -334,9 +334,9 @@ local styletemplate <const> = [[      local numbertoallign = {          [0] = "justify", ["0"] = "justify", [variables.normal    ] = "justify", -        [1] = "right",   ["1"] = "right",   [variables.flushright] = "right", -        [2] = "center",  ["2"] = "center",  [variables.middle    ] = "center", -        [3] = "left",    ["3"] = "left",    [variables.flushleft ] = "left", +              "right",   ["1"] = "right",   [variables.flushright] = "right", +              "center",  ["2"] = "center",  [variables.middle    ] = "center", +              "left",    ["3"] = "left",    [variables.flushleft ] = "left",      }      function wrapups.allusedstyles(filename) diff --git a/tex/context/base/mkxl/buff-ini.mkxl b/tex/context/base/mkxl/buff-ini.mkxl index a91e3272b..ed16bd6e3 100644 --- a/tex/context/base/mkxl/buff-ini.mkxl +++ b/tex/context/base/mkxl/buff-ini.mkxl @@ -115,7 +115,8 @@  \setupbuffer    [\c!before=, -   \c!after=] +   \c!after=, +   \c!define=\v!yes]  \newcount\c_buff_n_of_defined @@ -125,8 +126,12 @@      \global\advance\c_buff_n_of_defined\plusone      \setexpandedbufferparameter\c!number{\number\c_buff_n_of_defined}%      \edef\currentdefinedbuffer{def-\number\c_buff_n_of_defined}% -    \frozen\instance\setuevalue{\e!start\currentbuffer}{\buff_start_defined{\currentbuffer}{\currentdefinedbuffer}{\e!start\currentbuffer}{\e!stop\currentbuffer}}% -    \frozen\instance\setuevalue{\e!get  \currentbuffer}{\buff_get_stored   {\currentbuffer}{\currentdefinedbuffer}}% +    \ifcstok{\bufferparameter\c!define}\v!yes +      \frozen\instance\protected\edefcsname\e!start\currentbuffer\endcsname +        {\buff_start_defined{\currentbuffer}{\currentdefinedbuffer}{\e!start\currentbuffer}{\e!stop\currentbuffer}}% +      \frozen\instance\protected\edefcsname\e!get  \currentbuffer\endcsname +        {\buff_get_stored   {\currentbuffer}{\currentdefinedbuffer}}% +    \fi  \to \everydefinebuffer  \protected\def\buff_start_defined diff --git a/tex/context/base/mkxl/cont-new.mkxl b/tex/context/base/mkxl/cont-new.mkxl index 74d3aa873..7b5f090d9 100644 --- a/tex/context/base/mkxl/cont-new.mkxl +++ b/tex/context/base/mkxl/cont-new.mkxl @@ -13,7 +13,7 @@  % \normalend % uncomment this to get the real base runtime -\newcontextversion{2021.08.10 12:37} +\newcontextversion{2021.08.19 19:40}  %D This file is loaded at runtime, thereby providing an excellent place for hacks,  %D patches, extensions and new features. There can be local overloads in cont-loc diff --git a/tex/context/base/mkxl/cont-run.lmt b/tex/context/base/mkxl/cont-run.lmt index a256a4104..448992b4b 100644 --- a/tex/context/base/mkxl/cont-run.lmt +++ b/tex/context/base/mkxl/cont-run.lmt @@ -219,6 +219,14 @@ local function processjob()      environment.lmtxmode = CONTEXTLMTXMODE -- should be true +    -- directives +    -- silent +    -- batchmode + +if arguments.directives then +    directives.enable(arguments.directives) +end +      if arguments.nosynctex then          luatex.synctex.setup {              state  = interfaces.variables.never, diff --git a/tex/context/base/mkxl/context.mkxl b/tex/context/base/mkxl/context.mkxl index 91ec43d30..978808ec5 100644 --- a/tex/context/base/mkxl/context.mkxl +++ b/tex/context/base/mkxl/context.mkxl @@ -29,7 +29,7 @@  %D {YYYY.MM.DD HH:MM} format.  \immutable\edef\contextformat {\jobname} -\immutable\edef\contextversion{2021.08.10 12:37} +\immutable\edef\contextversion{2021.08.19 19:40}  %overloadmode 1 % check frozen / warning  %overloadmode 2 % check frozen / error diff --git a/tex/context/base/mkxl/core-sys.mkxl b/tex/context/base/mkxl/core-sys.mkxl index a0bd9debd..b8bd7905e 100644 --- a/tex/context/base/mkxl/core-sys.mkxl +++ b/tex/context/base/mkxl/core-sys.mkxl @@ -257,7 +257,8 @@  \appendtoks      \ifcstok{\highlightparameter\c!define}\v!yes -        \frozen\instance\setuevalue\currenthighlight{\typo_highlights_indeed{\currenthighlight}}% +        \frozen\instance\protected\edefcsname\currenthighlight\endcsname +           {\typo_highlights_indeed{\currenthighlight}}%      \fi  \to \everydefinehighlight @@ -291,6 +292,7 @@  \aliased\let\directhighlight\typo_highlights_indeed +%D Defining commands (rather old):  \permanent\protected\def\defineexpandable    {\integerdef\c_syst_parameter_catcode\catcode\hashasciicode diff --git a/tex/context/base/mkxl/core-uti.lmt b/tex/context/base/mkxl/core-uti.lmt index 1bb50060d..de514df30 100644 --- a/tex/context/base/mkxl/core-uti.lmt +++ b/tex/context/base/mkxl/core-uti.lmt @@ -19,7 +19,7 @@ saves much runtime but at the cost of more memory usage.</p>  local math = math  local format, match = string.format, string.match -local next, type, tostring, tonumber = next, type, tostring, tonumber +local next, type, tostring, tonumber, setmetatable = next, type, tostring, tonumber, setmetatable  local concat = table.concat  local definetable    = utilities.tables.definetable @@ -43,7 +43,7 @@ local report_passes  = logs.reporter("job","passes")  job                  = job or { }  local job            = job -job.version          = 1.32 +job.version          = 1.33  job.packversion      = 1.02  -- some day we will implement loading of other jobs and then we need @@ -209,6 +209,67 @@ local skiplist = {      "positions",  } +-- I'm not that impressed by the savings. It's some 5 percent on the luametatex +-- manual and probably some more on the m4 files (if so I might enable it). + +local deltapacking = false +-- local deltapacking = true + +local function packnumberdata(tobesaved) +    if deltapacking and tobesaved[1] then +        local last +        local current +        for i=1,#tobesaved do +            current = tobesaved[i] +            if last then +                if last.numbers and last.block then +                    for k, v in next, last do +                        if k ~= "numbers" and v ~= current[k] then +                            goto DIFFERENT +                        end +                    end +                    for k, v in next, current do +                        if k ~= "numbers" and v ~= last[k] then +                            goto DIFFERENT +                        end +                    end +                    tobesaved[i] = { +                        numbers = current.numbers, +                    } +                    goto CONTINUE +                else +                    current = nil +                end +            end +          ::DIFFERENT:: +            last = current +          ::CONTINUE:: +        end +    end +end + +local function unpacknumberdata(collected) +    if deltapacking and collected[1] then +        local key  = "numbers" +        local last = collected[1] +        local meta = false +        for i=2,#collected do +            local c = collected[i] +            if c.block then +                last = c +                meta = false +            elseif c.numbers then +                if not meta then +                    meta = { __index = last } +                end +                setmetatable(c, meta) +            end +        end +    end +end + +-- -- -- +  -- not ok as we can have arbitrary keys in userdata and dataset so some day we  -- might need a bit more granularity, like skippers @@ -253,6 +314,7 @@ function job.save(filename) -- we could return a table but it can get pretty lar          end          if job.pack then              packers.strip(jobpacker) +packnumberdata(jobpacker.index)           -- f:write("do\n\n")              f:write(serialize(jobpacker,"utilitydata.job.packed",true),"\n\n")           -- f:write("end\n\n") @@ -265,7 +327,6 @@ end  local function load(filename)      if lfs.isfile(filename) then -          local function dofile(filename)              local result = loadstring(io.loaddata(filename))              if result then @@ -274,7 +335,6 @@ local function load(filename)                  return nil              end          end -          local okay, data = pcall(dofile,filename)          if okay and type(data) == "table" then              local jobversion  = job.version @@ -299,28 +359,26 @@ function job.load(filename)      local utilitydata = load(filename)      if utilitydata then          local jobpacker = utilitydata.job.packed -        local handlers  = { } +unpacknumberdata(jobpacker.index)          for i=1,#savelist do -            local list        = savelist[i] -            local target      = list[1] -            local initializer = list[3] -            local result      = accesstable(target,utilitydata) +            local list   = savelist[i] +            local target = list[1] +            local result = accesstable(target,utilitydata)              if result then                  local done = packers.unpack(result,jobpacker,true)                  if done then                      migratetable(target,mark(result)) -                    if type(initializer) == "function" then -                        handlers[#handlers+1] = { initializer, result } -                    end                  else                      report_passes("pack version mismatch")                  end              end          end -        -- so we have all tables available (unpacked) -        for i=1,#handlers do -            local handler = handlers[i] -            handler[1](handler[2]) +    end +    for i=1,#savelist do +        local list        = savelist[i] +        local initializer = list[3] +        if type(initializer) == "function" then +            initializer(utilitydata and accesstable(list[1],utilitydata) or nil)          end      end      statistics.stoptiming(_load_) diff --git a/tex/context/base/mkxl/driv-shp.lmt b/tex/context/base/mkxl/driv-shp.lmt index 65221abf5..d84ecbcbc 100644 --- a/tex/context/base/mkxl/driv-shp.lmt +++ b/tex/context/base/mkxl/driv-shp.lmt @@ -148,7 +148,6 @@ local flush_character do      local vfinjectors = fonts.helpers.vfinjectors      local function flush_vf_packet(current,pos_h,pos_v,pos_r,font,char,data,factor,vfcommands,sx,sy) -          if nesting > 100 then              return          elseif nesting == 0 then @@ -194,7 +193,6 @@ local flush_character do          end          local refactored = 1000000 -          for i=1,#vfcommands do              local packet  = vfcommands[i]              local command = packet[1] diff --git a/tex/context/base/mkxl/file-job.lmt b/tex/context/base/mkxl/file-job.lmt index dac6f6d9a..84186a2d0 100644 --- a/tex/context/base/mkxl/file-job.lmt +++ b/tex/context/base/mkxl/file-job.lmt @@ -550,7 +550,9 @@ end  local function poptree()      top = remove(treestack) - -- inspect(top) +    if #top[#top].branches == 0 then +        top[#top].branches = nil -- saves space in tuc +    end  end  do @@ -558,10 +560,15 @@ do      local function log_tree(report,top,depth)          report("%s%s: %s",depth,top.type,top.name)          local branches = top.branches -        if #branches > 0 then -            depth = depth .. "  " -            for i=1,#branches do -                log_tree(report,branches[i],depth) +        if branches then +            local n = #branches +            if n > 0 then +                depth = depth .. "  " +                for i=1,n do +                    log_tree(report,branches[i],depth) +                end +            else +                top.brances = nil -- saves space in tuc              end          end      end @@ -1046,6 +1053,7 @@ function document.setcommandline() -- has to happen at the tex end in order to e      -- in the new mtx=context approach we always pass a stub file so we need to      -- to trick the files table which actually only has one entry in a tex job +      if arguments.timing then          context.usemodule { "timing" }      end diff --git a/tex/context/base/mkxl/font-def.lmt b/tex/context/base/mkxl/font-def.lmt index 614f98036..d71132180 100644 --- a/tex/context/base/mkxl/font-def.lmt +++ b/tex/context/base/mkxl/font-def.lmt @@ -183,6 +183,7 @@ function resolvers.file(specification)          specification.forcedname = name          specification.name       = removesuffix(name)      else +        -- maybe when no suffix and not found, then still run over suffixes          specification.name       = name -- can be resolved      end  end diff --git a/tex/context/base/mkxl/font-lib.mklx b/tex/context/base/mkxl/font-lib.mklx index d865da949..9985df105 100644 --- a/tex/context/base/mkxl/font-lib.mklx +++ b/tex/context/base/mkxl/font-lib.mklx @@ -46,7 +46,7 @@  \registerctxluafile{font-ota}{autosuffix}  % \registerctxluafile{font-ots-pre-scale}{autosuffix,optimize}  \registerctxluafile{font-ots}{autosuffix,optimize} -\registerctxluafile{font-otd}{optimize} +\registerctxluafile{font-otd}{autosuffix,optimize}  \registerctxluafile{font-otc}{}  \registerctxluafile{font-oth}{}  \registerctxluafile{font-osd}{} diff --git a/tex/context/base/mkxl/font-ogr.lmt b/tex/context/base/mkxl/font-ogr.lmt index ef0af904c..87166b36d 100644 --- a/tex/context/base/mkxl/font-ogr.lmt +++ b/tex/context/base/mkxl/font-ogr.lmt @@ -36,6 +36,9 @@ otf.pngenabled    = true  -- what here and what in backend ... +local report_fonts = logs.reporter("backend","fonts") +local trace_fonts    trackers.register("backend.fonts",function(v) trace_fonts = v end) +  do      -- This is a prelude to something better but I'm still experimenting. We should delay more. @@ -59,7 +62,8 @@ do          local t_properties        = t_tfmdata.properties          local d_tfmdata           = setmetatableindex({ },t_tfmdata)          local d_properties        = setmetatableindex({ },t_properties) -        d_properties.basefontname = "ContextRuntimeFont" .. droppedin +        local d_basefontname      = "ContextRuntimeFont" .. droppedin +        d_properties.basefontname = d_basefontname          d_tfmdata.properties      = d_properties          local d_characters        = { } -- setmetatableindex({ },t_characters)   -- hm, index vs unicode          local d_descriptions      = { } -- setmetatableindex({ },t_descriptions) -- hm, index vs unicode @@ -75,6 +79,9 @@ do          d_properties.indexdata    = { indexdata, ... } -- can take quite some memory          local slot                = #fonts + 1          fonts[slot]               = { id = droppedin } +        if trace_fonts then +            report_fonts("registering dropin %a using method %a",d_basefontname,method) +        end          return slot, droppedin, d_tfmdata, d_properties      end @@ -84,9 +91,10 @@ do          if method and shapes then              local characters   = tfmdata.characters              local descriptions = tfmdata.descriptions -            local droppedin, tfmdrop, dropchars, dropdescs, colrshapes +            local droppedin, tfmdrop, dropchars, dropdescs, colrshapes, props              local idx  = 255              local slot = 0 +            -- sorted ?              for k, v in next, characters do                  local index = v.index                  if index then @@ -97,7 +105,7 @@ do                              if idx >= 255 then                                  idx = 1                                  colrshapes = setmetatableindex({ },shapes) -                                slot, droppedin, tfmdrop = dropins.provide(method,tfmdata,colrshapes) +                                slot, droppedin, tfmdrop, props = dropins.provide(method,tfmdata,colrshapes)                                  dropchars = tfmdrop.characters                                  dropdescs = tfmdrop.descriptions                              else diff --git a/tex/context/base/mkxl/font-otd.lmt b/tex/context/base/mkxl/font-otd.lmt new file mode 100644 index 000000000..3f1b212db --- /dev/null +++ b/tex/context/base/mkxl/font-otd.lmt @@ -0,0 +1,284 @@ +if not modules then modules = { } end modules ['font-otd'] = { +    version   = 1.001, +    optimize  = true, +    comment   = "companion to font-ini.mkiv", +    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL", +    copyright = "PRAGMA ADE / ConTeXt Development Team", +    license   = "see context related readme files" +} + +local type = type +local match = string.match +local sequenced = table.sequenced + +local trace_dynamics     = false  trackers.register("otf.dynamics", function(v) trace_dynamics = v end) +local trace_applied      = false  trackers.register("otf.applied",  function(v) trace_applied      = v end) + +local report_otf         = logs.reporter("fonts","otf loading") +local report_process     = logs.reporter("fonts","otf process") + +local allocate           = utilities.storage.allocate + +local fonts              = fonts +local otf                = fonts.handlers.otf +local hashes             = fonts.hashes +local definers           = fonts.definers +local constructors       = fonts.constructors +local specifiers         = fonts.specifiers + +local fontidentifiers    = hashes.identifiers +local fontresources      = hashes.resources +local fontproperties     = hashes.properties +local fontdynamics       = hashes.dynamics + +local contextsetups      = specifiers.contextsetups +local contextnumbers     = specifiers.contextnumbers +local contextmerged      = specifiers.contextmerged + +local setmetatableindex  = table.setmetatableindex + +local a_to_script        = { } +local a_to_language      = { } + +-- we can have a scripts hash in fonts.hashes + +function otf.setdynamics(font,attribute) + -- local features = contextsetups[contextnumbers[attribute]] -- can be moved to caller +    local features = contextsetups[attribute] +    if features then +        local dynamics = fontdynamics[font] +        dynamic = contextmerged[attribute] or 0 +        local script, language +        if dynamic == 2 then -- merge +            language  = features.language or fontproperties[font].language or "dflt" +            script    = features.script   or fontproperties[font].script   or "dflt" +        else -- if dynamic == 1 then -- replace +            language  = features.language or "dflt" +            script    = features.script   or "dflt" +        end +        if script == "auto" then +            -- checkedscript and resources are defined later so we cannot shortcut them -- todo: make installer +            script = definers.checkedscript(fontidentifiers[font],fontresources[font],features) +        end +        local ds = dynamics[script] -- can be metatable magic (less testing) +-- or dynamics.dflt +        if not ds then +            ds = { } +            dynamics[script] = ds +        end +        local dsl = ds[language] +-- or ds.dflt +        if not dsl then +            dsl = { } +            ds[language] = dsl +        end +        local dsla = dsl[attribute] +        if not dsla then +            local tfmdata = fontidentifiers[font] +            a_to_script  [attribute] = script +            a_to_language[attribute] = language +            -- we need to save some values .. quite messy +            local properties = tfmdata.properties +            local shared     = tfmdata.shared +            local s_script   = properties.script +            local s_language = properties.language +            local s_mode     = properties.mode +            local s_features = shared.features +            properties.mode     = "node" +            properties.language = language +            properties.script   = script +            properties.dynamics = true -- handy for tracing +            shared.features     = { } +            -- end of save +            local set = constructors.checkedfeatures("otf",features) +            set.mode = "node" -- really needed +            dsla = otf.setfeatures(tfmdata,set) +            if trace_dynamics then +                report_otf("setting dynamics %s: attribute %a, script %a, language %a, set %a",contextnumbers[attribute],attribute,script,language,set) +            end +            -- we need to restore some values +            properties.script   = s_script +            properties.language = s_language +            properties.mode     = s_mode +            shared.features     = s_features +            -- end of restore +            dynamics[script][language][attribute] = dsla -- cache +        elseif trace_dynamics then +         -- report_otf("using dynamics %s: attribute %a, script %a, language %a",contextnumbers[attribute],attribute,script,language) +        end +        return dsla +    end +end + +function otf.scriptandlanguage(tfmdata,attr) +    local properties = tfmdata.properties +    if attr and attr > 0 then +        return a_to_script[attr] or properties.script or "dflt", a_to_language[attr] or properties.language or "dflt" +    else +        return properties.script or "dflt", properties.language or "dflt" +    end +end + +-- we reimplement the dataset resolver + +local autofeatures    = fonts.analyzers.features +local featuretypes    = otf.tables.featuretypes +local defaultscript   = otf.features.checkeddefaultscript +local defaultlanguage = otf.features.checkeddefaultlanguage + +local resolved = { } -- we only resolve a font,script,language,attribute pair once +local wildcard = "*" + +-- what about analyze in local and not in font + +-- needs checking: some added features can pass twice + +local P, C, Cc, lpegmatch = lpeg.P, lpeg.C, lpeg.Cc, lpeg.match + +local pattern = P("always") * (P(-1) * Cc(true) + P(":") * C((1-P(-1))^1)) + +local function initialize(sequence,script,language,s_enabled,a_enabled,font,attr,dynamic,ra,autoscript,autolanguage) +    local features = sequence.features +    if features then +        local order = sequence.order +        if order then +            local featuretype = featuretypes[sequence.type or "unknown"] +            local lookupdone  = false +            for i=1,#order do -- +                local kind = order[i] -- +                local e_e +                local a_e = a_enabled and a_enabled[kind] -- the value (location) +                if a_e ~= nil then +                    e_e = a_e +                else +                    e_e = s_enabled and s_enabled[kind] -- the value (font) +                end +                if e_e then +                    local usedattribute, usedscript, usedlanguage, usedlookup +                    local valid = type(e_e) == "string" and lpegmatch(pattern,e_e) +                    if valid then +                        -- we have hit always +                        usedattribute = autofeatures[kind] or false +                        usedlanguage  = "*" +                        usedscript    = "*" +                        usedlookup    = { valid, usedattribute, sequence, kind } +                    else +                        -- we already checked for e_e +                        local scripts   = features[kind] -- +                        local languages = scripts[script] or scripts[wildcard] +                        if not languages and autoscript then +                            langages = defaultscript(featuretype,autoscript,scripts) +                        end +                        if languages then +                            -- we need detailed control over default because we want to trace +                            -- only first attribute match check, so we assume simple fina's +                         -- local valid = false +                            if languages[language] then +                                valid = e_e +                            elseif languages[wildcard] then +                                valid = e_e +                            elseif autolanguage and defaultlanguage(featuretype,autolanguage,languages) then +                                valid = e_e +                            end +                        end +                        if valid then +                            usedattribute = autofeatures[kind] or false +                            usedlanguage  = script +                            usedscript    = language +                            usedlookup    = { valid, usedattribute, sequence, kind } +                        end +                    end +                    if not usedlookup then +                        -- go on +                    elseif lookupdone then +                        if trace_applied then +                            report_process( +                                "font %s, dynamic %a (%a), feature %a, script %a, language %a, lookup %a, value %a, nofsteps %a, lookup already set by %a", +                                    font,attr or 0,dynamic,kind,usedscript,usedlanguage,sequence.name,valid,sequence.nofsteps,ra[#ra][4]) +                        end +                    else +                        ra[#ra+1] = usedlookup +                        if trace_applied then +                            report_process( +                                "font %s, dynamic %a (%a), feature %a, script %a, language %a, lookup %a, value %a, nofsteps %a", +                                    font,attr or 0,dynamic,kind,usedscript,usedlanguage,sequence.name,valid,sequence.nofsteps) +                        else +                            return -- no need to look further +                        end +                        lookupdone = true +                    end +                end +            end +        end +    end +end + +-- there is some fuzzy language/script state stuff in properties (temporary) + +function otf.dataset(tfmdata,font,attr) -- attr only when explicit (as in special parbuilder) + +    local script, language, s_enabled, a_enabled, dynamic + +    if attr and attr ~= 0 then +        dynamic = contextmerged[attr] or 0 +     -- local features = contextsetups[contextnumbers[attr]] -- could be a direct list +        local features = contextsetups[attr] +        a_enabled = features -- location based +        if dynamic == 1 then -- or dynamic == -1 then +            -- replace +            language  = features.language or "dflt" +            script    = features.script   or "dflt" +        elseif dynamic == 2 then -- or dynamic == -2 then +            -- merge +            local properties = tfmdata.properties +            s_enabled = tfmdata.shared.features -- font based +            language  = features.language or properties.language or  "dflt" +            script    = features.script   or properties.script   or  "dflt" +        else +            -- error +            local properties = tfmdata.properties +            language  = properties.language or "dflt" +            script    = properties.script   or "dflt" +        end +    else +        local properties = tfmdata.properties +        language  = properties.language or "dflt" +        script    = properties.script   or "dflt" +        s_enabled = tfmdata.shared.features -- can be made local to the resolver +        dynamic   = 0 +    end + +    local res = resolved[font] +    if not res then +        res = { } +        resolved[font] = res +    end +    local rs = res[script] +    if not rs then +        rs = { } +        res[script] = rs +    end +    local rl = rs[language] +    if not rl then +        rl = { } +        rs[language] = rl +    end +    local ra = rl[attr] +    if ra == nil then -- attr can be false +        ra = { +            -- indexed but we can also add specific data by key in: +        } +        rl[attr] = ra +        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) +            for s=1,#sequences do +                -- just return nil or ra step +                initialize(sequences[s],script,language,s_enabled,a_enabled,font,attr,dynamic,ra,autoscript,autolanguage) +            end +        end +    end +    return ra + +end diff --git a/tex/context/base/mkxl/font-ots.lmt b/tex/context/base/mkxl/font-ots.lmt index b8fbc9702..ca6490fc0 100644 --- a/tex/context/base/mkxl/font-ots.lmt +++ b/tex/context/base/mkxl/font-ots.lmt @@ -210,8 +210,7 @@ local getscales          = nuts.getscales  ---------------------------------------------------------------------------------------  -- Beware: In ConTeXt components no longer are real components. We only keep track of --- their positions because some complex ligatures might need that. For the moment we --- use an x_ prefix because for now generic follows the other approach. +-- their positions because some complex ligatures might need that.  local components         = nuts.components  local copynocomponents   = components.copynocomponents @@ -244,9 +243,6 @@ local setmetatableindex  = table.setmetatableindex  local nextnode           = nuts.traversers.node ------ zwnj               = 0x200C ------ zwj                = 0x200D -  local nodecodes          = nodes.nodecodes  local glyphcodes         = nodes.glyphcodes @@ -261,7 +257,7 @@ local lefttoright_code   = nodes.dirvalues.lefttoright  local righttoleft_code   = nodes.dirvalues.righttoleft  local discretionarydisc_code = nodes.disccodes.discretionary -local ligatureglyph_code     = glyphcodes.ligature +----- ligatureglyph_code     = glyphcodes.ligature  local injections         = nodes.injections  local setmark            = injections.setmark @@ -378,7 +374,7 @@ local function cref(dataset,sequence,index)          return "no valid dataset"      end      local merged = sequence.merged and "merged " or "" -    if index then +    if index and index > 1 then          return formatters["feature %a, type %a, %schain lookup %a, index %a"](              dataset[4],sequence.type,merged,sequence.name,index)      else @@ -484,7 +480,7 @@ local function markstoligature(head,start,stop,char)          end          resetinjection(base)          setchar(base,char) -        setsubtype(base,ligatureglyph_code) +     -- setsubtype(base,ligatureglyph_code)          setcomponents(base,start)          setlink(prev,base,next)          flushcomponents(start) @@ -536,7 +532,7 @@ local function inhibited(start,stop)  end  local function toligature(head,start,stop,char,dataset,sequence,skiphash,discfound,hasmarks) -- brr head -    if start == stop and getchar(start) == char then +    if start == stop and getchar(start) == char and not hasmarks then          resetinjection(start)          setchar(start,char)          return head, start @@ -556,7 +552,7 @@ local function toligature(head,start,stop,char,dataset,sequence,skiphash,discfou      resetinjection(base)      setchar(base,char)      setoptions(base,getoptions(start) | getoptions(stop)) -- maybe only lig options -    setsubtype(base,ligatureglyph_code) + -- setsubtype(base,ligatureglyph_code)      setcomponents(base,comp)      setlink(prev,base,next)      if not discfound then @@ -790,7 +786,7 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip          while current do              local nxt, char = isnextchar(current,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)              if char and not hasglyphoption(current,no_left_ligature_code) then -                local lg = ligature[char] +                local lg = not tonumber(ligature) and ligature[char]                  if lg then                      stop     = current                      ligature = lg @@ -803,14 +799,14 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip              end          end          if stop then -            local lig = ligature.ligature -            if lig then +            local ligature = tonumber(ligature) or ligature.ligature +            if ligature then                  if trace_ligatures then                      local stopchar = getchar(stop) -                    head, start = markstoligature(head,start,stop,lig) +                    head, start = markstoligature(head,start,stop,ligature)                      logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(getchar(start)))                  else -                    head, start = markstoligature(head,start,stop,lig) +                    head, start = markstoligature(head,start,stop,ligature)                  end                  return head, start, true, false              else @@ -828,7 +824,7 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip                  elseif hasglyphoption(current,no_left_ligature_code) then                      break                  else -                    local lg = ligature[char] +                    local lg = not tonumber(ligature) and ligature[char]                      if lg then                          if marks[char] then                              hasmarks = true @@ -856,7 +852,9 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip          --          -- Challenge for Kai (latinmodern):  \hyphenation{fii-f-f-iif} fiiffiif          -- -        if discfound then +        if tonumber(ligature) then +            -- we're already done (endpoint reached) +        elseif discfound then              -- todo: check for no right ligature              -- don't assume marks in a disc and we don't run over a disc (for now)              local pre, post, replace = getdisc(discfound) @@ -920,27 +918,28 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip                  start = discfound                  return head, start, true, true              end +            ligature = ligature.ligature          end -        local lig = ligature.ligature -        if lig then +     -- local ligature = tonumber(ligature) or ligature.ligature +        if ligature then              if stop then                  if trace_ligatures then                      local stopchar = getchar(stop) -                 -- head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,discfound,hasmarks) -                    head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,false,hasmarks) -                    logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(lig)) +                 -- head, start = toligature(head,start,stop,ligature,dataset,sequence,skiphash,discfound,hasmarks) +                    head, start = toligature(head,start,stop,ligature,dataset,sequence,skiphash,false,hasmarks) +                    logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(ligature))                   -- we can have a rare case of multiple disc in a lig but that makes no sense language wise but if really                   -- needed we could backtrack if we're in a disc node                  else -                 -- head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,discfound,hasmarks) -                    head, start = toligature(head,start,stop,lig,dataset,sequence,skiphash,false,hasmarks) +                 -- head, start = toligature(head,start,stop,ligature,dataset,sequence,skiphash,discfound,hasmarks) +                    head, start = toligature(head,start,stop,ligature,dataset,sequence,skiphash,false,hasmarks)                  end              else                  -- weird but happens (in some arabic font)                  resetinjection(start) -                setchar(start,lig) +                setchar(start,ligature)                  if trace_ligatures then -                    logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(dataset,sequence),gref(startchar),gref(lig)) +                    logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(dataset,sequence),gref(startchar),gref(ligature))                  end              end              return head, start, true, false @@ -1502,19 +1501,19 @@ function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup              local last            = stop              local nofreplacements = 1              while current do -                local nxt, schar, id = isnextchar(current,currentfont,currentdynamic,currentscale,currentxscale,currentyscale) -                if schar then -if current and hasglyphoption(current,no_left_ligature_code) then -    break -end -                    if skiphash and skiphash[schar] then -- marks +                local nxt, char, id = isnextchar(current,currentfont,currentdynamic,currentscale,currentxscale,currentyscale) +                if char then +                    if current and hasglyphoption(current,no_left_ligature_code) then +                        break +                    end +                    if skiphash and skiphash[char] then -- marks                          -- if current == stop then -- maybe add this                          --     break                          -- else                              current = nxt                          -- end                      else -                        local lg = ligatures[schar] +                        local lg = not tonumber(ligatures) and ligatures[char]                          if lg then                              ligatures       = lg                              last            = current @@ -1544,7 +1543,8 @@ end                      break                  end              end -            local ligature = ligatures.ligature +            -- we can avoid the tonumber as already done but messy +            local ligature = tonumber(ligatures) or ligatures.ligature              if ligature then                  if chainindex then                      stop = last @@ -2001,7 +2001,6 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck)      local size         = ck[5] - ck[4] + 1      local chainlookups = ck[6]      local done         = false -      -- current match      if chainlookups then          -- Lookups can be like { 1, false, 3 } or { false, 2 } or basically anything and @@ -2012,7 +2011,6 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck)           -- if nofchainlookups > size then           --     -- bad rules           -- end -              local chainlookup = chainlookups[1]              for j=1,#chainlookup do                  local chainstep = chainlookup[j] @@ -2020,6 +2018,9 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck)                  local chainproc = chainprocs[chainkind]                  if chainproc then                      local ok +                 -- HH: chainindex 1 added here (for KAI to check too), there are weird ligatures e.g. +                 -- char + mark -> char where mark has to disappear +                 -- head, start, ok = chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash,1)                      head, start, ok = chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash)                      if ok then                          done = true @@ -2515,6 +2516,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s          local ck  = contexts[k]          local seq = ck[3]          local f   = ck[4] -- first current +local last = start          if not startchar or not seq[f][startchar] then              -- report("no hit in %a at %i of %i contexts",sequence.type,k,nofcontexts)              goto next @@ -2525,7 +2527,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s          else              local l       = ck[5] -- last current              local current = start -            local last    = start +-- local last    = start              if l > f then                  -- before/current/after | before/current | current/after @@ -3443,7 +3445,7 @@ local function t_run_single(start,stop,lookupcache)                  while s do                      local nxt, char = isnextchar(s,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)                      if char then -                        local lg = lookupmatch[char] +                        local lg = not tonumber(lookupmatch) and lookupmatch[char]                          if lg then                              if sstop then                                  d = 1 @@ -3473,7 +3475,7 @@ local function t_run_single(start,stop,lookupcache)                          break                      end                  end -                if l and l.ligature then -- so we test for ligature +                if l and (tonumber(l) or l.ligature) then -- so we test for ligature                      lastd = d                  end              end @@ -3587,7 +3589,7 @@ local function t_run_multiple(start,stop,steps,nofsteps)                      while s do                          local nxt, char = isnextchar(s,currentfont,currentdynamic,currentscale,currentxscale,currentyscale)                          if char then -                            local lg = lookupmatch[char] +                            local lg = not tonumber(lookupmatch) and lookupmatch[char]                              if lg then                                  if sstop then                                      d = 1 @@ -3617,7 +3619,7 @@ local function t_run_multiple(start,stop,steps,nofsteps)                              break                          end                      end -                    if l and l.ligature then +                    if l and (tonumber(l) or l.ligature) then                          lastd = d                      end                  end @@ -3728,7 +3730,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 otfdataset = nil +    local otfdataset + +    otfdataset = function(...) +        -- delayed alias +        otfdataset = otf.dataset +        return otfdataset(...) +    end      local getfastdisc = { __index = function(t,k)          local v = usesfont(k,currentfont) @@ -3752,9 +3761,6 @@ do          if nesting == 1 then              currentfont    = font              currentdynamic = dynamic ---             currentscale   = false ---             currentxscale  = false ---             currentyscale  = false              currentscale   = 1000              currentxscale  = 1000              currentyscale  = 1000 @@ -3768,11 +3774,11 @@ do              factor         = getthreshold(font)              checkmarks     = tfmdata.properties.checkmarks -            if not otfdataset then -                otfdataset = otf.dataset -            end +         -- if not otfdataset then +         --     otfdataset = otf.dataset +         -- end -            discs  = fastdisc and n and n > 1 and setmetatable({},getfastdisc) -- maybe inline +            discs  = fastdisc and n and n > 1 and setmetatable({},getfastdisc)              spaces = setmetatable({},getfastspace)          elseif currentfont ~= font then @@ -3806,7 +3812,7 @@ 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. -        for s=1,#datasets do +        for s=1,#datasets do -- todo: datasets.n              local dataset      = datasets[s]              local state        = dataset[2]              local sequence     = dataset[3] -- sequences[s] -- also dataset[5] @@ -4194,8 +4200,8 @@ registerotffeature {      }  } --- Moved here (up) a bit. This doesn't really belong in generic so it will --- move to a context module some day. +-- Moved here (up) a bit. This doesn't really belong in generic so it will move to a +-- context module some day.  local function markinitializer(tfmdata,value)      local properties = tfmdata.properties @@ -4223,239 +4229,3 @@ registerotffeature {  -- Also see (!!).  otf.handlers = handlers - -if context then -    return -else -    -- todo: move the following code someplace else -end - -local setspacekerns = nodes.injections.setspacekerns if not setspacekerns then os.exit() end - -local tag = "kern" - --- if fontfeatures then - ---     function handlers.trigger_space_kerns(head,dataset,sequence,initialrl,font,dynamic) ---         local features = fontfeatures[font] ---         local enabled  = features and features.spacekern and features[tag] ---         if enabled then ---             setspacekerns(font,sequence) ---         end ---         return head, enabled ---     end - --- else -- generic (no hashes) - -    function handlers.trigger_space_kerns(head,dataset,sequence,initialrl,font,dynamic) -        local shared   = fontdata[font].shared -        local features = shared and shared.features -        local enabled  = features and features.spacekern and features[tag] -        if enabled then -            setspacekerns(font,sequence) -        end -        return head, enabled -    end - --- end - --- There are fonts out there that change the space but we don't do that kind of --- things in TeX. - -local function hasspacekerns(data) -    local resources = data.resources -    local sequences = resources.sequences -    local validgpos = resources.features.gpos -    if validgpos and sequences then -        for i=1,#sequences do -            local sequence = sequences[i] -            local steps    = sequence.steps -            if steps and sequence.features[tag] then -                local kind = sequence.type -                if kind == "gpos_pair" or kind == "gpos_single" then -                    for i=1,#steps do -                        local step     = steps[i] -                        local coverage = step.coverage -                        local rules    = step.rules -                        if rules then -                            -- not now: analyze (simple) rules -                        elseif not coverage then -                            -- nothing to do -                        elseif kind == "gpos_single" then -                            -- maybe a message that we ignore -                        elseif kind == "gpos_pair" then -                            local format = step.format -                            if format == "move" or format == "kern" then -                                local kerns  = coverage[32] -                                if kerns then -                                    return true -                                end -                                for k, v in next, coverage do -                                    if v[32] then -                                        return true -                                    end -                                end -                            elseif format == "pair" then -                                local kerns  = coverage[32] -                                if kerns then -                                    for k, v in next, kerns do -                                        local one = v[1] -                                        if one and one ~= true then -                                            return true -                                        end -                                    end -                                end -                                for k, v in next, coverage do -                                    local kern = v[32] -                                    if kern then -                                        local one = kern[1] -                                        if one and one ~= true then -                                            return true -                                        end -                                    end -                                end -                            end -                        end -                    end -                end -            end -        end -    end -    return false -end - -otf.readers.registerextender { -    name   = "spacekerns", -    action = function(data) -        data.properties.hasspacekerns = hasspacekerns(data) -    end -} - -local function spaceinitializer(tfmdata,value) -- dynamic -    local resources  = tfmdata.resources -    local spacekerns = resources and resources.spacekerns -    if value and spacekerns == nil then -        local rawdata    = tfmdata.shared and tfmdata.shared.rawdata -        local properties = rawdata.properties -        if properties and properties.hasspacekerns then -            local sequences = resources.sequences -            local validgpos = resources.features.gpos -            if validgpos and sequences then -                local left  = { } -                local right = { } -                local last  = 0 -                local feat  = nil -                for i=1,#sequences do -                    local sequence = sequences[i] -                    local steps    = sequence.steps -                    if steps then -                        -- we don't support space kerns in other features -                        local kern = sequence.features[tag] -                        if kern then -                            local kind = sequence.type -                            if kind == "gpos_pair" or kind == "gpos_single" then -                                if feat then -                                    for script, languages in next, kern do -                                        local f = feat[script] -                                        if f then -                                            for l in next, languages do -                                                f[l] = true -                                            end -                                        else -                                            feat[script] = languages -                                        end -                                    end -                                else -                                    feat = kern -                                end -                                for i=1,#steps do -                                    local step     = steps[i] -                                    local coverage = step.coverage -                                    local rules    = step.rules -                                    if rules then -                                        -- not now: analyze (simple) rules -                                    elseif not coverage then -                                        -- nothing to do -                                    elseif kind == "gpos_single" then -                                        -- makes no sense in TeX -                                    elseif kind == "gpos_pair" then -                                        local format = step.format -                                        if format == "move" or format == "kern" then -                                            local kerns  = coverage[32] -                                            if kerns then -                                                for k, v in next, kerns do -                                                    right[k] = v -                                                end -                                            end -                                            for k, v in next, coverage do -                                                local kern = v[32] -                                                if kern then -                                                    left[k] = kern -                                                end -                                            end -                                        elseif format == "pair" then -                                            local kerns  = coverage[32] -                                            if kerns then -                                                for k, v in next, kerns do -                                                    local one = v[1] -                                                    if one and one ~= true then -                                                        right[k] = one[3] -                                                    end -                                                end -                                            end -                                            for k, v in next, coverage do -                                                local kern = v[32] -                                                if kern then -                                                    local one = kern[1] -                                                    if one and one ~= true then -                                                        left[k] = one[3] -                                                    end -                                                end -                                            end -                                        end -                                    end -                                end -                                last = i -                            end -                        else -                            -- no steps ... needed for old one ... we could use the basekerns -                            -- instead -                        end -                    end -                end -                left  = next(left)  and left  or false -                right = next(right) and right or false -                if left or right then -                    spacekerns = { -                        left  = left, -                        right = right, -                    } -                    if last > 0 then -                        local triggersequence = { -                            -- no steps, see (!!) -                            features = { [tag] = feat or { dflt = { dflt = true, } } }, -                            flags    = noflags, -                            name     = "trigger_space_kerns", -                            order    = { tag }, -                            type     = "trigger_space_kerns", -                            left     = left, -                            right    = right, -                        } -                        insert(sequences,last,triggersequence) -                    end -                end -            end -        end -        resources.spacekerns = spacekerns -    end -    return spacekerns -end - -registerotffeature { -    name         = "spacekern", -    description  = "space kern injection", -    default      = true, -    initializers = { -        node     = spaceinitializer, -    }, -} diff --git a/tex/context/base/mkxl/font-set.mklx b/tex/context/base/mkxl/font-set.mklx index fd6a2f1ff..98e2e96e1 100644 --- a/tex/context/base/mkxl/font-set.mklx +++ b/tex/context/base/mkxl/font-set.mklx @@ -88,7 +88,7 @@    {\glet\font_preload_default_fonts_tt\relax     \glet\font_preload_default_fonts_mm\relax     \glet\font_preloads_third_stage    \relax -%  \glet\font_preloads_fourth_stage   \relax +   \glet\font_preloads_fourth_stage   \relax    }  \def\font_preloads_zero_stage_indeed diff --git a/tex/context/base/mkxl/lpdf-emb.lmt b/tex/context/base/mkxl/lpdf-emb.lmt index c715c7b77..926455bb8 100644 --- a/tex/context/base/mkxl/lpdf-emb.lmt +++ b/tex/context/base/mkxl/lpdf-emb.lmt @@ -2101,6 +2101,7 @@ function lpdf.flushfonts()          local hash = getstreamhash(fontid)          if hash then              local parent = mainfonts[hash] +-- print("before",fontid,hash,parent)              if not parent then                  local fontdata   = usedfonts[fontid]                  local rawdata    = fontdata.shared and fontdata.shared.rawdata @@ -2145,6 +2146,7 @@ function lpdf.flushfonts()                      noffonts = noffonts + 1                  end              end +-- print("after ",fontid,hash,parent)              if parent then                  local indices = parent.indices                  for k, v in next, used do @@ -2159,21 +2161,23 @@ function lpdf.flushfonts()      for hash, details in sortedhash(mainfonts) do          local filename = details.filename          if next(details.indices) then -            if trace_fonts then -                report_fonts("embedding %a hashed as %a",filename,hash) -            end              local properties = details.properties              local bitmap     = properties.usedbitmap              local method     = properties.method -- will be pk | pdf | svg | ... +            if trace_fonts then +                if method then +                    report_fonts("embedding %a hashed as %a using method %a",filename,hash,method) +                else +                    report_fonts("embedding %a hashed as %a",filename,hash) +                end +            end              if bitmap or method then                  local format = "type3"                  local writer = mainwriters[format] -                if writer then -                    if trace_fonts then -                        report_fonts("using main writer %a",format) -                    end -                    writer(details) +                if trace_fonts then +                    report_fonts("using main writer %a",format)                  end +                writer(details)              else                  local format = properties.format                  local writer = mainwriters[format] @@ -2285,7 +2289,10 @@ function lpdf.flushfonts()           -- report_fonts("no indices for %a",filename)          end          if trace_fonts then -            report_fonts("embedded indices: % t",table.sortedkeys(details.indices)) +            local indices = details.indices +            if indices and next(indices) then +                report_fonts("embedded indices: % t",table.sortedkeys(details.indices)) +            end          end          mainfonts[details.hash] = false -- done      end diff --git a/tex/context/base/mkxl/lpdf-lmt.lmt b/tex/context/base/mkxl/lpdf-lmt.lmt index 826c1ce21..8d3fc7fde 100644 --- a/tex/context/base/mkxl/lpdf-lmt.lmt +++ b/tex/context/base/mkxl/lpdf-lmt.lmt @@ -3028,6 +3028,7 @@ do              --              statistics.register("result saved in file", function()                  local outputfilename = environment.outputfilename or environment.jobname or tex.jobname or "<unset>" +                outputfilename = string.gsub(outputfilename,"^%./+","") -- todo: make/use a helper                  return string.format("%s.%s, compresslevel %s, objectcompresslevel %s",outputfilename,"pdf",lpdf.getcompression())              end)              -- diff --git a/tex/context/base/mkxl/lpdf-wid.lmt b/tex/context/base/mkxl/lpdf-wid.lmt index fcc7b9c99..87cfcccd1 100644 --- a/tex/context/base/mkxl/lpdf-wid.lmt +++ b/tex/context/base/mkxl/lpdf-wid.lmt @@ -25,7 +25,7 @@ if not modules then modules = { } end modules ['lpdf-wid'] = {  -- html5 media in pdf then.  --  -- See mail by Michal Vlasák to the mailing list that discusses current support in --- viewers and also mentions a few fixes wrt embedding media. +-- viewers and also mentions (and submitted) a few fixes wrt embedding media.  local tonumber, next = tonumber, next  local gmatch, gsub, find, lower = string.gmatch, string.gsub, string.find, string.lower @@ -60,6 +60,7 @@ local v_auto                   = variables.auto  local v_embed                  = variables.embed  local v_max                    = variables.max  local v_yes                    = variables.yes +local v_compress               = variables.compress  local pdfconstant              = lpdf.constant  local pdfnull                  = lpdf.null @@ -649,6 +650,7 @@ local function insertrenderingwindow(specification)          Subtype = pdfconstant("Screen"),          P       = pdfreference(pdfpagereference(page)),          A       = a, -- needed in order to make the annotation clickable (i.e. don't bark) +        T       = pdfunicode(label), -- for JS          Border  = bs,          C       = bc,          AA      = actions, @@ -662,7 +664,7 @@ end  -- some dictionaries can have a MH (must honor) or BE (best effort) capsule  local function insertrendering(specification) -    local label = specification.label +    local label  = specification.label      local option = settings_to_hash(specification.option)      if not mf[label] then          local filename = specification.filename @@ -693,7 +695,7 @@ local function insertrendering(specification)       --     }       -- }          local parameters = pdfdictionary { -            Type = pdfconstant(MediaPermissions), +            Type = pdfconstant("MediaPermissions"),              TF   = pdfstring("TEMPALWAYS"), -- TEMPNEVER TEMPEXTRACT TEMPACCESS TEMPALWAYS / needed for acrobat/wmp          }          local descriptor = pdfdictionary { @@ -707,7 +709,7 @@ local function insertrendering(specification)              descriptor = codeinjections.embedfile {                  file           = filename,                  mimetype       = mimetype, -- yes or no -                compress       = false, +                compress       = option[v_compress] or false,                  forcereference = true,              }          end @@ -723,7 +725,7 @@ local function insertrendering(specification)          local rendition = pdfdictionary {              Type = pdfconstant("Rendition"),              S    = pdfconstant("MR"), -            N    = label, +            N    = pdfunicode(label),              C    = pdfreference(pdfflushobject(clip)),          }          mf[label] = pdfreference(pdfflushobject(rendition)) @@ -761,6 +763,21 @@ function codeinjections.processrendering(label)      end  end +-- needed mapping for access from JS + +local function flushrenderings() +    if next(mf) then +        local r = pdfarray() +        for label, reference in sortedhash(mf) do +            r[#r+1] = pdfunicode(label) +            r[#r+1] = reference -- already a reference +        end +        lpdf.addtonames("Renditions",pdfreference(pdfflushobject(pdfdictionary{ Names = r }))) +    end +end + +lpdf.registerdocumentfinalizer(flushrenderings,"renderings") +  function codeinjections.insertrenderingwindow(specification)      local label = specification.label      codeinjections.processrendering(label) diff --git a/tex/context/base/mkxl/luat-lib.mkxl b/tex/context/base/mkxl/luat-lib.mkxl index d26c90af5..7ad29e29e 100644 --- a/tex/context/base/mkxl/luat-lib.mkxl +++ b/tex/context/base/mkxl/luat-lib.mkxl @@ -18,7 +18,7 @@  \registerctxluafile{util-fil}{autosuffix}  \registerctxluafile{util-sac}{autosuffix}  \registerctxluafile{util-sto}{} % could also be done in trac-deb.mkiv -\registerctxluafile{util-pck}{} +\registerctxluafile{util-pck}{autosuffix}  \registerctxluafile{util-prs}{}  \registerctxluafile{util-fmt}{}  \registerctxluafile{util-dim}{} diff --git a/tex/context/base/mkxl/lxml-ini.mkxl b/tex/context/base/mkxl/lxml-ini.mkxl index b18d8053b..be249c1f3 100644 --- a/tex/context/base/mkxl/lxml-ini.mkxl +++ b/tex/context/base/mkxl/lxml-ini.mkxl @@ -195,6 +195,23 @@     %xmlpopdocument     \endgroup} +% This still doesn't solve a problem with weird grouping (start inside xml and +% end outside, so one has to do proper grouping inside xml mode). +% +% \def\lxml_process#1#2#3#4#5% flag \loader id name what initializersetup +%   {%\begingroup +%    \pushmacro\xmldocument +%    \edef\xmldocument{#3}% #2 can be \xmldocument and set as such +%    #2{#3}{#4}% +%    \pushcatcodetable +%    \setcatcodetable\notcatcodes +%    \doifelsenothing{#5}% +%      {\xmlsetup{#3}{xml:process}}% +%      {\xmlsetup{#3}{#5}}% +%    \popcatcodetable +%    \popmacro\xmldocument +%    }%\endgroup} +  \permanent\protected\def\xmlprocessfile  {\lxml_process\plusone  \xmlload}  \permanent\protected\def\xmlprocessdata  {\lxml_process\zerocount\xmlloaddata}  \permanent\protected\def\xmlprocessbuffer{\lxml_process\zerocount\xmlloadbuffer} diff --git a/tex/context/base/mkxl/math-ini.lmt b/tex/context/base/mkxl/math-ini.lmt index 857f21013..e31b14af3 100644 --- a/tex/context/base/mkxl/math-ini.lmt +++ b/tex/context/base/mkxl/math-ini.lmt @@ -120,13 +120,13 @@ local accents = allocate {  local codes = allocate {      ordinary       = 0, [0] = "ordinary", -    largeoperator  = 1, [1] = "largeoperator", -    binaryoperator = 2, [2] = "binaryoperator", -    relation       = 3, [3] = "relation", -    openingsymbol  = 4, [4] = "openingsymbol", -    closingsymbol  = 5, [5] = "closingsymbol", -    punctuation    = 6, [6] = "punctuation", -    variable       = 7, [7] = "variable", +    largeoperator  = 1,       "largeoperator", +    binaryoperator = 2,       "binaryoperator", +    relation       = 3,       "relation", +    openingsymbol  = 4,       "openingsymbol", +    closingsymbol  = 5,       "closingsymbol", +    punctuation    = 6,       "punctuation", +    variable       = 7,       "variable",  }  local extensibles = allocate { diff --git a/tex/context/base/mkxl/math-map.lmt b/tex/context/base/mkxl/math-map.lmt index 783b9db74..304cbe592 100644 --- a/tex/context/base/mkxl/math-map.lmt +++ b/tex/context/base/mkxl/math-map.lmt @@ -688,9 +688,9 @@ local issygreek = regular_tf.symbols  local isgreek   = merged(islcgreek,isucgreek,issygreek)  local greekremapping = { -    [1] = { what = "unchanged" }, -- upright -    [2] = { what = "upright", it = "tf", bi = "bf" }, -- upright -    [3] = { what = "italic",  tf = "it", bf = "bi" }, -- italic +    { what = "unchanged" }, -- upright +    { what = "upright", it = "tf", bi = "bf" }, -- upright +    { what = "italic",  tf = "it", bf = "bi" }, -- italic  }  local usedremap = { } diff --git a/tex/context/base/mkxl/math-rad.mklx b/tex/context/base/mkxl/math-rad.mklx index 8906efdc8..c5445f8f1 100644 --- a/tex/context/base/mkxl/math-rad.mklx +++ b/tex/context/base/mkxl/math-rad.mklx @@ -110,12 +110,14 @@  \def\m_math_no_degree{{}} +% style width [options: left middle right] +  \def\math_radical_alternative{\csname\??mathradicalalternative\mathradicalparameter\c!alternative\endcsname} -\setvalue{\??mathradicalalternative\v!default}%  #body% +\defcsname\??mathradicalalternative\v!default\endcsname %  #body%    {\rootradical{\currentmathradicaldegree}}   % {#body}} -\setvalue{\??mathradicalalternative\v!normal}#body% +\defcsname\??mathradicalalternative\v!normal\endcsname#body%    {\edef\p_color{\mathradicalparameter\c!color}%     \ifempty\p_color       \styledrootradical{\currentmathradicaldegree}{#body}% {} really needed as \rootradical expands first @@ -150,7 +152,7 @@  % todo: spacing .. this is just an experiment (article driven) -\setvalue{\??mathradicalalternative\v!mp}#body% we could use dowithnextbox +\defcsname\??mathradicalalternative\v!mp\endcsname#body% we could use dowithnextbox    {\begingroup     \scratchoffset\mathradicalparameter\c!mpoffset     \setbox\nextbox\mathstylehbox{#body}% @@ -213,7 +215,7 @@     \csname\??mathornamentalternative\mathornamentparameter\c!alternative\endcsname{#body}%     \endgroup} -\setvalue{\??mathornamentalternative\v!mp}#body% we could use dowithnextbox +\defcsname\??mathornamentalternative\v!mp\endcsname#body% we could use dowithnextbox    {\begingroup     \scratchoffset\mathornamentparameter\c!mpoffset     \setbox\nextbox\mathstylehbox{#body}% diff --git a/tex/context/base/mkxl/meta-ini.mkxl b/tex/context/base/mkxl/meta-ini.mkxl index 8abd1339a..b30f4826f 100644 --- a/tex/context/base/mkxl/meta-ini.mkxl +++ b/tex/context/base/mkxl/meta-ini.mkxl @@ -514,7 +514,8 @@       \meta_mpvar_default     \orelse\ifchkdim\m_meta_current_variable\or       \todimension\m_meta_current_variable\space\space -   \orelse\ifchknum\m_meta_current_variable\or + % \orelse\ifchknum\m_meta_current_variable\or % we need to catch 1>2 +   \orunless\iftok{\ifchknum\m_meta_current_variable\or\tointeger\m_meta_current_variable\fi}\emptytoks       \tointeger\m_meta_current_variable\space\space     \orelse\ifcsname\??colorattribute\currentcolorprefix\m_meta_current_variable\endcsname       \MPcolor\m_meta_current_variable\space @@ -532,7 +533,8 @@       \meta_mpvar_default     \orelse\ifchkdim\m_meta_current_variable\or       \todimension\m_meta_current_variable\space\space -   \orelse\ifchknum\m_meta_current_variable\or + % \orelse\ifchknum\m_meta_current_variable\or % we need to catch 1>2 +   \orunless\iftok{\ifchknum\m_meta_current_variable\or\tointeger\m_meta_current_variable\fi}\emptytoks       \tointeger\m_meta_current_variable\space\space     \orelse\ifcsname\??colorattribute\currentcolorprefix\m_meta_current_variable\endcsname       \MPcolor\m_meta_current_variable\space diff --git a/tex/context/base/mkxl/mlib-ctx.lmt b/tex/context/base/mkxl/mlib-ctx.lmt index 13dba92cd..64225e308 100644 --- a/tex/context/base/mkxl/mlib-ctx.lmt +++ b/tex/context/base/mkxl/mlib-ctx.lmt @@ -118,15 +118,15 @@ implement {      arguments = "string",      actions   = function(name)          local value = metapost.variables[name] -        if value ~= nil then -            local tvalue = type(value) -            if tvalue == "table" then -                context(concat(value," ")) -            elseif tvalue == "number" or tvalue == "boolean" then -                context(tostring(value)) -            elseif tvalue == "string" then -                context(value) -            end +        local tvalue = type(value) +        if tvalue == "nil" then +            context("0") +        elseif tvalue == "table" then +            context(concat(value," ")) +        elseif tvalue == "number" or tvalue == "boolean" then +            context(tostring(value)) +        elseif tvalue == "string" then +            context(value)          end      end  } @@ -362,16 +362,6 @@ function mptex.reset()  end  implement { -    name      = "mppushvariables", -    actions   = metapost.pushvariables, -} - -implement { -    name      = "mppopvariables", -    actions   = metapost.popvariables, -} - -implement {      name      = "mptexset",      arguments = "string",      actions   = mptex.set diff --git a/tex/context/base/mkxl/mlib-lmp.lmt b/tex/context/base/mkxl/mlib-lmp.lmt index e4944c92e..1614f0951 100644 --- a/tex/context/base/mkxl/mlib-lmp.lmt +++ b/tex/context/base/mkxl/mlib-lmp.lmt @@ -8,7 +8,9 @@ if not modules then modules = { } end modules ['mlib-lmp'] = {  -- path relates stuff ... todo: use a stack (or numeric index to list) -local type = type +local type, tonumber, tostring = type, tonumber, tostring +local find, match = string.find, string.match +local insert, remove = table.insert, table.remove  local aux            = mp.aux  local mpnumeric      = aux.numeric @@ -18,60 +20,86 @@ local registerdirect = metapost.registerdirect  local registerscript = metapost.registerscript  local scan           = mp.scan +local skip           = mp.skip  local get            = mp.get +local inject         = mp.inject +  local scannumber     = scan.number  local scanstring     = scan.string +local scaninteger    = scan.integer +local scannumeric    = scan.numeric +local scanwhatever   = scan.whatever  local scanpath       = scan.path  local scanproperty   = scan.property +  local gethashentry   = get.hashentry -local p = nil -local n = 0 +local bpfactor       = number.dimenfactors.bp -registerscript("pathreset", function() -    p = nil -    n = 0 -end) +local injectwhatever = inject.whatever +local injectboolean  = inject.boolean +local injectnumeric  = inject.numeric +local injectstring   = inject.string +local injectpair     = inject.pair -registerscript("pathlengthof", function() -    p = scanpath() -    n = p and #p or 1 -    mpnumeric(n) -end) +local injectwhd      = inject.whd -- scaled +local injectxy       = inject.xy +local injectpt       = inject.pt -registerscript("pathpointof", function() -    local i = scannumber() -    if i > 0 and i <= n then -        local pi = p[i] -        mppair(pi[1],pi[2]) -    end -end) +local report         = logs.reporter("metapost", "log") +local report_message = logs.reporter("metapost") -registerscript("pathleftof", function() -    local i = scannumber() -    if i > 0 and i <= n then -        local pi = p[i] -        mppair(pi[5],pi[6]) -    end -end) +local codes          = metapost.codes +local types          = metapost.types +local procodes       = mplib.propertycodes -registerscript("pathrightof", function() -    local i = scannumber() -    if i > 0 and i <= n then -        local pn -        if i == 1 then -            pn = p[2] or p[1] -        else -            pn = p[i+1] or p[1] +local implement      = interfaces.implement + +do + +    local p = nil +    local n = 0 + +    registerscript("pathreset", function() +        p = nil +        n = 0 +    end) + +    registerdirect("pathlengthof", function() +        p = scanpath() +        return p and #p or 1 +    end) + +    registerdirect("pathpointof", function() +        local i = scannumber() +        if i > 0 and i <= n then +            local pi = p[i] +            injectpair(pi[1],pi[2])          end -        mppair(pn[3],pn[4]) -    end -end) +    end) -local report   = logs.reporter("metapost", "log") -local codes    = metapost.codes -local types    = metapost.types -local procodes = mplib.propertycodes +    registerdirect("pathleftof", function() +        local i = scannumber() +        if i > 0 and i <= n then +            local pi = p[i] +            injectpair(pi[5],pi[6]) +        end +    end) + +    registerdirect("pathrightof", function() +        local i = scannumber() +        if i > 0 and i <= n then +            local pn +            if i == 1 then +                pn = p[2] or p[1] +            else +                pn = p[i+1] or p[1] +            end +            injectpair(pn[3],pn[4]) +        end +    end) + +end  registerscript("showproperty", function()      local k, s, p, d = scanproperty() @@ -92,9 +120,6 @@ end)  -- local getmacro      = tokens.getters.macro  -- local mpgnamespace  = getmacro("??graphicvariable") --- local scanmpstring  = mp.scan.string --- local injectnumeric = mp.inject.numeric --- local injectstring  = mp.inject.string  -- registerscript("mpv_numeric",   function() injectnumeric  (getmacro(mpgnamespace .. getmacro("currentmpcategory") .. ":" .. scanmpstring())) end)  -- registerscript("mpv_dimension", function() return getmacro(mpgnamespace .. getmacro("currentmpcategory") .. ":" .. scanmpstring()) end) @@ -103,46 +128,380 @@ end)  -- registerscript("mpvar", function() return getmacro(mpgnamespace .. getmacro("currentmpcategory") .. ":" .. scanmpstring(), true) end) -- Isn't it already edef'd?  -- registerscript("mpvar", function() return getmacro(metapost.namespace .. scanmpstring(), true) end) -- Isn't it already edef'd? -local scanstring      = mp.scan.string -local expandtex       = mp.expandtex +do + +    local expandtex       = mp.expandtex + +    local tokenvalues     = tokens.values +    local dimension_value = tokenvalues.dimension +    local integer_value   = tokenvalues.integer +    local boolean_value   = tokenvalues.boolean +    local string_value    = tokenvalues.string +    local unknown_value   = tokenvalues.none + +    registerdirect("mpvard", function() +        if not expandtex(dimension_value,"mpcategoryparameter",true,scanstring()) then +            injectnumeric(0) +        end +    end) + +    registerdirect("mpvarn", function() +        if not expandtex(integer_value,"mpcategoryparameter",true,scanstring()) then +            injectnumeric(0) +        end +    end) + +    registerdirect("mpvars", function() +        if not expandtex(string_value,"mpcategoryparameter",true,scanstring()) then +            injectstring("") +        end +    end) + +    registerdirect("mpvarb", function() +        if not expandtex(boolean_value,"mpcategoryparameter",true,scanstring()) then +            injectboolean(false) +        end +    end) + +    registerdirect("mpvar", function() +        if not expandtex(unknown_value,"mpcategoryparameter",true,scanstring()) then +            injectnumeric(0) +        end +    end) -local injectnumeric   = mp.inject.numeric -local injectstring    = mp.inject.string -local injectboolean   = mp.inject.boolean +    -- -local tokenvalues     = tokens.values -local dimension_value = tokenvalues.dimension -local integer_value   = tokenvalues.integer -local boolean_value   = tokenvalues.boolean -local string_value    = tokenvalues.string -local unknown_value   = tokenvalues.none +    local mpprint  = mp.print +    local mpquoted = mp.quoted +    local getmacro = tokens.getters.macro -registerdirect("mpvard", function() -    if not expandtex(dimension_value,"mpcategoryparameter",true,scanstring()) then -        injectnumeric(0) +    registerscript("texvar", function() mpprint (getmacro(metapost.namespace .. scanstring())) end) +    registerscript("texstr", function() mpquoted(getmacro(metapost.namespace .. scanstring())) end) + +end + +do + +    registerscript("textextanchor", function() +        local x, y = match(scanstring(),"tx_anchor=(%S+) (%S+)") -- todo: make an lpeg +        if x and y then +            x = tonumber(x) +            y = tonumber(y) +        end +        injectpair(x or 0,y or 0) +    end) + +end + +do + +    local mpnamedcolor = attributes.colors.mpnamedcolor +    local mpprint      = mp.aux.print + +    mp.mf_named_color = function(str) +        mpprint(mpnamedcolor(str))      end -end) -registerdirect("mpvarn", function() -    if not expandtex(integer_value,"mpcategoryparameter",true,scanstring()) then -        injectnumeric(0) +    -- todo: we can inject but currently we always get a string back so then +    -- we need to deal with it upstream in the color module ... not now + +    registerscript("namedcolor",function() mpprint(mpnamedcolor(scanstring())) end) + +end + +do + +    local hashes = table.setmetatableindex("table") + +    registerdirect("lmt_hash_new", function() +     -- local name  = scanstring() +        local name  = scanwhatever() +        hashes[name] = { } +    end) + +    registerdirect("lmt_hash_dispose", function() +     -- local name  = scanstring() +        local name  = scanwhatever() +        hashes[name] = nil +    end) + +    registerdirect("lmt_hash_in", function() +     -- local name  = scanstring() +        local name  = scanwhatever() +     -- local key   = scanstring() +        local key   = scanwhatever() +        local hash  = hashes[name] +        injectwhatever(hash and hash[key] and true or false) +    end) + +    registerdirect("lmt_hash_to", function() +     -- local name  = scanstring() +        local name  = scanwhatever() +     -- local key   = scanstring() +        local key   = scanwhatever() +        local value = scanwhatever() +        local hash  = hashes[name] +        if hash then +            hash[key] = value +        end +    end) + +    registerdirect("lmt_hash_from", function() +     -- local name  = scanstring() +        local name  = scanwhatever() +     -- local key  = scanstring() +        local key  = scanwhatever() +        local hash = hashes[name] +        injectwhatever(hash and hash[key] or false) +    end) + +    interfaces.implement { +        name      = "MPfromhash", +        arguments = "2 strings", +        actions   = function(name,key) +            local hash = hashes[name] or hashes[tonumber(name)] or hashes[tostring(name)] +            if hash then +                local v = hash[key] +                if v then +                    context(v) +                end +            end +        end +    } + +end + +do + +    local bpfactor     = number.dimenfactors.bp +    local nbdimensions = nodes.boxes.dimensions + +    registerdirect("boxdimensions", function() +        local category = scanstring() +        local index    = scanwhatever() +        injectwhd(nbdimensions(category,index)) +    end) + +end + +do + +    local skiptoken      = skip.token + +    local comma_code     = codes.comma + +    local getmacro       = tokens.getters.macro +    local setmacro       = tokens.setters.macro + +    local getdimen       = tex.getdimen +    local getcount       = tex.getcount +    local gettoks        = tex.gettoks +    local setdimen       = tex.setdimen +    local setcount       = tex.setcount +    local settoks        = tex.settoks + +    -- more helpers + +    registerdirect("getmacro", function() return getmacro(scanstring()) end) +    registerdirect("getcount", function() return getcount(scanwhatever()) end) +    registerdirect("gettoks",  function() return gettoks (scanwhatever()) end) +    registerdirect("getdimen", function() return getdimen(scanwhatever()) * bpfactor end) + +    registerscript("setmacro", function() setmacro(scanstring(),scanstring()) end) +    registerscript("setdimen", function() setdimen(scanwhatever(),scannumeric()/bpfactor) end) +    registerscript("setcount", function() setcount(scanwhatever(),scannumeric()) end) +    registerscript("settoks",  function() settoks (scanwhatever(),scanstring()) end) + +    registerscript("setglobalmacro", function() setmacro(scanstring(),scanstring(),"global") end) +    registerscript("setglobaldimen", function() setdimen("global",scanwhatever(),scannumeric()/bpfactor) end) +    registerscript("setglobalcount", function() setcount("global",scanwhatever(),scaninteger()) end) +    registerscript("setglobaltoks",  function() settoks ("global",scanwhatever(),scanstring()) end) + +    local utfnum = utf.byte +    local utflen = utf.len +    local utfsub = utf.sub + +    registerdirect("utfnum", function() return utfnum(scanstring()) end) +    registerdirect("utflen", function() return utflen(scanstring()) end) + +    registerdirect("utfsub", function() -- we have an optional third argument so we explicitly scan a text argument +        return utfsub(scanstring(),skiptoken(comma_code) and scannumeric(),skiptoken(comma_code) and scannumeric()) +    end) + +    local setlogging = metapost.setlogging + +    registerscript("message", function() +        setlogging(false) +        local str = scanstring() +        setlogging(true) +        report_message("message : %s",str) +    end) + +end + +-- position fun + +do + +    local mpprint        = mp.print +    local mpfprint       = mp.fprint + +    local jobpositions   = job.positions +    local getwhd         = jobpositions.whd +    local getxy          = jobpositions.xy +    local getposition    = jobpositions.position +    local getpage        = jobpositions.page +    local getparagraph   = jobpositions.paragraph +    local getregion      = jobpositions.region +    local getcolumn      = jobpositions.column +    local getmacro       = tokens.getters.macro + +    registerscript("positionpath", function() +        local w, h, d = getwhd(scanstring()) +        if w then +            mpfprint("((%p,%p)--(%p,%p)--(%p,%p)--(%p,%p)--cycle)",0,-d,w,-d,w,h,0,h) +        else +            mpprint("(origin--cycle)") +        end +    end) + +    registerscript("positioncurve", function() +        local w, h, d = getwhd(scanstring()) +        if w then +            mpfprint("((%p,%p)..(%p,%p)..(%p,%p)..(%p,%p)..cycle)",0,-d,w,-d,w,h,0,h) +        else +            mpprint("(origin--cycle)") +        end +    end) + +    registerscript("positionbox", function() +        local p, x, y, w, h, d = getposition(scanstring()) +        if p then +            mpfprint("((%p,%p)--(%p,%p)--(%p,%p)--(%p,%p)--cycle)",x,y-d,x+w,y-d,x+w,y+h,x,y+h) +        else +            mpprint("(%p,%p)--cycle",x or 0,y or 0) +        end +    end) + +    registerdirect("positionpage",      function() return getpage     (scanstring()) or 0 end) +    registerdirect("positioncolumn",    function() return getcolumn   (scanstring()) or 0 end) +    registerdirect("positionparagraph", function() return getparagraph(scanstring()) or 0 end) +    registerdirect("positionregion",    function() return getregion   (scanstring()) or "unknown" end) +    registerdirect("positionanchor",    function() return getmacro    ("MPanchorid") end) +    registerdirect("positionwhd",       function() injectwhd(getwhd(scanstring())) end) +    registerdirect("positionxy",        function() injectxy (getxy (scanstring())) end) +    registerdirect("positionx",         function() injectpt (getx  (scanstring())) end) +    registerdirect("positiony",         function() injectpt (gety  (scanstring())) end) + +end + +do + +    local modes       = tex.modes +    local systemmodes = tex.systemmodes + +    registerdirect("mode",       function() injectboolean(modes      [scanstring()] and true or false) end) +    registerdirect("systemmode", function() injectboolean(systemmodes[scanstring()] and true or false) end) + +end + +-- for alan's nodes: + +do + +    local lpegmatch, lpegpatterns, P = lpeg.match, lpeg.patterns, lpeg.P + +    -- todo: scansuffix / why no return boolean (first one) + +    registerdirect("isarray", function() +         injectboolean(find(scanstring(),"%d") and true or false) +    end) + +    registerdirect("prefix", function() +        local str = scanstring() +        return match(str,"^(.-)[%d%[]") or str +    end) + +    local dimension = lpeg.counter(P("[") * lpegpatterns.integer * P("]") + lpegpatterns.integer) + +    registerdirect("dimension", function() return dimension(scanstring()) end) + +    -- todo : share with mlib-pps.lua metapost,isobject + +    -- registerdirect("isobject", function() +    --     injectboolean(find(scanstring(),"mf_object=")) +    -- end + +    local p1      = P("mf_object=") +    local p2      = lpegpatterns.eol * p1 +    local pattern = (1-p2)^0 * p2 + p1 + +    registerdirect("isobject", function() +        local str = scanstring() +        injectboolean(pattern and str ~= "" and lpegmatch(pattern,str)) +    end) + +end + +-- key/values (moved here, old mechanism) + +do + +    local stack, top = { }, nil + +    local function setvariable(k,v) +        if top then +            top[k] = v +        else +            metapost.variables[k] = v +        end      end -end) -registerdirect("mpvars", function() -    if not expandtex(string_value,"mpcategoryparameter",true,scanstring()) then -        injectstring("") +    local function pushvariable(k) +        local t = { } +        if top then +            insert(stack,top) +            top[k] = t +        else +            metapost.variables[k] = t +        end +        top = t      end -end) -registerdirect("mpvarb", function() -    if not expandtex(boolean_value,"mpcategoryparameter",true,scanstring()) then -        injectboolean(false) +    local function popvariable() +        top = remove(stack)      end -end) -registerdirect("mpvar", function() -    if not expandtex(unknown_value,"mpcategoryparameter",true,scanstring()) then -        injectnumeric(0) +    registerscript("passvariable", function() setvariable (scanstring(), scanwhatever()) end) +    registerscript("pushvariable", function() pushvariable(scanstring()) end) +    registerscript("popvariable",  function() popvariable () end) + +    local stack = { } + +    local function pushvariables() +        insert(stack,metapost.variables) +        metapost.variables = { }      end -end) + +    local function popvariables() +        metapost.variables = remove(stack) or metapost.variables +    end + +    metapost.setvariable   = setvariable +    metapost.pushvariable  = pushvariable +    metapost.popvariable   = popvariable +    metapost.pushvariables = pushvariables +    metapost.popvariables  = popvariables + +    implement { +        name      = "mppushvariables", +        actions   = pushvariables, +    } + +    implement { +        name      = "mppopvariables", +        actions   = popvariables, +    } + +end + diff --git a/tex/context/base/mkxl/mlib-lua.lmt b/tex/context/base/mkxl/mlib-lua.lmt index ceca9668a..e40954456 100644 --- a/tex/context/base/mkxl/mlib-lua.lmt +++ b/tex/context/base/mkxl/mlib-lua.lmt @@ -16,8 +16,6 @@ local report = logs.reporter("metapost","instance")  local codes = mplib.getcodes()  local types = mplib.gettypes() --- for k,v in next, mplib do if type(v) == "function" then local f = v mplib[k] = function(...) print(k) return v(...) end end end -  table.hashed(codes)  table.hashed(types) @@ -29,6 +27,7 @@ local skip   = mp.skip  local get    = mp.get  local inject = mp.inject +local bpfactor   = number.dimenfactors.bp  local currentmpx = nil  local stack      = { } @@ -106,6 +105,12 @@ inject.whatever  = function(...)             if trace then reporti("whatever")  inject.triplet    = inject.color  inject.quadruplet = inject.cmykcolor +-- these can be optimized for zero: + +inject.whd = function(w,h,d) injectcolor  (currentmpx,(w or 0)*bpfactor,(h or 0)*bpfactor,(d or 0)*bpfactor) end +inject.xy  = function(x,y)   injectpair   (currentmpx,(x or 0)*bpfactor,(y or 0)*bpfactor) end +inject.pt  = function(n)     injectnumeric(currentmpx,(x or 0)*bpfactor) end +  local function same(p,n)      local f = p[1]      local l = p[n] @@ -122,35 +127,6 @@ local function same(p,n)      return false  end --- local p = mp.scan.path() --- mp.inject.path(p,true,true) - --- function inject.path(p,close,connector) ---     local curled = false ---     local n = #p ---     if p.close or p.cycle then ---         close = true ---     end ---     if p.curled then ---         curled = true ---     end ---     if n > 1 then ---         -- [ ../true | --/false | nil/auto ] ---         if connector == nil or connector == "auto" then ---             connector = #p[1] > 2 ---         end ---         if connector == false or connector == "--" then ---             curled = true ---         elseif connector == true or connector == ".." then ---             if close and not same(p,n) then ---                 p[n+1] = p[1] ---             end ---         end ---     end ---     if trace then reporti("path") end ---     return injectpath(currentmpx,p,close,curled) --- end -  function inject.path(p,close,connector)      local closed = false      local curled = false diff --git a/tex/context/base/mkxl/mlib-mpf.lmt b/tex/context/base/mkxl/mlib-mpf.lmt index 086bfb0ea..7c359fe96 100644 --- a/tex/context/base/mkxl/mlib-mpf.lmt +++ b/tex/context/base/mkxl/mlib-mpf.lmt @@ -9,7 +9,7 @@ if not modules then modules = { } end modules ['mlib-mpf'] = {  -- moved from mlib-lua:  local type, tostring, tonumber, select, loadstring = type, tostring, tonumber, select, loadstring -local find, match, gsub, gmatch = string.find, string.match, string.gsub, string.gmatch +local find, gsub = string.find, string.gsub  local concat = table.concat  local formatters   = string.formatters @@ -31,8 +31,6 @@ local set    = mp.set  local get    = mp.get  local aux    = mp.aux  local scan   = mp.scan -local skip   = mp.skip -local inject = mp.inject  do @@ -41,7 +39,7 @@ do      local f_integer      = formatters["%i"]      local f_numeric      = formatters["%F"] -    -- no %n as that can produce -e notation and that is not so nice for scaled butmaybe we +    -- no %n as that can produce -e notation and that is not so nice for scaled but maybe we      -- should then switch between ... i.e. make a push/pop for the formatters here ... not now.      local f_integer      = formatters["%i"] @@ -127,31 +125,9 @@ do      local buffer  = gbuffer      local n       = 0 -    local function mpdirect1(a) +    local function mpdirect(a)          n = n + 1 buffer[n] = a      end -    local function mpdirect2(a,b) -        n = n + 1 buffer[n] = a -        n = n + 1 buffer[n] = b -    end -    local function mpdirect3(a,b,c) -        n = n + 1 buffer[n] = a -        n = n + 1 buffer[n] = b -        n = n + 1 buffer[n] = c -    end -    local function mpdirect4(a,b,c,d) -        n = n + 1 buffer[n] = a -        n = n + 1 buffer[n] = b -        n = n + 1 buffer[n] = c -        n = n + 1 buffer[n] = d -    end -    local function mpdirect5(a,b,c,d,e) -        n = n + 1 buffer[n] = a -        n = n + 1 buffer[n] = b -        n = n + 1 buffer[n] = c -        n = n + 1 buffer[n] = d -        n = n + 1 buffer[n] = e -    end      local function mpflush(separator)          buffer[1] = concat(buffer,separator or "",1,n) @@ -299,7 +275,7 @@ do      -- writers -    local function mpp(value) +    local function rawmpp(value)          n = n + 1          local t = type(value)          if t == "number" then @@ -320,13 +296,13 @@ do      local function mpprint(first,second,...)          if second == nil then              if first ~= nil then -                mpp(first) +                rawmpp(first)              end          else              for i=1,select("#",first,second,...) do                  local value = (select(i,first,second,...))                  if value ~= nil then -                    mpp(value) +                    rawmpp(value)                  end              end          end @@ -404,7 +380,7 @@ do          if type(x) == "table" then              buffer[n] = f_pair(x[1],x[2])          else -            buffer[n] = f_pair(x,y) +            buffer[n] = f_pair(x,y or x)          end      end @@ -413,7 +389,7 @@ do          if type(x) == "table" then              buffer[n] = f_pair_pt(x[1],x[2])          else -            buffer[n] = f_pair_pt(x,y) +            buffer[n] = f_pair_pt(x,y or x)          end      end @@ -605,11 +581,7 @@ do          end      end -    aux.direct          = mpdirect1 -    aux.direct1         = mpdirect1 -    aux.direct2         = mpdirect2 -    aux.direct3         = mpdirect3 -    aux.direct4         = mpdirect4 +    aux.direct          = mpdirect      aux.flush           = mpflush      aux.print           = mpprint @@ -705,12 +677,29 @@ do      for k, v in next, aux do mp[k] = v end + -- mp.print = table.setmetatablecall(aux, function(t,...) + --     mpprint(...) + -- end) + +    mp.print = table.setmetatablecall(aux, function(t,first,second,...) +        if second == nil then +            if first ~= nil then +                rawmpp(first) +            end +        else +            for i=1,select("#",first,second,...) do +                local value = (select(i,first,second,...)) +                if value ~= nil then +                    rawmpp(value) +                end +            end +        end +    end) +  end  do -    -- Another experimental feature: -      local mpnumeric   = mp.numeric      local scanstring  = scan.string      local scriptindex = metapost.scriptindex @@ -729,27 +718,6 @@ do  end --- the next will move to mlib-lmp.lua - -do - -    local mpnamedcolor = attributes.colors.mpnamedcolor -    local mpprint      = aux.print -    local scanstring   = scan.string - -    mp.mf_named_color = function(str) -        mpprint(mpnamedcolor(str)) -    end - -    -- todo: we can inject but currently we always get a string back so then -    -- we need to deal with it upstream in the color module ... not now - -    metapost.registerscript("namedcolor",function() -        mpprint(mpnamedcolor(scanstring())) -    end) - -end -  function mp.n(t) -- used ?      return type(t) == "table" and #t or 0  end @@ -839,407 +807,14 @@ end  -- texts: -do - -    local mptriplet    = mp.triplet - -    local bpfactor     = number.dimenfactors.bp -    local textexts     = nil -    local mptriplet    = mp.triplet -    local nbdimensions = nodes.boxes.dimensions - -    function mp.mf_tt_initialize(tt) -        textexts = tt -    end - -    function mp.mf_tt_dimensions(n) -        local box = textexts and textexts[n] -        if box then -            -- could be made faster with nuts but not critical -            mptriplet(box.width*bpfactor,box.height*bpfactor,box.depth*bpfactor) -        else -            mptriplet(0,0,0) -        end -    end - -    function mp.mf_tb_dimensions(category,name) -        local w, h, d = nbdimensions(category,name) -        mptriplet(w*bpfactor,h*bpfactor,d*bpfactor) -    end - -    -- todo: scan ( ... ) - -    function mp.report(a,b,c,...) -        if c then -            report_message("%s : %s",a,formatters[(gsub(b,"@","%%"))](c,...)) -        elseif b then -            report_message("%s : %s",a,b) -        elseif a then -            report_message("message : %s",a) -        end -    end - -end - -do - -    local mpprint     = aux.print -    local modes       = tex.modes -    local systemmodes = tex.systemmodes - -    function mp.mode(s) -        mpprint(modes[s] and true or false) -    end - -    function mp.systemmode(s) -        mpprint(systemmodes[s] and true or false) -    end - -    mp.processingmode = mp.mode - -end - --- for alan's nodes: - -do - -    local mpprint  = aux.print -    local mpquoted = aux.quoted - -    function mp.isarray(str) -         mpprint(find(str,"%d") and true or false) -    end - -    function mp.prefix(str) -         mpquoted(match(str,"^(.-)[%d%[]") or str) -    end - -    -- function mp.dimension(str) -    --     local n = 0 -    --     for s in gmatch(str,"%[?%-?%d+%]?") do --todo: lpeg -    --         n = n + 1 -    --     end -    --     mpprint(n) -    -- end - -    mp.dimension = lpeg.counter(P("[") * lpegpatterns.integer * P("]") + lpegpatterns.integer,mpprint) - -    -- faster and okay as we don't have many variables but probably only -    -- basename makes sense and even then it's not called that often - -    -- local hash  = table.setmetatableindex(function(t,k) -    --     local v = find(k,"%d") and true or false -    --     t[k] = v -    --     return v -    -- end) -    -- -    -- function mp.isarray(str) -    --      mpprint(hash[str]) -    -- end -    -- -    -- local hash  = table.setmetatableindex(function(t,k) -    --     local v = '"' .. (match(k,"^(.-)%d") or k) .. '"' -    --     t[k] = v -    --     return v -    -- end) -    -- -    -- function mp.prefix(str) -    --      mpprint(hash[str]) -    -- end - -end - -do - -    local scanstring     = scan.string -    local scannumeric    = scan.numeric -    local scaninteger    = scan.integer -    local skiptoken      = skip.token - -    ----- injectstring   = inject.string -    ----- injectnumeric  = inject.numeric - -    local registerdirect = metapost.registerdirect -    local registerscript = metapost.registerscript - -    local comma_code     = metapost.codes.comma - -    local getmacro       = tokens.getters.macro -    local setmacro       = tokens.setters.macro - -    local getdimen       = tex.getdimen -    local getcount       = tex.getcount -    local gettoks        = tex.gettoks -    local setdimen       = tex.setdimen -    local setcount       = tex.setcount -    local settoks        = tex.settoks - -    local bpfactor       = number.dimenfactors.bp - -    -- more helpers - - -- registerscript("getmacro", function() injectstring (getmacro(scanstring())) end) - -- registerscript("getcount", function() injectnumeric(getcount(scanstring())) end) - -- registerscript("gettoks",  function() injectstring (gettoks (scanstring())) end) - -- registerscript("getdimen", function() injectnumeric(getdimen(scanstring()) * bpfactor) end) - -    registerdirect("getmacro", function() return getmacro(scanstring()) end) -    registerdirect("getcount", function() return getcount(scanstring()) end) -    registerdirect("gettoks",  function() return gettoks (scanstring()) end) -    registerdirect("getdimen", function() return getdimen(scanstring()) * bpfactor end) - -    registerscript("setmacro", function() setmacro(scanstring(),scanstring()) end) -    registerscript("setdimen", function() setdimen(scanstring(),scannumeric()/bpfactor) end) -    registerscript("setcount", function() setcount(scanstring(),scannumeric()) end) -    registerscript("settoks",  function() settoks (scanstring(),scanstring()) end) - -    registerscript("setglobalmacro", function() setmacro(scanstring(),scanstring(),"global") end) -    registerscript("setglobaldimen", function() setdimen("global",scanstring(),scannumeric()/bpfactor) end) -    registerscript("setglobalcount", function() setcount("global",scanstring(),scaninteger()) end) -    registerscript("setglobaltoks",  function() settoks ("global",scanstring(),scanstring()) end) - -    local utfnum = utf.byte -    local utflen = utf.len -    local utfsub = utf.sub - - -- registerscript("utfnum", function() injectnumeric(utfnum(scanstring())) end) - -- registerscript("utflen", function() injectnumeric(utflen(scanstring())) end) - -- - -- registerscript("utfsub", function() -- we have an optional third argument so we explicitly scan a text argument - --     injectstring(utfsub(scanstring(),skiptoken(comma_code) and scannumeric(),skiptoken(comma_code) and scannumeric())) - -- end) - -    registerdirect("utfnum", function() return utfnum(scanstring()) end) -    registerdirect("utflen", function() return utflen(scanstring()) end) - -    registerdirect("utfsub", function() -- we have an optional third argument so we explicitly scan a text argument -        utfsub(scanstring(),skiptoken(comma_code) and scannumeric(),skiptoken(comma_code) and scannumeric()) -    end) - -    local setlogging = metapost.setlogging - -    registerscript("message", function() -        setlogging(false) -        local str = scanstring() -        setlogging(true) -        report_message("message : %s",str) -    end) - -end - --- position fun - -do - -    local mpprint      = mp.print -    local mpfprint     = mp.fprint -    local mpquoted     = mp.quoted -    local jobpositions = job.positions -    local getwhd       = jobpositions.whd -    local getxy        = jobpositions.xy -    local getposition  = jobpositions.position -    local getpage      = jobpositions.page -    local getregion    = jobpositions.region -    local getmacro     = tokens.getters.macro - -    function mp.positionpath(name) -        local w, h, d = getwhd(name) -        if w then -            mpfprint("((%p,%p)--(%p,%p)--(%p,%p)--(%p,%p)--cycle)",0,-d,w,-d,w,h,0,h) -        else -            mpprint("(origin--cycle)") -        end -    end - -    function mp.positioncurve(name) -        local w, h, d = getwhd(name) -        if w then -            mpfprint("((%p,%p)..(%p,%p)..(%p,%p)..(%p,%p)..cycle)",0,-d,w,-d,w,h,0,h) -        else -            mpprint("(origin--cycle)") -        end -    end - -    function mp.positionbox(name) -        local p, x, y, w, h, d = getposition(name) -        if p then -            mpfprint("((%p,%p)--(%p,%p)--(%p,%p)--(%p,%p)--cycle)",x,y-d,x+w,y-d,x+w,y+h,x,y+h) -        else -            mpprint("(%p,%p)",x,y) -        end -    end - -    function mp.positionxy(name) -        local x, y = getxy(name) -        if x then -            mpfprint("(%p,%p)",x,y) -        else -            mpprint("origin") -        end -    end - -    function mp.positionpage(name) -        mpfprint("%i",getpage(name) or 0) -    end - -    function mp.positionregion(name) -        local r = getregion(name) -        if r then -            mpquoted(r) -        else -            mpquoted("unknown") -        end -    end - -    function mp.positionwhd(name) -        local w, h, d = getwhd(name) -        if w then -            mpfprint("(%p,%p,%p)",w,h,d) -        else -            mpprint("(0,0,0)") -        end -    end - -    function mp.positionpxy(name) -        local p, x, y = getposition(name) -        if p then -            mpfprint("(%p,%p,%p)",p,x,y) -        else -            mpprint("(0,0,0)") -        end -    end - -    function mp.positionanchor() -        mpquoted(getmacro("MPanchorid")) -    end - -end - -do - - -- local mppair = mp.pair - -- - -- function mp.textextanchor(s) - --     local x, y = match(s,"tx_anchor=(%S+) (%S+)") -- todo: make an lpeg - --     if x and y then - --         x = tonumber(x) - --         y = tonumber(y) - --     end - --     mppair(x or 0,y or 0) - -- end - -    local injectpair = inject.pair -    local scanstring = scan.string - -    metapost.registerscript("textextanchor", function() -        local x, y = match(scanstring(),"tx_anchor=(%S+) (%S+)") -- todo: make an lpeg -        if x and y then -            x = tonumber(x) -            y = tonumber(y) -        end -        injectpair(x or 0,y or 0) -    end) - -end - -do - -    local mpprint  = mp.print -    local mpquoted = mp.quoted -    local getmacro = tokens.getters.macro - -    function mp.texvar(name) -        mpprint(getmacro(metapost.namespace .. name)) -    end - -    function mp.texstr(name) -        mpquoted(getmacro(metapost.namespace .. name)) -    end - -end - -do - -    local mpprint  = aux.print -    local mpvprint = aux.vprint - -    local hashes   = { } - -    function mp.newhash(name) -        if name then -            hashes[name] = { } -        else -            for i=1,#hashes+1 do -                if not hashes[i] then -                    hashes[i] = { } -                    mpvprint(i) -                    return -                end -            end -        end -    end - -    function mp.disposehash(n) -        if tonumber(n) then -            hashes[n] = false -        else -            hashes[n] = nil -        end +function mp.report(a,b,c,...) +    if c then +        report_message("%s : %s",a,formatters[(gsub(b,"@","%%"))](c,...)) +    elseif b then +        report_message("%s : %s",a,b) +    elseif a then +        report_message("message : %s",a)      end - -    function mp.inhash(n,key) -        local h = hashes[n] -        mpvprint(h and h[key] and true or false) -    end - -    function mp.tohash(n,key,value) -        local h = hashes[n] -        if h then -            if value == nil then -                h[key] = true -            else -                h[key] = value -            end -        end -    end - -    function mp.fromhash(n,key) -        local h = hashes[n] -        mpvprint(h and h[key] or false) -    end - -    interfaces.implement { -        name      = "MPfromhash", -        arguments = "2 strings", -        actions   = function(name,key) -            local h = hashes[name] or hashes[tonumber(name)] -            if h then -                local v = h[key] or h[tonumber(key)] -                if v then -                    context(v) -                end -            end -        end -    } - -end - -do - -    -- a bit overkill: just a find(str,"mf_object=") can be enough -    -- -    -- todo : share with mlib-pps.lua metapost,isobject - -    local mpboolean = aux.boolean - -    local p1        = P("mf_object=") -    local p2        = lpegpatterns.eol * p1 -    local pattern   = (1-p2)^0 * p2 + p1 - -    function mp.isobject(str) -        mpboolean(pattern and str ~= "" and lpegmatch(pattern,str)) -    end -  end  function mp.flatten(t) diff --git a/tex/context/base/mkxl/mlib-pos.lmt b/tex/context/base/mkxl/mlib-pos.lmt index 5ea0e1a1e..fd54d7b5f 100644 --- a/tex/context/base/mkxl/mlib-pos.lmt +++ b/tex/context/base/mkxl/mlib-pos.lmt @@ -57,3 +57,7 @@ registerdirect("getposupperleft",  function() local x, y = upperleft (scanstring  registerdirect("getposlowerleft",  function() local x, y = lowerleft (scanstring()) t[1] = x * factor t[2] = y * factor return t end)  registerdirect("getposupperright", function() local x, y = upperright(scanstring()) t[1] = x * factor t[2] = y * factor return t end)  registerdirect("getposlowerright", function() local x, y = lowerright(scanstring()) t[1] = x * factor t[2] = y * factor return t end) +registerdirect("getposllx",        function() local x, y = lowerleft (scanstring()) return x * factor end) +registerdirect("getposlly",        function() local x, y = lowerleft (scanstring()) return y * factor end) +registerdirect("getposurx",        function() local x, y = upperright(scanstring()) return x * factor end) +registerdirect("getposury",        function() local x, y = upperright(scanstring()) return y * factor end) diff --git a/tex/context/base/mkxl/mlib-run.lmt b/tex/context/base/mkxl/mlib-run.lmt index 3e950a2d9..bf3d5434a 100644 --- a/tex/context/base/mkxl/mlib-run.lmt +++ b/tex/context/base/mkxl/mlib-run.lmt @@ -379,49 +379,6 @@ function metapost.reset(mpx)      end  end --- key/values - -do - -    local stack, top = { }, nil - -    function metapost.setvariable(k,v) -        if top then -            top[k] = v -        else -            metapost.variables[k] = v -        end -    end - -    function metapost.pushvariable(k) -        local t = { } -        if top then -            insert(stack,top) -            top[k] = t -        else -            metapost.variables[k] = t -        end -        top = t -    end - -    function metapost.popvariable() -        top = remove(stack) -    end - -    local stack = { } - -    function metapost.pushvariables() -        insert(stack,metapost.variables) -        metapost.variables = { } -    end - -    function metapost.popvariables() -        metapost.variables = remove(stack) or metapost.variables -    end - -end - -  if not metapost.process then      function metapost.process(specification) diff --git a/tex/context/base/mkxl/mlib-scn.lmt b/tex/context/base/mkxl/mlib-scn.lmt index 56fdce0fb..d92ef1385 100644 --- a/tex/context/base/mkxl/mlib-scn.lmt +++ b/tex/context/base/mkxl/mlib-scn.lmt @@ -165,6 +165,15 @@ end)  scanners.typescanners  = typescanners  scanners.tokenscanners = tokenscanners +scanners.whatever = function() +    local kind = scantoken(true) +    if kind == leftdelimiter_code or kind == tag_code or kind == capsule_code then +        return (typescanners[scanexpression(true)] or scanexpression)() +    else +        return tokenscanners[kind]() +    end +end +  -- a key like 'color' has code 'declare'  local function scanparameters(fenced) diff --git a/tex/context/base/mkxl/mult-sys.mkxl b/tex/context/base/mkxl/mult-sys.mkxl index 2941a7b08..6b8379753 100644 --- a/tex/context/base/mkxl/mult-sys.mkxl +++ b/tex/context/base/mkxl/mult-sys.mkxl @@ -399,6 +399,8 @@  \definesystemconstant {ymove}  \definesystemconstant {yoffset} +\definesystemconstant {syncpos} +  %D As the name of their define command states, the next set of constants is used in  %D the message macro's. diff --git a/tex/context/base/mkxl/node-nut.lmt b/tex/context/base/mkxl/node-nut.lmt index b5a371d5e..7970c1fe5 100644 --- a/tex/context/base/mkxl/node-nut.lmt +++ b/tex/context/base/mkxl/node-nut.lmt @@ -150,6 +150,7 @@ local nuts = {      insertbefore            = d_insertbefore,      appendaftertail         = direct.appendaftertail,      prependbeforehead       = direct.prependbeforehead, +    getparstate             = direct.getparstate,      isdirect                = isdirect,      isnode                  = isnode,      isnut                   = isdirect, diff --git a/tex/context/base/mkxl/pack-rul.mkxl b/tex/context/base/mkxl/pack-rul.mkxl index 42b472aa2..96872647a 100644 --- a/tex/context/base/mkxl/pack-rul.mkxl +++ b/tex/context/base/mkxl/pack-rul.mkxl @@ -558,8 +558,8 @@       \edef\m_overlay_region{\reservedautoregiontag}%     \fi} -\def\pack_framed_add_region % experiment -  {\anch_mark_tagged_box\b_framed_normal\m_overlay_region} +\def\pack_framed_add_region % experiment, zerocount forces the given region +  {\anch_mark_tagged_box\b_framed_normal\m_overlay_region\zerocount}  \def\pack_framed_add_background    {\ifconditional\c_pack_reanchor diff --git a/tex/context/base/mkxl/page-box.mklx b/tex/context/base/mkxl/page-box.mklx index c8ceeb828..c962b831a 100644 --- a/tex/context/base/mkxl/page-box.mklx +++ b/tex/context/base/mkxl/page-box.mklx @@ -59,15 +59,6 @@        \v_page_target_bottom_fill        \v_page_target_bottom}} -\def\page_boxes_apply_offset % #box -  {\unless\ifzeropt\topoffset -     \expandafter\page_boxes_apply_offset_indeed -   \orelse\ifzeropt\backoffset -     \expandafter\gobbleoneargument -   \else -     \expandafter\page_boxes_apply_offset_indeed -   \fi} -  \def\page_boxes_apply_offset_indeed#box%    {\scratchwidth \wd#box%     \scratchheight\ht#box% @@ -75,12 +66,28 @@     \setbox#box\vpack       {\offinterlineskip        \vskip\topoffset -      \hskip\doifbothsides\backoffset\backoffset{-\backoffset}% +     %\hskip\doifbothsides\backoffset\backoffset{-\backoffset}% +      \hskip\doifbothsides++-\backoffset        \box#box}%     \wd#box\scratchwidth     \ht#box\scratchheight     \dp#box\scratchdepth} +% a variant (no gain so we stay mkiv compatible): +% +% \def\page_boxes_apply_offset_indeed#box% this needs testing in real situations +%   {\boxxoffset#box\dimexpr\boxxoffset#box\doifbothsides++-\backoffset\relax +%    \boxyoffset#box\dimexpr\boxyoffset#box-\topoffset\relax} + +\def\page_boxes_apply_offset % #box +  {\unless\ifzeropt\topoffset +     \expandafter\page_boxes_apply_offset_indeed +   \orunless\ifzeropt\backoffset +     \expandafter\page_boxes_apply_offset_indeed +   \else +     \expandafter\gobbleoneargument +   \fi} +  \def\page_boxes_apply_replicate    {\ifnum\layoutparameter\c!nx>\plusone       \expandafter\page_boxes_apply_replicate_indeed @@ -251,6 +258,15 @@        \box#1}%     \dp#1\zeropoint} +% a variant (no gain so we stay mkiv compatible): +% +% \def\page_boxes_apply_offsets#1% +%   {\scratchhoffset\doifbothsides\backspace\backspace{\dimexpr\paperwidth-\backspace-\makeupwidth\relax}% +%    \boxxoffset#1\dimexpr\boxxoffset#1+\scratchhoffset\relax +%    \boxyoffset#1\dimexpr\boxyoffset#1-\topspace\relax +%    \wd#1\dimexpr\paperwidth+\scratchhoffset\relax +%    \ht#1\dimexpr\paperheight+\topspace\relax} +  %D This is rather specialized:  \newconditional\c_page_areas_enabled diff --git a/tex/context/base/mkxl/page-lay.mkxl b/tex/context/base/mkxl/page-lay.mkxl index a4f4666e2..835a3ca4b 100644 --- a/tex/context/base/mkxl/page-lay.mkxl +++ b/tex/context/base/mkxl/page-lay.mkxl @@ -469,35 +469,58 @@     \global\c_page_target_print_orientation\uprotationangle     \global\c_page_target_print_reverse    \uprotationangle} -\letvalue{\??layoutpaper\v!reset}\page_paper_reset_paper -\letvalue{\??layoutprint\v!reset}\page_paper_reset_print - -\setvalue{\??layoutpaper\v!landscape              }{\global\settrue\c_page_target_paper_landscape} -\setvalue{\??layoutpaper\v!mirrored               }{\global\settrue\c_page_target_paper_mirror} -\setvalue{\??layoutpaper\v!negative               }{\global\settrue\c_page_target_paper_negate} -\setvalue{\??layoutpaper\v!rotated                }{\global\c_page_target_paper_orientation\rightrotationangle -                                                    \global\c_page_target_paper_reverse    \leftrotationangle} -\setvalue{\??layoutpaper\number\rightrotationangle}{\global\c_page_target_paper_orientation\rightrotationangle -                                                    \global\c_page_target_paper_reverse    \leftrotationangle} -\setvalue{\??layoutpaper\number\downrotationangle }{\global\c_page_target_paper_orientation\downrotationangle -                                                    \global\c_page_target_paper_reverse    \zerocount} -\setvalue{\??layoutpaper\number\leftrotationangle }{\global\c_page_target_paper_orientation\leftrotationangle -                                                    \global\c_page_target_paper_reverse    \rightrotationangle} - -\setvalue{\??layoutprint\v!landscape              }{\global\settrue\c_page_target_print_landscape} -\setvalue{\??layoutprint\v!mirrored               }{\global\settrue\c_page_target_print_mirror} -\setvalue{\??layoutprint\v!negative               }{\global\settrue\c_page_target_print_negate} -\setvalue{\??layoutprint\v!rotated                }{\global\c_page_target_print_orientation\rightrotationangle -                                                    \global\c_page_target_print_reverse    \leftrotationangle} -\setvalue{\??layoutprint\number\rightrotationangle}{\global\c_page_target_print_orientation\rightrotationangle -                                                    \global\c_page_target_print_reverse    \leftrotationangle} -\setvalue{\??layoutprint\number\downrotationangle }{\global\c_page_target_print_orientation\downrotationangle -                                                    \global\c_page_target_print_reverse    \zerocount} -\setvalue{\??layoutprint\number\leftrotationangle }{\global\c_page_target_print_orientation\leftrotationangle -                                                    \global\c_page_target_print_reverse    \rightrotationangle} - -%def\page_paper_handle_page_option #1{\ifcsname\??layoutpaper#1\endcsname\csname\??layoutpaper#1\endcsname\fi} -%def\page_paper_handle_print_option#1{\ifcsname\??layoutprint#1\endcsname\csname\??layoutprint#1\endcsname\fi} +\letcsname\??layoutpaper\v!reset\endcsname\page_paper_reset_paper +\letcsname\??layoutprint\v!reset\endcsname\page_paper_reset_print + +\defcsname\??layoutpaper\v!landscape\endcsname +  {\global\settrue\c_page_target_paper_landscape} + +\defcsname\??layoutpaper\v!mirrored\endcsname +  {\global\settrue\c_page_target_paper_mirror} + +\defcsname\??layoutpaper\v!negative\endcsname +  {\global\settrue\c_page_target_paper_negate} + +\defcsname\??layoutpaper\v!rotated\endcsname +  {\global\c_page_target_paper_orientation\rightrotationangle +   \global\c_page_target_paper_reverse    \leftrotationangle} + +\defcsname\??layoutpaper\number\rightrotationangle\endcsname +  {\global\c_page_target_paper_orientation\rightrotationangle +   \global\c_page_target_paper_reverse    \leftrotationangle} + +\defcsname\??layoutpaper\number\downrotationangle\endcsname +  {\global\c_page_target_paper_orientation\downrotationangle +   \global\c_page_target_paper_reverse    \zerocount} + +\defcsname\??layoutpaper\number\leftrotationangle\endcsname +  {\global\c_page_target_paper_orientation\leftrotationangle +   \global\c_page_target_paper_reverse    \rightrotationangle} + +\defcsname\??layoutprint\v!landscape\endcsname +  {\global\settrue\c_page_target_print_landscape} + +\defcsname\??layoutprint\v!mirrored\endcsname +  {\global\settrue\c_page_target_print_mirror} + +\defcsname\??layoutprint\v!negative\endcsname +  {\global\settrue\c_page_target_print_negate} + +\defcsname\??layoutprint\v!rotated\endcsname +  {\global\c_page_target_print_orientation\rightrotationangle +   \global\c_page_target_print_reverse    \leftrotationangle} + +\defcsname\??layoutprint\number\rightrotationangle\endcsname +  {\global\c_page_target_print_orientation\rightrotationangle +   \global\c_page_target_print_reverse    \leftrotationangle} + +\defcsname\??layoutprint\number\downrotationangle \endcsname +  {\global\c_page_target_print_orientation\downrotationangle +   \global\c_page_target_print_reverse    \zerocount} + +\defcsname\??layoutprint\number\leftrotationangle \endcsname +  {\global\c_page_target_print_orientation\leftrotationangle +   \global\c_page_target_print_reverse    \rightrotationangle}  \def\page_paper_handle_page_option #1{\begincsname\??layoutpaper#1\endcsname}  \def\page_paper_handle_print_option#1{\begincsname\??layoutprint#1\endcsname} @@ -847,13 +870,6 @@  \installlayoutmethod\v!default{\page_layouts_check_default}  \installlayoutmethod\v!normal {\page_layouts_check_default} -% \def\page_layouts_check_next -%   {\csname\??layoutmethod\ifcsname\??layoutmethod\layoutparameter\c!method\endcsname -%      \layoutparameter\c!method -%    \else -%      \v!normal -%    \fi\endcsname} -  \permanent\protected\def\page_layouts_check_next    {\ifcsname\??layoutmethod\layoutparameter\c!method\endcsname       \lastnamedcs @@ -907,41 +923,55 @@    {\setfalse\c_page_layouts_location_is_set     \setfalse\c_page_layouts_location_is_middle     \let\v_page_target_left_fill  \relax -   \let\v_page_target_right_fill \hss % ? \relax +   \let\v_page_target_right_fill \hss  % ? \relax     \let\v_page_target_top_fill   \relax     \let\v_page_target_bottom_fill\vss} % \relax} -\setvalue{\??layoutlocation\v!right      }{\settrue\c_page_layouts_location_is_set -                                           \setfalse\c_page_layouts_location_is_middle -                                           \let\v_page_target_left_fill  \hss -                                           \let\v_page_target_right_fill \relax} -\setvalue{\??layoutlocation\v!left       }{\settrue\c_page_layouts_location_is_set -                                           \setfalse\c_page_layouts_location_is_middle -                                           \let\v_page_target_left_fill  \relax -                                           \let\v_page_target_right_fill \hss} -\setvalue{\??layoutlocation\v!bottom     }{\settrue\c_page_layouts_location_is_set -                                           \setfalse\c_page_layouts_location_is_middle -                                           \let\v_page_target_top_fill   \vss -                                           \let\v_page_target_bottom_fill\relax} -\setvalue{\??layoutlocation\v!top        }{\settrue\c_page_layouts_location_is_set -                                           \setfalse\c_page_layouts_location_is_middle -                                           \let\v_page_target_top_fill   \relax -                                           \let\v_page_target_bottom_fill\vss} -\setvalue{\??layoutlocation\v!middle     }{\settrue\c_page_layouts_location_is_set -                                           \settrue\c_page_layouts_location_is_middle -                                           \let\v_page_target_left_fill  \hss -                                           \let\v_page_target_right_fill \hss -                                           \let\v_page_target_top_fill   \vss -                                           \let\v_page_target_bottom_fill\vss} -\setvalue{\??layoutlocation\empty        }{\setfalse\c_page_layouts_location_is_set % default also signal to scrn_ -                                           \setfalse\c_page_layouts_location_is_middle -                                           \let\v_page_target_right_fill \hss -                                           \let\v_page_target_bottom_fill\hss} -\setvalue{\??layoutlocation\v!doublesided}{\settrue \c_page_target_print_doublesided} -\setvalue{\??layoutlocation\v!singlesided}{\setfalse\c_page_target_print_doublesided} +\defcsname\??layoutlocation\v!right\endcsname +  {\settrue\c_page_layouts_location_is_set +   \setfalse\c_page_layouts_location_is_middle +   \let\v_page_target_left_fill \hss +   \let\v_page_target_right_fill\relax} + +\defcsname\??layoutlocation\v!left\endcsname +  {\settrue\c_page_layouts_location_is_set +   \setfalse\c_page_layouts_location_is_middle +   \let\v_page_target_left_fill \relax +   \let\v_page_target_right_fill\hss} + +\defcsname\??layoutlocation\v!bottom\endcsname +  {\settrue\c_page_layouts_location_is_set +   \setfalse\c_page_layouts_location_is_middle +   \let\v_page_target_top_fill   \vss +   \let\v_page_target_bottom_fill\relax} + +\defcsname\??layoutlocation\v!top\endcsname +  {\settrue\c_page_layouts_location_is_set +   \setfalse\c_page_layouts_location_is_middle +   \let\v_page_target_top_fill    \relax +   \let\v_page_target_bottom_fill\vss} + +\defcsname\??layoutlocation\v!middle\endcsname +  {\settrue\c_page_layouts_location_is_set +   \settrue\c_page_layouts_location_is_middle +   \let\v_page_target_left_fill  \hss +   \let\v_page_target_right_fill \hss +   \let\v_page_target_top_fill   \vss +   \let\v_page_target_bottom_fill\vss} + +\defcsname\??layoutlocation\empty\endcsname +  {\setfalse\c_page_layouts_location_is_set % default also signal to scrn_ +   \setfalse\c_page_layouts_location_is_middle +   \let\v_page_target_right_fill \hss +   \let\v_page_target_bottom_fill\hss} + +\defcsname\??layoutlocation\v!doublesided\endcsname +  {\settrue\c_page_target_print_doublesided} + +\defcsname\??layoutlocation\v!singlesided\endcsname +  {\setfalse\c_page_target_print_doublesided}  \def\page_target_check_centering_indeed#1% - % {\ifcsname\??layoutlocation#1\endcsname\csname\??layoutlocation#1\endcsname\fi}    {\begincsname\??layoutlocation#1\endcsname}  \protected\def\page_target_check_centering @@ -967,13 +997,6 @@  \installlayoutalternative\v!default{\page_boxes_construct_content_default}  \installlayoutalternative\v!normal {\page_boxes_construct_content_default} -% \def\page_boxes_construct_content % targetbox flusher box -%   {\csname\??layoutalternative\ifcsname\??layoutalternative\layoutparameter\c!alternative\endcsname -%      \layoutparameter\c!alternative -%    \else -%      \v!normal -%    \fi\endcsname} -  \def\page_boxes_construct_content % targetbox flusher box    {\ifcsname\??layoutalternative\layoutparameter\c!alternative\endcsname       \expandafter\lastnamedcs @@ -1031,9 +1054,9 @@          \global\d_page_adapts_height\footerheight        \fi     \fi -% +   %     \global\d_page_adapts_delta-\dimexpr\adaptlayoutparameter\c!top\relax -% +   %     \global\advance\textheight   \d_page_adapts_height     \global\advance\footerheight-\d_page_adapts_height     \showmessage\m!layouts1{\the\dimexpr\d_page_adapts_height,\the\realpageno}% @@ -1044,14 +1067,6 @@     \glet\page_adapts_push\relax     \glet\page_adapts_pop\page_adapts_pop_indeed} -% \def\page_adapts_check -%   {\csname\??pageadaptations\the\ifcsname\??pageadaptations\the\realpageno\endcsname\realpageno\else\zerocount\fi\endcsname} -% -% \def\page_adapts_reset -%   {\ifcsname\??pageadaptations\the\realpageno\endcsname -%      \global\undefinevalue{\??pageadaptations\the\realpageno}% -%    \fi} -  \def\page_adapts_check    {\begincsname\??pageadaptations\the\realpageno\endcsname} diff --git a/tex/context/base/mkxl/page-otr.mklx b/tex/context/base/mkxl/page-otr.mklx index b24fa62e3..adca25eb5 100644 --- a/tex/context/base/mkxl/page-otr.mklx +++ b/tex/context/base/mkxl/page-otr.mklx @@ -86,10 +86,9 @@      \let\page_otr_specifics_preset\page_otr_specifics_preset_traced  \to \t_page_otr_tracers -%D We have a couple of output routines and the default one is -%D the single column routine. Then there is a multicolumn variant -%D that can be used mixed, and a columnset variant that is more -%D exclusive. +%D We have a couple of output routines and the default one is the single column +%D routine. Then there is a multicolumn variant that can be used mixed, and a +%D columnset variant that is more exclusive.  \installcorenamespace{otrtriggers} @@ -176,26 +175,6 @@        #content\relax        \the\everyafteroutput}} -% Just as fuzzy (and in 'one' we are okay with \aftergroup anyway): -% -% \ifdefined\everybeforeoutputgroup \else \newtoks\everybeforeoutputgroup \fi -% \ifdefined\everyafteroutputgroup  \else \newtoks\everyafteroutputgroup  \fi -% -% \def\page_otr_set_engine_output_routine#content% -%   {\the\everybeforeoutputgroup -%    \global\output -%      {\inotrtrue -%       \the\everybeforeoutput -%       #content\relax -%       \the\everyafteroutput -%       \aftergroup\the\aftergroup\everyafteroutputgroup}} -% -% \appendtoks -%     \ifnum\c_page_postponed_mode=\plusone -%         \page_postponed_blocks_flush % and then not in \page_otr_construct_and_shipout -%     \fi -% \to \everyafteroutputgroup -  \page_otr_set_engine_output_routine\page_otr_triggered_output_routine  \installoutputroutine\synchronizeoutput % use \triggerpagebuilder instead @@ -261,16 +240,14 @@     \penalty\c_page_otr_super_penalty     \resetpagebreak} -%D For those who've read the plain \TEX\ book, we provide the next -%D macro: +%D For those who've read the plain \TEX\ book, we provide the next macro:  \permanent\protected\def\bye    {\writestatus\m!system{Sorry, you're not done yet, so no goodbye!}} -%D We define a few constants because that (1) provides some checking -%D and (2) is handier when aligning definitions (checks nicer). Most -%D routines will use ard codes names but sometimes we want to adapt, -%D which is why we have these: +%D We define a few constants because that (1) provides some checking and (2) is +%D handier when aligning definitions (checks nicer). Most routines will use ard +%D codes names but sometimes we want to adapt, which is why we have these:  \definesystemconstant{page_otr_command_routine}  \definesystemconstant{page_otr_command_package_contents} diff --git a/tex/context/base/mkxl/scrn-wid.mklx b/tex/context/base/mkxl/scrn-wid.mklx index 724bdb807..b35047d94 100644 --- a/tex/context/base/mkxl/scrn-wid.mklx +++ b/tex/context/base/mkxl/scrn-wid.mklx @@ -652,6 +652,12 @@  \let\m_scrn_rendering_page\!!zerocount +%D \starttyping +%D \definerenderingwindow[soundplace][width=0pt, height=0pt] +%D \useexternalrendering[mainsound][audio/mp3][sound.mp3][embed=yes] +%D \placerenderingwindow[soundplace][mainsound] +%D \stoptyping +  \installcorenamespace{renderingwindow}  \installframedcommandhandler \??renderingwindow {renderingwindow} \??renderingwindow diff --git a/tex/context/base/mkxl/spac-hor.mkxl b/tex/context/base/mkxl/spac-hor.mkxl index 0a378d500..95be0a352 100644 --- a/tex/context/base/mkxl/spac-hor.mkxl +++ b/tex/context/base/mkxl/spac-hor.mkxl @@ -810,8 +810,8 @@  \permanent\protected\def\dosetleftskipadaption #1{\leftskipadaption \ifcsname\??skipadaptionleft #1\endcsname\lastnamedcs\else#1\fi\relax}  \permanent\protected\def\dosetrightskipadaption#1{\rightskipadaption\ifcsname\??skipadaptionright#1\endcsname\lastnamedcs\else#1\fi\relax} -\permanent\protected\def\doadaptleftskip #1{\normalexpanded{\dosetleftskipadaption {#1}}\permanent\advance\leftskip \leftskipadaption } -\permanent\protected\def\doadaptrightskip#1{\normalexpanded{\dosetrightskipadaption{#1}}\permanent\advance\rightskip\rightskipadaption} +\permanent\protected\def\doadaptleftskip #1{\normalexpanded{\dosetleftskipadaption {#1}}\frozen\advance\leftskip \leftskipadaption } +\permanent\protected\def\doadaptrightskip#1{\normalexpanded{\dosetrightskipadaption{#1}}\frozen\advance\rightskip\rightskipadaption}  \permanent\protected\def\forgetbothskips    {\leftskip\zeropoint diff --git a/tex/context/base/mkxl/spac-ver.lmt b/tex/context/base/mkxl/spac-ver.lmt index 1af5096fb..cae0f1d78 100644 --- a/tex/context/base/mkxl/spac-ver.lmt +++ b/tex/context/base/mkxl/spac-ver.lmt @@ -687,19 +687,19 @@ local function snap_hlist(where,current,method,height,depth) -- method[v_strut]      return h, d, ch, cd, lines, extra  end -local categories = { -     [0] = "discard", -     [1] = "largest", -     [2] = "force", -     [3] = "penalty", -     [4] = "add", -     [5] = "disable", -     [6] = "nowhite", -     [7] = "goback", -     [8] = "packed", -     [9] = "overlay", -    [10] = "enable", -    [11] = "notopskip", +local categories = { [0] = +    "discard", +    "largest", +    "force", +    "penalty", +    "add", +    "disable", +    "nowhite", +    "goback", +    "packed", +    "overlay", +    "enable", +    "notopskip",  }  categories          = allocate(table.swapped(categories,categories)) diff --git a/tex/context/base/mkxl/strc-des.mklx b/tex/context/base/mkxl/strc-des.mklx index 835c6807e..9f9119466 100644 --- a/tex/context/base/mkxl/strc-des.mklx +++ b/tex/context/base/mkxl/strc-des.mklx @@ -58,6 +58,7 @@    %\s!catcodes=,     \c!title=\v!yes,    %\c!text=, +   \c!define=\v!yes,    ]  \appendtoks @@ -67,9 +68,11 @@          \defineconstruction[\currentdescription][\s!handler=\v!description,\c!level=1]%      \fi      % We can combine these but in tracing (or errors) using a different caller is nicer. -    \frozen\protected\instance\edefcsname        \currentdescription\endcsname{\nameddescription[\currentdescription]}% -    \frozen\protected\instance\edefcsname\e!start\currentdescription\endcsname{\startnameddescription[\currentdescription]}% -    \frozen\protected\instance \defcsname \e!stop\currentdescription\endcsname{\stopnameddescription}% +    \ifcstok{\descriptionparameter\c!define}\v!yes +        \frozen\protected\instance\edefcsname        \currentdescription\endcsname{\nameddescription[\currentdescription]}% +        \frozen\protected\instance\edefcsname\e!start\currentdescription\endcsname{\startnameddescription[\currentdescription]}% +        \frozen\protected\instance \defcsname \e!stop\currentdescription\endcsname{\stopnameddescription}% +    \fi  \to \everydefinedescription  \let\p_strc_constructions_title \empty diff --git a/tex/context/base/mkxl/strc-enu.mklx b/tex/context/base/mkxl/strc-enu.mklx index a6ba99ce7..fd98b11f3 100644 --- a/tex/context/base/mkxl/strc-enu.mklx +++ b/tex/context/base/mkxl/strc-enu.mklx @@ -120,7 +120,8 @@     \c!number=\v!yes, % else description     \c!start=0,     \c!state=\v!start, -   \c!levels=4] +   \c!levels=4, +   \c!define=\v!yes]  % to be considered:  % @@ -129,17 +130,21 @@  % with push/pop (also at definition time)  \protected\def\strc_define_commands_enumeration#tag#level#parent% -  {\doifelsenothing{#parent} -     {\normalexpanded{\defineconstruction[#tag][\s!handler=\v!enumeration,\c!level=#level]}% -      \edefcsname\??enumeration#tag:\s!parent\endcsname{\??enumeration}}% -     {\normalexpanded{\defineconstruction[#tag][#parent][\s!handler=\v!enumeration,\c!level=#level]}% -      \edefcsname\??enumeration#tag:\s!parent\endcsname{\??enumeration#parent}}% -   \frozen\protected\instance\edefcsname\e!next #tag\endcsname{\strc_enumerations_next{#tag}{\number#level}}%  obsolete -   \frozen\protected\instance\edefcsname\c!reset#tag\endcsname{\strc_enumerations_reset{#tag}{\number#level}}% obsolete -   %frozen\protected\instance\edefcsname\c!set  #tag\endcsname{\strc_enumerations_set{#tag}{\number#level}}%   obsolete -   \frozen\protected\instance\edefcsname        #tag\endcsname{\namedenumeration[#tag]}% -   \frozen\protected\instance\edefcsname\e!start#tag\endcsname{\startnamedenumeration[#tag]}% -   \frozen\protected\instance \defcsname\e!stop #tag\endcsname{\stopnamedenumeration}} +  {\iftok{#parent}\emptytoks +     \normalexpanded{\defineconstruction[#tag][\s!handler=\v!enumeration,\c!level=#level]}% +     \edefcsname\??enumeration#tag:\s!parent\endcsname{\??enumeration}% +   \else +     \normalexpanded{\defineconstruction[#tag][#parent][\s!handler=\v!enumeration,\c!level=#level]}% +     \edefcsname\??enumeration#tag:\s!parent\endcsname{\??enumeration#parent}% +   \fi +   \ifcstok{\enumerationparameter\c!define}\v!yes +     \frozen\protected\instance\edefcsname\e!next #tag\endcsname{\strc_enumerations_next{#tag}{\number#level}}%  obsolete +     \frozen\protected\instance\edefcsname\c!reset#tag\endcsname{\strc_enumerations_reset{#tag}{\number#level}}% obsolete +     %frozen\protected\instance\edefcsname\c!set  #tag\endcsname{\strc_enumerations_set{#tag}{\number#level}}%   obsolete +     \frozen\protected\instance\edefcsname        #tag\endcsname{\namedenumeration[#tag]}% +     \frozen\protected\instance\edefcsname\e!start#tag\endcsname{\startnamedenumeration[#tag]}% +     \frozen\protected\instance \defcsname\e!stop #tag\endcsname{\stopnamedenumeration}% +   \fi}  \let\m_strc_enumeration_sub\empty diff --git a/tex/context/base/mkxl/strc-ref.mklx b/tex/context/base/mkxl/strc-ref.mklx index ad62a52e5..1f0491cf1 100644 --- a/tex/context/base/mkxl/strc-ref.mklx +++ b/tex/context/base/mkxl/strc-ref.mklx @@ -1234,20 +1234,20 @@     \doifelsesometoks\rightreferencetoks\rightofreferencecontent\donothing     \the\rightreferencetoks} -\letvalue{\??referencinginteraction\v!all}\strc_references_interaction_all +\letcsname\??referencinginteraction\v!all\endcsname\strc_references_interaction_all -\setvalue{\??referencinginteraction\v!label}% +\defcsname\??referencinginteraction\v!label\endcsname    {\leftofreference     \the\leftreferencetoks     \the\rightreferencetoks     \rightofreference} -\setvalue{\??referencinginteraction\v!text}% +\defcsname\??referencinginteraction\v!text\endcsname    {\leftofreference     \currentreferencecontent     \rightofreference} -\setvalue{\??referencinginteraction\v!symbol}% +\defcsname\??referencinginteraction\v!symbol\endcsname    {\referencesymbol}  \permanent\def\referencesequence diff --git a/tex/context/base/mkxl/strc-reg.lmt b/tex/context/base/mkxl/strc-reg.lmt index 6e26f6f37..ccb43bc28 100644 --- a/tex/context/base/mkxl/strc-reg.lmt +++ b/tex/context/base/mkxl/strc-reg.lmt @@ -497,7 +497,22 @@ local function preprocessentries(rawdata)              if not ok and etk == "" then                  entries[k] = nil              else -                entries[k] = { etk or "", ktk ~= "" and ktk or false, ptk ~= "" and ptk or false } +                if not etk then +                    etk = "" +                end +                -- we save some space by pruning +                if ptk == "" then +                    ptk = nil +                end +                -- we save even more space by pruning +                if ktk == "" then +                    if ptk then +                        ktk = false +                    else +                        ktk = nil +                    end +                end +                entries[k] = { etk, ktk, ptk }                  ok = true              end          end @@ -563,6 +578,7 @@ local function storeregister(rawdata) -- metadata, references, entries      --      local data     = notsaved and collected[name] or tobesaved[name]      local entries  = data.entries +    --      internalreferences[internal] = rawdata      preprocessentries(rawdata)      entries[#entries+1] = rawdata diff --git a/tex/context/base/mkxl/supp-box.mkxl b/tex/context/base/mkxl/supp-box.mkxl index 796c3663a..87660df49 100644 --- a/tex/context/base/mkxl/supp-box.mkxl +++ b/tex/context/base/mkxl/supp-box.mkxl @@ -2437,6 +2437,7 @@  %D         \getboxfromcache{foo}{\recurselevel}\zerocount  %D     \fi  %D } +%D % \startMPcode draw rawtexbox("category","name") ; \stopMPcode  %D \resetboxesincache{foo}  %D \stoptyping diff --git a/tex/context/base/mkxl/syst-aux.lmt b/tex/context/base/mkxl/syst-aux.lmt index 726fd4d32..eeb1c5aac 100644 --- a/tex/context/base/mkxl/syst-aux.lmt +++ b/tex/context/base/mkxl/syst-aux.lmt @@ -648,14 +648,14 @@ implement {  --     name      = "hascommonargumentcondition",  --     actions   = hascommonargumentcondition,  --     arguments = "2 strings", ---     arguments = { "argument", "argument" }, +--     arguments = "2 arguments",  -- }  implement {      name      = "doifelseinset",      actions   = doifelseinset,      arguments = "2 strings", ---     arguments = { "argument", "argument" }, +--  arguments = "2 arguments",  }  implement { diff --git a/tex/context/base/mkxl/syst-ini.mkxl b/tex/context/base/mkxl/syst-ini.mkxl index 1f58b42a1..f9b40c963 100644 --- a/tex/context/base/mkxl/syst-ini.mkxl +++ b/tex/context/base/mkxl/syst-ini.mkxl @@ -1389,10 +1389,12 @@  \permanent\protected\def\newlocaltoks  #1{\setnewlocaltoks  #1\emptytoks }  \permanent\protected\def\newlocalbox   #1{\setnewlocalbox   #1\emptybox  } -%D Let's be detailed: grouplevel:inputlevel: +%D Let's be detailed: grouplevel:inputlevel:catcodetable (bits 1 2 4)  \tracinglevels\plusthree +\permanent\protected\def\tracingcatcodes{\tracinglevels\plusseven} +  %D We just report duplicate patterns being ignored:  \tracinghyphenation\plusone diff --git a/tex/context/base/mkxl/tabl-ntb.mkxl b/tex/context/base/mkxl/tabl-ntb.mkxl index 418164daa..67fc39a89 100644 --- a/tex/context/base/mkxl/tabl-ntb.mkxl +++ b/tex/context/base/mkxl/tabl-ntb.mkxl @@ -1437,14 +1437,14 @@    {\global\advance\c_tabl_ntb_col\plusone     \kern\d_tabl_ntb_columndistance} -\setvalue{\??naturaltablecell\the\c_tabl_ntb_none}#1#2% +\defcsname\??naturaltablecell\the\c_tabl_ntb_none\endcsname#1#2%    {\scratchcounter\tabl_ntb_get_col{#1}{#2}\relax     \ifnum\scratchcounter>\zerocount       \etoksapp\t_tabl_ntb_row         {\tabl_ntb_span{\the\scratchcounter}}%     \fi} -\setvalue{\??naturaltablecell\the\c_tabl_ntb_cell}#1#2% +\defcsname\??naturaltablecell\the\c_tabl_ntb_cell\endcsname#1#2%    {\toksapp\t_tabl_ntb_row{\tabl_ntb_pass #1 #2 }% space delimited -> less tokens     \scratchcounter\tabl_ntb_get_col{#1}{#2}\relax     \ifnum\scratchcounter>\zerocount diff --git a/tex/context/base/mkxl/tabl-tab.mkxl b/tex/context/base/mkxl/tabl-tab.mkxl index e63492984..95d263ecd 100644 --- a/tex/context/base/mkxl/tabl-tab.mkxl +++ b/tex/context/base/mkxl/tabl-tab.mkxl @@ -1429,7 +1429,7 @@  \tolerant\def\tabl_table_define_template[#1]#*[#2]#*[#3]#*[#4]%    {\ifarguments\or\else -     \setgvalue{\??tabletemplate#1}{\tabl_table_use_template{#2}{#3}{#4}}% +     \gdefcsname\??tabletemplate#1\endcsname{\tabl_table_use_template{#2}{#3}{#4}}%     \fi     \egroup} @@ -1657,9 +1657,6 @@  \newconditional\hassometablehead  \newconditional\hassometabletail -% \def\tabl_table_set_head[#1][#2]#3\end{\setvalue{\??tablehead#1}{\noalign{\global\settrue\hassometablehead}#3}} -% \def\tabl_table_set_tail[#1][#2]#3\end{\setvalue{\??tabletail#1}{\noalign{\global\settrue\hassometabletail}#3}} -  \permanent\protected\def\settablehead{\dodoubleempty\tabl_table_set_head} % todo: use pickup  \permanent\protected\def\settabletail{\dodoubleempty\tabl_table_set_tail} % todo: use pickup diff --git a/tex/context/base/mkxl/tabl-tbl.mkxl b/tex/context/base/mkxl/tabl-tbl.mkxl index e22fb946c..c48c384f2 100644 --- a/tex/context/base/mkxl/tabl-tbl.mkxl +++ b/tex/context/base/mkxl/tabl-tbl.mkxl @@ -700,9 +700,9 @@  %  % \installcorenamespace{tabulatewidth}  % -% \setvalue{\??tabulatewidth\v!fit  }{\c_tabl_tabulate_modus\plusthree} -% \setvalue{\??tabulatewidth\v!fixed}{\c_tabl_tabulate_modus\plusthree\settrue\c_tabl_tabulate_nopbreak} -% \setvalue{\??tabulatewidth\v!auto }{\c_tabl_tabulate_modus\plusthree\settrue\c_tabl_tabulate_reshape} +% \defcsname\??tabulatewidth\v!fit  \endcsname{\c_tabl_tabulate_modus\plusthree} +% \defcsname\??tabulatewidth\v!fixed\endcsname{\c_tabl_tabulate_modus\plusthree\settrue\c_tabl_tabulate_nopbreak} +% \defcsname\??tabulatewidth\v!auto \endcsname{\c_tabl_tabulate_modus\plusthree\settrue\c_tabl_tabulate_reshape}  %  % \def\tabl_tabulate_set_width_step#1%  %   {\ifcsname\??tabulatewidth#1\endcsname diff --git a/tex/context/base/mkxl/tabl-xtb.mklx b/tex/context/base/mkxl/tabl-xtb.mklx index ba144c322..5aad01884 100644 --- a/tex/context/base/mkxl/tabl-xtb.mklx +++ b/tex/context/base/mkxl/tabl-xtb.mklx @@ -209,8 +209,8 @@  %D We can also define xtables.  \appendtoks -    \permanent\setuevalue{\e!start\currentxtable}{\tabl_x_start_named[\currentxtable]}% -    \permanent\setuevalue{\e!stop \currentxtable}{\tabl_x_stop_named}% +    \permanent\protected\edefcsname\e!start\currentxtable\endcsname{\tabl_x_start_named[\currentxtable]}% +    \permanent\protected\edefcsname\e!stop \currentxtable\endcsname{\tabl_x_stop_named}%  \to \everydefinextable  \protected\def\tabl_x_start_named[#tag]#spacer[#settings]% diff --git a/tex/context/base/mkxl/util-pck.lmt b/tex/context/base/mkxl/util-pck.lmt new file mode 100644 index 000000000..fff2d280a --- /dev/null +++ b/tex/context/base/mkxl/util-pck.lmt @@ -0,0 +1,176 @@ +if not modules then modules = { } end modules ['util-pck'] = { +    version   = 1.001, +    comment   = "companion to luat-lib.mkiv", +    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL", +    copyright = "PRAGMA ADE / ConTeXt Development Team", +    license   = "see context related readme files" +} + +-- moved from core-uti + +local next, tostring, type = next, tostring, type +local sort, concat = table.sort, table.concat +local format = string.format +local sortedhashkeys, sortedkeys, tohash = table.sortedhashkeys, table.sortedkeys, table.tohash + +utilities         = utilities         or { } +utilities.packers = utilities.packers or { } +local packers     = utilities.packers +packers.version   = 1.01 + +-- local fmt_kv <const> = "%s=%q" +-- local fmt_kt <const> = "%s={%q}" + +-- local function hashed(t) +--     local s, ns = { }, 0 +--     for k, v in next, t do +--         ns = ns + 1 +--         if type(v) == "table" then +--             s[ns] = format(fmt_kt,k,hashed(v)) +--         else +--             s[ns] = format(fmt_kv,k,v) +--         end +--     end +--     sort(s) +--     return concat(s,",") +-- end + +local function hashed(t) --local function tabstr_normal(t) +    local s = { } +    local n = 0 +    for k, v in next, t do +        n = n + 1 +        if type(v) == "table" then +            s[n] = k .. ">" .. hashed(v) +        elseif v == true then +            s[n] = k .. "+" -- "=true" +        elseif v then +            s[n] = k .. "=" .. v +        else +            s[n] = k .. "-" -- "=false" +        end +    end +    if n == 0 then +        return "" +    elseif n == 1 then +        return s[1] +    else +        sort(s) -- costly but needed (occasional wrong hit otherwise) +        return concat(s,",") +    end +end + +local function simplehashed(t) +    local s = { } +    local n = 0 +    for k, v in next, t do +        n = n + 1 +     -- s[n] = format(fmt_kv,k,v) +        s[n] = k .. "=" .. v +    end +    sort(s) +    return concat(s,",") +end + +packers.hashed       = hashed +packers.simplehashed = simplehashed + +-- In luatex < 0.74 (lua 5.1) a next chain was the same for each run so no sort was needed, +-- but in the latest greatest versions (lua 5.2) we really need to sort the keys in order +-- not to get endless runs due to a difference in tuc files. + +local function pack(t,keys,skip,hash,index) +    if t then +        local sk = #t > 0 and sortedkeys(t) or sortedhashkeys(t) +        for i=1,#sk do +            local k = sk[i] +            if not skip or not skip[k] then +                local v = t[k] +                if type(v) == "table" then +                    pack(v,keys,skip,hash,index) +                    if keys[k] then +                        local h = hashed(v) +                        local i = hash[h] +                        if not i then +                            i = #index + 1 +                            index[i] = v +                            hash[h] = i +                        end +                        t[k] = i +                    end +                end +            end +        end +    end +end + +local function unpack(t,keys,skip,index) +    if t then +        for k, v in next, t do +            if keys[k] and type(v) == "number" then +                local iv = index[v] +                if iv then +                    v = iv +                    t[k] = v +                end +            end +            if type(v) == "table" and (not skip or not skip[k]) then +                unpack(v,keys,skip,index) +            end +        end +    end +end + +function packers.new(keys,version,skip) +    return { +        version = version or packers.version, +        keys    = tohash(keys), +        skip    = tohash(skip), +        hash    = { }, +        index   = { }, +    } +end + +function packers.pack(t,p,shared) +    if shared then +        pack(t,p.keys,p.skip,p.hash,p.index) +    elseif not t.packer then +        pack(t,p.keys,p.skip,p.hash,p.index) +        if #p.index > 0 then +            t.packer = { +                version = p.version or packers.version, +                keys    = p.keys, +                skip    = p.skip, +                index   = p.index, +            } +        end +        p.hash  = { } +        p.index = { } +    end +end + +function packers.unpack(t,p,shared) +    if shared then +        if p then +            unpack(t,p.keys,p.skip,p.index) +        end +    else +        local tp = t.packer +        if tp then +            if tp.version == (p and p.version or packers.version) then +                unpack(t,tp.keys,tp.skip,tp.index) +            else +               return false +            end +            t.packer = nil +        end +    end +    return true +end + +function packers.strip(p) +    p.hash = nil +end + +-- We could have a packer.serialize where we first flush the shared table +-- and then use inline a reference . This saves an unpack. diff --git a/tex/context/interface/mkii/keys-ro.xml b/tex/context/interface/mkii/keys-ro.xml index e53fbb198..158a8b8ac 100644 --- a/tex/context/interface/mkii/keys-ro.xml +++ b/tex/context/interface/mkii/keys-ro.xml @@ -154,6 +154,7 @@  		<cd:variable name='commands' value='comenzi'/>  		<cd:variable name='comment' value='comentariu'/>  		<cd:variable name='component' value='componenta'/> +		<cd:variable name='compress' value='compress'/>  		<cd:variable name='compressseparator' value='compressseparator'/>  		<cd:variable name='compressstopper' value='compressstopper'/>  		<cd:variable name='concept' value='concept'/> diff --git a/tex/context/modules/mkiv/s-fonts-emoji.mkiv b/tex/context/modules/mkiv/s-fonts-emoji.mkiv index 1f303d569..096b90263 100644 --- a/tex/context/modules/mkiv/s-fonts-emoji.mkiv +++ b/tex/context/modules/mkiv/s-fonts-emoji.mkiv @@ -293,39 +293,64 @@ end  \start -\definedfont[seguiemj*seguiemj-cl] +\definedfont[file:seguiemj.any*default,seguiemj-cl] -\ShowEmojiSnippets -  [family man light skin tone woman dark skin tone girl medium skin tone boy medium skin tone] +% \ShowEmojiSnippets +%   [family man light skin tone woman dark skin tone girl medium skin tone boy medium skin tone] -\ShowEmojiSnippetsOverlay -  [family man light skin tone woman dark skin tone girl medium skin tone boy medium skin tone] +% \ShowEmojiSnippetsOverlay +%   [family man light skin tone woman dark skin tone girl medium skin tone boy medium skin tone] -\ShowEmojiGlyphs -  [family man light skin tone woman dark skin tone girl medium skin tone boy medium skin tone] +% \ShowEmojiGlyphs +%   [family man light skin tone woman dark skin tone girl medium skin tone boy medium skin tone] -\page +% \page -\ShowEmoji[^man] +% \ShowEmoji[^man] -\page +% \ShowEmoji[backhand index pointing down] +% \ShowEmoji[backhand index pointing up] -\definecolor[emoji-base][r=.4] -\definecolor[emoji-gray][s=.5,t=.5,a=1] +\startTEXpage +[\char8205\char128104]=\char988828\par +[\char8205\char128105]=\char988851\par +[\char8205\char983484]=\char988834\par +[\char8205\char983485]=\char988835\par +[\char8205\char983486]=\char988836\par +[\char8205\char983487]=\char988837\par +[\char8205\char983488]=\char988838\par +[\char8205\char983495]=\char988858\par +[\char8205\char983496]=\char988859\par +[\char8205\char983497]=\char988860\par +[\char8205\char983498]=\char988861\par +[\char8205\char983499]=\char988862\par +\stopTEXpage -\definefontcolorpalette -  [emoji-gray] -  [emoji-base,emoji-gray] -\definefontfeature[seguiemj-cl][ccmp=yes,dist=yes,colr=emoji-gray] -\definedfont[seguiemj*seguiemj-cl] -\ShowEmoji -\page -\ShowEmojiPalettes[1] + + +% \page + +% \definecolor[emoji-base][r=.4] +% \definecolor[emoji-gray][s=.5,t=.5,a=1] + +% \definefontcolorpalette +%   [emoji-gray] +%   [emoji-base,emoji-gray] + +% \definefontfeature[seguiemj-cl][ccmp=yes,dist=yes,colr=emoji-gray] + +% \definedfont[seguiemj*seguiemj-cl] + +% \ShowEmoji + +% \page + +% \ShowEmojiPalettes[1]  \stop diff --git a/tex/context/modules/mkiv/s-fonts-features.lua b/tex/context/modules/mkiv/s-fonts-features.lua index 34268c171..eb64e4c8f 100644 --- a/tex/context/modules/mkiv/s-fonts-features.lua +++ b/tex/context/modules/mkiv/s-fonts-features.lua @@ -200,6 +200,8 @@ function moduledata.fonts.features.showfeatureset(specification)      end  end +-- The next one looks a bit like the collector in font-oup.lua. +  local function collectligatures(tfmdata)      local sequences = tfmdata.resources.sequences @@ -207,18 +209,28 @@ local function collectligatures(tfmdata)          return      end +    -- Mostly the same as s-fonts-tables so we should make a helper. +      local series = { }      local stack  = { }      local max    = 0 +    local function add(v) +        local n = #stack +        if n > max then +            max = n +        end +        series[#series+1] = { v, unpack(stack) } +    end +      local function make(tree)          for k, v in sortedhash(tree) do              if k == "ligature" then -                local n = #stack -                if n > max then -                    max = n -                end -                series[#series+1] = { v, unpack(stack) } +                add(v) +            elseif tonumber(v) then +                insert(stack,k) +                add(v) +                remove(stack)              else                  insert(stack,k)                  make(v) diff --git a/tex/context/modules/mkiv/s-fonts-tables.lua b/tex/context/modules/mkiv/s-fonts-tables.lua index c9aa7b801..65725594b 100644 --- a/tex/context/modules/mkiv/s-fonts-tables.lua +++ b/tex/context/modules/mkiv/s-fonts-tables.lua @@ -678,18 +678,28 @@ end  local function collectligatures(steps) +    -- Mostly the same as s-fonts-features so we should make a helper. +      local series = { }      local stack  = { }      local max    = 0 +    local function add(v) +        local n = #stack +        if n > max then +            max = n +        end +        series[#series+1] = { v, unpack(stack) } +    end +      local function make(tree)          for k, v in sortedhash(tree) do              if k == "ligature" then -                local n = #stack -                if n > max then -                    max = n -                end -                series[#series+1] = { v, unpack(stack) } +                add(v) +            elseif tonumber(v) then +                insert(stack,k) +                add(v) +                remove(stack)              else                  insert(stack,k)                  make(v) diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua index d874bda0f..e764e950c 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  : 2021-08-10 12:37 +-- merge date  : 2021-08-19 19:40  do -- begin closure to overcome local limits and interference @@ -11298,15 +11298,15 @@ local weights={   [900]="black",  }  local widths={ - [1]="ultracondensed", - [2]="extracondensed", - [3]="condensed", - [4]="semicondensed", - [5]="normal", - [6]="semiexpanded", - [7]="expanded", - [8]="extraexpanded", - [9]="ultraexpanded", + "ultracondensed", + "extracondensed", + "condensed", + "semicondensed", + "normal", + "semiexpanded", + "expanded", + "extraexpanded", + "ultraexpanded",  }  setmetatableindex(weights,function(t,k)   local r=floor((k+50)/100)*100 @@ -11316,30 +11316,28 @@ end)  setmetatableindex(widths,function(t,k)   return "normal"  end) -local panoseweights={ - [ 0]="normal", - [ 1]="normal", - [ 2]="verylight", - [ 3]="light", - [ 4]="thin", - [ 5]="book", - [ 6]="medium", - [ 7]="demi", - [ 8]="bold", - [ 9]="heavy", - [10]="black", +local panoseweights={ [0]="normal", + "normal", + "verylight", + "light", + "thin", + "book", + "medium", + "demi", + "bold", + "heavy", + "black",  } -local panosewidths={ - [ 0]="normal", - [ 1]="normal", - [ 2]="normal", - [ 3]="normal", - [ 4]="normal", - [ 5]="expanded", - [ 6]="condensed", - [ 7]="veryexpanded", - [ 8]="verycondensed", - [ 9]="monospaced", +local panosewidths={ [0]="normal", + "normal", + "normal", + "normal", + "normal", + "expanded", + "condensed", + "veryexpanded", + "verycondensed", + "monospaced",  }  local helpers={}  readers.helpers=helpers @@ -12883,6 +12881,9 @@ end  function readers.compact(fontdata)   report("the %a helper is not yet implemented","compact")  end +function readers.condense(fontdata) + report("the %a helper is not yet implemented","condense") +end  local extenders={}  function readers.registerextender(extender)   extenders[#extenders+1]=extender @@ -15333,6 +15334,12 @@ local function applyaxis(glyph,shape,deltas,dowidth)         else          local n1=dpoints[d1]          local n3=dpoints[d3] +        if n1>nofpoints then +         n1=nofpoints +        end +        if n3>nofpoints then +         n3=nofpoints +        end          local p1=points[n1]          local p3=points[n3]          local p1x=p1[1] @@ -20926,7 +20933,7 @@ local trace_defining=false  registertracker("fonts.defining",function(v) trace_d  local report_otf=logs.reporter("fonts","otf loading")  local fonts=fonts  local otf=fonts.handlers.otf -otf.version=3.118  +otf.version=3.119   otf.cache=containers.define("fonts","otl",otf.version,true)  otf.svgcache=containers.define("fonts","svg",otf.version,true)  otf.pngcache=containers.define("fonts","png",otf.version,true) @@ -21061,6 +21068,9 @@ function otf.load(filename,sub,instance)     if cleanup==0 then      checkmemory(used,threshold,tracememory)     end +   if context then +    otfreaders.condense(data) +   end     otfreaders.pack(data)     report_otf("loading done")     report_otf("saving %a in cache",filename) @@ -21746,49 +21756,60 @@ end  local function makefake(tfmdata,name,present)   local private=getprivate(tfmdata)   local character={ intermediate=true,ligatures={} } - resources.unicodes[name]=private + tfmdata.resources.unicodes[name]=private   tfmdata.characters[private]=character   tfmdata.descriptions[private]={ name=name }   present[name]=private   return character  end  local function make_1(present,tree,name) - for k,v in next,tree do -  if k=="ligature" then -   present[name]=v + if tonumber(tree) then +  present[name]=v + else +  for k,v in next,tree do +   if k=="ligature" then +    present[name]=v +   else +    make_1(present,v,name.."_"..k) +   end +  end + end +end +local function make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,v) + local character=characters[preceding] + if not character then +  if trace_baseinit then +   report_prepare("weird ligature in lookup %a, current %C, preceding %C",sequence.name,v,preceding) +  end +  character=makefake(tfmdata,name,present) + end + local ligatures=character.ligatures + if ligatures then +  ligatures[unicode]={ char=v } + else +  character.ligatures={ [unicode]={ char=v } } + end + if done then +  local d=done[name] +  if not d then +   done[name]={ "dummy",v }    else -   make_1(present,v,name.."_"..k) +   d[#d+1]=v    end   end  end  local function make_2(present,tfmdata,characters,tree,name,preceding,unicode,done) - for k,v in next,tree do -  if k=="ligature" then -   local character=characters[preceding] -   if not character then -    if trace_baseinit then -     report_prepare("weird ligature in lookup %a, current %C, preceding %C",sequence.name,v,preceding) -    end -    character=makefake(tfmdata,name,present) -   end -   local ligatures=character.ligatures -   if ligatures then -    ligatures[unicode]={ char=v } + if tonumber(tree) then +  make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,tree) + else +  for k,v in next,tree do +   if k=="ligature" then +    make_3(present,tfmdata,characters,tree,name,preceding,unicode,done,v)     else -    character.ligatures={ [unicode]={ char=v } } +    local code=present[name] or unicode +    local name=name.."_"..k +    make_2(present,tfmdata,characters,v,name,code,k,done)     end -   if done then -    local d=done[name] -    if not d then -     done[name]={ "dummy",v } -    else -     d[#d+1]=v -    end -   end -  else -   local code=present[name] or unicode -   local name=name.."_"..k -   make_2(present,tfmdata,characters,v,name,code,k,done)    end   end  end @@ -24514,6 +24535,10 @@ function readers.getcomponents(fontdata)        local function traverse(p,k,v)         if k=="ligature" then          collected[v]={ unpack(l) } +       elseif tonumber(v) then +        insert(l,k) +        collected[v]={ unpack(l) } +        remove(l)         else          insert(l,k)          for k,vv in next,v do @@ -24674,7 +24699,6 @@ local function tabstr_flat(t)   end  end  local function tabstr_mixed(t)  - local s={}   local n=#t   if n==0 then    return "" @@ -24688,6 +24712,7 @@ local function tabstr_mixed(t)     return tostring(k)     end   else +  local s={}    for i=1,n do     local k=t[i]     if k==true then @@ -26011,6 +26036,85 @@ function readers.compact(data)    end   end  end +if CONTEXTLMTXMODE and CONTEXTLMTXMODE>0 then + local done=0 + local function condense_1(k,v,t) +  if type(v)=="table" then +   local u=false +   local l=false +   for k,v in next,v do +    if k=="ligature" then +     l=v +     if u then +      break +     end +    elseif u then +     break +    else +     u=true +    end +   end +   if l and not u then +    t[k]=l +    done=done+1 +   end +   if u then +    for k,vv in next,v do +     if k~="ligature" then +      condense_1(k,vv,v) +     end +    end +   end +  end + end + local function condensesteps_1(lookup) +  done=0 +  if lookup.type=="gsub_ligature" then +   local steps=lookup.steps +   if steps then +    for i=1,#steps do +     local step=steps[i] +     local coverage=step.coverage +     if coverage then +      for k,v in next,coverage do +       if condense_1(k,v,coverage) then +        coverage[k]=v.ligature +        done=done+1 +       end +      end +     end +    end +   end +  end +  return done + end + function readers.condense(data) +  if not data or data.condensed then +   return +  else +   data.condensed=true +  end +  local resources=data.resources +  local condensed=0 +  local function condense(what) +   local lookups=resources[what] +   if lookups then +    for i=1,#lookups do +     condensed=condensed+condensesteps_1(lookups[i]) +    end +   elseif trace_optimizations then +    report_optimizations("no lookups in %a",what) +   end +  end +  condense("sequences") +  condense("sublookups") +  if trace_optimizations then +   if condensed>0 then +    report_optimizations("%i ligatures condensed",condensed) +   end +  end + end +end  local function mergesteps(t,k)   if k=="merged" then    local merged={} @@ -27263,7 +27367,7 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip    while current do     local char=ischar(current,currentfont)     if char then -    local lg=ligature[char] +    local lg=not tonumber(ligature) and ligature[char]      if lg then       stop=current       ligature=lg @@ -27276,14 +27380,14 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip     end    end    if stop then -   local lig=ligature.ligature -   if lig then +   local ligature=tonumber(ligature) or ligature.ligature +   if ligature then      if trace_ligatures then       local stopchar=getchar(stop) -     head,start=markstoligature(head,start,stop,lig) +     head,start=markstoligature(head,start,stop,ligature)       logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(getchar(start)))      else -     head,start=markstoligature(head,start,stop,lig) +     head,start=markstoligature(head,start,stop,ligature)      end      return head,start,true,false     else @@ -27298,7 +27402,7 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip      if skiphash and skiphash[char] then       current=getnext(current)      else -     local lg=ligature[char] +     local lg=not tonumber(ligature) and ligature[char]       if lg then        if marks[char] then         hasmarks=true @@ -27324,20 +27428,20 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip     local match     if replace then      local char=ischar(replace,currentfont) -    if char and ligature[char] then +    if char and (not tonumber(ligature) and ligature[char]) then       match=true      end     end     if not match and pre then      local char=ischar(pre,currentfont) -    if char and ligature[char] then +    if char and (not tonumber(ligature) and ligature[char]) then       match=true      end     end     if not match and not pre or not replace then      local n=getnext(discfound)      local char=ischar(n,currentfont) -    if char and ligature[char] then +    if char and (not tonumber(ligature) and ligature[char]) then       match=true      end     end @@ -27380,21 +27484,21 @@ function handlers.gsub_ligature(head,start,dataset,sequence,ligature,rlmode,skip      return head,start,true,true     end    end -  local lig=ligature.ligature -  if lig then +  local ligature=tonumber(ligature) or ligature.ligature +  if ligature then     if stop then      if trace_ligatures then       local stopchar=getchar(stop) -     head,start=toligature(head,start,stop,lig,dataset,sequence,skiphash,false,hasmarks) -     logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(lig)) +     head,start=toligature(head,start,stop,ligature,dataset,sequence,skiphash,false,hasmarks) +     logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(dataset,sequence),gref(startchar),gref(stopchar),gref(ligature))      else -     head,start=toligature(head,start,stop,lig,dataset,sequence,skiphash,false,hasmarks) +     head,start=toligature(head,start,stop,ligature,dataset,sequence,skiphash,false,hasmarks)      end     else      resetinjection(start) -    setchar(start,lig) +    setchar(start,ligature)      if trace_ligatures then -     logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(dataset,sequence),gref(startchar),gref(lig)) +     logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(dataset,sequence),gref(startchar),gref(ligature))      end     end     return head,start,true,false @@ -27868,7 +27972,7 @@ function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup       if skiphash and skiphash[schar] then         current=getnext(current)       else -      local lg=ligatures[schar] +      local lg=not tonumber(ligatures) and ligatures[schar]        if lg then         ligatures=lg         last=current @@ -27887,7 +27991,7 @@ function chainprocs.gsub_ligature(head,start,stop,dataset,sequence,currentlookup       end      end     end -   local ligature=ligatures.ligature +   local ligature=tonumber(ligatures) or ligatures.ligature     if ligature then      if chainindex then       stop=last @@ -28299,7 +28403,7 @@ local function chainrun(head,start,last,dataset,sequence,rlmode,skiphash,ck)      local chainproc=chainprocs[chainkind]      if chainproc then       local ok -     head,start,ok=chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash) +     head,start,ok=chainproc(head,start,last,dataset,sequence,chainstep,rlmode,skiphash,1)       if ok then        done=true       end @@ -28716,6 +28820,7 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s    local ck=contexts[k]    local seq=ck[3]    local f=ck[4]  +local last=start    if not startchar or not seq[f][startchar] then     goto next    end @@ -28724,7 +28829,6 @@ local function handle_contextchain(head,start,dataset,sequence,contexts,rlmode,s    else     local l=ck[5]      local current=start -   local last=start     if l>f then      local discfound       local n=f+1 @@ -29508,7 +29612,7 @@ local function t_run_single(start,stop,font,attr,lookupcache)       while s do        local char=ischar(s,font)        if char then -       local lg=lookupmatch[char] +       local lg=not tonumber(lookupmatch) and lookupmatch[char]         if lg then          if sstop then           d=1 @@ -29538,7 +29642,7 @@ local function t_run_single(start,stop,font,attr,lookupcache)         break        end       end -     if l and l.ligature then  +     if l and (tonumber(l) or l.ligature) then         lastd=d       end      else @@ -29663,7 +29767,7 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps)        while s do         local char=ischar(s)         if char then -        local lg=lookupmatch[char] +        local lg=not tonumber(lookupmatch) and lookupmatch[char]          if lg then           if sstop then            d=1 @@ -29693,7 +29797,7 @@ local function t_run_multiple(start,stop,font,attr,steps,nofsteps)          break         end        end -      if l and l.ligature then +      if l and (tonumber(l) or l.ligature) then         lastd=d        end       end @@ -31665,7 +31769,7 @@ local function initializedevanagi(tfmdata)                 local h=coverage[k]                 if h then                  for k,v in next,h do -                 found=v and v.ligature +                 found=v and (tonumber(v) or v.ligature)                   if found then                    pre_base_reordering_consonants[found]=true                    break @@ -31682,7 +31786,7 @@ local function initializedevanagi(tfmdata)            end           else            for k,v in next,r do -           found=v and v.ligature +           found=v and (tonumber(v) or v.ligature)             if found then              pre_base_reordering_consonants[found]=true              break @@ -31730,7 +31834,7 @@ local function initializedevanagi(tfmdata)                    for k,v in next,halant do                     local h=r[k]                     if h then -                    reph=h.ligature or false +                    reph=tonumber(h) or h.ligature or false                      break                     end                    end @@ -31747,7 +31851,7 @@ local function initializedevanagi(tfmdata)              for k,v in next,halant do               local h=r[k]               if h then -              reph=h.ligature or false +              reph=tonumber(h) or h.ligature or false                break               end              end @@ -31791,7 +31895,7 @@ local function initializedevanagi(tfmdata)                  local h=coverage[k]                  if h then                   for k,v in next,h do -                  found=v and v.ligature +                  found=v and (tonumber(v) or v.ligature)                    if found then                     pre_base_reordering_consonants[found]=true                     break @@ -31808,7 +31912,7 @@ local function initializedevanagi(tfmdata)             end            else             for k,v in next,h do -            found=v and v.ligature +            found=v and (tonumber(v) or v.ligature)              if found then               pre_base_reordering_consonants[found]=true               break  | 
