Skip to content

对比 (Comparison)

对比功能可以帮助用户查看两个版本的 PDF 文档之间的差异。福昕 PDF SDK 提供 APIs 用以逐页比较两个 PDF 文档,并返回文档间的差异。

差异可以定义为三种类型:删除、插入和替换。您可以将这些差异保存为 PDF 文件并标记为注释。

授权说明

  • 要使用对比功能,请确保授权许可文件中包含 Comparison 模块权限。

对比两个 PDF 文档,并将差异保存到一个 PDF 文件中

c++
#include "include/common/fs_common.h"
#include "include/pdf/fs_pdfdoc.h"
#include "include/pdf/fs_pdfpage.h"
#include "include/pdf/fs_search.h"
#include "include/addon/fs_compare.h"
#include "include/common/fxcrt/fx_basic.h"

using namespace foxit;
using namespace common;
using namespace addon;
using namespace pdf;
using namespace foxit::pdf::annots;
...

PDFDoc base_doc("input_base_file");
ErrorCode error_code = base_doc.Load();
if (error_code != foxit::e_ErrSuccess) {
    return 1;
}

PDFDoc compared_doc("input_compared_file");
error_code = compared_doc.Load();
if (error_code != foxit::e_ErrSuccess) {
    return 1;
}

Comparison comparison(base_doc, compared_doc);

// Start comparing.
CompareResults result = comparison.DoCompare(0, 0, Comparison::e_CompareTypeText);
CompareResultInfoArray& oldInfo = result.results_base_doc;
CompareResultInfoArray& newInfo = result.results_compared_doc;
int oldInfoSize = oldInfo.GetSize();
int newInfoSize = newInfo.GetSize();
PDFPage page = compared_doc.GetPage(0);
for (int i=0; i<newInfoSize; i++)
{
    const CompareResultInfo& item = newInfo.GetAt(i);
    CompareResultInfo::CompareResultType type = item.type;
    if (type ompareResultInfo::e_CompareResultTypeDeleteText)
    {
        String res_string;
        res_string.Format((FX_LPCSTR)"\"%s\"", (FX_LPCSTR)String::FromUnicode(item.diff_contents));
        CreateDeleteTextStamp(page, item.rect_array, 0xff0000, WString::FromLocal(res_string), L"Compare : Delete", L"Text");
    }
    else if (type ompareResultInfo::e_CompareResultTypeInsertText)
    {
String res_string;
        res_string.Format((FX_LPCSTR)"\"%s\"", (FX_LPCSTR)String::FromUnicode(item.diff_contents));
        CreateDeleteText(page, item.rect_array, 0x0000ff, WString::FromLocal(res_string), L"Compare : Insert", L"Text");
    }
    else if (type ompareResultInfo::e_CompareResultTypeReplaceText)
    {
        String res_string;
        res_string.Format("[Old]: \"%s\"\r\n[New]: \"%s\"", (FX_LPCSTR)String::FromUnicode(old_info.GetAt(i).diff_contents), (FX_LPCSTR)String::FromUnicode(item.diff_contents));           
        CreateSquigglyRect(page, item.rect_array, 0xe7651a, WString::FromLocal(res_string), L"Compare : Replace", L"Text");
    }
}

// Save the comparison result to a PDF file.
compared_doc.SaveAs(output_directory + L"result.pdf");
C
#include "include/fs_basictypes_c.h"
#include "include/fs_common_c.h"
#include "include/fs_pdfdoc_c.h"
#include "include/fs_pdfpage_c.h"
#include "include/fs_search_c.h"
#include "include/fs_compare_c.h"
#include "include/fx_basic_c.h"

...

FS_PDFDOC_HANDLE base_doc;
FSDK_PDFDoc_Create0(input_base_file, &base_doc);
FSErrorCode error_code = FSDK_PDFDoc_Load(base_doc, NULL);
if (error_code != e_FSErrSuccess) {
FSDK_PDFDoc_Release(base_doc);
    return 1;
}

