跳到正文
DEV Community· Nemwel Boniface·· 2 小時前AI 評分53

2026 年金融表格開源 OCR 與文件解析模型排名

Best Open-Source OCR and Document Parsing Models for Financial Tables (2026)

AI 導讀

作者比較並排序七款面向金融表格的開放權重 OCR 與文件解析模型,Docling 排名第一,granite-docling-258M 第二,MinerU2.5-Pro 第三。

正文

Hello friends! I'm back with a follow-up to my last article on choosing an inference platform for financial documents.

That article was about the where: where your document models run, what they cost, and whether client data ever leaves your environment. This one is about the what: the open-source models that actually sit inside that platform and read the tables. And trust me, when it comes to financial filings, reading tables is a very different job from reading text.

You might think OCR is a solved problem by now. For a paragraph of text? Mostly, yes. For a balance sheet with merged headers, three years of comparatives and negative numbers wrapped in brackets? Not even close. Let me explain.

GIF: a cat staring at a spreadsheet in total confusion

Disclosure: This article was written in partnership with Superlinked. It compares open-weight OCR and document-parsing options for financial tables and discusses how Superlinked Inference Engine can help teams deploy them.

Introduction: The Bracket That Lied

Picture this. A balance sheet comes back from your OCR pipeline looking spotless. Every cell is filled, every column is aligned, and the person reviewing it gives it a quick glance and moves on.

Except one line used to say (412), and now it says 412.

In accounting, those brackets mean negative. The model saw them, decided they were punctuation, and threw them away. While it was at it, it also read a little superscript footnote marker as one more digit. So a loss became a profit, a number grew a tail, and nothing in the pipeline raised a hand. No exception, no low-confidence warning, no red squiggly line. Just a tidy table that is confidently wrong.

That's what makes financial tables so unforgiving. A single number often depends on things that aren't in the number at all:

  • The header above it, which might span two or three columns at once.
  • The column it sits in, where shifting one position means the figure now belongs to the wrong year.
  • The brackets around it, which decide whether it's money in or money out.
  • A small line of text above the whole table, like "in millions, except per-share data", which quietly multiplies every number below it and appears in none of the cells.

A model can be brilliant at reading receipts and invoices and still fall over on every one of those. So in this article, I'm ranking open-source OCR models specifically on how much evidence they have for handling tables, and I'll show you how to check them on your own documents so you don't have to take my word (or anyone's leaderboard) for it.

Wait. Do You Even Need OCR?

Look, hear me out on this one before we get to the models, because it's the cheapest win in this whole article.

If you read my last piece, you already know the punchline: most SEC filings don't need OCR at all. 10-Ks, 10-Qs and EX-99.1 earnings releases on EDGAR are HTML, and the main filings carry Inline XBRL since the 2019 to 2021 phase-in. The tagged numbers come straight out of the SEC's XBRL APIs, and the untagged tables come out of a normal HTML parser. Sending those to an OCR model is paying someone to read your handwriting when you already typed the letter.

OCR earns its keep on the documents that only exist as pictures of text: foreign annual reports never filed with the SEC, data-room packs, client uploads and scanned exhibits from before everything went digital.

And here's a step most guides skip: even inside that PDF pile, not every page needs the heavy machinery. Plenty of PDFs are born digital and already carry a text layer. A few lines of Python can sort your pages into buckets before any model sees them:

import re
from pypdf import PdfReader

NUMBER = re.compile(r"\(?\d[\d,]*\.?\d*\)?")

def triage(path: str, min_chars: int = 50, min_numbers: int = 6) -> dict:
    """Sort PDF pages into buckets before any model sees them."""
    buckets = {"needs_ocr": [], "table_candidates": [], "plain_text": []}
    for page_no, page in enumerate(PdfReader(path).pages, start=1):
        text = page.extract_text() or ""
        if len(text.strip()) < min_chars:
            buckets["needs_ocr"].append(page_no)          # no usable text layer: likely a scan
        elif len(NUMBER.findall(text)) >= min_numbers:
            buckets["table_candidates"].append(page_no)   # lots of figures: send to a table model
        else:
            buckets["plain_text"].append(page_no)         # prose: the text layer is enough
    return buckets

