Spaces:
Running on Zero
Running on Zero
File size: 16,153 Bytes
b6fa86a c43fc2d b6fa86a c43fc2d b6fa86a c43fc2d b6fa86a c43fc2d 62849e3 c43fc2d 62849e3 c43fc2d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 | ---
title: docparse
emoji: π
colorFrom: indigo
colorTo: gray
sdk: gradio
sdk_version: 5.49.1
python_version: "3.12"
app_file: app.py
pinned: false
license: mit
short_description: Extract text, tables and structure from PDF, image, Office
models:
- Kedhareswer/docparse
---
# docparse: a very small document parser
- **Try it:** [huggingface.co/spaces/Kedhareswer/docparse](https://huggingface.co/spaces/Kedhareswer/docparse) (drop a PDF, image, Word, Excel, CSV or PowerPoint file)
- **Weights and code:** [huggingface.co/Kedhareswer/docparse](https://huggingface.co/Kedhareswer/docparse): `artifacts/docparse/ocr.pt` (6.0 MB), the `docparse` package, benchmark reports and tests
- **Source:** [github.com/Kedhareswer/ML-AI](https://github.com/Kedhareswer/ML-AI) (`reasoning_harness/`)
`docparse` extracts **everything** from a document (text, headings, paragraphs,
lists, tables, sheets, formulas, headers/footers, metadata and figure positions)
and returns it as JSON, Markdown or plain text, with reading order preserved.
| Input | How it is read | Exact? |
|---|---|---|
| PDF with a text layer | Words and fonts from the PDF; layout analysis groups them into headings, paragraphs, lists, columns and tables | Text is exact |
| Scanned PDF pages (no text layer) | Rendered at 200 dpi, then the OCR pipeline below | OCR |
| Images: PNG, JPG, TIFF (multi-page), BMP, WEBP, GIF | OCR pipeline | OCR |
| Word `.docx` | Document structure: styles β headings/lists, tables (merged cells), text boxes, headers/footers, properties | Exact |
| Excel `.xlsx` | Every sheet: values, formulas, merged ranges, dates, separate tables split on empty rows/columns | Exact |
| CSV / TSV | Delimiter and encoding sniffed | Exact |
| `.doc`, `.rtf`, `.odt`, `.xls`, `.ods`, `.ppt`, `.pptx`, `.odp` | Converted with LibreOffice (if installed), then parsed as above | Exact |
The design choice: **a model is only used where one is needed.** Born-digital
files already contain their text; reading their structure is exact and much
faster than any network. The neural network reads pixels: images, scans and
(optionally, `--ocr-images`) pictures embedded inside documents.
## The model
A CRNN line recognizer trained **from scratch** (no pretrained weights) on a
4-core CPU:
- **1,484,357 parameters, 6.0 MB** (`artifacts/docparse/ocr.pt`): 6 convolution
layers, a 2-layer bidirectional LSTM and CTC decoding.
- Input: a grayscale text line, 32 px high, any width. Output: the text plus a
confidence (mean probability of the emitted characters).
- Characters: printable ASCII plus `β’ β¬ Β£ Β° Β©`. Typographic quotes, dashes and
accented letters are folded to ASCII (`Γ©` β `e`).
- Training: 480,000 synthetic lines (4 rounds of 120,000 freshly rendered
lines), 27 fonts (DejaVu, Liberation, FreeFont, Inter, JetBrains Mono),
text from app reviews and medical abstracts in this repository, plus
generated numbers, prices, dates, emails, URLs, IDs and list items. Lines are
degraded like scans and screenshots: blur, low resolution, noise, JPEG,
rotation, uneven light, inverted colours and table-rule remnants. Training
took 80 minutes.
Everything around the network is classical image processing (numpy only):
adaptive binarization (dark-on-light and light-on-dark), speck removal,
deskew, ruled-table detection from long horizontal/vertical strokes, frame
and outline removal, recursive XY-cut for columns and lines, then grouping
lines into headings (by x-height), paragraphs (by line spacing), list items,
and borderless tables (rows whose gaps line up).
## Results
All numbers are from `docparse bench` (`benchmarks/docparse/report.json`).
CER = character error rate (edit distance / reference length).
**1. Single lines.** Test fonts **Carlito and Caladea were never used in
training** (they are metric clones of Calibri and Cambria, the Office
defaults), and the test sentences come from a separate corpus.
| Test set | Lines | CER | Word error | Lines exactly right |
|---|---:|---:|---:|---:|
| Held-out fonts, clean | 2,000 | **0.27%** | 2.3% | 93.3% |
| Held-out fonts, degraded | 2,000 | **0.93%** | 6.0% | 84.8% |
| Training fonts, unseen text, degraded | 2,000 | 0.24% | 1.2% | 95.6% |
| Untrained network (same architecture), degraded | 500 | 99.7% | 99.8% | 0% |
About 3 ms per line on CPU.
**2. Synthetic A4 pages** (held-out fonts and text; titles, section headings,
paragraphs, bullet lists, ruled tables, key/value tables, two-column sections).
"Scanned" pages are rotated up to Β±2Β°, blurred, noised and JPEG-compressed.
| | Clean (40 pages) | Scanned (40 pages) |
|---|---:|---:|
| Page CER, in reading order (mean / median) | 0.32% / 0.00% | 1.65% / 0.19% |
| Word F1 (order-free) | 98.9% | 97.7% |
| Headings found | 86.7% | 85.7% |
| Tables found with the right rows Γ columns | 100% | 100% |
| Table cells exactly right | 98.5% | 90.2% |
| Seconds per page (CPU) | 0.9 | 1.3 |
A few hard pages pull the scanned mean up; the median page is almost perfect.
Missed headings are mostly short numbered section titles ("1. Results") read
as list items or paragraphs: headings are detected by letter size, not by
boldness.
**3. Real pages from this repository.** 40 pages of the three PDFs in this
repo (two Jupyter notebook exports and a slide deck) were rendered to
150 dpi images and read **from pixels only**. Each PDF's own text layer is the
reference.
| PDF | Pages | Median CER (reading order) | Median word F1 |
|---|---:|---:|---:|
| Customer Behavior Analysis (notebook) | 15 | 0.7% | 92.5% |
| Credit Card Fraud Detection (notebook) | 11 | 2.3% | 94.8% |
| PPT.pdf (slides with graphics) | 14 | 32.3% | 82.5% |
| **All** | **40** | β | **91.9%** (mean 86.3%) |
Most slide-deck error is reading order: slides scatter text boxes, and the
PDF's own order differs from top-to-bottom reading. Text inside charts (axis
labels, tick numbers) is also read as text. The weakest notebook pages are
mostly plots with a few short labels.
## ParseBench (LlamaIndex)
[ParseBench](https://huggingface.co/datasets/llamaindex/ParseBench)
([paper](https://arxiv.org/abs/2604.08538), [code](https://github.com/run-llama/ParseBench))
is LlamaIndex's public benchmark for document parsers: **2,078 real enterprise
pages** (insurance filings, financial reports, government documents, papers),
1,211 documents and 169,011 human-verified test rules in five dimensions.
Scoring is deterministic rule matching, with no LLM judge.
docparse was run on the **full** dataset with the official harness
(parse-bench 1.0.4) on a 4-core CPU: all 2,078 pages, 0 failures, median
0.5 s per page. The comparison rows are the "Open Source - Local" entries of
ParseBench's published leaderboard.
| Parser | Overall | Tables | Charts | Content faithfulness | Semantic formatting | Visual grounding |
|---|---:|---:|---:|---:|---:|---:|
| PyMuPDF4LLM | 53.5 | 72.0 | 2.2 | 79.3 | 52.0 | 61.9 |
| Warp Ingest | 40.2 | 57.5 | 7.0 | 70.8 | 45.8 | 19.8 |
| LiteParse (no OCR) | 36.9 | 42.4 | 1.8 | 70.0 | 38.5 | 31.8 |
| **docparse** | **36.2** | **42.1** | **1.0** | **72.5** | **35.5** | **30.0** |
| OpenDataLoader | 29.4 | 35.2 | 0.9 | 66.1 | 34.1 | 10.8 |
| pdf-inspector | 26.6 | 26.6 | 5.3 | 56.1 | 35.1 | 9.9 |
| MarkItDown | 18.6 | 15.8 | 2.0 | 64.5 | 0.9 | 9.9 |
| PyMuPDF (text) | 16.0 | 0.0 | 0.0 | 68.3 | 1.0 | 10.9 |
| pypdf | 14.9 | 0.0 | 0.0 | 62.5 | 0.9 | 10.9 |
Overall is the mean of the five dimensions, as on the official leaderboard.
For scale: the top of the full leaderboard (cloud services and multi-billion
parameter vision-language models) reaches 75β90.
**What this says honestly**
- docparse is mid-table among local open-source parsers: ahead of pypdf,
PyMuPDF, MarkItDown, pdf-inspector and OpenDataLoader; just behind LiteParse
(36.9); behind Warp Ingest and clearly behind PyMuPDF4LLM.
- Its **content faithfulness (72.5)** is second among local parsers. It is
strongest on simple (89.0) and multi-column (76.5) pages, and weakest on
handwriting (27.4), scans (56.1) and non-English text (58.8). That matches the
OCR model's limits: printed Latin text only.
- **Charts (1.0):** ParseBench wants each chart's data points as a table.
docparse finds charts and keeps their labels but does not convert them to
data. No local parser scores above 7.
- **Tables (42.1)** are limited by pdfplumber's ruled-grid detection on
borderless financial tables; 32.3 on "hard" tables.
- **Formatting (35.5):** bold 29%, italic 23%, superscript 59%, subscript 57%
of rules pass. Underline, strikeout, highlight, LaTeX and code blocks are not
produced (0%).
- **Visual grounding (30.0):** paragraph boxes and reading order are decent;
figure regions and box granularity often differ from the annotation.
**What the benchmark changed in docparse.** The first full run scored 33.66; the final run scored 36.2.
Its failures led to fixes that are now part of docparse itself:
| Change | Effect |
|---|---|
| Bold/italic from PDF font names, `<sup>`/`<sub>` from glyph size and baseline | Semantic formatting 24.0 β 35.5 |
| Running header/footer and printed page-number detection | Header rules 0 β 9.9%, footer rules 0 β 12.2%; content faithfulness 71.8 β 72.5 |
| Rotated margin text (e.g. arXiv stamps) kept out of the reading flow | It no longer turns body text into a fake table |
| Vector charts and diagrams detected as figures, their labels attached | Visual grounding 28.9 β 30.0 |
| Rows stacked in one cell of rule-less tables split into records | Correct rows in insurance rate tables |
| A malformed PDF link annotation no longer fails the whole page | 9 failing pages β 0 |
Reproduce (parse-bench needs Python 3.12+):
```bash
pip install "parse-bench[fast]"
python scripts/parsebench_docparse.py download # ~600 MB
python scripts/parsebench_docparse.py run docparse --max_concurrent 3
python scripts/parsebench_summary.py output/docparse leaderboard.csv
```
`docparse/parsebench.py` registers the pipeline through ParseBench's public
extension API. Each page is sent as Markdown with HTML tables, page
header/footer fields, and layout boxes in ParseBench's canonical classes.
Aggregate results are in `benchmarks/parsebench/report.json`.
## ExtractBench (LlamaIndex): small local check
[ExtractBench](https://github.com/run-llama/ExtractBench) scores **schema-guided
extraction**: a document plus a JSON Schema in, schema-valid JSON plus evidence
boxes out. A parser cannot do this alone; an LLM fills the schema. So docparse
was tested as the **parse stage** of a two-stage pipeline (as ExtractBench's own
parse β LLM baselines are), against plain pypdf text, with everything else fixed:
the same model (Qwen2.5-3B-Instruct, 4-bit, llama.cpp on 4 CPU cores), the same
prompt and 12,288-token context budget, and the official scorer.
| Parse stage (6-document test set) | Value F1 | Precision | Recall |
|---|---:|---:|---:|
| **docparse** | **35.8** | 63.8 | 32.5 |
| pypdf | 32.9 | 66.3 | 29.8 |
docparse helped most on a scanned document (Value F1 19.6 vs 8.0, from OCR) and
an earnings deck with tables (35.0 vs 25.0). It lost on the 66-page document
(1.4 vs 5.2), where both arms could send only the first few pages.
**Read this carefully.** This is 6 documents, so a 2.9-point gap is not
statistically meaningful. Absolute scores are far below the leaderboard (top
systems score ~96), because a 3B model with a 12k context must cut long documents
(up to 55 of 66 pages dropped) and sometimes even shorten the schema. Grounding is
0 for both arms because this pipeline does not return evidence boxes yet. A real
ExtractBench number needs a strong model over all 370 documents (ExtractBench
estimates $10 to $1,677 per full run, depending on the model). Per-document
details are in `benchmarks/extractbench/report.json`.
Reproduce: start an OpenAI-compatible server (`python -m llama_cpp.server --model
qwen2.5-3b-instruct-q4_k_m.gguf --n_ctx 12288 --port 8088`), then
`DOCPARSE_LLM_URL=http://127.0.0.1:8088 python scripts/extractbench_docparse.py run
docparse_extract_local --test` (and `pypdf_extract_local`).
**Structure parsers** (PDF text layer, DOCX, XLSX, CSV) are tested for exact
output in `tests/test_docparse.py`.
## Use it
```bash
pip install -e '.[docs]' # torch, numpy, pillow, pdfplumber, pypdfium2, python-docx, openpyxl
docparse extract report.pdf # Markdown to stdout
docparse extract scan.png --format json -o scan.json
docparse extract invoice.pdf --ocr always # OCR every page, even with a text layer
docparse extract deck.docx --ocr-images # also read text inside embedded pictures
docparse extract *.xlsx --format md -o out/ # many files -> a directory
docparse ui # browser UI on http://127.0.0.1:7862
```
```python
from docparse import extract
from docparse.extract import to_markdown
doc = extract("statement.pdf") # dict: source, format, metadata, pages, blocks, sheets, warnings
for b in doc["blocks"]:
print(b["type"], b.get("page"), b.get("text") or b.get("rows"))
print(to_markdown(doc))
```
Each block has a `type` (`heading`, `paragraph`, `list_item`, `table`,
`figure`, `header`, `footer`), a `source` (`text-layer`, `structure` or
`ocr`), and where meaningful a `page`, `bbox`, `level`, `sheet`, `range` and
an OCR `confidence`. Excel sheets also list formulas, merged ranges, charts and
images under `sheets`.
## Online demo
The [Space](https://huggingface.co/spaces/Kedhareswer/docparse) runs the same
`docparse ui` server and weights. Files up to 40 MB, one at a time. LibreOffice
is installed there, so `.doc`, `.rtf`, `.odt`, `.xls`, `.ods`, `.ppt`, `.pptx`
and `.odp` work too. It runs on Hugging Face's free ZeroGPU hardware with all
inference on CPU (the app registers an unused `@spaces.GPU` function because
ZeroGPU will not start without one). Measured there: Word, Excel and CSV files
about 1 s, an 11-page text-layer PDF 3.5 s, a 14-page slide PDF 13 s, a page
image read by OCR 2.5 s. Uploaded files are written to a temporary folder for
the extraction and deleted right after. The Space build is
`scripts/make_hf_docparse.sh`.
## Train or fine-tune your own
```bash
python scripts/build_doc_corpus.py # rebuild the text corpora
docparse train-ocr --run runs/ocr --rounds 4 # ~80 min on 4 CPU cores
docparse train-ocr --run runs/ocr --rounds 6 --resume # continue / extend training
docparse bench --model runs/ocr/ocr.pt --output runs/ocr-bench
```
To specialise it (your fonts, your vocabulary, e.g. invoices in one
typeface), add font files to `TRAIN_FONTS` and text to `docparse/data/corpus_train.txt`
in `docparse/ocr/render.py`, then train or resume. A checkpoint is written
after every round.
## Limits
- **Printed text only.** No handwriting. Latin script only (ASCII plus a few
symbols); accents are dropped and other scripts are not read.
- Tables: ruled grids and borderless tables with aligned columns are found.
Cells that span several rows or columns are kept in the first cell. Very
irregular tables may come out as text.
- Reading order is top-to-bottom, columns left to right. Magazine layouts,
slides with scattered boxes, sidebars and text running around pictures can
be read out of order.
- Pictures are reported as `figure` blocks with a position. Charts are not
converted to data.
- Photos of documents taken at an angle (perspective), curved pages and
very low resolution (text under about 10 px high) are not handled well.
- Born-digital PDFs are only as good as their text layer: some PDFs embed
fonts without a Unicode mapping. Use `--ocr always` for those.
- Besides ParseBench, the benchmarks are synthetic pages and three real PDFs from
this repository. No direct comparison with Tesseract was run here. |