Re: [xsl] text indent problem

Subject: Re: [xsl] text indent problem
From: Abel Braaksma <abel.online@xxxxxxxxx>
Date: Tue, 09 Jan 2007 12:21:47 +0100
Barbara Lackenbauer wrote:
Hi Abel,


Some text, no indent, first paragraph, <b>bold text</b> and <i>italicized</i> text <absatz /> <i>some</i> other text, with indent <absatz /> <b>yet</b> other text with <b>bold text</b> and <i>italicized</i> text and <b><i>bold italic</i></b> text <absatz /> again other text

So it should be possible, that the paragraphs start either with text, <i> or <b> creating an indent and one block element up to the next <absatz/>

I hope this makes everything clearer.

It certainly does. I eventually took the following input XML document: <root> Lorem ipsum 1 no indent, but with <b>bold</b> and <i>italic</i> <absatz /> Lorem ipsum 2<b><i>bold and italic</i></b> <absatz /> <b><i>start with b/i</i></b>Lorem ipsum 3 </root>



and applied the following stylesheet (it is commented for clarity):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
xmlns:fo="http://www.w3.org/1999/XSL/Format";>
<xsl:output indent="yes"/>
<xsl:template match="/">
<fo:root>
<xsl:apply-templates select="root/node()"/>
</fo:root>
</xsl:template>
<!--
"siblings processor". Mode switch is necessary to prevent
'normal' (tree) processing for being copied to the result tree
-->
<xsl:template match="*[not(self::absatz)] | @*" mode="siblings">
<xsl:copy-of select="." />
</xsl:template>
<!-- first paragraph: no indent, just copy -->
<xsl:template match="*[not(preceding::absatz)][not(self::absatz)]">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>


<!-- remove default template matches for text nodes on the main axis -->
<xsl:template match="text()[preceding::absatz]" /> <!--
match text() and other nodes of which
first prec. sibling is <absatz> node
'priority' is needed, otherwise the delete template above is ambiguous
-->
<xsl:template match="node()[preceding-sibling::node()[1][self::absatz]]" priority="1">
<xsl:variable
name="current-sibling-id"
select="generate-id(preceding-sibling::absatz[1])" />
<fo:block text-indent="6pt">
<xsl:copy-of select="."/>
<!-- process siblings -->
<xsl:apply-templates select="
following-sibling::node()
[generate-id(preceding-sibling::absatz[1])
= $current-sibling-id]" mode="siblings"/>
</fo:block>
</xsl:template>


</xsl:stylesheet>

to get the following output XML:

<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format";>
Lorem ipsum 1 no indent, but with <b>bold</b> and <i>italic</i>
<fo:block text-indent="6pt">
Lorem ipsum 2<b>
<i>bold and italic</i>
</b>
</fo:block>
<fo:block text-indent="6pt">
<b>
<i>start with b/i</i>
</b>Lorem ipsum 3
</fo:block>
</fo:root>



As you may understand by looking at the stylesheet above, the triviality of the original stylesheet has gone. You see now a few advanced concepts like the use of generate-id() (search google or jenni tennison's pages or any good xslt book for explanation), an adapted identity-template, priorities and the use of modes. I had some trouble with walking the sibling axis and removing the normal axis, until I remembered that using modes was the easy solution to this.


Also note that it I use the distinction of '*' (which matches elements) and 'node()' (which matches all kinds of nodes, including text nodes). Often, this distinction is not necessary, but in your case, with mixed content, it becomes vital.

Of course, there are more ways to do this, and most certainly, you need to adopt it to your current situation. Perhaps some other experts have some comments on things I left out. One alternative I want to recommend is using a multi-pass, if your solution becomes too complex to handle in a single pass.

Cheers,
-- Abel Braaksma
  http://www.nuntia.nl

Current Thread