О том как я на Java из PDF флэш получал

О том как я на 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

и начинаем комбинировать.

Перво-наперво вспоминаем про Маркуса и Бориса, и начинаем:

И пишем как это все будем использовать.

Дело за малым — написать все нужные конвертеры.

Первые грабли

Выясняется, что выбранная нами либа действительно шустра. Но у нее есть пара серьезных недостатков:

  1. Отсутствует поддержка SMask
  2. Градиентов тоже нет
  3. Непонимание JPEG2000 хроническое
  4. Странное со шрифтами в виде всяких там стрелочек и т.п. во всей красе

SMask приходится добавлять напильником в код PDFRenderer. Тривиально — добавляем в парсере распознавание, команду для сохранения в контекст, и отрисовку Shape меняем на рисование в картинку с наложением маски. Банально, но текстообильно.

Градиенты просто игнорируем — их нету в тех местах, которые попадают в слайды. Кропы и прочую обработку я для простоты не показываю, если что.

Первый этап пройден — рисует как надо. Реализуем наш API (обработку ошибок я убрал):

Приступим собственно к конвертации.

Начерчиллим абстракций — но не забудем про рузвельтаты

Предметная область такова, что даже Борис не запутается.

Наполним мясом наш скелет — и кадаврик будет готов к работе.

SVG и Батик

Тут в общем все уже сделано до нас, Apache Batik SVGgen спешит на помощь. Проблем не замечено. Вот были бы градиенты — то тогда да, с радиальными градиентами пришлось бы или распрощаться, или на батик патч накладывать. Это уже после того, как сами градиенты удастся добавить в PDFRenderer, само собой.

Единственная тонкость — хинты правильно составить, чтобы и антиалиасинг не забыть, и картинки на кусочки не порезало.

PNG: проще не бывает

Тут настолько банально, что просто приведу код.

А теперь перейдем к дессерту — создадим SWF

Код также прост (и большей частью позаимствован из примеров Flex SDK).

Ничто не предвещало, и вдруг. Часть картинок в SWF не появилась.

Расследование по горячим следам

Вот так вот — в SVG есть, в PNG есть, а в SWF нет.

Трассировка мысленным лучом исходников адоба навела на мысль, и я сделал код конем:

Вскрытие показало, что возвращает нам прямоугольник 1x1.

Итоги

Тестовый прогон показал, что первая версия, сшитая на живую нитку, требует меньше 10ти минут. Оно все еще много, и есть над чем подумать.

Однако, переход с кошерного С++ на Java ускорил процедуру более чем в шесть раз, а создание нового конвертера с нуля — потребовало решения пары граблей и в сумме трех дней.

Теперь ребятам есть над чем подумать, и повторить все эти шаги на С++. Время у них есть — java пока справляется.

Издержки: пришлось с собой тянуть 40 мегабайт jar-ок, да и в томкат это дело встраивать, веб сервис однако был на PHP.

📎📎📎📎📎📎📎📎📎📎