如何对绘制 PDF 图形的 Python 函数进行单元测试?
Posted
技术标签:
【中文标题】如何对绘制 PDF 图形的 Python 函数进行单元测试?【英文标题】:How to unit test a Python function that draws PDF graphics? 【发布时间】:2011-06-08 01:27:07 【问题描述】:我正在编写一个使用 Cairo 图形库输出 PDF 文件的 CAD 应用程序。许多单元测试不需要实际生成 PDF 文件,例如计算对象的预期边界框。但是,我想确保在更改代码后生成的 PDF 文件“看起来”正确。有没有一种自动化的方法来做到这一点?我怎样才能尽可能地自动化?我是否需要目视检查每个生成的 PDF?如何在不拔头发的情况下解决这个问题?
【问题讨论】:
您可以看看matplotlib
或sage
绘图功能是如何测试的。
类似问题,我发在这里:***.com/questions/1310836/…
【参考方案1】:
(另请参阅下面的更新!)
我在 Linux 上使用 shell 脚本做同样的事情
-
ImageMagick 的
compare
命令
pdftk
实用程序
Ghostscript(可选)
(将其移植到 DOS/Windows 的 .bat
批处理文件会相当容易。)
我的应用程序创建了一些“已知良好”的参考 PDF。将代码更改后新生成的 PDF 与这些参考 PDF 进行比较。比较是逐像素进行的,并保存为新的 PDF。在此 PDF 中,所有未更改的像素都涂成白色,而所有不同的像素都涂成红色。
这里是构建块:
pdftk
使用此命令将多页 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
鬼脚本
由于自动插入元数据(例如当前日期+时间),PDF 输出不适用于基于 MD5hash 的文件比较。
如果您想自动发现由纯白页组成的所有案例,您还可以使用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
如果 MD5sum 是您所期望的 595x842 PostScript 点的全白页面,那么您的单元测试通过了。
更新:
我不知道为什么我之前没有想过从 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)
(使用这些reference.pdf 和current.pdf 文件生成示例输出。)
我认为这种类型的输出非常适合自动单元测试。如果您评估这两个数字,您可以轻松计算“红色像素”百分比,您甚至可以根据某个阈值决定返回 PASSED 或 FAILED(如果您不'出于某种原因不一定需要“零红色”)。
【讨论】:
+1 为您提供非常详细的演练!就我而言,我使用的是矢量图形,我只是想知道是否有任何变化,所以我最终使用了二进制diff
。我用“人工验证”的 PDF 文件创建了一个 known_good
子目录,这段代码进行了实际比较:def different(a,b): return subprocess.call(['diff', a, b, '--brief']) != 0
。 (这使用 Mac OS X diff 命令;Python 中有一个可移植的命令。)如果我有太多 PDF 文件要检查或者生成器是否不确定,这种方法将不起作用,但到目前为止,我的问题似乎已经解决.【参考方案2】:
您可以将 PDF 捕获为位图(或至少是无损压缩)图像,然后将每个测试生成的图像与参考图像进行比较,以了解其外观。任何差异都会被标记为测试错误。
【讨论】:
在我的例子中,PDF 图形存储为矢量,因此文件大小非常小。我在 Cairo 做了一个测试,结果证明 PDF 生成是确定性的,这意味着一个简单的diff
就足以标记一个错误,就像你建议的那样。
请注意,这取决于 cairo 版本。不同版本的 cairo 库可能会生成略有不同的 PDF 输出。【参考方案3】:
我脑海中浮现的第一个想法是使用 diff 实用程序。这些通常用于比较文档的文本,但它们也可以比较 PDF 的布局。使用它,您可以将预期输出与提供的输出进行比较。
谷歌给我的第一个结果是this。尽管它是商业的,但可能还有其他免费/开源的替代品。
【讨论】:
【参考方案4】:我会尝试使用 xpresser - (https://wiki.ubuntu.com/Xpresser ) 您可以尝试将图像匹配到相似图像而不是精确副本 - 这是这些情况下的问题。
我不知道 xpresser 是否正在积极开发,或者它是否可以与独立的图像文件一起使用(我想是的)——无论如何,它的想法来自于 Sikuli 项目(它是带有 Jython 前端的 Java结束,而 xpresser 是 Python)。
【讨论】:
【参考方案5】:我用 Python 编写了一个工具来验证我雇主文档的 PDF。它能够将单个页面与主图像进行比较。我使用我找到的一个名为 swftools 的库将页面导出为 PNG,然后使用 Python Imaging Library 将其与母版进行比较。
相关代码看起来像这样(这不会运行,因为脚本的其他部分有一些依赖关系,但你应该明白):
# 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())
【讨论】:
以上是关于如何对绘制 PDF 图形的 Python 函数进行单元测试?的主要内容,如果未能解决你的问题,请参考以下文章
matlab图形标注与修饰函数titlexlabelylabel matlab图行绘制二
matlab图形标注与修饰函数titlexlabelylabel matlab图行绘制二