Skip to main content Skip to footer

What's New in Document Solutions for PDF JS v9

v9.2 - Sept 1, 2026

Document Solutions for PDF JS (DsPdfJS) v9.2 is a major expansion of the JavaScript PDF API introduced in v9.1. The release adds advanced document-security workflows, richer standards-oriented PDF generation, reusable PDF graphics objects, deeper font inspection, and new APIs for controlling output intent and annotation appearance.

Together, these updates make DsPdfJS better suited for document signing, accessible and archival PDF creation, externally managed signing services, advanced PDF inspection, and production-oriented browser or Node.js workflows.

Digital Signatures

DsPdfJS v9.2 adds support for creating, parsing, and verifying PDF digital signatures in JavaScript workflows. Developers can sign documents using an X.509 certificate and its corresponding private key in PEM format, or integrate an external signing provider without exposing private keys to DsPdfJS.

The signing architecture separates PDF structure management from cryptographic and network operations. The DsPdfJS WebAssembly core prepares the PDF, calculates the byte ranges, and reserves the signature container. JavaScript then performs asynchronous cryptographic operations and returns the completed signature data to the WebAssembly layer for final PDF generation.

This approach enables integrations with browser-native and externally hosted signing infrastructure, including Web Crypto, Azure Key Vault, hardware-backed signing services, and custom server-side signing APIs.

DsPdfJS v9.2 supports:

  • Certificate-based PDF signing
  • Visible and invisible signatures
  • Sequential signing through incremental updates
  • Asynchronous external signer implementations
  • Document timestamp signatures
  • Signature parsing and inspection
  • Signature-value verification
  • Certificate chains, OCSP responses, CRLs, and timestamp evidence
  • Document Security Store data for long-term validation scenarios
  • PAdES B-B, B-T, B-LT, and B-LTA signature levels

The main API includes PdfDocument.sign(), PdfDocument.timestamp(), Pkcs7Signer, Pkcs7SignerBase, TimeStampProvider, TimeStamper, PdfSignature, and the document security store.

Sign a PDF Using an X.509 Certificate

/**
 * This sample demonstrates how to create and sign a PDF with a certificate .crt and private key .pem files,
 * using a SignatureField.
 * The sample then loads the signed file back into another PdfDocument instance
 * and verifies the signature.
 */
const doc = new PdfDocument();
const page = doc.newPage();
const tf = new Format({ font: Font.getPdfFont(StandardPdfFont.Times), fontSize: 14 });
page.context.drawText("Hello, World!\n" +
                "Signed by DsPdfWeb SignDoc sample.",
                tf, 72, 72);
// load certificate and private key
const certificatePem = await loadFileAsString("sign_cert.crt");
const privatePem = await loadFileAsString("sign_privatekey.pem");
const pkcs7Signer = new Pkcs7Signer();
pkcs7Signer.location = "DsPdfJS Demo Browser";
pkcs7Signer.signerName = "DsPdfJS";
pkcs7Signer.signingDateTime = new Date();
pkcs7Signer.certificateChainPem = [certificatePem];
pkcs7Signer.privateKeyPem = privatePem;

// Init a signature field to hold the signature:
var sf = new SignatureField();
sf.widget.rect = { x: 72, y: 72 * 2, width: 72 * 4, height: 36};
sf.widget.page = page;
sf.widget.backColor = "LightSeaGreen";
sf.widget.defaultAppearance.font = Font.getPdfFont(StandardPdfFont.Helvetica);
sf.widget.buttonAppearance.caption =
        `Signer: ${pkcs7Signer.signerName}\r\nLocation: ${pkcs7Signer.location}`;
// Add the signature field to the document:
doc.acroForm.fields.add(sf);
// Connect the signature field and signer:
pkcs7Signer.signatureField = sf;
// Sign and save the document:
const docData = await doc.sign(pkcs7Signer);
// Verify signature
const doc2 = PdfDocument.load(docData);
const sf2 = doc2.acroForm.fields.getAt(0) as SignatureField;
const parsedSignature = sf2.signature?.parseContent();
if (!parsedSignature?.valueValid)
        throw new Error("Failed to verify the signature");
saveFile("digitallySignPdf.pdf", docData as any);

External signing providers can be implemented by deriving from Pkcs7SignerBase. This allows DsPdfJS to supply the data that must be signed while the caller controls where and how the private-key operation occurs.

JS PDF API | Sign a PDF Using an X.509 Certificate Example

DsPdfJS verifies that the embedded signature value corresponds to the signed PDF data. Certificate trust, expiration, and revocation-policy validation may require additional application-level processing.

Help | Demo

Output Intents and ICC Profiles

DsPdfJS v9.2 adds support for PDF output intents, allowing developers to describe the intended output device or production condition for a document. Output intents are especially important in standards-oriented workflows such as PDF/A and PDF/X, where color reproduction must be described consistently.

The new IccProfile API creates an ICC profile from raw profile data, while OutputIntent defines information such as the output-intent subtype, condition identifier, registry name, human-readable information, and destination output profile. The document’s output-intent collection is available through PdfDocument.outputIntents.

const profile = IccProfile.create(iccProfileBytes);
const outputIntent = new OutputIntent();
outputIntent.subtype = OutputIntent.GTS_PDFA1;
outputIntent.outputConditionIdentifier = "sRGB IEC61966-2.1";
outputIntent.info = "sRGB color profile";
outputIntent.destOutputProfile = profile;

doc.outputIntents.add(outputIntent);
Before setting the 'Use Overprint Preview' option After setting the 'Use Overprint Preview' option
Before Setting the 'Use Overprint Preview' option After Setting the 'Use Overprint Preview' option

For common PDF/A scenarios, developers can also use OutputIntent.createDefaultForPdfA() to create a default sRGB-based output intent.

Help | Demo

Tagged PDFs, Structure Trees, and PDF Conformance

DsPdfJS v9.2 introduces a broad set of APIs for generating and modifying tagged PDFs. These APIs help developers build the logical document structure required for accessible and archival PDF workflows, including PDF/A and PDF/UA documents.

Visible PDF content can now be wrapped in marked-content sequences using beginMarkedContent() and endMarkedContent(). Developers can associate that marked content with logical elements such as documents, parts, paragraphs, figures, forms, and other structural objects through the document’s structure tree.

