> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/web/migration-guides/migrating-to-v8.md).

# Migrating to V8 of WebViewer

Migrating to WebViewer v8? Learn about breaking changes, namespace adjustments, and custom data updates. Ensure smooth transition with these key insights. The Apryse Web SDK streamlines secure, server

There are a few **breaking changes** and other deprecated APIs when migrating to v8 from older versions.

## instance namespace adjustments

This is **not** actually a breaking change since you can still access the old namespaces, but a number of namespaces have been restructured to more logically group things. All of the WebViewer Core namespaces can now be found on `instance.Core` and all of the UI related APIs and namespaces can be found on `instance.UI`. The `CoreControls` namespace now only exists for backwards compatibility and you can access all functions that were previously found on `CoreControls`, directly on the `Core` namespace.

`annotManager` has been renamed to `annotationManager`

`docViewer` has been renamed to `documentViewer`

`on` has been renamed to `addEventListener`

`off` has been renamed to `removeEventListener`

Before:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
WebViewer(...)
  then(instance => {
    const { docViewer, annotManager, Tools, Annotations, PDFNet, CoreControls } = instance;
    CoreControls.setWorkerPath(...);

    instance.disableElements([...]);

    docViewer.on('documentLoaded', () => {

    });
  });
```

{% endcode %}
{% endtab %}
{% endtabs %}

After:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
WebViewer(...)
  then(instance => {
    const { Core, UI } = instance;

    const { documentViewer, annotationManager, Tools, Annotations, PDFNet } = Core;
    Core.setWorkerPath(...);

    instance.UI.disableElements([...]);

    documentViewer.addEventListener('documentLoaded', () => {

    });
  });
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Inside a config file

When running code inside a [config file](/web/advanced/config-files.md) you can now access `instance` on the window and it will be identical to the `instance` that is resolved from the `WebViewer` function. `readerControl` no longer exists inside the WebViewer iframe.

## Breaking changes

### CustomData changes

"Custom data" is stored on annotations and is automatically saved and restored when exporting and importing XFDF, as well as downloading and re-opening a PDF. Custom data is compatible across all platforms using Apryse APIs, so for example with the Android Apryse SDK you could retrieve custom data that was stored by WebViewer.

To be consistent and fully compatible with other platforms the `setCustomData` function now only accepts a string as the value. If you pass something other than a string it will automatically be stringified. Similarly `getCustomData` will only return a string.

To more easily enforce the proper types, setting of the `CustomData` property directly is now no longer possible. Please use `setCustomData` to set your properties instead.

If you would like to use object types you can use `JSON.stringify` and `JSON.parse`, or `parseInt` and `parseFloat` for numbers.

Before:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
annotation.setCustomData('number-data', 1);
annotation.setCustomData('object-data', { abc: 123 });

annotation.getCustomData('number-data'); // the number 1
annotation.getCustomData('object-data'); // an object

annotation.CustomData = {
  key1: 1,
  key2: 2
};
annotation.CustomData.key1; // the number 1
```

{% endcode %}
{% endtab %}
{% endtabs %}

After:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
annotation.setCustomData('number-data', '1');
annotation.setCustomData('object-data', JSON.stringify({ abc: 123 }));

annotation.getCustomData('number-data'); // the string '1'
JSON.parse(annotation.getCustomData('object-data')); // JSON.parse returns the object

// these will warn and automatically stringify the value
annotation.setCustomData('number-data', 1);
annotation.setCustomData('object-data', { abc: 123 });

// this code no longer works
// annotation.CustomData = {
// key1: 1,
// key2: 2
// };
// annotation.CustomData.key1;
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Legacy UI no longer officially supported