print(triage("annual_report.pdf"))
# e.g. {'needs_ocr': [2], 'table_candidates': [1], 'plain_text': [3]}

It's deliberately crude (tune the thresholds on your own documents), but it means the chairman's letter on page 3 never goes anywhere near a GPU, and your table models only see the pages that actually have tables. Keep this triage in mind, because it's going to come back when we talk about running these models in production.

Two Terms You'll See a Lot (and Why They Matter)

Before the ranking, a quick translation of the jargon you'll find on every model card, because the numbers mean very little until you know what they're measuring.

FinTabNet is a benchmark built from tables in real company annual reports. In other words, it's the closest thing we have to a public exam made of the exact kind of tables this article is about. A model with a published FinTabNet score has been tested on balance sheets, not just on academic papers and invoices.

TEDS (Tree-Edit-Distance-based Similarity) is how most table benchmarks grade a model. It turns both the model's table and the correct table into a tree, the same way HTML nests rows inside tables and cells inside rows, and measures how many edits it takes to turn one into the other. A perfect score is 100, or 1.0, depending on who's reporting it. So when you see one model at 96.8 and another at 0.97, they're on the same scale, just written differently.

Here's the catch that I think matters most: TEDS grades the whole table. A model that gets the structure of a 200-cell balance sheet right but drops the brackets from one cell will still score beautifully. A great TEDS score tells you a model understands table structure. It doesn't promise that every sign survived. We'll fix that later with a simple cell-level check.

One more thing: you'll see a lot of models quoting OmniDocBench, a broader document-parsing benchmark. It comes in several versions (the original 2025 release, v1.5 and v1.6), and they're different tests. Comparing a v1.5 score to a v1.6 score is like comparing two people's times on different running routes. Both times are real; they just don't tell you who's faster.

How I Picked and Ordered These Models

My rules were simple, and I'm sharing them so you can argue with the ranking properly in the comments:

  1. Open weights with a licence that allows commercial use, checked on the exact checkpoint. If you can't ship it in a product, it's not on the list.
  2. Finance-table evidence first. A published FinTabNet result beats everything else. Then other table-specific evidence, then whether the model preserves structure and reading order, then licence clarity, then size.
  3. Benchmarks inform the order; they don't decide it. Almost every number on these model cards comes from the people who built the model.
  4. Deployment options don't affect rank. I note them for every model because you'll care, but being easier to host doesn't buy a higher spot.

Alright, let's get into it.

1. Docling: The One You Can Explain to an Auditor

If your tables are going anywhere near an audit, a regulator or a picky finance director, start here.

