Пользователь
0,0
рейтинг
27 июля 2012 в 17:26

Разработка → Красивая печать в PDF из Django recovery mode

Т.к. эта статья — результат нескольких лет экспериментов, то букв будет много. Но — возможно — она сэкономит кому-то многие месяцы езды велосипедом по граблям, которые и описаны.
В общем случае речь идет даже не о Django, а о печати регламентированных документов из python с использованием шаблонизаторов.
Кому дальше читать лень — сразу скажу — проблема до конца не решена. Но более-менее рабочий вариант вырисовался.

1. Задача


  • Пользователь вводит данные в веб-форму
  • Сервер вставляет эти данные в шаблон печатной формы
  • И отдает пользователю в виде, пригодном для печати


2. Ограничения


  • Формы бывают “мягкие” (где точность не очень важна — например Договор или Счет) и “жесткие” (точность — максимальная, под сканер — например уведомление мигранта или заявление на УСН (форма 26.2-1)).
  • При этом даже “мягкие” формы должны печататься максимально близко к задуманному создателем (если я сказал, что границы — 1см, то пользователь должен получить документ с границами ровно 1см) и — особенно — учитывать разрывы страниц (см. формы 11001, 21001 и т.д.).
  • Обязательно — минимальные телодвижения по преобразованию исходного материала (как правило — .xls или .doc, цельнотянутые из «Консультаната» или «Гаранта»).
  • Т.к. речь идет о веб-приложении — крайне желательны отзывчивость и надежность решения => крайне желательна работа с нативными библиотеками python.
  • Желательна возможность размещения всего этого хозяйства на арендованном хостинге (в идеале — GAE).
  • Желательна возможность визуального редактирования шаблонов.
  • Желателен быстрый предварительный просмотр шаблона (а еще лучше — и результата).

Первый этап — выбор конечного формата. После недолгих размышлений с различных т.з. (кросс-платформенность, гарантированность результата, конвертируемость в) выбор пал на PDF.
Теперь — входные форматы и как их преобразовать.

3. Мягкие формы


ODF

Речь идет об Open Document Format — ODS, ODT и иже.
Здесь всё очень просто:
  • Редактируем шаблон в LibreOffice (оставляя место для данных).
  • Каким-то образом заполняем поля в Django.
  • Каким-то образом получаем PDF

Место для данных: или же добавляем user-defined поля в документ — или же прямо в текст вставляем {{ теги_django }}. В первом случае заполнение этих полей потом из python — скорее всего возможно, но я даже не представляю как (точнее — всё представляемое выглядит крайне заморочливо). Поэтому просто расставляем теги в виде текста.
В этом случае заполнение полей — элементарно — просто скармливаем шаблон шаблонизатору Django (ковыряние внутри шаблона библиотеками питона оставим гентушникам :-). А дабы не раззиповывать/зазиповывать документы при каждом пинке — документы сохраняются в *.fodX (Flat X) — один-единственный неупакованный xml. Шаблонеру скармливается как xml же.
Получение PDF — без вариантов — с помощью LibreOffice: скармливаем демону LibreOffice (libreofficed (где-то у убунтоводов нашел)) или unoconv или ручной работы запускалку LO в режиме демона. Все эти варианты — примерно одно и то же.
Достоинства

  • Можно сразу использовать нарытые в Интернетах документы (как правило — из “Консультанта”, в форматах Microsoft Office).
  • С редактированием шаблонов — никаких проблем.
  • Как и с предварительным просмотром.
  • Возможно — получение PDF об Google Docs — пока не пробовал. Но в том, что это будет реактивно — сомневаюсь уже сейчас (а в том, что некорректно — не сомневаюсь; попробуйте загрузить в гугледокс ту же форму 21001 от Консультанта (лежит на сайте налоговой)).

