> 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/viewercustomsavetest.md).

# Customize a PDF before Saving - Sample Code

JavaScript sample that lets you customize or inject information into a PDF document before saving or downloading. Add stamps or watermarks before downloading

{% 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 customize or modify a PDF, DOCX, XLSX or PPTX document before it is saved/downloaded (client-side, no servers or other external dependencies required).

In this example annotations are flattened before saving the PDF. This functionality allows you to do a variety of modifications to the document before downloading – some common examples include adding stamps / watermarks to the PDF or injecting information into a document.

In the case of MS Office files, modifications are saved into the PDF file. The use of MS Office files will require the [Office Conversion Package](https://apryse.com/capabilities#OfficeConversion). (More WebViewer options: you could also manipulate DOCX or XSLX files directly with our [DOCX Editor](/web/docx-editor/docx-editor.md) and [Spreadsheet Editor](/web/spreadsheet-editor/spreadsheet-editor.md).)

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

Learn more about our [Web SDK](/web/get-started/guides.md).

### **Implementation steps**

To customize and save PDF documents with JavaScript using WebViewer:

Step 1: Follow [get started in your preferred web stack for WebViewer](/web/get-started/readme.md) Step 2: [Enable the full API ](/web/full-api/full-api-overview.md)by passing the `fullAPI` option into the WebViewer constructor 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 mergeAndSave = (doc, xfdf) => {
    const main = async () => {
      // Import XFDF into FDF, then merge data from FDF into PDF
      // Annotations
      const fdfDoc = await PDFNet.FDFDoc.createFromXFDF(xfdf);

      const pitr = await doc.getPageIterator();
      let page;
      let annotObj;
      /* eslint no-await-in-loop: 0 */
      for (; await pitr.hasNext(); pitr.next()) {
        try {
          page = await pitr.current();
          for (let i = await page.getNumAnnots(); i > 0; ) {
            annotObj = await page.getAnnot(--i);
            switch (await annotObj.getType()) {
              case PDFNet.Annot.Type.e_Widget:
              case PDFNet.Annot.Type.e_Link:
              case PDFNet.Annot.Type.e_Sound:
              case PDFNet.Annot.Type.e_Movie:
              case PDFNet.Annot.Type.e_FileAttachment:
                // these are not supported for import from webviewer
                break;
              default:
                page.annotRemoveByIndex(i);
                break;
            }
          }
        } catch (e) {
          console.log('Error Removing Annotations: ' + e);
          (await page.getSDFObj()).erase('Annots');
        }
      }

      await doc.fdfMerge(fdfDoc);

      // run any custom logic here
      await doc.flattenAnnotations();

      const docbuf = await doc.saveMemoryBuffer(PDFNet.SDFDoc.SaveOptions.e_linearized);
      return docbuf;
    };
    // add your own license key as the second parameter, e.g. PDFNet.runWithCleanup(main, '<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>')
    return PDFNet.runWithCleanup(main);
  };

  const customDownload = options => {
    const documentViewer = instance.Core.documentViewer;
    const am = documentViewer.getAnnotationManager();
    const annotationsToRemove = am.getAnnotationsList();
    const currentDocument = documentViewer.getDocument();
    return PDFNet.initialize()
      .then(() => currentDocument.getPDFDoc())
      .then(pdfDoc => mergeAndSave(pdfDoc, options.xfdfString))
      .then(data => {
        // since we are flattening annotations we should remove the existing annotations in webviewer
        // and rerender so that the file displays correctly

        am.deleteAnnotations(annotationsToRemove);
        // clear the cache
        documentViewer.refreshAll();
        // update viewer with new document
        documentViewer.updateView();
        // Annotations may contain text so we need to regenerate
        // our text representation
        documentViewer.getDocument().refreshTextData();
        return data;
      });
  };

  window.addEventListener('documentLoaded', () => {
    const doc = instance.Core.documentViewer.getDocument();
    doc.getFileData = customDownload;
  });
})(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/viewercustomsavetest.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.
