Re: [xsl] From WordprocessingML inline styles to nested inline elements

Subject: Re: [xsl] From WordprocessingML inline styles to nested inline elements
From: Yves Forkl <Y.Forkl@xxxxxx>
Date: Tue, 27 Mar 2007 18:44:33 +0200
Hi David, Wendell and whoever might be curious,

here is a (somewhat late) follow-up to our discussion of dealing with configurable inline style nesting.

After testing both of your solutions, I discovered that David's does not do the right thing: the inner-most run style from the hierarchy is not wrapped around the text, but ends up as a singleton element just before the run's text. Wendell's solution, however, works perfectly (after minor typo corrections), so I favour this one. I even managed to dispose of one template and of explicit style names, so now the stylesheet looks like this:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
 xmlns:w="data:,w"
 exclude-result-prefixes="w">

<xsl:variable name="styles_tree"
    select="document('wp1style.xml')/style_nesting/*"/>

<xsl:template match="w:r">
<xsl:apply-templates select="$styles_tree" mode="style">
<xsl:with-param name="r" select="."/>
<!-- initiates traversal of the style-nesting tree, carrying the content -->
</xsl:apply-templates>
</xsl:template>


<xsl:template match="*" mode="style">
  <!-- matches elements inside the style nesting tree -->
  <xsl:param name="r"/>
  <xsl:variable name="contents">
    <!-- consolidates generation of the contents of this node
         since it's the same either way -->
    <xsl:apply-templates mode="style">
      <!-- continue down the style nesting tree -->
      <xsl:with-param name="r" select="$r"/>
    </xsl:apply-templates>
    <xsl:if test="not(*)">
      <!-- but if we're at the bottom, write our text -->
      <xsl:value-of select="$r/w:t"/>
    </xsl:if>
  </xsl:variable>
  <xsl:choose>
    <xsl:when test="$r/w:rPr/*[local-name()=local-name(current())]">
      <!-- if parameterized input has our style, we are copied
           (note: you might want a more sophisticated way to map them) -->
      <xsl:copy>
        <xsl:copy-of select="$contents"/>
      </xsl:copy>
    </xsl:when>
    <xsl:otherwise>
      <!-- even if not, we want our content -->
      <xsl:copy-of select="$contents"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>


As I will be generating the stylesheet, I decided to integrate the description of the style nesting into the stylesheet itself. That is, the configuration file is now read only at stylesheet creation time, not at run time. This gives me a generated stylesheet like this one:


<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
xmlns:w="data:,w"
xmlns:lookup="http://www.srz.de/xmlns/yforkl/xslt/lookup";
exclude-result-prefixes="w lookup">


  <lookup:style_nesting>
    <lookup:b><lookup:i><lookup:u/></lookup:i></lookup:b>
  </lookup:style_nesting>

<xsl:template match="w:r">
<xsl:apply-templates
select="document('')/xsl:stylesheet/lookup:style_nesting/*"
mode="style">
<xsl:with-param name="r" select="."/>
<!-- initiates traversal of the style-nesting tree, carrying the content -->
</xsl:apply-templates>
</xsl:template>


<xsl:template match="*" mode="style">
<!-- matches elements inside the style nesting tree -->
<xsl:param name="r"/>
<xsl:variable name="contents">
<!-- consolidates generation of the contents of this node
since it's the same either way -->
<xsl:apply-templates mode="style">
<!-- continue down the style nesting tree -->
<xsl:with-param name="r" select="$r"/>
</xsl:apply-templates>
<xsl:if test="not(*)">
<!-- but if we're at the bottom, write our text -->
<xsl:value-of select="$r/w:t"/>
</xsl:if>
</xsl:variable>
<xsl:choose>
<xsl:when test="$r/w:rPr/*[local-name()=local-name(current())]">
<!-- if parameterized input has our style, we are copied
(note: you might want a more sophisticated way to map them) -->
<xsl:element name="{local-name()}">
<xsl:copy-of select="$contents"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<!-- even if not, we want our content -->
<xsl:copy-of select="$contents"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>


</xsl:stylesheet>


Currently, I am thinking about getting even more "static", by replacing the above generic style parsing approach by individual generated "classical" templates, each mapping one style and controlling the subsequently called templates.


So I'd start out with a template for the outermost style:

<xsl:template match="w:r[w:rPr/w:b]">
  <xsl:element name="b">

Inside the template, I would like to pass the same run (w:r) to a template dealing with the next level's style, matched by "w:r[w:rPr/w:i]", and so on. I wonder how can I "materialize" the style hierarchy, read from the configuration file, into a series of properly chained templates.

My ideas: either use one mode per style-specific template, or one single named template that gets called recursively, with the current style and the run's text as parameters. Which way would you recommend?

Yves

Current Thread