Contents


Generate PDFs with XStream and XSL-FO

Leverage XStream and XSL-FO to generate dynamic documents

Comments

Many business applications require creation of a PDF document consisting of data stored in Java business objects. You can best think of these PDF documents as a view of the business data: This view, including the layout and structure, should be easily changeable and loosely tied to the business objects. This article provides a solution to this common business problem using XML, XStream, and Extensible Stylesheet Language Formatting Objects (XSL-FO). Lay the groundwork for the solution with an introduction to each technology and, then, integrate these technologies in a real-world example.

Start with a look at the technologies this article uses.

Serializing data with XStream

XStream is a simple but powerful library that enables you to serialize and de-serialize objects to and from XML. XStream's power comes from its flexibility, simplicity, speed, low memory usage, low overhead, and its control over the XML the library produces. Another key feature of the XStream library is its support for processing of deep object graphs, such as a catalog of CD objects housing tracks that contain track information. XStream doesn't require any changes to existing business objects unless you want to take advantage of its Java annotation support.

Data transformation with XSLT

The next building block is XSLT, which allows you to transform a structured XML document into various output formats, such as XML and HTML. XSLT is a complex and robust XML-based language that has several built-in functions (for example, string functions and formatting functions). It also uses XPath extensively to query and select XML nodes.

The XML transformations are done through XSLT. An XSLT stylesheet defines all the transformation rules for an XML document. It consists of templates that define how individual XML elements will be transformed. You can match templates based on element names, element context (/Book/Title differs from /Catalog/Title) or attributes, such as Title (/Book/@Title).

Formatting documents with XSL-FO

XSL-FO is nothing more than an XML schema for presentation. It is similar to HTML and CSS, but instead of the styling being externalized in the CSS file, it is stored within the FO document. Leveraging the XML serialization and XSL-FO approach has several benefits:

  • Separation of concerns. The presentation (formatting and layout) is separated from the data (Java business object) through XML serialization and XSL-FO.
  • Loose coupling of data and view. The XSL-FO stylesheet, which is responsible for formatting and layout, knows nothing about the Java business object: It is only aware of the XML representation of business objects. You can easily change the layout and formatting of the output document without modifying the underlying Java business objects because of the decoupling of data and presentation.
  • Presentation flexibility. The XSL-FO stylesheet provides tremendous flexibility with respect to layout and formatting. Similar to CSS, the XSL-FO standard provides common formatting elements such as font, font size, font weight, padding, and text decoration. In addition, XSL-FO provides:
    • Attribute sets. Share formatting definitions within a document through XSLT attribute sets.
    • Complex pagination support. Employ different layouts based on page conditions (first, last, other).
    • Footnotes. Add footnotes to a document.
    • Headers and footers. Define headers and footers for pages (first, last, other).
    • Optimization. Set "keep with next" and spacing optimizations.
    • Style inheritance. Inherit formatting and styles.
    • Table of contents and bookmarks. Generate a table of contents for a PDF document.
    • Tables. Supports tables including table header, footer, and body.
    • Writing mode. Options include left to right, right to left, top to bottom, and bottom to top.

Using XSL-FO is a matter of combining XSLT to transform an XML document into an FO document. The FO document is then piped through an FO engine to generate the desired output (in this case, a PDF document). Figure 1 illustrates the XSL-FO transformation process.

Figure 1. XSL-FO transformation process
XSL-FO transformation process for a XML document (generate an FO document and finally a PDF document)
XSL-FO transformation process for a XML document (generate an FO document and finally a PDF document)

Solution technical architecture

The primary goal of the solution in this article is to generate a PDF document from any Java business object in a manner that is flexible. The technology component that creates the PDF view should be insulated from the underlying Java classes. The interim XML that XStream generates, which is subsequently transformed into an FO document, provides the layer of insulation.

Figure 2 shows how the building blocks—Java business object classes, XStream, and XSL-FO—are combined to provide an end-to-end solution that builds the Purchase Order PDF document.

Figure 2. Solution architecture
Solution architecture to generate the XML document and then the PDF document
Solution architecture to generate the XML document and then the PDF document

The first step is to build the XML document by serializing the IPurchaseOrder (Purchase Order) business object, which comprises the Java classes IOrderItem and IAddress. To provide the greatest flexibility, you use Java annotations to map the classes to the corresponding elements and attributes in the Purchase Order XML file.

