Extraction checks / Recorded October 14, 2026

What came out
of five PDFs.

The simple table came back intact. A borderless header lost a word. A rotated table came back in the wrong order. Here are the exact files, settings and outputs.

5 invented PDFs6 case + setting combinations1 extraction libraryNo OCR tested

These are tiny, synthetic examples of pdfplumber 0.11.9. They are not an accuracy estimate, a comparison of tools or representative business documents. Each result applies to the shown file and configuration.

Two different questions.

Will a tool fit my setup?

The matcher uses reviewed documentation to check operating system, setup, privacy and licensing constraints. Its rankings are documented workflow fit, not measured extraction quality.

What did this file produce?

The checks below actually executed pdfplumber on five invented PDFs. They do not validate every compatibility profile, the other four tools, or the matcher's rankings.

The results, failures included

Look past “table found.”

Full recorded JSON ↗

Exact match means every cell and its position matched the expected logical grid. Two settings were tested on the same borderless file, so six observations do not mean six independent fixtures.

Case 01Exact match

A simple ruled table

All 12 cells, including the header, matched the expected 4 × 3 grid in the raw output.

Vertical / horizontal: lines / linesDownload this PDF ↗
Compare expected and extracted cells

Expected logical cells

Expected · page 1, table 1
ItemQuantityUnit price
Alpha210.00
Beta53.25
Gamma199.00

Actual raw output

Extracted · page 1, table 1
ItemQuantityUnit price
Alpha210.00
Beta53.25
Gamma199.00

Whitespace/blank-row cleanup did not change this output.

Raw settings and observations (JSON)
{
  "settings": {
    "vertical_strategy": "lines",
    "horizontal_strategy": "lines"
  },
  "observed_pages": [
    {
      "page": 1,
      "rotation": 0,
      "width": 612,
      "height": 792,
      "character_count": 53,
      "image_count": 0,
      "tables": [
        [
          [
            "Item",
            "Quantity",
            "Unit price"
          ],
          [
            "Alpha",
            "2",
            "10.00"
          ],
          [
            "Beta",
            "5",
            "3.25"
          ],
          [
            "Gamma",
            "1",
            "99.00"
          ]
        ]
      ]
    }
  ],
  "raw_exact_match": true,
  "normalized_exact_match": true
}
Case 02Exact pages; joining assumed

A table over two pages

Both 4 × 3 page tables matched. Separate example code joined them into the expected 7 × 3 grid, removing an exactly repeated header. It assumes the tables belong together; pdfplumber did not infer continuation.

Vertical / horizontal: lines / linesDownload this PDF ↗
Compare expected and extracted cells

Expected logical cells

Expected · page 1, table 1
ItemQuantityUnit price
Alpha210.00
Beta53.25
Gamma199.00
Expected · page 2, table 1
ItemQuantityUnit price
Delta48.50
Epsilon312.00
Zeta65.75

Actual raw output

Extracted · page 1, table 1
ItemQuantityUnit price
Alpha210.00
Beta53.25
Gamma199.00
Extracted · page 2, table 1
ItemQuantityUnit price
Delta48.50
Epsilon312.00
Zeta65.75

Whitespace/blank-row cleanup did not change this output.

Separate, explicitly assumed page joining

Concatenate tables in page order, removing a repeated first row only when it exactly equals the first table's header. Assumes these tables belong together; does not infer continuation or handle missing/split rows.

Joined output · exact match to expected 7 × 3 grid
ItemQuantityUnit price
Alpha210.00
Beta53.25
Gamma199.00
Delta48.50
Epsilon312.00
Zeta65.75
Raw settings and observations (JSON)
{
  "settings": {
    "vertical_strategy": "lines",
    "horizontal_strategy": "lines"
  },
  "observed_pages": [
    {
      "page": 1,
      "rotation": 0,
      "width": 612,
      "height": 792,
      "character_count": 53,
      "image_count": 0,
      "tables": [
        [
          [
            "Item",
            "Quantity",
            "Unit price"
          ],
          [
            "Alpha",
            "2",
            "10.00"
          ],
          [
            "Beta",
            "5",
            "3.25"
          ],
          [
            "Gamma",
            "1",
            "99.00"
          ]
        ]
      ]
    },
    {
      "page": 2,
      "rotation": 0,
      "width": 612,
      "height": 792,
      "character_count": 54,
      "image_count": 0,
      "tables": [
        [
          [
            "Item",
            "Quantity",
            "Unit price"
          ],
          [
            "Delta",
            "4",
            "8.50"
          ],
          [
            "Epsilon",
            "3",
            "12.00"
          ],
          [
            "Zeta",
            "6",
            "5.75"
          ]
        ]
      ]
    }
  ],
  "raw_exact_match": true,
  "normalized_exact_match": true
}
Case 03No table detected

