О том как я на Java из PDF флэш получал
Давным давно, когда трава была зеленее, меня поймали и долго пытали пришлось мне повышать перформанс в одной чудесной связке.
Как задачу понял архитекторДано: есть безумный каталог изделий в виде большого количества PDF по паре тыщ страниц каждый. Надо их выдать в веб в виде красочных анимированных презентаций.
Попытка решения: написали плееры на флэше и на javascript, которым скармливается этот преобразованный каталог, и они разными красочными эффектами по определенному алгоритму крутят нечто рекламное.
Проблема: каталоги постоянно меняются, а конвертация только одного гроссбуха из каталога занимает больше часа(!).
Почему так и как улучшить?
Как это сделано до насРезонен вопрос — а чего так сложно-то? Так кроме десктопов мобильники окучить обязательно, а генерировать растр для 200 устройств и потом на всем этом тестить — не наш метод. И потом — а с зумом тогда что делать?
Поэтому для десктопа — флэш в древних браузерах (ура корпоративному IE), и HTML5 для всего остального. Картинки векторные (кроме превьюшек, а то надцать SVG-шек сразу планшеты пока осиливают слабо).
Опен-сорц (как всегда) спешит на помощь.
Анализирую — что же там такое делается. Обнаруживаю вот что
- pdftoswf (ну и что что он discontinued? зато работает)
- pdftosvg (версию 0.1)
- ImageMagick (помним про PNG в виде уменьшенных превьюшек)
- pdftohtml (там ведь есть еще и текст — и надо достать подзаголовки, а из XML это делать как-то удобнее)
Вот тебе, бабушка, и Юрьев день!
Кто виноват?Очевидно, что парсить PDF аж четыре раза подряд — не лучший выбор.
Не менее очевидно, что ImageMagick — выбор явно не тот, 3/4 времени потрачено именно утилитой convert.
Именно в этот момент ко мне в комнату заглянул заказчик с окутанным паром утюгом и сказал: срочно нужна твоя мудрость! я вступаю в игру.
Общее направление признаем условно верным — куда деваться, релиз как всегда «еще вчера», а к плееру вопросов нет. Но вот утиль считаем подобранным неудачно, и обращаем свое внимание на Java.
Новые действующие лицаБерем следующий набор джентьмена:
- PDFRenderer
- Apache Batik
- Adobe Flex SDK: swfutils
- JAI
и начинаем комбинировать.
Перво-наперво вспоминаем про Маркуса и Бориса, и начинаем:
И пишем как это все будем использовать.
Дело за малым — написать все нужные конвертеры.
Первые граблиВыясняется, что выбранная нами либа действительно шустра. Но у нее есть пара серьезных недостатков:
- Отсутствует поддержка SMask
- Градиентов тоже нет
- Непонимание JPEG2000 хроническое
- Странное со шрифтами в виде всяких там стрелочек и т.п. во всей красе
SMask приходится добавлять напильником в код PDFRenderer. Тривиально — добавляем в парсере распознавание, команду для сохранения в контекст, и отрисовку Shape меняем на рисование в картинку с наложением маски. Банально, но текстообильно.
Градиенты просто игнорируем — их нету в тех местах, которые попадают в слайды. Кропы и прочую обработку я для простоты не показываю, если что.
Первый этап пройден — рисует как надо. Реализуем наш API (обработку ошибок я убрал):
Приступим собственно к конвертации.
Начерчиллим абстракций — но не забудем про рузвельтатыПредметная область такова, что даже Борис не запутается.
Наполним мясом наш скелет — и кадаврик будет готов к работе.
SVG и БатикТут в общем все уже сделано до нас, Apache Batik SVGgen спешит на помощь. Проблем не замечено. Вот были бы градиенты — то тогда да, с радиальными градиентами пришлось бы или распрощаться, или на батик патч накладывать. Это уже после того, как сами градиенты удастся добавить в PDFRenderer, само собой.
Единственная тонкость — хинты правильно составить, чтобы и антиалиасинг не забыть, и картинки на кусочки не порезало.
PNG: проще не бываетТут настолько банально, что просто приведу код.
А теперь перейдем к дессерту — создадим SWFКод также прост (и большей частью позаимствован из примеров Flex SDK).
Ничто не предвещало, и вдруг. Часть картинок в SWF не появилась.
Расследование по горячим следамВот так вот — в SVG есть, в PNG есть, а в SWF нет.
Трассировка мысленным лучом исходников адоба навела на мысль, и я сделал код конем:
Вскрытие показало, что возвращает нам прямоугольник 1x1.
ИтогиТестовый прогон показал, что первая версия, сшитая на живую нитку, требует меньше 10ти минут. Оно все еще много, и есть над чем подумать.
Однако, переход с кошерного С++ на Java ускорил процедуру более чем в шесть раз, а создание нового конвертера с нуля — потребовало решения пары граблей и в сумме трех дней.
Теперь ребятам есть над чем подумать, и повторить все эти шаги на С++. Время у них есть — java пока справляется.
Издержки: пришлось с собой тянуть 40 мегабайт jar-ок, да и в томкат это дело встраивать, веб сервис однако был на PHP.