if not modules then modules = { } end modules ['node-aux'] = { 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" } -- todo: n1 .. n2 : __concat metatable local type, tostring = type, tostring local nodes, node = nodes, node local utfvalues = utf.values local nodecodes = nodes.nodecodes local glyph_code = nodecodes.glyph local hlist_code = nodecodes.hlist local vlist_code = nodecodes.vlist local attributelist_code = nodecodes.attributelist -- temporary local math_code = nodecodes.math 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 find_tail = nuts.tail local insert_node_after = nuts.insert_after local isnode = nuts.is_node local getbox = nuts.getbox local nodes_traverse_id = nodes.traverse_id local nodes_first_glyph = nodes.first_glyph local nodepool = nuts.pool local new_glue = nodepool.glue local new_glyph = nodepool.glyph local unsetvalue = attributes.unsetvalue local current_font = font.current local texsetbox = tex.setbox local report_error = logs.reporter("node-aux:error") -- At some point we figured that copying before using was the safest bet -- when dealing with boxes at the tex end. This is because tex also needs -- to manage the grouping (i.e. savestack). However, there is an easy -- solution that keeps the tex end happy as tex.setbox deals with this. The -- overhead of one temporary list node is neglectable. -- -- function tex.takebox(id) -- local box = tex.getbox(id) -- if box then -- local copy = node.copy(box) -- local list = box.list -- copy.list = list -- box.list = nil -- tex.setbox(id,nil) -- return copy -- end -- end local function takebox(id) local box = getbox(id) if box then local copy = copy_node(box) local list = getlist(box) setfield(copy,"list",list) setfield(box,"list",nil) texsetbox(id,nil) return copy end end function nodes.takebox(id) local b = takebox(id) if b then return tonode(b) end end nuts.takebox = takebox tex.takebox = nodes.takebox -- sometimes more clear -- so far local function repackhlist(list,...) local temp, b = hpack_nodes(list,...) 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 setattr(n,attr,value) local id = getid(n) if id == hlist_node or id == vlist_node then 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 getattr(n,attr) then setattr(n,attr,value) end local id = getid(n) if id == hlist_code or id == vlist_code then set_unset_attributes(getlist(n),attr,value) end end end local function unset_attributes(head,attr) for n in traverse_nodes(head) do setattr(n,attr,unsetvalue) local id = getid(n) if id == hlist_code or id == vlist_code then unset_attributes(getlist(n),attr) end end end -- 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 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 -- or id == ins_node -- or id == mark_node -- or id == adjust_node -- or id == penalty_node -- or (id == glue_node and a.spec.writable) -- 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 -- -- local function glyph_width(a) -- local ch = chardata[getfont(a)][getchar(a)] -- return (ch and ch.width) or 0 -- end -- -- local function glyph_total(a) -- local ch = chardata[getfont(a)][getchar(a)] -- return (ch and (ch.height+ch.depth)) or 0 -- end -- -- local function non_discardable(a) -- inline -- return getid(id) < math_node -- brrrr -- end -- -- local function calculate_badness(t,s) -- if t == 0 then -- return 0 -- elseif s <= 0 then -- return INF_BAD -- else -- local r -- if t <= 7230584 then -- r = t * 297 / s -- elseif s >= 1663497 then -- r = t / floor(s / 297) -- else -- r = t -- end -- r = floor(r) -- if r > 1290 then -- return INF_BAD -- else -- return floor((r * r * r + 0x20000) / 0x40000) -- 0400000 / 01000000 -- end -- end -- end -- -- left-overs -- -- local function round_xn_over_d(x, n, d) -- local positive -- was x >= 0 -- if x >= 0 then -- positive = true -- else -- x = -x -- positive = false -- end -- local t = floor(x % 0x8000) * n -- 0100000 -- local f = floor(t / 0x8000) -- 0100000 -- local u = floor(x / 0x8000) * n + f -- 0100000 -- local v = floor(u % d) * 0x8000 + f -- 0100000 -- if floor(u / d) >= 0x8000 then -- 0100000 -- report_parbuilders('arith_error') -- else -- u = 0x8000 * floor(u / d) + floor(v / d) -- 0100000 -- end -- v = floor(v % d) -- if 2*v >= d then -- u = u + 1 -- end -- if positive then -- return u -- else -- 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 nuts.firstcharacter(n,untagged) -- tagged == subtype > 255 if untagged then return first_glyph(n) else for g in traverse_id(glyph_code,n) do return g end end end -- function nodes.firstcharacter(n,untagged) -- tagged == subtype > 255 -- if untagged then -- return nodes_first_glyph(n) -- else -- for g in nodes_traverse_id(glyph_code,n) do -- return g -- end -- end -- end local function firstcharinbox(n) local l = getlist(getbox(n)) if l then for g in traverse_id(glyph_code,l) do return getchar(g) end end return 0 end nuts .firstcharinbox = firstcharinbox nodes.firstcharinbox = firstcharinbox nodes.firstcharacter = vianuts(firstcharacter) interfaces.implement { name = "buildtextaccent", arguments = "integer", actions = function(n) -- Is this crap really used? Or was it an experiment? local char = firstcharinbox(n) if char > 0 then -- context.accent(false,char) context([[\accent%s\relax]],char) end end } -- this depends on fonts, so we have a funny dependency ... will be -- sorted out .. we could make tonodes a plugin into this local function tonodes(str,fnt,attr) -- (str,template_glyph) -- moved from blob-ini if not str or str == "" then return end local head, tail, space, fnt, template = nil, nil, nil, nil, nil if not fnt then fnt = current_font() 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 if s == 32 then if space then n = copy_node(space) elseif fonts then -- depedency local parameters = fonts.hashes.identifiers[fnt].parameters space = new_glue(parameters.space,parameters.space_stretch,parameters.space_shrink) n = space end elseif template then n = copy_node(template) setvalue(n,"char",s) else n = new_glyph(fnt,s) end if attr then -- normally false when template -- setfield(n,"attr",copy_node_list(attr)) setfield(n,"attr",attr) end if head then insert_node_after(head,tail,n) else head = n end tail = n end return head, tail end 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 = find_tail(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 = find_tail(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) -- an oldie, might be replaced for i=1,#list do local n = list[i] if n 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(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 elseif not head then head = n tail = find_tail(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 = find_tail(n) else tail = n end end else -- permitting nil is convenient end end return head, tail end 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 = getid(n) if id == wantedid then if not wantedsubtype or getsubtype(n) == wantedsubtype then return n end elseif id == hlist_code or id == vlist_code then local found = locate(getlist(n),wantedid,wantedsubtype) if found then return found end end end end 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: -- -- \skip0=10pt plus 2pt minus 2pt -- \cldcontext{"\letterpercent p",tex.stretch_amount(tex.skip[0],1000)} -- 14.30887pt -- -- local gluespec_code = nodes.nodecodes.gluespec -- -- function tex.badness_to_ratio(badness) -- return (badness/100)^(1/3) -- end -- -- 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