Development Techniques


Now we’ll look at techniques that will help you use FOP in your applications. We’ll start by looking at how to embed FOP in your program using the FOP Driver API, SAX, and DOM. FOP supplies an XML schema you can use to validate your XSL documents, and we’ll discuss how to do that as well. Then we’ll cover the various output formats FOP can produce and how to use fonts and graphics, and we’ll conclude by looking at FOP extensions, which are similar to Xalan extensions.

Embedding

Let’s begin with the simplest program that uses FOP from inside. The following program reads an XSL file from a file and creates a PDF file as its output. The two files are specified on the command line. (More sophisticated usages of FOP embedding will follow.)

  1: /*   2:  *    3:  * FOPMain.java   4:  *    5:  * Example from "Professional XML Development with Apache Tools"   6:  *   7:  */   8: package com.sauria.apachexml.ch3;   9:   10: import java.io.BufferedOutputStream;  11: import java.io.FileNotFoundException;  12: import java.io.FileOutputStream;  13: import java.io.IOException;  14: import java.io.OutputStream;  15:   16: import org.apache.avalon.framework.logger.ConsoleLogger;  17: import org.apache.avalon.framework.logger.Logger;  18: import org.apache.fop.apps.Driver;  19: import org.apache.fop.apps.FOPException;  20: import org.apache.fop.apps.FormattingResults;  21: import org.apache.fop.messaging.MessageHandler;  22: import org.xml.sax.InputSource;  23:   24: public class FOPMain {  25:     public static void main(String[] args) {  26:         String xslFile = args[0];  27:         String pdfFile = args[1];  28:           29:         OutputStream outFile = null;  30:           31:         try {  32:             outFile = new FileOutputStream(pdfFile);  33:             outFile = new BufferedOutputStream(outFile);  34:         } catch (FileNotFoundException fnfe) {  35:             fnfe.printStackTrace();  36:         }

For efficiency, you wrap the FileOutputStream in a BufferedOutputStream. Doing so is particularly important if you’re formatting large documents.

You construct the FOP Driver class and pass it an InputSource containing the XSL file and the output stream where the PDF is to be written:

 37:           38:         Driver fopDriver = new Driver(new InputSource(xslFile),  39:                                       outFile);

FOP uses the Apache Jakarta Avalon project’s logging framework to handle logging. If you want to see the log of messages as FOP is processing the input document, then you need to create the appropriate Logger subclass and then pass it to the FOP MessageHandler as well as the FOP Driver. Here you use a ConsoleLogger, but you can use any Logger the Avalon logging framework provides.

 40:         Logger logger =   41:             new ConsoleLogger(ConsoleLogger.LEVEL_WARN);  42:         MessageHandler.setScreenLogger(logger);  43:         fopDriver.setLogger(logger);

This line tells the Driver that you want to render the output as PDF:

 44:         fopDriver.setRenderer(Driver.RENDER_PDF);

You have to call the Driver’s run method to process the document:

 45:         try {  46:             fopDriver.run();  47:         } catch (IOException ioe) {  48:             ioe.printStackTrace();  49:         } catch (FOPException fe) {  50:             fe.printStackTrace();  51:         } 

After formatting is complete, you can get a FormattingResults object from the Driver and ask it for statistics about the processing. Here you ask for the number of pages that were formatted:

 52:           53:         FormattingResults results = fopDriver.getResults();  54:         System.out.println("Pages Formatted: "+  55:                            results.getPageCount());  56:     }  57: }

Using the Configuration Files and Options

FOP can read its configuration settings from a file. This is a version of the embedded FOP program that shows how to tell FOP to use a configuration file:

  1: /*   2:  *    3:  * FOPConfigMain.java   4:  *    5:  * Example from "Professional XML Development with Apache Tools"   6:  *   7:  */   8: package com.sauria.apachexml.ch3;   9:   10: import java.io.BufferedOutputStream;  11: import java.io.File;  12: import java.io.FileNotFoundException;  13: import java.io.FileOutputStream;  14: import java.io.IOException;  15: import java.io.OutputStream;  16:   17: import org.apache.avalon.framework.logger.ConsoleLogger;  18: import org.apache.avalon.framework.logger.Logger;  19: import org.apache.fop.apps.Driver;  20: import org.apache.fop.apps.FOPException;  21: import org.apache.fop.apps.FormattingResults;  22: import org.apache.fop.apps.Options;  23: import org.apache.fop.messaging.MessageHandler;  24: import org.xml.sax.InputSource;  25:   26: public class FOPConfigMain {  27:   28:     public static void main(String[] args) {  29:         String configFile = args[0];  30:         String xslFile = args[1];  31:         String pdfFile = args[2];  32:           33:         OutputStream outFile = null;  34:           35:         try {  36:             outFile = new FileOutputStream(pdfFile);  37:             outFile = new BufferedOutputStream(outFile);  38:         } catch (FileNotFoundException fnfe) {  39:             fnfe.printStackTrace();  40:         }  41:           42:         File config = new File(configFile);  43:         Options options = null;  44:         

This part of the FOP API is a bit inconsistent. To read the configuration file, you need to create an instance of java.io.File. Everything else relies on either InputSource or OutputStream, so you have to remember that you need a File here, not some kind of InputStream.

You specify the configuration by instantiating an instance of org.apache.fop.apps.Options and passing to it the File containing the configuration file. After that, you’re done—FOP takes care of the rest. (The Options constructor creates a static configuration instance under the covers. Note that this can be a problem if you try to use FOP in a multithreaded environment. If you’re going to use FOP in a threaded environment, you need to synchronize access to the FOP Driver.)

 45:         try {  46:             options = new Options(config);  47:         } catch (FOPException fe) {  48:             fe.printStackTrace();  49:         }  50:           51:         Driver fopDriver = new Driver(new InputSource(xslFile),  52:                                       outFile);  53:         Logger logger =   54:             new ConsoleLogger(ConsoleLogger.LEVEL_WARN);  55:         MessageHandler.setScreenLogger(logger);  56:         fopDriver.setLogger(logger);  57:         fopDriver.setRenderer(Driver.RENDER_PDF);  58:         try {  59:             fopDriver.run();  60:         } catch (IOException ioe) {  61:             ioe.printStackTrace();  62:         } catch (FOPException fe) {  63:             fe.printStackTrace();  64:         }  65:           66:         FormattingResults results = fopDriver.getResults();  67:         System.out.println("Pages Formatted: "+  68:                            results.getPageCount());             69:     }  70: }