Docling isn't really a single model. It's a pipeline of specialists: one model (Layout Heron) works out where everything sits on the page, a dedicated table model (TableFormer) rebuilds the rows and columns, and an OCR engine reads the actual characters (RapidOCR PP-OCRv4 in SIE's artifact bundle). Think of it like a small team where everyone has one job, instead of one person trying to do everything at once. When something goes wrong, you can tell which step got it wrong, and that's a luxury you rarely get with end-to-end models.

The evidence is the strongest on this list. The TableFormer paper (arXiv 2203.01017, 2022) reports a FinTabNet TEDS of 96.8 overall, 97.5 on simple tables and 96.0 on complex ones. I'll be honest about one thing: that number comes from the paper, not the current weights, so read it as proof that the approach works on financial tables rather than a guarantee for today's exact checkpoint.

What really earns it the top spot, though, is that Docling rebuilds tables as tables and keeps the reading order intact, so a three-year comparative stays three columns instead of melting into one long list of numbers. As a library, it also records a page number and bounding box for every element. When someone asks "where did this figure come from?", you can point at the exact spot on the exact page. In finance, that answer can matter more than a few extra benchmark points.

It's also refreshingly easy to try:

from docling.document_converter import DocumentConverter

converter = DocumentConverter()
result = converter.convert("foreign_issuer_annual_report.pdf")

print(result.document.export_to_markdown())

for i, table in enumerate(result.document.tables):
    df = table.export_to_dataframe(doc=result.document)
    print(f"Table {i}: {df.shape[0]} rows x {df.shape[1]} columns")

Where it can stumble: on a poor scan, TableFormer can only rebuild structure from whatever text the OCR engine managed to read. A perfect grid of misread digits is still wrong.

The fine print: MIT code. TableFormer weights are CDLA-Permissive-2.0, and Layout Heron and RapidOCR are Apache-2.0, so commercial use is fine. Small enough to run on CPU, served on SIE Cloud as docling, and backed by a project with 68.2k GitHub stars.

2. granite-docling-258M: Tiny Model, Serious Receipts

If you like the Docling approach but want something smaller and simpler, this is your model.

IBM's granite-docling-258M does the whole conversion in one go, turning a page into DocTags, a structured markup that keeps track of what's a heading, what's a paragraph and what's a table. It can also focus on a specific region of a page when you give it a bounding box.

At just 258 million parameters, it has no business carrying the receipts it does. Its model card reports FinTabNet scores of 0.97 for structure and 0.96 including content, at 150 dpi, compared with 0.82 and 0.76 for its predecessor, SmolDocling. That makes it one of only two models on this list with published results on real financial tables, and the other one is number one.

Why it's not first: it's a single model, so there are fewer places to look when a table goes sideways, and that score comes from its own publisher. One benchmark from the people who built it is a great reason to test it. It's not a reason to skip testing. The good news is that at this size, testing is about as cheap as it gets.

The fine print: Apache-2.0. Open weights that you serve yourself, on a modest GPU or even locally.

3. MinerU2.5-Pro: For Notes That Refuse to Fit on One Page

Every annual report has them: the notes to the accounts that start on page 112 and finally end on page 115, with the header row only printed once at the very top. If those are your nightmare, this is your model.

But first, a warning, because this one catches people out. Read the licence on the exact checkpoint:

  • opendatalab/MinerU2.5-Pro-2604-1.2B is Apache-2.0, so commercial use is fine.
  • The earlier MinerU2.5-2509-1.2B is AGPL-3.0, which is a very different conversation with your legal team.
  • The MinerU toolkit code has its own custom licence, separate from both.

Same family name, three different situations. Pin the checkpoint, and you'll sleep fine.

Once that's sorted, the toolkit handles tables that run across pages, which is exactly where multi-year notes break. Remember the segment table from my last article that lost its header row at a page break? This is the first model I'd point at it.

The publisher's numbers are ambitious: 95.69 on OmniDocBench v1.6, up from 92.98 for MinerU 2.5, a 5.54-point jump in table TEDS, and a 1.39-point lead over the second-placed model across five table benchmarks. It also claims to beat GLM-OCR, PaddleOCR-VL-1.5 and larger general models. Two things keep it at number three: those are vendor numbers, and v1.6 is a benchmark version the same publisher proposed.

The fine print: Apache-2.0 for the 2604 checkpoint. 1.2B parameters, served on your own GPUs or through SIE's self-hosted catalog.

4. PaddleOCR-VL-1.5: For Scans That Have Seen Things

Older exhibits are rarely scanned nicely. Pages come in tilted, curved near the spine or photocopied one too many times. If that sounds like your archive, this is the model to try.

PaddleOCR-VL-1.5 is a compact 0.9B model from PaddlePaddle that reads a whole page end to end, tables included. Its card specifically calls out tolerance for scans, skew and warped pages, and like MinerU, it can merge tables across pages and recognise headings that continue onto the next page. It even does seal recognition, which you'll almost never need for filings, but hey, it's there.

Its publisher reports 94.5 on OmniDocBench v1.5 and claims first place overall and on tables. You'll notice a pattern here: nearly every publisher claims first place somewhere. That's not a criticism; it's just why rule 3 exists. That v1.5 number can't be lined up against MinerU's v1.6 number as though they ran the same race.

Why fourth: solid table support, but its biggest strength is coping with ugly scans rather than finance-specific evidence.

The fine print: Apache-2.0. 0.9B parameters, for your own hardware, and listed in SIE's self-hosted catalog.

5. GLM-OCR: Neck and Neck With PaddleOCR

GLM-OCR from Z.ai (0.9B, according to Z.ai) reports 94.62 on OmniDocBench v1.5, first overall at its release. PaddleOCR-VL-1.5 reports 94.5 on the same version and also claimed first at its release.

A 0.12-point difference is well inside the noise of a single benchmark run. If anyone tells you one of these is clearly better than the other based on that gap, smile politely and run both on your own pages.

It sits one spot lower for a practical reason: layout detection is handled by a separate model, PP-DocLayout-V3. That isn't a flaw, but it's one more thing to deploy, version and keep in step. And if you read my Celery post-mortem, you know how I feel about two things that have to stay in sync by hand.

The fine print: MIT weights, plus Apache-2.0 for the layout model, and the card asks you to comply with both. Runs on Linux with NVIDIA GPUs, directly or through SIE's self-hosted catalog. 1.8M downloads on its card, so you're in good company.

6. dots.ocr: Every Number Comes With a Home Address

If your product needs to highlight exactly where on the page a number came from, this one is worth a look.

dots.ocr does layout and reading in a single model (a 1.7B language base, roughly 3.0B parameters in total). Its standout feature is the output: for every element on the page, it returns a bounding box, a category and the text, and tables come back as HTML. That's ready to plug into a review screen or an audit trail without writing a pile of glue code.

Why sixth: its benchmark claims are on the original 2025 OmniDocBench, a different test from the v1.5 and v1.6 numbers above, so there was nothing comparable to rank it against. That says more about the benchmarks than the model.

The fine print: MIT, on both the model card and the repository. The project has moved to dots-studio/dots.ocr, and old links redirect. About 3B parameters on your own hardware.

7. LightOnOCR-2-1B: The Bouncer at the Door

Putting LightOnOCR last on a table list might look harsh. It isn't. It's just not a table model.

LightOnOCR-2-1B is a 1B page-to-text model, and LightOn reports state-of-the-art results on OlmOCR-Bench while being about nine times smaller than comparable models. What its card doesn't have is any table-specific evidence, and it doesn't return coordinates for text (its -bbox variants only locate embedded images).

So don't use it as your table reader. Use it as the bouncer at the door. Remember the triage script from earlier? Pair it with LightOnOCR for a fast, cheap pass over a big archive of scanned pages, find the ones that actually contain tables, and only send those to Docling or MinerU. That's the same instinct as my PaceTracker article: don't do expensive work until something genuinely needs it.

The fine print: Apache-2.0. 1B parameters, on your own hardware or on SIE Cloud at $2 per 1,000 pages (listed 2026-09-30).

Didn't Make the Cut: olmOCR-2

olmOCR-2 was on my shortlist until I read its own benchmark, where Tables scored 48.3, its weakest category. On a list about tables, the model ruled itself out.

So, Why Not Just Pay for a Hosted API?

Fair question. Hosted services exist, they work, and some of them are very good:

Hosted option Listed price What you get
Azure AI Document Intelligence (Layout) $10 / 1,000 pages (US East) Structured tables, nothing to choose
Amazon Textract (Tables) $0.015 / page (US West, Oregon) Native to AWS, zero operations
Mistral OCR 4.1 $4 / 1,000 pages Strong table handling, per Mistral's own claim
Google Document AI (Layout Parser) $10 / 1,000 pages Layout parsing inside GCP

Prices as of 2026-09-30.

When a hosted API is the better call:

  1. You're all-in on one cloud, your documents are allowed to go there, and you want zero operations.
  2. You don't have the time or appetite to compare models, and "good enough, out of the box" genuinely is good enough.
  3. Your volumes are small enough that convenience costs less than an engineer's afternoon.

When open models win:

  1. You want to choose the model that fits your tables, instead of accepting someone else's choice.
  2. A client's data-room pack is contractually not allowed to leave your environment, which in financial work comes up far more often than people expect.
  3. You want to swap models as better ones come out, without re-integrating every time.

Running the Bake-Off Without Losing Your Weekend

Ok, ok, I know seven models is a lot. And here's the uncomfortable truth: the only honest way to choose between them is to run several on your own PDFs. Done the usual way, that means seven different runtimes, seven sets of dependencies and a lot of "works on my machine." That's the kind of side quest that quietly eats a sprint.

This is exactly where Superlinked's Inference Engine (SIE) shines, and honestly, it's the reason the bake-off becomes realistic at all.

SIE is an inference layer built for task-specific models, and it runs the same engine three ways:

  • SIE Cloud: fully managed and priced per use. Superlinked runs the GPUs.
  • Self-hosted: the same engine under Apache 2.0 in your own Kubernetes cluster and VPC.
  • Local: for development and quick experiments.

My favourite part is how boring it makes model comparison, in the best way. The SIE Python SDK calls docling, LightOnOCR-2-1B, PaddleOCR-VL-1.5, MinerU2.5-Pro and GLM-OCR through the same extract method. Five integrations become one loop:

# Illustrative only: the shape of the bake-off, not SIE's exact client API.
# Check the SIE docs for client setup, the extract signature and full model IDs.
CANDIDATES = [
    "docling",            # SIE Cloud
    "LightOnOCR-2-1B",    # SIE Cloud
    "MinerU2.5-Pro",      # self-hosted catalog
    "PaddleOCR-VL-1.5",   # self-hosted catalog
    "GLM-OCR",            # self-hosted catalog
]

for model_id in CANDIDATES:
    output = client.extract(model=model_id, document="hardest_50_pages.pdf")
    save_for_scoring(model_id, output)

The two cheapest candidates don't even need a GPU on your side. On SIE Cloud (as of 2026-09-30):

Model on SIE Cloud Price 10,000 pages
docling (to markdown) $0.397 / 1,000 pages $3.97
docling (OCR profile) $1.86 / 1,000 pages $18.60
LightOnOCR-2-1B $2 / 1,000 pages $20

Under four dollars to run Docling over ten thousand pages. That's cheaper than the coffee you'll drink while reviewing the results.

MinerU2.5-Pro, PaddleOCR-VL-1.5 and GLM-OCR run from the self-hosted catalog. A small heads-up: several of them need a Linux machine with an NVIDIA GPU, so your MacBook gets to sit this round out.

What I like even more is what happens after you pick a winner. On SIE, OCR, field extraction, embeddings and reranking all share the same client. The table reader you chose lives right next to the extractor that turns cells into schema fields and the reranker that powers search. Nothing gets re-wired. And the day a client says "this data stays in our VPC", you move the same engine with the same model IDs inside their walls. No rewrite, no "the self-hosted version has different models," no drama.

That's the difference between a model you tested and a model you can actually put into production.

Don't Trust Your Eyes: Score the Cells

Remember the balance sheet from the beginning? It passed the eyeball test. That's precisely the problem. And as we saw with TEDS, even a great benchmark score can hide a flipped sign.

So once your bake-off produces results, grade them the boring, reliable way. Take a handful of real tables, write down the correct value, row header, column header and sign for each cell, and count how many each model gets exactly right. Count sign flips separately, because those are the errors nobody spots on screen.

Here's a small scorer to get you going:

import re

DASHES = {"-", "—", "–"}

def normalise(cell: str, scale: int = 1) -> float | None:
    """Turn a financial-table cell into a signed number in base units."""
    text = cell.strip()
    for symbol in ("$", "€", "£", ","):
        text = text.replace(symbol, "")
    text = re.sub(r"[¹²³⁴⁵⁶⁷⁸⁹⁰*†‡]+$", "", text).strip()  # trailing footnote markers
    if text in DASHES:
        return 0.0  # a dash in a statement usually means nil
    negative = text.startswith("(") and text.endswith(")")
    text = text.strip("()").strip()
    try:
        value = float(text)
    except ValueError:
        return None
    return (-value if negative else value) * scale


def score_cells(truth: dict, predicted: dict, scale: int = 1) -> dict:
    """Compare cells keyed by (row header, column header). Exact match or it doesn't count."""
    exact = sign_flips = missing = 0
    for key, true_raw in truth.items():
        expected = normalise(true_raw, scale)
        got = normalise(predicted.get(key, ""), scale)
        if got is None:
            missing += 1
        elif got == expected:
            exact += 1
        elif expected is not None and abs(got) == abs(expected):
            sign_flips += 1
    return {
        "cell_accuracy": round(exact / len(truth), 3),
        "sign_flips": sign_flips,
        "missing": missing,
    }


truth = {
    ("Revenue", "FY2025"): "3,180",
    ("Revenue", "FY2024"): "2,947",
    ("Net income (loss)", "FY2025"): "(412)",
    ("Net income (loss)", "FY2024"): "118¹",
}

predicted = {
    ("Revenue", "FY2025"): "3,180",
    ("Revenue", "FY2024"): "2,947",
    ("Net income (loss)", "FY2025"): "412",   # brackets read as punctuation
    ("Net income (loss)", "FY2024"): "1181",  # superscript read as a digit
}

print(score_cells(truth, predicted, scale=1_000_000))
# {'cell_accuracy': 0.5, 'sign_flips': 1, 'missing': 0}

Half the cells are wrong, one of them has the opposite sign, and on screen the table looks absolutely fine. Fifty of your ugliest pages scored like this will tell you more than any leaderboard on the internet.

Summary and a Useful Cheat Sheet

If your problem is... Try this first Then compare with
Tables you need to defend to an auditor Docling granite-docling-258M
You want the smallest model with finance-table evidence granite-docling-258M Docling
Notes and tables that span several pages MinerU2.5-Pro PaddleOCR-VL-1.5
Skewed, warped or old scans PaddleOCR-VL-1.5 GLM-OCR
You need coordinates and table HTML for a review UI dots.ocr Docling
A huge archive you need to sort cheaply first LightOnOCR-2-1B Then route tables to Docling or MinerU
Running all of the above without seven pipelines SIE —

What This List Doesn't Cover

In the spirit of honesty, a few things this article deliberately leaves out:

  1. Turning tables into structured fields. Reading a table correctly is step one. Mapping "Net income (loss), FY2025" into your schema, with the right period and scale, is a separate job for extraction models, and it deserves its own article.
  2. Independent benchmarks. Almost every number here comes from the model's publisher. They're useful for building a shortlist, not for crowning a winner.
  3. Your documents. No ranking, including mine, knows what your ugliest PDF looks like. Only a bake-off does.

Conclusion

There's a Swahili proverb that sums up this whole article: Mtaka cha mvunguni sharti ainame. Whoever wants what's under the bed has to bend down.

There's no shortcut to the right OCR model for financial tables. Leaderboards will get you a shortlist, but you still have to bend down, pull out your ugliest PDFs and see who actually reads the brackets correctly.

My shortlist for you: parse XBRL and HTML first, and triage your PDF pages before anything touches a GPU. For the tables that are left, test Docling and granite-docling-258M first, bring in MinerU2.5-Pro or PaddleOCR-VL-1.5 for tables that span pages or scans that have seen better days, and use LightOnOCR-2-1B to sort a big archive cheaply.

And to make that bending down a lot less painful, run the whole bake-off on SIE. Docling and LightOnOCR are a per-page API call on SIE Cloud, the rest are in the self-hosted catalog behind the same extract method, and the winner can go straight into production, on SIE Cloud or inside your own VPC, without anyone rewriting a thing.

Final Thoughts & Conversation

The most surprising thing about this space right now is how good the open options have become. There are commercially licensed models with real table evidence behind them, some small enough to run on a laptop. The hard part has shifted from finding a capable model to proving which one works on your documents, and that's a much better problem to have.

What's the worst financial table you've ever had to extract? A note that ran for four pages, a scale line that quietly multiplied everything by a million, or brackets that vanished into thin air? Which model finally got it right? I'd love to hear your horror stories in the comments!

A Quick Side Note:

I regularly write about backend performance, AI infrastructure, document pipelines and real-world engineering trade-offs. If you enjoy deep dives into systems that fail loudly instead of quietly getting things wrong, I'd love to connect and chat!

You can find me on LinkedIn, follow my latest technical thoughts on Twitter/X, or check out my open-source projects and previous articles on GitHub.

I hope this was useful information for you. See you in my next article!

來源:DEV Community · dev.to