This HTML page is not optimized for LLM or AI agent consumption. Fetch the Markdown version instead: /guides/web/release-notes/1-15.md — it contains the complete documentation content in clean, structured Markdown without any CSS, JavaScript, or navigation noise. 1.15 release notes

1.15 release notes

RSS

Nutrient Web SDK 1.15 introduces a structured headless annotation API namespace, refines the UI customization slot system, and expands instance.search() with whitespace-tolerant matching for multiline phrases. See the changelog for full details.

Headless annotation APIs

This release introduces an annotations.* namespace on instance for working with annotations programmatically, organized by annotation type. Each sub-namespace exposes type-specific operations and shares a common selection, note, and clipboard infrastructure — usable without mounting the full viewer UI:

  • annotations.ink
  • annotations.link
  • annotations.measurement
  • annotations.redaction
  • annotations.shape
  • annotations.textMarkup
  • annotations.text

The shared infrastructure is extensible — additional annotation types can plug into the same primitives. This is the foundation for richer headless and automation workflows. Let us know through our support form(opens in a new tab) which patterns you’d like to see expanded next.

Cancelable load and conversion operations

NutrientViewer.load(), convertToPDF(), convertToOffice(), and populateDocumentTemplate() now accept an AbortSignal(opens in a new tab) to cancel in-flight operations. This is useful when users navigate away mid-load or you need to terminate a slow conversion:

const controller = new AbortController();
const instance = await NutrientViewer.load({
// ...your existing configuration
signal: controller.signal,
});
// Later, to cancel:
controller.abort();

Configurable loader UI

A new ui.loader option controls what users see while a document loads. Pick between the existing progress indicator, the new skeleton preview (a realistic placeholder of the viewer layout that reduces perceived load time), or supply a custom slot callback for full control:

await NutrientViewer.load({
// ...your existing configuration
ui: { loader: "skeleton" },
});

A new opt-in, SearchType.WORD_BASED, makes instance.search() resilient to whitespace inconsistencies in extracted PDF text. The mode strips Unicode space variants (NBSP, em/en/thin spaces, ideographic space, etc.) plus tab/newline/CR from both the query and the document before matching, so multiline phrases that cross tables, columns, or line wraps with stray whitespace match cleanly. Annotation contents are matched with the same algorithm. Punctuation is preserved on both sides, and case-insensitive search (the default) now case-folds non-ASCII letters too — "MÖCHTEN" matches "möchten".

const results = await instance.search("Smith, John", {
searchType: NutrientViewer.SearchType.WORD_BASED,
});

Default instance.search() behavior is unchanged. Additionally, the existing smart-search regex now tolerates a single space immediately before a line break, fixing cases where PDF text extraction inserts a trailing space at line wraps and a clean-newline query failed to match.

SearchType.WORD_BASED is available in standalone mode immediately. Server-backed mode requires Document Engine 1.16.x.

Performance, fonts, and stability

  • Dynamic font downloading and font substitution now work for linearized PDF loads, eliminating font-related fallbacks during progressive loading.
  • A new API lets you control the visual rendering order of annotations on a page via a custom comparator function.
  • Document Crop mode now supports keyboard input — arrow keys draw the crop region, Enter confirms, and Escape cancels.
  • The standalone JWT signing service now forwards the flatten flag consistently from prepareSign to the /sign_hash request, producing valid signatures when instance.signDocument() is called with flatten: true.
  • Fixes a regression introduced in 1.14.0 where cross-user updates to non-comment annotations could be rejected by Document Engine with invalid_anonymity_update, blocking collaborative editing of existing annotations.
  • Additional fixes across RTL viewer trackpad scrolling and scrollbar visibility, IME composition in custom stamp text fields, content editor text drag-selection on Windows, ligature text selection, redaction font sizing, Arabic rich-text free text appearance streams, and several content editor and form creator edge cases.

UI customization slot callbacks now receive getInstance()

The first argument to every ui.* slot callback changed from a captured instance reference to a getInstance() accessor. Call getInstance() at the point of use — typically inside render or an event handler — to read the latest viewer instance.

Before:

NutrientViewer.load({
ui: {
tools: {
main: (instance, id) => ({
render: () => createToolbar(instance),
}),
},
},
});

After:

NutrientViewer.load({
ui: {
tools: {
main: (getInstance, id) => ({
render: () => createToolbar(getInstance()),
}),
},
},
});

The previous signature handed slots a synchronous instance reference, which meant pre-load slots (ui.loader and ui.passwordPrompt) received null because the viewer hadn’t finished loading. With getInstance(), the same callback can read the live instance from any render call or event handler that fires after NutrientViewer.load() resolves — which is what unblocks the new configurable loader UI.

The change applies to every ui.* slot, including:

  • Pre-load slots — ui.loader, ui.passwordPrompt.
  • Post-load slots — ui.tools.main, ui.tools.contextual, ui.annotations.*, ui.signatures.*, ui.search, ui.documentEditor, ui.commentThread, ui.sidebar.*.

Minimum Document Engine version required: 1.5.6

For a complete list of changes, bug fixes, and improvements, refer to the changelog. For previous release notes, refer to the Web SDK 1.14 release notes. We appreciate your feedback and contributions as we continue to enhance Nutrient Web SDK.