Skip to content

Ask pdfium the four questions that were open, and act on the two it answered against us - #17

Merged
tannevaled merged 2 commits into
mainfrom
pdfium-oracle
Sep 4, 2026
Merged

Ask pdfium the four questions that were open, and act on the two it answered against us#17
tannevaled merged 2 commits into
mainfrom
pdfium-oracle

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Two changes, each with its own before/after, both settled by driving pdfium
over the corpus. They are independent and could be split; they are together
because one diagnosis produced both.

1 — Flow the one root subform that writes no layout (adab7ef)

pdf.js reads an absent or unrecognised layout as position wherever it
appears (getStringOption, utils.js:70-76), and this package followed it.
pdfium's GetLayout (cxfa_contentlayoutprocessor.cpp:365-379) returns the
attribute where it parses to one of the seven values, and where it does not
and the node's parent is the <form> root it returns Tb. A positioned
container is never flowed, so a <break> inside one is never reached — pdfium
recurses into one with bUseBreakControl false (line 1163) and the break stage
begins by testing it (line 1751). That is why the difference is a sheet count
and never a coordinate.

Four of the corpus's 560 root subforms write no layout; three of them are
forms this package put on one sheet where pdfium and pdf.js both use two.

form sheets unplaced boxes on pdfium's sheet
us-opm__sf39 1 → 2 6 → 0 96/104 → 110/110
us-opm__sf39a 1 → 2 0 → 0 24/50 → 50/50
us-opm__sf813 1 → 2 0 → 0 split 151/16, which is pdfium's 151/16

us-opm__sf181, the fourth, has one child and no break and does not move.
The whole-corpus box dump differs on those three forms and on no other.
TestPaginationAgainstPdfjs goes from six disagreements to two.

The half of bRootForceTb this does not do is named in placer.forcedTb:
pdfium also gives such a root's children their written x and y instead of the
packing cursor. Every child of all four roots writes no x and no y, so nothing
in 560 templates separates that from stacking them.

Tests that used an absent layout as shorthand for a positioned root now write
layout="position", which is what they meant.

2 — Begin a positioned child inside its container's insets (6488963)

The 3.6 pt on fr-cerfa__cerfa_12818 that v0.12.0 left open. It is not a
constant in pdfium and not an accumulation: it is the container's own written
inset. CXFA_ContentLayoutItem::GetAbsoluteRect
(xfa/fxfa/layout/cxfa_contentlayoutitem.cpp:82-90) adds every ancestor's
margin/@leftInset and @topInset on the way down, with no test of the
layout, and cxfa_ffwidget.cpp:228 makes that the rectangle pdfium draws with.
The anonymous <subform> between the cell Bouton and its four buttons writes
leftInset="1.27mm" topInset="1.27mm", and 1.27 mm is 3.6 pt. 0.05 inch is
the same number by coincidence.

placer.stack has always begun a stacked child at f.x+in.left, f.y+in.top.
The positioned arm of placer.children did not.

against pdfium before after
leaves under a table row 19454/19522 — 99.65% 19522/19522 — 100.00%
leaves under neither, the control 160022/167632 — 95.46% 163753/167632 — 97.69%
forms agreeing on x for EVERY control leaf 461/559 473/559

All 68 row disagreements are gone, the 64 on ca-cra__rc1-fill-11-25e that the
last release wrote off as "x on that form is not comparable at all" included.
9 794 boxes move on 26 of 560 forms; no sheet count and no unplaced count
changes anywhere.

Nothing regresses against pdf.js: placement 176/176 exact, heights 7794/7794,
pagination 151199/151199 on the same sheet, the same two known line violations.

What is NOT here

  • The corner anchor. 24 leaves on ca-cra__t183-fill-25e, all blocked by
    one <subform anchorType="bottomLeft"> that writes no w and no h.
    pdfium places them and its size rule is arithmetic on written numbers
    (DoLayoutPositionedContainer, 1194-1205), verified against the dump. It is
    a new capability — auto-sizing a positioned container — not a fix, and it is
    one node in 560 forms.
  • hAlign inside a line. Decided for pdfium and nothing moves: pdfium
    discards hAlign when the wrapping container writes no width
    (if (bContainerWidthAutoSize) uHAlign = 0;, 2499-2503), both of the
    corpus's two instances are such containers, and pdfium's own numbers show the
    centred child packed left in document order.

The full reading, including the four items it settles and the one it does not,
is on #7.

Gate on both commits: 100% of statements, go vet, gofmt -l empty against Go
1.27.1 via GOTOOLCHAIN, -race, nine cross-compile targets, CGO_ENABLED=0,
GOWORK=off.

🤖 Generated with Claude Code

tannevaled and others added 2 commits September 4, 2026 23:02
…lds on 3 more sheets

pdfium reads an absent `layout` on the outermost subform differently from
pdf.js, and the difference shows as a sheet count rather than as a coordinate.

