# README

## Content

# Document Solutions for PDF JS (DsPdfJS)

[![npm](https://img.shields.io/npm/v/@mescius/ds-pdf)](https://www.npmjs.com/package/@mescius/ds-pdf)
[![types](https://img.shields.io/badge/types-included-blue)](https://www.npmjs.com/package/@mescius/ds-pdf)
[![dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)](https://www.npmjs.com/package/@mescius/ds-pdf?activeTab=dependencies)

[**DsPdfJS**](https://developer.mescius.com/document-solutions/javascript-pdf-api?utm_source=nuget&utm_medium=documentsolutions&utm_campaign=DsPdfJS) is a PDF library for JavaScript and TypeScript providing fast, memory-efficient PDF and image processing in browsers and on the server.

Powered by **WebAssembly**, it runs in modern browsers and in **Node.js, Deno, and Bun**, with **no runtime dependencies**. Its object model follows the PDF specification, giving programmatic access to document properties, pages, fonts, annotations, and forms, alongside high-level APIs for building documents with formatted text, graphics, and images.

---

## Installation

```bash
npm install @mescius/ds-pdf
```

## Quick start

In Node.js, this is the whole thing - the Wasm module is located automatically:

```javascript
import { connectDsPdf, PdfDocument, pushObjectManager, popObjectManager } from "@mescius/ds-pdf";
import { writeFileSync } from "node:fs";

if (!(await connectDsPdf())) throw new Error("Failed to initialize DsPdfJS.");

pushObjectManager();
try {
  const doc = new PdfDocument();
  const page = doc.pages.addNew();

  page.context.drawText({ text: "Hello, World!", fontSize: 20 }, 72, 72);

  writeFileSync("hello.pdf", doc.savePdf());
} finally {
  popObjectManager();
}
```

In the browser, add one line telling the library where the Wasm module lives - see
[Loading the WebAssembly module](#loading-the-webassembly-module) for the options.

---

## Evaluating without a license key

**DsPdfJS runs without a license key** in Node.js, Deno, and Bun, and in a browser served
from a local host name - `localhost`, `127.0.0.1`, `::1`, a `*.local` or other dot-less
host name, or `file://`. You do not need to contact sales to try it: install the package,
run the code above, and you will get a working PDF.

Keyless builds:

* stamp a watermark on generated output, and
* limit document loading to the **first 5 pages**.

A browser page served from any other host name **requires a key**. There is no watermarked
fallback there: `connectDsPdf()` rejects with `License Not Found`.

To remove the watermark and the page limit, and to run in a browser off localhost,
request a free 30-day key
(see [How to Get Trial Keys](https://developer.mescius.com/document-solutions/javascript-pdf-api/docs/licensing-and-redistribution?utm_source=nuget&utm_medium=documentsolutions&utm_campaign=DsPdfJS)) and apply it before connecting:

```javascript
import { DsPdfConfig } from "@mescius/ds-pdf";
await DsPdfConfig.setLicenseKey("YOUR_LICENSE_KEY");
```

A commercial license is required for production use.

---

## Loading the WebAssembly module

DsPdfJS ships the Wasm module in several forms so it can fit different build setups.
Pick **one** of the following.

### Option A: Embedded (simplest; recommended for bundlers)

The Wasm binary is inlined into a JavaScript module, so there is no separate asset to
copy, host, or resolve. This is usually the right choice for Vite, webpack, Rollup,
Next.js, and edge runtimes, where asset URLs are the most common source of friction.

```javascript
import { DsPdfConfig, connectDsPdf } from "@mescius/ds-pdf";
import { embeddedWasmFactory } from "@mescius/ds-pdf/wasm-embedded";

DsPdfConfig.wasmFactory = embeddedWasmFactory;
await connectDsPdf();
```

No `wasmUrl` needed. The trade-off is a larger JavaScript bundle, since the binary
travels base64-encoded inside it.

Importing the module registers the factory on its own too, but assign it explicitly as
shown above: the package is marked side-effect-free, so a bare
`import "@mescius/ds-pdf/wasm-embedded";` may be dropped by tree-shaking and leave no
module registered.

### Option B: External file with an explicit URL

Serve `DsPdf.wasm` as a static asset and point the library at it. This keeps your
JavaScript bundle small and lets the browser cache the binary separately.

```javascript
import { DsPdfConfig, connectDsPdf } from "@mescius/ds-pdf";

DsPdfConfig.wasmUrl = "/assets/DsPdf.wasm";
await connectDsPdf();
```

Copy the file into your served output as part of your build, for example from
`node_modules/@mescius/ds-pdf/assets/DsPdf.wasm`. A CDN or versioned URL works too:

```javascript
DsPdfConfig.wasmUrl = "https://cdn.example.com/v9.2/DsPdf.wasm";
```

### Option C: Automatic detection (Node.js only)

If neither `wasmUrl` nor `wasmFactory` is set, DsPdfJS looks for the module under
`node_modules/@mescius/ds-pdf/assets/` relative to the current working directory.
Convenient for scripts and tests; set the path explicitly for production, since the
lookup depends on the working directory. Auto-detection also prints a `console.info`
notice recommending an explicit path - set `wasmUrl` to silence it.

### Option D: Custom factory

For full control over how the binary is fetched - a private CDN, an authenticated
endpoint, a preloaded buffer:

```javascript
import { DsPdfConfig } from "@mescius/ds-pdf";
import { externalWasmFactory } from "@mescius/ds-pdf/wasm-external";

DsPdfConfig.wasmFactory = async () => {
  const wasmBinary = await (await fetch("https://cdn.example.com/DsPdf.wasm")).arrayBuffer();
  return externalWasmFactory({ wasmBinary });
};
```

`externalWasmFactory` takes either `{ wasmBinary }` or `{ wasmUrl }`. DsPdfJS calls
`wasmFactory()` without arguments, so fetch the binary inside your factory as shown.

> **One factory wins.** `wasmFactory` takes precedence over `wasmUrl`, and importing either
> `@mescius/ds-pdf/wasm-embedded` or `@mescius/ds-pdf/wasm-external` sets `wasmFactory` as a
> side effect of the import itself, not when the factory is first called. A stray import
> anywhere in the app therefore overrides an Option B `wasmUrl` set elsewhere:
> `wasm-embedded` quietly loads the embedded binary and your URL is ignored, while
> `wasm-external` makes `connectDsPdf()` return `false` (it is invoked with no arguments, so
> `externalWasmFactory` throws).

> Set `DsPdfConfig.verbose = true` to log which module was resolved and from where.

---

## Usage

### Browser (ES Modules)

```javascript
import {
  DsPdfConfig, connectDsPdf, PdfDocument, pushObjectManager, popObjectManager
} from "@mescius/ds-pdf";

DsPdfConfig.wasmUrl = "/assets/DsPdf.wasm";
// required off localhost, see "Evaluating without a license key":
// await DsPdfConfig.setLicenseKey("...");

export async function createDocument() {
  if (!(await connectDsPdf())) throw new Error("Failed to initialize DsPdfJS.");

  pushObjectManager();
  try {
    const doc = new PdfDocument();
    const page = doc.pages.addNew();

    page.context.drawText({ text: "Hello, World!", fontSize: 20 }, 72, 72);

    return doc.savePdf();   // Uint8Array
  } finally {
    popObjectManager();
  }
}
```

### Browser (UMD / plain script tag)

```html
<script src="node_modules/@mescius/ds-pdf/umd/ds-pdf.js"></script>
<script>
  async function createDocument() {
    // The UMD bundle exposes the global 'DocSol'
    DocSol.DsPdfConfig.wasmUrl = "/assets/DsPdf.wasm";
    // await DocSol.DsPdfConfig.setLicenseKey("...");   // required off localhost

    if (!(await DocSol.connectDsPdf())) throw new Error("Failed to initialize DsPdfJS.");

    const om = new DocSol.ObjectManager();
    try {
      const doc = new DocSol.PdfDocument(om);
      const page = doc.pages.addNew();

      page.context.drawText({ text: "Hello, World!", fontSize: 20 }, 72, 72);

      return doc.savePdf();
    } finally {
      om.dispose();
    }
  }

  createDocument()
    .then(pdfData => console.log("PDF created:", pdfData.length, "bytes"))
    .catch(err => console.error(err));
</script>
```

### Node.js (CommonJS)

```javascript
const { connectDsPdf, PdfDocument, ObjectManager } = require("@mescius/ds-pdf");
const { writeFileSync } = require("node:fs");

(async () => {
  if (!(await connectDsPdf())) throw new Error("Failed to initialize DsPdfJS.");

  const om = new ObjectManager();
  try {
    const doc = new PdfDocument(om);
    const page = doc.pages.addNew();

    page.context.drawText({ text: "Hello, World!", fontSize: 20 }, 72, 72);

    writeFileSync("hello.pdf", doc.savePdf());
  } finally {
    om.dispose();
  }
})();
```

---

## Memory management: ObjectManager

Many objects are backed by Wasm memory, which is not reclaimed by the JavaScript
garbage collector. Scope them with one of these patterns so intermediates are released:

```javascript
// Push / pop - implicit, scoped
pushObjectManager();
try { /* ... */ } finally { popObjectManager(); }

// Explicit instance
const om = new ObjectManager();
try { /* ... */ } finally { om.dispose(); }

// Decorator
@withObjectManager
async buildReport() { /* ... */ }
```

Objects created inside a scope are disposed when it ends. Do not hold references to
them past that point.

---

## Features

* Create, load, modify, save, and inspect PDF documents
* Support for modern PDF standards, including PDF/A and PDF/UA conformance levels
* E-signatures: PKCS#7 signing, trusted timestamps, and OCSP revocation responses
* Tagged PDF support via the document structure tree
* Export pages to raster or vector image formats
* Merge and split documents
* Draw text, shapes, paths, and images with high-level graphics APIs
* Text layout engine with wrapping, alignment, spacing, tabs, RTL (including Kashida), and vertical writing
* Font embedding and subsetting
* Raster and vector images: PNG, JPEG, SVG, SVGZ
* Create, edit, fill, and flatten AcroForm forms
* Annotations, links, text markup, and rich media
* Find and replace text with configurable search options and exact match positions
* Redaction that permanently removes sensitive content
* Encryption and security APIs
* Image processing: resizing, transforming, filtering, drawing, and bitmap generation
* TypeScript definitions included

## Compatibility

* **Environments**: Modern browsers; Node.js, Deno, Bun, other Node-compatible runtimes
* **Module formats**: ES Modules, CommonJS, UMD
* **TypeScript**: Type definitions included
* **Runtime dependencies**: None

## Notes

* Coordinates in PDF drawing contexts are page units (points, 72 per inch) by default, and can be changed.
* Coordinates in image drawing contexts are pixels (96 per inch).
* As of 9.1.1, the default angle unit for rotation transforms is **radians**, matching the HTML canvas API.

---

## Latest changes

## [9.2.0] - 14-Aug-2026
### Added

- E-signature support:
  - Signing documents with PKCS#7 signatures (the `Pkcs7Signer` and `Pkcs7SignerBase` classes). (DOC-7384)
  - Embedding trusted timestamps into signatures. (DOC-7411)
  - Requesting and embedding OCSP revocation responses. (DOC-7412)
  - Storing signature validation data in the Document Security Store (the `DocumentSecurityStore` class). (DOC-7469)
  - The `Signature` class, representing a signature already present in a document. (DOC-7470)
- Support for tagged PDFs: the document structure tree is now available through the `StructTreeRoot` and `StructElement` classes. (DOC-7575)
- Support for 3D annotations. (DOC-7681)
- Support for the PDF/A-4 conformance levels (`PdfAConformanceLevel.PdfA4`, `PdfA4e`, `PdfA4f`) and for PDF/UA-2 (the `Metadata.pdfUa` and `Metadata.pdfUaRev` properties). (DOC-7682)
- The `PdfDocument.outputIntents` property and the relevant classes. (DOC-7512)
- The `PdfDocument.viewerPreferences` property and the `ViewerPreferences` class. (DOC-7471)
- The `PdfDocument.pageMode` and `PdfDocument.pageLayout` properties. (DOC-7455)
- The `PdfDocument.pageLabelingRanges` property and the related page labeling classes. (DOC-7624)
- The `PdfDocument.getFonts()` method, which enumerates the fonts used in a document. (DOC-7729)
- The `PdfPage.annotationsTabsOrder` property. (DOC-7511)
- Support for saving linearized ("fast web view") PDFs via the new `SaveMode.Linearized` member. (DOC-7655)
- Support for Form XObjects, allowing reusable content streams to be created and drawn on pages. (DOC-7575)
- Support for custom annotation appearance streams. (DOC-7575)
- A JPEG2000 image decoder, so JPEG2000 images in PDF documents are now decoded. (DOC-7656)
- The missing properties of the classes derived from `ActionBase`, such as `ActionLaunch`, `ActionHide`, `ActionImportData` and `ActionSound`. (DOC-7654)
- The missing `CheckBoxField` members: `hasRadioButtonBehavior`, `getCheckedAppearanceStreamName()`, `getCheckedAppearanceStreamNames()`, `setCheckedAppearanceStreamName()` and `setCheckedAppearanceStreamNames()`. (DOC-7628)
- The missing `IccProfile` members: the `n` setter, the `range` property and the `metadata` property. (DOC-7675, DOC-7676)
- The `LineBreakingRules.WhiteSpace` mode, which breaks lines only at white spaces or mandatory break characters. (DOC-7651)

See `CHANGELOG.md` inside the package for the full change history.

## Resources

* [Demos](https://developer.mescius.com/document-solutions/javascript-pdf-api/demos/features?utm_source=nuget&utm_medium=documentsolutions&utm_campaign=DsPdfJS)
* [Getting Started Guide](https://developer.mescius.com/document-solutions/javascript-pdf-api/demos/getting-started?utm_source=nuget&utm_medium=documentsolutions&utm_campaign=DsPdfJS)
* [Documentation](https://developer.mescius.com/document-solutions/javascript-pdf-api/docs/overview?utm_source=nuget&utm_medium=documentsolutions&utm_campaign=DsPdfJS)
* [API Reference](https://developer.mescius.com/document-solutions/javascript-pdf-api/api/README?utm_source=nuget&utm_medium=documentsolutions&utm_campaign=DsPdfJS)
* [Licensing information](https://developer.mescius.com/document-solutions/licensing?utm_source=nuget&utm_medium=documentsolutions&utm_campaign=DsPdfJS) and [FAQ](https://developer.mescius.com/document-solutions/licensing?utm_source=nuget&utm_medium=documentsolutions&utm_campaign=DsPdfJS#how-to)
* [How to get trial keys](https://developer.mescius.com/document-solutions/javascript-pdf-api/docs/licensing-and-redistribution?utm_source=nuget&utm_medium=documentsolutions&utm_campaign=DsPdfJS)
* [Technical Support and Bug Reports](mailto:customerengagement@mescius.com)
* [DsPdfJS on GitHub](https://github.com/GrapeCity/DsPdfJS)

## License

Free to evaluate, see [Evaluating without a license key](#evaluating-without-a-license-key).

A commercial license is required for production use, and each purchase includes one year
of updates and support. Contact [MESCIUS Sales](mailto:us.sales@mescius.com) for pricing
and licensing options.

---

## Other Document Solutions products

* [Document Solutions for PDF .NET](https://developer.mescius.com/document-solutions/dot-net-pdf-api?utm_source=nuget&utm_medium=documentsolutions&utm_campaign=DsPdfJS)
* [Document Solutions for Word .NET](https://developer.mescius.com/document-solutions/dot-net-word-api?utm_source=nuget&utm_medium=documentsolutions&utm_campaign=DsPdfJS)
* [Document Solutions for Excel .NET](https://developer.mescius.com/document-solutions/dot-net-excel-api?utm_source=nuget&utm_medium=documentsolutions&utm_campaign=DsPdfJS)
* [Document Solutions for Excel Java](https://developer.mescius.com/document-solutions/java-excel-api?utm_source=nuget&utm_medium=documentsolutions&utm_campaign=DsPdfJS)
* [Document Solutions for Imaging .NET](https://developer.mescius.com/document-solutions/dot-net-imaging-api?utm_source=nuget&utm_medium=documentsolutions&utm_campaign=DsPdfJS)

**JavaScript viewers:**
[PDF Viewer and Editor](https://developer.mescius.com/document-solutions/javascript-pdf-viewer?utm_source=nuget&utm_medium=documentsolutions&utm_campaign=DsPdfJS) &middot;
[Data Viewer](https://developer.mescius.com/document-solutions/javascript-data-viewer?utm_source=nuget&utm_medium=documentsolutions&utm_campaign=DsPdfJS) &middot;
[Image Viewer and Editor](https://developer.mescius.com/document-solutions/javascript-image-viewer?utm_source=nuget&utm_medium=documentsolutions&utm_campaign=DsPdfJS)
