# LibPDF-js/core for TypeScript PDF Parsing, Signing, and Generation

> LibPDF-js/core is a TypeScript library for parsing, modifying, generating, encrypting, and signing PDFs in Node.js, Bun, and browsers. It targets document workflows that need more control than conventional PDF tools provide, but its beta status and missing verification features require careful evaluation.

**LibPDF-js/core** — A modern PDF library for TypeScript. Parse, modify, and generate PDFs with a clean, intuitive API.

- Repository: https://github.com/LibPDF-js/core
- Website: https://libpdf.dev
- Stars: 1,850 · Forks: 71
- Language: TypeScript
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/libpdf-js-core

## Why LibPDF-js/core Targets Production PDF Workflows

LibPDF-js/core addresses a specific gap in the JavaScript PDF ecosystem: applications that need to open imperfect documents, alter them, and apply document-level security or signatures through one TypeScript API. It is intended for developers building document automation, electronic-signature services, form processors, and server-side PDF workflows. The README says Documenso uses it in production, although the library is also labelled beta software and may change its APIs between minor versions.

The scope is wider than PDF rendering. According to the feature table, LibPDF supports parsing, creation, modification, encryption, digital signatures, form filling, form flattening, page merging and extraction, attachments, positioned text extraction, font embedding, images, and incremental saves. That combination suits pipelines that must preserve an existing document before adding fields, signatures, pages, or metadata.

The project draws distinctions from three existing types of tool. It describes PDF.js as excellent for browser rendering, but unsuitable when the workflow requires a server runtime. It says pdf-lib offers an approachable API but rejects some malformed documents. PDFKit is characterized as a generation library without parsing. LibPDF instead attempts to combine tolerant document loading, a `pdf-lib`-style API, and cryptographic document operations.

That positioning also explains why it is not a direct replacement for every PDF component. A renderer may still be needed to display pages, while LibPDF handles the underlying document model and file transformations.

## How LibPDF-js/core Parses and Rewrites PDF Structures

LibPDF exposes two API layers. The high-level layer contains document classes such as `PDF`, `PDFPage`, and `PDFForm`, which cover common tasks. The low-level layer provides `PdfDict`, `PdfArray`, and `PdfStream` for direct access to PDF object structures. This split lets an application stay at the page and form level while retaining an escape hatch for cases that require lower-level document manipulation.

Its most distinctive parsing decision is deliberate leniency. The README says LibPDF prioritizes opening real-world documents over strict specification compliance. When standard parsing fails, the library falls back to brute-force recovery by scanning the complete file and rebuilding its structure. That behavior is particularly relevant for documents exported through tools that produce inconsistent cross-reference data or malformed objects. It does not prove that every recovered document will preserve all original semantics, so recovery success should not be confused with full visual fidelity.

Incremental saves address a separate requirement. Instead of always rewriting a complete file, the library can append changes and preserve existing signatures, according to the feature table. This is important for signed-document workflows because wholesale reconstruction can invalidate earlier cryptographic relationships. Encryption support covers RC4, AES-128, and AES-256 under revisions R2 through R6.

Document operations follow a direct data flow. Input bytes are loaded into `PDF`, the object model exposes pages or forms, an operation returns a modified document or byte output, and `pdf.save()` serializes the result. The signing API applies the same pattern by accepting a signer and signing reason before returning a signed document.

## Installing LibPDF-js/core and Parsing the First Document

Install the published package with npm or Bun. The package is `@libpdf/core`, and its current repository version is `v0.5.2`.

```bash
npm install @libpdf/core
```

With Bun, the documented installation command is:

```bash
bun add @libpdf/core
```

A first use can parse an existing byte array and report its page count. The application must obtain `bytes` from its own input source; the README does not define a file-loading helper.

```typescript
import { PDF } from "@libpdf/core";

const pdf = await PDF.load(bytes);
const pages = pdf.getPages();

console.log(`${pages.length} pages`);
```

`PDF.load()` accepts the PDF bytes as an in-memory value. After loading, `getPages()` returns the document's page collection, and the example logs its length. This makes the library suitable for request handlers and queued jobs where input has already been uploaded or fetched.

Encrypted files use the same load operation with credentials. The exact password string is passed through the `credentials` option.

```typescript
const pdf = await PDF.load(bytes, { credentials: "password" });
```

The runtime requirements are specific: Node.js 20 or later, Bun, or a modern browser with Web Crypto. The README does not document compatibility with older Node.js releases or browsers without the required cryptographic facilities.

## Filling Forms and Applying PAdES Signatures

Form filling uses `PDF.getForm()` and a field-oriented `fill()` call. Boolean values represent controls such as checkboxes, while strings can populate text fields. The returned bytes come from `pdf.save()`.

```typescript
const pdf = await PDF.load(bytes);
const form = pdf.getForm();

form.fill({
  name: "Jane Doe",
  email: "jane@example.com",
  agreed: true,
});

const filled = await pdf.save();
```

