четверг, 7 апреля 2016 г.

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

  1. Вычисляем размер шрифта.
  2. Обертывание в теги можно сделать более универсальным.
  3. Отступы
  4. Ссылки

Вычисляем размер шрифта.



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

С размерами тоже все довольно просто, но, поскольку полученные знания мы применяем для генерации разметки Cyberforum, то тут следует обратить внимание на то, что размер шрифта мы получаем в точках, да еще и в формате ##pt, а нам надо привести все это дело к единицам, которые в HTML используются в элементе FONT.

Как соотносится одно с другим - мне пришлось выяснять и вот что я выяснил. Нашел в сети вот такую таблицу
Таблица размеров шрифта. Правда там все представлено в пикселях, а не в точках. Но нашел информацию о том, как перевести одно в другое. Далее примерно округляя написал вот такую функцию для перевода единиц.
Код xml Выделить

<xsl:function name="my:calculate-font-syze">

  <xsl:param name="font-size"/>

  <xsl:variable name="font-size-num" select="number(substring-before($font-size, 'pt'))"/>

  <xsl:choose>

    <xsl:when test="$font-size-num &lt; 11">

      1

    </xsl:when>

    <xsl:when test="$font-size-num &gt;= 11 and $font-size-num &lt;= 13 ">

    </xsl:when>

    <xsl:when test="$font-size-num &gt;= 14 and $font-size-num &lt;= 15 ">

      3

    </xsl:when>

    <xsl:when test="$font-size-num &gt;= 16 and $font-size-num &lt;= 17 ">

      4

    </xsl:when>

    <xsl:when test="$font-size-num &gt;= 18 and $font-size-num &lt;= 23 ">

      5

    </xsl:when>

    <xsl:when test="$font-size-num &gt;= 24 and $font-size-num &lt;= 35 ">

      6

    </xsl:when>

    <xsl:when test="$font-size-num &gt;= 36">

      7

    </xsl:when>

  </xsl:choose>

</xsl:function>

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

Обертывание в теги можно сделать более универсальным.



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

Сначала пишем функцию, которая заключает текст в теги одного типа.
Код xml Выделить

<xsl:function name="my:wrap-with-tag">

  <xsl:param name="content"/>

  <xsl:param name="tag"/>

  <xsl:variable name="without-value" select="if (contains($tag, '=')) then substring-before($tag, '=') else $tag"/>

  <xsl:value-of select="

              if ($tag != '')

              then concat('[', $tag, ']', $content, '[/', $without-value, ']')

              else $content"

            />

</xsl:function>

Функция принимает текст-содержимое и имя тега. Причем теги, имеющие параметр передаются прямо с ним. Ну например так COLOR="red".
Далее нам понадобится функция, которая будет получать список тегов и оборачивать содержимое во все по очереди, последовательно вызывая только что показанную функцию. Помимо содержимого и списка тегов функция еще имеет параметр index, он нужен для последовательного обхода списка тегов при рекурсивном вызове. К сожалению в XSLT-функциях необязательные параметры не поддерживаются, поэтому при вызове функции "вручную" параметру index всегда надо передавать 1.
Код xml Выделить

<xsl:function name="my:wrap-with-tags">

  <xsl:param name="content"/>

  <xsl:param name="tags"/>

  <xsl:param name="index"/>

  <xsl:value-of select="

              if ($index &gt;= fn:count($tags))

              then my:wrap-with-tag($content, $tags[$index])

              else my:wrap-with-tags(my:wrap-with-tag($content, $tags[$index]), $tags, $index + 1)

              " />

</xsl:function>

Ну и теперь можно уже вычислить несколько характеристик и оформить документ с помощью написанного кода. Добавим следующую функцию
Код xml Выделить

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

  <xsl:param name="node"/>

  <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 ($COLOR != '') then concat('COLOR=', $COLOR) else '')), 1)" />

</xsl:function>

Функция принимает текстовый узел, вычисляет ряд параметров для него, находя значения соответствующих атрибутов стиля. И, в зависимости от результата включает в имена тегов в список, который тут же передается функции my:wrap-with-tags, представленной только что. Теперь в шаблоне, обрабатывающем текст, заменим вызов функции, раскрашивающей текст, на более продвинутую
Код xml Выделить

<xsl:template match="text()">

  <xsl:value-of select="my:calculate-styles(.)" disable-output-escaping="yes"/>

