if not modules then modules = { } end modules ['page-lin'] = { version = 1.001, comment = "companion to page-lin.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } -- experimental local trace_numbers = false trackers.register("lines.numbers", function(v) trace_numbers = v end) local format = string.format local texsprint, texwrite, texbox = tex.sprint, tex.write, tex.box local ctxcatcodes = tex.ctxcatcodes local variables = interfaces.variables nodes = nodes or { } nodes.lines = nodes.lines or { } nodes.lines.data = nodes.lines.data or { } -- start step tag storage.register("lines/data", nodes.lines.data, "nodes.lines.data") -- if there is demand for it, we can support multiple numbering streams -- and use more than one attibute local hlist, vlist, whatsit = node.id('hlist'), node.id('vlist'), node.id('whatsit') local display_math = attributes.private('display-math') local line_number = attributes.private('line-number') local line_reference = attributes.private('line-reference') local current_list = { } local cross_references = { } local chunksize = 250 -- not used in boxed local has_attribute = node.has_attribute 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 whatsit = node.id("whatsit") local data = nodes.lines.data local last = #data nodes.lines.scratchbox = nodes.lines.scratchbox or 0 -- cross referencing function nodes.lines.number(n) n = tonumber(n) local cr = cross_references[n] or 0 cross_references[n] = nil return cr end local function resolve(n,m) -- we can now check the 'line' flag (todo) while n do local id = n.id if id == whatsit then -- why whatsit local a = has_attribute(n,line_reference) if a then cross_references[a] = m end elseif id == hlist or id == vlist then resolve(n.list,m) end n = n.next end end function nodes.lines.finalize(t) local getnumber = nodes.lines.number for _,p in next, t do for _,r in next, p do if r.metadata.kind == "line" then local e = r.entries local u = r.userdata e.linenumber = getnumber(e.text or 0) -- we can nil e.text e.conversion = u and u.conversion r.userdata = nil -- hack end end end end local filters = jobreferences.filters local helpers = structure.helpers jobreferences.registerfinalizer(nodes.lines.finalize) filters.line = filters.line or { } function filters.line.default(data) -- helpers.title(data.entries.linenumber or "?",data.metadata) texsprint(ctxcatcodes,format("\\convertnumber{%s}{%s}",data.entries.conversion or "numbers",data.entries.linenumber or "0")) end function filters.line.page(data,prefixspec,pagespec) -- redundant helpers.prefixpage(data,prefixspec,pagespec) end function filters.line.linenumber(data) -- raw texwrite(data.entries.linenumber or "0") end -- boxed variant, todo: use number mechanism nodes.lines.boxed = { } -- todo: cache setups, and free id no longer used -- use interfaces.cachesetup(t) function nodes.lines.boxed.register(configuration) last = last + 1 data[last] = configuration if trace_numbers then logs.report("lines","registering setup %s",last) end return last end function nodes.lines.boxed.setup(n,configuration) local d = data[n] if d then if trace_numbers then logs.report("lines","updating setup %s",n) end for k,v in next, configuration do d[k] = v end else if trace_numbers then logs.report("lines","registering setup %s (br)",n) end data[n] = configuration end return n end local the_left_margin = nodes.the_left_margin local function check_number(n,a,skip) -- move inline local d = data[a] if d then local s = d.start or 1 current_list[#current_list+1] = { n, s } if not skip and s % d.step == 0 then local tag = d.tag or "" texsprint(ctxcatcodes, format("\\makenumber{%s}{%s}{%s}{%s}{%s}{%s}\\endgraf",tag,s,n.shift,n.width,the_left_margin(n.list),n.dir)) if trace_numbers then logs.report("numbers","making number %s for setup %s: %s (%s)",#current_list,a,s,d.continue or "no") end else texsprint(ctxcatcodes, "\\skipnumber\\endgraf") end d.start = s + 1 -- (d.step or 1) end end function nodes.lines.boxed.stage_one(n) current_list = { } local head = texbox[n] if head then local list = head.list local last_a, skip = nil, false for n in traverse_id(hlist,list) do -- attr test here and quit as soon as zero found if n.height == 0 and n.depth == 0 then -- skip funny hlists else local a = has_attribute(n.list,line_number) if a and a > 0 then if last_a ~= a then if data[a].method == variables.next then skip = true end last_a = a end if has_attribute(n,display_math) then if nodes.is_display_math(n) then check_number(n,a,skip) end else -- the following test fails somehow (change in luatex?) -- if node.first_character(n.list) then check_number(n,a,skip) -- end end skip = false end end end end end function nodes.lines.boxed.stage_two(n,m) if #current_list > 0 then m = m or nodes.lines.scratchbox local t, i = { }, 0 for l in traverse_id(hlist,texbox[m].list) do t[#t+1] = copy_node(l) end for i=1,#current_list do local li = current_list[i] local n, m, ti = li[1], li[2], t[i] ti.next, n.list = n.list, ti resolve(n,m) end end end