PDF Page Geometry Preflight¶
openmed.multimodal.pdf_geometry.read_pdf_geometry() reads a PDF's version, page count, and each page's MediaBox, CropBox, and rotation before any OCR or rasterization starts. Batch planners and memory estimators can then size work from numbers alone.
The reader needs no optional dependencies. It never decodes content streams, text, images, fonts, outlines, or document metadata, and its report contains only numbers and stable reason codes. No text, metadata strings, filenames, or paths are returned.
Example¶
from openmed.multimodal.pdf_geometry import PdfGeometryStatus, read_pdf_geometry
with open("synthetic.pdf", "rb") as handle:
report = read_pdf_geometry(handle, max_pages=500)
if report.status is PdfGeometryStatus.READABLE:
for page in report.pages:
print(page.page_index, page.width, page.height, page.rotation)
source may be bytes, bytearray, memoryview, or a readable binary stream. Seekable streams are restored to their starting position and are never closed.
What is read¶
- Version: the
%PDF-M.mheader in the first 1,024 bytes, raised by a later catalog/Versionwhen one is present. - Page tree: leaves of the catalog's
/Pagestree in document order, withMediaBox,CropBox, andRotateinherited from ancestor nodes. - Boxes: normalized to
(x0, y0, x1, y1)with the lower-left corner first. The effective crop box is clipped to the media box and defaults to it. - Rotation: normalized to 0, 90, 180, or 270 degrees clockwise.
widthandheightdescribe the crop box as displayed, so they swap for 90 and 270.
The reader scans indirect objects, uses the latest definition of an object for incremental updates, and expands uncompressed or FlateDecode object streams.
Statuses and reason codes¶
| Status | Meaning |
|---|---|
readable | Every page has valid geometry. |
review | Pages were read, but at least one finding needs attention. |
rejected | The document was not read and pages is empty. |
| Reason code | Status | Meaning |
|---|---|---|
pdf_size_limit | rejected | The input is larger than max_bytes. |
pdf_header_missing | rejected | No %PDF- header in the first 1,024 bytes. |
pdf_encrypted | rejected | A trailer declares /Encrypt. |
pdf_object_limit | rejected | More than max_objects indirect objects. |
pdf_decompression_limit | rejected | Object streams exceed max_decompressed_bytes. |
pdf_object_stream_unsupported | rejected | Needed objects sit in an object stream that uses another filter or is corrupt. |
pdf_catalog_missing | rejected | No resolvable document catalog. |
pdf_page_tree_invalid | rejected | Missing, cyclic, too deep, or malformed page tree. |
pdf_page_limit | rejected | More than max_pages pages. |
page_count_mismatch | review | The root /Count differs from the pages found. |
media_box_missing | review | A page has no media box; its geometry is None. |
media_box_invalid | review | A media box is not four finite numbers with a non-zero area. |
crop_box_invalid | review | A crop box is malformed or misses the media box; the media box is used. |
crop_box_outside_media_box | review | A crop box extends past the media box and was clipped. |
rotation_invalid | review | Rotate is not an integer multiple of 90; 0 is used. |
Reason codes appear in this fixed order on the report, and page-level codes also appear on the page that produced them.
Limits¶
| Argument | Default |
|---|---|
max_bytes | 64 MiB |
max_pages | 10,000 |
max_objects | 250,000 |
max_decompressed_bytes | 32 MiB |
Streams are read in chunks and stop one byte past max_bytes. Limits that are not positive integers, and sources that are neither bytes-like nor readable, raise PdfGeometryError with a stable category and no submitted value.
Serialization¶
report.to_dict() keeps a fixed field order, and report.to_json() returns compact JSON with sorted keys and the schema identifier openmed.multimodal.pdf_geometry.v1.
Out of scope¶
Rendering, OCR, password recovery, repairing damaged files, and full PDF conformance validation.