package org.docbag.creator.fop; import java.io.File; import java.util.Date; import java.util.Map; import javax.xml.transform.TransformerFactory; import javax.xml.transform.sax.SAXResult; import javax.xml.transform.stream.StreamSource; import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.FopFactory; import org.apache.fop.pdf.PDFEncryptionParams; import org.docbag.*; import org.docbag.stream.MemoryInputStream; import org.docbag.stream.MemoryOutputStream; import org.docbag.template.DocumentTemplateStream; import org.docbag.template.repo.DocumentTemplateRepository; import org.docbag.template.transformer.TemplateTransformer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The default implementation of a {@link DocumentCreator} interface. It uses Apache FOP as a * document generation engine. * <p/> * <p>The usual document creation flow:</p> * <pre> * 1. createDocument() method is invoked * 1.1. If this is the {@link FOPDocumentCreator#createDocument(String)} or {@link FOPDocumentCreator#createDocument(String, Context)} * version of createDocument() try to find {@link DocumentTemplateStream} using {@link DocumentTemplateRepository} * (templateRepository attribute). * 2. Transform {@link DocumentTemplateStream} * 2.1 If {@link TemplateTransformer} is set then transform DocumentTemplate into another DocumentTemplate. * The default behaviour in this step is to transform XHTML to XSL-FO and evaluate all the * expressions embedded in the template. * 2.2 If the {@link TemplateTransformer} is not set then this step is skipped and the * provided DocumentTemplate is passed directly to the Apache FOP. It means it has to be already * in the XSL-FO format. * 3. Create a {@link org.docbag.Document} * </pre> * * @author Jakub Torbicki */ public class FOPDocumentCreator implements DocumentCreator<DocumentStream, DocumentTemplateStream> { private static final Logger log = LoggerFactory.getLogger(FOPDocumentCreator.class); private final String mimeType; private final FopFactory fopFactory = FopFactory.newInstance(); private final TransformerFactory tFactory = TransformerFactory.newInstance(); private final TemplateTransformer<DocumentTemplateStream> templateTransformer; private final DocumentTemplateRepository<DocumentTemplateStream> templateRepository; private final FOUserAgent userAgent = fopFactory.newFOUserAgent(); public FOPDocumentCreator(String mimeType, TemplateTransformer<DocumentTemplateStream> templateTransformer, DocumentTemplateRepository<DocumentTemplateStream> templateRepository) { this(mimeType, templateTransformer, templateRepository, null); } public FOPDocumentCreator(String mimeType, TemplateTransformer<DocumentTemplateStream> templateTransformer, DocumentTemplateRepository<DocumentTemplateStream> templateRepository, DocBagConfig config) { this.mimeType = mimeType; this.templateTransformer = templateTransformer; this.templateRepository = templateRepository; if (config != null) { configure(config); } } public DocumentStream createDocument(DocumentTemplateStream templateStream) { return createDocument(templateStream, new DefaultContext()); } public DocumentStream createDocument(DocumentTemplateStream templateStream, Context context) { if (templateStream == null) { throw new NullPointerException("DocumentTemplate can't be null!"); } MemoryOutputStream pdf = new MemoryOutputStream(); try { // Prepare DocumentTemplate DocumentTemplateStream transformed = transformTemplate(templateStream, context); // Generate PDF tFactory.newTransformer().transform(new StreamSource(transformed.getStream()), new SAXResult(fopFactory.newFop(mimeType, userAgent, pdf).getDefaultHandler())); } catch (Exception e) { log.error("Error creating document: " + e.getLocalizedMessage(), e); throw new DocumentCreatorException("Error creating document: " + e.getLocalizedMessage(), e); } return new DocumentStream(new Date(), new MemoryInputStream(pdf)); } /** * To be able to use this version of createDocument the templateRepository attribute needs to be set */ public DocumentStream createDocument(String templateName) { return createDocument(templateName, new DefaultContext()); } /** * To be able to use this version of createDocument the templateRepository attribute needs to be set */ public DocumentStream createDocument(String templateName, Context context) { if (templateRepository == null) { throw new NullPointerException("Default template repository not set! If you want to use 'templateName' version of" + " createDocument() then you need to set valid instance of DocumentTemplateRepository"); } return createDocument(templateRepository.findTemplate(templateName), context); } private DocumentTemplateStream transformTemplate(DocumentTemplateStream templateStream, Context context) { if (templateTransformer != null) { return templateTransformer.transform(templateStream, context); } return templateStream; } private void configure(DocBagConfig config) { if (config.getFopConfig() != null) { DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder(); try { fopFactory.setUserConfig(cfgBuilder.buildFromFile(new File(config.getFopConfig()))); } catch (Exception e) { log.error("Error configuring Apache FOP!", e.getLocalizedMessage(), e); } } if (config.getRendererOptions() != null) { for (Map.Entry<String, Object> entry : config.getRendererOptions().entrySet()) { userAgent.getRendererOptions().put(entry.getKey(), entry.getValue()); } } } public String toString() { return "FOPDocumentCreator{" + "mimeType='" + mimeType + "\'}"; } }