[]
        
(Showing Draft Content)

README

Document Solutions for PDF JS (DsPdfJS)

npm types dependencies

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

npm install @mescius/ds-pdf

Quick start

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

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 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) and apply it before connecting:

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.

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.

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.

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:

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:

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)

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)

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

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:

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

License

Free to evaluate, see 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 for pricing and licensing options.


Other Document Solutions products

JavaScript viewers: PDF Viewer and Editor · Data Viewer · Image Viewer and Editor