> 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/get-started/samples/viewerpreprocesstest.md).

# Preprocess document before viewing

JavaScript to modify or preprocess a PDF, DOCX, XLSX, PPTX before displaying in browser. Fix corrupt content or add a watermark before displaying your documents

{% hint style="info" %}
**Requirements**

*These packages are required to use these features in production. Trial keys have unlimited access to all features*

<a href="/web/get-started/readme.md" class="button primary">Web SDK</a><a href="/web/full-api/full-api-overview.md" class="button primary">Full API</a>
{% endhint %}

This JavaScript sample lets you preprocess and make modifications to a PDF, DOCX, XLSX or PPTX document before it is displayed in WebViewer (no servers or other external dependencies required). This is often used to fix corrupt PDF content or add watermarks to documents before displaying them.

A common example of how this functionality is used occurs when user ID, timestamp and IP address is added to a document before loading to limit document sharing.

In the case of MS Office files, modifications are applied to the PDF file and will require the [Office Conversion Package ](https://apryse.com/capabilities#OfficeConversion)for WebViewer license.

This sample works on all browsers (including IE11) and mobile devices without using plug-ins.

### **Implementation steps**

Step 1: [Get started with WebViewer](/web/get-started/readme.md) in your preferred web stack

Step 2: [Enable the full API](/web/full-api/full-api-overview.md)

Step 3: Add the sample code provided in this guide

This full sample is one of many included in the [manual download of WebViewer.](/web/get-started/manually.md#1-download-webviewer)

<pre class="language-js" data-line-numbers><code class="lang-js">(exports => {
  const PDFNet = exports.Core.PDFNet;
  const runScript = () => {
    const ModAnnotations = async doc => {
      const imagefile = '../../samples/full-apis/TestFiles/grayscale.tif';

      await PDFNet.startDeallocateStack(); // start stack-based deallocation. All objects will be deallocated by end of function
      // The following code snippet traverses all annotations in the document
      console.log('Traversing all annotations in the document...');
      const apWriter = await PDFNet.ElementWriter.create();
      const apBuilder = await PDFNet.ElementBuilder.create();

      const sigImg = await PDFNet.Image.createFromURL(doc, imagefile);

      const itr = await doc.getPageIterator(1);
      let numMod = 0;

      for (itr; await itr.hasNext(); await itr.next()) {
        const page = await itr.current();
        const numAnnots = await page.getNumAnnots();

        for (let i = 0; i &#x3C; numAnnots; ++i) {
          const annot = await page.getAnnot(i);
          if (!(await annot.isValid())) {
            continue;
          }

          const annotType = await annot.getType();
          switch (annotType) {
            case PDFNet.Annot.Type.e_Stamp: {
              apWriter.begin(doc);
              const w = await sigImg.getImageWidth();
              const h = await sigImg.getImageHeight();
              let apElement = await apBuilder.createImageScaled(sigImg, 0, 0, w, h);
              apWriter.writePlacedElement(apElement);
              let apObj = await apWriter.end();
              apObj.putRect('BBox', 0, 0, w, h);
              apObj.putName('Subtype', 'Form');
              apObj.putName('Type', 'XObject');
              apElement = await apBuilder.createFormFromStream(apObj);
              apWriter.writePlacedElement(apElement);
              apObj = await apWriter.end();
              apObj.putRect('BBox', 0, 0, w, h);
              apObj.putName('Subtype', 'Form');
              apObj.putName('Type', 'XObject');
              await annot.setAppearance(apObj);
              numMod += 1;
              break;
            }
            default:
              break;
          }
        }
      }

      console.log('number of annotation modifications: ' + numMod);

      await PDFNet.endDeallocateStack();
    };

    const main = async () => {
      let doc = null;

      try {
        // todo load a document from url
        const inputURL = '../../samples/full-apis/TestFiles/';
        const inputFilename = 'fish_stamped.pdf';
        const url = inputURL + inputFilename;
        console.log('loading document from url: ' + url);
        doc = await PDFNet.PDFDoc.createFromURL(url);
        doc.initSecurityHandler();
        doc.lock();
        console.log('loaded document from url: ' + url);
        // modify annotations
        await ModAnnotations(doc);
        // flatten annotations
        doc.flattenAnnotations();
        console.log('flattened document from url: ' + url);
        return doc;
      } catch (err) {
        console.log(err.stack);
      } finally {
        if (doc) {
          doc.unlock();
        }
      }
    };
    // use "runWithoutCleanup" to lock the document data and run callback, use "with out cleanup" so data isn't cleared afterwards
    // add your own license key as the second parameter, e.g. PDFNet.runWithoutCleanup(main, '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>')
    return PDFNet.runWithoutCleanup(main);
  };

  window.addEventListener('viewerLoaded', () => {
    PDFNet.initialize()
      .then(() => runScript())
      .then(async doc => {
        instance.UI.loadDocument(doc);
        console.log('finished script');
      });
  });
})(window);
// eslint-disable-next-line spaced-comment
//# sourceURL=config.js
</code></pre>


---

# 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/get-started/samples/viewerpreprocesstest.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.
