пятница, 8 апреля 2016 г.

Об XML-фильтрах OpenOffice. Часть 6-я.

  1. Чуть не забыл о шрифтах
  2. Таблицы
  3. Списки
  4. Заключение

Чуть не забыл о шрифтах


Имя шрифта определяется атрибутом style:font-name. Его определение просто надо добавить в функию my:calculate-styles. Ничего необычного, так что просто привожу текущую версию функции.
Кликните здесь для просмотра всего текста
Код xml Выделить

<xsl:function name="my:calculate-styles">

  <xsl:param name="node"/>

  <xsl:variable name="text-position">

    <xsl:variable name="att-value" select="my:style-property-value($node, 'style:text-position')"/>

    <xsl:choose>

      <xsl:when test="starts-with($att-value, 'sub') ">

        <xsl:text>

          SUB

        </xsl:text>

      </xsl:when>

      <xsl:when test="starts-with($att-value, 'super')">

        <xsl:text>

          SUP

        </xsl:text>

      </xsl:when>

    </xsl:choose>

  </xsl:variable>

  <xsl:variable name="FONT" select="my:style-property-value($node, 'style:font-name')"/>

  <xsl:variable name="I" select="my:style-property-value($node, 'fo:font-style')"/>

  <xsl:variable name="B" select="my:style-property-value($node, 'fo:font-weight')"/>

  <xsl:variable name="U" select="my:style-property-value($node, 'style:text-underline-style')"/>

  <xsl:variable name="S" select="my:style-property-value($node, 'style:text-line-through-style')"/>

  <xsl:variable name="SIZE" select="my:style-property-value($node, 'fo:font-size')"/>

  <xsl:variable name="COLOR" select="my:style-property-value($node, 'fo:color')"/>

  <xsl:variable name="content">

    <xsl:value-of select="$node"/>

  </xsl:variable>

  <xsl:variable name="calculated-size"  select="my:calculate-font-syze($SIZE)"/>

  <xsl:value-of select="my:wrap-with-tags($node, (

                     (if ($I = 'italic') then 'I' else ''),

                     (if ($B = 'bold') then 'B' else ''),

                     (if ($U = 'solid') then 'U' else ''),

                     (if ($S = 'solid') then 'S' else ''),

                     (if ($calculated-size != '') then concat('SIZE=', $calculated-size) else ''),

                     (if ($FONT != '') then concat('FONT=', $FONT) else ''),

                     (if ($COLOR != '') then concat('COLOR=', $COLOR) else ''),

                     $text-position), 1)" />

</xsl:function>


Таблицы



Для построения таблиц нам понадобятся элементы table:table - сами таблицы, table:table-row - строки таблиц и table:table-cell - ячейки. С таблицами все просто - для них создаются теги TABLE внутри которых (без пробелов переходов строк) размещается содержимое. Строки после обработанных дочерних элементов вставляют новую строку. Что до ячеек, то тут есть нюансы. Нам нужно получить содержимое ячеек на одной строке, а внутри ячеек в документе обычно расположен элемент text:p, который у нас обрабатывается как блочный. Поэтому для элементов text:p находящихся внутри ячеек мы создаем отдельный шаблон с атрибутом mode="table" и при вызове шаблонов из ячейки обрабатываем их именно в этом режиме. Кроме того(на всякий пожарный) обработанное содержимое ячеек сначала передадим переменной, а потом нормализуем строки. Если ячейка не последняя в строке добавляем после нее вертикальную черту.
Код xml Выделить

<xsl:template match="text:p" mode="table">

  <xsl:apply-templates/>

</xsl:template>

 

<xsl:template match="table:table-cell">

  <xsl:variable name="app-t">

    <xsl:apply-templates mode="table"/>

  </xsl:variable>

  <xsl:value-of select="normalize-space($app-t)"/>

  <xsl:if test="following-sibling::table:table-cell">

    <xsl:text>

    </xsl:text>

  </xsl:if>

</xsl:template>

 

<xsl:template match="table:table-row">

  <xsl:apply-templates/>

  <xsl:text>

  </xsl:text>

</xsl:template>

 

<xsl:template match="table:table">

  <xsl:text>

    [TABLE]

  </xsl:text>

  <xsl:apply-templates/>

  <xsl:text>

    [/TABLE]

  </xsl:text>

</xsl:template>

Списки



По поводу обработки содержимого элементов списка. При вычислении отступов для списков вставляются дополнительные теги indent, это сильно разносит списки по документу, поскольку увеличиваются не столько отступы, сколько расстояния между пунктами. Поэтому в том месте, где мы вычисляли отступы для блочных элементов (text:h и text:p) неплохо бы ввести дополнительное условие, чтобы отступы вычислялись только если элемент не является непосредственным потомком пункта списка.

