Skip to content

Fix: brute-force xref scan loses stream position, missing recoverable trailers - #1360

Merged
BobLd merged 1 commit into
UglyToad:masterfrom
ArtificialNecessity:lenient-trailer-recovery
Jul 14, 2026
Merged

Fix: brute-force xref scan loses stream position, missing recoverable trailers#1360
BobLd merged 1 commit into
UglyToad:masterfrom
ArtificialNecessity:lenient-trailer-recovery

Conversation

@jeske

@jeske jeske commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Problem: brute-force xref scan loses stream position, missing recoverable trailers

PdfDocument.Open throws "Could not find an xref trailer or stream dictionary in the input file"
for some malformed-but-recoverable PDFs - even under lenient parsing - that Acrobat/Foxit open fine.

Repro: PDFium's Type3 test file ships a deliberately broken xref section:

pdfium_type3_basic.pdf

xref
0 
trailer
<< /Size 0 /Root 1 0 R >>
startxref
0
%%EOF

The body objects and the trailer dictionary itself are perfectly valid — only the xref machinery
around them is broken.

image

Root cause

XrefBruteForcer.FindAllXrefsInFileOrder scans the input byte-by-byte and has a handler that
captures a backup trailer dictionary — but it never fired for this file. When the scan hits
" xref" it calls XrefTableParser.TryReadTableAtOffset, which seeks the shared IInputBytes
and does not restore position on failure
. The failed parse of the malformed table left the stream
positioned past the trailer keyword, so the scan loop never saw it → LastTrailer stayed null
PdfDocumentFactory threw. The same hazard exists in the "/XRef" stream branch.

Fix

  • Snapshot bytes.CurrentOffset before the nested table/stream parse attempts and seek back
    afterwards, so the byte scan always resumes where it left off.
  • Seek the token scanner to the scan position before reading the backup trailer dictionary, since
    a preceding failed parse may have moved it elsewhere.

No behavior change for well-formed files (the brute-forcer only runs when direct xref parsing has
already failed), and no change to non-lenient error behavior.

Verified

  • The PDFium type3 file now opens; page content parses and renders correctly.
  • Existing test suite passes.

…tempts

FindAllXrefsInFileOrder scans the input byte-by-byte, but its ' xref' and
'/XRef' handlers call XrefTableParser/XrefStreamParser which seek the shared
input stream and do not restore position (including on parse failure). A
malformed xref table (e.g. 'xref\\n0 \\ntrailer...' as in PDFium's type3.pdf
test file) left the stream positioned past the 'trailer' keyword, so the
brute-forcer's own trailer-recovery handler never fired, LastTrailer stayed
null, and PdfDocument.Open threw 'Could not find an xref trailer or stream
dictionary' even under lenient parsing - for a file Acrobat/Foxit open fine.

Fix: snapshot bytes.CurrentOffset before the nested parse attempts and seek
back afterwards; also seek the token scanner to the scan position before
reading the trailer dictionary, since a prior failed parse may have moved it.
@BobLd
BobLd self-requested a review July 7, 2026 10:17
@BobLd

BobLd commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

@jeske can you add a test with the document you provided? The change looks good to me

