ROB: Recover a corrupt trailing startxref pointer (closes #3238) - #3826
Conversation
When the startxref keyword belonging to the final %%EOF is corrupt (for example a truncated 'tartxref'), scan further back for the most recent intact startxref from a previous revision instead of raising 'startxref not found'. Closes py-pdf#3238.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3826 +/- ##
==========================================
+ Coverage 97.73% 97.76% +0.02%
==========================================
Files 55 55
Lines 10417 10457 +40
Branches 1931 1943 +12
==========================================
+ Hits 10181 10223 +42
+ Misses 130 129 -1
+ Partials 106 105 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
stefan6419846
left a comment
There was a problem hiding this comment.
Thanks for the PR.
Please have a look at the review comments and coverage.
Add direct tests for _find_previous_startxref_pos: same-line keyword, offset-below-keyword, the unrecoverable cases (keyword without an offset and no keyword at all) that raise PdfReadError (py-pdf#3238).
|
Pushed 3d324ba to cover the patch-coverage gap: added direct tests for |
Exercise the _MAX_STARTXREF_RECOVERY_LINES limit: a long stream with no recoverable pointer gives up rather than scanning forever (py-pdf#3238).
5f03fb0 to
7dee5c7
Compare
## What's new ### Security (SEC) - Avoid infinite loops for outlines and text extraction (#3830) by @stefan6419846 ### New Features (ENH) - Add Japanese predefined CMaps (#3800) by @yasuhiroiwaki - Font: Collect all character widths, not only those that can be unicode mapped (#3798) by @PJBrs ### Robustness (ROB) - Recover a corrupt trailing startxref pointer (closes #3238) (#3826) by @gaoflow - Handle /Pages node without /Kids during flattening (#3825) by @gaoflow - Accept inline image EI marker at the end of a content stream (#3827) by @gaoflow ### Maintenance (MAINT) - Type the always-raising deprecation helpers as `NoReturn` (#3819) by @estelledc [Full Changelog](6.12.2...6.13.0)
Summary
Some PDFs end with a broken trailing cross-reference pointer left behind by a faulty incremental update — the
startxrefkeyword for the final%%EOFhas been corrupted (e.g. it readstartxref), while an earlier, intactstartxreffrom a previous revision is still present further up the file:Today
PdfReaderaborts on such a file withPdfReadError: startxref not found, even though the document is otherwise fully readable from the earlier pointer.This was noted in #3238, where the suggestion was to "circumvent this by further looking for another startxref". This PR does exactly that: when the
startxrefkeyword expected just above the offset is missing or corrupt, it scans further back for the most recent intact pointer rather than giving up.Details
_find_startxref_posnow delegates to a new_find_previous_startxref_posfallback when the keyword line is not where it should be.startxref.startxref not founderror is still raised.On the file from #3238 the reader now opens all 11 pages and extracts text correctly, using the valid
startxrefat 160113.Test
test_startxref_corrupt_trailing_pointerbuilds a minimal PDF with a validstartxref/%%EOFfollowed by a corrupttartxreftrailer and asserts the page is recovered. It runs fully offline (no sample files).AI use disclosure
Per the project's AI policy: I used AI coding assistance while preparing this PR. Tool: Claude Code. Model: Claude Opus 4.8 (
claude-opus-4-8). I've reviewed, run, and verified the change myself and take responsibility for it; the PR text and my replies in this thread are written in my own words.