PHPWord, шаблоны и PDF — настройка хорошей генерации

Очень часто внутри CRM/ERP-систем используется генератор DOC/PDF-файлов по шаблону, будь то счета-фактуры, акты приёма-передачи или целиком договоры с подписью и печатью. Чтобы дать клиенту возможность самому делать шаблоны документов и вставлять туда всякое (например, скан печати или подписи), я давно привык использовать PHPOffice/PHPWord вместе с его замечательным механизмом TemplateProcessor.

Суть его в том, что в DOCX-файле мы настраиваем переменные вида ${Variable}, а в PHP-скрипте пишем конструкцию типа

$templateProcessor->setValue('Variable', 'Value');

Чуть сложнее дела обстоят с табличными данными, но можно при желании разобраться. Вот цикл:

$i=1;
foreach($jobs as $job) {
$outputjobs[]=Array(
'Number' => $i,
'Name' => html_entity_decode($job['name']),
'Qty' => $job['qty'],
'Units' => $job['units'],
'Price' => $job['price'],
'Total' => $job['total'],
);
$i++;
}
$templateProcessor->cloneRowAndSetValues('Line1_Number', $outputjobs);

А в DOC-файле размечаем следующим образом:

Обратите внимание на название первой переменной (№), оно такое же, как и значение в cloneRowAndSetValues.

Если у нас таблица пустая, то нет смысла её выводить. В таком случае заключим в DOC-файле таблицу в тэг-контейнер ${isTable} … ${/isTable}, а в PHP-скрипте проверим на пустоту переменную, и получится следующий кусок кода:

if(!empty($jobs)) {
// таблица не пустая
$templateProcessor->cloneBlock('isTable', 1);
$templateProcessor->cloneRowAndSetValues('Line1_Number', $outputjobs);
} else {
// таблица пустая, не показываем
$templateProcessor->cloneBlock('isTable',0,true,true);
}

Это всё и многое другое есть в официальной документации PHPOffice. Чего там нет, так это подробной информации о создании PDF. Сам PHPOffice исповедует подход создания PDF-файлов через промежуточную генерацию в HTML, т.е. переводим DOC в HTML, а затем в PDF. Идея не самая плохая, особенно когда мы имеем возможность указать стили (css stylesheet) для PDF-файлов. Но поскольку у нас шаблоны, которые вообще проходят мимо нас, такая идея не совсем нам подходит.

Ещё один вариант — использовать dompdf в паре с PHPOffice, они отлично спарены и выдают нормальные документы. Но, к сожалению, вопрос с кириллицей там я решить не смог. В комплекте с dompdf идёт юникод-шрифт DejaVu, и можно конечно попросить клиентов писать документы именно в нём, но это ламерство. Можно попробовать доставить и привязать все возможные варианты шрифтов (Arial, Tahoma и т.п.) внутри dompdf, но у меня не вышло. Необходимо переводить их в формат afm и ешё что-то, я забил.

Вместо этого, поскольку у меня есть root-доступ и сервер мой, я решил передать генерацию PDF-файлов на сторону нормального ПО, а именно — компоненту unoconv внутри LibreOffice. Всё нижеперечисленное работает в Ubuntu 12.

Идём в консоль сервера. Если есть ISPManager, в нём есть Shell-клиент. Если вы под Windows 10, запустите Bash для Ubuntu.

Ставим unoconv:

apt-get install unoconv

Запускаем по инструкции и видим ошибку типа Office probably died. Unsupported URL: «type detection failed». Ставим LibreOffice:

apt-get install libreoffice

Переходим в директорию и запускаем:

doc2pdf word.docx

Видим массу каких-то ошибок, но — ура! — файл PDF создался в той же директории и с тем же названием, что и DOC!

Теперь нужно это всё настроить через PHP:

shell_exec('doc2pdf '.$the_file);

Ничего не работает. Скорее всего, LibreOffice стоит для root-пользователя, а PHP запускается из-под другого. Нужно запустить команду через sudo, для этого добавим в sudo пользователя, из-под которого работает PHP. Я использовал достаточно примитивный путь: открыл файл etc/sudoers и дописал вниз такое:

www-data ALL=(ALL) NOPASSWD: ALL

Разумеется, нормальный юникс-админ сделал бы по-другому, но у меня не было ни знаний, ни времени.

Также мы заметили, что наш PDF-файл генерируется с владельцем root, что в дальнейшем осложнит манипуляции с ним (удаление через PHP-скрипт, например). Поэтому в нашу команду запихаем ещё и изменение пользователя файла. Итоговая команда в PHP выглядит так:

chdir($dir); // эта команда нужна, если файлы создаются в другой директории, нежели PHP-скрипт
shell_exec('sudo doc2pdf '.$the_name.'.docx'; sudo chown www-data '.$the_name.'.pdf');

Надеюсь, это сэкономит кому-то время.



Игорь Балькин
Автор: Игорь Балькин. Веб-разработчик с 2001 года. Разработчик сайтов на WordPress, CRM и веб-приложений. Минск, Беларусь. Контактная информация.

Прокомментировать

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Можно использовать HTML-тэги и атрибуты:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>