Digital signing requires a signer implementation. `P12Signer.create()` loads a PKCS #12 byte value and its password, after which `PDF.sign()` applies it with a reason. The feature table lists PAdES B-B, B-T, B-LT, and B-LTA support.

```typescript
import { PDF, P12Signer } from "@libpdf/core";

const pdf = await PDF.load(bytes);
const signer = await P12Signer.create(p12Bytes, "password");

const signed = await pdf.sign({
  signer,
  reason: "I approve this document",
});
```

This distinction between signing and verification is essential. Signing a PDF does not mean LibPDF has checked an existing signature. Signature verification is explicitly listed as not implemented, with verification described as planned. An application that must establish whether a received signature is valid needs another component or another library. Likewise, certificate encryption is unsupported, although password encryption is available.

Form behavior has another boundary. JavaScript actions are ignored, so calculations and other actions embedded in a form do not execute. Flattening can bake fields into page content, but it should not be described as equivalent to running the document's action system.

## Generating Pages, Fonts, Images, and Composite Documents

New documents start with `PDF.create()`. A page can then be added at a chosen size, and page-level drawing methods add text and shapes. This is a direct generation model rather than a template engine. The README does not describe a layout system for flowing text around arbitrary regions.

```typescript
import { PDF, rgb } from "@libpdf/core";

const pdf = PDF.create();
const page = pdf.addPage({ size: "letter" });

page.drawText("Hello, World!", {
  x: 50,
  y: 700,
  fontSize: 24,
  color: rgb(0, 0, 0),
});

const output = await pdf.save();
```

The feature table says TTF and OpenType fonts can be embedded with subsetting, while JPEG and PNG images, including PNG alpha, are supported. TrueType Collections are not supported, so individual fonts must first be extracted from a `.ttc` file. JBIG2 and JPEG2000 images can pass through unchanged but are not decoded by the library. This distinction matters when an operation requires access to image pixels, processing, or rendering.

Multiple byte arrays can be combined through the static `PDF.merge()` method. The README shows three inputs, but does not document memory limits or streaming behavior.

```typescript
const merged = await PDF.merge([pdf1Bytes, pdf2Bytes, pdf3Bytes]);
```

Attachments, page splitting, metadata, positioned text extraction, and advanced object access are represented in the repository's example directories. Those areas are useful for document assembly, but the cleaned README provides no further behavioral guarantees for them.

## Recovery Trade-offs, Release Cadence, and License Boundaries

LibPDF's lenient recovery is its main parsing advantage and its main risk. A brute-force scan can turn an otherwise unopenable file into a usable object model, but it cannot by itself confirm that every object, appearance, action, or signature retains its intended meaning. Applications should compare recovered output with an expected reference, especially for regulated or legally sensitive documents. The README does not provide a conformance-test matrix for the full PDF specification.

The repository is not archived. Its last push was on 2026-10-16, and the listed releases include `v0.5.2` on 2026-10-07, `v0.5.1` on 2026-09-25, and `v0.5.0` on 2026-09-25. The README nevertheless calls the software beta and warns that APIs may change between minor versions. That combination suggests active release work, but consumers should account for API movement when pinning, upgrading, and maintaining internal abstractions around the package.

The package metadata declares the `main`, `module`, and `import` entry as `./dist/index.mjs`, with declarations at `./dist/index.d.mts`. It is an ES module and is marked as having no side effects. Browser support depends on Web Crypto.

The top-level project uses the MIT license. The README also states that `src/fontbox/` is licensed under Apache-2.0 because it derives from Apache PDFBox. Consumers reviewing distribution obligations should inspect `LICENSE.md` and `src/fontbox/LICENSE`. The npm package's `files` list publishes `dist`, `LICENSE.md`, and `README.md`, while the source directory retains its separate licensing statement in the repository.

## Conclusion

Engineers building document automation, form workflows, or PAdES signing services should evaluate LibPDF-js/core when lenient parsing and a unified TypeScript API matter. Projects that need signature verification, certificate encryption, or executed JavaScript actions should not adopt it for those jobs. Check compatibility with the required PDFs and Web Crypto environment first, then assess the beta API and the Apache-2.0 licensing boundary around `src/fontbox/`.

## FAQ

### Can LibPDF-js/core parse malformed PDF files?

Yes. The README says LibPDF falls back to brute-force recovery when normal parsing fails, scanning the file to rebuild its structure. It does not claim that recovered files always retain perfect visual or semantic fidelity.

### Does LibPDF-js/core verify existing digital signatures?

No. Signature verification is listed as not implemented. LibPDF supports PAdES signing profiles, but applications requiring validation of existing signatures need another solution.

### Which runtimes support LibPDF-js/core?

The documented runtimes are Node.js 20 or later, Bun, and modern browsers with Web Crypto.

## Sources

- [LibPDF-js/core on GitHub](https://github.com/LibPDF-js/core)
- [License: MIT](https://github.com/LibPDF-js/core/blob/main/LICENSE)
- [Project website](https://libpdf.dev)
- [README](https://github.com/LibPDF-js/core/blob/main/README.md)
- [Releases](https://github.com/LibPDF-js/core/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/libpdf-js-core
