How Vector-Grid Detection Works for Table Extraction in pdf-inspector

Vector-grid detection in pdf-inspector identifies tables by analyzing PDF path operators to find dense grids of horizontal and vertical lines, then clustering them into column and row boundaries before assigning text to cells.

The firecrawl/pdf-inspector repository implements three complementary table extraction strategies: rect-based, line-based (vector-grid), and heuristic detection. The vector-grid approach specifically targets PDFs containing explicit ruling lines, making it ideal for scanned forms and structured documents where text stream analysis alone proves unreliable.

What Is Vector-Grid Detection?

Vector-grid detection operates by inspecting the raw PDF drawing operators that create horizontal and vertical strokes. Unlike heuristic methods that infer structure from text positioning alone, this line-based strategy recognizes regular grids of lines that physically define column edges and row boundaries. Once identified, the algorithm assigns surrounding text items to these geometric cells, producing a structured Table representation.

The Vector-Grid Pipeline: Step-by-Step

The implementation lives primarily in src/tables/detect_lines.rs, where the detect_dense_line_chart_regions function orchestrates the extraction process.

1. Classifying PDF Path Operators

Every PdfLine is inspected to determine its orientation. The algorithm calculates slope ratios to classify lines as vertical (dx / dy ≤ ANGLE_TOLERANCE) or horizontal (dy / dx ≤ ANGLE_TOLERANCE). Segments shorter than MIN_GRID_LINE_LENGTH are discarded immediately to eliminate noise from decorative borders or dashes.

This classification occurs in detect_dense_line_chart_regions at lines 28-44.

2. Collecting Line Candidates

Valid vertical lines are stored as tuples of (x, y_min, y_max), while horizontal lines are stored as (y, x_min, x_max). This coordinate extraction enables subsequent geometric analysis of the grid structure.

See lines 45-51 in src/tables/detect_lines.rs.

3. Early Exit for Sparse Drawings

The algorithm enforces a density-first heuristic to distinguish tables from charts or decorative graphics. If the region contains fewer than DENSE_CHART_MIN_VERTICAL_EDGES (27) vertical lines or fewer than three horizontal lines, the function returns early, ignoring the region as non-tabular.

This validation appears at lines 51-53.

4. Bucketing Vertical Extents

To group vertical lines that belong to the same table panel, the algorithm quantizes the y-extent of each line into buckets using extent_key with a tolerance of EXTENT_TOLERANCE (6 pt). This bucketing prevents the repeated scanning of every line during the family-building phase.

Implementation details are found at lines 55-61.

5. Building Families of Vertical Lines

For each bucket, the code computes the average bottom (anchor_bottom) and top (anchor_top) coordinates. Adjacent buckets (±1) are merged if their extents fall within EXTENT_TOLERANCE, producing a family representing a candidate dense column set. This isolation prevents cross-contamination between separate tables on the same page.

See lines 62-78 for the family construction logic.

6. Snapping Column X-Coordinates

The X positions of all vertical lines within a family are fed to snap_edges, a clustering routine with a 3 pt tolerance. This groups nearby coordinates affected by anti-aliasing or rendering variations. Only families producing at least 27 distinct column edges survive this filtering.

This clustering occurs at lines 80-93.

7. Deriving Row Y-Coordinates

Horizontal rules intersecting the vertical family are examined to establish row boundaries. The algorithm collects Y-values per span—contiguous X-ranges containing sufficient vertical edges. A span is retained only if it contains at least three distinct Y values after snapping (snap_edges(&ys, 3.0)), ensuring the horizontal rule truly belongs to the grid rather than serving as a decorative element.

This derivation logic spans lines 103-126.

8. Validating Panel Dimensions

Before accepting a detected grid, the algorithm rejects panels that are too narrow (width < 100 pt) or too short (height < 20 pt). This prevents tiny graphic elements or separator lines from being misidentified as tables.

See the validation checks at lines 27-30.

9. Assigning Text to the Grid

