4Dec/11Off

# Introduction

In this example we will use the "post_linebreak_filter" to iterate over all the lines in a typeset paragraph and use the data provided by LuaTeX to insert pdf_literal nodes which draw a box to show the size of the glue between words. It is based on a nice piece of code on the LuaTeX wiki by (I believe) Patrick Gundlach.

Note: The code does not recurse into nested lists; so if you have further boxes within the lines of your paragraph you should note that this code is not designed to handle them. An exercise for the reader ;-).

If you extend the code to deal with nested boxes, other glue types, such as \baselineskip etc, and account for glyph widths, heights and depths, what this leads to is the ability to calculate precise (x,y) positions within many types of node lists.

## Code and explanations

Here, we are placing a paragraph into a \vbox{There are many variations of ....} and hooking into the post_linebreak_filter to access the node list after TeX has broken the paragraph into lines. Each line in the paragraph is accessible from LuaTeX as an hlist which is traversed using the LuaTeX API call node.traverse_id(...). We then scan through the nodes contained in each hlist, looking for glue. As mentioned, the code does not recurse so we are just choosing the individual paragraph lines and not exploring anything below that level.

Each hlist contains its glue setting parameters which let us calculate by how much the individual glue items are stretched or shrunk within each line of the paragraph. From those values you can calculate the size of the set glue and draw an appropriately sized box with a pdf_literal, whose zero size does not affect the positioning of the text. The pdf_literal is inserted into the hlist node tree just before each glue node.

For further reading on TeX's glue calculations see page 77 of The TeXBook.

\pdfoutput=1
\hoffset-1in
\voffset-1in
\nopagenumbers
\directlua{

function scanskips(hlist)

local size=0
% Some debug output
print(string.char(10), "glue_order = "..hlist.glue_order, "glue_set = "..hlist.glue_set, "glue_sign = "..hlist.glue_sign)

id = node.id("glue")

for n in node.traverse_id(id, hlist.head) do
% Some more debug output
print("subtype="..n.subtype, "width: ", n.spec.width,  "stretch: ", n.spec.stretch,  "shrink: ",
n.spec.shrink, "stretch order: ", n.spec.stretch_order,
"shrink order: ", n.spec.shrink_order, string.char(10))

order=hlist.glue_order
set=hlist.glue_set
sign=hlist.glue_sign

if sign ==2 then
size=n.spec.width - n.spec.shrink*set
else
size=n.spec.width + n.spec.stretch*set
end

bp=(size/65536)*(72.0/72.27)

local pdf = node.new("whatsit","pdf_literal")
pdf.mode = 0
pdf.data = "q 0 0 "..bp.." 12 re 0.3 w S Q"
local prev = n.prev
prev.next=pdf
pdf.next = n

end %for loop
end % function
}

\directlua{
scanskips(line)
end
end

callback.register("post_linebreak_filter",linelist)
}

\setbox1000=\vbox{\hsize=50mm There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.}
\pdfpagewidth=\wd1000
\vsize=\ht1000
\pdfpageheight=\vsize
\box1000
\bye