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 \mylength
s 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...