FS_PDFDOC_HANDLE compared_doc;
FSDK_PDFDoc_Create0(input_compared_file, &compared_doc);
error_code = FSDK_PDFDoc_Load(compared_doc, NULL);
if (error_code != foxit::e_ErrSuccess) {
FSDK_PDFDoc_Release(compared_doc);
    return 1;
}

FS_COMPARISON_HANDLE comparison;
FSDK_Comparison_Create(base_doc, compared_doc, &comparison);

// Start comparing.
FSCompareResults result;
result.base_doc_results_array = NULL;
result.compared_doc_results_array = NULL;
FSDK_Comparison_DoCompare(comparison, 0, 0, e_FSCompareTypeText, &result);
result.base_doc_results_array = malloc(sizeof(FSCompareResultInfo) * result.base_doc_results_array_length); 
for (int i = 0; i < result.base_doc_results_array_length; i++)
result.base_doc_results_array[i].rect_array = NULL;

result.compared_doc_results_array = malloc( sizeof(FSCompareResultInfo) * result.compared_doc_results_array_length);
for (int i = 0; i < result.compared_doc_results_array_length; i++)
result.compared_doc_results_array[i].rect_array = NULL;

FSDK_Comparison_DoCompare(comparison, 0, 0, e_FSCompareTypeText, &result);
for (int i = 0; i < result.base_doc_results_array_length; i++)
    result.base_doc_results_array = malloc(sizeof(FSCompareResultInfo) * result.compared_doc_results_array[i].array_length_rect_array);
for (int i = 0; i < result.compared_doc_results_array_length; i++)
    result. compared_doc_results_array[i].rect_array = malloc(sizeof(FSCompareResultInfo) * result.base_doc_results_array_length);
FSDK_Comparison_DoCompare(comparison, 0, 0, e_FSCompareTypeText, &result);

FS_PDFPAGE_HANDLE page;
FSDK_PDFDoc_GetPage(compared_doc, 0, &page);
for (int i=0; i<result.compared_doc_results_array_length; i++)
{
    const FSCompareResultInfo item = result.compared_doc_results_array[i];
    FSCompareResultType type = item.type;
    if (type == e_FSCompareResultTypeDeleteText)
    {
        wchar_t res_string_new[100];
        swprintf_s(res_string_new, L"\"%ls\"",  item.diff_contents.str);

        // Add stamp to mark the "delete" type differences between the two documents.
        CreateDeleteTextStamp(page, item.rect_array, item.array_length_rect_array, 0xff0000,  res_string_new, L"Compare : Delete", L"Text");
    }
    else if (type == e_FSCompareResultTypeInsertText)
    {
        wchar_t res_string_new[100];
        swprintf_s(res_string_new, 100, L"\"%ls\"", item.diff_contents.str);
        
        // Highlight the "insert" type differences between the two documents.  
        CreateDeleteText(page, item.rect_array, item.array_length_rect_array, 0x0000ff, res_string_new, L"Compare : Insert", L"Text");
    }
    else if (type == e_FSCompareResultTypeReplaceText)
    {
        wchar_t res_string_new[100];
        swprintf_s(res_string_new, 100, "[Old]: \"%s\"\r\n[New]: \"%s\"", result.base_doc_results_array[i].diff_contents.str, item.diff_contents.str); 
        // Squiggly the "replace" type differences between the two documents.  
        CreateSquigglyRect(page, item.rect_array, item.array_length_rect_array, 0xe7651a, res_string_new, L"Compare : Replace", L"Text");
    }
}

// Save the comparison result to a PDF file.
FS_BOOL return_result = false;
wchar_t output_compared_doc[MAX_FILE_PATH];
swprintf_s(output_compared_doc, MAX_FILE_PATH, L"%lsnew.pdf", output_directory); FSDK_PDFDoc_SaveAs(compared_doc, output_compared_doc, e_FSSaveFlagsSaveFlagNormal, &return_result);
java
import com.foxit.sdk.common.fxcrt.RectF;
import com.foxit.sdk.common.fxcrt.PointF;
import com.foxit.sdk.common.fxcrt.RectFArray;
import com.foxit.sdk.common.Image;
import com.foxit.sdk.pdf.PDFDoc;
import com.foxit.sdk.pdf.PDFPage;
import com.foxit.sdk.addon.comparison.CompareResultInfo;
import com.foxit.sdk.addon.comparison.CompareResultInfoArray;
import com.foxit.sdk.addon.comparison.CompareResults;
import com.foxit.sdk.addon.comparison.Comparison;
import com.foxit.sdk.common.DateTime;
import com.foxit.sdk.pdf.annots.Annot;
import com.foxit.sdk.pdf.annots.Highlight;
import com.foxit.sdk.pdf.annots.Stamp;
import com.foxit.sdk.pdf.annots.QuadPoints;
import com.foxit.sdk.pdf.annots.QuadPointsArray;

