Skip to content

fix(documents): keep an outline item /Prev readable - #432

Merged
T-Auto merged 3 commits into
T-Auto:mainfrom
ExSchwi:work/pdf-outline-prev
Oct 30, 2026
Merged

T-Auto merged 3 commits into
T-Auto:mainfrom
ExSchwi:work/pdf-outline-prev

Conversation

@ExSchwi

@ExSchwi ExSchwi commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Closes #427

What was wrong

read_pdf refused the document from the report as UNSUPPORTED_DOCUMENT ("Encrypted, incremental, object-stream and xref-stream PDFs are unsupported"), even though it is a plain classic-xref PDF with no incremental section.

native/crates/ops-documents/src/pdf_syntax.rs:216-223 made that decision at the name token: any /Encrypt, /Prev, /XRefStm, /ObjStm or /XRef seen anywhere in the file stopped parsing. But /Prev is also the key an outline item uses for its previous sibling, so an ordinary outline (<< /Title (Second) /Parent 6 0 R /Prev 7 0 R >>) made the whole document unreadable.

The change

  • pdf_syntax.rs: name() returns /Prev and /XRefStm verbatim. /Encrypt, /ObjStm and /XRef stay refused at the token, because no supported structure can hold them.
  • pdf_container.rs: those two names are refused while the trailer dictionary is read, which is the only place they denote an earlier xref section. Code (UNSUPPORTED_DOCUMENT) and message are unchanged, so a genuinely incremental file behaves exactly as before, escaped spellings (/Pr#65v) included.
  • docs/native-kernel.md: the parser paragraph now states which names are refused everywhere and which only as trailer keys.
  • docs/CHANGELOG.md: ## [Unreleased] entry.

Deliberately not done: nothing about the other three obstacles listed in the issue (MAX_PAGES = 32, the 16 MiB input cap, the 10,000 xref-entry cap); they stay separate issues.

Validation

  • one CI required run triggered by pull_request, URL pasted below
  • npm run verify output (below)
  • the checks go red when this implementation is removed (below)
  • docs/CHANGELOG.md ## [Unreleased] entry added
  • behaviour change mirrored in the related doc
  • no new dependency, no change to package.json version or optionalDependencies
  • no change to any model-facing description or parameter, so no schema/prompt byte delta
$ cd native && cargo test -p ops-documents
test pdf_container::tests::outline_prev_is_an_ordinary_key_while_a_trailer_prev_stays_unsupported ... ok
test result: ok. 20 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.30s

$ git stash push -- native/crates/ops-documents/src/pdf_syntax.rs native/crates/ops-documents/src/pdf_container.rs
$ cd native && cargo test -p ops-documents outline_prev
test pdf_container::tests::outline_prev_is_an_ordinary_key_while_a_trailer_prev_stays_unsupported ... FAILED
assertion failed at crates/ops-documents/src/pdf_container_tests.rs:97
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 19 filtered out; finished in 0.00s
$ git stash pop

$ node .repro.mjs   # the shipped worker, read_pdf, on the exact document from the report and one
                    # that really is incremental (same body, trailer carrying /Prev)
{"file":"outline-with-prev.pdf","status":"completed","code":null,"complete":true,"total":1,"firstText":"Outline sibling test"}
{"file":"incremental.pdf","status":"error","code":"UNSUPPORTED_DOCUMENT","message":"Encrypted, incremental, object-stream and xref-stream PDFs are unsupported"}

$ node test/run.mjs pdf document
dsh-ops pdf-worker: all 14 checks passed
dsh-ops test: 2 suite(s) passed in 6s

$ npm run check:size
check-file-size: 440 code file(s) checked, all at or under 1000 lines (longest: bench/setup/profile.mjs (927))

$ node scripts/verify-gate.mjs --skip-install
  ok   versions
  ok   cargo version
  ok   build the plugin
  ok   native worker unit tests
  ok   build and stage the native worker
  ok   Windows IPC payloads
  ok   manifest contract
  FAIL full suite verification
dsh-ops test: 1 of 150 suite(s) failed, 4 skipped in 450s

The one failing suite is test/linux-paths.test.mjs:143 ("a trailing separator is refused, not silently trimmed"). It fails identically on pristine main (a6e215a) on this host, before this branch is applied: node test/run.mjs linux-paths gives 1 of 8 check(s) failed, and driving native/target/release/dsh-ops-worker.exe directly shows .../norm.txt/ and ...\norm.txt\ both returning status: completed while ./norm.txt returns INVALID_ARGUMENT. main is green upstream (run 40458041379), so this is a host-specific pre-existing red, not an effect of this change; I will open a separate issue for it.

$ gh run view 40508666487 --repo T-Auto/dsh-ops --json url,event,conclusion --jq '"\(.url) \(.event) \(.conclusion)"'
https://github.com/T-Auto/dsh-ops/actions/runs/40508666487 pull_request action_required

$ gh run view 40509327439 --repo ExSchwi/dsh-ops --json url,event,conclusion
https://github.com/ExSchwi/dsh-ops/actions/runs/40509327439 pull_request success

The first URL is this PR's own pull_request run: as described in #150, the first run from a fork waits in action_required until a maintainer approves it. The second URL is the same head commit 6574899 triggered by a pull_request event inside the fork (path (b) in the template) and it is green, so the whole cross-platform matrix has already passed this branch end to end.

Environment

  • Windows 11 x64 (win32-x64)
  • Node v22.19.0, npm 10.9.3, cargo/rustc 1.98.1
  • based on upstream main a6e215a
  • install channel: git checkout, npm ci --ignore-scripts, npm run build, npm run build:ptc

/Prev and /XRefStm denote an incremental update only as trailer keys. Refusing
them at the name token made any outline item that carries a previous sibling
refuse the whole document as UNSUPPORTED_DOCUMENT (T-Auto#427).

Refuse both keys where the trailer is read, keep /Encrypt, /ObjStm and /XRef
refused at the token, and cover both directions with a regression test: the
outline document from the report parses, and the same names as trailer keys are
still refused, decoded escapes included.
@ExSchwi

ExSchwi commented Oct 27, 2026

Copy link
Copy Markdown
Contributor Author

Both validation runs point at the same head commit 6574899:

Approving the upstream run should reproduce the green result above; nothing else is pending from my side.

@T-Auto
T-Auto merged commit 1082093 into T-Auto:main Oct 30, 2026
10 checks passed
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.

[BUG] read_pdf 把大纲项里的 /Prev 当成增量更新,整个文档被判 UNSUPPORTED_DOCUMENT

2 participants