package org.displaytag.export;
import org.displaytag.model.TableModel;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FopFactory;
import org.apache.fop.fo.ValidationException;
import org.apache.xmlgraphics.util.MimeConstants;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.Log;
import javax.servlet.jsp.JspException;
import javax.xml.transform.*;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
/**
* Exports the data to a totaled xml format, and then transforms that data using XSL-FO to a pdf.
* The stylesheet can be fed in as a string from the property export.pdf.fo.stylesheetbody, or you can use a default
* stylesheet named by the property export.pdf.fo.stylesheet.
* When you are developing a stylesheet, this class will
* output the raw FO if you set your log level to debug, which is very handy if you are getting errors or unexpected
* pdf output. See asFo_us.xsl for a sample XSL-FO stylesheet.
*
* The basic structure of the intermediate XML is
* <table>
* <header>
* <header-cell>AntColumn</hearder-cell>
* </header>
* <data>
* <subgroup grouped-by="0">
* <row>
* <cell grouped="true">Ant</cell>
* </row>
* <subtotal> <subtotal-cell></subtotal-cell>
* </subgroup>
* </data>
* </table>
*
* @author rapruitt
* Date: Aug 26, 2009
* Time: 1:55:29 PM
* @see FopExportView#SPECIFIC_STYLESHEET the property that contains the text of a stylesheet
* @see FopExportView#DEFAULT_STYLESHEET the defualt stylesheet location
* @see XslTransformerTest#XML for a sample of the XML output
* @see XmlTotalsWriter
*
*/
public class FopExportView implements BinaryExportView {
private static Log log = LogFactory.getLog(FopExportView.class);
/**
* Default stylesheet.
*/
public static final String DEFAULT_STYLESHEET = "export.pdf.fo.stylesheet"; //$NON-NLS-1$
/**
* A stylesheet as a string on a property.
*/
public static final String SPECIFIC_STYLESHEET = "export.pdf.fo.stylesheetbody"; //$NON-NLS-1$
/**
* TableModel to render.
*/
protected TableModel model;
/**
* @see org.displaytag.export.ExportView#setParameters(TableModel, boolean, boolean, boolean)
*/
public void setParameters(TableModel tableModel, boolean exportFullList, boolean includeHeader,
boolean decorateValues)
{
this.model = tableModel;
}
/**
* @see org.displaytag.export.BaseExportView#getMimeType()
* @return "application/pdf"
*/
public String getMimeType()
{
return "application/pdf"; //$NON-NLS-1$
}
/**
* Load the stylesheet.
* @return the stylesheet
* @throws IOException if we cannot locate it
*/
public InputStream getStyleSheet() throws IOException
{
InputStream styleSheetStream;
String styleSheetString = model.getProperties().getProperty(SPECIFIC_STYLESHEET);
if (StringUtils.isNotEmpty(styleSheetString))
{
styleSheetStream = new ByteArrayInputStream(styleSheetString.getBytes());
}
else
{
String styleSheetPath = model.getProperties().getProperty(DEFAULT_STYLESHEET);
styleSheetStream = this.getClass().getResourceAsStream(styleSheetPath);
if (styleSheetStream == null)
{
throw new IOException("Cannot locate stylesheet " + styleSheetPath); //$NON-NLS-1$
}
}
return styleSheetStream;
}
/**
* Don't forget to enable debug if you want to see the raw FO.
* @param out output writer
* @throws IOException
* @throws JspException
*/
public void doExport(OutputStream out) throws IOException, JspException
{
String xmlResults = getXml();
FopFactory fopFactory = FopFactory.newInstance();
Source xslt = new StreamSource(getStyleSheet());
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer;
try
{
transformer = factory.newTransformer(xslt);
}
catch (TransformerConfigurationException e)
{
throw new JspException("Cannot configure pdf export "+e.getMessage(),e); //$NON-NLS-1$
}
boolean outputForDebug = log.isDebugEnabled();
if (outputForDebug)
{
logXsl(xmlResults, transformer, null);
}
Fop fop;
try
{
fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);
}
catch (FOPException e)
{
throw new JspException("Cannot configure pdf export "+e.getMessage(),e); //$NON-NLS-1$
}
StreamSource src = new StreamSource( new StringReader(xmlResults));
Result res;
try
{
res = new SAXResult(fop.getDefaultHandler());
}
catch (FOPException e)
{
throw new JspException("error setting up transform ",e); //$NON-NLS-1$
}
try
{
transformer.transform(src, res);
}
catch (TransformerException e)
{
if (e.getCause() instanceof ValidationException)
{
// recreate the errant fo
ValidationException ve = (ValidationException) e.getCause();
logXsl(xmlResults, transformer, ve);
}
else
{
throw new JspException("error creating pdf output",e); //$NON-NLS-1$
}
}
}
protected String getXml() throws JspException {
XmlTotalsWriter totals = new XmlTotalsWriter(model);
totals.writeTable(model, "-1");
return totals.getXml();
}
/**
* log it.
* @param xmlResults raw
* @param transformer the transformer
* @param e the optional exception
* @throws JspException wrapping an existing error
*/
protected void logXsl(String xmlResults,Transformer transformer, Exception e) throws JspException
{
StreamResult debugRes = new StreamResult(new StringWriter());
StreamSource src = new StreamSource( new StringReader(xmlResults));
try
{
transformer.transform(src, debugRes);
if (e != null)
{
log.error("xslt-fo error " + e.getMessage(), e); //$NON-NLS-1$
log.error("xslt-fo result of " + debugRes.getWriter()); //$NON-NLS-1$
throw new JspException("Stylesheet produced invalid xsl-fo result", e); //$NON-NLS-1$
}
else
{
log.info("xslt-fo result of " + debugRes.getWriter()); //$NON-NLS-1$
}
}
catch (TransformerException ee)
{
throw new JspException("error creating pdf output " +ee.getMessage(), ee); //$NON-NLS-1$
}
}
/**
* If you are authoring a stylesheet locally, this is highly recommended as a way to test your stylesheet agaisnt
* dummy data.
* @see XslTransformerTest#XML as a sample
* @param xmlSrc xml as string
* @param styleSheetPath the path to the stylesheet
* @throws Exception if trouble
*/
public static void transform(String xmlSrc, String styleSheetPath, File f) throws Exception
{ //"org.displaytag.export."
// String xmlFile = " C:\\dev\\displaytag\\trunk\\displaytag\\src\\main\\resources\\org\\displaytag\\export\\xmlExportValue.xml";
FopFactory fopFactory = FopFactory.newInstance();
InputStream styleSheetStream = FopExportView.class.getResourceAsStream(styleSheetPath);
Source xslt = new StreamSource(styleSheetStream );
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer;
try
{
transformer = factory.newTransformer(xslt);
}
catch (TransformerConfigurationException e)
{
throw new JspException("Cannot configure pdf export "+e.getMessage(),e); //$NON-NLS-1$
}
Fop fop;
try
{
FileOutputStream fw = new FileOutputStream(f);
fop = fopFactory.newFop(MimeConstants.MIME_PDF, fw);
}
catch (FOPException e)
{
throw new JspException("Cannot configure pdf export "+e.getMessage(),e); //$NON-NLS-1$
}
Source src = new StreamSource( new StringReader(xmlSrc));
Result res;
try
{
res = new SAXResult(fop.getDefaultHandler());
}
catch (FOPException e)
{
throw new JspException("error setting up transform ",e); //$NON-NLS-1$
}
try
{
transformer.transform(src, res);
}
catch (TransformerException e)
{
throw new JspException("error creating pdf output " + e.getMessage(),e); //$NON-NLS-1$
}
}
}