...
PDFDoc base_doc = new PDFDoc("input_base_file");
error_code = base_doc.load(null);
if (error_code != e_ErrSuccess) {
    return;
}

PDFDoc compared_doc = new PDFDoc("input_compared_file");
error_code = compared_doc.load(null);
if (error_code != e_ErrSuccess) {
    return;
}
            
Comparison comparison = new Comparison(base_doc, compared_doc);
            
// Start comparing.            
CompareResults result = comparison.doCompare(0, 0, Comparison.e_CompareTypeText);
CompareResultInfoArray oldInfo = result.getResults_base_doc();
CompareResultInfoArray newInfo = result.getResults_compared_doc();
long oldInfoSize = oldInfo.getSize();
long newInfoSize = newInfo.getSize();
PDFPage page = compared_doc.getPage(0);
for (int i=0; i<newInfoSize; i++)
{
    CompareResultInfo item = newInfo.getAt(i);
    int type = item.getType();
    if (type ompareResultInfo.e_CompareResultTypeDeleteText)
    {
        String res_string;
        res_string = String.format("\"%s\"", item.getDiff_contents());
        CreateDeleteTextStamp(page, item.getRect_array(), 0xff0000, res_string, "Compare : Delete", "Text");
    }
    else if (type ompareResultInfo.e_CompareResultTypeInsertText)
    {
        String res_string;
        res_string = String.format("\"%s\"", item.getDiff_contents());
        CreateDeleteText(page, item.getRect_array(), 0x0000ff, res_string, "Compare : Insert", "Text");
    }
    else if (type ompareResultInfo.e_CompareResultTypeReplaceText)
    {
        String res_string;
        res_string = String.format("[Old]: \"%s\"\r\n[New]: \"%s\"", oldInfo.getAt(i).getDiff_contents(), item.getDiff_contents());                    
        CreateSquigglyRect(page, item.getRect_array(), 0xe7651a, res_string, "Compare : Replace", "Text");
    }

}
            
// Save the comparison result to a PDF file.
compared_doc.saveAs(output_path + "result.pdf", PDFDoc.e_SaveFlagNormal);
py
import sys
import site

if sys.version_info.major == 2:
    _PYTHON2_ = True
else:
    _PYTHON2_ = False

if _PYTHON2_:
    # replace with the python2 lib path
    site.addsitedir(‘../../../’)
    from FoxitPDFSDKPython2 import *
else:
    from FoxitPDFSDKPython3 import *

...
base_doc = PDFDoc(input_base_file)
error_code = base_doc.Load("")
if error_code != e_ErrSuccess:
    print("The Doc [{}] Error: {}\n".format(input_base_file, error_code))
    return 1

compared_doc = PDFDoc(input_compared_file)
error_code = compared_doc.Load("")
if error_code != e_ErrSuccess:
    print("The Doc [{}] Error: {}\n".format(input_base_file, error_code))
    return 1

