Contents

Pageless PDF

Tools

Pageless PDF

A pageless PDF is a LA TEX document typeset by LuaTeX as one page as tall as the document: no page breaks, no floats moved to the top of a page, no space stretched to fill one. Every box and every glue is where TEX put it on the main vertical list. It is the geometry the browser reproduces, so it is the PDF to compare the browser with, line by line and down to the glyph.

Below is the AMS sample paper testmath.tex as a pageless PDF, 345pt wide and nearly 20000pt tall.

Download the PDF (one page, 415 × 19,742 bp). Some PDF readers refuse pages taller than 14,400 bp; browsers show it.

How it is made

The pipeline already keeps the galley: before the page builder sees anything, the serializer copies each item TEX adds to the main vertical list, with its real dimensions. pageless.ts compiles the document the way the pipeline does, inside the extraction template, and adds two things: pageless_pdf.lua, which at the end of the run ships that copy as a few pages of up to 16000pt (the largest a TEX dimension allows), and stack.ts, which stacks those pages into one.

Pages are cut only inside vertical glue or a kern, so nothing TEX placed moves, and stacking them edge to edge gives every distance back exactly. What a page’s output routine would have removed goes too: empty float markers, and the \topskip added after them. TikZ pictures are drawn back in their places.

Making one

The tool is in tools/pageless-pdf/. It needs LuaTeX and Node, with the repository’s packages installed (npm ci):

npm ci
node tools/pageless-pdf/pageless.ts paper.tex -o out/
# → out/pageless.pdf, and out/output.json from the same run

Options: --passes N (2 by default; a document with references needs 2 or 3), --margin (white either side of the column, 36pt by default), --template (the extraction template; testmath.tex has its own), and --width-extra (added to the document’s \textwidth, to typeset it wider than its own column).

Comparing with the browser

The same directory holds the comparison. site-from-run.ts builds a page from the run’s own output.json, so the PDF and the page come from one compilation. Two checks then compare them at the PDF’s column width:

vector-compare.ts No pixels: the position of every glyph in the PDF (from MuPDF’s trace) against the position the browser gave it, to a hundredth of a point. Run it first; whatever it reports is geometry, not rasterisation.

compare.ts Pixels: both drawn at the same scale, the rows paired line by line, each pair counted as identical, differing in place, or the same ink moved. It writes a side-by-side picture, a heat map and a report. tiles.ts cuts them into tiles for a web page.

The results are on Render tests, for every test, and Pixel comparison, for testmath.tex.

npx playwright install chromium
T=tools/pageless-pdf
node $T/site-from-run.ts out/ out/site/           # then serve out/site/, e.g. on port 8000
node $T/vector-compare.ts out/ http://localhost:8000/index.html
node $T/compare.ts out/ http://localhost:8000/index.html --ppp 4