The FOP user configuration file allow you to control a number of different things. It consists of some key-value pairs and then some element related to fonts. In this section we’ll cover the key value pairs and defer the font-related information to the “Fonts” section. You can look at conf/userconfig.xml in the FOP distribution directory for an example. Key value pairs in the configuration file look like this:

  1: <entry>   2:  <key> key </key>   3:  <value> value for key </value>   4: </entry>

The following table lists the possible keys and the meanings of their values:

Key

Value

baseDir

A URL specifying the directory containing the input files. Defaults to the current working directory if embedded, or the directory containing the input files if command line.

fontBaseDir

A URL specifying a directory where fonts can be found. Defaults to the same value as baseDir.

hyphenation-dir

A URL specifying a directory where custom hyphenation files can be found. No default.

strokeSVGText

For the PDF renderer only: False to force text in an SVG document to be converted to text. Default is true, which means some text may be converted to SVG shapes.

SAX

Sometimes your application will generate XSL directly, without placing the formatting objects into a file in the filesystem. In these cases, you should process the XSL via a stream of SAX events or a DOM tree your application creates. In this section, we’ll look at how to feed the FOP Driver from a SAX event stream:

  1: /*   2:  *    3:  * FOPSAXMain.java   4:  *    5:  * Example from "Professional XML Development with Apache Tools"   6:  *   7:  */   8: package com.sauria.apachexml.ch3;   9:   10: import java.io.BufferedOutputStream;  11: import java.io.FileNotFoundException;  12: import java.io.FileOutputStream;  13: import java.io.IOException;  14: import java.io.OutputStream;  15:   16: import javax.xml.parsers.ParserConfigurationException;  17: import javax.xml.parsers.SAXParserFactory;  18:   19: import org.apache.avalon.framework.logger.ConsoleLogger;  20: import org.apache.avalon.framework.logger.Logger;  21: import org.apache.fop.apps.Driver;  22: import org.apache.fop.apps.FormattingResults;  23: import org.apache.fop.messaging.MessageHandler;  24: import org.xml.sax.SAXException;  25: import org.xml.sax.XMLReader;  26:   27: public class FOPSAXMain {  28:   29:     public static void main(String[] args) {  30:         String xslFile = args[0];  31:         String pdfFile = args[1];  32:         OutputStream outputFile = null;  33:   34:         try {  35:             outputFile = new FileOutputStream(pdfFile);  36:             outputFile = new BufferedOutputStream(outputFile);  37:         } catch (FileNotFoundException fnfe) {  38:             fnfe.printStackTrace();  39:         }  40:   41:         Driver fopDriver = new Driver();  42:         fopDriver.setOutputStream(outputFile);  43:   44:         Logger logger =   45:             new ConsoleLogger(ConsoleLogger.LEVEL_WARN);  46:         MessageHandler.setScreenLogger(logger);  47:         fopDriver.setLogger(logger);  48:         fopDriver.setRenderer(Driver.RENDER_PDF);

This time you set up the FOP Driver using the default constructor. This means no input or output file is associated with the Driver. For this example, you send the output to a PDF file, so the code to set up the buffered FileOutputStream is the same (lines 34-39). After you construct the FOP Driver, you have to explicitly set the Driver’s output stream (line 42).

You finish setting up the FOP Driver as in the previous examples, but you don’t call its run method. Instead you create a namespace-aware SAX parser (XMLReader, actually) using the JAXP SAXParserFactory. You ask the driver to give you its SAX ContentHandler. You register this ContentHandler with the SAX parser instance you created (line 56) so the FOP-provided callbacks are called when the parser processes the documents. You then format the document by asking the SAX parser to parse the XSL document (line 57):

 49:   50:         XMLReader r = null;  51:         SAXParserFactory spf = SAXParserFactory.newInstance();  52:         spf.setNamespaceAware(true);  53:   54:         try {  55:             r = spf.newSAXParser().getXMLReader();  56:             r.setContentHandler(fopDriver.getContentHandler());  57:             r.parse(xslFile);  58:         } catch (SAXException se) {  59:             se.printStackTrace();  60:         } catch (ParserConfigurationException pce) {  61:             pce.printStackTrace();  62:         } catch (IOException ioe) {  63:             ioe.printStackTrace();  64:         }  65:   66:         FormattingResults results = fopDriver.getResults();  67:         System.out.println("Pages Formatted: "+  68:                            results.getPageCount());   69:     }  70: }

DOM

Using a DOM tree as input to FOP requires another slightly different style of invoking FOP:

  1: /*   2:  *    3:  * FOPDOMMain.java   4:  *    5:  * Example from "Professional XML Development with Apache Tools"   6:  *   7:  */   8: package com.sauria.apachexml.ch3;   9:   10: import java.io.BufferedOutputStream;  11: import java.io.FileNotFoundException;  12: import java.io.FileOutputStream;  13: import java.io.IOException;  14: import java.io.OutputStream;  15:   16: import javax.xml.parsers.DocumentBuilder;  17: import javax.xml.parsers.DocumentBuilderFactory;  18: import javax.xml.parsers.ParserConfigurationException;  19:   20: import org.apache.avalon.framework.logger.ConsoleLogger;  21: import org.apache.avalon.framework.logger.Logger;  22: import org.apache.fop.apps.Driver;  23: import org.apache.fop.apps.FOPException;  24: import org.apache.fop.apps.FormattingResults;  25: import org.apache.fop.messaging.MessageHandler;  26: import org.w3c.dom.Document;  27: import org.xml.sax.SAXException;  28:   29: public class FOPDOMMain {  30:   31:     public static void main(String[] args) {  32:         String xslFile = args[0];  33:         String pdfFile = args[1];  34:           35:         DocumentBuilderFactory dbf =   36:             DocumentBuilderFactory.newInstance();  37:         dbf.setNamespaceAware(true);

You use the JAXP DocumentBuilderFactory to create a namespace-aware DOM parser. Then you call the DocumentBuilder to parse the XSL document and create a DOM tree:

 38:           39:         DocumentBuilder builder = null;  40:         try {  41:             builder = dbf.newDocumentBuilder();  42:         } catch (ParserConfigurationException pce) {  43:             pce.printStackTrace();  44:         }  45:           46:         Document doc = null;  47:         try {  48:             doc = builder.parse(xslFile);

Again, you have to set up the FOP Driver using the default constructor and then set the output stream to be a buffered FileOutputStream:

 49:         } catch (SAXException se) {  50:             se.printStackTrace();  51:         } catch (IOException ioe) {  52:             ioe.printStackTrace();  53:         }  54:           55:         OutputStream outputFile = null;  56:                                   57:         try {  58:             outputFile = new FileOutputStream(pdfFile);  59:             outputFile = new BufferedOutputStream(outputFile);  60:         } catch (FileNotFoundException fnfe) {  61:             fnfe.printStackTrace();  62:         }  63:           64:         Driver fopDriver = new Driver();  65:         fopDriver.setOutputStream(outputFile);

You finish setting up the FOP driver as before:

 66:           67:         Logger logger =   68:             new ConsoleLogger(ConsoleLogger.LEVEL_WARN);  69:         MessageHandler.setScreenLogger(logger);  70:         fopDriver.setLogger(logger);  71:         fopDriver.setRenderer(Driver.RENDER_PDF);

You process the XSL document by calling the render method on the Driver and passing the DOM tree as an argument:

 72:   73:         try {  74:             fopDriver.render(doc);  75:         } catch (FOPException fe) {  76:             fe.printStackTrace();  77:         }  78:           79:         FormattingResults results = fopDriver.getResults();  80:         System.out.println("Pages Formatted: "+  81:                            results.getPageCount());  82:     }  83: }

Note that for each of the three input styles for the Driver, you use a different method name to invoke FOP. This seems like an area for improvement in the FOP APIs.

XSLT