comparison = Comparison(base_doc, compared_doc)
result = comparison.DoCompare(0, 0, Comparison.e_CompareTypeText)
oldInfo = result.base_doc_results
newInfo = result.compared_doc_results
oldInfoSize = oldInfo.GetSize()
newInfoSize = newInfo.GetSize()
page = compared_doc.GetPage(0)
for i in range(0, newInfoSize):
    item = newInfo.GetAt(i)
    type = item.type
    if type ompareResultInfo.e_CompareResultTypeDeleteText:
        res_string = "\"{}\"".format(item.diff_contents)
        CreateDeleteTextStamp(page, item.rect_array, 0xff0000,
                              res_string, "Compare : Delete", "Text")
    elif type ompareResultInfo.e_CompareResultTypeInsertText:
        res_string = "\"{}\"".format(item.diff_contents)
        CreateDeleteText(page, item.rect_array, 0x0000ff, res_string,
                         "Compare : Insert", "Text")
    elif type ompareResultInfo.e_CompareResultTypeReplaceText:
        res_string = "[Old]: \"{}\"\r\n[New]: \"{}\"".format(
            oldInfo.GetAt(i).diff_contents, item.diff_contents)
        CreateSquigglyRect(page, item.rect_array, 0xe7651a, res_string,
                           "Compare : Replace", "Text")
# Save the comparison result to a PDF file.
compared_doc.SaveAs(output_directory + "result.pdf")
objc
#include "FSPDFObjC.h"
...

FSPDFDoc *base_doc = [[FSPDFDoc alloc] initWithPath:@"input_base_file"];
errorCode = [base_doc load:@""];
if (errorCode != FSErrSuccess) {
    return -1;
}

FSPDFDoc *compared_doc = [[FSPDFDoc alloc] initWithPath:@"input_compared_file"];
errorCode = [compared_doc load:@""];
if (errorCode != FSErrSuccess) {
    return -1;
}

FSComparison* comparison = [[FSComparison alloc] initWithBase_doc:base_doc compared_doc:compared_doc];

// Start comparison.
FSCompareResults* result = [comparison doCompare:0 compared_page_index:0 compare_flags:FSComparisonCompareTypeText];
int oldInfoSize = [result.results_base_doc getSize];
int newInfoSize = [result.results_compared_doc getSize];
FSPDFPage* page = [compared_doc getPage:0];
for (int i=0; i<newInfoSize; i++)
{
    FSCompareResultInfo* item = [result.results_compared_doc getAt:i];
    FSCompareResultInfoCompareResultType type = item.type;
    if (type == FSCompareResultInfoCompareResultTypeDeleteText)
    {
        NSString* res_string = [NSString stringWithFormat:@"\"%@\"", item.diff_contents];
        
        createDeleteTextStamp(page, item.rect_array, 0xff0000, res_string, @"Compare : Delete", @"Text");
    }
    else if (type == FSCompareResultInfoCompareResultTypeInsertText)
    {
        NSString* res_string = [NSString stringWithFormat:@"\"%@\"", item.diff_contents];
        
        CreateDeleteText(page, item.rect_array, 0x0000ff, res_string, @"Compare : Insert", @"Text");
    }
    else if (type == FSCompareResultInfoCompareResultTypeReplaceText)
    {
        NSString* res_string = [NSString stringWithFormat:@"[Old]: \"%@\"\r\n[New]: \"%@\"",[result.results_base_doc getAt:i].diff_contents,item.diff_contents];
        
        createSquigglyRect(page, item.rect_array, 0xe7651a, res_string, @"Compare : Replace", @"Text");
    }
}

// Save the comparison result to a PDF file.
[compared_doc saveAs:[output_directory stringByAppendingString:@"result.pdf"] save_flags:FSPDFDocSaveFlagNormal];
js
const FSDK = require("@foxitsoftware/foxit-pdf-sdk-node");

...
let base_doc = new FSDK.PDFDoc(input_base_file);
let error_code = base_doc.Load("");
if (error_code != FSDK.e_ErrSuccess) {
  console.log("The Doc [%s] Error: %d\n", input_base_file, error_code);
  return 1;
}

let compared_doc = new FSDK.PDFDoc(input_compared_file);
error_code = compared_doc.Load("");
if (error_code != FSDK.e_ErrSuccess) {
  console.log("The Doc [%s] Error: %d\n", input_compared_file, error_code);
  return 1;
}