The feature group includes:

  • Tagged PDF content streams
  • PDF structure trees
  • Structure elements and content-item links
  • PDF 2.0 namespaces
  • Structure attributes, role maps, and class maps
  • Associated files
  • Structure-based annotation tab order
  • Form XObjects
  • Custom annotation appearance streams
  • PDF/A and PDF/UA conformance metadata

Create Tagged Content

// PDF structure tree basics: StructTreeRoot, StructElement, McidContentItemLink.
// Content is drawn inside marked-content brackets, and for each marked-content
// sequence a structure element is created and linked to the content via its MCID.
const doc = new PdfDocument();
const font = Font.getPdfFont(StandardPdfFont.Helvetica);
// Create a Part element under the structure tree root;
// it will contain P (paragraph) elements:
const sePart = new StructElement('Part');
doc.structTreeRoot.children.add(sePart);
const page = doc.pages.addNew();
const g = page.context;
let y = 20;
for (let i = 0; i < 3; i++) {
    // create a paragraph element and add it to the Part element
    const seParagraph = new StructElement('P');
    seParagraph.title = `Paragraph ${i + 1}`;
    // defaultPage is the page MCID-based content items of this element refer to
    seParagraph.defaultPage = page;
    sePart.children.add(seParagraph);
    const tl = new Layout({
        defaultFormat: new Format({ font: font, fontSize: 12 }),
        maxWidth: page.width - 40
    });
    tl.append(`Paragraph ${i + 1}: this text is drawn inside a marked-content bracket with MCID ${i} ` +
        'and referenced from a P structure element via McidContentItemLink.');
    tl.performLayout();
    // draw the paragraph within tagged content (BDC ... EMC with MCID i)
    g.beginMarkedContent({ name: 'P', mcid: i });
    g.drawLayout(tl, 20, y);
    g.endMarkedContent();
    y += tl.contentHeight + 20;
    // link the marked-content sequence to the structure element
    seParagraph.contentItems.add(new McidContentItemLink(i));
}
// the mark information dictionary of a tagged document must have marked = true
doc.markInfo.marked = true;
const outputFile = path.join(__dirname, 'StructureTreeBasics.pdf');
await writeFile(outputFile, doc.savePdf());

The above code produces the following structure tree of tagged content:

Create Tagged Content | JavaScript PDF API Library | SDK

DsPdfJS provides the APIs required to produce conforming documents, but it does not automatically validate that every requirement of a selected PDF standard has been satisfied. The calling application remains responsible for requirements such as embedding fonts, tagging all meaningful content, providing alternative text, and supplying the required metadata.

Help | Demo

Form XObjects

DsPdfJS v9.2 adds support for Form XObjects, reusable self-contained pieces of PDF content that can be drawn multiple times without duplicating the underlying content stream.

A FormXObject can be created from an explicit rectangle or from an existing PDF page, including a page from another document. Developers draw into the Form XObject through its context property and render it with drawForm().

This is useful for reusable graphics, page imposition, repeated headers or stamps, annotation appearance streams, and structure-tree scenarios. The below code shows how to create a Form XObject and render it multiple times with different bounds.

// Form XObjects: create a FormXObject, build its content, draw it with drawForm().
// A FormXObject is a reusable self-contained piece of content: it is stored once
// in the PDF and can be drawn any number of times on any page.
const doc = new PdfDocument();
const page = doc.pages.addNew();
const font = Font.getPdfFont(StandardPdfFont.Helvetica);
// Create a FormXObject with explicit bounds:
const bounds = { x: 0, y: 0, width: 144, height: 72 };
const fxo = new FormXObject(doc, bounds);
// Generate the form's content through its context property
// (all drawing APIs available for page content work here as well):
const g = fxo.context;
g.drawRect(bounds, { fillBrush: new LinearGradientBrush({ startColor: "LightCyan", endColor: "Cyan" }) });
g.drawRect(bounds, { lineColor: "DarkCyan", lineWidth: 2 });
const tl = new Layout({
    maxWidth: bounds.width,
    maxHeight: bounds.height,
    textAlignment: TextAlignment.Center,
    paragraphAlignment: ParagraphAlignment.Center,
    defaultFormat: new Format({ font: font, fontSize: 16 })
});
tl.append("Form XObject");
tl.performLayout();
g.drawLayout(tl, 0, 0);
// Draw the same FormXObject instance three times with different bounds;
// the content is scaled to the destination rectangle:
const pg = page.context;
pg.drawText("The same FormXObject drawn three times with different bounds:", new Format({ font: font, fontSize: 12 }), 20, 20);
// original size
pg.drawForm(fxo, 20, 50, 144, 72);
// stretched horizontally
pg.drawForm(fxo, 200, 50, 288, 72);
// bounds overload; keepAspectRatio fits the content proportionally
pg.drawForm(fxo, { x: 20, y: 150, width: 288, height: 144 }, { keepAspectRatio: true });
const outputFile = path.join(__dirname, 'FormXObjects.pdf');
await writeFile(outputFile, doc.savePdf());
console.log(`Created ${outputFile}`);

The above code generates the following Form XObjects:

JavaScript PDF API Library Support for PDF Form XObjects

The same Form XObject can be placed on multiple pages, transformed, scaled, rotated, or reused as part of an annotation appearance.

Help

Associated Files

DsPdfJS v9.2 adds support for PDF associated files. Unlike a general embedded attachment, an associated file is explicitly related to a document object such as the document itself, a page, an annotation, a Form XObject, or a structure element.

Associated files are important for workflows such as PDF/A-3 and PDF/A-4, where an attachment may need to be connected to the document content it describes. Developers can access associated-file collections through:

  • PdfDocument.associatedFiles
  • PdfPage.associatedFiles
  • AnnotationBase.associatedFiles
  • FormXObject.associatedFiles
  • StructElement.associatedFiles

Applications can specify the file relationship, file name, MIME type, and modification date as required by the target workflow.

Custom Annotation Appearance Streams

DsPdfJS v9.2 gives developers direct control over the normal, rollover, and down appearance streams used by PDF annotations.

Each appearance stream can reference a FormXObject, giving developers access to the full drawing API when defining how an annotation or form widget should look. Named streams are also supported for annotations with multiple visual states, such as checked and unchecked form controls.

