All posts
Product2026-10-178 min readby Jasper de Winter, Founder, PDFluent

PDFluent SDK 1.1.0 adds structured extraction

sdkreleaseextractionmarkdowntables

What 1.1.0 adds

PDFluent SDK 1.1.0 ships structured extraction on every channel: Markdown, blocks, words with boxes, and chunks for retrieval pipelines. Blocks cover paragraphs, headings with level, list items, table cells with grid position, and running heads, each with page and box. If you turn PDFs into Markdown or chunks for search and retrieval, these are the entry points you would use. The release also adds an optional balanced extraction quality that runs a layout model and a table-structure model locally, and image extraction on the plain document API. A compat layer follows part of the PyMuPDF API (coverage is partial and behaviour can differ in places; it is an independent implementation, not PyMuPDF), and the release ships a full command-line interface.

Structured extraction ships as Preview. Output quality is still improving and the API may change in a minor release. Plain-text extraction is not Preview.

Install and run it

bash
pip install pdfluent==1.1.0

You install the models with the pdfluent command-line tool. Download the archive for your platform from the 1.1.0 release page, put pdfluent on your PATH, and run:

bash
pdfluent models install layout tables
python
from pdfluent import Document

with Document("report.pdf") as doc:
    print(doc.extract_markdown(quality="balanced"))

If the models are not installed, the call fails with MODEL_NOT_AVAILABLE, naming the install command and the path it looked in.

Two quality tiers

fast is the default. It is the engine alone, pure Rust, no models, and it runs everywhere including WebAssembly.

balanced is Preview and opt-in. The layout model regions each page (headings, lists, tables, page furniture) and the table-structure model structures tables. Both models run locally through ONNX Runtime 1.23.2 [B49, 2026-10-10]; the SDK loads the ONNX Runtime library at run time and never links it. The tier is Markdown only: extract_markdown reads it, extract_blocks refuses it. It is native only, never WebAssembly, and runs on glibc Linux, macOS and Windows, not on musl Linux, where loading returns a typed ModelError. In Rust it needs the cargo features ort-layout and ort-tables, both off by default; we build the Python wheel, the Node addon and the CLI archives with them.

The models are a separate, explicit download so extraction does not reach the network on its own. The CLI from the release archives fetches them once with pdfluent models install layout tables; pdfluent models status shows what is installed and whether the pins verify. The installer checks every file against size and SHA-256 pins compiled into the binary, so installs are reproducible and nothing downloads automatically at run time. PDFLUENT_MODELS_BASE points the installer at a mirror or a local directory, and the installer enforces the pins the same way.

The package models-v1 holds three components: heron_ep03.onnx, the layout model, trained by PDFluent as a fine-tune of IBM's docling-layout-heron-101 (Apache-2.0) on the DocLayNet training split (CDLA-Permissive-1.0), licensed under the SDK terms with Apache-2.0 attribution to IBM's base model; tatr-v1.1-all.onnx, Microsoft Table Transformer v1.1-all as an unmodified ONNX export under MIT; and the ONNX Runtime 1.23.2 libraries under MIT [B49, 2026-10-10]. Third-party files ship as-is under their own licences; the full text is in MODELS-NOTICE.txt in the release [B48].

Table results

DatasetSampleMetricvs Doclingvs pymupdf4llmClaim
FinTabNet120 pages, 67 documentsTEDS+0.150 [+0.119; +0.183]+0.132 [+0.106; +0.158][A38, 2026-10-14]
PubTables-1M120 pages, 55 documentsTEDS+0.087 [+0.055; +0.128]+0.081 [+0.046; +0.127][A39, 2026-10-14]

In balanced mode with the table model, PDFluent scored higher than both Docling and pymupdf4llm on FinTabNet (financial reports) and PubTables-1M (scientific articles) [A38, A39, 2026-10-14].1 On ParseBench tables (100 pages from 32 documents, ParseBench's own scorer on each tool's Markdown), it is ahead of pymupdf4llm on both metrics: table record match +0.314 [+0.181; +0.452], GriTS +0.186 [+0.101; +0.275] [A40, 2026-10-14].1

Dataset sources: FinTabNet (IBM, CDLA-Permissive-1.0), PubTables-1M (CDLA-Permissive-2.0 annotations over CC0/CC BY articles), ParseBench (LlamaIndex, Apache-2.0 card over third-party documents).