The last example with an embedded FOP driver involves the use of XSLT. Your application might use XSLT and a stylesheet to convert an XML file into an XSL formatting objects document. This example shows how to chain together a JAXP-compliant XSLT processor, such as Xalan, and FOP:

  1: /*   2:  *    3:  * XSLTFOPMain.java   4:  *    5:  * Example from "Professional XML Development with Apache Tools"   6:  *   7:  */   8: package com.sauria.apachexml.ch3;   9:   10: import java.io.BufferedOutputStream;  11: import java.io.FileNotFoundException;  12: import java.io.FileOutputStream;  13: import java.io.OutputStream;  14:   15: import javax.xml.transform.Result;  16: import javax.xml.transform.Source;  17: import javax.xml.transform.Transformer;  18: import javax.xml.transform.TransformerConfigurationException;  19: import javax.xml.transform.TransformerException;  20: import javax.xml.transform.TransformerFactory;  21: import javax.xml.transform.sax.SAXResult;  22: import javax.xml.transform.stream.StreamSource;  23:   24: import org.apache.avalon.framework.logger.ConsoleLogger;  25: import org.apache.avalon.framework.logger.Logger;  26: import org.apache.fop.apps.Driver;  27: import org.apache.fop.apps.FormattingResults;  28: import org.apache.fop.messaging.MessageHandler;  29:   30: public class XSLTFOPMain {  31:   32:     public static void main(String[] args) {  33:         String xmlFile = args[0];  34:         String xsltFile = args[1];  35:         String pdfFile = args[2];  36:           37:         OutputStream outFile = null;  38:           39:         try {  40:             outFile = new FileOutputStream(pdfFile);  41:             outFile = new BufferedOutputStream(outFile);  42:         } catch (FileNotFoundException fnfe) {  43:             fnfe.printStackTrace();  44:         }  45:                   46:         Driver fopDriver = new Driver();  47:                                         48:         Logger logger =   49:             new ConsoleLogger(ConsoleLogger.LEVEL_WARN);  50:         MessageHandler.setScreenLogger(logger);  51:         fopDriver.setLogger(logger);  52:         fopDriver.setRenderer(Driver.RENDER_PDF);  53:         fopDriver.setOutputStream(outFile);

The setup for FOP should be familiar by now. You use the default constructor for the Driver and set the output stream explicitly. All the real work in this example has to do with where the FOP engine gets its input.

The key piece is that the result of the XSLT processor is the input to the FOP engine. So, you create a SAXResult that’s set to the Driver’s ContentHandler:

 54:   55:         Source xmlSource = new StreamSource(xmlFile);  56:         Source xsltSource = new StreamSource(xsltFile);  57:         Result result =   58:             new SAXResult(fopDriver.getContentHandler());

A SAXResult produces a SAX event stream to be consumed via a ContentHandler, so this is a perfect fit for what you’re trying to do. If for some reason you wanted to do this using DOM trees, you could create a DOMResult and then call the FOP Driver with it as you did in the DOM example.

Because you’re using SAX, asking the first stage in the SAX pipeline to do the work causes the entire pipeline to do its job. Calling the Transformer’s transform method is all you need to get the job done:

 59:           60:         TransformerFactory xformFactory =   61:             TransformerFactory.newInstance();  62:         Transformer xformer = null;  63:           64:         try {  65:             xformer = xformFactory.newTransformer(xsltSource);  66:         } catch (TransformerConfigurationException tce) {  67:             tce.printStackTrace();  68:         }  69:           70:         try {  71:             xformer.transform(xmlSource, result);  72:         } catch (TransformerException te) {  73:             te.printStackTrace();  74:         }  75:   76:         FormattingResults results = fopDriver.getResults();  77:         System.out.println("Pages Formatted: "+  78:                            results.getPageCount());   79:     }  80: }

Validating XSL

Sometimes it can be difficult to find problems in your XSL documents. One way to locate problems is to validate your XSL document against FOP’s XML Schema grammar. This grammar is included in the FOP source distribution in the file src/foschema/fop.xsd. If you’ve never used XML Schema to validate an XML document, you’ll need to add some attributes to the <fo:root> element in your XSL document. You need a namespace declaration for the XML Schema Instance namespace: xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”. You also need to add an xsi:schemaLocation attribute to tell the schema validator where to find the schema file. This will look something like xsi:schemaLocation= “http://www.w3.org/1999/XSL/Format <URI path to>/fop.xsd”. Be sure the path to fop.xsd is a URI (relative URIs are okay). Once you’ve added these attributes to your XSL document, you can use an XML Schema–aware validating XML parser to check your document for syntactic correctness.

Command-Line Usage

When you have embedded FOP in your application, it may be difficult to track down problems in the output. You can also run FOP as a command-line tool from your operating system’s command line. When you run FOP this way, you can play with the XSL input file and see how doing so changes the output file. In some situations, this can help you figure out how to generate XSL that produces the formatted output you want.

The FOP distribution provides a Windows batch file and a UNIX shell script file you use to invoke FOP from the command line. Before you use them, you need to edit them to make sure they point at the directory where you installed FOP. Under Windows, set the variable LOCAL_FOP_HOME in the fop.bat file. The variable is called FOP_HOME in the UNIX fop.sh script.

Once you’ve edited the batch or shell script file, you can execute it from the command line (UNIX users may need to set execute permissions on the shell script first). The syntax of the command is:

fop [options] [input options] [output options]

The following table describes the FOP options:

Option

Description

-d

Debug mode.

-x

Dump configuration settings.

-q

Quiet mode.

-c cfg.xml

Use configuration file cfg.xml.

-l <lang>

Language for user information.

-s

If outputting an area tree (see -at), omit the tree below block areas.

-txt.encoding

If using -txt encoding, the encoding should be used for the output file; the encoding must be valid Java encoding.

-o <password>

Encrypt the PDF file with the option owner password.

-u <password>

Encrypt the PDF file with the option user password.

-noprint

Encrypt the PDF file without printing permission.

-nocopy

Encrypt the PDF file without copy content permission.

-noedit

Encrypt the PDF file without edit content permission.

-noannotations

Encrypt the PDF file without edit annotation permission.

The following table describes the FOP input options:

-fo <infile>

XSL formatting objects file.

-xml <infile>

XML input file, used in conjunction with -xsl.

-xsl <stylesheet>

XSLT stylesheet that converts the XML input file specified with -xml to XSL with formatting objects.

The following table describes the FOP output options:

