views:

440

answers:

2

Hello,

I've been struggling with some weird behavior of fop 0.95 (don't know if I'm doing something wrong, or if there is a work around).

I have an auto generated XML as follows:

<projectteam>
 <projectname>Report Generation</projectname>
 <RoleTypes>
         <dev/>
         <qa/>
         <doc/>
 </RoleTypes>
 <member>
   <name>John</name>
   <dev>200</dev>
 </member>
 <member>
   <name>Max</name>
   <dev>60</dev>
 </member>
 <member>
   <name>Henry</name>
   <qa>80</qa>
 </member>
 <member>
   <name>Peter</name>
   <qa>40</qa>
 </member>
</projectteam>

(Note: This is a mocked up example, but I have a very similar need where I need to generate reports at the end of a job with sevaral colums akin to roleTypes)

My goal is to display the above data in pdf as follows:

Name   | dev | qa  | doc |
--------------------------
John   | 100 |     |     |
Max    |  60 |     |     |
Henry  |     |  80 |     |
Peter  |     |  40 |     |

I used xsl:for-each to loop over RoleTypes/* elements to define table columns, and then dynamically constructed XPath expression (using dyn:evaluate of exslt) to obtain data for the cells that correspond to the roles (dev, qa, and doc).

My xsl stylesheet works as expected if I run it through a pre-processor (xsltproc) to generate .fo, and then use fop to convert this .fo into a pdf. But, when I use fop directly (i.e. single step: fop -xml blah.xml -xsl blah.xsl -pdf out.pdf), I'm getting weird results - only the first column's data (i.e. the first child element of 'RoleTypes', in this example - 'dev') and rest of the colums are blank. I've also tried producing .fo first with fop itself (-foout option), and then using fop to generate the pdf, but got the same result, i.e. data gets displayed only in the column that corresponds to the first child element of RoleTypes element. Is this a bug with fop (as it seems to recognize dyn:evaluate, but doesn't do a complete job)?

I would really like to use the single step fop so that I wouldn't need to deploy additional tools on the client box (like xsltproc etc).

Here's the critical segment of the stylesheet I've been using:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:fo="http://www.w3.org/1999/XSL/Format"
  xmlns:dyn="http://exslt.org/dynamic">

<xsl:template match="projectteam">
 ...
 <fo:table table-layout="fixed" width="100%">
  <fo:table-column column-width="3cm"/>
  <xsl:for-each select="RoleTypes/*">
   <fo:table-column column-width="1cm"/>
  </xsl:for-each>

  <fo:table-body>
  <xsl:for-each select="member">
   <xsl:variable name="Member" select="."/>
   <fo:table-row>
    <fo:table-cell>
     <fo:block> <xsl:value-of select="name"/></fo:block>
    </fo:table-cell>

    <xsl:for-each select="../RoleTypes/*">
     <xsl:variable name="roleName" select="concat('$Member/', name(.))"/>
     <fo:table-cell><fo:block>

     <!-- This is where the problem is with fop; although the same statement works fine with xsltproc?? -->

      <xsl:value-of select="dyn:evaluate($roleName)"/>
     </fo:block></fo:table-cell>
    </xsl:for-each>
   </fo:table-row>
  </xsl:for-each>
  </fo:table-body>
 </fo:table>
</xsl:template>
</xsl:stylesheet>

Thanks

A: 

As was noted at http://markmail.org/message/np4t6fe4nsmr4vag this is not directly a FOP problem but one with the default XSLT processor that is active in your Java installation. The XSLT processor may need to be replaced explicitely as described here: http://xml.apache.org/xalan-j/faq.html#faq-N100EF

Of course, you can also just continue to use xsltproc for generating the XSL-FO and then feed FOP the FO file.

Jeremias Märki
+1  A: 

This doesn't answer the general problem of using dyn:evaluate in FOP, but for this particular schema and problem, dynamic evaluation is not needed. You can use the name() function to match the node names. If you replace the inner loop of your example with:

                    <xsl:for-each select="../RoleTypes/*">
                        <xsl:variable name="roleName" select="name(.)"/>
                        <fo:table-cell><fo:block>
                            <xsl:value-of select="$Member/*[name()=$roleName]"/>
                        </fo:block></fo:table-cell>
                    </xsl:for-each>

You will get the output you wanted.

Justin W
Thanks for suggesting this alternative! As far as the main problem is concerned, it was caused by XALAN xslt engine. Once I replaced it with Saxon, the transformation (and FOP) worked fine. I've created a bug report for XALAN for this issue, and also changed my code so that I don't need to use dyn:evaluate anymore.
Krishna