I'll post this here, so as not to clog the original question.
But basically, the intent behind the question is to debug influence of lengths on page typesetting... I have now looked a bit into it, and can provide a partial answer: synctex "just" establishes correspondence between source .tex and output .pdf for GUI editors - however, lualatex was already helpful in understanding things a bit better.
So, I've been looking into lua-visual-debug.lua/.sty; LuaTEX Reference Manual (luatexref-t.pdf); \AtBeginShipout from package atbegshi and the output routine and \box255. Basically, I'd like to handle cases like this:
\documentclass[12pt]{article}
\usepackage{lipsum}
\begin{document}
\newlength{\mylength}
\section{Lorem Ipsum}
\lipsum[1-6]
\setlength{\mylength}{5pt}
\vspace*{\mylength}
\section{Sed commodo}
\lipsum[7-12]
\end{document}
In the example above, I'd like to see the influence of diverse \mylengths on the page typesetting. The problem is that we don't exactly know which page is the second section going to end up (ok, I have typeset the file once, so I know it is going to be on page 2 - however, we don't know that a-priori). But in any case, latex would have to typeset at least once up to the \mylength part.
Now, as far as I (crudely) understand it, after this \mylength part is crossed, the tex engine will keep adding page elements, until the page is overfilled. At this point, we can utilize \AtBeginShipout to run a macro or lua script - and apparently some things can be changed there; but I'm not yet sure which things or how to change them.
In the example below, I've used lua code (whose original setup is from lua-visual-debug.lua) to basically iterate through all of the nodes in the so-called AtBeginShipoutBox. At the moment when \AtBeginShipout runs, the AtBeginShipoutBox is essentially \box256; while \box255 is nil.
This tells me that the output routine has possibly not run yet - and most likely, the pdf page is not generated yet (and the terminal log indeed shows the script output before the [1],[2]... strings that are output upon pdf page generation, I guess).
So apparently, there should be the possibility to change things - however, I try to insert a tex.print("\\mylength=20pt ") as a "hack" in the \AtBeginShipout routine - and unfortunately, there is absolutely no change in the generated PDF.
However, if there had been a change; then I could imagine the following as a solution:
- Place a "marker" in the tex file where we want to examine (e.g. at the
\setlength command in the first example in this post)
- Run latex
- Run
\AtBeginShipout
- Has the marker been encountered on this page? If so:
- Save the page context (style at beginning)
- set
\mylength to first step of iteration
- switch temporarily output to another pdf
- have output routine run and output a single page
- From this point on, do not return execution priority to latex!
- Start a loop until last step of iteration
- Retrieve page context
- change
\mylength to next step
- have output routine run and output a single page
- (repeat loop)
So, there are two questions here:
- Can we change something like
\mylength in something like lua code running in an \AtBeginShipout; such that latex (re-)typesets the page with the new \mylength;
- Is it possible to retrieve something like a "page context" (the "style": fonts, skips etc at the start of the typesetting of a page), save it, and retrieve the saved version, again in something like lua code running in an
\AtBeginShipout?
As far as "page context", I would consider values of, say \baselineskip and such at start of typesetting of a page (this is provided by luatex as tex.baselineskip); but also if an environment (such as list) is in effect (I'd have no idea if luatex allows for checking of that or not); presumably, for those variables that luatex provides, it shouldn't be a problem to save their values somehow. But is tex.baselineskip in \AtBeginShipout the \baselineskip at the beginning of typesetting of a given page; or the one at the end (if, say, you had a \setlength{\baselineskip}{...} in the middle of the page)?
Well, that is as far as I can get now - with my limited understanding of the process, it looks like doing something like this would be possible, though I cannot find an exact procedure. So it would be really good to know if this approach is somehow fundamentally impossible (with current versions of software, at least). Also, possibly related: Concurrently interleaving execution of Lua and TeX in LuaTeX.
Anyways, here is the above example, with added lua functions to iterate and print out a node list of a page to terminal:
\documentclass[12pt]{article}
\usepackage{lipsum}
%\usepackage{everyshi} % use atbegshi instead
\usepackage{atbegshi}
\usepackage{ifluatex}
\ifluatex
\usepackage{luacode}
\begin{luacode*}
--~ module(...,package.seeall)
--~ module("...","package.seeall")
-- running this \AtBeginShipout will typeset "test" (with the quotes) on start of every page other than first (regularly broken paragraph continues)
function mytest()
tex.sprint([["test"]]) -- typesets "test" (w/ quotations)
end
-- texlive/2011/texmf-dist/tex/context/base/node-ini.lua
local function simplified(t)
local r = { }
for k, v in next, t do
r[k] = string.gsub(v,"_","")
end
return r
end
local function table2string(t)
local ret = ""
for i,v in ipairs(t) do
-- print(i,v)
ret = ret .. "[" .. i .. "]= " .. tostring(v) .. "; "
end
return ret
end
function print_basic_nodedata()
local rep = "\n"
rep = rep .. " ## node.types: " .. table2string(node.types()) .. "\n" -- doesn't list whatsit?!
rep = rep .. " ## node.whatsits: " .. table2string(node.whatsits()) .. "\n"
rep = rep .. " ## tex: " .. tostring(tex) .. " / " .. table2string(tex) .. "\n" -- empty at this point
return rep
end
print(print_basic_nodedata()) -- once here, as well
-- texlive/2011/texmf-dist/tex/context/base/node-ini.lua
function nodes_fields(n)
local id = n.id
--local node_fields = node.fields
--local nodecodes = node.types() -- simplified(node.types())
--local whatsit_node = nodecodes.whatsit -- whatsit not listed in node.types(); go explicitly
--print(id .. " / " .. table2string(nodecodes) .. " / " .. tostring(whatsit_node))
local whatsit_node = 8
if id == whatsit_node then
return node.fields(id,n.subtype) --node_fields
else
local t = node.fields(id)
return t
end
end
function node_field_vals(n)
local id = n.id
local myfields = nodes_fields(n)
local rep = ""
local whatsit_node = 8
local whatsit_node_subtypes = node.whatsits()
for k, v in next, myfields do
if ((k>0) and not(v=="head")) then -- k=0 next; k=-1 prev; k=12 head; avoid those
local vv = ""
if (v == "subtype") then
if (n.id == whatsit_node) then
vv = " (" .. tostring(whatsit_node_subtypes[n.subtype]) .. ")"
end
end
rep = rep .. ".[" .. k .. "/" .. v .. "]= " .. tostring(n[v]) .. vv .. "; "
end
end
if (n.id == whatsit_node) then
if (n.subtype == 1) then -- 1: "write"
rep = rep .. table2string(n.data[1])
end
end
return rep .. "\n"
end
level = 0
-- texlive/2011/texmf-dist/tex/luatex/lua-visual-debug/lua-visual-debug.lua
function show_page_elements(parent)
local head = parent.list
level = level + 1
print("") -- just a newline
while head do
local spacer = ""
for i=1,level,1 do
spacer = spacer .. " "
end
print(level .. spacer .. ": " .. tostring(head) .. "/" .. head.id)
--texio.write("term and log", "\nsomething\n" )
-- below print is for more verbose information:
--~ print (" " .. spacer .. ": " .. node_field_vals(head) )
if head.id == 0 or head.id == 1 then -- hbox / vbox
show_page_elements(head)
end
head = head.next
end
level = level - 1
return true
end
function start_inspect_page()
print("")
print("Start inspect page")
print("tex.output: " .. tex.output) -- shows "{\let \par \@@par \ifnum \outputpenalty <-\@M \@specialoutput ..."
print("tex.box[255]: " .. tostring(tex.box[255])) -- nil here
print("tex.box[256]: " .. tostring(tex.box[256]))
print("tex.box[AtBeginShipoutBox]: " .. tostring(tex.box["AtBeginShipoutBox"])) -- same as 256; as it says in log file: "\AtBeginShipoutBox=\box256"
--~ print(print_basic_nodedata()) -- same as the first time
show_page_elements(tex.box["AtBeginShipoutBox"])
-- perform hack
tex.print("\\mylength=20pt ")
-- tex.tprint({-2,\\mylength=20pt}) -- gets typeset
end
\end{luacode*}
\AtBeginShipout {%
%\directlua{mytest()}
\directlua{start_inspect_page()}
}
\else
\fi
\begin{document}
\newlength{\mylength}
\section{Lorem Ipsum}
% \directlua{mytest()}
\showboxdepth=100
\showboxbreadth=100
% \showbox255 % > \box255=void here
% \showbox256 % > \box256=void here
\lipsum[1-6]
\setlength{\mylength}{5pt}
\vspace*{\mylength}
\section{Sed commodo}
\lipsum[7-12]
\end{document}
EDIT: Just experimented a bit with tex.set; it seems some things can be changed in \AtBeginShipout, and still have an influence on the produced PDF, such as voffset:
-- tex.voffset = "-2cm" -- works!
-- tex.set ('global', 'voffset', '2cm') --works!
-- tex.set ('global', '\\voffset', '2cm') -- nowork
tex.set ('voffset', '-2cm') --works!
However, tex.parindent is listed on the same page in the manual, "4.13.1.2 Dimension parameters", as tex.voffset - and yet, the only way a changing effect can be seen on the PDF is with global:
--tex.set ('parindent', '2cm') -- nowork
tex.set ('global', 'parindent', '2cm') -- work
The good with this is that it shows the line-breaking of words different, which I guess "proves" that we can change stuff here - and latex will typeset with changes included. which is good news. Note that tex.baselineskip is different:
-- tex.set ('baselineskip', '2cm') -- "There should have been a lua <node> here, not an object with type string!:"
-- tex.set ('\baselineskip', '20cm') -- pass, nowork
-- tex.set ('\\baselineskip', '20cm') -- pass, nowork
... and the manual also notes: "4.13.1.4 ... The glue parameters accept and return a userdata object that represents a glue_spec node"
However, I still cannot find a way to change \mylength with tex.set, so that is still a part of the problem...