npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mescius/ds-pdf

v9.2.1

Published

Create, edit, and sign PDFs in the browser and Node.js. WebAssembly based library.

Downloads

161

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.


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

Installation

npm install @mescius/ds-pdf

Quick Start

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

import { connectDsPdf, ObjectManager, PdfDocument } from "@mescius/ds-pdf";
import { writeFileSync } from "node:fs";

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

using om = new ObjectManager();

const doc = new PdfDocument(om);
const ctx = doc.newPageContext();
ctx.drawText({ text: "Hello, World!", fontSize: 40 }, 72, 72);
const pdfData = doc.savePdf();
writeFileSync("hello.pdf", pdfData);

const doc2 = PdfDocument.load(om, pdfData);
const page = doc2.pages.getAt(0);
page.context.drawText({ text: "JPEG export", fontSize: 30, foreColor: "Green" }, 72, 132);
page.context.drawRect(page.mediaBox, { lineWidth: 10, lineColor: "Red" });
const jpegData = page.saveAsJpeg();
writeFileSync("hello.jpeg", jpegData);

Save the above code as quick-start.mjs and run:

node quick-start.mjs

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.

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, or to run DsPdfJS in a browser on a non-local host name, request a free 30-day trial 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.

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.

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.


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

Latest changes

[9.2.1] - 10-Sep-2026

Added

  • Added the PdfPage.saveAsJpeg() method. (DOC-7829)

Changed

  • Loading a PDF document is no longer limited to the first 5 pages when the library is used without a license. (DOC-7871)

Fixed

  • Fixed JPEG2000 support issues. (DOC-7824)
  • Fixed an issue where the TypeScript types were not resolved for the recommended wasm-embedded import, caused by the order of the exports conditions. (DOC-7828)
  • Fixed an issue where the Node.js WASM auto-detection never ran when the package was loaded through an ESM import, so connectDsPdf() failed with ENOENT. (DOC-7837)
  • Fixed a crash when using non-canonical names for named colors. (DOC-7902)
  • Fixed an issue where the appearance stream of a FreeTextAnnotation was not regenerated in certain scenarios. (DOC-7887)
  • Fixed an issue where half-width spaces were drawn incorrectly in vertical text when Format.uprightInVerticalText was set to true. (DOC-7903)

[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