Расширенный генератор документов для Битрикс24


С недавних пор в Битрикс24 появился замечательный функционал – генератор документов. Он способен удовлетворить большинство потребностей в генерации документов для CRM, однако иногда его функционала не хватает для решения специфических задач. Одну из таких задач мы недавно решали.

Основная проблема заключалась в том, что необходимо было генерировать сам шаблон документа, по которому вдальнейшем произойдет его генерация. Что-то вроде ситуации, когда у вас есть несколько организаций (филиалов), совместно использующих Битрикс24, и необходимо для каждой организации генерировать собственные «шапки» и «футеры» документов по определенной логике.

Да, конечно можно создать несколько шаблонов, но в данном случае требовалось передать некие параметры сначала в сами эти шаблоны, а уже потом уже сгенерировать документ. Также по условиям заказчика мы должны были получать от пользователя DOCX с телом документа, вставлять в него шапку нужной организации, вставлять данные, файлы и картинки, как в шапку, так и в документ, и отдавать пользователю сгенерированный PDF-файл.

В качестве шаблонов необходимо было использовать файлы .DOCX
Соответственно, следует разобраться в том, как они устроены. По своей сути .DOCX файл это обычный ZIP-архив, содержащий в себе файлы XML-разметки документа, сохраненные в файле изображения, стили и т.п. Открыть его для изучения можно любым ZIP-архиватором, например 7Zip:

Снимок экрана 2019-07-08 в 16.10.40

Самый интересный файл находится по пути \word\document.xml

Снимок экрана 2019-07-08 в 16.11.54
В этом XML-файле и представлен наш документ, точнее его разметка. В нем уже можно искать текстовое содержимое вашего шаблона, и заменять его на необходимые данные. Например расставив «плейсхолдеры» для данных, найти их и заменить реальными данными.

Снимок экрана 2019-07-08 в 16.13.27

Работать с ZIP-архивами в PHP можно с помощью расширения ZipArchive:

$docx = new ZipArchive();
$docx->open($path);
$content = $ docx ->getFromName(‘word\document.xml’);
$content = str_replace($placeholder, $text, $content);
$ docx ->addFromString(‘word\document.xml’, $content);
$docx->close();

На этом этапе все достаточно просто. Однако нам необходимо иметь возможность вставлять не только текстовые данные, но и изображения (например образцы подписей), и даже другие docx-файлы прямо в этот шаблон (сохраняя внутренние стили форматирования). Неудобство составляет сама структура DOCX. К тому же на выходе заказчик должен получить PDF-документ. То есть необходимо сгенерированный DOCX переконвертировать в PDF.

Для решения этих двух проблем мы решили использовать OpenOffice, а точнее его форк — LibreOffice. Он без проблем ставиться на CentOS, и что главное умеет работать из командной строки. То есть мы можем его запустить прямо из PHP-скрипта. LibreOffice умеет конвертировать DOCX в PHP, и также DOCX в ODT.

Например конвертация DOCX в ODT из PHP:
shell_exec('/usr/bin/libreoffice --headless -convert-to odt:"writer8" --outdir '.realpath("/template.docx"));

Зачем нам нужен ODT? Это аналог DOCX от мира свободного ПО, и его внутренняя структура намного проще и чище (особенно это ценно при вставке одного документа в тело другого). Таким образом план такой:

1. Берем у клиента DOCX-шаблон.
2. Конвертируем его в ODT с помощью LibreOffice
3. Производим манипуляции с файлами
4. Конвертируем в PDF и отдаем клиенту.

ODT-файл также представляет из себя ZIP-архив:

Снимок экрана 2019-07-08 в 16.20.42

Все основное содержимое хранится в \content.xml, стили в styles.xml, картинки в папке Pictures и файле meta.xml

Для наглядности посмотрим на тот же фрагмент кода основного содержимого:

Снимок экрана 2019-07-08 в 16.21.49

Как видите, практически идентично с DOCX, но несколько понятнее и больше похоже на привычный HTML. Из-за более простой структуры, с этим файлом можно легче «играться» — например для вставки изображения мы просто вставили его в файл, и посмотрели что изменилось. Добавился лишь такой блок:

<draw:frame draw:style-name="fr1" draw:name="Picture1" text:anchor-type="as-char" svg:width="5cm" svg:height="5cm" draw:z-index="0"><draw:image xlink:href="Pictures/file1.jpg'" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad"/></draw:frame>

Как видите – все совсем не сложно и интуитивно понятно.

Ну и самое сложное – вставка одного DOCX файла в другой DOCX файл. Мы также конвертируем оба эти файла в ODT, затем берем их содержимое и переименовываем все стили в них, например, давая им префикс (file1_ и file2_ например). Вставляем содержимого одного файла в другой на месте заранее вставленного текстового «плейсхолдера». Затем копируем картинки одного файла в другой, также давая им префикс, и переименовывая их соответственно в файле meta.xml.

Так содержимое одного файла без проблем вставляется в другой. Например, у нас есть документ и шаблон. Шаблон содержит в себе шапку, в которую нужно вставить графический файл и данные, а после этого все вместе вставить в документ:

Вставлемый файл:

Снимок экрана 2019-07-08 в 16.26.11

Шапка-шаблон:

Снимок экрана 2019-07-08 в 16.27.17

Результат:

Снимок экрана 2019-07-08 в 16.28.39

,

Добавить комментарий

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