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.
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
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.