const annotation = new StampAnnotation();
annotation.rect = { x: 50, y: 50, width: 180, height: 60 };
page.annotations.add(annotation);
const appearance = annotation.createAppearanceContentStream();
appearance.context.drawRect(0, 0, 180, 60, {
  fillColor: "LightGreen",
  lineColor: "DarkGreen"
});
appearance.context.drawText("APPROVED", new Format({font, fontSize: 12}), 45, 22);
annotation.appearanceStreams.normal.default = appearance;

Because appearance streams are Form XObjects, they can also contain tagged content and participate in accessible PDF workflows. Below is an example of how a custom annotation appearance stream would appear in a real PDF:

Custom Annotation Appearance Streams ExampleHelp

PDF Font Inspection and Embedded Font Data

DsPdfJS v9.2 adds PdfDocument.getFonts(), allowing applications to inspect the fonts referenced by an existing PDF document.

The returned font collection is de-duplicated and includes fonts referenced from page resources, nested Form XObjects, and annotation appearance streams. Developers can inspect font properties such as:

  • PostScript and family names
  • Font weight, stretch, and italic state
  • PDF font subtype
  • Font descriptor metrics
  • Embedded-font status
  • Character-code and Unicode mappings
  • Descendant CID fonts for Type 0 fonts

Applications can also extract embedded font data or remove it when appropriate. Removing embedded font data can reduce file size, but it may change rendering when a matching font is unavailable on the viewing system.

Get Embedded Font Data

const doc = PdfDocument.load(existingPdfBytes);

for (const font of doc.getFonts()) {
  const attributes = font.getFontAttributes();

  console.log({
    baseFont: font.baseFont,
    family: attributes.fontFamily,
    weight: attributes.fontWeight,
    italic: attributes.fontItalic,
    embedded: font.isEmbedded
  });
}

Remove Embedded Font Data

for (const font of doc.getFonts()) {
  if (font.isEmbedded) {
    font.removeEmbeddedData(doc);
  }
}

const optimizedPdf = doc.savePdf();

Below is an image of a generated PDF that showcases the enumerated font information gathered via this new API implementation:

Enumerated font information sample

Help | Demo

Linearized PDF Output

DsPdfJS v9.2 adds support for saving linearized PDFs, commonly known as Fast Web View PDFs. Linearization organizes a PDF so that the first page and required document structures can be loaded before the entire file has been downloaded.

Developers can save a linearized PDF with SaveMode.Linearized and check the current state through PdfDocument.linearized.

const output = doc.savePdf({
  saveMode: SaveMode.Linearized
});

console.log(`Linearized: ${doc.linearized}`);

Linearization and incremental-update save modes are separate workflows and cannot be applied to the same save operation.

Help

Support for PDF/A-4, PDF/A-4e, PDF/A-4f, and PDF/UA-2

DsPdfJS v9.2 add APIs for creating documents that target PDF/A-4, PDF/A-4e, PDF/A-4f, and PDF/UA-2.

PDF/A-4 is based on PDF 2.0 and is designed for long-term preservation. Its specialized variants extend the format for engineering content and embedded-file workflows:

  • PDF/A-4 supports the core PDF/A-4 archival profile.
  • PDF/A-4e supports engineering-oriented content and may include formats such as CAD, STEP, XML, and rich media.
  • PDF/A-4f supports arbitrary embedded-file attachments when the required association rules are followed.

PDF/UA-2 is the second generation of the PDF Universal Accessibility standard. It builds on PDF 2.0 and defines the document structure and metadata needed for reliable use with assistive technologies such as screen readers and braille displays.

v9.2 provides corresponding standards-focused functionality while also making several capabilities already established in DsPdf .NET available to JavaScript developers, including tagged and marked content, output intents, associated files, and other PDF structure features.

The following example configures a document for PDF/UA-2 and initializes its PDF 2.0 structure tree. Additional tagging, alternative text, embedded fonts, and other required content-level details must be supplied for full conformance.

var doc = new GcPdfDocument();
doc.PdfVersion = "2.0";
doc.Lang = "en";
doc.Metadata.PdfUa = 2;
doc.Metadata.PdfUaRev = "2024";
doc.MarkInfo.Marked = true;
var ns = new Namespace("http://iso.org/pdf2/ssn");
doc.StructTreeRoot.Namespaces.Add(ns);
var documentElement = new StructElement("Document") { NS = ns };
doc.StructTreeRoot.Children.Add(documentElement);

As with existing PDF/A and PDF/UA support, these APIs enable applications to create documents that meet the standards. They do not automatically convert an arbitrary PDF into a conforming document or perform complete standards validation.

Help | PDF/UA-2 Demo | PDF/A-4 Demo

3D Annotation Support

DsPdfJS v9.2 add support for representing 3D artwork through PDF 3D annotations.

3D Annotation Support | Included in Document Solutions for PDF .NET and JS Libraries

The new object model includes 3D annotations, 3D streams, views, activation and deactivation settings, presentation styles, toolbar visibility, transparency, animation behavior, lighting, rendering modes, and camera transformations.

These APIs support common embedded and windowed 3D viewing scenarios without requiring developers to manually construct the lower-level PDF dictionaries. Refer to the following code to create a 3D model annotation:

public class Annotation3D
{
    public int CreatePDF(Stream stream)
    {
        var doc = new GcPdfDocument();
        var page = doc.NewPage();
        // Load the 3D model (U3D format).
        var modelPath = Path.Combine(
            "Resources",
            "3DModels",
            "plane64x64_base.u3d");
        using var modelStream = File.OpenRead(modelPath);
        // Add a 3D annotation showing the model.
        var annotation = new Model3DAnnotation
        {
            Stream = new Model3DStream(
                modelStream,
                Model3DStreamFormat.U3D),
            ActivationCondition =
                Model3DActivationCondition.PageBecomesVisible,
            DeactivationCondition =
                Model3DDeactivationCondition.PageBecomesInvisible,
            Page = page,
            Rect = new RectangleF(
                rc.X,
                rc.Bottom + 18,
                72 * 5,
                72 * 4)
        };

        doc.Save(stream);
        return doc.Pages.Count;
    }
}

Help | Demo


Document Solutions PDF Viewer (DsPdfViewer)

Document Solutions PDF Viewer (DsPdfViewer) v9.2 improves touch interaction for PDF viewing on mobile devices and touch-enabled screens.

Improved Pinch-and-Zoom Behavior

Previously, pinch-to-zoom operations used the top-left corner of the document as the zoom focal point, regardless of where the user placed their fingers.

