FibrumPDF
Structured PDF extraction at 318 pages per second on CPU.
FibrumPDF turns digital PDFs into structured JSON or clean Markdown without a GPU, a model download, or a remote API. It preserves headings, paragraphs, lists, tables, reading order, bounding boxes, links, and inline formatting while keeping extraction native and parallel.
The native pipeline targets workloads that need structure without spending seconds on every page.
Performance, measured
The repository benchmark contains 512 documents from the
datalab-to/marker_benchmark dataset. On the documented Ryzen 7 4800H test
system, the current FibrumPDF build processes 318.2 pages/s, about 77x
PyMuPDF4LLM and 517x Docling in this benchmark.
| Extractor | Pages/s | Text score | Table TEDS | Table precision | Table recall |
|---|---|---|---|---|---|
| FibrumPDF | 318.23 | 87.31 | 0.783 | 0.661 | 0.590 |
| PyMuPDF4LLM | 4.15 | 86.54 | 0.778 | 0.647 | 0.554 |
| Docling | 0.62 | 91.13 | 0.821 | 0.796 | 0.738 |
PyMuPDF4LLM is the closest speed comparison. Docling scores higher on difficult tables and irregular layouts, but takes substantially longer.
Open the interactive report or see how the benchmark works.
Features
- Semantic blocks: headings, paragraphs, lists, code, figures, and tables.
- Layout metadata: page numbers and block, row, cell, and span bounding boxes.
- Inline formatting: bold, italic, monospace, strikeout, super/subscript, links.
- Two consumption modes: collect small documents or stream large ones page by page with bounded memory.
- Atomic output: failed extraction never leaves a half-written destination.
- Native wheels for Python 3.11+ on Linux x86_64, macOS arm64, and Windows x64.
Quick start
uv pip install fibrum-pdf
Extract JSON:
from fibrum_pdf import to_json
result = to_json("report.pdf", "report.json")
print(result.path)
for page in result: # streamed; the whole document is not loaded
print(page.markdown)
Write Markdown directly. Pages are converted incrementally and the destination is replaced atomically:
from fibrum_pdf import to_markdown
path = to_markdown("report.pdf", "report.md")
For smaller documents, collect typed Pydantic-backed objects:
pages = to_json("report.pdf").collect()
print(pages[0].markdown)
print(pages[0][0].type, pages[0][0].bbox)
The CLI exposes both formats:
uv run fibrum-pdf report.pdf report.json
uv run fibrum-pdf report.pdf report.md --format markdown
The output path is optional; it defaults to the PDF name with a .json or .md
suffix.
Output model
The JSON document is an array of pages. Each page contains a data array of
blocks:
[
{
"page": 1,
"data": [
{
"type": "heading",
"level": 1,
"bbox": [178.64, 84.50, 433.36, 102.55],
"font_size": 24,
"spans": [
{"text": "Quarterly report", "font_size": 0, "bold": true}
]
}
]
}
]
Tables add row and cell geometry; lists add typed items, markers, and indentation. See the public models for the complete schema.
How it works
MuPDF handles PDF interpretation. Go performs layout analysis, table detection,
classification, cleanup, and bounded parallel page processing. Python stays a
thin orchestration layer and exposes the native result lazily through ijson.
Text and vector edges are captured in one page pass. Ruled tables use line grids. Borderless tables are built from contiguous multi-column rows, with structural checks to reject prose columns, diagrams, and fragmented labels.
MuPDF access is serialized where required. Page processing uses bounded workers, so memory use depends on active pages rather than document length.
Known limits
FibrumPDF does not run OCR or extract embedded images. Scanned PDFs require another tool. Forms, spreadsheets, heavily layered pages, and unconventional visual layouts can also defeat the heuristic extractor.
Use the benchmark as a starting point, then test representative documents from your own workload before committing to an extraction stack.
Reproduce the benchmark
Benchmark reports are generated by the code in benchmark/. The
committed comparison uses 512 deterministic samples (--seed 0, one timing run)
on an AMD Ryzen 7 4800H; Docling used the machine's GTX 1650 Ti where applicable.
uv sync --extra benchmark
uv run python -m benchmark download \
--output benchmark/data --max-rows 512
uv run python -m benchmark run \
--dataset-path benchmark/data \
--output benchmark/results \
--max-rows 512 --runs 1
Per-document measurements, aggregate rows, and generated reports are committed
under benchmark/results/. Throughput varies by hardware;
the quality scores are the more portable comparison.
Development
Source builds require MuPDF 1.27. See BUILD.md for platform setup. Once the native dependency is available:
uv sync --extra dev
uv run ruff check .
uv run ruff format --check .
uv run pytest -q
cd go
go test ./...
go vet ./...
License
FibrumPDF is licensed under AGPL-3.0. MuPDF is also AGPL-licensed and is available under a commercial license from Artifex.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fibrum_pdf-1.2.0-py3-none-win_amd64.whl.
File metadata
- Download URL: fibrum_pdf-1.2.0-py3-none-win_amd64.whl
- Upload date:
- Size: 41.1 MB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
002fabcdcff3524810f79160aa5eb2d544f1cfa743a701dcc50113bbc7bbea9a
|
|
| MD5 |
3fd8fe83d8d64347aa3d8bf8a5dbcc85
|
|
| BLAKE2b-256 |
6b37648b17dfbb55d109906e7c25c2c2b9505c2c43ba693b3266b1cc5723ec5e
|
Provenance
The following attestation bundles were made for fibrum_pdf-1.2.0-py3-none-win_amd64.whl:
Publisher:
publish.yml on intercepted16/fibrumpdf
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fibrum_pdf-1.2.0-py3-none-win_amd64.whl -
Subject digest:
002fabcdcff3524810f79160aa5eb2d544f1cfa743a701dcc50113bbc7bbea9a - Sigstore transparency entry: 2194471515
- Sigstore integration time:
-
Permalink:
intercepted16/fibrumpdf@7d1cc41b0d449f54f4180822756d407d1a275338 -
Branch / Tag:
refs/tags/v1.2.0 - Owner: https://github.com/intercepted16
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7d1cc41b0d449f54f4180822756d407d1a275338 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fibrum_pdf-1.2.0-py3-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: fibrum_pdf-1.2.0-py3-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 79.7 MB
- Tags: Python 3, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
89da40f6ed51c248383813b37f23aab66c325578f0f18e04428f55f93ee6cd2e
|
|
| MD5 |
f8b558ba2d2adb1772d762001278bfb5
|
|
| BLAKE2b-256 |
2f1bff11d00deedb319a42082ed98edca4ad090d6822e93be788cedc49073b14
|
Provenance
The following attestation bundles were made for fibrum_pdf-1.2.0-py3-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:
Publisher:
publish.yml on intercepted16/fibrumpdf
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fibrum_pdf-1.2.0-py3-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl -
Subject digest:
89da40f6ed51c248383813b37f23aab66c325578f0f18e04428f55f93ee6cd2e - Sigstore transparency entry: 2194471492
- Sigstore integration time:
-
Permalink:
intercepted16/fibrumpdf@7d1cc41b0d449f54f4180822756d407d1a275338 -
Branch / Tag:
refs/tags/v1.2.0 - Owner: https://github.com/intercepted16
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7d1cc41b0d449f54f4180822756d407d1a275338 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fibrum_pdf-1.2.0-py3-none-macosx_15_0_arm64.whl.
File metadata
- Download URL: fibrum_pdf-1.2.0-py3-none-macosx_15_0_arm64.whl
- Upload date:
- Size: 80.6 MB
- Tags: Python 3, macOS 15.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9c498b2ae5604efb4bd5ce22f21878dbf78efce47168a27867ee3ef090c67e3c
|
|
| MD5 |
7437fa4d6a9c131b70883199566a5b2a
|
|
| BLAKE2b-256 |
fe010311e42b2bbf3cb6b25d4934540973604005ed235721ac95dd249d718fb4
|
Provenance
The following attestation bundles were made for fibrum_pdf-1.2.0-py3-none-macosx_15_0_arm64.whl:
Publisher:
publish.yml on intercepted16/fibrumpdf
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fibrum_pdf-1.2.0-py3-none-macosx_15_0_arm64.whl -
Subject digest:
9c498b2ae5604efb4bd5ce22f21878dbf78efce47168a27867ee3ef090c67e3c - Sigstore transparency entry: 2194471544
- Sigstore integration time:
-
Permalink:
intercepted16/fibrumpdf@7d1cc41b0d449f54f4180822756d407d1a275338 -
Branch / Tag:
refs/tags/v1.2.0 - Owner: https://github.com/intercepted16
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7d1cc41b0d449f54f4180822756d407d1a275338 -
Trigger Event:
push
-
Statement type: