Skip to main content

pdf_engine/
lib.rs

1// Copyright (c) 2026 Innovation Trigger B.V.
2//
3// PDFluent is available under two licences, at your option: the GNU AGPLv3, or
4// the PDFluent Commercial Licence. See the LICENSE file in this repository --
5// that file travels with the copy you received, which a URL does not.
6
7#![warn(missing_docs)]
8//! Unified PDF rendering engine.
9//!
10//! `pdf-engine` is the main public-facing API for reading and rendering PDF
11//! documents. It wraps the lower-level `pdf-syntax` / `pdf-interpret` /
12//! `pdf-render` stack and exposes a single [`PdfDocument`] handle for all
13//! common operations: page rendering, text extraction, thumbnails, metadata,
14//! bookmarks, and full-text search.
15//!
16//! # Quick Start
17//!
18//! ```no_run
19//! use std::sync::Arc;
20//! use pdf_engine::{PdfDocument, RenderOptions};
21//!
22//! // Load from bytes (accepts Arc<Vec<u8>>, Vec<u8>, or any Into<PdfData>).
23//! let data = Arc::new(std::fs::read("invoice.pdf").unwrap());
24//! let doc = PdfDocument::open(data).unwrap();
25//!
26//! println!("{} pages — {:?}", doc.page_count(), doc.info().title);
27//!
28//! // Render page 0 at 150 DPI → raw RGBA pixel data.
29//! let opts = RenderOptions { dpi: 150.0, ..Default::default() };
30//! let rendered = doc.render_page(0, &opts).unwrap();
31//! println!("{}×{} px", rendered.width, rendered.height);
32//!
33//! // Plain-text extraction.
34//! let text = doc.extract_text(0).unwrap();
35//! println!("{text}");
36//!
37//! // Structured text with per-span positions.
38//! for block in doc.extract_text_blocks(0).unwrap() {
39//!     for span in &block.spans {
40//!         println!("  [{:.0}, {:.0}] {}", span.x, span.y, span.text);
41//!     }
42//! }
43//!
44//! // Full-text search — returns 0-based page indices.
45//! let hits = doc.search_text("total");
46//! println!("'total' found on {} page(s)", hits.len());
47//! ```
48//!
49//! # Key Types
50//!
51//! | Type | Description |
52//! |---|---|
53//! | [`BatchConfig`] / [`BatchResult`] | Worker-pool processing for many PDFs |
54//! | [`PdfDocument`] | Main document handle |
55//! | [`RenderConfig`] / [`RenderOptions`] | DPI, color mode, background colour, optional forced width/height |
56//! | [`RenderedPage`] | RGBA or CMYK pixel data (row-major, 4 bytes per pixel) |
57//! | [`PageGeometry`] | MediaBox, CropBox, TrimBox, BleedBox, rotation |
58//! | [`PageBox`] | A rectangle in PDF user-space points |
59//! | [`DocumentInfo`] | Title, author, subject, creator, producer |
60//! | [`TextBlock`] / [`TextSpan`] | Structured text with position and font size |
61//! | [`Block`] / [`BlockKind`] / [`ExtractOptions`] | Paragraphs, headings and list items, and which pages of them come back |
62//! | [`Word`] | One word with its box, page, block and line |
63//! | [`to_markdown`] / [`to_json`] | Those blocks as markdown for a reader, or as JSON for a chunker |
64//! | [`BookmarkItem`] | Outline node — title, target page, nested children |
65//! | [`ThumbnailOptions`] | Max-dimension constraint for thumbnail rendering |
66
67pub mod api;
68/// Public engine error type and stable error-code mapping. See
69/// [`api_error::PdfEngineError`].
70pub mod api_error;
71/// The balanced tier's page assembly: detector boxes plus our own text (#641).
72/// Off by default, beside [`layout`].
73#[cfg(feature = "ort-layout")]
74pub mod balanced;
75pub mod batch;
76/// Visual-to-logical reordering of a line of extracted text (UAX #9 L2).
77/// Internal: extraction reorders, nothing outside it needs to.
78mod bidi;
79/// Paragraphs and the [`blocks::Block`] model the structured serializers
80/// consume (#567).
81pub mod blocks;
82pub mod color;
83pub mod docling;
84pub mod document;
85pub mod error;
86/// Running headers, footers and page numbers, from what a document repeats on
87/// three or more pages (#571). Internal: it runs on the way out of
88/// `extract_blocks` and the kind it decides is on the block. The public API
89/// over the model is #573.
90mod furniture;
91pub mod geometry;
92/// Heading levels, from the document's own size and weight statistics (#568).
93/// Internal: it runs on the way out of `extract_blocks`, and the level it
94/// decides is on the block. The public API over the model is #573.
95mod headings;
96/// The block model as JSON: every block with its kind, page, box, text, spans
97/// and reading-order index, for a caller that chunks the document itself
98/// (#572).
99pub mod json;
100/// Layout detection on the shared ONNX Runtime layer (#641). Off by default.
101#[cfg(feature = "ort-layout")]
102pub mod layout;
103#[doc(hidden)]
104pub mod learned;
105/// Typographic ligatures fold to the letters they stand for. Internal: the
106/// option that turns it off rides on the extraction device.
107mod ligatures;
108pub mod limits;
109/// List items, from bullet glyphs, numbering and the column's indent steps
110/// (#569). Internal: it runs inside the paragraph merge, and the level and the
111/// number it decides are on the block. The public API over the model is #573.
112mod lists;
113/// The block model as markdown: headings, lists, tables and a marker where the
114/// page changed (#572).
115pub mod markdown;
116/// Figures in the block model: listing, naming, pixels, and references (#658).
117pub mod markdown_images;
118/// The model tiers' shared surface: specs, failures and install paths (#641).
119/// Always compiled; only the session half needs the `ort` feature.
120pub mod model;
121pub mod ocr;
122/// Scan detection: how much OCR a page needs, before any recognizer runs.
123pub mod ocr_need;
124/// OCR before extraction: a searchable copy of the document to read instead.
125/// Internal: it runs inside `extract_blocks` when `ExtractOptions::ocr` asks,
126/// and the one public method on it is for bindings with their own engine.
127mod ocr_searchable;
128/// The reading order of the whole document: the columns of a page, the pages
129/// in turn, and the paragraph a page break cut in two (#571). Internal, for
130/// the reason the other passes are.
131mod reading_order;
132pub mod render;
133/// Many documents as markdown in one process, for the structured benchmark's
134/// fair clock (#623).
135#[doc(hidden)]
136pub mod structured_batch;
137/// Marker-led rows the table pass left at the edge of an accepted table
138/// (#636). Internal: it runs after the learned pass, over its blocks, and
139/// only adopts paragraphs the accepted tables' own columns confirm.
140mod table_row_reclaim;
141/// Which stage produced each table, for the env-gated diagnostic trace
142/// (#636). Internal: it measures finished cells and veto verdicts and prints
143/// numbers-only lines on stderr. Rule A2 (drawing_dense) lives here too: it
144/// withdraws a table whose box holds three or more drawn clusters.
145pub(crate) mod table_trace;
146/// Tables: the column grid a run of rows stands in, and the cells that fill it
147/// (#570). Internal: it runs inside the paragraph merge, and the row, column
148/// and spans it decides are on the block. The public API over the model is
149/// #573.
150mod tables;
151/// Kinds and reading order from a tagged PDF's own structure tree (#636).
152/// Internal: it runs last over the finished blocks when
153/// `ExtractOptions::use_tags` is set, and only where the tree passes the
154/// validity check; anything else returns the geometric answer untouched.
155mod tagged_evidence;
156pub mod text;
157pub mod text_info;
158pub mod thumbnail;
159/// Optional vision layer: raster plus ONNX runner (#636). Off by default.
160#[cfg(feature = "vision")]
161pub mod vision;
162pub mod words;
163pub mod words_join;
164#[cfg(feature = "xfa")]
165pub mod xfa;
166
167pub use batch::{
168    process_batch, process_batch_with_page_progress, BatchConfig, BatchResult, ErrorStrategy,
169    PdfBatch,
170};
171pub use blocks::{
172    Block, BlockKind, ClipMargins, ExtractOptions, OcrMode, PageSeparators, Quality,
173    TableRenderMode, TableStrategy, TatrOptions,
174};
175/// The P3 test seam behind [`TatrOptions`]. Hidden: tests name it, callers
176/// never do.
177#[doc(hidden)]
178pub use blocks::{TatrTestCell, TatrTestHook};
179pub use color::preserve_device_cmyk;
180pub use docling::{to_docling_json, DoclingPage};
181pub use document::{join_page_texts, BookmarkItem, DocumentInfo, PdfDocument};
182pub use error::{EngineError, OptionsError, Result};
183pub use geometry::{PageBox, PageGeometry, PageRotation};
184pub use json::to_json;
185pub use limits::{LimitError, ProcessingLimits};
186pub use markdown::{
187    to_markdown, to_markdown_with_code, to_markdown_with_options, to_markdown_with_switches,
188    MarkdownOptions,
189};
190pub use ocr::{OcrBackend, OcrError, OcrResult, OcrWord};
191pub use ocr_need::{page_needs_ocr, OcrNeed};
192pub use render::{
193    ColorMode, PixelFormat, RasterQuality, RenderConfig, RenderOptions, RenderedPage,
194};
195pub use text::{
196    BoundsSource, FontMetrics, GeometryMode, TextBlock, TextExtractionDevice, TextSpan, WidthSource,
197};
198pub use text_info::TextSpanInfo;
199pub use thumbnail::ThumbnailOptions;
200pub use words::{to_json_with_words, to_words_json_lines, Word};
201
202#[cfg(not(target_arch = "wasm32"))]
203pub use ocr::best_available_backend;
204#[cfg(all(feature = "ocr", not(target_arch = "wasm32")))]
205pub use ocr::ocr_page_default;
206#[cfg(feature = "ocr-aws")]
207pub use ocr::AwsTextractBackend;
208#[cfg(feature = "ocr-azure")]
209pub use ocr::AzureDocIntelBackend;
210#[cfg(feature = "ocr-google")]
211pub use ocr::GoogleVisionBackend;
212#[cfg(feature = "ocr-mistral")]
213pub use ocr::MistralOcrBackend;
214#[cfg(feature = "ocr")]
215pub use ocr::OcrsBackend;
216#[cfg(feature = "ocr-onnx")]
217pub use ocr::PaddleOnnxBackend;