@BobLd
BobLd merged commit 28a1878 into UglyToad:master Jul 14, 2026
2 checks passed
BobLd added a commit to BobLd/PdfPig that referenced this pull request Jul 14, 2026
BobLd added a commit that referenced this pull request Jul 14, 2026
martin-stromberg added a commit to martin-stromberg/FinanceManager that referenced this pull request Aug 23, 2026
Updated [PdfPig](https://github.com/UglyToad/PdfPig) from 0.1.15 to
0.1.16.

<details>
<summary>Release notes</summary>

_Sourced from [PdfPig's
releases](https://github.com/UglyToad/PdfPig/releases)._

## 0.1.16

## What's Changed
* Bump version to 0.1.16 by @​github-actions[bot] in
UglyToad/PdfPig#1344
* Better deal with infinite recursion in
GlyphDataTable.ReadCompositeGlyph and fix #​1347 by @​BobLd in
UglyToad/PdfPig#1348
* Accept out of range characterCode in type 3 font and fix #​1349 by
@​BobLd in UglyToad/PdfPig#1350
* Correctly clamp values in IndexedColorSpaceDetails and fix #​1351 by
@​BobLd in UglyToad/PdfPig#1352
* Ensure abbreviated key name takes precedence in inline images by
@​BobLd in UglyToad/PdfPig#1353
* Prevent infinite recursion when resolving default substitute color
space and fix #​1354 by @​BobLd in
UglyToad/PdfPig#1355
* Properly handle missing ColorSpace entry in JPX image and fix #​1356
by @​BobLd in UglyToad/PdfPig#1357
* Expose annotation appearance streams and appearance state as public by
@​jeske in UglyToad/PdfPig#1359
* PageBuilder: Fix importing page contents stored as indirect array by
@​MultisoftPontus in UglyToad/PdfPig#1358
* Properly override Equals and GetHashCode methods for IToken and add
tests by @​BobLd in UglyToad/PdfPig#1362
* Cache values in DeviceNColorSpaceDetails.GetColor() and improve
Transform() method to use array by @​BobLd in
UglyToad/PdfPig#1363
* Cache color spaces in ResourceStore when possible by @​BobLd in
UglyToad/PdfPig#1364
* Make PdfFunctionType4.Eval() allocation-free and boxing-free by
@​BobLd in UglyToad/PdfPig#1365
* Fix: brute-force xref scan loses stream position, missing recoverable
trailers by @​jeske in UglyToad/PdfPig#1360
* Add integration test for 28a1878 (#​1360) by @​BobLd in
UglyToad/PdfPig#1366
* Expose font dictionary indirect reference on FontDetails by @​jeske in
UglyToad/PdfPig#1367
* BUGFIX: Decode Indexed colour-table bytes to the base space's
component ranges by @​jeske in
UglyToad/PdfPig#1369
* Do not clip form XObject when bbox is invalid and fix #​1371 by
@​BobLd in UglyToad/PdfPig#1373
* Expose XmpMetadata.GetXmlMemory() and add warning to GetXDocument() by
@​BobLd in UglyToad/PdfPig#1374
* Move Indexed table-byte decoding onto the base colour space (in place)
by @​jeske in UglyToad/PdfPig#1375
* Fix wrong number of components for DeviceNColorSpaceDetails and
SeparationColorSpaceDetails, and ensure BaseType is correctly set by
@​BobLd in UglyToad/PdfPig#1376
* Unseal ParsingOptions by @​BobLd in
UglyToad/PdfPig#1377
* Change GetColor() signature to use ReadOnlySpan<double> and avoid
allocation by @​BobLd in UglyToad/PdfPig#1378
* Split color space details in their own files by @​BobLd in
UglyToad/PdfPig#1379
* Prefer double array over span in IColorSpaceContext so that operands
can be saved without allocation by @​BobLd in
UglyToad/PdfPig#1380
* Fix #​1234 and ensure documents are disposed in Merge() by @​BobLd in
UglyToad/PdfPig#1382
* Use StackDictionary<> for marked content, shadings and patterns in
ResourceStore by @​BobLd in UglyToad/PdfPig#1384
* PdfPageBuilder: fix ResetColor() and line width in Draw methods by
@​BobLd in UglyToad/PdfPig#1385
* Expose ILookupFilterProvider in the doc's Structure, address
IFilterProvider propagation and fix #​1243 by @​BobLd in
UglyToad/PdfPig#1386
* Properly concatenate matrix in GetGlobalTransform and fix #​1163 by
@​BobLd in UglyToad/PdfPig#1387
* Allow skipping token that are not an IndirectReferenceToken in
BasePageFactory.Create and fix #​1286 by @​BobLd in
UglyToad/PdfPig#1388
* Add PdfDocEncoding as a valid encoding in ReadEncodingDictionary when
lenient is on and fix #​1284 by @​BobLd in
UglyToad/PdfPig#1389
* Improve caching: found tokens in token scanner, resolved resources and
XObject forms by @​BobLd in UglyToad/PdfPig#1391
* Prevent NullReferenceException in NameToken.GetHashCode() race
condition by @​BobLd in UglyToad/PdfPig#1392
* Check unencrypted metadata in DecryptInternal() and fix #​1393 by
@​BobLd in UglyToad/PdfPig#1395
* Track stack depth in CMapParser and fix #​1394 by @​BobLd in
UglyToad/PdfPig#1396
* Include time in nightly version date format by @​BobLd in
UglyToad/PdfPig#1397
* Tidy up netstandard2.1 by @​BobLd in
UglyToad/PdfPig#1400

## New Contributors
* @​MultisoftPontus made their first contribution in
UglyToad/PdfPig#1358

**Full Changelog**:
UglyToad/PdfPig@v0.1.15...v0.1.16

Commits viewable in [compare
view](UglyToad/PdfPig@v0.1.15...v0.1.16).
</details>

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>
github-actions Bot pushed a commit to Ank-KhoaHo/DocToolkit that referenced this pull request Aug 24, 2026
…to 0.1.16 (#366)

Updated [AngleSharp](https://github.com/AngleSharp/AngleSharp) from
1.7.1 to 1.7.2.

<details>
<summary>Release notes</summary>

_Sourced from [AngleSharp's
releases](https://github.com/AngleSharp/AngleSharp/releases)._

## 1.7.2

Released on Sunday, August 23 2026

## What's Changed

- Fixed matching of `annotation-xml` encoding to be case-insensitive
(#​1284) @​arpitjain099
- Fixed `<xmp>` usage in `<select>` elements
- Added ability to parse immutable byte buffers without a stream
(#​1286) @​dv00d00
- Released 1.7.2 by @​FlorianRappl in
AngleSharp/AngleSharp#1288

## New Contributors

* @​arpitjain099 made their first contribution in
AngleSharp/AngleSharp#1284

**Full Changelog**:
AngleSharp/AngleSharp@1.7.1...v1.7.2

Commits viewable in [compare
view](AngleSharp/AngleSharp@v1.7.1...v1.7.2).
</details>

Updated [OfficeIMO.Excel](https://github.com/EvotecIT/OfficeIMO) from
3.2.2 to 3.2.6.

<details>
<summary>Release notes</summary>

_Sourced from [OfficeIMO.Excel's
releases](https://github.com/EvotecIT/OfficeIMO/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/EvotecIT/OfficeIMO/commits).
</details>

Updated [OfficeIMO.Excel.Pdf](https://github.com/EvotecIT/OfficeIMO)
from 3.2.2 to 3.2.6.

<details>
<summary>Release notes</summary>

_Sourced from [OfficeIMO.Excel.Pdf's
releases](https://github.com/EvotecIT/OfficeIMO/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/EvotecIT/OfficeIMO/commits).
</details>

Updated [OfficeIMO.PowerPoint](https://github.com/EvotecIT/OfficeIMO)
from 3.2.2 to 3.2.6.

<details>
<summary>Release notes</summary>

_Sourced from [OfficeIMO.PowerPoint's
releases](https://github.com/EvotecIT/OfficeIMO/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/EvotecIT/OfficeIMO/commits).
</details>

Updated
[OfficeIMO.PowerPoint.Pdf](https://github.com/EvotecIT/OfficeIMO) from
3.2.2 to 3.2.6.

<details>
<summary>Release notes</summary>

_Sourced from [OfficeIMO.PowerPoint.Pdf's
releases](https://github.com/EvotecIT/OfficeIMO/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/EvotecIT/OfficeIMO/commits).
</details>

Updated [OfficeIMO.Word](https://github.com/EvotecIT/OfficeIMO) from
3.2.2 to 3.2.6.

<details>
<summary>Release notes</summary>

_Sourced from [OfficeIMO.Word's
releases](https://github.com/EvotecIT/OfficeIMO/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/EvotecIT/OfficeIMO/commits).
</details>

Updated [OfficeIMO.Word.Html](https://github.com/EvotecIT/OfficeIMO)
from 3.2.2 to 3.2.6.

<details>
<summary>Release notes</summary>

_Sourced from [OfficeIMO.Word.Html's
releases](https://github.com/EvotecIT/OfficeIMO/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/EvotecIT/OfficeIMO/commits).
</details>

Updated [OfficeIMO.Word.Markdown](https://github.com/EvotecIT/OfficeIMO)
from 3.2.2 to 3.2.6.

<details>
<summary>Release notes</summary>

_Sourced from [OfficeIMO.Word.Markdown's
releases](https://github.com/EvotecIT/OfficeIMO/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/EvotecIT/OfficeIMO/commits).
</details>

Updated [OfficeIMO.Word.Pdf](https://github.com/EvotecIT/OfficeIMO) from
3.2.2 to 3.2.6.

<details>
<summary>Release notes</summary>

_Sourced from [OfficeIMO.Word.Pdf's
releases](https://github.com/EvotecIT/OfficeIMO/releases)._

No release notes found for this version range.

Commits viewable in [compare
view](https://github.com/EvotecIT/OfficeIMO/commits).
</details>

Updated [PdfPig](https://github.com/UglyToad/PdfPig) from 0.1.15 to
0.1.16.

<details>
<summary>Release notes</summary>

_Sourced from [PdfPig's
releases](https://github.com/UglyToad/PdfPig/releases)._

## 0.1.16

## What's Changed
* Bump version to 0.1.16 by @​github-actions[bot] in
UglyToad/PdfPig#1344
* Better deal with infinite recursion in
GlyphDataTable.ReadCompositeGlyph and fix #​1347 by @​BobLd in
UglyToad/PdfPig#1348
* Accept out of range characterCode in type 3 font and fix #​1349 by
@​BobLd in UglyToad/PdfPig#1350
* Correctly clamp values in IndexedColorSpaceDetails and fix #​1351 by
@​BobLd in UglyToad/PdfPig#1352
* Ensure abbreviated key name takes precedence in inline images by
@​BobLd in UglyToad/PdfPig#1353
* Prevent infinite recursion when resolving default substitute color
space and fix #​1354 by @​BobLd in
UglyToad/PdfPig#1355
* Properly handle missing ColorSpace entry in JPX image and fix #​1356
by @​BobLd in UglyToad/PdfPig#1357
* Expose annotation appearance streams and appearance state as public by
@​jeske in UglyToad/PdfPig#1359
* PageBuilder: Fix importing page contents stored as indirect array by
@​MultisoftPontus in UglyToad/PdfPig#1358
* Properly override Equals and GetHashCode methods for IToken and add
tests by @​BobLd in UglyToad/PdfPig#1362
* Cache values in DeviceNColorSpaceDetails.GetColor() and improve
Transform() method to use array by @​BobLd in
UglyToad/PdfPig#1363
* Cache color spaces in ResourceStore when possible by @​BobLd in
UglyToad/PdfPig#1364
* Make PdfFunctionType4.Eval() allocation-free and boxing-free by
@​BobLd in UglyToad/PdfPig#1365
* Fix: brute-force xref scan loses stream position, missing recoverable
trailers by @​jeske in UglyToad/PdfPig#1360
* Add integration test for 28a1878 (#​1360) by @​BobLd in
UglyToad/PdfPig#1366
* Expose font dictionary indirect reference on FontDetails by @​jeske in
UglyToad/PdfPig#1367
* BUGFIX: Decode Indexed colour-table bytes to the base space's
component ranges by @​jeske in
UglyToad/PdfPig#1369
* Do not clip form XObject when bbox is invalid and fix #​1371 by
@​BobLd in UglyToad/PdfPig#1373
* Expose XmpMetadata.GetXmlMemory() and add warning to GetXDocument() by
@​BobLd in UglyToad/PdfPig#1374
* Move Indexed table-byte decoding onto the base colour space (in place)
by @​jeske in UglyToad/PdfPig#1375
* Fix wrong number of components for DeviceNColorSpaceDetails and
SeparationColorSpaceDetails, and ensure BaseType is correctly set by
@​BobLd in UglyToad/PdfPig#1376
* Unseal ParsingOptions by @​BobLd in
UglyToad/PdfPig#1377
* Change GetColor() signature to use ReadOnlySpan<double> and avoid
allocation by @​BobLd in UglyToad/PdfPig#1378
* Split color space details in their own files by @​BobLd in
UglyToad/PdfPig#1379
* Prefer double array over span in IColorSpaceContext so that operands
can be saved without allocation by @​BobLd in
UglyToad/PdfPig#1380
* Fix #​1234 and ensure documents are disposed in Merge() by @​BobLd in
UglyToad/PdfPig#1382
* Use StackDictionary<> for marked content, shadings and patterns in
ResourceStore by @​BobLd in UglyToad/PdfPig#1384
* PdfPageBuilder: fix ResetColor() and line width in Draw methods by
@​BobLd in UglyToad/PdfPig#1385
* Expose ILookupFilterProvider in the doc's Structure, address
IFilterProvider propagation and fix #​1243 by @​BobLd in
UglyToad/PdfPig#1386
* Properly concatenate matrix in GetGlobalTransform and fix #​1163 by
@​BobLd in UglyToad/PdfPig#1387
* Allow skipping token that are not an IndirectReferenceToken in
BasePageFactory.Create and fix #​1286 by @​BobLd in
UglyToad/PdfPig#1388
* Add PdfDocEncoding as a valid encoding in ReadEncodingDictionary when
lenient is on and fix #​1284 by @​BobLd in
UglyToad/PdfPig#1389
* Improve caching: found tokens in token scanner, resolved resources and
XObject forms by @​BobLd in UglyToad/PdfPig#1391
* Prevent NullReferenceException in NameToken.GetHashCode() race
condition by @​BobLd in UglyToad/PdfPig#1392
* Check unencrypted metadata in DecryptInternal() and fix #​1393 by
@​BobLd in UglyToad/PdfPig#1395
* Track stack depth in CMapParser and fix #​1394 by @​BobLd in
UglyToad/PdfPig#1396
* Include time in nightly version date format by @​BobLd in
UglyToad/PdfPig#1397
* Tidy up netstandard2.1 by @​BobLd in
UglyToad/PdfPig#1400

## New Contributors
* @​MultisoftPontus made their first contribution in
UglyToad/PdfPig#1358

**Full Changelog**:
UglyToad/PdfPig@v0.1.15...v0.1.16

Commits viewable in [compare
view](UglyToad/PdfPig@v0.1.15...v0.1.16).
</details>

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Khoa Ho <khoahoelca@gmail.com>
This was referenced Aug 24, 2026
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.

2 participants