The second step is to transform the Purchase Order XML file to an FO document that can be piped through the FO engine (processor). You do this through the Purchase Order XSL-FO stylesheet. The resulting FO document is in turn piped through Apache FOP—an open source implementation of the FO engine (processor)— to produce the PDF document.

The goal is to produce the PDF document in Figure 3. You do this by creating an XSL-FO stylesheet.

Figure 3. The Purchase Order PDF document
Screenshot of  sample Purchase Order PDF document
Screenshot of sample Purchase Order PDF document

Class diagrams

The solution consists of five interfaces and their corresponding implementations. Table 1 shows a brief description of each interface.

Table 1. Interfaces and their implementations
NameDescription
IXmlSerializableInterface for classes that are serializable to XML
IAddressInterface for the Address business object
IPurchaseOrderInterface for a Purchase Order business object
IOrderItemInterface for an Order Item business object
IPdfGeneratorInterface for a generator that transforms any business object that implements the IXmlSerializable to PDF

Figure 4 shows the Unified Modeling Language (UML) class diagram for these interfaces.

Figure 4. UML diagram of interfaces in Table 1
UML diagram of interfaces in Table 1
UML diagram of interfaces in Table 1

The solution is comprised of the implementations of the above interfaces and a Tester class. Figure 5 shows these business objects.

Figure 5. UML diagram of the business objects
UML diagram of the business objects
UML diagram of the business objects

Purchase Order XML schema

Table 2 shows the Purchase Order XML schema consists of the key components.

Table 2. Purchase Order schema components
Key elementDescription
OrderDateString containing the date of the purchase order
CompanyAddressContains key address information regarding the company, including the company name, street address, city, state, and zip code
CustomerAddressContains key address information regarding the customer, including the company name, street address, city, state, and zip code
ItemsContains all items ordered, including an item ID, the item name, and the quantity ordered

Generating the Purchase Order XML file using XStream

The first step in generating the Purchase Order XML file is to serialize the Purchase Order classes to XML.

For this solution, you use annotations to control the generation of XML from XStream. Annotations are the preferred method for customizing the XML output that XStream generates; they are simple to use and allow you to define the XML mapping rules at the class level, thus making the serialization code less brittle. Without annotations, any changes to a business object class require changes to the serialization code, as well. With annotations, the code is cleaner, because the mapping rules aren't defined separately from the class and field definitions. As you add new fields to a class, you can define the mapping rules in the same Java source file.

Note: Annotations are only available in Java software development kit (JDK) version 1.5 and later. This solution uses annotations to control the XML output.

Start with the PurchaseOrder class in Listing 1. The PurchaseOrder class implements the IPurchaseOrder and IXmlSerializable interfaces. The toXml method defined in the IXmlSerializable interface is the key to serializing the purchase order to XML. It contains minimal lines of code that invoke XStream serialization, with auto-detect of annotations enabled. Whenever XStream comes to a class contained in a class, it looks for annotations to determine how to serialize the class. XStream uses the default conventions if it finds no annotations in a class.

Listing 1. PurchaseOrder
class@XStreamAlias("PurchaseOrder")
public class PurchaseOrder implements IPurchaseOrder, IXmlSerializable {
    // private instance variables
    /** The m_orderId. */
    @XStreamAlias("OrderId")
    @XStreamAsAttribute
    private String m_orderId = null;

    /** The m_order date. */
    @XStreamAlias("OrderDate")
    private Date m_orderDate = null;

    /** The m_company addr. */
    @XStreamAlias("CompanyAddress")
    private IAddress m_companyAddr = null;

    /** The m_customerAdress addr. */
    @XStreamAlias("CustomerAddress")
    private IAddress m_customerAdress = null;

    /** The m_items. */
    @XStreamAlias("Items")
    private ArrayList<IOrderItem> m_items = null;

    ...

    // IXmlSerializable method
    public String toXml() {
        XStream xstream = new XStream();
        xstream.autodetectAnnotations(true);
        return xstream.toXML(this);
    }
}

The @XStreamAlias annotations tell XStream which element and attribute names to use during serialization and deserialization. The XStreamAsAttribute annotation indicates that the field should be serialized as an attribute rather than an element.

The next step is to create the Address and OrderItem classes. These classes need not implement the IXmlSerializable interface, because they won't be serialized independently of the PurchaseOrder class. These classes are contained in the source code attached to the article (see Download). They are fairly straightforward, and consist of private instance variables and public getters and setters. The private instance variables are annotated to control the XStream serialization.

When the toXml method is invoked, it produces the XML in Listing 2.

