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;
82/// The text of a table cell with its dot leaders struck and its currency
83/// sign joined to its figure (#714).
84mod cell_tidy;
85pub mod color;
86/// A line wrapped under its entry in a two-column index, joined to the
87/// entry (#714).
88mod continuation_rows;
89pub mod docling;
90pub mod document;
91/// A row or a column of a table with no text in it, dropped from the final
92/// table (#714).
93mod empty_lines;
94pub mod error;
95/// Running headers, footers and page numbers, from what a document repeats on
96/// three or more pages (#571). Internal: it runs on the way out of
97/// `extract_blocks` and the kind it decides is on the block. The public API
98/// over the model is #573.
99mod furniture;
100pub mod geometry;
101/// A column head wrapped over two or three lines, folded into one header row
102/// of the final table (#714).
103mod header_rows;
104/// Heading levels, from the document's own size and weight statistics (#568).
105/// Internal: it runs on the way out of `extract_blocks`, and the level it
106/// decides is on the block. The public API over the model is #573.
107mod headings;
108/// The block model as JSON: every block with its kind, page, box, text, spans
109/// and reading-order index, for a caller that chunks the document itself
110/// (#572).
111pub mod json;
112/// Layout detection on the shared ONNX Runtime layer (#641). Off by default.
113#[cfg(feature = "ort-layout")]
114pub mod layout;
115#[doc(hidden)]
116pub mod learned;
117/// Typographic ligatures fold to the letters they stand for. Internal: the
118/// option that turns it off rides on the extraction device.
119mod ligatures;
120pub mod limits;
121/// List items, from bullet glyphs, numbering and the column's indent steps
122/// (#569). Internal: it runs inside the paragraph merge, and the level and the
123/// number it decides are on the block. The public API over the model is #573.
124mod lists;
125/// The block model as markdown: headings, lists, tables and a marker where the
126/// page changed (#572).
127pub mod markdown;
128/// Figures in the block model: listing, naming, pixels, and references (#658).
129pub mod markdown_images;
130/// The model tiers' shared surface: specs, failures and install paths (#641).
131/// Always compiled; only the session half needs the `ort` feature.
132pub mod model;
133pub mod ocr;
134/// Scan detection: how much OCR a page needs, before any recognizer runs.
135pub mod ocr_need;
136/// OCR before extraction: a searchable copy of the document to read instead.
137/// Internal: it runs inside `extract_blocks` when `ExtractOptions::ocr` asks,
138/// and the one public method on it is for bindings with their own engine.
139mod ocr_searchable;
140/// The reading order of the whole document: the columns of a page, the pages
141/// in turn, and the paragraph a page break cut in two (#571). Internal, for
142/// the reason the other passes are.
143mod reading_order;
144pub mod render;
145/// A row whose every cell holds the same number of lines, split into that
146/// many rows (#714).
147mod split_merged_rows;
148/// Many documents as markdown in one process, for the structured benchmark's
149/// fair clock (#623).
150#[doc(hidden)]
151pub mod structured_batch;
152/// A stub wrapped onto the line of its figures, joined to it (#714).
153mod stub_wrap;
154/// Marker-led rows the table pass left at the edge of an accepted table
155/// (#636). Internal: it runs after the learned pass, over its blocks, and
156/// only adopts paragraphs the accepted tables' own columns confirm.
157mod table_row_reclaim;
158/// Which stage produced each table, for the env-gated diagnostic trace
159/// (#636). Internal: it measures finished cells and veto verdicts and prints
160/// numbers-only lines on stderr. Rule A2 (drawing_dense) lives here too: it
161/// withdraws a table whose box holds three or more drawn clusters.
162pub(crate) mod table_trace;
163/// Tables: the column grid a run of rows stands in, and the cells that fill it
164/// (#570). Internal: it runs inside the paragraph merge, and the row, column
165/// and spans it decides are on the block. The public API over the model is
166/// #573.
167mod tables;
168/// Kinds and reading order from a tagged PDF's own structure tree (#636).
169/// Internal: it runs last over the finished blocks when
170/// `ExtractOptions::use_tags` is set, and only where the tree passes the
171/// validity check; anything else returns the geometric answer untouched.
172mod tagged_evidence;
173pub mod text;
174pub mod text_info;
175pub mod thumbnail;
176/// A currency sign at the end of a cell, moved onto the figure in the cell
177/// to its right (#714).
178mod trailing_sign;
179/// Optional vision layer: raster plus ONNX runner (#636). Off by default.
180#[cfg(feature = "vision")]
181pub mod vision;
182pub mod words;
183pub mod words_join;
184#[cfg(feature = "xfa")]
185pub mod xfa;
186
187pub use batch::{
188    process_batch, process_batch_with_page_progress, BatchConfig, BatchResult, ErrorStrategy,
189    PdfBatch,
190};
191pub use blocks::{
192    Block, BlockKind, ClipMargins, ExtractOptions, OcrMode, PageSeparators, Quality,
193    TableRenderMode, TableStrategy, TatrOptions,
194};
195/// The P3 test seam behind [`TatrOptions`]. Hidden: tests name it, callers
196/// never do.
197#[doc(hidden)]
198pub use blocks::{TatrTestCell, TatrTestHook};
199pub use color::preserve_device_cmyk;
200pub use docling::{to_docling_json, DoclingPage};
201pub use document::{join_page_texts, BookmarkItem, DocumentInfo, PdfDocument};
202pub use error::{EngineError, OptionsError, Result};
203pub use geometry::{PageBox, PageGeometry, PageRotation};
204pub use json::to_json;
205pub use limits::{LimitError, ProcessingLimits};
206pub use markdown::{
207    to_markdown, to_markdown_with_code, to_markdown_with_options, to_markdown_with_switches,
208    MarkdownOptions,
209};
210pub use ocr::{OcrBackend, OcrError, OcrResult, OcrWord};
211pub use ocr_need::{page_needs_ocr, OcrNeed};
212pub use render::{
213    ColorMode, PixelFormat, RasterQuality, RenderConfig, RenderOptions, RenderedPage,
214};
215pub use text::{
216    BoundsSource, FontMetrics, GeometryMode, LayoutBlock, TextBlock, TextExtractionDevice,
217    TextSpan, WidthSource,
218};
219pub use text_info::TextSpanInfo;
220pub use thumbnail::ThumbnailOptions;
221pub use words::{to_json_with_words, to_words_json_lines, Word};
222
223#[cfg(not(target_arch = "wasm32"))]
224pub use ocr::best_available_backend;
225#[cfg(all(feature = "ocr", not(target_arch = "wasm32")))]
226pub use ocr::ocr_page_default;
227#[cfg(feature = "ocr-aws")]
228pub use ocr::AwsTextractBackend;
229#[cfg(feature = "ocr-azure")]
230pub use ocr::AzureDocIntelBackend;
231#[cfg(feature = "ocr-google")]
232pub use ocr::GoogleVisionBackend;
233#[cfg(feature = "ocr-mistral")]
234pub use ocr::MistralOcrBackend;
235#[cfg(feature = "ocr")]
236pub use ocr::OcrsBackend;
237#[cfg(feature = "ocr-onnx")]
238pub use ocr::PaddleOnnxBackend;