How accurate is PDFCells?
On PDFs with a text layer, PDFCells reads characters exactly from the PDF and the risk is table structure; on scans, OCR can misread digits. Either way, tables are checked arithmetically wherever they have something to check (running balances, totals, line totals), so most errors in those tables show up as a failed check you can see and fix before you download.
The checks
| Check | Holds when | Catches |
|---|---|---|
| Balance ties | Each balance = previous balance + money in − money out | A misread amount or balance on a statement, and names the cell |
| Opening and closing balances tie | Opening + credits − debits = closing, from the statement summary | A misread summary figure (the statement export separately requires the transactions to add up to the summary) |
| Footed | A Total row = the rows above it (or the earlier subtotals) | A misread number in a column with a total |
| Line totals | Quantity × unit price = line total | A misread price or quantity on an invoice |
| Cross-footed | A column = the sum of the columns beside it (Q1–Q4 = FY) | A misread cell in a report |
| Subtotal and tax tie | Subtotal + tax = total | A misread invoice total |
Amounts with two or more decimals must match within 1.1 units of the last decimal place. Rounded whole-number columns get half a unit per term, because printed reports round each figure. A check only runs once PDFCells recognises the pattern: a running balance needs at least three ties and at least 70% of the table agreeing, and line totals need most rows to fit. A table with nothing to check is simply not marked.
Measured on our test corpus
PDFCells is developed against a harness that scores every engine against exact ground truth. The corpus is synthetic: about 30 files and 43 pages generated to isolate known failure modes (wrapped rows, side-by-side tables, decimal commas, skewed photos, fax-quality scans). It shows which engine is better; it does not estimate how often real-world documents fail.
| Engine | Pages | Cells read exactly | Numeric cells exact | Checks pass |
|---|---|---|---|---|
| PDF text layer, digital pages | 32 | 97.5% | 97.6% | 72.2% |
| PDF text layer, bank statement pages | 7 | 100% | 100% | 88.9% |
| OCR (tesseract.js, the default), all pages | 43 | 94.3% | 95.5% | 78.6% |
| Experimental OCR (PP-OCRv6 tiny), all pages | 43 | 95.5% | 97.2% | 85.7% |
| OCR (tesseract.js), scanned pages | 3 | 66.7% | 66.7% | 100% |
In a later 44-page experiment that re-read every page with the experimental engine’s retry crop, cells read exactly rose from 94.4% to 96.1%; in the app the retry is only offered on pages that look weak, which that experiment didn’t measure. Checks-pass counts tables whose checks were verified; tables with nothing to check (no totals, no balance) aren’t counted. A missed table counts as every cell wrong. The scan sample is three pages, too small for a rate: one fax-quality page is most of the miss.
Reproduce the numbers
Everything above comes from code and files in the public repository: the eval harness (metrics and engines), the test corpus with its list of what each file stresses and the script that generates it, the committed reports (text layer and default OCR, retry experiment), and the OCR research notes. With Node 24 or later:
git clone https://github.com/mkerkstra/pdfcells
cd pdfcells
corepack pnpm install
pnpm eval # PDF text layer and default OCR, every page
pnpm eval --engine ppocr-v6-tiny # the experimental OCR engine
pnpm eval --engine ppocr-retry-band48 # the weak-page retry, cropped as the app does
pnpm eval --list # every engineThese commands evaluate the code as it is now. Each committed report records the commit it ran at, and the corpus has grown since the first baseline (29 files and 43 pages then, 30 files and 44 pages now), so expect small differences from the table above. Each run writes a Markdown and JSON report to eval/reports/. Everything runs locally and costs nothing; the Claude engine is off unless you pass your own API key, --hosted and --yes. Timings depend on your machine; the accuracy figures don’t.
What the corpus is, and isn’t
- It is about 30 generated files (43–44 pages): bank and card statements, invoices, financial reports, prose and blank pages, scans, photos, screenshots, TIFF and HEIC images. Each targets one way real documents go wrong (wrapped rows, page breaks, decimal commas, skew, fax noise, planted arithmetic errors), and every cell has exact ground truth because the script that drew the page also wrote the answer.
- It isn’t a sample of real documents. It holds no real bank’s statements and no customer files, so it can rank engines against each other and catch regressions, but it can’t tell you how often PDFCells is wrong on your documents.
What the numbers don’t cover
- AI reading: we have not published a measured accuracy for the Claude models on this corpus, and found no public benchmark for them on tables. Treat AI as a strong reader whose output still goes through the same checks.
- Real statements: no bank’s statements are in the corpus. PDFCells has no bank-specific templates; it reads every statement with the same layout engine.
- Layouts that fail: side-by-side tables and multi-level headers are the weakest layouts for both the text layer and OCR.
- A check that passes proves the arithmetic, not every character: a wrong description or date doesn’t break a balance.
Questions
Is PDFCells 100% accurate?
No converter is. Text-layer PDFs give exact characters but table structure can still be wrong on unusual layouts, and OCR can misread digits. The checks make most errors visible before you download.
What does “Footed” mean?
Every row labelled Total in the table equals the sum of the rows it totals. It is an accountant’s term for a column that adds up.
Why is the test corpus synthetic?
Generated documents have exact ground truth for every cell and can isolate one failure mode at a time. They show which engine is better, not real-world error rates.