Журнал ВРМ World

Мировая история развития технологий управления эффективностью бизнеса – обзоры зарубежных публикаций

Печатая из XML: введение в XSL-FO

Как известно, получение приемлемого качества печати выходных данных из XML
является непростой задачей. Первая статья рубрики знакомит с одним простым, но
очень интересным подходом, с помощью которого можно получить правильный макет
страницы и корректно распечатать выходные данные.

Основная сложность, с которой сталкиваешь при печати из XML - это получение правильного расположения страницы. Задав неверный макет, вряд ли стоит рассчитывать на то, что документ распечатается корректно. В этой статье рассказывается о Рекомендации Международного консорциума W3C XSL Formatting Objects (Объекты форматирования языка таблиц стилей для XML, далее XSL-FO) и о том, как с помощью одного простого подхода, основанного на этой спецификации, можно разрешить указанную проблему.

Цель этой статьи - показать, как распечатать страницу, содержащую выходные данные (назовем их программой "Hello World"), после чего читатель сможет самостоятельно решать поставленные перед ним задачи. Для XSL-FO мы приведем самые простые макеты страниц, используя минимально возможное количество элементов, необходимых для получения приемлемого результата печати.

Одна из проблем состоит в том, что в отличие от получения HTML-документа из исходного XML с использованием XSLT, обработка потомков корневых элементов не сводится к простой инструкции xsl:apply-templates из корневого элемента. Для того, чтобы форматер мог генерировать страницы, требуются заранее подготовить выходные данные.

Для получения из XML PDF-документ в готовом для печати виде необходимо придерживаться следующей последовательности действий. Сначала, этот XML вместе с подходящей таблицей стилей (как ее разрабатывать будет показано ниже) должен быть подан в XSLT-процессор для генерации другого XML-документа, который использует пространство имен XSL-FO и который предназначен для XSL-FO-форматера. Вторым шагом будет отправка результата первой операции в XSL-FO-форматер, который затем сможет произвести конечный продукт - готовый для печати документ, внешний вид которого примелем для визуального восприятия.

XML      ->   XSLT          XSL-FO   ->    XSL-FO  printable 
document      engine        document     formatter document
               ^
               |
           XSLT stylesheet

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

XSL-FO-документ

Напомним, что исходной целью XSLT-преобразования является XSL-FO-документ. Генерируемый документ - который затем подается в XSL-FO-форматер - состоит из небольшого числа элементов:


<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
 <fo:layout-master-set>                      [1]
  <fo:simple-page-master  
    master-name="simple" >                   [2]
   <fo:region-body/>
   </fo:simple-page-master>
 </fo:layout-master-set> 
 <fo:page-sequence 
               master-reference="simple">    [3]
   <fo:flow 
              flow-name="xsl-region-body">   [4]
        content                              [5]
   </fo:flow>
 </fo:page-sequence>
</fo:root>

[1] Для того, чтобы разместить содержимое на странице, форматер должен знать, с каким размером ему придется работать. Элемент layout-master-set включает simple-page-master [2], который содержит подобную информацию, например, какой формат станиц вы используете: европейский A4 или американский US-letter. Кроме того, он также включает элемент region-body, который может рассматриваться как основная часть макета страницы.

[3] Для поддержания сложного разбиения (текста) на страницы используется элемент page-sequence. В данном случае для простого макета требуется очень мало содержания, особенно если учесть описанное выше определение отдельной страницы (элемент simple-page-master).

Помимо этого, элемент page-sequence содержит элемент flow [4]. Возможно, вы знакомы (а возможно, и нет) с идеей потока (flow). Я столкнулся с ней, когда занимался подготовкой публикаций для институтского журнала - с помощью настольных издательских средств я пытался наполнить текстом колонки страницы.

Чтобы указать, какую часть страницы заполнять текстом, используется xsl-region-body. Это значение отделяет основную часть страницы от ее внешних областей (поля, заголовки, колонтитулы и так далее).

Наконец, последний из пронумерованных элементов - некоторое содержимое (content) [5], которое является потомком основного потока. Сюда не следует вставлять простой текст, поскольку в этом случае форматеру пришлось бы догадываться, что вы хотите делать с этим текстом. Поэтому реальное содержимое потока приняло бы форму <fo:block>content</fo:block>, определяющую блок текста (прямоугольного по форме, желаемого размера и занимающего по умолчанию весь лист), который будет размещен как первый элемент на странице.

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

Ниже приведена элементарная таблица стилей XSLT, которая используется для получения XSL-FO-документа:


<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"   [1]
  xmlns:fo="http://www.w3.org/1999/XSL/Format">      [2]
  version="1.0">                 
  <xsl:output method="xml"/>                         [3]

  <xsl:template match="/">
     ....                                            [4]
  </xsl:template>

Other templates go here.                             [5]
</xsl:stylesheet>

[1] и [2] - это пространства имен содержимого XSLT и FO для данного документа, который различает выводимое содержимое и запросы на преобразование.

Если XSLT-процессор видит содержимое в пространстве имен FO, он просто записывает его в выводимые данные, то есть делает именно то, что нам нужно. [3] говорит, что мы хотим, чтобы выходной документ был правильным XML, другими словами, как раз тем, чем является XSL-FO-документ. [4] - это корневой шаблон, который запускается первым, следовательно, это точка, в которую мы добавим необходимое содержимое, упомянутое выше.

