如何使用 Python 获取两个 PDF 文件的差异?

Posted

技术标签:

【中文标题】如何使用 Python 获取两个 PDF 文件的差异?【英文标题】:How to get the diff of two PDF files using Python? 【发布时间】:2010-11-21 14:27:51 【问题描述】:

我需要找出两个 PDF 文件之间的差异。有人知道任何与 Python 相关的工具,它具有直接给出两个 PDF 差异的功能吗?

【问题讨论】:

区分文本或全部内容或大小? 【参考方案1】:

“差异”是什么意思? PDF 文本的差异或某些布局更改(例如,调整了嵌入图形的大小)。第一个很容易检测,第二个几乎是不可能的(PDF 是一种非常复杂的文件格式,它提供了无穷无尽的文件格式化功能)。

如果要获取文本差异,只需在两个 PDF 上运行 pdf 到文本实用程序,然后使用 Python 的内置差异库来获取转换后文本的差异。

这个问题涉及python中的pdf到文本转换:Python module for converting PDF to text。

此方法的可靠性取决于您使用的 PDF 生成器。如果你使用例如Adobe Acrobat 和一些基于 Ghostscript 的 PDF-Creator 从相同的 word 文档制作两个 PDF,尽管源文档相同,您可能仍然会得到差异。

这是因为将源文档的信息编码为 PDF 的方法有很多种,并且每个转换器使用不同的方法。通常 pdf 到文本的转换器无法确定正确的文本流,尤其是对于复杂的布局或表格。

【讨论】:

文字就可以了。 pdf生成器应该不是问题。 如果您确实需要支持图像,您可以从 pdf 中提取 JPG:nedbatchelder.com/blog/200712/extracting_jpgs_from_pdfs.html 或将 pdf 转换为图像并进行比较。【参考方案2】:

我不知道您的用例,但对于使用 reportlab 生成 pdf 的脚本的回归测试,我会通过 diff pdfs by

    使用 ghostsript 将每个页面转换为图像 使用 PIL 将每个页面与标准 pdf 的页面图像进行比较

例如

im1 = Image.open(imagePath1)
im2 = Image.open(imagePath2)

imDiff = ImageChops.difference(im1, im2)

在我的情况下,这适用于标记由于代码更改而引入的任何更改。

【讨论】:

您的步骤#1 有参考吗?【参考方案3】:

在我的加密 pdf 单元测试中遇到了同样的问题,pdfminer 和 pyPdf 都不适合我。

这里有两个命令(pdftocairo、pdftotext)在我的测试中完美运行。 (Ubuntu 安装:apt-get install poppler-utils)

您可以通过以下方式获取pdf内容:

from subprocess import Popen, PIPE

def get_formatted_content(pdf_content):
    cmd = 'pdftocairo -pdf - -' # you can replace "pdftocairo -pdf" with "pdftotext" if you want to get diff info
    ps = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
    stdout, stderr = ps.communicate(input=pdf_content)
    if ps.returncode != 0:
        raise OSError(ps.returncode, cmd, stderr)
    return stdout

好像pdftocairo可以重绘pdf文件,pdftotext可以提取所有文本。

然后你可以比较两个pdf文件:

c1 = get_formatted_content(open('f1.pdf').read())
c2 = get_formatted_content(open('f2.pdf').read())
print(cmp(c1, c2)) # for binary compare
# import difflib
# print(list(difflib.unified_diff(c1, c2))) # for text compare

【讨论】:

【参考方案4】:

尽管这个问题已经很老了,但我猜我可以为这个话题做出贡献。

我们有多个生成大量 PDF 的应用程序。其中一个应用程序是用 Python 编写的,最近我想编写集成测试来检查 PDF 生成是否正常工作。

测试 PDF 生成是困难,因为 PDF 文件的规范非常复杂且不确定。使用完全相同的输入数据生成的两个 PDF 将生成不同的文件,因此直接文件比较被丢弃。

解决方案:我们必须测试它们的外观(因为 THAT 应该是确定性的!)。

在我们的例子中,PDF 是使用 reportlab 包生成的,但从测试的角度来看,这并不重要,我们只需要生成器中的文件名或 PDF blob(字节)。我们还需要一个包含“良好”PDF 的期望文件,以与来自生成器的 PDF 进行比较。

PDF 被转换为图像,然后进行比较。这可以通过多种方式完成,但我们决定使用ImageMagick,因为它用途广泛且非常成熟,几乎可以绑定所有编程语言。对于 Python 3,绑定由 Wand 包提供。

测试如下所示。删除了我们实现的具体细节并简化了示例:

import os
from unittest import TestCase
from wand.image import Image
from app.generators.pdf import PdfGenerator


DIR = os.path.dirname(__file__)


class PdfGeneratorTest(TestCase):

    def test_generated_pdf_should_match_expectation(self):
        # `pdf` is the blob of the generated PDF
        # If using reportlab, this is what you get calling `getpdfdata()`
        # on a Canvas instance, after all the drawing is complete
        pdf = PdfGenerator().generate()

        # PDFs are vectorial, so we need to set a resolution when
        # converting to an image
        actual_img = Image(blob=pdf, resolution=150)

        filename = os.path.join(DIR, 'expected.pdf')

        # Make sure to use the same resolution as above
        with Image(filename=filename, resolution=150) as expected:
            diff = actual.compare(expected, metric='root_mean_square')
            self.assertLess(diff[1], 0.01)

0.01 是我们可以容忍微小差异的最低点。考虑到 diff[1] 使用 root_mean_square 度量在 0 到 1 之间变化,我们在此接受与示例预期文件相比在所有通道上最多 1% 的差异。

【讨论】:

【参考方案5】:

看看这个,它会很有用:http://pybrary.net/pyPdf/

【讨论】:

pyPdf 在我的测试中不是很健壮。它在由 Illustrator/InDesign 和其他矢量绘图程序创建的 pdf 上崩溃。但是,对于来自 Office 应用程序的简单 PDF 可能没问题。一个更可靠的替代方案是 xpdf 工具包中的 pdftotext。

以上是关于如何使用 Python 获取两个 PDF 文件的差异?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用python代码查找PDF文件每一段的字体大小?

JS如何获取两个时间的差,比如03:00:00和17:00:00中间相差几个小时?

如何通过自动下载链接使用 Python 访问 PDF 文件?

如何使用 Python 将 pdf 文件发送到多个电子邮件?

一个用于合并pdf的简单Python脚本

如何从 Python 创建 PDF 文件,包括图像和文本? [关闭]