PDFs,
rebuilt from scratch.

A PDF engine written in Rust. It runs in the browser as 1.3 MB of WebAssembly.

Merge.

Bookmarks, links, form fields and tagging come along.

Split.

Any page range becomes its own file, in milliseconds.

Reorder and rotate.

Put pages in any order and turn them in 90° steps.

Compress.

Lossless repacking, JPEG recompression and font subsetting, on a worker pool.

Scroll

What is Quire

One engine for everything you do to a PDF file.

Merge, split, rotate, fill, sign, compress. Written from the PDF spec, with no borrowed PDF engine inside.

Small, and built to survive bad files.

Real-world PDFs are messy. Quire repairs broken cross-reference tables, tolerates sloppy syntax and reports what it fixed as warnings instead of throwing. It never panics on any input, and it enforces limits on file size, object count and decompressed data so a hostile file can’t take the tab down.

Every dependency is permissively licensed and checked in CI. The code was written without reading any copyleft PDF engine’s source.

// open, edit, save
using pdf = Pdf.open(bytes);
pdf.rotate("1-3", 90);
pdf.deletePages([5]);
const out = pdf.save({
  compress: true,
  images: { quality: 75, maxDpi: 150 },
});

// big jobs on a worker pool
await pdf.saveAsync({ images: true });

Organize

Merge, split, extract, reorder, rotate and delete. Merging keeps bookmarks, links, form fields and tagging.

Forms and markup

Fill fields, flatten them into the page, and add highlights, notes, ink, shapes and free text.

Sign

PAdES signatures, with optional RFC 3161 timestamps, saved as incremental updates so earlier signatures stay valid.

Compress

Lossless repacking, JPEG recompression with downsampling, font subsetting, and a worker pool for big files.

Images to PDF

One page per JPEG or PNG. JPEGs embed as they are, with EXIF orientation honoured.

Encrypted files

Opens password-protected documents and saves them unlocked.

By the numbers

Lighter in every way that matters on the web.

Quire
1.3 MB
MuPDF
10.4 MB

WebAssembly download, drawn to scale. About 8× smaller.

5 msTime to start the engineMuPDF takes 135 ms
2.5×Less memory on large files, at best542 MB vs 1,340 MB organizing a 270 MB scan. Extracting uses 2.2× less.
1,068Valid files from 1,190 damaged ones837 for MuPDF, on the project’s own malformed-file test (version 0.12.0)
3.4×Faster compression with workers100-page scan: 4.9 s on one thread, 1.5 s with the worker pool. 4.2× on a 500-page scan.

Performance

Faster to first result. Much faster on big files.

Measured against the MuPDF code that powers filemite today, on the same files, on the same Mac. Pick a case and replay it.

MuPDF
0 ms
Quire
0 ms
–

The replay runs the measured times at slowed speed. Nothing is simulated beyond that.

Quire vs MuPDF

Where it wins today, and where it doesn’t yet.

Every Quire output passed validation with the right page count and the same text. Lossless operations matched the original pixel for pixel in pdf.js, except on one file where every engine differed the same way.

AreaResultDetail
Start-up and downloadQuire ahead

1.3 MB and 5 ms, against 10.4 MB and 135 ms. This is most of the gap on small files.

Large documentsQuire ahead

Up to 3.7× faster once the engine is warm, and 2.2 to 2.5× less peak memory. In this test filemite’s MuPDF code also completed every large file.

Small files, warmEven

Extract, organize and merge land within about 8% of each other once the engine is loaded. Split is 1.35× faster in Quire.

Lossless file sizeQuire ahead

0.95× to 1.0× of MuPDF’s output, using Quire’s compressing save.

Damaged filesQuire ahead

Valid output from 1,068 of 1,190 deliberately damaged files, against 837. That is the project’s own test, not mine.

Lossy compression of real filesMuPDF ahead

filemite’s own pipeline reaches about 0.47 of the original size. Quire reaches about 0.59 at quality 75, and it is faster.

Scanned documentsMuPDF ahead

filemite turns black-and-white scans into 1-bit images: 1 MB from a 54 MB synthetic test scan. Quire produces 30 MB, because it doesn’t convert grayscale scans to 1-bit yet.

Rendering to pixelsNot included

Quire has no renderer. Its output is meant to be shown by stock pdf.js.

RedactionNot yet

Planned for version 1.1.

LicenceQuire ahead

MuPDF is AGPL. Quire uses permissive dependencies only. Its own package licence is still being decided.

  • Merging in filemite adds a bookmark per file. Quire keeps each file’s own bookmarks but has no option to add one per file.
  • On small text files Quire used more memory when merging (47 MB against 9 MB).
  • Tested in Node on one Mac, not in a browser. On small files, most of the first-use gap is engine start-up, not the work itself.
  • Large test files were generated by the project’s benchmark scripts. Real-world numbers come from 40 PDFs picked at random with a fixed seed from a public web crawl.

What’s next

Version 1 is complete. Here is what comes after.

Items come from the project’s decision log unless marked. The grouping into Next, Then and Later is an editorial reading, and no dates are promised.

Next

  • RedactionRemove content from pages for good, not just cover it up. Scoped for version 1.1.
  • A rendering workerQuire’s own renderer, in a separate worker. Today pdf.js draws Quire’s output.
  • Adoption in filemiteSwitch tools over one at a time, not all at once. That is the plan in the log.

Then

  • Smarter scan compressionSuggested by these benchmarks, not on the roadmap: 1-bit and palette image strategies to close the size gap on scans.
  • Speed on real-world filesOn the full test set including its hardest tier, lossless compress takes 1.35× MuPDF’s time at 0.946× the size. The log names that tier as where speed work should look.
  • More font subsettingSupport for CFF and Type 1 fonts, beyond TrueType.

Later

  • Re-encrypting on saveToday, decrypted documents are saved without encryption.
  • Content-stream rewritingDeeper lossless optimization of page content.
  • Beyond the webA C interface and Python bindings, built on the same engine.

How we measured

A comparison you can check.

  • filemite’s real MuPDF code ran unmodified against Quire 1.0.0 on identical inputs.
  • Each measurement ran in a fresh process, one at a time, with the two engines interleaved so drift hits both equally.
  • A result counted only if the output passed qpdf --check, had the right page count and kept the same text. Pixel comparison through pdf.js was reported separately: one file differed in every engine, and MuPDF’s merge of one more differed.
  • Quire’s compressing save was used, because it matches filemite’s own save options. Its default save writes uncompressed files that are about 9 to 15% larger.
  • Ratios are geometric means across files. Failures and invalid outputs are all reported, and none were dropped.