Недостатки

  • Иногда при записи шаблонов LibreOffice спонтанно гробит теги, вставляя внутрь {{..}} всякие span lang=”en-GB” и иже. Приходится потом вручную возвращать всё назад.
  • Просто фантастическая ресурсоемкость для сервера — CPU 100% (только одного, сколько бы их не было), сотни метров RAM, получение PDF — до минуты или после (форма 21001 — 50 секунд на P4-3.0). Java же.
  • Тянет за собой немеряно пакетов (Fedora, CentOS).
  • Наличие хоть какого-то X-сервера (Xvfb например).
  • Наверное, на каком-то хостинге и позволят развернуть LibreOffice — но я сильно сомневаюсь в nic.ru например. О GAE речь даже не идет.
  • Предварительного просмотра результата — нет.

Резюме

Как крайний запасной вариант — подходит. Но именно как крайний.
HTML

Здесь с редактированием шаблонов (руками) и шаблонизатором (искаропки) всё понятно. Остался один маленький, но главный вопрос — как получить PDF? Быстро, качественно, с разрывами страниц там, где надо. И вот здесь было больше всего экспериментов.
Многочисленные эксперименты с pure python html render (типа PISA и предков/наследников/форков) привели к одному важному (IMHO) выводу: чтобы получить гарантированный результат — надо использовать готовый html движок. Коих, как мы все знаем, аж 4 (из нормальных). Из них использовать в linux можно аж 2 — gecko и webkit. Вызвать gecko из python, скорее всего, возможно — но а) для этого нужен запущенный X (как в случае LibreOffice) и б) [полу]готового рецепта я не нашел.
Остался webkit:
  • PyQt4>Qt>WebKit>QPrinter (типа такого). Нативно (хоть и тащит с собой очень много), шустро — но pagebreak не ловит. Кроме того — нужны специальные танцы с DPI и ZoomFactor.
  • GTK>WebKit>GTK printer (типа такого такого). Нативно, шустро — но тоже page break не ловит.
  • Использовать специально доработанный webkit — wkhtmltopdf — в виде внешнего бинарника (сейчас именно этот вариант и используется) или через нативный python binding (в процессе, но есть небольшие проблемы). Нативно (если binding), шустро, ловит page-break, результат гарантирован.

Достоинства

  • Теоретически возможно визуальное редактирование.
  • Мгновенный предварительный просмотр (в виде html же) — как шаблона — так и результата.
  • Реактивная конвертация в PDF.
  • Pure python API конвертирования в PDF (это которое “в процессе”).

Недостатки

  • Всё-таки качественный HTML — ручной работы.
  • Сложные формы (типа 21001) придется самостоятельно писать или рисовать — ибо в Интернетах это — страшный .xls.
  • Т.к. используется либа/бинарник, собранные для Linux — на том же nic.ru (FreeBSD) ничего не получится (без костылей). О GAE речь по-прежнему не идет.

Резюме

Основной вариант для “мягких” документов. Но все-таки нужно искать качественный pure python html render — без флешей, JS и других мультиков — но с качественной обработкой CSS.
Возможно

На будущее рассматриваются форматы TeX, LaTeX, Lyx, docbook — но пока преимуществ не видать (особенно для “почти мягких” форм — типа той же 21001).

4. Жесткие формы


Здесь всё намного печальнее. Особенно в свете того, что здесь уже визуальный редактор крайне желателен.
Кроме того — в подавляющем большинстве (если не во всех) “жестких” форм РФ используются “квадратики” — когда текст разбивается на буквы — и каждая вписывается в свой квадратик (пример).
Отбросим первые попавшиеся (типа “натянуть текст на tiff”) и сразу перейдем к финалистам.
RML

Разработка компании Reportlab (да-да, python-reportlab — это их) — обыкновенный XML, позволяющий выделывать чудеса с PDF. Т.к. всем известный python-trml2pdf уже RIP (как мне честно отписал его разработчик) — пришлось взять этот trml2pdf и немного допилить, т.к. он не поддерживает многие интересные фичи RML, а покупать (а тем более — ломать) коммерческий rml2pdf мне запрещает религия.
Достоинства

  • Нативно
  • Шустро
  • Гибко
  • С хостингом (теоретически) проблем быть не должно — даже в GAE (не пробовал).

