facebook google plus twitter
Webucator's Free Introduction to XSL-FO Tutorial

Lesson: Formatting Objects Basics

Welcome to our free Introduction to XSL-FO tutorial. This tutorial is based on Webucator's Introduction to XSL-FO course.

Lesson Goals

  • To understand how XSL-FO works.
  • To do a simple XSL-FO transform.
  • To add flowing and static content to an XSL-FO document.

The purpose of XSL-FO

The purpose of XSL-FO is to provide a mechanism for formatting XML data for print, screen and other output media. XSL-FO, also known simply as XSL, is a specification of the World Wide Web Consortium and is closely related to XSLT. However, whereas XSLT is most often used for transforming XML into HTML or other XML structures, XSL-FO is most often used for formatting XML for print.

How XSL-FO Works

Transforming XML for print is accomplished by transforming an XML document to a Formatting Objects (FO) document, which itself is XML-based, via XSLT. The formatting objects processor is able to read the FO document and transform it for different types of print output. The most common and best supported print output is currently Adobe PDF.

In a moment, we'll take a look at our first FO document, but first let's look at how FO sees a page.

Note that the XSL specification requires the border-width to be 0 on all regions. However, RenderX's XEP and Antenna House both support the ability to put borders on regions.

As you can see, the page is divided into regions. The main region is the region-body. The regions above and below the body are called region-before and region-after and the regions to the right and left of the body are called region-start and region-end. We will examine page layout more closely in the Page Layout lesson.

XSL-FO Document Parts

An XSL-FO document is divided into two main parts:

  1. One layout master set (layout-master-set), which specifies one or more page master templates (simple-page-master) to be used in the document.
  2. One or more page sequences, which contain the content of the document.

The following code sample shows how the page is broken up.

Code Sample:

FoBasics/Demos/PageStructure.fo
<?xml version="1.0" encoding="utf-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">

  <!--
	  Define page templates using simple-page-master 
	  formatting objects inside of a layout master set.
	  These templates are used to specify the basic rules
	  for content layout; for example, the page margin size.
	-->
  
  <!--
	  Define page sequences, which use the templates
	  defined above.  These page sequences contain the
	  actual content to be output.
	-->
  
</fo:root>

An XSL-FO Example

Let's now look at a complete, though simple, FO document.

Code Sample:

FoBasics/Demos/HelloWorld.fo
<?xml version="1.0" encoding="utf-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">

  <fo:layout-master-set>
    <fo:simple-page-master master-name="hello"
				page-height="11in"  page-width="8.5in" margin-top="1in" 
				margin-bottom="1in" margin-left="1in" margin-right="1in">
      <fo:region-body margin-top="1in" margin-bottom=".5in"/>
    </fo:simple-page-master>
  </fo:layout-master-set>
  
  <fo:page-sequence master-reference="hello">
    <fo:flow flow-name="xsl-region-body">
      <fo:block font-size="18pt" text-align="center" font-weight="bold">
        Hello World!
      </fo:block>
    </fo:flow>
  </fo:page-sequence>
  
</fo:root>

The output is shown below.

All this simply to output "Hello World!"??? Yup. With FO, you need to do a little work to get things started. Let's break this code down.

The Root Element: root

The first thing to note is that this is an XML document with a root element of root with the namespace http://www.w3.org/1999/XSL/Format.

layout-master-set and simple-page-master

The layout-master-set formatting object (FO) holds simple-page-master FOs, which define page templates.

<fo:layout-master-set> <fo:simple-page-master master-name="hello" page-height="11in" page-width="8.5in" margin-top="1in" margin-bottom="1in" margin-left="1in" margin-right="1in"> <fo:region-body margin-top="1in" margin-bottom=".5in"/> </fo:simple-page-master> </fo:layout-master-set>

In this case, there is only one template and it is named "hello". The page height, width and margins are all defined in the simple-page-master FO.

Regions

The simple-page-master FO contains region settings in the form of the following child FOs.

  • region-body
  • region-start
  • region-end
  • region-before
  • region-after

The locations of these regions are shown on the diagram under How XSL-FO Works. Our simple "Hello-World" document only uses the region-body.

page-sequence

The layout-master-set FO is followed by one or more page-sequence FOs, which must reference one of the simple-page-master templates by name. The page-sequence FOs contain the contents of the page or pages.

<fo:page-sequence master-reference="hello"> <!--Page contents--> </fo:page-sequence>

Notice that the value of the master-reference attribute matches the master-name defined in the simple-page-master FO.

flow and static-content

The contents of the page inside the page-sequence FO are either static, meaning they appear the same on each page or flowing, meaning they flow from one page to the next. Static content is contained in static-content FOs and flowing content is contained in flow FOs.

