-
Notifications
You must be signed in to change notification settings - Fork 414
Description
Summary
When a figure uses \begin{figure}[H] (from LaTeX's float package), lualatex's PDF tagging system produces broken tag structure: /Caption and /Figure appear as direct children of /Document instead of being properly nested. This causes PDF/UA-2 validation failure: <Document> shall not contain <Caption>.
This affects multiple code paths that inject [H]:
- Code chunk figures — both knitr and Jupyter automatically inject
fig-pos='H'when code is echoed - Non-cross-referenceable captioned images —
pandoc3_figure.luainjects[H]on figures without#fig-labels - Panel layouts —
quarto-post/latex.luaforces[H]on figures inside panels
Cross-referenceable static images ({#fig-label}) were not affected only because they happen to not get [H] injected — not because of any structural difference in the LaTeX output.
Reproducer
---
title: "Code chunk figure tag structure"
lang: en
format:
pdf:
pdf-standard: ua-2
---
```{r}
#| label: fig-mpg-by-weight
#| fig-cap: "MPG vs weight by cylinder count"
library(ggplot2)
ggplot(mtcars, aes(wt, mpg, color = factor(cyl))) +
geom_point() +
geom_smooth(method = "lm")
```
See @fig-mpg-by-weight.Expected: verapdf validation passes.
Actual: verapdf reports <Document> shall not contain <Caption>.
Root cause
The [H] specifier (from LaTeX's float package) redefines the figure environment in a way that's incompatible with lualatex's PDF tagging system. Standard LaTeX placement specifiers use the native float mechanism, which the tagging system handles correctly.
The LaTeX output is structurally identical with and without [H] — both produce \begin{figure}...\caption{...}\end{figure}. The difference is entirely in how lualatex's tagging system processes the float internally.
Where [H] is injected
- Knitr:
src/resources/rmd/hooks.R:661-667— setsfig-pos='H'whenecho=TRUE, no userfig-pos, no layout options, and LaTeX output - Jupyter:
src/core/jupyter/jupyter.ts:1625-1631— same logic - Non-cross-referenceable figures:
src/resources/filters/layout/pandoc3_figure.lua:126-132— setsfig-pos='H'on figures with empty identifier - Panel layouts:
src/resources/filters/quarto-post/latex.lua— forces[H]on floats inside panels
Empirical testing of placement specifiers
Tested each placement specifier with pdf-standard: ua-2 and verapdf validation:
| Specifier | verapdf result |
|---|---|
| (none) | PASSED |
[h] |
PASSED |
[htbp] |
PASSED |
[H] |
FAILED — <Document> shall not contain <Caption> |
Only [H] (from the float package) breaks tagging. All standard LaTeX specifiers work correctly.
Proposed fix
When PDF tagging is active (pdf-standard includes a standard that requires tagging, like ua-2), replace fig-pos='H' with fig-pos='h' across all injection points. [h] is the closest semantic equivalent to [H] ("place here") without the float package's incompatible reimplementation.
Workaround
Setting echo: false on code chunks avoids the [H] injection for that specific case, since the placement logic only triggers when code is echoed.