Listing 2. Purchase Order XML
<PurchaseOrder OrderId="PO-123-456789">
    <OrderDate>2009-06-14 13:05:02.251 EDT</OrderDate>
    <CompanyAddress>
        <CompanyName>ACME Company</CompanyName>
        <StreetAddress>123 Main Street</StreetAddress>
        <City>Orlando</City>
        <State>FL</State>
        <ZipCode>32801</ZipCode>
    </CompanyAddress>
    <CustomerAddress>
        <CompanyName>A++</CompanyName>
        <StreetAddress>123 8th Avenue</StreetAddress>
        <City>Orlando</City>
        <State>FL</State>
        <ZipCode>32801</ZipCode>
    </CustomerAddress>
    <Items>
        <Item ItemId="A1B2C3" ItemName="Widget" Quantity="100" ItemCost="100.5"/>
        <Item ItemId="C3B2A1" ItemName="Micro-Widget" Quantity="1000" ItemCost="10.75"/>
    </Items>
</PurchaseOrder>

Generating the Purchase Order FO document

Next, convert the Purchase Order XML file in Listing 2 to an FO document. When you write a stylesheet, it is helpful to create a mock up of the target output file. The mock up can help you write the XSLT file. It is particularly helpful with XSL-FO development. To finalize the formatting and layout, create an FO document and run it through Apache FOP to ensure the output is correct. When the layout is finalized, it's a matter of creating an XSL-FO stylesheet that transforms the source XML to the target FO document.

Creating the FO document

An FO document consists of two main sections:

  • Page layout definition. This section describes page layout, including margins and content (for example, headers, footers, body).
  • Page sequence (content/data). This section contains the actual content for headers, footers, and body. Each page sequence must be attached to a page layout.

Start with the page layout model. Figure 6 shows the main components of a an FO page: Region-Before, Region-After, Region-Body, Region-Start, Region-End.

Figure 6. FO page layout model
FO page layout model
FO page layout model

In Figure 6, the Region-Body is the main content region. The Region-Before and Region-After are typically used for headers and footers. Similarly, you can render content to the left and right through the Region-Start and Region-End regions.

You can add two types of content to the regions:

  • Static. Static content is constrained to a static content area (for example, a header or footer).
  • Flow. Flow content is attached to a specific page layout and contains content that can span multiple pages. This is the main content of a document.

A page layout defines the page size and margins. A document can have multiple page layouts. Listing 3 is a basic 8.5 x 11-inch page definition.

Listing 3. Sample page layout
<fo:layout-master-set>
    <fo:simple-page-master 
        margin-right="1in" 
        margin-left="1in" 
        margin-bottom="0.5in" 
        margin-top="1in" 
        page-width="8.5in" 
        page-height="11in" 
        master-name="standardletter">

        <fo:region-body 
            margin-bottom="1in" 
            margin-top="1in"/>		
        <fo:region-after extent="0.5in"/>
    </fo:simple-page-master>
</fo:layout-master-set>

FO supports several content elements that you can add to static or flow elements:

  • block. Container for content similar to an HTML <DIV> tag (A block can contain text or other FO elements.)
  • inline. Container for content similar to an HTML <SPAN> tag
  • table. Similar to an HTML <TABLE> tag
  • list. List of data similar to the HTML <UL> or <OL> tags
  • external-graphic. A graphic or image; similar to the HTML <IMG> tag
  • character. A single character; used to apply special formatting to individual characters
  • basic-link. A document link; similar to the HTML <A> tag

Building the stylesheet for Purchase Order

Start by processing the root node PurchaseOrder to produce the layout-master-set and page-sequence elements. Listing 4 shows the template for the PurchaseOrder element.

