Форум программистов, компьютерный форум, киберфорум
diadiavova
Войти
Регистрация
Восстановить пароль
Оценить эту запись

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

Запись от diadiavova размещена 11.06.2015 в 10:37
Обновил(-а) diadiavova 12.04.2016 в 15:30
Метки libreoffice, openoffice, xslt

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

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

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

Как соотносится одно с другим - мне пришлось выяснять и вот что я выяснил. Нашел в сети вот такую таблицу
Таблица размеров шрифта. Правда там все представлено в пикселях, а не в точках. Но нашел информацию о том, как перевести одно в другое. Далее примерно округляя написал вот такую функцию для перевода единиц.
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
  <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
1
2
3
4
5
6
7
8
9
  <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
1
2
3
4
5
6
7
8
9
10
  <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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    <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
1
2
3
  <xsl:template match="text()">
    <xsl:value-of select="my:calculate-styles(.)" disable-output-escaping="yes"/>
  </xsl:template>
И можно смотреть, что получилось.
Код.

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<?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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  <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
1
2
3
4
5
6
  <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
1
2
3
4
5
6
7
8
9
10
11
12
13
  <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>
Это не окончательная версия шаблона, поскольку в некоторых случаях, возможно, нам захочется давать и обратные ссылки, а значит для таких случаев придется предусмотреть и якоря возле ссылок. Но пока так.

>>
Размещено в Без категории
Просмотров 589 Комментарии 0
Всего комментариев 0
Комментарии
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.