Skip to content

PDF 图像差异比较

从 9.0.0 版本开始,福昕 PDF SDK Web 版提供了 API 来支持通过叠加 PDF 页面来对比 PDF。该功能对两个文档进行逐像素对比,找出其中的不同之处,并生成一个输出图像。开发人员可以利用 SDK 提供的这些 API 在自己的应用中实现覆盖比较的功能。

功能概述

TIP

本节将提供一个完整的示例来展示如何通过叠加 PDF 页面来比较 PDF,包含以下步骤:

  • 打开文档
  • 创建文档
  • 获取 PDF 页面
  • 比较页面
  • 生成结果页面
  • 导出结果文档

初始化 PDFViewer

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

加载待比较的文档

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

文档加载方式

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

  1. loadPDFDocByHttpRangeRequest: 从远程异步按需加载 PDF 文档
  2. loadPDFDocByFile: 加载内存中的文件流,或通过 <input type="file"> 选择本地文件

在实际项目中,您也可以通过 PDFViewer.getCurrentPDFDoc 获取当前打开的文档来进行操作。

创建空白文档

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

获取待比较页面的 bitmap

javascript
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 接口说明。

开始比较文档

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

比较参数说明

  1. showDiffColor: 设置为 true 时才会标记文档差异
  2. sourceDiffColortargetDiffColor: 必须使用 DiffColor 枚举中的预定义颜色
  3. sourceOpacitytargetOpacity: 取值范围为 0~0xFF

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

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

将比较结果添加到新文档

javascript
async function addComparisonResultToNewDoc(resultBitmap) {
    const newDoc = await createBlankDoc();
    const page = await newDoc.addPage(resultBitmap.width, resultBitmap.height);
    const image = await page.addImage(resultBitmap.buffer);
    const matrix = PDFViewCtrl.PDF.PDFMatrix.create();
    await page.addImageObject(image, matrix);
    return newDoc;
}

导出结果文档

javascript
async function exportDoc(doc) {
    const file = await doc.getFile();
    const url = URL.createObjectURL(file);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'comparison-result.pdf';
    a.click();
    URL.revokeObjectURL(url);
}

完整示例

TIP

以下是将所有步骤组合在一起的完整示例代码:

javascript
(async function() {
    const loadedFiles = await loadFiles();
    const {sourceBitmap, targetBitmap} = await getPageBitmaps(loadedFiles);
    const resultBitmap = await comparePageBitmap(sourceBitmap, targetBitmap);
    const resultDoc = await addComparisonResultToNewDoc(resultBitmap);
    await exportDoc(resultDoc);
})();

API 说明

PDFViewer.getOverlayComparisonService()

返回一个 OverlayComparisonService 实例,用于比较两个图片。

OverlayComparisonService.compareImageData(options)

比较两个图片并返回一个包含比较结果的 Canvas 对象。

参数说明:

typescript
interface CompareImageDataOptions {
    // 源图片的 ImageData 对象
    sourceBitmap: ImageData;
    // 目标图片的 ImageData 对象
    targetBitmap: ImageData;
    // 图片合成选项
    combinePixelsOptions: {
        // 是否显示差异颜色,默认为 false
        showDiffColor?: boolean;
        // 源图片差异部分的颜色
        sourceDiffColor?: DiffColor;
        // 目标图片差异部分的颜色
        targetDiffColor?: DiffColor;
        // 源图片的透明度,取值范围 0~0xFF
        sourceOpacity?: number;
        // 目标图片的透明度,取值范围 0~0xFF
        targetOpacity?: number;
    };
    // 图片变换选项
    transformation?: {
        // X轴平移距离
        translateX?: number;
        // Y轴平移距离
        translateY?: number;
        // 旋转角度(弧度)
        rotate?: number;
    };
}

DiffColor 枚举值

可用的差异颜色选项:

  • DiffColor.RED
  • DiffColor.GREEN
  • DiffColor.BLUE
  • DiffColor.YELLOW
  • DiffColor.MAGENTA
  • DiffColor.CYAN

相关 API 参考

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

TIP

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