如何使用 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 文件的差异?的主要内容,如果未能解决你的问题,请参考以下文章
JS如何获取两个时间的差,比如03:00:00和17:00:00中间相差几个小时?
如何通过自动下载链接使用 Python 访问 PDF 文件?