Недостатки
  • Строго ручная работа
  • Очень заморочливый синтаксис — когда надо смешать точное позиционирование (“graphics”) с “мягким” текстом (“flowables”) (отсюда, видимо, отсутствие визуального редактора).
  • Никакого предварительного просмотра — ни шаблона, ни результата.

Резюме

Запасной вариант для точных форм (особенно простых).
PDF-формы

Здесь всё очень просто: исходник в PDF — и конечный результат в PDF.
  1. Берем исходную PDF-форму в левую руку
  2. XFDF (простенький xml), обработанный встроенным шаблонером Django — в правую
  3. сливаем их (populate) в новый PDF (“раскатанный” — flatten)
  4. и отдаем пользователю

Проблема всего лишь одна — п.3.
На сегодняшний день нативного и корректно работающего python API для работы с PDF-формами не обнаружено (хотя poppler кое-что уже умеет — но пилить там, судя по всему, еще много), поэтому единственный приемлемый вариант — iText. Через pdftk или свой велосипед — это уже по вкусу.
Достоинства

  • В PDF-форму можно превратить что угодно (как — отдельный вопрос).
  • Можно даже редактировать (аналогично).
  • Абсолютно гарантированный результат.
  • Встроенные в формат PDF “квадратики” (combo).
  • Скорее всего — нет проблем с хостингом (возможно — и с GAE) — не пробовал.

Недостатки

  • Вызов внешнего приложения вместо python API.
  • Java же.

Резюме

Основной вариант для точных печатных форм.

5. Общее резюме


Итого на сегодня сформировалось:
  • “Мягкие” формы — html|webkit — но через довольно тяжелую, избыточную и не слишком портабельную библиотеку webkittox (и продолжать искать).
  • “Жесткие” формы — PDF-формы, но через костыль внешнюю JAVA-библиотеку (и продолжать насиловать poppler).
  • ODF и RML — как запасные варианты соответственно.