DsPdfViewer v9.2 corrects this behavior so that zooming remains centered around the user’s actual pinch location. This provides a more natural mobile experience, particularly when viewing large documents, technical drawings, maps, or detailed page content.

The improvement does not require a new public API and applies automatically when users pinch to zoom on supported touchscreen devices.

Demo

v9.1 - May 5, 2026

Release of Document Solutions for PDF JavaScript (DsPdfJS)

We’re excited to introduce Document Solutions for PDF JavaScript (DsPdfJS), our new JavaScript PDF API library that brings core PDF document processing capabilities to JavaScript developers. With DsPdfJS, developers can create, load, edit, secure, optimize, and extract content from PDF documents using a JavaScript-first programming model, expanding the Document Solutions product line further into the JavaScript ecosystem.

This initial release focuses on the core PDF workflows developers need most, including:

As the main highlight of the v9.1 release, DsPdfJS opens the door for JavaScript developers to build modern PDF solutions for browser and server-side scenarios using the same Document Solutions approach to document APIs.

The below is just a quick overview of some of the features and functionality DsPdfJS has to offer. Discover more about all DsPdfJS has to offer by checking out our demos and documentation!

Create, Load, and Save PDFs

Document Solutions for PDF JavaScript (DsPdfJS) gives JavaScript developers a straightforward way to create, load, and save PDF documents in code. This is one of the core capabilities of the new product line, enabling applications to generate PDFs from scratch, open existing files for processing, and save results with configurable output options.

The main API centers on the PdfDocument class. Developers can create a new document with PdfDocument.create(), optionally supplying PdfDocumentOptions to control settings such as page size, PDF/A conformance, and font embedding behavior. Existing PDFs can be opened with PdfDocument.load(), and finished documents can be written out with savePdf(), including support for save options such as linearized output for faster web viewing. DsPdfJS also supports adding embedded files to a PDF using addEmbeddedFile(), making it possible to package related resources directly inside the document.

// Create a PDF
using rm = new ResourceManager();
const doc = PdfDocument.create(rm, {
  defaultPageWidth: 595,
  defaultPageHeight: 842,
  conformanceLevel: PdfAConformanceLevel.PdfA1a,
  fontEmbedMode: FontEmbedMode.EmbedFullFont
});
// Load a PDF
using rm = new ResourceManager();
const pdfDoc = PdfDocument.load(rm, await this.loadFileAsArray("Pdfs/ProcurementLetter.pdf"));
/* Loads a binary file from the specified URL */
async loadFileAsArray(url: string): Promise<Uint8Array> {
    const response = await fetch(url);
    if (!response.ok) {
        throw new Error(`Unable to load file: ${response.statusText}`);
    }
    const arrayBuffer = await response.arrayBuffer();
    return new Uint8Array(arrayBuffer);
}
// Save a PDF
const res: Uint8Array = doc.savePdf({ saveMode: SaveMode.Linearized });

Help | Demo

Merge, Split, Insert, and Remove Pages

DsPdfJS also provides the page-level editing features developers need for common PDF workflow automation, including splitting content, and inserting or removing pages. These capabilities are useful for assembling reports, extracting content into separate outputs, or restructuring existing PDF files entirely in JavaScript.

For merging, developers can use mergeWithDocument() to append pages from one PDF into another, with support for targeting a specific insertion point and page range. For inserting pages, DsPdfJS provides APIs such as newPageContext() for adding a new page and drawing on it immediately, along with page collection methods like insert() and insertNew() to place pages at a specific position in the document. Pages can be removed with remove() or removeAt(), making it easy to reorganize documents programmatically.

For split scenarios, DsPdfJS exposes split() and related SplitOptions support, which can be used to divide layout content across pages or carry overflow into a secondary layout. These options allow developers to control behaviors such as paragraph handling, widows and orphans, spacing, and the dimensions of the remaining layout area, giving them fine-grained control over how content is split when building multi-page PDF output.

Programmatically Split PDFs in Browser/Web Applications using the split() method exposed by DsPdfJS

const ds = window.DocSol;

async function createPdf() {
  // Load an existing PDF:
  const fn1 = "Wetlands.pdf";
  const fn2 = "WetlandsReview.pdf";
  const bytes1 = await loadFileAsArray(`pdfs/${fn1}`);
  const bytes2 = await loadFileAsArray(`pdfs/${fn2}`);

  const doc1 = ds.PdfDocument.load(bytes1);
  const doc2 = ds.PdfDocument.load(bytes2);
  // Merge doc2 into doc1:
  doc1.mergeWithDocument(doc2);
  return doc1;
}

Help | Demo

Text Extraction: Search, Replace, and Delete

DsPdfJS includes built-in support for extracting and editing PDF text, making it useful for search-driven workflows, document cleanup, and content updates directly in JavaScript. Developers can retrieve page text, search for specific words or phrases, replace matched content, or remove text entirely without leaving the PDF processing pipeline.

For extraction and search, DsPdfJS provides getText() to retrieve all text from a page and FindTextParams to define search criteria such as the target text, case sensitivity, and whole-word matching. This makes it easier to build features such as indexing, validation, content inspection, or targeted text editing across PDF pages.

For editing, DsPdfJS supports both replaceText() and deleteText() on PdfPage. Developers can replace matched text with new content, optionally specifying a font and font size, or delete text using modes such as Standard or PreserveSpace depending on whether surrounding content should reflow. Together, these APIs give JavaScript developers a practical way to implement search, replace, and delete operations for real-world PDF modification scenarios such as updating names, dates, contact information, or removing unwanted text fragments.

Programmatically Find and Replace Text in PDFs using JavaScript

const ds = window.DocSol;

async function createPdf()
{
  const doc = ds.PdfDocument.load(await loadFileAsArray('PDF/LeaseAgreementDemo.pdf'))

  // Replace:
  // "Jane Donahue" -> "John Doe"
  // "(123)098-7654" -> "(007)123-4567"
  // "janed@example.com" -> "johnd@example.com"
  doc.replaceText( {text: "Jane Donahue"}, "John Doe");
  doc.replaceText( {text: "(123)098-7654"}, "(007)123-4567");
  doc.replaceText( {text: "janed@example.com"}, "johnd@example.com");
  // "13-Dec-20 22:16:00" -> date now
  // "13-Dec-22 22:16:00" -> date now + 2 years
  const termStart = new Date();
  const termEnd = new Date(termStart);
  termEnd.setDate(termEnd.getDate() + 365 * 2);
  doc.replaceText( {text: "13-Dec-20 22:16:00"}, termStart.toLocaleString("en-US", {hour12: false}));
  doc.replaceText( {text: "13-Dec-22 22:16:00"}, termEnd.toLocaleString("en-US", {hour12: false}));
  // For reference, add time stamp page footer on all pages:
  for (i = 0; i < doc.pages.count; ++i) {
    const page = doc.pages.getAt(i);
    const ctx = page.context;
    const margin = ctx.resolution / 2;
    ctx.drawText({ text: `Modified on ${new Date().toLocaleString()}, DsPdfJS v${ds.DsPdf.instance.version}`, fontSize: 10, foreColor: 'DimGray' }, margin, ctx.height - margin);
  }
  return doc;
}