Listing 4. PurchaseOrder template
<xsl:template match="PurchaseOrder">
    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
        <!-- Create page layout - including margins -->
        <fo:layout-master-set>			
            <!-- Page size and margins -->
            <fo:simple-page-master 
                master-name="all"
                page-height="11in"
                page-width="8.5in" 
                margin-top="0.25in"
                margin-bottom="0.25in"
                margin-left="1in" 
                margin-right="1in">
            <!-- Main content layout -->
            <fo:region-body margin-top="2in" 
                margin-bottom="1in" />
            <!-- Header layout -->
            <fo:region-before extent="2in" />
            <!-- Footer layout -->
            <fo:region-after extent="1in" />
            </fo:simple-page-master>
        </fo:layout-master-set>

        <!-- Create page sequence-->
        <fo:page-sequence master-reference="all">
            <!-- Create header -->
            <fo:static-content flow-name="xsl-region-before">
                <!-- Output company address information in header -->
                <xsl:apply-templates 
                    select="CompanyAddress" />
                <fo:block font-size="18pt" 
                    font-family="sans-serif"
                    line-height="1.5em" 
                    background-color="black" 
                    color="white"
                    text-align="center"
                    >PURCHASE ORDER</fo:block>
            </fo:static-content>
            <!-- Create footer -->
            <fo:static-content flow-name="xsl-region-after">
                <!-- Add page number to footer -->
                <fo:block text-align="end" 
                font-size="10pt" 
                font-family="serif">
                    Page <fo:page-number />
                </fo:block>
            </fo:static-content>

            <!-- Create main document content -->
            <fo:flow flow-name="xsl-region-body">
                <!-- Display order information (date, id)-->
                <xsl:call-template name=
                "DisplayOrderInformation" />

                <!-- Display Customer Address information -->
                <xsl:apply-templates select=
                "CustomerAddress" />

                <!-- Display items-->
                <xsl:apply-templates select="Items" />
            </fo:flow>
        </fo:page-sequence>
    </fo:root>
</xsl:template>

Your next step is to create the header, which consists of the company logo and company address. Within the static content flow for the Region-Before is a call to apply templates to all CompanyAddress elements. The template creates a two-column table, the first column contains the company logo and the second column contains the company address. Listing 5 shows the template for the CompanyAddress element.

Listing 5. CompanyAddress template
<xsl:template match="CompanyAddress">
<fo:table width="100%">
    <fo:table-column column-width="40%" />
    <fo:table-column column-width="60%" />
    <fo:table-body>
        <fo:table-row>
            <fo:table-cell>
            <fo:block>
            <fo:external-graphic
            src="url(D:\workspace\DwArticle5\resources\CompanyLogo.jpg)" />
            </fo:block>
            </fo:table-cell>
            <fo:table-cell>
            <fo:block>
            <fo:block text-align="right">
            <xsl:value-of select="./StreetAddress" />
            </fo:block>
            <fo:block text-align="right">
            <xsl:value-of select="concat(./City, ' ', 
                ./State, ' ', 
                ./ZipCode)" />
            </fo:block>
            </fo:block>
            </fo:table-cell>
        </fo:table-row>
    </fo:table-body>
</fo:table>
</xsl:template>

Now, display the order information (that is, the Order Id and Order Date). The DisplayOrderInformation template is named and invoked in the line:

<xsl:call-template name="DisplayOrderInformation" />

Previously, all the templates were invoked with <xsl:apply-templates/>. Named templates are useful for procedural-type routines that return values or where you don't want to process templates for all the child nodes but rather pick data from them. They are also useful in recursive scenarios. You'll use a named template to display the order information for demonstration purposes, but other approaches are more optimal. Listing 6 shows the named template.

Listing 6. DisplayOrderInformation template
<xsl:template name="DisplayOrderInformation">
    <fo:block text-align="right">
        <fo:inline font-weight="bold">Id:</fo:inline>
        <xsl:value-of select="./@OrderId" />
    </fo:block>
    <fo:block text-align="right">
        <fo:inline font-weight="bold">Order Date:</fo:inline>
        <xsl:value-of select="./OrderDate" />
    </fo:block>
</xsl:template>

Transform the Items and Item elements to a table containing a row for each Item. Add line item numbers for each Item as well as the line item total and order total. To add the line numbers, use <xsl:number/>, which generates a sequential number. To calculated the line item totals, multiply the item quantity by the item cost. I provide two approaches to calculating the order total—a recursive named template approach and an alternative approach using XSL template modes and recursion. The total is computed by adding a summation of each item quantity multiplied by each item cost.

Start by rendering the table containing the items. Listing 7 shows all related templates.

