# # Compare PDFs by overlaying PDF pages

Starting from version 9.0.0, Foxit PDF SDK for Web provides APIs to support comparing PDFs by overlaying PDF pages, and then generate a new comparison result image which can intuitively display the differences between the two PDF pages.

## # A Simple Example

In this section, a simple example will be provided to show you how to compare PDFs by overlaying PDF pages. It includes: open document, create document, get PDF page, compare pages, generate result page, and export the result document.

First, initialize a blank project, and then the subsequent operations will be done based on this project.

    const libPath = window.top.location.origin + '/lib';
    const pdfViewer = new PDFViewCtrl.PDFViewer({
        libPath: libPath,
        jr: {
            licenseSN: licenseSN,
            licenseKey: licenseKey
        }
    });
    pdfViewer.init(document.body);

### # Load the documents that need to be compared

    async function loadFiles() {
        const sourcePDFDoc = await pdfViewer.loadPDFDocByHttpRangeRequest({
            range: {
                url: '/assets/test-doc1.pdf'
            }
        });
        const targetPDFDoc = await pdfViewer.loadPDFDocByHttpRangeRequest({
            range: {
                url: '/assets/test-doc2.pdf'
            }
        });
        return { sourcePDFDoc, targetPDFDoc };
    }

Foxit PDF SDK for Web provides two ways to load documents:

1. loadPDFDocByHttpRangeRequest
   (opens new window) : using this API, you can asynchronously load PDF documents as required from remote.

2. loadPDFDocByFile
   (opens new window) : using this API, you can load the file stream in the memory, or select files on the hard disk via `<input type="file">`.

The purpose of loading documents is that you can edit and read the documents without rendering them. Here is just for demonstration, so we use this method. In an actual project, you can also do further operations by getting the current opened document (PDFViewer.getCurrentPDFDoc
(opens new window)).

### # Create a blank document

The blank document will be used to save the comparison result.

    function createBlankDoc() {
        return pdfViewer.createNewDoc();
    }

### # Get the bitmaps of the pages that need to be compared

    async function getPageBitmaps(loadedFiles) {
        const { sourcePDFDoc, targetPDFDoc } = loadedFiles;

        const sourcePage = await sourcePDFDoc.getPageByIndex(0);
        const sourceBitmap = await sourcePage.render(1);

        const targetPage = await targetPDFDoc.getPageByIndex(0);
        const targetBitmap = await targetPage.render(1);

        return { sourceBitmap, targetBitmap };
    }

If you need to use other options (for example scale and rotation), please input parameters (scale, rotate) in the `render` method, and refer to the PDFPage.render
(opens new window) interface in the API Reference.

### # Start to compare documents

    function comparePageBitmap(sourceBitmap, targetBitmap) {
        const DiffColor = PDFViewCtrl.overlayComparison.DiffColor;
        const service = pdfViewer.getOverlayComparisonService();
        const resultCanvas = service.compareImageData({
            sourceBitmap,
            targetBitmap,
            combinePixelsOptions: {
                showDiffColor: true,
                sourceDiffColor: DiffColor.RED,
                targetDiffColor: DiffColor.BLUE,
                sourceOpacity: 0xFF,
                targetOpacity: 0xFF
            },
            transformation: {
                translateX: 0,
                translateY: 0,
                rotate: 2 / 180 * Math.PI
            }
        });
        return new Promise(resolve => {
            resultCanvas.toBlob(blob => {
                const fr = new FileReader();
                fr.onloadend = () => {
                    resolve({
                        buffer: fr.result,
                        width: resultCanvas.width,
                        height: resultCanvas.height
                    });
                };
                fr.readAsArrayBuffer(blob);
            });
        })
    }

Note:

1. The differences between the documents will be marked only when `shouldShowDiff` is set to `true`.
2. `sourceDiffColor` and `targetDiffColor` do not support all colors, you must select one of the colors in the `DiffColor` enumeration.
3. The value range of `sourceOpacity` and `targetOpacity` is `0~0xFF`.

### # Insert the comparison result into the PDF page

    async function insertResultIntoNewDoc(newDoc, resultImageData) {
        const page = await newDoc.getPageByIndex(0);

        // resultImageData is the return object of the comparePageBitmap function mentioned in the above example.
        // Convert the unit of width and height from pixel to point.
        const newPageWidth = resultImageData.width / 4 * 3;
        const newPageHeight = resultImageData.height / 4 * 3;
        // Reset the size of PDF page to make it the same size as the comparison image.
        await page.setPageSize(newPageWidth, newPageHeight);
        // Last, insert it into the PDF page as a PDF image object.
        await page.addImage(resultImageData.buffer, {
            left: 0,
            right: newPageWidth,
            bottom:0,
            top: newPageHeight
        });
    }

### # Export the result document

    async function exportResultPDFDocFile(newDoc) {
        return newDoc.getFile();
    }

`PDFDoc.getFile()`
(opens new window) interface will return the blob object of the document. You can use the general method to download this file stream or open the document by `PDFViewer.openPDFByFile`
(opens new window) interface to see the effect.

### # The final effect

Integrate all the above code, the final effect is as follows, please click `run` button to run the example:

    <html>
    </html>
    <script>
        const libPath = window.top.location.origin + '/lib';
        const pdfViewer = new PDFViewCtrl.PDFViewer({
            libPath: libPath,
            jr: {
                licenseSN: licenseSN,
                licenseKey: licenseKey
            }
        });
        pdfViewer.init(document.body);

        (async () => {
            const loadedFiles = await loadFiles();
            const newDoc = await createBlankDoc();

            const { sourceBitmap, targetBitmap } = await getPageBitmaps(loadedFiles);
            const resultImageData = await comparePageBitmap(sourceBitmap, targetBitmap);

            await pdfViewer.zoomTo(1);

            await insertResultIntoNewDoc(newDoc, resultImageData);
        })()

        async function loadFiles() {
            const sourcePDFDoc = await pdfViewer.loadPDFDocByHttpRangeRequest({
                range: {
                    url: '/assets/test-doc1.pdf'
                }
            });
            const targetPDFDoc = await pdfViewer.loadPDFDocByHttpRangeRequest({
                range: {
                    url: '/assets/test-doc2.pdf'
                }
            });
            return { sourcePDFDoc, targetPDFDoc };
        }
        function createBlankDoc() {
            return pdfViewer.createNewDoc();
        }
        async function getPageBitmaps(loadedFiles) {
            const { sourcePDFDoc, targetPDFDoc } = loadedFiles;

            const sourcePage = await sourcePDFDoc.getPageByIndex(0);
            const sourceBitmap = await sourcePage.render(1);

            const targetPage = await targetPDFDoc.getPageByIndex(0);
            const targetBitmap = await targetPage.render(1);

            return { sourceBitmap, targetBitmap };
        }
        function comparePageBitmap(sourceBitmap, targetBitmap) {
            const DiffColor = PDFViewCtrl.overlayComparison.DiffColor;
            const service = pdfViewer.getOverlayComparisonService();
            const resultCanvas = service.compareImageData({
                sourceBitmap, targetBitmap,
                combinePixelsOptions: {
                    showDiffColor: true,
                    sourceDiffColor: DiffColor.RED,
                    targetDiffColor: DiffColor.BLUE,
                    sourceOpacity: 0xFF,
                    targetOpacity: 0xFF
                },
                transformation: {
                    translateX: 0,
                    translateY: 0,
                    rotate: 2 / 180 * Math.PI
                }
            });
            return new Promise(resolve => {
                resultCanvas.toBlob(blob => {
                    const fr = new FileReader();
                    fr.onloadend = () => {
                        resolve({
                            buffer: fr.result,
                            width: resultCanvas.width,
                            height: resultCanvas.height
                        });
                    };
                    fr.readAsArrayBuffer(blob);
                });
            })
        }
        async function insertResultIntoNewDoc(newDoc, resultImageData) {
            const page = await newDoc.getPageByIndex(0);

            const newPageWidth = resultImageData.width / 4 * 3;
            const newPageHeight = resultImageData.height / 4 * 3;
            await page.setPageSize(newPageWidth, newPageHeight);
            await page.addImage(resultImageData.buffer, {
                left: 0,
                right: newPageWidth,
                bottom:0,
                top: newPageHeight
            });
        }
        async function exportResultPDFDocFile(newDoc) {
            return newDoc.getFile();
        }
    </script>

    {
        "iframeOptions": {
            "style": "height: 800px"
        }
    }

Note:

> To minimize the interference of irrelevant code and make the code more intuitive, the above example is written using ESNext syntax. Please use a modern browser to open the developer guide document and run the example. If you need to be compatible with older browsers, please use JavaScript transpiler such as babel in your project.