> 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/compare-files/diffing.md).

# Compare PDFs by Overlaying PDFs in JavaScript Document Viewer

Overlay and compare PDFs effortlessly with WebViewer's Full API. Generate a new PDF highlighting differences between two files, preserving text and searchability. Ideal for visual document comparisons

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

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

<a href="https://apryse.com/capabilities#Compare" class="button primary">Package: Compare</a><a href="/web/full-api/full-api-overview.md" class="button primary">Full API</a><a href="https://showcase.apryse.com/compare-files" class="button primary">Live demo</a>
{% endhint %}

{% hint style="warning" %}
Make sure you have [Full API enabled in WebViewer.](/web/what-is-webviewer/full-api.md)
{% endhint %}

WebViewer can take two PDF files and output the visual difference between them by overlaying the PDFs and generating a new PDF out of it. The generated PDF will preserve text and searchability. This can be useful in situations where you want to visually see the difference between two versions of a document (a blueprint for example). Check out the [demo](https://showcase.apryse.com/semantic-text-compare).

**In our config.js file (see** [**this guide**](/web/advanced/config-files.md) **for more information on config files)**, we start by waiting for WebViewer to fully initialize by waiting for the [`viewerLoaded`](/web/advanced/config-files.md#useful-events) event to fire. Once this is done, we can initialize the full API and get the documents into memory.

We'll start by writing a function that takes a URL and resolves with a document, and then use that function to load two sample documents.

{% hint style="warning" %}
The following code snippets are written using ES6+ syntax, which will only work in modern browsers. You may, however, transpile this code down to ES5 to ensure proper browser support. [See this guide](/web/get-started/faq/transpiling-javascript.md) for more details.
{% endhint %}

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}

<pre class="language-js" data-line-numbers><code class="lang-js">window.addEventListener('viewerLoaded', async () => {
  // initialize PDFNet
  await PDFNet.initialize('<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>');

  const getDocument = async (url) => {
    const newDoc = await Core.createDocument(url);
    return await newDoc.getPDFDoc();
  };

  const [doc1, doc2] = await Promise.all([
    getDocument('https://s3.amazonaws.com/pdftron/pdftron/example/test_doc_1.pdf'),
    getDocument('https://s3.amazonaws.com/pdftron/pdftron/example/test_doc_2.pdf')
  ])
});
</code></pre>

[Core.createDocument](https://sdk.apryse.com/api/web/Core.html#.createDocument__anchor) [Document.getPDFDoc](https://sdk.apryse.com/api/web/Core.Document.html#getPDFDoc__anchor) [Enabling full API](/web/full-api/full-api-overview.md)
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}

<pre class="language-js" data-line-numbers><code class="lang-js">window.addEventListener('viewerLoaded', async () => {
  // initialize PDFNet
  await PDFNet.initialize('<code class="expression">visitor.claims.wvKey || "YOUR_LICENSE_KEY"</code>');

  const getDocument = async (url) => {
    const newDoc = await CoreControls.createDocument(url);
    return await newDoc.getPDFDoc();
  };

  const [doc1, doc2] = await Promise.all([
    getDocument('https://s3.amazonaws.com/pdftron/pdftron/example/test_doc_1.pdf'),
    getDocument('https://s3.amazonaws.com/pdftron/pdftron/example/test_doc_2.pdf')
  ])
});
</code></pre>

[CoreControls.createDocument](https://sdk.apryse.com/api/web/Core.html#.createDocument__anchor) [Document.getPDFDoc](https://sdk.apryse.com/api/web/Core.Document.html#getPDFDoc__anchor) [Enabling full API](/web/full-api/full-api-overview.md)
{% endtab %}
{% endtabs %}

Now we need to get the pages that we want to diff. In this example, we will diff all pages. We'll write a helper function to help us get the pages into an array, and then use that function to get the pages for both our documents.

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

```js
// inside `viewerLoaded`
const getPageArray = async (doc) => {
  const arr = [];
  const itr = await doc.getPageIterator(1);

  for (itr; await itr.hasNext(); itr.next()) {
    const page = await itr.current();
    arr.push(page);
  }

  return arr;
}

const [doc1Pages, doc2Pages] = await Promise.all([
  getPageArray(doc1),
  getPageArray(doc2)
]);
```

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

[PDFDoc.getPageIterator](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#getPageIterator__anchor) [Iterator.hasNext](https://sdk.apryse.com/api/web/Core.PDFNet.Iterator.html#hasNext__anchor) [Iterator.next](https://sdk.apryse.com/api/web/Core.PDFNet.Iterator.html#next__anchor)

Now we can create a new blank document, and fill it with the diffed images from our two documents. Once that is done, we can tell WebViewer to display this new diffed document.

{% tabs %}
{% tab title="JavaScript (SDK v8.0+)" %}
{% code lineNumbers="true" %}

```js
// inside `viewerLoaded`
const newDoc = await PDFNet.PDFDoc.create();
newDoc.lock();

// we'll loop over the doc with the most pages
const biggestLength = Math.max(doc1Pages.length, doc2Pages.length)

for(let i = 0; i < biggestLength; i++) {
    let page1 = doc1Pages[i];
    let page2 = doc2Pages[i];

    // handle the case where one document has more pages than the other
    if (!page1) {
      page1 = await doc1.pageCreate(); // create a blank page
    } 
    if (!page2) {
      page2 = await doc2.pageCreate(); // create a blank page
    }
    await newDoc.appendVisualDiff(page1, page2)
}

newDoc.unlock(); 

// display the document!
// instance is a global variable thats automatically defined inside the config file.
instance.UI.loadDocument(newDoc);
```

{% endcode %}

[PDFDoc.create](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#.create__anchor) [PDFDoc.appendVisualDiff](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#appendVisualDiff__anchor)
{% endtab %}

{% tab title="JavaScript (SDK v6.0+)" %}
{% code lineNumbers="true" %}

```js
// inside `viewerLoaded`
const newDoc = await PDFNet.PDFDoc.create();
newDoc.lock();

// we'll loop over the doc with the most pages
const biggestLength = Math.max(doc1Pages.length, doc2Pages.length)

for(let i = 0; i < biggestLength; i++) {
    let page1 = doc1Pages[i];
    let page2 = doc2Pages[i];

    // handle the case where one document has more pages than the other
    if (!page1) {
      page1 = await doc1.pageCreate(); // create a blank page
    } 
    if (!page2) {
      page2 = await doc2.pageCreate(); // create a blank page
    }
    await newDoc.appendVisualDiff(page1, page2)
}

newDoc.unlock();

// display the document!
// readerControl is a global variable thats automatically defined inside the config file.
readerControl.loadDocument(newDoc);
```

{% endcode %}

[PDFDoc.create](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#.create__anchor) [PDFDoc.appendVisualDiff](https://sdk.apryse.com/api/web/Core.PDFNet.PDFDoc.html#appendVisualDiff__anchor)
{% endtab %}
{% endtabs %}

The full code sample should look like this:

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

```js
Webviewer({
  fullAPI: true,
  path: '/lib',
}, document.getElementById('viewer')).then(instance => {

  const { PDFNet } = instance.Core;

  instance.UI.addEventListener('viewerLoaded', async () => {
    // initialize PDFNet
    await PDFNet.initialize();

    const getDocument = async (url) => {
      const newDoc = await instance.Core.createDocument(url);
      return await newDoc.getPDFDoc();
    };

    const [doc1, doc2] = await Promise.all([
      getDocument('https://s3.amazonaws.com/pdftron/pdftron/example/test_doc_1.pdf'),
      getDocument('https://s3.amazonaws.com/pdftron/pdftron/example/test_doc_2.pdf')
    ])

    // inside `viewerLoaded`
    const getPageArray = async (doc) => {
      const arr = [];
      const itr = await doc.getPageIterator(1);

      for (itr; await itr.hasNext(); itr.next()) {
        const page = await itr.current();
        arr.push(page);
      }

      return arr;
    }

    const [doc1Pages, doc2Pages] = await Promise.all([
      getPageArray(doc1),
      getPageArray(doc2)
    ]);

    console.log(doc1Pages, doc2Pages);

    const newDoc = await PDFNet.PDFDoc.create();
    newDoc.lock();

    // we'll loop over the doc with the most pages
    const biggestLength = Math.max(doc1Pages.length, doc2Pages.length)

    for(let i = 0; i < biggestLength; i++) {
      let page1 = doc1Pages[i];
      let page2 = doc2Pages[i];

      // handle the case where one document has more pages than the other
      if (!page1) {
        page1 = await doc1.pageCreate(); // create a blank page
      } 
      if (!page2) {
        page2 = await doc2.pageCreate(); // create a blank page
      }
      await newDoc.appendVisualDiff(page1, page2)
    }
    
    newDoc.unlock(); 

    // display the document!
    // instance is a global variable thats automatically defined inside the config file.
    instance.UI.loadDocument(newDoc);

  });
});
```

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

WebViewer should now display the diffed document, like the image below.

![](https://3532544125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FX9YnTSKIHvV7m0A36LbO%2Fuploads%2Fgit-blob-e94850f7cdd425dea2232a20043cff8892788b8e%2F21dfb9fc309ec3450cebc5ee290038b2f093ec48-653x853.png?alt=media)

In this example:

* Blue represents content that is in document one and not document two.
* Red represents content in document two that is not in document one.
* Black represents overlap.

{% hint style="info" %}
Behind the scenes, WebViewer blends the two documents using the Porter/Duff 'darken' operator and displays the output.
{% endhint %}


---

# 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/compare-files/diffing.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.
