XSL-FO Basics


In the following examples you need an XML-FO processor to produce formatted output from your XMLFO documents. All the examples in this section are presented using the Apache processor that can be downloaded from http://www.xml.apache.org/fop/. This is an open-source project that processes the XSL-FO document you supply to the designated output. It is a command line utility and its syntax is discussed shortly.

Also, with a Microsoft Windows operating system you may need a Microsoft Java Virtual Machine. If you do not know whether the Java VM is installed, run or install software diagnostics such as Belarc Advisor (http://www.belarc.com) to audit the software installed on your machine. If you do not have the Java VM, you can download and install the service. Because Microsoft no longer supports the Java VM, various third party sites now provide the download:

  • q http://www.techtips4u.com/downloads/#MSJavx86

  • q http://www.download.windowsupdate.com/msdownload/update/v3-19990518/cabpool/MSJavWU_8073687b82d41db93f4c2a04af2b34d.exe

In order to make the examples a little easier, you can download the XMLSpy suite of applications on a free 30 day trial from http://www.altova.com. This provides an XML, XSL, XSLT editor and also a convenient download as a plug in which will install the Apache FOP and configure XMLSpy to use it. This enables you to create a project with an XML document source, XSLT to transform the data into an XSL-FO document and instruct XMLSpy to process the resulting XSL-FO document into PDF. All the subsequent examples in this chapter will use this method and therefore is the recommended method (You still need the Java VM for Microsoft platforms). In scenarios when you wish to see the results of your XSL-FO document in another format, you can use the Apache FOP directly through the command line.

In order to view the examples later in the chapter, you need Adobe Acrobat Reader as this will be the chosen output for the examples.

Hello World for XSL-FO

The following code shows a sample XSL-FO doc for the Hello World example.

      <?xml version="1.0" encoding="ISO-8859-1"?>      <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">         <fo:layout-master-set>          <fo:simple-page-master            master-name="A4"            page-height="29.7cm"            page-width="21cm"            margin-top="1.0cm"            margin-bottom="1.0cm"            margin-left="1.0cm"            margin-right="1.0cm">            <fo:region-body   margin="1.0cm"/>            <fo:region-before extent="1.0cm"/>            <fo:region-after  extent="1.0cm"/>            </fo:simple-page-master>            </fo:layout-master-set>            <fo:page-sequence master-reference="A4">                  <fo:flow flow-name="xsl-region-body">                        <fo:block>Hello World</fo:block>                  </fo:flow>            </fo:page-sequence>      </fo:root> 

Using the command prompt, run the XSL-FO code through the Apache Formatting Objects Processor (FOP). Your command line resembles the following:

      fop hello.xml -txt hello.txt 

Run through the Apache Formatting Objects Processor (FOP), the output for a text file looks like the screen in Figure 4-6.

image from book
Figure 4-6

If you change the output to a PDF file, as follows:

      fop hello.xml -pdf hello.pdf 

The output looks like what you see in Figure 4-7.

image from book
Figure 4-7

Not exactly earth-shattering, but this example enables you to try out using the Apache FOP and is a starting point for looking at some of the other features available within XSL-FO.

Note that the fo:flow element in the source XML-FO. The fo:flow element is the root level element for all subsequent content. The fo:flow does not create an area, areas are created for all the child elements contained within the open and closing tags in the order in which they appear. In our example, the child element is an fo:block containing the Hello World text.

Within the fo:flow the attribute flow-name specifies where in the page the flow will be rendered. The allowed values correspond to the regions discussed earlier in the list of possible values:

  • q xsl-region-body-Body

  • q xsl-region-before-Header

  • q xsl-region-after-Footer

  • q xsl-region-start-Left

  • q xsl-region-end-Right

The fo:block element usually host paragraphs, tables captions and so on. Formatting is applied to all elements within the block unless the child elements override the formatting. This is a form of inheritance within the XSL-FO model whereby the child elements inherit the formatting properties of their parents or they can override the settings by specifying their own property values.

To apply a font to the text you add the following:

      <fo:flow flow-name="xsl-region-body">             <fo:block font-size="20pt" font-weight="bold"                 font-family="verdana">Hello World      </fo:block>      </fo:flow> 

Basic Formatting

Begin by putting together a slightly better example than our Hello World application. Consider the following XML listing. This is a set of postal codes and that demonstrate some of the basic formatting.

      <?xml version="1.0" encoding="UTF-8"?>      <ROOT>        <PostCode>            <PostCode>G1</PostCode>            <City>Glasgow</City>        </PostCode>        <PostCode>            <PostCode>EH1</PostCode>            <City>Edinburgh</City>        </PostCode>        <PostCode>            <PostCode>PA1</PostCode>            <City>Paisley</City>        </PostCode>        <PostCode>            <PostCode>NE5</PostCode>            <City>Newcastle</City>        </PostCode>      </ROOT> 

You can generate a basic list of all the postcodes and cities they relate to. The following XSLT produces an XML-FO to list each of the postcode elements.

      <?xml version="1.0"?>      <xsl:stylesheet version="1.0"        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"        xmlns:fo="http://www.w3.org/1999/XSL/Format">        <xsl:template match="/">         <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">          <fo:layout-master-set>            <fo:simple-page-master               master-name="A4"               page-width="29.7cm"               page-height="21cm"               margin-top="1cm"               margin-bottom="1cm"               margin-left="1cm"               margin-right="1cm">            <fo:region-body/>            </fo:simple-page-master>            </fo:layout-master-set>            <fo:page-sequence master-reference="A4">            <fo:flow flow-name="xsl-region-body">                <xsl:apply-templates select="//ROOT/PostCode"/>                    </fo:flow>            </fo:page-sequence>          </fo:root>        </xsl:template>        <xsl:template match="//ROOT/PostCode">          <fo:block><xsl:value-of select="PostCode"/>-<xsl:value-of      select="City"/></fo:block>        </xsl:template>      </xsl:stylesheet> 

Using XMLSpy, you can apply the transformation to the XML and view the results in any of the following formats:

  • q PDF-Default

  • q Text-Standard textual representation of results

  • q XML-An XML area tree

  • q MIF-Maker Interchange Format

  • q PCL-Printer Control Language

  • q Postscript-Adobe postscript format

When you take the default PDF, the results look like the screen in Figure 4-8.

image from book
Figure 4-8

Lists

You can now change the transformation to produce XSL-FO that renders the XML using a bulleted list. The fo:list-block element contains fo:list-item elements which in turn must contain a fo:listitem: label and one or more fo:list-item-body elements. The fo-list-item is optional because it is implicit within the fo:list-block. The fo:list-item-label allows us to insert a bullet character or as we shall see shortly, a bullet image. The fo:list-body, contains the list items themselves.

Within either the fo:list-block or fo:list-body, you can specify any indents we wish to apply. The attributes start-indent and end-indent allow us to specify the indent in millimeters, centimeters, etc. There is also a special indent function body-start() that sets the start-indent to value calculated from provisional-distance-between-starts. The provisional-distance-between-starts can be set to a value at the fo-list-block element.

            <fo:page-sequence master-reference="A4">              <fo:flow flow-name="xsl-region-body">                        <fo:list-block provisional-distance-between-starts="2.0cm">                             <xsl:apply-templates select="//ROOT/PostCode"/>                            </fo:list-block>              </fo:flow>            </fo:page-sequence>            </fo:root>        </xsl:template>        <xsl:template match="//ROOT/PostCode">            <fo:list-item>                  <fo:list-item-label start-indent="1.0cm" end-indent="1.0cm">                       <fo:block >                             <fo:external-graphic src="/books/2/381/1/html/2/C:\Wrox\Professional      XML\Chp4\Code\POSTITL.jpg" content-height="0.5cm"/>                       </fo:block>                  </fo:list-item-label>                  <fo:list-item-body start-indent="body-start()" >                  <fo:block >                       <xsl:value-of select="PostCode"/>-<xsl:value-of select="City"/>                  </fo:block>                 </fo:list-item-body>            </fo:list-item>        </xsl:template> 

This code produces the following output shown in Figure 4-9.

image from book
Figure 4-9

You can also use images as the bullet points or include them in the list (for example, a traffic light indicator or progress bar for a task list).

              <xsl:template match="//ROOT/PostCode">          <fo:list-item>              <fo:list-item-label start-indent="1.0cm" end-indent="1.0cm">                  <fo:block >                      <fo:external-graphic src="/books/2/381/1/html/2/C:\Wrox\Professional      XML\Chp4\Code\POSTITL.jpg" content-height="0.5cm"/>                     </fo:block>              </fo:list-item-label>              <fo:list-item-body start-indent="body-start()" >                  <fo:block >                     <xsl:value-of select="PostCode"/>-<xsl:value-of select="City"/>                  </fo:block>              </fo:list-item-body>          </fo:list-item>        </xsl:template> 

The output is shown in Figure 4-10.

image from book
Figure 4-10

Tables

You can put this detail into a neatly formatted table. First, look at the HTML table with XSL-FO elements and attribute equivalents:

Open table as spreadsheet

HTML

XSL-FO Element

Description/Values

<TABLE>

fo:table-and-caption

Table and optional caption

<TH>

fo:table-header

Table header

<TR>

fo:table-row

Table row

<TD>

fo:table-cell

Table cell

<COLUMN>

fo-table-column

Table column

<TBODY>

fo:table-body

Table body

<TFOOT>

fo-table-footer

Table footer

<COLSPAN>

number-columns-spanned

number of columns to span on table-cell element

<ROWSPAN>

number of rows to span

number of rows to span in table-row element

NA

empty-cells

show or hide empty cells in table-default is show

NA

table-omit-header-at-break

if table spans multiple pages false (default) displays the header on each page; true does not.

NA

table-omit-footer-at-break

If table spans multiple pages false (default) displays the header on each page; true does not.

<TBODY>

fo:table-body

Table body

<TFOOT>

fo-table-footer

Table footer

<CELLPADDING>

padding-left, padding-right, padding-top, padding-bottom.

Specifies the padding width for cells.

<CELLSPACING>

NA

 

Width

column-width

Specifies the width of the table column.

This code shows an example of the table structure within an XSLT that produces the XSL-FO document for sample postal codes.

      <xsl:template match="/">        <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">          <fo:layout-master-set>            <fo:simple-page-master                    master-name="A4"                    page-width="29.7cm"                    page-height="21cm"                    margin-top="1cm"                    margin-bottom="1cm"                margin-left="1cm"                margin-right="1cm">              <fo:region-body/>            </fo:simple-page-master>          </fo:layout-master-set>          <fo:page-sequence master-reference="A4">            <fo:flow flow-name="xsl-region-body">                            <fo:table>                              <fo:table-column column-width="3cm"/>                              <fo:table-column column-width="3cm"/>                              <fo:table-body>                                   <fo:table-row background-color="silver">                                      <fo:table-cell border-style="solid" padding-      top="2px" padding-bottom="2px" padding-left="2px" padding-right="2px">                                            fo:block>Postcode</fo:block>                                             </fo:table-cell>                                             <fo:table-cell border-style="solid" padding-      top="2px" padding-bottom="2px" padding-left="2px" padding-right="2px">                                                      <fo:block>City</fo:block>                                             </fo:table-cell>                                     </fo:table-row>                                     <xsl:apply-templates select="//ROOT/PostCode">                                             <xsl:sort data-type="text" select="City"/>                                     </xsl:apply-templates>                              </fo:table-body>                    </fo:table>              </fo:flow>            </fo:page-sequence>          </fo:root>        </xsl:template>        <xsl:template match="//ROOT/PostCode">       <fo:table-row>             <fo:table-cell border-style="solid" padding-top="2px" padding-bottom="2px"      padding-left="2px" padding-right="2px">                       <fo:block><xsl:value-of select="PostCode"/></fo:block>             </fo:table-cell>               <fo:table-cell border-style="solid" padding-top="2px" padding-bottom="2px"      padding-left="2px" padding-right="2px">                      <fo:block><xsl:value-of select="City"/></fo:block>              </fo:table-cell>       </fo:table-row>        </xsl:template> 

The results of this transformation in PDF format are shown in Figure 4-11.

image from book
Figure 4-11

A Working Example

You now work through a real-world example to gain a more thorough understanding of the mechanics of the XSL-FO process. The example produces a printable invoice from a given XML set that is formatted and paginated appropriately and available to a consumer (a customer-facing Web site, for example) in PDF format.

A subset of the source XML for the example is shown in the following code (the full file can be downloaded from the companion Web site for this book at http://www.wrox.com):

Important 

It should be noted that depending on the software currently running on your computer, you may receive some warnings while processing and may receive slightly different output. This may also be the case if you are targeting a different browser (when rendering HTML).

If you have to ensure the content is presented identically for every target browser, you should test on each target, make changes to the XSL-FO for that target, then your page should use client-side script to check which target you are running on and produce the content accordingly.

      <?xml version="1.0" encoding="UTF-8"?>      <ROOT DATE="19/06/2006" Time="10:41:09">       <Invoice Invoice>               <InvoiceID>4561598</InvoiceID>               <ContractID>CH20721   </ContractID>               <AccountNumber>002585</AccountNumber>               <CustomerName>Smiths Construction</CustomerName>               <CustomerAddress1>123 Glassford St</CustomerAddress1>               <CustomerAddress2>Glasgow</CustomerAddress2>               <CustomerAddress3>Lanarkshire</CustomerAddress3>               <CustomerAddress4>Scotland</CustomerAddress4>               <CustomerPostCode>G2 4YR</CustomerPostCode>               <DeliveryAddress1>Carlisle Railway Station</DeliveryAddress1>               <DeliveryAddress2>HARKER</DeliveryAddress2>               <DeliveryAddress3>CARLISLE</DeliveryAddress3>               <OrderNumber>TC258567</OrderNumber>               <OrderName>MICK</OrderName>               <InvoiceDate>30/06/06</InvoiceDate>               <HireStatus>HIRE COMPLETE</HireStatus>               <PreVATTotal>166.28</PreVATTotal>               <VAT1>29.10</VAT1>               <InvoiceTotal>195.38</InvoiceTotal>               <CreditTerms>30</CreditTerms>               <HireItem>                     <ProductCode>CHAA</ProductCode>                     <Description>JUNCTION BOX </Description>                     <FromDate>01/06/06</FromDate>                     <ToDate>21/06/06</ToDate>                     <Weeks>3.00</Weeks>                     <Rate>10.75</Rate>                     <Quantity>1</Quantity>                     <Discount>50.00</Discount>                     <VATCode>1</VATCode>                     <Value>16.14</Value>                     </HireItem>               <HireItem>                      <ProductCode>CHXL 03206</ProductCode>                      <Description>EXT LEAD      </Description>                      <FromDate>01/06/06</FromDate>                      <ToDate>30/06/06</ToDate>                      <Weeks>4.40</Weeks>                      <Rate>1.25</Rate>                      <Quantity>1</Quantity>                      <VATCode>1</VATCode>                      <Value>5.50</Value>               </HireItem>               <HireItem>                      <ProductCode>CHXL 01917</ProductCode>                      <Description>EXT LEAD      </Description>                      <FromDate>01/06/06</FromDate>                      <ToDate>30/06/06</ToDate>                      <Weeks>4.40</Weeks>                      <Rate>1.25</Rate>                      <Quantity>1</Quantity>                      <VATCode>1</VATCode>                      <Value>5.50</Value>               </HireItem>               <SaleItem>                      <Description>STARTER KEY</Description>                      <Date>01/06/06</Date>                      <Quantity>1</Quantity>                      <Price>2.10</Price>                      <VATCode>1</VATCode>                      <Value>-2.10</Value>               </SaleItem>               <SaleItem>                      <Description>DRILL CHUCK KEY</Description>                      <Date>01/06/06</Date>                      <Quantity>1</Quantity>                      <Price>1.75</Price>                      <VATCode>1</VATCode>                      <Value>-1.75</Value>               </SaleItem>               <SaleItem>                      <Description>ALLEN KEY</Description>                      <Date>01/06/06</Date>                      <Quantity>1</Quantity>                      <Price>3.10</Price>                      <VATCode>1</VATCode>                      <Value>-3.10</Value>              </SaleItem>              <SaleItem>                      <Description>MISC</Description>                      <Date>01/06/06</Date>                      <Quantity>1</Quantity>                      <Price>3.10</Price>                      <VATCode>1</VATCode>                      <Value>-3.10</Value>               </SaleItem>       </Invoice>      </ROOT> 

The preceding XML represents an invoice produced for a customer who is being charged for hiring equipment from the providing company. The invoice is encapsulated within the <Invoice> tag and has child elements <HireItem> showing the specific invoicing details for every individual piece of equipment that has been hired by the customer.

The <invoice> section contains all the top level detail for the invoice, such as invoice number, customer details and summary values. The <HireItem> section contains the specifics about the equipment such as descriptions and rates charged. The <SaleItem> section contains a record of sales purchased by the customer.

The invoice you want to produce shows the invoice header information at the top of the invoice and provides a detailed breakdown of the equipment hired in a list following the header data.

Figure 4-12 shows a high-level layout for the invoice.

image from book
Figure 4-12

The header section has one table that holds the company logo and all the top level invoice details. You create a table and populate it using an apply-templates section for the ROOT/Invoice element.

The initial setup of the invoice, an A4–landscape oriented format is shown in the following code:

      <?xml version="1.0"?>      <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"      xmlns:fo="http://www.w3.org/1999/XSL/Format">      <xsl:template match="/">       <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">             <fo:layout-master-set>                     <fo:simple-page-master master-name="A4"                                             page-width="29.7cm"                                             page-height="21cm"                                             margin-top="1cm"                                             margin-bottom="1cm"                                             margin-left="1cm"                                             margin-right="1cm">                             <fo:region-body/>                      </fo:simple-page-master>                     </fo:layout-master-set>                     <fo:page-sequence master-reference="A4"> 

The XSLT code creates the table structure for the header data as shown in the following code:

      <fo:flow flow-name="xsl-region-body">       <fo:table>               <fo:table-column column-width="9cm"/>               <fo:table-column column-width="9cm"/>               <fo:table-column column-width="9cm"/>               <fo:table-body>                      <xsl:apply-templates select="//ROOT/Invoice"/>               </fo:table-body>               </fo:table> 

The header comprises three columns. The first holds invoice and contract information, the second customer information and the third delivery details. The apply-templates section builds up the table by creating and formatting the cells with relevant data from the source XML.

      <xsl:template match="//ROOT/Invoice">       <fo:table-row       background-color="silver"                           text-align="center"                           width="27cm">        <fo:table-cell number-columns-spanned="3">         <fo:block>          <fo:external-graphic            src="/books/2/381/1/html/2/C:\Wrox\Professional XML\Chp4\Code\AcmeLogo.JPG"            content-height="0.5cm"/>            </fo:block>          </fo:table-cell>        </fo:table-row>        <fo:table-row       background-color="white"                            width="27cm">         <fo:table-cell     number-columns-spanned="3"                            border-style="none">               <fo:block font-family="serif" font-weight="bold" font-size="28pt">           Hire Invoice         </fo:block>        </fo:table-cell>      </fo:table-row>      <fo:table-row       background-color="white"                          width="100%">       <fo:table-cell     padding="2px"                          border-top-style="solid"                          border-left-style="solid"                          border-right-style="solid"                          width="33%">        <fo:block>            Invoice:      <xsl:value-of select="InvoiceID"/>        </fo:block>       </fo:table-cell>       <fo:table-cell     padding="2px"                          border-top-style="solid"                          border-left-style="solid"                          border-right-style="solid"                          width="33%">        <fo:block  font-family="serif"                   font-weight="bold"                   font-size="14pt">           Customer Details        </fo:block>       </fo:table-cell>       <fo:table-cell       padding="2px"                      border-top-style="solid"                      border-left-style="solid"                      border-right-style="solid"                      width="33%"                      text-align="right">        <fo:block    font-family="serif"              font-weight="bold"              font-size="14pt">      Site Address        </fo:block>       </fo:table-cell>      </fo:table-row>      <fo:table-row   background-color="white"                      width="100%">       <fo:table-cell        padding="2px"                      border-left-style="solid"                      border-right-style="solid"                      width="33%">        <fo:block>      Contract:       <xsl:value-of select="ContractID"/>        </fo:block>       </fo:table-cell>       <fo:table-cell       padding="2px"                      border-left-style="solid"                      border-right-style="solid"                      width="33%">        <fo:block>              <xsl:value-of select="AccountNumber"/>          </fo:block>         </fo:table-cell>         <fo:table-cell     padding="2px"                       border-left-style="solid"                       border-right-style="solid"                       width="33%"                       text-align="right">         <fo:block>       <xsl:value-of select="DeliveryAddress1"/>         </fo:block>        </fo:table-cell>       </fo:table-row>      </xsl:template> 

The preceding code shows the first three rows of the header table. The subsequent header rows are identical to the last two rows with the exception of content.

Some new attributes are shown in the example that must be explained (these are bolded in the code). The first is the attribute number-columns-spanned. This enables you to create the HTML equivalent of COLSPAN on the table. For the logo and the header label you want the logo to span the three columns and be centered. You want the header to span the three columns and be left-aligned.

The second attribute is text-align. This attribute simply allows you to specify the alignment of the child content as left, right, or center.

The padding attribute sets the cell padding area that outlines the content. This can be broken down specifically using padding-top, padding-bottom, padding-left and padding-right. You can control cell padding for each dimension of the cell.

You can also applied various font attributes to some of the fo:block elements in the code. Some of the commonly used font attributes are shown in the following list:

  • q Font-family-Serif, sans-serif, fantasy etc

  • q Font-size-Can be specified in points, relatively (larger, smaller) or using constants (small, medium, large, x-large etc)

  • q Font-style-Normal, italic, oblique, backslant or inherit

  • q Font-weight-Constants (normal, bold, bolder or lighter) or an integer value representing the weighting.

Several attributes enable you to format the border of table cells. The border style is broken down into border-top-style, border-bottom-style, border-left-style and border-right-style; these can have the following values:

  • q None-No border

  • q Solid-Single pixel border

  • q Dotted-Single full stop broken border

  • q Dashed-Short line broken border

  • q Hidden-Same as none except when conflict occurs with borders for table elements

  • q Double-Double lined solid border. 1 pixel line, 1 pixel space, 1 pixel line

  • q Inset-Embedded cell style

  • q Outset-Raised cell style

  • q Groove-Embedded border style

  • q Ridge-Raised border style

If you create the subsequent rows for the header table and apply the transformation you achieve the results shown in Figure 4-13.

image from book
Figure 4-13

Important 

It should be noted that the presentation of the results on screen (in, for example, Adobe Acrobat) are not always exactly what will be printed. It is wise to periodically print to your target printer in order to check the results.

You now wish to show the payment terms on the invoice for the customer. This is just an fo:block element neatly formatted and presented under the header table.

      <fo:block      padding="2px">        Payment Terms: This invoice must be paid no later than        <xsl:value-of select="//ROOT/Invoice/CreditTerms"/>        from the invoice date.      </fo:block> 

Next you want to render the series of hire item records in a table. First, you build up the table structure and the header record. The XSLT code that follows shows how to achieve this based on the XML data shown previously.

      <fo:table padding="2px">      <fo:table-column column-width="4cm"/>      <fo:table-column column-width="5cm"/>      <fo:table-column column-width="3cm"/>      <fo:table-column column-width="3cm"/>            <fo:table-column column-width="2cm"/>      <fo:table-column column-width="2cm"/>      <fo:table-column column-width="2cm"/>      <fo:table-column column-width="2cm"/>      <fo:table-column column-width="2cm"/>      <fo:table-column column-width="2cm"/>      <fo:table-header border-style="solid">      <fo:table-row background-color="white">      <fo:table-cell number-columns-spanned="10">       <fo:block font-family="serif" font-weight="bold" font-size="20pt">Hire      Items</fo:block>      </fo:table-cell>      </fo:table-row>      <fo:table-row background-color="silver">      <fo:table-cell padding="2px">       <fo:block>Code</fo:block>      </fo:table-cell>      <fo:table-cell padding="2px">       <fo:block>Description</fo:block>      </fo:table-cell>      <fo:table-cell padding="2px">       <fo:block>From Date</fo:block>      </fo:table-cell>      <fo:table-cell padding="2px">       <fo:block>To Date</fo:block>      </fo:table-cell>      <fo:table-cell padding="2px" text-align="right">       <fo:block>Weeks</fo:block>      </fo:table-cell>      <fo:table-cell padding="2px" text-align="right">       <fo:block>Rate</fo:block>      </fo:table-cell>      <fo:table-cell padding="2px" text-align="right">       <fo:block>Quantity</fo:block>      </fo:table-cell>      <fo:table-cell padding="2px" text-align="right">       <fo:block>VATCode</fo:block>      </fo:table-cell>      <fo:table-cell padding="2px" text-align="right">       <fo:block>Discount</fo:block>      </fo:table-cell>      <fo:table-cell padding="2px" text-align="right">       <fo:block>Value</fo:block>      </fo:table-cell>      </fo:table-row>      </fo:table-header>      <fo:table-body>      <xsl:apply-templates select="//ROOT/Invoice/HireItem"/>      </fo:table-body>      </fo:table> 

This creates the header record for the list of items that have been hired by the customer on this particular contract. The apply-templates section creates and formats each of the records as cells of table shown in the previous code.

The XSLT code is shown here:

      <xsl:template match="//ROOT/Invoice/HireItem">       <fo:table-row background-color="white" width="100%">        <fo:table-cell      border-left-style="solid"                      padding="2px"                      border-right-style="solid">         <fo:block>          <xsl:value-of select="ProductCode"/>         </fo:block>        </fo:table-cell>        <fo:table-cell      border-left-style="solid"                      padding="2px"                      border-right-style="solid">         <fo:block>          <xsl:value-of select="Description"/>         </fo:block>        </fo:table-cell>        <fo:table-cell      border-left-style="solid"                      padding="2px"                      border-right-style="solid">         <fo:block>          <xsl:value-of select="FromDate"/>         </fo:block>        </fo:table-cell>        <fo:table-cell      border-left-style="solid"                      padding="2px"                      border-right-style="solid">          <fo:block>           <xsl:value-of select="ToDate"/>          </fo:block>         </fo:table-cell>         <fo:table-cell      border-left-style="solid"                       padding="2px"                       border-right-style="solid">          <fo:block>           <xsl:value-of select="Weeks"/>          </fo:block>         </fo:table-cell>         <fo:table-cell      border-left-style="solid"                       padding="2px"                       border-right-style="solid"                       text-align="right">          <fo:block>           <xsl:value-of select="Rate"/>          </fo:block>         </fo:table-cell>         <fo:table-cell      border-left-style="solid"                       padding="2px"                       border-right-style="solid"                       text-align="right">          <fo:block>           <xsl:value-of select="Quantity"/>          </fo:block>               </fo:table-cell>         <fo:table-cell        border-left-style="solid"                       padding="2px"                       border-right-style="solid"                       text-align="right">         <fo:block>          <xsl:value-of select="VATCode"/>         </fo:block>        </fo:table-cell>        <fo:table-cell         border-left-style="solid"                      padding="2px"                      border-right-style="solid"                      text-align="right">         <fo:block>          <xsl:value-of select="Discount"/>         </fo:block>        </fo:table-cell>        <fo:table-cell         border-left-style="solid"                      padding="2px"                      border-right-style="solid"                      text-align="right">         <fo:block>          <xsl:value-of select="Value"/>         </fo:block>        </fo:table-cell>        </fo:table-row>      </xsl:template> 

If this code is included with the previous XSLT code, the PDF in Figure 4-14 is produced.

image from book
Figure 4-14

You created the header row of the hire items table as a fo:table-header because the number of records may cause the table to be rendered on several pages. If the number of hire items requires a new page, the header row is rendered on the second page.

If you add a sufficient number of hire items to the source XML, the rendering of the second page is shown in Figure 4-15.

image from book
Figure 4-15

Any rows included under the fo:table-header element are rendered by default on each page of the table. The same concept applies to any records contained in a fo:table-footer element. This can be overridden by using the table-omit-header-at-break and the table-omit-footer-at-break attributes of a fo:table element. If you set the values of these attributes to true, you stop the rendering of the header record and any footer records on any page other than the start page (in the case of the header) or last page (in the case of the footer) of the table.

The same XML source rendered with the table-omit-header-at-break attribute set to true results in the second page being rendered as shown in Figure 4-16.

image from book
Figure 4-16

Next, you want to render the sale items in a table similar to the hire items. The columns are slightly different but the mechanism to render them is mainly the same.

The XSLT code to create and setup the columns for the sale item table is shown here:

      <fo:table>       <fo:table-column padding="2px" padding-top="5px" column-width="10cm"/>       <fo:table-column padding="2px" column-width="5cm"/>       <fo:table-column padding="2px" column-width="3cm"/>       <fo:table-column padding="2px" column-width="3cm"/>       <fo:table-column padding="2px" column-width="3cm"/>       <fo:table-column padding="2px" column-width="3cm"/>       <fo:table-header border-style="solid">        <fo:table-row      background-color="white"                           border-style="none">         <fo:table-cell number-columns-spanned="6">          <fo:block font-family="serif" font-weight="bold" font-size="20pt">            Sale Items          </fo:block>         </fo:table-cell>        </fo:table-row>        <fo:table-row background-color="silver">          <fo:table-cell>            <fo:block>Description</fo:block>          </fo:table-cell>          <fo:table-cell>           <fo:block>Date</fo:block>          </fo:table-cell>          <fo:table-cell>           <fo:block>Quantity</fo:block>          </fo:table-cell>          <fo:table-cell>           <fo:block>Price</fo:block>          </fo:table-cell>          <fo:table-cell>           <fo:block>VATCode</fo:block>          </fo:table-cell>          <fo:table-cell>           <fo:block>Value</fo:block>          </fo:table-cell>         </fo:table-row>        </fo:table-header>        <fo:table-body>         <xsl:apply-templates select="//ROOT/Invoice/SaleItem"/>        </fo:table-body>       </fo:table> 

The apply-templates section for the sale item rows is again very much like the hire items table and is shown here.

      <xsl:template match="//ROOT/Invoice/SaleItem">       <fo:table-row    keep-with-next="always"               background-color="white"               width="100%">        <fo:table-cell         border-left-style="solid"                      padding="2px"                      border-right-style="solid">         <fo:block>          <xsl:value-of select="Description"/>         </fo:block>        </fo:table-cell>        <fo:table-cell       border-left-style="solid"                      padding="2px"                      border-right-style="solid">         <fo:block>          <xsl:value-of select="Date"/>         </fo:block>        </fo:table-cell>        <fo:table-cell        border-left-style="solid"                      padding="2px"                      border-right-style="solid">         <fo:block>                <xsl:value-of select="Quantity"/>         </fo:block>        </fo:table-cell>        <fo:table-cell        border-left-style="solid"                      padding="2px"                      border-right-style="solid">         <fo:block>          <xsl:value-of select="Price"/>         </fo:block>        </fo:table-cell>        <fo:table-cell       border-left-style="solid"                      padding="2px"                      border-right-style="solid">         <fo:block>          <xsl:value-of select="VATCode"/>         </fo:block>        </fo:table-cell>        <fo:table-cell         border-left-style="solid"                      padding="2px"                      border-right-style="solid">         <fo:block>          <xsl:value-of select="Value"/>         </fo:block>        </fo:table-cell>       </fo:table-row>      </xsl:template> 

We have introduced one major difference in the rendering of the table. The row attribute keep-with-next has been used and set to the value always. This means that where possible, the rows of the table are rendered in the same area (in most cases, in the same page). If this attribute is omitted, the sale items table is rendered directly after the hire items table regardless of whether all the records in the sales item data set can be displayed on the same page. Figure 4-17 shows this scenario.

image from book
Figure 4-17

Setting keep-with-next to true means that the sales item table split in Figure 4-17 is (where possible) rendered in a contiguous area.

Finally, you want to create a summary table giving the total values for the invoice. The table does not introduce any new features, but the code is presented to complete the example.

      <fo:table       display-align="after"               text-align="right"               font-family="serif"               font-weight="bold"               font-size="18pt">       <fo:table-header border-style="none">        <fo:table-row  background-color="white"                      border-style="none">         <fo:table-cell       number-columns-spanned="2"                      text-align="left"                      padding-top="10px">          <fo:block   font-family="serif"               font-weight="bold"               font-size="20pt">       Invoice Totals          </fo:block>         </fo:table-cell>        </fo:table-row>       </fo:table-header>       <fo:table-column padding="2px" column-width="23cm"/>       <fo:table-column padding="2px" column-width="4cm"/>       <fo:table-body>        <fo:table-row border-style="none">         <fo:table-cell padding-top="5px" padding-right="10px">          <fo:block>Net</fo:block>         </fo:table-cell>         <fo:table-cell padding-top="5px" background-color="silver">          <fo:block>&#x00A3;<xsl:value-of select="//ROOT/Invoice/PreVATTotal"/></fo:block>         </fo:table-cell>        </fo:table-row>        <fo:table-row>         <fo:table-cell padding-right="10px">          <fo:block>VAT</fo:block>         </fo:table-cell>        <fo:table-cell background-color="silver">         <fo:block>£<xsl:value-of select="//ROOT/Invoice/VAT1"/>         </fo:block>        </fo:table-cell>       </fo:table-row>       <fo:table-row>         <fo:table-cell padding-right="10px">          <fo:block>Total</fo:block>         </fo:table-cell>         <fo:table-cell background-color="silver">          <fo:block>£<xsl:value-of select="//ROOT/Invoice/InvoiceTotal"/></fo:block>         </fo:table-cell>        </fo:table-row>       </fo:table-body>      </fo:table> 

The example in full is available for download from this books companion Web site. It renders as shown in Figure 4-18

image from book
Figure 4-18




Professional XML
Professional XML (Programmer to Programmer)
ISBN: 0471777773
EAN: 2147483647
Year: 2004
Pages: 215

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