我正在编写一个使用Cairo图形库输出PDF文件的CAD应用程序。许多单元测试不需要实际生成PDF文件,例如计算对象的预期边界框。但是,我想确保在更改代码后生成的PDF文件“看起来”正确。有没有自动化的方法可以做到这一点?如何尽可能地自动化?我需要手动检查每个生成的PDF吗?如何解决这个问题而不把头发都揪光?
我正在编写一个使用Cairo图形库输出PDF文件的CAD应用程序。许多单元测试不需要实际生成PDF文件,例如计算对象的预期边界框。但是,我想确保在更改代码后生成的PDF文件“看起来”正确。有没有自动化的方法可以做到这一点?如何尽可能地自动化?我需要手动检查每个生成的PDF吗?如何解决这个问题而不把头发都揪光?
我正在使用 Linux 上的 shell 脚本执行相同的操作,其中包装了以下几个工具:
compare
命令pdftk
工具(将其移植到 DOS/Windows 的 .bat
批处理文件中将非常容易。)
我的应用程序创建了一些参考 PDF,这些 PDF 是“已知好”的。代码更改后,新生成的 PDF 会与这些参考 PDF 进行像素级比较,并保存为一个新的 PDF。在这个 PDF 中,所有未更改的像素都涂成白色,而所有不同的像素都涂成红色。
以下是构建块:
使用此命令将多页 PDF 文件拆分为多个单页 PDF:
pdftk reference.pdf burst output somewhere/reference_page_%03d.pdf
pdftk comparison.pdf burst output somewhere/comparison_page_%03d.pdf
使用此命令为每个页面创建一个“差异”PDF页面:
compare \
-verbose \
-debug coder -log "%u %m:%l %e" \
somewhere/reference_page_001.pdf \
somewhere/comparison_page_001.pdf \
-compose src \
somewhereelse/reference_diff_page_001.pdf
由于自动插入的元数据(如当前日期+时间),基于MD5哈希值的文件比较在PDF输出方面表现不佳。
如果您想自动发现所有仅包含纯白页的情况,您还可以使用bmp256
输出设备将其转换为无元数据的位图格式。您可以对原始PDF(参考和比较)或diff-PDF页面执行此操作:
gs \
-o reference_diff_page_001.bmp \
-r72 \
-g595x842 \
-sDEVICE=bmp256 \
reference_diff_page_001.pdf
md5sum reference_diff_page_001.bmp
如果对于一张595x842的全白色页面,其MD5sum与您所期望的相符,则您的单元测试已经通过。
我不知道为什么之前我没有想到使用ImageMagick的compare
生成一张直方图输出...
以下是一个由2个不同命令链接起来的命令管道:
compare
相同,生成'白色像素相等,红色像素不同'-格式,但它将输出ImageMagick内部的miff
格式。它不会写入文件,而是输出到stdout。convert
读取stdin,生成直方图,并以文本形式输出结果。将会有两行:
这就是它:
compare \
reference.pdf \
current.pdf \
-compose src \
miff:- \
| \
convert \
- \
-define histogram:unique-colors=true \
-format %c \
histogram:info:-
输出示例:
56934: (61937, 0, 7710,52428) #F1F100001E1ECCCC srgba(241,0,30,0.8)
444056: (65535,65535,65535,52428) #FFFFFFFFFFFFCCCC srgba(255,255,255,0.8)
(这个示例输出是通过使用这些参考文献.pdf和当前文献.pdf文件生成的。)
我认为这种类型的输出非常适合自动化单元测试。如果你评估这两个数字,你可以很容易地计算出“红像素”的百分比,甚至可以根据某个阈值返回PASSED或FAILED(如果由于某种原因你不一定需要“零红色”)。
diff
。我创建了一个名为“known_good”的子目录,其中包含“人工验证”的PDF文件,并且此代码执行实际比较:def different(a,b): return subprocess.call(['diff', a, b, '--brief']) != 0
。(这使用了Mac OS X diff命令;Python中有一个便携式命令。)如果我需要检查的PDF文件太多或生成器是不确定性的,则此方法将无法正常工作,但到目前为止,看起来我的问题已经解决了。 - Nathan Farrington# exporting
gfxpdf = gfx.open("pdf", self.pdfpath)
if os.path.isfile(pngPath):
os.remove(pngPath)
page = gfxpdf.getPage(pagenum)
img = gfx.ImageList()
img.startpage(page.width, page.height)
page.render(img)
img.endpage()
img.save(pngPath)
return os.path.isfile(pngPath)
# comparing
outPng = os.path.join(outpath, pngname)
masterPng = os.path.join(outpath, "_master", pngname)
if os.path.isfile(masterPng):
output = Image.open(outPng).convert("RGB") # discard alpha channel, if any
master = Image.open(masterPng).convert("RGB")
mismatch = any(x[1] for x in ImageChops.difference(output, master).getextrema())
我建议使用xpresser - (https://wiki.ubuntu.com/Xpresser)。您可以尝试将图像与相似的图像匹配,而不是完全相同的副本 - 这是这些情况下的问题。
我不知道xpresser是否正在积极开发中,或者它是否可以与独立的图像文件一起使用(我认为可以) - 无论如何,它从Sikuli项目中汲取了灵感(该项目是Java和Jython前端,而xpresser是Python)。
“cmppdf” 可以比较 PDF 文件的视觉外观或文本内容。
这是一个 bash 脚本,可从 https://abhweb.org/jima/cmppdf?v 下载。
它使用 pdftk
和 compare
来进行图形化比较 PDF 文件,类似于其他答案中描述的内容。元数据(不影响实际外观的任何内容)不会被比较。
文本比较选项使用 pdftotxt
和 diff
。
matplotlib
或sage
的绘图功能。 - jfs