Integrate WebViewer into Node.js to Enable Saving and Loading XFDF Annotations

In addition to showing how to add the WebViewer component to a Node.js backend project, this sample expands on annotation functionality by enabling saving and loading the annotation objects into XFDF.

The saving and loading are done through POST and GET HTTP messages that are handled by an annotationHandler in the project. The annotations are stored as XFDF strings embedded in the messages and saved as XFDF files on disk.

Follow these steps:

  1. Create annotations with annotations tools in the header.
  2. Save annotations with the save button in the header.
  3. Load annotations by refreshing the app.
  4. Locate an XFDF file in server/xfdf folder where the annotations data saved into.

WebViewer provides a slick out-of-the-box responsive UI that enables you to view, annotate and manipulate PDFs and other document types inside any web project.

Click the button below to view the full project in GitHub.

1var viewerElement = document.getElementById('viewer');
2var DOCUMENT_ID = 'webviewer-demo-1';
3
4WebViewer({
5 path: 'lib',
6 initialDoc: 'https://pdftron.s3.amazonaws.com/downloads/pl/demo.pdf',
7 documentXFDFRetriever: () => loadXfdfString(DOCUMENT_ID)
8}, viewerElement).then(instance => {
9 var { annotationManager } = instance.Core;
10
11 // Add a save button on header
12 const topHeader = instance.UI.getModularHeader('default-top-header');
13 const items = topHeader.getItems();
14
15 const saveButton = {
16 type: 'customButton',
17 img: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z"/></svg>',
18 title: 'Save Annotations',
19 onClick: function() {
20 // Save annotations when button is clicked
21 // widgets and links will remain in the document without changing so it isn't necessary to export them
22 annotationManager.exportAnnotations({ links: false, widgets: false }).then(function(xfdfString) {
23 saveXfdfString(DOCUMENT_ID, xfdfString).then(function() {
24 alert('Annotations saved successfully.');
25 });
26 });
27 }
28 };
29
30 items.push(saveButton);
31 topHeader.setItems(items);
32});
33
34// Make a POST request with XFDF string
35var saveXfdfString = function(documentId, xfdfString) {
36 return new Promise(function(resolve) {
37 fetch(`/server/annotationHandler.js?documentId=${documentId}`, {
38 method: 'POST',
39 body: xfdfString
40 }).then(function(response) {
41 if (response.status === 200) {
42 resolve();
43 }
44 });
45 });
46};
47
48// Make a GET request to get XFDF string
49var loadXfdfString = function(documentId) {
50 return new Promise(function(resolve) {
51 fetch(`/server/annotationHandler.js?documentId=${documentId}`, {
52 method: 'GET'
53 }).then(function(response) {
54 if (response.status === 200) {
55 response.text().then(function(xfdfString) {
56 resolve(xfdfString);
57 })
58 }
59 });
60 });
61};
62

Did you find this helpful?

Trial setup questions?

Ask experts on Discord

Need other help?

Contact Support

Pricing or product questions?

Contact Sales