Help | Demo

Redact Annotations and Apply Redacts

DsPdfJS includes support for redact annotations and permanent redaction workflows, making it possible to mark sensitive content for removal and then apply those redactions directly to the PDF. This is especially useful for compliance, privacy, and secure document-sharing scenarios where visual masking alone is not enough and content must be actually removed from the file. The core annotation type is RedactAnnotation, which can define a redaction area and optional overlay settings such as fill color, border color, justification, and overlay text.

After annotations are added, developers use PdfDocument.redact() to apply them and permanently remove the affected content. DsPdfJS also provides RedactOptions for controlling how redaction is performed, including image handling, unreadable object behavior, precision, and render options. This makes it possible to build both simple “mark and apply” redaction workflows and more advanced scenarios such as removing targeted link areas or applying document-wide cleanup with controlled rendering behavior.

Apply Redactions to PDFs in Browser Web Applications using a JS PDF API Library

const ds = window.DocSol;

async function createPdf()
{
  // Create PDF doc object & load in PDF file
  const doc = ds.PdfDocument.load(await loadFileAsArray('pdfs/SlidePages.pdf'))
  //Create redact annotation and set redaction area
  const redact = new ds.RedactAnnotation();
  Object.assign(redact, {
    rect: { x: 16, y: 16, width: 280, height: 300 },
    page: doc.pages.getAt(0),
    overlayText: `This content has been redacted by DsPdfJS v${ds.DsPdf.instance.version} on ${new Date().toLocaleString()}`,
    overlayFillColor: "PaleGoldenrod",
  });
  // Apply redaction
  doc.redact();
  return doc;
}

Help | Demo

AcroForms

DsPdfJS includes support for AcroForms, allowing JavaScript developers to create interactive PDF forms from scratch or load existing forms and update them programmatically. This makes it possible to build workflows around form generation, form filling, and field inspection entirely in JavaScript, covering common field types such as text fields, checkboxes, radio buttons, combo boxes, list boxes, signature fields, and buttons.

The main entry point is the document’s acroForm object, which exposes the fields collection for adding, reading, and updating form fields. Developers can create fields by adding typed field definitions to doc.acroForm.fields, configure widget appearance and actions, and later load an existing PDF form and iterate through its fields to inspect or modify values. DsPdfJS also supports AcroForm.calculationOrder, giving developers control over which fields are recalculated when values change.

Programmatically Generate AcroForms using a JavaScript PDF API

const ds = window.DocSol;

async function createPdf() {
  const doc = new ds.PdfDocument();
  const page = doc.pages.addNew();
  const ctx = page.context;
  const inch = ctx.resolution;

  const font = ds.Font.getPdfFont(ds.StandardPdfFont.Times);
  const fontSize = 14;
  const tf = new ds.Format({ fontSize, foreColor: "Black" });

  const x = ctx.resolution;
  let y = ctx.resolution;

  const fldOffset = inch * 2;
  const fldHeight = fontSize * 1.2;
  const dY = inch / 2;

  // Text field:
  ctx.drawText({ text: "Text field:", format: tf }, x, y);
  {
    doc.acroForm.fields.add({
        type: "text",
        name: "fldText",
        value: "Initial text field value",
        defaultValue: "Default text field value",
        widget: {
            page,
            rect: { x: x + fldOffset, y, width: inch * 3, height: fldHeight },
            defaultAppearance: { font, fontSize }
        }
    });
  }
  y += dY;

  // Checkbox:
  ctx.drawText({ text: "Checkbox:", format: tf }, x, y);
  {
    doc.acroForm.fields.add({
        type: "checkbox",
        name: "fldCheckbox",
        checked: true,
        defaultChecked: true,
        widget: {
            page,
            rect: { x: x + fldOffset, y, width: fldHeight, height: fldHeight },
        }
    });
  }
  y += dY;

  // Radio button:
  ctx.drawText({ text: "Radio button:", format: tf }, x, y);
  {
    doc.acroForm.fields.add({
        type: "radiobutton",
        name: "fldRadio",
        value: 1,
        defaultValue: 0,
        widgets:[
          { page, rect: { x: x + fldOffset, y, width: fldHeight, height: fldHeight } },
          { page, rect: { x: x + fldOffset, y: y + fldHeight * 1.2, width: fldHeight, height: fldHeight } },
          { page, rect: { x: x + fldOffset, y: y + (fldHeight * 1.2) * 2, width: fldHeight, height: fldHeight } },
        ]
    });
  }
  y += fldHeight * 3 * 1.2 + fldHeight;

  // CombTextField:
  ctx.drawText({ text: "CombText field:", format: tf }, x, y);
  {
    doc.acroForm.fields.add({
        type: "spacedtext",
        name: "fldCombText",
        value: "123",
        defaultValue: "1234567890",
        widget: {
            page,
            rect: { x: x + fldOffset, y, width: inch * 3, height: fldHeight },
            defaultAppearance: { font, fontSize }
        }
    });
  }
  y += dY;

  // Combo-box:
  ctx.drawText({ text: "Combo box:", format: tf }, x, y);
  {
    doc.acroForm.fields.add({
        type: "combobox",
        name: "fldComboBox",
        items: [ "ComboBox Choice 1", "ComboBox Choice 2", "ComboBox Choice 3"],
        value: 2,
        defaultValue: 0,
        widget: {
            page,
            rect: { x: x + fldOffset, y, width: inch * 3, height: fldHeight },
            defaultAppearance: { font, fontSize }
        }
    });
  }
  y += dY;

  // List box:
  ctx.drawText({ text: "List box:", format: tf }, x, y);
  {
    doc.acroForm.fields.add({
        type: "listbox",
        name: "fldListBox",
        items: [ "ListBox Choice 1", "ListBox Choice 2", "ListBox Choice 3"],
        selectedIndexes: [0, 2],
        defaultSelectedIndexes: [0],
        multiSelect: true,
        widget: {
            page,
            rect: { x: x + fldOffset, y, width: ctx.resolution * 2.5, height: ctx.resolution },
            defaultAppearance: { font, fontSize }
        }
    });
  }
  y += ctx.resolution + dY;

  // Signature field:
  ctx.drawText({ text: "Signature field:", format: tf }, x, y);
  {
    doc.acroForm.fields.add({
        type: "signature",
        name: "fldSignature",
        alternateName: "All fields locked when the document is signed",
        lockedFields: { type: ds.SignatureLockedFieldsType.All },
        widget: {
            page,
            rect: { x: x + fldOffset, y: y - dY / 4, width: inch * 2, height: dY },
            defaultAppearance: { font, fontSize },
            buttonAppearance: { caption: "Click to sign", },
            border: { width: 0.5, color: "DarkSeaGreen" }
        }
    });
  }
  y += dY;

  // Buttons:
  ctx.drawText({ text: "Push buttons:", format: tf }, x, y);

  // Submit form button:
  {
    doc.acroForm.fields.add({
        type: "button",
        name: "btnSubmit",
        alternateName: "Click to submit the form",
        widget: {
            page,
            rect: { x: x + fldOffset, y, width: inch, height: fldHeight },
            defaultAppearance: { font, },
            buttonAppearance: { caption: "Submit", },
            activate: new ds.ActionSubmitForm("Sample Form Submit URI"),
        }
    });
  }

  // Reset form button:
  {
    doc.acroForm.fields.add({
        type: "button",
        name: "btnReset",
        alternateName: "Click to reset the form",
        widget: {
            page,
            rect: { x: x + fldOffset + inch * 1.5, y, width: inch, height: fldHeight },
            defaultAppearance: { font, },
            buttonAppearance: { caption: "Reset", },
            highlighting: ds.HighlightingMode.Invert,
            activate: new ds.ActionResetForm(),
        }
    });
  }

  drawFooter(ctx, ctx.resolution);
  return doc;
}

