From ff54944f72aa8a402a330a82e847c9c19fba5f24 Mon Sep 17 00:00:00 2001 From: Marius Date: Tue, 7 Jan 2014 15:20:21 +0200 Subject: beta 2014.01.07 14:00 --- tex/context/base/anch-pos.lua | 72 +- tex/context/base/back-exp.lua | 92 +- tex/context/base/cont-new.mkiv | 2 +- tex/context/base/context-version.pdf | Bin 4112 -> 4091 bytes tex/context/base/context.mkiv | 2 +- tex/context/base/font-chk.lua | 28 +- tex/context/base/font-col.lua | 21 +- tex/context/base/font-ctx.lua | 38 +- tex/context/base/font-gds.lua | 33 +- tex/context/base/font-nod.lua | 437 --- tex/context/base/font-odv.lua | 1010 ++++--- tex/context/base/font-otn.lua | 642 ++-- tex/context/base/font-otx.lua | 130 +- tex/context/base/font-sol.lua | 174 +- tex/context/base/lang-rep.lua | 189 -- tex/context/base/lang-wrd.lua | 28 +- tex/context/base/lpdf-nod.lua | 81 +- tex/context/base/lpdf-tag.lua | 65 +- tex/context/base/math-dir.lua | 35 +- tex/context/base/math-noa.lua | 411 +-- tex/context/base/math-tag.lua | 129 +- tex/context/base/node-acc.lua | 114 +- tex/context/base/node-aux.lua | 354 ++- tex/context/base/node-bck.lua | 109 +- tex/context/base/node-fin.lua | 313 +- tex/context/base/node-fnt.lua | 41 +- tex/context/base/node-inj.lua | 200 +- tex/context/base/node-ltp.lua | 3192 -------------------- tex/context/base/node-mig.lua | 93 +- tex/context/base/node-pro.lua | 113 +- tex/context/base/node-ref.lua | 220 +- tex/context/base/node-res.lua | 531 ++-- tex/context/base/node-rul.lua | 125 +- tex/context/base/node-tra.lua | 341 ++- tex/context/base/node-tst.lua | 69 +- tex/context/base/node-typ.lua | 57 +- tex/context/base/pack-rul.lua | 71 +- tex/context/base/page-lin.lua | 112 +- tex/context/base/page-mix.lua | 220 +- tex/context/base/scrp-cjk.lua | 131 +- tex/context/base/scrp-eth.lua | 22 +- tex/context/base/scrp-ini.lua | 85 +- tex/context/base/spac-ali.lua | 44 +- tex/context/base/spac-chr.lua | 95 +- tex/context/base/spac-ver.lua | 407 +-- tex/context/base/status-files.pdf | Bin 24576 -> 24546 bytes tex/context/base/status-lua.pdf | Bin 228159 -> 228232 bytes tex/context/base/strc-mar.lua | 29 +- tex/context/base/supp-box.lua | 147 +- tex/context/base/tabl-xtb.lua | 131 +- tex/context/base/trac-jus.lua | 58 +- tex/context/base/trac-par.lua | 125 - tex/context/base/trac-vis.lua | 294 +- tex/context/base/typo-bld.lua | 6 +- tex/context/base/typo-brk.lua | 122 +- tex/context/base/typo-cap.lua | 103 +- tex/context/base/typo-cln.lua | 17 +- tex/context/base/typo-dha.lua | 75 +- tex/context/base/typo-dig.lua | 58 +- tex/context/base/typo-dir.lua | 32 +- tex/context/base/typo-drp.lua | 101 +- tex/context/base/typo-dua.lua | 78 +- tex/context/base/typo-dub.lua | 79 +- tex/context/base/typo-fln.lua | 91 +- tex/context/base/typo-itc.lua | 36 +- tex/context/base/typo-krn.lua | 192 +- tex/context/base/typo-mar.lua | 145 +- tex/context/base/typo-pag.lua | 64 +- tex/context/base/typo-rep.lua | 50 +- tex/context/base/typo-spa.lua | 56 +- tex/context/base/typo-tal.lua | 78 +- tex/generic/context/luatex/luatex-fonts-inj.lua | 526 ++++ tex/generic/context/luatex/luatex-fonts-merged.lua | 770 +++-- tex/generic/context/luatex/luatex-fonts-otn.lua | 2848 +++++++++++++++++ tex/generic/context/luatex/luatex-fonts.lua | 4 +- 75 files changed, 8855 insertions(+), 8138 deletions(-) delete mode 100644 tex/context/base/font-nod.lua delete mode 100644 tex/context/base/lang-rep.lua delete mode 100644 tex/context/base/node-ltp.lua delete mode 100644 tex/context/base/trac-par.lua create mode 100644 tex/generic/context/luatex/luatex-fonts-inj.lua create mode 100644 tex/generic/context/luatex/luatex-fonts-otn.lua 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",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",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",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",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",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",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 2bbd9ea9b..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.07 01:06} +\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 index db893977e..7a76449f0 100644 Binary files a/tex/context/base/context-version.pdf and b/tex/context/base/context-version.pdf differ diff --git a/tex/context/base/context.mkiv b/tex/context/base/context.mkiv index 7934f5a26..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.07 01:06} +\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-- -

This is rather experimental. We need more control and some of this -might become a runtime module instead. This module will be cleaned up!

---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]+[<[]+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]+[<[]+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]+[<[]+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>} + C+[N]+[A] + [< H+[] | {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.

--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.

-- -- 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.

-- 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.

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).

--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 <>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 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 ---[[ -

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.

-]]-- - -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 index 7782770b4..357d89ed7 100644 Binary files a/tex/context/base/status-files.pdf and b/tex/context/base/status-files.pdf differ diff --git a/tex/context/base/status-lua.pdf b/tex/context/base/status-lua.pdf index e1fb8dcc7..3d1e83812 100644 Binary files a/tex/context/base/status-lua.pdf and b/tex/context/base/status-lua.pdf differ 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 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 5255cd5d0..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/07/14 01:06:05 +-- 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 n1 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-- +

This module is a bit more split up that I'd like but since we also want to test +with plain it has to be so. This module is part of +and discussion about improvements and functionality mostly happens on the + mailing list.

+ +

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.

+ +

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.

+ +

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 users ask for it.

+ +

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.

+ +

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 + 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 has to include +then in the output eventually.

+ +

The raw table as it coms from gets reorganized in to fit out needs. +In 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).

+ +

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.

+ +

As with the code, we may decide to store more information in the + table.

+ +

Incrementing the version number will force a re-cache. We jump the number by one +when there's a fix in the library or code that +results in different tables.

+--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 "" + 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-- +

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.

+--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-- +

I will implement multiple chain replacements once I run into a font that uses +it. It's not that complex to handle.

+--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-- +

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:

+ + +xxxabcdexxx [single a->A][multiple b->BCD][ligature cde->E] xxxABCDExxx + + +

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.

+--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-- +

Here we replace start by a single variant, First we delete the rest of the +match.

+--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-- +

Here we replace start by a sequence of new glyphs. First we delete the rest of +the match.

+--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-- +

Here we replace start by new glyph. First we delete the rest of the match.

+--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-- +

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).

+--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') -- cgit v1.2.3