<fo:page-sequence master-reference="hello"> <fo:flow flow-name="xsl-region-body"> <!--flowing content--> </fo:flow> </fo:page-sequence>

This example only has a flow FO. In most cases, the region-body has flowing content and the other regions have static content.

block

Paragraphs, lists and other elements that generally are vertically separated with whitespace are called blocks. The block FO is used for separating and formatting generic blocks, such as paragraphs. In our "Hello-World" example, there is only one block, which contains the text "Hello World!" and sets its font size, font weight and alignment via attributes.

You may notice that the formatting properties of the block FO are Cascading Style Sheets properties. Formatting Objects use CSS for display properties, so, if you use CSS in web design, you’ll find that most of your knowledge is transferrable to XSL-FO.

<fo:page-sequence master-reference="hello"> <fo:flow flow-name="xsl-region-body"> <fo:block font-size="18pt" text-align="center" font-weight="bold"> Hello World! </fo:block> </fo:flow> </fo:page-sequence>

Note that blocks can contain nested blocks, which can contain nested blocks, and so on.

Using XSLT to Create FO

Duration: 15 to 25 minutes.

In this exercise, you will generate the FO document we have been working with in this lesson from an XML document using XSLT.

  1. Open FoBasics/Exercises/HelloWorld.xml. You will see that it is a very simple XML document.
  2. Open FoBasics/Exercises/HelloWorld.xsl for editing.
  3. Finish this XSLT document so that it transforms FoBasics/Exercises/HelloWorld.xml to output the equivalent of FoBasics/Demos/HelloWorld.fo shown earlier.
  4. To test your solution, transform FoBasics/Exercises/HelloWorld.xml against FoBasics/Exercises/HelloWorld.xsl and then run the result through an FO engine.

Solution:

FoBasics/Solutions/HelloWorld.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
			xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:output method="xml" indent="yes"/>
	<xsl:template match="/Document">
		<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">

		<fo:layout-master-set>
			<fo:simple-page-master master-name="hello"
						  page-height="11in"  page-width="8.5in" margin-top="1in" 
						  margin-bottom="1in" margin-left="1in" margin-right="1in">
				<fo:region-body margin-top="1in" margin-bottom=".5in"/>
			</fo:simple-page-master>
		</fo:layout-master-set>
		  
		<fo:page-sequence master-reference="hello">
			<fo:flow flow-name="xsl-region-body">
				<fo:block font-size="18pt" text-align="center" font-weight="bold">
					<xsl:value-of select="Heading"/>
				</fo:block>
			</fo:flow>
		</fo:page-sequence>
	  
		</fo:root>
	</xsl:template>
</xsl:stylesheet>
            

Static Content

Static content is added to page sequences with the static-content FO. Generally, static content is used in regions other than the body region. The example below adds a header and a footer to our "Hello-World" document.

Code Sample:

FoBasics/Demos/HelloWorld-Static.fo
<?xml version="1.0" encoding="utf-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">

	<fo:layout-master-set>
		<fo:simple-page-master master-name="hello" page-height="11in" 
			page-width="8.5in" margin-top="1in" margin-bottom="1in" 
			margin-left="1in" margin-right="1in">
			<fo:region-body margin-top="1in" margin-bottom=".5in"/>
			<fo:region-before extent=".5in" background-color="silver"/>
			<fo:region-after extent=".5in" background-color="silver"/>
		</fo:simple-page-master>
	</fo:layout-master-set>
	
	<fo:page-sequence master-reference="hello">
		<fo:static-content flow-name="xsl-region-before">
			<fo:block font-size="24pt">Hello World!</fo:block>
		</fo:static-content>
		<fo:static-content flow-name="xsl-region-after">
			<fo:block font-size="24pt" text-align="right">
				Page <fo:page-number/> of <fo:page-number-citation ref-id="last-page"/>
			</fo:block>
		</fo:static-content>
		<fo:flow flow-name="xsl-region-body">
			<fo:block font-size="36pt" text-align="center" font-weight="bold">
				Hello World!
			</fo:block>
			<fo:block id="last-page"/>
		</fo:flow>
	</fo:page-sequence>
	
</fo:root>

The above code results in the following output.

Let's examine the new FOs we have added.

region-before and region-after

The region-before and region-after FOs have been added to the simple-page-master FO.

<fo:simple-page-master master-name="hello" page-height="11in" page-width="8.5in" margin-top="1in" margin-bottom="1in" margin-left="1in" margin-right="1in"> <fo:region-body margin-top="1in" margin-bottom=".5in"/> <fo:region-before extent=".5in" background-color="silver"/> <fo:region-after extent=".5in" background-color="silver"/> </fo:simple-page-master>

