Re: [xsl] RE: How to transform <BR> to </P><P>

Subject: Re: [xsl] RE: How to transform <BR> to </P><P>
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Fri, 19 Jan 2001 15:49:40 +0000
Hi Chris,

> There are more than <BR> and text() as children of <TEXT>. And I
> wish to break the text into paragraphs only when it reaches a <BR
> TYPE="END">.

I'm not sure what the best way of doing this is, but this is the
method that I usually use.  Basically, this is a grouping problem.
You want to group all the nodes within the TEXT element according to
which BR[@TYPE = 'END'] they come after and place each group within a
P element.

If you apply templates just to the BR[@TYPE = 'END'] elements, then
you can gather the relevant content for the paragraph at that point:
you know that there need to be as many paragraphs as BR[@TYPE = 'END']
elements (of course as long as they're preceded by some content).

So a template that takes a BR[@TYPE = 'END'] element and outputs a
paragraph of the content preceding it would look something like:

<xsl:template match="BR[@TYPE = 'END']">
   <!-- locate the preceding BR -->
   <xsl:variable name="br-before"
                 select="preceding-sibling::BR[@TYPE = 'END'][1]" />
   <!-- the content are those nodes that come before this one and
        after the preceding BR -->
   <xsl:variable name="content"
                          [not($br-before) or
                                        [@TYPE = 'END'][1]) =
                           generate-id($br-before)]" />
   <!-- if there are any nodes in that list -->
   <xsl:if test="$content">
      <!-- output a paragraph -->
         <!-- with a copy of those nodes as the content -->
         <xsl:apply-templates select="$content" />

To make this work properly, you need to only apply templates to those
BR elements:

   <xsl:apply-templates select="BR[@TYPE = 'END']" />

You also need to create a paragraph for any stray text at the end of
the TEXT element that *doesn't* have a BR[@TYPE = 'END'] following it:

   <xsl:variable name="end-content"
                 select="node()[not(self::BR[@TYPE = 'END']) and
                                      [@TYPE = 'END'])]" />
   <xsl:if test="$end-content">
         <xsl:apply-templates select="$end-content" />

These both need to go within a template that matches the TEXT element:

<xsl:template match="TEXT">
   <xsl:apply-templates select="BR[@TYPE = 'END']" />
   <xsl:variable name="end-content"
                 select="node()[not(self::BR[@TYPE = 'END']) and
                                      [@TYPE = 'END'])]" />
   <xsl:if test="$end-content">
         <xsl:copy-of select="$end-content" />

Finally, to ignore all that irrelevant whitespace, you need to strip
spaces within the TEXT element:

<xsl:strip-space elements="TEXT" />

This does the grouping that you're after.  There are other ways of
doing it - in particular you could use Muenchian grouping with keys in
order to achieve the same effect.  You can also write a recursive to
template to do collect the relevant nodes for the paragraph content.

I haven't addressed outputting the character entity - I think you know
how to do that from last time - or turning the AUTOITALIC element into
an ITALIC element, which you should be able to do yourself, I think?
I hope that helps anyway,


Jeni Tennison

 XSL-List info and archive:

Current Thread