Listing 7. Items and Item template
<!-- Template for Items element-->	
<!-- Outputs a TABLE -->	
<xsl:template match="Items">
    <fo:block font-weight="bold" background-color="black" color="white"
    padding="2pt">ITEMS</fo:block>

    <fo:table width="100%">
    <!-- Add table column widths -->
    <fo:table-column column-width="10%" />
    <fo:table-column column-width="15%" />
    <fo:table-column column-width="30%" />
    <fo:table-column column-width="15%" />
    <fo:table-column column-width="15%" />
    <fo:table-column column-width="15%" />
    <!-- Add table header row -->	
    <fo:table-header>
    <fo:table-row>
    <fo:table-cell border="solid black 1px">
    <fo:block font-weight="bold"
    >#</fo:block>
    </fo:table-cell>
    <fo:table-cell border="solid black 1px">
    <fo:block font-weight="bold"
    >Item ID</fo:block>
    </fo:table-cell>
    <fo:table-cell border="solid black 1px">
    <fo:block font-weight="bold"
    >Description</fo:block>
    </fo:table-cell>
    <fo:table-cell border="solid black 1px">
    <fo:block font-weight="bold"
    >Quantity</fo:block>
    </fo:table-cell>
    <fo:table-cell border="solid black 1px">
    <fo:block font-weight="bold"
    >Item Cost</fo:block>
    </fo:table-cell>
    <fo:table-cell border="solid black 1px">
    <fo:block font-weight="bold"
    >Total Cost</fo:block>
    </fo:table-cell>
    </fo:table-row>
    </fo:table-header>
    <fo:table-body>
    <xsl:apply-templates />
    <!-- Add row for summary. Span all columns and apply bold to total-->		
    <fo:table-row>
    <fo:table-cell border="solid black 1px"
    number-columns-spanned="5">
    <fo:block font-weight="bold" 
    text-align="right">Total
    </fo:block>
    </fo:table-cell>
    <fo:table-cell border="solid black 1px">
    <fo:block font-weight="bold">
    <!-- Return the number into a variable which is displayed below -->
        <xsl:variable name="total">
    <!-- The following line needs to be uncommented if the mode 
    and recursion approach is utilized. -->
    <!--							     
    <xsl:apply-templates select="Item[1]"
            mode="calculateTotal" />-->
    <!-- The following call-template needs to be commented out if the call-template 
    approach is not utilized. -->
            <xsl:call-template name="calculateTotal">
                <xsl:with-param name="nodes" select="Item" />
            </xsl:call-template> 
        </xsl:variable>
    <!-- Output value of "total" variable -->					
        <xsl:value-of select="format-number($total, '$#,##0.00')"/>
    </fo:block>
    </fo:table-cell>
    </fo:table-row>
    </fo:table-body>
    </fo:table>
</xsl:template>

<!-- Template for Item element-->	
<!-- Outputs a TR for each Item -->	
<xsl:template match="Item">
<fo:table-row>
    <fo:table-cell border="solid black 1px">
    <fo:block>
    <xsl:number/>
    </fo:block>
    </fo:table-cell>
    <fo:table-cell border="solid black 1px">
    <fo:block>
    <xsl:value-of select="@ItemId" />
    </fo:block>
    </fo:table-cell>
    <fo:table-cell border="solid black 1px">
    <fo:block>
    <xsl:value-of select="@ItemName" />
    </fo:block>
    </fo:table-cell>
    <fo:table-cell border="solid black 1px">
    <fo:block>
    <xsl:value-of select="@Quantity" />
    </fo:block>
    </fo:table-cell>
    <fo:table-cell border="solid black 1px">
    <fo:block>
    <xsl:value-of select="format-number(@ItemCost, '$#,##0.00')" />
    </fo:block>
    </fo:table-cell>
    <fo:table-cell border="solid black 1px">
    <xsl:variable name="total" select="@Quantity * @ItemCost" />
    <fo:block>
    <xsl:value-of select="format-number($total, '$#,##0.00')" />
    </fo:block>
    </fo:table-cell>
    </fo:table-row>
</xsl:template>

As mentioned before, you have two main options to generate the order total. Listing 8 shows the first—the named template approach—. The calculateTotal template is recursively called until no remaining Item elements are found. The inline comments describe how the template works.

Listing 8. Named template for calculation
<xsl:template name="calculateTotal">
<xsl:param name="nodes" select="/.." />
<xsl:param name="subtotal" select="0" />
<xsl:variable name="total"
 select="$subtotal + ($nodes[1]/@ItemCost * $nodes[1]/@Quantity)" />
<xsl:choose>
    <!-- check if more Item nodes to process and stop and return value if not -->
    <xsl:when test="not($nodes[2])">
        <xsl:value-of select="$total" />
    </xsl:when>
    <!-- recursively call template since there are more Item nodes to process -->
    <xsl:otherwise>
        <xsl:call-template name="calculateTotal">
            <xsl:with-param name="nodes"
            select="$nodes[position() > 1]" />
            <xsl:with-param name="subtotal" select="$total" />
        </xsl:call-template>
    </xsl:otherwise>
