Работа с odtPHP: подключаем картинки

Sep 3, 2014 18:50 · 457 words · 3 minute read php

В последнее время я всё глубже погружаюсь в Yii и вообще php-инфраструктуру. К сожалению, некоторые библиотеки авторы забросили, и приходится их дотачивать под свои нужды. Одной из таких является odtPHP, которая шаблонизирует odt документы. Мне казалось, что такая задача весьма распространена, ан решений всего парочка, и odtPHP понравилось мне больше всего. (Если кто-то использует что-то другое для работы с OOo из-под php, пожалуйста, отпишитесь). Однако, картинки она вставляет настолько криво, что современный LibreOffice напрочь отказывается читать документ. Почему же? Ответ под катом.

Учим odtPHP работать с картинками

Итак, дано: библиотека для генерации документа в формате ODT odtPHP, шаблон с картинкой. После применения при открытии получаем ошибку “Общая ошибка ввода-вывода”. Хм, а что из себя должен представлять корректный файл? Создадим такой же вручную и сравним (благо odt это простой zip). Используя kdiff3 находим, что отличие в файле META-INF/manifest.xml - в правильном присутствует строчка с указанием ссылки на картинку:

<manifest:file-entry manifest:full-path="Pictures/1.jpeg" manifest:media-type="image/jpeg"/>

А если её добавить в сгенерированный? Получилось - файл открылся! Теперь надо подправить odtPHP, чтобы он указывал ссылки на картинки в манифесте. Предлагается сделать по аналогии с основным содержимым - content.xml. В конструктор дописать:

if (($this->manifestXml = $this->file->getFromName('./META-INF/manifest.xml')) === false) {
    throw new OdfException("Nothing to parse - check that the META-INF/manifest.xml file is correctly formed");
}

а в метод _save() что-нибудь типа:

$images = array();
foreach ($this->images as $imageKey => $imageValue) {
    $this->file->addFile($imageKey, 'Pictures/' . $imageValue);
    array_push($images, '<manifest:file-entry manifest:full-path="Pictures/'.$imageValue.'" manifest:media-type="image/jpeg"/>');
}
if (count($images) > 0) {
    $this->manifestXml = str_replace('<manifest:file-entry manifest:full-path="content.xml" manifest:media-type="text/xml"/>', 
        "<manifest:file-entry manifest:full-path=\"content.xml\" manifest:media-type=\"text/xml\"/>\n".implode("\n", $images), $this->manifestXml);
    if (! $this->file->addFromString('META-INF/manifest.xml', $this->manifestXml)) {
        throw new OdfException('Error during file export');
    }
}

В цикле скопируем все файлы и заполним массив строчками, которые надо записать в manifest.xml; потом его таким образом подправим. На этом всё, должно заработать :)

Итог

Как порядочный программист я решил внести исправления в основной репозиторий и с удивлением обнаружил, что нечто похожее там уже есть. Как так?! А так, потому что я брал три года назад zip-архив, а не реп с гитхаба. Хорошо, что на всё про всё ушло пара часов, но в следующий раз я обязательно обновлюсь до последней официальной версии, посмотрю не только все pull request'ы, но также и форки. Вообще это очень полезная практика - смотреть целиком пути развития продукта. Многие авторы забрасывают проекты, но находятся добровольцы, которые готовы и дальше их развивать. Вполне может оказаться, что как раз у какого-то форка и будет реализована необходимая вам фича.

На этом, правда, моя борьба с этой библиотекой не закончилась. Я захотел её заиспользовать через composer, благо PhpStorm её нашёл, но штатными средствами Yii у меня не получилось. Дело в том, что автозагрузчик требует, чтобы имя файла совпадало с именем импортированного класса, а у нас отличие в первой букве. Надеюсь, что это скоро поправят, т.к. pull request уже есть, комментарий на смену имени файла я оставил.