Skip to main content

Reader limits

The properties of PdfReaderLimits, the record PdfReaderOptions.Limits carries, in AdCodicem.Pdf.Documents. Why the reader has them is explained in Reader limits; how to raise one, or refuse a document instead, in Read or refuse a document that reaches a limit.

The limits​

Property of PdfReaderLimitsDefaultReached byWhat is keptCode
MaxDecodedStreamLength256 MBA decompression bomb, or a large-format scan: a 9,600 × 11,410 RGB map decodes to 313 MBThe first bytes, up to the boundlimit.decoded-stream
MaxObjectLength16 MBAn object that long, the data of a stream aside: an array of a million referencesThe object as far as the boundlimit.object
MaxXRefSectionLength64 MBA classic cross-reference table of more than about 3.3 million entriesThe entries within the boundlimit.xref-section-length
MaxXRefSectionCount1,024A document saved incrementally more than a thousand timesThe newest sectionslimit.xref-section-count
MaxTrailerLength64 KBA trailer that long, or a cross-reference stream's dictionary: its /Index grows with every scattered updateThe trailer as far as the boundlimit.trailer
MemberValue
PdfReaderLimits.DefaultEvery limit at the default above; what PdfReaderOptions.Limits holds unless you set it
PdfReaderLimits.UnboundedEvery length at Array.MaxLength, about 2 GB, and MaxXRefSectionCount at int.MaxValue

A value of zero or less is refused with an ArgumentOutOfRangeException. A length above Array.MaxLength is taken as Array.MaxLength: a decoded stream is held in one piece, and no runtime allocates a larger one. PdfReaderOptions.Limits refuses null with an ArgumentNullException.

When a limit is reached​

The reader keeps what fits and reports a warning under the limit's own code, whose message names the property to raise:

Warning limit.decoded-stream at 48213: The /FlateDecode data decodes to more than 256 MB; decoding stopped
there. Raise PdfReaderLimits.MaxDecodedStreamLength to read past it.
  • An object, a cross-reference section or a trailer is reported once, however often it is read again.
  • A stream is reported each time it is decoded past the bound — in the diagnostics you pass to Decode, or in the document's own when you pass none.
  • A stream read from a document decodes under that document's limits, however long after opening you decode it. A stream you build in memory decodes under PdfReaderLimits.Default.
  • A classic trailer that ends within the window its cross-reference table was read through is read whole, whatever its length. MaxTrailerLength bounds how far the reader follows one past that window.
  • Until the reader decodes streams a piece at a time, a stream that decodes past about 2 GB is cut there, and reported, even under Unbounded.
  • The reader keeps the object streams it decoded within a budget of 32 MB, and always keeps the one it decoded last, whatever its size. Raising MaxDecodedStreamLength raises how large that one can be.

ThrowOnLimit​

With PdfReaderOptions.ThrowOnLimit set — it is false by default —, reaching a limit throws a PdfLimitExceededException instead of warning:

Member of PdfLimitExceededExceptionWhat it is
CodeThe limit's code, such as limit.decoded-stream
LimitNameThe property of PdfReaderLimits that lifts it, such as MaxDecodedStreamLength
LimitThe bound in force, such as 268435456
PositionThe byte offset where the limit was reached, or -1; a chain cut where it goes on outside the file gives the section whose /Prev names where

The exception comes from whichever operation reaches the limit:

OperationWhen
PdfDocument.OpenWhile it indexes the file and reads its catalog
GetObject, or resolving a referenceWhen an object is too long, or the object stream that holds it decodes too far; or, when the read rebuilds the index, at any limit the rebuild reaches, once the rebuild is finished
DecodeWhen a stream decodes too far
PdfValidator.ValidateAs it reads, like any other caller

A document whose index had to be rebuilt finishes the rebuild before throwing, so it stays usable afterwards.

What cannot be lifted​

Some bounds protect the reader itself — its stack, its rebuild of a damaged index — and are not reached by the documents producers write. They are not options:

BoundValueWhy it is not an option
Nesting of arrays and dictionaries128 levelsReal documents nest a handful of levels; the bound keeps a hostile file from exhausting the stack
A reference that resolves to another referencefollowed 32 timesProducers write the object itself; the bound breaks a chain that loops
Objects loaded while loading another64An indirect /Length leads to an integer, which loads nothing further
Objects indexed by a rebuild2,000,000A rebuild only runs on a damaged file
Entries one subsection claims50,000,000A table's rows are bounded by MaxXRefSectionLength already; a count past that describes rows that are not there
Object numbersup to 2,147,483,647An object's number is an int, and ISO 32000-1's Annex C advises at most 8,388,607 indirect objects. A number past it is refused where it is read — a header, a reference, a subsection, a row, an object stream's header —, never narrowed to one that would name another object. An object is numbered from 1; 0 names the head of the free list, where a reference, a subsection or a row may give it
Generations0 to 65,535No object in use has a higher one (ISO 32000-1, 7.5.4). A free row of a table is read whatever its generation: producers give the free list's head 65,536
Realswithin ±1.8 × 10³⁰⁸A real is a double, and ISO 32000-1's Annex C advises reals within ±3.403 × 10³⁸. A number past it reads as null, reported as syntax.number-out-of-range