Search code examples
xsltxslt-2.0xsl-fo

How do I match the elements generated by xsl-fo?


I have a .xsl file like this:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:exslt="http://exslt.org/common">
  <xsl:template match="/>
    <fo:root>
      <fo:block>...</fo:block>
    </fo:root>
  </xsl:template>
</xsl:stylesheet>

How can I use templates to match and style the generated fo elements? For example, if I want to give my fo:table-cells red backgrounds, I'd like to be able to do

<xsl:template match="fo:table-cell">
  <xsl:attribute name="background-color">red</xsl:attribute>
</xsl:template>

I found this and then tried something along the lines of

  <xsl:template match="/>
    <xsl:variable name="foRoot">
      <fo:root>
        <fo:block>...</fo:block>
      </fo:root>
    </xsl:variable>
    <xsl:apply-templates select="exslt:node-set($foRoot)" />
  </xsl:template>

but this results in a stack overflow due to endless recursion. When I try to avoid this, for example by doing

<xsl:apply-templates select="exslt:node-set($foRoot)/*" />

I get an empty document. When trying to fix that by adding

<xsl:copy-of select="$foRoot" />

right after, I don't get any errors but the table-cells still have a default white background.


Solution

  • If you really use an XSLT 2 processor then first of all you don't need exsl:node-set.

    As for your template

    <xsl:template match="fo:table-cell">
      <xsl:attribute name="background-color">red</xsl:attribute>
    </xsl:template>
    

    that would match a FO table-cell but transform it into an attribute. So you rather want

    <xsl:template match="fo:table-cell">
      <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <xsl:attribute name="background-color">red</xsl:attribute>
        <xsl:apply-templates/>
      </xsl:copy>
    </xsl:template>
    

    as that adds the attribute to a shallow copy of the element and then keeps processing alive for child elements with apply-templates.

    Of course you will also need to add the identity transformation template

    <xsl:template match="@* | node()">
      <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
      </xsl:copy>
    </xsl:template>
    

    to make sure the elements you don't want to change are copied through. It might be necessary to use modes to separate processing steps if the other templates you have interfere with the identity transformation.