The approach outlined in my other article worked well enough but it was difficult to preview the print before sending it to paper. This article builds on that one to produce a complete printing system for use in Java applications.
What we need to do is to render the page into PDF and then ask the default PDF viewer to display it for us to check and print if required. The approach given here has been tested on both Windows and Mac and should be easily made to work with Linux too. I don't have any Linux boxes with desktop environments so I can't test but if you make this work on Linux I'd be grateful of the code for inclusion.
The differences between this code and the other example are fairly trivial. Instead of a
PrinterJob we send the output to a temporary file which we then pass to the
os for it to deal with.
package uk.co.artran.print;
import java.io.*;
import javax.xml.transform.*;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.fop.apps.*;
import org.dom4j.Document;
import org.dom4j.io.DocumentSource;
public class FopPrint {
private FopFactory fopFactory;
public FopPrint() {
// Step 1: Construct a FopFactory
// (reuse if you plan to render multiple documents!)
fopFactory = FopFactory.newInstance();
}
public void printXML(Document xml, PrintType printType){
// Step 2: Set up output stream.
OutputStream os = null;
try {
// Step 3: Construct fop with desired output format.
FOUserAgent userAgent = fopFactory.newFOUserAgent();
File tmpFile = File.createTempFile("biodose-print", ".pdf");
tmpFile.deleteOnExit();
os = new BufferedOutputStream(new FileOutputStream(tmpFile));
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, userAgent, os);
// Step 4: Setup JAXP using transformer
TransformerFactory factory = TransformerFactory.newInstance();
Source xslt = new StreamSource(printType.getXslAsStream());
Transformer transformer = factory.newTransformer(xslt);
// Step 5: Setup input and output for XSLT transformation
// Setup input stream
Source src = new DocumentSource(xml);
// Resulting SAX events (the generated FO) must be piped through to FOP
Result res = new SAXResult(fop.getDefaultHandler());
// Step 6: Start XSLT transformation and FOP processing
transformer.transform(src, res);
os.flush();
os.close();
// Step 7: Feed the generated PDF to the operating system for it to handle
String osName = System.getProperty("os.name");
if (osName.startsWith("Windows")) {
@SuppressWarnings("unused")
Process printProc = Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + tmpFile.getAbsolutePath());
} else if (osName.startsWith("Mac OS")) {
@SuppressWarnings("unused")
Process printProc = Runtime.getRuntime().exec("open " + tmpFile.getAbsolutePath());
}
} catch(Exception ex) {
ex.printStackTrace();
} finally {
try {
if (os != null) os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
-
Step 1: Create a new
FopFactoryinstance. TheFopFactoryinstance holds references to configuration information and cached data. It's important to reuse this instance if you plan to render multiple documents during a JVM's lifetime. -
Step 2: Set up the
OutputStreamthat the generated document will be written to. -
Step 3: Create a new Fop instance through one of the factory
methods on the FopFactory. You tell the FopFactory what your desired output format
is. This is done by using the MIME type of the desired output format (ex. "application/pdf").
The example uses one of the
MimeConstants.*constants to specify pdf. -
Step 4 Setup a JAXP
Transformerto perform the transform of the input xml to the intermediate xsl-fo. At this point we only create the transformer and pass in the xsl file. In this case the xsl is a resource stored in the same package as theFopPrintclass. -
Step 5: Set up the input and output for the XSLT
transformation. The Source object is set up to load the xml
Documentwhich was passed in as a parameter. TheResultis set up so the output of the XSLT transformation is sent to FOP. The FO file is sent to FOP in the form of SAX events which is the most efficient way. Saving intermediate results to a file or a memory buffer affects performance negatively. -
Step 6: Finally, we start the XSLT transformation by starting
the JAXP Transformer. As soon as the JAXP Transformer starts to send its output
to FOP, FOP itself starts its processing in the background. When the
transform()method returns FOP will also have finished producing its output. - Step 7: The FOP output is now fed to the operating system for it to handle. The method for passing a file to the os is platform specific and I've given examples for both Mac and Windows. The result of this action will be that the default PDF viewer will be shown with our rendered page in it.
My references for the article were the Java API documentation, the FOP API documentation and the FOP embedding documentation.