Help | Demo

Encryption and Permissions

DsPdfJS includes support for PDF encryption and document permissions, allowing JavaScript developers to secure PDF files with passwords and control what recipients are allowed to do with the document. This is essential for workflows that require protected distribution, restricted printing, limited editing, or controlled content copying.

The main APIs here are EncryptOptions for applying security settings and PdfDecryptionOptions for opening protected files. Developers can use doc.security.setEncryptOptions() to set the encryption level, choose whether metadata is encrypted, define a user password, and apply permission settings such as printing or editing restrictions. When loading encrypted files, PdfDocument.load() can use decryption options such as a password, raw password bytes, and error-handling behavior for invalid passwords or unsupported security handlers. DsPdfJS also supports lower-level security handler workflows, including handlers such as StandardSecurityHandlerRev6, for more advanced control over password and permission behavior.

JavaScript support for PDF encryption and document permissions

const ds = window.DocSol;
function createPdf()
{
  const doc = new ds.PdfDocument();

  // Set the required document restrictions (a suitable security handler revision
  // will be created automatically):
  doc.security.setEncryptOptions({
      encryptionLevel: ds.EncryptionLevel.RC128,
      permissions: {
          copyContent: false,
          printingPermissions: ds.PrintingPermissions.Disabled,
          editingPermissions: ds.EditingPermissions.Disabled
      }
  });
  /* Alternatively, you may create a specific revision of the standard security handler
   * and set its options directly:
  // Create a Rev4 security handler and specify some restrictions:
  var ssh4 = ds.StandardSecurityHandlerRev4.create();
  ssh4.copyContent = false;
  ssh4.printingPermissions = ds.PrintingPermissions.Disabled; // TBD! printingPermissions
  ssh4.editingPermissions = ds.EditingPermissions.Disabled;
  doc.security.encryptHander = ssh4;
  */

  // Add a page to the PDF with some explanatory text:
  const ctx = doc.newPageContext();
  const res = ctx.resolution;
  const tf1 = new ds.Format({ fontSize: 20, foreColor: "Brown" });
  const tf2 = new ds.Format({ fontSize: 16, foreColor: "Blue" });
  const tl = new ds.Layout({
    marginAll: ctx.resolution,
    runs: [
      {
        text: "Document Restrictions.\n\n",
        format: tf1
      },
      {
        text:
          "This document has the following restrictions imposed using StandardSecurityHandlerRev4:\n" +
          "  - content copying is not allowed;\n" +
          "  - printing is not allowed;\n" +
          "  - document assembly is not allowed.\n" + 
        "\nSee index.js for details on how to programmatically set document restrictions.",
        format: tf2
      },
    ],
    maxWidth: ctx.width,
    maxHeight: ctx.height,
  });
  ctx.drawLayout(tl, 0, 0);
  // Add page footer with time stamp and DsPdfJS version:
  ctx.drawText({ text: `Generated on ${new Date().toLocaleString()}, DsPdfJS v${ds.DsPdf.instance.version}`, fontSize: 10, foreColor: 'DimGray' }, res, ctx.height - res);
  return doc;
}

Help | Demo

Optimization and Compression

DsPdfJS includes support for PDF optimization and compression, giving developers control over file size and save behavior when generating or modifying PDF documents. This is especially useful for delivery scenarios where smaller files improve storage efficiency, upload speed, and web performance.

The main APIs here are CompressionLevel, PdfStreamHandling, and SavePdfOptions. Developers can choose whether compression should prioritize speed, balanced output, or the smallest possible file, and they can control how existing streams in a loaded document are handled when saving. For example, streams can be copied as-is, recompressed using the document’s compression settings, or minimized for the best possible size reduction. These options are applied through savePdf(), giving JavaScript developers a flexible way to tune output for performance or compactness.

Font Support and Embedding

DsPdfJS includes strong support for fonts and font embedding, allowing developers to use standard PDF fonts, load custom font files, manage families through font collections, and control how text is rendered in generated documents. This is essential for maintaining visual fidelity, supporting brand typography, and ensuring text displays correctly across different PDF viewers and environments.