</xsl:choose>
</xsl:template>

The second option is recursive templates using modes. Listing 9 shows the template for calculating the total. It is applied to all Item elements when the mode is calculateTotal. You need to create an empty template for any child nodes that should be ignored during the application of templates using the mode. Similar to the named template, the template is recursively applied until no more Item elements are found. The inline comments describe how the template works.

Listing 9. Item template (calculateTotal mode)
<xsl:template match="Item" mode="calculateTotal">
<xsl:param name="subtotal" select="0" />
<xsl:variable name="total"
 select="$subtotal + (@ItemCost * @Quantity)" />
<xsl:choose>
<!-- check if more Item nodes to process and stop and return value if not -->
    <xsl:when test="not(following-sibling::Item)">
        <xsl:value-of select="$total" />
    </xsl:when>
    <xsl:otherwise>
<!-- recursively apply template since there are more Item nodes to process -->	
        <xsl:apply-templates select="following-sibling::Item[1]"
            mode="calculateTotal">
            <xsl:with-param name="subtotal" select="$total" />
        </xsl:apply-templates>
    </xsl:otherwise>
</xsl:choose>
</xsl:template>

Generate the PDF document

The last step is to create the PDF from the XML generated by XStream using the XSL stylesheet that you just created. The PdfGenerator class, responsible for generating the PDF, applies the stylesheet using the standard Transformer class and pipes the document through Apache FOP.

Listing 10. Item template (calculateTotal mode)
public class PdfGenerator implements IPdfGenerator {

    public OutputStream generate(IXmlSerializable object, String stylesheetPath, 
                    OutputStream pdfContent) {    
        try {
            // setup xml input source
            String xml = object.toXml();
            StreamSource xmlSource = 
                        new StreamSource(new ByteArrayInputStream(xml.getBytes()));
      
            // setup xsl stylesheet source
            File xslFile = new File(stylesheetPath);
            FileInputStream xslFileStream = new FileInputStream(xslFile);
            StreamSource xslSource = new StreamSource(xslFileStream);

            // get transformer
            TransformerFactory tfactory = TransformerFactory.newInstance();
            Transformer transformer = tfactory.newTransformer(xslSource);

            // setup FOP
            FopFactory fopFactory = FopFactory.newInstance();
            FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
            foUserAgent.setProducer(this.getClass().getName());
            Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent,
                                    pdfContent);
                  
            // perform transformation 
            Result res = new SAXResult(fop.getDefaultHandler());
            transformer.transform(xmlSource, res);
        } catch (FileNotFoundException e) { e.printStackTrace(); 
        } catch (TransformerException e) {  e.printStackTrace();
        } catch (FOPException e) { e.printStackTrace();
        }
            
        return pdfContent;
    }
}

Recommendations for using XStream and XSL-FO

Here are a few recommendations for XStream and XSL-FO solutions:

  • Use Java annotations for XStream configuration, as it provides the greatest flexibility and loose coupling.
  • Use templates with modes rather than call named templates, as XSLT is best thought of in terms of set processing rather than procedural programming.
  • Use recursion where needed. Too often, stylesheets are made more complex in an attempt to avoid recursive templates.
  • Think through the XML schema. Too often, schemas are rushed, and the schema design is difficult to grow and maintain. Give careful consideration to whether you use attributes or elements. For example, if you define Publisher as an attribute rather than an XML element, it's difficult to add additional information regarding the publisher to the XML document.

Conclusion

In this article, you saw how easily you can create a PDF document from Java business objects using XStream and XSL-FO. The separation of concerns allows you to isolate the view from the business objects, thus you can change the view (PDF document) without having to modify the Java code. You do all this by creating XSLT style sheets tailored to unique presentation requirements. Additionally, it's possible to use different XSLT stylesheets to generate different views (output documents) of the same Java business objects.


Downloadable resources


Related topics

  • W3C XSL version 1.1: Read the W3C specification for XSL-FO. Explore the features and syntax of XSL for expressing stylesheets.
  • Apache FOP: Download the API to generate PDFs from an FO document.
  • Check out three popular commercial FO engines:
  • XML technical library: See the developerWorks XML Zone for a wide range of technical articles and tips, tutorials, standards, and IBM Redbooks.
  • developerWorks podcasts: Listen to interesting interviews and discussions for software developers.

Comments

Sign in or register to add and subscribe to comments.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=XML
ArticleID=424210
ArticleTitle=Generate PDFs with XStream and XSL-FO
publish-date=09012009