DEV Community

Jasper | PDFluent
Jasper | PDFluent

Posted on

My Journey so far (beating Docling & PyMuPDF4LLM)

SDK 1.2 of PDFluent went out on 22 October. That's about nine months of work, by me, a solo founder in the Netherlands.

The release post has the changelog and the numbers: https://pdfluent.com/blog/sdk-1-2-0/. This post is about the other stuff, the things that never make it into release notes.

how it started

At the start I had no plan to write a PDF engine. The plan was lazier. There are a lot of good open source PDF tools out there, and I thought I could glue them together into one complete PDF product, call it done, and get to the part where people actually use it.

That lasted [JASPER: how long the glue phase lasted, and the specific tool or bug that made you stop gluing and start writing]. After that I started writing most of the core myself. Not all of it. Some parts are forks of existing crates, and I'm glad those crates exist. But most of the core.

The first thing I wanted to do properly was XFA. If you've never run into XFA, lucky you. It's Adobe's old dynamic form format, version 3.3, and it still lives inside tax and government systems where nobody is about to replace it. I wanted it working in Rust, properly. [JASPER: one real XFA moment, like the first form that calculated correctly, or the part of the spec that cost you a week]

From there it kept growing, which is what happens when you're one person and nobody tells you to stop. It became an SDK in Rust with bindings for Python, Node.js, .NET, Java and WebAssembly. There's a free desktop PDF editor built on the same engine. The SDK is AGPLv3 or a commercial licence. Then it grew sideways into extraction: getting text and structure out of PDF and Office files, so layout, tables, Markdown, the stuff people want to feed into LLMs now. That's when I started measuring against Docling and pymupdf4llm, because those were the two I wanted to beat.

the things I got wrong

I had written "pure Rust" everywhere. Website, README, probably in my sleep too. Then I found a C library in the build. QuickJS, for running form scripts. [JASPER: how you found out, e.g. a dependency tree, a licence scan, a build on a new machine] So it wasn't pure Rust, and I had been saying it was, in public. I pulled the claim from the website. In 1.2 form scripts run in a WebAssembly sandbox, and the copy says "written in Rust" now, which is true and boring, and I'll take that.

The benchmarks were worse, in a way. My first numbers came from the same half of the data I had been tuning on. Of course they looked good. I was grading my own homework with the answer sheet open next to it. I threw those numbers out and replaced them with results on a held-out half, data I don't touch while building.

Then the tests. I found functions that had been broken for months while tests for them existed the whole time. The tests were fine. Nothing in the CI pipeline ever ran them. A test file sitting in a folder feels nice and does nothing. So the rule now is boring: if a test doesn't run in CI, the work isn't done.

After the pure Rust thing I got a bit paranoid, which I think is healthy. Every number on the website now has an ID in a claims file. No ID, it doesn't ship. It slows me down. Fine.

where it is now

This is the part I'm a bit nervous to write. I believe PDFluent is now state of the art at extracting text from PDF and Office documents, layout included. That's my belief, and you should check it yourself.

Here are the numbers behind it. It's the average over 19 extraction measures on the holdout half, PDF only. PDFluent ran in balanced mode with the table model, which is still Preview.

PDFluent 0.721
pymupdf4llm 0.693
Docling 0.672
(Yes, I'm pretty proud of this result)

The harness is public, pdfluent-bench on GitHub (github.com/pdfluent/pdfluent-bench), so if you think I set it up in my own favour, go and look, and tell me where.

Traction is small. About 38 GitHub stars and a few hundred PyPI downloads a month. I'm not going to dress that up. After nine months, that's where it is.

If any of this is useful to you, use it, and let me know. Feedback, ideas, someone who wants to work on it together, all welcome. The full list of what changed in 1.2 is at https://pdfluent.com/blog/sdk-1-2-0/ if you want the dry version;-)

Top comments (0)