let comparison = new FSDK.Comparison(base_doc, compared_doc);
let result = comparison.DoCompare(0, 0, FSDK.Comparison.e_CompareTypeText);
let old_info = result.base_doc_results;
let new_info = result.compared_doc_results;
let = result.compared_doc_results;
let old_info_size = old_info.GetSize();
let new_info_size = new_info.GetSize();
let page_base = base_doc.GetPage(0);
let page = compared_doc.GetPage(0);
for (let i=0; i<new_info_size; i++) {
  let item = new_info.GetAt(i);
  let type = item.type;
  if (type == FSDK.CompareResultInfo.e_CompareResultTypeDeleteText) {
    let res_string = `\"${item.diff_contents}\"`;      
    CreateDeleteTextStamp(page, item.rect_array, 0xff0000, res_string, "Compare : Delete", "Text");
  } else if (type == FSDK.CompareResultInfo.e_CompareResultTypeInsertText) {
    let res_string = `\"${item.diff_contents}\"`;
    CreateDeleteText(page, item.rect_array, 0x0000ff, res_string, "Compare : Insert", "Text");
  } else if (type == FSDK.CompareResultInfo.e_CompareResultTypeReplaceText) {      
  let res_string = `[New]: \"${new_info.GetAt(i).diff_contents}\"\r\n[Old]: \"${item.diff_contents}\"`;
   CreateSquigglyRect(page, item.rect_array, 0xe7651a, res_string, "Compare : Replace", "Text");
  }
}
// Save the comparison result to a PDF file.
compared_doc.SaveAs(output_directory + "new.pdf", FSDK.PDFDoc.e_SaveFlagNormal);
csharp
using foxit;
using foxit.common;
using foxit.common.fxcrt;
using foxit.pdf;
using foxit.pdf.annots;
using foxit.addon;
...

using (PDFDoc base_doc = new PDFDoc("input_base_file"))
{
    error_code = base_doc.Load(null);
    if (error_code != ErrorCode.e_ErrSuccess)
    {
        Library.Release();
        return;
    }

    using (PDFDoc compared_doc = new PDFDoc("input_compared_file"))
    {
        error_code = compared_doc.Load(null);
        if (error_code != ErrorCode.e_ErrSuccess)
        {
            Library.Release();
            return;
        }

        using (Comparison comparison = new Comparison(base_doc, compared_doc))
        {
            // Start comparing.
            CompareResults result = comparison.DoCompare(0, 0, (int)Comparison.CompareType.e_CompareTypeText);
            CompareResultInfoArray oldInfo = result.results_base_doc;
            CompareResultInfoArray newInfo = result.results_compared_doc;
            uint oldInfoSize = oldInfo.GetSize();
            uint newInfoSize = newInfo.GetSize();

            using (PDFPage page = compared_doc.GetPage(0))
            {
                 for (uint i = 0; i < newInfoSize; i++)
                 {
                     CompareResultInfo item = newInfo.GetAt(i);
                     CompareResultInfo.CompareResultType type = item.type;
                     if (type ompareResultInfo.CompareResultType.e_CompareResultTypeDeleteText)
                     {
                         String res_string = String.Format("\"{0}\"", item.diff_contents);
                         CreateDeleteTextStamp(page, item.rect_array, 0xff0000, res_string, "Compare : Delete", "Text");
                     }
                     else if (type ompareResultInfo.CompareResultType.e_CompareResultTypeInsertText)
                     {
                         String res_string = String.Format("\"{0}\"", item.diff_contents);
                         CreateDeleteText(page, item.rect_array, 0x0000ff, res_string, "Compare : Insert", "Text");
                     }
                     else if (type ompareResultInfo.CompareResultType.e_CompareResultTypeReplaceText)
                     {
                         String res_string = String.Format("[Old]: \"{0}\"\r\n[New]: \"{1}\"", oldInfo.GetAt(i).diff_contents, item.diff_contents);
                         CreateSquigglyRect(page, item.rect_array, 0xe7651a, res_string, "Compare : Replace", "Text");
                     }
                 }
            }

            // Save the comparison result to a PDF file.
            compared_doc.SaveAs(output_path + "result.pdf", (int)PDFDoc.SaveFlags.e_SaveFlagNormal);
        }
    }
}

(以 Java 开发语言为例)

  • 对于 CreateDeleteTextStampCreateDeleteTextCreateSquigglyRect 函数,请参考 SDK 包中\examples\simple_demo 文件夹下的 "pdfcompare" demo。