diff options
75 files changed, 8855 insertions, 8138 deletions
diff --git a/tex/context/base/anch-pos.lua b/tex/context/base/anch-pos.lua index 9cc9fb128..0bd945c8a 100644 --- a/tex/context/base/anch-pos.lua +++ b/tex/context/base/anch-pos.lua @@ -30,15 +30,25 @@ local texsp = tex.sp  ----- texsp = string.todimen -- because we cache this is much faster but no rounding  local texgetcount       = tex.getcount -local texgetbox         = tex.getbox  local texsetcount       = tex.setcount  local texget            = tex.get  local pdf               = pdf -- h and v are variables  local setmetatableindex = table.setmetatableindex -local new_latelua       = nodes.pool.latelua -local find_tail         = node.slide + +local nuts              = nodes.nuts + +local getfield          = nuts.getfield +local setfield          = nuts.setfield +local getlist           = nuts.getlist +local getbox            = nuts.getbox +local getskip           = nuts.getskip + +local find_tail         = nuts.tail + +local new_latelua       = nuts.pool.latelua +local new_latelua_node  = nodes.pool.latelua  local variables         = interfaces.variables  local v_text            = variables.text @@ -302,13 +312,13 @@ function commands.bcolumn(tag,register) -- name will change      insert(columns,tag)      column = tag      if register then -        context(new_latelua(f_b_column(tag))) +        context(new_latelua_node(f_b_column(tag)))      end  end  function commands.ecolumn(register) -- name will change      if register then -        context(new_latelua(f_e_column())) +        context(new_latelua_node(f_e_column()))      end      remove(columns)      column = columns[#columns] @@ -340,10 +350,10 @@ function jobpositions.markregionbox(n,tag,correct)          nofregions = nofregions + 1          tag = f_region(nofregions)      end -    local box = texgetbox(n) -    local w = box.width -    local h = box.height -    local d = box.depth +    local box = getbox(n) +    local w = getfield(box,"width") +    local h = getfield(box,"height") +    local d = getfield(box,"depth")      tobesaved[tag] = {          p = true,          x = true, @@ -355,18 +365,18 @@ function jobpositions.markregionbox(n,tag,correct)      local push = new_latelua(f_b_region(tag))      local pop  = new_latelua(f_e_region(tostring(correct))) -- todo: check if tostring is needed with formatter      -- maybe we should construct a hbox first (needs experimenting) so that we can avoid some at the tex end -    local head = box.list +    local head = getlist(box)      if head then          local tail = find_tail(head) -        head.prev = push -        push.next = head -        pop .prev = tail -        tail.next = pop +        setfield(head,"prev",push) +        setfield(push,"next",head) +        setfield(pop,"prev",tail) +        setfield(tail,"next",pop)      else -- we can have a simple push/pop -        push.next = pop -        pop.prev = push +        setfield(push,"next",pop) +        setfield(pop,"prev",push)      end -    box.list = push +    setfield(box,"list",push)  end  function jobpositions.enhance(name) @@ -375,7 +385,7 @@ end  function commands.pos(name,t)      tobesaved[name] = t -    context(new_latelua(f_enhance(name))) +    context(new_latelua_node(f_enhance(name)))  end  local nofparagraphs = 0 @@ -383,19 +393,19 @@ local nofparagraphs = 0  function commands.parpos() -- todo: relate to localpar (so this is an intermediate variant)      nofparagraphs = nofparagraphs + 1      texsetcount("global","c_anch_positions_paragraph",nofparagraphs) -    local strutbox = texgetbox("strutbox") +    local strutbox = getbox("strutbox")      local t = {          p  = true,          c  = true,          r  = true,          x  = true,          y  = true, -        h  = strutbox.height, -        d  = strutbox.depth, +        h  = getfield(strutbox,"height"), +        d  = getfield(strutbox,"depth"),          hs = texget("hsize"),      } -    local leftskip   = texget("leftskip").width -    local rightskip  = texget("rightskip").width +    local leftskip   = getfield(getskip("leftskip"),"width") +    local rightskip  = getfield(getskip("rightskip"),"width")      local hangindent = texget("hangindent")      local hangafter  = texget("hangafter")      local parindent  = texget("parindent") @@ -420,7 +430,7 @@ function commands.parpos() -- todo: relate to localpar (so this is an intermedia      end      local tag = f_p_tag(nofparagraphs)      tobesaved[tag] = t -    context(new_latelua(f_enhance(tag))) +    context(new_latelua_node(f_enhance(tag)))  end  function commands.posxy(name) -- can node.write be used here? @@ -432,7 +442,7 @@ function commands.posxy(name) -- can node.write be used here?          y = true,          n = nofparagraphs > 0 and nofparagraphs or nil,      } -    context(new_latelua(f_enhance(name))) +    context(new_latelua_node(f_enhance(name)))  end  function commands.poswhd(name,w,h,d) @@ -447,7 +457,7 @@ function commands.poswhd(name,w,h,d)          d = d,          n = nofparagraphs > 0 and nofparagraphs or nil,      } -    context(new_latelua(f_enhance(name))) +    context(new_latelua_node(f_enhance(name)))  end  function commands.posplus(name,w,h,d,extra) @@ -463,22 +473,22 @@ function commands.posplus(name,w,h,d,extra)          n = nofparagraphs > 0 and nofparagraphs or nil,          e = extra,      } -    context(new_latelua(f_enhance(name))) +    context(new_latelua_node(f_enhance(name)))  end  function commands.posstrut(name,w,h,d) -    local strutbox = texgetbox("strutbox") +    local strutbox = getbox("strutbox")      tobesaved[name] = {          p = true,          c = column,          r = true,          x = true,          y = true, -        h = strutbox.height, -        d = strutbox.depth, +        h = getfield(strutbox,"height"), +        d = getfield(strutbox,"depth"),          n = nofparagraphs > 0 and nofparagraphs or nil,      } -    context(new_latelua(f_enhance(name))) +    context(new_latelua_node(f_enhance(name)))  end  function jobpositions.getreserved(tag,n) diff --git a/tex/context/base/back-exp.lua b/tex/context/base/back-exp.lua index 18a339247..dedfc22c0 100644 --- a/tex/context/base/back-exp.lua +++ b/tex/context/base/back-exp.lua @@ -95,10 +95,22 @@ local a_reference       = attributes.private('reference')  local a_textblock       = attributes.private("textblock") -local traverse_id       = node.traverse_id -local traverse_nodes    = node.traverse -local slide_nodelist    = node.slide -local locate_node       = nodes.locate +local nuts              = nodes.nuts +local tonut             = nuts.tonut + +local getnext           = nuts.getnext +local getsubtype        = nuts.getsubtype +local getfont           = nuts.getfont +local getchar           = nuts.getchar +local getlist           = nuts.getlist +local getid             = nuts.getid +local getfield          = nuts.getfield +local getattr           = nuts.getattr + +local setattr           = nuts.setattr + +local traverse_id       = nuts.traverse_id +local traverse_nodes    = nuts.traverse  local references        = structures.references  local structurestags    = structures.tags @@ -1826,25 +1838,25 @@ end  local function collectresults(head,list) -- is last used (we also have currentattribute)      local p      for n in traverse_nodes(head) do -        local id = n.id -- 14: image, 8: literal (mp) +        local id = getid(n) -- 14: image, 8: literal (mp)          if id == glyph_code then -            local at = n[a_tagged] +            local at = getattr(n,a_tagged)              if not at then               -- we need to tag the pagebody stuff as being valid skippable               --               -- report_export("skipping character: %C (no attribute)",n.char)              else                  -- we could add tonunicodes for ligatures (todo) -                local components =  n.components +                local components =  getfield(n,"components")                  if components then -- we loose data                      collectresults(components,nil)                  else -                    local c = n.char +                    local c = getchar(n)                      if last ~= at then                          local tl = taglist[at]                          pushcontent()                          currentnesting = tl -                        currentparagraph = n[a_taggedpar] +                        currentparagraph = getattr(n,a_taggedpar)                          currentattribute = at                          last = at                          pushentry(currentnesting) @@ -1853,13 +1865,13 @@ local function collectresults(head,list) -- is last used (we also have currentat                          end                          -- We need to intercept this here; maybe I will also move this                          -- to a regular setter at the tex end. -                        local r = n[a_reference] +                        local r = getattr(n,a_reference)                          if r then                              referencehash[tl[#tl]] = r -- fulltag                          end                          --                      elseif last then -                        local ap = n[a_taggedpar] +                        local ap = getattr(n,a_taggedpar)                          if ap ~= currentparagraph then                              pushcontent(currentparagraph,ap)                              pushentry(currentnesting) @@ -1874,7 +1886,7 @@ local function collectresults(head,list) -- is last used (we also have currentat                              report_export("%w<!-- processing glyph %C tagged %a) -->",currentdepth,c,at)                          end                      end -                    local s = n[a_exportstatus] +                    local s = getattr(n,a_exportstatus)                      if s then                          c = s                      end @@ -1883,7 +1895,7 @@ local function collectresults(head,list) -- is last used (we also have currentat                              report_export("%w<!-- skipping last glyph -->",currentdepth)                          end                      elseif c == 0x20 then -                        local a = n[a_characters] +                        local a = getattr(n,a_characters)                          nofcurrentcontent = nofcurrentcontent + 1                          if a then                              if trace_export then @@ -1894,7 +1906,7 @@ local function collectresults(head,list) -- is last used (we also have currentat                              currentcontent[nofcurrentcontent] = " "                          end                      else -                        local fc = fontchar[n.font] +                        local fc = fontchar[getfont(n)]                          if fc then                              fc = fc and fc[c]                              if fc then @@ -1919,20 +1931,23 @@ local function collectresults(head,list) -- is last used (we also have currentat              end          elseif id == disc_code then -- probably too late              if keephyphens then -                local pre = n.pre -                if pre and not pre.next and pre.id == glyph_code and pre.char == hyphencode then +                local pre = getfield(n,"pre") +                if pre and not getnext(pre) and getid(pre) == glyph_code and getchar(pre) == hyphencode then                      nofcurrentcontent = nofcurrentcontent + 1                      currentcontent[nofcurrentcontent] = hyphen                  end              end -            collectresults(n.replace,nil) +            local replace = getfield(n,"replace") +            if replace then +                collectresults(replace,nil) +            end          elseif id == glue_code then              -- we need to distinguish between hskips and vskips -            local ca = n[a_characters] +            local ca = getattr(n,a_characters)              if ca == 0 then                  -- skip this one ... already converted special character (node-acc)              elseif ca then -                local a = n[a_tagged] +                local a = getattr(n,a_tagged)                  if a then                      local c = specialspaces[ca]                      if last ~= a then @@ -1942,13 +1957,13 @@ local function collectresults(head,list) -- is last used (we also have currentat                          end                          pushcontent()                          currentnesting = tl -                        currentparagraph = n[a_taggedpar] +                        currentparagraph = getattr(n,a_taggedpar)                          currentattribute = a                          last = a                          pushentry(currentnesting)                          -- no reference check (see above)                      elseif last then -                        local ap = n[a_taggedpar] +                        local ap = getattr(n,a_taggedpar)                          if ap ~= currentparagraph then                              pushcontent(currentparagraph,ap)                              pushentry(currentnesting) @@ -1969,11 +1984,11 @@ local function collectresults(head,list) -- is last used (we also have currentat                      currentcontent[nofcurrentcontent] = c                  end              else -                local subtype = n.subtype +                local subtype = getsubtype(n)                  if subtype == userskip_code then -                    if n.spec.width > threshold then +                    if getfield(getfield(n,"spec"),"width") > threshold then                          if last and not somespace[currentcontent[nofcurrentcontent]] then -                            local a = n[a_tagged] +                            local a = getattr(n,a_tagged)                              if a == last then                                  if trace_export then                                      report_export("%w<!-- injecting spacing 5a -->",currentdepth) @@ -2000,7 +2015,7 @@ local function collectresults(head,list) -- is last used (we also have currentat                      end                  elseif subtype == spaceskip_code or subtype == xspaceskip_code then                      if not somespace[currentcontent[nofcurrentcontent]] then -                        local a = n[a_tagged] +                        local a = getattr(n,a_tagged)                          if a == last then                              if trace_export then                                  report_export("%w<!-- injecting spacing 7 (stay in element) -->",currentdepth) @@ -2029,7 +2044,7 @@ local function collectresults(head,list) -- is last used (we also have currentat                                  nofcurrentcontent = nofcurrentcontent - 1                              end                          elseif not somespace[r] then -                            local a = n[a_tagged] +                            local a = getattr(n,a_tagged)                              if a == last then                                  if trace_export then                                      report_export("%w<!-- injecting spacing 1 (end of line, stay in element) -->",currentdepth) @@ -2057,9 +2072,9 @@ local function collectresults(head,list) -- is last used (we also have currentat                  end              end          elseif id == hlist_code or id == vlist_code then -            local ai = n[a_image] +            local ai = getattr(n,a_image)              if ai then -                local at = n[a_tagged] +                local at = getattr(n,a_tagged)                  if nofcurrentcontent > 0 then                      pushcontent()                      pushentry(currentnesting) -- ?? @@ -2072,18 +2087,21 @@ local function collectresults(head,list) -- is last used (we also have currentat                  currentparagraph = nil              else                  -- we need to determine an end-of-line -                collectresults(n.list,n) +                local list = getlist(n) +                if list then +                    collectresults(list,n) +                end              end          elseif id == kern_code then -            local kern = n.kern +            local kern = getfield(n,"kern")              if kern > 0 then                  local limit = threshold -                if p and p.id == glyph_code then +                if p and getid(p) == glyph_code then                      limit = fontquads[p.font] / 4                  end                  if kern > limit then                      if last and not somespace[currentcontent[nofcurrentcontent]] then -                        local a = n[a_tagged] +                        local a = getattr(n,a_tagged)                          if a == last then                              if not somespace[currentcontent[nofcurrentcontent]] then                                  if trace_export then @@ -2123,7 +2141,7 @@ function nodes.handlers.export(head) -- hooks into the page builder      end   -- continueexport()      restart = true -    collectresults(head) +    collectresults(tonut(head))      if trace_export then          report_export("%w<!-- stop flushing page -->",currentdepth)      end @@ -2133,12 +2151,12 @@ end  function builders.paragraphs.tag(head)      noftextblocks = noftextblocks + 1 -    for n in traverse_id(hlist_code,head) do -        local subtype = n.subtype +    for n in traverse_id(hlist_code,tonut(head)) do +        local subtype = getsubtype(n)          if subtype == line_code then -            n[a_textblock] = noftextblocks +            setattr(n,a_textblock,noftextblocks)          elseif subtype == glue_code or subtype == kern_code then -            n[a_textblock] = 0 +            setattr(n,a_textblock,0)          end      end      return false diff --git a/tex/context/base/cont-new.mkiv b/tex/context/base/cont-new.mkiv index 27aa3bcbe..0918a2119 100644 --- a/tex/context/base/cont-new.mkiv +++ b/tex/context/base/cont-new.mkiv @@ -11,7 +11,7 @@  %C therefore copyrighted by \PRAGMA. See mreadme.pdf for  %C details. -\newcontextversion{2014.01.06 23:46} +\newcontextversion{2014.01.07 14:00}  %D This file is loaded at runtime, thereby providing an excellent place for  %D hacks, patches, extensions and new features. diff --git a/tex/context/base/context-version.pdf b/tex/context/base/context-version.pdf Binary files differindex db893977e..7a76449f0 100644 --- a/tex/context/base/context-version.pdf +++ b/tex/context/base/context-version.pdf diff --git a/tex/context/base/context.mkiv b/tex/context/base/context.mkiv index 186a7074d..579d8ea75 100644 --- a/tex/context/base/context.mkiv +++ b/tex/context/base/context.mkiv @@ -28,7 +28,7 @@  %D up and the dependencies are more consistent.  \edef\contextformat {\jobname} -\edef\contextversion{2014.01.06 23:46} +\edef\contextversion{2014.01.07 14:00}  \edef\contextkind   {beta}  %D For those who want to use this: diff --git a/tex/context/base/font-chk.lua b/tex/context/base/font-chk.lua index 6dc1667bb..5d4f6059b 100644 --- a/tex/context/base/font-chk.lua +++ b/tex/context/base/font-chk.lua @@ -41,9 +41,18 @@ local enableaction       = tasks.enableaction  local disableaction      = tasks.disableaction  local glyph_code         = nodes.nodecodes.glyph -local traverse_id        = node.traverse_id -local remove_node        = nodes.remove -local insert_node_after  = node.insert_after + +local nuts               = nodes.nuts +local tonut              = nuts.tonut +local tonode             = nuts.tonode + +local getfont            = nuts.getfont +local getchar            = nuts.getchar +local setfield           = nuts.setfield + +local traverse_id        = nuts.traverse_id +local remove_node        = nuts.remove +local insert_node_after  = nuts.insert_after  -- maybe in fonts namespace  -- deletion can be option @@ -205,9 +214,10 @@ end  function checkers.missing(head)      local lastfont, characters, found = nil, nil, nil +    head = tonut(head)      for n in traverse_id(glyph_code,head) do -- faster than while loop so we delay removal -        local font = n.font -        local char = n.char +        local font = getfont(n) +        local char = getchar(n)          if font ~= lastfont then              characters = fontcharacters[font]              lastfont = font @@ -236,8 +246,8 @@ function checkers.missing(head)      elseif action == "replace" then          for i=1,#found do              local n = found[i] -            local font = n.font -            local char = n.char +            local font = getfont(n) +            local char = getchar(n)              local tfmdata = fontdata[font]              local properties = tfmdata.properties              local privates = properties.privates @@ -255,13 +265,13 @@ function checkers.missing(head)                  head = remove_node(head,n,true)              else                  -- good, we have \definefontfeature[default][default][missing=yes] -                n.char = p +                setfield(n,"char",p)              end          end      else          -- maye write a report to the log      end -    return head, false +    return tonode(head), false  end  local relevant = { "missing (will be deleted)", "missing (will be flagged)", "missing" } diff --git a/tex/context/base/font-col.lua b/tex/context/base/font-col.lua index f5e17f1da..187e33311 100644 --- a/tex/context/base/font-col.lua +++ b/tex/context/base/font-col.lua @@ -17,7 +17,12 @@ local type, next, toboolean = type, next, toboolean  local gmatch = string.gmatch  local fastcopy = table.fastcopy -local traverse_id        = nodes.traverse_id +local nuts               = nodes.nuts +local tonut              = nuts.tonut +local getfont            = nuts.getfont +local getchar            = nuts.getchar +local setfield           = nuts.setfield +local traverse_id        = nuts.traverse_id  local settings_to_hash   = utilities.parsers.settings_to_hash @@ -199,7 +204,7 @@ end  --  -- if lpegmatch(okay,name) then -function collections.prepare(name) -- we can do this in lua now +function collections.prepare(name) -- we can do this in lua now .. todo      current = currentfont()      if vectors[current] then          return @@ -244,23 +249,23 @@ end  function collections.process(head) -- this way we keep feature processing      local done = false -    for n in traverse_id(glyph_code,head) do -        local v = vectors[n.font] +    for n in traverse_id(glyph_code,tonut(head)) do +        local v = vectors[getfont(n)]          if v then -            local id = v[n.char] +            local id = v[getchar(n)]              if id then                  if type(id) == "table" then                      local newid, newchar = id[1], id[2]                      if trace_collecting then                          report_fonts("remapping character %C in font %a to character %C in font %a",getchar(n),getfont(n),newchar,newid)                      end -                    n.font = newid -                    n.char = newchar +                    setfield(n,"font",newid) +                    setfield(n,"char",newchar)                  else                      if trace_collecting then                          report_fonts("remapping font %a to %a for character %C",getfont(n),id,getchar(n))                      end -                    n.font = id +                    setfield(n,"font",id)                  end              end          end diff --git a/tex/context/base/font-ctx.lua b/tex/context/base/font-ctx.lua index b08a6aed2..8c4992d0c 100644 --- a/tex/context/base/font-ctx.lua +++ b/tex/context/base/font-ctx.lua @@ -57,6 +57,16 @@ local helpers             = fonts.helpers  local hashes              = fonts.hashes  local currentfont         = font.current +local nuts                = nodes.nuts +local tonut               = nuts.tonut + +local getfield            = nuts.getfield +local getattr             = nuts.getattr +local getfont             = nuts.getfont + +local setfield            = nuts.setfield +local setattr             = nuts.setattr +  local texgetattribute     = tex.getattribute  local texsetattribute     = tex.setattribute  local texgetdimen         = tex.getdimen @@ -1901,24 +1911,25 @@ end  -- a fontkern plug: -local copy_node = node.copy -local kern      = nodes.pool.register(nodes.pool.kern()) -node.set_attribute(kern,attributes.private('fontkern'),1) -- we can have several, attributes are shared +local copy_node = nuts.copy +local kern      = nuts.pool.register(nuts.pool.kern()) + +setattr(kern,attributes.private('fontkern'),1) -- we can have several, attributes are shared  nodes.injections.installnewkern(function(k)      local c = copy_node(kern) -    c.kern = k +    setfield(c,"kern",k)      return c  end) -directives.register("nodes.injections.fontkern", function(v) kern.subtype = v and 0 or 1 end) +directives.register("nodes.injections.fontkern", function(v) setfield(kern,"subtype",v and 0 or 1) end)  -- here  local trace_analyzing    = false  trackers.register("otf.analyzing", function(v) trace_analyzing = v end) -local otffeatures        = fonts.constructors.newfeatures("otf") +local otffeatures        = constructors.newfeatures("otf")  local registerotffeature = otffeatures.register  local analyzers          = fonts.analyzers @@ -1926,7 +1937,7 @@ local methods            = analyzers.methods  local unsetvalue         = attributes.unsetvalue -local traverse_by_id     = node.traverse_id +local traverse_by_id     = nuts.traverse_id  local a_color            = attributes.private('color')  local a_colormodel       = attributes.private('colormodel') @@ -1953,16 +1964,17 @@ local names = {  local function markstates(head)      if head then -        local model = head[a_colormodel] or 1 +        head = tonut(head) +        local model = getattr(head,a_colormodel) or 1          for glyph in traverse_by_id(glyph_code,head) do -            local a = glyph[a_state] +            local a = getattr(glyph,a_state)              if a then                  local name = names[a]                  if name then                      local color = m_color[name]                      if color then -                        glyph[a_colormodel] = model -                        glyph[a_color] = color +                        setattr(glyph,a_colormodel,model) +                        setattr(glyph,a_color,color)                      end                  end              end @@ -2005,8 +2017,8 @@ registerotffeature { -- adapts  function methods.nocolor(head,font,attr)      for n in traverse_by_id(glyph_code,head) do -        if not font or n.font == font then -            n[a_color] = unsetvalue +        if not font or getfont(n) == font then +            setattr(n,a_color,unsetvalue)          end      end      return head, true diff --git a/tex/context/base/font-gds.lua b/tex/context/base/font-gds.lua index 7131ecad5..e57f784a0 100644 --- a/tex/context/base/font-gds.lua +++ b/tex/context/base/font-gds.lua @@ -46,7 +46,12 @@ local findfile           = resolvers.findfile  local glyph_code         = nodes.nodecodes.glyph -local traverse_id        = nodes.traverse_id +local nuts               = nodes.nuts +local tonut              = nuts.tonut +local getfont            = nuts.getfont +local getchar            = nuts.getchar +local getattr            = nuts.getattr +local traverse_id        = nuts.traverse_id  function fontgoodies.report(what,trace,goodies)      if trace_goodies or trace then @@ -311,16 +316,16 @@ local setnodecolor   = nodes.tracers.colors.set  -- function colorschemes.coloring(head)  --     local lastfont, lastscheme  --     local done = false ---     for n in traverse_id(glyph_code,head) do ---         local a = n[a_colorscheme] +--     for n in traverse_id(glyph_code,tonut(head)) do +--         local a = getattr(n,a_colorscheme)  --         if a then ---             local f = n.font +--             local f = getfont(n)  --             if f ~= lastfont then  --                 lastscheme = fontproperties[f].colorscheme  --                 lastfont   = f  --             end  --             if lastscheme then ---                 local sc = lastscheme[n.char] +--                 local sc = lastscheme[getchar(n)]  --                 if sc then  --                     done = true  --                     setnodecolor(n,"colorscheme:"..a..":"..sc) -- slow @@ -338,21 +343,21 @@ local setnodecolor   = nodes.tracers.colors.set  --     local lastattr   = nil  --     local lastscheme = nil  --     local lastprefix = nil ---     local done      = nil ---     for n in traverse_id(glyph_code,head) do ---         local a = n[a_colorscheme] +--     local done       = nil +--     for n in traverse_id(glyph_code,tonut(head)) do +--         local a = getattr(n,a_colorscheme)  --         if a then  --             if a ~= lastattr then  --                 lastattr   = a  --                 lastprefix = "colorscheme:" .. a .. ":"  --             end ---             local f = n.font +--             local f = getfont(n)  --             if f ~= lastfont then  --                 lastfont   = f  --                 lastscheme = fontproperties[f].colorscheme  --             end  --             if lastscheme then ---                 local sc = lastscheme[n.char] +--                 local sc = lastscheme[getchar(n)]  --                 if sc then  --                     setnodecolor(n,lastprefix .. sc) -- slow  --                     done = true @@ -384,10 +389,10 @@ function colorschemes.coloring(head)      local lastcache  = nil      local lastscheme = nil      local done       = nil -    for n in traverse_id(glyph_code,head) do -        local a = n[a_colorscheme] +    for n in traverse_id(glyph_code,tonut(head)) do +        local a = getattr(n,a_colorscheme)          if a then -            local f = n.font +            local f = getfont(n)              if f ~= lastfont then                  lastfont   = f                  lastscheme = fontproperties[f].colorscheme @@ -397,7 +402,7 @@ function colorschemes.coloring(head)                  lastcache = cache[a]              end              if lastscheme then -                local sc = lastscheme[n.char] +                local sc = lastscheme[getchar(n)]                  if sc then                      setnodecolor(n,lastcache[sc]) -- we could inline this one                      done = true diff --git a/tex/context/base/font-nod.lua b/tex/context/base/font-nod.lua deleted file mode 100644 index 7fa3297d4..000000000 --- a/tex/context/base/font-nod.lua +++ /dev/null @@ -1,437 +0,0 @@ -if not modules then modules = { } end modules ['font-nod'] = { -    version   = 1.001, -    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" -} - ---[[ldx-- -<p>This is rather experimental. We need more control and some of this -might become a runtime module instead. This module will be cleaned up!</p> ---ldx]]-- - -local tonumber, tostring = tonumber, tostring -local utfchar = utf.char -local concat = table.concat -local match, gmatch, concat, rep = string.match, string.gmatch, table.concat, string.rep - -local report_nodes = logs.reporter("fonts","tracing") - -fonts = fonts or { } -nodes = nodes or { } - -local fonts, nodes, node, context = fonts, nodes, node, context - -local tracers          = nodes.tracers or { } -nodes.tracers          = tracers - -local tasks            = nodes.tasks or { } -nodes.tasks            = tasks - -local handlers         = nodes.handlers or { } -nodes.handlers         = handlers - -local injections       = nodes.injections or { } -nodes.injections       = injections - -local char_tracers     = tracers.characters or { } -tracers.characters     = char_tracers - -local step_tracers     = tracers.steppers or { } -tracers.steppers       = step_tracers - -local texsetbox        = tex.setbox - -local copy_node_list   = nodes.copy_list -local hpack_node_list  = nodes.hpack -local free_node_list   = nodes.flush_list -local traverse_nodes   = nodes.traverse - -local nodecodes        = nodes.nodecodes -local whatcodes        = nodes.whatcodes - -local glyph_code       = nodecodes.glyph -local hlist_code       = nodecodes.hlist -local vlist_code       = nodecodes.vlist -local disc_code        = nodecodes.disc -local glue_code        = nodecodes.glue -local kern_code        = nodecodes.kern -local rule_code        = nodecodes.rule -local whatsit_code     = nodecodes.whatsit -local spec_code        = nodecodes.glue_spec - -local localpar_code    = whatcodes.localpar -local dir_code         = whatcodes.dir - -local nodepool         = nodes.pool -local new_glyph        = nodepool.glyph - -local formatters       = string.formatters -local formatter        = string.formatter - -local hashes           = fonts.hashes - -local fontidentifiers  = hashes.identifiers -local fontdescriptions = hashes.descriptions -local fontcharacters   = hashes.characters -local fontproperties   = hashes.properties -local fontparameters   = hashes.parameters - -function char_tracers.collect(head,list,tag,n) -    n = n or 0 -    local ok, fn = false, nil -    while head do -        local id = head.id -        if id == glyph_code then -            local f = head.font -            if f ~= fn then -                ok, fn = false, f -            end -            local c = head.char -            local i = fontidentifiers[f].indices[c] or 0 -            if not ok then -                ok = true -                n = n + 1 -                list[n] = list[n] or { } -                list[n][tag] = { } -            end -            local l = list[n][tag] -            l[#l+1] = { c, f, i } -        elseif id == disc_code then -            -- skip -        else -            ok = false -        end -        head = head.next -    end -end - -function char_tracers.equal(ta, tb) -    if #ta ~= #tb then -        return false -    else -        for i=1,#ta do -            local a, b = ta[i], tb[i] -            if a[1] ~= b[1] or a[2] ~= b[2] or a[3] ~= b[3] then -                return false -            end -        end -    end -    return true -end - -function char_tracers.string(t) -    local tt = { } -    for i=1,#t do -        tt[i] = utfchar(t[i][1]) -    end -    return concat(tt,"") -end - -local f_unicode = formatters["%U"] - -function char_tracers.unicodes(t,decimal) -    local tt = { } -    for i=1,#t do -        local n = t[i][1] -        if n == 0 then -            tt[i] = "-" -        elseif decimal then -            tt[i] = n -        else -            tt[i] = f_unicode(n) -        end -    end -    return concat(tt," ") -end - -function char_tracers.indices(t,decimal) -    local tt = { } -    for i=1,#t do -        local n = t[i][3] -        if n == 0 then -            tt[i] = "-" -        elseif decimal then -            tt[i] = n -        else -            tt[i] = f_unicode(n) -        end -    end -    return concat(tt," ") -end - -function char_tracers.start() -    local npc = handlers.characters -    local list = { } -    function handlers.characters(head) -        local n = #list -        char_tracers.collect(head,list,'before',n) -        local h, d = npc(head) -        char_tracers.collect(head,list,'after',n) -        if #list > n then -            list[#list+1] = { } -        end -        return h, d -    end -    function char_tracers.stop() -        tracers.list['characters'] = list -        local variables = { -            ['title']                = 'ConTeXt Character Processing Information', -            ['color-background-one'] = lmx.get('color-background-yellow'), -            ['color-background-two'] = lmx.get('color-background-purple'), -        } -        lmx.show('context-characters.lmx',variables) -        handlers.characters = npc -        tasks.restart("processors", "characters") -    end -    tasks.restart("processors", "characters") -end - -local stack = { } - -function tracers.start(tag) -    stack[#stack+1] = tag -    local tracer = tracers[tag] -    if tracer and tracer.start then -        tracer.start() -    end -end -function tracers.stop() -    local tracer = stack[#stack] -    if tracer and tracer.stop then -        tracer.stop() -    end -    stack[#stack] = nil -end - --- experimental - -local collection, collecting, messages = { }, false, { } - -function step_tracers.start() -    collecting = true -end - -function step_tracers.stop() -    collecting = false -end - -function step_tracers.reset() -    for i=1,#collection do -        local c = collection[i] -        if c then -            free_node_list(c) -        end -    end -    collection, messages = { }, { } -end - -function step_tracers.nofsteps() -    return context(#collection) -end - -function step_tracers.glyphs(n,i) -    local c = collection[i] -    if c then -        local b = hpack_node_list(copy_node_list(c)) -- multiple arguments  -        texsetbox(n,b) -    end -end - -function step_tracers.features() -    -- we cannot use first_glyph here as it only finds characters with subtype < 256 -    local f = collection[1] -    while f do -        if f.id == glyph_code then -            local tfmdata, t = fontidentifiers[f.font], { } -            for feature, value in table.sortedhash(tfmdata.shared.features) do -                if feature == "number" or feature == "features" then -                    -- private -                elseif type(value) == "boolean" then -                    if value then -                        t[#t+1] = formatters["%s=yes"](feature) -                    else -                        -- skip -                    end -                else -                    t[#t+1] = formatters["%s=%s"](feature,value) -                end -            end -            if #t > 0 then -                context(concat(t,", ")) -            else -                context("no features") -            end -            return -        end -        f = f.next -    end -end - -function tracers.fontchar(font,char) -    local n = new_glyph() -    n.font, n.char, n.subtype = font, char, 256 -    context(n) -end - -function step_tracers.font(command) -    local c = collection[1] -    while c do -        local id = c.id -        if id == glyph_code then -            local font = c.font -            local name = file.basename(fontproperties[font].filename or "unknown") -            local size = fontparameters[font].size or 0 -            if command then -                context[command](font,name,size) -- size in sp -            else -                context("[%s: %s @ %p]",font,name,size) -            end -            return -        else -            c = c.next -        end -    end -end - -function step_tracers.codes(i,command) -    local c = collection[i] -    while c do -        local id = c.id -        if id == glyph_code then -            if command then -                local f, c = c.font,c.char -                local d = fontdescriptions[f] -                local d = d and d[c] -                context[command](f,c,d and d.class or "") -            else -                context("[%s:U+%04X]",c.font,c.char) -            end -        elseif id == whatsit_code and (c.subtype == localpar_code or c.subtype == dir_code) then -            context("[%s]",c.dir) -        else -            context("[%s]",nodecodes[id]) -        end -        c = c.next -    end -end - -function step_tracers.messages(i,command,split) -    local list = messages[i] -- or { "no messages" } -    if list then -        for i=1,#list do -            local l = list[i] -            if not command then -                context("(%s)",l) -            elseif split then -                local a, b = match(l,"^(.-)%s*:%s*(.*)$") -                context[command](a or l or "",b or "") -            else -                context[command](l) -            end -        end -    end -end - --- hooks into the node list processor (see otf) - -function step_tracers.check(head) -    if collecting then -        step_tracers.reset() -        local n = copy_node_list(head) -        injections.handler(n,nil,"trace",true) -        handlers.protectglyphs(n) -- can be option -        collection[1] = n -    end -end - -function step_tracers.register(head) -    if collecting then -        local nc = #collection+1 -        if messages[nc] then -            local n = copy_node_list(head) -            injections.handler(n,nil,"trace",true) -            handlers.protectglyphs(n) -- can be option -            collection[nc] = n -        end -    end -end - -function step_tracers.message(str,...) -    str = formatter(str,...) -    if collecting then -        local n = #collection + 1 -        local m = messages[n] -        if not m then m = { } messages[n] = m end -        m[#m+1] = str -    end -    return str -- saves an intermediate var in the caller -end - --- - -local threshold = 65536 - -local function toutf(list,result,nofresult,stopcriterium) -    if list then -        for n in traverse_nodes(list) do -            local id = n.id -            if id == glyph_code then -                local components = n.components -                if components then -                    result, nofresult = toutf(components,result,nofresult) -                else -                    local c = n.char -                    local fc = fontcharacters[n.font] -                    if fc then -                        local u = fc[c].tounicode -                        if u then -                            for s in gmatch(u,"....") do -                                nofresult = nofresult + 1 -                                result[nofresult] = utfchar(tonumber(s,16)) -                            end -                        else -                            nofresult = nofresult + 1 -                            result[nofresult] = utfchar(c) -                        end -                    else -                        nofresult = nofresult + 1 -                        result[nofresult] = utfchar(c) -                    end -                end -            elseif id == disc_code then -                result, nofresult = toutf(n.replace,result,nofresult) -- needed? -            elseif id == hlist_code or id == vlist_code then -             -- if nofresult > 0 and result[nofresult] ~= " " then -             --     nofresult = nofresult + 1 -             --     result[nofresult] = " " -             -- end -                result, nofresult = toutf(n.list,result,nofresult) -            elseif id == glue_code then -                if nofresult > 0 and result[nofresult] ~= " " then -                    nofresult = nofresult + 1 -                    result[nofresult] = " " -                end -            elseif id == kern_code and n.kern > threshold then -                if nofresult > 0 and result[nofresult] ~= " " then -                    nofresult = nofresult + 1 -                    result[nofresult] = " " -                end -            end -            if n == stopcriterium then -                break -            end -        end -    end -    if nofresult > 0 and result[nofresult] == " " then -        result[nofresult] = nil -        nofresult = nofresult - 1 -    end -    return result, nofresult -end - -function nodes.toutf(list,stopcriterium) -    local result, nofresult = toutf(list,{},0,stopcriterium) -    return concat(result) -end diff --git a/tex/context/base/font-odv.lua b/tex/context/base/font-odv.lua index 7255c5be5..198acd6f9 100644 --- a/tex/context/base/font-odv.lua +++ b/tex/context/base/font-odv.lua @@ -63,9 +63,9 @@ if not modules then modules = { } end modules ['font-odv'] = {  --  -- local function ms_matra(c)  --     local prebase, abovebase, belowbase, postbase = true, true, true, true ---     local n = c.next ---     while n and n.id == glyph_code and n.subtype < 256 and n.font == font do ---         local char = n.char +--     local n = getnext(c) +--     while n and getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font do +--         local char = getchar(n)  --         if not dependent_vowel[char] then  --             break  --         elseif pre_mark[char] and prebase then @@ -79,7 +79,7 @@ if not modules then modules = { } end modules ['font-odv'] = {  --         else  --             return c  --         end ---         c = c.next +--         c = getnext(c)  --     end  --     return c  -- end @@ -106,11 +106,26 @@ local methods            = fonts.analyzers.methods  local otffeatures        = fonts.constructors.newfeatures("otf")  local registerotffeature = otffeatures.register -local insert_node_after  = nodes.insert_after -local copy_node          = nodes.copy -local free_node          = nodes.free -local remove_node        = nodes.remove -local flush_list         = nodes.flush_list +local nuts               = nodes.nuts +local tonode             = nuts.tonode +local tonut              = nuts.tonut + +local getnext            = nuts.getnext +local getprev            = nuts.getprev +local getid              = nuts.getid +local getchar            = nuts.getchar +local getfont            = nuts.getfont +local getsubtype         = nuts.getsubtype +local getfield           = nuts.getfield +local setfield           = nuts.setfield +local getattr            = nuts.getattr +local setattr            = nuts.setattr + +local insert_node_after  = nuts.insert_after +local copy_node          = nuts.copy +local free_node          = nuts.free +local remove_node        = nuts.remove +local flush_list         = nuts.flush_list  local unsetvalue         = attributes.unsetvalue @@ -147,7 +162,7 @@ xprocesscharacters = function(head,font)  end  local function processcharacters(head,font) -    return xprocesscharacters(head) +    return tonut(xprocesscharacters(tonode(head)))  end  -- function processcharacters(head,font) @@ -402,6 +417,7 @@ local reorder_class = {      [0x0CC4] = "after subscript",      [0x0CD5] = "after subscript",      [0x0CD6] = "after subscript", +    -- malayalam  }  -- We use some pseudo features as we need to manipulate the nodelist based @@ -615,30 +631,30 @@ local function deva_reorder(head,start,stop,font,attr,nbspaces)      local lookuphash, reph, vattu, blwfcache = deva_initialize(font,attr) -- could be inlines but ugly      local current   = start -    local n         = start.next +    local n         = getnext(start)      local base      = nil      local firstcons = nil      local lastcons  = nil      local basefound = false -    if ra[start.char] and halant[n.char] and reph then +    if ra[getchar(start)] and halant[getchar(n)] and reph then          -- if syllable starts with Ra + H and script has 'Reph' then exclude Reph          -- from candidates for base consonants          if n == stop then              return head, stop, nbspaces          end -        if n.next.char == c_zwj then +        if getchar(getnext(n)) == c_zwj then              current = start          else -            current = n.next -            start[a_state] = s_rphf +            current = getnext(n) +            setattr(start,a_state,s_rphf)          end      end -    if current.char == c_nbsp then +    if getchar(current) == c_nbsp then          -- Stand Alone cluster          if current == stop then -            stop = stop.prev +            stop = getprev(stop)              head = remove_node(head,current)              free_node(current)              return head, stop, nbspaces @@ -647,37 +663,37 @@ local function deva_reorder(head,start,stop,font,attr,nbspaces)              base      = current              firstcons = current              lastcons  = current -            current   = current.next +            current   = getnext(current)              if current ~= stop then -                if nukta[current.char] then -                    current = current.next +                if nukta[getchar(current)] then +                    current = getnext(current)                  end -                if current.char == c_zwj then +                if getchar(current) == c_zwj then                      if current ~= stop then -                        local next = current.next -                        if next ~= stop and halant[next.char] then +                        local next = getnext(current) +                        if next ~= stop and halant[getchar(next)] then                              current = next -                            next = current.next -                            local tmp = next and next.next or nil -- needs checking +                            next = getnext(current) +                            local tmp = next and getnext(next) or nil -- needs checking                              local changestop = next == stop                              local tempcurrent = copy_node(next)                              local nextcurrent = copy_node(current) -                            tempcurrent.next = nextcurrent -                            nextcurrent.prev = tempcurrent -                            tempcurrent[a_state] = s_blwf +                            setfield(tempcurrent,"next",nextcurrent) +                            setfield(nextcurrent,"prev",tempcurrent) +                            setattr(tempcurrent,a_state,s_blwf)                              tempcurrent = processcharacters(tempcurrent,font) -                            tempcurrent[a_state] = unsetvalue -                            if next.char == tempcurrent.char then +                            setattr(tempcurrent,a_state,unsetvalue) +                            if getchar(next) == getchar(tempcurrent) then                                  flush_list(tempcurrent)                                  local n = copy_node(current) -                                current.char = dotted_circle +                                setfield(current,"char",dotted_circle)                                  head = insert_node_after(head, current, n)                              else -                                current.char = tempcurrent.char    -- (assumes that result of blwf consists of one node) -                                local freenode = current.next -                                current.next = tmp +                                setfield(current,"char",getchar(tempcurrent))    -- (assumes that result of blwf consists of one node) +                                local freenode = getnext(current) +                                setfield(current,"next",tmp)                                  if tmp then -                                    tmp.prev = current +                                    setfield(tmp,"prev",current)                                  end                                  free_node(freenode)                                  flush_list(tempcurrent) @@ -694,82 +710,82 @@ local function deva_reorder(head,start,stop,font,attr,nbspaces)      while not basefound do          -- find base consonant -        if consonant[current.char] then -            current[a_state] = s_half +        if consonant[getchar(current)] then +            setattr(current,a_state,s_half)              if not firstcons then                  firstcons = current              end              lastcons = current              if not base then                  base = current -            elseif blwfcache[current.char] then +            elseif blwfcache[getchar(current)] then                  -- consonant has below-base (or post-base) form -                current[a_state] = s_blwf +                setattr(current,a_state,s_blwf)              else                  base = current              end          end          basefound = current == stop -        current = current.next +        current = getnext(current)      end      if base ~= lastcons then          -- if base consonant is not last one then move halant from base consonant to last one          local np = base -        local n = base.next -        if nukta[n.char] then +        local n = getnext(base) +        if nukta[getchar(n)] then              np = n -            n = n.next +            n = getnext(n)          end -        if halant[n.char] then +        if halant[getchar(n)] then              if lastcons ~= stop then -                local ln = lastcons.next -                if nukta[ln.char] then +                local ln = getnext(lastcons) +                if nukta[getchar(ln)] then                      lastcons = ln                  end              end -         -- local np = n.prev -            local nn = n.next -            local ln = lastcons.next -- what if lastcons is nn ? -            np.next = nn -            nn.prev = np -            lastcons.next = n +         -- local np = getprev(n) +            local nn = getnext(n) +            local ln = getnext(lastcons) -- what if lastcons is nn ? +            setfield(np,"next",nn) +            setfield(nn,"prev",np) +            setfield(lastcons,"next",n)              if ln then -                ln.prev = n +                setfield(ln,"prev",n)              end -            n.next = ln -            n.prev = lastcons +            setfield(n,"next",ln) +            setfield(n,"prev",lastcons)              if lastcons == stop then                  stop = n              end          end      end -    n = start.next -    if n ~= stop and ra[start.char] and halant[n.char] and not zw_char[n.next.char] then +    n = getnext(start) +    if n ~= stop and ra[getchar(start)] and halant[getchar(n)] and not zw_char[getchar(getnext(n))] then          -- if syllable starts with Ra + H then move this combination so that it follows either:          -- the post-base 'matra' (if any) or the base consonant          local matra = base          if base ~= stop then -            local next = base.next -            if dependent_vowel[next.char] then +            local next = getnext(base) +            if dependent_vowel[getchar(next)] then                  matra = next              end          end          -- [sp][start][n][nn] [matra|base][?]          -- [matra|base][start]  [n][?] [sp][nn] -        local sp = start.prev -        local nn = n.next -        local mn = matra.next +        local sp = getprev(start) +        local nn = getnext(n) +        local mn = getnext(matra)          if sp then -            sp.next = nn +            setfield(sp,"next",nn)          end -        nn.prev = sp -        matra.next = start -        start.prev = matra -        n.next = mn +        setfield(nn,"prev",sp) +        setfield(matra,"next",start) +        setfield(start,"prev",matra) +        setfield(n,"next",mn)          if mn then -            mn.prev = n +            setfield(mn,"prev",n)          end          if head == start then              head = nn @@ -782,17 +798,17 @@ local function deva_reorder(head,start,stop,font,attr,nbspaces)      local current = start      while current ~= stop do -        local next = current.next -        if next ~= stop and halant[next.char] and next.next.char == c_zwnj then -            current[a_state] = unsetvalue +        local next = getnext(current) +        if next ~= stop and halant[getchar(next)] and getchar(getnext(next)) == c_zwnj then +            setattr(current,a_state,unsetvalue)          end          current = next      end -    if base ~= stop and base[a_state] then -        local next = base.next -        if halant[next.char] and not (next ~= stop and next.next.char == c_zwj) then -            base[a_state] = unsetvalue +    if base ~= stop and getattr(base,a_state) then +        local next = getnext(base) +        if halant[getchar(next)] and not (next ~= stop and getchar(getnext(next)) == c_zwj) then +            setattr(base,a_state,unsetvalue)          end      end @@ -802,62 +818,62 @@ local function deva_reorder(head,start,stop,font,attr,nbspaces)      -- classify consonants and 'matra' parts as pre-base, above-base (Reph), below-base or post-base, and group elements of the syllable (consonants and 'matras') according to this classification      local current, allreordered, moved = start, false, { [base] = true } -    local a, b, p, bn = base, base, base, base.next -    if base ~= stop and nukta[bn.char] then +    local a, b, p, bn = base, base, base, getnext(base) +    if base ~= stop and nukta[getchar(bn)] then          a, b, p = bn, bn, bn      end      while not allreordered do          -- current is always consonant          local c = current -        local n = current.next +        local n = getnext(current)          local l = nil -- used ?          if c ~= stop then -            if nukta[n.char] then +            if nukta[getchar(n)] then                  c = n -                n = n.next +                n = getnext(n)              end              if c ~= stop then -                if halant[n.char] then +                if halant[getchar(n)] then                      c = n -                    n = n.next +                    n = getnext(n)                  end -                while c ~= stop and dependent_vowel[n.char] do +                while c ~= stop and dependent_vowel[getchar(n)] do                      c = n -                    n = n.next +                    n = getnext(n)                  end                  if c ~= stop then -                    if vowel_modifier[n.char] then +                    if vowel_modifier[getchar(n)] then                          c = n -                        n = n.next +                        n = getnext(n)                      end -                    if c ~= stop and stress_tone_mark[n.char] then +                    if c ~= stop and stress_tone_mark[getchar(n)] then                          c = n -                        n = n.next +                        n = getnext(n)                      end                  end              end          end -        local bp = firstcons.prev -        local cn = current.next -        local last = c.next +        local bp = getprev(firstcons) +        local cn = getnext(current) +        local last = getnext(c)          while cn ~= last do              -- move pre-base matras... -            if pre_mark[cn.char] then +            if pre_mark[getchar(cn)] then                  if bp then -                    bp.next = cn +                    setfield(bp,"next",cn)                  end -                local next = cn.next -                local prev = cn.prev +                local next = getnext(cn) +                local prev = getprev(cn)                  if next then -                    next.prev = prev +                    setfield(next,"prev",prev)                  end -                prev.next = next +                setfield(prev,"next",next)                  if cn == stop then                      stop = prev                  end -                cn.prev = bp -                cn.next = firstcons -                firstcons.prev = cn +                setfield(cn,"prev",bp) +                setfield(cn,"next",firstcons) +                setfield(firstcons,"prev",cn)                  if firstcons == start then                      if head == start then                          head = cn @@ -866,29 +882,29 @@ local function deva_reorder(head,start,stop,font,attr,nbspaces)                  end                  break              end -            cn = cn.next +            cn = getnext(cn)          end          allreordered = c == stop -        current = c.next +        current = getnext(c)      end      if reph or vattu then          local current, cns = start, nil          while current ~= stop do              local c = current -            local n = current.next -            if ra[current.char] and halant[n.char] then +            local n = getnext(current) +            if ra[getchar(current)] and halant[getchar(n)] then                  c = n -                n = n.next +                n = getnext(n)                  local b, bn = base, base                  while bn ~= stop  do -                    local next = bn.next -                    if dependent_vowel[next.char] then +                    local next = getnext(bn) +                    if dependent_vowel[getchar(next)] then                          b = next                      end                      bn = next                  end -                if current[a_state] == s_rphf then +                if getattr(current,a_state) == s_rphf then                      -- position Reph (Ra + H) after post-base 'matra' (if any) since these                      -- become marks on the 'matra', not on the base glyph                      if b ~= current then @@ -901,65 +917,65 @@ local function deva_reorder(head,start,stop,font,attr,nbspaces)                          if b == stop then                              stop = c                          end -                        local prev = current.prev +                        local prev = getprev(current)                          if prev then -                            prev.next = n +                            setfield(prev,"next",n)                          end                          if n then -                            n.prev = prev +                            setfield(n,"prev",prev)                          end -                        local next = b.next -                        c.next = next +                        local next = getnext(b) +                        setfield(c,"next",next)                          if next then -                            next.prev = c +                            setfield(next,"prev",c)                          end -                        c.next = next -                        b.next = current -                        current.prev = b +                        setfield(c,"next",next) +                        setfield(b,"next",current) +                        setfield(current,"prev",b)                      end -                elseif cns and cns.next ~= current then +                elseif cns and getnext(cns) ~= current then -- todo: optimize next                      -- position below-base Ra (vattu) following the consonants on which it is placed (either the base consonant or one of the pre-base consonants) -                    local cp, cnsn = current.prev, cns.next +                    local cp, cnsn = getprev(current), getnext(cns)                      if cp then -                        cp.next = n +                        setfield(cp,"next",n)                      end                      if n then -                        n.prev = cp +                        setfield(n,"prev",cp)                      end -                    cns.next = current -                    current.prev = cns -                    c.next = cnsn +                    setfield(cns,"next",current) +                    setfield(current,"prev",cns) +                    setfield(c,"next",cnsn)                      if cnsn then -                        cnsn.prev = c +                        setfield(cnsn,"prev",c)                      end                      if c == stop then                          stop = cp                          break                      end -                    current = n.prev +                    current = getprev(n)                  end              else -                local char = current.char +                local char = getchar(current)                  if consonant[char] then                      cns = current -                    local next = cns.next -                    if halant[next.char] then +                    local next = getnext(cns) +                    if halant[getchar(next)] then                          cns = next                      end                  elseif char == c_nbsp then                      nbspaces   = nbspaces + 1                      cns        = current -                    local next = cns.next -                    if halant[next.char] then +                    local next = getnext(cns) +                    if halant[getchar(next)] then                          cns = next                      end                  end              end -            current = current.next +            current = getnext(current)          end      end -    if base.char == c_nbsp then +    if getchar(base) == c_nbsp then          nbspaces = nbspaces - 1          head = remove_node(head,base)          free_node(base) @@ -979,24 +995,24 @@ end  function handlers.devanagari_reorder_matras(head,start,kind,lookupname,replacement) -- no leak      local current = start -- we could cache attributes here -    local startfont = start.font -    local startattr = start[a_syllabe] +    local startfont = getfont(start) +    local startattr = getattr(start,a_syllabe)      -- can be fast loop -    while current and current.id == glyph_code and current.subtype<256 and current.font == font and current[a_syllabe] == startattr do -        local next = current.next -        if halant[current.char] and not current[a_state] then -            if next and next.id == glyph_code and next.subtype<256 and next.font == font and next[a_syllabe] == startattr and zw_char[next.char] then +    while current and getid(current) == glyph_code and getsubtype(current) < 256 and getfont(current) == font and getattr(current,a_syllabe) == startattr do +        local next = getnext(current) +        if halant[getchar(current)] and not getattr(current,a_state) then +            if next and getid(next) == glyph_code and getsubtype(next) < 256 and getfont(next) == font and getattr(next,a_syllabe) == startattr and zw_char[getchar(next)] then                  current = next              end -            local startnext = start.next +            local startnext = getnext(start)              head = remove_node(head,start) -            local next = current.next +            local next = getnext(current)              if next then -                next.prev = start +                setfield(next,"prev",start)              end -            start.next = next -            current.next = start -            start.prev = current +            setfield(start,"next",next) +            setfield(current,"next",start) +            setfield(start,"prev",current)              start = startnext              break          end @@ -1032,98 +1048,98 @@ end  function handlers.devanagari_reorder_reph(head,start,kind,lookupname,replacement)      -- since in Devanagari reph has reordering position 'before postscript' dev2 only follows step 2, 4, and 6,      -- the other steps are still ToDo (required for scripts other than dev2) -    local current   = start.next +    local current   = getnext(start)      local startnext = nil      local startprev = nil -    local startfont = start.font -    local startattr = start[a_syllabe] -    while current and current.id == glyph_code and current.subtype<256 and current.font == startfont and current[a_syllabe] == startattr do    --step 2 -        if halant[current.char] and not current[a_state] then -            local next = current.next -            if next and next.id == glyph_code and next.subtype<256 and next.font == startfont and next[a_syllabe] == startattr and zw_char[next.char] then +    local startfont = getfont(start) +    local startattr = getattr(start,a_syllabe) +    while current and getid(current) == glyph_code and getsubtype(current) < 256 and getfont(current) == startfont and getattr(current,a_syllabe) == startattr do    --step 2 +        if halant[getchar(current)] and not getattr(current,a_state) then +            local next = getnext(current) +            if next and getid(next) == glyph_code and getsubtype(next) < 256 and getfont(next) == startfont and getattr(next,a_syllabe) == startattr and zw_char[getchar(next)] then                  current = next              end -            startnext = start.next +            startnext = getnext(start)              head = remove_node(head,start) -            local next = current.next +            local next = getnext(current)              if next then -                next.prev = start +                setfield(next,"prev",start)              end -            start.next = next -            current.next = start -            start.prev = current +            setfield(start,"next",next) +            setfield(current,"next",start) +            setfield(start,"prev",current)              start = startnext -            startattr = start[a_syllabe] +            startattr = getattr(start,a_syllabe)              break          end -        current = current.next +        current = getnext(current)      end      if not startnext then -        current = start.next -        while current and current.id == glyph_code and current.subtype<256 and current.font == startfont and current[a_syllabe] == startattr do    --step 4 -            if current[a_state] == s_pstf then    --post-base -                startnext = start.next +        current = getnext(start) +        while current and getid(current) == glyph_code and getsubtype(current) < 256 and getfont(current) == startfont and getattr(current,a_syllabe) == startattr do    --step 4 +            if getattr(current,a_state) == s_pstf then    --post-base +                startnext = getnext(start)                  head = remove_node(head,start) -                local prev = current.prev -                start.prev = prev -                prev.next = start -                start.next = current -                current.prev = start +                local prev = getprev(current) +                setfield(start,"prev",prev) +                setfield(prev,"next",start) +                setfield(start,"next",current) +                setfield(current,"prev",start)                  start = startnext -                startattr = start[a_syllabe] +                startattr = getattr(start,a_syllabe)                  break              end -            current = current.next +            current = getnext(current)          end      end      -- ToDo: determine position for reph with reordering position other than 'before postscript'      -- (required for scripts other than dev2)      -- leaks      if not startnext then -        current = start.next +        current = getnext(start)          local c = nil -        while current and current.id == glyph_code and current.subtype<256 and current.font == startfont and current[a_syllabe] == startattr do    --step 5 +        while current and getid(current) == glyph_code and getsubtype(current) < 256 and getfont(current) == startfont and getattr(current,a_syllabe) == startattr do    --step 5              if not c then -                local char = current.char +                local char = getchar(current)                  -- todo: combine in one                  if mark_above_below_post[char] and reorder_class[char] ~= "after subscript" then                      c = current                  end              end -            current = current.next +            current = getnext(current)          end          -- here we can loose the old start node: maybe best split cases          if c then -            startnext = start.next +            startnext = getnext(start)              head = remove_node(head,start) -            local prev = c.prev -            start.prev = prev -            prev.next = start -            start.next = c -            c.prev = start +            local prev = getprev(c) +            setfield(start,"prev",prev) +            setfield(prev,"next",start) +            setfield(start,"next",c) +            setfield(c,"prev",start)              -- end              start = startnext -            startattr = start[a_syllabe] +            startattr = getattr(start,a_syllabe)          end      end      -- leaks      if not startnext then          current = start -        local next = current.next -        while next and next.id == glyph_code and next.subtype<256 and next.font == startfont and next[a_syllabe] == startattr do    --step 6 +        local next = getnext(current) +        while next and getid(next) == glyph_code and getsubtype(next) < 256 and getfont(next) == startfont and getattr(next,a_syllabe) == startattr do    --step 6              current = next -            next = current.next +            next = getnext(current)          end          if start ~= current then -            startnext = start.next +            startnext = getnext(start)              head = remove_node(head,start) -            local next = current.next +            local next = getnext(current)              if next then -                next.prev = start +                setfield(next,"prev",start)              end -            start.next = next -            current.next = start -            start.prev = current +            setfield(start,"next",next) +            setfield(current,"next",start) +            setfield(start,"prev",current)              start = startnext          end      end @@ -1146,71 +1162,71 @@ function handlers.devanagari_reorder_pre_base_reordering_consonants(head,start,k      local current = start      local startnext = nil      local startprev = nil -    local startfont = start.font -    local startattr = start[a_syllabe] +    local startfont = getfont(start) +    local startattr = getattr(start,a_syllabe)      -- can be fast for loop + caching state -    while current and current.id == glyph_code and current.subtype<256 and current.font == startfont and current[a_syllabe] == startattr do -        local next = current.next -        if halant[current.char] and not current[a_state] then -            if next and next.id == glyph_code and next.subtype<256 and next.font == font and next[a_syllabe] == startattr then -                local char = next.char +    while current and getid(current) == glyph_code and getsubtype(current) < 256 and getfont(current) == startfont and getattr(current,a_syllabe) == startattr do +        local next = getnext(current) +        if halant[getchar(current)] and not getattr(current,a_state) then +            if next and getid(next) == glyph_code and getsubtype(next) < 256 and getfont(next) == font and getattr(next,a_syllabe) == startattr then +                local char = getchar(next)                  if char == c_zwnj or char == c_zwj then                      current = next                  end              end -            startnext = start.next +            startnext = getnext(start)              removenode(start,start) -            local next = current.next +            local next = getnext(current)              if next then -                next.prev = start +                setfield(next,"prev",start)              end -            start.next = next -            current.next = start -            start.prev = current +            setfield(start,"next",next) +            setfield(current,"next",start) +            setfield(start,"prev",current)              start = startnext              break          end          current = next      end      if not startnext then -        current = start.next -        startattr = start[a_syllabe] -        while current and current.id == glyph_code and current.subtype<256 and current.font == startfont and current[a_syllabe] == startattr do -            if not consonant[current.char] and current[a_state] then    --main -                startnext = start.next +        current = getnext(start) +        startattr = getattr(start,a_syllabe) +        while current and getid(current) == glyph_code and getsubtype(current) < 256 and getfont(current) == startfont and getattr(current,a_syllabe) == startattr do +            if not consonant[getchar(current)] and getattr(current,a_state) then    --main +                startnext = getnext(start)                  removenode(start,start) -                local prev = current.prev -                start.prev = prev -                prev.next = start -                start.next = current -                current.prev = start +                local prev = getprev(current) +                setfield(start,"prev",prev) +                setfield(prev,"next",start) +                setfield(start,"next",current) +                setfield(current,"prev",start)                  start = startnext                  break              end -            current = current.next +            current = getnext(current)          end      end      return head, start, true  end  function handlers.devanagari_remove_joiners(head,start,kind,lookupname,replacement) -    local stop = start.next -    local startfont = start.font -    while stop and stop.id == glyph_code and stop.subtype<256 and stop.font == startfont do -        local char = stop.char +    local stop = getnext(start) +    local startfont = getfont(start) +    while stop and getid(stop) == glyph_code and getsubtype(stop) < 256 and getfont(stop) == startfont do +        local char = getchar(stop)          if char == c_zwnj or char == c_zwj then -            stop = stop.next +            stop = getnext(stop)          else              break          end      end      if stop then -        stop.prev.next = nil -        stop.prev = start.prev +        setfield(getfield(stop,"prev"),"next",nil) +        setfield(stop,"prev",getprev(start))      end -    local prev = start.prev +    local prev = getprev(start)      if prev then -        prev.next = stop +        setfield(prev,"next",stop)      end      if head == start then      	head = stop @@ -1276,6 +1292,7 @@ local function dev2_initialize(font,attr)                                  for k, v in next, ra do                                      local r = lookupcache[k]                                      if r then +                                        local h = false                                          for k, v in next, halant do                                              local h = r[k]                                              if h then @@ -1331,7 +1348,6 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa              for k, v in next, ra do                  local r = lookupcache[k]                  if r then -                    local h = false                      for k, v in next, halant do                          local h = r[k]                          if h then @@ -1345,21 +1361,21 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa                  end              end              local current = start -            local last = stop.next +            local last = getnext(stop)              while current ~= last do                  if current ~= stop then -                    local c = locl[current] or current.char +                    local c = locl[current] or getchar(current)                      local found = lookupcache[c]                      if found then -                        local next = current.next -                        local n = locl[next] or next.char +                        local next = getnext(current) +                        local n = locl[next] or getchar(next)                          if found[n] then    --above-base: rphf    Consonant + Halant -                            local afternext = next ~= stop and next.next -                            if afternext and zw_char[afternext.char] then -- ZWJ and ZWNJ prevent creation of reph +                            local afternext = next ~= stop and getnext(next) +                            if afternext and zw_char[getchar(afternext)] then -- ZWJ and ZWNJ prevent creation of reph                                  current = next -                                current = current.next +                                current = getnext(current)                              elseif current == start then -                                current[a_state] = s_rphf +                                setattr(current,a_state,s_rphf)                                  current = next                              else                                  current = next @@ -1367,7 +1383,7 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa                          end                      end                  end -                current = current.next +                current = getnext(current)              end          elseif kind == "pref" then              -- why not global? pretty ineffient this way @@ -1391,87 +1407,87 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa              end              --              local current = start -            local last = stop.next +            local last = getnext(stop)              while current ~= last do                  if current ~= stop then -                    local c = locl[current] or current.char +                    local c = locl[current] or getchar(current)                      local found = lookupcache[c]                      if found then -                        local next = current.next -                        local n = locl[next] or next.char +                        local next = getnext(current) +                        local n = locl[next] or getchar(next)                          if found[n] then -                            current[a_state] = s_pref -                            next[a_state] = s_pref +                            setattr(current,a_state,s_pref) +                            setattr(next,a_state,s_pref)                              current = next                          end                      end                  end -                current = current.next +                current = getnext(current)              end          elseif kind == "half" then -- half forms: half / Consonant + Halant              local current = start -            local last = stop.next +            local last = getnext(stop)              while current ~= last do                  if current ~= stop then -                    local c = locl[current] or current.char +                    local c = locl[current] or getchar(current)                      local found = lookupcache[c]                      if found then -                        local next = current.next -                        local n = locl[next] or next.char +                        local next = getnext(current) +                        local n = locl[next] or getchar(next)                          if found[n] then -                            if next ~= stop and next.next.char == c_zwnj then    -- zwnj prevent creation of half +                            if next ~= stop and getchar(getnext(next)) == c_zwnj then    -- zwnj prevent creation of half                                  current = next                              else -                                current[a_state] = s_half +                                setattr(current,a_state,s_half)                                  if not halfpos then                                      halfpos = current                                  end                              end -                            current = current.next +                            current = getnext(current)                          end                      end                  end -                current = current.next +                current = getnext(current)              end          elseif kind == "blwf" then -- below-base: blwf / Halant + Consonant              local current = start -            local last = stop.next +            local last = getnext(stop)              while current ~= last do                  if current ~= stop then -                    local c = locl[current] or current.char +                    local c = locl[current] or getchar(current)                      local found = lookupcache[c]                      if found then -                        local next = current.next -                        local n = locl[next] or next.char +                        local next = getnext(current) +                        local n = locl[next] or getchar(next)                          if found[n] then -                            current[a_state] = s_blwf -                            next[a_state] = s_blwf +                            setattr(current,a_state,s_blwf) +                            setattr(next,a_state,s_blwf)                              current = next                              subpos = current                          end                      end                  end -                current = current.next +                current = getnext(current)              end          elseif kind == "pstf" then -- post-base: pstf / Halant + Consonant              local current = start -            local last = stop.next +            local last = getnext(stop)              while current ~= last do                  if current ~= stop then -                    local c = locl[current] or current.char +                    local c = locl[current] or getchar(current)                      local found = lookupcache[c]                      if found then -                        local next = current.next -                        local n = locl[next] or next.char +                        local next = getnext(current) +                        local n = locl[next] or getchar(next)                          if found[n] then -                            current[a_state] = s_pstf -                            next[a_state] = s_pstf +                            setattr(current,a_state,s_pstf) +                            setattr(next,a_state,s_pstf)                              current = next                              postpos = current                          end                      end                  end -                current = current.next +                current = getnext(current)              end          end      end @@ -1483,14 +1499,14 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa      local current, base, firstcons = start, nil, nil -    if start[a_state] == s_rphf then +    if getattr(start,a_state) == s_rphf then          -- if syllable starts with Ra + H and script has 'Reph' then exclude Reph from candidates for base consonants -        current = start.next.next +        current = getnext(getnext(start))      end      local function stand_alone(is_nbsp)          if current == stop then -            stop = stop.prev +            stop = getprev(stop)              head = remove_node(head,current)              free_node(current)              return head, stop, nbspaces @@ -1498,36 +1514,36 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa              if is_nbsp then                  nbspaces = nbspaces + 1              end -            base    = current -            current = current.next +            base     = current +            current  = getnext(current)              if current ~= stop then -                local char = current.char +                local char = getchar(current)                  if nukta[char] then -                    current = current.next -                    char = current.char +                    current = getnext(current) +                    char = getchar(current)                  end                  if char == c_zwj then -                    local next = current.next -                    if current ~= stop and next ~= stop and halant[next.char] then +                    local next = getnext(current) +                    if current ~= stop and next ~= stop and halant[getchar(next)] then                          current = next -                        next = current.next -                        local tmp = next.next +                        next = getnext(current) +                        local tmp = getnext(next)                          local changestop = next == stop -                        next.next = nil -                        current[a_state] = s_pref +                        setfield(next,"next",nil) +                        setattr(current,a_state,s_pref)                          current = processcharacters(current,font) -                        current[a_state] = s_blwf +                        setattr(current,a_state,s_blwf)                          current = processcharacters(current,font) -                        current[a_state] = s_pstf +                        setattr(current,a_state,s_pstf)                          current = processcharacters(current,font) -                        current[a_state] = unsetvalue -                        if halant[current.char] then -                            current.next.next = tmp +                        setattr(current,a_state,unsetvalue) +                        if halant[getchar(current)] then +                            setfield(getnext(current),"next",tmp)                              local nc = copy_node(current) -                            current.char = dotted_circle +                            setfield(current,"char",dotted_circle)                              head = insert_node_after(head,current,nc)                          else -                            current.next = tmp -- assumes that result of pref, blwf, or pstf consists of one node +                            setfield(current,"next",tmp) -- assumes that result of pref, blwf, or pstf consists of one node                              if changestop then                                  stop = current                              end @@ -1538,23 +1554,23 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa          end      end -    if current ~= stop.next then +    if current ~= getnext(stop) then          -- Stand Alone cluster          stand_alone() -    elseif current.char == c_nbsp then +    elseif getchar(current) == c_nbsp then          -- Stand Alone cluster          stand_alone(true)      else -- not Stand Alone cluster -        local last = stop.next +        local last = getnext(stop)          while current ~= last do    -- find base consonant -            local next = current.next -            if consonant[current.char] then -                if not (current ~= stop and next ~= stop and halant[next.char] and next.next.char == c_zwj) then +            local next = getnext(current) +            if consonant[getchar(current)] then +                if not (current ~= stop and next ~= stop and halant[getchar(next)] and getchar(getnext(next)) == c_zwj) then                      if not firstcons then                          firstcons = current                      end                      -- check whether consonant has below-base or post-base form or is pre-base reordering Ra -                    local a = current[a_state] +                    local a = getattr(current,a_state)                      if not (a == s_pref or a == s_blwf or a == s_pstf) then                          base = current                      end @@ -1568,13 +1584,13 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa      end      if not base then -        if start[a_state] == s_rphf then -            start[a_state] = unsetvalue +        if getattr(start,a_state) == s_rphf then +            setattr(start,a_state,unsetvalue)          end          return head, stop, nbspaces      else -        if base[a_state] then -            base[a_state] = unsetvalue +        if getattr(base,a_state) then +            setattr(base,a_state,unsetvalue)          end          basepos = base      end @@ -1592,32 +1608,32 @@ local function dev2_reorder(head,start,stop,font,attr,nbspaces) -- maybe do a pa      local moved = { }      local current = start -    local last = stop.next +    local last = getnext(stop)      while current ~= last do -        local char, target, cn = locl[current] or current.char, nil, current.next +        local char, target, cn = locl[current] or getchar(current), nil, getnext(current)  -- not so efficient (needed for malayalam)  local tpm = twopart_mark[char]  if tpm then      local extra = copy_node(current)      char = tpm[1] -    current.char = char -    extra.char = tpm[2] -    head = insert_after(head,current,extra) +    setfield(current,"char",char) +    setfield(extra,"char",tpm[2]) +    head = insert_node_after(head,current,extra)  end  --          if not moved[current] and dependent_vowel[char] then              if pre_mark[char] then            -- Before first half form in the syllable                  moved[current] = true -                local prev = current.prev -                local next = current.next +                local prev = getprev(current) +                local next = getnext(current)                  if prev then -                    prev.next = next +                    setfield(prev,"next",next)                  end                  if next then -                    next.prev = prev +                    setfield(next,"prev",prev)                  end                  if current == stop then -                    stop = current.prev +                    stop = getprev(current)                  end                  if halfpos == start then                      if head == start then @@ -1625,13 +1641,13 @@ end                      end                      start = current                  end -                local prev = halfpos.prev +                local prev = getprev(halfpos)                  if prev then -                    prev.next = current +                    setfield(prev,"next",current)                  end -                current.prev = prev -                halfpos.prev = current -                current.next = halfpos +                setfield(current,"prev",prev) +                setfield(halfpos,"prev",current) +                setfield(current,"next",halfpos)                  halfpos = current              elseif above_mark[char] then    -- After main consonant                  target = basepos @@ -1653,25 +1669,25 @@ end                  postpos = current              end              if mark_above_below_post[char] then -                local prev = current.prev +                local prev = getprev(current)                  if prev ~= target then -                    local next = current.next +                    local next = getnext(current)                      if prev then -- not needed, already tested with target -                        prev.next = next +                        setfield(prev,"next",next)                      end                      if next then -                        next.prev = prev +                        setfield(next,"prev",prev)                      end                      if current == stop then                          stop = prev                      end -                    local next = target.next +                    local next = getnext(target)                      if next then -                        next.prev = current +                        setfield(next,"prev",current)                      end -                    current.next = next -                    target.next = current -                    current.prev = target +                    setfield(current,"next",next) +                    setfield(target,"next",current) +                    setfield(current,"prev",target)                  end              end          end @@ -1682,7 +1698,7 @@ end      local current, c = start, nil      while current ~= stop do -        local char = current.char +        local char = getchar(current)          if halant[char] or stress_tone_mark[char] then              if not c then                  c = current @@ -1690,33 +1706,33 @@ end          else              c = nil          end -        local next = current.next -        if c and nukta[next.char] then +        local next = getnext(current) +        if c and nukta[getchar(next)] then              if head == c then                  head = next              end              if stop == next then                  stop = current              end -            local prev = c.prev +            local prev = getprev(c)              if prev then -                prev.next = next +                setfield(prev,"next",next)              end -            next.prev = prev -            local nextnext = next.next -            current.next = nextnext -            local nextnextnext = nextnext.next +            setfield(next,"prev",prev) +            local nextnext = getnext(next) +            setfield(current,"next",nextnext) +            local nextnextnext = getnext(nextnext)              if nextnextnext then -                nextnextnext.prev = current +                setfield(nextnextnext,"prev",current)              end -            c.prev = nextnext -            nextnext.next = c +            setfield(c,"prev",nextnext) +            setfield(nextnext,"next",c)          end          if stop == current then break end -        current = current.next +        current = getnext(current)      end -    if base.char == c_nbsp then +    if getchar(base) == c_nbsp then          nbspaces = nbspaces - 1          head = remove_node(head, base)          free_node(base) @@ -1740,30 +1756,30 @@ for k, v in next, halant do separator[k] = true end  local function analyze_next_chars_one(c,font,variant) -- skip one dependent vowel      -- why two variants ... the comment suggests that it's the same ruleset -    local n = c.next +    local n = getnext(c)      if not n then          return c      end      if variant == 1 then -        local v = n.id == glyph_code and n.subtype<256 and n.font == font -        if v and nukta[n.char] then -            n = n.next +        local v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font +        if v and nukta[getchar(n)] then +            n = getnext(n)              if n then -                v = n.id == glyph_code and n.subtype<256 and n.font == font +                v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font              end          end          if n and v then -            local nn = n.next -            if nn and nn.id == glyph_code and nn.subtype<256 and nn.font == font then -                local nnn = nn.next -                if nnn and nnn.id == glyph_code and nnn.subtype<256 and nnn.font == font then -                    local nnc = nn.char -                    local nnnc = nnn.char +            local nn = getnext(n) +            if nn and getid(nn) == glyph_code and getsubtype(nn) < 256 and getfont(nn) == font then +                local nnn = getnext(nn) +                if nnn and getid(nnn) == glyph_code and getsubtype(nnn) < 256 and getfont(nnn) == font then +                    local nnc = getchar(nn) +                    local nnnc = getchar(nnn)                      if nnc == c_zwj and consonant[nnnc] then                          c = nnn                      elseif (nnc == c_zwnj or nnc == c_zwj) and halant[nnnc] then -                        local nnnn = nnn.next -                        if nnnn and nnnn.id == glyph_code and consonant[nnnn.char] and nnnn.subtype<256 and nnnn.font == font then +                        local nnnn = getnext(nnn) +                        if nnnn and getid(nnnn) == glyph_code and consonant[getchar(nnnn)] and getsubtype(nnnn) < 256 and getfont(nnnn) == font then                              c = nnnn                          end                      end @@ -1771,94 +1787,94 @@ local function analyze_next_chars_one(c,font,variant) -- skip one dependent vowe              end          end      elseif variant == 2 then -        if n.id == glyph_code and nukta[n.char] and n.subtype<256 and n.font == font then +        if getid(n) == glyph_code and nukta[getchar(n)] and getsubtype(n) < 256 and getfont(n) == font then              c = n          end -        n = c.next -        if n and n.id == glyph_code and n.subtype<256 and n.font == font then -            local nn = n.next +        n = getnext(c) +        if n and getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font then +            local nn = getnext(n)              if nn then -                local nv = nn.id == glyph_code and nn.subtype<256 and nn.font == font -                if nv and zw_char[n.char] then +                local nv = getid(nn) == glyph_code and getsubtype(nn) < 256 and getfont(nn) == font +                if nv and zw_char[getchar(n)] then                      n = nn -                    nn = nn.next -                    nv = nn and nn.id == glyph_code and nn.subtype<256 and nn.font == font +                    nn = getnext(nn) +                    nv = nn and getid(nn) == glyph_code and getsubtype(nn) < 256 and getfont(nn) == font                  end -                if nv and halant[n.char] and consonant[nn.char] then +                if nv and halant[getchar(n)] and consonant[getchar(nn)] then                      c = nn                  end              end          end      end      -- c = ms_matra(c) -    local n = c.next +    local n = getnext(c)      if not n then          return c      end -    local v = n.id == glyph_code and n.subtype<256 and n.font == font +    local v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font      if not v then          return c      end -    local char = n.char +    local char = getchar(n)      if dependent_vowel[char] then -        c = c.next -        n = c.next +        c = getnext(c) +        n = getnext(c)          if not n then              return c          end -        v = n.id == glyph_code and n.subtype<256 and n.font == font +        v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font          if not v then              return c          end -        char = n.char +        char = getchar(n)      end      if nukta[char] then -        c = c.next -        n = c.next +        c = getnext(c) +        n = getnext(c)          if not n then              return c          end -        v = n.id == glyph_code and n.subtype<256 and n.font == font +        v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font          if not v then              return c          end -        char = n.char +        char = getchar(n)      end      if halant[char] then -        c = c.next -        n = c.next +        c = getnext(c) +        n = getnext(c)          if not n then              return c          end -        v = n.id == glyph_code and n.subtype<256 and n.font == font +        v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font          if not v then              return c          end -        char = n.char +        char = getchar(n)      end      if vowel_modifier[char] then -        c = c.next -        n = c.next +        c = getnext(c) +        n = getnext(c)          if not n then              return c          end -        v = n.id == glyph_code and n.subtype<256 and n.font == font +        v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font          if not v then              return c          end -        char = n.char +        char = getchar(n)      end      if stress_tone_mark[char] then -        c = c.next -        n = c.next +        c = getnext(c) +        n = getnext(c)          if not n then              return c          end -        v = n.id == glyph_code and n.subtype<256 and n.font == font +        v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font          if not v then              return c          end -        char = n.char +        char = getchar(n)      end      if stress_tone_mark[char] then          return n @@ -1868,38 +1884,38 @@ local function analyze_next_chars_one(c,font,variant) -- skip one dependent vowe  end  local function analyze_next_chars_two(c,font) -    local n = c.next +    local n = getnext(c)      if not n then          return c      end -    if n.id == glyph_code and nukta[n.char] and n.subtype<256 and n.font == font then +    if getid(n) == glyph_code and nukta[getchar(n)] and getsubtype(n) < 256 and getfont(n) == font then          c = n      end      n = c      while true do -        local nn = n.next -        if nn and nn.id == glyph_code and nn.subtype<256 and nn.font == font then -            local char = nn.char +        local nn = getnext(n) +        if nn and getid(nn) == glyph_code and getsubtype(nn) < 256 and getfont(nn) == font then +            local char = getchar(nn)              if halant[char] then                  n = nn -                local nnn = nn.next -                if nnn and nnn.id == glyph_code and zw_char[nnn.char] and nnn.subtype<256 and nnn.font == font then +                local nnn = getnext(nn) +                if nnn and getid(nnn) == glyph_code and zw_char[getchar(nnn)] and getsubtype(nnn) < 256 and getfont(nnn) == font then                      n = nnn                  end              elseif char == c_zwnj or char == c_zwj then               -- n = nn -- not here (?) -                local nnn = nn.next -                if nnn and nnn.id == glyph_code and halant[nnn.char] and nnn.subtype<256 and nnn.font == font then +                local nnn = getnext(nn) +                if nnn and getid(nnn) == glyph_code and halant[getchar(nnn)] and getsubtype(nnn) < 256 and getfont(nnn) == font then                      n = nnn                  end              else                  break              end -            local nn = n.next -            if nn and nn.id == glyph_code and consonant[nn.char] and nn.subtype<256 and nn.font == font then +            local nn = getnext(n) +            if nn and getid(nn) == glyph_code and consonant[getchar(nn)] and getsubtype(nn) < 256 and getfont(nn) == font then                  n = nn -                local nnn = nn.next -                if nnn and nnn.id == glyph_code and nukta[nnn.char] and nnn.subtype<256 and nnn.font == font then +                local nnn = getnext(nn) +                if nnn and getid(nnn) == glyph_code and nukta[getchar(nnn)] and getsubtype(nnn) < 256 and getfont(nnn) == font then                      n = nnn                  end                  c = n @@ -1915,114 +1931,114 @@ local function analyze_next_chars_two(c,font)          -- This shouldn't happen I guess.          return      end -    local n = c.next +    local n = getnext(c)      if not n then          return c      end -    local v = n.id == glyph_code and n.subtype<256 and n.font == font +    local v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font      if not v then          return c      end -    local char = n.char +    local char = getchar(n)      if char == c_anudatta then          c = n -        n = c.next +        n = getnext(c)          if not n then              return c          end -        v = n.id == glyph_code and n.subtype<256 and n.font == font +        v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font          if not v then              return c          end -        char = n.char +        char = getchar(n)      end      if halant[char] then -        c = c.next -        n = c.next +        c = getnext(c) +        n = getnext(c)          if not n then              return c          end -        v = n.id == glyph_code and n.subtype<256 and n.font == font +        v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font          if not v then              return c          end -        char = n.char +        char = getchar(n)          if char == c_zwnj or char == c_zwj then -            c = c.next -            n = c.next +            c = getnext(c) +            n = getnext(c)              if not n then                  return c              end -            v = n.id == glyph_code and n.subtype<256 and n.font == font +            v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font              if not v then                  return c              end -            char = n.char +            char = getchar(n)          end      else          -- c = ms_matra(c)          -- same as one          if dependent_vowel[char] then -            c = c.next -            n = c.next +            c = getnext(c) +            n = getnext(c)              if not n then                  return c              end -            v = n.id == glyph_code and n.subtype<256 and n.font == font +            v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font              if not v then                  return c              end -            char = n.char +            char = getchar(n)          end          if nukta[char] then -            c = c.next -            n = c.next +            c = getnext(c) +            n = getnext(c)              if not n then                  return c              end -            v = n.id == glyph_code and n.subtype<256 and n.font == font +            v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font              if not v then                  return c              end -            char = n.char +            char = getchar(n)          end          if halant[char] then -            c = c.next -            n = c.next +            c = getnext(c) +            n = getnext(c)              if not n then                  return c              end -            v = n.id == glyph_code and n.subtype<256 and n.font == font +            v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font              if not v then                  return c              end -            char = n.char +            char = getchar(n)          end      end      -- same as one      if vowel_modifier[char] then -        c = c.next -        n = c.next +        c = getnext(c) +        n = getnext(c)          if not n then              return c          end -        v = n.id == glyph_code and n.subtype<256 and n.font == font +        v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font          if not v then              return c          end -        char = n.char +        char = getchar(n)      end      if stress_tone_mark[char] then -        c = c.next -        n = c.next +        c = getnext(c) +        n = getnext(c)          if not n then              return c          end -        v = n.id == glyph_code and n.subtype<256 and n.font == font +        v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font          if not v then              return c          end -        char = n.char +        char = getchar(n)      end      if stress_tone_mark[char] then          return n @@ -2034,9 +2050,9 @@ end  local function inject_syntax_error(head,current,mark)      local signal = copy_node(current)      if mark == pre_mark then -        signal.char = dotted_circle +        setfield(signal,"char",dotted_circle)      else -        current.char = dotted_circle +        setfield(current,"char",dotted_circle)      end      return insert_node_after(head,current,signal)  end @@ -2045,31 +2061,32 @@ end  -- a lot. Common code has been synced.  function methods.deva(head,font,attr) +    head           = tonut(head)      local current  = head      local start    = true      local done     = false      local nbspaces = 0      while current do -        if current.id == glyph_code and current.subtype<256 and current.font == font then +        if getid(current) == glyph_code and getsubtype(current) < 256 and getfont(current) == font then              done = true              local syllablestart = current              local syllableend = nil              local c = current -            local n = c.next -            if n and ra[c.char] and n.id == glyph_code and halant[n.char] and n.subtype<256 and n.font == font then -                local n = n.next -                if n and n.id == glyph_code and n.subtype<256 and n.font == font then +            local n = getnext(c) +            if n and ra[getchar(c)] and getid(n) == glyph_code and halant[getchar(n)] and getsubtype(n) < 256 and getfont(n) == font then +                local n = getnext(n) +                if n and getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font then                      c = n                  end              end -            local standalone = c.char == c_nbsp +            local standalone = getchar(c) == c_nbsp              if standalone then -                local prev = current.prev +                local prev = getprev(current)                  if not prev then                      -- begin of paragraph or box -                elseif prev.id ~= glyph_code or prev.subtype>=256 or prev.font ~= font then +                elseif getid(prev) ~= glyph_code or getsubtype(prev) >= 256 or getfont(prev) ~= font then                      -- different font or language so quite certainly a different word -                elseif not separator[prev.char] then +                elseif not separator[getchar(prev)] then                      -- something that separates words                  else                      standalone = false @@ -2078,61 +2095,61 @@ function methods.deva(head,font,attr)              if standalone then                  -- stand alone cluster (at the start of the word only): #[Ra+H]+NBSP+[N]+[<[<ZWJ|ZWNJ>]+H+C>]+[{M}+[N]+[H]]+[SM]+[(VD)]  				local syllableend = analyze_next_chars_one(c,font,2) -				current = syllableend.next +				current = getnext(syllableend)                  if syllablestart ~= syllableend then                      head, current, nbspaces = deva_reorder(head,syllablestart,syllableend,font,attr,nbspaces) -                    current = current.next +                    current = getnext(current)                  end              else -                -- we can delay the n.subtype and n.font and test for say halant first +                -- we can delay the getsubtype(n) and getfont(n) and test for say halant first                  -- as an table access is faster than two function calls (subtype and font are                  -- pseudo fields) but the code becomes messy (unless we make it a function) -                local char = current.char +                local char = getchar(current)                  if consonant[char] then                      -- syllable containing consonant                      local prevc = true                      while prevc do                          prevc = false -                        local n = current.next +                        local n = getnext(current)                          if not n then                              break                          end -                        local v = n.id == glyph_code and n.subtype<256 and n.font == font +                        local v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font                          if not v then                              break                          end -                        local c = n.char +                        local c = getchar(n)                          if nukta[c] then -                            n = n.next +                            n = getnext(n)                              if not n then                                  break                              end -                            v = n.id == glyph_code and n.subtype<256 and n.font == font +                            v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font                              if not v then                                  break                              end -                            c = n.char +                            c = getchar(n)                          end                          if halant[c] then -                            n = n.next +                            n = getnext(n)                              if not n then                                  break                              end -                            v = n.id == glyph_code and n.subtype<256 and n.font == font +                            v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font                              if not v then                                  break                              end -                            c = n.char +                            c = getchar(n)                              if c == c_zwnj or c == c_zwj then -                                n = n.next +                                n = getnext(n)                                  if not n then                                      break                                  end -                                v = n.id == glyph_code and n.subtype<256 and n.font == font +                                v = getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font                                  if not v then                                      break                                  end -                                c = n.char +                                c = getchar(n)                              end                              if consonant[c] then                                  prevc = true @@ -2140,77 +2157,77 @@ function methods.deva(head,font,attr)                              end                          end                      end -                    local n = current.next -                    if n and n.id == glyph_code and nukta[n.char] and n.subtype<256 and n.font == font then +                    local n = getnext(current) +                    if n and getid(n) == glyph_code and nukta[getchar(n)] and getsubtype(n) < 256 and getfont(n) == font then                          -- nukta (not specified in Microsft Devanagari OpenType specification)                          current = n -                        n = current.next +                        n = getnext(current)                      end                      syllableend = current                      current = n                      if current then -                        local v = current.id == glyph_code and current.subtype<256 and current.font == font +                        local v = getid(current) == glyph_code and getsubtype(current) < 256 and getfont(current) == font                          if v then -                            if halant[current.char] then +                            if halant[getchar(current)] then                                  -- syllable containing consonant without vowels: {C + [Nukta] + H} + C + H -                                local n = current.next -                                if n and n.id == glyph_code and zw_char[n.char] and n.subtype<256 and n.font == font then +                                local n = getnext(current) +                                if n and getid(n) == glyph_code and zw_char[getchar(n)] and getsubtype(n) < 256 and getfont(n) == font then                                      -- code collapsed, probably needs checking with intention                                      syllableend = n -                                    current = n.next +                                    current = getnext(n)                                  else                                      syllableend = current                                      current = n                                  end                              else                                  -- syllable containing consonant with vowels: {C + [Nukta] + H} + C + [M] + [VM] + [SM] -                                local c = current.char +                                local c = getchar(current)                                  if dependent_vowel[c] then                                      syllableend = current -                                    current = current.next -                                    v = current and current.id == glyph_code and current.subtype<256 and current.font == font +                                    current = getnext(current) +                                    v = current and getid(current) == glyph_code and getsubtype(current) < 256 and getfont(current) == font                                      if v then -                                        c = current.char +                                        c = getchar(current)                                      end                                  end                                  if v and vowel_modifier[c] then                                      syllableend = current -                                    current = current.next -                                    v = current and current.id == glyph_code and current.subtype<256 and current.font == font +                                    current = getnext(current) +                                    v = current and getid(current) == glyph_code and getsubtype(current) < 256 and getfont(current) == font                                      if v then -                                        c = current.char +                                        c = getchar(current)                                      end                                  end                                  if v and stress_tone_mark[c] then                                      syllableend = current -                                    current = current.next +                                    current = getnext(current)                                  end                              end                          end                      end                      if syllablestart ~= syllableend then                          head, current, nbspaces = deva_reorder(head,syllablestart,syllableend,font,attr,nbspaces) -                        current = current.next +                        current = getnext(current)                      end                  elseif independent_vowel[char] then                      -- syllable without consonants: VO + [VM] + [SM]                      syllableend = current -                    current = current.next +                    current = getnext(current)                      if current then -                        local v = current.id == glyph_code and current.subtype<256 and current.font == font +                        local v = getid(current) == glyph_code and getsubtype(current) < 256 and getfont(current) == font                          if v then -                            local c = current.char +                            local c = getchar(current)                              if vowel_modifier[c] then                                  syllableend = current -                                current = current.next -                                v = current and current.id == glyph_code and current.subtype<256 and current.font == font +                                current = getnext(current) +                                v = current and getid(current) == glyph_code and getsubtype(current) < 256 and getfont(current) == font                                  if v then -                                    c = current.char +                                    c = getchar(current)                                  end                              end                              if v and stress_tone_mark[c] then                                  syllableend = current -                                current = current.next +                                current = getnext(current)                              end                          end                      end @@ -2219,11 +2236,11 @@ function methods.deva(head,font,attr)                      if mark then                          head, current = inject_syntax_error(head,current,mark)                      end -                    current = current.next +                    current = getnext(current)                  end              end          else -            current = current.next +            current = getnext(current)          end          start = false      end @@ -2232,7 +2249,7 @@ function methods.deva(head,font,attr)          head = replace_all_nbsp(head)      end -    head = typesetters.characters.handler(head) +    head = tonode(head)      return head, done  end @@ -2243,6 +2260,7 @@ end  -- handler(head,start,kind,lookupname,lookupmatch,sequence,lookuphash,1)  function methods.dev2(head,font,attr) +    head           = tonut(head)      local current  = head      local start    = true      local done     = false @@ -2250,18 +2268,18 @@ function methods.dev2(head,font,attr)      local nbspaces = 0      while current do          local syllablestart, syllableend = nil, nil -        if current.id == glyph_code and current.subtype<256 and current.font == font then +        if getid(current) == glyph_code and getsubtype(current) < 256 and getfont(current) == font then              done = true              syllablestart = current              local c = current -            local n = current.next -            if n and ra[c.char] and n.id == glyph_code and halant[n.char] and n.subtype<256 and n.font == font then -                local n = n.next -                if n and n.id == glyph_code and n.subtype<256 and n.font == font then +            local n = getnext(current) +            if n and ra[getchar(c)] and getid(n) == glyph_code and halant[getchar(n)] and getsubtype(n) < 256 and getfont(n) == font then +                local n = getnext(n) +                if n and getid(n) == glyph_code and getsubtype(n) < 256 and getfont(n) == font then                      c = n                  end              end -            local char = c.char +            local char = getchar(c)              if independent_vowel[char] then                  -- vowel-based syllable: [Ra+H]+V+[N]+[<[<ZWJ|ZWNJ>]+H+C|ZWJ+C>]+[{M}+[N]+[H]]+[SM]+[(VD)]                  current = analyze_next_chars_one(c,font,1) @@ -2270,12 +2288,12 @@ function methods.dev2(head,font,attr)                  local standalone = char == c_nbsp                  if standalone then                      nbspaces = nbspaces + 1 -                    local p = current.prev +                    local p = getprev(current)                      if not p then                          -- begin of paragraph or box -                    elseif p.id ~= glyph_code or p.subtype>=256 or p.font ~= font then +                    elseif getid(p) ~= glyph_code or getsubtype(p) >= 256 or getfont(p) ~= font then                          -- different font or language so quite certainly a different word -                    elseif not separator[p.char] then +                    elseif not separator[getchar(p)] then                          -- something that separates words                      else                          standalone = false @@ -2285,7 +2303,7 @@ function methods.dev2(head,font,attr)                      -- Stand Alone cluster (at the start of the word only): #[Ra+H]+NBSP+[N]+[<[<ZWJ|ZWNJ>]+H+C>]+[{M}+[N]+[H]]+[SM]+[(VD)]                      current = analyze_next_chars_one(c,font,2)                      syllableend = current -                elseif consonant[current.char] then +                elseif consonant[getchar(current)] then                      -- WHY current INSTEAD OF c ?                      -- Consonant syllable: {C+[N]+<H+[<ZWNJ|ZWJ>]|<ZWNJ|ZWJ>+H>} + C+[N]+[A] + [< H+[<ZWNJ|ZWJ>] | {M}+[N]+[H]>]+[SM]+[(VD)] @@ -2297,29 +2315,31 @@ function methods.dev2(head,font,attr)          if syllableend then              syllabe = syllabe + 1              local c = syllablestart -            local n = syllableend.next +            local n = getnext(syllableend)              while c ~= n do -                c[a_syllabe] = syllabe -                c = c.next +                setattr(c,a_syllabe,syllabe) +                c = getnext(c)              end          end          if syllableend and syllablestart ~= syllableend then              head, current, nbspaces = dev2_reorder(head,syllablestart,syllableend,font,attr,nbspaces)          end -        if not syllableend and current.id == glyph_code and current.subtype<256 and current.font == font and not current[a_state] then -            local mark = mark_four[current.char] +        if not syllableend and getid(current) == glyph_code and getsubtype(current) < 256 and getfont(current) == font and not getattr(current,a_state) then +            local mark = mark_four[getchar(current)]              if mark then                  head, current = inject_syntax_error(head,current,mark)              end          end          start = false -        current = current.next +        current = getnext(current)      end      if nbspaces > 0 then          head = replace_all_nbsp(head)      end +    head = tonode(head) +      return head, done  end diff --git a/tex/context/base/font-otn.lua b/tex/context/base/font-otn.lua index c57be5f02..7a5ae1758 100644 --- a/tex/context/base/font-otn.lua +++ b/tex/context/base/font-otn.lua @@ -171,12 +171,28 @@ registertracker("otf.injections","nodes.injections")  registertracker("*otf.sample","otf.steps,otf.actions,otf.analyzing") -local insert_node_after  = node.insert_after -local delete_node        = nodes.delete -local copy_node          = node.copy -local find_node_tail     = node.tail or node.slide -local flush_node_list    = node.flush_list -local end_of_math        = node.end_of_math +local nuts               = nodes.nuts +local tonode             = nuts.tonode +local tonut              = nuts.tonut + +local getfield           = nuts.getfield +local getnext            = nuts.getnext +local getprev            = nuts.getprev +local getid              = nuts.getid +local getattr            = nuts.getattr +local getfont            = nuts.getfont +local getsubtype         = nuts.getsubtype +local getchar            = nuts.getchar + +local setfield           = nuts.setfield +local setattr            = nuts.setattr + +local insert_node_after  = nuts.insert_after +local delete_node        = nuts.delete +local copy_node          = nuts.copy +local find_node_tail     = nuts.tail +local flush_node_list    = nuts.flush_list +local end_of_math        = nuts.end_of_math  local setmetatableindex  = table.setmetatableindex @@ -332,11 +348,11 @@ end  -- and indices.  local function copy_glyph(g) -- next and prev are untouched ! -    local components = g.components +    local components = getfield(g,"components")      if components then -        g.components = nil +        setfield(g,"components",nil)          local n = copy_node(g) -        g.components = components +        setfield(g,"components",components)          return n      else          return copy_node(g) @@ -346,28 +362,28 @@ end  -- start is a mark and we need to keep that one  local function markstoligature(kind,lookupname,head,start,stop,char) -    if start == stop and start.char == char then +    if start == stop and getchar(start) == char then          return head, start      else -        local prev = start.prev -        local next = stop.next -        start.prev = nil -        stop.next = nil +        local prev = getprev(start) +        local next = getnext(stop) +        setfield(start,"prev",nil) +        setfield(stop,"next",nil)          local base = copy_glyph(start)          if head == start then              head = base          end -        base.char = char -        base.subtype = ligature_code -        base.components = start +        setfield(base,"char",char) +        setfield(base,"subtype",ligature_code) +        setfield(base,"components",start)          if prev then -            prev.next = base +            setfield(prev,"next",base)          end          if next then -            next.prev = base +            setfield(next,"prev",base)          end -        base.next = next -        base.prev = prev +        setfield(base,"next",next) +        setfield(base,"prev",prev)          return head, base      end  end @@ -380,17 +396,17 @@ end  -- third component.  local function getcomponentindex(start) -    if start.id ~= glyph_code then +    if getid(start) ~= glyph_code then          return 0 -    elseif start.subtype == ligature_code then +    elseif getsubtype(start) == ligature_code then          local i = 0 -        local components = start.components +        local components = getfield(start,"components")          while components do              i = i + getcomponentindex(components) -            components = components.next +            components = getnext(components)          end          return i -    elseif not marks[start.char] then +    elseif not marks[getchar(start)] then          return 1      else          return 0 @@ -400,29 +416,29 @@ end  -- eventually we will do positioning in an other way (needs addional w/h/d fields)  local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound) -- brr head -    if start == stop and start.char == char then -        start.char = char +    if start == stop and getchar(start) == char then +        setfield(start,"char",char)          return head, start      end -    local prev = start.prev -    local next = stop.next -    start.prev = nil -    stop.next = nil +    local prev = getprev(start) +    local next = getnext(stop) +    setfield(start,"prev",nil) +    setfield(stop,"next",nil)      local base = copy_glyph(start)      if start == head then          head = base      end -    base.char = char -    base.subtype = ligature_code -    base.components = start -- start can have components +    setfield(base,"char",char) +    setfield(base,"subtype",ligature_code) +    setfield(base,"components",start) -- start can have components      if prev then -        prev.next = base +        setfield(prev,"next",base)      end      if next then -        next.prev = base +        setfield(next,"prev",base)      end -    base.next = next -    base.prev = prev +    setfield(base,"next",next) +    setfield(base,"prev",prev)      if not discfound then          local deletemarks = markflag ~= "mark"          local components = start @@ -432,35 +448,35 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun          local current = base          -- first we loop over the glyphs in start .. stop          while start do -            local char = start.char +            local char = getchar(start)              if not marks[char] then                  baseindex = baseindex + componentindex                  componentindex = getcomponentindex(start)              elseif not deletemarks then -- quite fishy -                start[a_ligacomp] = baseindex + (start[a_ligacomp] or componentindex) +                setattr(start,a_ligacomp,baseindex + (getattr(start,a_ligacomp) or componentindex))                  if trace_marks then -                    logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp]) +                    logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),getattr(start,a_ligacomp))                  end                  head, current = insert_node_after(head,current,copy_node(start)) -- unlikely that mark has components              elseif trace_marks then                  logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char))              end -            start = start.next +            start = getnext(start)          end          -- we can have one accent as part of a lookup and another following       -- local start = components -- was wrong (component scanning was introduced when more complex ligs in devanagari was added) -        local start = current.next -        while start and start.id == glyph_code do -            local char = start.char +        local start = getnext(current) +        while start and getid(start) == glyph_code do +            local char = getchar(start)              if marks[char] then -                start[a_ligacomp] = baseindex + (start[a_ligacomp] or componentindex) +                setattr(start,a_ligacomp,baseindex + (getattr(start,a_ligacomp) or componentindex))                  if trace_marks then -                    logwarning("%s: set mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp]) +                    logwarning("%s: set mark %s, gets index %s",pref(kind,lookupname),gref(char),getattr(start,a_ligacomp))                  end              else                  break              end -            start = start.next +            start = getnext(start)          end      end      return head, base @@ -468,9 +484,9 @@ end  function handlers.gsub_single(head,start,kind,lookupname,replacement)      if trace_singles then -        logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(start.char),gref(replacement)) +        logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(getchar(start)),gref(replacement))      end -    start.char = replacement +    setfield(start,"char",replacement)      return head, start, true  end @@ -497,7 +513,7 @@ local function get_alternative_glyph(start,alternatives,value,trace_alternatives                  return false, trace_alternatives and formatters["invalid value %a, %s"](value,"out of range")              end          elseif value == 0 then -            return start.char, trace_alternatives and formatters["invalid value %a, %s"](value,"no change") +            return getchar(start), trace_alternatives and formatters["invalid value %a, %s"](value,"no change")          elseif value < 1 then              return alternatives[1], trace_alternatives and formatters["invalid value %a, taking %a"](value,1)          else @@ -509,30 +525,30 @@ end  local function multiple_glyphs(head,start,multiple,ignoremarks)      local nofmultiples = #multiple      if nofmultiples > 0 then -        start.char = multiple[1] +        setfield(start,"char",multiple[1])          if nofmultiples > 1 then -            local sn = start.next +            local sn = getnext(start)              for k=2,nofmultiples do -- todo: use insert_node  -- untested:  -- --- while ignoremarks and marks[sn.char] then ---     local sn = sn.next +-- while ignoremarks and marks[getchar(sn)] then +--     local sn = getnext(sn)  -- end                  local n = copy_node(start) -- ignore components -                n.char = multiple[k] -                n.next = sn -                n.prev = start +                setfield(n,"char",multiple[k]) +                setfield(n,"next",sn) +                setfield(n,"prev",start)                  if sn then -                    sn.prev = n +                    setfield(sn,"prev",n)                  end -                start.next = n +                setfield(start,"next",n)                  start = n              end          end          return head, start, true      else          if trace_multiples then -            logprocess("no multiple for %s",gref(start.char)) +            logprocess("no multiple for %s",gref(getchar(start)))          end          return head, start, false      end @@ -543,12 +559,12 @@ function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence      local choice, comment = get_alternative_glyph(start,alternative,value,trace_alternatives)      if choice then          if trace_alternatives then -            logprocess("%s: replacing %s by alternative %a to %s, %s",pref(kind,lookupname),gref(start.char),choice,gref(choice),comment) +            logprocess("%s: replacing %s by alternative %a to %s, %s",pref(kind,lookupname),gref(getchar(start)),choice,gref(choice),comment)          end -        start.char = choice +        setfield(start,"char",choice)      else          if trace_alternatives then -            logwarning("%s: no variant %a for %s, %s",pref(kind,lookupname),value,gref(start.char),comment) +            logwarning("%s: no variant %a for %s, %s",pref(kind,lookupname),value,gref(getchar(start)),comment)          end      end      return head, start, true @@ -556,23 +572,23 @@ end  function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence)      if trace_multiples then -        logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple)) +        logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(getchar(start)),gref(multiple))      end      return multiple_glyphs(head,start,multiple,sequence.flags[1])  end  function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) -    local s, stop, discfound = start.next, nil, false -    local startchar = start.char +    local s, stop, discfound = getnext(start), nil, false +    local startchar = getchar(start)      if marks[startchar] then          while s do -            local id = s.id -            if id == glyph_code and s.font == currentfont and s.subtype<256 then -                local lg = ligature[s.char] +            local id = getid(s) +            if id == glyph_code and getfont(s) == currentfont and getsubtype(s)<256 then +                local lg = ligature[getchar(s)]                  if lg then                      stop = s                      ligature = lg -                    s = s.next +                    s = getnext(s)                  else                      break                  end @@ -584,9 +600,9 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)              local lig = ligature.ligature              if lig then                  if trace_ligatures then -                    local stopchar = stop.char +                    local stopchar = getchar(stop)                      head, start = markstoligature(kind,lookupname,head,start,stop,lig) -                    logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) +                    logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(getchar(start)))                  else                      head, start = markstoligature(kind,lookupname,head,start,stop,lig)                  end @@ -598,18 +614,18 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)      else          local skipmark = sequence.flags[1]          while s do -            local id = s.id -            if id == glyph_code and s.subtype<256 then -                if s.font == currentfont then -                    local char = s.char +            local id = getid(s) +            if id == glyph_code and getsubtype(s)<256 then +                if getfont(s) == currentfont then +                    local char = getchar(s)                      if skipmark and marks[char] then -                        s = s.next +                        s = getnext(s)                      else                          local lg = ligature[char]                          if lg then                              stop = s                              ligature = lg -                            s = s.next +                            s = getnext(s)                          else                              break                          end @@ -619,7 +635,7 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)                  end              elseif id == disc_code then                  discfound = true -                s = s.next +                s = getnext(s)              else                  break              end @@ -628,21 +644,20 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)          if lig then              if stop then                  if trace_ligatures then -                    local stopchar = stop.char +                    local stopchar = getchar(stop)                      head, start = toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound) -                    logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) +                    logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(getchar(start)))                  else                      head, start = toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound)                  end -                return head, start, true              else                  -- weird but happens (in some arabic font) -                start.char = lig +                setfield(start,"char",lig)                  if trace_ligatures then                      logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig))                  end -                return head, start, true              end +            return head, start, true          else              -- weird but happens          end @@ -656,16 +671,16 @@ we need to explicitly test for basechar, baselig and basemark entries.</p>  --ldx]]--  function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence) -    local markchar = start.char +    local markchar = getchar(start)      if marks[markchar] then -        local base = start.prev -- [glyph] [start=mark] -        if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -            local basechar = base.char +        local base = getprev(start) -- [glyph] [start=mark] +        if base and getid(base) == glyph_code and getfont(base) == currentfont and getsubtype(base)<256 then +            local basechar = getchar(base)              if marks[basechar] then                  while true do -                    base = base.prev -                    if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -                        basechar = base.char +                    base = getprev(base) +                    if base and getid(base) == glyph_code and getfont(base) == currentfont and getsubtype(base)<256 then +                        basechar = getchar(base)                          if not marks[basechar] then                              break                          end @@ -717,16 +732,16 @@ end  function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequence)      -- check chainpos variant -    local markchar = start.char +    local markchar = getchar(start)      if marks[markchar] then -        local base = start.prev -- [glyph] [optional marks] [start=mark] -        if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -            local basechar = base.char +        local base = getprev(start) -- [glyph] [optional marks] [start=mark] +        if base and getid(base) == glyph_code and getfont(base) == currentfont and getsubtype(base)<256 then +            local basechar = getchar(base)              if marks[basechar] then                  while true do -                    base = base.prev -                    if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -                        basechar = base.char +                    base = getprev(base) +                    if base and getid(base) == glyph_code and getfont(base) == currentfont and getsubtype(base)<256 then +                        basechar = getchar(base)                          if not marks[basechar] then                              break                          end @@ -738,7 +753,7 @@ function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequ                      end                  end              end -            local index = start[a_ligacomp] +            local index = getattr(start,a_ligacomp)              local baseanchors = descriptions[basechar]              if baseanchors then                  baseanchors = baseanchors.anchors @@ -785,22 +800,22 @@ function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequ  end  function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence) -    local markchar = start.char +    local markchar = getchar(start)      if marks[markchar] then -        local base = start.prev -- [glyph] [basemark] [start=mark] -        local slc = start[a_ligacomp] +        local base = getprev(start) -- [glyph] [basemark] [start=mark] +        local slc = getattr(start,a_ligacomp)          if slc then -- a rather messy loop ... needs checking with husayni              while base do -                local blc = base[a_ligacomp] +                local blc = getattr(base,a_ligacomp)                  if blc and blc ~= slc then -                    base = base.prev +                    base = getprev(base)                  else                      break                  end              end          end -        if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -- subtype test can go -            local basechar = base.char +        if base and getid(base) == glyph_code and getfont(base) == currentfont and getsubtype(base)<256 then -- subtype test can go +            local basechar = getchar(base)              local baseanchors = descriptions[basechar]              if baseanchors then                  baseanchors = baseanchors.anchors @@ -840,21 +855,21 @@ function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence  end  function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence) -- to be checked -    local alreadydone = cursonce and start[a_cursbase] +    local alreadydone = cursonce and getattr(start,a_cursbase)      if not alreadydone then          local done = false -        local startchar = start.char +        local startchar = getchar(start)          if marks[startchar] then              if trace_cursive then                  logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar))              end          else -            local nxt = start.next -            while not done and nxt and nxt.id == glyph_code and nxt.font == currentfont and nxt.subtype<256 do -                local nextchar = nxt.char +            local nxt = getnext(start) +            while not done and nxt and getid(nxt) == glyph_code and getfont(nxt) == currentfont and getsubtype(nxt)<256 do +                local nextchar = getchar(nxt)                  if marks[nextchar] then                      -- should not happen (maybe warning) -                    nxt = nxt.next +                    nxt = getnext(nxt)                  else                      local entryanchors = descriptions[nextchar]                      if entryanchors then @@ -889,14 +904,14 @@ function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence)          return head, start, done      else          if trace_cursive and trace_details then -            logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) +            logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(getchar(start)),alreadydone)          end          return head, start, false      end  end  function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence) -    local startchar = start.char +    local startchar = getchar(start)      local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar])      if trace_kerns then          logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) @@ -907,19 +922,19 @@ end  function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence)      -- todo: kerns in disc nodes: pre, post, replace -> loop over disc too      -- todo: kerns in components of ligatures -    local snext = start.next +    local snext = getnext(start)      if not snext then          return head, start, false      else          local prev, done = start, false          local factor = tfmdata.parameters.factor          local lookuptype = lookuptypes[lookupname] -        while snext and snext.id == glyph_code and snext.font == currentfont and snext.subtype<256 do -            local nextchar = snext.char +        while snext and getid(snext) == glyph_code and getfont(snext) == currentfont and getsubtype(snext)<256 do +            local nextchar = getchar(snext)              local krn = kerns[nextchar]              if not krn and marks[nextchar] then                  prev = snext -                snext = snext.next +                snext = getnext(snext)              else                  if not krn then                      -- skip @@ -927,14 +942,14 @@ function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence)                      if lookuptype == "pair" then -- probably not needed                          local a, b = krn[2], krn[3]                          if a and #a > 0 then -                            local startchar = start.char +                            local startchar = getchar(start)                              local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar])                              if trace_kerns then                                  logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)                              end                          end                          if b and #b > 0 then -                            local startchar = start.char +                            local startchar = getchar(start)                              local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar])                              if trace_kerns then                                  logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) @@ -946,7 +961,7 @@ function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence)                       -- if a and a ~= 0 then                       --     local k = setkern(snext,factor,rlmode,a)                       --     if trace_kerns then -                     --         logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) +                     --         logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar))                       --     end                       -- end                       -- if b and b ~= 0 then @@ -957,7 +972,7 @@ function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence)                  elseif krn ~= 0 then                      local k = setkern(snext,factor,rlmode,krn)                      if trace_kerns then -                        logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) +                        logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar))                      end                      done = true                  end @@ -1012,13 +1027,13 @@ end  -- itself. It is meant mostly for dealing with Urdu.  function chainprocs.reversesub(head,start,stop,kind,chainname,currentcontext,lookuphash,replacements) -    local char = start.char +    local char = getchar(start)      local replacement = replacements[char]      if replacement then          if trace_singles then              logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement))          end -        start.char = replacement +        setfield(start,"char",replacement)          return head, start, true      else          return head, start, false @@ -1047,9 +1062,9 @@ as less as needed but that would also make the code even more messy.</p>  --         -- done  --     elseif ignoremarks then  --         repeat -- start x x m x x stop => start m ---             local next = start.next ---             if not marks[next.char] then ---                 local components = next.components +--             local next = getnext(start) +--             if not marks[getchar(next)] then +--                 local components = getfield(next,"components")  --                 if components then -- probably not needed  --                     flush_node_list(components)  --                 end @@ -1059,8 +1074,8 @@ as less as needed but that would also make the code even more messy.</p>  --         until next == stop  --     else -- start x x x stop => start  --         repeat ---             local next = start.next ---             local components = next.components +--             local next = getnext(start) +--             local components = getfield(next,"components")  --             if components then -- probably not needed  --                 flush_node_list(components)  --             end @@ -1084,8 +1099,8 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo          logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," "))      end      while current do -        if current.id == glyph_code then -            local currentchar = current.char +        if getid(current) == glyph_code then +            local currentchar = getchar(current)              local lookupname = subtables[1] -- only 1              local replacement = lookuphash[lookupname]              if not replacement then @@ -1102,14 +1117,14 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo                      if trace_singles then                          logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement))                      end -                    current.char = replacement +                    setfield(current,"char",replacement)                  end              end              return head, start, true          elseif current == stop then              break          else -            current = current.next +            current = getnext(current)          end      end      return head, start, false @@ -1124,7 +1139,7 @@ the match.</p>  function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname)   -- local head, n = delete_till_stop(head,start,stop) -    local startchar = start.char +    local startchar = getchar(start)      local subtables = currentlookup.subtables      local lookupname = subtables[1]      local replacements = lookuphash[lookupname] @@ -1167,8 +1182,8 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext      local subtables = currentlookup.subtables      local value  = featurevalue == true and tfmdata.shared.features[kind] or featurevalue      while current do -        if current.id == glyph_code then -- is this check needed? -            local currentchar = current.char +        if getid(current) == glyph_code then -- is this check needed? +            local currentchar = getchar(current)              local lookupname = subtables[1]              local alternatives = lookuphash[lookupname]              if not alternatives then @@ -1183,7 +1198,7 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext                          if trace_alternatives then                              logprocess("%s: replacing %s by alternative %a to %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(char),choice,gref(choice),comment)                          end -                        start.char = choice +                        setfield(start,"char",choice)                      else                          if trace_alternatives then                              logwarning("%s: no variant %a for %s, %s",cref(kind,chainname,chainlookupname,lookupname),value,gref(char),comment) @@ -1197,7 +1212,7 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext          elseif current == stop then              break          else -            current = current.next +            current = getnext(current)          end      end      return head, start, false @@ -1212,7 +1227,7 @@ assume rather stupid ligatures (no complex disc nodes).</p>  --ldx]]--  function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) -    local startchar = start.char +    local startchar = getchar(start)      local subtables = currentlookup.subtables      local lookupname = subtables[1]      local ligatures = lookuphash[lookupname] @@ -1227,20 +1242,20 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,                  logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))              end          else -            local s = start.next +            local s = getnext(start)              local discfound = false              local last = stop              local nofreplacements = 0              local skipmark = currentlookup.flags[1]              while s do -                local id = s.id +                local id = getid(s)                  if id == disc_code then -                    s = s.next +                    s = getnext(s)                      discfound = true                  else -                    local schar = s.char +                    local schar = getchar(s)                      if skipmark and marks[schar] then -- marks -                        s = s.next +                        s = getnext(s)                      else                          local lg = ligatures[schar]                          if lg then @@ -1248,7 +1263,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,                              if s == stop then                                  break                              else -                                s = s.next +                                s = getnext(s)                              end                          else                              break @@ -1265,7 +1280,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,                      if start == stop then                          logprocess("%s: replacing character %s by ligature %s case 3",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2))                      else -                        logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2)) +                        logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(getchar(stop)),gref(l2))                      end                  end                  head, start = toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound) @@ -1274,7 +1289,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,                  if start == stop then                      logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))                  else -                    logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char)) +                    logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(getchar(stop)))                  end              end          end @@ -1285,7 +1300,7 @@ end  chainmores.gsub_ligature = chainprocs.gsub_ligature  function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) -    local markchar = start.char +    local markchar = getchar(start)      if marks[markchar] then          local subtables = currentlookup.subtables          local lookupname = subtables[1] @@ -1294,14 +1309,14 @@ function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext              markanchors = markanchors[markchar]          end          if markanchors then -            local base = start.prev -- [glyph] [start=mark] -            if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -                local basechar = base.char +            local base = getprev(start) -- [glyph] [start=mark] +            if base and getid(base) == glyph_code and getfont(base) == currentfont and getsubtype(base)<256 then +                local basechar = getchar(base)                  if marks[basechar] then                      while true do -                        base = base.prev -                        if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -                            basechar = base.char +                        base = getprev(base) +                        if base and getid(base) == glyph_code and getfont(base) == currentfont and getsubtype(base)<256 then +                            basechar = getchar(base)                              if not marks[basechar] then                                  break                              end @@ -1349,7 +1364,7 @@ function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext  end  function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) -    local markchar = start.char +    local markchar = getchar(start)      if marks[markchar] then          local subtables = currentlookup.subtables          local lookupname = subtables[1] @@ -1358,14 +1373,14 @@ function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcon              markanchors = markanchors[markchar]          end          if markanchors then -            local base = start.prev -- [glyph] [optional marks] [start=mark] -            if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -                local basechar = base.char +            local base = getprev(start) -- [glyph] [optional marks] [start=mark] +            if base and getid(base) == glyph_code and getfont(base) == currentfont and getsubtype(base)<256 then +                local basechar = getchar(base)                  if marks[basechar] then                      while true do -                        base = base.prev -                        if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -                            basechar = base.char +                        base = getprev(base) +                        if base and getid(base) == glyph_code and getfont(base) == currentfont and getsubtype(base)<256 then +                            basechar = getchar(base)                              if not marks[basechar] then                                  break                              end @@ -1378,7 +1393,7 @@ function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcon                      end                  end                  -- todo: like marks a ligatures hash -                local index = start[a_ligacomp] +                local index = getattr(start,a_ligacomp)                  local baseanchors = descriptions[basechar].anchors                  if baseanchors then                     local baseanchors = baseanchors['baselig'] @@ -1418,9 +1433,9 @@ function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcon  end  function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) -    local markchar = start.char +    local markchar = getchar(start)      if marks[markchar] then -     -- local alreadydone = markonce and start[a_markmark] +     -- local alreadydone = markonce and getattr(start,a_markmark)       -- if not alreadydone then          --  local markanchors = descriptions[markchar].anchors markanchors = markanchors and markanchors.mark              local subtables = currentlookup.subtables @@ -1430,20 +1445,20 @@ function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext                  markanchors = markanchors[markchar]              end              if markanchors then -                local base = start.prev -- [glyph] [basemark] [start=mark] -                local slc = start[a_ligacomp] +                local base = getprev(start) -- [glyph] [basemark] [start=mark] +                local slc = getattr(start,a_ligacomp)                  if slc then -- a rather messy loop ... needs checking with husayni                      while base do -                        local blc = base[a_ligacomp] +                        local blc = getattr(base,a_ligacomp)                          if blc and blc ~= slc then -                            base = base.prev +                            base = getprev(base)                          else                              break                          end                      end                  end -                if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -- subtype test can go -                    local basechar = base.char +                if base and getid(base) == glyph_code and getfont(base) == currentfont and getsubtype(base)<256 then -- subtype test can go +                    local basechar = getchar(base)                      local baseanchors = descriptions[basechar].anchors                      if baseanchors then                          baseanchors = baseanchors['basemark'] @@ -1483,9 +1498,9 @@ function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext  end  function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) -    local alreadydone = cursonce and start[a_cursbase] +    local alreadydone = cursonce and getattr(start,a_cursbase)      if not alreadydone then -        local startchar = start.char +        local startchar = getchar(start)          local subtables = currentlookup.subtables          local lookupname = subtables[1]          local exitanchors = lookuphash[lookupname] @@ -1499,12 +1514,12 @@ function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,l                      logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar))                  end              else -                local nxt = start.next -                while not done and nxt and nxt.id == glyph_code and nxt.font == currentfont and nxt.subtype<256 do -                    local nextchar = nxt.char +                local nxt = getnext(start) +                while not done and nxt and getid(nxt) == glyph_code and getfont(nxt) == currentfont and getsubtype(nxt)<256 do +                    local nextchar = getchar(nxt)                      if marks[nextchar] then                          -- should not happen (maybe warning) -                        nxt = nxt.next +                        nxt = getnext(nxt)                      else                          local entryanchors = descriptions[nextchar]                          if entryanchors then @@ -1539,7 +1554,7 @@ function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,l              return head, start, done          else              if trace_cursive and trace_details then -                logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) +                logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(getchar(start)),alreadydone)              end              return head, start, false          end @@ -1549,7 +1564,7 @@ end  function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence)      -- untested .. needs checking for the new model -    local startchar = start.char +    local startchar = getchar(start)      local subtables = currentlookup.subtables      local lookupname = subtables[1]      local kerns = lookuphash[lookupname] @@ -1570,9 +1585,9 @@ chainmores.gpos_single = chainprocs.gpos_single -- okay?  -- when machines become faster i will make a shared function  function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) -    local snext = start.next +    local snext = getnext(start)      if snext then -        local startchar = start.char +        local startchar = getchar(start)          local subtables = currentlookup.subtables          local lookupname = subtables[1]          local kerns = lookuphash[lookupname] @@ -1582,12 +1597,12 @@ function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,look                  local lookuptype = lookuptypes[lookupname]                  local prev, done = start, false                  local factor = tfmdata.parameters.factor -                while snext and snext.id == glyph_code and snext.font == currentfont and snext.subtype<256 do -                    local nextchar = snext.char +                while snext and getid(snext) == glyph_code and getfont(snext) == currentfont and getsubtype(snext)<256 do +                    local nextchar = getchar(snext)                      local krn = kerns[nextchar]                      if not krn and marks[nextchar] then                          prev = snext -                        snext = snext.next +                        snext = getnext(snext)                      else                          if not krn then                              -- skip @@ -1595,14 +1610,14 @@ function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,look                              if lookuptype == "pair" then                                  local a, b = krn[2], krn[3]                                  if a and #a > 0 then -                                    local startchar = start.char +                                    local startchar = getchar(start)                                      local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar])                                      if trace_kerns then                                          logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)                                      end                                  end                                  if b and #b > 0 then -                                    local startchar = start.char +                                    local startchar = getchar(start)                                      local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar])                                      if trace_kerns then                                          logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) @@ -1614,7 +1629,7 @@ function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,look                                  if a and a ~= 0 then                                      local k = setkern(snext,factor,rlmode,a)                                      if trace_kerns then -                                        logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) +                                        logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar))                                      end                                  end                                  if b and b ~= 0 then @@ -1625,7 +1640,7 @@ function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,look                          elseif krn ~= 0 then                              local k = setkern(snext,factor,rlmode,krn)                              if trace_kerns then -                                logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) +                                logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar))                              end                              done = true                          end @@ -1657,6 +1672,12 @@ local function show_skip(kind,chainname,char,ck,class)      end  end +local quit_on_no_replacement = true + +directives.register("otf.chain.quitonnoreplacement",function(value) -- maybe per font +    quit_on_no_replacement = value +end) +  local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash)      --  local rule, lookuptype, sequence, f, l, lookups = ck[1], ck[2] ,ck[3], ck[4], ck[5], ck[6]      local flags        = sequence.flags @@ -1677,7 +1698,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq          -- f..l = mid string          if s == 1 then              -- never happens -            match = current.id == glyph_code and current.font == currentfont and current.subtype<256 and seq[1][current.char] +            match = getid(current) == glyph_code and getfont(current) == currentfont and getsubtype(current)<256 and seq[1][getchar(current)]          else              -- maybe we need a better space check (maybe check for glue or category or combination)              -- we cannot optimize for n=2 because there can be disc nodes @@ -1692,13 +1713,13 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                   -- match = true                  else                      local n = f + 1 -                    last = last.next +                    last = getnext(last)                      while n <= l do                          if last then -                            local id = last.id +                            local id = getid(last)                              if id == glyph_code then -                                if last.font == currentfont and last.subtype<256 then -                                    local char = last.char +                                if getfont(last) == currentfont and getsubtype(last)<256 then +                                    local char = getchar(last)                                      local ccd = descriptions[char]                                      if ccd then                                          local class = ccd.class @@ -1707,10 +1728,10 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                                              if trace_skips then                                                  show_skip(kind,chainname,char,ck,class)                                              end -                                            last = last.next +                                            last = getnext(last)                                          elseif seq[n][char] then                                              if n < l then -                                                last = last.next +                                                last = getnext(last)                                              end                                              n = n + 1                                          else @@ -1726,7 +1747,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                                      break                                  end                              elseif id == disc_code then -                                last = last.next +                                last = getnext(last)                              else                                  match = false                                  break @@ -1740,15 +1761,15 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq              end              -- before              if match and f > 1 then -                local prev = start.prev +                local prev = getprev(start)                  if prev then                      local n = f-1                      while n >= 1 do                          if prev then -                            local id = prev.id +                            local id = getid(prev)                              if id == glyph_code then -                                if prev.font == currentfont and prev.subtype<256 then -- normal char -                                    local char = prev.char +                                if getfont(prev) == currentfont and getsubtype(prev)<256 then -- normal char +                                    local char = getchar(prev)                                      local ccd = descriptions[char]                                      if ccd then                                          local class = ccd.class @@ -1779,7 +1800,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                                  match = false                                  break                              end -                            prev = prev.prev +                            prev = getprev(prev)                          elseif seq[n][32] then -- somewhat special, as zapfino can have many preceding spaces                              n = n -1                          else @@ -1800,16 +1821,16 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq              end              -- after              if match and s > l then -                local current = last and last.next +                local current = last and getnext(last)                  if current then                      -- removed optimization for s-l == 1, we have to deal with marks anyway                      local n = l + 1                      while n <= s do                          if current then -                            local id = current.id +                            local id = getid(current)                              if id == glyph_code then -                                if current.font == currentfont and current.subtype<256 then -- normal char -                                    local char = current.char +                                if getfont(current) == currentfont and getsubtype(current)<256 then -- normal char +                                    local char = getchar(current)                                      local ccd = descriptions[char]                                      if ccd then                                          local class = ccd.class @@ -1840,7 +1861,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                                  match = false                                  break                              end -                            current = current.next +                            current = getnext(current)                          elseif seq[n][32] then                              n = n + 1                          else @@ -1864,7 +1885,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq              -- ck == currentcontext              if trace_contexts then                  local rule, lookuptype, f, l = ck[1], ck[2], ck[4], ck[5] -                local char = start.char +                local char = getchar(start)                  if ck[9] then                      logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a, %a => %a",                          cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10]) @@ -1899,12 +1920,12 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                      repeat                          if skipped then                              while true do -                                local char = start.char +                                local char = getchar(start)                                  local ccd = descriptions[char]                                  if ccd then                                      local class = ccd.class                                      if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then -                                        start = start.next +                                        start = getnext(start)                                      else                                          break                                      end @@ -1938,7 +1959,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                              end                          end                          if start then -                            start = start.next +                            start = getnext(start)                          else                              -- weird                          end @@ -1949,7 +1970,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                  if replacements then                      head, start, done = chainprocs.reversesub(head,start,last,kind,chainname,ck,lookuphash,replacements) -- sequence                  else -                    done = true -- can be meant to be skipped +                    done = quit_on_no_replacement -- can be meant to be skipped / quite inconsistent in fonts                      if trace_contexts then                          logprocess("%s: skipping match",cref(kind,chainname))                      end @@ -2099,12 +2120,12 @@ end  --             if ok then  --                 done = true  --             end ---             if start then start = start.next end +--             if start then start = getnext(start) end  --         else ---             start = start.next +--             start = getnext(start)  --         end  --     else ---         start = start.next +--         start = getnext(start)  --     end  -- there will be a new direction parser (pre-parsed etc) @@ -2126,6 +2147,8 @@ local function featuresprocessor(head,font,attr)          return head, false      end +    head = tonut(head) +      if trace_steps then          checkstep(head)      end @@ -2157,6 +2180,8 @@ local function featuresprocessor(head,font,attr)      -- Keeping track of the headnode is needed for devanagari (I generalized it a bit      -- so that multiple cases are also covered.) +    -- todo: retain prev +      for s=1,#datasets do          local dataset = datasets[s]          featurevalue = dataset[1] -- todo: pass to function instead of using a global @@ -2175,10 +2200,10 @@ local function featuresprocessor(head,font,attr)              -- we need to get rid of this slide! probably no longer needed in latest luatex              local start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo              while start do -                local id = start.id +                local id = getid(start)                  if id == glyph_code then -                    if start.font == font and start.subtype<256 then -                        local a = start[0] +                    if getfont(start) == font and getsubtype(start) < 256 then +                        local a = getattr(start,0)                          if a then                              a = a == attr                          else @@ -2189,7 +2214,7 @@ local function featuresprocessor(head,font,attr)                                  local lookupname = subtables[i]                                  local lookupcache = lookuphash[lookupname]                                  if lookupcache then -                                    local lookupmatch = lookupcache[start.char] +                                    local lookupmatch = lookupcache[getchar(start)]                                      if lookupmatch then                                          head, start, success = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)                                          if success then @@ -2200,15 +2225,15 @@ local function featuresprocessor(head,font,attr)                                      report_missing_cache(typ,lookupname)                                  end                              end -                            if start then start = start.prev end +                            if start then start = getprev(start) end                          else -                            start = start.prev +                            start = getprev(start)                          end                      else -                        start = start.prev +                        start = getprev(start)                      end                  else -                    start = start.prev +                    start = getprev(start)                  end              end          else @@ -2228,16 +2253,16 @@ local function featuresprocessor(head,font,attr)                          local head = start                          local done = false                          while start do -                            local id = start.id -                            if id == glyph_code and start.font == font and start.subtype <256 then -                                local a = start[0] +                            local id = getid(start) +                            if id == glyph_code and getfont(start) == font and getsubtype(start) < 256 then +                                local a = getattr(start,0)                                  if a then -                                    a = (a == attr) and (not attribute or start[a_state] == attribute) +                                    a = (a == attr) and (not attribute or getattr(start,a_state) == attribute)                                  else -                                    a = not attribute or start[a_state] == attribute +                                    a = not attribute or getattr(start,a_state) == attribute                                  end                                  if a then -                                    local lookupmatch = lookupcache[start.char] +                                    local lookupmatch = lookupcache[getchar(start)]                                      if lookupmatch then                                          -- sequence kan weg                                          local ok @@ -2246,12 +2271,12 @@ local function featuresprocessor(head,font,attr)                                              done = true                                          end                                      end -                                    if start then start = start.next end +                                    if start then start = getnext(start) end                                  else -                                    start = start.next +                                    start = getnext(start)                                  end                              else -                                start = start.next +                                start = getnext(start)                              end                          end                          if done then @@ -2261,19 +2286,19 @@ local function featuresprocessor(head,font,attr)                      end                      local function kerndisc(disc) -- we can assume that prev and next are glyphs -                        local prev = disc.prev -                        local next = disc.next +                        local prev = getprev(disc) +                        local next = getnext(disc)                          if prev and next then -                            prev.next = next -                         -- next.prev = prev -                            local a = prev[0] +                            setfield(prev,"next",next) +                         -- setfield(next,"prev",prev) +                            local a = getattr(prev,0)                              if a then -                                a = (a == attr) and (not attribute or prev[a_state] == attribute) +                                a = (a == attr) and (not attribute or getattr(prev,a_state) == attribute)                              else -                                a = not attribute or prev[a_state] == attribute +                                a = not attribute or getattr(prev,a_state) == attribute                              end                              if a then -                                local lookupmatch = lookupcache[prev.char] +                                local lookupmatch = lookupcache[getchar(prev)]                                  if lookupmatch then                                      -- sequence kan weg                                      local h, d, ok = handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) @@ -2283,24 +2308,24 @@ local function featuresprocessor(head,font,attr)                                      end                                  end                              end -                            prev.next = disc -                         -- next.prev = disc +                            setfield(prev,"next",disc) +                         -- setfield(next,"prev",disc)                          end                          return next                      end                      while start do -                        local id = start.id +                        local id = getid(start)                          if id == glyph_code then -                            if start.font == font and start.subtype<256 then -                                local a = start[0] +                            if getfont(start) == font and getsubtype(start) < 256 then +                                local a = getattr(start,0)                                  if a then -                                    a = (a == attr) and (not attribute or start[a_state] == attribute) +                                    a = (a == attr) and (not attribute or getattr(start,a_state) == attribute)                                  else -                                    a = not attribute or start[a_state] == attribute +                                    a = not attribute or getattr(start,a_state) == attribute                                  end                                  if a then -                                    local lookupmatch = lookupcache[start.char] +                                    local lookupmatch = lookupcache[getchar(start)]                                      if lookupmatch then                                          -- sequence kan weg                                          local ok @@ -2309,39 +2334,39 @@ local function featuresprocessor(head,font,attr)                                              success = true                                          end                                      end -                                    if start then start = start.next end +                                    if start then start = getnext(start) end                                  else -                                    start = start.next +                                    start = getnext(start)                                  end                              else -                                start = start.next +                                start = getnext(start)                              end                          elseif id == disc_code then                              -- mostly for gsub -                            if start.subtype == discretionary_code then -                                local pre = start.pre +                            if getsubtype(start) == discretionary_code then +                                local pre = getfield(start,"pre")                                  if pre then                                      local new = subrun(pre) -                                    if new then start.pre = new end +                                    if new then setfield(start,"pre",new) end                                  end -                                local post = start.post +                                local post = getfield(start,"post")                                  if post then                                      local new = subrun(post) -                                    if new then start.post = new end +                                    if new then setfield(start,"post",new) end                                  end -                                local replace = start.replace +                                local replace = getfield(start,"replace")                                  if replace then                                      local new = subrun(replace) -                                    if new then start.replace = new end +                                    if new then setfield(start,"replace",new) end                                  end  elseif typ == "gpos_single" or typ == "gpos_pair" then      kerndisc(start)                              end -                            start = start.next +                            start = getnext(start)                          elseif id == whatsit_code then -- will be function -                            local subtype = start.subtype +                            local subtype = getsubtype(start)                              if subtype == dir_code then -                                local dir = start.dir +                                local dir = getfield(start,"dir")                                  if     dir == "+TRT" or dir == "+TLT" then                                      topstack = topstack + 1                                      dirstack[topstack] = dir @@ -2360,7 +2385,7 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then                                      report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir)                                  end                              elseif subtype == localpar_code then -                                local dir = start.dir +                                local dir = getfield(start,"dir")                                  if dir == "TRT" then                                      rlparmode = -1                                  elseif dir == "TLT" then @@ -2374,11 +2399,11 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then                                      report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode)                                  end                              end -                            start = start.next +                            start = getnext(start)                          elseif id == math_code then -                            start = end_of_math(start).next +                            start = getnext(end_of_math(start))                          else -                            start = start.next +                            start = getnext(start)                          end                      end                  end @@ -2389,20 +2414,20 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then                      local head = start                      local done = false                      while start do -                        local id = start.id -                        if id == glyph_code and start.id == font and start.subtype <256 then -                            local a = start[0] +                        local id = getid(start) +                        if id == glyph_code and getfont(start) == font and getsubtype(start) < 256 then +                            local a = getattr(start,0)                              if a then -                                a = (a == attr) and (not attribute or start[a_state] == attribute) +                                a = (a == attr) and (not attribute or getattr(start,a_state) == attribute)                              else -                                a = not attribute or start[a_state] == attribute +                                a = not attribute or getattr(start,a_state) == attribute                              end                              if a then                                  for i=1,ns do                                      local lookupname = subtables[i]                                      local lookupcache = lookuphash[lookupname]                                      if lookupcache then -                                        local lookupmatch = lookupcache[start.char] +                                        local lookupmatch = lookupcache[getchar(start)]                                          if lookupmatch then                                              -- we could move all code inline but that makes things even more unreadable                                              local ok @@ -2419,12 +2444,12 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then                                          report_missing_cache(typ,lookupname)                                      end                                  end -                                if start then start = start.next end +                                if start then start = getnext(start) end                              else -                                start = start.next +                                start = getnext(start)                              end                          else -                            start = start.next +                            start = getnext(start)                          end                      end                      if done then @@ -2434,23 +2459,23 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then                  end                  local function kerndisc(disc) -- we can assume that prev and next are glyphs -                    local prev = disc.prev -                    local next = disc.next +                    local prev = getprev(disc) +                    local next = getnext(disc)                      if prev and next then -                        prev.next = next -                     -- next.prev = prev -                        local a = prev[0] +                        setfield(prev,"next",next) +                     -- setfield(next,"prev",prev) +                        local a = getattr(prev,0)                          if a then -                            a = (a == attr) and (not attribute or prev[a_state] == attribute) +                            a = (a == attr) and (not attribute or getattr(prev,a_state) == attribute)                          else -                            a = not attribute or prev[a_state] == attribute +                            a = not attribute or getattr(prev,a_state) == attribute                          end                          if a then                              for i=1,ns do                                  local lookupname = subtables[i]                                  local lookupcache = lookuphash[lookupname]                                  if lookupcache then -                                    local lookupmatch = lookupcache[prev.char] +                                    local lookupmatch = lookupcache[getchar(prev)]                                      if lookupmatch then                                          -- we could move all code inline but that makes things even more unreadable                                          local h, d, ok = handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) @@ -2464,28 +2489,28 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then                                  end                              end                          end -                        prev.next = disc -                     -- next.prev = disc +                        setfield(prev,"next",disc) +                     -- setfield(next,"prev",disc)                      end                      return next                  end                  while start do -                    local id = start.id +                    local id = getid(start)                      if id == glyph_code then -                        if start.font == font and start.subtype<256 then -                            local a = start[0] +                        if getfont(start) == font and getsubtype(start) < 256 then +                            local a = getattr(start,0)                              if a then -                                a = (a == attr) and (not attribute or start[a_state] == attribute) +                                a = (a == attr) and (not attribute or getattr(start,a_state) == attribute)                              else -                                a = not attribute or start[a_state] == attribute +                                a = not attribute or getattr(start,a_state) == attribute                              end                              if a then                                  for i=1,ns do                                      local lookupname = subtables[i]                                      local lookupcache = lookuphash[lookupname]                                      if lookupcache then -                                        local lookupmatch = lookupcache[start.char] +                                        local lookupmatch = lookupcache[getchar(start)]                                          if lookupmatch then                                              -- we could move all code inline but that makes things even more unreadable                                              local ok @@ -2502,39 +2527,39 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then                                          report_missing_cache(typ,lookupname)                                      end                                  end -                                if start then start = start.next end +                                if start then start = getnext(start) end                              else -                                start = start.next +                                start = getnext(start)                              end                          else -                            start = start.next +                            start = getnext(start)                          end                      elseif id == disc_code then                          -- mostly for gsub -                        if start.subtype == discretionary_code then -                            local pre = start.pre +                        if getsubtype(start) == discretionary_code then +                            local pre = getfield(start,"pre")                              if pre then                                  local new = subrun(pre) -                                if new then start.pre = new end +                                if new then setfield(start,"pre",new) end                              end -                            local post = start.post +                            local post = getfield(start,"post")                              if post then                                  local new = subrun(post) -                                if new then start.post = new end +                                if new then setfield(start,"post",new) end                              end -                            local replace = start.replace +                            local replace = getfield(start,"replace")                              if replace then                                  local new = subrun(replace) -                                if new then start.replace = new end +                                if new then setfield(start,"replace",new) end                              end  elseif typ == "gpos_single" or typ == "gpos_pair" then      kerndisc(start)                          end -                        start = start.next +                        start = getnext(start)                      elseif id == whatsit_code then -                        local subtype = start.subtype +                        local subtype = getsubtype(start)                          if subtype == dir_code then -                            local dir = start.dir +                            local dir = getfield(start,"dir")                              if     dir == "+TRT" or dir == "+TLT" then                                  topstack = topstack + 1                                  dirstack[topstack] = dir @@ -2553,7 +2578,7 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then                                  report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir)                              end                          elseif subtype == localpar_code then -                            local dir = start.dir +                            local dir = getfield(start,"dir")                              if dir == "TRT" then                                  rlparmode = -1                              elseif dir == "TLT" then @@ -2566,11 +2591,11 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then                                  report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode)                              end                          end -                        start = start.next +                        start = getnext(start)                      elseif id == math_code then -                        start = end_of_math(start).next +                        start = getnext(end_of_math(start))                      else -                        start = start.next +                        start = getnext(start)                      end                  end              end @@ -2582,6 +2607,9 @@ elseif typ == "gpos_single" or typ == "gpos_pair" then              registerstep(head)          end      end + +    head = tonode(head) +      return head, done  end diff --git a/tex/context/base/font-otx.lua b/tex/context/base/font-otx.lua index f39045223..b7d2ae0bc 100644 --- a/tex/context/base/font-otx.lua +++ b/tex/context/base/font-otx.lua @@ -30,15 +30,29 @@ analyzers.methods         = methods  local a_state             = attributes.private('state') +local nuts                = nodes.nuts +local tonut               = nuts.tonut + +local getfield            = nuts.getfield +local getnext             = nuts.getnext +local getprev             = nuts.getprev +local getid               = nuts.getid +local getattr             = nuts.getattr +local getfont             = nuts.getfont +local getsubtype          = nuts.getsubtype +local getchar             = nuts.getchar + +local setattr             = nuts.setattr + +local traverse_id         = nuts.traverse_id +local traverse_node_list  = nuts.traverse +local end_of_math         = nuts.end_of_math +  local nodecodes           = nodes.nodecodes  local glyph_code          = nodecodes.glyph  local disc_code           = nodecodes.disc  local math_code           = nodecodes.math -local traverse_id         = node.traverse_id -local traverse_node_list  = node.traverse -local end_of_math         = node.end_of_math -  local fontdata            = fonts.hashes.identifiers  local categories          = characters and characters.categories or { } -- sorry, only in context  local chardata            = characters and characters.data @@ -95,60 +109,61 @@ analyzers.useunicodemarks = false  -- todo: analyzers per script/lang, cross font, so we need an font id hash -> script  -- e.g. latin -> hyphenate, arab -> 1/2/3 analyze -- its own namespace -function analyzers.setstate(head,font) +function analyzers.setstate(head,font) -- we can skip math      local useunicodemarks  = analyzers.useunicodemarks      local tfmdata = fontdata[font]      local descriptions = tfmdata.descriptions      local first, last, current, n, done = nil, nil, head, 0, false -- maybe make n boolean +    current = tonut(current)      while current do -        local id = current.id -        if id == glyph_code and current.font == font then +        local id = getid(current) +        if id == glyph_code and getfont(current) == font then              done = true -            local char = current.char +            local char = getchar(current)              local d = descriptions[char]              if d then                  if d.class == "mark" then                      done = true -                    current[a_state] = s_mark +                    setattr(current,a_state,s_mark)                  elseif useunicodemarks and categories[char] == "mn" then                      done = true -                    current[a_state] = s_mark +                    setattr(current,a_state,s_mark)                  elseif n == 0 then                      first, last, n = current, current, 1 -                    current[a_state] = s_init +                    setattr(current,a_state,s_init)                  else                      last, n = current, n+1 -                    current[a_state] = s_medi +                    setattr(current,a_state,s_medi)                  end              else -- finish                  if first and first == last then -                    last[a_state] = s_isol +                    setattr(last,a_state,s_isol)                  elseif last then -                    last[a_state] = s_fina +                    setattr(last,a_state,s_fina)                  end                  first, last, n = nil, nil, 0              end          elseif id == disc_code then              -- always in the middle -            current[a_state] = s_medi +            setattr(current,a_state,s_medi)              last = current          else -- finish              if first and first == last then -                last[a_state] = s_isol +                setattr(last,a_state,s_isol)              elseif last then -                last[a_state] = s_fina +                setattr(last,a_state,s_fina)              end              first, last, n = nil, nil, 0              if id == math_code then                  current = end_of_math(current)              end          end -        current = current.next +        current = getnext(current)      end      if first and first == last then -        last[a_state] = s_isol +        setattr(last,a_state,s_isol)      elseif last then -        last[a_state] = s_fina +        setattr(last,a_state,s_fina)      end      return head, done  end @@ -209,7 +224,7 @@ methods.latn = analyzers.setstate  local arab_warned = { }  local function warning(current,what) -    local char = current.char +    local char = getchar(current)      if not arab_warned[char] then          log.report("analyze","arab: character %C has no %a class",char,what)          arab_warned[char] = true @@ -261,94 +276,95 @@ function methods.arab(head,font,attr)      local first, last = nil, nil      local c_first, c_last = nil, nil      local current, done = head, false +    current = tonut(current)      while current do -        local id = current.id -        if id == glyph_code and current.font == font and current.subtype<256 and not current[a_state] then +        local id = getid(current) +        if id == glyph_code and getfont(current) == font and getsubtype(current)<256 and not getattr(current,a_state) then              done = true -            local char = current.char +            local char = getchar(current)              local classifier = classifiers[char]              if not classifier then                  if last then                      if c_last == s_medi or c_last == s_fina then -                        last[a_state] = s_fina +                        setattr(last,a_state,s_fina)                      else                          warning(last,"fina") -                        last[a_state] = s_error +                        setattr(last,a_state,s_error)                      end                      first, last = nil, nil                  elseif first then                      if c_first == s_medi or c_first == s_fina then -                        first[a_state] = s_isol +                        setattr(first,a_state,s_isol)                      else                          warning(first,"isol") -                        first[a_state] = s_error +                        setattr(first,a_state,s_error)                      end                      first = nil                  end              elseif classifier == s_mark then -                current[a_state] = s_mark +                setattr(current,a_state,s_mark)              elseif classifier == s_isol then                  if last then                      if c_last == s_medi or c_last == s_fina then -                        last[a_state] = s_fina +                        setattr(last,a_state,s_fina)                      else                          warning(last,"fina") -                        last[a_state] = s_error +                        setattr(last,a_state,s_error)                      end                      first, last = nil, nil                  elseif first then                      if c_first == s_medi or c_first == s_fina then -                        first[a_state] = s_isol +                        setattr(first,a_state,s_isol)                      else                          warning(first,"isol") -                        first[a_state] = s_error +                        setattr(first,a_state,s_error)                      end                      first = nil                  end -                current[a_state] = s_isol +                setattr(current,a_state,s_isol)              elseif classifier == s_medi then                  if first then                      last = current                      c_last = classifier -                    current[a_state] = s_medi +                    setattr(current,a_state,s_medi)                  else -                    current[a_state] = s_init +                    setattr(current,a_state,s_init)                      first = current                      c_first = classifier                  end              elseif classifier == s_fina then                  if last then -                    if last[a_state] ~= s_init then -                        last[a_state] = s_medi +                    if getattr(last,a_state) ~= s_init then +                        setattr(last,a_state,s_medi)                      end -                    current[a_state] = s_fina +                    setattr(current,a_state,s_fina)                      first, last = nil, nil                  elseif first then -                 -- if first[a_state] ~= s_init then +                 -- if getattr(first,a_state) ~= s_init then                   --     -- needs checking -                 --     first[a_state] = s_medi +                 --     setattr(first,a_state,s_medi)                   -- end -                    current[a_state] = s_fina +                    setattr(current,a_state,s_fina)                      first = nil                  else -                    current[a_state] = s_isol +                    setattr(current,a_state,s_isol)                  end              else -- classifier == s_rest -                current[a_state] = s_rest +                setattr(current,a_state,s_rest)                  if last then                      if c_last == s_medi or c_last == s_fina then -                        last[a_state] = s_fina +                        setattr(last,a_state,s_fina)                      else                          warning(last,"fina") -                        last[a_state] = s_error +                        setattr(last,a_state,s_error)                      end                      first, last = nil, nil                  elseif first then                      if c_first == s_medi or c_first == s_fina then -                        first[a_state] = s_isol +                        setattr(first,a_state,s_isol)                      else                          warning(first,"isol") -                        first[a_state] = s_error +                        setattr(first,a_state,s_error)                      end                      first = nil                  end @@ -356,18 +372,18 @@ function methods.arab(head,font,attr)          else              if last then                  if c_last == s_medi or c_last == s_fina then -                    last[a_state] = s_fina +                    setattr(last,a_state,s_fina)                  else                      warning(last,"fina") -                    last[a_state] = s_error +                    setattr(last,a_state,s_error)                  end                  first, last = nil, nil              elseif first then                  if c_first == s_medi or c_first == s_fina then -                    first[a_state] = s_isol +                    setattr(first,a_state,s_isol)                  else                      warning(first,"isol") -                    first[a_state] = s_error +                    setattr(first,a_state,s_error)                  end                  first = nil              end @@ -375,21 +391,21 @@ function methods.arab(head,font,attr)                  current = end_of_math(current)              end          end -        current = current.next +        current = getnext(current)      end      if last then          if c_last == s_medi or c_last == s_fina then -            last[a_state] = s_fina +            setattr(last,a_state,s_fina)          else              warning(last,"fina") -            last[a_state] = s_error +            setattr(last,a_state,s_error)          end      elseif first then          if c_first == s_medi or c_first == s_fina then -            first[a_state] = s_isol +            setattr(first,a_state,s_isol)          else              warning(first,"isol") -            first[a_state] = s_error +            setattr(first,a_state,s_error)          end      end      return head, done diff --git a/tex/context/base/font-sol.lua b/tex/context/base/font-sol.lua index 9ccfd0588..a41e4a679 100644 --- a/tex/context/base/font-sol.lua +++ b/tex/context/base/font-sol.lua @@ -48,19 +48,41 @@ local v_split            = variables.split  local settings_to_array  = utilities.parsers.settings_to_array  local settings_to_hash   = utilities.parsers.settings_to_hash -local find_node_tail     = node.tail or node.slide -local free_node          = node.free -local free_nodelist      = node.flush_list -local copy_nodelist      = node.copy_list -local traverse_nodes     = node.traverse -local traverse_ids       = node.traverse_id -local protect_glyphs     = nodes.handlers.protectglyphs or node.protect_glyphs -local hpack_nodes        = node.hpack -local insert_node_before = node.insert_before -local insert_node_after  = node.insert_after -local repack_hlist       = nodes.repackhlist +local tasks              = nodes.tasks + +local nuts               = nodes.nuts +local tonut              = nuts.tonut +local tonode             = nuts.tonode + +local getfield           = nuts.getfield +local setfield           = nuts.setfield +local getnext            = nuts.getnext +local getprev            = nuts.getprev +local getid              = nuts.getid +local getattr            = nuts.getattr +local setattr            = nuts.setattr +local getfont            = nuts.getfont +local getsubtype         = nuts.getsubtype +local getchar            = nuts.getchar +local getlist            = nuts.getlist + +local find_node_tail     = nuts.tail +local free_node          = nuts.free +local free_nodelist      = nuts.flush_list +local copy_nodelist      = nuts.copy_list +local traverse_nodes     = nuts.traverse +local traverse_ids       = nuts.traverse_id +local hpack_nodes        = nuts.hpack +local insert_node_before = nuts.insert_before +local insert_node_after  = nuts.insert_after +local protect_glyphs     = nuts.protect_glyphs + +local repack_hlist       = nuts.repackhlist +  local nodes_to_utf       = nodes.listtoutf +----- protect_glyphs     = nodes.handlers.protectglyphs +  local setnodecolor       = nodes.tracers.colors.set  local nodecodes          = nodes.nodecodes @@ -79,8 +101,7 @@ local localpar_code      = whatsitcodes.localpar  local dir_code           = whatsitcodes.dir  local userdefined_code   = whatsitcodes.userdefined -local nodepool           = nodes.pool -local tasks              = nodes.tasks +local nodepool           = nuts.pool  local usernodeids        = nodepool.userids  local new_textdir        = nodepool.textdir @@ -90,7 +111,7 @@ local new_leftskip       = nodepool.leftskip  local starttiming        = statistics.starttiming  local stoptiming         = statistics.stoptiming -local process_characters = nodes.handlers.characters +----- process_characters = nodes.handlers.characters  local inject_kerns       = nodes.injections.handler  local fonthashes         = fonts.hashes @@ -317,11 +338,12 @@ end)  function splitters.split(head)      -- quite fast +    head = tonut(head)      local current, done, rlmode, start, stop, attribute = head, false, false, nil, nil, 0      cache, max_less, max_more = { }, 0, 0      local function flush() -- we can move this -        local font = start.font -        local last = stop.next +        local font = getfont(start) +        local last = getnext(stop)          local list = last and copy_nodelist(start,last) or copy_nodelist(start)          local n = #cache + 1          if encapsulate then @@ -332,18 +354,18 @@ function splitters.split(head)          else              local current = start              while true do -                current[a_word] = n +                setattr(current,a_word,n)                  if current == stop then                      break                  else -                    current = current.next +                    current = getnext(current)                  end              end          end          if rlmode == "TRT" or rlmode == "+TRT" then              local dirnode = new_textdir("+TRT") -            list.prev = dirnode -            dirnode.next = list +            setfield(list,"prev",dirnode) +            setfield(dirnode,"next",list)              list = dirnode          end          local c = { @@ -364,11 +386,11 @@ function splitters.split(head)          start, stop, done = nil, nil, true      end      while current do -- also nextid -        local next = current.next -        local id = current.id +        local next = getnext(current) +        local id = getid(current)          if id == glyph_code then -            if current.subtype < 256 then -                local a = current[a_split] +            if getsubtype(current) < 256 then +                local a = getattr(current,a_split)                  if not a then                      start, stop = nil, nil                  elseif not start then @@ -384,7 +406,7 @@ function splitters.split(head)                  if start then                      flush()                  end -            elseif start and next and next.id == glyph_code and next.subtype < 256 then +            elseif start and next and getid(next) == glyph_code and getsubtype(next) < 256 then                  -- beware: we can cross future lines                  stop = next              else @@ -394,9 +416,9 @@ function splitters.split(head)              if start then                  flush()              end -            local subtype = current.subtype +            local subtype = getsubtype(current)              if subtype == dir_code or subtype == localpar_code then -                rlmode = current.dir +                rlmode = getfield(current,"dir")              end          else              if start then @@ -410,17 +432,17 @@ function splitters.split(head)      end      nofparagraphs = nofparagraphs + 1      nofwords = nofwords + #cache -    return head, done +    return tonode(head), done  end  local function collect_words(list) -- can be made faster for attributes      local words, w, word = { }, 0, nil      if encapsulate then          for current in traverse_ids(whatsit_code,list) do -            if current.subtype == userdefined_code then -- hm -                local user_id = current.user_id +            if getsubtype(current) == userdefined_code then -- hm +                local user_id = getfield(current,"user_id")                  if user_id == splitter_one then -                    word = { current.value, current, current } +                    word = { getfield(current,"value"), current, current }                      w = w + 1                      words[w] = word                  elseif user_id == splitter_two then @@ -436,9 +458,9 @@ local function collect_words(list) -- can be made faster for attributes          local current, first, last, index = list, nil, nil, nil          while current do              -- todo: disc and kern -            local id = current.id +            local id = getid(current)              if id == glyph_code or id == disc_code then -                local a = current[a_word] +                local a = getattr(current,a_word)                  if a then                      if a == index then                          -- same word @@ -471,7 +493,7 @@ local function collect_words(list) -- can be made faster for attributes                          report_splitters("skipped: %C",current.char)                      end                  end -            elseif id == kern_code and (current.subtype == fontkern_code or current[a_fontkern]) then +            elseif id == kern_code and (getsubtype(current) == fontkern_code or getattr(current,a_fontkern)) then                  if first then                      last = current                  else @@ -489,7 +511,7 @@ local function collect_words(list) -- can be made faster for attributes                      end                  end              end -            current = current.next +            current = getnext(current)          end          if index then              w = w + 1 @@ -520,8 +542,8 @@ local function doit(word,list,best,width,badness,line,set,listdir)      if found then          local h, t          if encapsulate then -            h = word[2].next -- head of current word -            t = word[3].prev -- tail of current word +            h = getnext(word[2]) -- head of current word +            t = getprev(word[3]) -- tail of current word          else              h = word[2]              t = word[3] @@ -536,7 +558,7 @@ local function doit(word,list,best,width,badness,line,set,listdir)                      ok = true                      break                  else -                    c = c.next +                    c = getnext(c)                  end              end              if not ok then @@ -555,19 +577,20 @@ local function doit(word,list,best,width,badness,line,set,listdir)                  local first = copy_nodelist(original)                  if not trace_colors then                      for n in traverse_nodes(first) do -- maybe fast force so no attr needed -                        n[0] = featurenumber -- this forces dynamics +                        setattr(n,0,featurenumber) -- this forces dynamics                      end                  elseif set == "less" then                      for n in traverse_nodes(first) do                          setnodecolor(n,"font:isol") -- yellow -                        n[0] = featurenumber +                        setattr(n,0,featurenumber)                      end                  else                      for n in traverse_nodes(first) do                          setnodecolor(n,"font:medi") -- green -                        n[0] = featurenumber +                        setattr(n,0,featurenumber)                      end                  end +first = tonode(first)                  local font = found.font                  local setdynamics = setfontdynamics[font]                  if setdynamics then @@ -579,20 +602,21 @@ local function doit(word,list,best,width,badness,line,set,listdir)                      report_solutions("fatal error, no dynamics for font %a",font)                  end                  first = inject_kerns(first) -                if first.id == whatsit_code then +first = tonut(first) +                if getid(first) == whatsit_code then                      local temp = first -                    first = first.next +                    first = getnext(first)                      free_node(temp)                  end                  local last = find_node_tail(first)                  -- replace [u]h->t by [u]first->last -                local prev = h.prev -                local next = t.next -                prev.next = first -                first.prev = prev +                local prev = getprev(h) +                local next = getnext(t) +                setfield(prev,"next",first) +                setfield(first,"prev",prev)                  if next then -                    last.next = next -                    next.prev = last +                    setfield(last,"next",next) +                    setfield(next,"prev",last)                  end                  -- check new pack                  local temp, b = repack_hlist(list,width,'exactly',listdir) @@ -601,22 +625,22 @@ local function doit(word,list,best,width,badness,line,set,listdir)                          report_optimizers("line %a, badness before %a, after %a, criterium %a, verdict %a",line,badness,b,criterium,"quit")                      end                      -- remove last insert -                    prev.next = h -                    h.prev = prev +                    setfield(prev,"next",h) +                    setfield(h,"prev",prev)                      if next then -                        t.next = next -                        next.prev = t +                        setfield(t,"next",next) +                        setfield(next,"prev",t)                      else -                        t.next = nil +                        setfield(t,"next",nil)                      end -                    last.next = nil +                    setfield(last,"next",nil)                      free_nodelist(first)                  else                      if trace_optimize then                          report_optimizers("line %a, badness before: %a, after %a, criterium %a, verdict %a",line,badness,b,criterium,"continue")                      end                      -- free old h->t -                    t.next = nil +                    setfield(t,"next",nil)                      free_nodelist(h) -- somhow fails                      if not encapsulate then                          word[2] = first @@ -697,9 +721,9 @@ variants[v_random] = function(words,list,best,width,badness,line,set,listdir)  end  local function show_quality(current,what,line) -    local set    = current.glue_set -    local sign   = current.glue_sign -    local order  = current.glue_order +    local set    = getfield(current,"glue_set") +    local sign   = getfield(current,"glue_sign") +    local order  = getfield(current,"glue_order")      local amount = set * ((sign == 2 and -1) or 1)      report_optimizers("line %a, category %a, amount %a, set %a, sign %a, how %a, order %a",line,what,amount,set,sign,how,order)  end @@ -719,20 +743,25 @@ function splitters.optimize(head)          math.setrandomseedi(randomseed)          randomseed = nil      end -    local line = 0 -    local tex_hbadness, tex_hfuzz = tex.hbadness, tex.hfuzz -    tex.hbadness, tex.hfuzz = 10000, number.maxdimen +    local line         = 0 +    local tex_hbadness = tex.hbadness +    local tex_hfuzz    = tex.hfuzz +    tex.hbadness       = 10000 +    tex.hfuzz          = number.maxdimen      if trace_optimize then          report_optimizers("preroll %a, variant %a, criterium %a, cache size %a",preroll,variant,criterium,nc)      end -    for current in traverse_ids(hlist_code,head) do -     -- report_splitters("before: [%s] => %s",current.dir,nodes.tosequence(current.list,nil)) +    for current in traverse_ids(hlist_code,tonut(head)) do          line = line + 1 -        local sign, dir, list, width = current.glue_sign, current.dir, current.list, current.width -        if not encapsulate and list.id == glyph_code then +        local sign  = getfield(current,"glue_sign") +        local dir   = getfield(current,"dir") +        local width = getfield(current,"width") +        local list  = getlist(current) +        if not encapsulate and getid(list) == glyph_code then              -- nasty .. we always assume a prev being there .. future luatex will always have a leftskip set -         -- current.list, list = insert_node_before(list,list,new_glue(0)) -            current.list, list = insert_node_before(list,list,new_leftskip(0)) +            -- is this assignment ok ? .. needs checking +            list = insert_node_before(list,list,new_leftskip(0)) -- new_glue(0) +            setfield(current,"list",list)          end          local temp, badness = repack_hlist(list,width,'exactly',dir) -- it would be nice if the badness was stored in the node          if badness > 0 then @@ -792,7 +821,7 @@ function splitters.optimize(head)                  local words = collect_words(list)                  for best=lastbest or 1,max do                      local temp, done, changes, b = optimize(words,list,best,width,badness,line,set,dir) -                    current.list = temp +                    setfield(current,"list",temp)                      if trace_optimize then                          report_optimizers("line %a, alternative %a, changes %a, badness %a",line,best,changes,b)                      end @@ -810,15 +839,16 @@ function splitters.optimize(head)              end          end          -- we pack inside the outer hpack and that way keep the original wd/ht/dp as bonus -        current.list = hpack_nodes(current.list,width,'exactly',listdir) -     -- report_splitters("after: [%s] => %s",temp.dir,nodes.tosequence(temp.list,nil)) +        local list = hpack_nodes(getlist(current),width,'exactly',listdir) +        setfield(current,"list",list)      end      for i=1,nc do          local ci = cache[i]          free_nodelist(ci.original)      end      cache = { } -    tex.hbadness, tex.hfuzz = tex_hbadness, tex_hfuzz +    tex.hbadness = tex_hbadness +    tex.hfuzz    = tex_hfuzz      stoptiming(splitters)  end diff --git a/tex/context/base/lang-rep.lua b/tex/context/base/lang-rep.lua deleted file mode 100644 index 31ae36e6d..000000000 --- a/tex/context/base/lang-rep.lua +++ /dev/null @@ -1,189 +0,0 @@ -if not modules then modules = { } end modules ['lang-rep'] = { -    version   = 1.001, -    comment   = "companion to lang-rep.mkiv", -    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL", -    copyright = "PRAGMA ADE / ConTeXt Development Team", -    license   = "see context related readme files" -} - --- A BachoTeX 2013 experiment, probably not that useful. Eventually I used a simpler --- more generic example. - -local utfbyte, utfsplit = utf.byte, utf.split - -local trace_replacements = false  trackers.register("languages.replacements",        function(v) trace_replacements = v end) -local trace_detail       = false  trackers.register("languages.replacements.detail", function(v) trace_detail       = v end) - -local report_replacement = logs.reporter("languages","replacements") - -local glyph_code         = nodes.nodecodes.glyph - -local insert_node_before = nodes.insert_before -local remove_node        = nodes.remove -local copy_node          = nodes.copy - -local texsetattribute    = tex.setattribute -local unsetvalue         = attributes.unsetvalue - -local v_reset            = interfaces.variables.reset - -local replacements       = languages.replacements or { } -languages.replacements   = replacements - -local a_replacements     = attributes.private("replacements") - -local lists = { } -local last  = 0 -local trees = { } - -table.setmetatableindex(lists,function(lists,name) -    last = last + 1 -    local list = { } -    local data = { name = name, list = list, attribute = last } -    lists[last] = data -    lists[name] = data -    trees[last] = list -    return data -end) - -local function add(root,word,replacement) -    local list = utfsplit(word,true) -    for i=1,#list do -        local l = utfbyte(list[i]) -        if not root[l] then -            root[l] = { } -        end -        if i == #list then -            local newlist = utfsplit(replacement,true) -            for i=1,#newlist do -                newlist[i] = utfbyte(newlist[i]) -            end -            root[l].final = { -                word        = word, -                replacement = replacement, -                oldlength   = #list, -                newcodes    = newlist, -            } -        end -        root = root[l] -    end -end - -function replacements.add(category,word,replacement) -    local root = lists[category].list -    if type(word) == "table" then -        for word, replacement in next, word do -            add(root,word,replacement) -        end -    else -        add(root,word,replacement or "") -    end -end - -local function hit(a,head) -    local tree = trees[a] -    if tree then -        local root = tree[head.char] -        if root then -            local current   = head.next -            local lastrun   = false -            local lastfinal = false -            while current and current.id == glyph_code do -                local newroot = root[current.char] -                if not newroot then -                    return lastrun, lastfinal -                else -                    local final = newroot.final -                    if final then -                        if trace_detail then -                            report_replacement("hitting word %a, replacement %a",final.word,final.replacement) -                        end -                        lastrun   = current -                        lastfinal = final -                    else -                        root = newroot -                    end -                end -                current = current.next -            end -            if lastrun then -                return lastrun, lastfinal -            end -        end -    end -end - -function replacements.handler(head) -    local current = head -    local done    = false -    while current do -        if current.id == glyph_code then -            local a = getattr(current,a_replacements) -            if a then -                local last, final = hit(a,current) -                if last then -                    local oldlength = final.oldlength -                    local newcodes  = final.newcodes -                    local newlength = #newcodes -                    if report_replacement then -                        report_replacement("replacing word %a by %a",final.word,final.replacement) -                    end -                    if oldlength == newlength then -- #old == #new -                        for i=1,newlength do -                            current.char = newcodes[i] -                            current = current.next -                        end -                    elseif oldlength < newlength then -- #old < #new -                        for i=1,newlength-oldlength do -                            local n = copy_node(current) -                            n.char = newcodes[i] -                            head, current = insert_node_before(head,current,n) -                            current = current.next -                        end -                        for i=newlength-oldlength+1,newlength do -                            current.char = newcodes[i] -                            current = current.next -                        end -                    else -- #old > #new -                        for i=1,oldlength-newlength do -                            head, current = remove_node(head,current,true) -                        end -                        for i=1,newlength do -                            current.char = newcodes[i] -                            current = current.next -                        end -                    end -                    done = true -                end -            end -        end -        current = current.next -    end -    return head, done -end - -local enabled = false - -function replacements.set(n) -- number or 'reset' -    if n == v_reset then -        n = unsetvalue -    else -        n = lists[n].attribute -        if not enabled then -            nodes.tasks.enableaction("processors","languages.replacements.handler") -            if trace_replacements then -                report_replacement("enabling replacement handler") -            end -            enabled = true -        end -    end -    texsetattribute(a_replacements,n) -end - --- interface - -commands.setreplacements = replacements.set -commands.addreplacements = replacements.add - -nodes.tasks.prependaction("processors","words","languages.replacements.handler") -nodes.tasks.disableaction("processors","languages.replacements.handler") diff --git a/tex/context/base/lang-wrd.lua b/tex/context/base/lang-wrd.lua index bf066fc09..11d99976e 100644 --- a/tex/context/base/lang-wrd.lua +++ b/tex/context/base/lang-wrd.lua @@ -26,7 +26,17 @@ words.threshold       = 4  local numbers         = languages.numbers  local registered      = languages.registered -local traverse_nodes  = node.traverse +local nuts            = nodes.nuts +local tonut           = nuts.tonut + +local getfield        = nuts.getfield +local getnext         = nuts.getnext +local getid           = nuts.getid +local getsubtype      = nuts.getsubtype +local getchar         = nuts.getchar + +local traverse_nodes  = nuts.traverse +  local wordsdata       = words.data  local chardata        = characters.data  local tasks           = nodes.tasks @@ -96,7 +106,7 @@ end  -- there is an n=1 problem somewhere in nested boxes  local function mark_words(head,whenfound) -- can be optimized and shared -    local current, language, done = head, nil, nil, 0, false +    local current, language, done = tonut(head), nil, nil, 0, false      local str, s, nds, n = { }, 0, { }, 0 -- n could also be a table, saves calls      local function action()          if s > 0 then @@ -112,9 +122,9 @@ local function mark_words(head,whenfound) -- can be optimized and shared          n, s = 0, 0      end      while current do -        local id = current.id +        local id = getid(current)          if id == glyph_code then -            local a = current.lang +            local a = getfield(current,"lang")              if a then                  if a ~= language then                      if s > 0 then @@ -126,16 +136,16 @@ local function mark_words(head,whenfound) -- can be optimized and shared                  action()                  language = a              end -            local components = current.components +            local components = getfield(current,"components")              if components then                  n = n + 1                  nds[n] = current                  for g in traverse_nodes(components) do                      s = s + 1 -                    str[s] = utfchar(g.char) +                    str[s] = utfchar(getchar(g))                  end              else -                local code = current.char +                local code = getchar(current)                  local data = chardata[code]                  if is_letter[data.category] then                      n = n + 1 @@ -151,12 +161,12 @@ local function mark_words(head,whenfound) -- can be optimized and shared                  n = n + 1                  nds[n] = current              end -        elseif id == kern_code and current.subtype == kerning_code and s > 0 then +        elseif id == kern_code and getsubtype(current) == kerning_code and s > 0 then              -- ok          elseif s > 0 then              action()          end -        current = current.next +        current = getnext(current)      end      if s > 0 then          action() diff --git a/tex/context/base/lpdf-nod.lua b/tex/context/base/lpdf-nod.lua index 6b104d2fa..68d7fca90 100644 --- a/tex/context/base/lpdf-nod.lua +++ b/tex/context/base/lpdf-nod.lua @@ -6,21 +6,29 @@ if not modules then modules = { } end modules ['lpdf-nod'] = {      license   = "see context related readme files"  } -local formatters     = string.formatters +local type = type -local copy_node      = node.copy -local new_node       = node.new +local formatters     = string.formatters -local nodepool       = nodes.pool -local register       = nodepool.register  local whatsitcodes   = nodes.whatsitcodes  local nodeinjections = backends.nodeinjections -local pdfliteral     = register(new_node("whatsit", whatsitcodes.pdfliteral))    pdfliteral.mode  = 1 +local nuts           = nodes.nuts +local tonut          = nuts.tonut + +local setfield       = nuts.setfield + +local copy_node      = nuts.copy +local new_node       = nuts.new + +local nodepool       = nuts.pool +local register       = nodepool.register + +local pdfliteral     = register(new_node("whatsit", whatsitcodes.pdfliteral))    setfield(pdfliteral,"mode",1)  local pdfsave        = register(new_node("whatsit", whatsitcodes.pdfsave))  local pdfrestore     = register(new_node("whatsit", whatsitcodes.pdfrestore))  local pdfsetmatrix   = register(new_node("whatsit", whatsitcodes.pdfsetmatrix)) -local pdfdest        = register(new_node("whatsit", whatsitcodes.pdfdest))       pdfdest.named_id = 1 -- xyz_zoom untouched +local pdfdest        = register(new_node("whatsit", whatsitcodes.pdfdest))       setfield(pdfdest,"named_id",1) -- xyz_zoom untouched  local pdfannot       = register(new_node("whatsit", whatsitcodes.pdfannot))  local variables      = interfaces.variables @@ -38,14 +46,14 @@ local views = { -- beware, we do support the pdf keys but this is *not* official  function nodepool.pdfliteral(str)      local t = copy_node(pdfliteral) -    t.data = str +    setfield(t,"data",str)      return t  end  function nodepool.pdfdirect(str)      local t = copy_node(pdfliteral) -    t.data = str -    t.mode = 1 +    setfield(t,"data",str) +    setfield(t,"mode",1)      return t  end @@ -57,16 +65,10 @@ function nodepool.pdfrestore()      return copy_node(pdfrestore)  end -function nodepool.pdfsetmatrix(rx,sx,sy,ry,tx,ty) -    local t = copy_node(pdfsetmatrix) -    t.data = formatters["%s %s %s %s"](rx or 0,sx or 0,sy or 0,ry or 0) -- todo: tx ty -    return t -end - -function nodepool.pdfsetmatrix(rx,sx,sy,ry,tx,ty) +function nodepool.pdfsetmatrix(rx,sx,sy,ry,tx,ty) -- todo: tx ty      local t = copy_node(pdfsetmatrix)      if type(rx) == "string" then -        t.data = rx +        setfield(t,"data",rx)      else          if not rx then              rx = 1 @@ -86,12 +88,12 @@ function nodepool.pdfsetmatrix(rx,sx,sy,ry,tx,ty)          end          if sx == 0 and sy == 0 then              if rx == 1 and ry == 1 then -                t.data = "1 0 0 1" +                setfield(t,"data","1 0 0 1")              else -                t.data = formatters["%0.6f 0 0 %0.6f"](rx,ry) +                setfield(t,"data",formatters["%0.6f 0 0 %0.6f"](rx,ry))              end          else -            t.data = formatters["%0.6f %0.6f %0.6f %0.6f"](rx,sx,sy,ry) +            setfield(t,"data",formatters["%0.6f %0.6f %0.6f %0.6f"](rx,sx,sy,ry))          end      end      return t @@ -104,19 +106,19 @@ nodeinjections.transform = nodepool.pdfsetmatrix  function nodepool.pdfannotation(w,h,d,data,n)      local t = copy_node(pdfannot)      if w and w ~= 0 then -        t.width = w +        setfield(t,"width",w)      end      if h and h ~= 0 then -        t.height = h +        setfield(t,"height",h)      end      if d and d ~= 0 then -        t.depth = d +        setfield(t,"depth",d)      end      if n then -        t.objnum = n +        setfield(t,"objnum",n)      end      if data and data ~= "" then -        t.data = data +        setfield(t,"data",data)      end      return t  end @@ -138,35 +140,36 @@ function nodepool.pdfdestination(w,h,d,name,view,n)      local t = copy_node(pdfdest)      local hasdimensions = false      if w and w ~= 0 then -        t.width = w +        setfield(t,"width",w)          hasdimensions = true      end      if h and h ~= 0 then -        t.height = h +        setfield(t,"height",h)          hasdimensions = true      end      if d and d ~= 0 then -        t.depth = d +        setfield(t,"depth",d)          hasdimensions = true      end      if n then -        t.objnum = n +        setfield(t,"objnum",n)      end      view = views[view] or view or 1 -- fit is default -    t.dest_id = name -    t.dest_type = view +    setfield(t,"dest_id",name) +    setfield(t,"dest_type",view)      if hasdimensions and view == 0 then -- xyz          -- see (!) s -> m -> t -> r +        -- linked          local s = copy_node(pdfsave)          local m = copy_node(pdfsetmatrix)          local r = copy_node(pdfrestore) -        m.data = "1 0 0 1" -        s.next = m -        m.next = t -        t.next = r -        m.prev = s -        t.prev = m -        r.prev = t +        setfield(m,"data","1 0 0 1") +        setfield(s,"next",m) +        setfield(m,"next",t) +        setfield(t,"next",r) +        setfield(m,"prev",s) +        setfield(t,"prev",m) +        setfield(r,"prev",t)          return s -- a list      else          return t diff --git a/tex/context/base/lpdf-tag.lua b/tex/context/base/lpdf-tag.lua index 29ffcd207..83315da07 100644 --- a/tex/context/base/lpdf-tag.lua +++ b/tex/context/base/lpdf-tag.lua @@ -14,7 +14,9 @@ local trace_tags = false  trackers.register("structures.tags", function(v) trace  local report_tags = logs.reporter("backend","tags") -local backends, lpdf, nodes = backends, lpdf, nodes +local backends         = backends +local lpdf             = lpdf +local nodes            = nodes  local nodeinjections   = backends.pdf.nodeinjections  local codeinjections   = backends.pdf.codeinjections @@ -47,11 +49,22 @@ local glyph_code       = nodecodes.glyph  local a_tagged         = attributes.private('tagged')  local a_image          = attributes.private('image') -local traverse_nodes   = node.traverse -local traverse_id      = node.traverse_id -local tosequence       = nodes.tosequence -local copy_node        = node.copy -local slide_nodelist   = node.slide +local nuts             = nodes.nuts +local tonut            = nuts.tonut +local tonode           = nuts.tonode + +local getid            = nuts.getid +local getattr          = nuts.getattr +local getprev          = nuts.getprev +local getlist          = nuts.getlist +local setfield         = nuts.setfield + +local traverse_nodes   = nuts.traverse +local tosequence       = nuts.tosequence +local copy_node        = nuts.copy +local slide_nodelist   = nuts.slide +local insert_before    = nuts.insert_before +local insert_after     = nuts.insert_after  local structure_stack = { }  local structure_kids  = pdfarray() @@ -175,7 +188,8 @@ local function makeelement(fulltag,parent)  end  local function makecontent(parent,start,stop,slist,id) -    local tag, kids = parent.tag, parent.kids +    local tag  = parent.tag +    local kids = parent.kids      local last = index      if id == "image" then          local d = pdfdictionary { @@ -198,23 +212,16 @@ local function makecontent(parent,start,stop,slist,id)      end      --      local bliteral = pdfliteral(format("/%s <</MCID %s>>BDC",tag,last)) -    local prev = start.prev -    if prev then -        prev.next, bliteral.prev = bliteral, prev -    end -    start.prev, bliteral.next = bliteral, start -    if slist and slist.list == start then -        slist.list = bliteral -    elseif not prev then +    local eliteral = pdfliteral("EMC") +    -- +    if slist and getlist(slist) == start then +        setfield(slist,"list",bliteral) +    elseif not getprev(start) then          report_tags("this can't happen: injection in front of nothing")      end      -- -    local eliteral = pdfliteral("EMC") -    local next = stop.next -    if next then -        next.prev, eliteral.next = eliteral, next -    end -    stop.next, eliteral.prev = eliteral, stop +    insert_before(start,start,bliteral) +    insert_after(stop,stop,eliteral)      --      index = index + 1      list[index] = parent.pref @@ -227,9 +234,9 @@ local level, last, ranges, range = 0, nil, { }, nil  local function collectranges(head,list)      for n in traverse_nodes(head) do -        local id = n.id -- 14: image, 8: literal (mp) +        local id = getid(n) -- 14: image, 8: literal (mp)          if id == glyph_code then -            local at = n[a_tagged] +            local at = getattr(n,a_tagged)              if not at then                  range = nil              elseif last ~= at then @@ -240,9 +247,9 @@ local function collectranges(head,list)                  range[4] = n -- stop              end          elseif id == hlist_code or id == vlist_code then -            local at = n[a_image] +            local at = getattr(n,a_image)              if at then -                local at = n[a_tagged] +                local at = getattr(n,a_tagged)                  if not at then                      range = nil                  else @@ -250,7 +257,7 @@ local function collectranges(head,list)                  end                  last = nil              else -                local nl = n.list +                local nl = getlist(n)                  slide_nodelist(nl) -- temporary hack till math gets slided (tracker item)                  collectranges(nl,n)              end @@ -262,7 +269,7 @@ function nodeinjections.addtags(head)      -- no need to adapt head, as we always operate on lists      level, last, ranges, range = 0, nil, { }, nil      initializepage() -    collectranges(head) +    collectranges(tonut(head))      if trace_tags then          for i=1,#ranges do              local range = ranges[i] @@ -289,13 +296,13 @@ function nodeinjections.addtags(head)          local b, e = makecontent(prev,start,stop,list,id)          if start == head then              report_tags("this can't happen: parent list gets tagged") -            head = b +            head = tonode(b)          end      end      finishpage()      -- can be separate feature      -- -    -- injectspans(head) -- does to work yet +    -- injectspans(tonut(head)) -- does to work yet      --      return head, true  end diff --git a/tex/context/base/math-dir.lua b/tex/context/base/math-dir.lua index 507a24e41..ec64e6787 100644 --- a/tex/context/base/math-dir.lua +++ b/tex/context/base/math-dir.lua @@ -23,8 +23,19 @@ local trace_directions   = false  trackers.register("typesetters.directions.math  local report_directions  = logs.reporter("typesetting","math directions") -local insert_node_before = nodes.insert_before -local insert_node_after  = nodes.insert_after +local nuts               = nodes.nuts +local tonut              = nuts.tonut +local tonode             = nuts.tonode + +local getnext            = nuts.getnext +local getchar            = nuts.getchar +local getid              = nuts.getid +local getlist            = nuts.getlist +local setfield           = nuts.setfield +local getattr            = nuts.getattr + +local insert_node_before = nuts.insert_before +local insert_node_after  = nuts.insert_after  local nodecodes          = nodes.nodecodes  local tasks              = nodes.tasks @@ -33,7 +44,7 @@ local glyph_code         = nodecodes.glyph  local hlist_code         = nodecodes.hlist  local vlist_code         = nodecodes.vlist -local nodepool           = nodes.pool +local nodepool           = nuts.pool  local new_textdir        = nodepool.textdir @@ -61,9 +72,9 @@ local function processmath(head)          stop  = nil      end      while current do -        local id = current.id +        local id = getid(current)          if id == glyph_code then -            local char = current.char +            local char = getchar(current)              local cdir = chardirections[char]              if cdir == "en" or cdir == "an" then -- we could check for mathclass punctuation                  if not start then @@ -83,7 +94,7 @@ local function processmath(head)                      if mirror then                          local class = charclasses[char]                          if class == "open" or class == "close" then -                            current.char = mirror +                            setfield(current,"char",mirror)                              if trace_directions then                                  report_directions("mirrored: %C to %C",char,mirror)                              end @@ -101,14 +112,14 @@ local function processmath(head)              -- math can pack things into hlists .. we need to make sure we don't process              -- too often: needs checking              if id == hlist_code or id == vlist_code then -                local list, d = processmath(current.list) -                current.list = list +                local list, d = processmath(getlist(current)) +                setfield(current,"list",list)                  if d then                      done = true                  end              end          end -        current = current.next +        current = getnext(current)      end      if not start then          -- nothing @@ -124,9 +135,11 @@ local enabled = false  function directions.processmath(head) -- style, penalties      if enabled then -        local a = head[a_mathbidi] +        local h = tonut(head) +        local a = getattr(h,a_mathbidi)          if a and a > 0 then -            return processmath(head) +            local head, done = processmath(h) +            return tonode(head), done          end      end      return head, false diff --git a/tex/context/base/math-noa.lua b/tex/context/base/math-noa.lua index f3987c12f..4e25fe206 100644 --- a/tex/context/base/math-noa.lua +++ b/tex/context/base/math-noa.lua @@ -54,16 +54,35 @@ local report_families     = logs.reporter("mathematics","families")  local a_mathrendering     = attributes.private("mathrendering")  local a_exportstatus      = attributes.private("exportstatus") -local mlist_to_hlist      = node.mlist_to_hlist +local nuts                = nodes.nuts +local nodepool            = nuts.pool +local tonut               = nuts.tonut +local nutstring           = nuts.tostring + +local getfield            = nuts.getfield +local getnext             = nuts.getnext +local getprev             = nuts.getprev +local getid               = nuts.getid +local getattr             = nuts.getattr +local getfont             = nuts.getfont +local getsubtype          = nuts.getsubtype +local getchar             = nuts.getchar + +local setfield            = nuts.setfield +local setattr             = nuts.setattr + +local insert_node_after   = nuts.insert_after +local insert_node_before  = nuts.insert_before +local free_node           = nuts.free +local new_node            = nuts.new -- todo: pool: math_noad math_sub +local copy_node           = nuts.copy + +local mlist_to_hlist      = nodes.mlist_to_hlist +  local font_of_family      = node.family_font -local insert_node_after   = node.insert_after -local insert_node_before  = node.insert_before -local free_node           = node.free -local new_node            = node.new -- todo: pool: math_noad math_sub -local copy_node           = node.copy -local new_kern            = nodes.pool.kern -local new_rule            = nodes.pool.rule +local new_kern            = nodepool.kern +local new_rule            = nodepool.rule  local topoints            = number.points @@ -126,23 +145,23 @@ local function process(start,what,n,parent)      if n then n = n + 1 else n = 0 end      local prev = nil      while start do -        local id = start.id +        local id = getid(start)          if trace_processing then              if id == math_noad then -                report_processing("%w%S, class %a",n*2,start,noadcodes[start.subtype]) +                report_processing("%w%S, class %a",n*2,nutstring(start),noadcodes[getsubtype(start)])              elseif id == math_char then -                local char = start.char -                local fam = start.fam +                local char = getchar(start) +                local fam  = getfield(start,"fam")                  local font = font_of_family(fam) -                report_processing("%w%S, family %a, font %a, char %a, shape %c",n*2,start,fam,font,char,char) +                report_processing("%w%S, family %a, font %a, char %a, shape %c",n*2,nutstring(start),fam,font,char,char)              else -                report_processing("%w%S",n*2,start) +                report_processing("%w%S",n*2,nutstring(start))              end          end          local proc = what[id]          if proc then           -- report_processing("start processing") -            local done, newstart = proc(start,what,n,parent) -- prev is bugged:  or start.prev +            local done, newstart = proc(start,what,n,parent) -- prev is bugged:  or getprev(start)              if newstart then                  start = newstart               -- report_processing("stop processing (new start)") @@ -154,55 +173,55 @@ local function process(start,what,n,parent)          elseif id == math_noad then  if prev then      -- we have no proper prev in math nodes yet -    start.prev = prev +    setfield(start,"prev",prev)  end -            local noad = start.nucleus      if noad then process(noad,what,n,start) end -- list -                  noad = start.sup          if noad then process(noad,what,n,start) end -- list -                  noad = start.sub          if noad then process(noad,what,n,start) end -- list + +            local noad = getfield(start,"nucleus")      if noad then process(noad,what,n,start) end -- list +                  noad = getfield(start,"sup")          if noad then process(noad,what,n,start) end -- list +                  noad = getfield(start,"sub")          if noad then process(noad,what,n,start) end -- list          elseif id == math_box or id == math_sub then -         -- local noad = start.list         if noad then process(noad,what,n,start) end -- list -            local noad = start.head         if noad then process(noad,what,n,start) end -- list +            local noad = getfield(start,"list")         if noad then process(noad,what,n,start) end -- list (not getlist !)          elseif id == math_fraction then -            local noad = start.num          if noad then process(noad,what,n,start) end -- list -                  noad = start.denom        if noad then process(noad,what,n,start) end -- list -                  noad = start.left         if noad then process(noad,what,n,start) end -- delimiter -                  noad = start.right        if noad then process(noad,what,n,start) end -- delimiter +            local noad = getfield(start,"num")          if noad then process(noad,what,n,start) end -- list +                  noad = getfield(start,"denom")        if noad then process(noad,what,n,start) end -- list +                  noad = getfield(start,"left")         if noad then process(noad,what,n,start) end -- delimiter +                  noad = getfield(start,"right")        if noad then process(noad,what,n,start) end -- delimiter          elseif id == math_choice then -            local noad = start.display      if noad then process(noad,what,n,start) end -- list -                  noad = start.text         if noad then process(noad,what,n,start) end -- list -                  noad = start.script       if noad then process(noad,what,n,start) end -- list -                  noad = start.scriptscript if noad then process(noad,what,n,start) end -- list +            local noad = getfield(start,"display")      if noad then process(noad,what,n,start) end -- list +                  noad = getfield(start,"text")         if noad then process(noad,what,n,start) end -- list +                  noad = getfield(start,"script")       if noad then process(noad,what,n,start) end -- list +                  noad = getfield(start,"scriptscript") if noad then process(noad,what,n,start) end -- list          elseif id == math_fence then -            local noad = start.delim        if noad then process(noad,what,n,start) end -- delimiter +            local noad = getfield(start,"delim")        if noad then process(noad,what,n,start) end -- delimiter          elseif id == math_radical then -            local noad = start.nucleus      if noad then process(noad,what,n,start) end -- list -                  noad = start.sup          if noad then process(noad,what,n,start) end -- list -                  noad = start.sub          if noad then process(noad,what,n,start) end -- list -                  noad = start.left         if noad then process(noad,what,n,start) end -- delimiter -                  noad = start.degree       if noad then process(noad,what,n,start) end -- list +            local noad = getfield(start,"nucleus")      if noad then process(noad,what,n,start) end -- list +                  noad = getfield(start,"sup")          if noad then process(noad,what,n,start) end -- list +                  noad = getfield(start,"sub")          if noad then process(noad,what,n,start) end -- list +                  noad = getfield(start,"left")         if noad then process(noad,what,n,start) end -- delimiter +                  noad = getfield(start,"degree")       if noad then process(noad,what,n,start) end -- list          elseif id == math_accent then -            local noad = start.nucleus      if noad then process(noad,what,n,start) end -- list -                  noad = start.sup          if noad then process(noad,what,n,start) end -- list -                  noad = start.sub          if noad then process(noad,what,n,start) end -- list -                  noad = start.accent       if noad then process(noad,what,n,start) end -- list -                  noad = start.bot_accent   if noad then process(noad,what,n,start) end -- list +            local noad = getfield(start,"nucleus")      if noad then process(noad,what,n,start) end -- list +                  noad = getfield(start,"sup")          if noad then process(noad,what,n,start) end -- list +                  noad = getfield(start,"sub")          if noad then process(noad,what,n,start) end -- list +                  noad = getfield(start,"accent")       if noad then process(noad,what,n,start) end -- list +                  noad = getfield(start,"bot_accent")   if noad then process(noad,what,n,start) end -- list          elseif id == math_style then              -- has a next          else              -- glue, penalty, etc          end  prev = start -        start = start.next +        start = getnext(start)      end  end  local function processnoads(head,actions,banner)      if trace_processing then          report_processing("start %a",banner) -        process(head,actions) +        process(tonut(head),actions)          report_processing("stop %a",banner)      else -        process(head,actions) +        process(tonut(head),actions)      end  end @@ -233,37 +252,71 @@ local familymap = { [0] =      "pseudobold",  } +-- families[math_char] = function(pointer) +--     if getfield(pointer,"fam") == 0 then +--         local a = getattr(pointer,a_mathfamily) +--         if a and a > 0 then +--             setattr(pointer,a_mathfamily,0) +--             if a > 5 then +--                 local char = getchar(pointer) +--                 local bold = boldmap[char] +--                 local newa = a - 3 +--                 if bold then +--                     setattr(pointer,a_exportstatus,char) +--                     setfield(pointer,"char",bold) +--                     if trace_families then +--                         report_families("replacing %C by bold %C, family %s with remap %s becomes %s with remap %s",char,bold,a,familymap[a],newa,familymap[newa]) +--                     end +--                 else +--                     if trace_families then +--                         report_families("no bold replacement for %C, family %s with remap %s becomes %s with remap %s",char,a,familymap[a],newa,familymap[newa]) +--                     end +--                 end +--                 setfield(pointer,"fam",newa) +--             else +--                 if trace_families then +--                     local char = getchar(pointer) +--                     report_families("family of %C becomes %s with remap %s",char,a,familymap[a]) +--                 end +--                 setfield(pointer,"fam",a) +--             end +--         else +--          -- pointer.fam = 0 +--         end +--     end +-- end +  families[math_char] = function(pointer) -    if pointer.fam == 0 then -        local a = pointer[a_mathfamily] +    if getfield(pointer,"fam") == 0 then +        local a = getattr(pointer,a_mathfamily)          if a and a > 0 then -            pointer[a_mathfamily] = 0 +            setattr(pointer,a_mathfamily,0)              if a > 5 then -                local char = pointer.char +                local char = getchar(pointer)                  local bold = boldmap[char]                  local newa = a - 3                  if not bold then                      if trace_families then                          report_families("no bold replacement for %C, family %s with remap %s becomes %s with remap %s",char,a,familymap[a],newa,familymap[newa])                      end -                    pointer.fam = newa -               elseif not fontcharacters[font_of_family(newa)][bold] then +                    setfield(pointer,"fam",newa) +                elseif not fontcharacters[font_of_family(newa)][bold] then                      if trace_families then                          report_families("no bold character for %C, family %s with remap %s becomes %s with remap %s",char,a,familymap[a],newa,familymap[newa])                      end                      if newa > 3 then -                        pointer.fam = newa - 3 +                        setfield(pointer,"fam",newa-3)                      end                  else -                    pointer[a_exportstatus] = char -                    pointer.char = bold +                    setattr(pointer,a_exportstatus,char) +                    setfield(pointer,"char",bold)                      if trace_families then                          report_families("replacing %C by bold %C, family %s with remap %s becomes %s with remap %s",char,bold,a,familymap[a],newa,familymap[newa])                      end -                    pointer.fam = newa +                    setfield(pointer,"fam",newa)                  end              else -                local char = pointer.char +                local char = getchar(pointer)                  if not fontcharacters[font_of_family(a)][char] then                      if trace_families then                          report_families("no bold replacement for %C",char) @@ -272,7 +325,7 @@ families[math_char] = function(pointer)                      if trace_families then                          report_families("family of %C becomes %s with remap %s",char,a,familymap[a])                      end -                    pointer.fam = a +                    setfield(pointer,"fam",a)                  end              end          end @@ -280,31 +333,31 @@ families[math_char] = function(pointer)  end  families[math_delim] = function(pointer) -    if pointer.small_fam == 0 then -        local a = pointer[a_mathfamily] +    if getfield(pointer,"small_fam") == 0 then +        local a = getattr(pointer,a_mathfamily)          if a and a > 0 then -            pointer[a_mathfamily] = 0 +            setattr(pointer,a_mathfamily,0)              if a > 5 then                  -- no bold delimiters in unicode                  a = a - 3              end -            local char = pointer.small_char +            local char = getfield(pointer,"small_char")              local okay = fontcharacters[font_of_family(a)][char]              if okay then -                pointer.small_fam = a +                setfield(pointer,"small_fam",a)              elseif a > 2 then -                pointer.small_fam = a - 3 +                setfield(pointer,"small_fam",a-3)              end -            local char = pointer.large_char +            local char = getfield(pointer,"large_char")              local okay = fontcharacters[font_of_family(a)][char]              if okay then -                pointer.large_fam = a +                setfield(pointer,"large_fam",a)              elseif a > 2 then -                pointer.large_fam = a - 3 +                setfield(pointer,"large_fam",a-3)              end          else -            pointer.small_fam = 0 -            pointer.large_fam = 0 +            setfield(pointer,"small_fam",0) +            setfield(pointer,"large_fam",0)          end      end  end @@ -332,8 +385,8 @@ local fallbackstyleattr = mathematics.fallbackstyleattr  local setnodecolor      = nodes.tracers.colors.set  local function checked(pointer) -    local char = pointer.char -    local fam = pointer.fam +    local char = getchar(pointer) +    local fam = getfield(pointer,"fam")      local id = font_of_family(fam)      local tc = fontcharacters[id]      if not tc[char] then @@ -346,27 +399,27 @@ local function checked(pointer)              if trace_analyzing then                  setnodecolor(pointer,"font:isol")              end -            pointer[a_exportstatus] = char -- testcase: exponentiale -            pointer.char = newchar +            setattr(pointer,a_exportstatus,char) -- testcase: exponentiale +            setfield(pointer,"char",newchar)              return true          end      end  end  processors.relocate[math_char] = function(pointer) -    local g = pointer[a_mathgreek] or 0 -    local a = pointer[a_mathalphabet] or 0 +    local g = getattr(pointer,a_mathgreek) or 0 +    local a = getattr(pointer,a_mathalphabet) or 0      if a > 0 or g > 0 then          if a > 0 then -            pointer[a_mathgreek] = 0 +            setattr(pointer,a_mathgreek,0)          end          if g > 0 then -            pointer[a_mathalphabet] = 0 +            setattr(pointer,a_mathalphabet,0)          end -        local char = pointer.char +        local char = getchar(pointer)          local newchar = remapalphabets(char,a,g)          if newchar then -            local fam = pointer.fam +            local fam = getfield(pointer,"fam")              local id = font_of_family(fam)              local characters = fontcharacters[id]              if characters[newchar] then @@ -376,7 +429,7 @@ processors.relocate[math_char] = function(pointer)                  if trace_analyzing then                      setnodecolor(pointer,"font:isol")                  end -                pointer.char = newchar +                setfield(pointer,"char",newchar)                  return true              else                  local fallback = fallbackstyleattr(a) @@ -390,7 +443,7 @@ processors.relocate[math_char] = function(pointer)                              if trace_analyzing then                                  setnodecolor(pointer,"font:isol")                              end -                            pointer.char = newchar +                            setfield(pointer,"char",newchar)                              return true                          elseif trace_remapping then                              report_remap("char",id,char,newchar," fails (no fallback character)") @@ -436,19 +489,19 @@ processors.render = { }  local rendersets = mathematics.renderings.numbers or { } -- store  processors.render[math_char] = function(pointer) -    local attr = pointer[a_mathrendering] +    local attr = getattr(pointer,a_mathrendering)      if attr and attr > 0 then -        local char = pointer.char +        local char = getchar(pointer)          local renderset = rendersets[attr]          if renderset then              local newchar = renderset[char]              if newchar then -                local fam = pointer.fam +                local fam = getfield(pointer,"fam")                  local id = font_of_family(fam)                  local characters = fontcharacters[id]                  if characters and characters[newchar] then -                    pointer.char = newchar -                    pointer[a_exportstatus] = char +                    setfield(pointer,"char",newchar) +                    setattr(pointer,a_exportstatus,char)                  end              end          end @@ -475,19 +528,19 @@ local mathsize = attributes.private("mathsize")  local resize = { } processors.resize = resize  resize[math_fence] = function(pointer) -    local subtype = pointer.subtype +    local subtype = getsubtype(pointer)      if subtype == left_fence_code or subtype == right_fence_code then -        local a = pointer[mathsize] +        local a = getattr(pointer,mathsize)          if a and a > 0 then              local method, size = div(a,100), a % 100 -            pointer[mathsize] = 0 -            local delimiter = pointer.delim -            local chr = delimiter.small_char +            setattr(pointer,mathsize,0) +            local delimiter = getfield(pointer,"delim") +            local chr = getfield(delimiter,"small_char")              if chr > 0 then -                local fam = delimiter.small_fam +                local fam = getfield(delimiter,"small_fam")                  local id = font_of_family(fam)                  if id > 0 then -                    delimiter.small_char = mathematics.big(fontdata[id],chr,size,method) +                    setfield(delimiter,"small_char",mathematics.big(fontdata[id],chr,size,method))                  end              end          end @@ -499,7 +552,6 @@ function handlers.resize(head,style,penalties)      return true  end -  local collapse = { } processors.collapse = collapse  local mathpairs = characters.mathpairs @@ -538,20 +590,20 @@ local validpair = {  }  local function movesubscript(parent,current_nucleus,current_char) -    local prev = parent.prev -    if prev and prev.id == math_noad then -        if not prev.sup and not prev.sub then -            current_nucleus.char = movesub[current_char or current_nucleus.char] +    local prev = getfield(parent,"prev") +    if prev and getid(prev) == math_noad then +        if not getfield(prev,"sup") and not getfield(prev,"sub") then +            setfield(current_nucleus,"char",movesub[current_char or getchar(current_nucleus)])              -- {f} {'}_n => f_n^' -            local nucleus = parent.nucleus -            local sub     = parent.sub -            local sup     = parent.sup -            prev.sup = nucleus -            prev.sub = sub +            local nucleus = getfield(parent,"nucleus") +            local sub     = getfield(parent,"sub") +            local sup     = getfield(parent,"sup") +            setfield(prev,"sup",nucleus) +            setfield(prev,"sub",sub)              local dummy = copy_node(nucleus) -            dummy.char = 0 -            parent.nucleus = dummy -            parent.sub = nil +            setfield(dummy,"char",0) +            setfield(parent,"nucleus",dummy) +            setfield(parent,"sub",nil)              if trace_collapsing then                  report_collapsing("fixing subscript")              end @@ -561,40 +613,40 @@ end  local function collapsepair(pointer,what,n,parent,nested) -- todo: switch to turn in on and off      if parent then -        if validpair[parent.subtype] then -            local current_nucleus = parent.nucleus -            if current_nucleus.id == math_char then -                local current_char = current_nucleus.char -                if not parent.sub and not parent.sup then +        if validpair[getsubtype(parent)] then +            local current_nucleus = getfield(parent,"nucleus") +            if getid(current_nucleus) == math_char then +                local current_char = getchar(current_nucleus) +                if not getfield(parent,"sub") and not getfield(parent,"sup") then                      local mathpair = mathpairs[current_char]                      if mathpair then -                        local next_noad = parent.next -                        if next_noad and next_noad.id == math_noad then -                            if validpair[next_noad.subtype] then -                                local next_nucleus = next_noad.nucleus -                                if next_nucleus.id == math_char then -                                    local next_char = next_nucleus.char +                        local next_noad = getnext(parent) +                        if next_noad and getid(next_noad) == math_noad then +                            if validpair[getsubtype(next_noad)] then +                                local next_nucleus = getfield(next_noad,"nucleus") +                                if getid(next_nucleus) == math_char then +                                    local next_char = getchar(next_nucleus)                                      local newchar = mathpair[next_char]                                      if newchar then -                                        local fam = current_nucleus.fam +                                        local fam = getfield(current_nucleus,"fam")                                          local id = font_of_family(fam)                                          local characters = fontcharacters[id]                                          if characters and characters[newchar] then                                              if trace_collapsing then                                                  report_collapsing("%U + %U => %U",current_char,next_char,newchar)                                              end -                                            current_nucleus.char = newchar -                                            local next_next_noad = next_noad.next +                                            setfield(current_nucleus,"char",newchar) +                                            local next_next_noad = getnext(next_noad)                                              if next_next_noad then -                                                parent.next = next_next_noad -                                                next_next_noad.prev = parent +                                                setfield(parent,"next",next_next_noad) +                                                setfield(next_next_noad,"prev",parent)                                              else -                                                parent.next = nil +                                                setfield(parent,"next",nil)                                              end -                                            parent.sup = next_noad.sup -                                            parent.sub = next_noad.sub -                                            next_noad.sup = nil -                                            next_noad.sub = nil +                                            setfield(parent,"sup",getfield(next_noad,"sup")) +                                            setfield(parent,"sub",getfield(next_noad,"sub")) +                                            setfield(next_noad,"sup",nil) +                                            setfield(next_noad,"sub",nil)                                              free_node(next_noad)                                              collapsepair(pointer,what,n,parent,true)                                              if not nested and movesub[current_char] then @@ -634,13 +686,13 @@ local replaced = { }  local function replace(pointer,what,n,parent)      pointer = parent -- we're following the parent list (chars trigger this) -    local next = pointer.next +    local next = getnext(pointer)      local start_super, stop_super, start_sub, stop_sub      local mode = "unset" -    while next and next.id == math_noad do -        local nextnucleus = next.nucleus -        if nextnucleus and nextnucleus.id == math_char and not next.sub and not next.sup then -            local char = nextnucleus.char +    while next and getid(next) == math_noad do +        local nextnucleus = getfield(next,"nucleus") +        if nextnucleus and getid(nextnucleus) == math_char and not getfield(next,"sub") and not getfield(next,"sup") then +            local char = getchar(nextnucleus)              local s = superscripts[char]              if s then                  if not start_super then @@ -650,8 +702,8 @@ local function replace(pointer,what,n,parent)                      break                  end                  stop_super = next -                next = next.next -                nextnucleus.char = s +                next = getnext(next) +                setfield(nextnucleus,"char",s)                  replaced[char] = (replaced[char] or 0) + 1                  if trace_normalizing then                      report_normalizing("superscript %C becomes %C",char,s) @@ -666,8 +718,8 @@ local function replace(pointer,what,n,parent)                          break                      end                      stop_sub = next -                    next = next.next -                    nextnucleus.char = s +                    next = getnext(next) +                    setfield(nextnucleus,"char",s)                      replaced[char] = (replaced[char] or 0) + 1                      if trace_normalizing then                          report_normalizing("subscript %C becomes %C",char,s) @@ -682,29 +734,29 @@ local function replace(pointer,what,n,parent)      end      if start_super then          if start_super == stop_super then -            pointer.sup = start_super.nucleus +            setfield(pointer,"sup",getfield(start_super,"nucleus"))          else              local list = new_node(math_sub) -- todo attr -            list.head = start_super -            pointer.sup = list +            setfield(list,"list",start_super) +            setfield(pointer,"sup",list)          end          if mode == "super" then -            pointer.next = stop_super.next +            setfield(pointer,"next",getnext(stop_super))          end -        stop_super.next = nil +        setfield(stop_super,"next",nil)      end      if start_sub then          if start_sub == stop_sub then -            pointer.sub = start_sub.nucleus +            setfield(pointer,"sub",getfield(start_sub,"nucleus"))          else              local list = new_node(math_sub) -- todo attr -            list.head = start_sub -            pointer.sub = list +            setfield(list,"list",start_sub) +            setfield(pointer,"sub",list)          end          if mode == "sub" then -            pointer.next = stop_sub.next +            setfield(pointer,"next",getnext(stop_sub))          end -        stop_sub.next = nil +        setfield(stop_sub,"next",nil)      end      -- we could return stop  end @@ -785,20 +837,20 @@ function mathematics.setalternate(fam,tag)  end  alternate[math_char] = function(pointer) -    local a = pointer[a_mathalternate] +    local a = getattr(pointer,a_mathalternate)      if a and a > 0 then -        pointer[a_mathalternate] = 0 -        local tfmdata = fontdata[font_of_family(pointer.fam)] -- we can also have a famdata +        setattr(pointer,a_mathalternate,0) +        local tfmdata = fontdata[font_of_family(getfield(pointer,"fam"))] -- we can also have a famdata          local mathalternatesattributes = tfmdata.shared.mathalternatesattributes          if mathalternatesattributes then              local what = mathalternatesattributes[a] -            local alt = getalternate(tfmdata,pointer.char,what.feature,what.value) +            local alt = getalternate(tfmdata,getchar(pointer),what.feature,what.value)              if alt then                  if trace_alternates then                      report_alternates("alternate %a, value %a, replacing glyph %U by glyph %U", -                        tostring(what.feature),tostring(what.value),pointer.char,alt) +                        tostring(what.feature),tostring(what.value),getchar(pointer),alt)                  end -                pointer.char = alt +                setfield(pointer,"char",alt)              end          end      end @@ -885,13 +937,14 @@ end  local function insert_kern(current,kern)      local sub  = new_node(math_sub) -- todo: pool      local noad = new_node(math_noad) -- todo: pool -    sub.head = kern -    kern.next = noad -    noad.nucleus = current +    setfield(sub,"list",kern) +    setfield(kern,"next",noad) +    setfield(noad,"nucleus",current)      return sub  end  local setcolor     = nodes.tracers.colors.set +local resetcolor   = nodes.tracers.colors.reset  local italic_kern  = new_kern  local c_positive_d = "trace:db"  local c_negative_d = "trace:dr" @@ -913,44 +966,44 @@ trackers.register("math.italics", function(v)  end)  italics[math_char] = function(pointer,what,n,parent) -    local method = pointer[a_mathitalics] +    local method = getattr(pointer,a_mathitalics)      if method and method > 0 then -        local char = pointer.char -        local font = font_of_family(pointer.fam) -- todo: table +        local char = getchar(pointer) +        local font = font_of_family(getfield(pointer,"fam")) -- todo: table          local correction, visual = getcorrection(method,font,char)          if correction then -            local pid = parent.id +            local pid = getid(parent)              local sub, sup              if pid == math_noad then -                sup = parent.sup -                sub = parent.sub +                sup = getfield(parent,"sup") +                sub = getfield(parent,"sub")              end              if sup or sub then -                local subtype = parent.subtype +                local subtype = getsubtype(parent)                  if subtype == noad_oplimits then                      if sup then -                        parent.sup = insert_kern(sup,italic_kern(correction,font)) +                        setfield(parent,"sup",insert_kern(sup,italic_kern(correction,font)))                          if trace_italics then                              report_italics("method %a, adding %p italic correction for upper limit of %C",method,correction,char)                          end                      end                      if sub then                          local correction = - correction -                        parent.sub = insert_kern(sub,italic_kern(correction,font)) +                        setfield(parent,"sub",insert_kern(sub,italic_kern(correction,font)))                          if trace_italics then                              report_italics("method %a, adding %p italic correction for lower limit of %C",method,correction,char)                          end                      end                  else                      if sup then -                        parent.sup = insert_kern(sup,italic_kern(correction,font)) +                        setfield(parent,"sup",insert_kern(sup,italic_kern(correction,font)))                          if trace_italics then                              report_italics("method %a, adding %p italic correction before superscript after %C",method,correction,char)                          end                      end                  end              else -                local next_noad = parent.next +                local next_noad = getnext(parent)                  if not next_noad then                      if n== 1 then -- only at the outer level .. will become an option (always,endonly,none)                          if trace_italics then @@ -958,12 +1011,12 @@ italics[math_char] = function(pointer,what,n,parent)                          end                          insert_node_after(parent,parent,italic_kern(correction,font))                      end -                elseif next_noad.id == math_noad then -                    local next_subtype = next_noad.subtype +                elseif getid(next_noad) == math_noad then +                    local next_subtype = getsubtype(next_noad)                      if next_subtype == noad_punct or next_subtype == noad_ord then -                        local next_nucleus = next_noad.nucleus -                        if next_nucleus.id == math_char then -                            local next_char = next_nucleus.char +                        local next_nucleus = getfield(next_noad,"nucleus") +                        if getid(next_nucleus) == math_char then +                            local next_char = getchar(next_nucleus)                              local next_data = chardata[next_char]                              local visual = next_data.visual                              if visual == "it" or visual == "bi" then @@ -1047,15 +1100,15 @@ local validvariants = { -- fast check on valid  }  variants[math_char] = function(pointer,what,n,parent) -- also set export value -    local char = pointer.char +    local char = getchar(pointer)      local selector = validvariants[char]      if selector then -        local next = parent.next -        if next and next.id == math_noad then -            local nucleus = next.nucleus -            if nucleus and nucleus.id == math_char and nucleus.char == selector then +        local next = getnext(parent) +        if next and getid(next) == math_noad then +            local nucleus = getfield(next,"nucleus") +            if nucleus and getid(nucleus) == math_char and getchar(nucleus) == selector then                  local variant -                local tfmdata = fontdata[font_of_family(pointer.fam)] -- we can also have a famdata +                local tfmdata = fontdata[font_of_family(getfield(pointer,"fam"))] -- we can also have a famdata                  local mathvariants = tfmdata.resources.variants -- and variantdata                  if mathvariants then                      mathvariants = mathvariants[selector] @@ -1064,8 +1117,8 @@ variants[math_char] = function(pointer,what,n,parent) -- also set export value                      end                  end                  if variant then -                    pointer.char = variant -                    pointer[a_exportstatus] = char -- we don't export the variant as it's visual markup +                    setfield(pointer,"char",variant) +                    setattr(pointer,a_exportstatus,char) -- we don't export the variant as it's visual markup                      if trace_variants then                          report_variants("variant (%U,%U) replaced by %U",char,selector,variant)                      end @@ -1074,8 +1127,8 @@ variants[math_char] = function(pointer,what,n,parent) -- also set export value                          report_variants("no variant (%U,%U)",char,selector)                      end                  end -                next.prev = pointer -                parent.next = next.next +                setfield(next,"prev",pointer) +                setfield(parent,"next",getnext(next))                  free_node(next)              end          end @@ -1108,7 +1161,7 @@ local colors = {  }  classes[math_char] = function(pointer,what,n,parent) -    local color = colors[parent.subtype] +    local color = colors[getsubtype(parent)]      if color then          setcolor(pointer,color)      else diff --git a/tex/context/base/math-tag.lua b/tex/context/base/math-tag.lua index ab5902dd4..3cd4cae16 100644 --- a/tex/context/base/math-tag.lua +++ b/tex/context/base/math-tag.lua @@ -11,10 +11,22 @@ if not modules then modules = { } end modules ['math-tag'] = {  local find, match = string.find, string.match  local insert, remove = table.insert, table.remove -local attributes, nodes = attributes, nodes +local attributes          = attributes +local nodes               = nodes -local set_attributes      = nodes.setattributes -local traverse_nodes      = node.traverse +local nuts                = nodes.nuts +local tonut               = nuts.tonut + +local getnext             = nuts.getnext +local getid               = nuts.getid +local getchar             = nuts.getchar +local getlist             = nuts.getlist +local getfield            = nuts.getfield +local getattr             = nuts.getattr +local setattr             = nuts.setattr + +local set_attributes      = nuts.setattributes +local traverse_nodes      = nuts.traverse  local nodecodes           = nodes.nodecodes @@ -61,22 +73,24 @@ local function processsubsup(start)      -- At some point we might need to add an attribute signaling the      -- super- and subscripts because TeX and MathML use a different      -- order. -    local nucleus, sup, sub = start.nucleus, start.sup, start.sub +    local nucleus = getfield(start,"nucleus") +    local sup     = getfield(start,"sup") +    local sub     = getfield(start,"sub")      if sub then          if sup then -            start[a_tagged] = start_tagged("msubsup") +            setattr(start,a_tagged,start_tagged("msubsup"))              process(nucleus)              process(sub)              process(sup)              stop_tagged()          else -            start[a_tagged] = start_tagged("msub") +            setattr(start,a_tagged,start_tagged("msub"))              process(nucleus)              process(sub)              stop_tagged()          end      elseif sup then -        start[a_tagged] = start_tagged("msup") +        setattr(start,a_tagged,start_tagged("msup"))          process(nucleus)          process(sup)          stop_tagged() @@ -93,11 +107,11 @@ local actionstack = { }  process = function(start) -- we cannot use the processor as we have no finalizers (yet)      while start do -        local id = start.id +        local id = getid(start)          if id == math_char_code then -            local char = start.char +            local char = getchar(start)              -- check for code -            local a = start[a_mathcategory] +            local a = getattr(start,a_mathcategory)              if a then                  a = { detail = a }              end @@ -119,22 +133,22 @@ process = function(start) -- we cannot use the processor as we have no finalizer              else                  tag = "mo"              end -            start[a_tagged] = start_tagged(tag,a) +            setattr(start,a_tagged,start_tagged(tag,a))              stop_tagged()              break -- okay?          elseif id == math_textchar_code then              -- check for code -            local a = start[a_mathcategory] +            local a = getattr(start,a_mathcategory)              if a then -                start[a_tagged] = start_tagged("ms",{ detail = a }) +                setattr(start,a_tagged,start_tagged("ms",{ detail = a }))              else -                start[a_tagged] = start_tagged("ms") +                setattr(start,a_tagged,start_tagged("ms"))              end              stop_tagged()              break          elseif id == math_delim_code then              -- check for code -            start[a_tagged] = start_tagged("mo") +            setattr(start,a_tagged,start_tagged("mo"))              stop_tagged()              break          elseif id == math_style_code then @@ -143,14 +157,14 @@ process = function(start) -- we cannot use the processor as we have no finalizer              processsubsup(start)          elseif id == math_box_code or id == hlist_code or id == vlist_code then              -- keep an eye on math_box_code and see what ends up in there -            local attr = start[a_tagged] +            local attr = getattr(start,a_tagged)              local last = attr and taglist[attr]              if last and find(last[#last],"formulacaption[:%-]") then                  -- leave alone, will nicely move to the outer level              else                  local text = start_tagged("mtext") -                start[a_tagged] = text -                local list = start.list +                setattr(start,a_tagged,text) +                local list = getfield(start,"list")                  if not list then                      -- empty list                  elseif not attr then @@ -166,8 +180,8 @@ process = function(start) -- we cannot use the processor as we have no finalizer                      local function runner(list) -- quite inefficient                          local cache = { } -- we can have nested unboxed mess so best local to runner                          for n in traverse_nodes(list) do -                            local id = n.id -                            local aa = n[a_tagged] +                            local id = getid(n) +                            local aa = getattr(n,a_tagged)                              if aa then                                  local ac = cache[aa]                                  if not ac then @@ -185,12 +199,12 @@ process = function(start) -- we cannot use the processor as we have no finalizer                                      end                                      cache[aa] = ac                                  end -                                n[a_tagged] = ac +                                setattr(n,a_tagged,ac)                              else -                                n[a_tagged] = text +                                setattr(n,a_tagged,text)                              end                              if id == hlist_code or id == vlist_code then -                                runner(n.list) +                                runner(getlist(n))                              end                          end                      end @@ -199,47 +213,53 @@ process = function(start) -- we cannot use the processor as we have no finalizer                  stop_tagged()              end          elseif id == math_sub_code then -            local list = start.list +            local list = getfield(start,"list")              if list then -                local attr = start[a_tagged] +                local attr = getattr(start,a_tagged)                  local last = attr and taglist[attr]                  local action = last and match(last[#last],"maction:(.-)%-")                  if action and action ~= "" then                      if actionstack[#actionstack] == action then -                        start[a_tagged] = start_tagged("mrow") +                        setattr(start,a_tagged,start_tagged("mrow"))                          process(list)                          stop_tagged()                      else                          insert(actionstack,action) -                        start[a_tagged] = start_tagged("mrow",{ detail = action }) +                        setattr(start,a_tagged,start_tagged("mrow",{ detail = action }))                          process(list)                          stop_tagged()                          remove(actionstack)                      end                  else -                    start[a_tagged] = start_tagged("mrow") +                    setattr(start,a_tagged,start_tagged("mrow"))                      process(list)                      stop_tagged()                  end              end          elseif id == math_fraction_code then -            local num, denom, left, right = start.num, start.denom, start.left, start.right +            local num   = getfield(start,"num") +            local denom = getfield(start,"denom") +            local left  = getfield(start,"left") +            local right = getfield(start,"right")              if left then -               left[a_tagged] = start_tagged("mo") +               setattr(left,a_tagged,start_tagged("mo"))                 process(left)                 stop_tagged()              end -            start[a_tagged] = start_tagged("mfrac") +            setattr(start,a_tagged,start_tagged("mfrac"))              process(num)              process(denom)              stop_tagged()              if right then -                right[a_tagged] = start_tagged("mo") +                setattr(right,a_tagged,start_tagged("mo"))                  process(right)                  stop_tagged()              end          elseif id == math_choice_code then -            local display, text, script, scriptscript = start.display, start.text, start.script, start.scriptscript +            local display      = getfield(start,"display") +            local text         = getfield(start,"text") +            local script       = getfield(start,"script") +            local scriptscript = getfield(start,"scriptscript")              if display then                  process(display)              end @@ -253,67 +273,69 @@ process = function(start) -- we cannot use the processor as we have no finalizer                  process(scriptscript)              end          elseif id == math_fence_code then -            local delim = start.delim -            local subtype = start.subtype +            local delim   = getfield(start,"delim") +            local subtype = getfield(start,"subtype") +         -- setattr(start,a_tagged,start_tagged("mfenced")) -- needs checking              if subtype == 1 then                  -- left -                start[a_tagged] = start_tagged("mfenced")                  if delim then -                    start[a_tagged] = start_tagged("mleft") +                    setattr(start,a_tagged,start_tagged("mleft"))                      process(delim)                      stop_tagged()                  end              elseif subtype == 2 then                  -- middle                  if delim then -                    start[a_tagged] = start_tagged("mmiddle") +                    setattr(start,a_tagged,start_tagged("mmiddle"))                      process(delim)                      stop_tagged()                  end              elseif subtype == 3 then                  if delim then -                    start[a_tagged] = start_tagged("mright") +                    setattr(start,a_tagged,start_tagged("mright"))                      process(delim)                      stop_tagged()                  end -                stop_tagged()              else                  -- can't happen              end +         -- stop_tagged()          elseif id == math_radical_code then -            local left, degree = start.left, start.degree +            local left   = getfield(start,"left") +            local degree = getfield(start,"degree")              if left then                  start_tagged("")                  process(left) -- root symbol, ignored                  stop_tagged()              end              if degree then -- not good enough, can be empty mlist -                start[a_tagged] = start_tagged("mroot") +                setattr(start,a_tagged,start_tagged("mroot"))                  processsubsup(start)                  process(degree)                  stop_tagged()              else -                start[a_tagged] = start_tagged("msqrt") +                setattr(start,a_tagged,start_tagged("msqrt"))                  processsubsup(start)                  stop_tagged()              end          elseif id == math_accent_code then -            local accent, bot_accent = start.accent, start.bot_accent +            local accent     = getfield(start,"accent") +            local bot_accent = getfield(start,"bot_accent")              if bot_accent then                  if accent then -                    start[a_tagged] = start_tagged("munderover",{ detail = "accent" }) +                    setattr(start,a_tagged,start_tagged("munderover",{ detail = "accent" }))                      processsubsup(start)                      process(bot_accent)                      process(accent)                      stop_tagged()                  else -                    start[a_tagged] = start_tagged("munder",{ detail = "accent" }) +                    setattr(start,a_tagged,start_tagged("munder",{ detail = "accent" }))                      processsubsup(start)                      process(bot_accent)                      stop_tagged()                  end              elseif accent then -                start[a_tagged] = start_tagged("mover",{ detail = "accent" }) +                setattr(start,a_tagged,start_tagged("mover",{ detail = "accent" }))                  processsubsup(start)                  process(accent)                  stop_tagged() @@ -321,22 +343,23 @@ process = function(start) -- we cannot use the processor as we have no finalizer                  processsubsup(start)              end          elseif id == glue_code then -            start[a_tagged] = start_tagged("mspace") +            setattr(start,a_tagged,start_tagged("mspace"))              stop_tagged()          else -            start[a_tagged] = start_tagged("merror", { detail = nodecodes[i] }) +            setattr(start,a_tagged,start_tagged("merror", { detail = nodecodes[i] }))              stop_tagged()          end -        start = start.next +        start = getnext(start)      end  end  function noads.handlers.tags(head,style,penalties) +    head = tonut(head)      local v_math = start_tagged("math")      local v_mrow = start_tagged("mrow") -    local v_mode = head[a_mathmode] -    head[a_tagged] = v_math -    head[a_tagged] = v_mrow +    local v_mode = getattr(head,a_mathmode) + -- setattr(head,a_tagged,v_math) +    setattr(head,a_tagged,v_mrow)      tags.setattributehash(v_math,"mode",v_mode == 1 and "display" or "inline")      process(head)      stop_tagged() diff --git a/tex/context/base/node-acc.lua b/tex/context/base/node-acc.lua index 81ae496b2..6a1b986bc 100644 --- a/tex/context/base/node-acc.lua +++ b/tex/context/base/node-acc.lua @@ -11,10 +11,25 @@ local nodes, node = nodes, node  local nodecodes      = nodes.nodecodes  local tasks          = nodes.tasks -local traverse_nodes = node.traverse -local traverse_id    = node.traverse_id -local copy_node      = node.copy -local free_nodelist  = node.flush_list +local nuts           = nodes.nuts +local tonut          = nodes.tonut +local tonode         = nodes.tonode + +local getid          = nuts.getid +local getfield       = nuts.getfield +local getattr        = nuts.getattr +local getlist        = nuts.getlist +local getchar        = nuts.getchar +local getnext        = nuts.getnext + +local setfield       = nuts.setfield +local setattr        = nuts.setattr + +local traverse_nodes = nuts.traverse +local traverse_id    = nuts.traverse_id +local copy_node      = nuts.copy +local free_nodelist  = nuts.flush_list +local insert_after   = nuts.insert_after  local glue_code      = nodecodes.glue  local kern_code      = nodecodes.kern @@ -29,57 +44,68 @@ local threshold      = 65536  -- todo: nbsp etc  -- todo: collapse kerns +-- p_id +  local function injectspaces(head) -    local p +    local p, p_id      local n = head      while n do -        local id = n.id +        local id = getid(n)          if id == glue_code then -- todo: check for subtype related to spacing (13/14 but most seems to be 0) -       -- if n.spec.width > 0 then -- threshold -            if p and p.id == glyph_code then +       -- if getfield(getfield(n,"spec"),"width") > 0 then -- threshold +            if p and p_id == glyph_code then                  local g = copy_node(p) -                local c = g.components +                local c = getfield(g,"components")                  if c then -- it happens that we copied a ligature                      free_nodelist(c) -                    g.components = nil -                    g.subtype = 256 +                    setfield(g,"components",nil) +                    setfield(g,"subtype",256)                  end -                local a = n[a_characters] -                local s = copy_node(n.spec) -                g.char, n.spec = 32, s -                p.next, g.prev = g, p -                g.next, n.prev = n, g -                s.width = s.width - g.width +                -- p .. g +                local a = getattr(n,a_characters) +                local s = copy_node(getfield(n,"spec")) +                setfield(g,"char",32) +                insert_after(p,p,g) +             -- setfield(p,"next",g) +             -- setfield(g,"prev",p) +             -- setfield(g,"next",n) +             -- setfield(n,"prev",g) +                setfield(n,"spec",s) +                setfield(s,"width",getfield(s,"width") - getfield(g,"width"))                  if a then -                    g[a_characters] = a +                    setattr(g,a_characters,a)                  end -                s[a_characters] = 0 -                n[a_characters] = 0 +                setattr(s,a_characters,0) +                setattr(n,a_characters,0)              end         -- end          elseif id == hlist_code or id == vlist_code then -            injectspaces(n.list,attribute) +            injectspaces(getlist(n),attribute)       -- elseif id == kern_code then -- the backend already collapses       --     local first = n       --     while true do -     --         local nn = n.next -     --         if nn and nn.id == kern_code then +     --         local nn = getnext(n) +     --         if nn and getid(nn) == kern_code then       --          -- maybe we should delete kerns but who cares at this stage -     --             first.kern = first.kern + nn.kern -     --             nn.kern = 0 +     --             setfield(first,"kern",getfield(first,"kern") + getfield(nn,"kern") +     --             setfield(nn,"kern",0)       --             n = nn       --         else       --             break       --         end       --     end          end +        p_id = id          p = n -        n = n.next +        n = getnext(n)      end -    return head, true +    return head, true -- always done anyway  end -nodes.handlers.accessibility = injectspaces +nodes.handlers.accessibility = function(head) +    local head, done = injectspaces(tonut(head)) +    return tonode(head), done +end  -- todo: @@ -90,16 +116,18 @@ nodes.handlers.accessibility = injectspaces  -- local function compact(n)  --     local t = { }  --     for n in traverse_id(glyph_code,n) do ---         t[#t+1] = utfchar(n.char) -- check for unicode +--         t[#t+1] = utfchar(getchar(n)) -- check for unicode  --     end  --     return concat(t,"")  -- end  --  -- local function injectspans(head) ---     for n in traverse_nodes(head) do ---         local id = n.id +--     local done = false +--     for n in traverse_nodes(tonuts(head)) do +--         local id = getid(n)  --         if id == disc then ---             local r, p = n.replace, n.pre +--             local r = getfield(n,"replace") +--             local p = getfield(n,"pre")  --             if r and p then  --                 local str = compact(r)  --                 local hsh = hyphenated[str] @@ -108,13 +136,14 @@ nodes.handlers.accessibility = injectspaces  --                     hyphenated[str] = hsh  --                     codes[hsh] = str  --                 end ---                 n[a_hyphenated] = hsh +--                 setattr(n,a_hyphenated,hsh) +--                 done = true  --             end  --         elseif id == hlist_code or id == vlist_code then ---             injectspans(n.list) +--             injectspans(getlist(n))  --         end  --     end ---     return head, true +--     return tonodes(head), done  -- end  --  -- nodes.injectspans = injectspans @@ -122,19 +151,22 @@ nodes.handlers.accessibility = injectspaces  -- tasks.appendaction("processors", "words", "nodes.injectspans")  --  -- local function injectspans(head) ---     for n in traverse_nodes(head) do ---         local id = n.id +--     local done = false +--     for n in traverse_nodes(tonut(head)) do +--         local id = getid(n)  --         if id == disc then ---             local a = n[a_hyphenated] +--             local a = getattr(n,a_hyphenated)  --             if a then  --                 local str = codes[a]  --                 local b = new_pdfliteral(format("/Span << /ActualText %s >> BDC", lpdf.tosixteen(str)))  --                 local e = new_pdfliteral("EMC") ---                 node.insert_before(head,n,b) ---                 node.insert_after(head,n,e) +--                 insert_before(head,n,b) +--                 insert_after(head,n,e) +--                 done = true  --             end  --         elseif id == hlist_code or id == vlist_code then ---             injectspans(n.list) +--             injectspans(getlist(n))  --         end  --     end +--     return tonodes(head), done  -- end diff --git a/tex/context/base/node-aux.lua b/tex/context/base/node-aux.lua index 443c78547..7eb51e5b4 100644 --- a/tex/context/base/node-aux.lua +++ b/tex/context/base/node-aux.lua @@ -22,82 +22,105 @@ local vlist_code         = nodecodes.vlist  local attributelist_code = nodecodes.attributelist -- temporary  local math_code          = nodecodes.math -local nodepool           = nodes.pool - +local nuts               = nodes.nuts +local tonut              = nuts.tonut +local tonode             = nuts.tonode +local vianuts            = nuts.vianuts + +local getbox             = nuts.getbox +local getnext            = nuts.getnext +local getid              = nuts.getid +local getsubtype         = nuts.getsubtype +local getlist            = nuts.getlist +local getfont            = nuts.getfont +local getchar            = nuts.getchar +local getattr            = nuts.getattr +local setfield           = nuts.setfield +local setattr            = nuts.setattr + +local traverse_nodes     = nuts.traverse +local traverse_id        = nuts.traverse_id +local free_node          = nuts.free +local hpack_nodes        = nuts.hpack +local unset_attribute    = nuts.unset_attribute +local first_glyph        = nuts.first_glyph +local copy_node          = nuts.copy +local copy_node_list     = nuts.copy_list +local slide_nodes        = nuts.slide +local insert_node_after  = nuts.insert_after +local isnode             = nuts.is_node + +local nodepool           = nuts.pool  local new_glue           = nodepool.glue  local new_glyph          = nodepool.glyph -local traverse_nodes     = node.traverse -local traverse_id        = node.traverse_id -local free_node          = node.free -local hpack_nodes        = node.hpack -local unset_attribute    = node.unset_attribute -local first_glyph        = node.first_glyph or node.first_character -local copy_node          = node.copy -local copy_node_list     = node.copy_list -local slide_nodes        = node.slide -local insert_node_after  = node.insert_after -local isnode             = node.is_node -  local unsetvalue         = attributes.unsetvalue  local current_font       = font.current -local texgetbox          = tex.getbox -  local report_error       = logs.reporter("node-aux:error") -function nodes.repackhlist(list,...) ---~ nodes.showsimplelist(list) +local function repackhlist(list,...)      local temp, b = hpack_nodes(list,...) -    list = temp.list -    temp.list = nil +    list = getlist(temp) +    setfield(temp,"list",nil)      free_node(temp)      return list, b  end +nuts.repackhlist = repackhlist + +function nodes.repackhlist(list,...) +    local list, b = repackhlist(tonut(list),...) +    return tonode(list), b +end +  local function set_attributes(head,attr,value)      for n in traverse_nodes(head) do -        n[attr] = value -        local id = n.id +        setattr(n,attr,value) +        local id = getid(n)          if id == hlist_node or id == vlist_node then -            set_attributes(n.list,attr,value) +            set_attributes(getlist(n),attr,value)          end      end  end  local function set_unset_attributes(head,attr,value)      for n in traverse_nodes(head) do -        if not n[attr] then -            n[attr] = value +        if not getattr(n,attr) then +            setattr(n,attr,value)          end -        local id = n.id +        local id = getid(n)          if id == hlist_code or id == vlist_code then -            set_unset_attributes(n.list,attr,value) +            set_unset_attributes(getlist(n),attr,value)          end      end  end  local function unset_attributes(head,attr)      for n in traverse_nodes(head) do -        n[attr] = unsetvalue -        local id = n.id +        setattr(n,attr,unsetvalue) +        local id = getid(n)          if id == hlist_code or id == vlist_code then -            unset_attributes(n.list,attr) +            unset_attributes(getlist(n),attr)          end      end  end -nodes.setattribute       = node.set_attribute -nodes.getattribute       = node.has_attribute -nodes.unsetattribute     = node.unset_attribute -nodes.has_attribute      = node.has_attribute +-- for old times sake + +nuts.setattribute        = nuts.setattr                      nodes.setattribute       = nodes.setattr +nuts.getattribute        = nuts.getattr                      nodes.getattribute       = nodes.getattr +nuts.unsetattribute      = nuts.unset_attribute              nodes.unsetattribute     = nodes.unset_attribute +nuts.has_attribute       = nuts.has_attribute                nodes.has_attribute      = nodes.has_attribute +nuts.firstglyph          = nuts.first_glyph                  nodes.firstglyph         = nodes.first_glyph -nodes.firstglyph         = first_glyph -nodes.setattributes      = set_attributes -nodes.setunsetattributes = set_unset_attributes -nodes.unsetattributes    = unset_attributes +nuts.setattributes       = set_attributes                    nodes.setattributes      = vianuts(set_attributes) +nuts.setunsetattributes  = set_unset_attributes              nodes.setunsetattributes = vianuts(set_unset_attributes) +nuts.unsetattributes     = unset_attributes                  nodes.unsetattributes    = vianuts(unset_attributes) +-- history: +--  -- function nodes.is_skipable(a,id)  -- skipable nodes at the margins during character protrusion  --     return (  --             id ~= glyph_node @@ -106,29 +129,26 @@ nodes.unsetattributes    = unset_attributes  --         or  id == adjust_node  --         or  id == penalty_node  --         or (id == glue_node    and a.spec.writable) ---         or (id == disc_node    and a.pre == nil and a.post == nil and a.replace == nil) ---         or (id == math_node    and a.surround == 0) ---         or (id == kern_node    and (a.kern == 0 or a.subtype == NORMAL)) ---         or (id == hlist_node   and a.width == 0 and a.height == 0 and a.depth == 0 and a.list == nil) ---         or (id == whatsit_node and a.subtype ~= pdf_refximage_node and a.subtype ~= pdf_refxform_node) +--         or (id == disc_node    and getfield(a,"pre") == nil and getfield(a,"post") == nil and getfield(a,"replace") == nil) +--         or (id == math_node    and getfield(a,"surround") == 0) +--         or (id == kern_node    and (getfield(a,"kern") == 0 or getsubtype(subtype) == NORMAL)) +--         or (id == hlist_node   and getfield(a,"width") == 0 and getfield(a,"height") == 0 and getfield(a,"depth") == 0 and getlist(a) == nil) +--         or (id == whatsit_node and getsubtype(a) ~= pdf_refximage_node and getsubtype(a) ~= pdf_refxform_node)  --     )  -- end - --- history: ---  --  -- local function glyph_width(a) ---     local ch = chardata[a.font][a.char] +--     local ch = chardata[getfont(a)][getchar(a)]  --     return (ch and ch.width) or 0  -- end  --  -- local function glyph_total(a) ---     local ch = chardata[a.font][a.char] +--     local ch = chardata[getfont(a)][getchar(a)]  --     return (ch and (ch.height+ch.depth)) or 0  -- end  --  -- local function non_discardable(a) -- inline ---     return a.id < math_node -- brrrr +--     return getid(id) < math_node -- brrrr  -- end  --  -- local function calculate_badness(t,s) @@ -183,8 +203,36 @@ nodes.unsetattributes    = unset_attributes  --         return -u  --     end  -- end +-- +-- if not node.end_of_math then +--     function node.end_of_math(n) +--         for n in traverse_id(math_code,getnext(next)) do +--             return n +--         end +--     end +-- end +-- +-- nodes.endofmath = node.end_of_math +-- +-- local function firstline(n) +--     while n do +--         local id = getid(n) +--         if id == hlist_code then +--             if getsubtype(n) == line_code then +--                 return n +--             else +--                 return firstline(getlist(n)) +--             end +--         elseif id == vlist_code then +--             return firstline(getlist(n)) +--         end +--         n = getnext(n) +--     end +-- end +-- +-- nodes.firstline = firstline -function nodes.firstcharacter(n,untagged) -- tagged == subtype > 255 +local function firstcharacter(n,untagged) -- tagged == subtype > 255      if untagged then          return first_glyph(n)      else @@ -194,43 +242,18 @@ function nodes.firstcharacter(n,untagged) -- tagged == subtype > 255      end  end -function nodes.firstcharinbox(n) -    local l = texgetbox(n).list +local function firstcharinbox(n) +    local l = getlist(getbox(n))      if l then          for g in traverse_id(glyph_code,l) do -            return g.char +            return getchar(g)          end      end      return 0  end -if not node.end_of_math then -    function node.end_of_math(n) -        for n in traverse_id(math_code,n.next) do -            return n -        end -    end -end - -nodes.endofmath = node.end_of_math - --- local function firstline(n) ---     while n do ---         local id = n.id ---         if id == hlist_code then ---             if n.subtype == line_code then ---                 return n ---             else ---                 return firstline(n.list) ---             end ---         elseif id == vlist_code then ---             return firstline(n.list) ---         end ---         n = n.next ---     end --- end - --- nodes.firstline = firstline +nuts.firstcharacter = firstcharacter  nodes.firstcharacter = vianuts(firstcharacter) +nuts.firstcharinbox = firstcharinbox  nodes.firstcharinbox = vianuts(firstcharinbox)  -- this depends on fonts, so we have a funny dependency ... will be  -- sorted out .. we could make tonodes a plugin into this @@ -242,10 +265,8 @@ local function tonodes(str,fnt,attr) -- (str,template_glyph) -- moved from blob-      local head, tail, space, fnt, template = nil, nil, nil, nil, nil      if not fnt then          fnt = current_font() -    elseif type(fnt) ~= "number" and fnt.id == "glyph" then -        fnt, template = nil, fnt - -- else -     -- already a number +    elseif type(fnt) ~= "number" and getid(fnt) == glyph_code then -- so it has to be a real node +        fnt, template = nil, tonut(fnt)      end      for s in utfvalues(str) do          local n @@ -259,12 +280,12 @@ local function tonodes(str,fnt,attr) -- (str,template_glyph) -- moved from blob-              end          elseif template then              n = copy_node(template) -            n.char = s +            setvalue(n,"char",s)          else              n = new_glyph(fnt,s)          end          if attr then -- normally false when template -            n.attr = copy_node_list(attr) +            setfield(n,"attr",copy_node_list(attr))          end          if head then              insert_node_after(head,tail,n) @@ -276,68 +297,129 @@ local function tonodes(str,fnt,attr) -- (str,template_glyph) -- moved from blob-      return head, tail  end -nodes.tonodes = tonodes +nuts.tonodes = tonodes + +nodes.tonodes = function(str,fnt,attr) +    local head, tail = tonodes(str,fnt,attr) +    return tonode(head), tonode(tail) +end + +-- local function link(list,currentfont,currentattr,head,tail) +--     for i=1,#list do +--         local n = list[i] +--         if n then +--             local tn = isnode(n) +--             if not tn then +--                 local tn = type(n) +--                 if tn == "number" then +--                     if not currentfont then +--                         currentfont = current_font() +--                     end +--                     local h, t = tonodes(tostring(n),currentfont,currentattr) +--                     if not h then +--                         -- skip +--                     elseif not head then +--                         head = h +--                         tail = t +--                     else +--                         setfield(tail,"next",h) +--                         setfield(h,"prev",t) +--                         tail = t +--                     end +--                 elseif tn == "string" then +--                     if #tn > 0 then +--                         if not currentfont then +--                             currentfont = current_font() +--                         end +--                         local h, t = tonodes(n,currentfont,currentattr) +--                         if not h then +--                             -- skip +--                         elseif not head then +--                             head, tail = h, t +--                         else +--                             setfield(tail,"next",h) +--                             setfield(h,"prev",t) +--                             tail = t +--                         end +--                     end +--                 elseif tn == "table" then +--                     if #tn > 0 then +--                         if not currentfont then +--                             currentfont = current_font() +--                         end +--                         head, tail = link(n,currentfont,currentattr,head,tail) +--                     end +--                 end +--             elseif not head then +--                 head = n +--                 tail = slide_nodes(n) +--             elseif getid(n) == attributelist_code then +--                 -- weird case +--                 report_error("weird node type in list at index %s:",i) +--                 for i=1,#list do +--                     local l = list[i] +--                     report_error("%3i: %s %S",i,getid(l) == attributelist_code and "!" or ">",l) +--                 end +--                 os.exit() +--             else +--                 setfield(tail,"next",n) +--                 setfield(n,"prev",tail) +--                 if getnext(n) then +--                     tail = slide_nodes(n) +--                 else +--                     tail = n +--                 end +--             end +--         else +--             -- permitting nil is convenient +--         end +--     end +--     return head, tail +-- end -local function link(list,currentfont,currentattr,head,tail) +local function link(list,currentfont,currentattr,head,tail) -- an oldie, might be replaced      for i=1,#list do          local n = list[i]          if n then -            local tn = isnode(n) -            if not tn then -                local tn = type(n) -                if tn == "number" then +            local tn = type(n) +            if tn == "string" then +                if #tn > 0 then                      if not currentfont then                          currentfont = current_font()                      end -                    local h, t = tonodes(tostring(n),currentfont,currentattr) +                    local h, t = tonodes(n,currentfont,currentattr)                      if not h then                          -- skip                      elseif not head then                          head, tail = h, t                      else -                        tail.next, h.prev, tail = h, t, t -                    end -                elseif tn == "string" then -                    if #tn > 0 then -                        if not currentfont then -                            currentfont = current_font() -                        end -                        local h, t = tonodes(n,currentfont,currentattr) -                        if not h then -                            -- skip -                        elseif not head then -                            head, tail = h, t -                        else -                            tail.next, h.prev, tail = h, t, t -                        end +                        setfield(tail,"next",h) +                        setfield(h,"prev",t) +                        tail = t                      end -                elseif tn == "table" then -                    if #tn > 0 then -                        if not currentfont then -                            currentfont = current_font() -                        end -                        head, tail = link(n,currentfont,currentattr,head,tail) +                end +            elseif tn == "table" then +                if #tn > 0 then +                    if not currentfont then +                        currentfont = current_font()                      end +                    head, tail = link(n,currentfont,currentattr,head,tail)                  end              elseif not head then                  head = n -                if n.next then -                    tail = slide_nodes(n) -                else -                    tail = n -                end -            elseif n.id == attributelist_code then +                tail = slide_nodes(n) +            elseif getid(n) == attributelist_code then                  -- weird case                  report_error("weird node type in list at index %s:",i)                  for i=1,#list do                      local l = list[i] -                    report_error("%3i: %s %S",i,l.id == attributelist_code and "!" or ">",l) +                    report_error("%3i: %s %S",i,getid(l) == attributelist_code and "!" or ">",l)                  end                  os.exit()              else -                tail.next = n -                n.prev = tail -                if n.next then +                setfield(tail,"next",n) +                setfield(n,"prev",tail) +                if getnext(n) then                      tail = slide_nodes(n)                  else                      tail = n @@ -350,17 +432,22 @@ local function link(list,currentfont,currentattr,head,tail)      return head, tail  end -nodes.link = link +nuts.link = link + +nodes.link = function(list,currentfont,currentattr,head,tail) +    local head, tail = link(list,currentfont,currentattr,tonut(head),tonut(tail)) +    return tonode(head), tonode(tail) +end  local function locate(start,wantedid,wantedsubtype)      for n in traverse_nodes(start) do -        local id = n.id +        local id = getid(n)          if id == wantedid then -            if not wantedsubtype or n.subtype == wantedsubtype then +            if not wantedsubtype or getsubtype(n) == wantedsubtype then                  return n              end          elseif id == hlist_code or id == vlist_code then -            local found = locate(n.list,wantedid,wantedsubtype) +            local found = locate(getlist(n),wantedid,wantedsubtype)              if found then                  return found              end @@ -368,7 +455,12 @@ local function locate(start,wantedid,wantedsubtype)      end  end -nodes.locate =  locate +nuts.locate = locate + +nodes.locate = function(start,wantedid,wantedsubtype) +    local found = locate(tonut(start),wantedid,wantedsubtype) +    return found and tonode(found) +end  -- I have no use for this yet:  -- @@ -381,10 +473,12 @@ nodes.locate =  locate  --     return (badness/100)^(1/3)  -- end  -- --- function tex.stretch_amount(skip,badness) +-- function tex.stretch_amount(skip,badness) -- node no nut  --     if skip.id == gluespec_code then  --         return skip.width + (badness and (badness/100)^(1/3) or 1) * skip.stretch  --     else  --         return 0  --     end  -- end + + diff --git a/tex/context/base/node-bck.lua b/tex/context/base/node-bck.lua index feaa2c684..d756d43d6 100644 --- a/tex/context/base/node-bck.lua +++ b/tex/context/base/node-bck.lua @@ -11,6 +11,8 @@ if not modules then modules = { } end modules ['node-bck'] = {  local attributes, nodes, node = attributes, nodes, node +local tasks             = nodes.tasks +  local nodecodes         = nodes.nodecodes  local listcodes         = nodes.listcodes @@ -19,11 +21,25 @@ local vlist_code        = nodecodes.vlist  local glyph_code        = nodecodes.glyph  local cell_code         = listcodes.cell -local traverse          = node.traverse -local traverse_id       = node.traverse_id +local nuts              = nodes.nuts +local nodepool          = nuts.pool + +local tonode            = nuts.tonode +local tonut             = nuts.tonut + +local getfield          = nuts.getfield +local setfield          = nuts.setfield +local getnext           = nuts.getnext +local getprev           = nuts.getprev +local getid             = nuts.getid +local getlist           = nuts.getlist +local getattr           = nuts.getattr +local setattr           = nuts.setattr +local getsubtype        = nuts.getsubtype + +local traverse          = nuts.traverse +local traverse_id       = nuts.traverse_id -local nodepool          = nodes.pool -local tasks             = nodes.tasks  local new_rule          = nodepool.rule  local new_glue          = nodepool.glue @@ -37,50 +53,50 @@ local a_alignbackground = attributes.private('alignbackground')  local function add_backgrounds(head) -- rather old code .. to be redone      local current = head      while current do -        local id = current.id +        local id = getid(current)          if id == hlist_code or id == vlist_code then -            local list = current.list +            local list = getlist(current)              if list then                  local head = add_backgrounds(list)                  if head then -                    current.list = head +                    setfield(current,"list",head)                      list = head                  end              end -            local width = current.width +            local width = getfield(current,"width")              if width > 0 then -                local background = current[a_background] +                local background = getattr(current,a_background)                  if background then                      -- direct to hbox                      -- colorspace is already set so we can omit that and stick to color -                    local mode = current[a_colorspace] +                    local mode = getattr(current,a_colorspace)                      if mode then -                        local height = current.height -                        local depth = current.depth +                        local height = getfield(current,"height") +                        local depth = getfield(current,"depth")                          local skip = id == hlist_code and width or (height + depth)                          local glue = new_glue(-skip)                          local rule = new_rule(width,height,depth) -                        local color = current[a_color] -                        local transparency = current[a_transparency] -                        rule[a_colorspace] = mode +                        local color = getattr(current,a_color) +                        local transparency = getattr(current,a_transparency) +                        setattr(rule,a_colorspace,mode)                          if color then -                            rule[a_color] = color +                            setattr(rule,a_color,color)                          end                          if transparency then -                            rule[a_transparency] = transparency +                            setattr(rule,a_transparency,transparency)                          end -                        rule.next = glue -                        glue.prev = rule +                        setfield(rule,"next",glue) +                        setfield(glue,"prev",rule)                          if list then -                            glue.next = list -                            list.prev = glue +                            setfield(glue,"next",list) +                            setfield(list,"prev",glue)                          end -                        current.list = rule +                        setfield(current,"list",rule)                      end                  end              end          end -        current = current.next +        current = getnext(current)      end      return head, true  end @@ -88,16 +104,16 @@ end  local function add_alignbackgrounds(head)      local current = head      while current do -        local id = current.id +        local id = getid(current)          if id == hlist_code then -            local list = current.list +            local list = getlist(current)              if not list then                  -- no need to look -            elseif current.subtype == cell_code then +            elseif getsubtype(current) == cell_code then                  local background = nil                  local found = nil               -- for l in traverse(list) do -             --     background = l[a_alignbackground] +             --     background = getattr(l,a_alignbackground)               --     if background then               --         found = l               --         break @@ -106,7 +122,7 @@ local function add_alignbackgrounds(head)                  -- we know that it's a fake hlist (could be user node)                  -- but we cannot store tables in user nodes yet                  for l in traverse_id(hpack_code,list) do -                    background = l[a_alignbackground] +                    background = getattr(l,a_alignbackground)                      if background then                          found = l                      end @@ -115,28 +131,28 @@ local function add_alignbackgrounds(head)                  --                  if background then                      -- current has subtype 5 (cell) -                    local width = current.width +                    local width = getfield(current,"width")                      if width > 0 then -                        local mode = found[a_colorspace] +                        local mode = getattr(found,a_colorspace)                          if mode then                              local glue = new_glue(-width) -                            local rule = new_rule(width,current.height,current.depth) -                            local color = found[a_color] -                            local transparency = found[a_transparency] -                            rule[a_colorspace] = mode +                            local rule = new_rule(width,getfield(current,"height"),getfield(current,"depth")) +                            local color = getattr(found,a_color) +                            local transparency = getattr(found,a_transparency) +                            setattr(rule,a_colorspace,mode)                              if color then -                                rule[a_color] = color +                                setattr(rule,a_color,color)                              end                              if transparency then -                                rule[a_transparency] = transparency +                                setattr(rule,a_transparency,transparency)                              end -                            rule.next = glue -                            glue.prev = rule +                            setfield(rule,"next",glue) +                            setfield(glue,"prev",rule)                              if list then -                                glue.next = list -                                list.prev = glue +                                setfield(glue,"next",list) +                                setfield(list,"prev",glue)                              end -                            current.list = rule +                            setfield(current,"list",rule)                          end                      end                  end @@ -144,18 +160,21 @@ local function add_alignbackgrounds(head)                  add_alignbackgrounds(list)              end          elseif id == vlist_code then -            local list = current.list +            local list = getlist(current)              if list then                  add_alignbackgrounds(list)              end          end -        current = current.next +        current = getnext(current)      end      return head, true  end -nodes.handlers.backgrounds      = add_backgrounds -nodes.handlers.alignbackgrounds = add_alignbackgrounds +-- nodes.handlers.backgrounds      = add_backgrounds +-- nodes.handlers.alignbackgrounds = add_alignbackgrounds + +nodes.handlers.backgrounds      = function(head) local head, done = add_backgrounds     (tonut(head)) return tonode(head), done end +nodes.handlers.alignbackgrounds = function(head) local head, done = add_alignbackgrounds(tonut(head)) return tonode(head), done end  tasks.appendaction("shipouts","normalizers","nodes.handlers.backgrounds")  tasks.appendaction("shipouts","normalizers","nodes.handlers.alignbackgrounds") diff --git a/tex/context/base/node-fin.lua b/tex/context/base/node-fin.lua index 63a5ef83e..0d095cbde 100644 --- a/tex/context/base/node-fin.lua +++ b/tex/context/base/node-fin.lua @@ -8,36 +8,55 @@ if not modules then modules = { } end modules ['node-fin'] = {  -- this module is being reconstructed  -- local functions, only slightly slower +-- +-- leaders are also triggers  local next, type, format = next, type, string.format  local attributes, nodes, node = attributes, nodes, node -local copy_node       = node.copy -local find_tail       = node.slide - -local nodecodes       = nodes.nodecodes -local whatcodes       = nodes.whatcodes - -local glyph_code      = nodecodes.glyph -local disc_code       = nodecodes.disc -local glue_code       = nodecodes.glue -local rule_code       = nodecodes.rule -local whatsit_code    = nodecodes.whatsit -local hlist_code      = nodecodes.hlist -local vlist_code      = nodecodes.vlist - -local pdfliteral_code = whatcodes.pdfliteral - -local states          = attributes.states -local numbers         = attributes.numbers -local a_trigger       = attributes.private('trigger') -local triggering      = false - -local starttiming     = statistics.starttiming -local stoptiming      = statistics.stoptiming -local loadstripped    = utilities.lua.loadstripped -local unsetvalue      = attributes.unsetvalue +local nuts               = nodes.nuts +local tonode             = nuts.tonode +local tonut              = nuts.tonut + +local getfield           = nuts.getfield +local getnext            = nuts.getnext +local getprev            = nuts.getprev +local getid              = nuts.getid +local getlist            = nuts.getlist +local getleader          = nuts.getleader +local getattr            = nuts.getattr + +local setfield           = nuts.setfield +local setattr            = nuts.setattr + +local copy_node          = nuts.copy +local find_tail          = nuts.slide +local insert_node_before = nuts.insert_before +local insert_node_after  = nuts.insert_after + +local nodecodes          = nodes.nodecodes +local whatcodes          = nodes.whatcodes + +local glyph_code         = nodecodes.glyph +local disc_code          = nodecodes.disc +local glue_code          = nodecodes.glue +local rule_code          = nodecodes.rule +local whatsit_code       = nodecodes.whatsit +local hlist_code         = nodecodes.hlist +local vlist_code         = nodecodes.vlist + +local pdfliteral_code    = whatcodes.pdfliteral + +local states             = attributes.states +local numbers            = attributes.numbers +local a_trigger          = attributes.private('trigger') +local triggering         = false + +local starttiming        = statistics.starttiming +local stoptiming         = statistics.stoptiming +local loadstripped       = utilities.lua.loadstripped +local unsetvalue         = attributes.unsetvalue  -- these two will be like trackers @@ -102,10 +121,13 @@ function nodes.installattributehandler(plugin)      return loadstripped(template)()  end --- the injectors +-- for the moment: -local insert_node_before = node.insert_before -local insert_node_after  = node.insert_after +local function copied(n) +    return copy_node(tonut(n)) +end + +-- the injectors  local nsdata, nsnone, nslistwise, nsforced, nsselector, nstrigger  local current, current_selector, done = 0, 0, false -- nb, stack has a local current ! @@ -132,23 +154,25 @@ end  function states.finalize(namespace,attribute,head) -- is this one ok?      if current > 0 and nsnone then -        local id = head.id +        head = tonut(head) +        local id = getid(head)          if id == hlist_code or id == vlist_code then -            local list = head.list +            local list = getlist(head)              if list then -                head.list = insert_node_before(list,list,copy_node(nsnone)) +                list = insert_node_before(list,list,copied(nsnone)) -- two return values +                setfield(head,"list",list)              end          else -            head = insert_node_before(head,head,copy_node(nsnone)) +            head = insert_node_before(head,head,copied(nsnone))          end -        return head, true, true +        return tonode(head), true, true      end      return head, false, false  end  -- disc nodes can be ignored  -- we need to deal with literals too (reset as well as oval) --- if id == glyph_code or (id == whatsit_code and stack.subtype == pdfliteral_code) or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then +-- if id == glyph_code or (id == whatsit_code and getsubtype(stack) == pdfliteral_code) or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then  local function process(namespace,attribute,head,inheritance,default) -- one attribute      local stack  = head @@ -156,53 +180,57 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr      local check  = false      local leader = nil      while stack do -        local id = stack.id +        local id = getid(stack)          if id == glyph_code then              check = true          elseif id == glue_code then -            leader = stack.leader +            leader = getleader(stack)              if leader then                  check = true              end          elseif id == hlist_code or id == vlist_code then -            local content = stack.list +            local content = getlist(stack)              if content then                  -- begin nested -- -                local ok -                if nstrigger and stack[nstrigger] then -                    local outer = stack[attribute] +                if nstrigger and getattr(stack,nstrigger) then +                    local outer = getattr(stack,attribute)                      if outer ~= inheritance then -                        stack.list, ok = process(namespace,attribute,content,inheritance,outer) +                        local list, ok = process(namespace,attribute,content,inheritance,outer) +                        setfield(stack,"list",list) +                        done = done or ok                      else -                        stack.list, ok = process(namespace,attribute,content,inheritance,default) +                        local list, ok = process(namespace,attribute,content,inheritance,default) +                        setfield(stack,"list",list) +                        done = done or ok                      end                  else -                    stack.list, ok = process(namespace,attribute,content,inheritance,default) +                    local list, ok = process(namespace,attribute,content,inheritance,default) +                    setfield(stack,"list",list) +                    done = done or ok                  end                  -- end nested -- -                done = done or ok              end          elseif id == rule_code then -            check = stack.width ~= 0 +            check = getfield(stack,"width") ~= 0          end          -- much faster this way than using a check() and nested() function          if check then -            local c = stack[attribute] +            local c = getattr(stack,attribute)              if c then                  if default and c == inheritance then                      if current ~= default then -                        head = insert_node_before(head,stack,copy_node(nsdata[default])) +                        head = insert_node_before(head,stack,copied(nsdata[default]))                          current = default                          done = true                      end                  elseif current ~= c then -                    head = insert_node_before(head,stack,copy_node(nsdata[c])) +                    head = insert_node_before(head,stack,copied(nsdata[c]))                      current = c                      done = true                  end                  if leader then                      local savedcurrent = current -                    local ci = leader.id +                    local ci = getid(leader)                      if ci == hlist_code or ci == vlist_code then                          -- else we reset inside a box unneeded, okay, the downside is                          -- that we trigger color in each repeated box, so there is room @@ -210,41 +238,48 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr                          current = 0                      end                      -- begin nested -- -                    local ok = false -                    if nstrigger and stack[nstrigger] then -                        local outer = stack[attribute] +                    if nstrigger and getattr(stack,nstrigger) then +                        local outer = getattr(stack,attribute)                          if outer ~= inheritance then -                            stack.leader, ok = process(namespace,attribute,leader,inheritance,outer) +                            local list, ok = process(namespace,attribute,leader,inheritance,outer) +                            setfield(stack,"leader",list) +                            done = done or ok                          else -                            stack.leader, ok = process(namespace,attribute,leader,inheritance,default) +                            local list, ok = process(namespace,attribute,leader,inheritance,default) +                            setfield(stack,"leader",list) +                            done = done or ok                          end                      else -                        stack.leader, ok = process(namespace,attribute,leader,inheritance,default) +                        local list, ok = process(namespace,attribute,leader,inheritance,default) +                        setfield(stack,"leader",list) +                        done = done or ok                      end                      -- end nested -- -                    done = done or ok                      current = savedcurrent                      leader = false                  end              elseif default and inheritance then                  if current ~= default then -                    head = insert_node_before(head,stack,copy_node(nsdata[default])) +                    head = insert_node_before(head,stack,copied(nsdata[default]))                      current = default                      done = true                  end              elseif current > 0 then -                head = insert_node_before(head,stack,copy_node(nsnone)) +                head = insert_node_before(head,stack,copied(nsnone))                  current = 0                  done = true              end              check = false          end -        stack = stack.next +        stack = getnext(stack)      end      return head, done  end -states.process = process +states.process = function(namespace,attribute,head,default) +    local head, done = process(namespace,attribute,tonut(head),default) +    return tonode(head), done +end  -- we can force a selector, e.g. document wide color spaces, saves a little  -- watch out, we need to check both the selector state (like colorspace) and @@ -258,93 +293,103 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at      local check  = false      local leader = nil      while stack do -        local id = stack.id +        local id = getid(stack)          if id == glyph_code then              check = true          elseif id == glue_code then -            leader = stack.leader +            leader = getleader(stack)              if leader then                  check = true              end          elseif id == hlist_code or id == vlist_code then -            local content = stack.list +            local content = getlist(stack)              if content then -                local ok = false                  -- begin nested -                if nstrigger and stack[nstrigger] then -                    local outer = stack[attribute] +                if nstrigger and getattr(stack,nstrigger) then +                    local outer = getattr(stack,attribute)                      if outer ~= inheritance then -                        stack.list, ok = selective(namespace,attribute,content,inheritance,outer) +                        local list, ok = selective(namespace,attribute,content,inheritance,outer) +                        setfield(stack,"list",list) +                        done = done or ok                      else -                        stack.list, ok = selective(namespace,attribute,content,inheritance,default) +                        local list, ok = selective(namespace,attribute,content,inheritance,default) +                        setfield(stack,"list",list) +                        done = done or ok                      end                  else -                    stack.list, ok = selective(namespace,attribute,content,inheritance,default) +                    local list, ok = selective(namespace,attribute,content,inheritance,default) +                    setfield(stack,"list",list) +                    done = done or ok                  end                  -- end nested -                done = done or ok              end          elseif id == rule_code then -            check = stack.width ~= 0 +            check = getfield(stack,"width") ~= 0          end          if check then -            local c = stack[attribute] +            local c = getattr(stack,attribute)              if c then                  if default and c == inheritance then                      if current ~= default then                          local data = nsdata[default] -                        head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector])) +                        head = insert_node_before(head,stack,copied(data[nsforced or getattr(stack,nsselector) or nsselector]))                          current = default                          done = true                      end                  else -                    local s = stack[nsselector] +                    local s = getattr(stack,nsselector)                      if current ~= c or current_selector ~= s then                          local data = nsdata[c] -                        head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector])) +                        head = insert_node_before(head,stack,copied(data[nsforced or getattr(stack,nsselector) or nsselector]))                          current = c                          current_selector = s                          done = true                      end                  end                  if leader then -                    local ok = false                      -- begin nested -                    if nstrigger and stack[nstrigger] then -                        local outer = stack[attribute] +                    if nstrigger and getattr(stack,nstrigger) then +                        local outer = getatribute(stack,attribute)                          if outer ~= inheritance then -                            stack.leader, ok = selective(namespace,attribute,leader,inheritance,outer) +                            local list, ok = selective(namespace,attribute,leader,inheritance,outer) +                            setfield(stack,"leader",list) +                            done = done or ok                          else -                            stack.leader, ok = selective(namespace,attribute,leader,inheritance,default) +                            local list, ok = selective(namespace,attribute,leader,inheritance,default) +                            setfield(stack,"leader",list) +                            done = done or ok                          end                      else -                        stack.leader, ok = selective(namespace,attribute,leader,inheritance,default) +                        local list, ok = selective(namespace,attribute,leader,inheritance,default) +                        setfield(stack,"leader",list) +                        done = done or ok                      end                      -- end nested -                    done = done or ok -                    leader = false +					leader = false                  end              elseif default and inheritance then                  if current ~= default then                      local data = nsdata[default] -                    head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector])) +                    head = insert_node_before(head,stack,copied(data[nsforced or getattr(stack,nsselector) or nsselector]))                      current = default                      done = true                  end              elseif current > 0 then -                head = insert_node_before(head,stack,copy_node(nsnone)) +                head = insert_node_before(head,stack,copied(nsnone))                  current, current_selector, done = 0, 0, true              end              check = false          end - -        stack = stack.next +        stack = getnext(stack)      end      return head, done  end -states.selective = selective +states.selective = function(namespace,attribute,head,default) +    local head, done = selective(namespace,attribute,tonut(head),default) +    return tonode(head), done +end  -- Ideally the next one should be merged with the previous but keeping it separate is  -- safer. We deal with two situations: efficient boxwise (layoutareas) and mixed layers @@ -363,76 +408,80 @@ local function stacked(namespace,attribute,head,default) -- no triggering, no in      local check   = false      local leader  = false      while stack do -        local id = stack.id +        local id = getid(stack)          if id == glyph_code then              check = true          elseif id == glue_code then -            leader = stack.leader +            leader = getleader(stack)              if leader then                  check = true              end          elseif id == hlist_code or id == vlist_code then -            local content = stack.list +            local content = getlist(stack)              if content then               -- the problem is that broken lines gets the attribute which can be a later one                  if nslistwise then -                    local a = stack[attribute] +                    local a = getattr(stack,attribute)                      if a and current ~= a and nslistwise[a] then -- viewerlayer / needs checking, see below                          local p = current -                        current, done = a, true -                        head = insert_node_before(head,stack,copy_node(nsdata[a])) -                        stack.list = stacked(namespace,attribute,content,current) -                        head, stack = insert_node_after(head,stack,copy_node(nsnone)) +                        current = a +                        head = insert_node_before(head,stack,copied(nsdata[a])) +                        local list = stacked(namespace,attribute,content,current) -- two return values +                        setfield(stack,"list",list) +                        done = true +                        head, stack = insert_node_after(head,stack,copied(nsnone))                          current = p                      else -                        local ok = false -                        stack.list, ok = stacked(namespace,attribute,content,current) +                        local list, ok = stacked(namespace,attribute,content,current) +                        setfield(stack,"list",list) -- only if ok                          done = done or ok                      end                  else -                    local ok = false -                    stack.list, ok = stacked(namespace,attribute,content,current) +                    local list, ok = stacked(namespace,attribute,content,current) +                    setfield(stack,"list",list) -- only if ok                      done = done or ok                  end              end          elseif id == rule_code then -            check = stack.width ~= 0 +            check = getfield(stack,"width") ~= 0          end          if check then -            local a = stack[attribute] +            local a = getattr(stack,attribute)              if a then                  if current ~= a then -                    head = insert_node_before(head,stack,copy_node(nsdata[a])) +                    head = insert_node_before(head,stack,copied(nsdata[a]))                      depth = depth + 1                      current, done = a, true                  end                  if leader then -                    local ok = false -                    stack.leader, ok = stacked(namespace,attribute,content,current) +                    local list, ok = stacked(namespace,attribute,content,current) +                    setfield(stack,"leader",list) -- only if ok                      done = done or ok                      leader = false                  end              elseif default > 0 then                  --              elseif current > 0 then -                head = insert_node_before(head,stack,copy_node(nsnone)) +                head = insert_node_before(head,stack,copied(nsnone))                  depth = depth - 1                  current, done = 0, true              end              check = false          end - -        stack = stack.next +        stack = getnext(stack)      end      while depth > 0 do -        head = insert_node_after(head,stack,copy_node(nsnone)) +        head = insert_node_after(head,stack,copied(nsnone))          depth = depth - 1      end      return head, done  end -states.stacked = stacked +states.stacked = function(namespace,attribute,head,default) +    local head, done = stacked(namespace,attribute,tonut(head),default) +    return tonode(head), done +end  -- experimental @@ -446,52 +495,53 @@ local function stacker(namespace,attribute,head,default) -- no triggering, no in      local check    = false      local leader   = false      while current do -        local id = current.id +        local id = getid(current)          if id == glyph_code then              check = true          elseif id == glue_code then -            leader = current.leader +            leader = getleader(current)              if leader then                  check = true              end          elseif id == hlist_code or id == vlist_code then -            local content = current.list +            local content = getlist(current)              if not content then                  -- skip              elseif nslistwise then -                local a = current[attribute] +                local a = getattr(current,attribute)                  if a and attrib ~= a and nslistwise[a] then -- viewerlayer +                    head = insert_node_before(head,current,copied(nsdata[a])) +                    local list = stacker(namespace,attribute,content,a) +                    setfield(current,"list",list)                      done = true -                    head = insert_node_before(head,current,copy_node(nsdata[a])) -                    current.list = stacker(namespace,attribute,content,a) -                    head, current = insert_node_after(head,current,copy_node(nsnone)) +                    head, current = insert_node_after(head,current,copied(nsnone))                  else -                    local ok = false -                    current.list, ok = stacker(namespace,attribute,content,attrib) +                    local list, ok = stacker(namespace,attribute,content,attrib) +                    setfield(current,"list",list)                      done = done or ok                  end              else -                local ok = false -                current.list, ok = stacker(namespace,attribute,content,default) +                local list, ok = stacker(namespace,attribute,content,default) +                setfield(current,"list",list)                  done = done or ok              end          elseif id == rule_code then -            check = current.width ~= 0 +            check = getfield(current,"width") ~= 0          end          if check then -            local a = current[attribute] or unsetvalue +            local a = getattr(current,attribute) or unsetvalue              if a ~= attrib then                  local n = nsstep(a)                  if n then                   -- !!!! TEST CODE !!!! -                 -- head = insert_node_before(head,current,copy_node(nsdata[tonumber(n)])) -- a -                    head = insert_node_before(head,current,n) -- a +                 -- head = insert_node_before(head,current,copied(nsdata[tonumber(n)])) -- a +                    head = insert_node_before(head,current,tonut(n)) -- a                  end                  attrib, done, okay = a, true, true                  if leader then                      -- tricky as a leader has to be a list so we cannot inject before -                    local _, ok = stacker(namespace,attribute,leader,attrib) +                    local list, ok = stacker(namespace,attribute,leader,attrib)                      done = done or ok                      leader = false                  end @@ -500,20 +550,23 @@ local function stacker(namespace,attribute,head,default) -- no triggering, no in          end          previous = current -        current = current.next +        current = getnext(current)      end      if okay then          local n = nsend()          if n then               -- !!!! TEST CODE !!!! -         -- head = insert_node_after(head,previous,copy_node(nsdata[tostring(n)])) -            head = insert_node_after(head,previous,n) +         -- head = insert_node_after(head,previous,copied(nsdata[tostring(n)])) +            head = insert_node_after(head,previous,tonut(n))          end      end      return head, done  end -states.stacker = stacker +states.stacker = function(namespace,attribute,head,default) +    local head, done = stacker(namespace,attribute,tonut(head),default) +    return tonode(head), done +end  -- -- -- diff --git a/tex/context/base/node-fnt.lua b/tex/context/base/node-fnt.lua index 2f59d513c..7000c4fd7 100644 --- a/tex/context/base/node-fnt.lua +++ b/tex/context/base/node-fnt.lua @@ -23,12 +23,24 @@ local fontdata          = fonthashes.identifiers  local otf               = fonts.handlers.otf -local traverse_id       = node.traverse_id  local starttiming       = statistics.starttiming  local stoptiming        = statistics.stoptiming +  local nodecodes         = nodes.nodecodes  local handlers          = nodes.handlers +local nuts              = nodes.nuts +local tonut             = nuts.tonut + +local getattr           = nuts.getattr +local getid             = nuts.getid +local getfont           = nuts.getfont +local getsubtype        = nuts.getsubtype +local getchar           = nuts.getchar +local getnext           = nuts.getnext + +local traverse_id       = nuts.traverse_id +  local glyph_code        = nodecodes.glyph  local disc_code         = nodecodes.disc @@ -109,25 +121,25 @@ function handlers.characters(head)          report_fonts()          report_fonts("checking node list, run %s",run)          report_fonts() -        local n = head +        local n = tonut(head)          while n do -            local id = n.id +            local id = getid(n)              if id == glyph_code then -                local font = n.font -                local attr = n[0] or 0 -                report_fonts("font %03i, dynamic %03i, glyph %C",font,attr,n.char) +                local font = getfont(n) +                local attr = getattr(n,0) or 0 +                report_fonts("font %03i, dynamic %03i, glyph %C",font,attr,getchar(n))              elseif id == disc_code then                  report_fonts("[disc] %s",nodes.listtoutf(n,true,false,n))              else                  report_fonts("[%s]",nodecodes[id])              end -            n = n.next +            n = getnext(n)          end      end -    for n in traverse_id(glyph_code,head) do -     -- if n.subtype<256 then -- all are 1 -        local font = n.font -        local attr = n[0] or 0 -- zero attribute is reserved for fonts in context +    for n in traverse_id(glyph_code,tonut(head)) do +     -- if getsubtype(n) <256 then -- all are 1 +        local font = getfont(n) +        local attr = getattr(n,0) or 0 -- zero attribute is reserved for fonts in context          if font ~= prevfont or attr ~= prevattr then              if attr > 0 then                  local used = attrfonts[font] @@ -391,5 +403,8 @@ end  --         return head, true  --     end -handlers.protectglyphs   = node.protect_glyphs -handlers.unprotectglyphs = node.unprotect_glyphs +local d_protect_glyphs   = nuts.protect_glyphs +local d_unprotect_glyphs = nuts.unprotect_glyphs + +handlers.protectglyphs   = function(n) return d_protect_glyphs  (tonut(n)) end +handlers.unprotectglyphs = function(n) return d_unprotect_glyphs(tonut(n)) end diff --git a/tex/context/base/node-inj.lua b/tex/context/base/node-inj.lua index ae48150a6..f30070e9e 100644 --- a/tex/context/base/node-inj.lua +++ b/tex/context/base/node-inj.lua @@ -11,7 +11,7 @@ if not modules then modules = { } end modules ['node-inj'] = {  -- test fonts. Btw, future versions of luatex will have extended glyph properties  -- that can be of help. Some optimizations can go away when we have faster machines. --- todo: make a special one for context +-- todo: ignore kerns between disc and glyph  local next = next  local utfchar = utf.char @@ -30,13 +30,32 @@ local injections         = nodes.injections  local nodecodes          = nodes.nodecodes  local glyph_code         = nodecodes.glyph +local disc_code          = nodecodes.disc  local kern_code          = nodecodes.kern -local nodepool           = nodes.pool + +local nuts               = nodes.nuts +local nodepool           = nuts.pool +  local newkern            = nodepool.kern -local traverse_id        = node.traverse_id -local insert_node_before = node.insert_before -local insert_node_after  = node.insert_after +local tonode             = nuts.tonode +local tonut              = nuts.tonut + +local getfield           = nuts.getfield +local getnext            = nuts.getnext +local getprev            = nuts.getprev +local getid              = nuts.getid +local getattr            = nuts.getattr +local getfont            = nuts.getfont +local getsubtype         = nuts.getsubtype +local getchar            = nuts.getchar + +local setfield           = nuts.setfield +local setattr            = nuts.setattr + +local traverse_id        = nuts.traverse_id +local insert_node_before = nuts.insert_before +local insert_node_after  = nuts.insert_after  local a_kernpair = attributes.private('kernpair')  local a_ligacomp = attributes.private('ligacomp') @@ -71,8 +90,8 @@ function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmne      local dx, dy = factor*(exit[1]-entry[1]), factor*(exit[2]-entry[2])      local ws, wn = tfmstart.width, tfmnext.width      local bound = #cursives + 1 -    start[a_cursbase] = bound -    nxt[a_curscurs] = bound +    setattr(start,a_cursbase,bound) +    setattr(nxt,a_curscurs,bound)      cursives[bound] = { rlmode, dx, dy, ws, wn }      return dx, dy, bound  end @@ -81,14 +100,14 @@ function injections.setpair(current,factor,rlmode,r2lflag,spec,tfmchr)      local x, y, w, h = factor*spec[1], factor*spec[2], factor*spec[3], factor*spec[4]      -- dy = y - h      if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then -        local bound = current[a_kernpair] +        local bound = getattr(current,a_kernpair)          if bound then              local kb = kerns[bound]              -- inefficient but singles have less, but weird anyway, needs checking              kb[2], kb[3], kb[4], kb[5] = (kb[2] or 0) + x, (kb[3] or 0) + y, (kb[4] or 0)+ w, (kb[5] or 0) + h          else              bound = #kerns + 1 -            current[a_kernpair] = bound +            setattr(current,a_kernpair,bound)              kerns[bound] = { rlmode, x, y, w, h, r2lflag, tfmchr.width }          end          return x, y, w, h, bound @@ -100,7 +119,7 @@ function injections.setkern(current,factor,rlmode,x,tfmchr)      local dx = factor*x      if dx ~= 0 then          local bound = #kerns + 1 -        current[a_kernpair] = bound +        setattr(current,a_kernpair,bound)          kerns[bound] = { rlmode, dx }          return dx, bound      else @@ -110,7 +129,7 @@ end  function injections.setmark(start,base,factor,rlmode,ba,ma,index,baseismark) -- ba=baseanchor, ma=markanchor      local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2])     -- the index argument is no longer used but when this -    local bound = base[a_markbase]                    -- fails again we should pass it +    local bound = getattr(base,a_markbase)                   -- fails again we should pass it      local index = 1      if bound then          local mb = marks[bound] @@ -118,19 +137,19 @@ function injections.setmark(start,base,factor,rlmode,ba,ma,index,baseismark) --           -- if not index then index = #mb + 1 end              index = #mb + 1              mb[index] = { dx, dy, rlmode } -            start[a_markmark] = bound -            start[a_markdone] = index +            setattr(start,a_markmark,bound) +            setattr(start,a_markdone,index)              return dx, dy, bound          else -            report_injections("possible problem, %U is base mark without data (id %a)",base.char,bound) +            report_injections("possible problem, %U is base mark without data (id %a)",getchar(base),bound)          end      end  --     index = index or 1      index = index or 1      bound = #marks + 1 -    base[a_markbase] = bound -    start[a_markmark] = bound -    start[a_markdone] = index +    setattr(base,a_markbase,bound) +    setattr(start,a_markmark,bound) +    setattr(start,a_markdone,index)      marks[bound] = { [index] = { dx, dy, rlmode, baseismark } }      return dx, dy, bound  end @@ -142,15 +161,15 @@ end  local function trace(head)      report_injections("begin run")      for n in traverse_id(glyph_code,head) do -        if n.subtype < 256 then -            local kp = n[a_kernpair] -            local mb = n[a_markbase] -            local mm = n[a_markmark] -            local md = n[a_markdone] -            local cb = n[a_cursbase] -            local cc = n[a_curscurs] -            local char = n.char -            report_injections("font %s, char %U, glyph %c",n.font,char,char) +        if getsubtype(n) < 256 then +            local kp = getattr(n,a_kernpair) +            local mb = getattr(n,a_markbase) +            local mm = getattr(n,a_markmark) +            local md = getattr(n,a_markdone) +            local cb = getattr(n,a_cursbase) +            local cc = getattr(n,a_curscurs) +            local char = getchar(n) +            report_injections("font %s, char %U, glyph %c",getfont(n),char,char)              if kp then                  local k = kerns[kp]                  if k[3] then @@ -198,22 +217,24 @@ local function show_result(head)      local current = head      local skipping = false      while current do -        local id = current.id +        local id = getid(current)          if id == glyph_code then -            report_injections("char: %C, width %p, xoffset %p, yoffset %p",current.char,current.width,current.xoffset,current.yoffset) +            report_injections("char: %C, width %p, xoffset %p, yoffset %p", +                getchar(current),getfield(current,"width"),getfield(current,"xoffset"),getfield(current,"yoffset"))              skipping = false          elseif id == kern_code then -            report_injections("kern: %p",current.kern) +            report_injections("kern: %p",getfield(current,"kern"))              skipping = false          elseif not skipping then              report_injections()              skipping = true          end -        current = current.next +        current = getnext(current)      end  end  function injections.handler(head,where,keep) +    head = tonut(head)      local has_marks, has_cursives, has_kerns = next(marks), next(cursives), next(kerns)      if has_marks or has_cursives then          if trace_injections then @@ -224,17 +245,18 @@ function injections.handler(head,where,keep)          if has_kerns then -- move outside loop              local nf, tm = nil, nil              for n in traverse_id(glyph_code,head) do -- only needed for relevant fonts -                if n.subtype < 256 then +                if getsubtype(n) < 256 then                      nofvalid = nofvalid + 1                      valid[nofvalid] = n -                    if n.font ~= nf then -                        nf = n.font -                        tm = fontdata[nf].resources.marks +                    local f = getfont(n) +                    if f ~= nf then +                        nf = f +                        tm = fontdata[nf].resources.marks -- other hash in ctx                      end                      if tm then -                        mk[n] = tm[n.char] +                        mk[n] = tm[getchar(n)]                      end -                    local k = n[a_kernpair] +                    local k = getattr(n,a_kernpair)                      if k then                          local kk = kerns[k]                          if kk then @@ -254,15 +276,16 @@ function injections.handler(head,where,keep)          else              local nf, tm = nil, nil              for n in traverse_id(glyph_code,head) do -                if n.subtype < 256 then +                if getsubtype(n) < 256 then                      nofvalid = nofvalid + 1                      valid[nofvalid] = n -                    if n.font ~= nf then -                        nf = n.font -                        tm = fontdata[nf].resources.marks +                    local f = getfont(n) +                    if f ~= nf then +                        nf = f +                        tm = fontdata[nf].resources.marks -- other hash in ctx                      end                      if tm then -                        mk[n] = tm[n.char] +                        mk[n] = tm[getchar(n)]                      end                  end              end @@ -272,7 +295,7 @@ function injections.handler(head,where,keep)              local cx = { }              if has_kerns and next(ky) then                  for n, k in next, ky do -                    n.yoffset = k +                    setfield(n,"yoffset",k)                  end              end              -- todo: reuse t and use maxt @@ -283,9 +306,9 @@ function injections.handler(head,where,keep)                  for i=1,nofvalid do -- valid == glyphs                      local n = valid[i]                      if not mk[n] then -                        local n_cursbase = n[a_cursbase] +                        local n_cursbase = getattr(n,a_cursbase)                          if p_cursbase then -                            local n_curscurs = n[a_curscurs] +                            local n_curscurs = getattr(n,a_curscurs)                              if p_cursbase == n_curscurs then                                  local c = cursives[n_curscurs]                                  if c then @@ -310,20 +333,20 @@ function injections.handler(head,where,keep)                                  end                              end                          elseif maxt > 0 then -                            local ny = n.yoffset +                            local ny = getfield(n,"yoffset")                              for i=maxt,1,-1 do                                  ny = ny + d[i]                                  local ti = t[i] -                                ti.yoffset = ti.yoffset + ny +                                setfield(ti,"yoffset",getfield(ti,"yoffset") + ny)                              end                              maxt = 0                          end                          if not n_cursbase and maxt > 0 then -                            local ny = n.yoffset +                            local ny = getfield(n,"yoffset")                              for i=maxt,1,-1 do                                  ny = ny + d[i]                                  local ti = t[i] -                                ti.yoffset = ny +                                setfield(ti,"yoffset",ny)                              end                              maxt = 0                          end @@ -331,11 +354,11 @@ function injections.handler(head,where,keep)                      end                  end                  if maxt > 0 then -                    local ny = n.yoffset +                    local ny = getfield(n,"yoffset")                      for i=maxt,1,-1 do                          ny = ny + d[i]                          local ti = t[i] -                        ti.yoffset = ny +                        setfield(ti,"yoffset",ny)                      end                      maxt = 0                  end @@ -346,57 +369,83 @@ function injections.handler(head,where,keep)              if has_marks then                  for i=1,nofvalid do                      local p = valid[i] -                    local p_markbase = p[a_markbase] +                    local p_markbase = getattr(p,a_markbase)                      if p_markbase then -                        local mrks = marks[p_markbase] -                        local nofmarks = #mrks -                        for n in traverse_id(glyph_code,p.next) do -                            local n_markmark = n[a_markmark] +                        local mrks      = marks[p_markbase] +                        local nofmarks  = #mrks +                        for n in traverse_id(glyph_code,getnext(p)) do +                            local n_markmark = getattr(n,a_markmark)                              if p_markbase == n_markmark then -                                local index = n[a_markdone] or 1 +                                local index = getattr(n,a_markdone) or 1                                  local d = mrks[index]                                  if d then                                      local rlmode = d[3]                                      --                                      local k = wx[p] +                                    local px = getfield(p,"xoffset") +                                    local ox = 0                                      if k then                                          local x = k[2]                                          local w = k[4]                                          if w then                                              if rlmode and rlmode >= 0 then                                                  -- kern(x) glyph(p) kern(w-x) mark(n) -                                                n.xoffset = p.xoffset - p.width + d[1] - (w-x) +                                                ox = px - getfield(p,"width") + d[1] - (w-x) +                                             -- report_injections("l2r case 1: %p",ox)                                              else                                                  -- kern(w-x) glyph(p) kern(x) mark(n) -                                                n.xoffset = p.xoffset - d[1] - x +                                                ox = px - d[1] - x +                                             -- report_injections("r2l case 1: %p",ox)                                              end                                          else                                              if rlmode and rlmode >= 0 then                                                  -- okay for husayni -                                                n.xoffset = p.xoffset - p.width + d[1] +                                                ox = px - getfield(p,"width") + d[1] +                                             -- report_injections("r2l case 2: %p",ox)                                              else                                                  -- needs checking: is x ok here? -                                                n.xoffset = p.xoffset - d[1] - x +                                                ox = px - d[1] - x +                                             -- report_injections("r2l case 2: %p",ox)                                              end                                          end                                      else +                                     -- if rlmode and rlmode >= 0 then +                                     --     ox = px - getfield(p,"width") + d[1] +                                     --  -- report_injections("l2r case 3: %p",ox) +                                     -- else +                                     --     ox = px - d[1] +                                     --  -- report_injections("r2l case 3: %p",ox) +                                     -- end +                                        -- +                                        -- we need to deal with fonts that have marks with width +                                        -- +                                        local wp = getfield(p,"width") +                                        local wn = getfield(n,"width") -- in arial marks have widths                                          if rlmode and rlmode >= 0 then -                                            n.xoffset = p.xoffset - p.width + d[1] +                                            ox = px - wp + d[1] +                                         -- report_injections("l2r case 3: %p",ox)                                          else -                                            n.xoffset = p.xoffset - d[1] +                                            ox = px - d[1] +                                         -- report_injections("r2l case 3: %p",ox)                                          end -                                        local w = n.width -                                        if w ~= 0 then -                                            insert_node_before(head,n,newkern(-w/2)) -                                            insert_node_after(head,n,newkern(-w/2)) +                                        if wn ~= 0 then +                                            -- bad: we should center +                                            insert_node_before(head,n,newkern(-wn/2)) +                                            insert_node_after(head,n,newkern(-wn/2)) +                                         -- wx[n] = { 0, -wn/2, 0, -wn }                                          end +                                        -- so far                                      end -                                    --                                    -- +                                    setfield(n,"xoffset",ox) +                                    -- +                                    local py = getfield(p,"yoffset") +                                    local oy = 0                                      if mk[p] then -                                        n.yoffset = p.yoffset + d[2] +                                        oy = py + d[2]                                      else -                                        n.yoffset = n.yoffset + p.yoffset + d[2] +                                        oy = getfield(n,"yoffset") + py + d[2]                                      end +                                    setfield(n,"yoffset",oy)                                      --                                      if nofmarks == 1 then                                          break @@ -404,6 +453,8 @@ function injections.handler(head,where,keep)                                          nofmarks = nofmarks - 1                                      end                                  end +                            elseif not n_markmark then +                                break -- HH: added 2013-09-12: no need to deal with non marks                              else                                  -- KE: there can be <mark> <mkmk> <mark> sequences in ligatures                              end @@ -465,6 +516,7 @@ function injections.handler(head,where,keep)           -- if trace_injections then           --     show_result(head)           -- end +head = tonode(head)              return head, true          elseif not keep then              kerns, cursives, marks = { }, { }, { } @@ -474,14 +526,14 @@ function injections.handler(head,where,keep)              trace(head)          end          for n in traverse_id(glyph_code,head) do -            if n.subtype < 256 then -                local k = n[a_kernpair] +            if getsubtype(n) < 256 then +                local k = getattr(n,a_kernpair)                  if k then                      local kk = kerns[k]                      if kk then                          local rl, x, y, w = kk[1], kk[2] or 0, kk[3], kk[4]                          if y and y ~= 0 then -                            n.yoffset = y -- todo: h ? +                            setfield(n,"yoffset",y) -- todo: h ?                          end                          if w then                              -- copied from above @@ -518,9 +570,9 @@ function injections.handler(head,where,keep)       -- if trace_injections then       --     show_result(head)       -- end -        return head, true +        return tonode(head), true      else          -- no tracing needed      end -    return head, false +    return tonode(head), false  end diff --git a/tex/context/base/node-ltp.lua b/tex/context/base/node-ltp.lua deleted file mode 100644 index c52e001df..000000000 --- a/tex/context/base/node-ltp.lua +++ /dev/null @@ -1,3192 +0,0 @@ -if not modules then modules = { } end modules ['node-par'] = { -    version   = 1.001, -    comment   = "companion to node-par.mkiv", -    author    = "Hans Hagen", -    copyright = "ConTeXt Development Team", -    license   = "see context related readme files", -    comment   = "a translation of the built in parbuilder, initial convertsin by Taco Hoekwater", -} - --- todo: remove nest_stack from linebreak.w --- todo: use ex field as signal (index in ?) --- todo: attr driven unknown/on/off --- todo: permit global steps i.e. using an attribute that sets min/max/step and overloads the font parameters --- todo: split the three passes into three functions --- todo: simplify the direction stack, no copy needed --- todo: see if we can do without delta nodes (needs thinking) --- todo: add more mkiv like tracing --- todo: add a couple of plugin hooks --- todo: maybe split expansion code paths --- todo: fix line numbers (cur_list.pg_field needed) --- todo: make kerns stretch an option and disable it by default (definitely not shrink) --- todo: check and improve protrusion --- todo: arabic etc (we could use pretty large scales there) .. marks and cursive - ---[[ - -    This code is derived from traditional TeX and has bits of pdfTeX, Aleph (Omega), and of course LuaTeX. So, -    the basic algorithm for sure is not our work. On the other hand, the directional model in LuaTeX is cleaned -    up as is other code. And of course there are hooks for callbacks. - -    The first version of the code below was a conversion of the C code that in turn was a conversion from the -    original Pascal code. Around September 2008 we experimented with cq. discussed possible approaches to improved -    typesetting of Arabic and as our policy is that extensions happen in Lua this means that we need a parbuilder -    in Lua. Taco's first conversion still looked quite C-ish and in the process of cleaning up we uncovered some odd -    bits and pieces in the original code as well. I did some first cleanup to get rid of C-artefacts, and Taco and I -    spent the usual amount of Skyping to sort out problems. At that point we diverted to other LuaTeX issues. - -    A while later I decided to pick up this thread and decided to look into better ways to deal with font expansion -    (aka hz). I got it running using a simpler method. One reason why the built-in mechanims is slow is that there is -    lots of redudancy in calculations. Expanded widths are recalculated each time and because the hpakc routine does -    it again that gives some overhead. In the process extra fonts are created with different dimensions so that the -    backend can deal with it. The alternative method doesn't create fonts but passes an expansion factor to the -    pdf generator. The small patch needed for the backend code worked more or less okay but was never intergated into -    LuaTeX due to lack of time. - -    This all happened in 2010 while listening to Peter Gabriels "Scratch My Back" and Camels "Rayaz" so it was a -    rather relaxed job. - -    In 2012 I picked up this thread. Because both languages are similar but also quite different it took some time -    to get compatible output. Because the C code uses macros, careful checking was needed. Of course Lua's table model -    and local variables brought some work as well. And still the code looks a bit C-ish. We could not divert too much -    from the original model simply because it's well documented but future versions (or variants) might as well look -    different. - -    Eventually I'll split this code into passes so that we can better see what happens, but first we need to reach -    a decent level of stability. The current expansion results are not the same as the built-in but that was never -    the objective. It all has to do with slightly different calculations. - -    The original C-code related to protrusion and expansion is not that efficient as many (redundant) function -    calls take place in the linebreaker and packer. As most work related to fonts is done in the backend, we -    can simply stick to width calculations here. Also, it is no problem at all that we use floating point -    calculations (as Lua has only floats). The final result will look ok as the hpack will nicely compensate -    for rounding errors as it will normally distribute the content well enough. And let's admit: most texies -    won't see it anyway. As long as we're cross platform compatible it's fine. - -    We use the table checked_expansion to keep track of font related parameters (per paragraph). The table is -    also the signal that we have adjustments > 1. In retrospect one might wonder if adjusting kerns is such a -    good idea because other spacing is also not treated. If we would stick to the regular hpack routine -    we do have to follow the same logic, but I decided to use a Lua hpacker so that constraint went away. And -    anyway, instead of doing a lookup in the kern table (that we don't have in node mode) the set kern value -    is used. Disabling kern scaling will become an option in Luatex some day. You can blame me for all errors -    that crept in and I know that there are some. - -    To be honest, I slowly start to grasp the magic here as normally I start from scratch when implementing -    something (as it's the only way I can understand things). This time I had a recently acquired stack of -    Porcupine Tree disks to get me through. - -    Picking up this effort was inspired by discussions between Luigi Scarso and me about efficiency of Lua -    code and we needed some stress tests to compare regular LuaTeX and LuajitTeX. One of the tests was -    processing tufte.tex as that one has lots of hyphenations and is a tough one to get right. - -    tufte: boxed 1000 times, no flushing in backend: - -                           \testfeatureonce{1000}{\setbox0\hbox{\tufte}} -                           \testfeatureonce{1000}{\setbox0\vbox{\tufte}} -    \startparbuilder[basic]\testfeatureonce{1000}{\setbox0\vbox{\tufte}}\stopparbuilder - -                method     normal   hz      comment - -    luatex      tex hbox    9.64     9.64   baseline font feature processing, hyphenation etc: 9.74 -                tex vbox    9.84    10.16   0.20 linebreak / 0.52 with hz -> 0.32 hz overhead (150pct more) -                lua vbox   17.28    18.43   7.64 linebreak / 8.79 with hz -> 1.33 hz overhead ( 20pct more) - -    new laptop | no nuts -                            3.42            baseline -                            3.63            0.21 linebreak -                            7.38            3.96 linebreak - -    new laptop | most nuts -                            2.45            baseline -                            2.53            0.08 linebreak -                            6.16            3.71 linebreak -                 ltp nuts   5.45            3.00 linebreak - -    luajittex   tex hbox    6.33     6.33   baseline font feature processing, hyphenation etc: 6.33 -                tex vbox    6.53     6.81   0.20 linebreak / 0.48 with hz -> 0.28 hz overhead (expected 0.32) -                lua vbox   11.06    11.81   4.53 linebreak / 5.28 with hz -> 0.75 hz overhead - -    new laptop | no nuts -                            2.06            baseline -                            2.27            0.21 linebreak -                            3.95            1.89 linebreak - -    new laptop | most nuts -                            1.25            baseline -                            1.30            0.05 linebreak -                            3.03            1.78 linebreak -                 ltp nuts   2.47            1.22 linebreak - -    Interesting is that the runtime for the built-in parbuilder indeed increases much when expansion -    is enabled, but in the Lua variant the extra overhead is way less significant. This means that when we -    retrofit the same approach into the core, the overhead of expansion can be sort of nilled. - -]]-- - -local utfchar = utf.char -local write, write_nl = texio.write, texio.write_nl -local sub, format = string.sub, string.format -local round, floor = math.round, math.floor -local insert, remove = table.insert, table.remove - -local fonts, nodes, node = fonts, nodes, node - -local trace_basic         = false  trackers.register("builders.paragraphs.basic",       function(v) trace_basic       = v end) -local trace_lastlinefit   = false  trackers.register("builders.paragraphs.lastlinefit", function(v) trace_lastlinefit = v end) -local trace_adjusting     = false  trackers.register("builders.paragraphs.adjusting",   function(v) trace_adjusting   = v end) -local trace_protruding    = false  trackers.register("builders.paragraphs.protruding",  function(v) trace_protruding  = v end) -local trace_expansion     = false  trackers.register("builders.paragraphs.expansion",   function(v) trace_expansion   = v end) -local trace_quality       = false  trackers.register("builders.paragraphs.quality",     function(v) trace_quality     = v end) - -local report_parbuilders  = logs.reporter("nodes","parbuilders") -local report_hpackers     = logs.reporter("nodes","hpackers") - -local calculate_badness   = tex.badness -local texnest             = tex.nest -local texlists            = tex.lists - --- (t == 0 and 0) or (s <= 0 and 10000) or calculate_badness(t,s) - --- local function calculate_badness(t,s) ---     if t == 0 then ---         return 0 ---     elseif s <= 0 then ---         return 10000 -- infinite_badness ---     else ---         local r ---         if t <= 7230584 then ---             r = (t * 297) / s ---         elseif s >= 1663497 then ---             r = t / (s / 297) ---         else ---             r = t ---         end ---         if r > 1290 then ---             return 10000 -- infinite_badness ---         else ---             return (r * r * r + 0x20000) / 0x40000 ---         end ---     end --- end - -local parbuilders          = builders.paragraphs -local constructors         = parbuilders.constructors - -local setmetatableindex    = table.setmetatableindex - -local fonthashes           = fonts.hashes -local fontdata             = fonthashes.identifiers -local chardata             = fonthashes.characters -local quaddata             = fonthashes.quads -local parameters           = fonthashes.parameters - -local slide_nodes          = node.slide -local new_node             = node.new -local copy_node            = node.copy -local copy_node_list       = node.copy_list -local flush_node           = node.free -local flush_node_list      = node.flush_list -local hpack_nodes          = node.hpack -local xpack_nodes          = node.hpack -local replace_node         = nodes.replace -local insert_node_after    = node.insert_after -local insert_node_before   = node.insert_before -local traverse_by_id       = node.traverse_id - -local setnodecolor         = nodes.tracers.colors.set - -local nodepool             = nodes.pool - -local nodecodes            = nodes.nodecodes -local whatcodes            = nodes.whatcodes -local kerncodes            = nodes.kerncodes -local glyphcodes           = nodes.glyphcodes -local gluecodes            = nodes.gluecodes -local margincodes          = nodes.margincodes -local disccodes            = nodes.disccodes -local mathcodes            = nodes.mathcodes -local fillcodes            = nodes.fillcodes - -local temp_code            = nodecodes.temp -local glyph_code           = nodecodes.glyph -local ins_code             = nodecodes.ins -local mark_code            = nodecodes.mark -local adjust_code          = nodecodes.adjust -local penalty_code         = nodecodes.penalty -local whatsit_code         = nodecodes.whatsit -local disc_code            = nodecodes.disc -local math_code            = nodecodes.math -local kern_code            = nodecodes.kern -local glue_code            = nodecodes.glue -local hlist_code           = nodecodes.hlist -local vlist_code           = nodecodes.vlist -local unset_code           = nodecodes.unset -local marginkern_code      = nodecodes.marginkern - -local leaders_code         = gluecodes.leaders - -local localpar_code        = whatcodes.localpar -local dir_code             = whatcodes.dir -local pdfrefximage_code    = whatcodes.pdfrefximage -local pdfrefxform_code     = whatcodes.pdfrefxform - -local kerning_code         = kerncodes.kerning -- font kern -local userkern_code        = kerncodes.userkern - -local ligature_code        = glyphcodes.ligature - -local stretch_orders       = nodes.fillcodes - -local leftmargin_code      = margincodes.left -local rightmargin_code     = margincodes.right - -local automatic_disc_code  = disccodes.automatic -local regular_disc_code    = disccodes.regular -local first_disc_code      = disccodes.first -local second_disc_code     = disccodes.second - -local endmath_code         = mathcodes.endmath - -local nosubtype_code       = 0 - -local unhyphenated_code    = nodecodes.unhyphenated or 1 -local hyphenated_code      = nodecodes.hyphenated   or 2 -local delta_code           = nodecodes.delta        or 3 -local passive_code         = nodecodes.passive      or 4 - -local maxdimen             = number.maxdimen - -local max_halfword         = 0x7FFFFFFF -local infinite_penalty     =  10000 -local eject_penalty        = -10000 -local infinite_badness     =  10000 -local awful_badness        = 0x3FFFFFFF - -local fit_very_loose_class = 0  -- fitness for lines stretching more than their stretchability -local fit_loose_class      = 1  -- fitness for lines stretching 0.5 to 1.0 of their stretchability -local fit_decent_class     = 2  -- fitness for all other lines -local fit_tight_class      = 3  -- fitness for lines shrinking 0.5 to 1.0 of their shrinkability - -local new_penalty          = nodepool.penalty -local new_dir              = nodepool.textdir -local new_leftmarginkern   = nodepool.leftmarginkern -local new_rightmarginkern  = nodepool.rightmarginkern -local new_leftskip         = nodepool.leftskip -local new_rightskip        = nodepool.rightskip -local new_lineskip         = nodepool.lineskip -local new_baselineskip     = nodepool.baselineskip -local new_temp             = nodepool.temp -local new_rule             = nodepool.rule - -local is_rotated           = nodes.is_rotated -local is_parallel          = nodes.textdir_is_parallel -local is_opposite          = nodes.textdir_is_opposite -local textdir_is_equal     = nodes.textdir_is_equal -local pardir_is_equal      = nodes.pardir_is_equal -local glyphdir_is_equal    = nodes.glyphdir_is_equal - -local dir_pops             = nodes.dir_is_pop -local dir_negations        = nodes.dir_negation -local is_skipable          = node.protrusion_skippable -local a_fontkern           = attributes.private('fontkern') - --- helpers -- - --- It makes more sense to move the somewhat messy dir state tracking --- out of the main functions. First we create a stack allocator. - -local function new_dir_stack(dir) -- also use elsewhere -    return { n = 0, dir } -end - --- The next function checks a dir node and returns the new dir state. By --- using s static table we are quite efficient. This function is used --- in the parbuilder. - -local function checked_line_dir(stack,current) -    if not dir_pops[current] then -        local n = stack.n + 1 -        stack.n = n -        stack[n] = current -        return current.dir -    elseif n > 0 then -        local n = stack.n -        local dirnode = stack[n] -        dirstack.n = n - 1 -        return dirnode.dir -    else -        report_parbuilders("warning: missing pop node (%a)",1) -- in line ... -    end -end - --- The next function checks a dir nodes in a list and appends the negations --- that are currently needed (some day LuaTeX will be more tolerant). We use --- the negations for the next line. - -local function inject_dirs_at_end_of_line(stack,current,start,stop) -    local e = start -    local n = stack.n -    local h = nil -    while start and start ~= stop do -        if start.id == whatsit_code and start.subtype == dir_code then -            if not dir_pops[start.dir] then -                n = n + 1 -                stack[n] = start -            elseif n > 0 then -                n = n - 1 -            else -                report_parbuilders("warning: missing pop node (%a)",2) -- in line ... -            end -        end -        start = start.next -    end -    for i=n,1,-1 do -        h, current = insert_node_after(current,current,new_dir(dir_negations[stack[i].dir])) -    end -    stack.n = n -    return current -end - -local function inject_dirs_at_begin_of_line(stack,current) -    local h = nil -    for i=stack.n,1,-1 do -        h, current = insert_node_after(current,current,new_dir(stack[i])) -    end -    stack.n = 0 -    return current -end - --- diagnostics -- - -local dummy = function() end - -local diagnostics = { -    start          = dummy, -    stop           = dummy, -    current_pass   = dummy, -    break_node     = dummy, -    feasible_break = dummy, -} - --- statistics -- - -local nofpars, noflines, nofprotrudedlines, nofadjustedlines = 0, 0, 0, 0 - -local function register_statistics(par) -    local statistics  = par.statistics -    nofpars           = nofpars           + 1 -    noflines          = noflines          + statistics.noflines -    nofprotrudedlines = nofprotrudedlines + statistics.nofprotrudedlines -    nofadjustedlines  = nofadjustedlines  + statistics.nofadjustedlines -end - --- resolvers -- - -local whatsiters = { -    get_width      = { }, -    get_dimensions = { }, -} - -local get_whatsit_width      = whatsiters.get_width -local get_whatsit_dimensions = whatsiters.get_dimensions - -local function get_width     (n) return n.width end -local function get_dimensions(n) return n.width, n.height, n.depth end - -get_whatsit_width[pdfrefximage_code] = get_width -get_whatsit_width[pdfrefxform_code ] = get_width - -get_whatsit_dimensions[pdfrefximage_code] = get_dimensions -get_whatsit_dimensions[pdfrefxform_code ] = get_dimensions - --- expansion etc -- - -local function calculate_fraction(x,n,d,max_answer) -    local the_answer = x * n/d + 1/2 -- round ? -    if the_answer > max_answer then -        return  max_answer -    elseif the_answer < -max_answer then -        return -max_answer -    else -        return  the_answer -    end -end - -local function check_shrinkage(par,n) -    -- called often, so maybe move inline -- use NORMAL -    if n.shrink_order ~= 0 and n.shrink ~= 0 then -        if par.no_shrink_error_yet then -            par.no_shrink_error_yet = false -            report_parbuilders("infinite glue shrinkage found in a paragraph and removed") -        end -        n = copy_node(n) -        n.shrink_order = 0 -    end -    return n -end - --- It doesn't really speed up much but the additional memory usage is --- rather small so it doesn't hurt too much. - -local expansions = { } -local nothing    = { stretch = 0, shrink = 0 } - -setmetatableindex(expansions,function(t,font) -- we can store this in tfmdata if needed -    local expansion = parameters[font].expansion -- can be an extra hash -    if expansion and expansion.auto then -        local factors = { } -        local c = chardata[font] -        setmetatableindex(factors,function(t,char) -            local fc = c[char] -            local ef = fc.expansion_factor -            if ef and ef > 0 then -                local stretch = expansion.stretch -                local shrink  = expansion.shrink -                if stretch ~= 0 or shrink ~= 0 then -                    local factor  = ef / 1000 -                    local ef_quad = factor * quaddata[font] / 1000 -                    local v = { -                        glyphstretch = stretch * ef_quad, -                        glyphshrink  = shrink  * ef_quad, -                        factor       = factor, -                        stretch      = stretch, -                        shrink       = shrink, -                    } -                    t[char] = v -                    return v -                end -            end -            t[char] = nothing -            return nothing -        end) -        t[font] = factors -        return factors -    else -        t[font] = false -        return false -    end -end) - --- local function char_stretch_shrink(p) ---     local data = expansions[p.font][p.char] ---     if data then ---         return data.glyphstretch, data.glyphshrink ---     else ---         return 0, 0 ---     end --- end --- --- local cal_margin_kern_var = char_stretch_shrink - --- local function kern_stretch_shrink(p,d) ---     local l = p.prev ---     if l and l.id == glyph_code then -- how about disc nodes? ---         local r = p.next ---         if r and r.id == glyph_code then ---             local lf, rf = l.font, r.font ---             if lf == rf then ---                 local data = expansions[lf][l.char] ---                 if data then ---                     local stretch = data.stretch ---                     local shrink  = data.shrink ---                     if stretch ~= 0 then ---                      -- stretch = data.factor * (d *  stretch - d) ---                         stretch = data.factor *  d * (stretch - 1) ---                     end ---                     if shrink ~= 0 then ---                      -- shrink = data.factor * (d *  shrink - d) ---                         shrink = data.factor *  d * (shrink - 1) ---                     end ---                     return stretch, shrink ---                 end ---             end ---         end ---     end ---     return 0, 0 --- end - -local function kern_stretch_shrink(p,d) -    local left = p.prev -    if left and left.id == glyph_code then -- how about disc nodes? -        local data = expansions[left.font][left.char] -        if data then -            local stretch = data.stretch -            local shrink  = data.shrink -            if stretch ~= 0 then -             -- stretch = data.factor * (d *  stretch - d) -                stretch = data.factor *  d * (stretch - 1) -            end -            if shrink ~= 0 then -             -- shrink = data.factor * (d *  shrink - d) -                shrink = data.factor *  d * (shrink - 1) -            end -            return stretch, shrink -        end -    end -    return 0, 0 -end - --- local function kern_stretch_shrink(p,d) ---     -- maybe make it an option in luatex where we also need to check for attribute fontkern but in general ---     -- it makes no sense to scale kerns ---     return 0, 0 --- end - -local expand_kerns = false --- local expand_kerns = "both" - -directives.register("builders.paragraphs.adjusting.kerns",function(v) -    if not v then -        expand_kerns = false -    elseif v == "stretch" or v == "shrink" then -        expand_kerns = v -    elseif v == "both" then -        expand_kerns = true -    else -        expand_kerns = toboolean(v,true) or false -    end -end) - --- state: - -local function check_expand_pars(checked_expansion,f) -    local expansion = parameters[f].expansion -    if not expansion then -        checked_expansion[f] = false -        return false -    end --- expansion.step = 1 -    local step    = expansion.step    or 0 -    local stretch = expansion.stretch or 0 -    local shrink  = expansion.shrink  or 0 -    if step == 0 or (stretch == 0 and schrink == 0) then -        checked_expansion[f] = false -        return false -    end -    local par = checked_expansion.par -    if par.cur_font_step < 0 then -        par.cur_font_step = step -    elseif par.cur_font_step ~= step then -        report_parbuilders("using fonts with different step of expansion in one paragraph is not allowed") -        checked_expansion[f] = false -        return false -    end -    if stretch == 0 then -        -- okay -    elseif par.max_stretch_ratio < 0 then -        par.max_stretch_ratio = stretch -- expansion_factor -    elseif par.max_stretch_ratio ~= stretch then -        report_parbuilders("using fonts with different stretch limit of expansion in one paragraph is not allowed") -        checked_expansion[f] = false -        return false -    end -    if shrink == 0 then -        -- okay -    elseif par.max_shrink_ratio < 0 then -        par.max_shrink_ratio = shrink -- - expansion_factor -    elseif par.max_shrink_ratio ~= shrink then -        report_parbuilders("using fonts with different shrink limit of expansion in one paragraph is not allowed") -        checked_expansion[f] = false -        return false -    end -    if trace_adjusting then -        report_parbuilders("expanding font %a using step %a, shrink %a and stretch %a",f,step,stretch,shrink) -    end -    local e = expansions[f] -    checked_expansion[f] = e -    return e -end - -local function check_expand_lines(checked_expansion,f) -    local expansion = parameters[f].expansion -    if not expansion then -        checked_expansion[f] = false -        return false -    end --- expansion.step = 1 -    local step    = expansion.step    or 0 -    local stretch = expansion.stretch or 0 -    local shrink  = expansion.shrink  or 0 -    if step == 0 or (stretch == 0 and schrink == 0) then -        checked_expansion[f] = false -        return false -    end -    if trace_adjusting then -        report_parbuilders("expanding font %a using step %a, shrink %a and stretch %a",f,step,stretch,shrink) -    end -    local e = expansions[f] -    checked_expansion[f] = e -    return e -end - --- protrusion - -local function find(head) -- do we really want to recurse into an hlist? -    while head do -        local id = head.id -        if id == glyph_code then -            return head -        elseif id == hlist_code then -            local found = find(head.list) -            if found then -                return found -            else -                head = head.next -            end -        elseif is_skipable(head) then -            head = head.next -        else -            return head -        end -    end -    return nil -end - -local function find_protchar_left(l) -- weird function -    local ln = l.next -    if ln and ln.id == hlist_code and not ln.list and ln.width == 0 and ln.height == 0 and ln.depth == 0 then -        l = l.next -    else -- if d then -- was always true -        local id = l.id -        while ln and not (id == glyph_code or id < math_code) do -- is there always a glyph? -            l = ln -            ln = l.next -            id = ln.id -        end -    end - -- if l.id == glyph_code then - --     return l - -- end -    return find(l) or l -end - -local function find(head,tail) -    local tail = tail or slide_nodes(head) -    while tail do -        local id = tail.id -        if id == glyph_code then -            return tail -        elseif id == hlist_code then -            local found = find(tail.list) -            if found then -                return found -            else -                tail = tail.prev -            end -        elseif is_skipable(tail) then -            tail = tail.prev -        else -            return tail -        end -    end -    return nil -end - -local function find_protchar_right(l,r) -    return r and find(l,r) or r -end - -local function left_pw(p) -    local font = p.font -    local prot = chardata[font][p.char].left_protruding -    if not prot or prot == 0 then -        return 0 -    end -    return prot * quaddata[font] / 1000, p -end - -local function right_pw(p) -    local font = p.font -    local prot = chardata[font][p.char].right_protruding -    if not prot or prot == 0 then -        return 0 -    end -    return prot * quaddata[font] / 1000, p -end - --- par parameters - -local function reset_meta(par) -    local active = { -        id          = hyphenated_code, -        line_number = max_halfword, -    } -    active.next = par.active -- head of metalist -    par.active  = active -    par.passive = nil -end - -local function add_to_width(line_break_dir,checked_expansion,s) -- split into two loops (normal and expansion) -    local size           = 0 -    local adjust_stretch = 0 -    local adjust_shrink  = 0 -    while s do -        local id = s.id -        if id == glyph_code then -            if is_rotated[line_break_dir] then -- can be shared -                size = size + s.height + s.depth -            else -                size = size + s.width -            end -            if checked_expansion then -                local data = checked_expansion[s.font] -                if data then -                    data = data[s.char] -                    if data then -                        adjust_stretch = adjust_stretch + data.glyphstretch -                        adjust_shrink  = adjust_shrink  + data.glyphshrink -                    end -                end -            end -        elseif id == hlist_code or id == vlist_code then -            if is_parallel[s.dir][line_break_dir] then -                size = size + s.width -            else -                size = size + s.depth + s.height -            end -        elseif id == kern_code then -            local d = s.kern -            if d ~= 0 then  -                if checked_expansion and expand_kerns and (s.subtype == kerning_code or s[a_fontkern]) then -                    local stretch, shrink = kern_stretch_shrink(s,d) -                    if expand_kerns == "stretch" then -                        adjust_stretch = adjust_stretch + stretch -                    elseif expand_kerns == "shrink" then -                        adjust_shrink  = adjust_shrink  + shrink -                    else -                        adjust_stretch = adjust_stretch + stretch -                        adjust_shrink  = adjust_shrink  + shrink -                    end -                end -                size = size + d -            end -        elseif id == rule_code then -            size = size + s.width -        else -            report_parbuilders("unsupported node at location %a",6) -        end -        s = s.next -    end -    return size, adjust_stretch, adjust_shrink -end - -local function compute_break_width(par,break_type,p) -- split in two -    local break_width = par.break_width -    if break_type > unhyphenated_code then -        local disc_width           = par.disc_width -        local checked_expansion    = par.checked_expansion -        local line_break_dir       = par.line_break_dir -        local break_size           = break_width.size           + disc_width.size -        local break_adjust_stretch = break_width.adjust_stretch + disc_width.adjust_stretch -        local break_adjust_shrink  = break_width.adjust_shrink  + disc_width.adjust_shrink -        local replace = p.replace -        if replace then -            local size, adjust_stretch, adjust_shrink = add_to_width(line_break_dir,checked_expansion,replace) -            break_size           = break_size           - size -            break_adjust_stretch = break_adjust_stretch - adjust_stretch -            break_adjust_shrink  = break_adjust_shrink  - adjust_shrink -        end -        local post = p.post -        if post then -            local size, adjust_stretch, adjust_shrink = add_to_width(line_break_dir,checked_expansion,post) -            break_size           = break_size           + size -            break_adjust_stretch = break_adjust_stretch + adjust_stretch -            break_adjust_shrink  = break_adjust_shrink  + adjust_shrink -        end -        break_width.size           = break_size -        break_width.adjust_stretch = break_adjust_stretch -        break_width.adjust_shrink  = break_adjust_shrink -        if not post then -            p = p.next -        else -            return -        end -    end -    while p do -- skip spacing etc -        local id = p.id -        if id == glyph_code then -            return -- happens often -        elseif id == glue_code then -            local spec = p.spec -            local order = stretch_orders[spec.stretch_order] -            break_width.size   = break_width.size   - spec.width -            break_width[order] = break_width[order] - spec.stretch -            break_width.shrink = break_width.shrink - spec.shrink -        elseif id == penalty_code then -            -- do nothing -        elseif id == kern_code then -            if p.subtype == userkern_code then -                break_width.size = break_width.size - p.kern -            else -                return -            end -        elseif id == math_code then -            break_width.size = break_width.size - p.surround -        else -            return -        end -        p = p.next -    end -end - -local function append_to_vlist(par, b) -    local prev_depth = par.prev_depth -    if prev_depth > par.ignored_dimen then -        if b.id == hlist_code then -            local d = par.baseline_skip.width - prev_depth - b.height -- deficiency of space between baselines -            local s = d < par.line_skip_limit and new_lineskip(tex.lineskip) or new_baselineskip(d) -         -- local s = d < par.line_skip_limit -         -- if s then -         --     s = new_lineskip() -         --     s.spec = tex.lineskip -         -- else -         --     s = new_baselineskip(d) -         -- end -            local head_field = par.head_field -            if head_field then -                local n = slide_nodes(head_field) -                n.next = s -                s.prev = n -            else -                par.head_field = s -            end -        end -    end -    local head_field = par.head_field -    if head_field then -        local n = slide_nodes(head_field) -        n.next = b -        b.prev = n -    else -        par.head_field = b -    end -    if b.id == hlist_code then -        local pd = b.depth -        par.prev_depth = pd -        texnest[texnest.ptr].prevdepth = pd -    end -end - -local function append_list(par, b) -    local head_field = par.head_field -    if head_field then -        local n = slide_nodes(head_field) -        n.next = b -        b.prev = n -    else -        par.head_field = b -    end -end - --- We can actually make par local to this module as we never break inside a break call and that way the --- array is reused. At some point the information will be part of the paragraph spec as passed. - -local hztolerance = 2500 -local hzwarned    = false - -local function initialize_line_break(head,display) - -    local hang_indent    = tex.hangindent or 0 -    local hsize          = tex.hsize or 0 -    local hang_after     = tex.hangafter or 0 -    local par_shape_ptr  = tex.parshape -    local left_skip      = tex.leftskip -- nodes -    local right_skip     = tex.rightskip -- nodes -    local pretolerance   = tex.pretolerance -    local tolerance      = tex.tolerance -    local adjust_spacing = tex.pdfadjustspacing -    local protrude_chars = tex.pdfprotrudechars -    local last_line_fit  = tex.lastlinefit - -    local newhead = new_temp() -    newhead.next  = head - -    local adjust_spacing_status = adjust_spacing > 1 and -1 or 0 - -    -- metatables - -    local par = { -        head                         = newhead, -        head_field                   = nil, -        display                      = display, -        font_in_short_display        = 0, -        no_shrink_error_yet          = true,   -- have we complained about infinite shrinkage? -        second_pass                  = false,  -- is this our second attempt to break this paragraph? -        final_pass                   = false,  -- is this our final attempt to break this paragraph? -        threshold                    = 0,      -- maximum badness on feasible lines - -        passive                      = nil,    -- most recent node on passive list -        printed_node                 = head,   -- most recent node that has been printed -        pass_number                  = 0,      -- the number of passive nodes allocated on this pass -        auto_breaking                = 0,      -- make auto_breaking accessible out of line_break - -        active_width                 = { size = 0, stretch = 0, fi = 0, fil = 0, fill = 0, filll = 0, shrink = 0, adjust_stretch = 0, adjust_shrink = 0 }, -        break_width                  = { size = 0, stretch = 0, fi = 0, fil = 0, fill = 0, filll = 0, shrink = 0, adjust_stretch = 0, adjust_shrink = 0 }, -        disc_width                   = { size = 0,                                                                adjust_stretch = 0, adjust_shrink = 0 }, -        fill_width                   = {           stretch = 0, fi = 0, fil = 0, fill = 0, filll = 0, shrink = 0                                        }, -        background                   = { size = 0, stretch = 0, fi = 0, fil = 0, fill = 0, filll = 0, shrink = 0                                        }, - -        hang_indent                  = hang_indent, -        hsize                        = hsize, -        hang_after                   = hang_after, -        par_shape_ptr                = par_shape_ptr, -        left_skip                    = left_skip, -        right_skip                   = right_skip, -        pretolerance                 = pretolerance, -        tolerance                    = tolerance, - -        protrude_chars               = protrude_chars, -        adjust_spacing               = adjust_spacing, -        max_stretch_ratio            = adjust_spacing_status, -        max_shrink_ratio             = adjust_spacing_status, -        cur_font_step                = adjust_spacing_status, -        checked_expansion            = false, -        tracing_paragraphs           = tex.tracingparagraphs > 0, - -        emergency_stretch            = tex.emergencystretch     or 0, -        looseness                    = tex.looseness            or 0, -        line_penalty                 = tex.linepenalty          or 0, -        hyphen_penalty               = tex.hyphenpenalty        or 0, -        broken_penalty               = tex.brokenpenalty        or 0, -        inter_line_penalty           = tex.interlinepenalty     or 0, -        club_penalty                 = tex.clubpenalty          or 0, -        widow_penalty                = tex.widowpenalty         or 0, -        display_widow_penalty        = tex.displaywidowpenalty  or 0, -        ex_hyphen_penalty            = tex.exhyphenpenalty      or 0, - -        adj_demerits                 = tex.adjdemerits          or 0, -        double_hyphen_demerits       = tex.doublehyphendemerits or 0, -        final_hyphen_demerits        = tex.finalhyphendemerits  or 0, - -        first_line                   = 0, -- tex.nest[tex.nest.ptr].modeline, -- 0, -- cur_list.pg_field - -        each_line_height             = tex.pdfeachlineheight    or 0, -- this will go away -        each_line_depth              = tex.pdfeachlinedepth     or 0, -- this will go away -        first_line_height            = tex.pdffirstlineheight   or 0, -- this will go away -        last_line_depth              = tex.pdflastlinedepth     or 0, -- this will go away -        ignored_dimen                = tex.pdfignoreddimen      or 0, -- this will go away - -        baseline_skip                = tex.baselineskip         or 0, -        lineskip                     = tex.lineskip             or 0, -        line_skip_limit              = tex.lineskiplimit        or 0, - -        prev_depth                   = texnest[texnest.ptr].prevdepth, - -        final_par_glue               = slide_nodes(head), -- todo: we know tail already, slow - -        par_break_dir                = tex.pardir, -        line_break_dir               = tex.pardir, - -        internal_pen_inter           = 0,   -- running localinterlinepenalty -        internal_pen_broken          = 0,   -- running localbrokenpenalty -        internal_left_box            = nil, -- running localleftbox -        internal_left_box_width      = 0,   -- running localleftbox width -        init_internal_left_box       = nil, -- running localleftbox -        init_internal_left_box_width = 0,   -- running localleftbox width -        internal_right_box           = nil, -- running localrightbox -        internal_right_box_width     = 0,   -- running localrightbox width - -        best_place                   = { }, -- how to achieve minimal_demerits -        best_pl_line                 = { }, -- corresponding line number -        easy_line                    = 0,   -- line numbers easy_line are equivalent in break nodes -        last_special_line            = 0,   -- line numbers last_special_line all have the same width -        first_width                  = 0,   -- the width of all lines last_special_line, if no parshape has been specified -        second_width                 = 0,   -- the width of all lines last_special_line -        first_indent                 = 0,   -- left margin to go with first_width -        second_indent                = 0,   -- left margin to go with second_width - -        best_bet                     = nil, -- use this passive node and its predecessors -        fewest_demerits              = 0,   -- the demerits associated with best_bet -        best_line                    = 0,   -- line number following the last line of the new paragraph -        line_diff                    = 0,   -- the difference between the current line number and the optimum best_line - -        -- not yet used - -        best_pl_short                = { }, -- shortfall corresponding to minimal_demerits -        best_pl_glue                 = { }, -- corresponding glue stretch or shrink -        do_last_line_fit             = false, -        last_line_fit                = last_line_fit, - -        minimum_demerits             = awful_badness, - -        minimal_demerits             = { - -            [fit_very_loose_class] = awful_badness, -            [fit_loose_class]      = awful_badness, -            [fit_decent_class]     = awful_badness, -            [fit_tight_class]      = awful_badness, - -        }, - -        prev_char_p                  = nil, - -        statistics                   = { - -            noflines          = 0, -            nofprotrudedlines = 0, -            nofadjustedlines  = 0, - -        }, - -     -- -- just a thought ... parshape functions ... it would be nice to -     -- -- also store the height so far (probably not too hard) although -     -- -- in most cases we work on grids in such cases -     -- -     -- adapt_width = function(par,line) -     --     -- carry attribute, so that we can accumulate -     --     local left  = 655360 * (line - 1) -     --     local right = 655360 * (line - 1) -     --     return left, right -     -- end - -    } - -    if adjust_spacing > 1 then -        local checked_expansion = { par = par } -        setmetatableindex(checked_expansion,check_expand_pars) -        par.checked_expansion = checked_expansion - -        if par.tolerance < hztolerance then -            if not hzwarned then -                report_parbuilders("setting tolerance to %a for hz",hztolerance) -                hzwarned = true -            end -            par.tolerance = hztolerance -        end - -    end - -    -- we need par for the error message - -    local background = par.background - -    local l = check_shrinkage(par,left_skip) -    local r = check_shrinkage(par,right_skip) -    local l_order = stretch_orders[l.stretch_order] -    local r_order = stretch_orders[r.stretch_order] - -    background.size     = l.width + r.width -    background.shrink   = l.shrink + r.shrink -    background[l_order] = l.stretch -    background[r_order] = r.stretch + background[r_order] - -    -- this will move up so that we can assign the whole par table - -    if not par_shape_ptr then -        if hang_indent == 0 then -            par.second_width  = hsize -            par.second_indent = 0 -        else -            local abs_hang_after  = hang_after >0 and hang_after  or -hang_after -            local abs_hang_indent = hang_indent>0 and hang_indent or -hang_indent -            par.last_special_line = abs_hang_after -            if hang_after < 0 then -                par.first_width = hsize - abs_hang_indent -                if hang_indent >= 0 then -                    par.first_indent = hang_indent -                else -                    par.first_indent = 0 -                end -                par.second_width  = hsize -                par.second_indent = 0 -            else -                par.first_width  = hsize -                par.first_indent = 0 -                par.second_width = hsize - abs_hang_indent -                if hang_indent >= 0 then -                    par.second_indent = hang_indent -                else -                    par.second_indent = 0 -                end -            end -        end -    else -        local last_special_line = #par_shape_ptr -        par.last_special_line = last_special_line -        local parshape = par_shape_ptr[last_special_line] -        par.second_width  = parshape[2] -        par.second_indent = parshape[1] -    end - -    if par.looseness == 0 then -        par.easy_line = par.last_special_line -    else -        par.easy_line = max_halfword -    end - -    if pretolerance >= 0 then -        par.threshold   = pretolerance -        par.second_pass = false -        par.final_pass  = false -    else -        par.threshold   = tolerance -        par.second_pass = true -        par.final_pass  = par.emergency_stretch <= 0 -        if trace_basic then -            if par.final_pass then -                report_parbuilders("enabling second and final pass") -            else -                report_parbuilders("enabling second pass") -            end -        end -    end - -    if last_line_fit > 0 then -        local spec          = par.final_par_glue.spec -        local stretch       = spec.stretch -        local stretch_order = spec.stretch_order -        if stretch > 0 and stretch_order > 0 and background.fi == 0 and background.fil == 0 and background.fill == 0 and background.filll == 0 then -            par.do_last_line_fit = true -            local si = stretch_orders[stretch_order] -            if trace_lastlinefit or trace_basic then -                report_parbuilders("enabling last line fit, stretch order %a set to %a, linefit is %a",si,stretch,last_line_fit) -            end -            par.fill_width[si] = stretch -        end -    end - -    return par -end - -local function post_line_break(par) - -    local prevgraf       = texnest[texnest.ptr].prevgraf -    local cur_line       = prevgraf + 1 -- the current line number being justified -    local cur_p          = nil - -    local adjust_spacing = par.adjust_spacing -    local protrude_chars = par.protrude_chars -    local statistics     = par.statistics - -    local p, s, k, w  -- check when local - -    local q = par.best_bet.break_node -    repeat -- goto first breakpoint -        local r = q -        q = q.prev_break -        r.prev_break = cur_p -        cur_p = r -    until not q - -    local stack = new_dir_stack() - -    repeat - -        inject_dirs_at_begin_of_line(stack,par.head) - -        local q = nil -        local r = cur_p.cur_break - -        local disc_break      = false -        local post_disc_break = false -        local glue_break      = false - -        if not r then -            r = slide_nodes(par.head) -            if r == par.final_par_glue then -                q = r      -- q refers to the last node of the line (and paragraph) -                r = r.prev -- r refers to the node after which the dir nodes should be closed -            end -        else -            local id = r.id -            if id == glue_code then -                -- r is normal skip -                r = replace_node(r,new_rightskip(par.right_skip)) -                glue_break = true -                q = r      -- q refers to the last node of the line -                r = r.prev -- r refers to the node after which the dir nodes should be closed -            elseif id == disc_code then -                -- todo: use insert_before/after -                local prev_r  = r.prev -                local next_r  = r.next -                local subtype = r.subtype -                local pre     = r.pre -                local post    = r.post -                local replace = r.replace -                if subtype == second_disc_code then -                    if not (prev_r.id == disc_code and prev_r.subtype == first_disc_code) then -                        report_parbuilders('unsupported disc at location %a',3) -                    end -                    if pre then -                        flush_node_list(pre) -                        r.pre = nil -                        pre   = nil -- signal -                    end -                    if replace then -                        local n      = slide_nodes(replace) -                        prev_r.next  = replace -                        replace.prev = prev_r -                        n.next       = r -                        r.prev       = n -                        r.replace    = nil -                        replace      = nil -- signal -                    end -                    local pre     = prev_r.pre -                    local post    = prev_r.post -                    local replace = prev_r.replace -                    if pre then -                        flush_node_list(pre) -                        prev_r.pre = nil -                    end -                    if replace then -                        flush_node_list(replace) -                        prev_r.replace = nil -                    end -                    if post then -                        flush_node_list(post) -                        prev_r.post = nil -                    end -                elseif subtype == first_disc_code then -                    if not (v.id == disc_code and v.subtype == second_disc_code) then -                        report_parbuilders('unsupported disc at location %a',4) -                    end -                    next_r.subtype = regular_disc_code -                    next_r.replace = post -                    r.post         = nil -                end -                if replace then -                    r.replace = nil -- free -                    flush_node_list(replace) -                end -                if pre then -                    local n     = slide_nodes(pre) -                    prev_r.next = pre -                    pre.prev    = prev_r -                    n.next      = r -                    r.prev      = n -                    r.pre       = nil -                end -                if post then -                    local n         = slide_nodes(post) -                    r.next          = post -                    post.prev       = r -                    n.next          = next_r -                    next_r.prev     = n -                    r.post          = nil -                    post_disc_break = true -                end -                disc_break = true -            elseif id == kern_code then -                r.kern = 0 -            elseif r.id == math_code then -                r.surround = 0 -            end -        end -        r = inject_dirs_at_end_of_line(stack,r,par.head.next,cur_p.cur_break) -        local crb  = cur_p.passive_right_box -        if crb then -            local s = copy_node(crb) -            local e = r.next -            r.next = s -            s.prev = r -            s.next = e -            if e then -                e.prev = s -            end -            r = s -        end -        if not q then -            q = r -        end -        if q and q ~= par.head and protrude_chars > 0 then -            local id = q.id -            local c = (disc_break and (id == glyph_code or id ~= disc_code) and q) or q.prev -            local p = find_protchar_right(par.head.next,c) -            if p and p.id == glyph_code then -                local w, last_rightmost_char = right_pw(p) -                if last_rightmost_char and w ~= 0 then -                    -- so we inherit attributes, q is new pseudo head -                    q, c = insert_node_after(q,c,new_rightmarginkern(copy_node(last_rightmost_char),-w)) -                end -            end -        end -        if not glue_break then -            local h -            h, q = insert_node_after(q,q,new_rightskip(par.right_skip)) -- q moves on as pseudo head -        end -        r           = q.next -        q.next      = nil -        local phead = par.head -        q           = phead.next -        phead.next  = r -        if r then -            r.prev = phead -        end -        local clb  = cur_p.passive_left_box -        if clb then -- here we miss some prev links -            local s = copy_node(cb) -            s       = q.next -            r.next  = q -            q       = r -            if s and cur_line == (par.first_line + 1) and s.id == hlist_code and not s.list then -                q      = q.next -                r.next = s.next -                s.next = r -            end -        end -        if protrude_chars > 0 then -            local p = find_protchar_left(q) -            if p and p.id == glyph_code then -                local w, last_leftmost_char = left_pw(p) -                if last_leftmost_char and w ~= 0 then -                    -- so we inherit attributes, q is pseudo head and moves back -                    q = insert_node_before(q,q,new_leftmarginkern(copy_node(last_leftmost_char),-w)) -                end -            end -        end -        local ls = par.left_skip -        if ls and (ls.width ~= 0 or ls.stretch ~= 0 or ls.shrink ~= 0) then -            q = insert_node_before(q,q,new_leftskip(ls)) -        end -        local curwidth, cur_indent -        if cur_line > par.last_special_line then -            cur_indent = par.second_indent -            cur_width  = par.second_width -        else -            local psp = par.par_shape_ptr -            if psp then -                cur_indent = psp[cur_line][1] -                cur_width  = psp[cur_line][2] -            else -                cur_indent = par.first_indent -                cur_width  = par.first_width -            end -        end -        statistics.noflines = statistics.noflines + 1 -        if adjust_spacing > 0 then -            statistics.nofadjustedlines = statistics.nofadjustedlines + 1 -            -- in the built-in hpack cal_expand_ratio will later on call subst_ext_font -            -- in the alternative approach we can do both in one run -            just_box = xpack_nodes(q,cur_width,"cal_expand_ratio",par.par_break_dir) -- ,cur_p.analysis) -        else -            just_box = xpack_nodes(q,cur_width,"exactly",par.par_break_dir) -- ,cur_p.analysis) -        end -        if protrude_chars > 0 then -            statistics.nofprotrudedlines = statistics.nofprotrudedlines + 1 -        end -        -- wrong: -        local adjust_head     = texlists.adjust_head -        local pre_adjust_head = texlists.pre_adjust_head -        -- -        just_box.shift = cur_indent -        if par.each_line_height ~= par.ignored_dimen then -            just_box.height = par.each_line_height -        end -        if par.each_line_depth ~= par.ignored_dimen then -            just_box.depth = par.each_line_depth -        end -        if par.first_line_height ~= par.ignored_dimen and (cur_line == par.first_line + 1) then -            just_box.height = par.first_line_height -        end -        if par.last_line_depth ~= par.ignored_dimen and cur_line + 1 == par.best_line then -            just_box.depth = par.last_line_depth -        end -        if texlists.pre_adjust_head ~= pre_adjust_head then -            append_list(par, texlists.pre_adjust_head) -            texlists.pre_adjust_head = pre_adjust_head -        end -        append_to_vlist(par, just_box) -        if texlists.adjust_head ~= adjust_head then -            append_list(par, texlists.adjust_head) -            texlists.adjust_head = adjust_head -        end -        local pen -        if cur_line + 1 ~= par.best_line then -            if cur_p.passive_pen_inter then -                pen = cur_p.passive_pen_inter -            else -                pen = par.inter_line_penalty -            end -            if cur_line == prevgraf + 1 then -                pen = pen + par.club_penalty -            end -            if cur_line + 2 == par.best_line then -                if par.display then -                    pen = pen + par.display_widow_penalty -                else -                    pen = pen + par.widow_penalty -                end -            end -            if disc_break then -                if cur_p.passive_pen_broken ~= 0 then -                    pen = pen + cur_p.passive_pen_broken -                else -                    pen = pen + par.broken_penalty -                end -            end -            if pen ~= 0 then -                append_to_vlist(par,new_penalty(pen)) -             end -        end -        cur_line = cur_line + 1 -        cur_p = cur_p.prev_break -        if cur_p and not post_disc_break then -            local phead = par.head -            local r = phead -            while true do -                q = r.next -                if q == cur_p.cur_break or q.id == glyph_code then -                    break -                end -                local id = q.id -                if not (id == whatsit_code and q.subtype == localpar_code) then -                    if id < math_code or (id == kern_code and q.subtype ~= userkern_code) then -                        break -                    end -                end -                r = q -            end -            if r ~= phead then -                r.next = nil -                flush_node_list(phead.next) -                phead.next = q -                if q then -                    q.prev = phead -                end -            end -        end -    until not cur_p -    if cur_line ~= par.best_line then -- or not par.head.next then -        report_parbuilders("line breaking") -    end -    if par.head then -- added ---         flush_node(par.head) -- the localpar_code whatsit -        par.head = nil -    end -    cur_line = cur_line - 1 -    if trace_basic then -        report_parbuilders("paragraph broken into %a lines",cur_line) -    end -    texnest[texnest.ptr].prevgraf  = cur_line -end - -local function wrap_up(par) -    if par.tracing_paragraphs then -        diagnostics.stop() -    end -    if par.do_last_line_fit then -        local best_bet     = par.best_bet -        local active_short = best_bet.active_short -        local active_glue  = best_bet.active_glue -        if active_short == 0 then -            if trace_lastlinefit then -                report_parbuilders("disabling last line fit, no active_short") -            end -            par.do_last_line_fit = false -        else -            local glue = par.final_par_glue -            local spec = copy_node(glue.spec) -            spec.width = spec.width + active_short - active_glue -            spec.stretch = 0 -        --  flush_node(glue.spec) -- brrr, when we do this we can get an "invalid id stretch message", maybe dec refcount -            glue.spec = spec -            if trace_lastlinefit then -                report_parbuilders("applying last line fit, short %a, glue %p",active_short,active_glue) -            end -        end -    end -    -- we have a bunch of glue and and temp nodes not freed -    local head = par.head -    if head.id == temp_code then -        par.head = head.next -        flush_node(head) -    end -    post_line_break(par) -    reset_meta(par) -    register_statistics(par) -    return par.head_field -end - --- we could do active nodes differently ... table instead of linked list or a list --- with prev nodes - -local function deactivate_node(par,prev_prev_r,prev_r,r,cur_active_width,checked_expansion) -- no need for adjust if disabled -    local active = par.active -    local active_width = par.active_width -    prev_r.next = r.next -    -- removes r -    -- r = nil -    if prev_r == active then -        r = active.next -        if r.id == delta_code then -            local aw = active_width.size    + r.size    active_width.size    = aw  cur_active_width.size    = aw -            local aw = active_width.stretch + r.stretch active_width.stretch = aw  cur_active_width.stretch = aw -            local aw = active_width.fi      + r.fi      active_width.fi      = aw  cur_active_width.fi      = aw -            local aw = active_width.fil     + r.fil     active_width.fil     = aw  cur_active_width.fil     = aw -            local aw = active_width.fill    + r.fill    active_width.fill    = aw  cur_active_width.fill    = aw -            local aw = active_width.filll   + r.filll   active_width.filll   = aw  cur_active_width.filll   = aw -            local aw = active_width.shrink  + r.shrink  active_width.shrink  = aw  cur_active_width.shrink  = aw -            if checked_expansion then -                local aw = active_width.adjust_stretch + r.adjust_stretch  active_width.adjust_stretch = aw  cur_active_width.adjust_stretch = aw -                local aw = active_width.adjust_shrink  + r.adjust_shrink   active_width.adjust_shrink  = aw  cur_active_width.adjust_shrink  = aw -            end -            active.next = r.next -            -- removes r -            -- r = nil -        end -    elseif prev_r.id == delta_code then -        r = prev_r.next -        if r == active then -            cur_active_width.size    = cur_active_width.size    - prev_r.size -            cur_active_width.stretch = cur_active_width.stretch - prev_r.stretch -            cur_active_width.fi      = cur_active_width.fi      - prev_r.fi -            cur_active_width.fil     = cur_active_width.fil     - prev_r.fil -            cur_active_width.fill    = cur_active_width.fill    - prev_r.fill -            cur_active_width.filll   = cur_active_width.filll   - prev_r.filll -            cur_active_width.shrink  = cur_active_width.shrink  - prev_r.shrink -            if checked_expansion then -                cur_active_width.adjust_stretch = cur_active_width.adjust_stretch - prev_r.adjust_stretch -                cur_active_width.adjust_shrink  = cur_active_width.adjust_shrink  - prev_r.adjust_shrink -            end -            prev_prev_r.next = active -            -- removes prev_r -            -- prev_r = nil -            prev_r = prev_prev_r -        elseif r.id == delta_code then -            local rn = r.size     cur_active_width.size    = cur_active_width.size    + rn  prev_r.size    = prev_r.size    + rn -            local rn = r.stretch  cur_active_width.stretch = cur_active_width.stretch + rn  prev_r.stretch = prev_r.stretch + rn -            local rn = r.fi       cur_active_width.fi      = cur_active_width.fi      + rn  prev_r.fi      = prev_r.fi      + rn -            local rn = r.fil      cur_active_width.fil     = cur_active_width.fil     + rn  prev_r.fil     = prev_r.fil     + rn -            local rn = r.fill     cur_active_width.fill    = cur_active_width.fill    + rn  prev_r.fill    = prev_r.fill    + rn -            local rn = r.filll    cur_active_width.filll   = cur_active_width.filll   + rn  prev_r.filll   = prev_r.fill    + rn -            local rn = r.shrink   cur_active_width.shrink  = cur_active_width.shrink  + rn  prev_r.shrink  = prev_r.shrink  + rn -            if checked_expansion then -                local rn = r.adjust_stretch  cur_active_width.adjust_stretch = cur_active_width.adjust_stretch + rn  prev_r.adjust_stretch = prev_r.adjust_stretch    + rn -                local rn = r.adjust_shrink   cur_active_width.adjust_shrink  = cur_active_width.adjust_shrink  + rn  prev_r.adjust_shrink  = prev_r.adjust_shrink     + rn -            end -            prev_r.next = r.next -            -- removes r -            -- r = nil -        end -    end -    return prev_r, r -end - -local function lastlinecrap(shortfall,active_short,active_glue,cur_active_width,fill_width,last_line_fit) -    if active_short == 0 or active_glue <= 0 then -        return false, 0, fit_decent_class, 0, 0 -    end -    if cur_active_width.fi ~= fill_width.fi or cur_active_width.fil ~= fill_width.fil or cur_active_width.fill ~= fill_width.fill or cur_active_width.filll ~= fill_width.filll then -        return false, 0, fit_decent_class, 0, 0 -    end -    local adjustment = active_short > 0 and cur_active_width.stretch or cur_active_width.shrink -    if adjustment <= 0 then -        return false, 0, fit_decent_class, adjustment, 0 -    end -    adjustment = calculate_fraction(adjustment,active_short,active_glue,maxdimen) -    if last_line_fit < 1000 then -        adjustment = calculate_fraction(adjustment,last_line_fit,1000,maxdimen) -- uses previous adjustment -    end -    local fit_class = fit_decent_class -    if adjustment > 0 then -        local stretch = cur_active_width.stretch -        if adjustment > shortfall then -            adjustment = shortfall -        end -        if adjustment > 7230584 and stretch < 1663497 then -            return true, fit_very_loose_class, shortfall, adjustment, infinite_badness -        end -     -- if adjustment == 0 then -- badness = 0 -     --     return true, shortfall, fit_decent_class, 0, 0 -     -- elseif stretch <= 0 then -- badness = 10000 -     --     return true, shortfall, fit_very_loose_class, adjustment, 10000 -     -- end -     -- local badness = (adjustment == 0 and 0) or (stretch <= 0 and 10000) or calculate_badness(adjustment,stretch) -        local badness = calculate_badness(adjustment,stretch) -        if badness > 99 then -            return true, shortfall, fit_very_loose_class, adjustment, badness -        elseif badness > 12 then -            return true, shortfall, fit_loose_class, adjustment, badness -        else -            return true, shortfall, fit_decent_class, adjustment, badness -        end -    elseif adjustment < 0 then -        local shrink = cur_active_width.shrink -        if -adjustment > shrink then -            adjustment = -shrink -        end -        local badness = calculate_badness(-adjustment,shrink) -        if badness > 12 then -            return true, shortfall, fit_tight_class, adjustment, badness -        else -            return true, shortfall, fit_decent_class, adjustment, badness -        end -    else -        return false, 0, fit_decent_class, 0, 0 -    end -end - -local function try_break(pi, break_type, par, first_p, cur_p, checked_expansion) - -    if pi >= infinite_penalty then -        return                              -- this breakpoint is inhibited by infinite penalty -    elseif pi <= -infinite_penalty then -        pi = eject_penalty                  -- this breakpoint will be forced -    end - -    local prev_prev_r         = nil         -- a step behind prev_r, if type(prev_r)=delta_code -    local prev_r              = par.active  -- stays a step behind r -    local r                   = nil         -- runs through the active list -    local no_break_yet        = true        -- have we found a feasible break at cur_p? -    local node_r_stays_active = false       -- should node r remain in the active list? -    local line_width          = 0           -- the current line will be justified to this width -    local line_number         = 0           -- line number of current active node -    local old_line_number     = 0           -- maximum line number in current equivalence class of lines - -    local protrude_chars      = par.protrude_chars -    local checked_expansion   = par.checked_expansion -    local break_width         = par.break_width -    local active_width        = par.active_width -    local background          = par.background -    local minimal_demerits    = par.minimal_demerits -    local best_place          = par.best_place -    local best_pl_line        = par.best_pl_line -    local best_pl_short       = par.best_pl_short -    local best_pl_glue        = par.best_pl_glue -    local do_last_line_fit    = par.do_last_line_fit -    local final_pass          = par.final_pass -    local tracing_paragraphs  = par.tracing_paragraphs - -- local par_active          = par.active - -    local cur_active_width = checked_expansion and { -- distance from current active node -        size           = active_width.size, -        stretch        = active_width.stretch, -        fi             = active_width.fi, -        fil            = active_width.fil, -        fill           = active_width.fill, -        filll          = active_width.filll, -        shrink         = active_width.shrink, -        adjust_stretch = active_width.adjust_stretch, -        adjust_shrink  = active_width.adjust_shrink, -    } or { -        size           = active_width.size, -        stretch        = active_width.stretch, -        fi             = active_width.fi, -        fil            = active_width.fil, -        fill           = active_width.fill, -        filll          = active_width.filll, -        shrink         = active_width.shrink, -    } - -    while true do -        r = prev_r.next -        if r.id == delta_code then -            cur_active_width.size    = cur_active_width.size    + r.size -            cur_active_width.stretch = cur_active_width.stretch + r.stretch -            cur_active_width.fi      = cur_active_width.fi      + r.fi -            cur_active_width.fil     = cur_active_width.fil     + r.fil -            cur_active_width.fill    = cur_active_width.fill    + r.fill -            cur_active_width.filll   = cur_active_width.filll   + r.filll -            cur_active_width.shrink  = cur_active_width.shrink  + r.shrink -            if checked_expansion then -                cur_active_width.adjust_stretch = cur_active_width.adjust_stretch + r.adjust_stretch -                cur_active_width.adjust_shrink  = cur_active_width.adjust_shrink  + r.adjust_shrink -            end -            prev_prev_r = prev_r -            prev_r = r -        else -            line_number = r.line_number -            if line_number > old_line_number then -                local minimum_demerits = par.minimum_demerits -                if minimum_demerits < awful_badness and (old_line_number ~= par.easy_line or r == par.active) then -                    if no_break_yet then -                        no_break_yet = false -                        break_width.size    = background.size -                        break_width.stretch = background.stretch -                        break_width.fi      = background.fi -                        break_width.fil     = background.fil -                        break_width.fill    = background.fill -                        break_width.filll   = background.filll -                        break_width.shrink  = background.shrink -                        if checked_expansion then -                            break_width.adjust_stretch = 0 -                            break_width.adjust_shrink  = 0 -                        end -                        if cur_p then -                            compute_break_width(par,break_type,cur_p) -                        end -                    end -                    if prev_r.id == delta_code then -                        prev_r.size    = prev_r.size    - cur_active_width.size   + break_width.size -                        prev_r.stretch = prev_r.stretch - cur_active_width.stretc + break_width.stretch -                        prev_r.fi      = prev_r.fi      - cur_active_width.fi     + break_width.fi -                        prev_r.fil     = prev_r.fil     - cur_active_width.fil    + break_width.fil -                        prev_r.fill    = prev_r.fill    - cur_active_width.fill   + break_width.fill -                        prev_r.filll   = prev_r.filll   - cur_active_width.filll  + break_width.filll -                        prev_r.shrink  = prev_r.shrink  - cur_active_width.shrink + break_width.shrink -                        if checked_expansion then -                            prev_r.adjust_stretch = prev_r.adjust_stretch - cur_active_width.adjust_stretch + break_width.adjust_stretch -                            prev_r.adjust_shrink  = prev_r.adjust_shrink  - cur_active_width.adjust_shrink  + break_width.adjust_shrink -                        end -                    elseif prev_r == par.active then -                        active_width.size    = break_width.size -                        active_width.stretch = break_width.stretch -                        active_width.fi      = break_width.fi -                        active_width.fil     = break_width.fil -                        active_width.fill    = break_width.fill -                        active_width.filll   = break_width.filll -                        active_width.shrink  = break_width.shrink -                        if checked_expansion then -                            active_width.adjust_stretch = break_width.adjust_stretch -                            active_width.adjust_shrink  = break_width.adjust_shrink -                        end -                    else -                        local q = checked_expansion and { -                            id             = delta_code, -                            subtype        = nosubtype_code, -                            next           = r, -                            size           = break_width.size           - cur_active_width.size, -                            stretch        = break_width.stretch        - cur_active_width.stretch, -                            fi             = break_width.fi             - cur_active_width.fi, -                            fil            = break_width.fil            - cur_active_width.fil, -                            fill           = break_width.fill           - cur_active_width.fill, -                            filll          = break_width.filll          - cur_active_width.filll, -                            shrink         = break_width.shrink         - cur_active_width.shrink, -                            adjust_stretch = break_width.adjust_stretch - cur_active_width.adjust_stretch, -                            adjust_shrink  = break_width.adjust_shrink  - cur_active_width.adjust_shrink, -                        } or { -                            id             = delta_code, -                            subtype        = nosubtype_code, -                            next           = r, -                            size           = break_width.size           - cur_active_width.size, -                            stretch        = break_width.stretch        - cur_active_width.stretch, -                            fi             = break_width.fi             - cur_active_width.fi, -                            fil            = break_width.fil            - cur_active_width.fil, -                            fill           = break_width.fill           - cur_active_width.fill, -                            filll          = break_width.filll          - cur_active_width.filll, -                            shrink         = break_width.shrink         - cur_active_width.shrink, -                        } -                        prev_r.next = q -                        prev_prev_r = prev_r -                        prev_r = q -                    end -                    local adj_demerits     = par.adj_demerits -                    local abs_adj_demerits = adj_demerits > 0 and adj_demerits or -adj_demerits -                    if abs_adj_demerits >= awful_badness - minimum_demerits then -                        minimum_demerits = awful_badness - 1 -                    else -                        minimum_demerits = minimum_demerits + abs_adj_demerits -                    end -                    for fit_class = fit_very_loose_class, fit_tight_class do -                        if minimal_demerits[fit_class] <= minimum_demerits then -                            -- insert a new active node from best_place[fit_class] to cur_p -                            par.pass_number = par.pass_number + 1 -                            local prev_break = best_place[fit_class] -                            local passive = { -                                id                          = passive_code, -                                subtype                     = nosubtype_code, -                                next                        = par.passive, -                                cur_break                   = cur_p, -                                serial                      = par.pass_number, -                                prev_break                  = prev_break, -                                passive_pen_inter           = par.internal_pen_inter, -                                passive_pen_broken          = par.internal_pen_broken, -                                passive_last_left_box       = par.internal_left_box, -                                passive_last_left_box_width = par.internal_left_box_width, -                                passive_left_box            = prev_break and prev_break.passive_last_left_box or par.init_internal_left_box, -                                passive_left_box_width      = prev_break and prev_break.passive_last_left_box_width or par.init_internal_left_box_width, -                                passive_right_box           = par.internal_right_box, -                                passive_right_box_width     = par.internal_right_box_width, --- analysis = table.fastcopy(cur_active_width), -                            } -                            par.passive = passive -                            local q = { -                                id             = break_type, -                                subtype        = fit_class, -                                break_node     = passive, -                                line_number    = best_pl_line[fit_class] + 1, -                                total_demerits = minimal_demerits[fit_class], --  or 0, -                                next           = r, -                            } -                            if do_last_line_fit then -                                local active_short = best_pl_short[fit_class] -                                local active_glue  = best_pl_glue[fit_class] -                                q.active_short = active_short -                                q.active_glue  = active_glue -                                if trace_lastlinefit then -                                    report_parbuilders("setting short to %i and glue to %p using class %a",active_short,active_glue,fit_class) -                                end -                            end -                         -- q.next = r -- already done -                            prev_r.next = q -                            prev_r = q -                            if tracing_paragraphs then -                                diagnostics.break_node(par,q,fit_class,break_type,cur_p) -                            end -                        end -                        minimal_demerits[fit_class] = awful_badness -                    end -                    par.minimum_demerits = awful_badness -                    if r ~= par.active then -                        local q = checked_expansion and { -                            id             = delta_code, -                            subtype        = nosubtype_code, -                            next           = r, -                            size           = cur_active_width.size           - break_width.size, -                            stretch        = cur_active_width.stretch        - break_width.stretch, -                            fi             = cur_active_width.fi             - break_width.fi, -                            fil            = cur_active_width.fil            - break_width.fil, -                            fill           = cur_active_width.fill           - break_width.fill, -                            filll          = cur_active_width.filll          - break_width.filll, -                            shrink         = cur_active_width.shrink         - break_width.shrink, -                            adjust_stretch = cur_active_width.adjust_stretch - break_width.adjust_stretch, -                            adjust_shrink  = cur_active_width.adjust_shrink  - break_width.adjust_shrink, -                        } or { -                            id             = delta_code, -                            subtype        = nosubtype_code, -                            next           = r, -                            size           = cur_active_width.size           - break_width.size, -                            stretch        = cur_active_width.stretch        - break_width.stretch, -                            fi             = cur_active_width.fi             - break_width.fi, -                            fil            = cur_active_width.fil            - break_width.fil, -                            fill           = cur_active_width.fill           - break_width.fill, -                            filll          = cur_active_width.filll          - break_width.filll, -                            shrink         = cur_active_width.shrink         - break_width.shrink, -                        } -                     -- q.next = r -- already done -                        prev_r.next = q -                        prev_prev_r = prev_r -                        prev_r = q -                    end -                end -                if r == par.active then -                    return -                end -                if line_number > par.easy_line then -                    old_line_number = max_halfword - 1 -                    line_width = par.second_width -                else -                    old_line_number = line_number -                    if line_number > par.last_special_line then -                        line_width = par.second_width -                    elseif par.par_shape_ptr then -                        line_width = par.par_shape_ptr[line_number][2] -                    else -                        line_width = par.first_width -                    end -                end -            end -            local artificial_demerits = false -- has d been forced to zero -            local shortfall = line_width - cur_active_width.size - par.internal_right_box_width -- used in badness calculations -            if not r.break_node then -                shortfall = shortfall - par.init_internal_left_box_width -            else -                shortfall = shortfall - (r.break_node.passive_last_left_box_width or 0) -            end -            local pw, lp, rp -- used later on -            if protrude_chars > 1 then -                -- this is quite time consuming -                local b = r.break_node -                local l = b and b.cur_break or first_p -                local o = cur_p and cur_p.prev -                if cur_p and cur_p.id == disc_code and cur_p.pre then -                    o = slide_nodes(cur_p.pre) -                else -                    o = find_protchar_right(l,o) -                end -                if o and o.id == glyph_code then -                    pw, rp = right_pw(o) -                    shortfall = shortfall + pw -                end -                local id = l.id -                if id == glyph_code then -                    -- ok ? -                elseif id == disc_code and l.post then -                    l = l.post -- TODO: first char could be a disc -                else -                    l = find_protchar_left(l) -                end -                if l and l.id == glyph_code then -                    pw, lp = left_pw(l) -                    shortfall = shortfall + pw -                end -            end -            if checked_expansion and shortfall ~= 0 then -                local margin_kern_stretch = 0 -                local margin_kern_shrink  = 0 -                if protrude_chars > 1 then -                    if lp then ---                         margin_kern_stretch, margin_kern_shrink = cal_margin_kern_var(lp) -local data = expansions[lp.font][lp.char] -if data then -    margin_kern_stretch, margin_kern_shrink = data.glyphstretch, data.glyphshrink -end -                    end -                    if rp then ---                         local mka, mkb = cal_margin_kern_var(rp) ---                         margin_kern_stretch = margin_kern_stretch + mka ---                         margin_kern_shrink  = margin_kern_shrink  + mkb -local data = expansions[lp.font][lp.char] -if data then -    margin_kern_stretch = margin_kern_stretch + data.glyphstretch -    margin_kern_shrink  = margin_kern_shrink  + data.glyphshrink -end -                    end -                end -                local total = cur_active_width.adjust_stretch + margin_kern_stretch -                if shortfall > 0 and total > 0 then -                    if total > shortfall then -                        shortfall = total / (par.max_stretch_ratio / par.cur_font_step) / 2 -- to be adapted -                    else -                        shortfall = shortfall - total -                    end -                else -                    total = cur_active_width.adjust_shrink + margin_kern_shrink -                    if shortfall < 0 and total > 0 then -                        if total > - shortfall then -                            shortfall = - total / (par.max_shrink_ratio / par.cur_font_step) / 2 -- to be adapted -                        else -                            shortfall = shortfall + total -                        end -                    end -                end -            end -            local b = 0 -            local g = 0 -            local fit_class = fit_decent_class -            local found = false -            if shortfall > 0  then -                if cur_active_width.fi ~= 0 or cur_active_width.fil ~= 0 or cur_active_width.fill ~= 0 or cur_active_width.filll ~= 0 then -                    if not do_last_line_fit then -                        -- okay -                    elseif not cur_p then -                        found, shortfall, fit_class, g, b = lastlinecrap(shortfall,r.active_short,r.active_glue,cur_active_width,par.fill_width,par.last_line_fit) -                    else -                        shortfall = 0 -                    end -                else -                    local stretch = cur_active_width.stretch -                    if shortfall > 7230584 and stretch < 1663497 then -                        b = infinite_badness -                        fit_class = fit_very_loose_class -                    else -                        b = calculate_badness(shortfall,stretch) -                        if b > 99 then -                            fit_class = fit_very_loose_class -                        elseif b > 12 then -                            fit_class = fit_loose_class -                        else -                            fit_class = fit_decent_class -                        end -                    end -                end -            else -                local shrink = cur_active_width.shrink -                if -shortfall > shrink then -                    b = infinite_badness + 1 -                else -                    b = calculate_badness(-shortfall,shrink) -                end -                if b > 12 then -                    fit_class = fit_tight_class -                else -                    fit_class = fit_decent_class -                end -            end -            if do_last_line_fit and not found then -                if not cur_p then -                 -- g = 0 -                    shortfall = 0 -                elseif shortfall > 0 then -                    g = cur_active_width.stretch -                elseif shortfall < 0 then -                    g = cur_active_width.shrink -                else -                    g = 0 -                end -            end -            -- ::FOUND:: -            local continue_only = false -- brrr -            if b > infinite_badness or pi == eject_penalty then -                if final_pass and par.minimum_demerits == awful_badness and r.next == par.active and prev_r == par.active then -                    artificial_demerits = true -- set demerits zero, this break is forced -                    node_r_stays_active = false -                elseif b > par.threshold then -                    prev_r, r = deactivate_node(par,prev_prev_r,prev_r,r,cur_active_width,checked_expansion) -                    continue_only = true -                else -                    node_r_stays_active = false -                end -            else -                prev_r = r -                if b > par.threshold then -                    continue_only = true -                else -                    node_r_stays_active = true -                end -            end -            if not continue_only then -                local d = 0 -                if not artificial_demerits then -                    d = par.line_penalty + b -                    if (d >= 0 and d or -d) >= 10000 then -- abs(d) -                        d = 100000000 -                    else -                        d = d * d -                    end -                    if pi == 0 then -                        -- nothing -                    elseif pi > 0 then -                        d = d + pi * pi -                    elseif pi > eject_penalty then -                        d = d - pi * pi -                    end -                    if break_type == hyphenated_code and r.id == hyphenated_code then -                        if cur_p then -                            d = d + par.double_hyphen_demerits -                        else -                            d = d + par.final_hyphen_demerits -                        end -                    end -                    local delta = fit_class - r.subtype -                    if (delta >= 0 and delta or -delta) > 1 then -- abs(delta) -                        d = d + par.adj_demerits -                    end -                end -                if tracing_paragraphs then -                    diagnostics.feasible_break(par,cur_p,r,b,pi,d,artificial_demerits) -                end -                d = d + r.total_demerits -- this is the minimum total demerits from the beginning to cur_p via r -                if d <= minimal_demerits[fit_class] then -                    minimal_demerits[fit_class] = d -                    best_place      [fit_class] = r.break_node -                    best_pl_line    [fit_class] = line_number -                    if do_last_line_fit then -                        best_pl_short[fit_class] = shortfall -                        best_pl_glue [fit_class] = g -                        if trace_lastlinefit then -                            report_parbuilders("storing last line fit short %a and glue %p in class %a",shortfall,g,fit_class) -                        end -                    end -                    if d < par.minimum_demerits then -                        par.minimum_demerits = d -                    end -                end -                if not node_r_stays_active then -                    prev_r, r = deactivate_node(par,prev_prev_r,prev_r,r,cur_active_width,checked_expansion) -                end -            end -        end -    end -end - -local function kern_break(par, cur_p, first_p, checked_expansion) -- move inline if needed -    local v = cur_p.next -    if par.auto_breaking and v.id == glue_code then -        try_break(0, unhyphenated_code, par, first_p, cur_p, checked_expansion) -    end -    local active_width = par.active_width -    if cur_p.id ~= math_code then -        active_width.size = active_width.size + cur_p.kern -    else -        active_width.size = active_width.size + cur_p.surround -    end -end - --- we can call the normal one for simple box building in the otr so we need --- frequent enabling/disabling - -local temp_head = new_temp() - -function constructors.methods.basic(head,d) -    if trace_basic then -        report_parbuilders("starting at %a",head) -    end - -    local par = initialize_line_break(head,d) - -    local checked_expansion  = par.checked_expansion -    local active_width       = par.active_width -    local disc_width         = par.disc_width -    local background         = par.background -    local tracing_paragraphs = par.tracing_paragraphs - -    local dirstack = new_dir_stack() - -    if tracing_paragraphs then -        diagnostics.start() -        if par.pretolerance >= 0 then -            diagnostics.current_pass(par,"firstpass") -        end -    end - -    while true do -        reset_meta(par) -        if par.threshold > infinite_badness then -            par.threshold = infinite_badness -        end -        par.active.next = { -            id             = unhyphenated_code, -            subtype        = fit_decent_class, -            next           = par.active, -            break_node     = nil, -            line_number    = par.first_line + 1, -            total_demerits = 0, -            active_short   = 0, -            active_glue    = 0, -        } -        active_width.size           = background.size -        active_width.stretch        = background.stretch -        active_width.fi             = background.fi -        active_width.fil            = background.fil -        active_width.fill           = background.fill -        active_width.filll          = background.filll -        active_width.shrink         = background.shrink - -        if checked_expansion then -            active_width.adjust_stretch = 0 -            active_width.adjust_shrink  = 0 -        end - -        par.passive                 = nil -- = 0 -        par.printed_node            = temp_head -- only when tracing, shared -        par.printed_node.next       = head -        par.pass_number             = 0 -        par.auto_breaking           = true - -        local cur_p                 = head -        local first_p               = cur_p - -        par.font_in_short_display   = 0 - -        if cur_p and cur_p.id == whatsit_code and cur_p.subtype == localpar_code then -            par.init_internal_left_box       = cur_p.box_left -            par.init_internal_left_box_width = cur_p.box_left_width -            par.internal_pen_inter           = cur_p.pen_inter -            par.internal_pen_broken          = cur_p.pen_broken -            par.internal_left_box            = par.init_internal_left_box -            par.internal_left_box_width      = par.init_internal_left_box_width -            par.internal_right_box           = cur_p.box_right -            par.internal_right_box_width     = cur_p.box_right_width -        end - -        -- all passes are combined in this loop so maybe we should split this into -        -- three function calls; we then also need to do the wrap_up elsewhere - -        -- split into normal and expansion loop - -        -- use an active local - -        local fontexp, lastfont -- we can pass fontexp to calculate width if needed - -        while cur_p and par.active.next ~= par.active do -            while cur_p and cur_p.id == glyph_code do -                if is_rotated[par.line_break_dir] then -                    active_width.size = active_width.size + cur_p.height + cur_p.depth -                else -                    active_width.size = active_width.size + cur_p.width -                end -                if checked_expansion then -                    local data= checked_expansion[cur_p.font] -                    if data then -                        local currentfont = cur_p.font -                        if currentfont ~= lastfont then -                            fontexps = checked_expansion[currentfont] -- a bit redundant for the par line packer -                            lastfont = currentfont -                        end -                        if fontexps then -                            local expansion = fontexps[cur_p.char] -                            if expansion then -                                active_width.adjust_stretch = active_width.adjust_stretch + expansion.glyphstretch -                                active_width.adjust_shrink  = active_width.adjust_shrink  + expansion.glyphshrink -                            end -                        end -                    end -                end -                cur_p = cur_p.next -            end -            if not cur_p then -- TODO -                report_parbuilders("problems with linebreak_tail") -                os.exit() -            end -            local id = cur_p.id -            if id == hlist_code or id == vlist_code then -                if is_parallel[cur_p.dir][par.line_break_dir] then -                    active_width.size = active_width.size + cur_p.width -                else -                    active_width.size = active_width.size + cur_p.depth + cur_p.height -                end -            elseif id == glue_code then -                if par.auto_breaking then -                    local prev_p = cur_p.prev -                    if prev_p and prev_p ~= temp_head then -                        local id = prev_p.id -                        if id == glyph_code or -                            (id < math_code and (id ~= whatsit_code or prev_p.subtype ~= dir_code)) or -- was: precedes_break(prev_p) -                            (id == kern_code and prev_p.subtype ~= userkern_code) then -                            try_break(0, unhyphenated_code, par, first_p, cur_p, checked_expansion) -                        end -                    end -                end -                local spec = check_shrinkage(par,cur_p.spec) -                local order = stretch_orders[spec.stretch_order] -                cur_p.spec = spec -                active_width.size   = active_width.size   + spec.width -                active_width[order] = active_width[order] + spec.stretch -                active_width.shrink = active_width.shrink + spec.shrink -            elseif id == disc_code then -                local subtype = cur_p.subtype -                if subtype ~= second_disc_code then -- are there still second_disc_code in luatex -                    local line_break_dir = par.line_break_dir -                    if par.second_pass then -- todo: make second pass local -                        local actual_pen = subtype == automatic_disc_code and par.ex_hyphen_penalty or par.hyphen_penalty -                        local pre = cur_p.pre -                        if not pre then    --  trivial pre-break -                            disc_width.size = 0 -                            if checked_expansion then -                                disc_width.adjust_stretch = 0 -                                disc_width.adjust_shrink  = 0 -                            end -                            try_break(actual_pen, hyphenated_code, par, first_p, cur_p, checked_expansion) -                        else -                            local size, adjust_stretch, adjust_shrink = add_to_width(line_break_dir,checked_expansion,pre) -                            disc_width.size   = size -                            active_width.size = active_width.size + size -                            if checked_expansion then -                                disc_width.adjust_stretch   = adjust_stretch -                                disc_width.adjust_shrink    = adjust_shrink -                                active_width.adjust_stretch = active_width.adjust_stretch + adjust_stretch -                                active_width.adjust_shrink  = active_width.adjust_shrink  + adjust_shrink -                            else -                             -- disc_width.adjust_stretch = 0 -                             -- disc_width.adjust_shrink  = 0 -                            end -                            try_break(actual_pen, hyphenated_code, par, first_p, cur_p, checked_expansion) -                            if subtype == first_disc_code then -                                local cur_p_next = cur_p.next -                                if cur_p_next.id ~= disc_code or cur_p_next.subtype ~= second_disc_code then -                                    report_parbuilders("unsupported disc at location %a",1) -                                else -                                    local pre = cur_p_next.pre -                                    if pre then -                                        local size, adjust_stretch, adjust_shrink = add_to_width(line_break_dir,checked_expansion,pre) -                                        disc_width.size = disc_width.size + size -                                        if checked_expansion then -                                            disc_width.adjust_stretch = disc_width.adjust_stretch + adjust_stretch -                                            disc_width.adjust_shrink  = disc_width.adjust_shrink  + adjust_shrink -                                        end -                                        try_break(actual_pen, hyphenated_code, par, first_p, cur_p_next, checked_expansion) -                                        -- -                                        -- I will look into this some day ... comment in linebreak.w says that this fails, -                                        -- maybe this is what Taco means with his comment in the luatex manual. -                                        -- -                                        -- do_one_seven_eight(sub_disc_width_from_active_width); -                                        -- do_one_seven_eight(reset_disc_width); -                                        -- s = vlink_no_break(vlink(cur_p)); -                                        -- add_to_widths(s, line_break_dir, pdf_adjust_spacing,disc_width); -                                        -- ext_try_break(...,first_p,vlink(cur_p)); -                                        -- -                                    else -                                        report_parbuilders("unsupported disc at location %a",2) -                                    end -                                end -                            end -                            -- beware, we cannot restore to a saved value as the try_break adapts active_width -                            active_width.size = active_width.size - disc_width.size -                            if checked_expansion then -                                active_width.adjust_stretch = active_width.adjust_stretch - disc_width.adjust_stretch -                                active_width.adjust_shrink  = active_width.adjust_shrink  - disc_width.adjust_shrink -                            end -                        end -                    end -                    local replace = cur_p.replace -                    if replace then -                        local size, adjust_stretch, adjust_shrink = add_to_width(line_break_dir,checked_expansion,replace) -                        active_width.size = active_width.size + size -                        if checked_expansion then -                            active_width.adjust_stretch = active_width.adjust_stretch + adjust_stretch -                            active_width.adjust_shrink  = active_width.adjust_shrink  + adjust_shrink -                        end -                    end -                end -            elseif id == kern_code then -                if cur_p.subtype == userkern_code then -                    kern_break(par,cur_p,first_p, checked_expansion) -                else -                    local d = cur_p.kern -                    of d ~= 0 then  -                        active_width.size = active_width.size + d -                        if checked_expansion and expand_kerns and (cur_p.subtype == kerning_code or cur_p[a_fontkern]) then -                            local stretch, shrink = kern_stretch_shrink(cur_p,d) -                            if expand_kerns == "stretch" then -                                active_width.adjust_stretch = active_width.adjust_stretch + stretch -                            elseif expand_kerns == "shrink" then -                                active_width.adjust_shrink  = active_width.adjust_shrink  + shrink -                            else -                                active_width.adjust_stretch = active_width.adjust_stretch + stretch -                                active_width.adjust_shrink  = active_width.adjust_shrink  + shrink -                            end -                        end -                    end -                end -            elseif id == math_code then -                par.auto_breaking = cur_p.subtype == endmath_code -                kern_break(par,cur_p, first_p, checked_expansion) -            elseif id == rule_code then -                active_width.size = active_width.size + cur_p.width -            elseif id == penalty_code then -                try_break(cur_p.penalty, unhyphenated_code, par, first_p, cur_p, checked_expansion) -            elseif id == whatsit_code then -                local subtype = cur_p.subtype -                if subtype == localpar_code then -                    par.internal_pen_inter       = cur_p.pen_inter -                    par.internal_pen_broken      = cur_p.pen_broken -                    par.internal_left_box        = cur_p.box_left -                    par.internal_left_box_width  = cur_p.box_left_width -                    par.internal_right_box       = cur_p.box_right -                    par.internal_right_box_width = cur_p.box_right_width -                elseif subtype == dir_code then -                    par.line_break_dir = checked_line_dir(dirstack) or par.line_break_dir -                else -                    local get_width = get_whatsit_width[subtype] -                    if get_width then -                        active_width.size = active_width.size + get_width(cur_p) -                    end -                end -            elseif id == mark_code or id == ins_code or id == adjust_code then -                -- skip -            else -                report_parbuilders("node of type %a found in paragraph",type(id)) -            end -            cur_p = cur_p.next -        end -        if not cur_p then -            try_break(eject_penalty, hyphenated_code, par, first_p, cur_p, checked_expansion) -            local p_active = par.active -            local n_active = p_active.next -            if n_active ~= p_active then -                local r = n_active -                par.fewest_demerits = awful_badness -                repeat -- use local d -                    if r.id ~= delta_code and r.total_demerits < par.fewest_demerits then -                        par.fewest_demerits = r.total_demerits -                        par.best_bet = r -                    end -                    r = r.next -                until r == p_active -                par.best_line = par.best_bet.line_number -                local asked_looseness = par.looseness -                if asked_looseness == 0 then -                    return wrap_up(par) -                end -                local r = n_active -                local actual_looseness = 0 -                -- minimize assignments to par but happens seldom -                repeat -                    if r.id ~= delta_code then -                        local line_diff = r.line_number - par.best_line -                        par.line_diff = line_diff -                        if (line_diff < actual_looseness and asked_looseness <= line_diff)   or -                           (line_diff > actual_looseness and asked_looseness >= line_diff) then -                            par.best_bet = r -                            actual_looseness = line_diff -                            par.fewest_demerits = r.total_demerits -                        elseif line_diff == actual_looseness and r.total_demerits < par.fewest_demerits then -                            par.best_bet = r -                            par.fewest_demerits = r.total_demerits -                        end -                    end -                    r = r.next -                until r == p_active -- weird, loop list? -                par.best_line = par.best_bet.line_number -                if actual_looseness == asked_looseness or par.final_pass then -                    return wrap_up(par) -                end -            end -        end -        reset_meta(par) -- clean up the memory by removing the break nodes -        if not par.second_pass then -            if tracing_paragraphs then -                diagnostics.current_pass(par,"secondpass") -            end -            par.threshold = par.tolerance -            par.second_pass = true -            par.final_pass = par.emergency_stretch <= 0 -        else -            if tracing_paragraphs then -                diagnostics.current_pass(par,"emergencypass") -            end -            par.background.stretch = par.background.stretch + par.emergency_stretch -            par.final_pass = true -        end -    end -    return wrap_up(par) -end - --- standard tex logging .. will be adapted .. - -local function write_esc(cs) -    local esc = tex.escapechar -    if esc then -        write("log",utfchar(esc),cs) -    else -        write("log",cs) -    end -end - -function diagnostics.start() -end - -function diagnostics.stop() -    write_nl("log",'') -end - -function diagnostics.current_pass(par,what) -    write_nl("log",format("@%s",what)) -end - -local function short_display(a,font_in_short_display) -    while a do -        local id = a.id -        if id == glyph_code then -            local font = a.font -            if font ~= font_in_short_display then -                write("log",tex.fontidentifier(font) .. ' ') -                font_in_short_display = font -            end -            if a.subtype == ligature_code then -                font_in_short_display = short_display(a.components,font_in_short_display) -            else -                write("log",utfchar(a.char)) -            end ---         elseif id == rule_code then ---             write("log","|") ---         elseif id == glue_code then ---             if a.spec.writable then ---                 write("log"," ") ---             end ---         elseif id == math_code then ---             write("log","$") -        elseif id == disc_code then -            font_in_short_display = short_display(a.pre,font_in_short_display) -            font_in_short_display = short_display(a.post,font_in_short_display) -        else -- no explicit checking -            write("log",format("[%s]",nodecodes[id])) -        end -        a = a.next -    end -    return font_in_short_display -end - -diagnostics.short_display = short_display - -function diagnostics.break_node(par, q, fit_class, break_type, cur_p) -- %d ? -    local passive = par.passive -    local typ_ind = break_type == hyphenated_code and '-' or "" -    if par.do_last_line_fit then -        local s = number.toscaled(q.active_short) -        local g = number.toscaled(q.active_glue) -        if cur_p then -            write_nl("log",format("@@%d: line %d.%d%s t=%s s=%s g=%s", -                passive.serial or 0,q.line_number-1,fit_class,typ_ind,q.total_demerits,s,g)) -        else -            write_nl("log",format("@@%d: line %d.%d%s t=%s s=%s a=%s", -                passive.serial or 0,q.line_number-1,fit_class,typ_ind,q.total_demerits,s,g)) -        end -    else -        write_nl("log",format("@@%d: line %d.%d%s t=%s", -            passive.serial or 0,q.line_number-1,fit_class,typ_ind,q.total_demerits)) -    end -    if not passive.prev_break then -        write("log"," -> @0") -    else -        write("log",format(" -> @%d", passive.prev_break.serial or 0)) -    end -end - -function diagnostics.feasible_break(par, cur_p, r, b, pi, d, artificial_demerits) -    local printed_node = par.printed_node -    if printed_node ~= cur_p then -        write_nl("log","") -        if not cur_p then -            par.font_in_short_display = short_display(printed_node.next,par.font_in_short_display) -        else -            local save_link = cur_p.next -            cur_p.next = nil -            write_nl("log","") -            par.font_in_short_display = short_display(printed_node.next,par.font_in_short_display) -            cur_p.next = save_link -        end -        par.printed_node = cur_p -    end -    write_nl("log","@") -    if not cur_p then -        write_esc("par") -    else -        local id = cur_p.id -        if id == glue_code then -            -- print nothing -        elseif id == penalty_code then -            write_esc("penalty") -        elseif id == disc_code then -            write_esc("discretionary") -        elseif id == kern_code then -            write_esc("kern") -        elseif id == math_code then -            write_esc("math") -        else -            write_esc("unknown") -        end -    end -    local via, badness, demerits = 0, '*', '*' -    if r.break_node then -        via = r.break_node.serial or 0 -    end -    if b <= infinite_badness then -        badness = tonumber(d) -- format("%d", b) -    end -    if not artificial_demerits then -        demerits = tonumber(d) -- format("%d", d) -    end -    write("log",format(" via @%d b=%s p=%s d=%s", via, badness, pi, demerits)) -end - --- reporting -- - -statistics.register("alternative parbuilders", function() -    if nofpars > 0 then -        return format("%s paragraphs, %s lines (%s protruded, %s adjusted)", nofpars, noflines, nofprotrudedlines, nofadjustedlines) -    end -end) - --- actually scaling kerns is not such a good idea and it will become --- configureable - --- This is no way a replacement for the built in (fast) packer --- it's just an alternative for special (testing) purposes. --- --- We could use two hpacks: one to be used in the par builder --- and one to be used for other purposes. The one in the par --- builder is much more simple as it does not need the expansion --- code but only need to register the effective expansion factor --- with the glyph. - -local function glyph_width_height_depth(curdir,pdir,p) -    if is_rotated[curdir] then -        if is_parallel[curdir][pdir] then -            local half = (p.height + p.depth) / 2 -            return p.width, half, half -        else -            local half = p.width / 2 -            return p.height + p.depth, half, half -        end -    elseif is_rotated[pdir] then -        if is_parallel[curdir][pdir] then -            local half = (p.height + p.depth) / 2 -            return p.width, half, half -        else -            return p.height + p.depth, p.width, 0 -- weird -        end -    else -        if glyphdir_is_equal[curdir][pdir] then -            return p.width, p.height, p.depth -        elseif is_opposite[curdir][pdir] then -            return p.width, p.depth, p.height -        else -- can this happen? -            return p.height + p.depth, p.width, 0 -- weird -        end -    end -end - -local function pack_width_height_depth(curdir,pdir,p) -    if is_rotated[curdir] then -        if is_parallel[curdir][pdir] then -            local half = (p.height + p.depth) / 2 -            return p.width, half, half -        else -- can this happen? -            local half = p.width / 2 -            return p.height + p.depth, half, half -        end -    else -        if pardir_is_equal[curdir][pdir] then -            return p.width, p.height, p.depth -        elseif is_opposite[curdir][pdir] then -            return p.width, p.depth, p.height -        else -- weird dimensions, can this happen? -         -- return p.width, p.depth, p.height -            return p.height + p.depth, p.width, 0 -        end -    end -end - --- local function xpack(head,width,method,direction,analysis) --- ---     -- inspect(analysis) --- ---     local expansion         = method == "cal_expand_ratio" ---     local natural           = analysis.size ---     local font_stretch      = analysis.adjust_stretch ---     local font_shrink       = analysis.adjust_shrink ---     local font_expand_ratio = 0 ---     local delta             = width - natural --- ---     local hlist             = new_node("hlist") --- ---     hlist.list              = head ---     hlist.dir               = direction or tex.textdir ---     hlist.width             = width ---     hlist.height            = height ---     hlist.depth             = depth --- ---     if delta == 0 then --- ---         hlist.glue_sign  = 0 ---         hlist.glue_order = 0 ---         hlist.glue_set   = 0 --- ---     else --- ---         local order = analysis.filll ~= 0 and fillcodes.filll or ---                       analysis.fill  ~= 0 and fillcodes.fill  or ---                       analysis.fil   ~= 0 and fillcodes.fil   or ---                       analysis.fi    ~= 0 and fillcodes.fi    or 0 --- ---         if delta > 0 then --- ---             if expansion and order == 0 and font_stretch > 0 then ---                 font_expand_ratio = (delta/font_stretch) * 1000 ---             else ---                 local stretch = analysis.stretch ---                 if stretch ~= 0 then ---                     hlist.glue_sign  = 1 -- stretch ---                     hlist.glue_order = order ---                     hlist.glue_set   = delta/stretch ---                 else ---                     hlist.glue_sign  = 0 -- nothing ---                     hlist.glue_order = order ---                     hlist.glue_set   = 0 ---                 end ---             end --- print("stretch",hlist.glue_sign,hlist.glue_order,hlist.glue_set) --- ---         else --- ---             if expansion and order == 0 and font_shrink > 0 then ---                 font_expand_ratio = (delta/font_shrink) * 1000 ---             else ---                 local shrink = analysis.shrink ---                 if shrink ~= 0 then ---                     hlist.glue_sign  = 2 -- shrink ---                     hlist.glue_order = order ---                     hlist.glue_set   = - delta/shrink ---                 else ---                     hlist.glue_sign  = 0 -- nothing ---                     hlist.glue_order = order ---                     hlist.glue_set   = 0 ---                 end ---             end --- print("shrink",hlist.glue_sign,hlist.glue_order,hlist.glue_set) --- ---         end --- ---     end --- ---     if not expansion or font_expand_ratio == 0 then ---         -- nothing ---     elseif font_expand_ratio > 0 then ---         if font_expand_ratio > 1000 then ---             font_expand_ratio = 1000 ---         end ---         local current = head ---         while current do ---             local id = current.id ---             if id == glyph_code then ---                 local stretch, shrink = char_stretch_shrink(current) -- get only one ---                 if stretch then ---                     if trace_expansion then ---                         setnodecolor(g,"hz:positive") ---                     end ---                     current.expansion_factor = font_expand_ratio * stretch ---                 end ---             elseif id == kern_code then ---                 local kern = current.kern ---                 if kern ~= 0 and current.subtype == kerning_code then ---                     current.kern = font_expand_ratio * current.kern ---                 end ---             end ---             current = current.next ---         end ---     elseif font_expand_ratio < 0 then ---         if font_expand_ratio < -1000 then ---             font_expand_ratio = -1000 ---         end ---         local current = head ---         while current do ---             local id = current.id ---             if id == glyph_code then ---                 local stretch, shrink = char_stretch_shrink(current) -- get only one ---                 if shrink then ---                     if trace_expansion then ---                         setnodecolor(g,"hz:negative") ---                     end ---                     current.expansion_factor = font_expand_ratio * shrink ---                 end ---             elseif id == kern_code then ---                 local kern = current.kern ---                 if kern ~= 0 and current.subtype == kerning_code then ---                     current.kern = font_expand_ratio * current.kern ---                 end ---             end ---             current = current.next ---         end ---     end ---     return hlist, 0 --- end - -local function hpack(head,width,method,direction) -- fast version when head = nil - -    -- we can pass the adjust_width and adjust_height so that we don't need to recalculate them but -    -- with the glue mess it's less trivial as we lack detail - -    local hlist = new_node("hlist") - -    if head == nil then -        return hlist, 0 -    end - -    local cal_expand_ratio  = method == "cal_expand_ratio" or method == "subst_ex_font" - -    direction               = direction or tex.textdir - -    local line              = 0 - -    local height            = 0 -    local depth             = 0 -    local natural           = 0 -    local font_stretch      = 0 -    local font_shrink       = 0 -    local font_expand_ratio = 0 -    local last_badness      = 0 -    local disc_stack        = { } -    local disc_level        = 0 -    local expansion_stack   = cal_expand_ratio and { } -- todo: optionally pass this -    local expansion_index   = 0 -    local total_stretch     = { [0] = 0, 0, 0, 0, 0 } -    local total_shrink      = { [0] = 0, 0, 0, 0, 0 } - -    local hpack_dir         = direction - -    local adjust_head       = texlists.adjust_head -    local pre_adjust_head   = texlists.pre_adjust_head -    local adjust_tail       = adjust_head and slide_nodes(adjust_head) -    local pre_adjust_tail   = pre_adjust_head and slide_nodes(pre_adjust_head) - -    hlist.list = head -    hlist.dir  = hpack_dir - -    new_dir_stack(hpack_dir) - -    local checked_expansion = false - -    if cal_expand_ratio then -        checked_expansion = { } -        setmetatableindex(checked_expansion,check_expand_lines) -    end - -    -- this one also needs to check the font, so in the end indeed we might end up with two variants - -    local fontexps, lastfont - -    local current = head - -    while current do -        local id = current.id -        if id == glyph_code then -            if cal_expand_ratio then -                local currentfont = current.font -                if currentfont ~= lastfont then -                    fontexps = checked_expansion[currentfont] -- a bit redundant for the par line packer -                    lastfont = currentfont -                end -                if fontexps then -                    local expansion = fontexps[current.char] -                    if expansion then -                        font_stretch = font_stretch + expansion.glyphstretch -                        font_shrink  = font_shrink  + expansion.glyphshrink -                        expansion_index = expansion_index + 1 -                        expansion_stack[expansion_index] = current -                    end -                end -            end -            -- use inline if no expansion -            local wd, ht, dp = glyph_width_height_depth(hpack_dir,"TLT",current) -- was TRT ? -            natural = natural + wd -            if ht > height then -                height = ht -            end -            if dp > depth then -                depth = dp -            end -            current = current.next -        elseif id == kern_code then -            local kern = current.kern -            if kern == 0 then -                -- no kern -            else -                if cal_expand_ratio and expand_kerns and current.subtype == kerning_code or current[a_fontkern] then -- check p.kern -                    local stretch, shrink = kern_stretch_shrink(current,kern) -                    if expand_kerns == "stretch" then -                        font_stretch = font_stretch + stretch -                    elseif expand_kerns == "shrink" then -                        font_shrink  = font_shrink + shrink -                    else -                        font_stretch = font_stretch + stretch -                        font_shrink  = font_shrink + shrink -                    end -                    expansion_index = expansion_index + 1 -                    expansion_stack[expansion_index] = current -                end -                natural = natural + kern -            end -            current = current.next -        elseif id == disc_code then -            if current.subtype ~= second_disc_code then -                -- we follow the end of line disc chain -                local replace = current.replace -                if replace then -                    disc_level = disc_level + 1 -                    disc_stack[disc_level] = current.next -                    current = replace -                else -                    current = current.next -                end -            else -                current = current.next -            end -        elseif id == glue_code then -            local spec = current.spec -            natural = natural + spec.width -            local op = spec.stretch_order -            local om = spec.shrink_order -            total_stretch[op] = total_stretch[op] + spec.stretch -            total_shrink [om] = total_shrink [om] + spec.shrink -            if current.subtype >= leaders_code then -                local leader = current.leader -                local ht = leader.height -                local dp = leader.depth -                if ht > height then -                    height = ht -                end -                if dp > depth then -                    depth = dp -                end -            end -            current = current.next -        elseif id == hlist_code or id == vlist_code then -            local sh = current.shift -            local wd, ht, dp = pack_width_height_depth(hpack_dir,current.dir or hpack_dir,current) -- added: or pack_dir -            local hs, ds = ht - sh, dp + sh -            natural = natural + wd -            if hs > height then -                height = hs -            end -            if ds > depth then -                depth = ds -            end -            current = current.next -        elseif id == rule_code then -            local wd = current.width -            local ht = current.height -            local dp = current.depth -            natural = natural + wd -            if ht > height then -                height = ht -            end -            if dp > depth then -                depth = dp -            end -            current = current.next -        elseif id == math_code then -            natural = natural + current.surround -            current = current.next -        elseif id == unset_code then -            local wd = current.width -            local ht = current.height -            local dp = current.depth -            local sh = current.shift -            local hs = ht - sh -            local ds = dp + sh -            natural = natural + wd -            if hs > height then -                height = hs -            end -            if ds > depth then -                depth = ds -            end -            current = current.next -        elseif id == ins_code or id == mark_code then -            local prev = current.prev -            local next = current.next -            if adjust_tail then -- todo -                if next then -                    next.prev = prev -                end -                if prev then -                    prev.next = next -                end -                current.prev = adjust_tail -                current.next = nil -                adjust_tail.next = current -                adjust_tail = current -            else -                adjust_head = current -                adjust_tail = current -                current.prev = nil -                current.next = nil -            end -            current = next -        elseif id == adjust_code then -            local list = current.list -            if adjust_tail then -                adjust_tail.next = list -                adjust_tail = slide_nodes(list) -            else -                adjust_head = list -                adjust_tail = slide_nodes(list) -            end -            current = current.next -        elseif id == whatsit_code then -            local subtype = current.subtype -            if subtype == dir_code then -                hpack_dir = checked_line_dir(stack,current) or hpack_dir -            else -                local get_dimensions = get_whatsit_dimensions[subtype] -                if get_dimensions then -                    local wd, ht, dp = get_dimensions(current) -                    natural = natural + wd -                    if ht > height then -                        height = ht -                    end -                    if dp > depth then -                        depth = dp -                    end -                end -            end -            current = current.next -        elseif id == marginkern_code then -            if cal_expand_ratio then -                local glyph = current.glyph -                local char_pw = current.subtype == leftmargin_code and left_pw or right_pw -                font_stretch = font_stretch - current.width - char_pw(glyph) -                font_shrink  = font_shrink  - current.width - char_pw(glyph) -                expansion_index = expansion_index + 1 -                expansion_stack[expansion_index] = glyph -            end -            natural = natural + current.width -            current = current.next -        else -            current = current.next -        end -        if not current and disc_level > 0 then -            current = disc_stack[disc_level] -            disc_level = disc_level - 1 -        end -    end -    if adjust_tail then -        adjust_tail.next = nil -- todo -    end -    if pre_adjust_tail then -        pre_adjust_tail.next = nil -- todo -    end -    if mode == "additional" then -        width = width + natural -    end - -    hlist.width  = width -    hlist.height = height -    hlist.depth  = depth - -    local delta  = width - natural -    if delta == 0 then -        hlist.glue_sign = 0 -        hlist.glue_order = 0 -        hlist.glue_set = 0 -    elseif delta > 0 then -        -- natural width smaller than requested width -        local order = (total_stretch[4] ~= 0 and 4 or total_stretch[3] ~= 0 and 3) or -                      (total_stretch[2] ~= 0 and 2 or total_stretch[1] ~= 0 and 1) or 0 ---         local correction = 0 -        if cal_expand_ratio and order == 0 and font_stretch > 0 then -- check sign of font_stretch -            font_expand_ratio = delta/font_stretch - -            if font_expand_ratio > 1 then -                font_expand_ratio = 1 -            end - -            local fontexps, lastfont -            for i=1,expansion_index do -                local g = expansion_stack[i] -                local e -                if g.id == glyph_code then -                    local currentfont = g.font -                    if currentfont ~= lastfont then -                        fontexps = expansions[currentfont] -                        lastfont = currentfont -                    end -                    local data = fontexps[g.char] -                    if trace_expansion then -                        setnodecolor(g,"hz:positive") -                    end -                    e = font_expand_ratio * data.glyphstretch / 1000 ---                     correction = correction + (e / 1000) * g.width -                else -                    local kern = g.kern -                    local stretch, shrink = kern_stretch_shrink(g,kern) -                    e = font_expand_ratio * stretch / 1000 ---                     correction = correction + (e / 1000) * kern -                end -                g.expansion_factor = e -            end -        end ---         delta = delta - correction -        local tso = total_stretch[order] -        if tso ~= 0 then -            hlist.glue_sign  = 1 -            hlist.glue_order = order -            hlist.glue_set   = delta/tso -        else -            hlist.glue_sign  = 0 -            hlist.glue_order = order -            hlist.glue_set   = 0 -        end -        if font_expand_ratio ~= 0 then -            -- todo -        elseif order == 0 then -- and hlist.list then -            last_badness = calculate_badness(delta,total_stretch[0]) -            if last_badness > tex.hbadness then -                if last_badness > 100 then -                    diagnostics.underfull_hbox(hlist,line,last_badness) -                else -                    diagnostics.loose_hbox(hlist,line,last_badness) -                end -            end -        end -    else -        -- natural width larger than requested width -        local order = total_shrink[4] ~= 0 and 4 or total_shrink[3] ~= 0 and 3 -                   or total_shrink[2] ~= 0 and 2 or total_shrink[1] ~= 0 and 1 or 0 ---         local correction = 0 -        if cal_expand_ratio and order == 0 and font_shrink > 0 then -- check sign of font_shrink -            font_expand_ratio = delta/font_shrink - -            if font_expand_ratio < 1 then -                font_expand_ratio = -1 -            end - -            local fontexps, lastfont -            for i=1,expansion_index do -                local g = expansion_stack[i] -                local e -                if g.id == glyph_code then -                    local currentfont = g.font -                    if currentfont ~= lastfont then -                        fontexps = expansions[currentfont] -                        lastfont = currentfont -                    end -                    local data = fontexps[g.char] -                    if trace_expansion then -                        setnodecolor(g,"hz:negative") -                    end -                    e = font_expand_ratio * data.glyphshrink / 1000 -                 -- local d = (e / 1000) * 1000 -                 -- local eps = g.width - (1 + d / 1000000) * g.width -                 -- correction = correction + eps -                 -- e = d ---                     correction = correction + (e / 1000) * g.width -                else -                    local kern = g.kern -                    local stretch, shrink = kern_stretch_shrink(g,kern) -                    e = font_expand_ratio * shrink / 1000 ---                     correction = correction + (e / 1000) * kern -                end -                g.expansion_factor = e -            end -        end ---         delta = delta - correction -        local tso = total_shrink[order] -        if tso ~= 0 then -            hlist.glue_sign  = 2 -            hlist.glue_order = order -            hlist.glue_set   = -delta/tso -        else -            hlist.glue_sign  = 0 -            hlist.glue_order = order -            hlist.glue_set   = 0 -        end -        if font_expand_ratio ~= 0 then -            -- todo -        elseif tso < -delta and order == 0 then -- and hlist.list then -            last_badness = 1000000 -            hlist.glue_set = 1 -            local fuzz = - delta - total_shrink[0] -            local hfuzz = tex.hfuzz -            if fuzz > hfuzz or tex.hbadness < 100 then -                local overfullrule = tex.overfullrule -                if fuzz > hfuzz and overfullrule > 0 then -                    -- weird, is always called and no rules shows up -                    slide_nodes(list).next = new_rule(overfullrule,nil,nil,hlist.dir) -                end -                diagnostics.overfull_hbox(hlist,line,-delta) -            end -        elseif order == 0 and hlist.list and last_badness > tex.hbadness then -            diagnostics.bad_hbox(hlist,line,last_badness) -        end -    end -    return hlist, last_badness -end - -xpack_nodes = hpack -- comment this for old fashioned expansion - -local function common_message(hlist,line,str) -    write_nl("") -    if status.output_active then -- unset -        write(str," has occurred while \\output is active") -    end -    local fileline = status.linenumber -    if line > 0 then -        write(str," in paragraph at lines ",fileline,"--",fileline+line-1) -    elseif line < 0 then -        write(str," in alignment at lines ",fileline,"--",fileline-line-1) -    else -        write(str," detected at line ",fileline) -    end -    write_nl("") -    diagnostics.short_display(hlist.list,false) -    write_nl("") - -- diagnostics.start() - -- show_box(hlist.list) - -- diagnostics.stop() -end - -function diagnostics.overfull_hbox(hlist,line,d) -    common_message(hlist,line,format("Overfull \\hbox (%spt too wide)",number.toscaled(d))) -end - -function diagnostics.bad_hbox(hlist,line,b) -    common_message(hlist,line,format("Tight \\hbox (badness %i)",b)) -end - -function diagnostics.underfull_hbox(hlist,line,b) -    common_message(hlist,line,format("Underfull \\hbox (badness %i)",b)) -end - -function diagnostics.loose_hbox(hlist,line,b) -    common_message(hlist,line,format("Loose \\hbox (badness %i)",b)) -end - --- e = font_expand_ratio * data.glyphstretch / 1000 --- local stretch = data.stretch --- if e >= stretch then ---     e = stretch --- else ---     local step = 5 ---     e = math.round(e/step) * step --- end - --- local shrink = - data.shrink --- if e <= shrink then ---     e = shrink --- else ---     local step = 5 ---     e = math.round(e/step) * step --- end diff --git a/tex/context/base/node-mig.lua b/tex/context/base/node-mig.lua index 9fc35a048..2b35335c3 100644 --- a/tex/context/base/node-mig.lua +++ b/tex/context/base/node-mig.lua @@ -6,15 +6,32 @@ if not modules then modules = { } end modules ['node-mig'] = {      license   = "see context related readme files"  } +-- todo: insert_after +  local format = string.format -local attributes, nodes, node = attributes, nodes, node +local trace_migrations = false trackers.register("nodes.migrations", function(v) trace_migrations = v end) -local remove_nodes  = nodes.remove +local report_nodes = logs.reporter("nodes","migrations") -local nodecodes     = nodes.nodecodes +local attributes    = attributes +local nodes         = nodes  local tasks         = nodes.tasks +local nuts          = nodes.nuts +local tonut         = nuts.tonut + +local getnext       = nuts.getnext +local getid         = nuts.getid +local getlist       = nuts.getlist +local getattr       = nuts.getattr + +local setfield      = nuts.setfield +local setattr       = nuts.setattr + +local remove_nodes  = nuts.remove + +local nodecodes     = nodes.nodecodes  local hlist_code    = nodecodes.hlist  local vlist_code    = nodecodes.vlist  local insert_code   = nodecodes.ins @@ -22,10 +39,6 @@ local mark_code     = nodecodes.mark  local a_migrated    = attributes.private("migrated") -local trace_migrations = false trackers.register("nodes.migrations", function(v) trace_migrations = v end) - -local report_nodes = logs.reporter("nodes","migrations") -  local migrate_inserts, migrate_marks, inserts_too  local t_inserts, t_marks, t_sweeps = 0, 0, 0 @@ -33,32 +46,42 @@ local t_inserts, t_marks, t_sweeps = 0, 0, 0  local function locate(head,first,last,ni,nm)      local current = head      while current do -        local id = current.id +        local id = getid(current)          if id == vlist_code or id == hlist_code then -            current.list, first, last, ni, nm = locate(current.list,first,last,ni,nm) -            current = current.next +            local list = getlist(current) +            if list then +                list, first, last, ni, nm = locate(list,first,last,ni,nm) +                setfield(current,"list",list) +            end +            current = getnext(current)          elseif migrate_inserts and id == insert_code then              local insert              head, current, insert = remove_nodes(head,current) -            insert.next = nil +            setfield(insert,"next",nil)              if first then -                insert.prev, last.next = last, insert +                setfield(insert,"prev",last) +                setfield(last,"next",insert)              else -                insert.prev, first = nil, insert +                setfield(insert,"prev",nil) +                first = insert              end -            last, ni = insert, ni + 1 +            last = insert +            ni = ni + 1          elseif migrate_marks and id == mark_code then              local mark              head, current, mark = remove_nodes(head,current) -            mark.next = nil +            setfield(mark,"next",nil)              if first then -                mark.prev, last.next = last, mark +                setfield(mark,"prev",last) +                setfield(last,"next",mark)              else -                mark.prev, first = nil, mark +                setfield(mark,"prev",nil) +                first = mark              end -            last, nm = mark, nm + 1 +            last = mark +            nm = nm + 1          else -            current= current.next +            current = getnext(current)          end      end      return head, first, last, ni, nm @@ -70,39 +93,43 @@ function nodes.handlers.migrate(head,where)          if trace_migrations then              report_nodes("migration sweep %a",where)          end -        local current = head +        local current = tonut(head)          while current do -            local id = current.id +            local id = getid(current)              -- inserts_too is a temp hack, we should only do them when it concerns              -- newly placed (flushed) inserts -            if id == vlist_code or id == hlist_code or (inserts_too and id == insert_code) and not current[a_migrated] then -                current[a_migrated] = 1 +            if id == vlist_code or id == hlist_code or (inserts_too and id == insert_code) and not getattr(current,a_migrated) then +                setattr(current,a_migrated,1)                  t_sweeps = t_sweeps + 1 -                local h = current.list +                local h = getlist(current)                  local first, last, ni, nm                  while h do -                    local id = h.id +                    local id = getid(h)                      if id == vlist_code or id == hlist_code then                          h, first, last, ni, nm = locate(h,first,last,0,0)                      end -                    h = h.next +                    h = getnext(h)                  end                  if first then -                    t_inserts, t_marks = t_inserts + ni, t_marks + nm +                    t_inserts = t_inserts + ni +                    t_marks = t_marks + nm                      if trace_migrations and (ni > 0 or nm > 0) then                          report_nodes("sweep %a, container %a, %s inserts and %s marks migrated outwards during %a",                              t_sweeps,nodecodes[id],ni,nm,where)                      end -                    -- inserts after head -                    local n = current.next +                    -- inserts after head, use insert_after +                    local n = getnext(current)                      if n then -                        last.next, n.prev = n, last +                        setfield(last,"next",n) +                        setfield(n,"prev",last)                      end -                    current.next, first.prev = first, current -                    done, current = true, last +                    setfield(current,"next",first) +                    setfield(first,"prev",current) +                    done = true +                    current = last                  end              end -            current = current.next +            current = getnext(next)          end          return head, done      end diff --git a/tex/context/base/node-pro.lua b/tex/context/base/node-pro.lua index aa6692d7b..2cc00601c 100644 --- a/tex/context/base/node-pro.lua +++ b/tex/context/base/node-pro.lua @@ -13,15 +13,15 @@ local trace_callbacks = false  trackers.register("nodes.callbacks", function(v)  local report_nodes = logs.reporter("nodes","processors") -local nodes, node = nodes, node +local nodes = nodes  local nodecodes     = nodes.nodecodes  local glyph_code    = nodecodes.glyph  local tasks         = nodes.tasks +local nuts          = nodes.nuts -local free_node     = node.free -local first_glyph   = node.first_glyph or node.first_character -local has_attribute = node.has_attribute +local first_glyph   = nodes.first_glyph +local has_glyph     = nodes.has_glyph  nodes.processors    = nodes.processors or { }  local processors    = nodes.processors @@ -31,43 +31,53 @@ local processors    = nodes.processors  local actions = tasks.actions("processors") -local n = 0 +do -local function reconstruct(head) -- we probably have a better one -    local t, n, h = { }, 0, head -    while h do +    local tonut   = nuts.tonut +    local getid   = nuts.getid +    local getchar = nuts.getchar +    local getnext = nuts.getnext + +    local n = 0 + +    local function reconstruct(head) -- we probably have a better one +        local t, n, h = { }, 0, tonut(head) +        while h do +            n = n + 1 +            local id = getid(h) +            if id == glyph_code then -- todo: disc etc +                t[n] = utfchar(getchar(h)) +            else +                t[n] = "[]" +            end +            h = getnext(h) +        end +        return concat(t) +    end + +    local function tracer(what,state,head,groupcode,before,after,show) +        if not groupcode then +            groupcode = "unknown" +        elseif groupcode == "" then +            groupcode = "mvl" +        end          n = n + 1 -        local id = h.id -        if id == glyph_code then -- todo: disc etc -            t[n] = utfchar(h.char) +        if show then +            report_nodes("%s: location %a, state %a, group %a, # before %a, # after %s, stream: %s",what,n,state,groupcode,before,after,reconstruct(head))          else -            t[n] = "[]" +            report_nodes("%s: location %a, state %a, group %a, # before %a, # after %s",what,n,state,groupcode,before,after)          end -        h = h.next      end -    return concat(t) -end -local function tracer(what,state,head,groupcode,before,after,show) -    if not groupcode then -        groupcode = "unknown" -    elseif groupcode == "" then -        groupcode = "mvl" -    end -    n = n + 1 -    if show then -        report_nodes("%s: location %a, state %a, group %a, # before %a, # after %s, stream: %s",what,n,state,groupcode,before,after,reconstruct(head)) -    else -        report_nodes("%s: location %a, state %a, group %a, # before %a, # after %s",what,n,state,groupcode,before,after) -    end -end +    processors.tracer = tracer -processors.tracer = tracer +end  processors.enabled = true -- this will become a proper state (like trackers)  function processors.pre_linebreak_filter(head,groupcode) -- ,size,packtype,direction -    local first, found = first_glyph(head) -- they really need to be glyphs + -- local first, found = first_glyph(head) -- they really need to be glyphs +    local found = has_glyph(head)      if found then          if trace_callbacks then              local before = nodes.count(head,true) @@ -94,10 +104,8 @@ local enabled = true  function processors.hpack_filter(head,groupcode,size,packtype,direction)      if enabled then -     -- if not head.next and head.id ~= glyph_code then -- happens often but not faster -     --     return true -     -- end -        local first, found = first_glyph(head) -- they really need to be glyphs +     -- local first, found = first_glyph(head) -- they really need to be glyphs +        local found = has_glyph(head)          if found then              if trace_callbacks then                  local before = nodes.count(head,true) @@ -121,15 +129,36 @@ function processors.hpack_filter(head,groupcode,size,packtype,direction)      return true  end -local hpack = node.hpack +do + +    local setfield = nodes.setfield +    local hpack    = nodes.hpack + +    function nodes.fasthpack(...) -- todo: pass explicit arguments +        enabled = false +        local hp, b = hpack(...) +        setfield(hp,"prev",nil) +        setfield(hp,"next",nil) +        enabled = true +        return hp, b +    end + +end + +do + +    local setfield = nuts.setfield +    local hpack    = nuts.hpack + +    function nuts.fasthpack(...) -- todo: pass explicit arguments +        enabled = false +        local hp, b = hpack(...) +        setfield(hp,"prev",nil) +        setfield(hp,"next",nil) +        enabled = true +        return hp, b +    end -function nodes.fasthpack(...) -- todo: pass explicit arguments -    enabled = false -    local hp, b = hpack(...) -    hp.prev = nil -    hp.next = nil -    enabled = true -    return hp, b  end  callbacks.register('pre_linebreak_filter', processors.pre_linebreak_filter, "all kind of horizontal manipulations (before par break)") diff --git a/tex/context/base/node-ref.lua b/tex/context/base/node-ref.lua index aa864fb1c..7cfbde849 100644 --- a/tex/context/base/node-ref.lua +++ b/tex/context/base/node-ref.lua @@ -21,7 +21,6 @@ local attributes, nodes, node = attributes, nodes, node  local allocate            = utilities.storage.allocate, utilities.storage.mark  local mark                = utilities.storage.allocate, utilities.storage.mark -  local nodeinjections      = backends.nodeinjections  local codeinjections      = backends.codeinjections @@ -33,9 +32,6 @@ local colors              = attributes.colors  local references          = structures.references  local tasks               = nodes.tasks -local hpack_list          = node.hpack -local list_dimensions     = node.dimensions -  local trace_backend       = false  trackers.register("nodes.backend",      function(v) trace_backend      = v end)  local trace_references    = false  trackers.register("nodes.references",   function(v) trace_references   = v end)  local trace_destinations  = false  trackers.register("nodes.destinations", function(v) trace_destinations = v end) @@ -44,6 +40,27 @@ local report_reference    = logs.reporter("backend","references")  local report_destination  = logs.reporter("backend","destinations")  local report_area         = logs.reporter("backend","areas") +local nuts                = nodes.nuts +local nodepool            = nuts.pool + +local tonode              = nuts.tonode +local tonut               = nuts.tonut + +local getfield            = nuts.getfield +local setfield            = nuts.setfield +local getnext             = nuts.getnext +local getprev             = nuts.getprev +local getid               = nuts.getid +local getlist             = nuts.getlist +local getattr             = nuts.getattr +local setattr             = nuts.setattr +local getsubtype          = nuts.getsubtype + +local hpack_list          = nuts.hpack +local list_dimensions     = nuts.dimensions +local traverse            = nuts.traverse +local find_node_tail      = nuts.tail +  local nodecodes           = nodes.nodecodes  local skipcodes           = nodes.skipcodes  local whatcodes           = nodes.whatcodes @@ -63,21 +80,18 @@ local dir_code            = whatcodes.dir  local line_code           = listcodes.line -local nodepool            = nodes.pool - +local new_rule            = nodepool.rule  local new_kern            = nodepool.kern -local traverse            = node.traverse -local find_node_tail      = node.tail or node.slide  local tosequence          = nodes.tosequence  -- local function dimensions(parent,start,stop) ---     stop = stop and stop.next +--     stop = stop and getnext(stop)  --     if parent then  --         if stop then ---             return list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,start,stop) +--             return list_dimensions(getfield(parent,"glue_set"),getfield(parent,"glue_sign"),getfield(parent,"glue_order"),start,stop)  --         else ---             return list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,start) +--             return list_dimensions(getfield(parent,"glue_set"),getfield(parent,"glue_sign",getfield(parent,"glue_order"),start)  --         end  --     else  --         if stop then @@ -92,9 +106,9 @@ local tosequence          = nodes.tosequence  local function dimensions(parent,start,stop)      if parent then -        return list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,start,stop and stop.next) +        return list_dimensions(getfield(parent,"glue_set"),getfield(parent,"glue_sign"),getfield(parent,"glue_order"),start,stop and getnext(stop))      else -        return list_dimensions(start,stop and stop.next) +        return list_dimensions(start,stop and getnext(stop))      end  end @@ -111,25 +125,25 @@ local function inject_range(head,first,last,reference,make,stack,parent,pardir,t              if trace_backend then                  report_area("head: %04i %s %s %s => w=%p, h=%p, d=%p, c=%s",reference,pardir or "---",txtdir or "----",tosequence(first,last,true),width,height,depth,resolved)              end -            result.next = first -            first.prev = result +            setfield(result,"next",first) +            setfield(first,"prev",result)              return result, last          else              if trace_backend then                  report_area("middle: %04i %s %s => w=%p, h=%p, d=%p, c=%s",reference,pardir or "---",txtdir or "----",tosequence(first,last,true),width,height,depth,resolved)              end -            local prev = first.prev +            local prev = getprev(first)              if prev then -                result.next = first -                result.prev = prev -                prev.next = result -                first.prev = result +                setfield(result,"next",first) +                setfield(result,"prev",prev) +                setfield(prev,"next",result) +                setfield(first,"prev",result)              else -                result.next = first -                first.prev = result +                setfield(result,"next",first) +                setfield(first,"prev",result)              end -            if first == head.next then -                head.next = result -- hm, weird +            if first == getnext(head) then +                setfield(head,"next",result) -- hm, weird              end              return head, last          end @@ -139,9 +153,9 @@ local function inject_range(head,first,last,reference,make,stack,parent,pardir,t  end  local function inject_list(id,current,reference,make,stack,pardir,txtdir) -    local width, height, depth, correction = current.width, current.height, current.depth, 0 +    local width, height, depth, correction = getfield(current,"width"), getfield(current,"height"), getfield(current,"depth"), 0      local moveright = false -    local first = current.list +    local first = getlist(current)      if id == hlist_code then -- box_code line_code          -- can be either an explicit hbox or a line and there is no way          -- to recognize this; anyway only if ht/dp (then inline) @@ -149,17 +163,17 @@ local function inject_list(id,current,reference,make,stack,pardir,txtdir)          if first then              if sr and sr[2] then                  local last = find_node_tail(first) -                if last.id == glue_code and last.subtype == rightskip_code then -                    local prev = last.prev -                    moveright = first.id == glue_code and first.subtype == leftskip_code -                    if prev and prev.id == glue_code and prev.subtype == parfillskip_code then -                        width = dimensions(current,first,prev.prev) -- maybe not current as we already take care of it +                if getid(last) == glue_code and getsubtype(last) == rightskip_code then +                    local prev = getprev(last) +                    moveright = getid(first) == glue_code and getsubtype(first) == leftskip_code +                    if prev and getid(prev) == glue_code and getsubtype(prev) == parfillskip_code then +                        width = dimensions(current,first,getprev(prev)) -- maybe not current as we already take care of it                      else -                        if moveright and first.writable then -                            width = width - first.spec.stretch*current.glue_set * current.glue_sign +                        if moveright and getfield(first,"writable") then +                            width = width - getfield(getfield(first,"spec"),"stretch") * getfield(current,"glue_set") * getfield(current,"glue_sign")                          end -                        if last.writable then -                            width = width - last.spec.stretch*current.glue_set * current.glue_sign +                        if getfield(last,"writable") then +                            width = width - getfield(getfield(last,"spec"),"stretch") * getfield(current,"glue_set") * getfield(current,"glue_sign")                          end                      end                  end @@ -184,19 +198,21 @@ local function inject_list(id,current,reference,make,stack,pardir,txtdir)              report_area("box: %04i %s %s: w=%p, h=%p, d=%p, c=%s",reference,pardir or "---",txtdir or "----",width,height,depth,resolved)          end          if not first then -            current.list = result +            setfield(current,"list",result)          elseif moveright then -- brr no prevs done              -- result after first -            local n = first.next -            result.next = n -            first.next = result -            result.prev = first -            if n then n.prev = result end +            local n = getnext(first) +            setfield(result,"next",n) +            setfield(first,"next",result) +            setfield(result,"prev",first) +            if n then +                setfield(n,"prev",result) +            end          else              -- first after result -            result.next = first -            first.prev = result -            current.list = result +            setfield(result,"next",first) +            setfield(first,"prev",result) +            setfield(current,"list",result)          end      end  end @@ -209,9 +225,9 @@ local function inject_areas(head,attribute,make,stack,done,skip,parent,pardir,tx          pardir = pardir or "==="          txtdir = txtdir or "==="          while current do -            local id = current.id +            local id = getid(current)              if id == hlist_code or id == vlist_code then -                local r = current[attribute] +                local r = getattr(current,attribute)                  -- somehow reference is true so the following fails (second one not done) in                  --    test \goto{test}[page(2)] test \gotobox{test}[page(2)]                  -- so let's wait till this fails again @@ -222,32 +238,33 @@ local function inject_areas(head,attribute,make,stack,done,skip,parent,pardir,tx                  if r then                      done[r] = (done[r] or 0) + 1                  end -                local list = current.list +                local list = getlist(current)                  if list then -                    local _ -                    current.list, _, pardir, txtdir = inject_areas(list,attribute,make,stack,done,r or skip or 0,current,pardir,txtdir) +                    local h, ok +                    h, ok , pardir, txtdir = inject_areas(list,attribute,make,stack,done,r or skip or 0,current,pardir,txtdir) +                    setfield(current,"list",h)                  end                  if r then                      done[r] = done[r] - 1                  end              elseif id == whatsit_code then -                local subtype = current.subtype +                local subtype = getsubtype(current)                  if subtype == localpar_code then -                    pardir = current.dir +                    pardir = getfield(current,"dir")                  elseif subtype == dir_code then -                    txtdir = current.dir +                    txtdir = getfield(current,"dir")                  end -            elseif id == glue_code and current.subtype == leftskip_code then -- any glue at the left? +            elseif id == glue_code and getsubtype(current) == leftskip_code then -- any glue at the left?                  --              else -                local r = current[attribute] +                local r = getattr(current,attribute)                  if not r then                      -- just go on, can be kerns                  elseif not reference then                      reference, first, last, firstdir = r, current, current, txtdir                  elseif r == reference then                      last = current -                elseif (done[reference] or 0) == 0 then -- or id == glue_code and current.subtype == right_skip_code +                elseif (done[reference] or 0) == 0 then -- or id == glue_code and getsubtype(current) == right_skip_code                      if not skip or r > skip then -- maybe no > test                          head, current = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir)                          reference, first, last, firstdir = nil, nil, nil, nil @@ -256,7 +273,7 @@ local function inject_areas(head,attribute,make,stack,done,skip,parent,pardir,tx                      reference, first, last, firstdir = r, current, current, txtdir                  end              end -            current = current.next +            current = getnext(current)          end          if reference and (done[reference] or 0) == 0 then              head = inject_range(head,first,last,reference,make,stack,parent,pardir,firstdir) @@ -271,32 +288,32 @@ local function inject_area(head,attribute,make,stack,done,parent,pardir,txtdir)          txtdir = txtdir or "==="          local current = head          while current do -            local id = current.id +            local id = getid(current)              if id == hlist_code or id == vlist_code then -                local r = current[attribute] +                local r = getattr(current,attribute)                  if r and not done[r] then                      done[r] = true                      inject_list(id,current,r,make,stack,pardir,txtdir)                  end -                local list = current.list +                local list = getlist(current)                  if list then -                    current.list = inject_area(list,attribute,make,stack,done,current,pardir,txtdir) +                    setfield(current,"list",inject_area(list,attribute,make,stack,done,current,pardir,txtdir))                  end              elseif id == whatsit_code then -                local subtype = current.subtype +                local subtype = getsubtype(current)                  if subtype == localpar_code then -                    pardir = current.dir +                    pardir = getfield(current,"dir")                  elseif subtype == dir_code then -                    txtdir = current.dir +                    txtdir = getfield(current,"dir")                  end              else -                local r = current[attribute] +                local r = getattr(current,attribute)                  if r and not done[r] then                      done[r] = true                      head, current = inject_range(head,current,current,r,make,stack,parent,pardir,txtdir)                  end              end -            current = current.next +            current = getnext(current)          end      end      return head, true @@ -304,12 +321,6 @@ end  -- tracing -local nodepool       = nodes.pool - -local new_rule       = nodepool.rule -local new_kern       = nodepool.kern - -local set_attribute  = node.set_attribute  local register_color = colors.register  local a_color        = attributes.private('color') @@ -346,15 +357,15 @@ local function colorize(width,height,depth,n,reference,what)          height = 65536/2          depth  = height      end -    local rule = new_rule(width,height,depth) -    rule[a_colormodel] = 1 -- gray color model -    rule[a_color] = u_color -    rule[a_transparency] = u_transparency +    local rule = new_rule(width,height,depth) -- todo: use tracer rule +    setattr(rule,a_colormodel,1) -- gray color model +    setattr(rule,a_color,u_color) +    setattr(rule,a_transparency,u_transparency)      if width < 0 then          local kern = new_kern(width) -        rule.width = -width -        kern.next = rule -        rule.prev = kern +        setfield(rule,"width",-width) +        setfield(kern,"next",rule) +        setfield(rule,"prev",kern)          return kern      else          return rule @@ -363,9 +374,6 @@ end  -- references: -local nodepool        = nodes.pool -local new_kern        = nodepool.kern -  local texsetattribute = tex.setattribute  local texsetcount     = tex.setcount @@ -410,22 +418,25 @@ local function makereference(width,height,depth,reference)          end          local annot = nodeinjections.reference(width,height,depth,set)          if annot then +annot = tonut(annot)              nofreferences = nofreferences + 1              local result, current              if trace_references then                  local step = 65536                  result = hpack_list(colorize(width,height-step,depth-step,2,reference,"reference")) -- step subtracted so that we can see seperate links -                result.width = 0 +                setfield(result,"width",0)                  current = result              end              if current then -                current.next = annot +                setfield(current,"next",annot)              else                  result = annot              end              references.registerpage(n)              result = hpack_list(result,0) -            result.width, result.height, result.depth = 0, 0, 0 +            setfield(result,"width",0) +            setfield(result,"height",0) +            setfield(result,"depth",0)              if cleanupreferences then stack[reference] = nil end              return result, resolved          elseif trace_references then @@ -436,9 +447,19 @@ local function makereference(width,height,depth,reference)      end  end +-- function nodes.references.handler(head) +--     if topofstack > 0 then +--         return inject_areas(head,attribute,makereference,stack,done) +--     else +--         return head, false +--     end +-- end +  function nodes.references.handler(head)      if topofstack > 0 then -        return inject_areas(head,attribute,makereference,stack,done) +        head = tonut(head) +        local head, done = inject_areas(head,attribute,makereference,stack,done) +        return tonode(head), done      else          return head, false      end @@ -484,12 +505,12 @@ local function makedestination(width,height,depth,reference)              end              for n=1,#name do                  local rule = hpack_list(colorize(width,height,depth,3,reference,"destination")) -                rule.width = 0 +                setfield(rule,"width",0)                  if not result then                      result, current = rule, rule                  else -                    current.next = rule -                    rule.prev = current +                    setfield(current,"next",rule) +                    setfield(rule,"prev",current)                      current = rule                  end                  width, height = width - step, height - step @@ -499,12 +520,12 @@ local function makedestination(width,height,depth,reference)          for n=1,#name do              local annot = nodeinjections.destination(width,height,depth,name[n],view)              if annot then -                -- probably duplicate +annot = tonut(annot) -- obsolete soon                  if not result then                      result  = annot                  else -                    current.next = annot -                    annot.prev = current +                    setfield(current,"next",annot) +                    setfield(annot,"prev",current)                  end                  current = find_node_tail(annot)              end @@ -512,7 +533,9 @@ local function makedestination(width,height,depth,reference)          if result then              -- some internal error              result = hpack_list(result,0) -            result.width, result.height, result.depth = 0, 0, 0 +            setfield(result,"width",0) +            setfield(result,"height",0) +            setfield(result,"depth",0)          end          if cleanupdestinations then stack[reference] = nil end          return result, resolved @@ -521,14 +544,25 @@ local function makedestination(width,height,depth,reference)      end  end +-- function nodes.destinations.handler(head) +--     if topofstack > 0 then +--         return inject_area(head,attribute,makedestination,stack,done) -- singular +--     else +--         return head, false +--     end +-- end +  function nodes.destinations.handler(head)      if topofstack > 0 then -        return inject_area(head,attribute,makedestination,stack,done) -- singular +        head = tonut(head) +        local head, done = inject_areas(head,attribute,makedestination,stack,done) +        return tonode(head), done      else          return head, false      end  end +  -- will move  function references.mark(reference,h,d,view) diff --git a/tex/context/base/node-res.lua b/tex/context/base/node-res.lua index ca9d67f91..709532d76 100644 --- a/tex/context/base/node-res.lua +++ b/tex/context/base/node-res.lua @@ -18,13 +18,8 @@ local report_nodes = logs.reporter("nodes","housekeeping")  local nodes, node = nodes, node -local copy_node    = node.copy -local free_node    = node.free -local free_list    = node.flush_list -local new_node     = node.new -  nodes.pool         = nodes.pool or { } -local pool         = nodes.pool +local nodepool     = nodes.pool  local whatsitcodes = nodes.whatsitcodes  local skipcodes    = nodes.skipcodes @@ -35,400 +30,454 @@ local glyph_code   = nodecodes.glyph  local allocate     = utilities.storage.allocate -local texgetbox    = tex.getbox  local texgetcount  = tex.getcount  local reserved, nofreserved = { }, 0 -local function register_node(n) -    nofreserved = nofreserved + 1 -    reserved[nofreserved] = n -    return n -end +-- user nodes -pool.register = register_node +local userids = allocate() +local lastid  = 0 -function pool.cleanup(nofboxes) -- todo -    if nodes.tracers.steppers then -- to be resolved -        nodes.tracers.steppers.reset() -- todo: make a registration subsystem -    end -    local nl, nr = 0, nofreserved -    for i=1,nofreserved do -        local ri = reserved[i] -    --  if not (ri.id == glue_spec and not ri.is_writable) then -            free_node(reserved[i]) -    --  end +setmetatable(userids, { +    __index = function(t,k) +        if type(k) == "string" then +            lastid = lastid + 1 +            rawset(userids,lastid,k) +            rawset(userids,k,lastid) +            return lastid +        else +            rawset(userids,k,k) +            return k +        end +    end, +    __call = function(t,k) +        return t[k]      end -    if nofboxes then -        for i=0,nofboxes do -            local l = texgetbox(i) -            if l then -                free_node(l) -- also list ? -                nl = nl + 1 -            end +} ) + +-- nuts overload + +local nuts      = nodes.nuts +local nutpool   = { } +nuts.pool       = nutpool + +local tonut     = nuts.tonut +local tonode    = nuts.tonode + +local getbox    = nuts.getbox +local getfield  = nuts.getfield +local setfield  = nuts.setfield +local getid     = nuts.getid + +local copy_nut  = nuts.copy +local new_nut   = nuts.new +local free_nut  = nuts.free + +-- at some point we could have a dual set (the overhead of tonut is not much larger than +-- metatable associations at the lua/c end esp if we also take assignments into account + +-- table.setmetatableindex(nodepool,function(t,k,v) +--  -- report_nodes("defining nodepool[%s] instance",k) +--     local f = nutpool[k] +--     local v = function(...) +--         return tonode(f(...)) +--     end +--     t[k] = v +--     return v +-- end) +-- +-- -- we delay one step because that permits us a forward reference +-- -- e.g. in pdfsetmatrix + +table.setmetatableindex(nodepool,function(t,k,v) + -- report_nodes("defining nodepool[%s] instance",k) +    local v = function(...) +        local f = nutpool[k] +        local v = function(...) +            return tonode(f(...))          end +        t[k] = v +        return v(...)      end -    reserved = { } -    nofreserved = 0 -    return nr, nl, nofboxes -- can be nil +    t[k] = v +    return v +end) + +local function register_nut(n) +    nofreserved = nofreserved + 1 +    reserved[nofreserved] = n +    return n  end -function pool.usage() -    local t = { } -    for n, tag in gmatch(status.node_mem_usage,"(%d+) ([a-z_]+)") do -        t[tag] = n +local function register_node(n) +    nofreserved = nofreserved + 1 +    if type(n) == "number" then -- isnut(n) +        reserved[nofreserved] = n +    else +        reserved[nofreserved] = tonut(n)      end -    return t +    return n  end -local disc              = register_node(new_node("disc")) -local kern              = register_node(new_node("kern",kerncodes.userkern)) -local fontkern          = register_node(new_node("kern",kerncodes.fontkern)) -local penalty           = register_node(new_node("penalty")) -local glue              = register_node(new_node("glue")) -- glue.spec = nil -local glue_spec         = register_node(new_node("glue_spec")) -local glyph             = register_node(new_node("glyph",0)) -local textdir           = register_node(new_node("whatsit",whatsitcodes.dir)) -local latelua           = register_node(new_node("whatsit",whatsitcodes.latelua)) -local special           = register_node(new_node("whatsit",whatsitcodes.special)) -local user_n            = register_node(new_node("whatsit",whatsitcodes.userdefined)) user_n.type = 100 -- 44 -local user_l            = register_node(new_node("whatsit",whatsitcodes.userdefined)) user_l.type = 110 -- 44 -local user_s            = register_node(new_node("whatsit",whatsitcodes.userdefined)) user_s.type = 115 -- 44 -local user_t            = register_node(new_node("whatsit",whatsitcodes.userdefined)) user_t.type = 116 -- 44 -local left_margin_kern  = register_node(new_node("margin_kern",0)) -local right_margin_kern = register_node(new_node("margin_kern",1)) -local lineskip          = register_node(new_node("glue",skipcodes.lineskip)) -local baselineskip      = register_node(new_node("glue",skipcodes.baselineskip)) -local leftskip          = register_node(new_node("glue",skipcodes.leftskip)) -local rightskip         = register_node(new_node("glue",skipcodes.rightskip)) -local temp              = register_node(new_node("temp",0)) -local noad              = register_node(new_node("noad")) +nodepool.userids  = userids +nodepool.register = register_node + +nutpool.userids   = userids +nutpool.register  = register_node -- could be register_nut + +-- so far + +local disc              = register_nut(new_nut("disc")) +local kern              = register_nut(new_nut("kern",kerncodes.userkern)) +local fontkern          = register_nut(new_nut("kern",kerncodes.fontkern)) +local penalty           = register_nut(new_nut("penalty")) +local glue              = register_nut(new_nut("glue")) -- glue.spec = nil +local glue_spec         = register_nut(new_nut("glue_spec")) +local glyph             = register_nut(new_nut("glyph",0)) +local textdir           = register_nut(new_nut("whatsit",whatsitcodes.dir)) +local latelua           = register_nut(new_nut("whatsit",whatsitcodes.latelua)) +local special           = register_nut(new_nut("whatsit",whatsitcodes.special)) +local user_n            = register_nut(new_nut("whatsit",whatsitcodes.userdefined)) setfield(user_n,"type",100) -- 44 +local user_l            = register_nut(new_nut("whatsit",whatsitcodes.userdefined)) setfield(user_l,"type",110) -- 44 +local user_s            = register_nut(new_nut("whatsit",whatsitcodes.userdefined)) setfield(user_s,"type",115) -- 44 +local user_t            = register_nut(new_nut("whatsit",whatsitcodes.userdefined)) setfield(user_t,"type",116) -- 44 +local left_margin_kern  = register_nut(new_nut("margin_kern",0)) +local right_margin_kern = register_nut(new_nut("margin_kern",1)) +local lineskip          = register_nut(new_nut("glue",skipcodes.lineskip)) +local baselineskip      = register_nut(new_nut("glue",skipcodes.baselineskip)) +local leftskip          = register_nut(new_nut("glue",skipcodes.leftskip)) +local rightskip         = register_nut(new_nut("glue",skipcodes.rightskip)) +local temp              = register_nut(new_nut("temp",0)) +local noad              = register_nut(new_nut("noad"))  -- the dir field needs to be set otherwise crash: -local rule              = register_node(new_node("rule"))  rule .dir = "TLT" -local hlist             = register_node(new_node("hlist")) hlist.dir = "TLT" -local vlist             = register_node(new_node("vlist")) vlist.dir = "TLT" - -function pool.zeroglue(n) -    local s = n.spec -    return not writable or ( -                     s.width == 0 -         and       s.stretch == 0 -         and        s.shrink == 0 -         and s.stretch_order == 0 -         and  s.shrink_order == 0 -        ) -end - -function pool.glyph(fnt,chr) -    local n = copy_node(glyph) -    if fnt then n.font = fnt end -    if chr then n.char = chr end +local rule              = register_nut(new_nut("rule"))  setfield(rule, "dir","TLT") +local hlist             = register_nut(new_nut("hlist")) setfield(hlist,"dir","TLT") +local vlist             = register_nut(new_nut("vlist")) setfield(vlist,"dir","TLT") + +function nutpool.zeroglue(n) +    local s = getfield(n,"spec") +    return not writable or ( -- still valid? writable +             getfield(s,"width") == 0 +         and getfield(s,"stretch") == 0 +         and getfield(s,"shrink") == 0 +         and getfield(s,"stretch_order") == 0 +         and getfield(s,"shrink_order") == 0 +    ) +end + +function nutpool.glyph(fnt,chr) +    local n = copy_nut(glyph) +    if fnt then setfield(n,"font",fnt) end +    if chr then setfield(n,"char",chr) end      return n  end -function pool.penalty(p) -    local n = copy_node(penalty) -    n.penalty = p +function nutpool.penalty(p) +    local n = copy_nut(penalty) +    setfield(n,"penalty",p)      return n  end -function pool.kern(k) -    local n = copy_node(kern) -    n.kern = k +function nutpool.kern(k) +    local n = copy_nut(kern) +    setfield(n,"kern",k)      return n  end -function pool.fontkern(k) -    local n = copy_node(fontkern) -    n.kern = k +function nutpool.fontkern(k) +    local n = copy_nut(fontkern) +    setfield(n,"kern",k)      return n  end -function pool.gluespec(width,stretch,shrink,stretch_order,shrink_order) -    local s = copy_node(glue_spec) -    if width         then s.width         = width         end -    if stretch       then s.stretch       = stretch       end -    if shrink        then s.shrink        = shrink        end -    if stretch_order then s.stretch_order = stretch_order end -    if shrink_order  then s.shrink_order  = shrink_order  end +function nutpool.gluespec(width,stretch,shrink,stretch_order,shrink_order) +    local s = copy_nut(glue_spec) +    if width         then setfield(s,"width",width)                 end +    if stretch       then setfield(s,"stretch",stretch)             end +    if shrink        then setfield(s,"shrink",shrink)               end +    if stretch_order then setfield(s,"stretch_order",stretch_order) end +    if shrink_order  then setfield(s,"shrink_order",shrink_order)   end      return s  end  local function someskip(skip,width,stretch,shrink,stretch_order,shrink_order) -    local n = copy_node(skip) +    local n = copy_nut(skip)      if not width then          -- no spec      elseif width == false or tonumber(width) then -        local s = copy_node(glue_spec) -        if width         then s.width         = width         end -        if stretch       then s.stretch       = stretch       end -        if shrink        then s.shrink        = shrink        end -        if stretch_order then s.stretch_order = stretch_order end -        if shrink_order  then s.shrink_order  = shrink_order  end -        n.spec = s +        local s = copy_nut(glue_spec) +        if width         then setfield(s,"width",width)                 end +        if stretch       then setfield(s,"stretch",stretch)             end +        if shrink        then setfield(s,"shrink",shrink)               end +        if stretch_order then setfield(s,"stretch_order",stretch_order) end +        if shrink_order  then setfield(s,"shrink_order",shrink_order)   end +        setfield(n,"spec",s)      else          -- shared -        n.spec = copy_node(width) +        setfield(n,"spec",copy_nut(width))      end      return n  end -function pool.stretch(a,b) -    local n = copy_node(glue) -    local s = copy_node(glue_spec) +function nutpool.stretch(a,b) +    local n = copy_nut(glue) +    local s = copy_nut(glue_spec)      if b then -        s.stretch       = a -        s.stretch_order = b +        setfield(s,"stretch",a) +        setfield(s,"stretch_order",b)      else -        s.stretch       = 1 -        s.stretch_order = a or 1 +        setfield(s,"stretch",1) +        setfield(s,"stretch_order",a or 1)      end -    n.spec = s +    setfield(n,"spec",s)      return n  end -function pool.shrink(a,b) -    local n = copy_node(glue) -    local s = copy_node(glue_spec) +function nutpool.shrink(a,b) +    local n = copy_nut(glue) +    local s = copy_nut(glue_spec)      if b then -        s.shrink       = a -        s.shrink_order = b +        setfield(s,"shrink",a) +        setfield(s,"shrink_order",b)      else -        s.shrink       = 1 -        s.shrink_order = a or 1 +        setfield(s,"shrink",1) +        setfield(s,"shrink_order",a or 1)      end -    n.spec = s +    setfield(n,"spec",s)      return n  end - -function pool.glue(width,stretch,shrink,stretch_order,shrink_order) +function nutpool.glue(width,stretch,shrink,stretch_order,shrink_order)      return someskip(glue,width,stretch,shrink,stretch_order,shrink_order)  end -function pool.leftskip(width,stretch,shrink,stretch_order,shrink_order) +function nutpool.leftskip(width,stretch,shrink,stretch_order,shrink_order)      return someskip(leftskip,width,stretch,shrink,stretch_order,shrink_order)  end -function pool.rightskip(width,stretch,shrink,stretch_order,shrink_order) +function nutpool.rightskip(width,stretch,shrink,stretch_order,shrink_order)      return someskip(rightskip,width,stretch,shrink,stretch_order,shrink_order)  end -function pool.lineskip(width,stretch,shrink,stretch_order,shrink_order) +function nutpool.lineskip(width,stretch,shrink,stretch_order,shrink_order)      return someskip(lineskip,width,stretch,shrink,stretch_order,shrink_order)  end -function pool.baselineskip(width,stretch,shrink) +function nutpool.baselineskip(width,stretch,shrink)      return someskip(baselineskip,width,stretch,shrink)  end -function pool.disc() -    return copy_node(disc) +function nutpool.disc() +    return copy_nut(disc)  end -function pool.textdir(dir) -    local t = copy_node(textdir) -    t.dir = dir +function nutpool.textdir(dir) +    local t = copy_nut(textdir) +    setfield(t,"dir",dir)      return t  end -function pool.rule(width,height,depth,dir) -- w/h/d == nil will let them adapt -    local n = copy_node(rule) -    if width  then n.width  = width  end -    if height then n.height = height end -    if depth  then n.depth  = depth  end -    if dir    then n.dir    = dir    end +function nutpool.rule(width,height,depth,dir) -- w/h/d == nil will let them adapt +    local n = copy_nut(rule) +    if width  then setfield(n,"width",width)   end +    if height then setfield(n,"height",height) end +    if depth  then setfield(n,"depth",depth)   end +    if dir    then setfield(n,"dir",dir)       end      return n  end -if node.has_field(latelua,'string') then -    function pool.latelua(code) -        local n = copy_node(latelua) -        n.string = code +-- if node.has_field(latelua,'string') then +    function nutpool.latelua(code) +        local n = copy_nut(latelua) +        setfield(n,"string",code)          return n      end -else -    function pool.latelua(code) -        local n = copy_node(latelua) -        n.data = code -        return n -    end -end - -function pool.leftmarginkern(glyph,width) -    local n = copy_node(left_margin_kern) +-- else +--     function nutpool.latelua(code) +--         local n = copy_nut(latelua) +--         setfield(n,"data",code) +--         return n +--     end +-- end + +function nutpool.leftmarginkern(glyph,width) +    local n = copy_nut(left_margin_kern)      if not glyph then          report_nodes("invalid pointer to left margin glyph node") -    elseif glyph.id ~= glyph_code then +    elseif getid(glyph) ~= glyph_code then          report_nodes("invalid node type %a for %s margin glyph node",nodecodes[glyph],"left")      else -        n.glyph = glyph +        setfield(n,"glyph",glyph)      end      if width then -        n.width = width +        setfield(n,"width",width)      end      return n  end -function pool.rightmarginkern(glyph,width) -    local n = copy_node(right_margin_kern) +function nutpool.rightmarginkern(glyph,width) +    local n = copy_nut(right_margin_kern)      if not glyph then          report_nodes("invalid pointer to right margin glyph node") -    elseif glyph.id ~= glyph_code then +    elseif getid(glyph) ~= glyph_code then          report_nodes("invalid node type %a for %s margin glyph node",nodecodes[p],"right")      else -        n.glyph = glyph +        setfield(n,"glyph",glyph)      end      if width then -        n.width = width +        setfield(n,"width",width)      end      return n  end -function pool.temp() -    return copy_node(temp) +function nutpool.temp() +    return copy_nut(temp)  end -function pool.noad() -    return copy_node(noad) +function nutpool.noad() +    return copy_nut(noad)  end -function pool.hlist(list,width,height,depth) -    local n = copy_node(hlist) +function nutpool.hlist(list,width,height,depth) +    local n = copy_nut(hlist)      if list then -        n.list = list +        setfield(n,"list",list)      end      if width then -        n.width = width +        setfield(n,"width",width)      end      if height then -        n.height = height +        setfield(n,"height",height)      end      if depth then -        n.depth = depth +        setfield(n,"depth",depth)      end      return n  end -function pool.vlist(list,width,height,depth) -    local n = copy_node(vlist) +function nutpool.vlist(list,width,height,depth) +    local n = copy_nut(vlist)      if list then -        n.list = list +        setfield(n,"list",list)      end      if width then -        n.width = width +        setfield(n,"width",width)      end      if height then -        n.height = height +        setfield(n,"height",height)      end      if depth then -        n.depth = depth +        setfield(n,"depth",depth)      end      return n  end ---[[ -<p>At some point we ran into a problem that the glue specification -of the zeropoint dimension was overwritten when adapting a glue spec -node. This is a side effect of glue specs being shared. After a -couple of hours tracing and debugging Taco and I came to the -conclusion that it made no sense to complicate the spec allocator -and settled on a writable flag. This all is a side effect of the -fact that some glues use reserved memory slots (with the zeropoint -glue being a noticeable one). So, next we wrap this into a function -and hide it for the user. And yes, LuaTeX now gives a warning as -well.</p> -]]-- - -function nodes.writable_spec(n) -- not pool -    local spec = n.spec -    if not spec then -        spec = copy_node(glue_spec) -        n.spec = spec -    elseif not spec.writable then -        spec = copy_node(spec) -        n.spec = spec -    end -    return spec -end -  -- local num = userids["my id"]  -- local str = userids[num] -local userids = allocate()  pool.userids = userids -local lastid  = 0 - -setmetatable(userids, { -    __index = function(t,k) -        if type(k) == "string" then -            lastid = lastid + 1 -            rawset(userids,lastid,k) -            rawset(userids,k,lastid) -            return lastid -        else -            rawset(userids,k,k) -            return k -        end -    end, -    __call = function(t,k) -        return t[k] -    end -} ) - -function pool.usernumber(id,num) -    local n = copy_node(user_n) +function nutpool.usernumber(id,num) +    local n = copy_nut(user_n)      if num then -        n.user_id, n.value = id, num +        setfield(n,"user_id",id) +        setfield(n,"value",num)      elseif id then -        n.value = id +        setfield(n,"value",id)      end      return n  end -function pool.userlist(id,list) -    local n = copy_node(user_l) +function nutpool.userlist(id,list) +    local n = copy_nut(user_l)      if list then -        n.user_id, n.value = id, list +        setfield(n,"user_id",id) +        setfield(n,"value",list)      else -        n.value = id +        setfield(n,"value",id)      end      return n  end -function pool.userstring(id,str) -    local n = copy_node(user_s) +function nutpool.userstring(id,str) +    local n = copy_nut(user_s)      if str then -        n.user_id, n.value = id, str +        setfield(n,"user_id",id) +        setfield(n,"value",str)      else -        n.value = id +        setfield(n,"value",id)      end      return n  end -function pool.usertokens(id,tokens) -    local n = copy_node(user_t) +function nutpool.usertokens(id,tokens) +    local n = copy_nut(user_t)      if tokens then -        n.user_id, n.value = id, tokens +        setfield(n,"user_id",id) +        setfield(n,"value",tokens)      else -        n.value = id +        setfield(n,"value",id)      end      return n  end -function pool.special(str) -    local n = copy_node(special) -    n.data = str +function nutpool.special(str) +    local n = copy_nut(special) +    setfield(n,"data",str)      return n  end +-- housekeeping + +local function cleanup(nofboxes) -- todo +    if nodes.tracers.steppers then -- to be resolved +        nodes.tracers.steppers.reset() -- todo: make a registration subsystem +    end +    local nl, nr = 0, nofreserved +    for i=1,nofreserved do +        local ri = reserved[i] +    --  if not (getid(ri) == glue_spec and not getfield(ri,"is_writable")) then +            free_nut(reserved[i]) +    --  end +    end +    if nofboxes then +        for i=0,nofboxes do +            local l = getbox(i) +            if l then +                free_nut(l) -- also list ? +                nl = nl + 1 +            end +        end +    end +    reserved = { } +    nofreserved = 0 +    return nr, nl, nofboxes -- can be nil +end + + +local function usage() +    local t = { } +    for n, tag in gmatch(status.node_mem_usage,"(%d+) ([a-z_]+)") do +        t[tag] = n +    end +    return t +end + +nutpool .cleanup = cleanup +nodepool.cleanup = cleanup + +nutpool .usage   = usage +nodepool.usage   = usage + +-- end +  statistics.register("cleaned up reserved nodes", function() -    return format("%s nodes, %s lists of %s", pool.cleanup(texgetcount("c_syst_last_allocated_box"))) +    return format("%s nodes, %s lists of %s", cleanup(texgetcount("c_syst_last_allocated_box")))  end) -- \topofboxstack  statistics.register("node memory usage", function() -- comes after cleanup !      return status.node_mem_usage  end) -lua.registerfinalizer(pool.cleanup, "cleanup reserved nodes") +lua.registerfinalizer(cleanup, "cleanup reserved nodes") diff --git a/tex/context/base/node-rul.lua b/tex/context/base/node-rul.lua index 96d6bdf41..6f3bc9df9 100644 --- a/tex/context/base/node-rul.lua +++ b/tex/context/base/node-rul.lua @@ -13,12 +13,28 @@ if not modules then modules = { } end modules ['node-rul'] = {  local attributes, nodes, node = attributes, nodes, node -local nodecodes  = nodes.nodecodes -local tasks      = nodes.tasks - -local glyph_code = nodecodes.glyph -local disc_code  = nodecodes.disc -local rule_code  = nodecodes.rule +local nuts         = nodes.nuts +local tonode       = nuts.tonode +local tonut        = nuts.tonut + +local getfield     = nuts.getfield +local setfield     = nuts.setfield +local getnext      = nuts.getnext +local getprev      = nuts.getprev +local getid        = nuts.getid +local getattr      = nuts.getattr +local setattr      = nuts.setattr +local getfont      = nuts.getfont +local getsubtype   = nuts.getsubtype +local getchar      = nuts.getchar +local getlist      = nuts.getlist + +local nodecodes    = nodes.nodecodes +local tasks        = nodes.tasks + +local glyph_code   = nodecodes.glyph +local disc_code    = nodecodes.disc +local rule_code    = nodecodes.rule  function nodes.striprange(first,last) -- todo: dir      if first and last then -- just to be sure @@ -26,11 +42,11 @@ function nodes.striprange(first,last) -- todo: dir              return first, last          end          while first and first ~= last do -            local id = first.id +            local id = getid(first)              if id == glyph_code or id == disc_code then -- or id == rule_code                  break              else -                first = first.next +                first = getnext(first)              end          end          if not first then @@ -39,13 +55,13 @@ function nodes.striprange(first,last) -- todo: dir              return first, last          end          while last and last ~= first do -            local id = last.id +            local id = getid(last)              if id == glyph_code or id == disc_code then -- or id == rule_code                  break              else -                local prev = last.prev -- luatex < 0.70 has italic correction kern not prev'd +                local prev = getprev(last) -- luatex < 0.70 has italic correction kern not prev'd                  if prev then -                    last = last.prev +                    last = prev                  else                      break                  end @@ -73,12 +89,12 @@ local a_color            = attributes.private('color')  local a_transparency     = attributes.private('transparency')  local a_colorspace       = attributes.private('colormodel') -local insert_node_before = node.insert_before -local insert_node_after  = node.insert_after -local striprange         = nodes.striprange -local list_dimensions    = node.dimensions +local insert_node_before = nuts.insert_before +local insert_node_after  = nuts.insert_after +local list_dimensions    = nuts.dimensions +local hpack_nodes        = nuts.hpack -local hpack_nodes        = node.hpack +local striprange         = nodes.striprange  local fontdata           = fonts.hashes.identifiers  local variables          = interfaces.variables @@ -111,7 +127,7 @@ local dir_code           = whatcodes.dir  local kerning_code       = kerncodes.kern -local nodepool           = nodes.pool +local nodepool           = nuts.pool  local new_rule           = nodepool.rule  local new_kern           = nodepool.kern @@ -141,9 +157,9 @@ local function processwords(attribute,data,flush,head,parent) -- we have hlistdi          local f, l, a, d, i, class          local continue, done, strip, level = false, false, true, -1          while n do -            local id = n.id +            local id = getid(n)              if id == glyph_code or id == rule_code then -                local aa = n[attribute] +                local aa = getattr(n,attribute)                  if aa then                      if aa == a then                          if not f then -- ? @@ -172,13 +188,13 @@ local function processwords(attribute,data,flush,head,parent) -- we have hlistdi                      end                      f, l, a = nil, nil, nil                  end ---             elseif f and (id == disc_code or (id == kern_code and n.subtype == kerning_code)) then +--             elseif f and (id == disc_code or (id == kern_code and getsubtype(n) == kerning_code)) then  --                 l = n              elseif id == disc_code then                  if f then                      l = n                  end -            elseif id == kern_code and n.subtype == kerning_code then +            elseif id == kern_code and getsubtype(n) == kerning_code then                  if f then                      l = n                  end @@ -187,11 +203,11 @@ local function processwords(attribute,data,flush,head,parent) -- we have hlistdi                      head, done = flush(head,f,l,d,level,parent,strip), true                      f, l, a = nil, nil, nil                  end -                local list = n.list +                local list = getlist(n)                  if list then -                    n.list = processwords(attribute,data,flush,list,n) +                    setfield(n,"list",(processwords(attribute,data,flush,list,n))) -- watch ()                  end -            elseif checkdir and id == whatsit_code and n.subtype == dir_code then -- only changes in dir, we assume proper boundaries +            elseif checkdir and id == whatsit_code and getsubtype(n) == dir_code then -- only changes in dir, we assume proper boundaries                  if f and a then                      l = n                  end @@ -203,8 +219,8 @@ local function processwords(attribute,data,flush,head,parent) -- we have hlistdi                   --     l = n                      elseif id == glue_code then                          -- catch \underbar{a} \underbar{a} (subtype test is needed) -                        local subtype = n.subtype -                        if n[attribute] and (subtype == userskip_code or subtype == spaceskip_code or subtype == xspaceskip_code) then +                        local subtype = getsubtype(n) +                        if getattr(n,attribute) and (subtype == userskip_code or subtype == spaceskip_code or subtype == xspaceskip_code) then                              l = n                          else                              head, done = flush(head,f,l,d,level,parent,strip), true @@ -216,7 +232,7 @@ local function processwords(attribute,data,flush,head,parent) -- we have hlistdi                      f, l, a = nil, nil, nil                  end              end -            n = n.next +            n = getnext(n)          end          if f then              head, done = flush(head,f,l,d,level,parent,strip), true @@ -227,7 +243,16 @@ local function processwords(attribute,data,flush,head,parent) -- we have hlistdi      end  end -nodes.processwords = processwords +-- nodes.processwords = processwords + +nodes.processwords = function(attribute,data,flush,head,parent) -- we have hlistdir and local dir +    head = tonut(head) +    if parent then +        parent = tonut(parent) +    end +    local head, done = processwords(attribute,data,flush,head,parent) +    return tonode(head), done +end  -- @@ -246,7 +271,7 @@ end  local a_viewerlayer = attributes.private("viewerlayer")  local function flush_ruled(head,f,l,d,level,parent,strip) -- not that fast but acceptable for this purpose -    if f.id ~= glyph_code then +    if getid(f) ~= glyph_code then          -- saveguard ... we need to deal with rules and so (math)          return head      end @@ -264,16 +289,16 @@ local function flush_ruled(head,f,l,d,level,parent,strip) -- not that fast but a      if not f then          return head      end -    local w = list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,f,l.next) +    local w = list_dimensions(getfield(parent,"glue_set"),getfield(parent,"glue_sign"),getfield(parent,"glue_order"),f,getnext(l))      local method, offset, continue, dy, order, max = d.method, d.offset, d.continue, d.dy, d.order, d.max      local rulethickness, unit = d.rulethickness, d.unit      local ma, ca, ta = d.ma, d.ca, d.ta -    local colorspace   = ma > 0 and ma or f[a_colorspace] or 1 -    local color        = ca > 0 and ca or f[a_color] -    local transparency = ta > 0 and ta or f[a_transparency] +    local colorspace   = ma > 0 and ma or getattr(f,a_colorspace) or 1 +    local color        = ca > 0 and ca or getattr(f,a_color) +    local transparency = ta > 0 and ta or getattr(f,a_transparency)      local foreground = order == v_foreground -    local e = dimenfactor(unit,f.font) -- what if no glyph node +    local e = dimenfactor(unit,getfont(f)) -- what if no glyph node      local rt = tonumber(rulethickness)      if rt then @@ -281,7 +306,7 @@ local function flush_ruled(head,f,l,d,level,parent,strip) -- not that fast but a      else          local n, u = splitdimen(rulethickness)          if n and u then -- we need to intercept ex and em and % and ... -            rulethickness = n * dimenfactor(u,fontdata[f.font]) / 2 +            rulethickness = n * dimenfactor(u,fontdata[getfont(f)]) / 2          else              rulethickness = 1/5          end @@ -300,18 +325,18 @@ local function flush_ruled(head,f,l,d,level,parent,strip) -- not that fast but a          local ht =  (offset+(i-1)*dy)*e + rulethickness - m          local dp = -(offset+(i-1)*dy)*e + rulethickness + m          local r = new_rule(w,ht,dp) -        local v = f[a_viewerlayer] +        local v = getattr(f,a_viewerlayer)          -- quick hack          if v then -            r[a_viewerlayer] = v +            setattr(r,a_viewerlayer,v)          end          --          if color then -            r[a_colorspace] = colorspace -            r[a_color] = color +            setattr(r,a_colorspace,colorspace) +            setattr(r,a_color,color)          end          if transparency then -            r[a_transparency] = transparency +            setattr(r,a_transparency,transparency)          end          local k = new_kern(-w)          if foreground then @@ -365,21 +390,27 @@ local function flush_shifted(head,first,last,data,level,parent,strip) -- not tha      if true then          first, last = striprange(first,last)      end -    local prev, next = first.prev, last.next -    first.prev, last.next = nil, nil -    local width, height, depth = list_dimensions(parent.glue_set,parent.glue_sign,parent.glue_order,first,next) +    local prev = getprev(first) +    local next = getnext(last) +    setfield(first,"prev",nil) +    setfield(last,"next",nil) +    local width, height, depth = list_dimensions(getfield(parent,"glue_set"),getfield(parent,"glue_sign"),getfield(parent,"glue_order"),first,next)      local list = hpack_nodes(first,width,"exactly")      if first == head then          head = list      end      if prev then -        prev.next, list.prev = list, prev +       setfield(prev,"next",list) +       setfield(list,"prev",prev)      end      if next then -        next.prev, list.next = list, next +        setfield(next,"prev",list) +        setfield(list,"next",next)      end -    local raise = data.dy * dimenfactor(data.unit,fontdata[first.font]) -    list.shift, list.height, list.depth = raise, height, depth +    local raise = data.dy * dimenfactor(data.unit,fontdata[getfont(first)]) +    setfield(list,"shift",raise) +    setfield(list,"height",height) +    setfield(list,"depth",depth)      if trace_shifted then          report_shifted("width %p, nodes %a, text %a",width,n_tostring(first,last),n_tosequence(first,last,true))      end diff --git a/tex/context/base/node-tra.lua b/tex/context/base/node-tra.lua index 9617f7476..89c3e52f9 100644 --- a/tex/context/base/node-tra.lua +++ b/tex/context/base/node-tra.lua @@ -34,9 +34,30 @@ nodes.handlers         = handlers  local injections       = nodes.injections or { }  nodes.injections       = injections -local traverse_nodes   = node.traverse -local traverse_by_id   = node.traverse_id -local count_nodes      = nodes.count +local nuts             = nodes.nuts +local tonut            = nuts.tonut +local tonode           = nuts.tonode + +local getfield         = nuts.getfield +local getnext          = nuts.getnext +local getprev          = nuts.getprev +local getid            = nuts.getid +local getchar          = nuts.getchar +local getsubtype       = nuts.getsubtype +local getlist          = nuts.getlist + +local setattr          = nuts.setattr + +local flush_list       = nuts.flush_list +local count_nodes      = nuts.count +local used_nodes       = nuts.usedlist + +local traverse_by_id   = nuts.traverse_id +local traverse_nodes   = nuts.traverse +local d_tostring       = nuts.tostring + +local nutpool          = nuts.pool +local new_rule         = nutpool.rule  local nodecodes        = nodes.nodecodes  local whatcodes        = nodes.whatcodes @@ -56,9 +77,6 @@ local gluespec_code    = nodecodes.gluespec  local localpar_code    = whatcodes.localpar  local dir_code         = whatcodes.dir -local nodepool         = nodes.pool -local new_rule         = nodepool.rule -  local dimenfactors     = number.dimenfactors  local formatters       = string.formatters @@ -68,15 +86,16 @@ function nodes.showlist(head, message)      if message then          report_nodes(message)      end -    for n in traverse_nodes(head) do -        report_nodes(tostring(n)) +    for n in traverse_nodes(tonut(head)) do +        report_nodes(d_tostring(n))      end  end  function nodes.handlers.checkglyphs(head,message) +    local h = tonut(head)      local t = { } -    for g in traverse_by_id(glyph_code,head) do -        t[#t+1] = formatters["%U:%s"](g.char,g.subtype) +    for g in traverse_by_id(glyph_code,h) do +        t[#t+1] = formatters["%U:%s"](getchar(g),getsubtype(g))      end      if #t > 0 then          if message and message ~= "" then @@ -90,12 +109,12 @@ end  function nodes.handlers.checkforleaks(sparse)      local l = { } -    local q = node.usedlist() -    for p in traverse(q) do -        local s = table.serialize(nodes.astable(p,sparse),nodecodes[p.id]) +    local q = used_nodes() +    for p in traverse_nodes(q) do +        local s = table.serialize(nodes.astable(p,sparse),nodecodes[getid(p)])          l[s] = (l[s] or 0) + 1      end -    node.flush_list(q) +    flush_list(q)      for k, v in next, l do          report_nodes("%s * %s",v,k)      end @@ -105,39 +124,40 @@ local f_sequence = formatters["U+%04X:%s"]  local function tosequence(start,stop,compact)      if start then +        start = tonut(start) +        stop = stop and tonut(stop)          local t = { }          while start do -            local id = start.id +            local id = getid(start)              if id == glyph_code then -                local c = start.char +                local c = getchar(start)                  if compact then -                    if start.components then -                        t[#t+1] = tosequence(start.components,nil,compact) +                    local components = getfield(start,"components") +                    if components then +                        t[#t+1] = tosequence(components,nil,compact)                      else                          t[#t+1] = utfchar(c)                      end                  else                      t[#t+1] = f_sequence(c,utfchar(c))                  end -            elseif id == whatsit_code and start.subtype == localpar_code or start.subtype == dir_code then -                t[#t+1] = "[" .. start.dir .. "]"              elseif id == rule_code then                  if compact then                      t[#t+1] = "|"                  else                      t[#t+1] = nodecodes[id]                  end +            elseif id == whatsit_code and getsubtype(start) == localpar_code or getsubtype(start) == dir_code then +                t[#t+1] = "[" .. getfield(start,"dir") .. "]" +            elseif compact then +                t[#t+1] = "[]"              else -                if compact then -                    t[#t+1] = "[]" -                else -                    t[#t+1] = nodecodes[id] -                end +                t[#t+1] = nodecodes[id]              end              if start == stop then                  break              else -                start = start.next +                start = getnext(start)              end          end          if compact then @@ -153,21 +173,23 @@ end  nodes.tosequence = tosequence  function nodes.report(t,done) -    report_nodes("output %a, %changed %a, %s nodes",status.output_active,done,count_nodes(t)) +    report_nodes("output %a, %changed %a, %s nodes",status.output_active,done,count_nodes(tonut(t)))  end  function nodes.packlist(head)      local t = { } -    for n in traverse(head) do -        t[#t+1] = tostring(n) +    for n in traverse_nodes(tonut(head)) do +        t[#t+1] = d_tostring(n)      end      return t  end  function nodes.idstostring(head,tail) +    head = tonut(head) +    tail = tail and tonut(tail)      local t, last_id, last_n = { }, nil, 0      for n in traverse_nodes(head,tail) do -- hm, does not stop at tail -        local id = n.id +        local id = getid(n)          if not last_id then              last_id, last_n = id, 1          elseif last_id == id then @@ -195,6 +217,8 @@ function nodes.idstostring(head,tail)  end  -- function nodes.xidstostring(head,tail) -- only for special tracing of backlinks +--     head = tonut(head) +--     tail = tonut(tail)  --     local n = head  --     while n.next do  --         n = n.next @@ -217,7 +241,7 @@ end  --         if n == head then  --             break  --         end ---         n = n.prev +--         n = getprev(n)  --     end  --     if not last_id then  --         t[#t+1] = "no nodes" @@ -230,51 +254,56 @@ end  -- end  local function showsimplelist(h,depth,n) +    h = h and tonut(h)      while h do          report_nodes("% w%s",n,d_tostring(h))          if not depth or n < depth then -            local id = h.id +            local id = getid(h)              if id == hlist_code or id == vlist_code then -                showsimplelist(h.list,depth,n+1) +                showsimplelist(getlist(h),depth,n+1)              end          end -        h = h.next +        h = getnext(h)      end  end ---~ \startluacode ---~ callback.register('buildpage_filter',function() nodes.show_simple_list(tex.lists.contrib_head) end) ---~ \stopluacode ---~ \vbox{b\footnote{n}a} ---~ \startluacode ---~ callback.register('buildpage_filter',nil) ---~ \stopluacode +-- \startluacode +-- callback.register('buildpage_filter',function() nodes.show_simple_list(tex.lists.contrib_head) end) +-- \stopluacode +-- \vbox{b\footnote{n}a} +-- \startluacode +-- callback.register('buildpage_filter',nil) +-- \stopluacode  nodes.showsimplelist = function(h,depth) showsimplelist(h,depth,0) end  local function listtoutf(h,joiner,textonly,last) -    local joiner = (joiner == true and utfchar(0x200C)) or joiner -- zwnj      local w = { }      while h do -        local id = h.id +        local id = getid(h)          if id == glyph_code then -- always true -            local c = h.char +            local c = getchar(h)              w[#w+1] = c >= 0 and utfchar(c) or formatters["<%i>"](c)              if joiner then                  w[#w+1] = joiner              end          elseif id == disc_code then -            local pre = h.pre -            local pos = h.post -            local rep = h.replace +            local pre = getfield(h,"pre") +            local pos = getfield(h,"post") +            local rep = getfield(h,"replace")              w[#w+1] = formatters["[%s|%s|%s]"] (                  pre and listtoutf(pre,joiner,textonly) or "",                  pos and listtoutf(pos,joiner,textonly) or "",                  rep and listtoutf(rep,joiner,textonly) or ""              )          elseif textonly then -            if id == glue_code and h.spec and h.spec.width > 0 then -                w[#w+1] = " " +            if id == glue_code then +                local spec = getfield(h,"spec") +                if spec and getfield(spec,"width") > 0 then +                    w[#w+1] = " " +                end +            elseif id == hlist_code or id == vlist_code then +                w[#w+1] = "[]"              end          else              w[#w+1] = "[-]" @@ -282,24 +311,28 @@ local function listtoutf(h,joiner,textonly,last)          if h == last then              break          else -            h = h.next +            h = getnext(h)          end      end      return concat(w)  end -nodes.listtoutf = listtoutf +function nodes.listtoutf(h,joiner,textonly,last) +    local joiner = joiner == true and utfchar(0x200C) or joiner -- zwnj +    return listtoutf(tonut(h),joiner,textonly,last and tonut(last)) +end  local what = { [0] = "unknown", "line", "box", "indent", "row", "cell" }  local function showboxes(n,symbol,depth) -    depth, symbol = depth or 0, symbol or "." -    for n in traverse_nodes(n) do -        local id = n.id +    depth  = depth  or 0 +    symbol = symbol or "." +    for n in traverse_nodes(tonut(n)) do +        local id = getid(n)          if id == hlist_code or id == vlist_code then -            local s = n.subtype +            local s = getsubtype(n)              report_nodes(rep(symbol,depth) .. what[s] or s) -            showboxes(n.list,symbol,depth+1) +            showboxes(getlist(n),symbol,depth+1)          end      end  end @@ -322,15 +355,8 @@ local stripper = lpeg.patterns.stripzeros  local dimenfactors = number.dimenfactors -local function numbertodimen(d,unit,fmt,strip) -    if not d then -        local str = formatters[fmt](0,unit) -        return strip and lpegmatch(stripper,str) or str -    end -    local t = type(d) -    if t == 'string' then -        return d -    end +local function nodetodimen(d,unit,fmt,strip) +    d = tonut(d) -- tricky: direct nuts are an issue      if unit == true then          unit = "pt"          fmt  = "%0.5f%s" @@ -342,27 +368,23 @@ local function numbertodimen(d,unit,fmt,strip)              fmt = "%0.5f%s"          end      end -    if t == "number" then -        local str = formatters[fmt](d*dimenfactors[unit],unit) -        return strip and lpegmatch(stripper,str) or str -    end -    local id = d.id +    local id = getid(d)      if id == kern_code then -        local str = formatters[fmt](d.width*dimenfactors[unit],unit) +        local str = formatters[fmt](getfield(d,"width")*dimenfactors[unit],unit)          return strip and lpegmatch(stripper,str) or str      end      if id == glue_code then -        d = d.spec +        d = getfield(d,"spec")      end -    if not d or not d.id == gluespec_code then +    if not d or not getid(d) == gluespec_code then          local str = formatters[fmt](0,unit)          return strip and lpegmatch(stripper,str) or str      end -    local width   = d.width -    local plus    = d.stretch_order -    local minus   = d.shrink_order -    local stretch = d.stretch -    local shrink  = d.shrink +    local width   = getfield(d,"width") +    local plus    = getfield(d,"stretch_order") +    local minus   = getfield(d,"shrink_order") +    local stretch = getfield(d,"stretch") +    local shrink  = getfield(d,"shrink")      if plus ~= 0 then          plus = " plus " .. stretch/65536 .. fillcodes[plus]      elseif stretch ~= 0 then @@ -379,11 +401,39 @@ local function numbertodimen(d,unit,fmt,strip)      else          minus = ""      end -    local str = formatters[fmt](d.width*dimenfactors[unit],unit) +    local str = formatters[fmt](getfield(d,"width")*dimenfactors[unit],unit)      return (strip and lpegmatch(stripper,str) or str) .. plus .. minus  end +local function numbertodimen(d,unit,fmt,strip) +    if not d then +        local str = formatters[fmt](0,unit) +        return strip and lpegmatch(stripper,str) or str +    end +    local t = type(d) +    if t == 'string' then +        return d +    elseif t == "number" then +        if unit == true then +            unit = "pt" +            fmt  = "%0.5f%s" +        else +            unit = unit or 'pt' +            if not fmt then +                fmt = "%s%s" +            elseif fmt == true then +                fmt = "%0.5f%s" +            end +        end +        local str = formatters[fmt](d*dimenfactors[unit],unit) +        return strip and lpegmatch(stripper,str) or str +    else +        return nodetodimen(d,unit,fmt,strip) -- real node +    end +end +  number.todimen = numbertodimen +nodes .todimen = nodetodimen  function number.topoints      (n,fmt) return numbertodimen(n,"pt",fmt) end  function number.toinches      (n,fmt) return numbertodimen(n,"in",fmt) end @@ -398,6 +448,19 @@ function number.tociceros     (n,fmt) return numbertodimen(n,"cc",fmt) end  function number.tonewdidots   (n,fmt) return numbertodimen(n,"nd",fmt) end  function number.tonewciceros  (n,fmt) return numbertodimen(n,"nc",fmt) end +function nodes.topoints      (n,fmt) return nodetodimen(n,"pt",fmt) end +function nodes.toinches      (n,fmt) return nodetodimen(n,"in",fmt) end +function nodes.tocentimeters (n,fmt) return nodetodimen(n,"cm",fmt) end +function nodes.tomillimeters (n,fmt) return nodetodimen(n,"mm",fmt) end +function nodes.toscaledpoints(n,fmt) return nodetodimen(n,"sp",fmt) end +function nodes.toscaledpoints(n)     return          n .. "sp"      end +function nodes.tobasepoints  (n,fmt) return nodetodimen(n,"bp",fmt) end +function nodes.topicas       (n,fmt) return nodetodimen(n "pc",fmt) end +function nodes.todidots      (n,fmt) return nodetodimen(n,"dd",fmt) end +function nodes.tociceros     (n,fmt) return nodetodimen(n,"cc",fmt) end +function nodes.tonewdidots   (n,fmt) return nodetodimen(n,"nd",fmt) end +function nodes.tonewciceros  (n,fmt) return nodetodimen(n,"nc",fmt) end +  -- stop redefinition  local points = function(n) @@ -443,8 +506,13 @@ number.basepoints = basepoints  number.pts        = pts  number.nopts      = nopts -local colors   = { } -tracers.colors = colors +nodes.points     = function(n) return numbertodimen(n,"pt",true,true) end +nodes.basepoints = function(n) return numbertodimen(n,"bp",true,true) end +nodes.pts        = function(n) return numbertodimen(n,"pt",true)      end +nodes.nopts      = function(n) return format("%.5f",n*ptfactor)       end + +local colors          = { } +tracers.colors        = colors  local unsetvalue      = attributes.unsetvalue @@ -454,36 +522,34 @@ local m_color         = attributes.list[a_color] or { }  function colors.set(n,c,s)      local mc = m_color[c] -    if not mc then -        n[a_color] = unsetvalue +    local nn = tonut(n) +    if mc then +        local mm = s or texgetattribute(a_colormodel) +        setattr(nn,a_colormodel,mm <= 0 and mm or 1) +        setattr(nn,a_color,mc)      else -        if not n[a_colormodel] then -            n[a_colormodel] = s or 1 -        end -        n[a_color] = mc +        setattr(nn,a_color,unsetvalue)      end      return n  end  function colors.setlist(n,c,s) -    local f = n -    while n do -        local mc = m_color[c] -        if not mc then -            n[a_color] = unsetvalue -        else -            if not n[a_colormodel] then -                n[a_colormodel] = s or 1 -            end -            n[a_color] = mc -        end -        n = n.next +    local nn = tonut(n) +    local mc = m_color[c] or unsetvalue +    local mm = s or texgetattribute(a_colormodel) +    if mm <= 0 then +        mm = 1 +    end +    while nn do +        setattr(nn,a_colormodel,mm) +        setattr(nn,a_color,mc) +        nn = getnext(nn)      end -    return f +    return n  end  function colors.reset(n) -    n[a_color] = unsetvalue +    setattr(tonut(n),a_color,unsetvalue)      return n  end @@ -496,31 +562,22 @@ local a_transparency   = attributes.private('transparency')  local m_transparency   = attributes.list[a_transparency] or { }  function transparencies.set(n,t) -    local mt = m_transparency[t] -    if not mt then -        n[a_transparency] = unsetvalue -    else -        n[a_transparency] = mt -    end +    setattr(tonut(n),a_transparency,m_transparency[t] or unsetvalue)      return n  end  function transparencies.setlist(n,c,s) -    local f = n -    while n do -        local mt = m_transparency[c] -        if not mt then -            n[a_transparency] = unsetvalue -        else -            n[a_transparency] = mt -        end -        n = n.next +    local nn = tonut(n) +    local mt = m_transparency[c] or unsetvalue +    while nn do +        setattr(nn,a_transparency,mt) +        nn = getnext(nn)      end -    return f +    return n  end  function transparencies.reset(n) -    n[a_transparency] = unsetvalue +    setattr(n,a_transparency,unsetvalue)      return n  end @@ -537,52 +594,62 @@ end  -- although tracers are used seldom  local function setproperties(n,c,s) +    local nn = tonut(n)      local mm = texgetattribute(a_colormodel) -    n[a_colormodel]   = mm > 0 and mm or 1 -    n[a_color]        = m_color[c] -    n[a_transparency] = m_transparency[c] +    setattr(nn,a_colormodel,mm > 0 and mm or 1) +    setattr(nn,a_color,m_color[c]) +    setattr(nn,a_transparency,m_transparency[c])      return n  end  tracers.setproperties = setproperties -function tracers.setlistv(n,c,s) -    local f = n +function tracers.setlist(n,c,s) +    local nn = tonut(n)      local mc = m_color[c]      local mt = m_transparency[c]      local mm = texgetattribute(a_colormodel)      if mm <= 0 then          mm = 1      end -    while n do -        n[a_colormodel]   = mm -        n[a_color]        = mc -        n[a_transparency] = mt -        n = n.next +    while nn do +        setattr(nn,a_colormodel,mm) +        setattr(nn,a_color,mc) +        setattr(nn,a_transparency,mt) +        nn = getnext(nn)      end -    return f +    return n  end  function tracers.resetproperties(n) -    n[a_color]        = unsetvalue -    n[a_transparency] = unsetvalue +    local nn = tonut(n) +    setattr(nn,a_color,unsetvalue) +    setattr(nn,a_transparency,unsetvalue)      return n  end -function tracers.rule(w,h,d,c,s) -- so some day we can consider using literals (speedup) -    return setproperties(new_rule(w,h,d),c,s) -end - --- only nodes +-- this one returns a nut  local nodestracerpool = { } +local nutstracerpool  = { }  tracers.pool = {      nodes = nodestracerpool, +    nuts  = nutstracerpool,  } -function nodestracerpool.rule(w,h,d,c,s) -- so some day we can consider using literals (speedup) +table.setmetatableindex(nodestracerpool,function(t,k,v) +    local f = nutstracerpool[k] +    local v = function(...) +        return tonode(f(...)) +    end +    t[k] = v +    return v +end) + +function nutstracerpool.rule(w,h,d,c,s) -- so some day we can consider using literals (speedup)      return setproperties(new_rule(w,h,d),c,s)  end  tracers.rule = nodestracerpool.rule -- for a while + diff --git a/tex/context/base/node-tst.lua b/tex/context/base/node-tst.lua index bfe0051bd..7f5102d5f 100644 --- a/tex/context/base/node-tst.lua +++ b/tex/context/base/node-tst.lua @@ -24,17 +24,26 @@ local rightskip_code             = skipcodes.rightskip  local abovedisplayshortskip_code = skipcodes.abovedisplayshortskip  local belowdisplayshortskip_code = skipcodes.belowdisplayshortskip -local find_node_tail             = node.tail or node.slide +local nuts                       = nodes.nuts -function nodes.leftmarginwidth(n) -- todo: three values +local getfield                   = nuts.getfield +local getnext                    = nuts.getnext +local getprev                    = nuts.getprev +local getid                      = nuts.getid +local getchar                    = nuts.getchar +local getsubtype                 = nuts.getsubtype + +local find_node_tail             = nuts.tail + +function nuts.leftmarginwidth(n) -- todo: three values      while n do -        local id = n.id +        local id = getid(n)          if id == glue_code then -            return n.subtype == leftskip_code and n.spec.width or 0 +            return getsubtype(n) == leftskip_code and getfield(getfield(n,"spec"),"width") or 0          elseif id == whatsit_code then -            n = n.next +            n = getnext(n)          elseif id == hlist_code then -            return n.width +            return getfield(n,"width")          else              break          end @@ -42,15 +51,15 @@ function nodes.leftmarginwidth(n) -- todo: three values      return 0  end -function nodes.rightmarginwidth(n) +function nuts.rightmarginwidth(n)      if n then          n = find_node_tail(n)          while n do -            local id = n.id +            local id = getid(n)              if id == glue_code then -                return n.subtype == rightskip_code and n.spec.width or 0 +                return getsubtype(n) == rightskip_code and getfield(getfield(n,"spec"),"width") or 0              elseif id == whatsit_code then -                n = n.prev +                n = getprev(n)              else                  break              end @@ -59,15 +68,15 @@ function nodes.rightmarginwidth(n)      return false  end -function nodes.somespace(n,all) +function nuts.somespace(n,all)      if n then -        local id = n.id +        local id = getid(n)          if id == glue_code then -            return (all or (n.spec.width ~= 0)) and glue_code +            return (all or (getfield(getfield(n,"spec"),"width") ~= 0)) and glue_code          elseif id == kern_code then -            return (all or (n.kern ~= 0)) and kern +            return (all or (getfield(n,"kern") ~= 0)) and kern          elseif id == glyph_code then -            local category = chardata[n.char].category +            local category = chardata[getchar(n)].category           -- maybe more category checks are needed              return (category == "zs") and glyph_code          end @@ -75,12 +84,12 @@ function nodes.somespace(n,all)      return false  end -function nodes.somepenalty(n,value) +function nuts.somepenalty(n,value)      if n then -        local id = n.id +        local id = getid(n)          if id == penalty_code then              if value then -                return n.penalty == value +                return getfield(n,"penalty") == value              else                  return true              end @@ -89,32 +98,38 @@ function nodes.somepenalty(n,value)      return false  end -function nodes.is_display_math(head) -    local n = head.prev +function nuts.is_display_math(head) +    local n = getprev(head)      while n do -        local id = n.id +        local id = getid(n)          if id == penalty_code then          elseif id == glue_code then -            if n.subtype == abovedisplayshortskip_code then +            if getsubtype(n) == abovedisplayshortskip_code then                  return true              end          else              break          end -        n = n.prev +        n = getprev(n)      end -    n = head.next +    n = getnext(head)      while n do -        local id = n.id +        local id = getid(n)          if id == penalty_code then          elseif id == glue_code then -            if n.subtype == belowdisplayshortskip_code then +            if getsubtype(n) == belowdisplayshortskip_code then                  return true              end          else              break          end -        n = n.next +        n = getnext(n)      end      return false  end + +nodes.leftmarginwidth  = nodes.vianuts(nuts.leftmarginwidth) +nodes.rightmarginwidth = nodes.vianuts(nuts.rightmarginwidth) +nodes.somespace        = nodes.vianuts(nuts.somespace) +nodes.somepenalty      = nodes.vianuts(nuts.somepenalty) +nodes.is_display_math  = nodes.vianuts(nuts.is_display_math) diff --git a/tex/context/base/node-typ.lua b/tex/context/base/node-typ.lua index 4a2ef8d49..5a4dfe4be 100644 --- a/tex/context/base/node-typ.lua +++ b/tex/context/base/node-typ.lua @@ -8,21 +8,27 @@ if not modules then modules = { } end modules ['node-typ'] = {  -- code has been moved to blob-ini.lua -local typesetters = nodes.typesetters or { } -nodes.typesetters = typesetters +local typesetters     = nodes.typesetters or { } +nodes.typesetters     = typesetters -local hpack_node_list = nodes.hpack -local vpack_node_list = nodes.vpack -local fast_hpack_list = nodes.fasthpack +local nuts            = nodes.nuts +local tonode          = nuts.tonode +local tonut           = nuts.tonut -local nodepool        = nodes.pool +local setfield        = nuts.setfield + +local hpack_node_list = nuts.hpack +local vpack_node_list = nuts.vpack +local fast_hpack_list = nuts.fasthpack + +local nodepool        = nuts.pool  local new_glyph       = nodepool.glyph  local new_glue        = nodepool.glue  local utfvalues       = utf.values -local currentfont    = font.current -local fontparameters = fonts.hashes.parameters +local currentfont     = font.current +local fontparameters  = fonts.hashes.parameters  local function tonodes(str,fontid,spacing) -- quick and dirty      local head, prev = nil, nil @@ -53,8 +59,8 @@ local function tonodes(str,fontid,spacing) -- quick and dirty          elseif not head then              head = next          else -            prev.next = next -            next.prev = prev +            setfield(prev,"next",next) +            setfield(next,"prev",prev)          end          prev = next      end @@ -77,17 +83,30 @@ end  local tovpackfast = tovpack -typesetters.tonodes     = tonodes -typesetters.tohpack     = tohpack -typesetters.tohpackfast = tohpackfast -typesetters.tovpack     = tovpack -typesetters.tovpackfast = tovpackfast +local tnuts       = { } +nuts.typesetters  = tnuts + +tnuts.tonodes     = tonodes +tnuts.tohpack     = tohpack +tnuts.tohpackfast = tohpackfast +tnuts.tovpack     = tovpack +tnuts.tovpackfast = tovpackfast + +tnuts.hpack       = tohpack     -- obsolete +tnuts.fast_hpack  = tohpackfast -- obsolete +tnuts.vpack       = tovpack     -- obsolete + +typesetters.tonodes     = function(...) local h, b = tonodes    (...) return tonode(h), b end +typesetters.tohpack     = function(...) local h, b = tohpack    (...) return tonode(h), b end +typesetters.tohpackfast = function(...) local h, b = tohpackfast(...) return tonode(h), b end +typesetters.tovpack     = function(...) local h, b = tovpack    (...) return tonode(h), b end +typesetters.tovpackfast = function(...) local h, b = tovpackfast(...) return tonode(h), b end -typesetters.hpack       = tohpack -typesetters.fast_hpack  = tohpackfast -typesetters.vpack       = tovpack +typesetters.hpack       = typesetters.tohpack     -- obsolete +typesetters.fast_hpack  = typesetters.tofasthpack -- obsolete +typesetters.vpack       = typesetters.tovpack     -- obsolete  -- node.write(nodes.typestters.hpack("Hello World!"))  -- node.write(nodes.typestters.hpack("Hello World!",1,100*1024*10)) -string.tonodes = tonodes -- quite convenient +string.tonodes = function(...) return tonode(tonodes(...)) end  -- quite convenient diff --git a/tex/context/base/pack-rul.lua b/tex/context/base/pack-rul.lua index 329ea63b8..b9fa141fc 100644 --- a/tex/context/base/pack-rul.lua +++ b/tex/context/base/pack-rul.lua @@ -21,15 +21,25 @@ local line_code       = nodes.listcodes.line  local texsetdimen     = tex.setdimen  local texsetcount     = tex.setcount -local texgetbox       = tex.getbox -local hpack           = nodes.hpack -local free            = nodes.free -local copy            = nodes.copy_list -local traverse_id     = nodes.traverse_id -local node_dimensions = nodes.dimensions + +local nuts            = nodes.nuts + +local getfield        = nuts.getfield +local setfield        = nuts.setfield +local getnext         = nuts.getnext +local getprev         = nuts.getprev +local getlist         = nuts.getlist +local getsubtype      = nuts.getsubtype +local getbox          = nuts.getbox + +local hpack           = nuts.hpack +local free            = nuts.free +local copy            = nuts.copy_list +local traverse_id     = nuts.traverse_id +local node_dimensions = nuts.dimensions  function commands.doreshapeframedbox(n) -    local box            = texgetbox(n) +    local box            = getbox(n)      local noflines       = 0      local firstheight    = nil      local lastdepth      = nil @@ -38,27 +48,27 @@ function commands.doreshapeframedbox(n)      local maxwidth       = 0      local totalwidth     = 0      local averagewidth   = 0 -    local boxwidth       = box.width +    local boxwidth       = getfield(box,"width")      if boxwidth ~= 0 then -- and h.subtype == vlist_code -        local list = box.list +        local list = getlist(box)          if list then              local function check(n,repack)                  if not firstheight then -                    firstheight = n.height +                    firstheight = getfield(n,"height")                  end -                lastdepth = n.depth +                lastdepth = getfield(n,"depth")                  noflines = noflines + 1 -                local l = n.list +                local l = getlist(n)                  if l then                      if repack then -                        local subtype = n.subtype +                        local subtype = getsubtype(n)                          if subtype == box_code or subtype == line_code then -                            lastlinelength = node_dimensions(l,n.dir) -- used to be: hpack(copy(l)).width +                            lastlinelength = node_dimensions(l,getfield(n,"dir")) -- used to be: hpack(copy(l)).width                          else -                            lastlinelength = n.width +                            lastlinelength = getfield(n,"width")                          end                      else -                        lastlinelength = n.width +                        lastlinelength = getfield(n,"width")                      end                      if lastlinelength > maxwidth then                          maxwidth = lastlinelength @@ -84,28 +94,27 @@ function commands.doreshapeframedbox(n)              elseif maxwidth ~= 0 then                  if hdone then                      for h in traverse_id(hlist_code,list) do -                        local l = h.list +                        local l = getlist(h)                          if l then -                            local subtype = h.subtype +                            local subtype = getsubtype(n)                              if subtype == box_code or subtype == line_code then -                                h.list = hpack(l,maxwidth,'exactly',h.dir) -                                h.shift = 0 -- needed for display math +                                l = hpack(l,maxwidth,'exactly',getfield(h,"dir")) -- multiple return values +                                setfield(h,"list",l) +                                setfield(h,"shift",0) -- needed for display math, so no width check possible                              end -                            h.width = maxwidth +                            setfield(h,"width",maxwidth)                          end                      end -                    box.width    = maxwidth -- moved -                    averagewidth = noflines > 0 and totalwidth/noflines or 0                  end               -- if vdone then               --     for v in traverse_id(vlist_code,list) do -             --         local width = n.width +             --         local width = getfield(n,"width")               --         if width > maxwidth then -             --             v.width = maxwidth +             --             setfield(v,"width",maxwidth)               --         end               --     end               -- end -                box.width = maxwidth +                setfield(box,"width",maxwidth)                  averagewidth = noflines > 0 and totalwidth/noflines or 0              end          end @@ -119,18 +128,18 @@ function commands.doreshapeframedbox(n)  end  function commands.doanalyzeframedbox(n) -    local box         = texgetbox(n) +    local box         = getbox(n)      local noflines    = 0      local firstheight = nil      local lastdepth   = nil -    if box.width ~= 0 then -        local list = box.list +    if getfield(box,"width") ~= 0 then +        local list = getlist(box)          if list then              local function check(n)                  if not firstheight then -                    firstheight = n.height +                    firstheight = getfield(n,"height")                  end -                lastdepth = n.depth +                lastdepth = getfield(n,"depth")                  noflines = noflines + 1              end              for h in traverse_id(hlist_code,list) do diff --git a/tex/context/base/page-lin.lua b/tex/context/base/page-lin.lua index 7e8e9ad8a..66b7e4684 100644 --- a/tex/context/base/page-lin.lua +++ b/tex/context/base/page-lin.lua @@ -8,32 +8,36 @@ if not modules then modules = { } end modules ['page-lin'] = {  -- experimental -> will become builders -local trace_numbers = false  trackers.register("lines.numbers",  function(v) trace_numbers = v end) - -local report_lines = logs.reporter("lines") +-- if there is demand for it, we can support multiple numbering streams +-- and use more than one attibute -local attributes, nodes, node, context = attributes, nodes, node, context +local next, tonumber = next, tonumber -nodes.lines       = nodes.lines or { } -local lines       = nodes.lines +local trace_numbers      = false  trackers.register("lines.numbers",  function(v) trace_numbers = v end) -lines.data        = lines.data or { } -- start step tag -local data        = lines.data -local last        = #data +local report_lines       = logs.reporter("lines") -local texgetbox   = tex.getbox +local attributes         = attributes +local nodes              = nodes +local context            = context -lines.scratchbox  = lines.scratchbox or 0 +nodes.lines              = nodes.lines or { } +local lines              = nodes.lines -local leftmarginwidth = nodes.leftmarginwidth +lines.data               = lines.data or { } -- start step tag +local data               = lines.data +local last               = #data -storage.register("lines/data", lines.data, "nodes.lines.data") +lines.scratchbox         = lines.scratchbox or 0 --- if there is demand for it, we can support multiple numbering streams --- and use more than one attibute +storage.register("lines/data", data, "nodes.lines.data")  local variables          = interfaces.variables +local v_next             = variables.next +local v_page             = variables.page +local v_no               = variables.no +  local nodecodes          = nodes.nodecodes  local hlist_code         = nodecodes.hlist @@ -49,12 +53,25 @@ local current_list       = { }  local cross_references   = { }  local chunksize          = 250 -- not used in boxed -local traverse_id        = node.traverse_id -local traverse           = node.traverse -local copy_node          = node.copy -local hpack_node         = node.hpack -local insert_node_after  = node.insert_after -local insert_node_before = node.insert_before +local nuts               = nodes.nuts + +local getid              = nuts.getid +local getnext            = nuts.getnext +local getattr            = nuts.getattr +local getlist            = nuts.getlist +local getbox             = nuts.getbox +local getfield           = nuts.getfield + +local setfield           = nuts.setfield + +local traverse_id        = nuts.traverse_id +local traverse           = nuts.traverse +local copy_node          = nuts.copy +local hpack_node         = nuts.hpack +local insert_node_after  = nuts.insert_after +local insert_node_before = nuts.insert_before +local is_display_math    = nuts.is_display_math +local leftmarginwidth    = nuts.leftmarginwidth  -- cross referencing @@ -67,16 +84,16 @@ end  local function resolve(n,m) -- we can now check the 'line' flag (todo)      while n do -        local id = n.id +        local id = getid(n)          if id == whatsit_code then -- why whatsit -            local a = n[a_linereference] +            local a = getattr(n,a_linereference)              if a then                  cross_references[a] = m              end          elseif id == hlist_code or id == vlist_code then -            resolve(n.list,m) +            resolve(getlist(n),m)          end -        n = n.next +        n = getnext(n)      end  end @@ -165,20 +182,20 @@ local function check_number(n,a,skip,sameline)          if sameline then              skipflag = 0              if trace_numbers then -                report_lines("skipping broken line number %s for setup %a: %s (%s)",#current_list,a,s,d.continue or "no") +                report_lines("skipping broken line number %s for setup %a: %s (%s)",#current_list,a,s,d.continue or v_no)              end          elseif not skip and s % d.step == 0 then              skipflag, d.start = 1, s + 1 -- (d.step or 1)              if trace_numbers then -                report_lines("making number %s for setup %a: %s (%s)",#current_list,a,s,d.continue or "no") +                report_lines("making number %s for setup %a: %s (%s)",#current_list,a,s,d.continue or v_no)              end          else              skipflag, d.start = 0, s + 1 -- (d.step or 1)              if trace_numbers then -                report_lines("skipping line number %s for setup %a: %s (%s)",#current_list,a,s,d.continue or "no") +                report_lines("skipping line number %s for setup %a: %s (%s)",#current_list,a,s,d.continue or v_no)              end          end -        context.makelinenumber(tag,skipflag,s,n.shift,n.width,leftmarginwidth(n.list),n.dir) +        context.makelinenumber(tag,skipflag,s,getfield(n,"shift"),getfield(n,"width"),leftmarginwidth(getlist(n)),getfield(n,"dir"))      end  end @@ -189,26 +206,26 @@ end  local function identify(list)      if list then          for n in traverse_id(hlist_code,list) do -            if n[a_linenumber] then +            if getattr(n,a_linenumber) then                  return list              end          end          local n = list          while n do -            local id = n.id +            local id = getid(n)              if id == hlist_code or id == vlist_code then -                local ok = identify(n.list) +                local ok = identify(getlist(n))                  if ok then                      return ok                  end              end -            n = n.next +            n = getnext(n)          end      end  end  function boxed.stage_zero(n) -    return identify(texgetbox(n).list) +    return identify(getlist(getbox(n)))  end  -- reset ranges per page @@ -217,39 +234,39 @@ end  function boxed.stage_one(n,nested)      current_list = { } -    local box = texgetbox(n) +    local box = getbox(n)      if box then -        local list = box.list +        local list = getlist(box)          if nested then              list = identify(list)          end          local last_a, last_v, skip = nil, -1, false          for n in traverse_id(hlist_code,list) do -- attr test here and quit as soon as zero found -            if n.height == 0 and n.depth == 0 then +            if getfield(n,"height") == 0 and getfield(n,"depth") == 0 then                  -- skip funny hlists -- todo: check line subtype              else -                local list = n.list -                local a = list[a_linenumber] +                local list = getlist(n) +                local a = getattr(list,a_linenumber)                  if a and a > 0 then                      if last_a ~= a then                          local da = data[a]                          local ma = da.method -                        if ma == variables.next then +                        if ma == v_next then                              skip = true -                        elseif ma == variables.page then +                        elseif ma == v_page then                              da.start = 1 -- eventually we will have a normal counter                          end                          last_a = a                          if trace_numbers then -                            report_lines("starting line number range %s: start %s, continue",a,da.start,da.continue or "no") +                            report_lines("starting line number range %s: start %s, continue %s",a,da.start,da.continue or v_no)                          end                      end -                    if n[a_displaymath] then -                        if nodes.is_display_math(n) then +                    if getattr(n,a_displaymath) then +                        if is_display_math(n) then                              check_number(n,a,skip)                          end                      else -                        local v = list[a_verbatimline] +                        local v = getattr(list,a_verbatimline)                          if not v or v ~= last_v then                              last_v = v                              check_number(n,a,skip) @@ -268,7 +285,7 @@ function boxed.stage_two(n,m)      if #current_list > 0 then          m = m or lines.scratchbox          local t, tn = { }, 0 -        for l in traverse_id(hlist_code,texgetbox(m).list) do +        for l in traverse_id(hlist_code,getlist(getbox(m))) do              tn = tn + 1              t[tn] = copy_node(l)          end @@ -276,7 +293,8 @@ function boxed.stage_two(n,m)              local li = current_list[i]              local n, m, ti = li[1], li[2], t[i]              if ti then -                ti.next, n.list = n.list, ti +                setfield(ti,"next",getlist(n)) +                setfield(n,"list",ti)                  resolve(n,m)              else                  report_lines("error in linenumbering (1)") diff --git a/tex/context/base/page-mix.lua b/tex/context/base/page-mix.lua index 7d13d9e4e..a7db58f82 100644 --- a/tex/context/base/page-mix.lua +++ b/tex/context/base/page-mix.lua @@ -15,46 +15,71 @@ if not modules then modules = { } end modules ["page-mix"] = {  local concat = table.concat -local nodecodes        = nodes.nodecodes -local gluecodes        = nodes.gluecodes -local nodepool         = nodes.pool - -local hlist_code       = nodecodes.hlist -local vlist_code       = nodecodes.vlist -local kern_code        = nodecodes.kern -local glue_code        = nodecodes.glue -local penalty_code     = nodecodes.penalty -local insert_code      = nodecodes.ins -local mark_code        = nodecodes.mark - -local new_hlist        = nodepool.hlist -local new_vlist        = nodepool.vlist -local new_glue         = nodepool.glue - -local hpack            = node.hpack -local vpack            = node.vpack -local freenode         = node.free -local concatnodes      = nodes.concat - -local texgetbox        = tex.getbox -local texsetbox        = tex.setbox -local texgetskip       = tex.getskip - -local points           = number.points - -local settings_to_hash = utilities.parsers.settings_to_hash - -local variables        = interfaces.variables -local v_yes            = variables.yes -local v_global         = variables["global"] -local v_local          = variables["local"] -local v_columns        = variables.columns -  local trace_state  = false  trackers.register("mixedcolumns.trace",  function(v) trace_state  = v end)  local trace_detail = false  trackers.register("mixedcolumns.detail", function(v) trace_detail = v end)  local report_state = logs.reporter("mixed columns") +local nodecodes           = nodes.nodecodes +local gluecodes           = nodes.gluecodes + +local hlist_code          = nodecodes.hlist +local vlist_code          = nodecodes.vlist +local kern_code           = nodecodes.kern +local glue_code           = nodecodes.glue +local penalty_code        = nodecodes.penalty +local insert_code         = nodecodes.ins +local mark_code           = nodecodes.mark +local rule_code           = nodecodes.rule + +local topskip_code        = gluecodes.topskip +local lineskip_code       = gluecodes.lineskip +local baselineskip_code   = gluecodes.baselineskip +local userskip_code       = gluecodes.userskip + +local nuts                = nodes.nuts +local tonode              = nuts.tonode +local nodetostring        = nuts.tostring +local listtoutf           = nodes.listtoutf + +local hpack               = nuts.hpack +local vpack               = nuts.vpack +local freenode            = nuts.free +local concatnodes         = nuts.concat + +local getfield            = nuts.getfield +local setfield            = nuts.setfield +local getnext             = nuts.getnext +local getprev             = nuts.getprev +local getid               = nuts.getid +local getlist             = nuts.getlist +local getsubtype          = nuts.getsubtype +local getbox              = nuts.getbox +local setbox              = nuts.setbox +local getskip             = nuts.getskip +local getattribute        = nuts.getattribute + +local nodepool            = nuts.pool + +local new_hlist           = nodepool.hlist +local new_vlist           = nodepool.vlist +local new_glue            = nodepool.glue + +local points              = number.points + +local settings_to_hash    = utilities.parsers.settings_to_hash + +local variables           = interfaces.variables +local v_yes               = variables.yes +local v_global            = variables["global"] +local v_local             = variables["local"] +local v_columns           = variables.columns +local v_fixed             = variables.fixed +local v_auto              = variables.auto +local v_none              = variables.none +local v_more              = variables.more +local v_less              = variables.less +  pagebuilders              = pagebuilders or { }  pagebuilders.mixedcolumns = pagebuilders.mixedcolumns or { }  local mixedcolumns        = pagebuilders.mixedcolumns @@ -77,13 +102,13 @@ local function collectinserts(result,nxt,nxtid)      local inserts, currentskips, nextskips, inserttotal = { }, 0, 0, 0      while nxt do          if nxtid == insert_code then -            inserttotal = inserttotal + nxt.height + nxt.depth -            local s = nxt.subtype +            inserttotal = inserttotal + getfield(nxt,"height") + getfield(nxt,"depth") +            local s = getsubtype(nxt)              local c = inserts[s]              if not c then                  c = { }                  inserts[s] = c -                local width = texgetskip(s).width +                local width = getfield(getskip(s),"width")                  if not result.inserts[s] then                      currentskips = currentskips + width                  end @@ -100,9 +125,9 @@ local function collectinserts(result,nxt,nxtid)          else              break          end -        nxt = nxt.next +        nxt = getnext(nxt)          if nxt then -            nxtid = nxt.id +            nxtid = getid(nxt)          else              break          end @@ -128,30 +153,30 @@ end  local function discardtopglue(current,discarded)      local size = 0      while current do -        local id = current.id +        local id = getid(current)          if id == glue_code then -            size = size + current.spec.width +            size = size + getfield(getfield(current,"spec"),"width")              discarded[#discarded+1] = current -            current = current.next +            current = getnext(current)          elseif id == penalty_code then -            if current.penalty == forcedbreak then +            if getfield(current,"penalty") == forcedbreak then                  discarded[#discarded+1] = current -                current = current.next -                while current and current.id == glue_code do -                    size = size + current.spec.width +                current = getnext(current) +                while current and getid(current) == glue_code do +                    size = size + getfield(getfield(current,"spec"),"width")                      discarded[#discarded+1] = current -                    current = current.next +                    current = getnext(current)                  end              else                  discarded[#discarded+1] = current -                current = current.next +                current = getnext(current)              end          else              break          end      end      if current then -        current.prev = nil +        setfield(current,"prev",nil) -- prevent look back      end      return current, size  end @@ -162,13 +187,13 @@ local function stripbottomglue(results,discarded)          local r = results[i]          local t = r.tail          while t and t ~= r.head do -            local prev = t.prev +            local prev = getprev(t)              if not prev then                  break              end -            local id = t.id +            local id = getid(t)              if id == penalty_code then -                if t.penalty == forcedbreak then +                if getfield(t,"penalty") == forcedbreak then                      break                  else                      discarded[#discarded+1] = t @@ -177,7 +202,7 @@ local function stripbottomglue(results,discarded)                  end              elseif id == glue_code then                  discarded[#discarded+1] = t -                local width = t.spec.width +                local width = getfield(getfield(t,"spec"),"width")                  if trace_state then                      report_state("columns %s, discarded bottom glue %p",i,width)                  end @@ -201,20 +226,20 @@ local function setsplit(specification) -- a rather large function          report_state("fatal error, no box")          return      end -    local list = texgetbox(box) +    local list = getbox(box)      if not list then          report_state("fatal error, no list")          return      end -    local head = list.head or specification.originalhead +    local head = getlist(list) or specification.originalhead      if not head then          report_state("fatal error, no head")          return      end      local discarded = { }      local originalhead = head -    local originalwidth = specification.originalwidth or list.width -    local originalheight = specification.originalheight or list.height +    local originalwidth = specification.originalwidth or getfield(list,"width") +    local originalheight = specification.originalheight or getfield(list,"height")      local current = head      local skipped = 0      local height = 0 @@ -277,20 +302,20 @@ local function setsplit(specification) -- a rather large function          local current = start          -- first skip over glue and penalty          while current do -            local id = current.id +            local id = getid(current)              if id == glue_code or id == penalty_code then -                current = current.prev +                current = getprev(current)              else                  break              end          end          -- then skip over content          while current do -            local id = current.id +            local id = getid(current)              if id == glue_code or id == penalty_code then                  break              else -                current = current.prev +                current = getprev(current)              end          end          if not current then @@ -324,7 +349,7 @@ local function setsplit(specification) -- a rather large function              if current == head then                  result.tail = head              else -                result.tail = current.prev +                result.tail = getprev(current)              end              result.height = height              result.depth  = depth @@ -344,6 +369,9 @@ local function setsplit(specification) -- a rather large function                  report_state("setting collector to column %s",column)              end              current, skipped = discardtopglue(current,discarded) +            if trace_detail and skipped ~= 0 then +                report_state("check > column 1, discarded %p",skipped) +            end              head = current              return true, skipped          end @@ -387,7 +415,7 @@ local function setsplit(specification) -- a rather large function      head = current      local function process_skip(current,nxt) -        local advance = current.spec.width +        local advance = getfield(getfield(current,"spec"),"width")          if advance ~= 0 then              local state, skipped = checked(advance,"glue")              if trace_state then @@ -411,7 +439,7 @@ local function setsplit(specification) -- a rather large function      end      local function process_kern(current,nxt) -        local advance = current.kern +        local advance = getfield(current,"kern")          if advance ~= 0 then              local state, skipped = checked(advance,"kern")              if trace_state then @@ -434,7 +462,7 @@ local function setsplit(specification) -- a rather large function      local function process_rule(current,nxt)          -- simple variant of h|vlist -        local advance = current.height -- + current.depth +        local advance = getfield(current,"height") -- + getfield(current,"depth")          local state, skipped = checked(advance+currentskips,"rule")          if trace_state then              report_state("%-7s > column %s, state %a, rule, advance %p, height %p","line",column,state,advance,inserttotal,height) @@ -451,7 +479,7 @@ local function setsplit(specification) -- a rather large function          else              height = height + currentskips          end -        depth = current.depth +        depth = getfield(current,"depth")          skip  = 0      end @@ -462,12 +490,12 @@ local function setsplit(specification) -- a rather large function      -- [chapter] [penalty] [section] [penalty] [first line]      local function process_penalty(current,nxt) -        local penalty = current.penalty +        local penalty = getfield(current,"penalty")          if penalty == 0 then              lastlocked  = nil              lastcurrent = nil          elseif penalty == forcedbreak then -            local needed  = current[a_checkedbreak] +            local needed  = getattribute(current,a_checkedbreak)              local proceed = not needed or needed == 0              if not proceed then                  local available = target - height @@ -515,12 +543,12 @@ local function setsplit(specification) -- a rather large function      end      local function process_list(current,nxt) -        local nxtid = nxt and nxt.id +        local nxtid = nxt and getid(nxt)          line = line + 1          local inserts, currentskips, nextskips, inserttotal = nil, 0, 0, 0 -        local advance = current.height -- + current.depth +        local advance = getfield(current,"height") -- + getfield(current,"depth")          if trace_state then -            report_state("%-7s > column %s, content: %s","line",column,listtoutf(current.list,true,true)) +            report_state("%-7s > column %s, content: %s","line",column,listtoutf(getlist(current),true,true))          end          if nxt and (nxtid == insert_code or nxtid == mark_code) then              nxt, inserts, localskips, insertskips, inserttotal = collectinserts(result,nxt,nxtid) @@ -541,7 +569,7 @@ local function setsplit(specification) -- a rather large function          else              height = height + currentskips          end -        depth = current.depth +        depth = getfield(current,"depth")          skip  = 0          if inserts then              -- so we already collect them ... makes backtracking tricky ... alternatively @@ -555,8 +583,8 @@ local function setsplit(specification) -- a rather large function      while current do -        local id  = current.id -        local nxt = current.next +        local id  = getid(current) +        local nxt = getnext(current)          backtracked = false @@ -629,7 +657,7 @@ local function setsplit(specification) -- a rather large function      specification.overflow       = overflow      specification.discarded      = discarded -    texgetbox(specification.box).list = nil +    setfield(getbox(specification.box),"list",nil)      return specification  end @@ -641,12 +669,12 @@ function mixedcolumns.finalize(result)              local r = results[i]              local h = r.head              if h then -                h.prev = nil +                setfield(h,"prev",nil)                  local t = r.tail                  if t then -                    t.next = nil +                    setfield(t,"next",nil)                  else -                    h.next = nil +                    setfield(h,"next",nil)                      r.tail = h                  end                  for c, list in next, r.inserts do @@ -655,13 +683,13 @@ function mixedcolumns.finalize(result)                          local l = list[i]                          local h = new_hlist()                          t[i] = h -                        h.head = l.head -                        h.height = l.height -                        h.depth = l.depth +                        setfield(h,"list",l.head) +                        setfield(h,"height",l.height) +                        setfield(h,"depth",l.depth)                          l.head = nil                      end -                    t[1].prev  = nil -- needs checking -                    t[#t].next = nil -- needs checking +                    setfield(t[1],"prev",nil)  -- needs checking +                    setfield(t[#t],"next",nil) -- needs checking                      r.inserts[c] = t                  end              end @@ -733,13 +761,13 @@ function mixedcolumns.getsplit(result,n)          return new_glue(result.originalwidth)      end -    h.prev = nil -- move up +    setfield(h,"prev",nil) -- move up      local strutht    = result.strutht      local strutdp    = result.strutdp      local lineheight = strutht + strutdp      local v = new_vlist() -    v.head = h +    setfield(v,"list",h)   -- local v = vpack(h,"exactly",height) @@ -761,14 +789,14 @@ function mixedcolumns.getsplit(result,n)          dp = result.depth      end -    v.width  = wd -    v.height = ht -    v.depth  = dp +    setfield(v,"width",wd) +    setfield(v,"height",ht) +    setfield(v,"depth",dp)      if trace_state then -        local id = h.id +        local id = getid(h)          if id == hlist_code then -            report_state("flush, column %s, grid %a, width %p, height %p, depth %p, %s: %s",n,grid,wd,ht,dp,"top line",nodes.toutf(h.list)) +            report_state("flush, column %s, grid %a, width %p, height %p, depth %p, %s: %s",n,grid,wd,ht,dp,"top line",listtoutf(getlist(h)))          else              report_state("flush, column %s, grid %a, width %p, height %p, depth %p, %s: %s",n,grid,wd,ht,dp,"head node",nodecodes[id])          end @@ -777,8 +805,8 @@ function mixedcolumns.getsplit(result,n)      for c, list in next, r.inserts do          local l = concatnodes(list)          local b = vpack(l) -- multiple arguments, todo: fastvpack -     -- texsetbox("global",c,b) -        texsetbox(c,b) +     -- setbox("global",c,b) +        setbox(c,b)          r.inserts[c] = nil      end @@ -822,7 +850,7 @@ end  function commands.mixgetsplit(n)      if result then -        context(mixedcolumns.getsplit(result,n)) +        context(tonode(mixedcolumns.getsplit(result,n)))      end  end @@ -834,13 +862,13 @@ end  function commands.mixflushrest()      if result then -        context(mixedcolumns.getrest(result)) +        context(tonode(mixedcolumns.getrest(result)))      end  end  function commands.mixflushlist()      if result then -        context(mixedcolumns.getlist(result)) +        context(tonode(mixedcolumns.getlist(result)))      end  end diff --git a/tex/context/base/scrp-cjk.lua b/tex/context/base/scrp-cjk.lua index 681fc4c43..9050da6be 100644 --- a/tex/context/base/scrp-cjk.lua +++ b/tex/context/base/scrp-cjk.lua @@ -14,15 +14,29 @@ if not modules then modules = { } end modules ['scrp-cjk'] = {  -- sense either because otherwise a wanted space at the end of a  -- line would have to be a hard coded ones. -local utfchar = utf.char - -local insert_node_after  = nodes.insert_after -local insert_node_before = nodes.insert_before -local remove_node        = nodes.remove -local copy_node          = nodes.copy -local traverse_id        = nodes.traverse_id - -local nodepool           = nodes.pool +local utfchar = utf.getchar + +local nuts               = nodes.nuts +local tonut              = nodes.tonut +local tonode             = nodes.tonode + +local insert_node_after  = nuts.insert_after +local insert_node_before = nuts.insert_before +local copy_node          = nuts.copy +local remove_node        = nuts.remove +local traverse_id        = nuts.traverse_id + +local getnext            = nuts.getnext +local getprev            = nuts.getprev +local getfont            = nuts.getfont +local getchar            = nuts.getchar +local getid              = nuts.getid +local getattr            = nuts.getattr +local getsubtype         = nuts.getsubtype +local getfield           = nuts.getfield +local setfield           = nuts.setfield + +local nodepool           = nuts.pool  local new_glue           = nodepool.glue  local new_kern           = nodepool.kern  local new_penalty        = nodepool.penalty @@ -88,20 +102,20 @@ end  -- at font definition time and/or just assume a correct font  local function trace_detail(current,what) -    local prev = current.prev -    local c_id = current.id -    local p_id = prev and prev.id +    local prev = getprev(current) +    local c_id = getid(current) +    local p_id = prev and getid(prev)      if c_id == glyph_code then -        local c_ch = current.char +        local c_ch = getchar(current)          if p_id == glyph_code then -            local p_ch = p_id and prev.char +            local p_ch = p_id and getchar(prev)              report_details("[%C %a] [%s] [%C %a]",p_ch,hash[p_ch],what,c_ch,hash[c_ch])          else              report_details("[%s] [%C %a]",what,c_ch,hash[c_ch])          end      else          if p_id == glyph_code then -            local p_ch = p_id and prev.char +            local p_ch = p_id and getchar(prev)              report_details("[%C %a] [%s]",p_ch,hash[p_ch],what)          else              report_details("[%s]",what) @@ -110,8 +124,8 @@ local function trace_detail(current,what)  end  local function trace_detail_between(p,n,what) -    local p_ch = p.char -    local n_ch = n.char +    local p_ch = getchar(p) +    local n_ch = getchar(n)      report_details("[%C %a] [%s] [%C %a]",p_ch,hash[p_ch],what,n_ch,hash[n_ch])  end @@ -427,29 +441,29 @@ local function process(head,first,last)      if first ~= last then          local lastfont, previous, last = nil, "start", nil          while true do -            local upcoming, id = first.next, first.id +            local upcoming, id = getnext(first), getid(first)              if id == glyph_code then -                local a = first[a_scriptstatus] +                local a = getattr(first,a_scriptstatus)                  local current = numbertocategory[a]                  local action = injectors[previous]                  if action then                      action = action[current]                      if action then -                        local font = first.font +                        local font = getfont(first)                          if font ~= lastfont then                              lastfont = font -                            set_parameters(font,numbertodataset[first[a_scriptinjection]]) +                            set_parameters(font,numbertodataset[getattr(first,a_scriptinjection)])                          end                          action(head,first)                      end                  end                  previous = current              else -- glue -                local p, n = first.prev, upcoming +                local p, n = getprev(first), upcoming                  if p and n then -                    local pid, nid = p.id, n.id +                    local pid, nid = getid(p), getid(n)                      if pid == glyph_code and nid == glyph_code then -                        local pa, na = p[a_scriptstatus], n[a_scriptstatus] +                        local pa, na = getattr(p,a_scriptstatus), getattr(n,a_scriptstatus)                          local pcjk, ncjk = pa and numbertocategory[pa], na and numbertocategory[na]                          if not pcjk                 or not ncjk                              or pcjk == "korean"     or ncjk == "korean" @@ -495,23 +509,24 @@ scripts.installmethod {  }  function scripts.decomposehangul(head) +    local head = tonut(head)      local done = false      for current in traverse_id(glyph_code,head) do -        local lead_consonant, medial_vowel, tail_consonant = decomposed(current.char) +        local lead_consonant, medial_vowel, tail_consonant = decomposed(getchar(current))          if lead_consonant then -            current.char = lead_consonant +            setfield(current,"char",lead_consonant)              local m = copy_node(current) -            m.char = medial_vowel +            setfield(m,"char",medial_vowel)              head, current = insert_node_after(head,current,m)              if tail_consonant then                  local t = copy_node(current) -                t.char = tail_consonant +                setfield(t,"char",tail_consonant)                  head, current = insert_node_after(head,current,t)              end              done = true          end      end -    return head, done +    return tonode(head), done  end  -- nodes.tasks.prependaction("processors","normalizers","scripts.decomposehangul") @@ -682,29 +697,29 @@ local function process(head,first,last)      if first ~= last then          local lastfont, previous, last = nil, "start", nil          while true do -            local upcoming, id = first.next, first.id +            local upcoming, id = getnext(first), getid(first)              if id == glyph_code then -                local a = first[a_scriptstatus] +                local a = getattr(first,a_scriptstatus)                  local current = numbertocategory[a]                  local action = injectors[previous]                  if action then                      action = action[current]                      if action then -                        local font = first.font +                        local font = getfont(first)                          if font ~= lastfont then                              lastfont = font -                            set_parameters(font,numbertodataset[first[a_scriptinjection]]) +                            set_parameters(font,numbertodataset[getattr(first,a_scriptinjection)])                          end                          action(head,first)                      end                  end                  previous = current              else -- glue -                local p, n = first.prev, upcoming +                local p, n = getprev(first), upcoming                  if p and n then -                    local pid, nid = p.id, n.id +                    local pid, nid = getid(p), getid(n)                      if pid == glyph_code and nid == glyph_code then -                        local pa, na = p[a_scriptstatus], n[a_scriptstatus] +                        local pa, na = getattr(p,a_scriptstatus), getattr(n,a_scriptstatus)                          local pcjk, ncjk = pa and numbertocategory[pa], na and numbertocategory[na]                          if not pcjk                       or not ncjk                              or pcjk == "korean"           or ncjk == "korean" @@ -904,34 +919,32 @@ local function process(head,first,last)      if first ~= last then          local lastfont, previous, last = nil, "start", nil          while true do -            local upcoming, id = first.next, first.id +            local upcoming, id = getnext(first), getid(first)              if id == glyph_code then -                local a = first[a_scriptstatus] +                local a = getattr(first,a_scriptstatus)                  local current = numbertocategory[a]                  local action = injectors[previous]                  if action then                      action = action[current]                      if action then -                        local font = first.font +                        local font = getfont(first)                          if font ~= lastfont then                              lastfont = font -                            set_parameters(font,numbertodataset[first[a_scriptinjection]]) +                            set_parameters(font,numbertodataset[getattr(first,a_scriptinjection)])                          end                          action(head,first)                      end                  end                  previous = current - --- elseif id == math_code then ---     upcoming = end_of_math(current).next ---     previous = "start" - +         -- elseif id == math_code then +         --     upcoming = getnext(end_of_math(current)) +         --     previous = "start"              else -- glue -                local p, n = first.prev, upcoming -- we should remember prev +                local p, n = getprev(first), upcoming -- we should remember prev                  if p and n then -                    local pid, nid = p.id, n.id +                    local pid, nid = getid(p), getid(n)                      if pid == glyph_code and nid == glyph_code then -                        local pa, na = p[a_scriptstatus], n[a_scriptstatus] +                        local pa, na = getattr(p,a_scriptstatus), getattr(n,a_scriptstatus)                          local pcjk, ncjk = pa and numbertocategory[pa], na and numbertocategory[na]                          if not pcjk                       or not ncjk                              or pcjk == "korean"           or ncjk == "korean" @@ -940,17 +953,17 @@ local function process(head,first,last)                              or pcjk == "half_width_close" or ncjk == "half_width_open" then -- extra compared to korean                              previous = "start"                          else -- if head ~= first then -if id == glue_code and first.subtype == userskip_code then -- also scriptstatus check? -    -- for the moment no distinction possible between space and userskip -    local w = first.spec.width -    local s = spacedata[p.font] -    if w == s then -- could be option -        if trace_details then -            trace_detail_between(p,n,"space removed") -        end -                            remove_node(head,first,true) -    end -end +                            if id == glue_code and getsubtype(first) == userskip_code then -- also scriptstatus check? +                                -- for the moment no distinction possible between space and userskip +                                local w = getfield(getfield(first,"spec"),"width") +                                local s = spacedata[getfont(p)] +                                if w == s then -- could be option +                                    if trace_details then +                                        trace_detail_between(p,n,"space removed") +                                    end +                                    remove_node(head,first,true) +                                end +                            end                              previous = pcjk                      --    else                      --        previous = pcjk diff --git a/tex/context/base/scrp-eth.lua b/tex/context/base/scrp-eth.lua index 597afa1b5..8ecbce522 100644 --- a/tex/context/base/scrp-eth.lua +++ b/tex/context/base/scrp-eth.lua @@ -9,9 +9,17 @@ if not modules then modules = { } end modules ['scrp-eth'] = {  -- at some point I will review the script code but for the moment we  -- do it this way; so space settings like with cjk yet -local insert_node_before = node.insert_before +local nuts               = nodes.nuts -local nodepool           = nodes.pool +local getnext            = nuts.getnext +local getfont            = nuts.getfont +local getchar            = nuts.getchar +local getid              = nuts.getid +local getattr            = nuts.getattr + +local insert_node_before = nuts.insert_before + +local nodepool           = nuts.pool  local new_glue           = nodepool.glue  local new_penalty        = nodepool.penalty @@ -37,13 +45,13 @@ local inter_character_stretch_factor = 1  local inter_character_shrink_factor  = 1  local function space_glue(current) -    local data = numbertodataset[current[a_scriptinjection]] +    local data = numbertodataset[getattr(current,a_scriptinjection)]      if data then          inter_character_space_factor   = data.inter_character_space_factor   or 1          inter_character_stretch_factor = data.inter_character_stretch_factor or 1          inter_character_shrink_factor  = data.inter_character_shrink_factor  or 1      end -    local font = current.font +    local font = getfont(current)      if lastfont ~= font then          local pf = parameters[font]          space    = pf.space @@ -104,9 +112,9 @@ local function process(head,first,last)          local injector = false          local current = first          while current do -            local id = current.id +            local id = getid(current)              if id == glyph_code then -                local scriptstatus = current[a_scriptstatus] +                local scriptstatus = getattr(current,a_scriptstatus)                  local category = numbertocategory[scriptstatus]                  if injector then                      local action = injector[category] @@ -121,7 +129,7 @@ local function process(head,first,last)              if current == last then                  break              else -                current = current.next +                current = getnext(current)              end          end      end diff --git a/tex/context/base/scrp-ini.lua b/tex/context/base/scrp-ini.lua index 56422e622..a6bfe4cf9 100644 --- a/tex/context/base/scrp-ini.lua +++ b/tex/context/base/scrp-ini.lua @@ -14,7 +14,7 @@ local attributes, nodes, node = attributes, nodes, node  local trace_analyzing    = false  trackers.register("scripts.analyzing",        function(v) trace_analyzing   = v end)  local trace_injections   = false  trackers.register("scripts.injections",       function(v) trace_injections  = v end)  local trace_splitting    = false  trackers.register("scripts.splitting",        function(v) trace_splitting   = v end) -local trace_splitdetail  = false  trackers.register("scripts.splitring.detail", function(v) trace_splitdetail = v end) +local trace_splitdetail  = false  trackers.register("scripts.splitting.detail", function(v) trace_splitdetail = v end)  local report_preprocessing = logs.reporter("scripts","preprocessing")  local report_splitting     = logs.reporter("scripts","splitting") @@ -22,9 +22,6 @@ local report_splitting     = logs.reporter("scripts","splitting")  local utfbyte, utfsplit = utf.byte, utf.split  local gmatch = string.gmatch -local first_glyph       = node.first_glyph or node.first_character -local traverse_id       = node.traverse_id -  local texsetattribute   = tex.setattribute  local nodecodes         = nodes.nodecodes @@ -48,9 +45,23 @@ local setmetatableindex = table.setmetatableindex  local enableaction      = nodes.tasks.enableaction  local disableaction     = nodes.tasks.disableaction -local insert_node_after = node.insert_after +local nuts              = nodes.nuts +local tonut             = nuts.tonut +local tonode            = nuts.tonode + +local getnext           = nuts.getnext +local getchar           = nuts.getchar +local getfont           = nuts.getfont +local getid             = nuts.getid +local getattr           = nuts.getattr +local setattr           = nuts.setattr + +local insert_node_after = nuts.insert_after +local first_glyph       = nuts.first_glyph +local traverse_id       = nuts.traverse_id + +local nodepool          = nuts.pool -local nodepool          = nodes.pool  local new_glue          = nodepool.glue  local new_rule          = nodepool.rule  local new_penalty       = nodepool.penalty @@ -400,7 +411,7 @@ scripts.numbertocategory = numbertocategory  local function colorize(start,stop)      for n in traverse_id(glyph_code,start) do -        local kind = numbertocategory[n[a_scriptstatus]] +        local kind = numbertocategory[getattr(n,a_scriptstatus)]          if kind then              local ac = scriptcolors[kind]              if ac then @@ -432,16 +443,17 @@ end  -- we can have a fonts.hashes.originals  function scripts.injectors.handler(head) +    head = tonut(head)      local start = first_glyph(head) -- we already have glyphs here (subtype 1)      if not start then -        return head, false +        return tonode(head), false      else          local last_a, normal_process, lastfont, originals = nil, nil, nil, nil          local done, first, last, ok = false, nil, nil, false          while start do -            local id = start.id +            local id = getid(start)              if id == glyph_code then -                local a = start[a_scriptinjection] +                local a = getattr(start,a_scriptinjection)                  if a then                      if a ~= last_a then                          if first then @@ -463,7 +475,7 @@ function scripts.injectors.handler(head)                          normal_process = handler.injector                      end                      if normal_process then -                        local f = start.font +                        local f = getfont(start)                          if f ~= lastfont then                              originals = fontdata[f].resources                              if resources then @@ -473,13 +485,13 @@ function scripts.injectors.handler(head)                              end                              lastfont = f                          end -                        local c = start.char +                        local c = getchar(start)                          if originals then                              c = originals[c] or c                          end                          local h = hash[c]                          if h then -                            start[a_scriptstatus] = categorytonumber[h] +                            setattr(start,a_scriptstatus,categorytonumber[h])                              if not first then                                  first, last = start, start                              else @@ -540,7 +552,7 @@ function scripts.injectors.handler(head)                      first, last = nil, nil                  end              end -            start = start.next +            start = getnext(start)          end          if ok then              if trace_analyzing then @@ -553,7 +565,7 @@ function scripts.injectors.handler(head)              end              done = true          end -        return head, done +        return tonode(head), done      end  end @@ -683,11 +695,11 @@ end)  local categories = characters.categories or { }  local function hit(root,head) -    local current   = head.next +    local current   = getnext(head)      local lastrun   = false      local lastfinal = false -    while current and current.id == glyph_code do -        local char = current.char +    while current and getid(current) == glyph_code do +        local char = getchar(current)          local newroot = root[char]          if newroot then              local final = newroot.final @@ -701,7 +713,7 @@ local function hit(root,head)          else              return lastrun, lastfinal          end -        current = current.next +        current = getnext(current)      end      if lastrun then          return lastrun, lastfinal @@ -710,12 +722,13 @@ end  local tree, attr, proc -function splitters.handler(head) +function splitters.handler(head) -- todo: also first_glyph test +    head = tonut(head)      local current = head      local done = false      while current do -        if current.id == glyph_code then -            local a = current[a_scriptsplitting] +        if getid(current) == glyph_code then +            local a = getattr(current,a_scriptsplitting)              if a then                  if a ~= attr then                      local handler = numbertohandler[a] @@ -724,14 +737,14 @@ function splitters.handler(head)                      proc = handler.splitter                  end                  if proc then -                    local root = tree[current.char] +                    local root = tree[getchar(current)]                      if root then                          -- we don't check for attributes in the hitter (yet)                          local last, final = hit(root,current)                          if last then -                            local next = last.next -                            if next and next.id == glyph_code then -                                local nextchar = next.char +                            local next = getnext(last) +                            if next and getid(next) == glyph_code then +                                local nextchar = getchar(next)                                  if tree[nextchar] then                                      if trace_splitdetail then                                          if type(final) == "string" then @@ -760,9 +773,9 @@ function splitters.handler(head)                  end              end          end -        current = current.next +        current = getnext(current)      end -    return head, done +    return tonode(head), done  end  local function marker(head,current,font,color) -- could become: nodes.tracers.marker @@ -792,8 +805,8 @@ end  local last_a, last_f, last_s, last_q  function splitters.insertafter(handler,head,first,last,detail) -    local a = first[a_scriptsplitting] -    local f = first.font +    local a = getattr(first,a_scriptsplitting) +    local f = getfont(first)      if a ~= last_a or f ~= last_f then          last_s = emwidths[f] * numbertodataset[a].inter_word_stretch_factor          last_a = a @@ -870,15 +883,15 @@ setmetatableindex(cache_nop,function(t,k) local v = { } t[k] = v return v end)  -- playing nice  function autofontfeature.handler(head) -    for n in traverse_id(glyph_code,head) do -     -- if n[a_scriptinjection] then +    for n in traverse_id(glyph_code,tonut(head)) do +     -- if getattr(n,a_scriptinjection) then       --     -- already tagged by script feature, maybe some day adapt       -- else -            local char = n.char +            local char = getchar(n)              local script = otfscripts[char]              if script then -                local dynamic = n[0] or 0 -                local font = n.font +                local dynamic = getattr(n,0) or 0 +                local font = getfont(n)                  if dynamic > 0 then                      local slot = cache_yes[font]                      local attr = slot[script] @@ -904,7 +917,7 @@ function autofontfeature.handler(head)                          end                      end                      if attr ~= 0 then -                        n[0] = attr +                        setattr(n,0,attr)                          -- maybe set scriptinjection when associated                      end                  end diff --git a/tex/context/base/spac-ali.lua b/tex/context/base/spac-ali.lua index 25cc6cd66..cf7d45172 100644 --- a/tex/context/base/spac-ali.lua +++ b/tex/context/base/spac-ali.lua @@ -15,8 +15,25 @@ local prependaction    = tasks.prependaction  local disableaction    = tasks.disableaction  local enableaction     = tasks.enableaction -local slide_nodes      = node.slide -local hpack_nodes      = node.hpack -- nodes.fasthpack not really faster here +local nuts             = nodes.nuts +local nodepool         = nuts.pool + +local tonode           = nuts.tonode +local tonut            = nuts.tonut + +local getfield         = nuts.getfield +local setfield         = nuts.setfield +local getnext          = nuts.getnext +local getprev          = nuts.getprev +local getid            = nuts.getid +local getlist          = nuts.getlist +local getattr          = nuts.getattr +local setattr          = nuts.setattr +local getsubtype       = nuts.getsubtype + +local slide_nodes      = nuts.slide +local hpack_nodes      = nuts.hpack -- nodes.fasthpack not really faster here +local linked_nodes     = nuts.linked  local unsetvalue       = attributes.unsetvalue @@ -27,8 +44,6 @@ local hlist_code       = nodecodes.hlist  local vlist_code       = nodecodes.vlist  local line_code        = listcodes.line -local nodepool         = nodes.pool -  local new_stretch      = nodepool.stretch  local a_realign        = attributes.private("realign") @@ -56,10 +71,10 @@ local function handler(head,leftpage,realpageno)      local current = head      local done = false      while current do -        local id = current.id +        local id = getid(current)          if id == hlist_code then -            if current.subtype == line_code then -                local a = current[a_realign] +            if getsubtype(current) == line_code then +                local a = getattr(current,a_realign)                  if not a or a == 0 then                      -- skip                  else @@ -75,12 +90,12 @@ local function handler(head,leftpage,realpageno)                              action = leftpage and 2 or 1                          end                          if action == 1 then -                            current.list = hpack_nodes(current.list .. new_stretch(3),current.width,"exactly") +                            setfield(current,"list",hpack_nodes(linked_nodes(getlist(current),new_stretch(3)),getfield(current,"width"),"exactly"))                              if trace_realign then                                  report_realign("flushing left, align %a, page %a, realpage %a",align,pageno,realpageno)                              end                          elseif action == 2 then -                            current.list = hpack_nodes(new_stretch(3) .. current.list,current.width,"exactly") +                            setfield(current,"list",hpack_nodes(linked_nodes(new_stretch(3),getlist(current)),getfield(current,"width"),"exactly"))                              if trace_realign then                                  report_realign("flushing right. align %a, page %a, realpage %a",align,pageno,realpageno)                              end @@ -90,14 +105,14 @@ local function handler(head,leftpage,realpageno)                          done = true                          nofrealigned = nofrealigned + 1                      end -                    current[a_realign] = unsetvalue +                    setattr(current,a_realign,unsetvalue)                  end              end -            handler(current.list,leftpage,realpageno) +            handler(getlist(current),leftpage,realpageno)          elseif id == vlist_code then -            handler(current.list,leftpage,realpageno) +            handler(getlist(current),leftpage,realpageno)          end -        current = current.next +        current = getnext(current)      end      return head, done  end @@ -105,7 +120,8 @@ end  function alignments.handler(head)      local leftpage = isleftpage(true,false)      local realpageno = texgetcount("realpageno") -    return handler(head,leftpage,realpageno) +    local head, done = handler(tonut(head),leftpage,realpageno) +    return tonode(head), done  end  local enabled = false diff --git a/tex/context/base/spac-chr.lua b/tex/context/base/spac-chr.lua index db98b42a6..4122a64b6 100644 --- a/tex/context/base/spac-chr.lua +++ b/tex/context/base/spac-chr.lua @@ -22,14 +22,29 @@ report_characters = logs.reporter("typesetting","characters")  local nodes, node = nodes, node -local insert_node_after  = nodes.insert_after -local remove_node        = nodes.remove -local copy_node_list     = nodes.copy_list -local traverse_id        = nodes.traverse_id +local nuts               = nodes.nuts + +local tonode             = nuts.tonode +local tonut              = nuts.tonut + +local getfield           = nuts.getfield +local setfield           = nuts.setfield +local getnext            = nuts.getnext +local getprev            = nuts.getprev +local getid              = nuts.getid +local getattr            = nuts.getattr +local setattr            = nuts.setattr +local getfont            = nuts.getfont +local getchar            = nuts.getchar + +local insert_node_after  = nuts.insert_after +local remove_node        = nuts.remove +local copy_node_list     = nuts.copy_list +local traverse_id        = nuts.traverse_id  local tasks              = nodes.tasks -local nodepool           = nodes.pool +local nodepool           = nuts.pool  local new_penalty        = nodepool.penalty  local new_glue           = nodepool.glue @@ -63,48 +78,47 @@ local c_zero   = byte('0')  local c_period = byte('.')  local function inject_quad_space(unicode,head,current,fraction) -    local attr = current.attr +    local attr = getfield(current,"attr")      if fraction ~= 0 then -        fraction = fraction * fontquads[current.font] +        fraction = fraction * fontquads[getfont(current)]      end      local glue = new_glue(fraction) ---     glue.attr = copy_node_list(attr) -    glue.attr = attr -    current.attr = nil -    glue[a_character] = unicode +    setfield(glue,"attr",attr) +    setfield(current,"attr",nil) +    setattr(glue,a_character,unicode)      head, current = insert_node_after(head,current,glue)      return head, current  end  local function inject_char_space(unicode,head,current,parent) -    local attr = current.attr -    local font = current.font +    local attr = getfield(current,"attr") +    local font = getfont(current)      local char = fontcharacters[font][parent]      local glue = new_glue(char and char.width or fontparameters[font].space) -    glue.attr = current.attr -    current.attr = nil -    glue[a_character] = unicode +    setfield(glue,"attr",attr) +    setfield(current,"attr",nil) +    setattr(glue,a_character,unicode)      head, current = insert_node_after(head,current,glue)      return head, current  end  local function inject_nobreak_space(unicode,head,current,space,spacestretch,spaceshrink) -    local attr = current.attr +    local attr = getfield(current,"attr")      local glue = new_glue(space,spacestretch,spaceshrink)      local penalty = new_penalty(10000) -    glue.attr = attr -    current.attr = nil -    glue[a_character] = unicode +    setfield(glue,"attr",attr) +    setfield(current,"attr",nil) +    setattr(glue,a_character,unicode)      head, current = insert_node_after(head,current,penalty)      head, current = insert_node_after(head,current,glue)      return head, current  end  local function nbsp(head,current) -    local para = fontparameters[current.font] -    if current[a_alignstate] == 1 then -- flushright +    local para = fontparameters[getfont(current)] +    if getattr(current,a_alignstate) == 1 then -- flushright          head, current = inject_nobreak_space(0x00A0,head,current,para.space,0,0) -        current.subtype = space_skip_code +        setfield(current,"subtype",space_skip_code)      else          head, current = inject_nobreak_space(0x00A0,head,current,para.space,para.spacestretch,para.spaceshrink)      end @@ -121,7 +135,7 @@ end  function characters.replacenbspaces(head)      for current in traverse_id(glyph_code,head) do -        if current.char == 0x00A0 then +        if getchar(current) == 0x00A0 then              local h = nbsp(head,current)              if h then                  head = remove_node(h,current,true) @@ -147,21 +161,21 @@ local methods = {      -- don't have the 'local' value.      [0x00A0] = function(head,current) -- nbsp -        local next = current.next -        if next and next.id == glyph_code then -            local char = next.char +        local next = getnext(current) +        if next and getid(next) == glyph_code then +            local char = getchar(next)              if char == 0x200C or char == 0x200D then -- nzwj zwj -                next = next.next -				if next and nbsphash[next.char] then +                next = getnext(next) +				if next and nbsphash[getchar(next)] then                      return false                  end              elseif nbsphash[char] then                  return false              end          end -        local prev = current.prev -        if prev and prev.id == glyph_code and nbsphash[prev.char] then -            return false -- kannada +        local prev = getprev(current) +        if prev and getid(prev) == glyph_code and nbsphash[getchar(prev)] then +            return false          end          return nbsp(head,current)      end, @@ -215,11 +229,11 @@ local methods = {      end,      [0x202F] = function(head,current) -- narrownobreakspace -        return inject_nobreak_space(0x202F,head,current,fontquads[current.font]/8) +        return inject_nobreak_space(0x202F,head,current,fontquads[getfont(current)]/8)      end,      [0x205F] = function(head,current) -- math thinspace -        return inject_nobreak_space(0x205F,head,current,fontparameters[current.font].space/8) +        return inject_nobreak_space(0x205F,head,current,fontparameters[getfont(current)].space/8)      end,   -- [0xFEFF] = function(head,current) -- zerowidthnobreakspace @@ -228,14 +242,15 @@ local methods = {  } -function characters.handler(head) +function characters.handler(head) -- todo: use traverse_id +    head = tonut(head)      local current = head      local done = false      while current do -        local id = current.id +        local id = getid(current)          if id == glyph_code then -            local next = current.next -            local char = current.char +            local next = getnext(current) +            local char = getchar(current)              local method = methods[char]              if method then                  if trace_characters then @@ -249,8 +264,8 @@ function characters.handler(head)              end              current = next          else -            current = current.next +            current = getnext(current)          end      end -    return head, done +    return tonode(head), done  end diff --git a/tex/context/base/spac-ver.lua b/tex/context/base/spac-ver.lua index 0035c4119..960180dc2 100644 --- a/tex/context/base/spac-ver.lua +++ b/tex/context/base/spac-ver.lua @@ -37,7 +37,6 @@ local nodes, node, trackers, attributes, context, commands, tex =  nodes, node,  local texlists    = tex.lists  local texgetdimen = tex.getdimen  local texnest     = tex.nest -local texgetbox   = tex.getbox  local variables   = interfaces.variables @@ -63,23 +62,41 @@ local a_skiporder         = attributes.private('skiporder')  local a_snapmethod        = attributes.private('snapmethod')  local a_snapvbox          = attributes.private('snapvbox') -local find_node_tail      = node.tail -local free_node           = node.free -local free_node_list      = node.flush_list -local copy_node           = node.copy -local traverse_nodes      = node.traverse -local traverse_nodes_id   = node.traverse_id -local insert_node_before  = node.insert_before -local insert_node_after   = node.insert_after -local remove_node         = nodes.remove -local count_nodes         = nodes.count -local nodeidstostring     = nodes.idstostring -local hpack_node          = node.hpack -local vpack_node          = node.vpack -local writable_spec       = nodes.writable_spec +local nuts                = nodes.nuts +local tonode              = nuts.tonode +local tonut               = nuts.tonut +local ntostring           = nuts.tostring + +local getfield            = nuts.getfield +local setfield            = nuts.setfield +local getnext             = nuts.getnext +local getprev             = nuts.getprev +local getid               = nuts.getid +local getlist             = nuts.getlist +local getattr             = nuts.getattr +local setattr             = nuts.setattr +local getsubtype          = nuts.getsubtype +local getbox              = nuts.getbox + +local find_node_tail      = nuts.tail +local free_node           = nuts.free +local free_node_list      = nuts.flush_list +local copy_node           = nuts.copy +local traverse_nodes      = nuts.traverse +local traverse_nodes_id   = nuts.traverse_id +local insert_node_before  = nuts.insert_before +local insert_node_after   = nuts.insert_after +local remove_node         = nuts.remove +local count_nodes         = nuts.count +local hpack_node          = nuts.hpack +local vpack_node          = nuts.vpack +local writable_spec       = nuts.writable_spec +local nodereference       = nuts.reference +  local listtoutf           = nodes.listtoutf +local nodeidstostring     = nodes.idstostring -local nodepool            = nodes.pool +local nodepool            = nuts.pool  local new_penalty         = nodepool.penalty  local new_kern            = nodepool.kern @@ -179,28 +196,26 @@ end  -- local rule_id  = nodecodes.rule  -- local vlist_id = nodecodes.vlist  -- function nodes.makevtop(n) ---     if n.id == vlist_id then ---         local list = n.list ---         local height = (list and list.id <= rule_id and list.height) or 0 ---         n.depth = n.depth - height + n.height ---         n.height = height +--     if getid(n) == vlist_id then +--         local list = getlist(n) +--         local height = (list and getid(list) <= rule_id and getfield(list,"height")) or 0 +--         setfield(n,"depth",getfield(n,"depth") - height + getfield(n,"height") +--         setfield(n,"height",height  --     end  -- end -local reference = nodes.reference -  local function validvbox(parentid,list)      if parentid == hlist_code then -        local id = list.id +        local id = getid(list)          if id == whatsit_code then -- check for initial par subtype -            list = list.next +            list = getnext(list)              if not next then                  return nil              end          end          local done = nil          for n in traverse_nodes(list) do -            local id = n.id +            local id = getid(n)              if id == vlist_code or id == hlist_code then                  if done then                      return nil @@ -214,9 +229,9 @@ local function validvbox(parentid,list)              end          end          if done then -            local id = done.id +            local id = getid(done)              if id == hlist_code then -                return validvbox(id,done.list) +                return validvbox(id,getlist(done))              end          end          return done -- only one vbox @@ -226,19 +241,19 @@ end  local function already_done(parentid,list,a_snapmethod) -- todo: done when only boxes and all snapped      -- problem: any snapped vbox ends up in a line      if list and parentid == hlist_code then -        local id = list.id +        local id = getid(list)          if id == whatsit_code then -- check for initial par subtype -            list = list.next +            list = getnext(list)              if not next then                  return false              end          end  --~ local i = 0          for n in traverse_nodes(list) do -            local id = n.id ---~ i = i + 1 print(i,nodecodes[id],n[a_snapmethod]) +            local id = getid(n) +--~ i = i + 1 print(i,nodecodes[id],getattr(n,a_snapmethod))              if id == hlist_code or id == vlist_code then -                local a = n[a_snapmethod] +                local a = getattr(n,a_snapmethod)                  if not a then                   -- return true -- not snapped at all                  elseif a == 0 then @@ -276,11 +291,11 @@ end  -- check variables.none etc  local function snap_hlist(where,current,method,height,depth) -- method.strut is default -    local list = current.list +    local list = getlist(current)      local t = trace_vsnapping and { }      if t then          t[#t+1] = formatters["list content: %s"](listtoutf(list)) -        t[#t+1] = formatters["parent id: %s"](reference(current)) +        t[#t+1] = formatters["parent id: %s"](nodereference(current))          t[#t+1] = formatters["snap method: %s"](method.name)          t[#t+1] = formatters["specification: %s"](method.specification)      end @@ -312,7 +327,8 @@ local function snap_hlist(where,current,method,height,depth) -- method.strut is              t[#t+1] = formatters["auto: snapht %p snapdp %p"](snapht,snapdp)          end      end -    local h, d = height or current.height, depth or current.depth +    local h = height or getfield(current,"height") +    local d = depth or getfield(current,"depth")      local hr, dr, ch, cd = method.hfraction or 1, method.dfraction or 1, h, d      local tlines, blines = method.tlines or 1, method.blines or 1      local done, plusht, plusdp = false, snapht, snapdp @@ -339,22 +355,22 @@ local function snap_hlist(where,current,method,height,depth) -- method.strut is      if method.first then          local thebox = current -        local id = thebox.id +        local id = getid(thebox)          if id == hlist_code then -            thebox = validvbox(id,thebox.list) -            id = thebox and thebox.id +            thebox = validvbox(id,getlist(thebox)) +            id = thebox and getid(thebox)          end          if thebox and id == vlist_code then -            local list = thebox.list +            local list = getlist(thebox)              local lh, ld              for n in traverse_nodes_id(hlist_code,list) do -                lh = n.height -                ld = n.depth +                lh = getfield(n,"height") +                ld = getfield(n,"depth")                  break              end              if lh then -                local ht = thebox.height -                local dp = thebox.depth +                local ht = getfield(thebox,"height") +                local dp = getfield(thebox,"depth")                  if t then                      t[#t+1] = formatters["first line: height %p depth %p"](lh,ld)                      t[#t+1] = formatters["dimensions: height %p depth %p"](ht,dp) @@ -362,9 +378,9 @@ local function snap_hlist(where,current,method,height,depth) -- method.strut is                  local delta = h - lh                  ch, cd = lh, delta + d                  h, d = ch, cd -                local shifted = hpack_node(current.list) -                shifted.shift = delta -                current.list = shifted +                local shifted = hpack_node(getlist(current)) +                setfield(shifted,"shift",delta) +                setfield(current,"list",shifted)                  done = true                  if t then                      t[#t+1] = formatters["first: height %p depth %p shift %p"](ch,cd,delta) @@ -377,20 +393,21 @@ local function snap_hlist(where,current,method,height,depth) -- method.strut is          end      elseif method.last then          local thebox = current -        local id = thebox.id +        local id = getid(thebox)          if id == hlist_code then -            thebox = validvbox(id,thebox.list) -            id = thebox and thebox.id +            thebox = validvbox(id,getlist(thebox)) +            id = thebox and getid(thebox)          end          if thebox and id == vlist_code then -            local list, lh, ld = thebox.list +            local list = getlist(thebox) +            local lh, ld              for n in traverse_nodes_id(hlist_code,list) do -                lh = n.height -                ld = n.depth +                lh = getfield(n,"height") +                ld = getfield(n,"depth")              end              if lh then -                local ht = thebox.height -                local dp = thebox.depth +                local ht = getfield(thebox,"height") +                local dp = getfield(thebox,"depth")                  if t then                      t[#t+1] = formatters["last line: height %p depth %p" ](lh,ld)                      t[#t+1] = formatters["dimensions: height %p depth %p"](ht,dp) @@ -398,9 +415,9 @@ local function snap_hlist(where,current,method,height,depth) -- method.strut is                  local delta = d - ld                  cd, ch = ld, delta + h                  h, d = ch, cd -                local shifted = hpack_node(current.list) -                shifted.shift = delta -                current.list = shifted +                local shifted = hpack_node(getlist(current)) +                setfield(shifted,"shift",delta) +                setfield(current,"list",shifted)                  done = true                  if t then                      t[#t+1] = formatters["last: height %p depth %p shift %p"](ch,cd,delta) @@ -461,25 +478,25 @@ local function snap_hlist(where,current,method,height,depth) -- method.strut is      if offset then          -- we need to set the attr          if t then -            t[#t+1] = formatters["before offset: %p (width %p height %p depth %p)"](offset,current.width,current.height,current.depth) +            t[#t+1] = formatters["before offset: %p (width %p height %p depth %p)"](offset,getfield(current,"width"),getfield(current,"height"),getfield(current,"depth"))          end -        local shifted = hpack_node(current.list) -        shifted.shift = offset -        current.list = shifted +        local shifted = hpack_node(getlist(current)) +        setfield(shifted,"shift",offset) +        setfield(current,"list",shifted)          if t then -            t[#t+1] = formatters["after offset: %p (width %p height %p depth %p)"](offset,current.width,current.height,current.depth) +            t[#t+1] = formatters["after offset: %p (width %p height %p depth %p)"](offset,getfield(current,"width"),getfield(current,"height"),getfield(current,"depth"))          end -        shifted[a_snapmethod] = 0 -        current[a_snapmethod] = 0 +        setattr(shifted,a_snapmethod,0) +        setattr(current,a_snapmethod,0)      end      if not height then -        current.height = ch +        setfield(current,"height",ch)          if t then              t[#t+1] = formatters["forced height: %p"](ch)          end      end      if not depth then -        current.depth = cd +        setfield(current,"depth",cd)          if t then              t[#t+1] = formatters["forced depth: %p"](cd)          end @@ -493,17 +510,17 @@ local function snap_hlist(where,current,method,height,depth) -- method.strut is          t[#t+1] = formatters["final depth: %p -> %p"](d,cd)      end      if t then -        report_snapper("trace: %s type %s\n\t%\n\tt",where,nodecodes[current.id],t) +        report_snapper("trace: %s type %s\n\t%\n\tt",where,nodecodes[getid(current)],t)      end      return h, d, ch, cd, lines  end  local function snap_topskip(current,method) -    local spec = current.spec -    local w = spec.width +    local spec = getfield(current,"spec") +    local w = getfield(spec,"width")      local wd = w -    if spec.writable then -        spec.width = 0 +    if getfield(spec,"writable") then +        setfield(spec,"width",0)          wd = 0      end      return w, wd @@ -664,18 +681,18 @@ local trace_list, tracing_info, before, after = { }, false, "", ""  local function nodes_to_string(head)      local current, t = head, { }      while current do -        local id = current.id +        local id = getid(current)          local ty = nodecodes[id]          if id == penalty_code then -            t[#t+1] = formatters["%s:%s"](ty,current.penalty) +            t[#t+1] = formatters["%s:%s"](ty,getfield(current,"penalty"))          elseif id == glue_code then -- or id == kern_code then -- to be tested              t[#t+1] = formatters["%s:%p"](ty,current)          elseif id == kern_code then -            t[#t+1] = formatters["%s:%p"](ty,current.kern) +            t[#t+1] = formatters["%s:%p"](ty,getfield(current,"kern"))          else              t[#t+1] = ty          end -        current = current.next +        current = getnext(current)      end      return concat(t," + ")  end @@ -699,7 +716,7 @@ local function trace_info(message, where, what)  end  local function trace_node(what) -    local nt = nodecodes[what.id] +    local nt = nodecodes[getid(what)]      local tl = trace_list[#trace_list]      if tl and tl[1] == "node" then          trace_list[#trace_list] = { "node", formatters["%s + %s"](tl[2],nt) } @@ -709,8 +726,8 @@ local function trace_node(what)  end  local function trace_done(str,data) -    if data.id == penalty_code then -        trace_list[#trace_list+1] = { "penalty", formatters["%s | %s"](str,data.penalty) } +    if getid(data) == penalty_code then +        trace_list[#trace_list+1] = { "penalty", formatters["%s | %s"](str,getfield(data,"penalty")) }      else          trace_list[#trace_list+1] = { "glue", formatters["%s | %p"](str,data) }      end @@ -753,17 +770,17 @@ local free_glue_node = free_node  function vspacing.snapbox(n,how)      local sv = snapmethods[how]      if sv then -        local box = texgetbox(n) -        local list = box.list +        local box = getbox(n) +        local list = getlist(box)          if list then -            local s = list[a_snapmethod] +            local s = getattr(list,a_snapmethod)              if s == 0 then                  if trace_vsnapping then                  --  report_snapper("box list not snapped, already done")                  end              else -                local ht = box.height -                local dp = box.depth +                local ht = getfield(box,"height") +                local dp = getfield(box,"depth")                  if false then -- todo: already_done                      -- assume that the box is already snapped                      if trace_vsnapping then @@ -772,14 +789,14 @@ function vspacing.snapbox(n,how)                      end                  else                      local h, d, ch, cd, lines = snap_hlist("box",box,sv,ht,dp) -                    box.height= ch -                    box.depth = cd +                    setfield(box,"height",ch) +                    setfield(box,"depth",cd)                      if trace_vsnapping then                          report_snapper("box list snapped from (%p,%p) to (%p,%p) using method %a (%s) for %a (%s lines): %s",                              h,d,ch,cd,sv.name,sv.specification,"direct",lines,listtoutf(list))                      end -                    box[a_snapmethod] = 0 -- -                    list[a_snapmethod] = 0 -- yes or no +                    setattr(box,a_snapmethod,0)  -- +                    setattr(list,a_snapmethod,0) -- yes or no                  end              end          end @@ -801,8 +818,10 @@ local w, h, d = 0, 0, 0  ----- w, h, d = 100*65536, 65536, 65536  local function forced_skip(head,current,width,where,trace) -    if head == current and head.subtype == baselineskip_code then -        width = width - head.spec.width +    if head == current then +        if getsubtype(head) == baselineskip_code then +            width = width - getfield(getfield(head,"spec"),"width") +        end      end      if width == 0 then          -- do nothing @@ -834,25 +853,25 @@ local special_penalty_max = 35000  local function specialpenalty(start,penalty)   -- nodes.showsimplelist(texlists.page_head,1) -    local current = find_node_tail(texlists.page_head) +    local current = find_node_tail(tonut(texlists.page_head)) -- no texlists.page_tail yet      while current do -        local id = current.id +        local id = getid(current)          if id == glue_code then -            current = current.prev +            current = getprev(current)          elseif id == penalty_code then -            local p = current.penalty +            local p = getfield(current,"penalty")              if p == penalty then                  if trace_vspacing then                      report_vspacing("overloading penalty %a",p)                  end                  return current              elseif p >= 10000 then -                current = current.prev +                current = getprev(current)              else                  break              end          else -            current = current.prev +            current = getprev(current)          end      end  end @@ -875,12 +894,12 @@ local function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also              head = insert_node_before(head,current,p)          end          if glue_data then -            local spec = glue_data.spec +            local spec = getfield(glue_data,"spec")              if force_glue then                  if trace then trace_done("flushed due to " .. why,glue_data) end -                head = forced_skip(head,current,spec.width,"before",trace) +                head = forced_skip(head,current,getfield(spec,"width"),"before",trace)                  free_glue_node(glue_data) -            elseif spec.writable then +            elseif getfield(spec,"writable") then                  if trace then trace_done("flushed due to " .. why,glue_data) end                  head = insert_node_before(head,current,glue_data)              else @@ -900,12 +919,12 @@ local function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also      end      if trace then trace_info("start analyzing",where,what) end      while current do -        local id = current.id +        local id = getid(current)          if id == hlist_code or id == vlist_code then              -- needs checking, why so many calls              if snap then -                local list = current.list -                local s = current[a_snapmethod] +                local list = getlist(current) +                local s = getattr(current,a_snapmethod)                  if not s then                  --  if trace_vsnapping then                  --      report_snapper("mvl list not snapped") @@ -919,8 +938,8 @@ local function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also                      if sv then                          -- check if already snapped                          if list and already_done(id,list,a_snapmethod) then -                            local ht = current.height -                            local dp = current.depth +                            local ht = getfield(current,"height") +                            local dp = getfield(current,"depth")                              -- assume that the box is already snapped                              if trace_vsnapping then                                  report_snapper("mvl list already snapped at (%p,%p): %s",ht,dp,listtoutf(list)) @@ -935,37 +954,37 @@ local function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also                      elseif trace_vsnapping then                          report_snapper("mvl %a not snapped due to unknown snap specification: %s",nodecodes[id],listtoutf(list))                      end -                    current[a_snapmethod] = 0 +                    setattr(current,a_snapmethod,0)                  end              else                  --              end          --  tex.prevdepth = 0              flush("list") -            current = current.next +            current = getnext(current)          elseif id == penalty_code then -         -- natural_penalty = current.penalty +         -- natural_penalty = getfield(current,"penalty")           -- if trace then trace_done("removed penalty",current) end           -- head, current = remove_node(head, current, true) -            current = current.next +            current = getnext(current)          elseif id == kern_code then -            if snap and trace_vsnapping and current.kern ~= 0 then -                report_snapper("kern of %p kept",current.kern) +            if snap and trace_vsnapping and getfield(current,"kern") ~= 0 then +                report_snapper("kern of %p kept",getfield(current,"kern"))              end              flush("kern") -            current = current.next +            current = getnext(current)          elseif id == glue_code then -            local subtype = current.subtype +            local subtype = getsubtype(current)              if subtype == userskip_code then -                local sc = current[a_skipcategory]   -- has no default, no unset (yet) -                local so = current[a_skiporder] or 1 -- has  1 default, no unset (yet) -                local sp = current[a_skippenalty]    -- has no default, no unset (yet) +                local sc = getattr(current,a_skipcategory)   -- has no default, no unset (yet) +                local so = getattr(current,a_skiporder) or 1 -- has  1 default, no unset (yet) +                local sp = getattr(current,a_skippenalty)    -- has no default, no unset (yet)                  if sp and sc == penalty then  if where == "page" and sp >= special_penalty_min and sp <= special_penalty_max then      local previousspecial = specialpenalty(current,sp)      if previousspecial then -        previousspecial.penalty = 0 +        setfield(previousspecial,"penalty",0)          sp = 0      end  end @@ -983,37 +1002,37 @@ end                          if trace then trace_done("flush",glue_data) end                          head = insert_node_before(head,current,glue_data)                          if trace then trace_natural("natural",current) end -                        current = current.next +                        current = getnext(current)                      else                          -- not look back across head  -- todo: prev can be whatsit (latelua) -                        local previous = current.prev -                        if previous and previous.id == glue_code and previous.subtype == userskip_code then -                            local ps = previous.spec -                            if ps.writable then -                                local cs = current.spec -                                if cs.writable and ps.stretch_order == 0 and ps.shrink_order == 0 and cs.stretch_order == 0 and cs.shrink_order == 0 then -                                    local pw, pp, pm = ps.width, ps.stretch, ps.shrink -                                    local cw, cp, cm = cs.width, cs.stretch, cs.shrink +                        local previous = getprev(current) +                        if previous and getid(previous) == glue_code and getsubtype(previous) == userskip_code then +                            local ps = getfield(previous,"spec") +                            if getfield(ps,"writable") then +                                local cs = getfield(current,"spec") +                                if getfield(cs,"writable") and getfield(ps,"stretch_order") == 0 and getfield(ps,"shrink_order") == 0 and getfield(cs,"stretch_order") == 0 and getfield(cs,"shrink_order") == 0 then +                                    local pw, pp, pm = getfield(ps,"width"), getfield(ps,"stretch"), getfield(ps,"shrink") +                                    local cw, cp, cm = getfield(cs,"width"), getfield(cs,"stretch"), getfield(cs,"shrink")                                   -- ps = writable_spec(previous) -- no writable needed here                                   -- ps.width, ps.stretch, ps.shrink = pw + cw, pp + cp, pm + cm -                                    previous.spec = new_gluespec(pw + cw, pp + cp, pm + cm) -- else topskip can disappear +                                    setfield(previous,"spec",new_gluespec(pw + cw, pp + cp, pm + cm)) -- else topskip can disappear                                      if trace then trace_natural("removed",current) end                                      head, current = remove_node(head, current, true)                                   -- current = previous                                      if trace then trace_natural("collapsed",previous) end -                                 -- current = current.next +                                 -- current = getnext(current)                                  else                                      if trace then trace_natural("filler",current) end -                                    current = current.next +                                    current = getnext(current)                                  end                              else                                  if trace then trace_natural("natural (no prev spec)",current) end -                                current = current.next +                                current = getnext(current)                              end                          else                              if trace then trace_natural("natural (no prev)",current) end -                            current = current.next +                            current = getnext(current)                          end                      end                      glue_order, glue_data = 0, nil @@ -1046,12 +1065,12 @@ end                  elseif glue_order == so then                      -- is now exclusive, maybe support goback as combi, else why a set                      if sc == largest then -                        local cs, gs = current.spec, glue_data.spec -                        local cw, gw = cs.width, gs.width +                        local cs, gs = getfield(current,"spec"), getfield(glue_data,"spec") +                        local cw, gw = getfield(cs,"width"), getfield(gs,"width")                          if cw > gw then                              if trace then trace_skip("largest",sc,so,sp,current) end                              free_glue_node(glue_data) -- also free spec -                            head, current, glue_data = remove_node(head, current) +                            head, current, glue_data = remove_node(head,current)                          else                              if trace then trace_skip("remove smallest",sc,so,sp,current) end                              head, current = remove_node(head, current, true) @@ -1059,7 +1078,7 @@ end                      elseif sc == goback then                          if trace then trace_skip("goback",sc,so,sp,current) end                          free_glue_node(glue_data) -- also free spec -                        head, current, glue_data = remove_node(head, current) +                        head, current, glue_data = remove_node(head,current)                      elseif sc == force then                          -- last one counts, some day we can provide an accumulator and largest etc                          -- but not now @@ -1073,11 +1092,11 @@ end                          head, current = remove_node(head, current, true)                      elseif sc == add then                          if trace then trace_skip("add",sc,so,sp,current) end -                     -- local old, new = glue_data.spec, current.spec -                        local old, new = writable_spec(glue_data), current.spec -                        old.width   = old.width   + new.width -                        old.stretch = old.stretch + new.stretch -                        old.shrink  = old.shrink  + new.shrink +                     -- local old, new = glue_data.spec, getfield(current,"spec") +                        local old, new = writable_spec(glue_data), getfield(current,"spec") +                        setfield(old,"width",getfield(old,"width") + getfield(new,"width")) +                        setfield(old,"stretch",getfield(old,"stretch") + getfield(new,"stretch")) +                        setfield(old,"shrink",getfield(old,"shrink") + getfield(new,"shrink"))                          -- toto: order                          head, current = remove_node(head, current, true)                      else @@ -1093,12 +1112,13 @@ end                  end              elseif subtype == lineskip_code then                  if snap then -                    local s = current[a_snapmethod] +                    local s = getattr(current,a_snapmethod)                      if s and s ~= 0 then -                        current[a_snapmethod] = 0 -                        if current.spec.writable then +                        setattr(current,a_snapmethod,0) +                        local spec = getfield(current,"spec") +                        if getfield(spec,"writable") then                              local spec = writable_spec(current) -                            spec.width = 0 +                            setfield(spec,"width",0)                              if trace_vsnapping then                                  report_snapper("lineskip set to zero")                              end @@ -1111,15 +1131,16 @@ end                      if trace then trace_skip("lineskip",sc,so,sp,current) end                      flush("lineskip")                  end -                current = current.next +                current = getnext(current)              elseif subtype == baselineskip_code then                  if snap then -                    local s = current[a_snapmethod] +                    local s = getattr(current,a_snapmethod)                      if s and s ~= 0 then -                        current[a_snapmethod] = 0 -                        if current.spec.writable then +                        setattr(current,a_snapmethod,0) +                        local spec = getfield(current,"spec") +                        if getfield(spec,"writable") then                              local spec = writable_spec(current) -                            spec.width = 0 +                            setfield(spec,"width",0)                              if trace_vsnapping then                                  report_snapper("baselineskip set to zero")                              end @@ -1132,17 +1153,17 @@ end                      if trace then trace_skip("baselineskip",sc,so,sp,current) end                      flush("baselineskip")                  end -                current = current.next +                current = getnext(current)              elseif subtype == parskip_code then                  -- parskip always comes later                  if ignore_whitespace then                      if trace then trace_natural("ignored parskip",current) end                      head, current = remove_node(head, current, true)                  elseif glue_data then -                    local ps = current.spec -                    local gs = glue_data.spec -                    if ps.writable and gs.writable and ps.width > gs.width then -                        glue_data.spec = copy_node(ps) +                    local ps = getfield(current,"spec") +                    local gs = getfield(glue_data,"spec") +                    if getfield(ps,"writable") and getfield(gs,"writable") and getfield(ps,"width") > getfield(gs,"width") then +                        setfield(glue_data,"spec",copy_node(ps))                          if trace then trace_natural("taking parskip",current) end                      else                          if trace then trace_natural("removed parskip",current) end @@ -1154,9 +1175,9 @@ end                  end              elseif subtype == topskip_code or subtype == splittopskip_code then                  if snap then -                    local s = current[a_snapmethod] +                    local s = getattr(current,a_snapmethod)                      if s and s ~= 0 then -                        current[a_snapmethod] = 0 +                        setattr(current,a_snapmethod,0)                          local sv = snapmethods[s]                          local w, cw = snap_topskip(current,sv)                          if trace_vsnapping then @@ -1170,46 +1191,46 @@ end                      if trace then trace_skip("topskip",sc,so,sp,current) end                      flush("topskip")                  end -                current = current.next +                current = getnext(current)              elseif subtype == abovedisplayskip_code then                  --                  if trace then trace_skip("above display skip (normal)",sc,so,sp,current) end                  flush("above display skip (normal)") -                current = current.next +                current = getnext(current)                  --              elseif subtype == belowdisplayskip_code then                  --                  if trace then trace_skip("below display skip (normal)",sc,so,sp,current) end                  flush("below display skip (normal)") -                current = current.next -                -- +                current = getnext(current) +               --              elseif subtype == abovedisplayshortskip_code then                  --                  if trace then trace_skip("above display skip (short)",sc,so,sp,current) end                  flush("above display skip (short)") -                current = current.next +                current = getnext(current)                  --              elseif subtype == belowdisplayshortskip_code then                  --                  if trace then trace_skip("below display skip (short)",sc,so,sp,current) end                  flush("below display skip (short)") -                current = current.next +                current = getnext(current)                  --              else -- other glue                  if snap and trace_vsnapping then -                    local spec = current.spec -                    if spec.writable and spec.width ~= 0 then -                        report_snapper("glue %p of type %a kept",current.spec.width,skipcodes[subtype]) -                     -- spec.width = 0 +                    local spec = getfield(current,"spec") +                    if getfield(spec,"writable") and getfield(spec,"width") ~= 0 then +                        report_snapper("glue %p of type %a kept",getfield(spec,"width"),skipcodes[subtype]) +                     -- setfield(spec,"width",0)                      end                  end -                if trace then trace_skip(formatter["glue of type %a"](subtype),sc,so,sp,current) end +                if trace then trace_skip(formatters["glue of type %a"](subtype),sc,so,sp,current) end                  flush("some glue") -                current = current.next +                current = getnext(current)              end          else -            flush("something else") -            current = current.next +            flush(formatters["node with id %a"](id)) +            current = getnext(current)          end      end      if trace then trace_info("stop analyzing",where,what) end @@ -1230,7 +1251,8 @@ end          if not tail then tail = find_node_tail(head) end          if trace then trace_done("result",glue_data) end          if force_glue then -            head, tail = forced_skip(head,tail,glue_data.spec.width,"after",trace) +            local spec = getfield(glue_data,"spec") +            head, tail = forced_skip(head,tail,getfield(spec,"width"),"after",trace)              free_glue_node(glue_data)          else              head, tail = insert_node_after(head,tail,glue_data) @@ -1243,7 +1265,7 @@ texnest[texnest.ptr].prevdepth = 0 -- appending to the list bypasses tex's prevd          end          show_tracing(head)          if oldhead ~= head then -            trace_info("head has been changed from %a to %a",nodecodes[oldhead.id],nodecodes[head.id]) +            trace_info("head has been changed from %a to %a",nodecodes[getid(oldhead)],nodecodes[getid(head)])          end      end      return head, true @@ -1271,16 +1293,17 @@ end  function vspacing.pagehandler(newhead,where)      -- local newhead = texlists.contrib_head      if newhead then +        newhead = tonut(newhead)          local newtail = find_node_tail(newhead) -- best pass that tail, known anyway          local flush = false          stackhack = true -- todo: only when grid snapping once enabled          -- todo: fast check if head = tail          for n in traverse_nodes(newhead) do -- we could just look for glue nodes -            local id = n.id +            local id = getid(n)              if id ~= glue_code then                  flush = true -            elseif n.subtype == userskip_code then -                if n[a_skipcategory] then +            elseif getsubtype(n) == userskip_code then +                if getattr(n,a_skipcategory) then                      stackhack = true                  else                      flush = true @@ -1292,35 +1315,36 @@ function vspacing.pagehandler(newhead,where)          if flush then              if stackhead then                  if trace_collect_vspacing then report("appending %s nodes to stack (final): %s",newhead) end -                stacktail.next = newhead -                newhead.prev = stacktail +                setfield(stacktail,"next",newhead) +                setfield(newhead,"prev",stacktail)                  newhead = stackhead                  stackhead, stacktail = nil, nil              end              if stackhack then                  stackhack = false                  if trace_collect_vspacing then report("processing %s nodes: %s",newhead) end -                 -- texlists.contrib_head = collapser(newhead,"page",where,trace_page_vspacing,true,a_snapmethod) -                    newhead = collapser(newhead,"page",where,trace_page_vspacing,true,a_snapmethod) +             -- texlists.contrib_head = collapser(newhead,"page",where,trace_page_vspacing,true,a_snapmethod) +                newhead = collapser(newhead,"page",where,trace_page_vspacing,true,a_snapmethod)              else                  if trace_collect_vspacing then report("flushing %s nodes: %s",newhead) end               -- texlists.contrib_head = newhead              end +            return tonode(newhead)          else              if stackhead then                  if trace_collect_vspacing then report("appending %s nodes to stack (intermediate): %s",newhead) end -                stacktail.next = newhead -                newhead.prev = stacktail +                setfield(stacktail,"next",newhead) +                setfield(newhead,"prev",stacktail)              else                  if trace_collect_vspacing then report("storing %s nodes in stack (initial): %s",newhead) end                  stackhead = newhead              end              stacktail = newtail           -- texlists.contrib_head = nil -            newhead = nil +         -- newhead = nil          end      end -    return newhead +    return nil  end  local ignore = table.tohash { @@ -1330,18 +1354,23 @@ local ignore = table.tohash {  }  function vspacing.vboxhandler(head,where) -    if head and not ignore[where] and head.next then -        head = collapser(head,"vbox",where,trace_vbox_vspacing,true,a_snapvbox) -- todo: local snapper +    if head and not ignore[where] then +        local h = tonut(head) +        if getnext(h) then +            h = collapser(h,"vbox",where,trace_vbox_vspacing,true,a_snapvbox) -- todo: local snapper +            return tonode(h) +        end      end      return head  end  function vspacing.collapsevbox(n) -- for boxes but using global a_snapmethod -    local box = texgetbox(n) +    local box = getbox(n)      if box then -        local list = box.list +        local list = getlist(box)          if list then -            box.list = vpack_node(collapser(list,"snapper","vbox",trace_vbox_vspacing,true,a_snapmethod)) +            list = collapser(list,"snapper","vbox",trace_vbox_vspacing,true,a_snapmethod) +            setfield(box,"list",vpack_node(list))          end      end  end @@ -1352,7 +1381,9 @@ end  local outer = texnest[0]  function vspacing.resetprevdepth() -    outer.prevdepth = 0 +    if texlists.hold_head then +        outer.prevdepth = 0 +    end  end  -- interface diff --git a/tex/context/base/status-files.pdf b/tex/context/base/status-files.pdf Binary files differindex 7782770b4..357d89ed7 100644 --- a/tex/context/base/status-files.pdf +++ b/tex/context/base/status-files.pdf diff --git a/tex/context/base/status-lua.pdf b/tex/context/base/status-lua.pdf Binary files differindex e1fb8dcc7..3d1e83812 100644 --- a/tex/context/base/status-lua.pdf +++ b/tex/context/base/status-lua.pdf diff --git a/tex/context/base/strc-mar.lua b/tex/context/base/strc-mar.lua index b3a6e8f35..258787d0a 100644 --- a/tex/context/base/strc-mar.lua +++ b/tex/context/base/strc-mar.lua @@ -19,14 +19,27 @@ local commands           = commands  local allocate           = utilities.storage.allocate  local setmetatableindex  = table.setmetatableindex -local traversenodes      = nodes.traverse +local nuts               = nodes.nuts +local tonut              = nuts.tonut + +local getfield           = nuts.getfield +local setfield           = nuts.setfield +local getnext            = nuts.getnext +local getprev            = nuts.getprev +local getid              = nuts.getid +local getlist            = nuts.getlist +local getattr            = nuts.getattr +local setattr            = nuts.setattr +local getbox             = nuts.getbox + +local traversenodes      = nuts.traverse +  local nodecodes          = nodes.nodecodes  local glyph_code         = nodecodes.glyph  local hlist_code         = nodecodes.hlist  local vlist_code         = nodecodes.vlist  local texsetattribute    = tex.setattribute -local texgetbox          = tex.getbox  local a_marks            = attributes.private("structure","marks") @@ -106,9 +119,9 @@ end  local function sweep(head,first,last)      for n in traversenodes(head) do -        local id = n.id +        local id = getid(n)          if id == glyph_code then -            local a = n[a_marks] +            local a = getattr(n,a_marks)              if not a then                  -- next              elseif first == 0 then @@ -118,7 +131,7 @@ local function sweep(head,first,last)              end          elseif id == hlist_code or id == vlist_code then              if boxes_too then -                local a = n[a_marks] +                local a = getattr(n,a_marks)                  if not a then                      -- next                  elseif first == 0 then @@ -127,7 +140,7 @@ local function sweep(head,first,last)                      last = a                  end              end -            local list = n.list +            local list = getlist(n)              if list then                  first, last = sweep(list,first,last)              end @@ -143,9 +156,9 @@ setmetatableindex(classes, function(t,k) local s = settings_to_array(k) t[k] = s  local lasts = { }  function marks.synchronize(class,n,option) -    local box = texgetbox(n) +    local box = getbox(n)      if box then -        local first, last = sweep(box.list,0,0) +        local first, last = sweep(getlist(box),0,0)          if option == v_keep and first == 0 and last == 0 then              if trace_marks_get or trace_marks_set then                  report_marks("action %a, class %a, box %a","retain at synchronize",class,n) diff --git a/tex/context/base/supp-box.lua b/tex/context/base/supp-box.lua index 27078f46f..3c5a3383d 100644 --- a/tex/context/base/supp-box.lua +++ b/tex/context/base/supp-box.lua @@ -26,101 +26,118 @@ local glue_code    = nodecodes.glue  local kern_code    = nodecodes.kern  local glyph_code   = nodecodes.glyph -local new_penalty  = nodes.pool.penalty -local new_hlist    = nodes.pool.hlist -local new_glue     = nodes.pool.glue +local nuts         = nodes.nuts +local tonut        = nuts.tonut +local tonode       = nuts.tonode -local free_node    = nodes.free -local copy_list    = nodes.copy_list -local copy_node    = nodes.copy -local find_tail    = nodes.tail +local getfield     = nuts.getfield +local getnext      = nuts.getnext +local getprev      = nuts.getprev +local getid        = nuts.getid +local getlist      = nuts.getlist +local getattribute = nuts.getattribute +local getbox       = nuts.getbox + +local setfield     = nuts.setfield +local setbox       = nuts.setbox + +local free_node    = nuts.free +local copy_list    = nuts.copy_list +local copy_node    = nuts.copy +local find_tail    = nuts.tail + +local listtoutf    = nodes.listtoutf + +local nodepool     = nuts.pool +local new_penalty  = nodepool.penalty +local new_hlist    = nodepool.hlist +local new_glue     = nodepool.glue -local texsetbox    = tex.setbox -local texgetbox    = tex.getbox  local texget       = tex.get -local function hyphenatedlist(list) -    while list do -        local id, next, prev = list.id, list.next, list.prev +local function hyphenatedlist(head) +    local current = head and tonut(head) +    while current do +        local id   = getid(current) +        local next = getnext(current) +        local prev = getprev(current)          if id == disc_code then -            local hyphen = list.pre +            local hyphen = getfield(current,"pre")              if hyphen then                  local penalty = new_penalty(-500) -                hyphen.next, penalty.prev = penalty, hyphen -                prev.next, next.prev = hyphen, penalty -                penalty.next, hyphen.prev = next, prev -                list.pre = nil -                free_node(list) +                -- insert_after etc +                setfield(hyphen,"next",penalty) +                setfield(penalty,"prev",hyphen) +                setfield(prev,"next",hyphen) +                setfield(next,"prev", penalty) +                setfield(penalty,"next",next) +                setfield(hyphen,"prev",prev) +                setfield(current,"pre",nil) +                free_node(current)              end          elseif id == vlist_code or id == hlist_code then -            hyphenatedlist(list.list) +            hyphenatedlist(getlist(current))          end -        list = next +        current = next      end  end  commands.hyphenatedlist = hyphenatedlist  function commands.showhyphenatedinlist(list) -    report_hyphenation("show: %s",nodes.listtoutf(list,false,true)) +    report_hyphenation("show: %s",listtoutf(tonut(list),false,true))  end  local function checkedlist(list)      if type(list) == "number" then -        return texgetbox(list).list +        return getlist(getbox(tonut(list)))      else -        return list +        return tonut(list)      end  end -local function applytochars(list,what,nested) -    local doaction = context[what or "ruledhbox"] -    local noaction = context -    local current  = checkedlist(list) +local function applytochars(current,doaction,noaction,nested)      while current do -        local id = current.id +        local id = getid(current)          if nested and (id == hlist_code or id == vlist_code) then              context.beginhbox() -            applytochars(current.list,what,nested) +            applytochars(getlist(current),what,nested)              context.endhbox()          elseif id ~= glyph_code then -            noaction(copy_node(current)) +            noaction(tonode(copy_node(current)))          else -            doaction(copy_node(current)) +            doaction(tonode(copy_node(current)))          end -        current = current.next +        current = getnext(current)      end  end -local function applytowords(list,what,nested) -    local doaction = context[what or "ruledhbox"] -    local noaction = context -    local current  = checkedlist(list) +local function applytowords(current,doaction,noaction,nested)      local start      while current do -        local id = current.id +        local id = getid(current)          if id == glue_code then              if start then -                doaction(copy_list(start,current)) +                doaction(tonode(copy_list(start,current)))                  start = nil              end -            noaction(copy_node(current)) +            noaction(tonode(copy_node(current)))          elseif nested and (id == hlist_code or id == vlist_code) then              context.beginhbox() -            applytowords(current.list,what,nested) +            applytowords(getlist(current),what,nested)              context.egroup()          elseif not start then              start = current          end -        current = current.next +        current = getnext(current)      end      if start then -        doaction(copy_list(start)) +        doaction(tonode(copy_list(start)))      end  end -commands.applytochars = applytochars -commands.applytowords = applytowords +commands.applytochars = function(list,what,nested) applytochars(checkedlist(list),context[what or "ruledhbox"],context,nested) end +commands.applytowords = function(list,what,nested) applytowords(checkedlist(list),context[what or "ruledhbox"],context,nested) end  local split_char = lpeg.Ct(lpeg.C(1)^0)  local split_word = lpeg.tsplitat(lpeg.patterns.space) @@ -176,36 +193,36 @@ end  local a_vboxtohboxseparator = attributes.private("vboxtohboxseparator")  function commands.vboxlisttohbox(original,target,inbetween) -    local current = texgetbox(original).list +    local current = getlist(getbox(original))      local head = nil      local tail = nil      while current do -        local id   = current.id -        local next = current.next +        local id   = getid(current) +        local next = getnext(current)          if id == hlist_code then -            local list = current.list +            local list = getlist(current)              if head then                  if inbetween > 0 then                      local n = new_glue(0,0,inbetween) -                    tail.next = n -                    n.prev = tail +                    setfield(tail,"next",n) +                    setfield(n,"prev",tail)                      tail = n                  end -                tail.next = list -                list.prev = tail +                setfield(tail,"next",list) +                setfield(list,"prev",tail)              else                  head = list              end              tail = find_tail(list)              -- remove last separator -            if tail.id == hlist_code and tail[a_vboxtohboxseparator] == 1 then +            if getid(tail) == hlist_code and getattribute(tail,a_vboxtohboxseparator) == 1 then                  local temp = tail -                local prev = tail.prev +                local prev = getprev(tail)                  if next then -                    local list = tail.list -                    prev.next = list -                    list.prev = prev -                    tail.list = nil +                    local list = getlist(tail) +                    setfield(prev,"next",list) +                    setfield(list,"prev",prev) +                    setfield(tail,"list",nil)                      tail = find_tail(list)                  else                      tail = prev @@ -213,21 +230,21 @@ function commands.vboxlisttohbox(original,target,inbetween)                  free_node(temp)              end              -- done -            tail.next = nil -            current.list = nil +            setfield(tail,"next",nil) +            setfield(current,"list",nil)          end          current = next      end      local result = new_hlist() -    result.list = head -    texsetbox(target,result) +    setfield(result,"list",head) +    setbox(target,result)  end  function commands.hboxtovbox(original) -    local b = texgetbox(original) +    local b = getbox(original)      local factor = texget("baselineskip").width / texget("hsize") -    b.depth = 0 -    b.height = b.width * factor +    setfield(b,"depth",0) +    setfield(b,"height",getfield(b,"width") * factor)  end  function commands.boxtostring(n) diff --git a/tex/context/base/tabl-xtb.lua b/tex/context/base/tabl-xtb.lua index 488ef5b78..653eb6e08 100644 --- a/tex/context/base/tabl-xtb.lua +++ b/tex/context/base/tabl-xtb.lua @@ -25,18 +25,21 @@ this mechamism will be improved so that it can replace its older cousin.  -- todo: use linked list instead of r/c array -local commands, context, tex, node = commands, context, tex, node +local tonumber = tonumber -local texgetcount = tex.getcount -local texsetcount = tex.setcount -local texgetbox   = tex.getbox -local texgetdimen = tex.getdimen -local texsetdimen = tex.setdimen -local texget      = tex.get +local commands                = commands +local context                 = context +local tex                     = tex + +local texgetcount             = tex.getcount +local texsetcount             = tex.setcount +local texgetdimen             = tex.getdimen +local texsetdimen             = tex.setdimen +local texget                  = tex.get -local format      = string.format -local concat      = table.concat -local points      = number.points +local format                  = string.format +local concat                  = table.concat +local points                  = number.points  local context                 = context  local context_beginvbox       = context.beginvbox @@ -49,13 +52,23 @@ local variables               = interfaces.variables  local setmetatableindex       = table.setmetatableindex  local settings_to_hash        = utilities.parsers.settings_to_hash -local copy_node_list          = node.copy_list -local hpack_node_list         = node.hpack -local vpack_node_list         = node.vpack -local slide_node_list         = node.slide -local flush_node_list         = node.flush_list +local nuts                    = nodes.nuts -- here nuts gain hardly nothing +local tonut                   = nuts.tonut +local tonode                  = nuts.tonode + +local getnext                 = nuts.getnext +local getprev                 = nuts.getprev +local getlist                 = nuts.getlist +local getfield                = nuts.getfield +local getbox                  = nuts.getbox + +local setfield                = nuts.setfield -local nodepool                = nodes.pool +local copy_node_list          = nuts.copy_list +local hpack_node_list         = nuts.hpack +local flush_node_list         = nuts.flush_list + +local nodepool                = nuts.pool  local new_glue                = nodepool.glue  local new_kern                = nodepool.kern @@ -215,20 +228,20 @@ function xtables.set_reflow_width()      while row[c].span do -- can also be previous row ones          c = c + 1      end -    local tb = texgetbox("b_tabl_x") +    local tb = getbox("b_tabl_x")      local drc = row[c]      --      drc.list = true -- we don't need to keep the content around as we're in trial mode (no: copy_node_list(tb))      -- -    local widths, width = data.widths, tb.width +    local widths, width = data.widths, getfield(tb,"width")      if width > widths[c] then          widths[c] = width      end -    local heights, height = data.heights, tb.height +    local heights, height = data.heights, getfield(tb,"height")      if height > heights[r] then          heights[r] = height      end -    local depths, depth = data.depths, tb.depth +    local depths, depth = data.depths, getfield(tb,"depth")      if depth > depths[r] then          depths[r] = depth      end @@ -319,14 +332,14 @@ function xtables.set_reflow_height()   -- while row[c].span do -- we could adapt drc.nx instead   --     c = c + 1   -- end -    local tb = texgetbox("b_tabl_x") +    local tb = getbox("b_tabl_x")      local drc = row[c]      if data.fixedrows[r] == 0 then --  and drc.dimensionstate < 2 -        local heights, height = data.heights, tb.height +        local heights, height = data.heights, getfield(tb,"height")          if height > heights[r] then              heights[r] = height          end -        local depths, depth = data.depths, tb.depth +        local depths, depth = data.depths, getfield(tb,"depth")          if depth > depths[r] then              depths[r] = depth          end @@ -373,7 +386,7 @@ function xtables.set_construct()   -- end      local drc = row[c]      -- this will change as soon as in luatex we can reset a box list without freeing -    drc.list = copy_node_list(texgetbox("b_tabl_x")) +    drc.list = copy_node_list(getbox("b_tabl_x"))   -- c = c + drc.nx - 1   -- data.currentcolumn = c  end @@ -646,23 +659,23 @@ function xtables.construct()              end              local list = drc.list              if list then -                list.shift = list.height + list.depth +                setfield(list,"shift",getfield(list,"height") + getfield(list,"depth"))               -- list = hpack_node_list(list) -- is somehow needed -             -- list.width = 0 -             -- list.height = 0 -             -- list.depth = 0 +             -- setfield(list,"width",0) +             -- setfield(list,"height",0) +             -- setfield(list,"depth",0)                  -- faster:                  local h = new_hlist() -                h.list = list +                setfield(h,"list",list)                  list = h                  --                  if start then -                    stop.next = list -                    list.prev = stop +                    setfield(stop,"next",list) +                    setfield(list,"prev",stop)                  else                      start = list                  end -                stop = list -- one node anyway, so not needed: slide_node_list(list) +                stop = list              end              local step = widths[c]              if c < nofcolumns then @@ -670,8 +683,8 @@ function xtables.construct()              end              local kern = new_kern(step)              if stop then -                stop.next = kern -                kern.prev = stop +                setfield(stop,"next",kern) +                setfield(kern,"prev",stop)              else -- can be first spanning next row (ny=...)                  start = kern              end @@ -680,8 +693,8 @@ function xtables.construct()          if start then              if rightmargindistance > 0 then                  local kern = new_kern(rightmargindistance) -                stop.next = kern -                kern.prev = stop +                setfield(stop,"next",kern) +                setfield(kern,"prev",stop)               -- stop = kern              end              return start, heights[r] + depths[r], hasspan @@ -721,7 +734,7 @@ function xtables.construct()          texsetdimen("global","d_tabl_x_final_width",0)      else          texsetcount("global","c_tabl_x_state",1) -        texsetdimen("global","d_tabl_x_final_width",body[1][1].width) +        texsetdimen("global","d_tabl_x_final_width",getfield(body[1][1],"width"))      end  end @@ -734,8 +747,8 @@ local function inject(row,copy,package)      end      if package then          context_beginvbox() -        context(list) -        context(new_kern(row[2])) +        context(tonode(list)) +        context(tonode(new_kern(row[2])))          context_endvbox()          context_nointerlineskip() -- figure out a better way          if row[4] then @@ -743,13 +756,13 @@ local function inject(row,copy,package)          elseif row[3] then              context_blank(row[3] .. "sp") -- why blank ?          else -            context(new_glue(0)) +            context(tonode(new_glue(0)))          end      else -        context(list) -        context(new_kern(row[2])) +        context(tonode(list)) +        context(tonode(new_kern(row[2])))          if row[3] then -            context(new_glue(row[3])) +            context(tonode(new_glue(row[3])))          end      end  end @@ -822,7 +835,7 @@ function xtables.flush(directives) -- todo split by size / no inbetween then ..                          inject(head[i],repeatheader)                      end                      if rowdistance > 0 then -                        context(new_glue(rowdistance)) +                        context(tonode(new_glue(rowdistance)))                      end                      if not repeatheader then                          results[head_mode] = { } @@ -835,7 +848,7 @@ function xtables.flush(directives) -- todo split by size / no inbetween then ..                          inject(more[i],true)                      end                      if rowdistance > 0 then -                        context(new_glue(rowdistance)) +                        context(tonode(new_glue(rowdistance)))                      end                  end              elseif headsize > 0 and repeatheader then -- following chunk gets head @@ -845,7 +858,7 @@ function xtables.flush(directives) -- todo split by size / no inbetween then ..                          inject(head[i],true)                      end                      if rowdistance > 0 then -                        context(new_glue(rowdistance)) +                        context(tonode(new_glue(rowdistance)))                      end                  end              else -- following chunk gets nothing @@ -872,7 +885,7 @@ function xtables.flush(directives) -- todo split by size / no inbetween then ..                      -- all is flushed and footer fits                      if footsize > 0 then                          if rowdistance > 0 then -                            context(new_glue(rowdistance)) +                            context(tonode(new_glue(rowdistance)))                          end                          for i=1,#foot do                              inject(foot[i]) @@ -886,7 +899,7 @@ function xtables.flush(directives) -- todo split by size / no inbetween then ..                      -- todo: try to flush a few more lines                      if repeatfooter and footsize > 0 then                          if rowdistance > 0 then -                            context(new_glue(rowdistance)) +                            context(tonode(new_glue(rowdistance)))                          end                          for i=1,#foot do                              inject(foot[i],true) @@ -938,13 +951,13 @@ function xtables.flush(directives) -- todo split by size / no inbetween then ..                  inject(head[i])              end              if #head > 0 and rowdistance > 0 then -                context(new_glue(rowdistance)) +                context(tonode(new_glue(rowdistance)))              end              for i=1,#body do                  inject(body[i])              end              if #foot > 0 and rowdistance > 0 then -                context(new_glue(rowdistance)) +                context(tonode(new_glue(rowdistance)))              end              for i=1,#foot do                  inject(foot[i]) @@ -964,6 +977,24 @@ function xtables.cleanup()              flush_node_list(r[1])          end      end + +    -- local rows = data.rows +    -- for i=1,#rows do +    --     local row = rows[i] +    --     for i=1,#row do +    --         local cell = row[i] +    --         local list = cell.list +    --         if list then +    --             cell.width  = getfield(list,"width") +    --             cell.height = getfield(list,"height") +    --             cell.depth  = getfield(list,"depth") +    --             cell.list   = true +    --         end +    --     end +    -- end +    -- data.result = nil +    -- inspect(data) +      data = table.remove(stack)  end diff --git a/tex/context/base/trac-jus.lua b/tex/context/base/trac-jus.lua index 38220a752..d95e48816 100644 --- a/tex/context/base/trac-jus.lua +++ b/tex/context/base/trac-jus.lua @@ -14,14 +14,29 @@ typesetters.checkers      = checkers  local a_alignstate        = attributes.private("alignstate")  local a_justification     = attributes.private("justification") -local tracers             = nodes.tracers -local tracedrule          = tracers.rule - -local new_rule            = nodes.pool.rule -local new_hlist           = nodes.pool.hlist -local new_glue            = nodes.pool.glue -local new_kern            = nodes.pool.kern -local get_list_dimensions = node.dimensions +local nuts                = nodes.nuts +local tonut               = tonut + +local getfield            = nuts.getfield +local setfield            = nuts.setfield +local getattr             = nuts.getattr +local setattr             = nuts.setattr +local setlist             = nuts.setlist + +local traverse_id         = nuts.travers_id +local get_list_dimensions = nuts.dimensions +local linked_nodes        = nuts.linked +local copy_node           = nuts.copy + +local tracedrule          = nodes.tracers.pool.nuts.rule + +local nodepool            = nuts.pool + +local new_rule            = nodepool.rule +local new_hlist           = nodepool.hlist +local new_glue            = nodepool.glue +local new_kern            = nodepool.kern +  local hlist_code          = nodes.nodecodes.hlist  local texsetattribute     = tex.setattribute @@ -59,34 +74,35 @@ trackers.register("visualizers.justification", function(v)  end)  function checkers.handler(head) -    for current in node.traverse_id(hlist_code,head) do -        if current[a_justification] == 1 then -            current[a_justification] = 0 -            local width = current.width +    for current in traverse_id(hlist_code,tonut(head)) do +        if getattr(current,a_justification) == 1 then +            setattr(current,a_justification,0) +            local width = setfield(current,"width")              if width > 0 then -                local list = current.list +                local list = getlist(current)                  if list then                      local naturalwidth, naturalheight, naturaldepth = get_list_dimensions(list)                      local delta = naturalwidth - width                      if naturalwidth == 0 or delta == 0 then                          -- special box                      elseif delta >= max_threshold then -                        local rule = tracedrule(delta,naturalheight,naturaldepth,list.glue_set == 1 and "trace:dr"or "trace:db") -                        current.list = list .. new_hlist(rule) +                        local rule = tracedrule(delta,naturalheight,naturaldepth,getfield(list,"glue_set") == 1 and "trace:dr" or "trace:db") +                        setfield(current,"list",linked_nodes(list,new_hlist(rule)))                      elseif delta <= min_threshold then -                        local alignstate = list[a_alignstate] +                        local alignstate = getattr(list,a_alignstate)                          if alignstate == 1 then                              local rule = tracedrule(-delta,naturalheight,naturaldepth,"trace:dc") -                            current.list = new_hlist(rule) .. list +                            setfield(current,"list",linked_nodes(new_hlist(rule),list))                          elseif alignstate == 2 then -                            local rule = tracedrule(-delta/2,naturalheight,naturaldepth,"trace:dy") -                            current.list = new_hlist(rule^1) .. list .. new_kern(delta/2) .. new_hlist(rule) +                            local lrule = tracedrule(-delta/2,naturalheight,naturaldepth,"trace:dy") +                            local rrule = copy_node(lrule) +                            setfield(current,"list",linked_nodes(new_hlist(lrule),list,new_kern(delta/2),new_hlist(rrule)))                          elseif alignstate == 3 then                              local rule = tracedrule(-delta,naturalheight,naturaldepth,"trace:dm") -                            current.list = list .. new_kern(delta) .. new_hlist(rule) +                            setfield(current,"list",linked_nodes(list,new_kern(delta),new_hlist(rule)))                          else                              local rule = tracedrule(-delta,naturalheight,naturaldepth,"trace:dg") -                            current.list = list .. new_kern(delta) .. new_hlist(rule) +                            setfield(current,"list",linked_nodes(list,new_kern(delta),new_hlist(rule)))                          end                      end                  end diff --git a/tex/context/base/trac-par.lua b/tex/context/base/trac-par.lua deleted file mode 100644 index 262a9cc33..000000000 --- a/tex/context/base/trac-par.lua +++ /dev/null @@ -1,125 +0,0 @@ --- for the moment here: - -local utfchar = utf.char -local concat = table.concat - -local nodecodes     = nodes.nodecodes -local hlist_code    = nodecodes.hlist -local vlist_code    = nodecodes.vlist -local glyph_code    = nodecodes.glyph -local kern_code     = nodecodes.kern -local setnodecolor  = nodes.tracers.colors.set -local parameters    = fonts.hashes.parameters -local basepoints    = number.basepoints - --- definecolor[hz:positive] [r=0.6] --- definecolor[hz:negative] [g=0.6] --- definecolor[hz:zero]     [b=0.6] - --- scale = multiplier + ef/multiplier - -local trace_both    = false  trackers.register("builders.paragraphs.expansion.both",    function(v) trace_verbose = false trace_both  = v end) -local trace_verbose = false  trackers.register("builders.paragraphs.expansion.verbose", function(v) trace_verbose = v     trace_color = v end) - -local report_verbose = logs.reporter("fonts","expansion") - -local function colorize(n) -    local size, font, ef, width, list, flush, length -    if trace_verbose then -        width  = 0 -        length = 0 -        list   = { } -        flush  = function() -            if length > 0 then -                report_verbose("%0.3f : %10s  %10s  %s",ef/1000,basepoints(width),basepoints(width*ef/1000000),concat(list,"",1,length)) -                width  = 0 -                length = 0 -            end -        end -    else -        length = 0 -    end -    -- tricky: the built-in method creates dummy fonts and the last line normally has the -    -- original font and that one then has ex.auto set -    while n do -        local id = n.id -        if id == glyph_code then -            local ne = n.expansion_factor -            if ne == 0 then -                if length > 0 then flush() end -                setnodecolor(n,"hz:zero") -            else -                local f = n.font -                if f ~= font then -                    if length > 0 then -                        flush() -                    end -                    local pf = parameters[f] -                    local ex = pf.expansion -                    if ex and ex.auto then -                        size = pf.size -                        font = f -- save lookups -                    else -                        size = false -                    end -                end -                if size then -                    if ne ~= ef then -                        if length > 0 then -                            flush() -                        end -                        ef = ne -                    end -                    if ef > 1 then -                        setnodecolor(n,"hz:plus") -                    elseif ef < 1 then -                        setnodecolor(n,"hz:minus") -                    else -                        setnodecolor(n,"hz:zero") -                    end -                    if trace_verbose then -                        length = length + 1 -                        list[length] = utfchar(n.char) -                        width = width + n.width -- no kerning yet -                    end -                end -            end -        elseif id == hlist_code or id == vlist_code then -            if length > 0 then -                flush() -            end -            colorize(n.list,flush) -        else -- nothing to show on kerns -            if length > 0 then -                flush() -            end -        end -        n = n.next -    end -    if length > 0 then -        flush() -    end -end - -builders.paragraphs.expansion = builders.paragraphs.expansion or { } - -function builders.paragraphs.expansion.trace(head) -    colorize(head,true) -    return head -end - -local tasks = nodes.tasks - -tasks.prependaction("shipouts","normalizers","builders.paragraphs.expansion.trace") -tasks.disableaction("shipouts","builders.paragraphs.expansion.trace") - -local function set(v) -    if v then -        tasks.enableaction("shipouts","builders.paragraphs.expansion.trace") -    else -        tasks.disableaction("shipouts","builders.paragraphs.expansion.trace") -    end -end - -trackers.register("builders.paragraphs.expansion.verbose",set) -trackers.register("builders.paragraphs.expansion.both",set) diff --git a/tex/context/base/trac-vis.lua b/tex/context/base/trac-vis.lua index dc8bcc5e7..420e9a00d 100644 --- a/tex/context/base/trac-vis.lua +++ b/tex/context/base/trac-vis.lua @@ -34,6 +34,7 @@ local formatters = string.formatters  -- todo: inline concat (more efficient)  local nodecodes           = nodes.nodecodes +local disc_code           = nodecodes.disc  local kern_code           = nodecodes.kern  local glyph_code          = nodecodes.glyph  local hlist_code          = nodecodes.hlist @@ -58,21 +59,41 @@ local rightskip_code      = gluecodes.rightskip  local whatsitcodes        = nodes.whatsitcodes -local hpack_nodes         = node.hpack -local vpack_nodes         = node.vpack -local fast_hpack_string   = nodes.typesetters.fast_hpack -local copy_node           = node.copy -local copy_list           = node.copy_list -local free_node           = node.free -local free_node_list      = node.flush_list -local insert_node_before  = node.insert_before -local insert_node_after   = node.insert_after -local fast_hpack          = nodes.fasthpack -local traverse_nodes      = node.traverse +local nuts                = nodes.nuts +local tonut               = nuts.tonut +local tonode              = nuts.tonode + +local getfield            = nuts.getfield +local getnext             = nuts.getnext +local getprev             = nuts.getprev +local getid               = nuts.getid +local setfield            = nuts.setfield +local getattr             = nuts.getattr +local setattr             = nuts.setattr +local getfont             = nuts.getfont +local getsubtype          = nuts.getsubtype +local getchar             = nuts.getchar +local getbox              = nuts.getbox +local getlist             = nuts.getlist +local getleader           = nuts.getleader + +local hpack_nodes         = nuts.hpack +local vpack_nodes         = nuts.vpack +local copy_node           = nuts.copy +local copy_list           = nuts.copy_list +local free_node           = nuts.free +local free_node_list      = nuts.flush_list +local insert_node_before  = nuts.insert_before +local insert_node_after   = nuts.insert_after +local traverse_nodes      = nuts.traverse +local linked_nodes        = nuts.linked + +local fast_hpack          = nuts.fasthpack +local fast_hpack_string   = nuts.typesetters.fast_hpack  local texgetattribute     = tex.getattribute  local texsetattribute     = tex.setattribute -local texgetbox           = tex.getbox +  local unsetvalue          = attributes.unsetvalue  local current_font        = font.current @@ -81,7 +102,7 @@ local exheights           = fonts.hashes.exheights  local emwidths            = fonts.hashes.emwidths  local pt_factor           = number.dimenfactors.pt -local nodepool            = nodes.pool +local nodepool            = nuts.pool  local new_rule            = nodepool.rule  local new_kern            = nodepool.kern  local new_glue            = nodepool.glue @@ -293,39 +314,39 @@ local c_white_d    = "trace:dw"  local function sometext(str,layer,color,textcolor) -- we can just paste verbatim together .. no typesteting needed      local text = fast_hpack_string(str,usedfont) -    local size = text.width +    local size = getfield(text,"width")      local rule = new_rule(size,2*exheight,exheight/2)      local kern = new_kern(-size)      if color then          setcolor(rule,color)      end      if textcolor then -        setlistcolor(text.list,textcolor) +        setlistcolor(getlist(text),textcolor)      end -    local info = rule .. kern .. text +    local info = linked_nodes(rule,kern,text)      setlisttransparency(info,c_zero)      info = fast_hpack(info)      if layer then -        info[a_layer] = layer +        setattr(info,a_layer,layer)      end -    local width = info.width -    info.width = 0 -    info.height = 0 -    info.depth = 0 +    local width = getfield(info,"width") +    setfield(info,"width",0) +    setfield(info,"height",0) +    setfield(info,"depth",0)      return info, width  end  local f_cache = { }  local function fontkern(head,current) -    local kern = current.kern +    local kern = getfield(current,"kern")      local info = f_cache[kern]      if info then          -- print("hit fontkern")      else          local text = fast_hpack_string(formatters[" %0.3f"](kern*pt_factor),usedfont)          local rule = new_rule(emwidth/10,6*exheight,2*exheight) -        local list = text.list +        local list = getlist(text)          if kern > 0 then              setlistcolor(list,c_positive_d)          elseif kern < 0 then @@ -335,13 +356,12 @@ local function fontkern(head,current)          end          setlisttransparency(list,c_text_d)          settransparency(rule,c_text_d) -        text.shift = -5 * exheight -        info = rule .. text -        info = fast_hpack(info) -        info[a_layer] = l_fontkern -        info.width = 0 -        info.height = 0 -        info.depth = 0 +        setfield(text,"shift",-5 * exheight) +        info = fast_hpack(linked_nodes(rule,text)) +        setattr(info,a_layer,l_fontkern) +        setfield(info,"width",0) +        setfield(info,"height",0) +        setfield(info,"depth",0)          f_cache[kern] = info      end      head = insert_node_before(head,current,copy_list(info)) @@ -382,7 +402,7 @@ local tags = {  }  local function whatsit(head,current) -    local what = current.subtype +    local what = getsubtype(current)      local info = w_cache[what]      if info then          -- print("hit whatsit") @@ -390,7 +410,7 @@ local function whatsit(head,current)          local tag = whatsitcodes[what]          -- maybe different text colors per tag          info = sometext(formatters["W:%s"](tag and tags[tag] or what),usedfont,nil,c_white) -        info[a_layer] = l_whatsit +        setattr(info,a_layer,l_whatsit)          w_cache[what] = info      end      head, current = insert_node_after(head,current,copy_list(info)) @@ -398,13 +418,13 @@ local function whatsit(head,current)  end  local function user(head,current) -    local what = current.subtype +    local what = getsubtype(current)      local info = w_cache[what]      if info then          -- print("hit user")      else          info = sometext(formatters["U:%s"](what),usedfont) -        info[a_layer] = l_user +        setattr(info,a_layer,l_user)          w_cache[what] = info      end      head, current = insert_node_after(head,current,copy_list(info)) @@ -414,14 +434,14 @@ end  local b_cache = { }  local function ruledbox(head,current,vertical,layer,what,simple,previous) -    local wd = current.width +    local wd = getfield(current,"width")      if wd ~= 0 then -        local ht = current.height -        local dp = current.depth -        local next = current.next -        local prev = previous -- current.prev ... prev can be wrong in math mode -        current.next = nil -        current.prev = nil +        local ht = getfield(current,"height") +        local dp = getfield(current,"depth") +        local next = getnext(current) +        local prev = previous -- getprev(current) ... prev can be wrong in math mode +        setfield(current,"next",nil) +        setfield(current,"prev",nil)          local linewidth = emwidth/10          local baseline, baseskip          if dp ~= 0 and ht ~= 0 then @@ -430,16 +450,16 @@ local function ruledbox(head,current,vertical,layer,what,simple,previous)                  if not baseline then                      -- due to an optimized leader color/transparency we need to set the glue node in order                      -- to trigger this mechanism -                    local leader = new_glue(2*linewidth) .. new_rule(6*linewidth,linewidth,0) .. new_glue(2*linewidth) +                    local leader = linked_nodes(new_glue(2*linewidth),new_rule(6*linewidth,linewidth,0),new_glue(2*linewidth))                   -- setlisttransparency(leader,c_text)                      leader = fast_hpack(leader)                   -- setlisttransparency(leader,c_text)                      baseline = new_glue(0) -                    baseline.leader = leader -                    baseline.subtype = cleaders_code -                    local spec = baseline.spec -                    spec.stretch = 65536 -                    spec.stretch_order = 2 +                    setfield(baseline,"leader",leader) +                    setfield(baseline,"subtype",cleaders_code) +                    local spec = getfield(baseline,"spec") +                    setfield(spec,"stretch",65536) +                    setfield(spec,"stretch_order",2)                      setlisttransparency(baseline,c_text)                      b_cache.baseline = baseline                  end @@ -461,47 +481,49 @@ local function ruledbox(head,current,vertical,layer,what,simple,previous)              this = b_cache[what]              if not this then                  local text = fast_hpack_string(what,usedfont) -                this = new_kern(-text.width) .. text +                this = linked_nodes(new_kern(-getfield(text,"width")),text)                  setlisttransparency(this,c_text)                  this = fast_hpack(this) -                this.width = 0 -                this.height = 0 -                this.depth = 0 +                setfield(this,"width",0) +                setfield(this,"height",0) +                setfield(this,"depth",0)                  b_cache[what] = this              end          end          -- we need to trigger the right mode (else sometimes no whatits) -        local info = -            (this and copy_list(this) or nil) .. -            new_rule(linewidth,ht,dp) .. -            new_rule(wd-2*linewidth,-dp+linewidth,dp) .. -            new_rule(linewidth,ht,dp) .. -            new_kern(-wd+linewidth) .. +        local info = linked_nodes( +            this and copy_list(this) or nil, +            new_rule(linewidth,ht,dp), +            new_rule(wd-2*linewidth,-dp+linewidth,dp), +            new_rule(linewidth,ht,dp), +            new_kern(-wd+linewidth),              new_rule(wd-2*linewidth,ht,-ht+linewidth) +        )          if baseskip then -            info = info .. baseskip .. baseline +            info = linked_nodes(info,baseskip,baseline)          end          setlisttransparency(info,c_text)          info = fast_hpack(info) -        info.width = 0 -        info.height = 0 -        info.depth = 0 -        info[a_layer] = layer -        local info = current .. new_kern(-wd) .. info +        setfield(info,"width",0) +        setfield(info,"height",0) +        setfield(info,"depth",0) +        setattr(info,a_layer,layer) +        local info = linked_nodes(current,new_kern(-wd),info)          info = fast_hpack(info,wd)          if vertical then              info = vpack_nodes(info)          end          if next then -            info.next = next -            next.prev = info +            setfield(info,"next",next) +            setfield(next,"prev",info)          end          if prev then -            if prev.id == gluespec_code then -                -- weird, how can this happen, an inline glue-spec +            if getid(prev) == gluespec_code then +                report_visualize("ignoring invalid prev") +                -- weird, how can this happen, an inline glue-spec, probably math              else -                info.prev = prev -                prev.next = info +                setfield(info,"prev",prev) +                setfield(prev,"next",info)              end          end          if head == current then @@ -515,14 +537,14 @@ local function ruledbox(head,current,vertical,layer,what,simple,previous)  end  local function ruledglyph(head,current,previous) -    local wd = current.width +    local wd = getfield(current,"width")      if wd ~= 0 then -        local ht = current.height -        local dp = current.depth -        local next = current.next +        local ht = getfield(current,"height") +        local dp = getfield(current,"depth") +        local next = getnext(current)          local prev = previous -        current.next = nil -        current.prev = nil +        setfield(current,"next",nil) +        setfield(current,"prev",nil)          local linewidth = emwidth/20          local baseline          if dp ~= 0 and ht ~= 0 then @@ -530,31 +552,32 @@ local function ruledglyph(head,current,previous)          end          local doublelinewidth = 2*linewidth          -- could be a pdf rule -        local info = -            new_rule(linewidth,ht,dp) .. -            new_rule(wd-doublelinewidth,-dp+linewidth,dp) .. -            new_rule(linewidth,ht,dp) .. -            new_kern(-wd+linewidth) .. -            new_rule(wd-doublelinewidth,ht,-ht+linewidth) .. -            new_kern(-wd+doublelinewidth) .. +        local info = linked_nodes( +            new_rule(linewidth,ht,dp), +            new_rule(wd-doublelinewidth,-dp+linewidth,dp), +            new_rule(linewidth,ht,dp), +            new_kern(-wd+linewidth), +            new_rule(wd-doublelinewidth,ht,-ht+linewidth), +            new_kern(-wd+doublelinewidth),              baseline +        )          setlistcolor(info,c_glyph)          setlisttransparency(info,c_glyph_d)          info = fast_hpack(info) -        info.width = 0 -        info.height = 0 -        info.depth = 0 -        info[a_layer] = l_glyph -        local info = current .. new_kern(-wd) .. info +        setfield(info,"width",0) +        setfield(info,"height",0) +        setfield(info,"depth",0) +        setattr(info,a_layer,l_glyph) +        local info = linked_nodes(current,new_kern(-wd),info)          info = fast_hpack(info) -        info.width = wd +        setfield(info,"width",wd)          if next then -            info.next = next -            next.prev = info +            setfield(info,"next",next) +            setfield(next,"prev",info)          end          if prev then -            info.prev = prev -            prev.next = info +            setfield(info,"prev",prev) +            setfield(prev,"next",info)          end          if head == current then              return info, info @@ -599,9 +622,9 @@ local tags = {  -- we sometimes pass previous as we can have issues in math (not watertight for all)  local function ruledglue(head,current,vertical) -    local spec = current.spec -    local width = spec.width -    local subtype = current.subtype +    local spec = getfield(current,"spec") +    local width = getfield(spec,"width") +    local subtype = getsubtype(current)      local amount = formatters["%s:%0.3f"](tags[subtype] or (vertical and "VS") or "HS",width*pt_factor)      local info = g_cache[amount]      if info then @@ -629,13 +652,13 @@ local function ruledglue(head,current,vertical)          info = vpack_nodes(info)      end      head, current = insert_node_before(head,current,info) -    return head, current.next +    return head, getnext(current)  end  local k_cache = { }  local function ruledkern(head,current,vertical) -    local kern = current.kern +    local kern = getfield(current,"kern")      local info = k_cache[kern]      if info then          -- print("kern hit") @@ -655,13 +678,13 @@ local function ruledkern(head,current,vertical)          info = vpack_nodes(info)      end      head, current = insert_node_before(head,current,info) -    return head, current.next +    return head, getnext(current)  end  local p_cache = { }  local function ruledpenalty(head,current,vertical) -    local penalty = current.penalty +    local penalty = getfield(current,"penalty")      local info = p_cache[penalty]      if info then          -- print("penalty hit") @@ -681,7 +704,7 @@ local function ruledpenalty(head,current,vertical)          info = vpack_nodes(info)      end      head, current = insert_node_before(head,current,info) -    return head, current.next +    return head, getnext(current)  end  local function visualize(head,vertical) @@ -702,8 +725,8 @@ local function visualize(head,vertical)      local attr           = unsetvalue      local prev_trace_fontkern = nil      while current do -        local id = current.id -        local a = current[a_visual] or unsetvalue +        local id = getid(current) +        local a = getattr(current,a_visual) or unsetvalue          if a ~= attr then              prev_trace_fontkern = trace_fontkern              if a == unsetvalue then @@ -736,30 +759,30 @@ local function visualize(head,vertical)              attr = a          end          if trace_strut then -            current[a_layer] = l_strut +            setattr(current,a_layer,l_strut)          elseif id == glyph_code then              if trace_glyph then                  head, current = ruledglyph(head,current,previous)              end          elseif id == disc_code then              if trace_glyph then -                local pre = current.pre +                local pre = getfield(current,"pre")                  if pre then -                    current.pre = ruledglyph(pre,pre) +                    setfield(current,"pre",ruledglyph(pre,pre))                  end -                local post = current.post +                local post = getfield(current,"post")                  if post then -                    current.post = ruledglyph(post,post) +                    setfield(current,"post",ruledglyph(post,post))                  end -                local replace = current.replace +                local replace = getfield(current,"replace")                  if replace then -                    current.replace = ruledglyph(replace,replace) +                    setfield(current,"replace",ruledglyph(replace,replace))                  end              end          elseif id == kern_code then -            local subtype = current.subtype +            local subtype = getsubtype(current)              -- tricky ... we don't copy the trace attribute in node-inj (yet) -            if subtype == font_kern_code or current[a_fontkern] then +            if subtype == font_kern_code or getattr(current,a_fontkern) then                  if trace_fontkern or prev_trace_fontkern then                      head, current = fontkern(head,current)                  end @@ -769,9 +792,9 @@ local function visualize(head,vertical)                  end              end          elseif id == glue_code then -            local content = current.leader +            local content = getleader(current)              if content then -                current.leader = visualize(content,false) +                setfield(current,"leader",visualize(content,false))              elseif trace_glue then                  head, current = ruledglue(head,current,vertical)              end @@ -780,21 +803,21 @@ local function visualize(head,vertical)                  head, current = ruledpenalty(head,current,vertical)              end          elseif id == disc_code then -            current.pre = visualize(current.pre) -            current.post = visualize(current.post) -            current.replace = visualize(current.replace) +            setfield(current,"pre",visualize(getfield(current,"pre"))) +            setfield(current,"post",isualize(getfield(current,"post"))) +            setfield(current,"replace",visualize(getfield(current,"replace")))          elseif id == hlist_code then -            local content = current.list +            local content = getlist(current)              if content then -                current.list = visualize(content,false) +                setfield(current,"list",visualize(content,false))              end              if trace_hbox then                  head, current = ruledbox(head,current,false,l_hbox,"H__",trace_simple,previous)              end          elseif id == vlist_code then -            local content = current.list +            local content = getlist(current)              if content then -                current.list = visualize(content,true) +                setfield(current,"list",visualize(content,true))              end              if trace_vtop then                  head, current = ruledbox(head,current,true,l_vtop,"_T_",trace_simple,previous) @@ -811,7 +834,7 @@ local function visualize(head,vertical)              end          end          previous = current -        current  = current.next +        current  = getnext(current)      end      return head  end @@ -840,25 +863,36 @@ local function cleanup()   -- report_visualize("cache: %s fontkerns, %s skips, %s penalties, %s kerns, %s whatsits, %s boxes",nf,ng,np,nk,nw,nb)  end -function visualizers.handler(head) +local function handler(head)      if usedfont then          starttiming(visualizers)       -- local l = texgetattribute(a_layer)       -- local v = texgetattribute(a_visual)       -- texsetattribute(a_layer,unsetvalue)       -- texsetattribute(a_visual,unsetvalue) -        head = visualize(head) +        head = visualize(tonut(head))       -- texsetattribute(a_layer,l)       -- texsetattribute(a_visual,v)       -- -- cleanup()          stoptiming(visualizers) +        return tonode(head), true +    else +        return head, false      end -    return head, false  end +visualizers.handler = handler +  function visualizers.box(n) -    local box = texgetbox(n) -    box.list = visualizers.handler(box.list) +    if usedfont then +        starttiming(visualizers) +        local box = getbox(n) +        setfield(box,"list",visualize(getlist(box))) +        stoptiming(visualizers) +        return head, true +    else +        return head, false +    end  end  local last = nil @@ -872,9 +906,9 @@ local mark = {  local function markfonts(list)      for n in traverse_nodes(list) do -        local id = n.id +        local id = getid(n)          if id == glyph_code then -            local font = n.font +            local font = getfont(n)              local okay = used[font]              if not okay then                  last = last + 1 @@ -883,14 +917,14 @@ local function markfonts(list)              end              setcolor(n,okay)          elseif id == hlist_code or id == vlist_code then -            markfonts(n.list) +            markfonts(getlist(n))          end      end  end  function visualizers.markfonts(list)      last, used = 0, { } -    markfonts(type(n) == "number" and texgetbox(n).list or n) +    markfonts(type(n) == "number" and getlist(getbox(n)) or n)  end  function commands.markfonts(n) diff --git a/tex/context/base/typo-bld.lua b/tex/context/base/typo-bld.lua index bc9f66ee4..1cdda7c4b 100644 --- a/tex/context/base/typo-bld.lua +++ b/tex/context/base/typo-bld.lua @@ -6,6 +6,8 @@ if not modules then modules = { } end modules ['typo-bld'] = { -- was node-par      license   = "see context related readme files"  } +-- no need for nuts in the one-line demo (that might move anyway) +  local insert, remove = table.insert, table.remove  local builders, nodes, node = builders, nodes, node @@ -36,8 +38,8 @@ local texlists           = tex.lists  local nodepool           = nodes.pool  local new_baselineskip   = nodepool.baselineskip  local new_lineskip       = nodepool.lineskip -local insert_node_before = node.insert_before -local hpack_node         = node.hpack +local insert_node_before = nodes.insert_before +local hpack_node         = nodes.hpack  local starttiming        = statistics.starttiming  local stoptiming         = statistics.stoptiming diff --git a/tex/context/base/typo-brk.lua b/tex/context/base/typo-brk.lua index 3558efa8e..be11da9c3 100644 --- a/tex/context/base/typo-brk.lua +++ b/tex/context/base/typo-brk.lua @@ -20,19 +20,36 @@ local report_breakpoints = logs.reporter("typesetting","breakpoints")  local nodes, node = nodes, node  local settings_to_array  = utilities.parsers.settings_to_array -local copy_node          = node.copy -local copy_nodelist      = node.copy_list -local free_node          = node.free -local insert_node_before = node.insert_before -local insert_node_after  = node.insert_after -local remove_node        = nodes.remove -- ! nodes -local tonodes            = nodes.tonodes +local nuts               = nodes.nuts +local tonut              = nuts.tonut +local tonode             = nuts.tonode + +local getnext            = nuts.getnext +local getprev            = nuts.getprev +local getsubtype         = nuts.getsubtype +local getchar            = nuts.getchar +local getfont            = nuts.getfont +local getid              = nuts.getid +local getfield           = nuts.getfield +local getattr            = nuts.getattr + +local setfield           = nuts.setfield +local setattr            = nuts.setattr + +local copy_node          = nuts.copy +local copy_nodelist      = nuts.copy_list +local free_node          = nuts.free +local insert_node_before = nuts.insert_before +local insert_node_after  = nuts.insert_after +local remove_node        = nuts.remove + +local tonodes            = nuts.tonodes  local texsetattribute    = tex.setattribute  local unsetvalue         = attributes.unsetvalue -local nodepool           = nodes.pool +local nodepool           = nuts.pool  local tasks              = nodes.tasks  local v_reset            = interfaces.variables.reset @@ -80,74 +97,82 @@ local function insert_break(head,start,before,after)  end  methods[1] = function(head,start) -    if start.prev and start.next then +    if getprev(start) and getnext(start) then          insert_break(head,start,10000,0)      end      return head, start  end  methods[2] = function(head,start) -- ( => (- -    if start.prev and start.next then +    if getprev(start) and getnext(start) then          local tmp          head, start, tmp = remove_node(head,start)          head, start = insert_node_before(head,start,new_disc()) -        start.attr = copy_nodelist(tmp.attr) -- todo: critical only -        start.replace = tmp -        local tmp, hyphen = copy_node(tmp), copy_node(tmp) -        hyphen.char = languages.prehyphenchar(tmp.lang) -        tmp.next, hyphen.prev = hyphen, tmp -        start.post = tmp +        setfield(start,"attr",copy_nodelist(getfield(tmp,"attr"))) +        setfield(start,"replace",tmp) +        local tmp = copy_node(tmp) +        local hyphen = copy_node(tmp) +        setfield(hyphen,"char",languages.prehyphenchar(getfield(tmp,"lang"))) +        setfield(tmp,"next",hyphen) +        setfield(hyphen,"prev",tmp) +        setfield(start,"post",tmp)          insert_break(head,start,10000,10000)      end      return head, start  end  methods[3] = function(head,start) -- ) => -) -    if start.prev and start.next then +    if getprev(start) and getnext(start) then          local tmp          head, start, tmp = remove_node(head,start)          head, start = insert_node_before(head,start,new_disc()) -        start.attr = copy_nodelist(tmp.attr) -- todo: critical only -        start.replace = tmp -        local tmp, hyphen = copy_node(tmp), copy_node(tmp) -        hyphen.char = languages.prehyphenchar(tmp.lang) -        tmp.prev, hyphen.next = hyphen, tmp -        start.pre = hyphen +        setfield(start,"attr",copy_nodelist(getfield(tmp,"attr"))) +        setfield(start,"replace",tmp) +        local tmp = copy_node(tmp) +        local hyphen = copy_node(tmp) +        setfield(hyphen,"char",languages.prehyphenchar(getfield(tmp,"lang"))) +        setfield(tmp,"prev",hyphen) +        setfield(hyphen,"next",tmp) +        setfield(start,"pre",hyphen)          insert_break(head,start,10000,10000)      end      return head, start  end  methods[4] = function(head,start) -- - => - - - -    if start.prev and start.next then +    if getprev(start) and getnext(start) then          local tmp          head, start, tmp = remove_node(head,start)          head, start = insert_node_before(head,start,new_disc()) -        start.attr = copy_nodelist(tmp.attr) -- todo: critical only -        start.pre, start.post, start.replace = copy_node(tmp), copy_node(tmp), tmp +        setfield(start,"attr",copy_nodelist(getfield(tmp,"attr"))) +        setfield(start,"pre",copy_node(tmp)) +        setfield(start,"post",copy_node(tmp)) +        setfield(start,"replace",tmp)          insert_break(head,start,10000,10000)      end      return head, start  end  methods[5] = function(head,start,settings) -- x => p q r -    if start.prev and start.next then +    if getprev(start) and getnext(start) then          local tmp          head, start, tmp = remove_node(head,start)          head, start = insert_node_before(head,start,new_disc()) -        local attr = tmp.attr -        local font = tmp.font -        start.attr = copy_nodelist(attr) -- todo: critical only -        local left, right, middle = settings.left, settings.right, settings.middle +        local attr = getfield(tmp,"attr") +        local font = getfont(tmp) +        local left = settings.left +        local right = settings.right +        local middle = settings.middle          if left then -            start.pre = tonodes(tostring(left),font,attr) -- was right +            setfield(start,"pre",(tonodes(tostring(left),font,attr))) -- was right          end          if right then -            start.post = tonodes(tostring(right),font,attr) -- was left +            setfield(start,"post",(tonodes(tostring(right),font,attr))) -- was left          end          if middle then -            start.replace = tonodes(tostring(middle),font,attr) +            setfield(start,"replace",(tonodes(tostring(middle),font,attr)))          end +        setfield(start,"attr",copy_nodelist(attr)) -- todo: critical only          free_node(tmp)          insert_break(head,start,10000,10000)      end @@ -155,31 +180,32 @@ methods[5] = function(head,start,settings) -- x => p q r  end  function breakpoints.handler(head) +    head = tonut(head)      local done, numbers = false, languages.numbers      local start, n = head, 0      while start do -        local id = start.id +        local id = getid(start)          if id == glyph_code then -            local attr = start[a_breakpoints] +            local attr = getattr(start,a_breakpoints)              if attr and attr > 0 then -                start[a_breakpoints] = unsetvalue -- maybe test for subtype > 256 (faster) +                setattr(start,a_breakpoints,unsetvalue) -- maybe test for subtype > 256 (faster)                  -- look ahead and back n chars                  local data = mapping[attr]                  if data then                      local map = data.characters -                    local cmap = map[start.char] +                    local cmap = map[getchar(start)]                      if cmap then -                        local lang = start.lang +                        local lang = getfield(start,"lang")                          -- we do a sanity check for language                          local smap = lang and lang >= 0 and lang < 0x7FFF and (cmap[numbers[lang]] or cmap[""])                          if smap then                              if n >= smap.nleft then                                  local m = smap.nright -                                local next = start.next +                                local next = getnext(start)                                  while next do -- gamble on same attribute (not that important actually) -                                    local id = next.id +                                    local id = getid(next)                                      if id == glyph_code then -- gamble on same attribute (not that important actually) -                                        if map[next.char] then +                                        if map[getchar(next)] then                                              break                                          elseif m == 1 then                                              local method = methods[smap.type] @@ -190,10 +216,10 @@ function breakpoints.handler(head)                                              break                                          else                                              m = m - 1 -                                            next = next.next +                                            next = getnext(next)                                          end -                                    elseif id == kern_code and next.subtype == kerning_code then -                                        next = next.next +                                    elseif id == kern_code and getsubtype(next) == kerning_code then +                                        next = getnext(next)                                          -- ignore intercharacter kerning, will go way                                      else                                          -- we can do clever and set n and jump ahead but ... not now @@ -214,14 +240,14 @@ function breakpoints.handler(head)              else               -- n = n + 1 -- if we want single char handling (|-|) then we will use grouping and then we need this              end -        elseif id == kern_code and start.subtype == kerning_code then +        elseif id == kern_code and getsubtype(start) == kerning_code then              -- ignore intercharacter kerning, will go way          else              n = 0          end -        start = start.next +        start = getnext(start)      end -    return head, done +    return tonode(head), done  end  local enabled = false diff --git a/tex/context/base/typo-cap.lua b/tex/context/base/typo-cap.lua index 0fc1a3093..78ed8700a 100644 --- a/tex/context/base/typo-cap.lua +++ b/tex/context/base/typo-cap.lua @@ -16,9 +16,23 @@ local report_casing = logs.reporter("typesetting","casing")  local nodes, node = nodes, node -local copy_node       = nodes.copy -local end_of_math     = nodes.end_of_math - +local nuts            = nodes.nuts +local tonode          = nuts.tonode +local tonut           = nuts.tonut + +local getfield        = nuts.getfield +local setfield        = nuts.setfield +local getnext         = nuts.getnext +local getprev         = nuts.getprev +local getid           = nuts.getid +local getattr         = nuts.getattr +local setattr         = nuts.setattr +local getfont         = nuts.getfont +local getsubtype      = nuts.getsubtype +local getchar         = nuts.getchar + +local copy_node       = nuts.copy +local end_of_math     = nuts.end_of_math  local nodecodes       = nodes.nodecodes  local skipcodes       = nodes.skipcodes @@ -96,14 +110,14 @@ local lccodes = characters.lccodes  -- true false true == mixed  local function helper(start,attr,lastfont,n,codes,special,once,keepother) -    local char = start.char +    local char = getchar(start)      local dc   = codes[char]      if dc then -        local fnt = start.font +        local fnt = getfont(start)          if keepother and dc == char then              local lfa = lastfont[n]              if lfa then -                start.font = lfa +                setfield(start,"font",lfa)                  return start, true              else                  return start, false @@ -112,10 +126,10 @@ local function helper(start,attr,lastfont,n,codes,special,once,keepother)              if special then                  local lfa = lastfont[n]                  if lfa then -                    local previd = start.prev.id +                    local previd = getid(getprev(start))                      if previd ~= glyph_code and previd ~= disc_code then                          fnt = lfa -                        start.font = lfa +                        setfield(start,"font",lfa)                      end                  end              end @@ -137,18 +151,18 @@ local function helper(start,attr,lastfont,n,codes,special,once,keepother)                          local chr = dc[i]                          prev = start                          if i == 1 then -                            start.char = chr +                            setfield(start,"char",chr)                          else                              local g = copy_node(original) -                            g.char = chr -                            local next = start.next -                            g.prev = start +                            setfield(g,"char",chr) +                            local next = getnext(start) +                            setfield(g,"prev",start)                              if next then -                                g.next = next -                                start.next = g -                                next.prev = g +                                setfield(g,"next",next) +                                setfield(start,"next",g) +                                setfield(next,"prev",g)                              end -                            start = g  +                            start = g                          end                      end                      if once then @@ -161,7 +175,7 @@ local function helper(start,attr,lastfont,n,codes,special,once,keepother)                  end                  return start, false              elseif ifc[dc] then -                start.char = dc +                setfield(start,"char",dc)                  if once then                      lastfont[n] = false                  end @@ -203,29 +217,29 @@ local function word(start,attr,lastfont,n)  end  local function blockrest(start) -    local n = start.next +    local n = getnext(start)      while n do -        local id = n.id -        if id == glyph_code or id == disc_node and n[a_cases] == attr then -            n[a_cases] = unsetvalue +        local id = getid(n) +        if id == glyph_code or id == disc_node and getattr(n,a_cases) == attr then +            setattr(n,a_cases,unsetvalue)          else           -- break -- we can have nested mess          end -        n = n.next +        n = getnext(n)      end  end  local function Word(start,attr,lastfont,n) -- looks quite complex      lastfont[n] = false -    local prev = start.prev -    if prev and prev.id == kern_code and prev.subtype == kerning_code then -        prev = prev.prev +    local prev = getprev(start) +    if prev and getid(prev) == kern_code and getsubtype(prev) == kerning_code then +        prev = getprev(prev)      end      if not prev then          blockrest(start)          return helper(start,attr,lastfont,n,uccodes)      end -    local previd = prev.id +    local previd = getid(prev)      if previd ~= glyph_code and previd ~= disc_code then          -- only the first character is treated          blockrest(start) @@ -239,14 +253,14 @@ end  local function Words(start,attr,lastfont,n)      lastfont[n] = false -    local prev = start.prev -    if prev and prev.id == kern_code and prev.subtype == kerning_code then -        prev = prev.prev +    local prev = getprev(start) +    if prev and getid(prev) == kern_code and getsubtype(prev) == kerning_code then +        prev = getprev(prev)      end      if not prev then          return helper(start,attr,lastfont,n,uccodes)      end -    local previd = prev.id +    local previd = getid(prev)      if previd ~= glyph_code and previd ~= disc_code then          return helper(start,attr,lastfont,n,uccodes)      else @@ -272,15 +286,15 @@ end  local function random(start,attr,lastfont,n)      lastfont[n] = false -    local ch  = start.char -    local tfm = fontchar[start.font] +    local ch  = getchar(start) +    local tfm = fontchar[getfont(start)]      if lccodes[ch] then          while true do              local d = chardata[randomnumber(1,0xFFFF)]              if d then                  local uc = uccodes[d]                  if uc and tfm[uc] then -- this also intercepts tables -                    start.char = uc +                    setfield(start,"char",uc)                      return start, true                  end              end @@ -291,7 +305,7 @@ local function random(start,attr,lastfont,n)              if d then                  local lc = lccodes[d]                  if lc and tfm[lc] then -- this also intercepts tables -                    start.char = lc +                    setfield(start,"char",lc)                      return start, true                  end              end @@ -314,19 +328,20 @@ register(variables.cap,     variables.capital) -- clone  register(variables.Cap,     variables.Capital) -- clone  function cases.handler(head) -- not real fast but also not used on much data +    head           = tonut(head)      local lastfont = { }      local lastattr = nil      local done     = false      local start    = head      while start do -- while because start can jump ahead -        local id = start.id +        local id = getid(start)          if id == glyph_code then -            local attr = start[a_cases] +            local attr = getattr(start,a_cases)              if attr and attr > 0 then                  if attr ~= lastattr then                      lastattr = attr                  end -                start[a_cases] = unsetvalue +                setattr(start,a_cases,unsetvalue)                  local n, id, m = get(attr)                  if lastfont[n] == nil then                      lastfont[n] = id @@ -345,27 +360,27 @@ function cases.handler(head) -- not real fast but also not used on much data                  end              end          elseif id == disc_code then -            local attr = start[a_cases] +            local attr = getattr(start,a_cases)              if attr and attr > 0 then                  if attr ~= lastattr then                      lastattr = attr                  end -                start[a_cases] = unsetvalue +                setattr(start,a_cases,unsetvalue)                  local n, id, m = get(attr)                  if lastfont[n] == nil then                      lastfont[n] = id                  end                  local action = actions[n] -- map back to low number                  if action then -                    local replace = start.replace +                    local replace = getfield(start,"replace")                      if replace then                          action(replace,attr,lastfont,n)                      end -                    local pre = start.pre +                    local pre = getfield(start,"pre")                      if pre then                          action(pre,attr,lastfont,n)                      end -                    local post = start.post +                    local post = getfield(start,"post")                      if post then                          action(post,attr,lastfont,n)                      end @@ -375,10 +390,10 @@ function cases.handler(head) -- not real fast but also not used on much data              start = end_of_math(start)          end          if start then -- why test -            start = start.next +            start = getnext(start)          end      end -    return head, done +    return tonode(head), done  end  local enabled = false diff --git a/tex/context/base/typo-cln.lua b/tex/context/base/typo-cln.lua index 2aa05b6d1..b7e337662 100644 --- a/tex/context/base/typo-cln.lua +++ b/tex/context/base/typo-cln.lua @@ -28,7 +28,14 @@ local tasks           = nodes.tasks  local texsetattribute = tex.setattribute -local traverse_id     = node.traverse_id +local nuts            = nodes.nuts +local tonut           = nuts.tonut + +local setfield        = nuts.setfield +local getchar         = nuts.getchar +local getattr         = nuts.getattr + +local traverse_id     = nuts.traverse_id  local unsetvalue      = attributes.unsetvalue @@ -48,18 +55,18 @@ local resetter = { -- this will become an entry in char-def  function cleaners.handler(head)      local inline, done = false, false -    for n in traverse_id(glyph_code,head) do -        local char = n.char +    for n in traverse_id(glyph_code,tonut(head)) do +        local char = getchar(n)          if resetter[char] then              inline = false          elseif not inline then -            local a = n[a_cleaner] +            local a = getattr(n,a_cleaner)              if a == 1 then -- currently only one cleaner so no need to be fancy                  local upper = uccodes[char]                  if type(upper) == "table" then                      -- some day, not much change that \SS ends up here                  else -                    n.char = upper +                    setfield(n,"char",upper)                      done = true                      if trace_autocase then                          report_autocase("") diff --git a/tex/context/base/typo-dha.lua b/tex/context/base/typo-dha.lua index d5ad66e7e..15e345ff8 100644 --- a/tex/context/base/typo-dha.lua +++ b/tex/context/base/typo-dha.lua @@ -49,13 +49,30 @@ local trace_directions   = false  trackers.register("typesetters.directions.defa  local report_directions  = logs.reporter("typesetting","text directions") - -local insert_node_before = nodes.insert_before -local insert_node_after  = nodes.insert_after -local remove_node        = nodes.remove -local end_of_math        = nodes.end_of_math - -local nodepool           = nodes.pool +local nuts               = nodes.nuts +local tonut              = nuts.tonut +local tonode             = nuts.tonode +local nutstring          = nuts.tostring + +local getnext            = nuts.getnext +local getprev            = nuts.getprev +local getfont            = nuts.getfont +local getchar            = nuts.getchar +local getid              = nuts.getid +local getsubtype         = nuts.getsubtype +local getlist            = nuts.getlist +local getfield           = nuts.getfield +local setfield           = nuts.setfield +local getattr            = nuts.getattr +local setattr            = nuts.setattr + +local insert_node_before = nuts.insert_before +local insert_node_after  = nuts.insert_after +local remove_node        = nuts.remove +local end_of_math        = nuts.end_of_math + + +local nodepool           = nuts.pool  local nodecodes          = nodes.nodecodes  local whatcodes          = nodes.whatcodes @@ -108,7 +125,7 @@ end  local function process(start) -    local head     = start +    local head     = tonut(start) -- we have a global head      local current  = head      local inserted = nil @@ -180,31 +197,31 @@ local function process(start)      end      local function nextisright(current) -        current = current.next -        local id = current.id +        current = getnext(current) +        local id = getid(current)          if id == glyph_code then -            local character = current.char +            local character = getchar(current)              local direction = chardirections[character]              return direction == "r" or direction == "al" or direction == "an"          end      end      local function previsright(current) -        current = current.prev -        local id = current.id +        current = getprev(current) +        local id = getid(current)          if id == glyph_code then -            local char = current.char +            local character = getchar(current)              local direction = chardirections[character]              return direction == "r" or direction == "al" or direction == "an"          end      end      while current do -        local id = current.id +        local id = getid(current)          if id == math_code then -            current = end_of_math(current.next).next +            current = getnext(end_of_math(getnext(current)))          else -            local attr = current[a_directions] +            local attr = getattr(current,a_directions)              if attr and attr > 0 and attr ~= prevattr then                  if not getglobal(a) then                      lro, rlo = false, false @@ -213,7 +230,7 @@ local function process(start)              end              if id == glyph_code then                  if attr and attr > 0 then -                    local character = current.char +                    local character = getchar(current)                      local direction = chardirections[character]                      local reversed  = false                      if rlo or override > 0 then @@ -223,24 +240,24 @@ local function process(start)                          end                      elseif lro or override < 0 then                          if direction == "r" or direction == "al" then -                            current[a_state] = s_isol +                            setattr(current,a_state,s_isol)                              direction = "l"                              reversed = true                          end                      end                      if direction == "on" then                          local mirror = charmirrors[character] -                        if mirror and fontchar[current.font][mirror] then +                        if mirror and fontchar[getfont(current)][mirror] then                              local class = charclasses[character]                              if class == "open" then                                  if nextisright(current) then                                      if autodir >= 0 then                                          force_auto_right_before(direction)                                      end -                                    current.char = mirror +                                    setfield(current,"char",mirror)                                      done = true                                  elseif autodir < 0 then -                                    current.char = mirror +                                    setfield(current,"char",mirror)                                      done = true                                  else                                      mirror = false @@ -251,14 +268,14 @@ local function process(start)                                  local fencedir = fences[#fences]                                  fences[#fences] = nil                                  if fencedir < 0 then -                                    current.char = mirror +                                    setfield(current,"char",mirror)                                      done = true                                      force_auto_right_before(direction)                                  else                                      mirror = false                                  end                              elseif autodir < 0 then -                                current.char = mirror +                                setfield(current,"char",mirror)                                  done = true                              else                                  mirror = false @@ -336,9 +353,9 @@ local function process(start)                      -- we do nothing                  end              elseif id == whatsit_code then -                local subtype = current.subtype +                local subtype = getsubtype(current)                  if subtype == localpar_code then -                    local dir = current.dir +                    local dir = getfield(current,"dir")                      if dir == 'TRT' then                          autodir = -1                      elseif dir == 'TLT' then @@ -351,7 +368,7 @@ local function process(start)                      if finish then                          finish_auto_before()                      end -                    local dir = current.dir +                    local dir = getfield(current,"dir")                      if dir == "+TRT" then                          finish, autodir = "TRT", -1                      elseif dir == "-TRT" then @@ -370,7 +387,7 @@ local function process(start)              elseif finish then                  finish_auto_before()              end -            local cn = current.next +            local cn = getnext(current)              if cn then                  -- we're okay              elseif finish then @@ -390,7 +407,7 @@ local function process(start)          end      end -    return head, done +    return tonode(head), done  end diff --git a/tex/context/base/typo-dig.lua b/tex/context/base/typo-dig.lua index ef05e62da..67849c6d4 100644 --- a/tex/context/base/typo-dig.lua +++ b/tex/context/base/typo-dig.lua @@ -19,10 +19,24 @@ local report_digits = logs.reporter("typesetting","digits")  local nodes, node = nodes, node -local hpack_node         = node.hpack -local traverse_id        = node.traverse_id -local insert_node_before = node.insert_before -local insert_node_after  = node.insert_after +local nuts               = nodes.nuts +local tonut              = nuts.tonut +local tonode             = nuts.tonode + +local getnext            = nuts.getnext +local getprev            = nuts.getprev +local getfont            = nuts.getfont +local getchar            = nuts.getchar +local getid              = nuts.getid +local getfield           = nuts.getfield +local setfield           = nuts.setfield +local getattr            = nuts.getattr +local setattr            = nuts.setattr + +local hpack_node         = nuts.hpack +local traverse_id        = nuts.traverse_id +local insert_node_before = nuts.insert_before +local insert_node_after  = nuts.insert_after  local texsetattribute    = tex.setattribute  local unsetvalue         = attributes.unsetvalue @@ -30,7 +44,7 @@ local unsetvalue         = attributes.unsetvalue  local nodecodes          = nodes.nodecodes  local glyph_code         = nodecodes.glyph -local nodepool           = nodes.pool +local nodepool           = nuts.pool  local tasks              = nodes.tasks  local new_glue           = nodepool.glue @@ -66,16 +80,20 @@ function nodes.aligned(head,start,stop,width,how)      if how == "flushleft" or how == "middle" then          head, stop = insert_node_after(head,stop,new_glue(0,65536,65536))      end -    local prv, nxt = start.prev, stop.next -    start.prev, stop.next = nil, nil +    local prv = getprev(start) +    local nxt = getnext(stop) +    setfield(start,"prev",nil) +    setfield(stop,"next",nil)      local packed = hpack_node(start,width,"exactly") -- no directional mess here, just lr      if prv then -        prv.next, packed.prev = packed, prv +        setfield(prv,"next",packed) +        setfield(packed,"prev",prv)      end      if nxt then -        nxt.prev, packed.next = packed, nxt +        setfield(nxt,"prev",packed) +        setfield(packed,"next",nxt)      end -    if packed.prev then +    if getprev(packed) then          return head, packed      else          return packed, packed @@ -83,12 +101,13 @@ function nodes.aligned(head,start,stop,width,how)  end  actions[1] = function(head,start,attr) -    local font = start.font -    local char = start.char +    local font = getfont(start) +    local char = getchar(start)      local unic = chardata[font][char].tounicode      local what = unic and tonumber(unic,16) or char      if charbase[what].category == "nd" then -        local oldwidth, newwidth = start.width, getdigitwidth(font) +        local oldwidth = getfield(start,"width") +        local newwidth = getdigitwidth(font)          if newwidth ~= oldwidth then              if trace_digits then                  report_digits("digit trigger %a, instance %a, char %C, unicode %U, delta %s", @@ -102,12 +121,13 @@ actions[1] = function(head,start,attr)  end  function digits.handler(head) +    head = tonut(head)      local done, current, ok = false, head, false      while current do -        if current.id == glyph_code then -            local attr = current[a_digits] +        if getid(current) == glyph_code then +            local attr = getattr(current,a_digits)              if attr and attr > 0 then -                current[a_digits] = unsetvalue +                setattr(current,a_digits,unsetvalue)                  local action = actions[attr%100] -- map back to low number                  if action then                      head, current, ok = action(head,current,attr) @@ -117,9 +137,11 @@ function digits.handler(head)                  end              end          end -        current = current and current.next +        if current then +            current = getnext(current) +        end      end -    return head, done +    return tonode(head), done  end  local m, enabled = 0, false -- a trick to make neighbouring ranges work diff --git a/tex/context/base/typo-dir.lua b/tex/context/base/typo-dir.lua index a04028452..fbca0f024 100644 --- a/tex/context/base/typo-dir.lua +++ b/tex/context/base/typo-dir.lua @@ -40,21 +40,35 @@ local trace_directions     = false  trackers.register("typesetters.directions",  local report_textdirections = logs.reporter("typesetting","text directions")  local report_mathdirections = logs.reporter("typesetting","math directions") +local nuts               = nodes.nuts +local tonut              = nuts.tonut +local tonode             = nuts.tonode +local nutstring          = nuts.tostring + +local getnext            = nuts.getnext +local getprev            = nuts.getprev +local getfont            = nuts.getfont +local getchar            = nuts.getchar +local getid              = nuts.getid +local getsubtype         = nuts.getsubtype +local getlist            = nuts.getlist +local getfield           = nuts.getfield +local setfield           = nuts.setfield +local getattr            = nuts.getattr +local setattr            = nuts.setattr +local hasbit             = number.hasbit - -local traverse_id        = node.traverse_id -local insert_node_before = node.insert_before -local insert_node_after  = node.insert_after -local remove_node        = nodes.remove -local end_of_math        = nodes.end_of_math +local traverse_id        = nuts.traverse_id +local insert_node_before = nuts.insert_before +local insert_node_after  = nuts.insert_after +local remove_node        = nuts.remove +local end_of_math        = nuts.end_of_math  local texsetattribute    = tex.setattribute  local texsetcount        = tex.setcount  local unsetvalue         = attributes.unsetvalue -local hasbit             = number.hasbit -  local nodecodes          = nodes.nodecodes  local whatcodes          = nodes.whatcodes  local mathcodes          = nodes.mathcodes @@ -76,7 +90,7 @@ local vlist_code         = nodecodes.vlist  local localpar_code      = whatcodes.localpar  local dir_code           = whatcodes.dir -local nodepool           = nodes.pool +local nodepool           = nuts.pool  local new_textdir        = nodepool.textdir diff --git a/tex/context/base/typo-drp.lua b/tex/context/base/typo-drp.lua index 903140dae..b3f840ae1 100644 --- a/tex/context/base/typo-drp.lua +++ b/tex/context/base/typo-drp.lua @@ -24,15 +24,31 @@ typesetters.initials    = initials or { }  local nodes             = nodes  local tasks             = nodes.tasks -local hpack_nodes       = nodes.hpack +local nuts              = nodes.nuts +local tonut             = nuts.tonut +local tonode            = nuts.tonode + +local getnext           = nuts.getnext +local getprev           = nuts.getprev +local getchar           = nuts.getchar +local getid             = nuts.getid +local getsubtype        = nuts.getsubtype +local getfield          = nuts.getfield +local getattr           = nuts.getattr + +local setfield          = nuts.setfield +local setattr           = nuts.setattr + +local hpack_nodes       = nuts.hpack +  local nodecodes         = nodes.nodecodes  local whatsitcodes      = nodes.whatsitcodes -local nodepool          = nodes.pool +local nodepool          = nuts.pool  local new_kern          = nodepool.kern -local insert_before     = nodes.insert_before -local insert_after      = nodes.insert_after +local insert_before     = nuts.insert_before +local insert_after      = nuts.insert_after  local variables         = interfaces.variables  local v_default         = variables.default @@ -86,48 +102,48 @@ commands.setinitial = initials.set  actions[v_default] = function(head,setting)      local done = false -    if head.id == whatsit_code and head.subtype == localpar_code then +    if getid(head) == whatsit_code and getsubtype(head) == localpar_code then          -- begin of par -        local first = head.next +        local first = getnext(head)          -- parbox .. needs to be set at 0 -        if first and first.id == hlist_code then -            first = first.next +        if first and getid(first) == hlist_code then +            first = getnext(first)          end          -- we need to skip over kerns and glues (signals) -        while first and first.id ~= glyph_code do -            first = first.next +        while first and getid(first) ~= glyph_code do +            first = getnext(first)          end -        if first and first.id == glyph_code then -            local char = first.char -            local prev = first.prev -            local next = first.next -         -- if prev.id == hlist_code then +        if first and getid(first) == glyph_code then +            local char = getchar(first) +            local prev = getprev(first) +            local next = getnext(first) +         -- if getid(prev) == hlist_code then           --     -- set the width to 0           -- end -            if next and next.id == kern_node then -                next.kern = 0 +            if next and getid(next) == kern_node then +                setfield(next,"kern",0)              end              if setting.font then -                first.font = setting.font +                setfield(first,"font",setting.font)              end              if setting.dynamic > 0 then -                first[0] = setting.dynamic +                setattr(first,0,setting.dynamic)              end              -- can be a helper              local ma = setting.ma or 0              local ca = setting.ca              local ta = setting.ta              if ca and ca > 0 then -                first[a_colorspace] = ma == 0 and 1 or ma -                first[a_color] = ca +                setattr(first,a_colorspace,ma == 0 and 1 or ma) +                setattr(first,a_color,ca)              end              if ta and ta > 0 then -                first[a_transparency] = ta +                setattr(first,a_transparency,ta)              end              -- -            local width     = first.width -            local height    = first.height -            local depth     = first.depth +            local width     = getfield(first,"width") +            local height    = getfield(first,"height") +            local depth     = getfield(first,"depth")              local distance  = setting.distance or 0              local voffset   = setting.voffset or 0              local hoffset   = setting.hoffset or 0 @@ -135,22 +151,22 @@ actions[v_default] = function(head,setting)              local baseline  = texget("baselineskip").width              local lines     = tonumber(setting.n) or 0              -- -            first.xoffset   = - width  - hoffset - distance - parindent -            first.yoffset   = - voffset -- no longer - height here  +            setfield(first,"xoffset",- width  - hoffset - distance - parindent) +            setfield(first,"yoffset",- voffset) -- no longer - height here              -- We pack so that successive handling cannot touch the dropped cap. Packaging              -- in a hlist is also needed because we cannot locally adapt e.g. parindent (not              -- yet stored in with localpar). -            first.prev = nil -            first.next = nil +            setfield(first,"prev",nil) +            setfield(first,"next",nil)              local h = hpack_nodes(first) -            h.width = 0 -            h.height = 0 -            h.depth = 0 -            prev.next = h -            next.prev = h -            h.next = next -            h.prev = prev - +            setfield(h,"width",0) +            setfield(h,"height",0) +            setfield(h,"depth",0) +            setfield(prev,"next",h) +            setfield(next,"prev",h) +            setfield(h,"next",next) +            setfield(h,"prev",prev) +            first = h              -- end of packaging              if setting.location == v_margin then                  -- okay @@ -178,16 +194,17 @@ actions[v_default] = function(head,setting)  end  function initials.handler(head) +    head = tonut(head)      local start = head      local attr  = nil      while start do -        attr = start[a_initial] +        attr = getattr(start,a_initial)          if attr then              break -        elseif start.id == glyph then +        elseif getid(start) == glyph then              break          else -            start = start.next +            start = getnext(start)          end      end      if attr then @@ -201,8 +218,8 @@ function initials.handler(head)                  report_initials("processing initials, alternative %a",alternative)              end              local head, done = action(head,settings) -            return head, done +            return tonode(head), done          end      end -    return head, false +    return tonode(head), false  end diff --git a/tex/context/base/typo-dua.lua b/tex/context/base/typo-dua.lua index ec85a3d9f..91a27a30e 100644 --- a/tex/context/base/typo-dua.lua +++ b/tex/context/base/typo-dua.lua @@ -66,11 +66,24 @@ local formatters = string.formatters  local directiondata       = characters.directions  local mirrordata          = characters.mirrors -local remove_node         = nodes.remove -local insert_node_after   = nodes.insert_after -local insert_node_before  = nodes.insert_before - -local nodepool            = nodes.pool +local nuts                = nodes.nuts +local tonut               = nuts.tonut +local tonode              = nuts.tonode +local nutstring           = nuts.tostring + +local getnext             = nuts.getnext +local getchar             = nuts.getchar +local getid               = nuts.getid +local getsubtype          = nuts.getsubtype +local getlist             = nuts.getlist +local getfield            = nuts.getfield +local setfield            = nuts.setfield + +local remove_node         = nuts.remove +local insert_node_after   = nuts.insert_after +local insert_node_before  = nuts.insert_before + +local nodepool            = nuts.pool  local new_textdir         = nodepool.textdir  local nodecodes           = nodes.nodecodes @@ -189,17 +202,17 @@ local function build_list(head) -- todo: store node pointer ... saves loop      local size    = 0      while current do          size = size + 1 -        local id = current.id +        local id = getid(current)          if id == glyph_code then -            local chr = current.char +            local chr = getchar(current)              local dir = directiondata[chr]              list[size] = { char = chr, direction = dir, original = dir, level = 0 } -            current = current.next +            current = getnext(current)          elseif id == glue_code then              list[size] = { char = 0x0020, direction = "ws", original = "ws", level = 0 } -            current = current.next -        elseif id == whatsit_code and current.subtype == dir_code then -            local dir = current.dir +            current = getnext(current) +        elseif id == whatsit_code and getsubtype(current) == dir_code then +            local dir = getfield(current,"dir")              if dir == "+TLT" then                  list[size] = { char = 0x202A, direction = "lre", original = "lre", level = 0 }              elseif dir == "+TRT" then @@ -209,27 +222,27 @@ local function build_list(head) -- todo: store node pointer ... saves loop              else                  list[size] = { char = 0xFFFC, direction = "on", original = "on", level = 0, id = id } -- object replacement character              end -            current = current.next +            current = getnext(current)          elseif id == math_code then              local skip = 0 -            current    = current.next -            while current.id ~= math_code do +            current    = getnext(current) +            while getid(current) ~= math_code do                  skip    = skip + 1 -                current = current.next +                current = getnext(current)              end -            skip    = skip + 1 -            current = current.next +            skip       = skip + 1 +            current    = getnext(current)              list[size] = { char = 0xFFFC, direction = "on", original = "on", level = 0, skip = skip, id = id }          else              local skip = 0              local last = id -            current    = current.next +            current    = getnext(current)              while n do -                local id = current.id -                if id ~= glyph_code and id ~= glue_code and not (id == whatsit_code and current.subtype == dir_code) then +                local id = getid(current) +                if id ~= glyph_code and id ~= glue_code and not (id == whatsit_code and getsubtype(current) == dir_code) then                      skip    = skip + 1                      last    = id -                    current = current.next +                    current = getnext(current)                  else                      break                  end @@ -289,8 +302,8 @@ local function find_run_limit_b_s_ws_on(list,start,limit)  end  local function get_baselevel(head,list,size) -- todo: skip if first is object (or pass head and test for local_par) -    if head.id == whatsit_code and head.subtype == localpar_code then -        if head.dir == "TRT" then +    if getid(head) == whatsit_code and getsubtype(head) == localpar_code then +        if getfield(head,"dir") == "TRT" then              return 1, "TRT", true          else              return 0, "TLT", true @@ -677,30 +690,30 @@ local function apply_to_list(list,size,head,pardir)              report_directions("fatal error, size mismatch")              break          end -        local id       = current.id +        local id       = getid(current)          local entry    = list[index]          local begindir = entry.begindir          local enddir   = entry.enddir          if id == glyph_code then              local mirror = entry.mirror              if mirror then -                current.char = mirror +                setfield(current,"char",mirror)              end              if trace_directions then                  local direction = entry.direction                  setcolor(current,direction,direction ~= entry.original,mirror)              end          elseif id == hlist_code or id == vlist_code then -            current.dir = pardir -- is this really needed? +            setfield(current,"dir",pardir) -- is this really needed?          elseif id == glue_code then -            if enddir and current.subtype == parfillskip_code then +            if enddir and getsubtype(current) == parfillskip_code then                  -- insert the last enddir before \parfillskip glue                  head = insert_node_before(head,current,new_textdir(enddir))                  enddir = false                  done = true              end          elseif id == whatsit_code then -            if begindir and current.subtype == localpar_code then +            if begindir and getsubtype(current) == localpar_code then                  -- local_par should always be the 1st node                  head, current = insert_node_after(head,current,new_textdir(begindir))                  begindir = nil @@ -714,7 +727,7 @@ local function apply_to_list(list,size,head,pardir)          local skip = entry.skip          if skip and skip > 0 then              for i=1,skip do -                current = current.next +                current = getnext(current)              end          end          if enddir then @@ -722,13 +735,13 @@ local function apply_to_list(list,size,head,pardir)              done = true          end          if not entry.remove then -            current = current.next +            current = getnext(current)          elseif remove_controls then              -- X9              head, current = remove_node(head,current,true)              done = true          else -            current = current.next +            current = getnext(current)          end          index = index + 1      end @@ -736,6 +749,7 @@ local function apply_to_list(list,size,head,pardir)  end  local function process(head) +    head = tonut(head)      local list, size = build_list(head)      local baselevel, pardir, dirfound = get_baselevel(head,list,size) -- we always have an inline dir node in context      if not dirfound and trace_details then @@ -752,7 +766,7 @@ local function process(head)          report_directions("result : %s",show_done(list,size))      end      head, done = apply_to_list(list,size,head,pardir) -    return head, done +    return tonode(head), done  end  directions.installhandler(interfaces.variables.one,process) diff --git a/tex/context/base/typo-dub.lua b/tex/context/base/typo-dub.lua index 3ecfce364..4dc0f21fb 100644 --- a/tex/context/base/typo-dub.lua +++ b/tex/context/base/typo-dub.lua @@ -54,11 +54,25 @@ local directiondata       = characters.directions  local mirrordata          = characters.mirrors  local textclassdata       = characters.textclasses -local remove_node         = nodes.remove -local insert_node_after   = nodes.insert_after -local insert_node_before  = nodes.insert_before - -local nodepool            = nodes.pool +local nuts                = nodes.nuts +local tonut               = nuts.tonut +local tonode              = nuts.tonode +local nutstring           = nuts.tostring + +local getnext             = nuts.getnext +local getchar             = nuts.getchar +local getid               = nuts.getid +local getsubtype          = nuts.getsubtype +local getlist             = nuts.getlist +local getattr             = nuts.getattr +local getfield            = nuts.getfield +local setfield            = nuts.setfield + +local remove_node         = nuts.remove +local insert_node_after   = nuts.insert_after +local insert_node_before  = nuts.insert_before + +local nodepool            = nuts.pool  local new_textdir         = nodepool.textdir  local nodecodes           = nodes.nodecodes @@ -242,17 +256,17 @@ local function build_list(head) -- todo: store node pointer ... saves loop      local size    = 0      while current do          size = size + 1 -        local id = current.id +        local id = getid(current)          if id == glyph_code then -            local chr = current.char +            local chr = getchar(current)              local dir = directiondata[chr]              list[size] = { char = chr, direction = dir, original = dir, level = 0 } -            current = current.next +            current = getnext(current)          elseif id == glue_code then              list[size] = { char = 0x0020, direction = "ws", original = "ws", level = 0 } -            current = current.next -        elseif id == whatsit_code and current.subtype == dir_code then -            local dir = current.dir +            current = getnext(current) +        elseif id == whatsit_code and getsubtype(current) == dir_code then +            local dir = getfield(current,"dir")              if dir == "+TLT" then                  list[size] = { char = 0x202A, direction = "lre", original = "lre", level = 0 }              elseif dir == "+TRT" then @@ -262,27 +276,27 @@ local function build_list(head) -- todo: store node pointer ... saves loop              else                  list[size] = { char = 0xFFFC, direction = "on", original = "on", level = 0, id = id } -- object replacement character              end -            current = current.next +            current = getnext(current)          elseif id == math_code then              local skip = 0 -            current = current.next -            while current.id ~= math_code do +            current    = getnext(current) +            while getid(current) ~= math_code do                  skip    = skip + 1 -                current = current.next +                current = getnext(current)              end              skip       = skip + 1 -            current    = current.next +            current    = getnext(current)              list[size] = { char = 0xFFFC, direction = "on", original = "on", level = 0, skip = skip, id = id }          else              local skip = 0              local last = id -            current    = current.next +            current    = getnext(current)              while n do -                local id = current.id -                if id ~= glyph_code and id ~= glue_code and not (id == whatsit_code and current.subtype == dir_code) then +                local id = getid(current) +                if id ~= glyph_code and id ~= glue_code and not (id == whatsit_code and getsubtype(current) == dir_code) then                      skip    = skip + 1                      last    = id -                    current = current.next +                    current = getnext(current)                  else                      break                  end @@ -365,8 +379,8 @@ end  -- the action  local function get_baselevel(head,list,size) -- todo: skip if first is object (or pass head and test for local_par) -	if head.id == whatsit_code and head.subtype == localpar_code then -        if head.dir == "TRT" then +    if getid(head) == whatsit_code and getsubtype(head) == localpar_code then +        if getfield(head,"dir") == "TRT" then              return 1, "TRT", true          else              return 0, "TLT", true @@ -785,30 +799,30 @@ local function apply_to_list(list,size,head,pardir)              report_directions("fatal error, size mismatch")              break          end -        local id       = current.id +        local id       = getid(current)          local entry    = list[index]          local begindir = entry.begindir          local enddir   = entry.enddir          if id == glyph_code then              local mirror = entry.mirror              if mirror then -                current.char = mirror +                setfield(current,"char",mirror)              end              if trace_directions then                  local direction = entry.direction                  setcolor(current,direction,direction ~= entry.original,mirror)              end          elseif id == hlist_code or id == vlist_code then -            current.dir = pardir -- is this really needed? +            setfield(current,"dir",pardir) -- is this really needed?          elseif id == glue_code then -            if enddir and current.subtype == parfillskip_code then +            if enddir and getsubtype(current) == parfillskip_code then                  -- insert the last enddir before \parfillskip glue                  head = insert_node_before(head,current,new_textdir(enddir))                  enddir = false                  done = true              end          elseif id == whatsit_code then -            if begindir and current.subtype == localpar_code then +            if begindir and getsubtype(current) == localpar_code then                  -- local_par should always be the 1st node                  head, current = insert_node_after(head,current,new_textdir(begindir))                  begindir = nil @@ -822,7 +836,7 @@ local function apply_to_list(list,size,head,pardir)          local skip = entry.skip          if skip and skip > 0 then              for i=1,skip do -                current = current.next +                current = getnext(current)              end          end          if enddir then @@ -830,13 +844,13 @@ local function apply_to_list(list,size,head,pardir)              done = true          end          if not entry.remove then -            current = current.next +            current = getnext(current)          elseif remove_controls then              -- X9              head, current = remove_node(head,current,true)              done = true          else -            current = current.next +            current = getnext(current)          end          index = index + 1      end @@ -844,8 +858,9 @@ local function apply_to_list(list,size,head,pardir)  end  local function process(head) +    head = tonut(head)      -- for the moment a whole paragraph property -    local attr = head[a_directions] +    local attr = getattr(head,a_directions)      local analyze_fences = getfences(attr)      --      local list, size = build_list(head) @@ -864,7 +879,7 @@ local function process(head)          report_directions("result : %s",show_done(list,size))      end      head, done = apply_to_list(list,size,head,pardir) -    return head, done +    return tonode(head), done  end  directions.installhandler(interfaces.variables.two,process) diff --git a/tex/context/base/typo-fln.lua b/tex/context/base/typo-fln.lua index 4c97af450..7ce41cd81 100644 --- a/tex/context/base/typo-fln.lua +++ b/tex/context/base/typo-fln.lua @@ -23,25 +23,38 @@ local firstlines         = typesetters.firstlines  local nodes              = nodes  local tasks              = nodes.tasks -local getbox             = nodes.getbox +local nuts               = nodes.nuts +local tonut              = nuts.tonut +local tonode             = nuts.tonode + +local getnext            = nuts.getnext +local getid              = nuts.getid +local getfield           = nuts.getfield +local getlist            = nuts.getlist +local getattr            = nuts.getattr +local getbox             = nuts.getbox + +local setfield           = nuts.setfield +local setattr            = nuts.setattr +  local nodecodes          = nodes.nodecodes  local glyph_code         = nodecodes.glyph  local disc_code          = nodecodes.disc  local kern_code          = nodecodes.kern -local traverse_id        = nodes.traverse_id -local free_node_list     = nodes.flush_list -local free_node          = nodes.flush_node -local copy_node_list     = nodes.copy_list -local insert_node_after  = nodes.insert_after -local insert_node_before = nodes.insert_before -local hpack_node_list    = nodes.hpack -local remove_node        = nodes.remove +local traverse_id        = nuts.traverse_id +local free_node_list     = nuts.flush_list +local free_node          = nuts.flush_node +local copy_node_list     = nuts.copy_list +local insert_node_after  = nuts.insert_after +local insert_node_before = nuts.insert_before +local hpack_node_list    = nuts.hpack +local remove_node        = nuts.remove -local nodepool           = nodes.pool +local nodepool           = nuts.pool  local newpenalty         = nodepool.penalty  local newkern            = nodepool.kern -local tracerrule         = nodes.tracers.pool.nodes.rule +local tracerrule         = nodes.tracers.pool.nuts.rule  local actions            = { }  firstlines.actions       = actions @@ -92,9 +105,9 @@ actions[v_line] = function(head,setting)      local linebreaks = { }      for g in traverse_id(glyph_code,temp) do          if dynamic > 0 then -            g[0] = dynamic +            setattr(g,0,dynamic)          end -        g.font = font +        setfield(g,"font",font)      end      local start = temp      local list  = temp @@ -108,7 +121,7 @@ actions[v_line] = function(head,setting)              hsize = hsize - hangindent          end          while start do -            local id = start.id +            local id = getid(start)              if id == glyph_code then                  n = n + 1              elseif id == disc_code then @@ -117,7 +130,7 @@ actions[v_line] = function(head,setting)                  -- this could be an option              elseif n > 0 then                  local pack = hpack_node_list(copy_node_list(list,start)) -                if pack.width > hsize then +                if getfield(pack,"width") > hsize then                      free_node_list(pack)                      list = prev                      break @@ -128,7 +141,7 @@ actions[v_line] = function(head,setting)                      nofchars = n                  end              end -            start = start.next +            start = getnext(start)          end          if not linebreaks[i] then              linebreaks[i] = n @@ -139,18 +152,18 @@ actions[v_line] = function(head,setting)      for i=1,noflines do          local linebreak = linebreaks[i]          while start and n < nofchars do -            local id = start.id +            local id = getid(start)              if id == glyph_code then -- or id == disc_code then                  if dynamic > 0 then -                    start[0] = dynamic +                    setattr(start,0,dynamic)                  end -                start.font = font +                setfield(start,"font",font)                  if ca and ca > 0 then -                    start[a_colorspace] = ma == 0 and 1 or ma -                    start[a_color]      = ca +                    setattr(start,a_colorspace,ma == 0 and 1 or ma) +                    setattr(start,a_color,ca)                  end                  if ta and ta > 0 then -                    start[a_transparency] = ta +                    setattr(start,a_transparency,ta)                  end                  n = n + 1              end @@ -163,7 +176,7 @@ actions[v_line] = function(head,setting)                  head, start = insert_node_after(head,start,newpenalty(-10000)) -- break                  break              end -            start = start.next +            start = getnext(start)          end      end      free_node_list(temp) @@ -182,7 +195,7 @@ actions[v_word] = function(head,setting)      local ca       = setting.ca      local ta       = setting.ta      while start do -        local id = start.id +        local id = getid(start)          -- todo: delete disc nodes          if id == glyph_code then              if not ok then @@ -190,16 +203,16 @@ actions[v_word] = function(head,setting)                  ok = true              end              if ca and ca > 0 then -                start[a_colorspace] = ma == 0 and 1 or ma -                start[a_color]      = ca +                setattr(start,a_colorspace,ma == 0 and 1 or ma) +                setattr(start,a_color,ca)              end              if ta and ta > 0 then -                start[a_transparency] = ta +                setattr(start,a_transparency,ta)              end              if dynamic > 0 then -                start[0] = dynamic +                setattr(start,0,dynamic)              end -            start.font = font +            setfield(start,"font",font)          elseif id == disc_code then              -- continue          elseif id == kern_code then -- todo: fontkern @@ -210,7 +223,7 @@ actions[v_word] = function(head,setting)                  break              end          end -        start = start.next +        start = getnext(start)      end      return head, true  end @@ -218,16 +231,17 @@ end  actions[v_default] = actions[v_line]  function firstlines.handler(head) +    head = tonut(head)      local start = head      local attr  = nil      while start do -        attr = start[a_firstline] +        attr = getattr(start,a_firstline)          if attr then              break -        elseif start.id == glyph then +        elseif getid(start) == glyph_code then              break          else -            start = start.next +            start = getnext(start)          end      end      if attr then @@ -240,17 +254,18 @@ function firstlines.handler(head)              if trace_firstlines then                  report_firstlines("processing firstlines, alternative %a",alternative)              end -            return action(head,settings) +            local head, done = action(head,settings) +            return tonode(head), done          end      end -    return head, false +    return tonode(head), false  end  -- goodie  function commands.applytofirstcharacter(box,what)      local tbox = getbox(box) -- assumes hlist -    local list = tbox.list +    local list = getlist(tbox)      local done = nil      for n in traverse_id(glyph_code,list) do          list = remove_node(list,n) @@ -258,10 +273,10 @@ function commands.applytofirstcharacter(box,what)          break      end      if done then -        tbox.list = list +        setfield(tbox,"list",list)          local kind = type(what)          if kind == "string" then -            context[what](done) +            context[what](tonode(done))          elseif kind == "function" then              what(done)          else diff --git a/tex/context/base/typo-itc.lua b/tex/context/base/typo-itc.lua index 452b623c8..997190675 100644 --- a/tex/context/base/typo-itc.lua +++ b/tex/context/base/typo-itc.lua @@ -24,17 +24,30 @@ local math_code           = nodecodes.math  local tasks               = nodes.tasks -local insert_node_after   = node.insert_after -local delete_node         = nodes.delete -local end_of_math         = node.end_of_math +local nuts                = nodes.nuts +local nodepool            = nuts.pool + +local tonode              = nuts.tonode +local tonut               = nuts.tonut + +local getfield            = nuts.getfield +local getnext             = nuts.getnext +local getid               = nuts.getid +local getfont             = nuts.getfont +local getchar             = nuts.getchar +local getattr             = nuts.getattr + +local insert_node_after   = nuts.insert_after +local delete_node         = nuts.delete +local end_of_math         = nuts.end_of_math  local texgetattribute     = tex.getattribute  local texsetattribute     = tex.setattribute  local a_italics           = attributes.private("italics")  local unsetvalue          = attributes.unsetvalue -local new_correction_kern = nodes.pool.fontkern -local new_correction_glue = nodes.pool.glue +local new_correction_kern = nodepool.fontkern +local new_correction_glue = nodepool.glue  local fonthashes          = fonts.hashes  local fontdata            = fonthashes.identifiers @@ -83,6 +96,7 @@ end  -- todo: clear attribute  function italics.handler(head) +    head = tonut(head)      local done     = false      local italic   = 0      local lastfont = nil @@ -92,10 +106,10 @@ function italics.handler(head)      local current  = head      local inserted = nil      while current do -        local id = current.id +        local id = getid(current)          if id == glyph_code then -            local font = current.font -            local char = current.char +            local font = getfont(current) +            local char = getchar(current)              local data = italicsdata[font]              if font ~= lastfont then                  if italic ~= 0 then @@ -121,7 +135,7 @@ function italics.handler(head)                  lastfont = font              end              if data then -                local attr = forcedvariant or current[a_italics] +                local attr = forcedvariant or getattr(current,a_italics)                  if attr and attr > 0 then                      local cd = data[char]                      if not cd then @@ -173,7 +187,7 @@ function italics.handler(head)              italic = 0              done = true          end -        current = current.next +        current = getnext(current)      end      if italic ~= 0 and lastattr > 1 then -- more control is needed here          if trace_italics then @@ -182,7 +196,7 @@ function italics.handler(head)          insert_node_after(head,previous,new_correction_kern(italic))          done = true      end -    return head, done +    return tonode(head), done  end  local enable diff --git a/tex/context/base/typo-krn.lua b/tex/context/base/typo-krn.lua index 56f58bb73..a8ffe557b 100644 --- a/tex/context/base/typo-krn.lua +++ b/tex/context/base/typo-krn.lua @@ -13,21 +13,36 @@ local utfchar = utf.char  local nodes, node, fonts = nodes, node, fonts -local find_node_tail     = node.tail or node.slide -local free_node          = node.free -local free_nodelist      = node.flush_list -local copy_node          = node.copy -local copy_nodelist      = node.copy_list -local insert_node_before = node.insert_before -local insert_node_after  = node.insert_after -local end_of_math        = node.end_of_math +local tasks              = nodes.tasks +local nuts               = nodes.nuts +local nodepool           = nuts.pool + +local tonode             = nuts.tonode +local tonut              = nuts.tonut + +local find_node_tail     = nuts.tail +local free_node          = nuts.free +local free_nodelist      = nuts.flush_list +local copy_node          = nuts.copy +local copy_nodelist      = nuts.copy_list +local insert_node_before = nuts.insert_before +local insert_node_after  = nuts.insert_after +local end_of_math        = nuts.end_of_math + +local getfield           = nuts.getfield +local setfield           = nuts.setfield +local getnext            = nuts.getnext +local getprev            = nuts.getprev +local getid              = nuts.getid +local getattr            = nuts.getattr +local setattr            = nuts.setattr +local getfont            = nuts.getfont +local getsubtype         = nuts.getsubtype +local getchar            = nuts.getchar  local texsetattribute    = tex.setattribute  local unsetvalue         = attributes.unsetvalue -local nodepool           = nodes.pool -local tasks              = nodes.tasks -  local new_gluespec       = nodepool.gluespec  local new_kern           = nodepool.kern  local new_glue           = nodepool.glue @@ -107,10 +122,10 @@ kerns.keeptogether = false -- just for fun (todo: control setting with key/value  -- blue  : keep by goodie  function kerns.keepligature(n) -- might become default -    local f = n.font -    local a = n[0] or 0 +    local f = getfont(n) +    local a = getattr(n,0) or 0      if trace_ligatures then -        local c = n.char +        local c = getchar(n)          local d = fontdescriptions[f][c].name          if a > 0 and contextsetups[a].keepligatures == v_auto then              report("font %!font:name!, glyph %a, slot %X -> ligature %s, by %s feature %a",f,d,c,"kept","dynamic","keepligatures") @@ -169,9 +184,9 @@ end  local function kern_injector(fillup,kern)      if fillup then          local g = new_glue(kern) -        local s = g.spec -        s.stretch = kern -        s.stretch_order = 1 +        local s = getfield(g,"spec") +        setfield(s,"stretch",kern) +        setfield(s,"stretch_order",1)          return g      else          return new_kern(kern) @@ -181,7 +196,7 @@ end  local function spec_injector(fillup,width,stretch,shrink)      if fillup then          local s = new_gluespec(width,2*stretch,2*shrink) -        s.stretch_order = 1 +        setfield(s,"stretch_order",1)          return s      else          return new_gluespec(width,stretch,shrink) @@ -197,9 +212,9 @@ local function do_process(head,force) -- todo: glue so that we can fully stretch      local fillup = false      while start do          -- faster to test for attr first -        local attr = force or start[a_kerns] +        local attr = force or getattr(start,a_kerns)          if attr and attr > 0 then -            start[a_kerns] = unsetvalue +            setattr(start,a_kerns,unsetvalue)              local krn = mapping[attr]              if krn == v_max then                  krn = .25 @@ -208,10 +223,10 @@ local function do_process(head,force) -- todo: glue so that we can fully stretch                  fillup = false              end              if krn and krn ~= 0 then -                local id = start.id -                if id == glyph_code then -                    lastfont = start.font -                    local c = start.components +                local id = getid(start) +                if id == glyph_code then -- we could use the subtype ligature +                    lastfont = getfont(start) +                    local c = getfield(start,"components")                      if not c then                          -- fine                      elseif keepligature and keepligature(start) then @@ -219,47 +234,47 @@ local function do_process(head,force) -- todo: glue so that we can fully stretch                      else                          c = do_process(c,attr)                          local s = start -                        local p, n = s.prev, s.next +                        local p, n = getprev(s), getnext(s)                          local tail = find_node_tail(c)                          if p then -                            p.next = c -                            c.prev = p +                            setfield(p,"next",c) +                            setfield(c,"prev",p)                          else                              head = c                          end                          if n then -                            n.prev = tail +                            setfield(n,"prev",tail)                          end -                        tail.next = n +                        setfield(tail,"next",n)                          start = c -                        s.components = nil +                        setfield(s,"components",nil)                          -- we now leak nodes ! -                     -- free_node(s) +                    --  free_node(s)                          done = true                      end -                    local prev = start.prev +                    local prev = getprev(start)                      if not prev then                          -- skip -                    elseif markdata[lastfont][start.char] then +                    elseif markdata[lastfont][getchar(start)] then                              -- skip                      else -                        local pid = prev.id +                        local pid = getid(prev)                          if not pid then                              -- nothing                          elseif pid == kern_code then -                            if prev.subtype == kerning_code or prev[a_fontkern] then -                                if keeptogether and prev.prev.id == glyph_code and keeptogether(prev.prev,start) then -- we could also pass start +                            if getsubtype(prev) == kerning_code or getattr(prev,a_fontkern) then +                                if keeptogether and getid(getprev(prev)) == glyph_code and keeptogether(getprev(prev),start) then -- we could also pass start                                      -- keep 'm                                  else                                      -- not yet ok, as injected kerns can be overlays (from node-inj.lua) -                                    prev.subtype = userkern_code -                                    prev.kern = prev.kern + quaddata[lastfont]*krn -- here +                                    setfield(prev,"subtype",userkern_code) +                                    setfield(prev,"kern",getfield(prev,"kern") + quaddata[lastfont]*krn) -- here                                      done = true                                  end                              end                          elseif pid == glyph_code then -                            if prev.font == lastfont then -                                local prevchar, lastchar = prev.char, start.char +                            if getfont(prev) == lastfont then +                                local prevchar, lastchar = getchar(prev), getchar(start)                                  if keeptogether and keeptogether(prev,start) then                                      -- keep 'm                                  else @@ -278,102 +293,102 @@ local function do_process(head,force) -- todo: glue so that we can fully stretch                              -- a bit too complicated, we can best not copy and just calculate                              -- but we could have multiple glyphs involved so ...                              local disc = prev -- disc -                            local prv, nxt = disc.prev, disc.next -                            if disc.subtype == discretionary_code then +                            local prv, nxt = getprev(disc), getnext(disc) +                            if getsubtype(disc) == discretionary_code then                                  -- maybe we should forget about this variant as there is no glue                                  -- possible -                                local pre, post, replace = disc.pre, disc.post, disc.replace -                                if pre and prv then -- must pair with start.prev -                                    -- this one happens in most cases +                                local pre, post, replace = getfield(disc,"pre"), getfield(disc,"post"), getfield(disc,"replace") +                                if pre and prv then -- must pair with getprev(start)                                      local before = copy_node(prv) -                                    pre.prev = before -                                    before.next = pre -                                    before.prev = nil +                                    setfield(pre,"prev",before) +                                    setfield(before,"next",pre) +                                    setfield(before,"prev",nil)                                      pre = do_process(before,attr) -                                    pre = pre.next -                                    pre.prev = nil -                                    disc.pre = pre +                                    pre = getnext(pre) +                                    setfield(pre,"prev",nil) +                                    setfield(disc,"pre",pre)                                      free_node(before)                                  end                                  if post and nxt then  -- must pair with start                                      local after = copy_node(nxt)                                      local tail = find_node_tail(post) -                                    tail.next = after -                                    after.prev = tail -                                    after.next = nil +                                    setfield(tail,"next",after) +                                    setfield(after,"prev",tail) +                                    setfield(after,"next",nil)                                      post = do_process(post,attr) -                                    tail.next = nil -                                    disc.post = post +                                    setfield(tail,"next",nil) +                                    setfield(disc,"post",post)                                      free_node(after)                                  end                                  if replace and prv and nxt then -- must pair with start and start.prev                                      local before = copy_node(prv)                                      local after = copy_node(nxt)                                      local tail = find_node_tail(replace) -                                    replace.prev = before -                                    before.next = replace -                                    before.prev = nil -                                    tail.next = after -                                    after.prev = tail -                                    after.next = nil +                                    setfield(replace,"prev",before) +                                    setfield(before,"next",replace) +                                    setfield(before,"prev",nil) +                                    setfield(tail,"next",after) +                                    setfield(after,"prev",tail) +                                    setfield(after,"next",nil)                                      replace = do_process(before,attr) -                                    replace = replace.next -                                    replace.prev = nil -                                    after.prev.next = nil -                                    disc.replace = replace +                                    replace = getnext(replace) +                                    setfield(replace,"prev",nil) +                                    setfield(getfield(after,"prev"),"next",nil) +                                    setfield(disc,"replace",replace)                                      free_node(after)                                      free_node(before) -                                elseif prv and prv.id == glyph_code and prv.font == lastfont then -                                    local prevchar, lastchar = prv.char, start.char +                                elseif prv and getid(prv) == glyph_code and getfont(prv) == lastfont then +                                    local prevchar, lastchar = getchar(prv), getchar(start)                                      local kerns = chardata[lastfont][prevchar].kerns                                      local kern = kerns and kerns[lastchar] or 0                                      krn = kern + quaddata[lastfont]*krn -- here -                                    disc.replace = kern_injector(false,krn) -- only kerns permitted, no glue +                                    setfield(disc,"replace",kern_injector(false,krn)) -- only kerns permitted, no glue                                  else                                      krn = quaddata[lastfont]*krn -- here -                                    disc.replace = kern_injector(false,krn) -- only kerns permitted, no glue +                                    setfield(disc,"replace",kern_injector(false,krn)) -- only kerns permitted, no glue                                  end                              else                                  -- this one happens in most cases: automatic (-), explicit (\-), regular (patterns) -                                if prv and prv.id == glyph_code and prv.font == lastfont then -                                    local prevchar, lastchar = prv.char, start.char +                                if prv and getid(prv) == glyph_code and getfont(prv) == lastfont then +                                    -- the normal case +                                    local prevchar, lastchar = getchar(prv), getchar(start)                                      local kerns = chardata[lastfont][prevchar].kerns                                      local kern = kerns and kerns[lastchar] or 0 -                                    krn = kern + quaddata[lastfont]*krn -- here +                                    krn = kern + quaddata[lastfont]*krn                                  else -                                    krn = quaddata[lastfont]*krn -- here +                                    krn = quaddata[lastfont]*krn                                  end                                  insert_node_before(head,start,kern_injector(fillup,krn))                              end                          end                      end                  elseif id == glue_code then -                    local subtype = start.subtype +                    local subtype = getsubtype(start)                      if subtype == userskip_code or subtype == xspaceskip_code or subtype == spaceskip_code then -                        local s = start.spec -                        local w = s.width +                        local s = getfield(start,"spec") +                        local w = getfield(s,"width")                          if w > 0 then -                            local width, stretch, shrink = w+gluefactor*w*krn, s.stretch, s.shrink -                            start.spec = spec_injector(fillup,width,stretch*width/w,shrink*width/w) +                            local width, stretch, shrink = w+gluefactor*w*krn, getfield(s,"stretch"), getfield(s,"shrink") +                            setfield(start,"spec",spec_injector(fillup,width,stretch*width/w,shrink*width/w))                              done = true                          end                      end                  elseif id == kern_code then -                 -- if start.subtype == kerning_code then -- handle with glyphs -                 --     local sk = start.kern +                 -- if getsubtype(start) == kerning_code then -- handle with glyphs +                 --     local sk = getfield(start,"kern")                   --     if sk > 0 then -                 --         start.kern = sk*krn +                 --         setfield(start,"kern",sk*krn)                   --         done = true                   --     end                   -- end                  elseif lastfont and (id == hlist_code or id == vlist_code) then -- todo: lookahead -                    local p = start.prev -                    if p and p.id ~= glue_code then +                    local p = getprev(start) +                    if p and getid(p) ~= glue_code then                          insert_node_before(head,start,kern_injector(fillup,quaddata[lastfont]*krn))                          done = true                      end -                    local n = start.next -                    if n and n.id ~= glue_code then +                    local n = getnext(start) +                    if n and getid(n) ~= glue_code then                          insert_node_after(head,start,kern_injector(fillup,quaddata[lastfont]*krn))                          done = true                      end @@ -383,7 +398,7 @@ local function do_process(head,force) -- todo: glue so that we can fully stretch              end          end          if start then -            start = start.next +            start = getnext(start)          end      end      return head, done @@ -414,7 +429,8 @@ function kerns.set(factor)  end  function kerns.handler(head) -    return do_process(head)  -- no direct map, because else fourth argument is tail == true +    local head, done = do_process(tonut(head))  -- no direct map, because else fourth argument is tail == true +    return tonode(head), done  end  -- interface diff --git a/tex/context/base/typo-mar.lua b/tex/context/base/typo-mar.lua index 85d5c85a8..a41a409dd 100644 --- a/tex/context/base/typo-mar.lua +++ b/tex/context/base/typo-mar.lua @@ -115,13 +115,31 @@ local v_first            = variables.first  local v_text             = variables.text  local v_column           = variables.column -local copy_node_list     = node.copy_list -local slide_nodes        = node.slide -local hpack_nodes        = node.hpack -- nodes.fasthpack not really faster here -local traverse_id        = node.traverse_id -local free_node_list     = node.flush_list -local insert_node_after  = node.insert_after -local insert_node_before = node.insert_before +local nuts               = nodes.nuts +local nodepool           = nuts.pool + +local tonode             = nuts.tonode +local tonut              = nuts.tonut + +local copy_node_list     = nuts.copy_list +local slide_nodes        = nuts.slide +local hpack_nodes        = nuts.hpack -- nodes.fasthpack not really faster here +local traverse_id        = nuts.traverse_id +local free_node_list     = nuts.flush_list +local insert_node_after  = nuts.insert_after +local insert_node_before = nuts.insert_before +local linked_nodes       = nuts.linked + +local getfield           = nuts.getfield +local setfield           = nuts.setfield +local getnext            = nuts.getnext +local getprev            = nuts.getprev +local getid              = nuts.getid +local getattr            = nuts.getattr +local setattr            = nuts.setattr +local getsubtype         = nuts.getsubtype +local getbox             = nuts.getbox +local getlist            = nuts.getlist  local nodecodes          = nodes.nodecodes  local listcodes          = nodes.listcodes @@ -144,7 +162,7 @@ local userdefined_code   = whatsitcodes.userdefined  local dir_code           = whatsitcodes.dir  local localpar_code      = whatsitcodes.localpar -local nodepool           = nodes.pool +local nodepool           = nuts.pool  local new_kern           = nodepool.kern  local new_glue           = nodepool.glue @@ -155,13 +173,12 @@ local new_latelua        = nodepool.latelua  local texgetcount        = tex.getcount  local texgetdimen        = tex.getdimen -local texgetbox          = tex.getbox  local texget             = tex.get  local points             = number.points  local isleftpage         = layouts.status.isleftpage -local registertogether   = builders.paragraphs.registertogether +local registertogether   = builders.paragraphs.registertogether -- tonode  local jobpositions       = job.positions  local getposition        = jobpositions.position @@ -170,7 +187,7 @@ local a_margindata       = attributes.private("margindata")  local inline_mark        = nodepool.userids["margins.inline"] -local margins            =  { } +local margins            = { }  typesetters.margins      = margins  local locations          = { v_left, v_right, v_inner, v_outer } -- order might change @@ -242,7 +259,7 @@ end  function margins.save(t)      setmetatable(t,defaults) -    local content  = texgetbox(t.number) +    local content  = getbox(t.number)      local location = t.location      local category = t.category      local inline   = t.inline @@ -310,11 +327,11 @@ function margins.save(t)          -- nice is to make a special status table mechanism          local leftmargindistance  = texgetdimen("naturalleftmargindistance")          local rightmargindistance = texgetdimen("naturalrightmargindistance") -        local strutbox        = texgetbox("strutbox") -        t.strutdepth          = strutbox.depth -        t.strutheight         = strutbox.height -        t.leftskip            = texget("leftskip").width  -- we're not in forgetall -        t.rightskip           = texget("rightskip").width -- we're not in forgetall +        local strutbox        = getbox("strutbox") +        t.strutdepth          = getfield(strutbox,"depth") +        t.strutheight         = getfield(strutbox,"height") +        t.leftskip            = getfield(texget("leftskip"),"width")  -- we're not in forgetall +        t.rightskip           = getfield(texget("rightskip"),"width") -- we're not in forgetall          t.leftmargindistance  = leftmargindistance -- todo:layoutstatus table          t.rightmargindistance = rightmargindistance          t.leftedgedistance    = texgetdimen("naturalleftedgedistance") @@ -404,7 +421,7 @@ local function realign(current,candidate)      -- we assume that list is a hbox, otherwise we had to take the whole current      -- in order to get it right -    current.width = 0 +    setfield(current,"width",0)      local anchornode, move_x      -- this mess is needed for alignments (combinations) so we use that @@ -446,9 +463,9 @@ local function realign(current,candidate)              report_margindata("realigned %a, location %a, margin %a",candidate.n,location,margin)          end      end - -    current.list = hpack_nodes(anchornode .. new_kern(-delta) .. current.list .. new_kern(delta)) -    current.width = 0 +    local list = hpack_nodes(linked_nodes(anchornode,new_kern(-delta),getlist(current),new_kern(delta))) +    setfield(current,"list",list) +    setfield(current,"width",0)  end  local function realigned(current,a) @@ -490,7 +507,8 @@ local function markovershoot(current)      v_anchors = v_anchors + 1      cache[v_anchors] = stacked      local anchor = new_latelua(format("typesetters.margins.ha(%s)",v_anchors)) -- todo: alleen als offset > line -    current.list = hpack_nodes(anchor .. current.list) +    local list = hpack_nodes(linked_nodes(anchor,getlist(current))) +    setfield(current,"list",list)  end  local function getovershoot(location) @@ -512,10 +530,10 @@ end  local function inject(parent,head,candidate)      local box          = candidate.box -    local width        = box.width -    local height       = box.height -    local depth        = box.depth -    local shift        = box.shift +    local width        = getfield(box,"width") +    local height       = getfield(box,"height") +    local depth        = getfield(box,"depth") +    local shift        = getfield(box,"shift")      local stack        = candidate.stack      local location     = candidate.location      local method       = candidate.method @@ -524,7 +542,7 @@ local function inject(parent,head,candidate)      local baseline     = candidate.baseline      local strutheight  = candidate.strutheight      local strutdepth   = candidate.strutdepth -    local psubtype     = parent.subtype +    local psubtype     = getsubtype(parent)      local offset       = stacked[location]      local firstonstack = offset == false or offset == nil      nofstatus          = nofstatus  + 1 @@ -546,7 +564,7 @@ local function inject(parent,head,candidate)          end      end      candidate.width = width -    candidate.hsize = parent.width -- we can also pass textwidth +    candidate.hsize = getfield(parent,"width") -- we can also pass textwidth      candidate.psubtype = psubtype      if trace_margindata then          report_margindata("processing, index %s, height %p, depth %p, parent %s",candidate.n,height,depth,listcodes[psubtype]) @@ -573,7 +591,7 @@ local function inject(parent,head,candidate)      -- experimental.      -- -- --      if method == v_top then -        local delta = height - parent.height +        local delta = height - getfield(parent,"height")          if trace_margindata then              report_margindata("top aligned by %p",delta)          end @@ -616,22 +634,23 @@ local function inject(parent,head,candidate)          shift = shift + delta          offset = offset + delta      end -    box.shift = shift -    box.width = 0 +    setfield(box,"shift",shift) +    setfield(box,"width",0)      if not head then          head = box -    elseif head.id == whatsit_code and head.subtype == localpar_code then +    elseif getid(head) == whatsit_code and getsubtype(head) == localpar_code then          -- experimental -        if head.dir == "TRT" then -            box.list = hpack_nodes(new_kern(candidate.hsize) .. box.list .. new_kern(-candidate.hsize)) +        if getfield(head,"dir") == "TRT" then +            local list = hpack_nodes(linked_nodes(new_kern(candidate.hsize),getlist(box),new_kern(-candidate.hsize))) +            setfield(box,"list",list)          end          insert_node_after(head,head,box)      else -        head.prev = box -        box.next = head +        setfield(head,"prev",box) +        setfield(box,"next",head)          head = box      end -    box[a_margindata] = nofstatus +    setfield(box,a_margindata,nofstatus)      if trace_margindata then          report_margindata("injected, location %a, shift %p",location,shift)      end @@ -656,12 +675,12 @@ local function flushinline(parent,head)      local current = head      local done = false      local continue = false -    local room, don, con +    local room, don, con, list      while current and nofinlined > 0 do -        local id = current.id +        local id = getid(current)          if id == whatsit_code then -            if current.subtype == userdefined_code and current.user_id == inline_mark then -                local n = current.value +            if getsubtype(current) == userdefined_code and getfield(current,"user_id") == inline_mark then +                local n = getfield(current,"value")                  local candidate = inlinestore[n]                  if candidate then -- no vpack, as we want to realign                      inlinestore[n] = nil @@ -674,11 +693,12 @@ local function flushinline(parent,head)              end          elseif id == hlist_code or id == vlist_code then              -- optional (but sometimes needed) -            current.list, don, con = flushinline(current,current.list) +            list, don, con = flushinline(current,getlist(current)) +            setfield(current,"list",list)              continue = continue or con              done = done or don          end -        current = current.next +        current = getnext(current)      end      return head, done, continue  end @@ -686,7 +706,7 @@ end  local a_linenumber = attributes.private('linenumber')  local function flushed(scope,parent) -- current is hlist -    local head = parent.list +    local head = getlist(parent)      local done = false      local continue = false      local room, con, don @@ -702,7 +722,7 @@ local function flushed(scope,parent) -- current is hlist                      done = true                      continue = continue or con                      nofstored = nofstored - 1 -                    registertogether(parent,room) +                    registertogether(tonode(parent),room) -- !! tonode                  else                      break                  end @@ -711,17 +731,18 @@ local function flushed(scope,parent) -- current is hlist      end      if nofinlined > 0 then          if done then -            parent.list = head +            setfield(parent,"list",head)          end          head, don, con = flushinline(parent,head)          continue = continue or con          done = done or don      end      if done then -        local a = head[a_linenumber] -- hack .. we need a more decent critical attribute inheritance mechanism -        parent.list = hpack_nodes(head,parent.width,"exactly") +        local a = getattr(head,a_linenumber) -- hack .. we need a more decent critical attribute inheritance mechanism +        local l = hpack_nodes(head,getfield(parent,"width"),"exactly") +        setfield(parent,"list",l)          if a then -            parent.list[a_linenumber] = a +            setattr(l,a_linenumber,a)          end       -- resetstacked()      end @@ -736,14 +757,15 @@ local function handler(scope,head,group)          if trace_margindata then              report_margindata("flushing stage one, stored %s, scope %s, delayed %s, group %a",nofstored,scope,nofdelayed,group)          end +        head = tonut(head)          local current = head          local done = false          while current do -            local id = current.id -            if (id == vlist_code or id == hlist_code) and not current[a_margindata] then +            local id = getid(current) +            if (id == vlist_code or id == hlist_code) and not getattr(current,a_margindata) then                  local don, continue = flushed(scope,current)                  if don then -                    current[a_margindata] = 0 -- signal to prevent duplicate processing +                    setattr(current,a_margindata,0) -- signal to prevent duplicate processing                      if continue then                          markovershoot(current)                      end @@ -753,12 +775,12 @@ local function handler(scope,head,group)                      done = true                  end              end -            current = current.next +            current = getnext(current)          end       -- if done then          resetstacked() -- why doesn't done work ok here?       -- end -        return head, done +        return tonode(head), done      else          return head, false      end @@ -811,11 +833,11 @@ local function finalhandler(head)          local current = head          local done = false          while current do -            local id = current.id +            local id = getid(current)              if id == hlist_code then -                local a = current[a_margindata] +                local a = getattr(current,a_margindata)                  if not a or a == 0 then -                    finalhandler(current.list) +                    finalhandler(getlist(current))                  elseif realigned(current,a) then                      done = true                      if nofdelayed == 0 then @@ -823,9 +845,9 @@ local function finalhandler(head)                      end                  end              elseif id == vlist_code then -                finalhandler(current.list) +                finalhandler(getlist(current))              end -            current = current.next +            current = getnext(current)          end          return head, done      else @@ -838,7 +860,10 @@ function margins.finalhandler(head)       -- if trace_margindata then       --     report_margindata("flushing stage two, instore: %s, delayed: %s",nofstored,nofdelayed)       -- end -        return finalhandler(head) +head = tonut(head) +local head, done = finalhandler(head) +head = tonode(head) +        return head, done      else          return head, false      end diff --git a/tex/context/base/typo-pag.lua b/tex/context/base/typo-pag.lua index 0dd75ddf9..148eac875 100644 --- a/tex/context/base/typo-pag.lua +++ b/tex/context/base/typo-pag.lua @@ -14,13 +14,23 @@ local glue_code           = nodecodes.glue  local kern_code           = nodecodes.kern  local penalty_code        = nodecodes.penalty -local insert_node_after   = node.insert_after -local new_penalty         = nodes.pool.penalty -  local unsetvalue          = attributes.unsetvalue -  local a_keeptogether      = attributes.private("keeptogether") +local nuts                = nodes.nuts +local tonut               = nuts.tonut + +local getfield            = nuts.getfield +local setfield            = nuts.setfield +local getnext             = nuts.getnext +local getprev             = nuts.getprev +local getid               = nuts.getid +local getattr             = nuts.getattr +local setattr             = nuts.setattr + +local insert_node_after   = nuts.insert_after +local new_penalty         = nuts.pool.penalty +  local trace_keeptogether  = false  local report_keeptogether = logs.reporter("parbuilders","keeptogether") @@ -37,7 +47,7 @@ function builders.paragraphs.registertogether(line,specification) -- might chang      if not enabled then          nodes.tasks.enableaction("finalizers","builders.paragraphs.keeptogether")      end -    local a = line[a_keeptogether] +    local a = getattr(line,a_keeptogether)      local c = a and cache[a]      if c then          local height = specification.height @@ -64,7 +74,7 @@ function builders.paragraphs.registertogether(line,specification) -- might chang          if not specification.slack then              specification.slack = 0          end -        line[a_keeptogether] = last +        setattr(line,a_keeptogether,last)      end      if trace_keeptogether then          local a = a or last @@ -88,24 +98,24 @@ local function keeptogether(start,a)      if start then          local specification = cache[a]          if a then -            local current = start.next +            local current = getnext(start)              local previous = start -            local total = previous.depth +            local total = getfield(previous,"depth")              local slack = specification.slack              local threshold = specification.depth - slack              if trace_keeptogether then                  report_keeptogether("%s, index %s, total %p, threshold %p, slack %p","list",a,total,threshold,slack)              end              while current do -                local id = current.id +                local id = getid(current)                  if id == vlist_code or id == hlist_code then -                    total = total + current.height + current.depth +                    total = total + getfield(current,"height") + getfield(current,"depth")                      if trace_keeptogether then                          report_keeptogether("%s, index %s, total %p, threshold %p","list",a,total,threshold)                      end                      if total <= threshold then -                        if previous.id == penalty_code then -                            previous.penalty = 10000 +                        if getid(previous) == penalty_code then +                            setfield(previous,"penalty",10000)                          else                              insert_node_after(head,previous,new_penalty(10000))                          end @@ -114,13 +124,13 @@ local function keeptogether(start,a)                      end                  elseif id == glue_code then                      -- hm, breakpoint, maybe turn this into kern -                    total = total + current.spec.width +                    total = total + getfield(getfield(current,"spec"),"width")                      if trace_keeptogether then                          report_keeptogether("%s, index %s, total %p, threshold %p","glue",a,total,threshold)                      end                      if total <= threshold then -                        if previous.id == penalty_code then -                            previous.penalty = 10000 +                        if getid(previous) == penalty_code then +                            setfield(previous,"penalty",10000)                          else                              insert_node_after(head,previous,new_penalty(10000))                          end @@ -128,13 +138,13 @@ local function keeptogether(start,a)                          break                      end                  elseif id == kern_code then -                    total = total + current.kern +                    total = total + getfield(current,"kern")                      if trace_keeptogether then                          report_keeptogether("%s, index %s, total %s, threshold %s","kern",a,total,threshold)                      end                      if total <= threshold then -                        if previous.id == penalty_code then -                            previous.penalty = 10000 +                        if getid(previous) == penalty_code then +                            setfield(previous,"penalty",10000)                          else                              insert_node_after(head,previous,new_penalty(10000))                          end @@ -143,16 +153,16 @@ local function keeptogether(start,a)                      end                  elseif id == penalty_code then                      if total <= threshold then -                        if previous.id == penalty_code then -                            previous.penalty = 10000 +                        if getid(previous) == penalty_code then +                            setfield(previous,"penalty",10000)                          end -                        current.penalty = 10000 +                        setfield(current,"penalty",10000)                      else                          break                      end                  end                  previous = current -                current = current.next +                current = getnext(current)              end          end      end @@ -162,18 +172,18 @@ end  function builders.paragraphs.keeptogether(head)      local done = false -    local current = head +    local current = tonut(head)      while current do -        if current.id == hlist_code then -            local a = current[a_keeptogether] +        if getid(current) == hlist_code then +            local a = getattr(current,a_keeptogether)              if a and a > 0 then                  keeptogether(current,a) -                current[a_keeptogether] = unsetvalue +                setattr(current,a_keeptogether,unsetvalue)                  cache[a] = nil                  done = true              end          end -        current = current.next +        current = getnext(current)      end      return head, done  end diff --git a/tex/context/base/typo-rep.lua b/tex/context/base/typo-rep.lua index 01868f490..95b801e2e 100644 --- a/tex/context/base/typo-rep.lua +++ b/tex/context/base/typo-rep.lua @@ -10,31 +10,44 @@ if not modules then modules = { } end modules ['typo-rep'] = {  -- endure it by listening to a couple cd's by The Scene and The Lau  -- on the squeezebox on my desk. +local next, type, tonumber = next, type, tonumber +  local trace_stripping = false  trackers.register("nodes.stripping",  function(v) trace_stripping = v end)                                 trackers.register("fonts.stripping",  function(v) trace_stripping = v end)  local report_stripping = logs.reporter("fonts","stripping") -local nodes, node = nodes, node +local nodes           = nodes +local tasks           = nodes.tasks + +local nuts            = nodes.nuts +local tonut           = nuts.tonut +local tonode          = nuts.tonode + +local getnext         = nuts.getnext +local getchar         = nuts.getchar +local getid           = nuts.getid +local getattr         = nuts.getid -local delete_node     = nodes.delete -local replace_node    = nodes.replace -local copy_node       = node.copy +local setattr         = nuts.setattr + +local delete_node     = nuts.delete +local replace_node    = nuts.replace +local copy_node       = nuts.copy + +local nodecodes       = nodes.nodecodes +local glyph_code      = nodecodes.glyph  local chardata        = characters.data  local collected       = false -local a_stripping     = attributes.private("stripping")  local fontdata        = fonts.hashes.identifiers -local tasks           = nodes.tasks +local a_stripping     = attributes.private("stripping")  local texsetattribute = tex.setattribute  local unsetvalue      = attributes.unsetvalue  local v_reset         = interfaces.variables.reset -local nodecodes       = nodes.nodecodes -local glyph_code      = nodecodes.glyph -  -- todo: other namespace -> typesetters  nodes.stripping  = nodes.stripping  or { } local stripping  = nodes.stripping @@ -59,13 +72,13 @@ local function process(what,head,current,char)          head, current = delete_node(head,current)      elseif type(what) == "function" then          head, current = what(head,current) -        current = current.next +        current = getnext(current)          if trace_stripping then              report_stripping("processing %C in text",char)          end      elseif what then  -- assume node          head, current = replace_node(head,current,copy_node(what)) -        current = current.next +        current = getnext(current)          if trace_stripping then              report_stripping("replacing %C in text",char)          end @@ -74,28 +87,29 @@ local function process(what,head,current,char)  end  function nodes.handlers.stripping(head) +    head = tonut(head)      local current, done = head, false      while current do -        if current.id == glyph_code then +        if getid(current) == glyph_code then              -- it's more efficient to keep track of what needs to be kept -            local todo = current[a_stripping] +            local todo = getattr(current,a_stripping)              if todo == 1 then -                local char = current.char +                local char = getchar(current)                  local what = glyphs[char]                  if what then                      head, current = process(what,head,current,char)                      done = true                  else -- handling of spacing etc has to be done elsewhere -                    current = current.next +                    current = getnext(current)                  end              else -                current = current.next +                current = getnext(current)              end          else -            current = current.next +            current = getnext(current)          end      end -    return head, done +    return tonode(head), done  end  local enabled = false diff --git a/tex/context/base/typo-spa.lua b/tex/context/base/typo-spa.lua index c3f50fe98..039f7c81d 100644 --- a/tex/context/base/typo-spa.lua +++ b/tex/context/base/typo-spa.lua @@ -15,10 +15,7 @@ local report_spacing = logs.reporter("typesetting","spacing")  local nodes, fonts, node = nodes, fonts, node -local insert_node_before = node.insert_before -local insert_node_after  = node.insert_after -local remove_node        = nodes.remove -local end_of_math        = node.end_of_math +local tasks              = nodes.tasks  local fonthashes         = fonts.hashes  local fontdata           = fonthashes.identifiers @@ -29,6 +26,27 @@ local unsetvalue         = attributes.unsetvalue  local v_reset            = interfaces.variables.reset +local nuts               = nodes.nuts +local tonut              = nuts.tonut +local tonode             = nuts.tonode + +local getnext            = nuts.getnext +local getprev            = nuts.getprev +local getchar            = nuts.getchar +local getid              = nuts.getid +local getattr            = nuts.getattr + +local setattr            = nuts.setattr + +local insert_node_before = nuts.insert_before +local insert_node_after  = nuts.insert_after +local remove_node        = nuts.remove +local end_of_math        = nuts.end_of_math + +local nodepool           = nuts.pool +local new_penalty        = nodepool.penalty +local new_glue           = nodepool.glue +  local nodecodes          = nodes.nodecodes  local glyph_code         = nodecodes.glyph  local math_code          = nodecodes.math @@ -36,12 +54,6 @@ local math_code          = nodecodes.math  local somespace          = nodes.somespace  local somepenalty        = nodes.somepenalty -local nodepool           = nodes.pool -local tasks              = nodes.tasks - -local new_penalty        = nodepool.penalty -local new_glue           = nodepool.glue -  typesetters              = typesetters or { }  local typesetters        = typesetters @@ -52,7 +64,6 @@ spacings.mapping         = spacings.mapping or { }  spacings.numbers         = spacings.numbers or { }  local a_spacings         = attributes.private("spacing") -spacings.attribute       = a_spacings  storage.register("typesetters/spacings/mapping", spacings.mapping, "typesetters.spacings.mapping") @@ -67,29 +78,30 @@ end  -- todo cache lastattr  function spacings.handler(head) +    head = tonut(head)      local done = false      local start = head      -- head is always begin of par (whatsit), so we have at least two prev nodes      -- penalty followed by glue      while start do -        local id = start.id +        local id = getid(start)          if id == glyph_code then -            local attr = start[a_spacings] +            local attr = getattr(start,a_spacings)              if attr and attr > 0 then                  local data = mapping[attr]                  if data then -                    local char = start.char +                    local char = getchar(start)                      local map = data.characters[char] -                    start[a_spacings] = unsetvalue -- needed? +                    setattr(start,a_spacings,unsetvalue) -- needed?                      if map then                          local left = map.left                          local right = map.right                          local alternative = map.alternative -                        local quad = quaddata[start.font] -                        local prev = start.prev +                        local quad = quaddata[getfont(start)] +                        local prev = getprev(start)                          if left and left ~= 0 and prev then                              local ok = false -                            local prevprev = prev.prev +                            local prevprev = getprev(prev)                              if alternative == 1 then                                  local somespace = somespace(prev,true)                                  if somespace then @@ -120,10 +132,10 @@ function spacings.handler(head)                                  done = true                              end                          end -                        local next = start.next +                        local next = getnext(start)                          if right and right ~= 0 and next then                              local ok = false -                            local nextnext = next.next +                            local nextnext = getnext(next)                              if alternative == 1 then                                  local somepenalty = somepenalty(next,10000)                                  if somepenalty then @@ -164,10 +176,10 @@ function spacings.handler(head)              start = end_of_math(start) -- weird, can return nil .. no math end?          end          if start then -            start = start.next +            start = getnext(start)          end      end -    return head, done +    return tonode(head), done  end  local enabled = false diff --git a/tex/context/base/typo-tal.lua b/tex/context/base/typo-tal.lua index 63a66d037..e8c14e3e3 100644 --- a/tex/context/base/typo-tal.lua +++ b/tex/context/base/typo-tal.lua @@ -20,19 +20,34 @@ local fontcharacters       = fonts.hashes.characters  local unicodes             = fonts.hashes.unicodes  local categories           = characters.categories -- nd -local insert_node_before   = nodes.insert_before -local insert_node_after    = nodes.insert_after -local traverse_list_by_id  = nodes.traverse_id -local dimensions_of_list   = nodes.dimensions -local first_glyph          = nodes.first_glyph +local nuts                 = nodes.nuts +local tonut                = nuts.tonut +local tonode               = nuts.tonode -local nodepool             = nodes.pool +local getnext              = nuts.getnext +local getprev              = nuts.getprev +local getid                = nuts.getid +local getfont              = nuts.getfont +local getchar              = nuts.getchar +local getattr              = nuts.getattr +local getfield             = nuts.getfield + +local setattr              = nuts.setattr +local setfield             = nuts.setfield + +local insert_node_before   = nuts.insert_before +local insert_node_after    = nuts.insert_after +local traverse_list_by_id  = nuts.traverse_id +local dimensions_of_list   = nuts.dimensions +local first_glyph          = nuts.first_glyph + +local nodepool             = nuts.pool  local new_kern             = nodepool.kern  local new_gluespec         = nodepool.gluespec  local tracers              = nodes.tracers  local setcolor             = tracers.colors.set -local tracedrule           = tracers.pool.nodes.rule +local tracedrule           = tracers.pool.nuts.rule  local characteralign       = { }  typesetters.characteralign = characteralign @@ -69,10 +84,11 @@ local function traced_kern(w)      return tracedrule(w,nil,nil,"darkgray")  end -function characteralign.handler(head,where) +function characteralign.handler(originalhead,where)      if not datasets then -        return head, false +        return originalhead, false      end +    local head = tonut(originalhead)   -- local first = first_glyph(head) -- we could do that once      local first      for n in traverse_list_by_id(glyph_code,head) do @@ -80,11 +96,11 @@ function characteralign.handler(head,where)          break      end      if not first then -        return head, false +        return originalhead, false      end -    local a = first[a_characteralign] +    local a = getattr(first,a_characteralign)      if not a or a == 0 then -        return head, false +        return originalhead, false      end      local column    = div(a,100)      local row       = a % 100 @@ -100,10 +116,10 @@ function characteralign.handler(head,where)      local sign      = nil      -- we can think of constraints      while current do -        local id = current.id +        local id = getid(current)          if id == glyph_code then -            local char = current.char -            local font = current.font +            local char = getchar(current) +            local font = getfont(current)              local unicode = unicodes[font][char]              if not unicode then                  -- no unicode so forget about it @@ -126,13 +142,13 @@ function characteralign.handler(head,where)                      if not b_start then                          if sign then                              b_start = sign -                            local new = validsigns[sign.char] -                            if char == new or not fontcharacters[sign.font][new] then +                            local new = validsigns[getchar(sign)] +                            if char == new or not fontcharacters[getfont(sign)][new] then                                  if trace_split then                                      setcolor(sign,"darkyellow")                                  end                              else -                                sign.char = new +                                setfield(sign,"char",new)                                  if trace_split then                                      setcolor(sign,"darkmagenta")                                  end @@ -158,14 +174,14 @@ function characteralign.handler(head,where)              end          elseif (b_start or a_start) and id == glue_code then              -- somewhat inefficient -            local next = current.next -            local prev = current.prev -            if next and prev and next.id == glyph_code and prev.id == glyph_code then -- too much checking -                local width = fontcharacters[b_start.font][separator or period].width -             -- local spec = current.spec +            local next = getnext(current) +            local prev = getprev(current) +            if next and prev and getid(next) == glyph_code and getid(prev) == glyph_code then -- too much checking +                local width = fontcharacters[getfont(b_start)][separator or period].width +             -- local spec = getfield(current,"spec")               -- nodes.free(spec) -- hm, we leak but not that many specs -                current.spec = new_gluespec(width) -                current[a_character] = punctuationspace +                setfield(current,"spec",new_gluespec(width)) +                setattr(current,a_character,punctuationspace)                  if a_start then                      a_stop = current                  elseif b_start then @@ -173,7 +189,7 @@ function characteralign.handler(head,where)                  end              end          end -        current = current.next +        current = getnext(current)      end      local entry = list[row]      if entry then @@ -207,7 +223,7 @@ function characteralign.handler(head,where)              if not c then               -- print("[before]")                  if dataset.hasseparator then -                    local width = fontcharacters[b_stop.font][separator].width +                    local width = fontcharacters[getfont(b_stop)][separator].width                      insert_node_after(head,b_stop,new_kern(maxafter+width))                  end              elseif a_start then @@ -229,7 +245,7 @@ function characteralign.handler(head,where)                  end              else               -- print("[after]") -                local width = fontcharacters[b_stop.font][separator].width +                local width = fontcharacters[getfont(b_stop)][separator].width                  head = insert_node_before(head,a_start,new_kern(maxbefore+width))              end              if after < maxafter then @@ -246,12 +262,12 @@ function characteralign.handler(head,where)          end      else          entry = { -            before = b_start and dimensions_of_list(b_start,b_stop.next) or 0, -            after  = a_start and dimensions_of_list(a_start,a_stop.next) or 0, +            before = b_start and dimensions_of_list(b_start,getnext(b_stop)) or 0, +            after  = a_start and dimensions_of_list(a_start,getnext(a_stop)) or 0,          }          list[row] = entry      end -    return head, true +    return tonode(head), true  end  function setcharacteralign(column,separator) diff --git a/tex/generic/context/luatex/luatex-fonts-inj.lua b/tex/generic/context/luatex/luatex-fonts-inj.lua new file mode 100644 index 000000000..ae48150a6 --- /dev/null +++ b/tex/generic/context/luatex/luatex-fonts-inj.lua @@ -0,0 +1,526 @@ +if not modules then modules = { } end modules ['node-inj'] = { +    version   = 1.001, +    comment   = "companion to node-ini.mkiv", +    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL", +    copyright = "PRAGMA ADE / ConTeXt Development Team", +    license   = "see context related readme files", +} + +-- This is very experimental (this will change when we have luatex > .50 and +-- a few pending thingies are available. Also, Idris needs to make a few more +-- test fonts. Btw, future versions of luatex will have extended glyph properties +-- that can be of help. Some optimizations can go away when we have faster machines. + +-- todo: make a special one for context + +local next = next +local utfchar = utf.char + +local trace_injections = false  trackers.register("nodes.injections", function(v) trace_injections = v end) + +local report_injections = logs.reporter("nodes","injections") + +local attributes, nodes, node = attributes, nodes, node + +fonts                    = fonts +local fontdata           = fonts.hashes.identifiers + +nodes.injections         = nodes.injections or { } +local injections         = nodes.injections + +local nodecodes          = nodes.nodecodes +local glyph_code         = nodecodes.glyph +local kern_code          = nodecodes.kern +local nodepool           = nodes.pool +local newkern            = nodepool.kern + +local traverse_id        = node.traverse_id +local insert_node_before = node.insert_before +local insert_node_after  = node.insert_after + +local a_kernpair = attributes.private('kernpair') +local a_ligacomp = attributes.private('ligacomp') +local a_markbase = attributes.private('markbase') +local a_markmark = attributes.private('markmark') +local a_markdone = attributes.private('markdone') +local a_cursbase = attributes.private('cursbase') +local a_curscurs = attributes.private('curscurs') +local a_cursdone = attributes.private('cursdone') + +-- This injector has been tested by Idris Samawi Hamid (several arabic fonts as well as +-- the rather demanding Husayni font), Khaled Hosny (latin and arabic) and Kaj Eigner +-- (arabic, hebrew and thai) and myself (whatever font I come across). I'm pretty sure +-- that this code is not 100% okay but examples are needed to figure things out. + +function injections.installnewkern(nk) +    newkern = nk or newkern +end + +local cursives = { } +local marks    = { } +local kerns    = { } + +-- Currently we do gpos/kern in a bit inofficial way but when we have the extra fields in +-- glyphnodes to manipulate ht/dp/wd explicitly I will provide an alternative; also, we +-- can share tables. + +-- For the moment we pass the r2l key ... volt/arabtype tests .. idris: this needs +-- checking with husayni (volt and fontforge). + +function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmnext) +    local dx, dy = factor*(exit[1]-entry[1]), factor*(exit[2]-entry[2]) +    local ws, wn = tfmstart.width, tfmnext.width +    local bound = #cursives + 1 +    start[a_cursbase] = bound +    nxt[a_curscurs] = bound +    cursives[bound] = { rlmode, dx, dy, ws, wn } +    return dx, dy, bound +end + +function injections.setpair(current,factor,rlmode,r2lflag,spec,tfmchr) +    local x, y, w, h = factor*spec[1], factor*spec[2], factor*spec[3], factor*spec[4] +    -- dy = y - h +    if x ~= 0 or w ~= 0 or y ~= 0 or h ~= 0 then +        local bound = current[a_kernpair] +        if bound then +            local kb = kerns[bound] +            -- inefficient but singles have less, but weird anyway, needs checking +            kb[2], kb[3], kb[4], kb[5] = (kb[2] or 0) + x, (kb[3] or 0) + y, (kb[4] or 0)+ w, (kb[5] or 0) + h +        else +            bound = #kerns + 1 +            current[a_kernpair] = bound +            kerns[bound] = { rlmode, x, y, w, h, r2lflag, tfmchr.width } +        end +        return x, y, w, h, bound +    end +    return x, y, w, h -- no bound +end + +function injections.setkern(current,factor,rlmode,x,tfmchr) +    local dx = factor*x +    if dx ~= 0 then +        local bound = #kerns + 1 +        current[a_kernpair] = bound +        kerns[bound] = { rlmode, dx } +        return dx, bound +    else +        return 0, 0 +    end +end + +function injections.setmark(start,base,factor,rlmode,ba,ma,index,baseismark) -- ba=baseanchor, ma=markanchor +    local dx, dy = factor*(ba[1]-ma[1]), factor*(ba[2]-ma[2])     -- the index argument is no longer used but when this +    local bound = base[a_markbase]                    -- fails again we should pass it +    local index = 1 +    if bound then +        local mb = marks[bound] +        if mb then +         -- if not index then index = #mb + 1 end +            index = #mb + 1 +            mb[index] = { dx, dy, rlmode } +            start[a_markmark] = bound +            start[a_markdone] = index +            return dx, dy, bound +        else +            report_injections("possible problem, %U is base mark without data (id %a)",base.char,bound) +        end +    end +--     index = index or 1 +    index = index or 1 +    bound = #marks + 1 +    base[a_markbase] = bound +    start[a_markmark] = bound +    start[a_markdone] = index +    marks[bound] = { [index] = { dx, dy, rlmode, baseismark } } +    return dx, dy, bound +end + +local function dir(n) +    return (n and n<0 and "r-to-l") or (n and n>0 and "l-to-r") or "unset" +end + +local function trace(head) +    report_injections("begin run") +    for n in traverse_id(glyph_code,head) do +        if n.subtype < 256 then +            local kp = n[a_kernpair] +            local mb = n[a_markbase] +            local mm = n[a_markmark] +            local md = n[a_markdone] +            local cb = n[a_cursbase] +            local cc = n[a_curscurs] +            local char = n.char +            report_injections("font %s, char %U, glyph %c",n.font,char,char) +            if kp then +                local k = kerns[kp] +                if k[3] then +                    report_injections("  pairkern: dir %a, x %p, y %p, w %p, h %p",dir(k[1]),k[2],k[3],k[4],k[5]) +                else +                    report_injections("  kern: dir %a, dx %p",dir(k[1]),k[2]) +                end +            end +            if mb then +                report_injections("  markbase: bound %a",mb) +            end +            if mm then +                local m = marks[mm] +                if mb then +                    local m = m[mb] +                    if m then +                        report_injections("  markmark: bound %a, index %a, dx %p, dy %p",mm,md,m[1],m[2]) +                    else +                        report_injections("  markmark: bound %a, missing index",mm) +                    end +                else +                    m = m[1] +                    report_injections("  markmark: bound %a, dx %p, dy %p",mm,m and m[1],m and m[2]) +                end +            end +            if cb then +                report_injections("  cursbase: bound %a",cb) +            end +            if cc then +                local c = cursives[cc] +                report_injections("  curscurs: bound %a, dir %a, dx %p, dy %p",cc,dir(c[1]),c[2],c[3]) +            end +        end +    end +    report_injections("end run") +end + +-- todo: reuse tables (i.e. no collection), but will be extra fields anyway +-- todo: check for attribute + +-- We can have a fast test on a font being processed, so we can check faster for marks etc +-- but I'll make a context variant anyway. + +local function show_result(head) +    local current = head +    local skipping = false +    while current do +        local id = current.id +        if id == glyph_code then +            report_injections("char: %C, width %p, xoffset %p, yoffset %p",current.char,current.width,current.xoffset,current.yoffset) +            skipping = false +        elseif id == kern_code then +            report_injections("kern: %p",current.kern) +            skipping = false +        elseif not skipping then +            report_injections() +            skipping = true +        end +        current = current.next +    end +end + +function injections.handler(head,where,keep) +    local has_marks, has_cursives, has_kerns = next(marks), next(cursives), next(kerns) +    if has_marks or has_cursives then +        if trace_injections then +            trace(head) +        end +        -- in the future variant we will not copy items but refs to tables +        local done, ky, rl, valid, cx, wx, mk, nofvalid = false, { }, { }, { }, { }, { }, { }, 0 +        if has_kerns then -- move outside loop +            local nf, tm = nil, nil +            for n in traverse_id(glyph_code,head) do -- only needed for relevant fonts +                if n.subtype < 256 then +                    nofvalid = nofvalid + 1 +                    valid[nofvalid] = n +                    if n.font ~= nf then +                        nf = n.font +                        tm = fontdata[nf].resources.marks +                    end +                    if tm then +                        mk[n] = tm[n.char] +                    end +                    local k = n[a_kernpair] +                    if k then +                        local kk = kerns[k] +                        if kk then +                            local x, y, w, h = kk[2] or 0, kk[3] or 0, kk[4] or 0, kk[5] or 0 +                            local dy = y - h +                            if dy ~= 0 then +                                ky[n] = dy +                            end +                            if w ~= 0 or x ~= 0 then +                                wx[n] = kk +                            end +                            rl[n] = kk[1] -- could move in test +                        end +                    end +                end +            end +        else +            local nf, tm = nil, nil +            for n in traverse_id(glyph_code,head) do +                if n.subtype < 256 then +                    nofvalid = nofvalid + 1 +                    valid[nofvalid] = n +                    if n.font ~= nf then +                        nf = n.font +                        tm = fontdata[nf].resources.marks +                    end +                    if tm then +                        mk[n] = tm[n.char] +                    end +                end +            end +        end +        if nofvalid > 0 then +            -- we can assume done == true because we have cursives and marks +            local cx = { } +            if has_kerns and next(ky) then +                for n, k in next, ky do +                    n.yoffset = k +                end +            end +            -- todo: reuse t and use maxt +            if has_cursives then +                local p_cursbase, p = nil, nil +                -- since we need valid[n+1] we can also use a "while true do" +                local t, d, maxt = { }, { }, 0 +                for i=1,nofvalid do -- valid == glyphs +                    local n = valid[i] +                    if not mk[n] then +                        local n_cursbase = n[a_cursbase] +                        if p_cursbase then +                            local n_curscurs = n[a_curscurs] +                            if p_cursbase == n_curscurs then +                                local c = cursives[n_curscurs] +                                if c then +                                    local rlmode, dx, dy, ws, wn = c[1], c[2], c[3], c[4], c[5] +                                    if rlmode >= 0 then +                                        dx = dx - ws +                                    else +                                        dx = dx + wn +                                    end +                                    if dx ~= 0 then +                                        cx[n] = dx +                                        rl[n] = rlmode +                                    end +                                --  if rlmode and rlmode < 0 then +                                        dy = -dy +                                --  end +                                    maxt = maxt + 1 +                                    t[maxt] = p +                                    d[maxt] = dy +                                else +                                    maxt = 0 +                                end +                            end +                        elseif maxt > 0 then +                            local ny = n.yoffset +                            for i=maxt,1,-1 do +                                ny = ny + d[i] +                                local ti = t[i] +                                ti.yoffset = ti.yoffset + ny +                            end +                            maxt = 0 +                        end +                        if not n_cursbase and maxt > 0 then +                            local ny = n.yoffset +                            for i=maxt,1,-1 do +                                ny = ny + d[i] +                                local ti = t[i] +                                ti.yoffset = ny +                            end +                            maxt = 0 +                        end +                        p_cursbase, p = n_cursbase, n +                    end +                end +                if maxt > 0 then +                    local ny = n.yoffset +                    for i=maxt,1,-1 do +                        ny = ny + d[i] +                        local ti = t[i] +                        ti.yoffset = ny +                    end +                    maxt = 0 +                end +                if not keep then +                    cursives = { } +                end +            end +            if has_marks then +                for i=1,nofvalid do +                    local p = valid[i] +                    local p_markbase = p[a_markbase] +                    if p_markbase then +                        local mrks = marks[p_markbase] +                        local nofmarks = #mrks +                        for n in traverse_id(glyph_code,p.next) do +                            local n_markmark = n[a_markmark] +                            if p_markbase == n_markmark then +                                local index = n[a_markdone] or 1 +                                local d = mrks[index] +                                if d then +                                    local rlmode = d[3] +                                    -- +                                    local k = wx[p] +                                    if k then +                                        local x = k[2] +                                        local w = k[4] +                                        if w then +                                            if rlmode and rlmode >= 0 then +                                                -- kern(x) glyph(p) kern(w-x) mark(n) +                                                n.xoffset = p.xoffset - p.width + d[1] - (w-x) +                                            else +                                                -- kern(w-x) glyph(p) kern(x) mark(n) +                                                n.xoffset = p.xoffset - d[1] - x +                                            end +                                        else +                                            if rlmode and rlmode >= 0 then +                                                -- okay for husayni +                                                n.xoffset = p.xoffset - p.width + d[1] +                                            else +                                                -- needs checking: is x ok here? +                                                n.xoffset = p.xoffset - d[1] - x +                                            end +                                        end +                                    else +                                        if rlmode and rlmode >= 0 then +                                            n.xoffset = p.xoffset - p.width + d[1] +                                        else +                                            n.xoffset = p.xoffset - d[1] +                                        end +                                        local w = n.width +                                        if w ~= 0 then +                                            insert_node_before(head,n,newkern(-w/2)) +                                            insert_node_after(head,n,newkern(-w/2)) +                                        end +                                    end +                                    --                                    -- +                                    if mk[p] then +                                        n.yoffset = p.yoffset + d[2] +                                    else +                                        n.yoffset = n.yoffset + p.yoffset + d[2] +                                    end +                                    -- +                                    if nofmarks == 1 then +                                        break +                                    else +                                        nofmarks = nofmarks - 1 +                                    end +                                end +                            else +                                -- KE: there can be <mark> <mkmk> <mark> sequences in ligatures +                            end +                        end +                    end +                end +                if not keep then +                    marks = { } +                end +            end +            -- todo : combine +            if next(wx) then +                for n, k in next, wx do +                 -- only w can be nil (kernclasses), can be sped up when w == nil +                    local x = k[2] +                    local w = k[4] +                    if w then +                        local rl = k[1] -- r2l = k[6] +                        local wx = w - x +                        if rl < 0 then	-- KE: don't use r2l here +                            if wx ~= 0 then +                                insert_node_before(head,n,newkern(wx)) -- type 0/2 +                            end +                            if x ~= 0 then +                                insert_node_after (head,n,newkern(x))  -- type 0/2 +                            end +                        else +                            if x ~= 0 then +                                insert_node_before(head,n,newkern(x))  -- type 0/2 +                            end +                            if wx ~= 0 then +                                insert_node_after (head,n,newkern(wx)) -- type 0/2 +                            end +                        end +                    elseif x ~= 0 then +                        -- this needs checking for rl < 0 but it is unlikely that a r2l script +                        -- uses kernclasses between glyphs so we're probably safe (KE has a +                        -- problematic font where marks interfere with rl < 0 in the previous +                        -- case) +                        insert_node_before(head,n,newkern(x)) -- a real font kern, type 0 +                    end +                end +            end +            if next(cx) then +                for n, k in next, cx do +                    if k ~= 0 then +                        local rln = rl[n] +                        if rln and rln < 0 then +                            insert_node_before(head,n,newkern(-k)) -- type 0/2 +                        else +                            insert_node_before(head,n,newkern(k))  -- type 0/2 +                        end +                    end +                end +            end +            if not keep then +                kerns = { } +            end +         -- if trace_injections then +         --     show_result(head) +         -- end +            return head, true +        elseif not keep then +            kerns, cursives, marks = { }, { }, { } +        end +    elseif has_kerns then +        if trace_injections then +            trace(head) +        end +        for n in traverse_id(glyph_code,head) do +            if n.subtype < 256 then +                local k = n[a_kernpair] +                if k then +                    local kk = kerns[k] +                    if kk then +                        local rl, x, y, w = kk[1], kk[2] or 0, kk[3], kk[4] +                        if y and y ~= 0 then +                            n.yoffset = y -- todo: h ? +                        end +                        if w then +                            -- copied from above +                         -- local r2l = kk[6] +                            local wx = w - x +                            if rl < 0 then  -- KE: don't use r2l here +                                if wx ~= 0 then +                                    insert_node_before(head,n,newkern(wx)) +                                end +                                if x ~= 0 then +                                    insert_node_after (head,n,newkern(x)) +                                end +                            else +                                if x ~= 0 then +                                    insert_node_before(head,n,newkern(x)) +                                end +                                if wx ~= 0 then +                                    insert_node_after(head,n,newkern(wx)) +                                end +                            end +                        else +                            -- simple (e.g. kernclass kerns) +                            if x ~= 0 then +                                insert_node_before(head,n,newkern(x)) +                            end +                        end +                    end +                end +            end +        end +        if not keep then +            kerns = { } +        end +     -- if trace_injections then +     --     show_result(head) +     -- end +        return head, true +    else +        -- no tracing needed +    end +    return head, false +end diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua index 751d8b9d3..79755720a 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 : luatex-fonts-merged.lua  -- parent file : luatex-fonts.lua --- merge date  : 01/06/14 23:46:25 +-- merge date  : 01/07/14 14:00:03  do -- begin closure to overcome local limits and interference @@ -8903,26 +8903,12 @@ nodes.injections=nodes.injections or {}  local injections=nodes.injections  local nodecodes=nodes.nodecodes  local glyph_code=nodecodes.glyph -local disc_code=nodecodes.disc  local kern_code=nodecodes.kern -local nuts=nodes.nuts -local nodepool=nuts.pool +local nodepool=nodes.pool  local newkern=nodepool.kern -local tonode=nuts.tonode -local tonut=nuts.tonut -local getfield=nuts.getfield -local getnext=nuts.getnext -local getprev=nuts.getprev -local getid=nuts.getid -local getattr=nuts.getattr -local getfont=nuts.getfont -local getsubtype=nuts.getsubtype -local getchar=nuts.getchar -local setfield=nuts.setfield -local setattr=nuts.setattr -local traverse_id=nuts.traverse_id -local insert_node_before=nuts.insert_before -local insert_node_after=nuts.insert_after +local traverse_id=node.traverse_id +local insert_node_before=node.insert_before +local insert_node_after=node.insert_after  local a_kernpair=attributes.private('kernpair')  local a_ligacomp=attributes.private('ligacomp')  local a_markbase=attributes.private('markbase') @@ -8941,21 +8927,21 @@ function injections.setcursive(start,nxt,factor,rlmode,exit,entry,tfmstart,tfmne    local dx,dy=factor*(exit[1]-entry[1]),factor*(exit[2]-entry[2])    local ws,wn=tfmstart.width,tfmnext.width    local bound=#cursives+1 -  setattr(start,a_cursbase,bound) -  setattr(nxt,a_curscurs,bound) +  start[a_cursbase]=bound +  nxt[a_curscurs]=bound    cursives[bound]={ rlmode,dx,dy,ws,wn }    return dx,dy,bound  end  function injections.setpair(current,factor,rlmode,r2lflag,spec,tfmchr)    local x,y,w,h=factor*spec[1],factor*spec[2],factor*spec[3],factor*spec[4]    if x~=0 or w~=0 or y~=0 or h~=0 then -    local bound=getattr(current,a_kernpair) +    local bound=current[a_kernpair]      if bound then        local kb=kerns[bound]        kb[2],kb[3],kb[4],kb[5]=(kb[2] or 0)+x,(kb[3] or 0)+y,(kb[4] or 0)+w,(kb[5] or 0)+h      else        bound=#kerns+1 -      setattr(current,a_kernpair,bound) +      current[a_kernpair]=bound        kerns[bound]={ rlmode,x,y,w,h,r2lflag,tfmchr.width }      end      return x,y,w,h,bound @@ -8966,7 +8952,7 @@ function injections.setkern(current,factor,rlmode,x,tfmchr)    local dx=factor*x    if dx~=0 then      local bound=#kerns+1 -    setattr(current,a_kernpair,bound) +    current[a_kernpair]=bound      kerns[bound]={ rlmode,dx }      return dx,bound    else @@ -8975,25 +8961,25 @@ function injections.setkern(current,factor,rlmode,x,tfmchr)  end  function injections.setmark(start,base,factor,rlmode,ba,ma,index,baseismark)     local dx,dy=factor*(ba[1]-ma[1]),factor*(ba[2]-ma[2])    -  local bound=getattr(base,a_markbase)           +  local bound=base[a_markbase]              local index=1    if bound then      local mb=marks[bound]      if mb then        index=#mb+1        mb[index]={ dx,dy,rlmode } -      setattr(start,a_markmark,bound) -      setattr(start,a_markdone,index) +      start[a_markmark]=bound +      start[a_markdone]=index        return dx,dy,bound      else -      report_injections("possible problem, %U is base mark without data (id %a)",getchar(base),bound) +      report_injections("possible problem, %U is base mark without data (id %a)",base.char,bound)      end    end    index=index or 1    bound=#marks+1 -  setattr(base,a_markbase,bound) -  setattr(start,a_markmark,bound) -  setattr(start,a_markdone,index) +  base[a_markbase]=bound +  start[a_markmark]=bound +  start[a_markdone]=index    marks[bound]={ [index]={ dx,dy,rlmode,baseismark } }    return dx,dy,bound  end @@ -9003,15 +8989,15 @@ end  local function trace(head)    report_injections("begin run")    for n in traverse_id(glyph_code,head) do -    if getsubtype(n)<256 then -      local kp=getattr(n,a_kernpair) -      local mb=getattr(n,a_markbase) -      local mm=getattr(n,a_markmark) -      local md=getattr(n,a_markdone) -      local cb=getattr(n,a_cursbase) -      local cc=getattr(n,a_curscurs) -      local char=getchar(n) -      report_injections("font %s, char %U, glyph %c",getfont(n),char,char) +    if n.subtype<256 then +      local kp=n[a_kernpair] +      local mb=n[a_markbase] +      local mm=n[a_markmark] +      local md=n[a_markdone] +      local cb=n[a_cursbase] +      local cc=n[a_curscurs] +      local char=n.char +      report_injections("font %s, char %U, glyph %c",n.font,char,char)        if kp then          local k=kerns[kp]          if k[3] then @@ -9052,23 +9038,21 @@ local function show_result(head)    local current=head    local skipping=false    while current do -    local id=getid(current) +    local id=current.id      if id==glyph_code then -      report_injections("char: %C, width %p, xoffset %p, yoffset %p", -        getchar(current),getfield(current,"width"),getfield(current,"xoffset"),getfield(current,"yoffset")) +      report_injections("char: %C, width %p, xoffset %p, yoffset %p",current.char,current.width,current.xoffset,current.yoffset)        skipping=false      elseif id==kern_code then -      report_injections("kern: %p",getfield(current,"kern")) +      report_injections("kern: %p",current.kern)        skipping=false      elseif not skipping then        report_injections()        skipping=true      end -    current=getnext(current) +    current=current.next    end  end  function injections.handler(head,where,keep) -  head=tonut(head)    local has_marks,has_cursives,has_kerns=next(marks),next(cursives),next(kerns)    if has_marks or has_cursives then      if trace_injections then @@ -9078,18 +9062,17 @@ function injections.handler(head,where,keep)      if has_kerns then         local nf,tm=nil,nil        for n in traverse_id(glyph_code,head) do  -        if getsubtype(n)<256 then +        if n.subtype<256 then            nofvalid=nofvalid+1            valid[nofvalid]=n -          local f=getfont(n) -          if f~=nf then -            nf=f -            tm=fontdata[nf].resources.marks  +          if n.font~=nf then +            nf=n.font +            tm=fontdata[nf].resources.marks            end            if tm then -            mk[n]=tm[getchar(n)] +            mk[n]=tm[n.char]            end -          local k=getattr(n,a_kernpair) +          local k=n[a_kernpair]            if k then              local kk=kerns[k]              if kk then @@ -9109,16 +9092,15 @@ function injections.handler(head,where,keep)      else        local nf,tm=nil,nil        for n in traverse_id(glyph_code,head) do -        if getsubtype(n)<256 then +        if n.subtype<256 then            nofvalid=nofvalid+1            valid[nofvalid]=n -          local f=getfont(n) -          if f~=nf then -            nf=f -            tm=fontdata[nf].resources.marks  +          if n.font~=nf then +            nf=n.font +            tm=fontdata[nf].resources.marks            end            if tm then -            mk[n]=tm[getchar(n)] +            mk[n]=tm[n.char]            end          end        end @@ -9127,7 +9109,7 @@ function injections.handler(head,where,keep)        local cx={}        if has_kerns and next(ky) then          for n,k in next,ky do -          setfield(n,"yoffset",k) +          n.yoffset=k          end        end        if has_cursives then @@ -9136,9 +9118,9 @@ function injections.handler(head,where,keep)          for i=1,nofvalid do             local n=valid[i]            if not mk[n] then -            local n_cursbase=getattr(n,a_cursbase) +            local n_cursbase=n[a_cursbase]              if p_cursbase then -              local n_curscurs=getattr(n,a_curscurs) +              local n_curscurs=n[a_curscurs]                if p_cursbase==n_curscurs then                  local c=cursives[n_curscurs]                  if c then @@ -9161,20 +9143,20 @@ function injections.handler(head,where,keep)                  end                end              elseif maxt>0 then -              local ny=getfield(n,"yoffset") +              local ny=n.yoffset                for i=maxt,1,-1 do                  ny=ny+d[i]                  local ti=t[i] -                setfield(ti,"yoffset",getfield(ti,"yoffset")+ny) +                ti.yoffset=ti.yoffset+ny                end                maxt=0              end              if not n_cursbase and maxt>0 then -              local ny=getfield(n,"yoffset") +              local ny=n.yoffset                for i=maxt,1,-1 do                  ny=ny+d[i]                  local ti=t[i] -                setfield(ti,"yoffset",ny) +                ti.yoffset=ny                end                maxt=0              end @@ -9182,11 +9164,11 @@ function injections.handler(head,where,keep)            end          end          if maxt>0 then -          local ny=getfield(n,"yoffset") +          local ny=n.yoffset            for i=maxt,1,-1 do              ny=ny+d[i]              local ti=t[i] -            setfield(ti,"yoffset",ny) +            ti.yoffset=ny            end            maxt=0          end @@ -9197,66 +9179,57 @@ function injections.handler(head,where,keep)        if has_marks then          for i=1,nofvalid do            local p=valid[i] -          local p_markbase=getattr(p,a_markbase) +          local p_markbase=p[a_markbase]            if p_markbase then              local mrks=marks[p_markbase]              local nofmarks=#mrks -            for n in traverse_id(glyph_code,getnext(p)) do -              local n_markmark=getattr(n,a_markmark) +            for n in traverse_id(glyph_code,p.next) do +              local n_markmark=n[a_markmark]                if p_markbase==n_markmark then -                local index=getattr(n,a_markdone) or 1 +                local index=n[a_markdone] or 1                  local d=mrks[index]                  if d then                    local rlmode=d[3]                    local k=wx[p] -                  local px=getfield(p,"xoffset") -                  local ox=0                    if k then                      local x=k[2]                      local w=k[4]                      if w then                        if rlmode and rlmode>=0 then -                        ox=px-getfield(p,"width")+d[1]-(w-x) +                        n.xoffset=p.xoffset-p.width+d[1]-(w-x)                        else -                        ox=px-d[1]-x +                        n.xoffset=p.xoffset-d[1]-x                        end                      else                        if rlmode and rlmode>=0 then -                        ox=px-getfield(p,"width")+d[1] +                        n.xoffset=p.xoffset-p.width+d[1]                        else -                        ox=px-d[1]-x +                        n.xoffset=p.xoffset-d[1]-x                        end                      end                    else -                    local wp=getfield(p,"width") -                    local wn=getfield(n,"width")                       if rlmode and rlmode>=0 then -                      ox=px-wp+d[1] +                      n.xoffset=p.xoffset-p.width+d[1]                      else -                      ox=px-d[1] +                      n.xoffset=p.xoffset-d[1]                      end -                    if wn~=0 then -                      insert_node_before(head,n,newkern(-wn/2)) -                      insert_node_after(head,n,newkern(-wn/2)) +                    local w=n.width +                    if w~=0 then +                      insert_node_before(head,n,newkern(-w/2)) +                      insert_node_after(head,n,newkern(-w/2))                      end                    end -                  setfield(n,"xoffset",ox) -                  local py=getfield(p,"yoffset") -                  local oy=0                    if mk[p] then -                    oy=py+d[2] +                    n.yoffset=p.yoffset+d[2]                    else -                    oy=getfield(n,"yoffset")+py+d[2] +                    n.yoffset=n.yoffset+p.yoffset+d[2]                    end -                  setfield(n,"yoffset",oy)                    if nofmarks==1 then                      break                    else                      nofmarks=nofmarks-1                    end                  end -              elseif not n_markmark then -                break                 else                end              end @@ -9308,7 +9281,6 @@ function injections.handler(head,where,keep)        if not keep then          kerns={}        end -head=tonode(head)        return head,true      elseif not keep then        kerns,cursives,marks={},{},{} @@ -9318,14 +9290,14 @@ head=tonode(head)        trace(head)      end      for n in traverse_id(glyph_code,head) do -      if getsubtype(n)<256 then -        local k=getattr(n,a_kernpair) +      if n.subtype<256 then +        local k=n[a_kernpair]          if k then            local kk=kerns[k]            if kk then              local rl,x,y,w=kk[1],kk[2] or 0,kk[3],kk[4]              if y and y~=0 then -              setfield(n,"yoffset",y)  +              n.yoffset=y               end              if w then                local wx=w-x @@ -9356,10 +9328,10 @@ head=tonode(head)      if not keep then        kerns={}      end -    return tonode(head),true +    return head,true    else    end -  return tonode(head),false +  return head,false  end  end -- closure @@ -9774,25 +9746,12 @@ registertracker("otf.positions","otf.marks,otf.kerns,otf.cursive")  registertracker("otf.actions","otf.replacements,otf.positions")  registertracker("otf.injections","nodes.injections")  registertracker("*otf.sample","otf.steps,otf.actions,otf.analyzing") -local nuts=nodes.nuts -local tonode=nuts.tonode -local tonut=nuts.tonut -local getfield=nuts.getfield -local getnext=nuts.getnext -local getprev=nuts.getprev -local getid=nuts.getid -local getattr=nuts.getattr -local getfont=nuts.getfont -local getsubtype=nuts.getsubtype -local getchar=nuts.getchar -local setfield=nuts.setfield -local setattr=nuts.setattr -local insert_node_after=nuts.insert_after -local delete_node=nuts.delete -local copy_node=nuts.copy -local find_node_tail=nuts.tail -local flush_node_list=nuts.flush_list -local end_of_math=nuts.end_of_math +local insert_node_after=node.insert_after +local delete_node=nodes.delete +local copy_node=node.copy +local find_node_tail=node.tail or node.slide +local flush_node_list=node.flush_list +local end_of_math=node.end_of_math  local setmetatableindex=table.setmetatableindex  local zwnj=0x200C  local zwj=0x200D @@ -9903,83 +9862,83 @@ local function pref(kind,lookupname)    return formatters["feature %a, lookup %a"](kind,lookupname)  end  local function copy_glyph(g)  -  local components=getfield(g,"components") +  local components=g.components    if components then -    setfield(g,"components",nil) +    g.components=nil      local n=copy_node(g) -    setfield(g,"components",components) +    g.components=components      return n    else      return copy_node(g)    end  end  local function markstoligature(kind,lookupname,head,start,stop,char) -  if start==stop and getchar(start)==char then +  if start==stop and start.char==char then      return head,start    else -    local prev=getprev(start) -    local next=getnext(stop) -    setfield(start,"prev",nil) -    setfield(stop,"next",nil) +    local prev=start.prev +    local next=stop.next +    start.prev=nil +    stop.next=nil      local base=copy_glyph(start)      if head==start then        head=base      end -    setfield(base,"char",char) -    setfield(base,"subtype",ligature_code) -    setfield(base,"components",start) +    base.char=char +    base.subtype=ligature_code +    base.components=start      if prev then -      setfield(prev,"next",base) +      prev.next=base      end      if next then -      setfield(next,"prev",base) +      next.prev=base      end -    setfield(base,"next",next) -    setfield(base,"prev",prev) +    base.next=next +    base.prev=prev      return head,base    end  end  local function getcomponentindex(start) -  if getid(start)~=glyph_code then +  if start.id~=glyph_code then      return 0 -  elseif getsubtype(start)==ligature_code then +  elseif start.subtype==ligature_code then      local i=0 -    local components=getfield(start,"components") +    local components=start.components      while components do        i=i+getcomponentindex(components) -      components=getnext(components) +      components=components.next      end      return i -  elseif not marks[getchar(start)] then +  elseif not marks[start.char] then      return 1    else      return 0    end  end  local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound)  -  if start==stop and getchar(start)==char then -    setfield(start,"char",char) +  if start==stop and start.char==char then +    start.char=char      return head,start    end -  local prev=getprev(start) -  local next=getnext(stop) -  setfield(start,"prev",nil) -  setfield(stop,"next",nil) +  local prev=start.prev +  local next=stop.next +  start.prev=nil +  stop.next=nil    local base=copy_glyph(start)    if start==head then      head=base    end -  setfield(base,"char",char) -  setfield(base,"subtype",ligature_code) -  setfield(base,"components",start)  +  base.char=char +  base.subtype=ligature_code +  base.components=start     if prev then -    setfield(prev,"next",base) +    prev.next=base    end    if next then -    setfield(next,"prev",base) +    next.prev=base    end -  setfield(base,"next",next) -  setfield(base,"prev",prev) +  base.next=next +  base.prev=prev    if not discfound then      local deletemarks=markflag~="mark"      local components=start @@ -9988,42 +9947,42 @@ local function toligature(kind,lookupname,head,start,stop,char,markflag,discfoun      local head=base      local current=base      while start do -      local char=getchar(start) +      local char=start.char        if not marks[char] then          baseindex=baseindex+componentindex          componentindex=getcomponentindex(start)        elseif not deletemarks then  -        setattr(start,a_ligacomp,baseindex+(getattr(start,a_ligacomp) or componentindex)) +        start[a_ligacomp]=baseindex+(start[a_ligacomp] or componentindex)          if trace_marks then -          logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),getattr(start,a_ligacomp)) +          logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp])          end          head,current=insert_node_after(head,current,copy_node(start))         elseif trace_marks then          logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char))        end -      start=getnext(start) +      start=start.next      end -    local start=getnext(current) -    while start and getid(start)==glyph_code do -      local char=getchar(start) +    local start=current.next +    while start and start.id==glyph_code do +      local char=start.char        if marks[char] then -        setattr(start,a_ligacomp,baseindex+(getattr(start,a_ligacomp) or componentindex)) +        start[a_ligacomp]=baseindex+(start[a_ligacomp] or componentindex)          if trace_marks then -          logwarning("%s: set mark %s, gets index %s",pref(kind,lookupname),gref(char),getattr(start,a_ligacomp)) +          logwarning("%s: set mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp])          end        else          break        end -      start=getnext(start) +      start=start.next      end    end    return head,base  end  function handlers.gsub_single(head,start,kind,lookupname,replacement)    if trace_singles then -    logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(getchar(start)),gref(replacement)) +    logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(start.char),gref(replacement))    end -  setfield(start,"char",replacement) +  start.char=replacement    return head,start,true  end  local function get_alternative_glyph(start,alternatives,value,trace_alternatives) @@ -10049,7 +10008,7 @@ local function get_alternative_glyph(start,alternatives,value,trace_alternatives          return false,trace_alternatives and formatters["invalid value %a, %s"](value,"out of range")        end      elseif value==0 then -      return getchar(start),trace_alternatives and formatters["invalid value %a, %s"](value,"no change") +      return start.char,trace_alternatives and formatters["invalid value %a, %s"](value,"no change")      elseif value<1 then        return alternatives[1],trace_alternatives and formatters["invalid value %a, taking %a"](value,1)      else @@ -10060,25 +10019,25 @@ end  local function multiple_glyphs(head,start,multiple,ignoremarks)    local nofmultiples=#multiple    if nofmultiples>0 then -    setfield(start,"char",multiple[1]) +    start.char=multiple[1]      if nofmultiples>1 then -      local sn=getnext(start) +      local sn=start.next        for k=2,nofmultiples do          local n=copy_node(start)  -        setfield(n,"char",multiple[k]) -        setfield(n,"next",sn) -        setfield(n,"prev",start) +        n.char=multiple[k] +        n.next=sn +        n.prev=start          if sn then -          setfield(sn,"prev",n) +          sn.prev=n          end -        setfield(start,"next",n) +        start.next=n          start=n        end      end      return head,start,true    else      if trace_multiples then -      logprocess("no multiple for %s",gref(getchar(start))) +      logprocess("no multiple for %s",gref(start.char))      end      return head,start,false    end @@ -10088,34 +10047,34 @@ function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence    local choice,comment=get_alternative_glyph(start,alternative,value,trace_alternatives)    if choice then      if trace_alternatives then -      logprocess("%s: replacing %s by alternative %a to %s, %s",pref(kind,lookupname),gref(getchar(start)),choice,gref(choice),comment) +      logprocess("%s: replacing %s by alternative %a to %s, %s",pref(kind,lookupname),gref(start.char),choice,gref(choice),comment)      end -    setfield(start,"char",choice) +    start.char=choice    else      if trace_alternatives then -      logwarning("%s: no variant %a for %s, %s",pref(kind,lookupname),value,gref(getchar(start)),comment) +      logwarning("%s: no variant %a for %s, %s",pref(kind,lookupname),value,gref(start.char),comment)      end    end    return head,start,true  end  function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence)    if trace_multiples then -    logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(getchar(start)),gref(multiple)) +    logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple))    end    return multiple_glyphs(head,start,multiple,sequence.flags[1])  end  function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) -  local s,stop,discfound=getnext(start),nil,false -  local startchar=getchar(start) +  local s,stop,discfound=start.next,nil,false +  local startchar=start.char    if marks[startchar] then      while s do -      local id=getid(s) -      if id==glyph_code and getfont(s)==currentfont and getsubtype(s)<256 then -        local lg=ligature[getchar(s)] +      local id=s.id +      if id==glyph_code and s.font==currentfont and s.subtype<256 then +        local lg=ligature[s.char]          if lg then            stop=s            ligature=lg -          s=getnext(s) +          s=s.next          else            break          end @@ -10127,9 +10086,9 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)        local lig=ligature.ligature        if lig then          if trace_ligatures then -          local stopchar=getchar(stop) +          local stopchar=stop.char            head,start=markstoligature(kind,lookupname,head,start,stop,lig) -          logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(getchar(start))) +          logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char))          else            head,start=markstoligature(kind,lookupname,head,start,stop,lig)          end @@ -10140,18 +10099,18 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)    else      local skipmark=sequence.flags[1]      while s do -      local id=getid(s) -      if id==glyph_code and getsubtype(s)<256 then -        if getfont(s)==currentfont then -          local char=getchar(s) +      local id=s.id +      if id==glyph_code and s.subtype<256 then +        if s.font==currentfont then +          local char=s.char            if skipmark and marks[char] then -            s=getnext(s) +            s=s.next            else              local lg=ligature[char]              if lg then                stop=s                ligature=lg -              s=getnext(s) +              s=s.next              else                break              end @@ -10161,7 +10120,7 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)          end        elseif id==disc_code then          discfound=true -        s=getnext(s) +        s=s.next        else          break        end @@ -10170,35 +10129,36 @@ function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence)      if lig then        if stop then          if trace_ligatures then -          local stopchar=getchar(stop) +          local stopchar=stop.char            head,start=toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound) -          logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(getchar(start))) +          logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char))          else            head,start=toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound)          end +        return head,start,true        else -        setfield(start,"char",lig) +        start.char=lig          if trace_ligatures then            logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig))          end +        return head,start,true        end -      return head,start,true      else      end    end    return head,start,false  end  function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence) -  local markchar=getchar(start) +  local markchar=start.char    if marks[markchar] then -    local base=getprev(start)  -    if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then -      local basechar=getchar(base) +    local base=start.prev  +    if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then +      local basechar=base.char        if marks[basechar] then          while true do -          base=getprev(base) -          if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then -            basechar=getchar(base) +          base=base.prev +          if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then +            basechar=base.char              if not marks[basechar] then                break              end @@ -10247,16 +10207,16 @@ function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence    return head,start,false  end  function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequence) -  local markchar=getchar(start) +  local markchar=start.char    if marks[markchar] then -    local base=getprev(start)  -    if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then -      local basechar=getchar(base) +    local base=start.prev  +    if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then +      local basechar=base.char        if marks[basechar] then          while true do -          base=getprev(base) -          if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then -            basechar=getchar(base) +          base=base.prev +          if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then +            basechar=base.char              if not marks[basechar] then                break              end @@ -10268,7 +10228,7 @@ function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequ            end          end        end -      local index=getattr(start,a_ligacomp) +      local index=start[a_ligacomp]        local baseanchors=descriptions[basechar]        if baseanchors then          baseanchors=baseanchors.anchors @@ -10313,22 +10273,22 @@ function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequ    return head,start,false  end  function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence) -  local markchar=getchar(start) +  local markchar=start.char    if marks[markchar] then -    local base=getprev(start)  -    local slc=getattr(start,a_ligacomp) +    local base=start.prev  +    local slc=start[a_ligacomp]      if slc then         while base do -        local blc=getattr(base,a_ligacomp) +        local blc=base[a_ligacomp]          if blc and blc~=slc then -          base=getprev(base) +          base=base.prev          else            break          end        end      end -    if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then  -      local basechar=getchar(base) +    if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then  +      local basechar=base.char        local baseanchors=descriptions[basechar]        if baseanchors then          baseanchors=baseanchors.anchors @@ -10366,20 +10326,20 @@ function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence    return head,start,false  end  function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence)  -  local alreadydone=cursonce and getattr(start,a_cursbase) +  local alreadydone=cursonce and start[a_cursbase]    if not alreadydone then      local done=false -    local startchar=getchar(start) +    local startchar=start.char      if marks[startchar] then        if trace_cursive then          logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar))        end      else -      local nxt=getnext(start) -      while not done and nxt and getid(nxt)==glyph_code and getfont(nxt)==currentfont and getsubtype(nxt)<256 do -        local nextchar=getchar(nxt) +      local nxt=start.next +      while not done and nxt and nxt.id==glyph_code and nxt.font==currentfont and nxt.subtype<256 do +        local nextchar=nxt.char          if marks[nextchar] then -          nxt=getnext(nxt) +          nxt=nxt.next          else            local entryanchors=descriptions[nextchar]            if entryanchors then @@ -10413,13 +10373,13 @@ function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence)      return head,start,done    else      if trace_cursive and trace_details then -      logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(getchar(start)),alreadydone) +      logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone)      end      return head,start,false    end  end  function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence) -  local startchar=getchar(start) +  local startchar=start.char    local dx,dy,w,h=setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar])    if trace_kerns then      logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) @@ -10427,33 +10387,33 @@ function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence)    return head,start,false  end  function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence) -  local snext=getnext(start) +  local snext=start.next    if not snext then      return head,start,false    else      local prev,done=start,false      local factor=tfmdata.parameters.factor      local lookuptype=lookuptypes[lookupname] -    while snext and getid(snext)==glyph_code and getfont(snext)==currentfont and getsubtype(snext)<256 do -      local nextchar=getchar(snext) +    while snext and snext.id==glyph_code and snext.font==currentfont and snext.subtype<256 do +      local nextchar=snext.char        local krn=kerns[nextchar]        if not krn and marks[nextchar] then          prev=snext -        snext=getnext(snext) +        snext=snext.next        else          if not krn then          elseif type(krn)=="table" then            if lookuptype=="pair" then               local a,b=krn[2],krn[3]              if a and #a>0 then -              local startchar=getchar(start) +              local startchar=start.char                local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar])                if trace_kerns then                  logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h)                end              end              if b and #b>0 then -              local startchar=getchar(start) +              local startchar=start.char                local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar])                if trace_kerns then                  logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) @@ -10466,7 +10426,7 @@ function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence)          elseif krn~=0 then            local k=setkern(snext,factor,rlmode,krn)            if trace_kerns then -            logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(getchar(prev)),gref(nextchar)) +            logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar))            end            done=true          end @@ -10501,13 +10461,13 @@ function chainmores.chainsub(head,start,stop,kind,chainname,currentcontext,looku    return head,start,false  end  function chainprocs.reversesub(head,start,stop,kind,chainname,currentcontext,lookuphash,replacements) -  local char=getchar(start) +  local char=start.char    local replacement=replacements[char]    if replacement then      if trace_singles then        logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement))      end -    setfield(start,"char",replacement) +    start.char=replacement      return head,start,true    else      return head,start,false @@ -10520,8 +10480,8 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo      logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," "))    end    while current do -    if getid(current)==glyph_code then -      local currentchar=getchar(current) +    if current.id==glyph_code then +      local currentchar=current.char        local lookupname=subtables[1]         local replacement=lookuphash[lookupname]        if not replacement then @@ -10538,21 +10498,21 @@ function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lo            if trace_singles then              logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement))            end -          setfield(current,"char",replacement) +          current.char=replacement          end        end        return head,start,true      elseif current==stop then        break      else -      current=getnext(current) +      current=current.next      end    end    return head,start,false  end  chainmores.gsub_single=chainprocs.gsub_single  function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) -  local startchar=getchar(start) +  local startchar=start.char    local subtables=currentlookup.subtables    local lookupname=subtables[1]    local replacements=lookuphash[lookupname] @@ -10581,8 +10541,8 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext    local subtables=currentlookup.subtables    local value=featurevalue==true and tfmdata.shared.features[kind] or featurevalue    while current do -    if getid(current)==glyph_code then  -      local currentchar=getchar(current) +    if current.id==glyph_code then  +      local currentchar=current.char        local lookupname=subtables[1]        local alternatives=lookuphash[lookupname]        if not alternatives then @@ -10597,7 +10557,7 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext              if trace_alternatives then                logprocess("%s: replacing %s by alternative %a to %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(char),choice,gref(choice),comment)              end -            setfield(start,"char",choice) +            start.char=choice            else              if trace_alternatives then                logwarning("%s: no variant %a for %s, %s",cref(kind,chainname,chainlookupname,lookupname),value,gref(char),comment) @@ -10611,14 +10571,14 @@ function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext      elseif current==stop then        break      else -      current=getnext(current) +      current=current.next      end    end    return head,start,false  end  chainmores.gsub_alternate=chainprocs.gsub_alternate  function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) -  local startchar=getchar(start) +  local startchar=start.char    local subtables=currentlookup.subtables    local lookupname=subtables[1]    local ligatures=lookuphash[lookupname] @@ -10633,20 +10593,20 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,          logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))        end      else -      local s=getnext(start) +      local s=start.next        local discfound=false        local last=stop        local nofreplacements=0        local skipmark=currentlookup.flags[1]        while s do -        local id=getid(s) +        local id=s.id          if id==disc_code then -          s=getnext(s) +          s=s.next            discfound=true          else -          local schar=getchar(s) +          local schar=s.char            if skipmark and marks[schar] then  -            s=getnext(s) +            s=s.next            else              local lg=ligatures[schar]              if lg then @@ -10654,7 +10614,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,                if s==stop then                  break                else -                s=getnext(s) +                s=s.next                end              else                break @@ -10671,7 +10631,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,            if start==stop then              logprocess("%s: replacing character %s by ligature %s case 3",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2))            else -            logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(getchar(stop)),gref(l2)) +            logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2))            end          end          head,start=toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound) @@ -10680,7 +10640,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,          if start==stop then            logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar))          else -          logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(getchar(stop))) +          logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char))          end        end      end @@ -10689,7 +10649,7 @@ function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,  end  chainmores.gsub_ligature=chainprocs.gsub_ligature  function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) -  local markchar=getchar(start) +  local markchar=start.char    if marks[markchar] then      local subtables=currentlookup.subtables      local lookupname=subtables[1] @@ -10698,14 +10658,14 @@ function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext        markanchors=markanchors[markchar]      end      if markanchors then -      local base=getprev(start)  -      if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then -        local basechar=getchar(base) +      local base=start.prev  +      if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then +        local basechar=base.char          if marks[basechar] then            while true do -            base=getprev(base) -            if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then -              basechar=getchar(base) +            base=base.prev +            if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then +              basechar=base.char                if not marks[basechar] then                  break                end @@ -10752,7 +10712,7 @@ function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext    return head,start,false  end  function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) -  local markchar=getchar(start) +  local markchar=start.char    if marks[markchar] then      local subtables=currentlookup.subtables      local lookupname=subtables[1] @@ -10761,14 +10721,14 @@ function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcon        markanchors=markanchors[markchar]      end      if markanchors then -      local base=getprev(start)  -      if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then -        local basechar=getchar(base) +      local base=start.prev  +      if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then +        local basechar=base.char          if marks[basechar] then            while true do -            base=getprev(base) -            if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then -              basechar=getchar(base) +            base=base.prev +            if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then +              basechar=base.char                if not marks[basechar] then                  break                end @@ -10780,7 +10740,7 @@ function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcon              end            end          end -        local index=getattr(start,a_ligacomp) +        local index=start[a_ligacomp]          local baseanchors=descriptions[basechar].anchors          if baseanchors then            local baseanchors=baseanchors['baselig'] @@ -10819,7 +10779,7 @@ function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcon    return head,start,false  end  function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) -  local markchar=getchar(start) +  local markchar=start.char    if marks[markchar] then        local subtables=currentlookup.subtables        local lookupname=subtables[1] @@ -10828,20 +10788,20 @@ function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext          markanchors=markanchors[markchar]        end        if markanchors then -        local base=getprev(start)  -        local slc=getattr(start,a_ligacomp) +        local base=start.prev  +        local slc=start[a_ligacomp]          if slc then             while base do -            local blc=getattr(base,a_ligacomp) +            local blc=base[a_ligacomp]              if blc and blc~=slc then -              base=getprev(base) +              base=base.prev              else                break              end            end          end -        if base and getid(base)==glyph_code and getfont(base)==currentfont and getsubtype(base)<256 then  -          local basechar=getchar(base) +        if base and base.id==glyph_code and base.font==currentfont and base.subtype<256 then  +          local basechar=base.char            local baseanchors=descriptions[basechar].anchors            if baseanchors then              baseanchors=baseanchors['basemark'] @@ -10877,9 +10837,9 @@ function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext    return head,start,false  end  function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) -  local alreadydone=cursonce and getattr(start,a_cursbase) +  local alreadydone=cursonce and start[a_cursbase]    if not alreadydone then -    local startchar=getchar(start) +    local startchar=start.char      local subtables=currentlookup.subtables      local lookupname=subtables[1]      local exitanchors=lookuphash[lookupname] @@ -10893,11 +10853,11 @@ function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,l            logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar))          end        else -        local nxt=getnext(start) -        while not done and nxt and getid(nxt)==glyph_code and getfont(nxt)==currentfont and getsubtype(nxt)<256 do -          local nextchar=getchar(nxt) +        local nxt=start.next +        while not done and nxt and nxt.id==glyph_code and nxt.font==currentfont and nxt.subtype<256 do +          local nextchar=nxt.char            if marks[nextchar] then -            nxt=getnext(nxt) +            nxt=nxt.next            else              local entryanchors=descriptions[nextchar]              if entryanchors then @@ -10931,7 +10891,7 @@ function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,l        return head,start,done      else        if trace_cursive and trace_details then -        logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(getchar(start)),alreadydone) +        logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone)        end        return head,start,false      end @@ -10939,7 +10899,7 @@ function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,l    return head,start,false  end  function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) -  local startchar=getchar(start) +  local startchar=start.char    local subtables=currentlookup.subtables    local lookupname=subtables[1]    local kerns=lookuphash[lookupname] @@ -10956,9 +10916,9 @@ function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lo  end  chainmores.gpos_single=chainprocs.gpos_single  function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) -  local snext=getnext(start) +  local snext=start.next    if snext then -    local startchar=getchar(start) +    local startchar=start.char      local subtables=currentlookup.subtables      local lookupname=subtables[1]      local kerns=lookuphash[lookupname] @@ -10968,26 +10928,26 @@ function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,look          local lookuptype=lookuptypes[lookupname]          local prev,done=start,false          local factor=tfmdata.parameters.factor -        while snext and getid(snext)==glyph_code and getfont(snext)==currentfont and getsubtype(snext)<256 do -          local nextchar=getchar(snext) +        while snext and snext.id==glyph_code and snext.font==currentfont and snext.subtype<256 do +          local nextchar=snext.char            local krn=kerns[nextchar]            if not krn and marks[nextchar] then              prev=snext -            snext=getnext(snext) +            snext=snext.next            else              if not krn then              elseif type(krn)=="table" then                if lookuptype=="pair" then                  local a,b=krn[2],krn[3]                  if a and #a>0 then -                  local startchar=getchar(start) +                  local startchar=start.char                    local x,y,w,h=setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar])                    if trace_kerns then                      logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h)                    end                  end                  if b and #b>0 then -                  local startchar=getchar(start) +                  local startchar=start.char                    local x,y,w,h=setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar])                    if trace_kerns then                      logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) @@ -10999,7 +10959,7 @@ function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,look                  if a and a~=0 then                    local k=setkern(snext,factor,rlmode,a)                    if trace_kerns then -                    logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar)) +                    logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar))                    end                  end                  if b and b~=0 then @@ -11010,7 +10970,7 @@ function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,look              elseif krn~=0 then                local k=setkern(snext,factor,rlmode,krn)                if trace_kerns then -                logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(getchar(prev)),gref(nextchar)) +                logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar))                end                done=true              end @@ -11031,10 +10991,6 @@ local function show_skip(kind,chainname,char,ck,class)      logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2])    end  end -local quit_on_no_replacement=true -directives.register("otf.chain.quitonnoreplacement",function(value)  -  quit_on_no_replacement=value -end)  local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash)    local flags=sequence.flags    local done=false @@ -11052,7 +11008,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq      local seq=ck[3]      local s=#seq      if s==1 then -      match=getid(current)==glyph_code and getfont(current)==currentfont and getsubtype(current)<256 and seq[1][getchar(current)] +      match=current.id==glyph_code and current.font==currentfont and current.subtype<256 and seq[1][current.char]      else        local f,l=ck[4],ck[5]        if f==1 and f==l then @@ -11060,13 +11016,13 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq          if f==l then          else            local n=f+1 -          last=getnext(last) +          last=last.next            while n<=l do              if last then -              local id=getid(last) +              local id=last.id                if id==glyph_code then -                if getfont(last)==currentfont and getsubtype(last)<256 then -                  local char=getchar(last) +                if last.font==currentfont and last.subtype<256 then +                  local char=last.char                    local ccd=descriptions[char]                    if ccd then                      local class=ccd.class @@ -11075,10 +11031,10 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                        if trace_skips then                          show_skip(kind,chainname,char,ck,class)                        end -                      last=getnext(last) +                      last=last.next                      elseif seq[n][char] then                        if n<l then -                        last=getnext(last) +                        last=last.next                        end                        n=n+1                      else @@ -11094,7 +11050,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                    break                  end                elseif id==disc_code then -                last=getnext(last) +                last=last.next                else                  match=false                  break @@ -11107,15 +11063,15 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq          end        end        if match and f>1 then -        local prev=getprev(start) +        local prev=start.prev          if prev then            local n=f-1            while n>=1 do              if prev then -              local id=getid(prev) +              local id=prev.id                if id==glyph_code then -                if getfont(prev)==currentfont and getsubtype(prev)<256 then  -                  local char=getchar(prev) +                if prev.font==currentfont and prev.subtype<256 then  +                  local char=prev.char                    local ccd=descriptions[char]                    if ccd then                      local class=ccd.class @@ -11145,7 +11101,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                  match=false                  break                end -              prev=getprev(prev) +              prev=prev.prev              elseif seq[n][32] then                 n=n -1              else @@ -11165,15 +11121,15 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq          end        end        if match and s>l then -        local current=last and getnext(last) +        local current=last and last.next          if current then            local n=l+1            while n<=s do              if current then -              local id=getid(current) +              local id=current.id                if id==glyph_code then -                if getfont(current)==currentfont and getsubtype(current)<256 then  -                  local char=getchar(current) +                if current.font==currentfont and current.subtype<256 then  +                  local char=current.char                    local ccd=descriptions[char]                    if ccd then                      local class=ccd.class @@ -11203,7 +11159,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                  match=false                  break                end -              current=getnext(current) +              current=current.next              elseif seq[n][32] then                n=n+1              else @@ -11226,7 +11182,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq      if match then        if trace_contexts then          local rule,lookuptype,f,l=ck[1],ck[2],ck[4],ck[5] -        local char=getchar(start) +        local char=start.char          if ck[9] then            logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a, %a => %a",              cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10]) @@ -11260,12 +11216,12 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq            repeat              if skipped then                while true do -                local char=getchar(start) +                local char=start.char                  local ccd=descriptions[char]                  if ccd then                    local class=ccd.class                    if class==skipmark or class==skipligature or class==skipbase or (markclass and class=="mark" and not markclass[char]) then -                    start=getnext(start) +                    start=start.next                    else                      break                    end @@ -11295,7 +11251,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq                end              end              if start then -              start=getnext(start) +              start=start.next              else              end            until i>nofchainlookups @@ -11305,7 +11261,7 @@ local function normal_handle_contextchain(head,start,kind,chainname,contexts,seq          if replacements then            head,start,done=chainprocs.reversesub(head,start,last,kind,chainname,ck,lookuphash,replacements)           else -          done=quit_on_no_replacement  +          done=true             if trace_contexts then              logprocess("%s: skipping match",cref(kind,chainname))            end @@ -11422,7 +11378,6 @@ local function featuresprocessor(head,font,attr)    if not lookuphash then      return head,false    end -  head=tonut(head)    if trace_steps then      checkstep(head)    end @@ -11455,10 +11410,10 @@ local function featuresprocessor(head,font,attr)        local handler=handlers[typ]        local start=find_node_tail(head)         while start do -        local id=getid(start) +        local id=start.id          if id==glyph_code then -          if getfont(start)==font and getsubtype(start)<256 then -            local a=getattr(start,0) +          if start.font==font and start.subtype<256 then +            local a=start[0]              if a then                a=a==attr              else @@ -11469,7 +11424,7 @@ local function featuresprocessor(head,font,attr)                  local lookupname=subtables[i]                  local lookupcache=lookuphash[lookupname]                  if lookupcache then -                  local lookupmatch=lookupcache[getchar(start)] +                  local lookupmatch=lookupcache[start.char]                    if lookupmatch then                      head,start,success=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)                      if success then @@ -11480,15 +11435,15 @@ local function featuresprocessor(head,font,attr)                    report_missing_cache(typ,lookupname)                  end                end -              if start then start=getprev(start) end +              if start then start=start.prev end              else -              start=getprev(start) +              start=start.prev              end            else -            start=getprev(start) +            start=start.prev            end          else -          start=getprev(start) +          start=start.prev          end        end      else @@ -11506,16 +11461,16 @@ local function featuresprocessor(head,font,attr)              local head=start              local done=false              while start do -              local id=getid(start) -              if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then -                local a=getattr(start,0) +              local id=start.id +              if id==glyph_code and start.font==font and start.subtype<256 then +                local a=start[0]                  if a then -                  a=(a==attr) and (not attribute or getattr(start,a_state)==attribute) +                  a=(a==attr) and (not attribute or start[a_state]==attribute)                  else -                  a=not attribute or getattr(start,a_state)==attribute +                  a=not attribute or start[a_state]==attribute                  end                  if a then -                  local lookupmatch=lookupcache[getchar(start)] +                  local lookupmatch=lookupcache[start.char]                    if lookupmatch then                      local ok                      head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) @@ -11523,12 +11478,12 @@ local function featuresprocessor(head,font,attr)                        done=true                      end                    end -                  if start then start=getnext(start) end +                  if start then start=start.next end                  else -                  start=getnext(start) +                  start=start.next                  end                else -                start=getnext(start) +                start=start.next                end              end              if done then @@ -11537,18 +11492,18 @@ local function featuresprocessor(head,font,attr)              end            end            local function kerndisc(disc)  -            local prev=getprev(disc) -            local next=getnext(disc) +            local prev=disc.prev +            local next=disc.next              if prev and next then -              setfield(prev,"next",next) -              local a=getattr(prev,0) +              prev.next=next +              local a=prev[0]                if a then -                a=(a==attr) and (not attribute or getattr(prev,a_state)==attribute) +                a=(a==attr) and (not attribute or prev[a_state]==attribute)                else -                a=not attribute or getattr(prev,a_state)==attribute +                a=not attribute or prev[a_state]==attribute                end                if a then -                local lookupmatch=lookupcache[getchar(prev)] +                local lookupmatch=lookupcache[prev.char]                  if lookupmatch then                    local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1)                    if ok then @@ -11557,22 +11512,22 @@ local function featuresprocessor(head,font,attr)                    end                  end                end -              setfield(prev,"next",disc) +              prev.next=disc              end              return next            end            while start do -            local id=getid(start) +            local id=start.id              if id==glyph_code then -              if getfont(start)==font and getsubtype(start)<256 then -                local a=getattr(start,0) +              if start.font==font and start.subtype<256 then +                local a=start[0]                  if a then -                  a=(a==attr) and (not attribute or getattr(start,a_state)==attribute) +                  a=(a==attr) and (not attribute or start[a_state]==attribute)                  else -                  a=not attribute or getattr(start,a_state)==attribute +                  a=not attribute or start[a_state]==attribute                  end                  if a then -                  local lookupmatch=lookupcache[getchar(start)] +                  local lookupmatch=lookupcache[start.char]                    if lookupmatch then                      local ok                      head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) @@ -11580,38 +11535,38 @@ local function featuresprocessor(head,font,attr)                        success=true                      end                    end -                  if start then start=getnext(start) end +                  if start then start=start.next end                  else -                  start=getnext(start) +                  start=start.next                  end                else -                start=getnext(start) +                start=start.next                end              elseif id==disc_code then -              if getsubtype(start)==discretionary_code then -                local pre=getfield(start,"pre") +              if start.subtype==discretionary_code then +                local pre=start.pre                  if pre then                    local new=subrun(pre) -                  if new then setfield(start,"pre",new) end +                  if new then start.pre=new end                  end -                local post=getfield(start,"post") +                local post=start.post                  if post then                    local new=subrun(post) -                  if new then setfield(start,"post",new) end +                  if new then start.post=new end                  end -                local replace=getfield(start,"replace") +                local replace=start.replace                  if replace then                    local new=subrun(replace) -                  if new then setfield(start,"replace",new) end +                  if new then start.replace=new end                  end  elseif typ=="gpos_single" or typ=="gpos_pair" then    kerndisc(start)                end -              start=getnext(start) +              start=start.next              elseif id==whatsit_code then  -              local subtype=getsubtype(start) +              local subtype=start.subtype                if subtype==dir_code then -                local dir=getfield(start,"dir") +                local dir=start.dir                  if   dir=="+TRT" or dir=="+TLT" then                    topstack=topstack+1                    dirstack[topstack]=dir @@ -11630,7 +11585,7 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then                    report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir)                  end                elseif subtype==localpar_code then -                local dir=getfield(start,"dir") +                local dir=start.dir                  if dir=="TRT" then                    rlparmode=-1                  elseif dir=="TLT" then @@ -11643,11 +11598,11 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then                    report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode)                  end                end -              start=getnext(start) +              start=start.next              elseif id==math_code then -              start=getnext(end_of_math(start)) +              start=end_of_math(start).next              else -              start=getnext(start) +              start=start.next              end            end          end @@ -11656,20 +11611,20 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then            local head=start            local done=false            while start do -            local id=getid(start) -            if id==glyph_code and getfont(start)==font and getsubtype(start)<256 then -              local a=getattr(start,0) +            local id=start.id +            if id==glyph_code and start.id==font and start.subtype<256 then +              local a=start[0]                if a then -                a=(a==attr) and (not attribute or getattr(start,a_state)==attribute) +                a=(a==attr) and (not attribute or start[a_state]==attribute)                else -                a=not attribute or getattr(start,a_state)==attribute +                a=not attribute or start[a_state]==attribute                end                if a then                  for i=1,ns do                    local lookupname=subtables[i]                    local lookupcache=lookuphash[lookupname]                    if lookupcache then -                    local lookupmatch=lookupcache[getchar(start)] +                    local lookupmatch=lookupcache[start.char]                      if lookupmatch then                        local ok                        head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) @@ -11684,12 +11639,12 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then                      report_missing_cache(typ,lookupname)                    end                  end -                if start then start=getnext(start) end +                if start then start=start.next end                else -                start=getnext(start) +                start=start.next                end              else -              start=getnext(start) +              start=start.next              end            end            if done then @@ -11698,22 +11653,22 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then            end          end          local function kerndisc(disc)  -          local prev=getprev(disc) -          local next=getnext(disc) +          local prev=disc.prev +          local next=disc.next            if prev and next then -            setfield(prev,"next",next) -            local a=getattr(prev,0) +            prev.next=next +            local a=prev[0]              if a then -              a=(a==attr) and (not attribute or getattr(prev,a_state)==attribute) +              a=(a==attr) and (not attribute or prev[a_state]==attribute)              else -              a=not attribute or getattr(prev,a_state)==attribute +              a=not attribute or prev[a_state]==attribute              end              if a then                for i=1,ns do                  local lookupname=subtables[i]                  local lookupcache=lookuphash[lookupname]                  if lookupcache then -                  local lookupmatch=lookupcache[getchar(prev)] +                  local lookupmatch=lookupcache[prev.char]                    if lookupmatch then                      local h,d,ok=handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i)                      if ok then @@ -11726,26 +11681,26 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then                  end                end              end -            setfield(prev,"next",disc) +            prev.next=disc            end            return next          end          while start do -          local id=getid(start) +          local id=start.id            if id==glyph_code then -            if getfont(start)==font and getsubtype(start)<256 then -              local a=getattr(start,0) +            if start.font==font and start.subtype<256 then +              local a=start[0]                if a then -                a=(a==attr) and (not attribute or getattr(start,a_state)==attribute) +                a=(a==attr) and (not attribute or start[a_state]==attribute)                else -                a=not attribute or getattr(start,a_state)==attribute +                a=not attribute or start[a_state]==attribute                end                if a then                  for i=1,ns do                    local lookupname=subtables[i]                    local lookupcache=lookuphash[lookupname]                    if lookupcache then -                    local lookupmatch=lookupcache[getchar(start)] +                    local lookupmatch=lookupcache[start.char]                      if lookupmatch then                        local ok                        head,start,ok=handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) @@ -11760,38 +11715,38 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then                      report_missing_cache(typ,lookupname)                    end                  end -                if start then start=getnext(start) end +                if start then start=start.next end                else -                start=getnext(start) +                start=start.next                end              else -              start=getnext(start) +              start=start.next              end            elseif id==disc_code then -            if getsubtype(start)==discretionary_code then -              local pre=getfield(start,"pre") +            if start.subtype==discretionary_code then +              local pre=start.pre                if pre then                  local new=subrun(pre) -                if new then setfield(start,"pre",new) end +                if new then start.pre=new end                end -              local post=getfield(start,"post") +              local post=start.post                if post then                  local new=subrun(post) -                if new then setfield(start,"post",new) end +                if new then start.post=new end                end -              local replace=getfield(start,"replace") +              local replace=start.replace                if replace then                  local new=subrun(replace) -                if new then setfield(start,"replace",new) end +                if new then start.replace=new end                end  elseif typ=="gpos_single" or typ=="gpos_pair" then    kerndisc(start)              end -            start=getnext(start) +            start=start.next            elseif id==whatsit_code then -            local subtype=getsubtype(start) +            local subtype=start.subtype              if subtype==dir_code then -              local dir=getfield(start,"dir") +              local dir=start.dir                if   dir=="+TRT" or dir=="+TLT" then                  topstack=topstack+1                  dirstack[topstack]=dir @@ -11810,7 +11765,7 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then                  report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir)                end              elseif subtype==localpar_code then -              local dir=getfield(start,"dir") +              local dir=start.dir                if dir=="TRT" then                  rlparmode=-1                elseif dir=="TLT" then @@ -11823,11 +11778,11 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then                  report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode)                end              end -            start=getnext(start) +            start=start.next            elseif id==math_code then -            start=getnext(end_of_math(start)) +            start=end_of_math(start).next            else -            start=getnext(start) +            start=start.next            end          end        end @@ -11839,7 +11794,6 @@ elseif typ=="gpos_single" or typ=="gpos_pair" then        registerstep(head)      end    end -  head=tonode(head)    return head,done  end  local function generic(lookupdata,lookupname,unicode,lookuphash) diff --git a/tex/generic/context/luatex/luatex-fonts-otn.lua b/tex/generic/context/luatex/luatex-fonts-otn.lua new file mode 100644 index 000000000..c57be5f02 --- /dev/null +++ b/tex/generic/context/luatex/luatex-fonts-otn.lua @@ -0,0 +1,2848 @@ +if not modules then modules = { } end modules ['font-otn'] = { +    version   = 1.001, +    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", +} + +-- preprocessors = { "nodes" } + +-- this is still somewhat preliminary and it will get better in due time; +-- much functionality could only be implemented thanks to the husayni font +-- of Idris Samawi Hamid to who we dedicate this module. + +-- in retrospect it always looks easy but believe it or not, it took a lot +-- of work to get proper open type support done: buggy fonts, fuzzy specs, +-- special made testfonts, many skype sessions between taco, idris and me, +-- torture tests etc etc ... unfortunately the code does not show how much +-- time it took ... + +-- todo: +-- +-- kerning is probably not yet ok for latin around dics nodes (interesting challenge) +-- extension infrastructure (for usage out of context) +-- sorting features according to vendors/renderers +-- alternative loop quitters +-- check cursive and r2l +-- find out where ignore-mark-classes went +-- default features (per language, script) +-- handle positions (we need example fonts) +-- handle gpos_single (we might want an extra width field in glyph nodes because adding kerns might interfere) +-- mark (to mark) code is still not what it should be (too messy but we need some more extreem husayni tests) +-- remove some optimizations (when I have a faster machine) +-- +-- maybe redo the lot some way (more context specific) + +--[[ldx-- +<p>This module is a bit more split up that I'd like but since we also want to test +with plain <l n='tex'/> it has to be so. This module is part of <l n='context'/> +and discussion about improvements and functionality mostly happens on the +<l n='context'/> mailing list.</p> + +<p>The specification of OpenType is kind of vague. Apart from a lack of a proper +free specifications there's also the problem that Microsoft and Adobe +may have their own interpretation of how and in what order to apply features. +In general the Microsoft website has more detailed specifications and is a +better reference. There is also some information in the FontForge help files.</p> + +<p>Because there is so much possible, fonts might contain bugs and/or be made to +work with certain rederers. These may evolve over time which may have the side +effect that suddenly fonts behave differently.</p> + +<p>After a lot of experiments (mostly by Taco, me and Idris) we're now at yet another +implementation. Of course all errors are mine and of course the code can be +improved. There are quite some optimizations going on here and processing speed +is currently acceptable. Not all functions are implemented yet, often because I +lack the fonts for testing. Many scripts are not yet supported either, but I will +look into them as soon as <l n='context'/> users ask for it.</p> + +<p>Because there are different interpretations possible, I will extend the code +with more (configureable) variants. I can also add hooks for users so that they can +write their own extensions.</p> + +<p>Glyphs are indexed not by unicode but in their own way. This is because there is no +relationship with unicode at all, apart from the fact that a font might cover certain +ranges of characters. One character can have multiple shapes. However, at the +<l n='tex'/> end we use unicode so and all extra glyphs are mapped into a private +space. This is needed because we need to access them and <l n='tex'/> has to include +then in the output eventually.</p> + +<p>The raw table as it coms from <l n='fontforge'/> gets reorganized in to fit out needs. +In <l n='context'/> that table is packed (similar tables are shared) and cached on disk +so that successive runs can use the optimized table (after loading the table is +unpacked). The flattening code used later is a prelude to an even more compact table +format (and as such it keeps evolving).</p> + +<p>This module is sparsely documented because it is a moving target. The table format +of the reader changes and we experiment a lot with different methods for supporting +features.</p> + +<p>As with the <l n='afm'/> code, we may decide to store more information in the +<l n='otf'/> table.</p> + +<p>Incrementing the version number will force a re-cache. We jump the number by one +when there's a fix in the <l n='fontforge'/> library or <l n='lua'/> code that +results in different tables.</p> +--ldx]]-- + +-- action                    handler     chainproc             chainmore              comment +-- +-- gsub_single               ok          ok                    ok +-- gsub_multiple             ok          ok                    not implemented yet +-- gsub_alternate            ok          ok                    not implemented yet +-- gsub_ligature             ok          ok                    ok +-- gsub_context              ok          -- +-- gsub_contextchain         ok          -- +-- gsub_reversecontextchain  ok          -- +-- chainsub                  --          ok +-- reversesub                --          ok +-- gpos_mark2base            ok          ok +-- gpos_mark2ligature        ok          ok +-- gpos_mark2mark            ok          ok +-- gpos_cursive              ok          untested +-- gpos_single               ok          ok +-- gpos_pair                 ok          ok +-- gpos_context              ok          -- +-- gpos_contextchain         ok          -- +-- +-- todo: contextpos and contextsub and class stuff +-- +-- actions: +-- +-- handler   : actions triggered by lookup +-- chainproc : actions triggered by contextual lookup +-- chainmore : multiple substitutions triggered by contextual lookup (e.g. fij -> f + ij) +-- +-- remark: the 'not implemented yet' variants will be done when we have fonts that use them +-- remark: we need to check what to do with discretionaries + +-- We used to have independent hashes for lookups but as the tags are unique +-- we now use only one hash. If needed we can have multiple again but in that +-- case I will probably prefix (i.e. rename) the lookups in the cached font file. + +-- Todo: make plugin feature that operates on char/glyphnode arrays + +local concat, insert, remove = table.concat, table.insert, table.remove +local gmatch, gsub, find, match, lower, strip = string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip +local type, next, tonumber, tostring = type, next, tonumber, tostring +local lpegmatch = lpeg.match +local random = math.random +local formatters = string.formatters + +local logs, trackers, nodes, attributes = logs, trackers, nodes, attributes + +local registertracker = trackers.register + +local fonts = fonts +local otf   = fonts.handlers.otf + +local trace_lookups      = false  registertracker("otf.lookups",      function(v) trace_lookups      = v end) +local trace_singles      = false  registertracker("otf.singles",      function(v) trace_singles      = v end) +local trace_multiples    = false  registertracker("otf.multiples",    function(v) trace_multiples    = v end) +local trace_alternatives = false  registertracker("otf.alternatives", function(v) trace_alternatives = v end) +local trace_ligatures    = false  registertracker("otf.ligatures",    function(v) trace_ligatures    = v end) +local trace_contexts     = false  registertracker("otf.contexts",     function(v) trace_contexts     = v end) +local trace_marks        = false  registertracker("otf.marks",        function(v) trace_marks        = v end) +local trace_kerns        = false  registertracker("otf.kerns",        function(v) trace_kerns        = v end) +local trace_cursive      = false  registertracker("otf.cursive",      function(v) trace_cursive      = v end) +local trace_preparing    = false  registertracker("otf.preparing",    function(v) trace_preparing    = v end) +local trace_bugs         = false  registertracker("otf.bugs",         function(v) trace_bugs         = v end) +local trace_details      = false  registertracker("otf.details",      function(v) trace_details      = v end) +local trace_applied      = false  registertracker("otf.applied",      function(v) trace_applied      = v end) +local trace_steps        = false  registertracker("otf.steps",        function(v) trace_steps        = v end) +local trace_skips        = false  registertracker("otf.skips",        function(v) trace_skips        = v end) +local trace_directions   = false  registertracker("otf.directions",   function(v) trace_directions   = v end) + +local report_direct   = logs.reporter("fonts","otf direct") +local report_subchain = logs.reporter("fonts","otf subchain") +local report_chain    = logs.reporter("fonts","otf chain") +local report_process  = logs.reporter("fonts","otf process") +local report_prepare  = logs.reporter("fonts","otf prepare") +local report_warning  = logs.reporter("fonts","otf warning") + +registertracker("otf.verbose_chain", function(v) otf.setcontextchain(v and "verbose") end) +registertracker("otf.normal_chain",  function(v) otf.setcontextchain(v and "normal")  end) + +registertracker("otf.replacements", "otf.singles,otf.multiples,otf.alternatives,otf.ligatures") +registertracker("otf.positions","otf.marks,otf.kerns,otf.cursive") +registertracker("otf.actions","otf.replacements,otf.positions") +registertracker("otf.injections","nodes.injections") + +registertracker("*otf.sample","otf.steps,otf.actions,otf.analyzing") + +local insert_node_after  = node.insert_after +local delete_node        = nodes.delete +local copy_node          = node.copy +local find_node_tail     = node.tail or node.slide +local flush_node_list    = node.flush_list +local end_of_math        = node.end_of_math + +local setmetatableindex  = table.setmetatableindex + +local zwnj               = 0x200C +local zwj                = 0x200D +local wildcard           = "*" +local default            = "dflt" + +local nodecodes          = nodes.nodecodes +local whatcodes          = nodes.whatcodes +local glyphcodes         = nodes.glyphcodes +local disccodes          = nodes.disccodes + +local glyph_code         = nodecodes.glyph +local glue_code          = nodecodes.glue +local disc_code          = nodecodes.disc +local whatsit_code       = nodecodes.whatsit +local math_code          = nodecodes.math + +local dir_code           = whatcodes.dir +local localpar_code      = whatcodes.localpar + +local discretionary_code = disccodes.discretionary + +local ligature_code      = glyphcodes.ligature + +local privateattribute   = attributes.private + +-- Something is messed up: we have two mark / ligature indices, one at the injection +-- end and one here ... this is bases in KE's patches but there is something fishy +-- there as I'm pretty sure that for husayni we need some connection (as it's much +-- more complex than an average font) but I need proper examples of all cases, not +-- of only some. + +local a_state            = privateattribute('state') +local a_markbase         = privateattribute('markbase') +local a_markmark         = privateattribute('markmark') +local a_markdone         = privateattribute('markdone') -- assigned at the injection end +local a_cursbase         = privateattribute('cursbase') +local a_curscurs         = privateattribute('curscurs') +local a_cursdone         = privateattribute('cursdone') +local a_kernpair         = privateattribute('kernpair') +local a_ligacomp         = privateattribute('ligacomp') -- assigned here (ideally it should be combined) + +local injections         = nodes.injections +local setmark            = injections.setmark +local setcursive         = injections.setcursive +local setkern            = injections.setkern +local setpair            = injections.setpair + +local markonce           = true +local cursonce           = true +local kernonce           = true + +local fonthashes         = fonts.hashes +local fontdata           = fonthashes.identifiers + +local otffeatures        = fonts.constructors.newfeatures("otf") +local registerotffeature = otffeatures.register + +local onetimemessage     = fonts.loggers.onetimemessage or function() end + +otf.defaultnodealternate = "none" -- first last + +-- we share some vars here, after all, we have no nested lookups and less code + +local tfmdata             = false +local characters          = false +local descriptions        = false +local resources           = false +local marks               = false +local currentfont         = false +local lookuptable         = false +local anchorlookups       = false +local lookuptypes         = false +local handlers            = { } +local rlmode              = 0 +local featurevalue        = false + +-- head is always a whatsit so we can safely assume that head is not changed + +-- we use this for special testing and documentation + +local checkstep       = (nodes and nodes.tracers and nodes.tracers.steppers.check)    or function() end +local registerstep    = (nodes and nodes.tracers and nodes.tracers.steppers.register) or function() end +local registermessage = (nodes and nodes.tracers and nodes.tracers.steppers.message)  or function() end + +local function logprocess(...) +    if trace_steps then +        registermessage(...) +    end +    report_direct(...) +end + +local function logwarning(...) +    report_direct(...) +end + +local f_unicode = formatters["%U"] +local f_uniname = formatters["%U (%s)"] +local f_unilist = formatters["% t (% t)"] + +local function gref(n) -- currently the same as in font-otb +    if type(n) == "number" then +        local description = descriptions[n] +        local name = description and description.name +        if name then +            return f_uniname(n,name) +        else +            return f_unicode(n) +        end +    elseif n then +        local num, nam = { }, { } +        for i=1,#n do +            local ni = n[i] +            if tonumber(ni) then -- later we will start at 2 +                local di = descriptions[ni] +                num[i] = f_unicode(ni) +                nam[i] = di and di.name or "-" +            end +        end +        return f_unilist(num,nam) +    else +        return "<error in node mode tracing>" +    end +end + +local function cref(kind,chainname,chainlookupname,lookupname,index) -- not in the mood to alias f_ +    if index then +        return formatters["feature %a, chain %a, sub %a, lookup %a, index %a"](kind,chainname,chainlookupname,lookupname,index) +    elseif lookupname then +        return formatters["feature %a, chain %a, sub %a, lookup %a"](kind,chainname,chainlookupname,lookupname) +    elseif chainlookupname then +        return formatters["feature %a, chain %a, sub %a"](kind,chainname,chainlookupname) +    elseif chainname then +        return formatters["feature %a, chain %a"](kind,chainname) +    else +        return formatters["feature %a"](kind) +    end +end + +local function pref(kind,lookupname) +    return formatters["feature %a, lookup %a"](kind,lookupname) +end + +-- We can assume that languages that use marks are not hyphenated. We can also assume +-- that at most one discretionary is present. + +-- We do need components in funny kerning mode but maybe I can better reconstruct then +-- as we do have the font components info available; removing components makes the +-- previous code much simpler. Also, later on copying and freeing becomes easier. +-- However, for arabic we need to keep them around for the sake of mark placement +-- and indices. + +local function copy_glyph(g) -- next and prev are untouched ! +    local components = g.components +    if components then +        g.components = nil +        local n = copy_node(g) +        g.components = components +        return n +    else +        return copy_node(g) +    end +end + +-- start is a mark and we need to keep that one + +local function markstoligature(kind,lookupname,head,start,stop,char) +    if start == stop and start.char == char then +        return head, start +    else +        local prev = start.prev +        local next = stop.next +        start.prev = nil +        stop.next = nil +        local base = copy_glyph(start) +        if head == start then +            head = base +        end +        base.char = char +        base.subtype = ligature_code +        base.components = start +        if prev then +            prev.next = base +        end +        if next then +            next.prev = base +        end +        base.next = next +        base.prev = prev +        return head, base +    end +end + +-- The next code is somewhat complicated by the fact that some fonts can have ligatures made +-- from ligatures that themselves have marks. This was identified by Kai in for instance +-- arabtype:  KAF LAM SHADDA ALEF FATHA (0x0643 0x0644 0x0651 0x0627 0x064E). This becomes +-- KAF LAM-ALEF with a SHADDA on the first and a FATHA op de second component. In a next +-- iteration this becomes a KAF-LAM-ALEF with a SHADDA on the second and a FATHA on the +-- third component. + +local function getcomponentindex(start) +    if start.id ~= glyph_code then +        return 0 +    elseif start.subtype == ligature_code then +        local i = 0 +        local components = start.components +        while components do +            i = i + getcomponentindex(components) +            components = components.next +        end +        return i +    elseif not marks[start.char] then +        return 1 +    else +        return 0 +    end +end + +-- eventually we will do positioning in an other way (needs addional w/h/d fields) + +local function toligature(kind,lookupname,head,start,stop,char,markflag,discfound) -- brr head +    if start == stop and start.char == char then +        start.char = char +        return head, start +    end +    local prev = start.prev +    local next = stop.next +    start.prev = nil +    stop.next = nil +    local base = copy_glyph(start) +    if start == head then +        head = base +    end +    base.char = char +    base.subtype = ligature_code +    base.components = start -- start can have components +    if prev then +        prev.next = base +    end +    if next then +        next.prev = base +    end +    base.next = next +    base.prev = prev +    if not discfound then +        local deletemarks = markflag ~= "mark" +        local components = start +        local baseindex = 0 +        local componentindex = 0 +        local head = base +        local current = base +        -- first we loop over the glyphs in start .. stop +        while start do +            local char = start.char +            if not marks[char] then +                baseindex = baseindex + componentindex +                componentindex = getcomponentindex(start) +            elseif not deletemarks then -- quite fishy +                start[a_ligacomp] = baseindex + (start[a_ligacomp] or componentindex) +                if trace_marks then +                    logwarning("%s: keep mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp]) +                end +                head, current = insert_node_after(head,current,copy_node(start)) -- unlikely that mark has components +            elseif trace_marks then +                logwarning("%s: delete mark %s",pref(kind,lookupname),gref(char)) +            end +            start = start.next +        end +        -- we can have one accent as part of a lookup and another following +     -- local start = components -- was wrong (component scanning was introduced when more complex ligs in devanagari was added) +        local start = current.next +        while start and start.id == glyph_code do +            local char = start.char +            if marks[char] then +                start[a_ligacomp] = baseindex + (start[a_ligacomp] or componentindex) +                if trace_marks then +                    logwarning("%s: set mark %s, gets index %s",pref(kind,lookupname),gref(char),start[a_ligacomp]) +                end +            else +                break +            end +            start = start.next +        end +    end +    return head, base +end + +function handlers.gsub_single(head,start,kind,lookupname,replacement) +    if trace_singles then +        logprocess("%s: replacing %s by single %s",pref(kind,lookupname),gref(start.char),gref(replacement)) +    end +    start.char = replacement +    return head, start, true +end + +local function get_alternative_glyph(start,alternatives,value,trace_alternatives) +    local n = #alternatives +    if value == "random" then +        local r = random(1,n) +        return alternatives[r], trace_alternatives and formatters["value %a, taking %a"](value,r) +    elseif value == "first" then +        return alternatives[1], trace_alternatives and formatters["value %a, taking %a"](value,1) +    elseif value == "last" then +        return alternatives[n], trace_alternatives and formatters["value %a, taking %a"](value,n) +    else +        value = tonumber(value) +        if type(value) ~= "number" then +            return alternatives[1], trace_alternatives and formatters["invalid value %s, taking %a"](value,1) +        elseif value > n then +            local defaultalt = otf.defaultnodealternate +            if defaultalt == "first" then +                return alternatives[n], trace_alternatives and formatters["invalid value %s, taking %a"](value,1) +            elseif defaultalt == "last" then +                return alternatives[1], trace_alternatives and formatters["invalid value %s, taking %a"](value,n) +            else +                return false, trace_alternatives and formatters["invalid value %a, %s"](value,"out of range") +            end +        elseif value == 0 then +            return start.char, trace_alternatives and formatters["invalid value %a, %s"](value,"no change") +        elseif value < 1 then +            return alternatives[1], trace_alternatives and formatters["invalid value %a, taking %a"](value,1) +        else +            return alternatives[value], trace_alternatives and formatters["value %a, taking %a"](value,value) +        end +    end +end + +local function multiple_glyphs(head,start,multiple,ignoremarks) +    local nofmultiples = #multiple +    if nofmultiples > 0 then +        start.char = multiple[1] +        if nofmultiples > 1 then +            local sn = start.next +            for k=2,nofmultiples do -- todo: use insert_node +-- untested: +-- +-- while ignoremarks and marks[sn.char] then +--     local sn = sn.next +-- end +                local n = copy_node(start) -- ignore components +                n.char = multiple[k] +                n.next = sn +                n.prev = start +                if sn then +                    sn.prev = n +                end +                start.next = n +                start = n +            end +        end +        return head, start, true +    else +        if trace_multiples then +            logprocess("no multiple for %s",gref(start.char)) +        end +        return head, start, false +    end +end + +function handlers.gsub_alternate(head,start,kind,lookupname,alternative,sequence) +    local value = featurevalue == true and tfmdata.shared.features[kind] or featurevalue +    local choice, comment = get_alternative_glyph(start,alternative,value,trace_alternatives) +    if choice then +        if trace_alternatives then +            logprocess("%s: replacing %s by alternative %a to %s, %s",pref(kind,lookupname),gref(start.char),choice,gref(choice),comment) +        end +        start.char = choice +    else +        if trace_alternatives then +            logwarning("%s: no variant %a for %s, %s",pref(kind,lookupname),value,gref(start.char),comment) +        end +    end +    return head, start, true +end + +function handlers.gsub_multiple(head,start,kind,lookupname,multiple,sequence) +    if trace_multiples then +        logprocess("%s: replacing %s by multiple %s",pref(kind,lookupname),gref(start.char),gref(multiple)) +    end +    return multiple_glyphs(head,start,multiple,sequence.flags[1]) +end + +function handlers.gsub_ligature(head,start,kind,lookupname,ligature,sequence) +    local s, stop, discfound = start.next, nil, false +    local startchar = start.char +    if marks[startchar] then +        while s do +            local id = s.id +            if id == glyph_code and s.font == currentfont and s.subtype<256 then +                local lg = ligature[s.char] +                if lg then +                    stop = s +                    ligature = lg +                    s = s.next +                else +                    break +                end +            else +                break +            end +        end +        if stop then +            local lig = ligature.ligature +            if lig then +                if trace_ligatures then +                    local stopchar = stop.char +                    head, start = markstoligature(kind,lookupname,head,start,stop,lig) +                    logprocess("%s: replacing %s upto %s by ligature %s case 1",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) +                else +                    head, start = markstoligature(kind,lookupname,head,start,stop,lig) +                end +                return head, start, true +            else +                -- ok, goto next lookup +            end +        end +    else +        local skipmark = sequence.flags[1] +        while s do +            local id = s.id +            if id == glyph_code and s.subtype<256 then +                if s.font == currentfont then +                    local char = s.char +                    if skipmark and marks[char] then +                        s = s.next +                    else +                        local lg = ligature[char] +                        if lg then +                            stop = s +                            ligature = lg +                            s = s.next +                        else +                            break +                        end +                    end +                else +                    break +                end +            elseif id == disc_code then +                discfound = true +                s = s.next +            else +                break +            end +        end +        local lig = ligature.ligature +        if lig then +            if stop then +                if trace_ligatures then +                    local stopchar = stop.char +                    head, start = toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound) +                    logprocess("%s: replacing %s upto %s by ligature %s case 2",pref(kind,lookupname),gref(startchar),gref(stopchar),gref(start.char)) +                else +                    head, start = toligature(kind,lookupname,head,start,stop,lig,skipmark,discfound) +                end +                return head, start, true +            else +                -- weird but happens (in some arabic font) +                start.char = lig +                if trace_ligatures then +                    logprocess("%s: replacing %s by (no real) ligature %s case 3",pref(kind,lookupname),gref(startchar),gref(lig)) +                end +                return head, start, true +            end +        else +            -- weird but happens +        end +    end +    return head, start, false +end + +--[[ldx-- +<p>We get hits on a mark, but we're not sure if the it has to be applied so +we need to explicitly test for basechar, baselig and basemark entries.</p> +--ldx]]-- + +function handlers.gpos_mark2base(head,start,kind,lookupname,markanchors,sequence) +    local markchar = start.char +    if marks[markchar] then +        local base = start.prev -- [glyph] [start=mark] +        if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then +            local basechar = base.char +            if marks[basechar] then +                while true do +                    base = base.prev +                    if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then +                        basechar = base.char +                        if not marks[basechar] then +                            break +                        end +                    else +                        if trace_bugs then +                            logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) +                        end +                        return head, start, false +                    end +                end +            end +            local baseanchors = descriptions[basechar] +            if baseanchors then +                baseanchors = baseanchors.anchors +            end +            if baseanchors then +                local baseanchors = baseanchors['basechar'] +                if baseanchors then +                    local al = anchorlookups[lookupname] +                    for anchor,ba in next, baseanchors do +                        if al[anchor] then +                            local ma = markanchors[anchor] +                            if ma then +                                local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) +                                if trace_marks then +                                    logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)", +                                        pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) +                                end +                                return head, start, true +                            end +                        end +                    end +                    if trace_bugs then +                        logwarning("%s, no matching anchors for mark %s and base %s",pref(kind,lookupname),gref(markchar),gref(basechar)) +                    end +                end +            elseif trace_bugs then +            --  logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) +                onetimemessage(currentfont,basechar,"no base anchors",report_fonts) +            end +        elseif trace_bugs then +            logwarning("%s: prev node is no char",pref(kind,lookupname)) +        end +    elseif trace_bugs then +        logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) +    end +    return head, start, false +end + +function handlers.gpos_mark2ligature(head,start,kind,lookupname,markanchors,sequence) +    -- check chainpos variant +    local markchar = start.char +    if marks[markchar] then +        local base = start.prev -- [glyph] [optional marks] [start=mark] +        if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then +            local basechar = base.char +            if marks[basechar] then +                while true do +                    base = base.prev +                    if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then +                        basechar = base.char +                        if not marks[basechar] then +                            break +                        end +                    else +                        if trace_bugs then +                            logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) +                        end +                        return head, start, false +                    end +                end +            end +            local index = start[a_ligacomp] +            local baseanchors = descriptions[basechar] +            if baseanchors then +                baseanchors = baseanchors.anchors +                if baseanchors then +                   local baseanchors = baseanchors['baselig'] +                   if baseanchors then +                        local al = anchorlookups[lookupname] +                        for anchor, ba in next, baseanchors do +                            if al[anchor] then +                                local ma = markanchors[anchor] +                                if ma then +                                    ba = ba[index] +                                    if ba then +                                        local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) -- index +                                        if trace_marks then +                                            logprocess("%s, anchor %s, index %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)", +                                                pref(kind,lookupname),anchor,index,bound,gref(markchar),gref(basechar),index,dx,dy) +                                        end +                                        return head, start, true +                                    else +                                        if trace_bugs then +                                            logwarning("%s: no matching anchors for mark %s and baselig %s with index %a",pref(kind,lookupname),gref(markchar),gref(basechar),index) +                                        end +                                    end +                                end +                            end +                        end +                        if trace_bugs then +                            logwarning("%s: no matching anchors for mark %s and baselig %s",pref(kind,lookupname),gref(markchar),gref(basechar)) +                        end +                    end +                end +            elseif trace_bugs then +            --  logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) +                onetimemessage(currentfont,basechar,"no base anchors",report_fonts) +            end +        elseif trace_bugs then +            logwarning("%s: prev node is no char",pref(kind,lookupname)) +        end +    elseif trace_bugs then +        logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) +    end +    return head, start, false +end + +function handlers.gpos_mark2mark(head,start,kind,lookupname,markanchors,sequence) +    local markchar = start.char +    if marks[markchar] then +        local base = start.prev -- [glyph] [basemark] [start=mark] +        local slc = start[a_ligacomp] +        if slc then -- a rather messy loop ... needs checking with husayni +            while base do +                local blc = base[a_ligacomp] +                if blc and blc ~= slc then +                    base = base.prev +                else +                    break +                end +            end +        end +        if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -- subtype test can go +            local basechar = base.char +            local baseanchors = descriptions[basechar] +            if baseanchors then +                baseanchors = baseanchors.anchors +                if baseanchors then +                    baseanchors = baseanchors['basemark'] +                    if baseanchors then +                        local al = anchorlookups[lookupname] +                        for anchor,ba in next, baseanchors do +                            if al[anchor] then +                                local ma = markanchors[anchor] +                                if ma then +                                    local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,true) +                                    if trace_marks then +                                        logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", +                                            pref(kind,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) +                                    end +                                    return head, start, true +                                end +                            end +                        end +                        if trace_bugs then +                            logwarning("%s: no matching anchors for mark %s and basemark %s",pref(kind,lookupname),gref(markchar),gref(basechar)) +                        end +                    end +                end +            elseif trace_bugs then +            --  logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(basechar)) +                onetimemessage(currentfont,basechar,"no base anchors",report_fonts) +            end +        elseif trace_bugs then +            logwarning("%s: prev node is no mark",pref(kind,lookupname)) +        end +    elseif trace_bugs then +        logwarning("%s: mark %s is no mark",pref(kind,lookupname),gref(markchar)) +    end +    return head, start, false +end + +function handlers.gpos_cursive(head,start,kind,lookupname,exitanchors,sequence) -- to be checked +    local alreadydone = cursonce and start[a_cursbase] +    if not alreadydone then +        local done = false +        local startchar = start.char +        if marks[startchar] then +            if trace_cursive then +                logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) +            end +        else +            local nxt = start.next +            while not done and nxt and nxt.id == glyph_code and nxt.font == currentfont and nxt.subtype<256 do +                local nextchar = nxt.char +                if marks[nextchar] then +                    -- should not happen (maybe warning) +                    nxt = nxt.next +                else +                    local entryanchors = descriptions[nextchar] +                    if entryanchors then +                        entryanchors = entryanchors.anchors +                        if entryanchors then +                            entryanchors = entryanchors['centry'] +                            if entryanchors then +                                local al = anchorlookups[lookupname] +                                for anchor, entry in next, entryanchors do +                                    if al[anchor] then +                                        local exit = exitanchors[anchor] +                                        if exit then +                                            local dx, dy, bound = setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) +                                            if trace_cursive then +                                                logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) +                                            end +                                            done = true +                                            break +                                        end +                                    end +                                end +                            end +                        end +                    elseif trace_bugs then +                    --  logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) +                        onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) +                    end +                    break +                end +            end +        end +        return head, start, done +    else +        if trace_cursive and trace_details then +            logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) +        end +        return head, start, false +    end +end + +function handlers.gpos_single(head,start,kind,lookupname,kerns,sequence) +    local startchar = start.char +    local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) +    if trace_kerns then +        logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),dx,dy,w,h) +    end +    return head, start, false +end + +function handlers.gpos_pair(head,start,kind,lookupname,kerns,sequence) +    -- todo: kerns in disc nodes: pre, post, replace -> loop over disc too +    -- todo: kerns in components of ligatures +    local snext = start.next +    if not snext then +        return head, start, false +    else +        local prev, done = start, false +        local factor = tfmdata.parameters.factor +        local lookuptype = lookuptypes[lookupname] +        while snext and snext.id == glyph_code and snext.font == currentfont and snext.subtype<256 do +            local nextchar = snext.char +            local krn = kerns[nextchar] +            if not krn and marks[nextchar] then +                prev = snext +                snext = snext.next +            else +                if not krn then +                    -- skip +                elseif type(krn) == "table" then +                    if lookuptype == "pair" then -- probably not needed +                        local a, b = krn[2], krn[3] +                        if a and #a > 0 then +                            local startchar = start.char +                            local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) +                            if trace_kerns then +                                logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) +                            end +                        end +                        if b and #b > 0 then +                            local startchar = start.char +                            local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) +                            if trace_kerns then +                                logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",pref(kind,lookupname),gref(startchar),gref(nextchar),x,y,w,h) +                            end +                        end +                    else -- wrong ... position has different entries +                        report_process("%s: check this out (old kern stuff)",pref(kind,lookupname)) +                     -- local a, b = krn[2], krn[6] +                     -- if a and a ~= 0 then +                     --     local k = setkern(snext,factor,rlmode,a) +                     --     if trace_kerns then +                     --         logprocess("%s: inserting first kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) +                     --     end +                     -- end +                     -- if b and b ~= 0 then +                     --     logwarning("%s: ignoring second kern xoff %s",pref(kind,lookupname),b*factor) +                     -- end +                    end +                    done = true +                elseif krn ~= 0 then +                    local k = setkern(snext,factor,rlmode,krn) +                    if trace_kerns then +                        logprocess("%s: inserting kern %s between %s and %s",pref(kind,lookupname),k,gref(prev.char),gref(nextchar)) +                    end +                    done = true +                end +                break +            end +        end +        return head, start, done +    end +end + +--[[ldx-- +<p>I will implement multiple chain replacements once I run into a font that uses +it. It's not that complex to handle.</p> +--ldx]]-- + +local chainmores = { } +local chainprocs = { } + +local function logprocess(...) +    if trace_steps then +        registermessage(...) +    end +    report_subchain(...) +end + +local logwarning = report_subchain + +local function logprocess(...) +    if trace_steps then +        registermessage(...) +    end +    report_chain(...) +end + +local logwarning = report_chain + +-- We could share functions but that would lead to extra function calls with many +-- arguments, redundant tests and confusing messages. + +function chainprocs.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname) +    logwarning("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) +    return head, start, false +end + +function chainmores.chainsub(head,start,stop,kind,chainname,currentcontext,lookuphash,lookuplist,chainlookupname,n) +    logprocess("%s: a direct call to chainsub cannot happen",cref(kind,chainname,chainlookupname)) +    return head, start, false +end + +-- The reversesub is a special case, which is why we need to store the replacements +-- in a bit weird way. There is no lookup and the replacement comes from the lookup +-- itself. It is meant mostly for dealing with Urdu. + +function chainprocs.reversesub(head,start,stop,kind,chainname,currentcontext,lookuphash,replacements) +    local char = start.char +    local replacement = replacements[char] +    if replacement then +        if trace_singles then +            logprocess("%s: single reverse replacement of %s by %s",cref(kind,chainname),gref(char),gref(replacement)) +        end +        start.char = replacement +        return head, start, true +    else +        return head, start, false +    end +end + +--[[ldx-- +<p>This chain stuff is somewhat tricky since we can have a sequence of actions to be +applied: single, alternate, multiple or ligature where ligature can be an invalid +one in the sense that it will replace multiple by one but not neccessary one that +looks like the combination (i.e. it is the counterpart of multiple then). For +example, the following is valid:</p> + +<typing> +<line>xxxabcdexxx [single a->A][multiple b->BCD][ligature cde->E] xxxABCDExxx</line> +</typing> + +<p>Therefore we we don't really do the replacement here already unless we have the +single lookup case. The efficiency of the replacements can be improved by deleting +as less as needed but that would also make the code even more messy.</p> +--ldx]]-- + +-- local function delete_till_stop(head,start,stop,ignoremarks) -- keeps start +--     local n = 1 +--     if start == stop then +--         -- done +--     elseif ignoremarks then +--         repeat -- start x x m x x stop => start m +--             local next = start.next +--             if not marks[next.char] then +--                 local components = next.components +--                 if components then -- probably not needed +--                     flush_node_list(components) +--                 end +--                 head = delete_node(head,next) +--             end +--             n = n + 1 +--         until next == stop +--     else -- start x x x stop => start +--         repeat +--             local next = start.next +--             local components = next.components +--             if components then -- probably not needed +--                 flush_node_list(components) +--             end +--             head = delete_node(head,next) +--             n = n + 1 +--         until next == stop +--     end +--     return head, n +-- end + +--[[ldx-- +<p>Here we replace start by a single variant, First we delete the rest of the +match.</p> +--ldx]]-- + +function chainprocs.gsub_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) +    -- todo: marks ? +    local current = start +    local subtables = currentlookup.subtables +    if #subtables > 1 then +        logwarning("todo: check if we need to loop over the replacements: %s",concat(subtables," ")) +    end +    while current do +        if current.id == glyph_code then +            local currentchar = current.char +            local lookupname = subtables[1] -- only 1 +            local replacement = lookuphash[lookupname] +            if not replacement then +                if trace_bugs then +                    logwarning("%s: no single hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) +                end +            else +                replacement = replacement[currentchar] +                if not replacement or replacement == "" then +                    if trace_bugs then +                        logwarning("%s: no single for %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar)) +                    end +                else +                    if trace_singles then +                        logprocess("%s: replacing single %s by %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(currentchar),gref(replacement)) +                    end +                    current.char = replacement +                end +            end +            return head, start, true +        elseif current == stop then +            break +        else +            current = current.next +        end +    end +    return head, start, false +end + +chainmores.gsub_single = chainprocs.gsub_single + +--[[ldx-- +<p>Here we replace start by a sequence of new glyphs. First we delete the rest of +the match.</p> +--ldx]]-- + +function chainprocs.gsub_multiple(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) + -- local head, n = delete_till_stop(head,start,stop) +    local startchar = start.char +    local subtables = currentlookup.subtables +    local lookupname = subtables[1] +    local replacements = lookuphash[lookupname] +    if not replacements then +        if trace_bugs then +            logwarning("%s: no multiple hits",cref(kind,chainname,chainlookupname,lookupname)) +        end +    else +        replacements = replacements[startchar] +        if not replacements or replacement == "" then +            if trace_bugs then +                logwarning("%s: no multiple for %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar)) +            end +        else +            if trace_multiples then +                logprocess("%s: replacing %s by multiple characters %s",cref(kind,chainname,chainlookupname,lookupname),gref(startchar),gref(replacements)) +            end +            return multiple_glyphs(head,start,replacements,currentlookup.flags[1]) +        end +    end +    return head, start, false +end + +chainmores.gsub_multiple = chainprocs.gsub_multiple + +--[[ldx-- +<p>Here we replace start by new glyph. First we delete the rest of the match.</p> +--ldx]]-- + +-- char_1 mark_1 -> char_x mark_1 (ignore marks) +-- char_1 mark_1 -> char_x + +-- to be checked: do we always have just one glyph? +-- we can also have alternates for marks +-- marks come last anyway +-- are there cases where we need to delete the mark + +function chainprocs.gsub_alternate(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) +    local current = start +    local subtables = currentlookup.subtables +    local value  = featurevalue == true and tfmdata.shared.features[kind] or featurevalue +    while current do +        if current.id == glyph_code then -- is this check needed? +            local currentchar = current.char +            local lookupname = subtables[1] +            local alternatives = lookuphash[lookupname] +            if not alternatives then +                if trace_bugs then +                    logwarning("%s: no alternative hit",cref(kind,chainname,chainlookupname,lookupname)) +                end +            else +                alternatives = alternatives[currentchar] +                if alternatives then +                    local choice, comment = get_alternative_glyph(current,alternatives,value,trace_alternatives) +                    if choice then +                        if trace_alternatives then +                            logprocess("%s: replacing %s by alternative %a to %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(char),choice,gref(choice),comment) +                        end +                        start.char = choice +                    else +                        if trace_alternatives then +                            logwarning("%s: no variant %a for %s, %s",cref(kind,chainname,chainlookupname,lookupname),value,gref(char),comment) +                        end +                    end +                elseif trace_bugs then +                    logwarning("%s: no alternative for %s, %s",cref(kind,chainname,chainlookupname,lookupname),gref(currentchar),comment) +                end +            end +            return head, start, true +        elseif current == stop then +            break +        else +            current = current.next +        end +    end +    return head, start, false +end + +chainmores.gsub_alternate = chainprocs.gsub_alternate + +--[[ldx-- +<p>When we replace ligatures we use a helper that handles the marks. I might change +this function (move code inline and handle the marks by a separate function). We +assume rather stupid ligatures (no complex disc nodes).</p> +--ldx]]-- + +function chainprocs.gsub_ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex) +    local startchar = start.char +    local subtables = currentlookup.subtables +    local lookupname = subtables[1] +    local ligatures = lookuphash[lookupname] +    if not ligatures then +        if trace_bugs then +            logwarning("%s: no ligature hits",cref(kind,chainname,chainlookupname,lookupname,chainindex)) +        end +    else +        ligatures = ligatures[startchar] +        if not ligatures then +            if trace_bugs then +                logwarning("%s: no ligatures starting with %s",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) +            end +        else +            local s = start.next +            local discfound = false +            local last = stop +            local nofreplacements = 0 +            local skipmark = currentlookup.flags[1] +            while s do +                local id = s.id +                if id == disc_code then +                    s = s.next +                    discfound = true +                else +                    local schar = s.char +                    if skipmark and marks[schar] then -- marks +                        s = s.next +                    else +                        local lg = ligatures[schar] +                        if lg then +                            ligatures, last, nofreplacements = lg, s, nofreplacements + 1 +                            if s == stop then +                                break +                            else +                                s = s.next +                            end +                        else +                            break +                        end +                    end +                end +            end +            local l2 = ligatures.ligature +            if l2 then +                if chainindex then +                    stop = last +                end +                if trace_ligatures then +                    if start == stop then +                        logprocess("%s: replacing character %s by ligature %s case 3",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(l2)) +                    else +                        logprocess("%s: replacing character %s upto %s by ligature %s case 4",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char),gref(l2)) +                    end +                end +                head, start = toligature(kind,lookupname,head,start,stop,l2,currentlookup.flags[1],discfound) +                return head, start, true, nofreplacements +            elseif trace_bugs then +                if start == stop then +                    logwarning("%s: replacing character %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar)) +                else +                    logwarning("%s: replacing character %s upto %s by ligature fails",cref(kind,chainname,chainlookupname,lookupname,chainindex),gref(startchar),gref(stop.char)) +                end +            end +        end +    end +    return head, start, false, 0 +end + +chainmores.gsub_ligature = chainprocs.gsub_ligature + +function chainprocs.gpos_mark2base(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) +    local markchar = start.char +    if marks[markchar] then +        local subtables = currentlookup.subtables +        local lookupname = subtables[1] +        local markanchors = lookuphash[lookupname] +        if markanchors then +            markanchors = markanchors[markchar] +        end +        if markanchors then +            local base = start.prev -- [glyph] [start=mark] +            if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then +                local basechar = base.char +                if marks[basechar] then +                    while true do +                        base = base.prev +                        if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then +                            basechar = base.char +                            if not marks[basechar] then +                                break +                            end +                        else +                            if trace_bugs then +                                logwarning("%s: no base for mark %s",pref(kind,lookupname),gref(markchar)) +                            end +                            return head, start, false +                        end +                    end +                end +                local baseanchors = descriptions[basechar].anchors +                if baseanchors then +                    local baseanchors = baseanchors['basechar'] +                    if baseanchors then +                        local al = anchorlookups[lookupname] +                        for anchor,ba in next, baseanchors do +                            if al[anchor] then +                                local ma = markanchors[anchor] +                                if ma then +                                    local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) +                                    if trace_marks then +                                        logprocess("%s, anchor %s, bound %s: anchoring mark %s to basechar %s => (%p,%p)", +                                            cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) +                                    end +                                    return head, start, true +                                end +                            end +                        end +                        if trace_bugs then +                            logwarning("%s, no matching anchors for mark %s and base %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) +                        end +                    end +                end +            elseif trace_bugs then +                logwarning("%s: prev node is no char",cref(kind,chainname,chainlookupname,lookupname)) +            end +        elseif trace_bugs then +            logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) +        end +    elseif trace_bugs then +        logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) +    end +    return head, start, false +end + +function chainprocs.gpos_mark2ligature(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) +    local markchar = start.char +    if marks[markchar] then +        local subtables = currentlookup.subtables +        local lookupname = subtables[1] +        local markanchors = lookuphash[lookupname] +        if markanchors then +            markanchors = markanchors[markchar] +        end +        if markanchors then +            local base = start.prev -- [glyph] [optional marks] [start=mark] +            if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then +                local basechar = base.char +                if marks[basechar] then +                    while true do +                        base = base.prev +                        if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then +                            basechar = base.char +                            if not marks[basechar] then +                                break +                            end +                        else +                            if trace_bugs then +                                logwarning("%s: no base for mark %s",cref(kind,chainname,chainlookupname,lookupname),markchar) +                            end +                            return head, start, false +                        end +                    end +                end +                -- todo: like marks a ligatures hash +                local index = start[a_ligacomp] +                local baseanchors = descriptions[basechar].anchors +                if baseanchors then +                   local baseanchors = baseanchors['baselig'] +                   if baseanchors then +                        local al = anchorlookups[lookupname] +                        for anchor,ba in next, baseanchors do +                            if al[anchor] then +                                local ma = markanchors[anchor] +                                if ma then +                                    ba = ba[index] +                                    if ba then +                                        local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma) -- index +                                        if trace_marks then +                                            logprocess("%s, anchor %s, bound %s: anchoring mark %s to baselig %s at index %s => (%p,%p)", +                                                cref(kind,chainname,chainlookupname,lookupname),anchor,a or bound,gref(markchar),gref(basechar),index,dx,dy) +                                        end +                                        return head, start, true +                                    end +                                end +                            end +                        end +                        if trace_bugs then +                            logwarning("%s: no matching anchors for mark %s and baselig %s",cref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) +                        end +                    end +                end +            elseif trace_bugs then +                logwarning("feature %s, lookup %s: prev node is no char",kind,lookupname) +            end +        elseif trace_bugs then +            logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) +        end +    elseif trace_bugs then +        logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) +    end +    return head, start, false +end + +function chainprocs.gpos_mark2mark(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) +    local markchar = start.char +    if marks[markchar] then +     -- local alreadydone = markonce and start[a_markmark] +     -- if not alreadydone then +        --  local markanchors = descriptions[markchar].anchors markanchors = markanchors and markanchors.mark +            local subtables = currentlookup.subtables +            local lookupname = subtables[1] +            local markanchors = lookuphash[lookupname] +            if markanchors then +                markanchors = markanchors[markchar] +            end +            if markanchors then +                local base = start.prev -- [glyph] [basemark] [start=mark] +                local slc = start[a_ligacomp] +                if slc then -- a rather messy loop ... needs checking with husayni +                    while base do +                        local blc = base[a_ligacomp] +                        if blc and blc ~= slc then +                            base = base.prev +                        else +                            break +                        end +                    end +                end +                if base and base.id == glyph_code and base.font == currentfont and base.subtype<256 then -- subtype test can go +                    local basechar = base.char +                    local baseanchors = descriptions[basechar].anchors +                    if baseanchors then +                        baseanchors = baseanchors['basemark'] +                        if baseanchors then +                            local al = anchorlookups[lookupname] +                            for anchor,ba in next, baseanchors do +                                if al[anchor] then +                                    local ma = markanchors[anchor] +                                    if ma then +                                        local dx, dy, bound = setmark(start,base,tfmdata.parameters.factor,rlmode,ba,ma,true) +                                        if trace_marks then +                                            logprocess("%s, anchor %s, bound %s: anchoring mark %s to basemark %s => (%p,%p)", +                                                cref(kind,chainname,chainlookupname,lookupname),anchor,bound,gref(markchar),gref(basechar),dx,dy) +                                        end +                                        return head, start, true +                                    end +                                end +                            end +                            if trace_bugs then +                                logwarning("%s: no matching anchors for mark %s and basemark %s",gref(kind,chainname,chainlookupname,lookupname),gref(markchar),gref(basechar)) +                            end +                        end +                    end +                elseif trace_bugs then +                    logwarning("%s: prev node is no mark",cref(kind,chainname,chainlookupname,lookupname)) +                end +            elseif trace_bugs then +                logwarning("%s: mark %s has no anchors",cref(kind,chainname,chainlookupname,lookupname),gref(markchar)) +            end +     -- elseif trace_marks and trace_details then +     --     logprocess("%s, mark %s is already bound (n=%s), ignoring mark2mark",pref(kind,lookupname),gref(markchar),alreadydone) +     -- end +    elseif trace_bugs then +        logwarning("%s: mark %s is no mark",cref(kind,chainname,chainlookupname),gref(markchar)) +    end +    return head, start, false +end + +function chainprocs.gpos_cursive(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname) +    local alreadydone = cursonce and start[a_cursbase] +    if not alreadydone then +        local startchar = start.char +        local subtables = currentlookup.subtables +        local lookupname = subtables[1] +        local exitanchors = lookuphash[lookupname] +        if exitanchors then +            exitanchors = exitanchors[startchar] +        end +        if exitanchors then +            local done = false +            if marks[startchar] then +                if trace_cursive then +                    logprocess("%s: ignoring cursive for mark %s",pref(kind,lookupname),gref(startchar)) +                end +            else +                local nxt = start.next +                while not done and nxt and nxt.id == glyph_code and nxt.font == currentfont and nxt.subtype<256 do +                    local nextchar = nxt.char +                    if marks[nextchar] then +                        -- should not happen (maybe warning) +                        nxt = nxt.next +                    else +                        local entryanchors = descriptions[nextchar] +                        if entryanchors then +                            entryanchors = entryanchors.anchors +                            if entryanchors then +                                entryanchors = entryanchors['centry'] +                                if entryanchors then +                                    local al = anchorlookups[lookupname] +                                    for anchor, entry in next, entryanchors do +                                        if al[anchor] then +                                            local exit = exitanchors[anchor] +                                            if exit then +                                                local dx, dy, bound = setcursive(start,nxt,tfmdata.parameters.factor,rlmode,exit,entry,characters[startchar],characters[nextchar]) +                                                if trace_cursive then +                                                    logprocess("%s: moving %s to %s cursive (%p,%p) using anchor %s and bound %s in rlmode %s",pref(kind,lookupname),gref(startchar),gref(nextchar),dx,dy,anchor,bound,rlmode) +                                                end +                                                done = true +                                                break +                                            end +                                        end +                                    end +                                end +                            end +                        elseif trace_bugs then +                        --  logwarning("%s: char %s is missing in font",pref(kind,lookupname),gref(startchar)) +                            onetimemessage(currentfont,startchar,"no entry anchors",report_fonts) +                        end +                        break +                    end +                end +            end +            return head, start, done +        else +            if trace_cursive and trace_details then +                logprocess("%s, cursive %s is already done",pref(kind,lookupname),gref(start.char),alreadydone) +            end +            return head, start, false +        end +    end +    return head, start, false +end + +function chainprocs.gpos_single(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) +    -- untested .. needs checking for the new model +    local startchar = start.char +    local subtables = currentlookup.subtables +    local lookupname = subtables[1] +    local kerns = lookuphash[lookupname] +    if kerns then +        kerns = kerns[startchar] -- needed ? +        if kerns then +            local dx, dy, w, h = setpair(start,tfmdata.parameters.factor,rlmode,sequence.flags[4],kerns,characters[startchar]) +            if trace_kerns then +                logprocess("%s: shifting single %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),dx,dy,w,h) +            end +        end +    end +    return head, start, false +end + +chainmores.gpos_single = chainprocs.gpos_single -- okay? + +-- when machines become faster i will make a shared function + +function chainprocs.gpos_pair(head,start,stop,kind,chainname,currentcontext,lookuphash,currentlookup,chainlookupname,chainindex,sequence) +    local snext = start.next +    if snext then +        local startchar = start.char +        local subtables = currentlookup.subtables +        local lookupname = subtables[1] +        local kerns = lookuphash[lookupname] +        if kerns then +            kerns = kerns[startchar] +            if kerns then +                local lookuptype = lookuptypes[lookupname] +                local prev, done = start, false +                local factor = tfmdata.parameters.factor +                while snext and snext.id == glyph_code and snext.font == currentfont and snext.subtype<256 do +                    local nextchar = snext.char +                    local krn = kerns[nextchar] +                    if not krn and marks[nextchar] then +                        prev = snext +                        snext = snext.next +                    else +                        if not krn then +                            -- skip +                        elseif type(krn) == "table" then +                            if lookuptype == "pair" then +                                local a, b = krn[2], krn[3] +                                if a and #a > 0 then +                                    local startchar = start.char +                                    local x, y, w, h = setpair(start,factor,rlmode,sequence.flags[4],a,characters[startchar]) +                                    if trace_kerns then +                                        logprocess("%s: shifting first of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) +                                    end +                                end +                                if b and #b > 0 then +                                    local startchar = start.char +                                    local x, y, w, h = setpair(snext,factor,rlmode,sequence.flags[4],b,characters[nextchar]) +                                    if trace_kerns then +                                        logprocess("%s: shifting second of pair %s and %s by (%p,%p) and correction (%p,%p)",cref(kind,chainname,chainlookupname),gref(startchar),gref(nextchar),x,y,w,h) +                                    end +                                end +                            else +                                report_process("%s: check this out (old kern stuff)",cref(kind,chainname,chainlookupname)) +                                local a, b = krn[2], krn[6] +                                if a and a ~= 0 then +                                    local k = setkern(snext,factor,rlmode,a) +                                    if trace_kerns then +                                        logprocess("%s: inserting first kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) +                                    end +                                end +                                if b and b ~= 0 then +                                    logwarning("%s: ignoring second kern xoff %s",cref(kind,chainname,chainlookupname),b*factor) +                                end +                            end +                            done = true +                        elseif krn ~= 0 then +                            local k = setkern(snext,factor,rlmode,krn) +                            if trace_kerns then +                                logprocess("%s: inserting kern %s between %s and %s",cref(kind,chainname,chainlookupname),k,gref(prev.char),gref(nextchar)) +                            end +                            done = true +                        end +                        break +                    end +                end +                return head, start, done +            end +        end +    end +    return head, start, false +end + +chainmores.gpos_pair = chainprocs.gpos_pair -- okay? + +-- what pointer to return, spec says stop +-- to be discussed ... is bidi changer a space? +-- elseif char == zwnj and sequence[n][32] then -- brrr + +-- somehow l or f is global +-- we don't need to pass the currentcontext, saves a bit +-- make a slow variant then can be activated but with more tracing + +local function show_skip(kind,chainname,char,ck,class) +    if ck[9] then +        logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a, %a => %a",cref(kind,chainname),gref(char),class,ck[1],ck[2],ck[9],ck[10]) +    else +        logwarning("%s: skipping char %s, class %a, rule %a, lookuptype %a",cref(kind,chainname),gref(char),class,ck[1],ck[2]) +    end +end + +local function normal_handle_contextchain(head,start,kind,chainname,contexts,sequence,lookuphash) +    --  local rule, lookuptype, sequence, f, l, lookups = ck[1], ck[2] ,ck[3], ck[4], ck[5], ck[6] +    local flags        = sequence.flags +    local done         = false +    local skipmark     = flags[1] +    local skipligature = flags[2] +    local skipbase     = flags[3] +    local someskip     = skipmark or skipligature or skipbase -- could be stored in flags for a fast test (hm, flags could be false !) +    local markclass    = sequence.markclass                   -- todo, first we need a proper test +    local skipped      = false +    for k=1,#contexts do +        local match   = true +        local current = start +        local last    = start +        local ck      = contexts[k] +        local seq     = ck[3] +        local s       = #seq +        -- f..l = mid string +        if s == 1 then +            -- never happens +            match = current.id == glyph_code and current.font == currentfont and current.subtype<256 and seq[1][current.char] +        else +            -- maybe we need a better space check (maybe check for glue or category or combination) +            -- we cannot optimize for n=2 because there can be disc nodes +            local f, l = ck[4], ck[5] +            -- current match +            if f == 1 and f == l then -- current only +                -- already a hit +             -- match = true +            else -- before/current/after | before/current | current/after +                -- no need to test first hit (to be optimized) +                if f == l then -- new, else last out of sync (f is > 1) +                 -- match = true +                else +                    local n = f + 1 +                    last = last.next +                    while n <= l do +                        if last then +                            local id = last.id +                            if id == glyph_code then +                                if last.font == currentfont and last.subtype<256 then +                                    local char = last.char +                                    local ccd = descriptions[char] +                                    if ccd then +                                        local class = ccd.class +                                        if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then +                                            skipped = true +                                            if trace_skips then +                                                show_skip(kind,chainname,char,ck,class) +                                            end +                                            last = last.next +                                        elseif seq[n][char] then +                                            if n < l then +                                                last = last.next +                                            end +                                            n = n + 1 +                                        else +                                            match = false +                                            break +                                        end +                                    else +                                        match = false +                                        break +                                    end +                                else +                                    match = false +                                    break +                                end +                            elseif id == disc_code then +                                last = last.next +                            else +                                match = false +                                break +                            end +                        else +                            match = false +                            break +                        end +                    end +                end +            end +            -- before +            if match and f > 1 then +                local prev = start.prev +                if prev then +                    local n = f-1 +                    while n >= 1 do +                        if prev then +                            local id = prev.id +                            if id == glyph_code then +                                if prev.font == currentfont and prev.subtype<256 then -- normal char +                                    local char = prev.char +                                    local ccd = descriptions[char] +                                    if ccd then +                                        local class = ccd.class +                                        if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then +                                            skipped = true +                                            if trace_skips then +                                                show_skip(kind,chainname,char,ck,class) +                                            end +                                        elseif seq[n][char] then +                                            n = n -1 +                                        else +                                            match = false +                                            break +                                        end +                                    else +                                        match = false +                                        break +                                    end +                                else +                                    match = false +                                    break +                                end +                            elseif id == disc_code then +                                -- skip 'm +                            elseif seq[n][32] then +                                n = n -1 +                            else +                                match = false +                                break +                            end +                            prev = prev.prev +                        elseif seq[n][32] then -- somewhat special, as zapfino can have many preceding spaces +                            n = n -1 +                        else +                            match = false +                            break +                        end +                    end +                elseif f == 2 then +                    match = seq[1][32] +                else +                    for n=f-1,1 do +                        if not seq[n][32] then +                            match = false +                            break +                        end +                    end +                end +            end +            -- after +            if match and s > l then +                local current = last and last.next +                if current then +                    -- removed optimization for s-l == 1, we have to deal with marks anyway +                    local n = l + 1 +                    while n <= s do +                        if current then +                            local id = current.id +                            if id == glyph_code then +                                if current.font == currentfont and current.subtype<256 then -- normal char +                                    local char = current.char +                                    local ccd = descriptions[char] +                                    if ccd then +                                        local class = ccd.class +                                        if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then +                                            skipped = true +                                            if trace_skips then +                                                show_skip(kind,chainname,char,ck,class) +                                            end +                                        elseif seq[n][char] then +                                            n = n + 1 +                                        else +                                            match = false +                                            break +                                        end +                                    else +                                        match = false +                                        break +                                    end +                                else +                                    match = false +                                    break +                                end +                            elseif id == disc_code then +                                -- skip 'm +                            elseif seq[n][32] then -- brrr +                                n = n + 1 +                            else +                                match = false +                                break +                            end +                            current = current.next +                        elseif seq[n][32] then +                            n = n + 1 +                        else +                            match = false +                            break +                        end +                    end +                elseif s-l == 1 then +                    match = seq[s][32] +                else +                    for n=l+1,s do +                        if not seq[n][32] then +                            match = false +                            break +                        end +                    end +                end +            end +        end +        if match then +            -- ck == currentcontext +            if trace_contexts then +                local rule, lookuptype, f, l = ck[1], ck[2], ck[4], ck[5] +                local char = start.char +                if ck[9] then +                    logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a, %a => %a", +                        cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype,ck[9],ck[10]) +                else +                    logwarning("%s: rule %s matches at char %s for (%s,%s,%s) chars, lookuptype %a", +                        cref(kind,chainname),rule,gref(char),f-1,l-f+1,s-l,lookuptype) +                end +            end +            local chainlookups = ck[6] +            if chainlookups then +                local nofchainlookups = #chainlookups +                -- we can speed this up if needed +                if nofchainlookups == 1 then +                    local chainlookupname = chainlookups[1] +                    local chainlookup = lookuptable[chainlookupname] +                    if chainlookup then +                        local cp = chainprocs[chainlookup.type] +                        if cp then +                            local ok +                            head, start, ok = cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,nil,sequence) +                            if ok then +                                done = true +                            end +                        else +                            logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) +                        end +                    else -- shouldn't happen +                        logprocess("%s is not yet supported",cref(kind,chainname,chainlookupname)) +                    end +                 else +                    local i = 1 +                    repeat +                        if skipped then +                            while true do +                                local char = start.char +                                local ccd = descriptions[char] +                                if ccd then +                                    local class = ccd.class +                                    if class == skipmark or class == skipligature or class == skipbase or (markclass and class == "mark" and not markclass[char]) then +                                        start = start.next +                                    else +                                        break +                                    end +                                else +                                    break +                                end +                            end +                        end +                        local chainlookupname = chainlookups[i] +                        local chainlookup = lookuptable[chainlookupname] +                        if not chainlookup then +                            -- okay, n matches, < n replacements +                            i = i + 1 +                        else +                            local cp = chainmores[chainlookup.type] +                            if not cp then +                                -- actually an error +                                logprocess("%s: %s is not yet supported",cref(kind,chainname,chainlookupname),chainlookup.type) +                                i = i + 1 +                            else +                                local ok, n +                                head, start, ok, n = cp(head,start,last,kind,chainname,ck,lookuphash,chainlookup,chainlookupname,i,sequence) +                                -- messy since last can be changed ! +                                if ok then +                                    done = true +                                    -- skip next one(s) if ligature +                                    i = i + (n or 1) +                                else +                                    i = i + 1 +                                end +                            end +                        end +                        if start then +                            start = start.next +                        else +                            -- weird +                        end +                    until i > nofchainlookups +                end +            else +                local replacements = ck[7] +                if replacements then +                    head, start, done = chainprocs.reversesub(head,start,last,kind,chainname,ck,lookuphash,replacements) -- sequence +                else +                    done = true -- can be meant to be skipped +                    if trace_contexts then +                        logprocess("%s: skipping match",cref(kind,chainname)) +                    end +                end +            end +        end +    end +    return head, start, done +end + +-- Because we want to keep this elsewhere (an because speed is less an issue) we +-- pass the font id so that the verbose variant can access the relevant helper tables. + +local verbose_handle_contextchain = function(font,...) +    logwarning("no verbose handler installed, reverting to 'normal'") +    otf.setcontextchain() +    return normal_handle_contextchain(...) +end + +otf.chainhandlers = { +    normal  = normal_handle_contextchain, +    verbose = verbose_handle_contextchain, +} + +function otf.setcontextchain(method) +    if not method or method == "normal" or not otf.chainhandlers[method] then +        if handlers.contextchain then -- no need for a message while making the format +            logwarning("installing normal contextchain handler") +        end +        handlers.contextchain = normal_handle_contextchain +    else +        logwarning("installing contextchain handler %a",method) +        local handler = otf.chainhandlers[method] +        handlers.contextchain = function(...) +            return handler(currentfont,...) -- hm, get rid of ... +        end +    end +    handlers.gsub_context             = handlers.contextchain +    handlers.gsub_contextchain        = handlers.contextchain +    handlers.gsub_reversecontextchain = handlers.contextchain +    handlers.gpos_contextchain        = handlers.contextchain +    handlers.gpos_context             = handlers.contextchain +end + +otf.setcontextchain() + +local missing = { } -- we only report once + +local function logprocess(...) +    if trace_steps then +        registermessage(...) +    end +    report_process(...) +end + +local logwarning = report_process + +local function report_missing_cache(typ,lookup) +    local f = missing[currentfont] if not f then f = { } missing[currentfont] = f end +    local t = f[typ]               if not t then t = { } f[typ]               = t end +    if not t[lookup] then +        t[lookup] = true +        logwarning("missing cache for lookup %a, type %a, font %a, name %a",lookup,typ,currentfont,tfmdata.properties.fullname) +    end +end + +local resolved = { } -- we only resolve a font,script,language pair once + +-- todo: pass all these 'locals' in a table + +local lookuphashes = { } + +setmetatableindex(lookuphashes, function(t,font) +    local lookuphash = fontdata[font].resources.lookuphash +    if not lookuphash or not next(lookuphash) then +        lookuphash = false +    end +    t[font] = lookuphash +    return lookuphash +end) + +-- fonts.hashes.lookups = lookuphashes + +local autofeatures = fonts.analyzers.features -- was: constants + +local function initialize(sequence,script,language,enabled) +    local features = sequence.features +    if features then +        for kind, scripts in next, features do +            local valid = enabled[kind] +            if valid then +                local languages = scripts[script] or scripts[wildcard] +                if languages and (languages[language] or languages[wildcard]) then +                    return { valid, autofeatures[kind] or false, sequence.chain or 0, kind, sequence } +                end +            end +        end +    end +    return false +end + +function otf.dataset(tfmdata,font) -- generic variant, overloaded in context +    local shared     = tfmdata.shared +    local properties = tfmdata.properties +    local language   = properties.language or "dflt" +    local script     = properties.script   or "dflt" +    local enabled    = shared.features +    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 = { +            -- indexed but we can also add specific data by key +        } +        rs[language] = rl +        local sequences = tfmdata.resources.sequences +--         setmetatableindex(rl, function(t,k) +--             if type(k) == "number" then +--                 local v = enabled and initialize(sequences[k],script,language,enabled) +--                 t[k] = v +--                 return v +--             end +--         end) +for s=1,#sequences do +    local v = enabled and initialize(sequences[s],script,language,enabled) +    if v then +        rl[#rl+1] = v +    end +end +    end +    return rl +end + +-- elseif id == glue_code then +--     if p[5] then -- chain +--         local pc = pp[32] +--         if pc then +--             start, ok = start, false -- p[1](start,kind,p[2],pc,p[3],p[4]) +--             if ok then +--                 done = true +--             end +--             if start then start = start.next end +--         else +--             start = start.next +--         end +--     else +--         start = start.next +--     end + +-- there will be a new direction parser (pre-parsed etc) + +-- less bytecode: 290 -> 254 +-- +-- attr = attr or false +-- +-- local a = getattr(start,0) +-- if (a == attr and (not attribute or getattr(start,a_state) == attribute)) or (not attribute or getattr(start,a_state) == attribute) then +--     -- the action +-- end + +local function featuresprocessor(head,font,attr) + +    local lookuphash = lookuphashes[font] -- we can also check sequences here + +    if not lookuphash then +        return head, false +    end + +    if trace_steps then +        checkstep(head) +    end + +    tfmdata         = fontdata[font] +    descriptions    = tfmdata.descriptions +    characters      = tfmdata.characters +    resources       = tfmdata.resources + +    marks           = resources.marks +    anchorlookups   = resources.lookup_to_anchor +    lookuptable     = resources.lookups +    lookuptypes     = resources.lookuptypes + +    currentfont     = font +    rlmode          = 0 + +    local sequences = resources.sequences +    local done      = false +    local datasets  = otf.dataset(tfmdata,font,attr) + +    local dirstack  = { } -- could move outside function + +    -- We could work on sub start-stop ranges instead but I wonder if there is that +    -- much speed gain (experiments showed that it made not much sense) and we need +    -- to keep track of directions anyway. Also at some point I want to play with +    -- font interactions and then we do need the full sweeps. + +    -- Keeping track of the headnode is needed for devanagari (I generalized it a bit +    -- so that multiple cases are also covered.) + +    for s=1,#datasets do +        local dataset = datasets[s] +        featurevalue = dataset[1] -- todo: pass to function instead of using a global + +        local sequence  = dataset[5] -- sequences[s] -- also dataset[5] +        local rlparmode = 0 +        local topstack  = 0 +        local success   = false +        local attribute = dataset[2] +        local chain     = dataset[3] -- sequence.chain or 0 +        local typ       = sequence.type +        local subtables = sequence.subtables +        if chain < 0 then +            -- this is a limited case, no special treatments like 'init' etc +            local handler = handlers[typ] +            -- we need to get rid of this slide! probably no longer needed in latest luatex +            local start = find_node_tail(head) -- slow (we can store tail because there's always a skip at the end): todo +            while start do +                local id = start.id +                if id == glyph_code then +                    if start.font == font and start.subtype<256 then +                        local a = start[0] +                        if a then +                            a = a == attr +                        else +                            a = true +                        end +                        if a then +                            for i=1,#subtables do +                                local lookupname = subtables[i] +                                local lookupcache = lookuphash[lookupname] +                                if lookupcache then +                                    local lookupmatch = lookupcache[start.char] +                                    if lookupmatch then +                                        head, start, success = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) +                                        if success then +                                            break +                                        end +                                    end +                                else +                                    report_missing_cache(typ,lookupname) +                                end +                            end +                            if start then start = start.prev end +                        else +                            start = start.prev +                        end +                    else +                        start = start.prev +                    end +                else +                    start = start.prev +                end +            end +        else +            local handler = handlers[typ] +            local ns = #subtables +            local start = head -- local ? +            rlmode = 0 -- to be checked ? +            if ns == 1 then -- happens often +                local lookupname = subtables[1] +                local lookupcache = lookuphash[lookupname] +                if not lookupcache then -- also check for empty cache +                    report_missing_cache(typ,lookupname) +                else + +                    local function subrun(start) +                        -- mostly for gsub, gpos would demand a more clever approach +                        local head = start +                        local done = false +                        while start do +                            local id = start.id +                            if id == glyph_code and start.font == font and start.subtype <256 then +                                local a = start[0] +                                if a then +                                    a = (a == attr) and (not attribute or start[a_state] == attribute) +                                else +                                    a = not attribute or start[a_state] == attribute +                                end +                                if a then +                                    local lookupmatch = lookupcache[start.char] +                                    if lookupmatch then +                                        -- sequence kan weg +                                        local ok +                                        head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) +                                        if ok then +                                            done = true +                                        end +                                    end +                                    if start then start = start.next end +                                else +                                    start = start.next +                                end +                            else +                                start = start.next +                            end +                        end +                        if done then +                            success = true +                            return head +                        end +                    end + +                    local function kerndisc(disc) -- we can assume that prev and next are glyphs +                        local prev = disc.prev +                        local next = disc.next +                        if prev and next then +                            prev.next = next +                         -- next.prev = prev +                            local a = prev[0] +                            if a then +                                a = (a == attr) and (not attribute or prev[a_state] == attribute) +                            else +                                a = not attribute or prev[a_state] == attribute +                            end +                            if a then +                                local lookupmatch = lookupcache[prev.char] +                                if lookupmatch then +                                    -- sequence kan weg +                                    local h, d, ok = handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) +                                    if ok then +                                        done = true +                                        success = true +                                    end +                                end +                            end +                            prev.next = disc +                         -- next.prev = disc +                        end +                        return next +                    end + +                    while start do +                        local id = start.id +                        if id == glyph_code then +                            if start.font == font and start.subtype<256 then +                                local a = start[0] +                                if a then +                                    a = (a == attr) and (not attribute or start[a_state] == attribute) +                                else +                                    a = not attribute or start[a_state] == attribute +                                end +                                if a then +                                    local lookupmatch = lookupcache[start.char] +                                    if lookupmatch then +                                        -- sequence kan weg +                                        local ok +                                        head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,1) +                                        if ok then +                                            success = true +                                        end +                                    end +                                    if start then start = start.next end +                                else +                                    start = start.next +                                end +                            else +                                start = start.next +                            end +                        elseif id == disc_code then +                            -- mostly for gsub +                            if start.subtype == discretionary_code then +                                local pre = start.pre +                                if pre then +                                    local new = subrun(pre) +                                    if new then start.pre = new end +                                end +                                local post = start.post +                                if post then +                                    local new = subrun(post) +                                    if new then start.post = new end +                                end +                                local replace = start.replace +                                if replace then +                                    local new = subrun(replace) +                                    if new then start.replace = new end +                                end +elseif typ == "gpos_single" or typ == "gpos_pair" then +    kerndisc(start) +                            end +                            start = start.next +                        elseif id == whatsit_code then -- will be function +                            local subtype = start.subtype +                            if subtype == dir_code then +                                local dir = start.dir +                                if     dir == "+TRT" or dir == "+TLT" then +                                    topstack = topstack + 1 +                                    dirstack[topstack] = dir +                                elseif dir == "-TRT" or dir == "-TLT" then +                                    topstack = topstack - 1 +                                end +                                local newdir = dirstack[topstack] +                                if newdir == "+TRT" then +                                    rlmode = -1 +                                elseif newdir == "+TLT" then +                                    rlmode = 1 +                                else +                                    rlmode = rlparmode +                                end +                                if trace_directions then +                                    report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir) +                                end +                            elseif subtype == localpar_code then +                                local dir = start.dir +                                if dir == "TRT" then +                                    rlparmode = -1 +                                elseif dir == "TLT" then +                                    rlparmode = 1 +                                else +                                    rlparmode = 0 +                                end +                                -- one might wonder if the par dir should be looked at, so we might as well drop the next line +                                rlmode = rlparmode +                                if trace_directions then +                                    report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode) +                                end +                            end +                            start = start.next +                        elseif id == math_code then +                            start = end_of_math(start).next +                        else +                            start = start.next +                        end +                    end +                end +            else + +                local function subrun(start) +                    -- mostly for gsub, gpos would demand a more clever approach +                    local head = start +                    local done = false +                    while start do +                        local id = start.id +                        if id == glyph_code and start.id == font and start.subtype <256 then +                            local a = start[0] +                            if a then +                                a = (a == attr) and (not attribute or start[a_state] == attribute) +                            else +                                a = not attribute or start[a_state] == attribute +                            end +                            if a then +                                for i=1,ns do +                                    local lookupname = subtables[i] +                                    local lookupcache = lookuphash[lookupname] +                                    if lookupcache then +                                        local lookupmatch = lookupcache[start.char] +                                        if lookupmatch then +                                            -- we could move all code inline but that makes things even more unreadable +                                            local ok +                                            head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) +                                            if ok then +                                                done = true +                                                break +                                            elseif not start then +                                                -- don't ask why ... shouldn't happen +                                                break +                                            end +                                        end +                                    else +                                        report_missing_cache(typ,lookupname) +                                    end +                                end +                                if start then start = start.next end +                            else +                                start = start.next +                            end +                        else +                            start = start.next +                        end +                    end +                    if done then +                        success = true +                        return head +                    end +                end + +                local function kerndisc(disc) -- we can assume that prev and next are glyphs +                    local prev = disc.prev +                    local next = disc.next +                    if prev and next then +                        prev.next = next +                     -- next.prev = prev +                        local a = prev[0] +                        if a then +                            a = (a == attr) and (not attribute or prev[a_state] == attribute) +                        else +                            a = not attribute or prev[a_state] == attribute +                        end +                        if a then +                            for i=1,ns do +                                local lookupname = subtables[i] +                                local lookupcache = lookuphash[lookupname] +                                if lookupcache then +                                    local lookupmatch = lookupcache[prev.char] +                                    if lookupmatch then +                                        -- we could move all code inline but that makes things even more unreadable +                                        local h, d, ok = handler(head,prev,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) +                                        if ok then +                                            done = true +                                            break +                                        end +                                    end +                                else +                                    report_missing_cache(typ,lookupname) +                                end +                            end +                        end +                        prev.next = disc +                     -- next.prev = disc +                    end +                    return next +                end + +                while start do +                    local id = start.id +                    if id == glyph_code then +                        if start.font == font and start.subtype<256 then +                            local a = start[0] +                            if a then +                                a = (a == attr) and (not attribute or start[a_state] == attribute) +                            else +                                a = not attribute or start[a_state] == attribute +                            end +                            if a then +                                for i=1,ns do +                                    local lookupname = subtables[i] +                                    local lookupcache = lookuphash[lookupname] +                                    if lookupcache then +                                        local lookupmatch = lookupcache[start.char] +                                        if lookupmatch then +                                            -- we could move all code inline but that makes things even more unreadable +                                            local ok +                                            head, start, ok = handler(head,start,dataset[4],lookupname,lookupmatch,sequence,lookuphash,i) +                                            if ok then +                                                success = true +                                                break +                                            elseif not start then +                                                -- don't ask why ... shouldn't happen +                                                break +                                            end +                                        end +                                    else +                                        report_missing_cache(typ,lookupname) +                                    end +                                end +                                if start then start = start.next end +                            else +                                start = start.next +                            end +                        else +                            start = start.next +                        end +                    elseif id == disc_code then +                        -- mostly for gsub +                        if start.subtype == discretionary_code then +                            local pre = start.pre +                            if pre then +                                local new = subrun(pre) +                                if new then start.pre = new end +                            end +                            local post = start.post +                            if post then +                                local new = subrun(post) +                                if new then start.post = new end +                            end +                            local replace = start.replace +                            if replace then +                                local new = subrun(replace) +                                if new then start.replace = new end +                            end +elseif typ == "gpos_single" or typ == "gpos_pair" then +    kerndisc(start) +                        end +                        start = start.next +                    elseif id == whatsit_code then +                        local subtype = start.subtype +                        if subtype == dir_code then +                            local dir = start.dir +                            if     dir == "+TRT" or dir == "+TLT" then +                                topstack = topstack + 1 +                                dirstack[topstack] = dir +                            elseif dir == "-TRT" or dir == "-TLT" then +                                topstack = topstack - 1 +                            end +                            local newdir = dirstack[topstack] +                            if newdir == "+TRT" then +                                rlmode = -1 +                            elseif newdir == "+TLT" then +                                rlmode = 1 +                            else +                                rlmode = rlparmode +                            end +                            if trace_directions then +                                report_process("directions after txtdir %a: parmode %a, txtmode %a, # stack %a, new dir %a",dir,rlparmode,rlmode,topstack,newdir) +                            end +                        elseif subtype == localpar_code then +                            local dir = start.dir +                            if dir == "TRT" then +                                rlparmode = -1 +                            elseif dir == "TLT" then +                                rlparmode = 1 +                            else +                                rlparmode = 0 +                            end +                            rlmode = rlparmode +                            if trace_directions then +                                report_process("directions after pardir %a: parmode %a, txtmode %a",dir,rlparmode,rlmode) +                            end +                        end +                        start = start.next +                    elseif id == math_code then +                        start = end_of_math(start).next +                    else +                        start = start.next +                    end +                end +            end +        end +        if success then +            done = true +        end +        if trace_steps then -- ? +            registerstep(head) +        end +    end +    return head, done +end + +local function generic(lookupdata,lookupname,unicode,lookuphash) +    local target = lookuphash[lookupname] +    if target then +        target[unicode] = lookupdata +    else +        lookuphash[lookupname] = { [unicode] = lookupdata } +    end +end + +local action = { + +    substitution = generic, +    multiple     = generic, +    alternate    = generic, +    position     = generic, + +    ligature = function(lookupdata,lookupname,unicode,lookuphash) +        local target = lookuphash[lookupname] +        if not target then +            target = { } +            lookuphash[lookupname] = target +        end +        for i=1,#lookupdata do +            local li = lookupdata[i] +            local tu = target[li] +            if not tu then +                tu = { } +                target[li] = tu +            end +            target = tu +        end +        target.ligature = unicode +    end, + +    pair = function(lookupdata,lookupname,unicode,lookuphash) +        local target = lookuphash[lookupname] +        if not target then +            target = { } +            lookuphash[lookupname] = target +        end +        local others = target[unicode] +        local paired = lookupdata[1] +        if others then +            others[paired] = lookupdata +        else +            others = { [paired] = lookupdata } +            target[unicode] = others +        end +    end, + +} + +local function prepare_lookups(tfmdata) + +    local rawdata          = tfmdata.shared.rawdata +    local resources        = rawdata.resources +    local lookuphash       = resources.lookuphash +    local anchor_to_lookup = resources.anchor_to_lookup +    local lookup_to_anchor = resources.lookup_to_anchor +    local lookuptypes      = resources.lookuptypes +    local characters       = tfmdata.characters +    local descriptions     = tfmdata.descriptions + +    -- we cannot free the entries in the descriptions as sometimes we access +    -- then directly (for instance anchors) ... selectively freeing does save +    -- much memory as it's only a reference to a table and the slot in the +    -- description hash is not freed anyway + +    for unicode, character in next, characters do -- we cannot loop over descriptions ! + +        local description = descriptions[unicode] + +        if description then + +            local lookups = description.slookups +            if lookups then +                for lookupname, lookupdata in next, lookups do +                    action[lookuptypes[lookupname]](lookupdata,lookupname,unicode,lookuphash) +                end +            end + +            local lookups = description.mlookups +            if lookups then +                for lookupname, lookuplist in next, lookups do +                    local lookuptype = lookuptypes[lookupname] +                    for l=1,#lookuplist do +                        local lookupdata = lookuplist[l] +                        action[lookuptype](lookupdata,lookupname,unicode,lookuphash) +                    end +                end +            end + +            local list = description.kerns +            if list then +                for lookup, krn in next, list do  -- ref to glyph, saves lookup +                    local target = lookuphash[lookup] +                    if target then +                        target[unicode] = krn +                    else +                        lookuphash[lookup] = { [unicode] = krn } +                    end +                end +            end + +            local list = description.anchors +            if list then +                for typ, anchors in next, list do -- types +                    if typ == "mark" or typ == "cexit" then -- or entry? +                        for name, anchor in next, anchors do +                            local lookups = anchor_to_lookup[name] +                            if lookups then +                                for lookup, _ in next, lookups do +                                    local target = lookuphash[lookup] +                                    if target then +                                        target[unicode] = anchors +                                    else +                                        lookuphash[lookup] = { [unicode] = anchors } +                                    end +                                end +                            end +                        end +                    end +                end +            end + +        end + +    end + +end + +local function split(replacement,original) +    local result = { } +    for i=1,#replacement do +        result[original[i]] = replacement[i] +    end +    return result +end + +local valid = { +    coverage        = { chainsub = true, chainpos = true, contextsub = true }, +    reversecoverage = { reversesub = true }, +    glyphs          = { chainsub = true, chainpos = true }, +} + +local function prepare_contextchains(tfmdata) +    local rawdata    = tfmdata.shared.rawdata +    local resources  = rawdata.resources +    local lookuphash = resources.lookuphash +    local lookups    = rawdata.lookups +    if lookups then +        for lookupname, lookupdata in next, rawdata.lookups do +            local lookuptype = lookupdata.type +            if lookuptype then +                local rules = lookupdata.rules +                if rules then +                    local format = lookupdata.format +                    local validformat = valid[format] +                    if not validformat then +                        report_prepare("unsupported format %a",format) +                    elseif not validformat[lookuptype] then +                        -- todo: dejavu-serif has one (but i need to see what use it has) +                        report_prepare("unsupported format %a, lookuptype %a, lookupname %a",format,lookuptype,lookupname) +                    else +                        local contexts = lookuphash[lookupname] +                        if not contexts then +                            contexts = { } +                            lookuphash[lookupname] = contexts +                        end +                        local t, nt = { }, 0 +                        for nofrules=1,#rules do +                            local rule         = rules[nofrules] +                            local current      = rule.current +                            local before       = rule.before +                            local after        = rule.after +                            local replacements = rule.replacements +                            local sequence     = { } +                            local nofsequences = 0 +                            -- Eventually we can store start, stop and sequence in the cached file +                            -- but then less sharing takes place so best not do that without a lot +                            -- of profiling so let's forget about it. +                            if before then +                                for n=1,#before do +                                    nofsequences = nofsequences + 1 +                                    sequence[nofsequences] = before[n] +                                end +                            end +                            local start = nofsequences + 1 +                            for n=1,#current do +                                nofsequences = nofsequences + 1 +                                sequence[nofsequences] = current[n] +                            end +                            local stop = nofsequences +                            if after then +                                for n=1,#after do +                                    nofsequences = nofsequences + 1 +                                    sequence[nofsequences] = after[n] +                                end +                            end +                            if sequence[1] then +                                -- Replacements only happen with reverse lookups as they are single only. We +                                -- could pack them into current (replacement value instead of true) and then +                                -- use sequence[start] instead but it's somewhat ugly. +                                nt = nt + 1 +                                t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements } +                                for unic, _  in next, sequence[start] do +                                    local cu = contexts[unic] +                                    if not cu then +                                        contexts[unic] = t +                                    end +                                end +                            end +                        end +                    end +                else +                    -- no rules +                end +            else +                report_prepare("missing lookuptype for lookupname %a",lookupname) +            end +        end +    end +end + +-- we can consider lookuphash == false (initialized but empty) vs lookuphash == table + +local function featuresinitializer(tfmdata,value) +    if true then -- value then +        -- beware we need to use the topmost properties table +        local rawdata    = tfmdata.shared.rawdata +        local properties = rawdata.properties +        if not properties.initialized then +            local starttime = trace_preparing and os.clock() +            local resources = rawdata.resources +            resources.lookuphash = resources.lookuphash or { } +            prepare_contextchains(tfmdata) +            prepare_lookups(tfmdata) +            properties.initialized = true +            if trace_preparing then +                report_prepare("preparation time is %0.3f seconds for %a",os.clock()-starttime,tfmdata.properties.fullname) +            end +        end +    end +end + +registerotffeature { +    name         = "features", +    description  = "features", +    default      = true, +    initializers = { +        position = 1, +        node     = featuresinitializer, +    }, +    processors   = { +        node     = featuresprocessor, +    } +} + +-- This can be used for extra handlers, but should be used with care! + +otf.handlers = handlers diff --git a/tex/generic/context/luatex/luatex-fonts.lua b/tex/generic/context/luatex/luatex-fonts.lua index 7995be33e..5e5c9a4cf 100644 --- a/tex/generic/context/luatex/luatex-fonts.lua +++ b/tex/generic/context/luatex/luatex-fonts.lua @@ -210,9 +210,9 @@ if non_generic_context.luatex_fonts.skip_loading ~= true then          loadmodule('font-oti.lua')          loadmodule('font-otf.lua')          loadmodule('font-otb.lua') -        loadmodule('node-inj.lua')         -- will be replaced (luatex >= .70) +        loadmodule('luatex-fonts-inj.lua') -- will be replaced (luatex >= .80)          loadmodule('font-ota.lua') -        loadmodule('font-otn.lua') +        loadmodule('luatex-fonts-otn.lua')          loadmodule('font-otp.lua')         -- optional          loadmodule('luatex-fonts-lua.lua')          loadmodule('font-def.lua')  | 
