Skip to content

Float placement: a captured body that keeps its assignments, and floats that ride their own page - #196

Merged
tannevaled merged 1 commit into
mainfrom
floats
Sep 3, 2026
Merged

tannevaled merged 1 commit into
mainfrom
floats

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Measuring the gated single-column placer over the 157-paper arXiv corpus put it
1059 pages worse than the inline path it replaces. Two defects, both now
fixed and both read back against latex.ltx.

The body was executed without a group

All of those 1059 pages came from ONE paper. The captured float body ran with no
group of its own, so an assignment inside it escaped into the document — where
LaTeX sets a float inside \vbox\bgroup…\egroup (latex.ltx:12950), and this
engine's own inline \@float opens a \begingroup that \end@float closes.

The paper has a figure holding \put(-0.33\textwidth,0.5\textwidth){…}. \put
is undefined here, so \textwidth reads as the start of an assignment and takes
its missing number as zero — exactly what TeX itself would do. \hsize then
stayed 0pt for the remaining 230 pages, which set one word per line: 1439
pages against a reference of 333.

(Defining \put and the rest of ltpictur is worth doing on its own; it is not
what this PR is about. With the body properly grouped, an undefined command
inside a figure can no longer reach past it.)

A float could not ride the page it was written on

A float was only a candidate for a page when it was anchored BEFORE that page
began, so every float came out at least a page late and, once the text ran out,
on a float page of its own. LaTeX contributes a float at its anchor — \@xfloat
fires the output routine there with \@floatpenalty — and \@addtocurcol
(latex.ltx:15636) tests it against the room LEFT in the column: it rides the page
being built whenever \@colroom exceeds the height already set plus
\textfraction plus the float itself. That is why a figure declared halfway down
a page comes out at the top of that page.

Floats now join the page they are written on, keeping their order per caption
type — \@bitor\@currtype\@deferlist: figure 3 never precedes figure 2.

Measured, 157 arXiv papers against tectonic, GOTEX_FLOATS=1

page error exact divergence (50 papers)
inline (flag off) 632 20 4.408
placer, before 1691 21
placer, body grouped 619 21
placer, both fixes 620 21 4.438

beamer is untouched: 40 pages of error over 79 decks either way. The divergence
difference is inside the noise band this measure showed when the same binary was
run over two different 50-paper samples (±0.07).

The flag stays OFF in this PR. What it changes is that turning it on is now an
improvement rather than a regression — that, with the figure geometry work of
issue #146, is the next step.

Two regression tests, each failing on main: an assignment inside a captured
figure leaves \hsize alone, and a figure written inside a page comes out on it.

…loat on its own page

Two defects in the gated single-column placer, both found by measuring it over the
157-paper arXiv corpus, where it was 1059 pages WORSE than the inline path.

The whole of that came from one paper. A captured float body was executed with no
group of its own, so an assignment inside it escaped into the document — where
LaTeX sets the float inside \vbox\bgroup…\egroup (latex.ltx:12950) and the
engine's own inline \@float opens a \begingroup that \end@float closes. The paper
has a figure holding \put(-0.33\textwidth,0.5\textwidth){…}: \put is undefined
here, so \textwidth reads as the start of an assignment and takes its missing
number as zero, and \hsize stayed 0pt for the remaining 230 pages. They set one
word per line, and the paper ran to 1439 pages against a reference of 333.

Second, a float was only a candidate for a page when it was anchored BEFORE that
page began, so every float came out at least a page late and, once the text ran
out, on a float page of its own. LaTeX contributes a float at its anchor —
\@Xfloat fires the output routine there — and \@addtocurcol (latex.ltx:15636)
tests it against the room LEFT in the column: it rides the page being built
whenever \@colroom exceeds the height already set plus \textfraction plus the
float. Floats now join the page they are written on, in their own order per
caption type (\@bitor\@currtype\@deferlist: figure 3 never precedes figure 2).

Measured over 157 arXiv papers against tectonic, with GOTEX_FLOATS=1:

    page error   1691 -> 620   (inline, flag off: 632)
    exact         21 -> 21     (inline: 20)
    divergence   4.438         (inline: 4.408 — flat within this measure's noise)

beamer is untouched (40 over 79 decks either way). The flag stays OFF by default;
this makes the placer worth turning on, which is the next step.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 46609f1 into main Sep 3, 2026
18 checks passed
@tannevaled
tannevaled deleted the floats branch September 3, 2026 16:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant