I have recently been trying to do some printing from a Java application. The Java printing API appears to be extremely powerful but it has, in my opinion, two significant problems. First, it doesn't seem to have a default set of bits to plug together and produce printout. Instead you get some useful components and a set of interfaces. The second problem is that you effectively paint every component yourself, something that I really didn't fancy doing for the rather complex layouts that I had to work with.

I began to think about how easy the output would be in something like HTML which led to me deciding to unearth my memories of XSL-FO. By digging around it seemed like it was possible to use Fop (a Java XSL-FO toolkit) to render data straight to the printer utilising some of the support classes from the Java print API. Sadly I struggled to find any documentation of how to do it so I pieced together the scraps I could find and the result is shown below.

The code below borrows heavily from the example on the FOP site but I have made the changes that were needed to use a print selection dialog and to allow the xml source to be passed as a parameter.


package uk.co.artran.print;

import java.awt.print.PrinterJob;

import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.fop.apps.*;
import org.apache.fop.render.print.PrintRenderer;
import org.w3c.dom.Document;

public class FopPrint {
    private FopFactory fopFactory;

    public FopPrint() {
        // Step 1: Construct a FopFactory
        fopFactory = FopFactory.newInstance();
    }
    
    public void printXML(Document xml){
        // Step 2: Set up the PrinterJob using its dialog
        PrinterJob printerJob = PrinterJob.getPrinterJob();
        
        try {
            if (printerJob.printDialog()) {
                // Create a PrintRenderer from our PrinterJob and use it to override the default renderer
                PrintRenderer printRenderer = new PrintRenderer(printerJob);
                FOUserAgent userAgent = fopFactory.newFOUserAgent();
                userAgent.setRendererOverride(printRenderer);
                
                // Step 3: Construct fop with desired output format
                Fop fop = fopFactory.newFop(MimeConstants.MIME_POSTSCRIPT, userAgent);
    
                // Step 4: Setup JAXP using transformer
                TransformerFactory factory = TransformerFactory.newInstance();
                Source xslt = new StreamSource(this.getClass().getResourceAsStream("test.xsl"));
                Transformer transformer = factory.newTransformer(xslt);
    
                // Step 5: Setup input and output for XSLT transformation
                // Setup input document
                Source src = new DOMSource(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);
            }
        } catch(Exception ex) {
            ex.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 PrinterJob that the generated document will be written to. The PrinterJob has a method called printDialog() that will create a print dialog to allow the user to specify the printer, paper size, number of copies and so on, if possible the dialog will use the native dialog for the OS. The dialog is modal and returns true if the user clicks OK. After returning from the dialog the PrinterJob will be configured with the users selections and can be passed to the PrintRenderer which is then used to configure the FOUserAgent.
  • 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 postscript which should work well for most printers. The second parameter is the FOUserAgent setup up in step 2.
  • 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.

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