Extract text and tables (Preview) from a PDF for RAG and LLM pipelines

Pull position-aware text out of a PDF in Rust, Python, or Node.js, turn it into retrieval-ready chunks (Preview), and use the balanced tier (Preview) for table-aware extraction. Structured extraction is Preview.

Install PDFluent

Install the SDK for Rust, Python, or Node.js. The install lines carry no version. The balanced tier (Preview) needs models installed once with the pdfluent CLI from the 1.1.0 release archives.

bash
# Rust
cargo add pdfluent@1.1.0

# Python
pip install pdfluent==1.1.0

# Node.js
npm install @pdfluent/node

# Models for the balanced tier (CLI from the 1.1.0 release archives)
pdfluent models install layout tables

Preview: extract Markdown and text blocks from a PDF in Rust, Python, and Node.js

A retrieval-augmented generation (RAG) pipeline needs more than a blob of text: knowing which page a passage came from, and roughly how it was laid out, makes later chunking and citation much easier. PDFluent's structured extraction returns Markdown, blocks, words with boxes, and chunks. Structured extraction is Preview: output quality is still improving and the API may change in a minor release.

rust
use pdfluent::{ExtractOptions, PdfDocument, Quality};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let doc = PdfDocument::open("report.pdf")?;
    let markdown = doc.extract_markdown(&ExtractOptions::default())?;
    let blocks = doc.extract_blocks(&ExtractOptions::default())?;
    println!("{} blocks", blocks.len());
    // balanced: needs the ort-layout/ort-tables features and installed models
    let tables = doc.extract_markdown(&ExtractOptions::default().with_quality(Quality::Balanced))?;
    println!("{markdown}\n{tables}");
    Ok(())
}
  1. Rust: open the document and extract Markdown and blocks (Preview)

    PdfDocument::open takes a path. extract_markdown returns Markdown (Preview); extract_blocks returns a Vec<Block> (Preview). The default options use the fast tier, which runs the engine alone with no models.

    rust
    use pdfluent::{ExtractOptions, PdfDocument, Quality};
    
    fn main() -> Result<(), Box<dyn std::error::Error>> {
        let doc = PdfDocument::open("report.pdf")?;
        let markdown = doc.extract_markdown(&ExtractOptions::default())?;
        let blocks = doc.extract_blocks(&ExtractOptions::default())?;
        println!("{} blocks", blocks.len());
        Ok(())
    }
  2. Python: extract Markdown, words, and chunks (Preview)

    Document is a context manager. extract_markdown returns Markdown (Preview); extract_words returns words with boxes (Preview); iter_chunks yields chunks with a .text attribute (Preview). Structured extraction is Preview.

    python
    from pdfluent import Document
    from pdfluent.chunks import iter_chunks
    
    with Document("report.pdf") as doc:
        md = doc.extract_markdown()                        # fast tier, no models
        words = doc.extract_words()
        for chunk in iter_chunks(doc, mode="markdown"):
            print(chunk.text)
  3. Node.js: extract Markdown and chunks (Preview)

    PdfDocument.open takes a buffer. extractMarkdown returns Markdown (Preview); iterChunks yields chunks with a .text attribute (Preview). Structured extraction is Preview.

    javascript
    const fs = require("node:fs");
    const { PdfDocument, iterChunks } = require("@pdfluent/node");
    
    const doc = PdfDocument.open(fs.readFileSync("report.pdf"));
    const md = doc.extractMarkdown();
    for (const chunk of iterChunks(doc, { mode: "markdown" })) {
      console.log(chunk.text);
    }
  • Structured extraction is Preview: output quality is still improving and the API may change in a minor release. Plain-text extraction is not Preview.
  • The fast tier is the default. It runs the engine alone, pure Rust, no models, and runs everywhere including WebAssembly.
  • The balanced tier (Preview) runs a layout model and a table-structure model locally through ONNX Runtime 1.23.2. It is native only, never WebAssembly, and supports glibc Linux, macOS, and Windows. On musl Linux loading returns a typed ModelError. Balanced (Preview) is Markdown only: extract_markdown reads it, extract_blocks refuses it.
  • Models install once, explicitly: pdfluent models install layout tables. The pdfluent command comes from the CLI archives on the 1.1.0 release page. pip install pdfluent==1.1.0 and npm install @pdfluent/node do not install it. pdfluent models status shows what is installed and whether the pins verify. Nothing downloads automatically at run time. Without installed models (or, in Rust, the cargo features ort-layout / ort-tables), the balanced tier (Preview) refuses with MODEL_NOT_AVAILABLE.
  • Pages: first_page (from zero) and page_count choose a window of the answer; the document is still read whole.
  • 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.

Preview: build retrieval chunks from extracted text

Embedding models and vector stores work best on bounded chunks, not whole-page or whole-document strings. PDFluent provides iter_chunks for Python and iterChunks for Node.js, which yield chunks with a .text attribute. Structured extraction is Preview: output quality is still improving and the API may change in a minor release.

