Подтвердить что ты не робот

Как unit test функция Python, которая рисует графику PDF?

Я пишу приложение САПР, которое выводит файлы PDF с использованием графической библиотеки Cairo. Большая часть модульного тестирования не требует фактически создания файлов PDF, таких как вычисление ожидаемых ограничивающих прямоугольников объектов. Тем не менее, я хочу убедиться, что сгенерированные PDF файлы выглядят корректно после изменения кода. Есть ли автоматизированный способ сделать это? Как я могу автоматизировать как можно больше? Нужно ли мне визуально проверять каждый сгенерированный PDF файл? Как я могу решить эту проблему, не вытаскивая волосы?

4b9b3361

Ответ 1

Вы можете захватить PDF в виде растрового изображения (или, по крайней мере, без потерь), а затем сравнить образ, сгенерированный каждым тестом, с образцом ссылки на то, как он должен выглядеть. Любые различия будут помечены как ошибка для теста.

Ответ 2

(См. также обновление ниже!)

Я делаю то же самое, используя оболочку script в Linux, которая обертывает

  • Команда ImageMagick compare
  • утилита pdftk
  • Ghostscript (необязательно)

(Было бы довольно легко переносить это в пакетный файл .bat для DOS/Windows.)

У меня есть несколько справочных 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

<сравнения/h2 >

Используйте эту команду для создания страницы "diff" 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

Ghostscript

Из-за автоматически вставленных метаданных (например, текущей даты + времени) выход 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 - это то, что вы ожидаете от всей белой страницы точек PostScript 595x842, то ваш unit test прошел.


Update:

Я не знаю, почему я раньше не думал о генерации вывода гистограммы из ImageMagick compare...

Ниже приведена цепочка команд, связывающая две разные команды:

  • первая такая же, как приведенная выше compare, которая генерирует "белые пиксели равны, красные пиксели являются отличительными", только он выводит внутренний формат miff ImageMagick. Он не записывается в файл, а в стандартный вывод.
  • второй использует 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 на основе определенного порога (если по какой-то причине вам необязательно нужен "нулевой красный цвет" ).

Ответ 3

Первая идея, которая появляется у меня в голове, - использовать утилиту diff. Они обычно используются для сравнения текстов документов, но они также могут сравнивать макет PDF. Используя его, вы можете сравнить ожидаемый результат с поставляемым выходом.

Первый результат google дает мне this. Хотя это коммерчески, могут быть и другие альтернативы с открытым/открытым исходным кодом.

Ответ 4

Я бы попробовал это с помощью xpresser - (https://wiki.ubuntu.com/Xpresser). Вы можете попытаться совместить изображения с похожими изображениями, а не точные копии, что является проблемой в этих случаях.

Я не знаю, развивается ли xpresser, или если он может использоваться с автономными файлами изображений (я так думаю) - в любом случае он берет свои идеи из проекта Sikuli (который является Java с фронтом Jython end, а xpresser - Python).

Ответ 5

Я написал инструмент в Python для проверки PDF файлов для документации моего работодателя. Он имеет возможность сравнивать отдельные страницы с основными изображениями. Я использовал найденную мной библиотеку swftools для экспорта страницы в PNG, затем использовал Python Imaging Library, чтобы сравнить его с мастером.

Соответствующий код выглядит примерно так (это не будет работать, так как есть некоторые зависимости от других частей script, но вы должны получить идею):

# 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())