Wow, it works! (or, nodes and output routines)

Introduction

As I explore more of LuaTeX I’m constantly amazed by its versatility. Over the last few days I have been reading about \output routines which are, deservedly, deemed some of the more complex parts of TeX. Caveat to readers: I do not profess to understand very much about \output routines and am not going to cover the genuinely complicated areas such as inserts (footnotes, graphics etc), main vertical lists, page breaking, recent contributions, etc. The following is based on typesetting pure text with no bells or whistles such as headers, footers, page numbers etc.

My objective was to experiment with Plain TeX to understand more about the relationship between the size of the PDF page and just how you control where the typeset results are physically placed within the PDF page area. With the clear cavets above in mind, the way I think about this is that TeX is busily building the typeset output into a box (number 255) with width \hsize and height \vsize and when it decides there is enough to dump out a page it will “call” the \output routine. It is through the \output routine that you control the physical location of TeX’s page (box 255) within your PDF page area. This is undoubtedly a pretty simple-minded view but it works for me (until I know better…).

Setting the page parameters via Lua code

To start with, here is a small LuaTeX routine to set up the PDF page size together with \hsize and \vsize, again for use with Plain TeX. Note you can also set \hoffset, \voffset and \topskip through the LuaTeX API.

\setpageparams#1#2#3#4{…} takes 4 values (assumed in mm), converts them to TeX’s special points, and assigns the values to the appropriate dimension parameter.

\pdfoutput=1
\hoffset-1in
\voffset-1in
\topskip=0pt
\nopagenumbers

\def\setpageparams#1#2#3#4{%
\directlua{
        local mm = 25.4
        tex.pdfpagewidth=(#1/mm)*72.27*65536
        tex.pdfpageheight= (#2/mm)*72.27*65536
        tex.hsize=(#3/mm)*72.27*65536
        tex.vsize=(#4/mm)*72.27*65536
}}

\setpageparams{95}{200}{90}{180}
\def\para{Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance.\par}

\para\para\para\para\para

\bye

Centering \box255 within our PDF page area

The following code performs three main tasks:

  1. It creates an \output routine which places \box255 horizontally and vertically centred within the PDF page area.
  2. It sets up a Lua table (coords) which contains the corner coordinates of the centred \box255 relative to the PDF page origin, which I’m assuming is the standard lower-left corner of the PDF page.
  3. Using LuaTeX nodes (pdf_literal) it creates a shaded box that sits behind \box255 and is shipped out on every page using the \output routine.
\pdfoutput=1
\hoffset-1in
\voffset-1in
\topskip=0pt
\maxdepth=0pt

\def\setpageparams#1#2#3#4{%
\directlua{

function sptobp(sp)
	return string.format("\%3.3f", (sp*72.0)/(72.27*65536.0))
end

% The function makebox() accepts the array of coordinates and uses them to
% construct the PDF code to draw a shaded box the same size as \box 255

function makebox(coords)

% print to terminal for debugging
print(coords["BL"].x, coords["BL"].y)
print(coords["BR"].x, coords["BR"].y)
print(coords["TL"].x, coords["TL"].y)
print(coords["TR"].x, coords["TR"].y)

data=string.format("q \%3.3f \%3.3f  m \%3.3f \%3.3f  l \%3.3f \%3.3f  l \%3.3f \%3.3f  l   \%3.3f \%3.3f  l 0.5 w 0.75 g f Q",
 coords["BL"].x, coords["BL"].y,
 coords["TL"].x, coords["TL"].y, 
 coords["TR"].x, coords["TR"].y, 
 coords["BR"].x, coords["BR"].y,
 coords["BL"].x, coords["BL"].y
  )

% A pdf_literal node to draw the box
% VERY IMPORTANT: node.mode is set to 2 so that 
% the code origin is relative to the lower-left corner
% of the PDF page. 

pdf1 = node.new("whatsit","pdf_literal")  
pdf1.mode = 2
pdf1.data=data
return(pdf1)
end

local mm = 25.4
tex.pdfpagewidth=(#1/mm)*72.27*65536
tex.pdfpageheight= (#2/mm)*72.27*65536
tex.hsize=(#3/mm)*72.27*65536
tex.vsize=(#4/mm)*72.27*65536

% The use of TeX token registers in the following code is really not
% necessary I just added this as an example to show that you can
% use them in this way

tex.toks[500]=sptobp(0.5*(tex.pdfpagewidth - tex.hsize))
tex.toks[501]=sptobp(0.5*(tex.pdfpageheight - tex.vsize))

% The next part of the code creates a Lua table which stores the
% corner coordinates of \box255 relative to the PDF page
% origin, based on \box255 being horizontally and vertically centred
% within the PDF page area. 

coords={}

coords["BL"]={x=tex.toks[500], y=tex.toks[501]}
coords["BR"]={x=tex.toks[500]+sptobp(tex.hsize), y=tex.toks[501]} 
coords["TL"]={x=tex.toks[500], y=tex.toks[501]+sptobp(tex.vsize)}
coords["TR"]={x=tex.toks[500]+sptobp(tex.hsize), y=tex.toks[501]+sptobp(tex.vsize)} 

% Here we create a pdf_literal and store it in into an hbox.
% The pdf_literal is created by the function makebox() and the
% result is packed into \box2001 which we shipout on every
% page via the \output routine.

tex.box[2001]= node.hpack(makebox(coords))

}}


\setpageparams{95}{200}{80}{175}

% OK, here is my first attempt at an output routine. This is responsible for
% horizontally and vertically centering \box255 within the PDF page area
% and shipping out our \box2001 to draw the shaded box on each page.
% It outline, it starts by creating a \vbox to the same size as \pdfpageheight
% and sandwiches \box255 with \vfill glue top and bottom to centre
% it vertically. Inside that \vbox is an \hbox to \pdfpagewidth with \hfill
% glue to centre \box255 horizontally. \copy2001 is used to add our
% shaded box onto every page

\output={\shipout\vbox to \pdfpageheight{\vfill\vbox to \vsize{%
\offinterlineskip\copy2001\vfill\hbox to \pdfpagewidth{\hfill\box255 \hfill}%
\vfill}\vfill}}

\def\para{Hello Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet.."\par}

\para\para\para
\bye

Example results

Again I prefer to use Google Docs PDF viewer. Download here if you can’t see the resulting PDF. I hope this example is useful and, as always, I’d appreciate any notice of any technical errors.