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 FopFactory instance. The FopFactory instance 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 OutputStream that 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 Transformer to 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 the FopPrint class.
  • Step 5: Set up the input and output for the XSLT transformation. The Source object is set up to load the xml Document which was passed in as a parameter. The Result is 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.

contact us
  • Name
  • Email
  • Message

Note: This information will only be used to reply to your feedback. We respect your privacy and will never abuse your email address or other personal information.

bottom corner