如何对绘制 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?如何在不拔头发的情况下解决这个问题?

【问题讨论】:

您可以看看matplotlibsage 绘图功能是如何测试的。 类似问题,我发在这里:***.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.pdfcurrent.pdf 文件生成示例输出。)

我认为这种类型的输出非常适合自动单元测试。如果您评估这两个数字,您可以轻松计算“红色像素”百分比,您甚至可以根据某个阈值决定返回 PASSEDFAILED(如果您不'出于某种原因不一定需要“零红色”)。

【讨论】:

+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如何绘制三维隐函数图形

matlab图形标注与修饰函数titlexlabelylabel matlab图行绘制二

matlab图形标注与修饰函数titlexlabelylabel matlab图行绘制二

iOS实现图形编程可以使用三种API(UIKITCore GraphicsOpenGL ES及GLKit)

dpplot命令安装

python中的turtle库绘制图形