pdf.js reads an absent or unrecognised `layout` as "position" wherever it
appears (getStringOption, utils.js:70-76), and this package followed it.
pdfium's GetLayout (cxfa_contentlayoutprocessor.cpp:365-379) returns the
attribute where it parses to one of the seven values, and where it does NOT
and the node's parent is the <form> root it returns Tb. A positioned container
is never flowed, so a <break> inside one is never reached — pdfium recurses
into one with bUseBreakControl false (line 1163) and the break stage begins by
testing it (line 1751) — which is why the whole of the difference is which
sheet things land on.

FOUR of the corpus's 560 root subforms write no `layout`. Three of them are
forms this package put on ONE sheet where pdfium and pdf.js BOTH use two:

    us-opm__sf39   1 -> 2 sheets, and its 6 unplaced fields are now placed
    us-opm__sf39a  1 -> 2 sheets
    us-opm__sf813  1 -> 2 sheets

The fourth, us-opm__sf181, has one child and no break, and does not move.
Nothing else in the corpus moves: the whole-corpus box dump differs on those
three forms and on no other.

Against pdfium, per form, boxes on the same sheet:

    us-opm__sf39    96/104 -> 110/110      (and 6 unplaced -> 0)
    us-opm__sf39a   24/50  -> 50/50
    us-opm__sf813   sheet split 151/16, which is pdfium's 151/16 exactly

The half of bRootForceTb this does NOT do is named in [placer.forcedTb]:
pdfium also gives such a root's children their WRITTEN x and y instead of the
packing cursor. Every child of all four roots writes no x and no y, so nothing
in 560 templates separates that from stacking them.

The tests that used an absent `layout` as shorthand for a positioned root now
write `layout="position"`, which is what they meant and what pdfium reads.

Gate: 100% of statements, go vet, gofmt -l empty against Go 1.27.1 via
GOTOOLCHAIN, -race, nine cross-compile targets, CGO_ENABLED=0, GOWORK=off.
The corpus suite is green, and TestPaginationAgainstPdfjs goes from six
disagreements to two.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…h pdfium on 3 731 more leaves

The second thing the pdfium oracle settled, and the one that had been open
since v0.12.0: on fr-cerfa__cerfa_12818 pdfium put four buttons 3.6 pt right of
where this package put them, and the line responsible had not been found.

It is not a constant in pdfium and not an accumulation. It is the container's
own written inset. CXFA_ContentLayoutItem::GetAbsoluteRect
(xfa/fxfa/layout/cxfa_contentlayoutitem.cpp:82-90) adds every ancestor's
margin/@leftInset and @topInset on the way down, with NO test of the layout,
and cxfa_ffwidget.cpp:228 makes that the rectangle pdfium draws and hit-tests
with. The template says the rest: the anonymous <subform> between the cell
`Bouton` and its four buttons writes

    <margin leftInset="1.27mm" topInset="1.27mm"
            rightInset="1.27mm" bottomInset="1.27mm"/>

and 1.27mm IS 3.6pt. 0.05 inch is the same number by coincidence.

[placer.stack] has always begun a stacked child at f.x+in.left, f.y+in.top.
The positioned arm of [placer.children] did not, and that is the whole change.
An inset nobody can read refuses the children rather than being taken as
nought, which is the refusal stack has always made.

pdf.js casts no vote: it cannot lay this form out at all, it hands <margin> to
CSS as `margin` (html_utils.js:171-174) and never resolves it into a
coordinate, and the accumulating judge sums `left` and cannot see a CSS margin.

## Before and after, against pdfium, over all 559 dumps

                                    before            after
  leaves under a table row      19454/19522       19522/19522  100.00%
  leaves under neither, the     160022/167632    163753/167632   97.69%
    control                        95.46%
  forms agreeing on x for
    EVERY control leaf              461/559          473/559

Every one of the 68 row disagreements is gone, the 64 on ca-cra__rc1-fill-11-25e
that the last release could not read included: x on that form was not
comparable at all, and this is why. y improves with it — 93.45% to 94.13% for
the control, 76.53% to 76.72% under a row — though y stays informative rather
than a verdict, because pdfium measures text and this package does not.

9 794 boxes move, on 26 of 560 forms. No sheet count and no unplaced count
changes anywhere in the corpus.

## And nothing regresses against pdf.js

  TestPlacementAgainstPdfjs     176/176 exact, 0 disagreements  (unchanged)
  TestFlowHeightsAgainstPdfjs   7794/7794, 0 disagreements      (unchanged)
  TestPaginationAgainstPdfjs    151199/151199 on the same sheet (unchanged)
  TestIntraLinePlacementProperties  the same 2 known violations (unchanged)

Gate: 100% of statements, go vet, gofmt -l empty against Go 1.27.1 via
GOTOOLCHAIN, -race, nine cross-compile targets, CGO_ENABLED=0, GOWORK=off.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 787809a into main Sep 4, 2026
1 check passed
@tannevaled
tannevaled deleted the pdfium-oracle branch September 4, 2026 21:10
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