python
from pdfluent import Document
from pdfluent.chunks import iter_chunks

with Document("report.pdf") as doc:
    md = doc.extract_markdown()                        # fast tier, no models
    for chunk in iter_chunks(doc, mode="markdown"):
        print(chunk.text)
  1. Python: iterate chunks (Preview)

    iter_chunks takes a Document and a mode ("blocks" or "markdown") and yields chunks with a .text attribute. Structured extraction is Preview.

    python
    from pdfluent import Document
    from pdfluent.chunks import iter_chunks
    
    with Document("report.pdf") as doc:
        for chunk in iter_chunks(doc, mode="markdown"):
            print(chunk.text)
  2. Node.js: iterate chunks (Preview)

    iterChunks is exported from @pdfluent/node. It takes a document and an options object with mode: "markdown" and yields chunks with a .text attribute. Structured extraction is Preview.

    javascript
    const fs = require("node:fs");
    const { PdfDocument, iterChunks } = require("@pdfluent/node");
    
    const doc = PdfDocument.open(fs.readFileSync("report.pdf"));
    for (const chunk of iterChunks(doc, { mode: "markdown" })) {
      console.log(chunk.text);
    }
  • PDFluent does not include an embedding model or a vector store client. The chunks (Preview) produced by iter_chunks and iterChunks carry text; feed them to whatever embedding API and vector database your pipeline already uses.
  • Structured extraction is Preview: output quality is still improving and the API may change in a minor release.
  • For documents with running headers/footers repeated on every page, strip them before building chunks (Preview) — otherwise the same boilerplate line gets embedded once per page and can crowd out real content in retrieval.

Preview: table and layout-aware extraction for RAG Preview

The fast tier returns Markdown, blocks, words with boxes, and chunks. The balanced tier (Preview) adds a layout model and a table-structure model for better table and structure detection. Structured extraction, including the balanced tier, is Preview: output quality is still improving and the API may change in a minor release.

rust
use pdfluent::{ExtractOptions, PdfDocument, Quality};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let doc = PdfDocument::open("report.pdf")?;
    let markdown = doc.extract_markdown(&ExtractOptions::default())?;
    let blocks = doc.extract_blocks(&ExtractOptions::default())?;
    println!("{} blocks", blocks.len());
    // balanced: needs the ort-layout/ort-tables features and installed models
    let tables = doc.extract_markdown(&ExtractOptions::default().with_quality(Quality::Balanced))?;
    println!("{markdown}\n{tables}");
    Ok(())
}
  1. Python: use the balanced tier (Preview)

    Pass quality="balanced" to extract_markdown. It needs installed models. Balanced (Preview) is Markdown only: extract_markdown reads it, extract_blocks refuses it. Structured extraction is Preview.

    python
    from pdfluent import Document
    
    with Document("report.pdf") as doc:
        md_tables = doc.extract_markdown(quality="balanced")  # needs installed models
  2. Node.js: use the balanced tier (Preview)

    Pass { quality: "balanced" } to extractMarkdown. It needs installed models. Balanced (Preview) is Markdown only: extractMarkdown reads it, extractBlocks refuses it. Structured extraction is Preview.

    javascript
    const fs = require("node:fs");
    const { PdfDocument, iterChunks } = require("@pdfluent/node");
    
    const doc = PdfDocument.open(fs.readFileSync("report.pdf"));
    const mdTables = doc.extractMarkdown({ quality: "balanced" });
  • Structured extraction, including the balanced tier, is Preview: output quality is still improving and the API may change in a minor release.
  • The balanced tier (Preview) runs a layout model and a table-structure model locally through ONNX Runtime 1.23.2. It is native only, never WebAssembly, and supports glibc Linux, macOS, and Windows. On musl Linux loading returns a typed ModelError.
  • Models install once, explicitly: pdfluent models install layout tables. The pdfluent command comes from the CLI archives on the 1.1.0 release page. pdfluent models status shows what is installed and whether the pins verify. Nothing downloads automatically at run time. Without installed models (or, in Rust, the cargo features ort-layout / ort-tables), the balanced tier (Preview) refuses with MODEL_NOT_AVAILABLE.
  • Model dir options: model_dir= / table_model_dir= (Python), modelDir / tableModelDir (Node), --model-dir (CLI), env PDFLUENT_MODEL_DIR.
  • Table structure (A38, A39, A40; measured 2026-10-14 on the tuning split): balanced (Preview) with the table model scores higher than Docling and pymupdf4llm on FinTabNet and PubTables-1M, and higher than pymupdf4llm on ParseBench.
  • Office input to Markdown (Preview): in Python, pdfluent.extract_office(path_or_bytes, format="markdown") takes .docx, .pptx, .xlsx (routing by the file's content types, not the extension); format="docling" returns DoclingDocument JSON (Preview). HTML input is not part of this call.