-pdf <outfile>

Render output as PDF and save it in <outfile>.

-awt

Display output on the screen.

-mif <outfile>

Render output as MIF and save it in <outfile>.

-pcl <outfile>

Render output as PCL and save it in <outfile>.

-ps <outfile>

Render output as PostScript and save it in <outfile>.

-txt <outfile>

Render output as text (according to the text encoding) and save it in <outfile>.

-svg <outfile>

Render output as an SVG slides file and save it in <outfile>.

-at <outfile>

Render output as an area tree in XML and save it in <outfile>.

-print

Render output and send it to the printer.

The following table describes the sub-options for the -print option:

-Dstart=i

Start printing at page i.

-Dend=i

Stop printing at page i.

-Dcopies=i

Print i copies.

-Deven=true|false

Print only even pages if true, only odd pages if false.

Ant Task

A third way of using FOP is as an Ant task. Ant is a very popular XML-based build tool for Java. You may want to use FOP to generate PDF, or FrameMaker MIF documentation for the project you’re building. The FOP Ant task allows you to easily integrate FOP functionality into your Ant-based build. Ant can be extended using new tasks that reside in jars besides the Ant jar file. To do this, you must place a <taskdef> element in the build file before you try to use the FOP task. The code for the FOP <taskdef> looks like this:

  1: <property name="fop.dir"    2:           value="....path to your FOP jar files..."/>   3:   4: <taskdef name="fop"    5:         classname="org.apache.fop.tools.anttasks.Fop">   6:         <classpath>   7:            <pathelement location="${fop.dir}\build\fop.jar"/>   8:            <pathelement location="${fop.dir}\lib\avalon.jar"/>   9:            <pathelement location="${fop.dir}\lib\batik.jar"/>  10:         </classpath>  11: </taskdef>

Now that the FOP task is accessible to Ant, you can create Ant tasks that call FOP. You do this by including an <fop> element inside a target. The parameters to FOP are specified as attributes of the <fop> element. The FOP task is able to use Ant’s <fileset> element to operate on directories of files at once. The following table lists the attributes you can use and their meaning:

Task Attribute

Description

Required

fofile

XSL file to be rendered.

Yes, unless a nested fileset element is used.

outfile

Output filename.

Yes, if fofile is used.

outdir

Output directory.

Yes, if a nested fileset element is used. Also specifies the directory if outfile is used.

format

Available output formats are application/pdf, application/ postscript, application/ vnd.mif, application/rtf, application/vnd.hp-PCL, text/plain, and text/xml.

No; defaults to application/pdf.

userconfig

Name of the user configuration file.

No.

messagelevel

Logging level values are error, warn, info, verbose, and debug.

No; defaults to verbose.

logfiles

Log the names of files that are processed (if true).

No; defaults to true.

Here’s a simple Ant target that uses FOP to generate a PDF file for each .fo file in the myxsldir directory. The PDFs are placed in the directory mypdfdir. Both directories are in the current directory at the time the task is executed:

  1: <target name="generate-multiple-pdfs"    2:         description="Generates multiple PDF files">   3:  <fop format="application/pdf"    4:       outdir="mypdfdir" messagelevel="debug">   5:   <fileset dir="myxsldir">   6:    <include name="*.fo"/>   7:   </fileset>   8:  </fop>   9: </target>

Fonts

FOP provides its own system for registering fonts. Most of the time this won’t cause a problem, but a few of the output renderers don’t use the FOP font system. In particular, the AWT and print renderers use Java’s AWT package to do their work, and the AWT gets its information from the underlying operating system. Differences can result, depending on the operating system. These may include different font metrics or completely different fonts. You sure you use renderers that use the same font model.

FOP starts with a built-in set of 14 fonts taken from the Adobe PDF specification: Helvetica (normal, bold, italic, and bold italic), Times (normal, bold, italic, and bold italic), Courier (normal, bold, italic, and bold italic), Symbol, and ZapfDingbats. FOP also has support for custom fonts beyond this set. The current version of FOP (0.20.5) can add Type 1 and Truetype fonts; this is done by creating font metric files in XML format.

You generate the font metrics for a Type 1 font using the PFMReader class, which understands the PFM files that usually come with a Type 1 font. PFMReader looks at the PFM file and generates an XML font metrics file FOP can understand. The usage of PFMReader looks something like this:

java org.apache.fop.fonts.apps.PFMReader <pfm-file> <xml-file>

In order for PFMReader to work, fop.jar, avalon-framework.jar, and the Xerces and Xalan jars must be in the classpath.

In a similar fashion, The TTFReader class can generate a compatible font metrics file by reading a .ttf font file. Its usage looks similar to that of PFMReader:

java org.apache.fop.fonts.apps.TTFReader <ttf-file> <xml-file>

Again, fop.jar, avalon-framework.jar, and the Xerces and Xalan jars must be in the classpath. TrueType also allows for collections of fonts. Running TTFReader on a TrueType font collection causes TTFReader to list all the font names in the collection and then exit with an exception. You can use this fact to obtain the name of the specific TrueType font you need and then run TTFReader with the -ttcname “font-name” option, which generates the metrics file for the font you named.