1 Mode: quality=balanced with the table-structure model. We measured our side on 2026-10-14 on a 1.1.0 pre-release build with the same extraction code, and Docling 2.126.0 and pymupdf4llm 1.28.2 on 2026-10-08 on the identical pages. The pages come from each dataset's development (tuning) split, which we used while building the table work, so our side is an upper bound, not an independent held-out test. Each number is ours minus theirs, with a 95% interval from a paired per-document bootstrap. The table-structure model we ship is Microsoft's Table Transformer v1.1-all, which Microsoft trained on PubTables-1M and FinTabNet.c. We have not checked whether our sample pages fall inside its training data, so the FinTabNet and PubTables-1M results may favour us for that reason too. Table structure only, on these three samples. Reproducible with pdfluent-bench (coming).

What else is new

  • —Rust: extract_markdown, extract_blocks (Preview), extract_words, extract_text_pages(), image extraction on PdfDocument, keep_ligatures(bool), and new ExtractOptions fields including quality, progress, code, actual_text and OCR options.
  • —Python: Document.extract_markdown, extract_blocks, extract_words, image extraction, quality=, model_dir=, table_model_dir=, a progress callback, ocr_function=, pdfluent.chunks.iter_chunks, and a compat layer.
  • —Node: extractMarkdown, extractBlocks, extractWords, image extraction, iterChunks, quality, modelDir, tableModelDir, and one shared PdfluentError across CJS, ESM and compat exports.
  • —WebAssembly: encryptPdf/decryptPdf (AES-256, on bytes), PdfDoc.addWatermark(options), extractImages(bytes) returning { images, skipped }, and signPdf(bytes, p12, password, options?). No balanced tier.
  • —Java: extractMarkdown/extractBlocks and image extraction on PdfluentDocument. Java 1.1.0 is available from the PDFluent Maven repository (maven.pdfluent.com); Maven Central carries 1.0.0, and 1.1.x follows there later.
  • —.NET: structured extraction and image extraction, plus AcroForm read/write, annotations, images, merge and PDF/A validation on PdfDocument.
  • —C API: extract_markdown/extract_blocks, image extraction, form field write and read-back, document serialisation. pdf_version() now returns the crate version.
  • —CLI: extract, merge, split, pages, compress, protect/unprotect, watermark, convert, redact, sign, validate --pdfa, global --json, -q/-v, - for stdin/stdout, --raster quality|speed, --words and --format words.
  • —In pdfluent 1.1.0, OCR is reachable from Python only: it is opt-in, off by default, and runs through a system tesseract binary on PATH (or PDFLUENT_TESSERACT), or through your own function passed as ocr_function=. From plain Rust, the pdfluent 1.1.0 crate does not reach tesseract (its ocr-tesseract feature is empty). The lower-level pdf-engine crate has optional OCR backends behind feature flags (ocr and ocr-cloud), but the pdfluent 1.1.0 facade does not expose them. Node.js on Linux (glibc): the @pdfluent/node addons need at most GLIBC_2.17, so glibc 2.17 or newer runs them. This does not apply to the pdfluent CLI, whose linux-gnu binaries need glibc 2.18 or newer. [B50, 2026-10-16]

What to check when upgrading

  • —CLI exit codes changed: 0 ok, 1 usage, 2 input error, 3 processing error. Before they were 2 usage, 3 missing file, 4 malformed PDF, 5 policy opt-in, 6/7/70 stub/IO/unknown. The --json envelope names the exact failure.
  • —Block is #[non_exhaustive] and gained confidence and grid, so struct literals stop compiling. Use Block::new(kind, page, bbox, text, index) and set fields on the result.
  • —The extractor joins words by default: Safety rules instead of S a f e t y, and 18.90 instead of split decimals. Spans merge to one per word or run, so block and span counts in JSON output drop.
  • —PdfDocument::outlines() returns page None for an outline entry with no destination; 1.0.0 returned Some(0) through a page-1 fallback. Code that treated Some(0) as "first page" for such entries needs to handle None.
  • —Plain-text extraction can be slightly slower on some documents: the extractor now places combining marks such as c⃝ and ≠ after the glyph they mark, which adds a small per-glyph cost. Follow-up work on this is planned after 1.1.0.
  • —pdf-syntax and pdf-interpret move to 0.6.0; crates pinned to 0.5.x must move.

The Docling comparison has the full method and sources. For extraction, see the structured extraction guide and the output formats guide; for Office files, the Office to Markdown guide. The Python API is covered by the PyMuPDF-style Python API guide and the Python docs, with full details in the changelog.

Report problems or ask questions in GitHub Discussions. The full release notes are on the v1.1.0 release page.

PyMuPDF and MuPDF are trademarks of Artifex Software, Inc. PDFluent is an independent product of Innovation Trigger B.V. and is not affiliated with or endorsed by Artifex.

All posts