The main APIs here are Font and FontCollection. Developers can load fonts from files, use built-in standard PDF fonts, register multiple fonts in a collection, and let DsPdfJS resolve the best match by family and style. FontCollection also supports fallback fonts, default fonts, and font search, which makes it easier to handle mixed typography or missing glyphs. Beyond simple font loading, DsPdfJS also supports font features, bold/italic emulation, and custom drawing workflows that use either specific font objects or family-based resolution through a collection.

JS PDF API has Strong Font Support for Generated PDFs

const ds = window.DocSol;

async function createPdf()
{
  // A FontCollection lets you register multiple fonts once and then resolve them by
  // family name + style (bold/italic) when drawing text. You can attach it to a Layout
  // (for DrawingContext.drawLayout) or to a DrawingContext (for drawText/measureText).  
  const fc = new ds.FontCollection();
  async function addFont(fontFileName) {
    fc.addFont(ds.Font.load(await loadFileAsArray(fontFileName)));
  }
  await Promise.all([
    addFont("fonts/NotoSerif-Regular.ttf"),
    addFont("fonts/NotoSerif-Bold.ttf"),
    addFont("fonts/NotoSerif-Italic.ttf"),
    addFont("fonts/NotoSerif-BoldItalic.ttf"),
    addFont("fonts/NotoSans-Regular.ttf"),
    addFont("fonts/NotoSans-Bold.ttf"),
    addFont("fonts/NotoSans-BoldItalic.ttf"),
  ]);

  const doc = new ds.PdfDocument();
  const ctx = doc.newPageContext();
  const page = ctx.page;

  const margin = ctx.resolution / 2;
  const maxWidth = ctx.width - margin * 2;
  const maxHeight = ctx.height - margin * 2;

  const tl = new ds.Layout({
    marginAll: margin,
    maxWidth,
    maxHeight,
    fontCollection: fc,
  });

  let tf = new ds.Format({ fontFamily: "noto serif", fontSize: 16, foreColor: "DarkBlue" });
  tl.append({text: "Text drawn using Noto Serif Regular font. ", format: tf});
  // Setting bold/italic tells the FontCollection to resolve the best matching font
  // in that family. If an exact variant isn't available, the renderer may emulate it.
  tf = new ds.Format(tf, { italic: true, });
  tl.append({text: "Text drawn using Noto Serif Italic font. ", format: tf});
  tf = new ds.Format(tf, { bold: true, });
  tl.append({text: "Text drawn using Noto Serif Bold Italic font. ", format: tf});
  tf = new ds.Format(tf, { italic: false, });
  tl.append({text: "Text drawn using Noto Serif Bold font. ", format: tf});
  // Switch to another font family:
  tf = new ds.Format(tf, { fontFamily: "noto sans", });
  tl.append({text: "Text drawn using Noto Sans Bold font. ", format: tf});
  tf = new ds.Format(tf, { italic: true, });
  tl.append({text: "Text drawn using Noto Sans Bold Italic font. ", format: tf});
  tf = new ds.Format(tf, { bold: false, italic: false, });
  tl.append({text: "Text drawn using Noto Sans Regular font. ", format: tf});
  ctx.drawLayout(tl, 0, 0);

  // You can also assign the FontCollection to the DrawingContext so drawText(), drawLayout(),
  // and measureText() can resolve fontFamily names from that collection
  // (comment out the next line to confirm that the correct font won't be found):
  ctx.fontCollection = fc;
  ctx.drawText("Text drawn with DrawingContext.drawText() method using Noto Serif Bold font from the collection.",
    new ds.Format({ fontFamily: "noto serif", fontSize: 16, foreColor: "darkgreen" }),
    margin,
    tl.contentRect.y + tl.contentRect.height,
    page.width - margin * 2,
  );

  // Add a page footer with time stamp and DsPdfJS version:
  drawFooter(ctx, margin * 2);
  return doc;
}

Help | Demo

Raster and Scalable Vector Image Handling

DsPdfJS includes support for both raster and scalable vector image handling, allowing JavaScript developers to load, draw, clip, and save image content as part of PDF generation workflows. In v9.1, DsPdfJS supports JPEG, PNG, and SVG image scenarios, making it possible to work with both common raster formats and scalable vector artwork when building PDF documents.

For raster images, the main API is the Image class, which can load JPEG or PNG image data, expose image dimensions and type, convert image content to a bitmap, and save image data back out when needed. Developers can then draw images onto PDF pages using the drawing context, with support for scaling, clipping, opacity, alignment, and repeated placement. This makes it easy to handle common tasks such as inserting photos, placing logos, creating cropped image views, or layering semi-transparent images into document layouts.

For SVG workflows, DsPdfJS also provides APIs for drawing SVG content through its SVG drawing support, making it possible to place vector artwork into PDF output while preserving resolution independence. Together, these image APIs give developers a flexible way to combine raster and vector graphics in JavaScript-based PDF generation.

JavaScript Dev SDK: load, draw, clip, and save image content as part of PDF generation workflows

const ds = window.DocSol;

async function createPdf() {
  const doc = new ds.PdfDocument();
  const img = ds.Image.load(await loadFileAsArray("img/wargravepink.jpg"));
  const bmpFull = img.toBitmap();
  const scale = 0.5; // draw at half pixel size

  // Note about PDF/page/image units:
  // - Page coordinates are in “points” (pt). By default, ctx.resolution is 72 pt/in (PDF points),
  //   but you can set ctx.resolution to another value if you want a different coordinate scale.
  // - Images use pixels (px).
  // - If you treat images as 96 dpi, convert with pt = px * (ctx.resolution / 96) before scaling.

  // Page 1:
  let ctx = doc.newPageContext();
  const margin = ctx.resolution / 2;
  let top = margin;
  // Draw using 0.5 points per source pixel (≈ 67% of 96-dpi size):
  ctx.drawImage(img, margin, top, img.width * scale, img.height * scale);
  top += img.height * scale + 36;
  // Draw a clipped portion of the image:
  ctx.drawImage(
    img,
    margin,
    top,
    img.width * scale,
    img.height * scale,
    { clipBounds: { x: margin + 90, y: top + 90, width: 150, height: 100 } }
  );

  drawFooter(ctx, margin);

  // Page 2:
  ctx = doc.newPageContext();
  top = margin;

  // Clip region specified in page units above; convert to bitmap pixel units:
  const clipPx = { x: 90 / scale, y: 90 / scale, width: 150 / scale, height: 100 / scale };
  const bmp = bmpFull.clip(clipPx);

  ctx.drawImage(bmp, margin, top, bmp.width / 2, bmp.height);
  top += bmp.height * 1.5;
  ctx.drawImage(bmp, margin, top, bmp.width, bmp.height / 2);

  drawFooter(ctx, margin);
  return doc;
}

