Работа с odtPHP: подключаем картинки
Sep 3, 2014 18:50 · 457 words · 3 minute read
В последнее время я всё глубже погружаюсь в 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 уже есть, комментарий на смену имени файла я оставил.