I have been using LuaTeX recently and have thoroughly enjoyed typesetting my documents with it. There are still a few issues though I am trying to solve though. One of the issues is introducing page references so that a document can reference a page number and so forth.
I am already very familiar with the 2-pass concept in which on one scan of the document, you have TeX write to an auxiliary file or table of contents file, then on the next scan it can actually use the page numbers from said file to add references to the doc. I still intend to do this, but now I want to accomplish it with Lua as part of LuaTex.
Attempt 1
As a first pass I thought I could just reference the 0 counter since TeX holds the page number in that. Something like
\def\MakePageReference#1{ % #1 is the name of the reference we wish to track \directlua{ tex.count[0] -- use this value to get the page number and write it to a file (aux/toc file) } } \def\PlaceReference#1{ % #1 the name of the page reference we created % do something in here to get the page number of said reference } However this doesn't work too well after learning about how LuaTex executes its code alongside the TeX engine. For starters, it appears that LuaTeX will start expanding the directlua calls immediately which means in the same Lua body, the page number will never be updated. I have to do some kind of switching back and forth between TeX and Lua and that led me down this coroutine rabbit hole: Concurrently interleaving execution of Lua and TeX in LuaTeX.
Attempt 2
Since TeX only knows the page number for some material during the shipout process, I thought I could maybe take advantage of some of LuaTeX's callbacks.
I could create a whatsit Lua node every time I introduce a page reference and then hook into the pre_output_filter that LuaTeX provided. Something similar to the following (some of this will be pseudo code since I am forgetting the syntax):
\def\MakePageRef#1{ #1 % Output the material as normal, such as word \directlua{ new_node = node.new("whatsit", "user_defined") new_node.value = "#1" -- give the node the value of the text node.write(new_node) } } \directlua{ function page_output_callback(head, ...other params...) -- loop through each node of head and if the type is whatsit -- get the page number with tex.count[0] and write this to a file end tex.register_callback("pre_output_filter", page_output_callback) } However I still get stumped on this since as I iterate through the nodes of head in my callback function, my whatsit nodes do not appear in the list.
If anyone has more experience on this or could mention a better way of handling pagerefs in LuaTeX, I would greatly appreciate it.
Edit 1
Added a MWE for the pre_output_filter attempt I described
-- helper.lua function create_node_reference(name) whatsit_id = node.id("whatsit") user_define_id = node.subtype("user_defined") local new_node = node.new(whatsit_id, user_define_id) new_node.type = 115 -- internal code for a string value new_node.value = name node.write(new_node) end function print_whatsit_nodes_page(head, groupcode, size, packtype, maxdepth) -- traverse_id will only iterate over nodes with the specified id, in this case "whatsit" -- There seems to be no whatsit nodes though in this list for n in node.traverse_id(node.id("whatsit"), head) do print(n) end end callback.register("pre_output_filter", print_whatsit_nodes_page) Main tex file
\directlua{ dofile("helper.lua") } \def\CreateRef#1{ \directlua{ create_node_reference("#1") } } \CreateRef{Test here on page 1} \vfil \eject \CreateRef{Something on page 2} \vfil \eject Edit 2
After a discovery it appears that if there is text (or horizontal material?) right before the directlua call, the node.write appears to not work.
\def\CreateRef#1{ Some text here \directlua{ create_node_reference("#1") } }
pre_output_filteris the wrong place for this since LaTeX calls the output routine every time that you add a footnote or figure. Instead, you probably want to usepre_shipout_filter. You should probably also look into\latelua, which is like\directluaexcept that it isn't executed until shipout.head, although you'll likely need to recursively traverse the list (function f(n) for m in node.traverse(n) do if m.head then f(m) end end f(head)) to find them.luaand three to close (on their own lines) also works.