Help | Demo

Annotations

DsPdfJS includes broad support for PDF annotations, allowing JavaScript developers to add comments, markup, links, attachments, shapes, stamps, multimedia, watermarks, widgets, and redaction markers to PDF documents. Supported types include common review and markup annotations such as TextAnnotation, FreeTextAnnotation, TextMarkupAnnotation, SquareAnnotation, CircleAnnotation, LineAnnotation, InkAnnotation, PolygonAnnotation, PolyLineAnnotation, StampAnnotation, LinkAnnotation, FileAttachmentAnnotation, and RedactAnnotation, along with specialized types such as RichMediaAnnotation, SoundAnnotation, WatermarkAnnotation, and WidgetAnnotation.

Many of these annotation types derive from MarkupAnnotation, which provides the shared foundation for comment-style and review-style annotations. In practice, developers can create annotation objects, set properties such as content, color, position, and related appearance settings, and then add them to a page through the page’s annotations collection. This makes DsPdfJS well suited for workflows involving document review, collaboration, commenting, visual markup, links, and interactive PDF experiences.

JavaScript developers can add comments, markup, links, attachments, shapes, stamps, multimedia, watermarks, widgets, and redaction markers to PDF documents

const ds = window.DocSol;

function createPdf()
{
  const doc = new ds.PdfDocument();
  const page = doc.pages.addNew();
  const ctx = page.context;
  const margin = ctx.resolution;
  const gap = margin / 2;
  const maxWidth = ctx.width - margin * 2;
  let ipX = margin, ipY = margin;
  const tf = new ds.Format({ fontSize: 16, font: ds.Font.getPdfFont(ds.StandardPdfFont.Times) });
  const tl = new ds.Layout({
    defaultFormat: tf,
    maxWidth,
  });
  function drawNote(text) {
    tl.clear();
    tl.appendLine({ text });
    ctx.drawLayout(tl, ipX, ipY);
    ctx.drawRect(ipX, ipY, tl.contentWidth, tl.contentHeight, {lineColor: "gray"});
  }
  drawNote("This sample demonstrates some types of annotattions that can be created with DsPdfJS.");
  ipY += tl.contentHeight + gap;

  // A text annotation (red):
  tl.clear();
  drawNote("A red text annotation is placed to the right of this note.");
  const textAnnot = new ds.TextAnnotation(); //ds.getCurrentObjectManager());
  Object.assign(textAnnot, {
    userName: "Jaime Smith",
    contents: "This is a red TextAnnotation.",
    rect: { x: ipX + tl.contentWidth, y: ipY, width: 100, height: 50 },
    color: "Red",
  });
  page.annotations.add(textAnnot);
  // A reply to the previous annotation:
  const textAnnotReply = new ds.TextAnnotation();
  Object.assign(textAnnotReply, {
    userName: "Jane Donahue",
    contents: "This is a reply to the first annotation.",
    referenceAnnotation: textAnnot,
    referenceType: "R",
  });
  page.annotations.add(textAnnotReply);
  ipY += tl.contentHeight + gap;
  // Open text annotation (green):
  drawNote("An initially open green text annotation is placed to the right of this note.");
  const textAnnotOpen = new ds.TextAnnotation();
  Object.assign(textAnnotOpen, {
    open: true,
    userName: "Jaime Smith",
    contents: "This is an initially open annotation (green).",
    rect: { x: ipX + tl.contentWidth, y: ipY, width: 100, height: 50 },
    color: "green",
  });
  page.annotations.add(textAnnotOpen);
  ipY += tl.contentHeight + gap;
  // Free text annotation:
  drawNote("A blue free text annotation is placed below and to the right, with a callout going from it to this note.");
  const calloutLine = [
    {x: ipX + tl.contentWidth / 2, y: ipY + tl.contentHeight},
    {x: ctx.width - 220, y: ipY + tl.contentHeight + gap}
  ];
  ipY += tl.contentHeight + gap;
  const freeAnnot = new ds.FreeTextAnnotation();
  Object.assign(freeAnnot, {
    contents: "This is a free text annotation with a callout line going to the note on the left.",
    rect: { x: ctx.width - 220, y: ipY, width: 200, height: 50 },
    calloutLine,
    color: "LightSkyBlue",
  });
  page.annotations.add(freeAnnot);
  ipY = freeAnnot.rect.y + freeAnnot.rect.height + gap;
  // Free text annotation with rich text:
  const freeRichTextAnnot = new ds.FreeTextAnnotation();
  Object.assign(freeRichTextAnnot, {
    richText: "<body><p>This is another <i>free text annotation</i>, with <b><i>Rich Text</i></b> content.</p>" +
              "<p>Even though a <b>free text</b> annotation displays text directly on a page, " +
              "as other annotations it can be placed outside the page's bounds.</p></body>",
    rect: { x: ipX - margin * 2, y: ipY, width: margin * 5, height: margin },
    calloutLine,
    color: "LightSalmon",
  });
  page.annotations.add(freeRichTextAnnot);
  ipY = freeRichTextAnnot.rect.y + freeRichTextAnnot.rect.height + gap;
  // Square annotation around a note:
  drawNote("A square annotation drawn with a 3pt wide orange line around this note has a rich text associated with it.");
  const sauareAnnot = new ds.SquareAnnotation();
  Object.assign(sauareAnnot, {
    userName: "Jane Donahue",
    contents: "This is a free text annotation with a callout line going to the note on the left.",
    rect: { x: ipX - 7, y: ipY - 7, width: tl.contentWidth + 14, height: tl.contentHeight + 14},
    lineWidth: 3,
    color: "Orange",
    richText: "<body><p>This <b><i>rich text</i></b> is associated with the square annotation around a text note.</p></body>"
  });
  page.annotations.add(sauareAnnot);

  // Add page footer with time stamp and DsPdfJS version:
  ctx.drawText({ text: `Generated on ${new Date().toLocaleString()}, DsPdfJS v${ds.DsPdf.instance.version}`, fontSize: 10, foreColor: 'DimGray' }, margin, ctx.height - margin);
  return doc;
}

Help | Demo