These FOs effectively create header and footer regions. The extent attribute specifies the header and footer height. In both cases, it is set to .5 inches. The background-color attribute specifies the background color.

static-content

<fo:static-content flow-name="xsl-region-before"> <fo:block font-size="12pt">Hello World!</fo:block> </fo:static-content><fo:static-content flow-name="xsl-region-after"> <fo:block font-size="12pt" text-align="right"> Page <fo:page-number/> of <fo:page-number-citation ref-id="last-page"/> </fo:block> </fo:static-content>

The static-content FO is used to add content that will repeat for every page in the current page sequence. The flow-name attribute specifies the region in which this content will appear.

In this case, the first static-content causes the text "Hello World!" to be added to the header of every page in this sequence. The second static-content causes a right-aligned page number to be added to the footer of every page. Let's look at that one a little more closely.

<fo:static-content flow-name="xsl-region-after"> <fo:block font-size="12pt" text-align="right"> Page <fo:page-number/> of <fo:page-number-citation ref-id="last-page"/> </fo:block> </fo:static-content>

The page-number FO is pretty straightforward. It simply inserts the current page number. The page-number-citation FO is used to reference the page number of an FO on another page by its unique id, in this case "last-page". If you look further down in the document, you'll see <fo:block id="last-page"/> at the bottom of the region-body flow. The purpose of placing this FO there is to mark the last page of the document.

Creating a Multi-page Document

Duration: 20 to 30 minutes.

In this exercise, you will use what we have covered so far to create a multi-page FO document from an XML document.

  1. Open FoBasics/Exercises/WinnieThePooh.xml and examine its structure.
  2. Open FoBasics/Exercises/WinnieThePooh.xsl for editing.
  3. Modify the document so that it produces a document like FoBasics/Exercises/WinnieThePooh.pdf.
  4. To test your solution, transform FoBasics/Exercises/WinnieThePooh.xml against FoBasics/Exercises/WinnieThePooh.xsl.

Code Sample:

FoBasics/Exercises/WinnieThePooh.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
			xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
			xmlns:fo="http://www.w3.org/1999/XSL/Format">
	<xsl:output method="xml" indent="yes"/>
	<xsl:template match="/Document">
		<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">

		<fo:layout-master-set>
			<fo:simple-page-master master-name="all"
					  page-height="11in"  page-width="8.5in" margin-top="1in" 
					  margin-bottom="1in" margin-left="1in" margin-right="1in">
				<fo:region-body margin-top="1in" margin-bottom=".5in"/>
				
			</fo:simple-page-master>
		</fo:layout-master-set>
		  
		<fo:page-sequence master-reference="all">
			
		</fo:page-sequence>
	  
		</fo:root>
	</xsl:template>
</xsl:stylesheet>

Solution:

FoBasics/Solutions/WinnieThePooh.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
			xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
			xmlns:fo="http://www.w3.org/1999/XSL/Format">
	<xsl:output method="xml" indent="yes"/>
	<xsl:template match="/Document">
		<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">

		<fo:layout-master-set>
			<fo:simple-page-master master-name="all"
						  page-height="11in"  page-width="8.5in" margin-top="1in" 
						  margin-bottom="1in" margin-left="1in" margin-right="1in">
				<fo:region-body margin-top="1in" margin-bottom=".5in"/>
				<fo:region-before extent=".5in" background-color="silver"/>
				<fo:region-after extent=".5in" background-color="silver"/>
			</fo:simple-page-master>
		</fo:layout-master-set>
		  
		<fo:page-sequence master-reference="all">
			<fo:static-content flow-name="xsl-region-before">
				<fo:block font-size="12pt"><xsl:value-of select="Title"/></fo:block>
			</fo:static-content>
			<fo:static-content flow-name="xsl-region-after">
				<fo:block font-size="12pt" text-align="right">
					Page <fo:page-number/> of 
						<fo:page-number-citation ref-id="last-page"/>
				</fo:block>
			</fo:static-content>
			<fo:flow flow-name="xsl-region-body">
				<fo:block>
					<xsl:value-of select="Title"/>
				</fo:block>
				<fo:block>
					<xsl:value-of select="SubTitle"/>
				</fo:block>
				<xsl:apply-templates select="Body"/>
				<fo:block id="last-page"/>
			</fo:flow>
		</fo:page-sequence>
	  
		</fo:root>
	</xsl:template>
	
	<xsl:template match="Para">
		<fo:block>
			<xsl:value-of select="."/>
		</fo:block>
	</xsl:template>
	<xsl:template match="Poem">
		<fo:block>
			<xsl:apply-templates />
		</fo:block>
	</xsl:template>
	<xsl:template match="Line">
		<fo:block>
			<xsl:apply-templates/>
		</fo:block>
	</xsl:template>
</xsl:stylesheet>