Search code examples
spring-mvcxsltxsl-fosaxonapache-fop

Saxon seems to be ignoring certain XSL calls in Spring MVC application, causes FOP exception


I'm getting an error while trying to transform my xml file using a formatting object in one of my service classes in my Spring MVC web application:

net.sf.saxon.trans.XPathException: org.apache.fop.fo.ValidationException: "fo:root" is missing child elements. Required content model: (layout-master-set, declarations?, bookmark-tree?, (page-sequence|fox:external-document)+) (See position 30:-1)

The Java code that is throwing the exception is used to convert an xml file to pdf following the rules outlined in my formatting object. Java code in question:

FopFactory f = FopFactory.newInstance();
//Load the fop configuration file (for installing custom fonts)
DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder();
Configuration cfg = cfgBuilder.buildFromFile(new File("src/main/webapp/resources/fop-config.xml"));
f.setUserConfig(cfg);

FOUserAgent agent = f.newFOUserAgent();         

OutputStream os = new FileOutputStream(new File(resultFileName));
os = new BufferedOutputStream(os);

Fop fop = f.newFop(MimeConstants.MIME_PDF, agent, os);

//Load the formatting object
TransformerFactory fac = new net.sf.saxon.TransformerFactoryImpl();
Transformer t = fac.newTransformer(new StreamSource(new File("src/main/webapp/resources/fo.xsl")));

File file = new File(fileName);
Source src = new StreamSource(file);
Result r = new SAXResult(fop.getDefaultHandler());

//Perform the transformation and close resources
t.transform(src, r);
os.close();

And here is the part of my formatting object that FOP doesn't seem to like:

<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
    <fo:layout-master-set>
        <fo:simple-page-master master-name="letter-first"
            page-height="11in" page-width="8.5in" margin-top="0.25in">

            <fo:region-body margin-top="1.0in" margin-bottom="0.75in"
                margin-left="0.60in" margin-right="0.60in" />
            <fo:region-before extent="0.7in" />
            <fo:region-after extent="0.4in" />
        </fo:simple-page-master>

        <fo:simple-page-master master-name="letter-rest"
            page-height="11in" page-width="8.5in" margin-top="0.25in">

            <fo:region-body margin-top="0.5in" margin-bottom="0.75in"
                margin-left="0.60in" margin-right="0.60in" />
            <fo:region-after extent="0.4in" />
        </fo:simple-page-master>
        <fo:page-sequence-master master-name="letter">
            <fo:single-page-master-reference
                master-reference="letter-first" />
            <fo:repeatable-page-master-reference
                master-reference="letter-rest" />
        </fo:page-sequence-master>
    </fo:layout-master-set>

    <!-- Drop in the body -->
    <xsl:apply-templates />
</fo:root>

I know that the exception is trying to tell me that I'm missing my fo:page-sequence element. However, this element is present in the template that I'm trying to apply in the root element. Immediately below the code above, I declare my template:

<xsl:template match="template">
    <fo:page-sequence master-reference="letter"

Which contains the page-sequence element that FOP is complaining about.

I've tried this exact code on a regular Java Application, and it runs just fine with no exceptions. However, when running it through a service class in my Spring MVC web application, it all of a sudden can't apply xsl templates, and I haven't slightest clue why.

It looks like Saxon is not correctly interpreting the call in my web application, but it is able to do so in an offline Java application with exactly the same code.

Does anyone have any ideas? Also, let me know if you need more information and I'll be happy to provide it.

Edit: Saxon and FOP dependencies:

    <dependency>
        <groupId>org.apache.xmlgraphics</groupId>
        <artifactId>fop</artifactId>
        <version>1.1</version>
        <exclusions>
            <exclusion>
                <groupId>org.apache.avalon.framework</groupId>
                <artifactId>avalon-framework-api</artifactId>
            </exclusion>
            <exclusion>
                <groupId>org.apache.avalon.framework</groupId>
                <artifactId>avalon-framework-impl</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>net.sourceforge.saxon</groupId>
        <artifactId>saxon</artifactId>
        <version>9.1.0.8</version>
    </dependency>

Solution

  • I never updated this, but I figured out that the problem was that the input file actually didn't have the correct namespaces declared, as Martin suggested in the comments. I thought I was using a particular input file when it turns out I was using a different one entirely.