</xsl:template>

И можно смотреть, что получилось.
Код.

Код xml Выделить

<?xml version="1.0" encoding="UTF-8"?>

<root>

  <![CDATA[

              [COLOR=#2e74b5][SIZE=4]Заголовок 1[/SIZE][/COLOR]

                Обычный текст

                [COLOR=#2e74b5]Заголовок 2[/COLOR]

                [COLOR=#1f4d78]Заголовок 3[/COLOR]

              вЂвЂ‚[COLOR=#2e74b5][I]Заголовок 4[/I][/COLOR]

              вЂвЂ

              вЂвЂ‚[COLOR=#2e74b5]Заголовок 5[/COLOR]

              вЂвЂ‚[COLOR=#1f4d78]Заголовок 6[/COLOR]

              вЂвЂ‚[COLOR=#1f4d78][I]Заголовок 7[/I][/COLOR]

                [SIZE=6]Название[/SIZE]

              вЂвЂ‚[COLOR=#5a5a5a]Подзаголовок[/COLOR]

              вЂвЂ‚[COLOR=#5b9bd5][I]Сильное выделение[/I][/COLOR]

              вЂвЂ‚[B]Строгий[/B]

              вЂвЂ‚[COLOR=#404040][B][I]Цитата 2[/I][/B][/COLOR]

              вЂвЂ‚[COLOR=#5a5a5a]Слабая ссылка[/COLOR]

              вЂвЂ‚[COLOR=#5b9bd5][B]Сильная ссылка[/B][/COLOR]

                

                [B][I]Название книги[/I][/B]

                [B][I]Абзац списка.[/I][/B]

                [B]Жирный[/B]

              вЂвЂ‚[I]Курисив[/I]

                [U]Подчеркнутый[/U]

                [S]Перечекнутый[/S]

                ХНижний индекс

                ХВерхний идекс

                [SIZE=7]Крупный шрифт 72[/SIZE]

                [SIZE=1]Мелкий шрифт 8[/SIZE]

                Левое выравнивание

                Выравнивание по центру

                Правое выравнивание

                Первый пункт маркированного списка

                Второй пункт маркированного списка

                

                Первый пункт                                                                                                           нумерованного списка

                Пункт 1.a;alfjasdf; lj l ;l lj ;lk

              lkj;lkj l sdls dfslkj as;l

              вЂвЂПункт 1.b b xj 'nj pf [eqyz&

                Второй пункт нумерованного списка

              вЂ‚ Текст до десяти пробелов                  Текст после десяти пробелов

                [COLOR=#ff0000]Текст красного цвета[/COLOR]

                [COLOR=#00b0f0]Текст синего цвета[/COLOR]

                Выделение желтым

                /*Далее таблица*/

                1.1

                1.2

                1.3

                1.4

                1.2

                2.2

                2.3

                2.4

                3.1

                3.2

                3.3

                3.4

                

                [COLOR=#0563c1][U]Ссылка на гуголь[/U][/COLOR]

                

                /*Далее картинка*/

                

                Закладка

                Текст с отступом 2 см

                Ссылка на сноску1[SIZE=1]Сноска на странице[/SIZE]

                .

                

                

              ]]>

</root>



Результат.
Цитата:
Заголовок 1
  Обычный текст
  Заголовок 2
  Заголовок 3
   Заголовок 4
  
  Заголовок 5
  Заголовок 6
   Заголовок 7
  Название
  Подзаголовок
   Сильное выделение
  Строгий
   Цитата 2
  Слабая ссылка
   Сильная ссылка
  
   Название книги
   Абзац списка.
  Жирный
  Курисив
  Подчеркнутый
  Перечекнутый
  ХНижний индекс
  ХВерхний идекс
  Крупный шрифт 72
  Мелкий шрифт 8
  Левое выравнивание
  Выравнивание по центру
  Правое выравнивание
  Первый пункт маркированного списка
  Второй пункт маркированного списка
  
  Первый пункт                                                                                                           нумерованного списка
  Пункт 1.a;alfjasdf; lj l ;l lj ;lk
lkj;lkj l sdls dfslkj as;l
  Пункт 1.b b xj 'nj pf [eqyz&
  Второй пункт нумерованного списка
  Текст до десяти пробелов                  Текст после десяти пробелов
  Текст красного цвета
  Текст синего цвета
  Выделение желтым
  /*Далее таблица*/
  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Сноска на странице
  .

Что ж, текст все больше похож на оригинальный документ, а значит - мы на верном пути.

Отступы



С отступами не все так просто. Отступы задаются в стиле абзаца атрибутом fo:margin-left. Значением будет дробное число и единица измерения. В качестве единицы измерения в нашем документе используются сантиметры, на них мы и будем ориентироваться. Для элемента с отступом в полтора сантиметра атрибут будет иметь значение 1.5cm. Проблема здесь в том, что ширина отступа может быть указана достаточно точно, в документе я видел отступы с точностью до третьего десятичного знака, а у нас в арсенале только тег indent, либо несколько вложенных тегов indent. Стало быть задача будет в том, чтобы округлить, выполнить преобразование типов данных и "наклепать" приблизительно нужное количество отступов. Кроме того, поскольку отступы будут вычисляться для элементов text:p и text:h, следует помнить о том, что функция поиска значения свойства стиля рассчитана на то, что ей первоначально передавался текстовый узел и она начинала поиск не с самого узла, а с контейнера этого узла. Таким образом, для поиска значения свойств элементов, мы могли бы переделать функцию, но будет проще найти первый дочерний узел абзаца и передавать функции именно его. Переделанный шаблон будет выглядеть так.
Код xml Выделить

<xsl:template match="text:h|text:p">

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

    <xsl:apply-templates/>

  </xsl:variable>

  <xsl:variable name="indent" select="substring-before(my:style-property-value(./*[1], 'fo:margin-left'), 'cm')"/>

  <xsl:choose>

    <xsl:when test="$indent = '' or starts-with($indent, '0')">

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

    </xsl:when>

    <xsl:otherwise>

      <xsl:variable name="indent-width" select="fn:ceiling(number($indent)) cast as xsd:integer"/>

      <xsl:variable name="indent-count" select="$indent-width" as="xsd:integer"/>

      <xsl:value-of select="my:wrap-with-tags($app-t, for $i in (1 to $indent-count) return 'indent' , 1)"/>

    </xsl:otherwise>

  </xsl:choose>

  <xsl:text>

  </xsl:text>

</xsl:template>

Здесь берутся только числовые значения (все, что в значении атрибута расположено до cm), отступы меньше сантиметра игнорируются. Дальше полученное значение переводится сначала в числовой тип, потом округляется(в данном случае я использовал функцию fn:ceiling, но можно было воспользоваться fn:round или fn:floor, если нужно), конвертируется в xsd:integer. И сколько получилось, столько раз и оборачивается в indent.



Об обычных ссылках писать особенно нечего, код предельно ясен: элемент text:a, адрес указан в атрибуте xlink:href. То есть вставка простого шаблона обрабатывает ссылки
Код xml Выделить

<xsl:template match="text:a">

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

    <xsl:apply-templates/>

  </xsl:variable>

  <xsl:value-of select="concat('[URL=&quot;', @xlink:href, '&quot;]', $app-t, '[/URL]')"/>

</xsl:template>

Но уж коль скоро помимо ссылок на внешние ресурсы у нас еще есть возможность создавать якоря внутри страницы и ссылаться на них, то можно поработать в этом направлении.

Во-первых, "местные" ссылки - в документе оформляются так же как и внешние, а нам надо для внешних создавать тег URL, а для "местных" ALINK. Кроме того, ссылки на элементы текущей страницы имеют адреса, начинающиеся с диеза #. Учитывая эти обстоятельства перепишем шаблон следующим образом.
Код xml Выделить

<xsl:template match="text:a">

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

    <xsl:apply-templates />

  </xsl:variable>

  <xsl:choose>

    <xsl:when test="starts-with(@xlink:href, '#')">

      <xsl:value-of select="concat('[ALINK=&quot;', substring-after(@xlink:href, '#'), '&quot;]', $app-t, '[/ALINK]')"/>

    </xsl:when>

    <xsl:otherwise>

      <xsl:value-of select="concat('[URL=&quot;', @xlink:href, '&quot;]', $app-t, '[/URL]')"/>

    </xsl:otherwise>

  </xsl:choose>

</xsl:template>

Это не окончательная версия шаблона, поскольку в некоторых случаях, возможно, нам захочется давать и обратные ссылки, а значит для таких случаев придется предусмотреть и якоря возле ссылок. Но пока так.

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

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