No borders, line-based settings

The line-based configuration returned no table from this borderless fixture. This observation is about this file and these settings.

Vertical / horizontal: lines / linesDownload this PDF ↗
Compare expected and extracted cells

Expected logical cells

Expected · page 1, table 1
ItemQuantityUnit price
Alpha210.00
Beta53.25
Gamma199.00

Actual raw output

Page 1: no tables returned

Whitespace/blank-row cleanup did not change this output.

Raw settings and observations (JSON)
{
  "settings": {
    "vertical_strategy": "lines",
    "horizontal_strategy": "lines"
  },
  "observed_pages": [
    {
      "page": 1,
      "rotation": 0,
      "width": 612,
      "height": 792,
      "character_count": 53,
      "image_count": 0,
      "tables": []
    }
  ],
  "raw_exact_match": false,
  "normalized_exact_match": false
}
Case 04A header word is missing

Same file, text-based settings

One table was detected, but “Unit price” became “Unit”. Removing blank rows restored the expected shape, not the missing word. A table count alone would hide the error.

Vertical / horizontal: text / textDownload this PDF ↗
Compare expected and extracted cells

Expected logical cells

Expected · page 1, table 1
ItemQuantityUnit price
Alpha210.00
Beta53.25
Gamma199.00

Actual raw output

Extracted · page 1, table 1
ItemQuantityUnit
emptyemptyempty
Alpha210.00
emptyemptyempty
Beta53.25
emptyemptyempty
Gamma199.00

After the documented cleanup

Normalized · page 1, table 1
ItemQuantityUnit
Alpha210.00
Beta53.25
Gamma199.00
Raw settings and observations (JSON)
{
  "settings": {
    "vertical_strategy": "text",
    "horizontal_strategy": "text"
  },
  "observed_pages": [
    {
      "page": 1,
      "rotation": 0,
      "width": 612,
      "height": 792,
      "character_count": 53,
      "image_count": 0,
      "tables": [
        [
          [
            "Item",
            "Quantity",
            "Unit"
          ],
          [
            "",
            "",
            ""
          ],
          [
            "Alpha",
            "2",
            "10.00"
          ],
          [
            "",
            "",
            ""
          ],
          [
            "Beta",
            "5",
            "3.25"
          ],
          [
            "",
            "",
            ""
          ],
          [
            "Gamma",
            "1",
            "99.00"
          ]
        ]
      ]
    }
  ],
  "raw_exact_match": false,
  "normalized_exact_match": false
}
Case 05Wrong logical orientation

A page rotated 90 degrees

The output was 3 rows × 4 columns, transposed/reversed relative to the expected 4 × 3 grid. The header also contained a line break. No rotation correction or cell reordering was applied.

Vertical / horizontal: lines / linesDownload this PDF ↗
Compare expected and extracted cells

Expected logical cells

Expected · page 1, table 1
ItemQuantityUnit price
Alpha210.00
Beta53.25
Gamma199.00

Actual raw output

Extracted · page 1, table 1
GammaBetaAlphaItem
152Quantity
99.003.2510.00Unit price

Whitespace/blank-row cleanup did not change this output.

Raw settings and observations (JSON)
{
  "settings": {
    "vertical_strategy": "lines",
    "horizontal_strategy": "lines"
  },
  "observed_pages": [
    {
      "page": 1,
      "rotation": 90,
      "width": 792,
      "height": 612,
      "character_count": 53,
      "image_count": 0,
      "tables": [
        [
          [
            "Gamma",
            "Beta",
            "Alpha",
            "Item"
          ],
          [
            "1",
            "5",
            "2",
            "Quantity"
          ],
          [
            "99.00",
            "3.25",
            "10.00",
            "Unit\nprice"
          ]
        ]
      ]
    }
  ],
  "raw_exact_match": false,
  "normalized_exact_match": false
}
Case 06Expected negative control