Списки создаются с помощью элементов text:list, а отдельные пункты списков - text:list-item. Вроде все просто, но тут сразу встает несколько вопросов: как определить нумерованный это список или маркированный. Типы маркеров в разметке Cyberforum не поддерживаются, а вот типы нумерации - вполне. Как определить тип нумерации, если список вложенный. Вот этими вопросами сейчас и займемся.

Итак, из разметки мы видим, что список не содержит информации о маркерах, а стало быть единственная зацепка, по которой мы можем что-то узнать - это уже известный нам атрибут text:style-name. Посмотрим на один из стилей списков, представленных в документе и попытаемся выяснить что-нибудь полезное.
Кликните здесь для просмотра всего текста
Код xml Выделить

<text:list-style style:name="L1">

  <text:list-level-style-bullet text:level="1" text:style-name="WW_5f_CharLFO1LVL1" text:bullet-char="п‚·">

    <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">

      <style:list-level-label-alignment text:label-followed-by="listtab" fo:text-indent="-0.635cm" fo:margin-left="1.27cm"/>

    </style:list-level-properties>

    <style:text-properties style:font-name="Symbol"/>

  </text:list-level-style-bullet>

  <text:list-level-style-bullet text:level="2" text:style-name="WW_5f_CharLFO1LVL2" text:bullet-char="o">

    <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">

      <style:list-level-label-alignment text:label-followed-by="listtab" fo:text-indent="-0.635cm" fo:margin-left="2.54cm"/>

    </style:list-level-properties>

    <style:text-properties style:font-name="Courier New"/>

  </text:list-level-style-bullet>

  <text:list-level-style-bullet text:level="3" text:style-name="WW_5f_CharLFO1LVL3" text:bullet-char="">

    <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">

      <style:list-level-label-alignment text:label-followed-by="listtab" fo:text-indent="-0.635cm" fo:margin-left="3.81cm"/>

    </style:list-level-properties>

    <style:text-properties style:font-name="Wingdings"/>

  </text:list-level-style-bullet>

  <text:list-level-style-bullet text:level="4" text:style-name="WW_5f_CharLFO1LVL4" text:bullet-char="п‚·">

    <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">

      <style:list-level-label-alignment text:label-followed-by="listtab" fo:text-indent="-0.635cm" fo:margin-left="5.08cm"/>

    </style:list-level-properties>

    <style:text-properties style:font-name="Symbol"/>

  </text:list-level-style-bullet>

  <text:list-level-style-bullet text:level="5" text:style-name="WW_5f_CharLFO1LVL5" text:bullet-char="o">

    <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">

      <style:list-level-label-alignment text:label-followed-by="listtab" fo:text-indent="-0.635cm" fo:margin-left="6.35cm"/>

    </style:list-level-properties>

    <style:text-properties style:font-name="Courier New"/>

  </text:list-level-style-bullet>

  <text:list-level-style-bullet text:level="6" text:style-name="WW_5f_CharLFO1LVL6" text:bullet-char="">

    <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">

      <style:list-level-label-alignment text:label-followed-by="listtab" fo:text-indent="-0.635cm" fo:margin-left="7.62cm"/>

    </style:list-level-properties>

    <style:text-properties style:font-name="Wingdings"/>

  </text:list-level-style-bullet>

  <text:list-level-style-bullet text:level="7" text:style-name="WW_5f_CharLFO1LVL7" text:bullet-char="п‚·">

    <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">

      <style:list-level-label-alignment text:label-followed-by="listtab" fo:text-indent="-0.635cm" fo:margin-left="8.89cm"/>

    </style:list-level-properties>

    <style:text-properties style:font-name="Symbol"/>

  </text:list-level-style-bullet>

  <text:list-level-style-bullet text:level="8" text:style-name="WW_5f_CharLFO1LVL8" text:bullet-char="o">

    <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">

      <style:list-level-label-alignment text:label-followed-by="listtab" fo:text-indent="-0.635cm" fo:margin-left="10.16cm"/>

    </style:list-level-properties>

    <style:text-properties style:font-name="Courier New"/>

  </text:list-level-style-bullet>

  <text:list-level-style-bullet text:level="9" text:style-name="WW_5f_CharLFO1LVL9" text:bullet-char="">

    <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">

      <style:list-level-label-alignment text:label-followed-by="listtab" fo:text-indent="-0.635cm" fo:margin-left="11.43cm"/>

    </style:list-level-properties>

    <style:text-properties style:font-name="Wingdings"/>

  </text:list-level-style-bullet>

  <text:list-level-style-number text:level="10" style:num-suffix="." style:num-format="1">

    <style:list-level-properties text:list-level-position-and-space-mode="label-alignment">

      <style:list-level-label-alignment text:label-followed-by="listtab" text:list-tab-stop-position="6.985cm" fo:text-indent="-0.635cm" fo:margin-left="6.985cm"/>

    </style:list-level-properties>

  </text:list-level-style-number>

</text:list-style>



Как нетрудно заметить, внутри стиля списка есть набор элементов text:list-level-style-bullet и text:list-level-style-number. Это отдельные стили, которые описывают каждый отдельный уровень вложенного списка. Первый тип указывает, что список должен быть маркированным, из него мы можем узнать тип маркера, но нам эта информация ни к чему. Второй указывает, что список на данном уровне должен быть нумерованным и он содержит тип нумерации style:num-format(1, I, i, a, A). Форматы имеют именно такой вид как нам и надо, так что, если мы будем их использовать, то можно использовать напрямую. Кроме того, атрибут text:level указывает к спискам какого уровня надо применять данные правила.
Большинство свойств мы просто проигнорируем, хотя, если нужно, то некоторые можно и задействовать. А вот формат числа для вложенных списков мы, пожалуй используем. Наша главная задача найти нужный стиль для нужного списка, а там уже поиск конкретного свойства - не проблема.

Итак, результат преобразования нашего тестового документа.

Кликните здесь для просмотра всего текста
Цитата:
Ссылка на закладку
   Заголовок 1
  Обычный текст
   Заголовок 2
   Заголовок 3
   Заголовок 4
  
   Заголовок 5
   Заголовок 6
   Заголовок 7
   Название
  Подзаголовок
   Сильное выделение
  Строгий
  
Цитата 2
  Слабая ссылка
   Сильная ссылка
  
   Название книги
  
Абзац списка.
  Жирный
  Курисив
  Подчеркнутый
  Перечекнутый
  ХНижний индекс
  ХВерхний идекс
  Крупный шрифт 72
  Мелкий шрифт 8
  Левое выравнивание
  
Выравнивание по центру
  
Правое выравнивание
  
  • Первый пункт маркированного списка
      
  • Второй пункт маркированного списка
      
  
  1. Первый пункт                                                                                                           нумерованного списка
      
    1. Пункт 1.a;alfjasdf; lj l ;l lj ;lk 

      lkj;lkj l sdls dfslkj as;l
        
    2. Пункт 1.b b xj 'nj pf [eqyz&amp;
        
  2. Второй пункт нумерованного списка
      
Текст до десяти пробелов                  Текст после десяти пробелов
  Текст красного цвета
  Текст синего цвета
  Выделение желтым
  /*Далее таблица*/
  
1.1 1.2 1.3 1.4
1.2 2.2 2.3 2.4
3.1 3.2 3.3 3.4

   Ссылка на гуголь
  
  /*Далее картинка*/
  
  Закладка
  
Текст с отступом 2 см
  Ссылка на сноску1.
  
  
Сноски
  1. 1 Сноска на странице
      
      


Окончательный вариант преобразования - во вложении. Так же там файл-источник, результат преобразования и fodt-файл - отформатированный источник. Возможно где-то будут несовпадения, я мог что-то поменять в процессе, но в общем это те файлы, на которых я все проверял.

Заключение

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

Описанные здесь возможности не являются исчерпывающими. Есть огромный ряд вопросов, которые я не рассмотрел. Например я не представляю себе, что делать с изображениями. Они в документе содержаться в формате base64string и что с этим делать я не знаю, хотя при желании можно что-то придумать. Также не были рассмотрены возможности, которые могут дать именованные стили. Например для вставки кода можно создать стили, которые не будут обрабатываться обычным образом, а будут обрамляться тегами соответствующего языка программирования.

Был рассмотрен только одни тип документа. А там их довольно много и это не только таблицы и прочее, но даже тот же Writer может создавать те же XML-формы к примеру и их структура будет сильно отличаться от того, что мы уже рассмотрели, хотя многое из рассмотренного тоже окажется полезным.

Не были рассмотрены фильтры импорта. Они хоть выглядят очень сложными, в силу того, что нужно сформировать сложный документ, но на самом деле с ними, как мне кажется, работать даже проще. Заготовку документа можно вставить в преобразование практически ничего не меняя. А потом брать ту часть содержимого, которая должна быть генерироваться фильтром и заменять конструкциями XSLT. В результате можно добиться, чтобы документ соответствовал определенным требованиям и этого будет достаточно. В случае же экспорта никогда нельзя быть уверенным в том, что программа не выдаст чего-то совсем неожиданного в содержимом документа.

В общем и целом, все что здесь описано, скорее база для дальнейшего исследования, нежели готовая методика.

Комментариев нет :

Отправить комментарий