Understanding the Three‑Stage Table Detection Process in pdf‑inspector
The pdf‑inspector library extracts tables from PDFs using a cascading three‑stage detection process that prioritizes geometric certainty over textual inference: rect‑based detection runs first, line‑based detection runs second when rects fail, and heuristic detection runs last as a fallback for text‑only tables.
This Rust‑based open‑source tool handles the diverse ways PDFs represent tabular data—from explicitly drawn borders to subtle whitespace patterns. The three‑stage architecture ensures robust extraction across document types while maintaining precision by preferring high‑confidence geometric matches.
Stage 1: Rect‑Based Detection
The rect‑based detection stage targets PDFs that use explicit rectangle drawing operators (re) to outline cell borders. This is the most reliable detection method because rectangles unambiguously define table boundaries.
In src/tables/detect_rects.rs, the detect_tables_from_rects function implements this stage through three phases:
- Size filtering – Removes rectangles smaller than 2×2 points to eliminate noise.
- Union‑find clustering – Groups overlapping rectangles into connected components using a spatial grid.
- Grid validation – Builds a regular grid from clustered rectangles and verifies it forms a complete table structure.
The stage runs on every page containing at least 6 rectangles after filtering. When a valid grid emerges, the table is immediately emitted and subsequent stages are skipped for that page.
// From detect_rects.rs – high-level entry point
pub fn detect_tables_from_rects(
items: &[TextItem],
rects: &[PdfRect],
page_num: i32,
) -> (Vec<Table>, Vec<TableRegionHint>)
Stage 2: Line‑Based Detection
When rect‑based detection produces no tables, line‑based detection activates as the second priority. This stage handles documents that use path operators (m for move, l for line, S for stroke) to draw ruled lines—common in government forms, IRS documents, and financial reports.
The implementation in src/tables/detect_lines.rs focuses on horizontal rules as primary anchors:
- Line merging – Combines collinear segments into continuous horizontal rules.
- Rule grouping – Clusters rules that share similar y‑coordinates or spacing patterns.
- Table construction – Builds tables using either:
- Text‑anchor columns – Infers vertical boundaries from text alignment when no vertical lines exist.
- Open‑edge grids – Creates partially bounded tables when only horizontal rules are present.
This stage succeeds even without vertical strokes, leveraging text positions to reconstruct column structure.
// From detect_lines.rs – primary entry point
pub fn detect_tables_from_lines(
items: &[TextItem],
lines: &[PdfLine],
page_num: i32,
) -> Vec<Table>
Stage 3: Heuristic Detection
The heuristic detection stage in src/tables/detect_heuristic.rs serves as the final fallback when both geometric methods fail. It operates on purely text‑driven cues and residual geometric hints that didn't form complete grids.
The stage employs three complementary heuristics:
| Heuristic | Input | Behavior |
|---|---|---|
| Rect‑based hint regions | Large rectangle clusters lacking grid completeness | Creates bounding boxes that scope text‑based search |
| Failed‑cluster hints | Small rectangle clusters | Treats partial geometric evidence as table region indicators |
| Pure text‑anchor heuristics | Gap histograms, whitespace patterns, paragraph breaks | Detects row stripes and column alignments without any graphic guidance |
These heuristics ensure recovery of tables drawn solely with whitespace or minimal visual structure.
Priority Order and Cascade Logic
The three stages execute in a fixed priority order that maximizes precision:
Rect‑based → Line‑based → Heuristic
This cascade guarantees that high‑confidence geometric detection always precedes speculative inference. The orchestration logic in src/tables/mod.rs implements this flow:
- First pass – Invoke
detect_tables_from_rectson all pages. - Second pass – For pages with no rect tables, invoke
detect_tables_from_lines. - Third pass – For pages still lacking tables, invoke heuristic detection with accumulated hints from previous stages.
When multiple candidates survive a stage, selection criteria include row/column count maximization and evidence scoring functions like table_evidence_score and tables_share_items.
Source Code Reference
| File | Purpose | Direct Link |
|---|---|---|
detect_rects.rs |
Rectangle clustering, grid validation, primary geometric detection | [src/tables/detect_rects.rs](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/detect_rects.rs) |
detect_lines.rs |
Horizontal rule processing, text‑anchor column inference | [src/tables/detect_lines.rs](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/detect_lines.rs) |
detect_heuristic.rs |
Fallback heuristics for text‑only tables and incomplete geometry | [src/tables/detect_heuristic.rs](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/detect_heuristic.rs) |
mod.rs |
Stage orchestration and public API exports | [src/tables/mod.rs](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/mod.rs) |
lib.rs |
Top‑level process_pdf_with_options entry point |
[src/lib.rs](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs) |
Usage Examples
Standard Pipeline Invocation
Call the complete three‑stage detector through the public API:
use pdf_inspector::extractor::process_pdf_with_options;
use pdf_inspector::types::PdfOptions;
let opts = PdfOptions::default();
let result = process_pdf_with_options("document.pdf", opts)?;
for (i, table) in result.tables.iter().enumerate() {
println!(
"Table {}: {} rows × {} columns",
i + 1,
table.rows.len(),
table.columns.len()
);
}
Stage‑Specific Debugging
Invoke individual stages for targeted analysis:
use pdf_inspector::tables::{
detect_tables_from_rects,
detect_tables_from_lines,
detect_heuristic_tables,
};
use pdf_inspector::types::{TextItem, PdfRect, PdfLine};
// Stage 1: Rect-based only
let (rect_tables, rect_hints) = detect_tables_from_rects(items, rects, 1);
// Stage 2: Line-based only (if rects found nothing)
let line_tables = detect_tables_from_lines(items, lines, 1);
// Stage 3: Heuristic fallback
let heuristic_tables = detect_heuristic_tables(items, rects, lines, 1);
Summary
- Rect‑based detection runs first as the most reliable method, targeting explicit rectangle borders.
- Line‑based detection runs second for ruled‑line documents, using horizontal rules and text anchors.
- Heuristic detection runs last for text‑only tables, combining residual geometry hints with whitespace analysis.
- The fixed priority order ensures geometric certainty precedes speculative inference.
- Source implementations reside in
detect_rects.rs,detect_lines.rs, anddetect_heuristic.rsundersrc/tables/.
Frequently Asked Questions
What determines which detection stage runs for a given page?
The cascade runs sequentially: rect‑based always executes first. Only pages yielding no valid rect tables proceed to line‑based detection. Heuristic detection activates solely when both geometric stages fail. This order is hardcoded in the table module's orchestration logic.
Can I disable specific stages or change the priority order?
The public API process_pdf_with_options does not expose stage toggles. For custom behavior, import individual functions from pdf_inspector::tables and implement your own orchestration. The modular design in detect_rects.rs, detect_lines.rs, and detect_heuristic.rs supports this composition.
How does pdf‑inspector handle tables with only horizontal lines and no vertical borders?
The line‑based detection stage specifically addresses this case. In detect_lines.rs, the text‑anchor column inference mechanism analyzes text item positions to reconstruct vertical boundaries when physical vertical strokes are absent.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →