# 通过叠加 PDF 来比较 PDF

9.0.0 版本开始,我们开始提供接口支持通过叠加PDF页面来对比差异,并生成新的比较结果图片, 通过这个比较结果图片,可以很直观地看出来两个PDF页面的差异。

# 一个简单的示例

接下来,我们将通过代码示例展示如何使用叠加比较 API, 包括:打开文档、创建文档、获取页面、比较页面、生成结果页面、导出结果文档。

在开始前,我们先初始化一个空白项目,后续的示例将基于此项目逐步完善代码以实现最终效果:

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

# 加载待比较的文档

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 };
}

FoxitPDFSDKForWeb 提供了两种方法加载文档:

  1. loadPDFDocByHttpRangeRequest (opens new window) 使用它可以从远程异步按需加载PDF文档
  2. loadPDFDocByFile (opens new window) 使用它可以加载内存中的文件流,或者通过 <input type="file"> 选择的硬盘上的文件。

加载文档的目的是可以在不渲染文件的情况下对其进行编辑和读取操作。这里是为了演示,所以这么做。在实际项目中,您也可以通过获取当前打开的文档(PDFViewer.getCurrentPDFDoc (opens new window)),进行更进一步的操作。

# 创建一个空文档

空文档将用来保存比较的结果

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

# 获取待比较页面的 bitmap

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 };
}

如果您需要使用其他缩放比例和旋转角度等,请在 render 方法中输入 scale, rotate 等参数,请参考 API Reference PDFPage.render (opens new window) 接口

# 开始比较

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: 15 / 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);
        });
    })
}

注意事项:

  1. shouldShowDiff 指定为 true 时才会显示差异颜色;
  2. sourceDiffColortargetDiffColor 不支持任意颜色,必须选择 DiffColor 枚举中的某一项颜色;
  3. sourceOpacitytargetOpacity 值的范围是 0~0xFF

# 将比较结果插入到PDF页面中

async function insertResultIntoNewDoc(newDoc, resultImageData) {
    const page = await newDoc.getPageByIndex(0);
    
    // resultImageData 就是上一个示例 comparePageBitmap 方法返回的对象
    // 这里使用到的width,height值是像素单位,我们要先转换成 PDF 的 point 单位。
    const newPageWidth = resultImageData.width / 4 * 3;
    const newPageHeight = resultImageData.height / 4 * 3;
    // 重置PDF页面大小,使其和比较结果图片的大小相同
    await page.setPageSize(newPageWidth, newPageHeight);
    // 最后将其作为PDF图片对象插入到PDF页面中
    await page.addImage(resultImageData.buffer, {
        left: 0,
        right: newPageWidth,
        bottom:0,
        top: newPageHeight
    });
}

# 导出结果文档

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

PDFDoc.getFile() (opens new window) 接口将返回文档的 Blob 对象, 我们可以使用常规手段下载这个文件流或者再通过 PDFViewer.openPDFByFile (opens new window) 接口打开文档,查看效果。

# 最终效果

我们整合上面所有代码,最终效果如下,请点击 run 按钮运行示例:

注意:

为了尽量减少不相干代码的干扰,保证代码更加直观,上面的示例使用了 ESNext 语法编写,请使用现代浏览器打开我们的 developer-guide 文档和运行示例。如果需要兼容旧版本浏览器,请在您的项目中使用 babel 等工具转译。