if not modules then modules = { } end modules ['node-syn'] = { 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" } -- Because we have these fields in some node that are used by sunctex, I decided (because -- some users seem to like that feature) to implement a variant that might work out better -- for ConTeXt. This is experimental code. I don't use it myself so it will take a while -- to mature. There will be some helpers that one can use in more complex situations like -- included xml files. -- -- It is unclear how the output gets interpreted. For instance, we only need to be able to -- go back to a place where text is entered, but still we need all that redundant box -- wrapping. -- -- Possible optimizations: pack whole lines. local type, rawset = type, rawset local concat = table.concat local formatters = string.formatters local trace = false trackers.register("system.syntex.visualize", function(v) trace = v end) local nuts = nodes.nuts local tonut = nuts.tonut local tonode = nuts.tonode local getid = nuts.getid local getlist = nuts.getlist local setlist = nuts.setlist local getnext = nuts.getnext local getwhd = nuts.getwhd local getwidth = nuts.getwidth local getsubtype = nuts.getsubtype local getattr = nuts.getattr local nodecodes = nodes.nodecodes local kerncodes = nodes.kerncodes local glue_code = nodecodes.glue local kern_code = nodecodes.kern local kern_disc = nodecodes.disc local rule_code = nodecodes.rule ----- math_code = nodecodes.math local hlist_code = nodecodes.hlist local vlist_code = nodecodes.vlist local glyph_code = nodecodes.glyph local fontkern_code = kerncodes.fontkern local insert_before = nuts.insert_before local insert_after = nuts.insert_after local nodepool = nuts.pool local new_latelua = nodepool.latelua local new_rule = nodepool.rule local new_hlist = nodepool.hlist local getdimensions = nuts.dimensions local getrangedimensions = nuts.rangedimensions local a_fontkern = attributes.private("fontkern") local get_synctex_fields = nuts.get_synctex_fields local set_synctex_fields = nuts.set_synctex_fields local set_syntex_tag = nodes.set_synctex_tag local getcount = tex.getcount local setcount = tex.setcount local getpos = function() getpos = backends.codeinjections.getpos return getpos() end local f_glue = formatters["g%i,%i:%i,%i"] local f_glyph = formatters["x%i,%i:%i,%i"] local f_kern = formatters["k%i,%i:%i,%i:%i"] local f_rule = formatters["r%i,%i:%i,%i:%i,%i,%i"] local f_hlist = formatters["[%i,%i:%i,%i:%i,%i,%i"] local f_vlist = formatters["(%i,%i:%i,%i:%i,%i,%i"] local s_hlist = "]" local s_vlist = ")" local f_hvoid = formatters["h%i,%i:%i,%i:%i,%i,%i"] local f_vvoid = formatters["v%i,%i:%i,%i:%i,%i,%i"] local characters = fonts.hashes.characters local synctex = { } luatex.synctex = synctex -- the file name stuff local noftags = 0 local stnums = { } local sttags = table.setmetatableindex(function(t,name) noftags = noftags + 1 t[name] = noftags stnums[noftags] = name return noftags end) function synctex.setfilename(name) if set_syntex_tag and name then set_syntex_tag(sttags[name]) end end function synctex.resetfilename() if set_syntex_tag then local name = luatex.currentfile() if name then set_syntex_tag(name) end end end -- the node stuff local result = { } local r = 0 local f = nil local nofsheets = 0 local nofobjects = 0 local last = 0 local filesdone = 0 local enabled = false local compact = true local function writeanchor() local size = f:seek("end") f:write("!" .. (size-last) .. "\n") last = size end local function writefiles() local total = #stnums if filesdone < total then for i=filesdone+1,total do f:write("Input:"..i..":"..stnums[i].."\n") end filesdone = total end end local function flushpreamble() local jobname = tex.jobname stnums[0] = jobname f = io.open(file.replacesuffix(jobname,"syncctx"),"w") f:write("SyncTeX Version:1\n") f:write("Input:0:"..jobname.."\n") writefiles() f:write("Output:pdf\n") f:write("Magnification:1000\n") f:write("Unit:1\n") f:write("X Offset:0\n") f:write("Y Offset:0\n") f:write("Content:\n") flushpreamble = writefiles end local function flushpostamble() writeanchor() f:write("Postamble:\n") f:write("Count:"..nofobjects.."\n") writeanchor() f:write("Post scriptum:\n") f:close() enabled = false end local pageheight = 0 -- todo: set before we do this! local function b_hlist(head,current,t,l,w,h,d) return insert_before(head,current,new_latelua(function() local x, y = getpos() r = r + 1 result[r] = f_hlist(t,l,x,tex.pageheight-y,w,h,d) nofobjects = nofobjects + 1 end)) end local function b_vlist(head,current,t,l,w,h,d) return insert_before(head,current,new_latelua(function() local x, y = getpos() r = r + 1 result[r] = f_vlist(t,l,x,tex.pageheight-y,w,h,d) nofobjects = nofobjects + 1 end)) end local function e_hlist(head,current) return insert_after(head,current,new_latelua(function() r = r + 1 result[r] = s_hlist nofobjects = nofobjects + 1 end)) end local function e_vlist(head,current) return insert_after(head,current,new_latelua(function() r = r + 1 result[r] = s_vlist nofobjects = nofobjects + 1 end)) end local function x_hlist(head,current,t,l,w,h,d) return insert_before(head,current,new_latelua(function() local x, y = getpos() r = r + 1 result[r] = f_hvoid(t,l,x,tex.pageheight-y,w,h,d) nofobjects = nofobjects + 1 end)) end local function x_vlist(head,current,t,l,w,h,d) return insert_before(head,current,new_latelua(function() local x, y = getpos() r = r + 1 result[r] = f_vvoid(t,l,x,tex.pageheight-y,w,h,d) nofobjects = nofobjects + 1 end)) end -- local function x_glyph(head,current,t,l) -- return insert_before(head,current,new_latelua(function() -- local x, y = getpos() -- r = r + 1 -- result[r] = f_glyph(t,l,x,tex.pageheight-y) -- nofobjects = nofobjects + 1 -- end)) -- end -- local function x_glue(head,current,t,l) -- return insert_before(head,current,new_latelua(function() -- local x, y = getpos() -- r = r + 1 -- result[r] = f_glue(t,l,x,tex.pageheight-y) -- nofobjects = nofobjects + 1 -- end)) -- end -- local function x_kern(head,current,t,l,k) -- return insert_before(head,current,new_latelua(function() -- local x, y = getpos() -- r = r + 1 -- result[r] = f_kern(t,l,x,tex.pageheight-y,k) -- nofobjects = nofobjects + 1 -- end)) -- end -- local function x_rule(head,current,t,l,w,h,d) -- return insert_before(head,current,new_latelua(function() -- local x, y = getpos() -- r = r + 1 -- result[r] = f_rule(t,l,x,tex.pageheight-y,w,h,d) -- nofobjects = nofobjects + 1 -- end)) -- end local function collect(head,t,l) local current = head while current do local id = getid(current) if id == glyph_code then local first = current local last = current while true do id = getid(current) if id == glyph_code or id == disc_code then last = current elseif id == kern_code and (getsubtype(current) == fontkern_code or getattr(current,a_fontkern)) then last = current else if id == glue_code then -- we could go on when we're in the same t/l run local tc, lc = get_synctex_fields(current) if tc > 0 then t, l = tc, lc end id = nil -- so no test later on end local w, h, d = getdimensions(first,getnext(last)) -- local w, h, d = getrangedimensions(head,first,getnext(last)) if trace then -- color is already handled so no colors head = insert_before(head,first,new_hlist(new_rule(w,32768,32768))) end if h < 655360 then h = 655360 end if d < 327680 then d = 327680 end head = x_hlist(head,first,t,l,w,h,d) break end current = getnext(current) if not current then local w, h, d = getdimensions(first,getnext(last)) -- local w, h, d = getrangedimensions(head,first,getnext(last)) if trace then -- color is already handled so no colors head = insert_before(head,first,new_hlist(new_rule(w,32768,32768))) end if h < 655360 then h = 655360 end if d < 327680 then d = 327680 end head = x_hlist(head,first,t,l,w,h,d) return head end end end if id == hlist_code then local list = getlist(current) local tc, lc = get_synctex_fields(current) if tc > 0 then t, l = tc, lc end if compact then if list then local l = collect(list,t,l) if l ~= list then setlist(current,l) end end else local w, h, d = getwhd(current) if w == 0 or (h == 0 and d == 0) then if list then local l = collect(list,t,l) if l ~= list then setlist(current,l) end end elseif list then -- head = b_hlist(head,current,t,l,w,h,d) head = b_hlist(head,current,0,0,w,h,d) local l = collect(list,t,l) if l ~= list then setlist(current,l) end head, current = e_hlist(head,current) else -- head = x_hlist(head,current,t,l,w,h,d) head = x_hlist(head,current,0,0,w,h,d) end end elseif id == vlist_code then local list = getlist(current) local tc, lc = get_synctex_fields(current) if tc > 0 then t, l = tc, lc end if compact then if list then local l = collect(list,t,l) if l ~= list then setlist(current,l) end end else local w, h, d = getwhd(current) if w == 0 or (h == 0 and d == 0) then if list then local l = collect(list,t,l) if l ~= list then setlist(current,l) end end elseif list then -- head = b_vlist(head,current,t,l,w,h,d) head = b_vlist(head,current,0,0,w,h,d) local l = collect(list,t,l) if l ~= list then setlist(current,l) end head, current = e_vlist(head,current) else -- head = x_vlist(head,current,t,l,w,h,d) head = x_vlist(head,current,0,0,w,h,d) end end elseif id == glue_code then local tc, lc = get_synctex_fields(current) if tc > 0 then t, l = tc, lc end -- head = x_glue(head,current,t,l) -- elseif id == kern_code then -- local tc, lc = get_synctex_fields(current) -- if tc > 0 then -- t, l = tc, lc -- end -- -- local k = getwidth(current) -- -- if k ~= 0 then -- -- head = x_kern(head,current,t,l,k) -- -- end -- elseif id == rule_code then -- local tc, lc = get_synctex_fields(current) -- if tc > 0 then -- t, l = tc, lc -- end -- -- if t > 0 and l > 0 then -- -- local w, h, d = getwhd(current) -- -- head = x_rule(head,current,t,l,w,h,d) -- -- end end current = getnext(current) end return head end -- range of same numbers function synctex.collect(head) if enabled then result, r = { }, 0 head = collect(tonut(head),0,0) return tonode(head), true else return head, false end end -- also no solution for bad first file resolving in sumatra function synctex.flush() if enabled then nofsheets = nofsheets + 1 -- could be realpageno flushpreamble() writeanchor() f:write("{"..nofsheets.."\n") if compact then f:write(f_vlist(0,0,0,0,tex.pagewidth,tex.pageheight,0)) f:write("\n") end f:write(concat(result,"\n")) if compact then f:write("\n") f:write(s_vlist) end f:write("\n") writeanchor() f:write("}"..nofsheets.."\n") nofobjects = nofobjects + 2 result, r = { }, 0 end end function synctex.enable() if not enabled and node.set_synctex_mode then enabled = true node.set_synctex_mode(1) tex.normalsynctex = 0 nodes.tasks.appendaction("shipouts", "after", "nodes.synctex.collect") end end function synctex.finish() if enabled then flushpostamble() end end -- not the best place luatex.registerstopactions(synctex.finish) nodes.tasks.appendaction("shipouts", "after", "luatex.synctex.collect") -- moved here local report_system = logs.reporter("system") local synctex = false directives.register("system.synctex", function(v) if v == "context" then luatex.synctex.enable() setcount("normalsynctex",0) synctex = true else v = tonumber(v) or (toboolean(v,true) and 1) or (v == "zipped" and 1) or (v == "unzipped" and -1) or 0 setcount("normalsynctex",v) synctex = v ~= 0 end if synctex then report_system("synctex functionality is enabled (%s), expect runtime overhead!",tostring(v)) else report_system("synctex functionality is disabled!") end end) statistics.register("synctex tracing",function() if synctex or getcount("normalsynctex") ~= 0 then return "synctex has been enabled (extra log file generated)" end end)