Год начался с занятной задачи — нашей команде нужно было за короткий срок собрать пилот приложения «Мобильное рабочее место руководителя» на ОС Sailfish. По сути, это мобильный клиент СЭД, т.е. он предназначен для работы с документами. Ну и конечно же, для работы с документами необходим pdf-reader. Но оказалось, что заставить его работать гладко не так-то просто. Но мы, можно сказать, справились (для прототипа, конечно). Как? Читайте под катом.
Из хорошего могу сказать, что pdf-reader, встроенный в операционную систему, — это просто замечательно, не нужно отдельно собирать. И «Аврора» (вроде как, это будущее «русское» название для труднопроизносимой «Sailfish Mobile OS Rus») этим похвастаться может, за что ей большое спасибо. Но всё-таки написать быстрое решение оказалось не так просто.
Poppler — библиотека для рендеринга pdf, встроенная в Sailfish, собственно, потому она и была выбрана для наших тёмных целей. Но только для написания прототипа, ибо лицензия GPL не позволит в будущем выпускать на нём коммерческий продукт. Да и, немного потрогав её, прихожу к выводу, что наверняка есть более удобные решения, но об этом чуть позже.
Рассказать хочется главным образом о двух основных проблемах, с которыми я столкнулась, пытаясь хоть что-то выжать из poppler в сжатые сроки. Предлагаю посмотреть видео, демонстрирующее работу приложения, которое получилось, прежде чем читать моё нытье.
Проблема номер раз
Видите красивую карусель? Это такой способ отображения документов по папкам — интуитивно понятный и простой для пользователя, позволяет быстро просматривать документы, детали по нему, прежде чем переходить непосредственно к чтению документа и согласованию. Нужен он для того, чтобы важные министры, подписывая свои очень важные документы, могли взор свой услаждать PathView.
PathView — замечательный инструмент, можно кучу всякой красоты понаделать. Но наша проблема заключается в делегате.
Рисовать первую страницу документа (превью документа на главном экране) в онлайн режиме, используя poppler, — идея так себе, ибо папки не кэшируются, и переход между ними начинает невероятно висеть, poppler рисует медленно. Точнее рендерит, рисует, конечно, QPainter.
Проблема решается просто, но совсем не изящно: рендерим превью при запуске приложения, сохраняем на устройство картинку и в итоге рисуем через QImage в делегате. И, честно говоря, как переписать это в более изящный вид, я пока не придумала.
Проблема номер два
Если пытаться нарисовать документ большого размера весь разом (страница, отображающая контент документа), он мало того, что делает это очень долго, так ещё и смазывает картинку. Думала, напутала с размерами, что документ рендерится в картинку маленького размера, а потом растягивает его на заданную величину, но нет, проверила всё по 100 раз. Пыталась делать картинку большего размера — всё то же самое. Да и мажет не как с потерей качества, а скорее, как будто провели рукой по свежим чернилам.
Это, конечно, решается все одним махом, нужно рисовать не целиком документ, а только текущую страницу и соседние, но не успела ещё этого сделать.
Итог
Poppler медленный и странный, но спасибо, что он есть. Сейчас хотим в рамках пилота реализовать графические и текстовые аннотации pdf, поэтому собираюсь переписать все это дело на другой библиотеке, это что-то невероятно мощное на первый взгляд, надеюсь, что заявленная функциональность действительности соответствует.
Если кто пробовал или знает решение более подходящее, буду максимально признательна за совет.
Комментарии (15)
apro
19.02.2019 14:29mupdf вроде самая быстрая из opensource библиотек, смотрели в ее сторону?
Gorthauer87
19.02.2019 20:15Честно сказать, код примеров выглядит так будто его писали марсиане, да и документации нормальной нет, как этим вообще пользоваться?
ranissafiullin9
19.02.2019 20:07Попробуйте посмотреть в сторону html. Потом уже генерировать pdf документ.
AndrewSu
20.02.2019 09:20Можно ещё в сторону libpodofo посмотреть
http://podofo.sourceforge.net/about.html
SokoloffA
20.02.2019 13:57+1а я в надеждах найти что-то, что еще и писать умеет
Возможно мой опыт будет полезен.
Мне для виртуального принтера надо преобразовывать PDF-ы — сливать несколько страниц в одну, масштабировать, поворачивать страницы и.т.д.
Вначале я использовал poppler как для рендеринга, так и для чтения/преобразования PDF-ов. И если с рендерингом все более-менее хорошо, то с обработкой я натерпелся. Poppler имеет 2 API. Один публичный высокоуровневый, но он очень ограничен, по сути только отрисовать страницу и все. И второй низкоуровневый и полузакрытый, на сайте про него не написано, но во всех дистрибутивах есть пакеты с нужными заголовочными файлами. Вот этот API позволяет преобразовывать PDF-ы на уровне объектов. Публичного API мне не хватало, и я решил использовать низкоуровневый, ох и натерпелся я. Этот API очень запутанный и недокументированный. А главное он нестабильный, и может внезапно поменяться в минорной версии библиотеки. Добило меня когда KDE-шники взяли и добавили аргументы в функцию вообще без изменения версии. А т.к. проект опенсорсный, то мне надо поддерживать совместимость с несколькими версиями библиотек (от винтажной в Debian Stable, до модной в ArchLinux)
Тогда я плюнул, и написал свой велосипед, который позволяет читать менять и сохранять PDF-ы. Рендерю пока через poppler. Смотрел в сторону PDFium — выглядит обещающе, но ее нет в стандартный пакетах, а для вас, для закрытого проекта можно и собрать самому.
al_sh
20.02.2019 14:15с pdfium все аналогично. В public только минимально рисующий функционал, все остальное спрятано. Зато можно делать почти все что угодно. Я пдф, через pdfium в набор QPainterPath складываю, а затем отрисовываю по мере необходимости. т.е. по существу и SVG и PDF и шрифты внутренне лежат в одном формате, миксуются, а затем сохраняются в результирующий PDF или SVG. Пришлось, правда, помучиться с многофокусными градиентами, но на 98% удалось получить однозначное соответствие. Да, и собирать pdfium из сырцов та еще песня. В Qt в стоке стоит сильно порезанный вариант
SokoloffA
20.02.2019 15:34Я пдф, через pdfium в набор QPainterPath складываю, а затем отрисовываю по мере необходимости.
Не совсем понял, отдельные страницы складываете в QPainterPath, или куски одной страницы?
Если страницы целиком, то у меня немного другой подход. У меня минимальный квант, это отдельный PDF-ный объект. Я работаю с PDF на уровне исходного текстового документа. Т.е. перенумеровываю объекты в PDF-е, меняю тип пдфной страницы на пдфный Form и дописываю потом страницу, которая включает в себя эти Form-ы. Мне кажется это быстрее.
Если куски, то это круто! Я до таких высот еще не опускался. Мне вроде как и не надо, но было бы интересно.
al_sh
20.02.2019 16:34Квантом является кривая(4точки) + fill + stroke(способы обводки и заливки) + матрица. Кроме того, шрифты описываются аналогично. Т.е. всю страницу, кроме растра можно описать списком этих структур QPainterPath + QBrush/QPen + QMatrix/QTransform. Аналогично c SVG и TTF/OTF. Т.е вы оперируете этим списком безотносительно источника (двигать, удалять, добавлять, крутить), а затем этот список сериализуете либо pdf либо в svg либо еще во что.
SokoloffA
20.02.2019 17:12А как со скоростью? Это для подчеркиваний и выделений? Может быстрее будет накладывать поверх объекта страницы второй объект с выделениями?
al_sh
20.02.2019 17:29Нет не для подчеркиваний и выделений. Для всего. Практически все в пдф представлено, в виде кривых (буквы в тексте, изображения(кроме растра), контуры, короче вообще всЁ). Соответсвенно, практически все можно представить в виде 4(3) точек и способа обводки/заливки получившегося контура. Глиф в шрифте, кстати имеет аналогичную структуру. В mupdf или pdfium все работает аналогично, просто за отрисовку полученных кривых отвечает, если мне память не изменяет — skia. т.е. в качестве системы отрисовки я использую нативный кьютовый QPainter, а не skia, или что там использует Ваш рендер. Скорость +-аналогична. Зато полный контроль над рендерингом. Вывод гораздо быстрей, поскольку можно гнать сразу в опенгль текстуру.
al_sh
Qt вроде с pdfium дружит, через QPdfDocument
nastika Автор
в Sailfish Qt 5.6, QPdf для более поздних версий, если я правильно помню)
al_sh
да, но ничего не мешает собрать и для 5.6 blog.qt.io/blog/2017/01/30/new-qtpdf-qtlabs-module.