Lancement

Signature

Développement

Introducing LibPDF: The PDF Library TypeScript Deserves

Introducing LibPDF: The PDF Library TypeScript Deserves

23 janv. 2026

Building the PDF library we needed, so you don't have to.

At Documenso, we've spent the last few years building an open-source document signing platform. That means we've spent a lot of time working with PDFs.

And we have a confession: for too long, we were holding the ecosystem together with duct tape.

The Problem with PDF Libraries

When we started Documenso, we surveyed the JavaScript PDF landscape. Here's what we found:

  • pdf.js is Mozilla's battle-tested library, excellent for rendering. It handles the weirdest, most malformed PDFs you can throw at it. But it's read-only. You can display a PDF, but you can't modify it, sign it, or fill its forms.

  • pdf-lib has a beautiful TypeScript API. Genuinely well-designed, and our go-to for years. But when you're processing thousands of documents a day from every PDF producer imaginable, you start hitting edge cases. A quirky header here, a malformed xref there. Each one rare, but at scale, rare becomes routine.

  • pdfkit does generation only. No parsing at all.

None of these libraries could handle the full lifecycle of a document signing workflow: parse whatever PDF the user uploads, fill the form fields, add a signature, and save, all while preserving any existing signatures.

Our Journey Through the Workarounds

We wrote our own methods to handle the edge cases, more robust parsing routines that we'd call instead of pdf-lib's built-in versions. We added preprocessing steps to "clean up" problematic PDFs before processing. For digital signatures, we wrote our own Rust library with N-API bindings so we could call it natively from Node.js.

It worked. Mostly. But every few weeks, another customer would upload a PDF that broke something. And we'd patch again.

And then there were the feature requests we simply couldn't fulfill. Incremental saves, where you append changes to a PDF instead of rewriting it so existing signatures stay valid? Not possible. Certain encryption formats? Not supported.

We found ourselves telling customers "we can't do that" far too often, knowing that the only real fix would be to rethink everything from the ground up.

So we decided to build the library we actually needed.

Introducing LibPDF

Today, we're releasing LibPDF, a modern PDF library for TypeScript that actually works on real-world documents.

import { PDF, P12Signer } from '@libpdf/core';

// Load an encrypted PDF
const pdf = await PDF.load(bytes, { password: 'secret' });

// Fill form fields
const form = pdf.getForm();
form.getTextField('name').setText('Jane Doe');
form.getCheckBox('agree').check();

// Sign with a certificate
const signer = await P12Signer.create(p12Bytes, 'password');
const { bytes: signed } = await pdf.sign({ signer });

What makes LibPDF different?

Lenient parsing. We borrowed from pdf.js and PDFBox: when standard parsing fails, fall back to brute-force recovery. Scan the entire file. Rebuild the structure from scratch. Your users shouldn't have to know or care that their PDF has a quirky xref table.

Incremental saves. Modify a signed document, add another signature, save. All previous signatures remain valid. This is essential for multi-party signing workflows.

Native digital signatures. PAdES B-B through B-LTA, with full long-term validation support. Embed timestamps, OCSP responses, and CRLs. No more calling out to external signing services or maintaining Rust bindings.

Complete feature set. Encryption (RC4, AES-128, AES-256). Form filling and flattening. Merge and split. Text extraction. Font embedding with automatic subsetting. Image embedding (JPEG and PNG with alpha). Layer flattening.

TypeScript-first. Not "TypeScript-compatible," actually designed for TypeScript from day one. Full type inference, no any escape hatches, proper async patterns.

The Joy of Native Signing

Let us tell you how good it feels to finally delete our Rust signing library.

For years, we maintained a separate codebase just for PDF signatures. Rust code with N-API bindings, deployed to npm with platform-specific binary downloads. Every new Node.js version was a gamble. Every CI pipeline needed special handling for native compilation.

Now, signing is just TypeScript:

const signer = await P12Signer.create(certificateBytes, 'password');
const signed = await pdf.sign({
  signer,
  level: 'B-LTA',  // Long-term archival
  timestampServer: 'http://timestamp.example.com',
});

One dependency. One language. No compilation. No platform-specific binaries. It runs in Node.js, Bun, and the browser, all from the same code.

Standing on Shoulders

LibPDF wouldn't exist without the incredible work that came before us.

Hopding/pdf-lib — Andrew Dillon's library set the standard for what a TypeScript PDF API should feel like. LibPDF's high-level API is directly inspired by pdf-lib's intuitive design. If you've used pdf-lib, you'll feel at home.

Mozilla/pdf.js — The most battle-tested PDF parser in existence. pdf.js handles documents that would crash any other library. We studied its recovery strategies extensively, and our brute-force parser follows similar patterns.

Apache PDFBox — The reference implementation for "how do I actually implement this PDF feature?" PDFBox's comprehensive test suite and edge case handling taught us what production-ready PDF support looks like. Our entire font subsystem (src/fontbox/) is a TypeScript port of PDFBox's fontbox, maintained under the Apache 2.0 license.

To Andrew, the Mozilla team, and the Apache PDFBox contributors: thank you.

The Road Ahead

LibPDF is currently in beta. We're using it in production at Documenso, but we expect APIs to evolve as we learn from real-world usage.

Here's what we're working on:

  • Signature verification. We can create signatures, but not yet verify them. This is our next major milestone.

  • Annotation support. Comments, highlights, stamps, and other markup.

  • HTML-to-PDF. We believe there's a more modern approach to generating PDFs from HTML that doesn't involve standing up browser infrastructure.

  • PDF rendering. Eventually, we want LibPDF to be a complete PDF solution, including rendering to canvas or image formats.

Get Started

npm

Documentation: https://libpdf.documenso.com

GitHub: https://github.com/libpdf-js/core

Try It

If you've ever struggled with PDF parsing in JavaScript, we'd love for you to try LibPDF. Open your weirdest PDFs. Fill your most complex forms. Let us know what breaks.

And if you're building a document workflow (signing, form filling, template generation), we hope LibPDF becomes the foundation you've been looking for.

LibPDF is developed by Documenso, the open-source document signing platform.