Наверняка не очень редко возникает задача печати HTML-документов с какого-то сервера в точности как задумано автором этого сервера. Делать это лучше всего не в надежде на браузер клиента, а на стороне сервера. А если на сервере крутится нечто на питоне (Django/Flask/тысячи их), то хорошо бы оценить во что это обойдется.
Для тестов выбирались такие библиотеки, чтобы как минимум были в виде пакетов в официальных репо RH-based дистрибутивов или же — в крайнем случае — можно было таковые собрать. И чтобы без долгих танцев с бубном.
В macOS всё ставилось с помощью homebrew и pip3, в Fedora — из стандартного репо (искл. xhtml2pdf — этого в репах нет, но при должной усидчивости за пару часов можно собрать вполне себе цивильный rpm).
Дано:
После тщательного отбора кандидатов накопилось аж 3:
-
python-pdfkit — адаптер к вызову бинарника wkhtmltopdf.
-
weasyprint — прокладка между html5lib и reportlab.
-
xhtml2pdf — примерно то же самое, что и weasyprint, но со своими
тараканамиособенностями. В таблице указано как «Pisa» (основной модуль).
Платформ для тестирования набралось под руками тоже 3 (все x64):
-
MacBook — Apple MacBookPro9.2 (13″ mid 2012, i5-3210M (2.5GHz)), HDD, macOS 10.15 «Catalina», Python 3.9 (brew)
-
LinBook (так это назовем) — тот же самый макбук, но с Fedora 33, Python 3.9
-
DeskTop — Intel G3450 (3.4GHz), HDD, Fedora 33, Python 3.9
Документов для тестов — 3 (все — на одну страничку каждый):
-
ПД4 — квитанция на оплату налогов и сборов в Сбер (форма ПД-4сб). HTML ручной работы, максимально соответствующий стандартам. Требования к точности передачи задумки автора в печати довольно высокие.
-
Инструкция — чей-то документ с заголовком, комментариями, табличками и местом для подписи. Получен из .doc экспортом из Word 2007. HTML не так, чтобы очень тяжелый, но на тяп-ляп. То есть как оно и будет в жизни. Требования к точности — никакие.
-
Р21001 — последний листик (стр.5Б) формы Р21001 — с якорями для сканера, буквами в квадратиках и всем остальным, что мы так любим в документах для налоговой. Экспорт из Excel 2007, IE6-совместимо. Получилось 2 МБ формально правильного HTML, но совершенно фееричной разметки, то есть достаточно тяжелого для парсера-генератора. Требования к точности очень высокие.
Решение:
Код на коленке
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Benchmark of html-to-pdf converters. (c) @justhabrauser, GPLv3. """ # 1. system import os import sys from time import time # 2. 3rd from pdfkit import from_string # https://github.com/JazzCore/python-pdfkit from weasyprint import HTML # https://github.com/Kozea/WeasyPrint from xhtml2pdf.pisa import CreatePDF # https://github.com/xhtml2pdf/xhtml2pdf def __pdfkit(html: str) -> bytes: return from_string(html, False, options={'quiet': ''}) def __weasy(html: str) -> bytes: return HTML(string=html).write_pdf() def __pisa(html: str) -> bytes: pdf = CreatePDF(html) if not pdf.err: pdf.dest.seek(0) return pdf.dest.read() def main(indir: str, outdir: str, count: int) -> None: # 1. Load all htmls modules = (__pdfkit, __weasy, __pisa) html_list = list() # (filename, content)[] dir_list = os.listdir(indir) dir_list.sort() for i, fn in enumerate(dir_list): fpath = os.path.join(indir, fn) if os.path.isfile(fpath) and fpath.endswith(".html"): print("Load '{}'".format(fn), file=sys.stderr) with open(fpath, "rt") as i_f: html = i_f.read() html_list.append(html) # 2. write results (and warm up) for j, m in enumerate(modules): with open(os.path.join(outdir, "%d_%d.pdf" % (i, j)), "wb") as o_f: o_f.write(m(html)) # 2. for C times x I pages x J engines: print("Count\tPage\tEngine\tTime\n=====\t====\t======\t====") for c in range(count): # count for i, h in enumerate(html_list): # html page for j, m in enumerate(modules): # engine t0 = time() m(h) t1 = time() print("{}\t{}\t{}\t{}".format(c, i, j, t1-t0)) if __name__ == '__main__': if len(sys.argv) != 3: print("Usage: {} <dir_with_htmls> <output_dir_for_pdfs>".format(sys.argv[0]), file=sys.stderr) elif not os.path.isdir(sys.argv[1]): print("Input '{}' is not dir or not exists.".format(sys.argv[1]), file=sys.stderr) elif not os.path.isdir(sys.argv[2]): print("Output '{}' is not dir or not exists.".format(sys.argv[2]), file=sys.stderr) else: main(sys.argv[1], sys.argv[2], 5)
Среднее время обработки каждого документа (в разрезе документов, библиотек и платформ (D=DeskTop, L=LinBook, M=MacBook)), сек.:
Lib |
ПД4 |
Смета |
Р21001 |
||||||
D |
L |
M |
D |
L |
M |
D |
L |
M |
|
Pdfkit |
0,36 |
0,44 |
1,49 |
0,36 |
0,44 |
1,23 |
1,3 |
1,9 |
6,9 |
Weasy |
0,36 |
0,47 |
0,60 |
0,27 |
0,36 |
0,65 |
26,1 |
34,8 |
54,7 |
Pisa |
0,12 |
0,17 |
0,28 |
0,29 |
0,41 |
0,68 |
20,4 |
27,3 |
42,2 |
Выводы: таракан без ног не слышит ©
Общий вывод — счастья нет. То есть я не смог ни одного кандидата однозначно выгнать на мороз или наградить золотой медалью. В среднем по больнице видно, что pdfkit дольше запрягает, но потом быстрее едет, но это и без тестов логично (хотя разница все-равно впечатляет). Ну а так каждый может оценить цифры, протестировать самостоятельно и сделать свои выводы. Я могу только привести свои личные впечатления:
-
pdfkit. Все-таки это не чистокровный питон и даже не обертка C-либы, что нарушает внутреннюю гармонию и бесит перфекционизм. Радует высокое качество полученного PDF, максимально точная передача задумки (реально WYSIWYG), максимальная скорость на тяжелых документах. Не радует неторопливость на мелких задачах и почти полная неуправляемость.
-
weasyprint. Бедненько — но чистенько. Всеядное, приемлемая (а иногда и неплохая) скорость, достаточно предсказуемый результат. Но без наворотов и без рекордов.
-
xhtml2pdf. Вредное. HTML должен не просто идеально соответствовать стандартам, он должен еще понравиться этой либе, иначе «инжалид дежице». Отдельно идут упражнения с кириллицей (кстати, я тестировал без этих упражнений (лениво), то есть не совсем корректно) и фееричность получаемого результата. За это там куча наворотов и в среднем хорошая скорость работы (как для питона).
Отдельно стоят вопросы управления разрывами страниц, нумерация страниц, хорошо бы еще попробовать iText7 (но это вязать python с java, что из категории секаса переводит вопрос в категорию прона), wkhtmltopdf-static и иные окружения. Но я хотел просто быстро оценить порядок скорости на целевой лично для меня платформе (RHEL8+).
ссылка на оригинал статьи https://habr.com/ru/post/540756/
Добавить комментарий