Once you have generated font metrics files that FOP can understand, you still need to register these fonts with FOP. This brings us back to the FOP configuration file. Let’s assume you’ve generated a font metrics file for Arial in arialfm.xml and the font itself is in Arial.ttf, both in the directory specified by fontBaseDir.

The last section in the configuration file looks like this:

  1: <fonts>   2:  <font metrics-file="arialfm.xml" kerning="yes"    3:        embed-file="Arial.ttf">   4:   <font-triplet name="Arial" style="italic" weight="bold" />   5:  </font>   6: </fonts>

The options for <font-triplet>’s style attribute are normal and italic, and the options for its weight attribute are normal and bold. <font>’s kerning attribute can be either yes or no. The embed-file attribute is optional. When you’re using Type 1 fonts, be sure you give the name of the font file, not the metrics file, for <font>’s embed-file attribute.

If you supply the embed-file attribute on <font>, the font is embedded in the output document rather than being referenced. When you use Type 1 fonts, the entire font is embedded. When you use TrueType fonts (or collections), a new font that contains only the glpyhs that were actually used is embedded. This behavior can cause problems with searching, indexing, and cut and paste, because the output document contains indexes of the glyphs in the fonts, not the actual characters.

Output

FOP is all about rendering XSL documents in different formats. FOP provides a collection of classes that perform the job of rendering. In this section we’ll take a closer look at the renderers that come with FOP.

PDF