Наконец, в [5] мы можем начать использовать обработку. Теперь мы можем объединить эти два фрагмента, чтобы получить кое-что полезное. Ниже приведена законченная таблица стилей XSLT, которая используется XSLT-процессором для генерации правильного XSL-FO-документа.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:fo="http://www.w3.org/1999/XSL/Format">
  version="1.0">
  <xsl:output method="xml"/>

  <xsl:template match="/">
   <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
    <fo:layout-master-set>                  
      <fo:simple-page-master  
       master-name="simple"                 
                  page-height  ="29.7cm"       [1]
                  page-width   ="21cm"
                  margin-left  ="2.5cm"
                  margin-right ="2.5cm">
       <fo:region-body margin-top="3cm"/>      [2]
       </fo:simple-page-master>
 </fo:layout-master-set> 
 <fo:page-sequence 
               master-reference="simple">      [3]
   <fo:flow 
              flow-name="xsl-region-body">     [4]
        <xsl:apply-templates/>                 [5]
   </fo:flow>
 </fo:page-sequence>
</fo:root>
  </xsl:template>

  <xsl:template match="document">              [6]
   <fo:block>
    <xsl:apply-templates/>
   </fo:block>
  </xsl:template>


  <xsl:template match="head">                  [7]
   <fo:block>
      <xsl:apply-templates/>
    </fo:block>
  </xsl:template>

  <xsl:template match="para">                  [8]
    <fo:block>
     <xsl:apply-templates/>
    </fo:block>
  </xsl:template>

  <xsl:template match="em">                    [9]
   <fo:inline font-style="italic">
     <xsl:apply-templates/>
   </fo:inline>
  </xsl:template>


<xsl:template match="*">                       [10]
  <fo:block background-color="red">
   <xsl:apply-templates/>
  </fo:block>
 </xsl:template>

</xsl:stylesheet>

Исходный документ

Прежде чем перейти к рассмотрению структуры этого примера, необходимо упомянуть об исходном документе, для которого мы разрабатываем эту таблицу стилей. Предположим, что этот документ состоит из 4 элементов (см. ниже). Такая простая структура была выбрана умышленно, поскольку в подавляющем большинстве случаев она типична для XML-содержимого, предназначенного для XSL-FO-документа. Этот документ состоит только из элементов блока (head и para) и единственного вложенного элемента (em).

Рассматриваемый документ находится во внешнем элементе document и комбинации элементов head и para, которые включают некое выделение в тексте:

<document>
   <head>My very first xsl-fo document</head>
   <para>has an <em>important</em>  paragraph inside it</para>
</document>

Размер страницы определяется в [1] - в данном случае используется европейский стандарт. Если у вас принят другой формат, измените значения соответствующих атрибутов. Также добавлены поля, чтобы содержимое аккуратно располагалось на странице.

В [2] добавлено верхнее поле. [3] и [4] имеют тот же смысл, что и в первом примере. В [5] произошло существенное изменение: если раньше в этой точке было просто указано "содержимое" ("content"), то теперь мы воспользуемся возможностями XSLT, чтобы XSLT-процессор обработал входной документ. В [6] XSLT-процессор обрабатывает элемент document входного XML-файла, где размещается элемент fo:block, внутри которого располагается все оставшееся содеримое. Это не является проблемой, поскольку, к счастью, блоки могут вставляться в XSL-FO. Что этот элемент делает на самом деле - так это гарантирует, что любое содержимое, которое "просачивается", то есть не обрабатывается явно таблицей стилей, по-прежнему остается в блоке.

В [7], [8] и [9] мы снова в "привычном мире" XML и XSLT - сопоставляем элемент исходного документа и размещаем соответствующий элемент из словаря XSL-FO. Первые два пункта - идентичны и требуют только незначительных модификаций, последний немного отличается в том смысле, что это вложенный объект форматирования, который выводит текст курсивом.

[10] - это контейнер, который показывает, какие элементы, если такие имеются, не подверглись стилевому оформлению. Если применить стиль ко всем элементам, этот шаблон ничего не обработает. Его наличие так же необходимо, как использование отладки во время разработки.

Рассмотренная таблица стилей включает два новых элемента. Первый из них - это fo:block, который используется для многих элементов в этой таблице стилей. Это основной элемент верстки, который применяется для выравнивания текста; вспомните о теге p в HTML.

Элемент fo:inline - контейнер для встроенных элементов в XSL-FO. Каждый из указанных двух элементов располагает богатым набором свойств, которые синтаксически выражаются в виде атрибутов, используемых для форматирования содержимого.

Начиная новые страницы

Давайте усложним структуру исходного документа, дополнив ее разделом (section), который будет включать точку начала новой страницы. Тогда исходный документ будет иметь следующий вид:

<document>
  <section>
   <head>My very first xsl-fo document</head>
   <para>has an <em>important</em> paragraph inside it</para>
  </section>
  <section>
    <head>The second section, starting on a new page </head>
   <para>Some content in the second section</para>
  </section>
</document>

Теперь нам нужно применить стиль к этой дополнительной части документа, используя одно из доступных свойств блока:

<xsl:template match="section">
  <fo:block break-before="page">
    <xsl:apply-templates/>
  </fo:block>
</xsl:template>

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

<xsl:template match="head">        
   <fo:block    font-size="14pt"
                font-weight="bold"
                space-after="1cm"       
                space-after.conditionality = 'retain'
                >
      <xsl:apply-templates/>
   </fo:block>
</xsl:template>

Вот и все. Подведем краткий итог: в самом простом случае обработка выполняется за два этапа. Отправьте исходный документ и приведенную выше таблицу стилей XSLT в XSLT-процессор, и вы получите правильный XSL-FO-документ. Затем поместите его в XSL-FO-процессор: RenderX или Antenna House (оба являются коммерческими продуктами, но их можно бесплатно попробовать), либо FOP (некоммерческий проект).

Вы также можете скачать файлы с примерами, рассмотренными в данной статье: xsl-fo-assets.zip.

"Рекомендуемая литература"

Автор: Дейв Посон (Dave Pawson)