Стили текста.
Стили описывают способы отображения всего, что есть в документе. Характеристик там очень много и за подробностями, естественно, надо лезть в спецификацию. Но для текста мы будем рассматривать самые основные. Возьмем за основу разметку Cyberforum и постараемся сделать так, чтобы документ, созданный в OpenOffice можно было конвертировать в BB-коды, необязательно реализовывать все, но это будет достаточно показательным примером.
Итак смотрим документ. Возьмем то место, где текст выделен красным и синим цветами.
Код xml | Выделить |
<text:span text:style-name="T21">
Текст красного цвета
</text:span>
Код xml | Выделить |
<text:span text:style-name="T22">
Текст синего цвета
</text:span>
Код xml | Выделить |
<style:style style:name="T21"
style:family="text">
<style:text-properties fo:color="#ff0000"
fo:font-size="12pt"
fo:font-style="normal"
fo:font-weight="normal"
style:font-size-asian="12pt"
style:font-style-asian="normal"
style:font-weight-asian="normal"
style:font-size-complex="12pt"/>
</style:style>
<style:style style:name="T22"
style:family="text">
<style:text-properties fo:color="#00b0f0"
fo:font-size="12pt"
fo:font-style="normal"
fo:font-weight="normal"
style:font-size-asian="12pt"
style:font-style-asian="normal"
style:font-weight-asian="normal"
style:font-size-complex="12pt"/>
</style:style>
style:font-style-asian="normal" style:font-weight-asian="normal". Но нам и не нужно понимать абсолютно все. Мы пытаемся разобраться в основных характеристиках, а они выглядят вполне понятно. Конечно, может возникнуть вопрос: а какой из этих атрибутов важнее при определении нужной характеристики, например, fo:font-size или style:font-size-complex? Ведь какого-то из них может не оказаться там где мы будем его искать. Или он будет иметь другое значение. В таких ситуациях надо смотреть определения для других элементов, сравнивать, в худшем случае - смотреть спецификацию. Но в основном ничего непосильно сложного делать не придется.
Итак, займемся поиском цвета. Допустим у нас есть какая-то переменная или параметр под именем text-node, которая ссылается на некий текстовый узел. Наша задача, используя только что обретенные знания определить для этого узла цвет. Мы выяснили, что текстовый узел содержится в элементе, имеющем атрибут text:style-name, значение этого атрибута совпадает со значением атрибута style:name нужного нам стиля. А у этого стиля есть дочерний элемент style:text-properties и вот значение его атрибута fo:color нас как раз и интересует.
Для начала следует сказать, что LibreOffice (а скорей всего и OpenOffice тоже) не поддерживают фильтры, которые выдают форматы, отличные от XML. Таким образом аутпут-метод у нас должен быть XML и документ должен получаться well-formed. Поскольку мы собираемся генерировать текст, то придется заключить его в какой-то XML-элемент и в блок CDATA. Последнее не предусмотрено XSLT, так что его придется создать как текст.
Код xml | Выделить |
<xsl:template match="/">
<root>
<xsl:text disable-output-escaping="yes">
<![CDATA[
</xsl:text>
Здесь будет содержимое.
<xsl:text disable-output-escaping="yes">
]]>
</xsl:text>
</root>
</xsl:template>
Код xml | Выделить |
<xsl:function name="my:find-color">
<xsl:param name="text-node"/>
<xsl:variable name="style-name"
select="$text-node/../@text:style-name"/>
<xsl:variable name="style"
select="$text-node/ancestor::*[last()]//style:style[@style:name = $style-name]"/>
<xsl:value-of select="$style/style:text-properties/@fo:color"/>
</xsl:function>
Далее в том месте где будет формироваться содержимое выходного документа мы вызовем эту функцию для "красного" текста следующим образом.
Код xml | Выделить |
<xsl:value-of select="my:find-color(//text()[.
= 'Текст красного цвета'])"/>
Код xml | Выделить |
<?xml version="1.0"
encoding="utf-8"?>
<root>
<![CDATA[
#ff0000
]]>
</root>
Код xml | Выделить |
<xsl:value-of select="my:find-color(//text()[.
= 'Заголовок 1'])"/>
Код xml | Выделить |
<text:h text:style-name="P7"
text:outline-level="1">
Заголовок 1
</text:h>
Код xml | Выделить |
<style:style style:name="P7"
style:family="paragraph"
style:parent-style-name="Heading_20_1"
style:master-page-name="MP0">
<style:paragraph-properties style:page-number="auto"
fo:break-before="page"/>
</style:style>
Код xml | Выделить |
<style:style style:name="Heading_20_1"
style:display-name="Heading 1"
style:family="paragraph"
style:parent-style-name="Обычный"
style:next-style-name="Обычный"
style:default-outline-level="1"
style:class="text">
<style:paragraph-properties fo:margin-top="0.423cm"
fo:margin-bottom="0cm"
loext:contextual-spacing="false"
fo:keep-together="always"
fo:hyphenation-ladder-count="no-limit"
fo:keep-with-next="always"/>
<style:text-properties fo:color="#2e74b5"
style:font-name="Calibri Light"
fo:font-family="'Calibri Light'"
style:font-family-generic="swiss"
style:font-pitch="variable"
fo:font-size="16pt"
style:font-name-asian="Times New Roman"
style:font-family-asian="'Times New Roman'"
style:font-family-generic-asian="roman"
style:font-pitch-asian="variable"
style:font-size-asian="16pt"
style:font-name-complex="Times New Roman"
style:font-family-complex="'Times New Roman'"
style:font-family-generic-complex="roman"
style:font-pitch-complex="variable"
style:font-size-complex="16pt"
fo:hyphenate="false"
fo:hyphenation-remain-char-count="2"
fo:hyphenation-push-char-count="2"/>
</style:style>
Просматриваем родительские стили.
Теперь нам нужно написать новую функцию, которая будет двигаться по стилям, переходя от потомков к предкам, если это необходимо, до тех пор, пока не будет найден ответ. Но тут есть один нюанс: ведь такое может произойти с любым свойством, а не только с цветом. Мы видели, что непосредственный стиль заголовка вообще не содержал текстовых свойств, а какие-то правила к нему применялись и, как выяснилось правил этих оказалось довольно много. Поэтому писать отдельную функцию, которая запрашивает только цвет, при том, что информацию о любом другом свойстве стиля придется искать точно так же - как-то неразумно. Лучше напишем функцию, которая будет принимать стиль и имя атрибута, значение которого мы ищем в стилях. В этом случае мы не будем привязаны конкретно к цвету, а вместо этого получим возможность запрашивать любые свойства стиля.
Код xml | Выделить |
<xsl:function name="my:seek-parent-styles">
<xsl:param name="style"/>
<xsl:param name="property-name"/>
<xsl:variable name="property-value"
select="$style/style:*[contains(name(), '-properties')]/@*[name() = $property-name]"/>
<xsl:choose>
<xsl:when test="$property-value
!= ''">
<xsl:value-of select="$property-value"/>
</xsl:when>
<xsl:when test="$style[@style:parent-style-name]">
<xsl:variable name="parent-style"
select="$style/ancestor::*[last()]//style:style[@style:name = $style/@style:parent-style-name]"/>
<xsl:value-of select="my:seek-parent-styles($parent-style,
$property-name)"/>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:function>
Сначала у стиля, полученного с аргументом style мы пытаемся запросить значение атрибута непосредственно. Поскольку в примере со стилем заголовка мы видели, что стиль может иметь несколько дочерних элементов со свойствами, мы не ищем конкретно элемент style:text-properties, а вместо этого берем все элементы с префиксом style и содержащие "-properties" в имени, или можно было искать заканчивающиеся этим текстом (fn:ends-with). Если такой атрибут найден - возвращаем его значение, если нет - ищем у стиля атрибут style:parent-style-name. Найдено имя родительского стиля - находим сам стиль и вызываем эту же функцию рекурсивно, передавая ей при этом родительский стиль и имя атрибута, полученное из параметра. Не выполняется ни одно из условий - ничего не возвращаем.
Далее тестируем функцию
Код xml | Выделить |
<xsl:variable name="style-name"
select="//text()[. = 'Заголовок 1']/../@text:style-name"/>
<xsl:value-of select="my:seek-parent-styles(//style:style[@style:name
= $style-name], 'fo:color')"/>
Код xml | Выделить |
<xsl:variable name="style-name"
select="//text()[. = 'Заголовок 1']/../@text:style-name"/>
<xsl:value-of select="my:seek-parent-styles(//style:style[@style:name
= $style-name], 'fo:font-size')"/>
Обходим контейнеры.
Теперь попробуем применить эту же функцию к тексту, написанному курсивом(в моем документе-примере он записан как "Курисив", ошибку я заметил еще до того, как выложил, но исправлять не стал).Код xml | Выделить |
<xsl:variable name="style-name"
select="//text()[. = 'Курисив']/../@text:style-name"/>
<xsl:value-of select="my:seek-parent-styles(//style:style[@style:name
= $style-name], 'fo:font-style')"/>
Если просмотреть визуально все стили данного узла по иерархии вверх, то действительно там нигде нет информации о том, что текст должен отображаться курсивом. Для того, чтобы понять, откуда берется эта информация мы просмотрим код данного узла в немного более широком контексте чем раньше.
Код xml | Выделить |
<text:p text:style-name="Обычный">
<text:span text:style-name="Название_20_книги">
<text:span text:style-name="T4">
Курисив
</text:span>
</text:span>
</text:p>
Код xml | Выделить |
<style:style style:name="Название_20_книги"
style:display-name="Название книги"
style:family="text"
style:parent-style-name="Основной_20_шрифт_20_абзаца">
<style:text-properties fo:letter-spacing="0.009cm"
fo:font-style="italic"
fo:font-weight="bold"
style:font-style-asian="italic"
style:font-weight-asian="bold"
style:font-style-complex="italic"
style:font-weight-complex="bold"/>
</style:style>
Код xml | Выделить |
<text:p text:style-name="Обычный">
<text:span text:style-name="Название_20_книги">
<text:span text:style-name="T1">
Жирный
</text:span>
</text:span>
</text:p>
Код xml | Выделить |
<style:style style:name="T1"
style:family="text">
<style:text-properties fo:font-style="normal"
style:font-style-asian="normal"/>
</style:style>
Код xml | Выделить |
<xsl:function name="my:style-property-value">
<xsl:param name="node"/>
<xsl:param name="property-name"/>
<xsl:variable name="root"
select="$node/ancestor::*[last()]"/>
<xsl:variable name="ancestor-style-name"
select="$node/ancestor::*[@text:style-name][1]/@text:style-name"/>
<xsl:variable name="ancestor-style"
select="$root//style:style[@style:name = $ancestor-style-name]"/>
<xsl:variable name="property-value"
select="my:seek-parent-styles($ancestor-style, $property-name)"/>
<xsl:if test="$ancestor-style-name
!= ''">
<xsl:value-of select="
if ($property-value != '') then
$property-value
else
(my:style-property-value($node/ancestor::*[@text:style-name][1], $property-name))
"
/>
</xsl:if>
</xsl:function>
С параметром "property-name", как я полагаю, проблем не должно возникнуть. Это имя атрибута, значение которого мы ищем.
Параметр "node". Здесь предполагается, что первоначально будет передаваться текстовый узел, поэтому все операции выполняются не на нем непосредственно, а вместо этого будет искаться контейнер со стилем.
Переменная "root" возвращает корневой элемент документа. Я уже говорил, что функции не имеют контекста, так что его надо найти по узлу документа.
Переменная "ancestor-style-name". В ней мы вычисляем имя стиля ближайшего стилизированного контейнера. Я не стал тут делать как это делалось раньше, когда для поиска имени стиля использовался просто непосредственный контейнер узла по двум причинам: во-первых, нет гарантии, что любой текстовый узел будет расположен в контейнере, имеющем ссылку на стиль; во-вторых, функция вызывается рекурсивно и тут тоже нет гарантии, что непосредственный родитель элемента также будет содержать ссылкку на стиль. В примерах, которые мы рассматривали это не актуально, но гарантировать ничего нельзя, так что лучше написать более надежный код.
Переменная "ancestor-style" находит стиль по имени.
Переменная "property-value" ищет значение свойства в стиле "ancestor-style" с помощью ранее написанной функции "my:seek-parent-styles", то есть с просмотром родительских стилей.
Далее, если существует и найдено значение интересующего нас атрибута, то это значение и возвращается. Если стиль существует и значение свойства не найдено - вызываем функцию рекурсивно и в качестве узла, с которого будет начат отсчет передаем уже родительский контейнер, то есть перемещаемся по иерархии стилизованных контейнеров на один уровень выше(например в рассматриваемом случае от элемента со стилем "T1" переходим к элементу со стилем "Название_20_книги"). Если контейнер со стилем не найден - ничего не возвращаем.
Тестиуем функцию
Код xml | Выделить |
<xsl:value-of select="my:style-property-value(//text()[.
= 'Курисив'], 'fo:font-style')"/>
Комментариев нет :
Отправить комментарий