【问题标题】:Comparing two PDF files text using PDFBox is failing eventhough both files are having same text即使两个文件具有相同的文本,使用 PDFBox 比较两个 PDF 文件的文本也会失败
【发布时间】:2018-03-14 04:46:16
【问题描述】:

我在我的 selenium 自动化中使用 PDFBOX 作为一个实用程序来进行导出测试。我们正在将实际导出的 pdf 文件与使用 pdfbox 的预期文件进行比较,然后相应地通过/失败测试。这非常顺利。但是最近我遇到了实际导出的文件,它看起来和预期的一样(就数据而言),但是当它与 pdfbox 比较时,它失败了

Expected pdf file

Actual pdf file

以下是我用来比较 pdf 文件的通用实用程序

    private static void arePDFFilesEqual(File pdfFile1, File pdfFile2) throws IOException
{
    LOG.info("Comparing PDF files ("+pdfFile1+","+pdfFile2+")");
    PDDocument pdf1 = PDDocument.load(pdfFile1);
    PDDocument pdf2 = PDDocument.load(pdfFile2);
    PDPageTree pdf1pages = pdf1.getDocumentCatalog().getPages();
    PDPageTree pdf2pages = pdf2.getDocumentCatalog().getPages();
    try
    {
        if (pdf1pages.getCount() != pdf2pages.getCount())
        {
            String message = "Number of pages in the files ("+pdfFile1+","+pdfFile2+") do not match. pdfFile1 has "+pdf1pages.getCount()+" no pages, while pdf2pages has "+pdf2pages.getCount()+" no of pages";
            LOG.debug(message);
            throw new TestException(message);
        }
        PDFTextStripper pdfStripper = new PDFTextStripper();
        LOG.debug("pdfStripper is :- " + pdfStripper);
        LOG.debug("pdf1pages.size() is :- " + pdf1pages.getCount());
        for (int i = 0; i < pdf1pages.getCount(); i++)
        {
            pdfStripper.setStartPage(i + 1);
            pdfStripper.setEndPage(i + 1);
            String pdf1PageText = pdfStripper.getText(pdf1);
            String pdf2PageText = pdfStripper.getText(pdf2);
            if (!pdf1PageText.equals(pdf2PageText))
            {
                String message = "Contents of the files ("+pdfFile1+","+pdfFile2+") do not match on Page no: " + (i + 1)+" pdf1PageText is : "+pdf1PageText+" , while pdf2PageText is : "+pdf2PageText;
                LOG.debug(message);
                System.out.println("fff");
                LOG.debug("pdf1PageText is " + pdf1PageText);
                LOG.debug("pdf2PageText is " + pdf2PageText);
                String difference = StringUtils.difference(pdf1PageText, pdf2PageText);
                LOG.debug("difference is "+difference);
                throw new TestException(message+" [[ Difference is ]] "+difference);
            }
        }
        LOG.info("Returning True , as PDF Files ("+pdfFile1+","+pdfFile2+") get matched");
    } finally {
        pdf1.close();
        pdf2.close();
    }
}

Eclipse 在控制台中显示了这种差异

https://s3.amazonaws.com/uploads.hipchat.com/95223/845692/9Ex0QW2fFeRqu8s/upload.png

我可以看到它因为(花括号、{}、哈希#、感叹号!)等符号而失败,但是我不知道如何解决这个问题..

谁能告诉我如何解决这个问题?

【问题讨论】:

    标签: java pdfbox


    【解决方案1】:

    但是最近我遇到了实际导出的文件,它看起来和预期的一样(就数据而言),但是当它与 pdfbox 比较时,它失败了

    这可能会发生,你不应该感到惊讶。毕竟您的测试不是比较相关页面的外观,而是比较文本提取的结果。

    虽然页面上文本数据的外观取决于相应(如果是您的文件)嵌入字体文件中相关字形的绘图说明,但页面上相同文本数据的文本提取结果取决于ToUnicode 表或该字体文件的 PDF 字体信息结构的 Encoding 值。

    事实上,虽然预期文档和实际文档的文本数据使用各自字体的相同字形,但预期文档和实际文档中的 ToUnicode 表对于一种字体声称某些字形表示不同的 Unicode 代码点。

    有问题的字体有这三个字形:

    您预期文档中该字体的 ToUnicode 映射包含映射

    <0000> <0000> <0000>
    <0001> <0002> [<F125> <F128> ] 
    

    声称这三个字符对应于 U+0000、U+F125 和 U+F128。

    实际文档中该字体的 ToUnicode 映射包含映射

    <0000> <0000> <0000>
    <0001> <0002> [<F126> <F129> ] 
    

    声称这三个字符对应于 U+0000、U+F126 和 U+F129。

    因此,您的测试正确地发现了预期文档和实际文档之间的差异,因此 其失败结果是正确的。 因此,您无需修复任何内容,生成实际文档的软件已一个问题!

    (有人可能会争辩说,差异在 Unicode 私人使用区域内并且无关紧要。在这种情况下,您必须更新测试以忽略来自 Unicode 私人使用区域的字符差异。但这应该被告知在你开始创建测试之前。)

    【讨论】:

    • 感谢您提供如此有用的信息。我看到我的 pdf 中使用的字形 Unicode 是不同的。顺便说一句,我不知道 pdf 包含的 ToUnicode Table/Map,感谢您让我知道这一点。 Moerover 能否请您告诉我您是如何发现 unicode 差异的?
    • “你能告诉我你是如何发现 unicode 差异的吗?” - 我不只是记录不同的文本输出,我以足够好的编码(UTF-8,但任何完整的 Unicode 编码都可以)将它们写入文件,并使用十六进制比较来比较文件。
    【解决方案2】:

    这是一个棘手的问题,因为相似甚至相同的 Unicode 字符可能具有不同的字节表示,这取决于 PDF 生成期间的字体、编码和其他因素。

    如果您可以安全地假设相关文本片段由 8 位字符表示,我可以想到一个可能的解决方案:

    String stripUnicode(String s) {
        StringBuilder sb = new StringBuilder(s.length());
        for (char c : s.toCharArray()) {
            if (c <= 0xFF) {
                sb.append(c);
            }
        }
        return sb.toString();
    }
    
    ...
    
    String pdf1PageText = pdfStripper.getText(pdf1);
    String pdf2PageText = pdfStripper.getText(pdf2);
    if (!stripUnicode(pdf1PageText).equals(stripUnicode(pdf2PageText)))
    ...
    

    如果您需要 Unicode 支持,您需要实现自己的自定义比较算法,该算法能够识别相似字符并将它们视为平等。

    【讨论】:

    • 很好,如果需要我会使用它.. 谢谢
    猜你喜欢
    • 2011-08-25
    • 1970-01-01
    • 1970-01-01
    • 2011-10-05
    • 2017-06-18
    • 2012-10-03
    • 1970-01-01
    • 1970-01-01
    • 2015-05-27
    相关资源
    最近更新 更多