Sunday, September 19, 2010

Using Spring XsltView with Apache FOP

Originally posted on SAT, OCTOBER 3, 2009 AT 15:44

In the springsource cookbook, there is a recipe for using XLST-FO with and XsltView. However the recipe uses the now deprecated AbstractXsltView. Here is an example using an subclass of XsltView and Apache FOP. If you are using SpringSource Tool Suite, be sure to add Apache FOP as a dependency in your Maven pom. Otherwise, download the latest version directly from the Apache FOP project site and include the batik and and avalon framework dependencies.



XsltFoView.java


package com.queueq.dandy;

import java.io.StringReader;
import java.util.Map;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.MimeConstants;
import org.springframework.web.servlet.view.xslt.XsltView;

public class XsltFoView extends XsltView {

@SuppressWarnings("unchecked")
@Override
protected void renderMergedOutputModel(Map model, HttpServletRequest request,
HttpServletResponse response) throws Exception{

FopFactory fopFactory = FopFactory.newInstance();
FOUserAgent foUserAgent = fopFactory.newFOUserAgent();

ServletOutputStream out = null;

try {

out = response.getOutputStream();
response.setContentType(MimeConstants.MIME_PDF);
// Construct fop with desired output format
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);

// Setup XSLT
TransformerFactory factory = TransformerFactory.newInstance();

// Create new transformer and get the stylesheet using
// getStylesheetSource() from superclass (XsltView)
// there must be an XSL file in WEB-INF/xsl with file name
// equal to the name of the view
Transformer transformer = factory.newTransformer(super.getStylesheetSource());


transformer.setParameter("versionParam", "2.0");

// Setup input for XSLT transformation
StringReader xmlReader = new StringReader((String)model.get("xml"));
Source src = new StreamSource(xmlReader);

// Resulting SAX events (the generated FO) must be piped through to FOP
Result res = new SAXResult(fop.getDefaultHandler());

// Start XSLT transformation and FOP processing
transformer.transform(src, res);
} finally {
out.close();
}

}
}


To use the view you need to add a view handler to your mvc-config.xml


from mvc-config.xml


<bean id="xsltViewResolver" class="org.springframework.web.servlet.view.xslt.XsltViewResolver">
     <property name="viewClass" value="com.queueq.dandy.XsltFoView"/>
     <property name="order" value="2"/>
     <property name="prefix" value="/WEB-INF/xsl/"/>
     <property name="suffix" value=".xsl"/>
    </bean>


This configuration will now look in WEB-INF/xsl for xsl files with names that match a view name


xslfo.xsl


<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:fo="http://www.w3.org/1999/XSL/Format">
    <xsl:output method="xml" indent="yes"/>
      <xsl:template match="/">
          <fo:root>
              <fo:layout-master-set>
                <fo:simple-page-master master-name="A4-portrait"
              page-height="29.7cm" page-width="21.0cm" margin="2cm">
                  <fo:region-body/>
                </fo:simple-page-master>
              </fo:layout-master-set>
              <fo:page-sequence master-reference="A4-portrait">
                  <fo:flow flow-name="xsl-region-body">
                    <fo:block>
            Hello, <xsl:value-of select="name"/>!
                    </fo:block>
                  </fo:flow>
              </fo:page-sequence>
          </fo:root>
      </xsl:template>
</xsl:stylesheet>


Finally, to use the new XstlFoView simply add a RequestMapping to your controller. The model must contain an attribute called "xml" that contains the XML that is transformed in the stylesheet. In this case, the xml is simply <name>...</name> which is matched and substituted in the stylesheet above.




WelcomeController.java

package com.queueq.dandy;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class WelcomeController {


@RequestMapping("/transform")
public ModelAndView transform() {
                // xslfo is the name of the stylesheet created above
ModelAndView m = new ModelAndView("xslfo");


String s = "<name>Kristian</name>";
m.getModelMap().addAttribute("xml",s);


return m;
}
}

No comments:

Post a Comment