[Python, HTML, PDF] HTML ⟹ PDF @ Python

Автор Сообщение
news_bot ®

Стаж: 6 лет 3 месяца
Сообщений: 27286

Создавать темы news_bot ® написал(а)
27-Фев-2021 15:31

Наверняка не очень редко возникает задача печати 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СметаР21001DLMDLMDLMPdfkit0,360,441,490,360,441,231,31,96,9Weasy0,360,470,600,270,360,6526,134,854,7Pisa0,120,170,280,290,410,6820,427,342,2Выводы: таракан без ног не слышит ©Общий вывод - счастья нет. То есть я не смог ни одного кандидата однозначно выгнать на мороз или наградить золотой медалью. В среднем по больнице видно, что pdfkit дольше запрягает, но потом быстрее едет, но это и без тестов логично (хотя разница все-равно впечатляет). Ну а так каждый может оценить цифры, протестировать самостоятельно и сделать свои выводы. Я могу только привести свои личные впечатления:
  • pdfkit. Все-таки это не чистокровный питон и даже не обертка C-либы, что нарушает внутреннюю гармонию и бесит перфекционизм. Радует высокое качество полученного PDF, максимально точная передача задумки (реально WYSIWYG), максимальная скорость на тяжелых документах. Не радует неторопливость на мелких задачах и почти полная неуправляемость.
  • weasyprint. Бедненько - но чистенько. Всеядное, приемлемая (а иногда и неплохая) скорость, достаточно предсказуемый результат. Но без наворотов и без рекордов.
  • xhtml2pdf. Вредное. HTML должен не просто идеально соответствовать стандартам, он должен еще понравиться этой либе, иначе "инжалид дежице". Отдельно идут упражнения с кириллицей (кстати, я тестировал без этих упражнений (лениво), то есть не совсем корректно) и фееричность получаемого результата. За это там куча наворотов и в среднем хорошая скорость работы (как для питона).
Отдельно стоят вопросы управления разрывами страниц, нумерация страниц, хорошо бы еще попробовать iText7 (но это вязать python с java, что из категории секаса переводит вопрос в категорию прона), wkhtmltopdf-static и иные окружения. Но я хотел просто быстро оценить порядок скорости на целевой лично для меня платформе (RHEL8+).
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_python, #_html, #_pdf, #_html, #_pdf, #_python, #_python, #_html, #_pdf
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 17-Май 15:13
Часовой пояс: UTC + 5