# How pdf-inspector Detects Tables in PDFs: A Complete Technical Guide

> Discover how pdf-inspector detects tables in PDFs using rectangle line and heuristic pipelines Learn about its validation process for accurate table extraction and analysis

- Repository: [Firecrawl/pdf-inspector](https://github.com/firecrawl/pdf-inspector)
- Tags: deep-dive
- Published: 2026-08-31

---

**pdf‑inspector uses three complementary detection pipelines—rectangle‑based, line‑based, and heuristic—tried in priority order until a valid table is found, with strict validation heuristics to filter false positives.**

The `firecrawl/pdf-inspector` crate extracts tabular data from PDF documents by analyzing layout primitives including text items, rectangles, and vector drawing operators. Its detection strategy prioritizes explicit structural evidence over guesswork, falling back to increasingly speculative methods only when necessary.

## The Three-Stage Detection Pipeline

The main entry point `detect_tables()` in [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs) orchestrates the detection process. It attempts **rectangle‑based detection** first, then **line‑based detection**, and finally **heuristic detection**—returning the first valid table found.

```rust
// src/lib.rs (excerpt)
pub fn detect_tables(items: &[TextItem], page_width: f32, ocr: bool) -> Vec<Table> {
    // 1️⃣ Try rect‑based detection
    if let Some(rects) = extract_rects(items) {
        if let Some(t) = detect_tables_from_rects(&items, &rects) {
            return vec![t];
        }
    }

    // 2️⃣ Try line‑based detection
    if let Some(lines) = extract_path_lines(items) {
        if let Some(t) = detect_tables_from_lines(&items, &lines) {
            return vec![t];
        }
    }

    // 3️⃣ Heuristic detection (merge chars, split financial, column‑based)
    let heuristics = detect_tables(&items, page_width, ocr);
    heuristics
}

```

## Rectangle‑Based Detection

**Rectangle‑based detection** clusters axis‑aligned bounding boxes from the PDF's structure tree or OCR‑generated rectangles. This approach is implemented in [`src/tables/detect_rects.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/detect_rects.rs).

The algorithm uses a **union‑find algorithm** to group overlapping rectangles with a configurable tolerance:

1. Extract `PdfRect` objects from the page's drawing operators
2. Cluster overlapping rects into connected components
3. Filter components that meet minimum table thresholds

```rust
// Conceptual flow from detect_rects.rs
let components = union_find_cluster(rects, tolerance=2.0);
let table_candidates: Vec<Table> = components
    .into_iter()
    .filter(|c| c.rects.len() >= MIN_RECTS_FOR_TABLE)
    .filter_map(|c| build_table_from_rect_grid(&items, &c))
    .collect();

```

This method excels when PDFs contain explicit table structures from the document's internal representation.

## Line‑Based Detection

When no structural rectangles exist, **line‑based detection** searches for explicit PDF path commands that draw horizontal and vertical rules. This is implemented in [`src/tables/detect_lines.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/detect_lines.rs).

The detector parses PDF drawing operators:

- `m` — move to
- `l` — line to
- `S` — stroke path

Horizontal rules are merged via `merge_horizontal_segments`, split into runs, and assembled into a grid by snapping rule edges. Text items falling inside grid cells are assigned to corresponding rows and columns via `assign_items_to_grid`.

## Heuristic Detection: The Fallback Arsenal

**Heuristic detection** in [`src/tables/detect_heuristic.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/detect_heuristic.rs) handles cases without explicit graphics. It contains three sub‑strategies tried sequentially:

### Text Merging and Column Detection

First, the detector merges adjacent glyph‑level items and optionally expands financial‑style consolidated items. It then uses the layout engine's **column detection** to infer column boundaries.

### Key‑Value Table Detection

For two‑column "label‑value" tables (common in specification sheets), the detector:

1. Groups text rows by Y‑coordinate
2. Infers a vertical split (`split_x`) from the biggest gap between word clusters
3. Applies label‑vs‑value guards including header inference and "EDGAR tag" handling

The functions `try_build_key_value_table_from_rows()` and `infer_key_value_split_x()` implement this logic.

### Column‑Based Detection

When the layout engine has already discovered column regions (e.g., from newspaper‑style column detection), `try_build_table_from_columns()` builds a table directly from those column X‑ranges.

## Validation Heuristics Prevent False Positives

Every candidate table undergoes strict validation in [`detect_heuristic.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/detect_heuristic.rs) (lines 334–363, 386–441, 463–485, 509–525):

| Check | Threshold | Purpose |
|-------|-----------|---------|
| **Fill‑rate** | ≥15% cells non‑empty | Reject sparse layouts |
| **Row span** | ≥3 rows, ≥4 columns | Ensure table‑like dimensions |
| **Prose detection** | Cells <40 chars, minimal sentence punctuation | Exclude paragraph text |
| **Column asymmetry** | <60% cells in single column | Distinguish from newspaper layouts |

Failure on any check causes immediate rejection, ensuring the detector does not return false positives on multi‑column prose.

## Table Construction and Classification

All pipelines return a `Table` struct defined in [`src/tables/mod.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/mod.rs). The struct captures:

- Column X positions
- Row Y positions
- Cell matrix
- Classification via `TableKind`

The `Table::new()` method calls `is_table_of_contents(&cells)` to distinguish data tables from tables of contents by scanning the first column for page‑number patterns.

```rust
// From src/tables/mod.rs
pub struct Table {
    pub columns: Vec<f32>,
    pub rows: Vec<f32>,
    pub cells: Vec<Vec<Vec<TextItem>>>,
    pub kind: TableKind,
}

pub enum TableKind {
    Data,  // Standard data table
    Toc,   // Table of contents
}

```

## Practical Usage Examples

### Extract Tables from a PDF (Rust)

```rust
use pdf_inspector::lib::{process_pdf_with_options, ExtractionOptions};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let opts = ExtractionOptions {
        extract_tables: true,
        ..Default::default()
    };
    let result = process_pdf_with_options("sample.pdf", &opts)?;
    
    for table in result.tables {
        println!("--- Table ({} columns × {} rows) ---", 
            table.columns.len(), table.rows.len());
        println!("{}", pdf_inspector::tables::format::table_to_markdown(&table));
    }
    Ok(())
}

```

### Direct Rectangle Detection (Python Bindings)

```python
import pdf_inspector

items = pdf_inspector.extract_text_items("sample.pdf")
tables = pdf_inspector.tables.detect_tables_from_rects(
    items,
    pdf_inspector.tables.detect_rects.cluster_rects(...)
)

for t in tables:
    print(pdf_inspector.tables.format.table_to_markdown(t))

```

### Customize Detection Thresholds

```rust
use pdf_inspector::tables::detect_heuristic::detect_tables_with_page_width;

let custom_page_width = 595.0; // A4 width in points
let tables = detect_tables_with_page_width(&items, custom_page_width, false);
// The `ocr` flag adapts heuristics for OCR‑generated PDFs

```

## Key Source Files

| File | Responsibility |
|------|---------------|
| [`src/tables/mod.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/mod.rs) | Public API, `Table` struct, formatting helpers |
| [`src/tables/detect_rects.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/detect_rects.rs) | Rectangle clustering with union‑find |
| [`src/tables/detect_lines.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/detect_lines.rs) | PDF path parsing, line merging, grid construction |
| [`src/tables/detect_heuristic.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/detect_heuristic.rs) | Heuristic detection, validation, key‑value handling |
| [`src/tables/grid.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/grid.rs) | Boundary finding, cell assignment, header recovery |
| [`src/tables/format.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/format.rs) | Markdown and JSON output conversion |
| [`src/tables/detect_struct.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/detect_struct.rs) | Tagged PDF structure‑tree detection |

This architecture—explicit‑first with rigorous validation—makes pdf‑inspector robust across diverse PDF generation methods while maintaining low false‑positive rates.

## Summary

- **pdf‑inspector detects tables** through three prioritized pipelines: rectangle‑based, line‑based, and heuristic
- **Rectangle detection** uses union‑find clustering of `PdfRect` objects from the PDF structure tree
- **Line detection** parses PDF path commands (`m`, `l`, `S`) to build explicit grids
- **Heuristic detection** includes column‑based, key‑value, and text‑clustering strategies
- **Strict validation** enforces fill‑rate, dimensional, and prose constraints to eliminate false positives
- **Table classification** distinguishes data tables from tables of contents via content pattern analysis

## Frequently Asked Questions

### What makes pdf‑inspector's table detection different from other PDF parsers?

pdf‑inspector prioritizes explicit structural evidence (rectangles and drawing lines) before resorting to heuristics, and applies rigorous validation checks including fill‑rate thresholds and prose detection. This explicit‑first approach with multi‑layer validation reduces false positives compared to purely heuristic detectors.

### How does pdf‑inspector handle tables without visible borders?

For borderless tables, pdf‑inspector falls back to **heuristic detection** in [`detect_heuristic.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/detect_heuristic.rs). It merges text items, infers column boundaries from X‑coordinate clustering, builds rows from Y‑coordinate clustering, and validates the candidate against strict heuristics before accepting it as a table.

### Can pdf‑inspector detect financial tables with merged cells?

Yes. The heuristic detector includes optional **financial‑style consolidated item expansion** that handles merged cells typical in financial reports. The `expand_financial_consolidated_items` preprocessing step splits combined cell content before grid assignment.

### What is the minimum table size pdf‑inspector will recognize?

The validation heuristics require **at least 3 rows and 4 columns** (`columns.len() < 4` triggers rejection). Additionally, at least 15% of cells must contain content. These thresholds filter out incidental text alignments that do not constitute genuine data tables.