Papaya PDF

The engine

Papaya PDF is built on a PDF engine we wrote ourselves, in Rust. That is an unusual amount of work for a browser app, and this page explains what it buys you.

Open Papaya PDF Read the blog

Why write an engine instead of assembling libraries

There is a well-worn way to build a PDF tool for the browser: take a viewer library to read the file, take a writer library to produce a new one, and glue them together. It works, and it gets you a merge tool or a page rotator in a weekend.

It stops working the moment someone wants to change a sentence. The viewer gives you glyphs at coordinates; the writer wants to draw new text. Nothing in between knows that those glyphs were a paragraph, which font drew them, whether that font contains the letters you just typed, or what the line should look like once it rewraps. So the usual answer is to cover the old text with a patch and paint replacement text on top. That is why edited PDFs so often come back with a shifted font, text that no longer selects properly, or a page that has quietly become a picture.

Reading, editing, fonts, layout, and export have to agree with each other. Getting them to agree is the engine.

What that makes possible

Editable layout, recovered A PDF stores positioned drawing instructions, not paragraphs. The engine reconstructs lines, blocks, tables, and lists from geometry and the document's own structure, so there is something real to edit.
The document's own fonts Edits reuse the embedded font where the glyphs exist, and fall back on a metric-matched face where they don't, instead of substituting a lookalike and hoping.
Edits that stay text Changing a word rewrites the text, so the exported file still selects, searches, and copies. Nothing is flattened into an image to hide a change.
The rest of the file preserved Export round-trips the parts of the document it isn't changing rather than rebuilding a thin copy, so form fields, fonts, and structure survive the trip.

One engine, two apps

The same Rust core runs the web app and the Mac app. They are not two implementations kept loosely in sync by hand — the browser reaches it through WebAssembly and the Mac app links it natively, but the code deciding how a paragraph wraps or where a caret lands is one body of code.

We hold that honest with a parity suite. Both bridges replay a shared corpus of fixture documents against shared golden results, and the build fails if the two ever disagree. It has already caught differences a human would not have: a font probe that made bold text frame slightly taller in the browser than on the Mac, which is exactly the sort of thing that rots quietly for a year in a codebase with two implementations.

How we know it works

PDF is a format where confidence has to be earned against real files, because the specification permits far more than any single producer emits. Right now the engine carries 1,292 tests across its six crates, alongside the cross-bridge parity suite and a fixture corpus that grows every time a real document breaks something.

That last part is the actual work. Most of what the engine knows, it learned from a file that did something we hadn't anticipated: fonts keyed by an internal glyph index rather than the identifier you would expect, tables whose grid lines don't align with their cells, source text that had to stop being re-editable once it became part of a table. Each one was a bug someone hit, and each fix is a class of document that now behaves. We expect to keep finding them for a long time, and that is the point — it is not a phase that ends, it is the thing that compounds.

What it doesn't do yet

Being specific about the limits seems more useful than claiming there aren't any.

Text-box shadows and list markers are flattened into vector shapes on export rather than round-tripping as live, re-editable structure — they look right in the exported file, but reopening it gives you shapes instead of a list. Layout analysis can still mistake a strongly aligned list for a table. And the models that run entirely on your device are smaller and less accurate than the hosted ones, which the privacy page covers in detail.

If you have a PDF that breaks something, we would genuinely like to see it. The awkward files are where the engine gets better.

Read more