PS. Как всё это работает — можно посмотреть здесь — без ODF и RML, но последние предусмотрены.
Евгений Пивнев @TIEugene
карма
1,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (29)

  • 0
    Классно, спасибо. Наверное, еще можно добавить Pisa
    • +1
      Для чего-то не сложнее Заявления — да. Но ту же Форму 21001 она полностью гробит. Я решил не плодить зоопарк.
    • 0
      Да, Pisa отличная штука, мы ей печатаем вот такие инвойсы, совмещенные с трекингом и таможенными формами CN22 на одном листе самоклейки. Сделали за 3 часа примерно. Просто конвертируется HTML-ка, картинки и шрифты все подключаются автоматом. Никаких особых подводных камней не было замечено.
      • +1
        Дело в том, что при отладке шаблона я пишу html, тут же _одинаково_ вижу его в FireFox и любом webkit-браузере (да, можно достичь и такого) — при этом в конечный PDF рендерится так, как видно при отладке. Даже print preview из FireFox дает практически конечный результат.
        А PISA рендерит совершенно по-своему. Сиречь — отладка шаблона замедляется в разы.
        Ну и насчет page-break я не сильно уверен — кажется, не ловит. Могу и ошибаться.
        • 0
          Возможно, какие-то особо сложные конструкции она рендерит по-своему. Я задавал размер странице и дальше все размеры, включая шрифты, в мм, ширины ячеек таблицы в процентах. Сделал подобие reset.css, чтобы задать умолчания. Дальше были проблемы только с размером картинок, их пришлось подогнать вручную. Если у вас получился идеальный рендеринг, то это отлично.
          • +1
            Ok, на живом примере: из такого должно в точности получиться такое (wget, если что).
            PISA (0.0.3) у меня просто поломалась: Traceback…
            • 0
              Красота. Моя, кхм, Pisa, на этом примере вообще дает белый лист :)
              • +1
                При этом заметим, что даже если форма «мягкая» — не дай бог она не понравится в налоговой (т.е. не будет максимально похожа на Консультантовскую). Просто завернут без комментариев (проверенно нашей юристкой).
  • 0
    А TeX не рассматривали? Вроде бы, наиболее простой и удобный вариант.
    • 0
      «3. Мягкие формы» > «Возможно».
      Т.е. — в планах
    • 0
      Рисовать на нём таблички с нужными параметрами это просто ад.
  • 0
    Я бы посоветовал, использовать например pdfcrowd.com/.
    Это сервис использует Prince ( www.princexml.com/ ), стоимость которого начинается от $3800. Прекрасен он тем, что умеет рендерить практически любой html, таким образом сверстать на twitter-bootstrap страничку пару пустяков, а получить еще и pdf еще проще. Заметьте, все это при минимальном количестве кода — значит не упадет.
    • +1
      Опять же — из тестового примера получилась немного ерунда (еще и с рекламой, которую в налоговой могут и не принять.
      Но отработал очень корректно.
      Спасибо за идею — запишу в TODO.
  • 0
    Два дня назад решал похожую проблему. Остановился на mPDF, он умеет конвертировать html в pdf, при этом написан полностью на php. Для предпросмотра может не подойти, так как его понимание html отличается от понимания браузеров, но добиться хорошего результата для мягких форм вполне можно.
  • 0
    Вот еще есть github.com/NetAngels/django-webodt
    • 0
      Относится к категории «LibreOffice демон:… или свой велосипед».
    • 0
      Кстати, можно посмотреть сразу доку packages.python.org/django-webodt/, чтобы понять на сколько красивая штука
  • 0
    wkhtmltopdf же. Наверняка к нему есть питоновский интерфейс.
    • 0
      Этот случай рассмотрен. Интерфейс есть, он и применяется — вызов бинарника. Что не совсем нативно.
      Python-binding к библиотеке libwkhtmltox пока падает.
  • +1
    Я использую с своем проекте trml2pdf от reportlab. Со временем разобрался в особенностях rml и дело пошло как по маслу.
    Для рендера использую django шаблонизатор, и функцию подсмотренную где то на сайте RL.
    Вызывается на подобие django.shortcuts.render типа так, это дает возможность использовать всю мощь шаблонов джанго и языка rml.
    • 0
      Не поддерживает многие фичи RML. Нет preview. Нет визуального редактора.
      Читайте внимательнее, пожалуйста.
      • 0
        Например, что за фичи?
        • 0
          Перечислять очень долго.
          Из 158 entities, перечисленные в RML user guide, trml2pdf поддерживает примерно 60.
          В сырцах они все видны (def render(...):), можете сравнить самостоятельно.
          • 0
            Видели как сделаны отчеты в OpenErp?
            • 0
              Особо не присматривался. Увидел, что внутри лежит trml2pdf (слегка допиленный функционально — но сильно переделанный в коде) — и двинулся дальше.
  • 0
    Похоже — в QWebView 4.8 (или webkit 2.2 вообще?) поломалось page-break-*. ПО крайней мере — webkit-браузеры (кроме Chromium, что поняно) печатают без единого разрыва.
    Ждем пачей — тогда проблема с html будет решена окончательно.
  • 0
    Резюме: Красивая печать в PDF из Django — это миф.
    Тоже голову ломал в своё время…
    • 0
      Ну почему же… Вы можете наблюдать реальное приложение с таки достаточно красивыми PDF (налоговой и ФМС нравится, по крайней мере).
      Другой вопрос что сейчас это решается чуть-чуть некрасиво — через вызов внешних программ.
      До нативного python осталось совсем немного:
      * допилить webkit
      * и допилить poppler
    • 0
      >Резюме: Красивая печать в PDF из Django — это миф.

      «Мы рождены, чтоб сказку сделать былью» ;)

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.