With WebViewer 8.0 the legacy UI is not officially supported. If you would like to continue using the legacy UI you can stay on version 7.3. Note that the legacy 7.3 branch is available in the [public UI repo](https://github.com/ApryseSDK/webviewer-ui/tree/legacy/7.3) and you can attempt to update it to be compatible with 8.0 if you like.

### Updated annotation linking APIs

The `associateLink`, `getAssociatedLinks` and `unassociateLinks` APIs have been removed. Links are now associated with annotations by using the normal annotation grouping APIs, for example [`annotationManager.groupAnnotations`](https://sdk.apryse.com/api/web/Core.AnnotationManager.html#groupAnnotations).

If you have associated links already saved in your XFDF then WebViewer will automatically convert them over to groups the next time they are loaded.

Before:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
annotation.associateLink(linkAnnotation);
```

{% endcode %}
{% endtab %}
{% endtabs %}

After:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
annotationManager.groupAnnotations(annotation, [linkAnnotation]);
```

{% endcode %}
{% endtab %}
{% endtabs %}

### annotationSelected event never returns null for annotation list

In previous version when all annotations were deselected the `annotationSelected` event would provide `null` as the `annotations` parameter of the event for a `deselect` action. Now all `deselect` actions will return an array of the annotations that were deselected, never null.

Before:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
annotManager.on('annotationSelected', (annotations, action) => {
  // when all annotations are deselected, action is 'deselected', annotations is null
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

After:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
annotationManager.addEventListener('annotationSelected', (annotations, action) => {
  // when all annotations are deselected, action is 'deselected'
  // annotations is the array of annotations that were previously selected, possibly an empty array
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

### CoreControls.js renamed

The file `lib/core/CoreControls.js` has been renamed to `lib/core/webviewer-core.min.js`.

### getDisplayAuthor parameter change

The `AnnotationManager` function `getDisplayAuthor` previously would take an annotation object as a parameter and returned the transformed author name, however sometimes there were times that a transformed author name was needed but no annotation object existed yet.

In WebViewer 8.0 the `getDisplayAuthor` function now accepts the author id so that it can be used in all cases, with or without an anotation. This also means that the `setAnnotationDisplayAuthorMap` callback function receives an author id instead of an annotation.

Before:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
annotManager.setAnnotationDisplayAuthorMap((annotation) => {
  if (annotation.Id === '1') {
    return 'John';
  } else {
    return 'Guest';
  }
});

const displayAuthor = annotManager.getDisplayAuthor(annotation);
```

{% endcode %}
{% endtab %}
{% endtabs %}

After:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
annotationManager.setAnnotationDisplayAuthorMap((authorId) => {
  if (authorId === '1') {
    return 'John';
  } else {
    return 'Guest';
  }
});

const displayAuthor = annotationManager.getDisplayAuthor(annotation.Author);
```

{% endcode %}
{% endtab %}
{% endtabs %}

### WidgetEditingManager replaced by FormFieldCreationManager

With support for form field creation directly in the UI the `WidgetEditingManager` class has been replaced by the `FormFieldCreationManager` which includes all the functionality of the old `WidgetEditingManager` along with new field creation support. Previously you would use `annotManager.getWidgetEditingManager()` and now `annotationManager.getFormFieldCreationManager()`.

### A few functions are now asynchronous

`RubberStampCreateTool`

* [`setRubberStamp`](https://sdk.apryse.com/api/web/Core.Tools.RubberStampCreateTool.html#setRubberStamp)
* [`getPreview`](https://sdk.apryse.com/api/web/Core.Tools.RubberStampCreateTool.html#getPreview__anchor)

`SignatureCreateTool`

* [`exportSignatures`](https://sdk.apryse.com/api/web/Core.Tools.SignatureCreateTool.html#exportSignatures)
* [`isEmptySignature`](https://sdk.apryse.com/api/web/Core.Tools.SignatureCreateTool.html#isEmptySignature)

### Consistent NoZoom handling across all annotations

The `NoZoom` property on annotations now works consistently for all types of annotations. If you were overriding the `draw` function for sticky note annotations then it has changed somewhat. Previously the canvas context was already translated to where the sticky note was drawn, but now you must first translate by `annotation.X` and `annotation.Y`.

Before:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
Annotations.setCustomDrawHandler(Annotations.StickyAnnotation, function(ctx, pageMatrix, options) {
  // ctx functions here without translating
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

After:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
Annotations.setCustomDrawHandler(Annotations.StickyAnnotation, function(ctx, pageMatrix, options) {
  ctx.translate(options.annotation.X, options.annotation.Y);
  // continue with previous drawing code
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Small parameter updates for consistency

The callback for `documentViewer.setPagesUpdatedInternalAnnotationsTransform` now provides 1-indexed page numbers instead of 0-indexed page numbers.

Before:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
docViewer.setPagesUpdatedInternalAnnotationsTransform((xfdfData, pageList, callback) => {
  console.log(pageList); // 0, 1, 2, etc
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

After:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
documentViewer.setPagesUpdatedInternalAnnotationsTransform((xfdfData, pageList, callback) => {
  console.log(pageList); // 1, 2, 3 etc
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

The `documentViewer.select` function expects the point objects to have the `pageNumber` property instead of `pageIndex`.

Before:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
const location1 = {
  x: 0,
  y: 100,
  pageIndex: 0
};

const location2 = {
  x: 100,
  y: 200,
  pageIndex: 0
};

docViewer.select(location1, location2);
```

{% endcode %}
{% endtab %}
{% endtabs %}

After:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
const location1 = {
  x: 0,
  y: 100,
  pageNumber: 1
};

const location2 = {
  x: 100,
  y: 200,
  pageNumber: 1
};

documentViewer.select(location1, location2);
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Deprecated usages

To make the API more consistent many boolean APIs have mostly moved to the form `enableXYZ/disableXYZ/isXYZEnabled`. The previous functions are still accessible but have been deprecated.

AnnotationManager

* `setReadOnly` becomes `enableReadOnlyMode`, `disableReadOnlyMode`, `isReadOnlyModeEnabled`
* `setFreeformRotationEnabled` becomes `enableFreeformRotation`, `disableFreeformRotation`
* `enableRedaction(boolean)` becomes `enableRedaction`, `disableRedaction`
* `setIsAdminUser`, `getIsAdminUser` becomes `promoteUserToAdmin`, `demoteUserFromAdmin`, `isUserAdmin`

DocumentViewer

* `getRightToLeftPages`, `setRightToLeftPages` becomes `enableRightToLeftPageRendering`, `disableRightToLeftPageRendering`, `isRightToLeftPageRenderingEnabled`
* `setLoadAnnotationsFromVisiblePages` becomes `enableLoadingAnnotationsFromVisiblePages`, `disableLoadingAnnotationsFromVisiblePages`
* `setEnableAutomaticLinking` becomes `enableAutomaticLinking`, `disableAutomaticLinking`
* `setEnableStylusMode` becomes `enableStylusMode`, `disableStylusMode`

Document

* `enableColorSeparations(boolean)` becomes `enableColorSeparations`, `disableColorSeparations`
* `setOfflineModeEnabled` becomes `enableOfflineMode`, `disableOfflineMode`

Tools

* `AnnotationSelectTool.setEnableImmediateActionOnAnnotationSelection` becomes `AnnotationSelectTool.enableImmediateActionOnAnnotationSelection`, `AnnotationSelectTool.disableImmediateActionOnAnnotationSelection`
* `DistanceMeasurementCreateTool.setEnableLeaderLines` becomes `DistanceMeasurementCreateTool.enableLeaderLines`, `DistanceMeasurementCreateTool.disableLeaderLines`
* `RedactionCreateTool.setEnableTextAutoSize` becomes `RedactionCreateTool.enableAutoSizedText`, `RedactionCreateTool.disableAutoSizedText`
* `setAllowCreationOverAnnotation` becomes `enableCreationOverAnnotation`, `disableCreationOverAnnotation`

Other

* `CoreControls.enableFullPDF(boolean)` becomes `Core.enableFullPDF`, `Core.disableFullPDF`
* `annotation.setRotationControlEnabled` becomes `annotation.enableRotationControl`, `annotation.disableRotationControl`
* `popupAnnotation.setOpen` becomes `popupAnnotation.open`, `popupAnnotation.close`
* WebViewer constructor option `pdftronServer` becomes `webviewerServerURL`

## APIs removed

Previously deprecated APIs that have been removed:

* The deprecated `new PDFTron.WebViewer` constructor for WebViewer has been removed.

Before:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
const viewerInstance = new PDFTron.WebViewer(options, viewerElement);
viewerElement.addEventListener('ready', () => {
  // viewer ready
});
```

{% endcode %}
{% endtab %}
{% endtabs %}

After:

{% tabs %}
{% tab title="JavaScript" %}
{% code lineNumbers="true" %}

```js
WebViewer(options, viewerElement)
  .then(viewerInstance => {
    // viewer ready
  });
```

{% endcode %}
{% endtab %}
{% endtabs %}

* `annotation.getLeft()` use `annotation.getX()` or `annotation.X` instead
* `annotation.getRight()` use `annotation.X + annotation.Width` instead
* `annotation.getTop()` use `annotation.getY()` or `annotation.Y` instead
* `annotation.getBottom()` use `annotation.Y + annotation.Height` instead
* `signatureWidget.isSignedInitially()` use `signatureWidget.isSignedDigitally()` instead
* `annotationManager.getAnnotCommand()` use `annotationManager.exportAnnotCommand()` instead


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/web/migration-guides/migrating-to-v8.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