Adobe’s Portable Document Format (PDF) is the best-supported output format. FOP supports PDF version 1.3, which corresponds to the version implemented in Acrobat Reader 4.0. There are some PDF features that FOP doesn’t support at the moment; these include tagged PDF, document properties, and watermarks. You can use the open-source program iText (http://sourceforge.net/projects/itext) to add these features to PDF files generated by FOP. Starting with version 0.20.5, FOP has built-in support for PDF encryption. Here’s a program that shows how to encrypt a PDF file:

  1: /*   2:  *    3:  * FOPEncryptionMain.java   4:  *    5:  * Example from "Professional XML Development with Apache Tools"   6:  *   7:  */   8: package com.sauria.apachexml.ch3;   9:   10: import java.io.BufferedOutputStream;  11: import java.io.FileNotFoundException;  12: import java.io.FileOutputStream;  13: import java.io.IOException;  14: import java.io.OutputStream;  15: import java.util.Map;  16:   17: import org.apache.avalon.framework.logger.ConsoleLogger;  18: import org.apache.avalon.framework.logger.Logger;  19: import org.apache.fop.apps.Driver;  20: import org.apache.fop.apps.FOPException;  21: import org.apache.fop.apps.FormattingResults;  22: import org.apache.fop.messaging.MessageHandler;  23: import org.xml.sax.InputSource;  24:   25: public class FOPEncryptionMain {  26:   27:     public static void main(String[] args) {  28:         String xslFile = args[0];  29:         String pdfFile = args[1];  30:           31:         OutputStream outFile = null;  32:           33:         try {  34:             outFile = new FileOutputStream(pdfFile);  35:             outFile = new BufferedOutputStream(outFile);  36:         } catch (FileNotFoundException fnfe) {  37:             fnfe.printStackTrace();  38:         }  39:           40:         Driver fopDriver = new Driver(new InputSource(xslFile),  41:                                       outFile);  42:         Logger logger =   43:             new ConsoleLogger(ConsoleLogger.LEVEL_WARN);  44:         MessageHandler.setScreenLogger(logger);  45:         fopDriver.setLogger(logger);  46:         fopDriver.setRenderer(Driver.RENDER_PDF);  47:   48:         Map rendererOptions = new java.util.HashMap();  49:         rendererOptions.put("ownerPassword", "ownerpassword");  50:         rendererOptions.put("userPassword", "userpassword");  51:         rendererOptions.put("allowCopyContent", "FALSE");  52:         rendererOptions.put("allowEditContent", "FALSE");  53:         rendererOptions.put("allowPrint", "FALSE");  54:         rendererOptions.put("allowEditAnnotations","FALSE");  55:         fopDriver.getRenderer().setOptions(rendererOptions);  56:   57:         try {  58:             fopDriver.run();  59:         } catch (IOException ioe) {  60:             ioe.printStackTrace();  61:         } catch (FOPException fe) {  62:             fe.printStackTrace();  63:         }  64:           65:         FormattingResults results = fopDriver.getResults();  66:         System.out.println("Pages Formatted: "+  67:                            results.getPageCount());   68:     }  69: }

To encrypt a PDF document, you create a java.util.Map that contains a set of rendering options and pass it to the setOptions method of the Driver’s renderer (assuming you’ve selected the PDF renderer). In this example, you set all the available PDF encryption options. If you look carefully, you’ll see that these correspond to options that can be set from the FOP command line. Setting any one of these options causes the PDF file to be encrypted.

The Driver constant for the PDF renderer is Driver.RENDER_PDF.

PostScript

The PostScript renderer generates PostScript level 3 with most DSC comments. As of FOP 0.20.5, there are still a number of limitations in the PostScript renderer:

  • Images and SVG may not display correctly.

  • SVG support is very incomplete.

  • No image transparency is available.

  • Character spacings may be wrong.

  • Font embedding and multibyte characters aren’t supported.

  • PPD support is missing.

The Driver constant for the PostScript renderer is Driver.RENDER_PS.

PCL

The PCL renderer generates the Hewlett-Package Page Control Language (PCL) used in HP printers. The printed PCL output should be as close to identical as possible when compared to the printed output of the PDF renderer, subject to the limitations of the renderer and output device. The list of limitations is as follows:

  • Text or graphics outside the top or left of the printable aren’t rendered properly.

  • Helvetica is mapped to Arial, and Times is mapped to Times New.

  • Custom fonts aren’t supported.

  • Symbols are used for the non-symbol fonts ISO-8859-1.

  • Multibyte characters aren’t supported.

  • SVG support is limited to lines, rectangles, circles, ellipses, text, simple paths, and images. Colors (dithered black and white) are supported, but gradients aren’t. SVG clipping isn’t supported.

  • Images print black and white only. To print a non-monochrome image, it should be dithered first.

  • Image scaling is accomplished by modifying the effective resolution of the image data. Available resolutions are 75, 100, 150, 300, and 600 DPI.

  • Color printing isn’t supported. Colors are rendered by mapping the color intensity to one of the PCL fill shades (nine steps from white to black).

The Driver constant for the PCL renderer is Driver.RENDER.PCL.

SVG

The SVG renderer creates an SVG document that has links between the pages. This renderer is most useful for rendering slides and SVG images of pages. Large document create SVG documents that are too large for current SVG viewers to display. SVG font information is obtained from the Java VM, so there is the possibility of font mismatches when compared to the other renderers.

The Driver constant for the SVG renderer is Driver.RENDER_SVG.

MIF

The MIF renderer produces the Maker Interchange Format used by Adobe’s FrameMaker. This renderer isn’t fully implemented.

The Driver constant for the MIF renderer is Driver.RENDER_MIF.

TXT

The Text renderer produces ASCII text that tries to approximate the output of the PDF renderer. It’s most useful for getting an idea what the PDF document will look like. When you use this renderer, there are often extra or missing spaces between characters and lines due to the way FOP lays out text. You can reduce the number of spacing problems by adding the following attributes to your XSL document: font-family=”Courier”, font-size=”7.3pt”, line-height=”10.5pt”.

The Driver constant for the Text renderer is Driver.RENDER_TXT.

XML

The XML renderer produces an XML description of the area tree for the document. It’s primarily used for testing.

The Driver constant for the XML renderer is Driver.RENDER_XML.

AWT

The AWT renderer displays the pages one at a time in an AWT window. Each page is displayed inside a Java graphic. This renderer uses the Java VM font model, so there is the possibility of font mismatches compared to the other renderers.

The Driver constant for the AWT renderer is Driver.RENDER_AWT.

Graphics

FOP supports a number of different graphics file formats. This support is either built into FOP or provided via a separate library that must be downloaded.

Graphics Resolution

There is a mismatch between most of the FOP output formats (including PDF) and some bitmapped image file formats. The bitmap formats store some notion of the resolution of the image (most often dots per inch [dpi]), whereas the output formats have no notion of resolution. FOP ignores any resolution specified by the image file and uses the dimensions specified by the <fo:external-graphic> element when rendering the image:

  • If no dimensions are specified, FOP uses a 72dpi resolution to convert image dots to inches in the output.

  • If only one dimension is specified, FOP again uses the 72dpi resolution to convert image dots to images for the specified dimension. The other dimension is computed by preserving the image’s aspect ratio.

  • If both dimensions are specified, FOP renders the image in the space specified by the dimensions and adjusts the image’s resolution to fit all of the image’s pixels into the dimensions.

Image Caching

FOP caches images between multiple runs of FOP in the same JVM. Ordinarily this is a good performance-enhancing technique. However, if you’re dynamically generating images, this can be a problem, because FOP uses the URL of the image as the key to its cache. If you’re dynamically generating images, then you must make your URL change each time the image is regenerated. One way to do this is to add a dummy parameter to the image URL. You can then change the value of the parameter each time, to fool FOP’s cache.

The other problem with the FOP cache is that it can cause an OutOfMemoryError. To prevent this problem, you need to manually empty the cache before it happens. You can do this by calling org.apache.fop.image.FopImageFactory.resetCache.

Natively Supported Formats

FOP has built-in support for BMP, EPS, GIF, JPEG, and TIFF files:

  • BMP—FOP’s support for Microsoft Windows Bitmap (BMP) files is limited to the RGB color space. BMPs are supported for all renderers.

  • EPS—FOP supports Encapsulated Postscript (EPS) metafiles in both bitmap and vector modes. It provides full support for the PostScript renderer but only partial support for the PDF renderer. FOP doesn’t have a built-in PostScript interpreter, so it can only embed an EPS file into the PDF file. The problem is that the Adobe Acrobat reader, which also lacks a built-in PostScript interpreter, can’t display the EPS image. When you print the PDF using a PostScript device (GhostScript counts as a PostScript interpreter), the EPS image will print properly.

  • GIF—FOP supports GIF files without any problems and for all renderers.

  • JPEG—FOP’s native JPEG support works for the grayscale, RGB, and CMYK color spaces. It has trouble with unusual color lookup tables and color profiles. If a JPEG image isn’t displaying properly, you may be able to correct the problem by opening the image in a graphics program such as PhotoShop or the Gimp and saving it back out (specifying 24-bit color output before you save can also help).

  • TIFF—FOP’s native TIFF support only works for the PDF and PostScript renderers. In addition, the TIFF images must be single-channel images (bi-level or grayscale) that use white-is-zero in the TIFF PhotometricInterpretation tag. The TIFF images must also either be uncompressed or use CCITT (T.4, T.6) or JPEG compression.

Jimi

Jimi is a library developed by Sun for managing images. It can handle a number of formats including GIF, JPEG, TIFF, PNG, PICT, Photoshop, BMP, Targa, ICO, CUR, Sunraster, XBM, XPM, and PCX files. Due to Sun’s binary licensing for Jimi, the ASF is unable to provide downloadable Jimi jars for use with FOP. If you want to use a graphics format supported through Jimi (PNG at the moment), then you’ll need to go to Sun’s Website (http://java.sun.com/products/jimi), agree to the Sun binary license, download your own copy, and install it. The standard FOP distribution has been compiled with support for Jimi, so all you need to do is place the Jimi libraries in the Java classpath. The FOP documentation suggests that you do this by taking the JimiProClasses.zip file from the Jimi distribution and copying it to the FOP lib directory as jimi-1.0.jar.

Jimi’s biggest limitation is that it’s only compatible with JDK 1.1. Users of JDK 1.3 and above will need to use JAI.

FOP uses Jimi to provide support for PNG files. There are no limitations on FOP renderers or PNG images.

JAI

The Java Advanced Imaging API is compatible with JDK versions 1.3 and above and provides support for the BMP, JPEG, JPEG 2000, PNG, PNM, Raw, TIFF, and WBMP image formats. Supported JAI platforms include Win32, Solaris, Linux, AIX, Mac OS X, and Digital Linux. FOP uses JAI to provide support for PNG and TIFF files but is limited to the RGB and RGBA color spaces for both formats.

Batik

Batik is another xml.apache.org project that provides an SVG renderer and viewer. XSL allows non-XSL data, such as SVG, to be incorporated into documents. There are two ways to do this. You can place the SVG data in the XSL document using the <fo:instream-foreign-object> element or place it in a separate file referenced via <fo:external-graphic>. FOP uses Batik to render SVG images in the PDF renderer. SVG images are rendered according to the dimensions specified in the SVG file, but inside the viewport specified by the <fo:external-graphic> element. For best results, the dimensions and the viewport should be equal. You need to be aware of two issues when using SVG images with the PDF renderer: how SVG graphics are incorporated into the PDF file and how SVG text is placed into the PDF file.

Batik uses PDF commands to draw graphics objects such as lines and curves when rendering SVG files as PDF. However, some SVG constructs don’t map directly to PDF drawing commands. An SVG graphic may specify effects, patterns, or images, which don’t have PDF analogs. Therefore, these constructs are rendered as raster graphics and are influenced by FOP’s 72dpi resolution. The current version of FOP (0.20.5) doesn’t support PDF transparency, so SVG images with transparency effects render incorrectly.

Batik tries to use PDF text to render SVG whenever possible. But if the font isn’t supported or the text can’t be drawn normally, then Batik converts the text to a set of SVG curves and draws all the curves. One side effect is that the PDF document will contain the curves and not the characters, which means text-searching won’t work.

FOP Extensions

FOP provides a namespace-based extensions mechanism that allows extensions to XSL. Of course, these extensions won’t be compatible with other XSL formatters. Three extensions are provided with FOP: an extension for SVG, an extension for bookmarks in PDF, and an extension for placing a label in a page header/footer when a table spans multiple pages.

The PDF bookmark extension allows you to create outlines that contain links (bookmarks) to other parts of the document (identified via ID attributes). To use the extension, you need to declare the FOP extension namespace (line 2):

  1: <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"   2:          xmlns:fox="http://xml.apache.org/fop/extensions">   3:  <fox:outline internal-destination="ch1">   4:   <fox:label>Chapter 1</fox:label>   5:  </fox:outline>   6: </fo:root>

Once you’ve declared the extension namespace, you place <fox:outline> elements to indicate where the bookmark points and <fox:label> elements to specify the label for the bookmark.

The continued-label extension allows you to print a custom table header or footer when a table is too big to fit on a single page and overflows across page boundaries. This extension also uses the FOP extension namespace. You place a <fox:continued-label> element inside the table cell in the table header or footer where you want the continuation label to appear. You can use <fo:inline> to format the label. The label will appear only when the table has overflowed onto a second (or subsequent) page.

The current version of FOP (0.20.5) lets you write your own extensions by subclassing org.apache.fop .extensions.ExtensionObj. The FOP developers are in the middle of a major architectural overhaul of FOP, and it’s likely that this extension API will change because it depends on the internals of the FOP implementation.




Professional XML Development with Apache Tools. Xerces, Xalan, FOP, Cocoon, Axis, Xindice
Professional XML Development with Apache Tools: Xerces, Xalan, FOP, Cocoon, Axis, Xindice (Wrox Professional Guides)
ISBN: 0764543555
EAN: 2147483647
Year: 2003
Pages: 95

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net