An image-only copy

The page contained one image, zero extracted characters and zero tables. This is the expected result without OCR. No OCR pipeline was tested.

Vertical / horizontal: lines / linesDownload this PDF ↗
Compare expected and extracted cells

Expected logical cells

Expected · page 1, table 1
ItemQuantityUnit price
Alpha210.00
Beta53.25
Gamma199.00

Actual raw output

Page 1: no tables returned

Whitespace/blank-row cleanup did not change this output.

Raw settings and observations (JSON)
{
  "settings": {
    "vertical_strategy": "lines",
    "horizontal_strategy": "lines"
  },
  "observed_pages": [
    {
      "page": 1,
      "rotation": 0,
      "width": 612,
      "height": 792,
      "character_count": 0,
      "image_count": 1,
      "tables": []
    }
  ],
  "raw_exact_match": false,
  "normalized_exact_match": false
}

Inspect it / Run it yourself

Nothing to take on faith.

Download the evidence ZIP and open the proof-of-use-evidence folder. The recorded outputs, expected cells and SHA-256 hashes are included. No customer files are needed.

1. Check the downloaded record

Uses Python's standard library. Confirms the saved observations and fixture hashes; it does not rerun extraction.

python3 verify.py

2. Rerun the experiment

Package installation uses the registry. The experiment itself stays local. Rerunning replaces the extracted folder's generated fixtures and result, so keep a copy to compare.

python3 -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
.venv/bin/python run.py
.venv/bin/python verify.py

On Windows, use .venv\Scripts\python.exe. See the full reproduction guide.

The recorded environment

Executed UTC
2026-10-14T11:45:45+00:00
Runtime
Python 3.12.14, Linux x86_64
Packages
pdfplumber 0.11.9 · reportlab 4.4.9 · pypdfium2 5.13.0
Extractor
pdfplumber.Page.extract_tables
Settings
vertical_strategy / horizontal_strategy:
lines / lines or text / text

Direct and key transitive packages are pinned, not a full platform lock. ReportLab generates fixtures; PDFium renders them. Only pdfplumber extracts tables.

A passing verifier means the recorded behavior is consistent, including the known failures. It does not mean all extraction cases succeeded. Compare changed outcomes before updating expectations.

Download individual PDFs and inspect their hashes
  • digital-ruled.pdf2,194 bytes · SHA-256c8f832ec40ef5d7b3b77c222cd782f2f7f258e632664aed623cbe66b28889f40
  • multipage-ruled.pdf3,292 bytes · SHA-25607027d40f391b07dd88939b44bc4f8d1d7a0272d2c77b8d85d90b999e982e4f9
  • borderless-regular.pdf1,985 bytes · SHA-2567018b0a5f5be6177bb5c4242cbaec90044cf6255635139e7b5ce18a3f3f0f5b7
  • rotated-90.pdf2,195 bytes · SHA-25662876cd904ef60170b438073b62fd2b0dc758270938e939f55f329c9e6416a42
  • image-only.pdf25,125 bytes · SHA-256b8524f856ae512604d80ce8426966ac0664129cf15cb129ee20084396229c2ec

What these checks leave open.

These are regular English text and simple numeric cells, with one tiny fixture per layout. No OCR pipeline or model was installed or run. Camelot, Tabula, Docling and PaddleOCR were not executed. Speed, memory, security and production robustness were not measured.

No cropping, fixture-specific extraction coordinates, substitutions from expected cells or corrective row/column reordering were used. Normalization only strips cell-edge whitespace and discards wholly blank rows; raw outputs remain visible. Page joining is separate example code with an explicit continuation assumption.

All text and numbers are invented. The fixtures contain no customer documents or personal data. The reproduction guide documents provenance and scope. These measured examples leave the matcher's dataset, ranking and documentation-based labels unchanged.

Try the decision side.

A documented fit, a tool ruled out, and no qualified match. Three short examples show how the matcher handles each.

Try three examples