В этой статье будет продолжен рассказ об использовании Apache FOP — процессора XSL-FO компании Apache Software Foundation — для генерации документов строгой формы отчетности, стандартизированных для применения в РФ.

Ранее уже были обсуждены базовые функции FOP и принципы их работы, а данный материал посвящен практической стороне задачи. Для примера возьмем форму, имеющую в реестре отчетности наименование или код МХ-20. Она достаточно часто употребляется как в складском учете, так и вообще там, где происходит движение товарно-материальных ценностей в местах хранения. Для составления формы нужны следующие данные: название организации, обеспечивающей хранение чего-либо; обозначение подразделения организации, хранящей что-либо; коды этой организации (ОКПО и ОКДП); номера склада, камеры и секции; номер и дата составления документа; период времени, за который составляется документ; ФИО ответственного лица; данные о товарах, их стоимости, а также о том, сколько чего-либо прибыло в места хранения и убыло из них; информация о приложениях; подписи нескольких ответственных лиц, ФИО бухгалтера, дата.

Необходимых данных действительно много, но приводить в информационной системе можно не все, поскольку в процессе работы склада дозволяется вручную заполнять отдельные поля. Кстати, ставить подписи действительно придется вручную. Если в файле с данными не окажется части информации, ожидаемой в файле трансформации (XSL), то соответствующие поля останутся пустыми, что вполне приемлемо.

Прежде всего следует отделить форму от содержания. В случае генерации документа под формой будет пониматься XSL-FO преобразование, обеспечивающее подстановку данных в определенный метадокумент. А в последнюю очередь происходит преобразование метадокумента в документ в каком-либо реальном формате. Для Apache FOP основной выходной формат — Portable Document Format (PDF). Содержание ограничивается данными, поставляемыми информационной системой в виде XML-файла. Форму документа следует получить от заказчика или из правовой информационно-справочной системы. Вероятнее всего, она будет предоставлена в файле формата Microsoft Office Word или Excel.

Теперь рассмотрим некоторые принципы «верстки» документа. И здесь, пожалуй, придется заглянуть в описание стандарта. Кстати, преобразование можно выполнить и с помощью визуального редактора, например Altova Stylevision. Однако практика показывает, что красивый и опрятный документ проще и быстрее сделать вручную. В этом случае следует уделить внимание прежде всего размерам шрифтов и ячеек. Зачастую в исходном коде XSLT-преобразования эти параметры изменить проще. Кроме того, код, генерируемый визуальным редактором, грешит избыточностью.

В приведенном примере предполагается самый полный набор, а все данные будут готовиться в корпоративной системе. На практике при генерации документа порой требуется определенная обработка информации, однако более простым подходом станет транслирование данных в представление «один к одному». Все-таки генерация печатного документа де-факто относится к слою отображения данных, от которого не стоит требовать сложной логики их преобразования. По крайней мере, в системах, состоящих из более чем двух «слоев».

Схему данных разделим на три составляющие: шапка, таблица и то, что находится после нее. Проще действовать тем, у кого есть опыт верстки HTML, особенно тех времен, когда расположение элементов на экране версталось с помощью таблиц. При создании .fo-файлов следует основываться на том же принципе. Приведенный рисунок поможет понять, как осуществляется табличная верстка. Следует отметить, что в XSL-FO на порядок больше параметров, определяющих расположение компонентов и их атрибуты, нежели в HTML.

Во всем документе значения из XML вставляются посредством XSL-конструкций. Например, инструкция вставляет номер документа. Путь к тегу с данными определяется с помощью XPath. Это еще один стандарт, обеспечивающий эффективную работу с данными в формате XML. В приведенном примере в рамках текущей схемы будет взято содержимое тега Number, вложенного в тег DocumentData, который в свою очередь входит в тег Header. Чтобы обратиться к содержимому атрибута тега, следует добавить к названию атрибута префикс «коммерческое at» (@). Так, Period/@from ссылается на содержимое атрибута from тега Period.

Чтобы воспроизвести подчеркивание в итоговом документе, следует либо употребить символ подчеркивания, либо «окрасить» верхнюю или нижнюю границу ячейки таблицы. Причем в последнем случае в этой ячейке таблицы должен присутствовать какой-либо символ. Если поле окажется пустым, то имеет смысл поставить в ячейке пробел, который в кодировке utf-8 будет выглядеть так:   .

Табличные данные необходимо разбить на три секции. Первая из них, table-header, будет содержать неизменяемые строки, располагающиеся над телом таблицы, содержащим переменное число строк. Чтобы сформировать их из XML-файла, стоит применить конструкцию . В теге Table может содержаться несколько тегов Item, представляющих строки таблицы. И тогда ссылаться на имя позиции (товара) можно следующим образом: . Вывести порядковый номер записи в таблицы поможет такая конструкция: .

На прилагаемом к журналу диске имеется набор файлов для самостоятельного воспроизведения формы МХ-20. В нем содержатся файл-пример с данными, схема данных и файл преобразования.

Следует признать, что дело не обошлось без проблем. Первая из них — выбор способа распечатки получившегося документа. Если сгенерированный файл отдается пользователю, он сможет сам разобраться с тем, в каком виде его печатать. Однако если наша информационная система полностью работает на сервере, то печатать PDF напрямую из нее не получится. Найти принтер, понимающий набор инструкций PDF, бывает сложно, так как подобные устройства не очень широко распространены. Скорее всего удастся распечатать такой документ на принтере, поддерживающем набор инструкций PostScript. И здесь на помощь придет преобразователь pdf2ps, в проприетарных ОС иногда называемый pdftops. Если же возникнут проблемы с распечаткой полученных файлов PostScript, то следует попробовать привести при конвертации получаемый файл к PostScript Level 1.

Вторая проблема — определение значения суммы на страницу, обычно необходимой для таблицы. К сожалению, пока не существует стандартного встроенного механизма для подсчета подобного рода сумм. Одним из решений может быть написание расширения для Apache FOP, но это процесс чрезвычайно трудоемкий и далеко не тривиальный. В рассматриваемом примере сумма на страницу сохраняется в XML-файле сразу из информационной системы или на промежуточном этапе. Если таблица в документе одна, то несложно опытным путем определить количество записей на первой странице, когда она появится, а затем на второй и последующих страницах, если они заполнены целиком. Иными словами, для каждого «Итого на страницу» следует подготовить значение еще в момент генерации XML-файла. Нужно признать, что это не слишком красивое решение, но зато оно сработает.