With final col_edges and row_edges vectors established, the code calls assign_items_to_grid (imported from src/tables/detect_rects.rs) at line 59. This function places each TextItem into the appropriate cell based on its center point coordinates.

10. Constructing the Table Structure

The collected cells, column and row coordinates, and the indices of used text items are wrapped in a Table struct via Table::new. The completed table object is then returned to the higher-level detector at lines 50-55, where it may be merged with rect-based or heuristic candidates.

Why the Algorithm Works

The vector-grid detector achieves high precision through four specific design decisions:

  • Density-first heuristic: Real tables produce dense vertical stroke patterns (≥ 27 distinct X positions) because column separators repeat across many rows. Charts typically exhibit far fewer vertical divisions, allowing the threshold to filter them out early.
  • Robust edge clustering: The snap_edges utility groups coordinates within a few points of each other, ensuring that minor rendering variations or anti-aliasing artifacts do not fracture the grid detection.
  • Span-aware row extraction: Horizontal rules must span at least 80% of the table width and intersect enough vertical edges to qualify as row boundaries, eliminating decorative horizontal lines.
  • Panel-level isolation: By grouping vertical lines into families based on Y-extents, the detector isolates separate tables sharing the same page, preventing cross-panel contamination.

Running the Vector-Grid Detector

To extract tables using the full pipeline (including vector-grid detection):

pdf2md --json my_report.pdf > out.json

To debug the line-based detector specifically and view detailed logging:

RUST_LOG=pdf_inspector::tables=debug cargo run --bin pdf2md -- \
    --detect-lines-only my_report.pdf

The debug output includes messages from the functions described above, such as "detect_lines p1: accepted text-anchor rule table," allowing you to trace the detection process through each step.

Key Source Files

  • src/tables/detect_lines.rs: Implements the complete vector-grid pipeline, including detect_dense_line_chart_regions, derive_columns_from_horizontal_segments, and the detect_tables_from_lines entry point.
  • src/tables/grid.rs: Provides shared utilities for column/row clustering, numeric-column merging, and header-row recovery used by all three detection strategies.
  • src/tables/detect_heuristic.rs: Implements the fallback heuristic detector that analyzes text positioning when explicit lines are absent.

Summary

  • Vector-grid detection analyzes PDF drawing operators to identify horizontal and vertical ruling lines.
  • The algorithm requires at least 27 vertical edges and 3 horizontal edges to qualify a region as a dense table grid.
  • Snap clustering with 3-6 pt tolerances groups lines affected by rendering artifacts while preserving true grid structure.
  • Panel validation (minimum 100 pt width, 20 pt height) and span-aware filtering prevent false positives on decorative graphics.
  • Detected grids assign text items to cells via geometric center-point testing before constructing the final Table struct.

Frequently Asked Questions

What is the minimum number of vertical lines required for vector-grid detection?

The algorithm requires at least 27 vertical edges (DENSE_CHART_MIN_VERTICAL_EDGES) to consider a region a dense table grid. This threshold filters out charts and decorative graphics that typically contain far fewer vertical divisions.

How does pdf-inspector distinguish between tables and charts using vector-grid detection?

The system relies on density heuristics. Real tables produce dense patterns of vertical strokes because column separators repeat across rows, while charts exhibit sparse line distributions. The early-exit logic at lines 51-53 of detect_lines.rs rejects regions with fewer than 27 vertical or three horizontal lines.

What tolerance values does the vector-grid algorithm use for clustering lines?

The implementation uses 6 pt (EXTENT_TOLERANCE) for bucketing vertical line extents and 3 pt for the snap_edges clustering routine. These tolerances accommodate minor anti-aliasing variations and PDF rendering artifacts without breaking grid continuity.

Can vector-grid detection work on scanned PDFs?

Yes, provided the scanned document contains explicit ruling lines. The vector-grid detector serves as a high-confidence fallback for PDFs where the underlying text stream is unreliable, such as scanned forms with visible grid lines. However, it cannot detect tables in scanned images lacking visible borders, where the heuristic detector would take over.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →