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

Lesson: Page Layout

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

Lesson Goals

  • How to layout simple pages.
  • How to create different layouts for odd and even pages.
  • How to create blank pages.

Page Masters

As we have seen, templates are created with page masters using the <fo:simple-page-master> tag. The page can be divided into up to five regions. All of these regions, except region-body, generally contain static content.

Simple Layout

Let's take a look at the simple page layout that we saw earlier in the course.

The simple page master that creates this layout is shown in the code sample below.

Code Sample:

PageLayout/Demos/SimplePageMaster.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="page" 
			page-height="11in" page-width="8.5in">
			<fo:region-body margin="1in" background-color="yellow" 
				border="solid thick orange"/>
			<fo:region-before extent="1in" background-color="lightblue" 
				border="solid thick blue"/>
			<fo:region-after extent="1in" background-color="lightblue" 
				border="solid thick blue"/>
			<fo:region-start extent="1in" background-color="lightgreen" 
				border="solid thick green"/>
			<fo:region-end extent="1in" background-color="lightgreen" 
				border="solid thick green"/>
		</fo:simple-page-master>
	</fo:layout-master-set>
	<fo:page-sequence master-reference="page" font-size="24pt" 
		font-weight="bold" text-align="center">
---- C O D E   O M I T T E D ----
</fo:page-sequence>
</fo:root>

fo:simple-page-master

The fo:simple-page-master is used to specify the name of the master page, the height and width of the page, the margins of the entire page and the orientation of the page (e.g, portrait or landscape). Its most common attributes are shown below.

<fo:simple-page-master> Attributes
Attribute Description
master-name the name of the master page
page-height the height of the page
page-width the width of the page
margin the size of the margin around the entire page
margin-top the size of the top margin
margin-right the size of the right margin
margin-bottom the size of the bottom margin
margin-left the size of the left margin
reference-orientation sets the direction for page

Most of these attributes are self explanatory. However, we should take a closer look at reference-orientation.

Reference Orientation

The reference-orientation attribute takes a number which indicates the number of degrees to rotate the orientation. Possible values are 0, 90, 180, 270, -90, -180, -270, and inherit. To create a landscape orientation, reference-orientation should be set to 90. The following example illustrates this.

Code Sample:

PageLayout/Demos/LandscapePageMaster.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="page" 
			page-height="11in" page-width="8.5in"
			reference-orientation="90">
			<fo:region-body margin="1in" background-color="yellow" 
				border="solid thick orange"/>
			<fo:region-before extent="1in" background-color="lightblue" 
				border="solid thick blue"/>
			<fo:region-after extent="1in" background-color="lightblue" 
				border="solid thick blue"/>
			<fo:region-start extent="1in" background-color="lightgreen" 
				border="solid thick green"/>
			<fo:region-end extent="1in" background-color="lightgreen" 
				border="solid thick green"/>
		</fo:simple-page-master>
	</fo:layout-master-set>
	<fo:page-sequence master-reference="page" font-size="24pt" 
		font-weight="bold" text-align="center">
---- C O D E   O M I T T E D ----
</fo:page-sequence>
</fo:root>

The only difference between this page and the previous one is the reference orientation. The result is shown below.

Notice that the whole page shifts, so that the region-before is now on the left rather than on the top.

fo:region-body

The <fo:region-body> tag is used to define the space, background and borders for the region-body. Its most common attributes are shown below.

Most of these attributes are self explanatory. We'll take a closer look at margin and padding.

margins and padding

We saw that the <fo:simple-page-master> tag can take margin attributes. These margins are applied to the whole page, meaning that they push all the regions inward. The margin attributes of the <fo:region-body> tag affect only region-body. They specify how far each edge of the region-body box should be from the edge of the outer box defined by the <fo:simple-page-master> tag. The padding attributes specify how far the elements contained in the body should appear from the edge of the body. The following code sample illustrates how margin and padding work.

Code Sample:

PageLayout/Demos/BodyRegionPageMaster.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="page" 
			page-height="11in" page-width="8.5in" 
			margin="1in">
			<fo:region-body margin="1in" padding="1in"
				background-color="yellow" border="solid thick orange"/>
			<fo:region-before extent="1in" background-color="lightblue" 
				border="solid thick blue"/>
			<fo:region-after extent="1in" background-color="lightblue" 
				border="solid thick blue"/>
			<fo:region-start extent="1in" background-color="lightgreen" 
				border="solid thick green"/>
			<fo:region-end extent="1in" background-color="lightgreen" 
				border="solid thick green"/>
		</fo:simple-page-master>
	</fo:layout-master-set>
	<fo:page-sequence master-reference="page" font-size="24pt" 
		font-weight="bold" text-align="center">
---- C O D E   O M I T T E D ----
</fo:page-sequence>
</fo:root>

The result is shown below.

  • The margin specified in the <fo:simple-page-master> tag creates the white area.
  • The margin specified in the <fo:region-body> tag forces the region-body edges in one inch from the simple-page-master rectangle.
  • The padding specified in the <fo:region-body> tag creates space between the content of the region-body and the edges of the region-body.

Note that the positioning and size of region-body are not affected in any way by the attributes of the other regions.

fo:region-before, fo:region-after, fo:region-start, and fo:region-end

The four other region tags take all the same attributes as <fo:region-body> except for the margin attributes. These regions do not have margins. They always sit on the edge of the simple-page-master rectangle. In addition, these region tags take two other attributes: extent and precedence.

The extent attribute specifies the width of region-start and region-end and the height of region-before and region-after (assuming a portrait layout).

The precedence attribute specifies which regions should sit on top. As you can see from the examples we have looked at thus far, by default region-start and region-end take precedence over region-before and region-after. The following code sample shows how to change this.

Code Sample:

PageLayout/Demos/PrecedencePageMaster.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="page" 
			page-height="11in" page-width="8.5in" 
			margin="1in">
			<fo:region-body margin="1in" padding="1in"
				background-color="yellow" border="solid thick orange"/>
			<fo:region-before extent="1in" precedence="true"
				background-color="lightblue" border="solid thick blue"/>
			<fo:region-after extent="1in" precedence="true"
				background-color="lightblue" border="solid thick blue"/>
			<fo:region-start extent="1in" background-color="lightgreen" 
				border="solid thick green"/>
			<fo:region-end extent="1in" background-color="lightgreen" 
				border="solid thick green"/>
		</fo:simple-page-master>
	</fo:layout-master-set>
	<fo:page-sequence master-reference="page" font-size="24pt" 
		font-weight="bold" text-align="center">
---- C O D E   O M I T T E D ----
</fo:page-sequence>
</fo:root>

The result is shown below. As you can see, region-before and region-after now sit on top of region-start and region-end.

page-sequence-master

The layout-master-set FO takes two child FOs, one of which is simple-page-master, which we've already seen. The other is page-sequence-master, which is used to specify the sequence of simple-page-masters to be used. The page-sequence-master FO takes three possible children:

  • single-page-master-reference - used to create a template for a single page (e.g, a cover page).
  • repeatable-page-master-reference - used to create a template to be used for n number of pages, where n is set with the maximum-repeats attribute.
  • repeatable-page-master-alternatives - used to create different templates for pages depending on where they fall in the document (e.g, first page, even pages, odd pages, blank pages, last even page, and last odd page).

Creating a Cover Page

A cover page is created using single-page-master-reference as shown in the example below.

Code Sample:

PageLayout/Demos/CoverPage.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="Cover" margin="1in" page-height="11in" page-width="8.5in">
			<fo:region-body margin-top="2in"/>
		</fo:simple-page-master>
		<fo:page-sequence-master master-name="CoverPage">
			<fo:single-page-master-reference master-reference="Cover"/>
		</fo:page-sequence-master>
	</fo:layout-master-set>
	<fo:page-sequence master-reference="CoverPage">
		<fo:flow flow-name="xsl-region-body">
			<fo:block text-align="center" font-weight="bold" font-size="1in">
				Cover Page
			</fo:block>
		</fo:flow>
	</fo:page-sequence>
</fo:root>

Odd and Even Pages

To create alternating odd and even pages, you follow these steps:

  1. Create a simple-page-master for the odd pages.
  2. Create a simple-page-master for the even pages.
  3. Create a page-sequence-master that references the two simple-page-masters.

The following sample illustrates this.

Code Sample:

PageLayout/Demos/OddEven.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="StoriesOdd" 
			page-height="11in" page-width="8.5in" margin=".5in">
			<fo:region-body margin=".5in" />
			<fo:region-before region-name="oddHead" extent=".5in" />
			<fo:region-after region-name="oddFoot" extent=".5in" />
		</fo:simple-page-master>
		
		<fo:simple-page-master master-name="StoriesEven" 
			page-height="11in" page-width="8.5in" margin=".5in">
			<fo:region-body margin=".5in" />
			<fo:region-before region-name="evenHead" extent=".5in" />
			<fo:region-after region-name="evenFoot" extent=".5in" />
		</fo:simple-page-master>
		
		<fo:page-sequence-master master-name="Stories">
			<fo:repeatable-page-master-alternatives>
				<fo:conditional-page-master-reference 
					master-reference="StoriesOdd" odd-or-even="odd" />
				<fo:conditional-page-master-reference 
					master-reference="StoriesEven" odd-or-even="even" />
			</fo:repeatable-page-master-alternatives>
		</fo:page-sequence-master>
	</fo:layout-master-set>
	
	<fo:page-sequence master-reference="Stories">
	
		<fo:static-content flow-name="oddHead">
			<fo:block border-bottom-width="thin" 
				border-bottom-style="solid" border-bottom-color="green" 
				font-weight="bold" text-align="outside">
					Story Title
				</fo:block>
		</fo:static-content>
		
		<fo:static-content flow-name="evenHead">
			<fo:block border-bottom-width="thin" 
				border-bottom-style="solid" border-bottom-color="green" 
				font-weight="bold" text-align="outside">
					Book of Stories
				</fo:block>
		</fo:static-content>
		
		<fo:static-content flow-name="oddFoot">
			<fo:block border-bottom-width="thin" 
				border-bottom-style="solid" border-bottom-color="green" 
				font-weight="bold" text-align="outside">
				<fo:page-number /> |
			</fo:block>
		</fo:static-content>
		
		<fo:static-content flow-name="evenFoot">
			<fo:block border-bottom-width="thin" 
				border-bottom-style="solid" border-bottom-color="green" 
				font-weight="bold" text-align="outside">
				| <fo:page-number />
			</fo:block>
		</fo:static-content>
		
		<fo:flow flow-name="xsl-region-body">
---- C O D E   O M I T T E D ----
</fo:flow>
		
	</fo:page-sequence>
</fo:root>

Let's examine the code step by step.

  1. At the top of the page, two simple-page-masters are created:
    <fo:simple-page-master master-name="StoriesOdd" page-height="11in" page-width="8.5in" margin=".5in"> <fo:region-body margin=".5in" /> <fo:region-before region-name="oddHead" extent=".5in" /> <fo:region-after region-name="oddFoot" extent=".5in" /> </fo:simple-page-master> <fo:simple-page-master master-name="StoriesEven" page-height="11in" page-width="8.5in" margin=".5in"> <fo:region-body margin=".5in" /> <fo:region-before region-name="evenHead" extent=".5in" /> <fo:region-after region-name="evenFoot" extent=".5in" /> </fo:simple-page-master>
    • Notice that <fo:region-before> and <fo:region-after> have region-name attributes. This is so that we can distinguish between these regions on odd pages and even pages.
  2. The next step is to create a page-sequence-master.
    <fo:page-sequence-master master-name="Stories"> <fo:repeatable-page-master-alternatives> <fo:conditional-page-master-reference master-reference="StoriesOdd" odd-or-even="odd" /> <fo:conditional-page-master-reference master-reference="StoriesEven" odd-or-even="even" /> </fo:repeatable-page-master-alternatives> </fo:page-sequence-master>
    As it sounds, a page-sequence-master is used for defining page sequences.
    • In this case, we want the sequence to be repeatable (e.g, odd, even, odd, even, odd, etc.). The <fo:repeatable-page-master-alternatives> tag takes care of this.
    • We also want to make sure the "StoriesOdd" simple-page-master is used for odd pages and the "StoriesEven" simple-page-master is used for even pages. This is handled with the <fo:conditional-page-master-reference> tags.
    • We name this page-sequence-master "Stories".
  3. In our page-sequence, we see four fo:static-content elements and one fo:flow element. There is nothing special about the fo:flow element; it contains the body content, which appears the same on every page. The fo-static-content elements, however, are different than the ones we've used thus far in that there are two for region-before: "oddHead" and "evenHead", and two for region-after: "oddFoot" and "evenFoot". The "oddHead" and "oddFoot" regions are defined in the "StoriesOdd" simple-page-master and the "evenHead" and "evenFoot" regions are defined in the "StoriesEven" simple-page-master. The only difference between the odd pages and the even pages is the content.
    <fo:page-sequence master-reference="Stories"> <fo:static-content flow-name="oddHead"> <fo:block border-bottom-width="thin" border-bottom-style="solid" border-bottom-color="green" font-weight="bold" text-align="outside"> Story Title </fo:block> </fo:static-content> <fo:static-content flow-name="evenHead"> <fo:block border-bottom-width="thin" border-bottom-style="solid" border-bottom-color="green" font-weight="bold" text-align="outside"> Book of Stories </fo:block> </fo:static-content> <fo:static-content flow-name="oddFoot"> <fo:block border-bottom-width="thin" border-bottom-style="solid" border-bottom-color="green" font-weight="bold" text-align="outside"> | <fo:page-number /> </fo:block> </fo:static-content> <fo:static-content flow-name="evenFoot"> <fo:block border-bottom-width="thin" border-bottom-style="solid" border-bottom-color="green" font-weight="bold" text-align="outside"> <fo:page-number /> | </fo:block> </fo:static-content> <!--fo:flow OMITTED--> </fo:page-sequence>

The first two pages of the output are shown below:

Blank Pages

The output document created in the last example contains three stories. The way the FO page is currently written, the stories run one after the other with no break. We may want to force a break between the stories. Furthermore, we might want to make sure that each story starts on an odd page.

break-before and break-after

Breaks can be forced with the break-before and break-after attributes, which can appear in any block-level element. Possible values are shown below.

  • auto - does nothing.
  • column - starts a new column.
  • page - starts a new page.
  • odd-page - starts a new odd page, possibly resulting in a blank even page.
  • even-page - starts a new even page, possibly resulting in a blank odd page.

In our case, we want to add break-before attributes to the elements containing the story titles.

The revised code is shown below. Open PageLayout/Demos/OddEvenBreak.pdf to see the output.

Code Sample:

PageLayout/Demos/OddEvenBreak.fo
<?xml version="1.0" encoding="UTF-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
	<fo:layout-master-set>
---- C O D E   O M I T T E D ----
</fo:layout-master-set>
	
	<fo:page-sequence master-reference="Stories">
---- C O D E   O M I T T E D ----
<fo:flow flow-name="xsl-region-body">
			<fo:block font-weight="bold" font-size="larger" break-before="odd-page">
				POOH GOES VISITING - A.A. Milne
			</fo:block>
---- C O D E   O M I T T E D ----
<fo:block font-weight="bold" font-size="larger" break-before="odd-page">
				A MAD TEA-PARTY - Lewis Carroll
			</fo:block>
---- C O D E   O M I T T E D ----
<fo:block font-weight="bold" font-size="larger" break-before="odd-page">
				THE NAUGHTY BOY - Hans Christian Andersen
			</fo:block>
---- C O D E   O M I T T E D ----
</fo:flow>
		
	</fo:page-sequence>
</fo:root>

Creating the Page Layout with XSLT

Duration: 20 to 25 minutes.

In this exercise, you will create an XSLT (virtually from scratch) that transforms PageLayout/Exercises/Stories.xml into a FO page identical to PageLayout/Demos/OddEvenBreak.fo.

  1. Open and examine PageLayout/Exercises/Stories.xml.
  2. Open PageLayout/Exercises/Stories.xsl for editing.
  3. Add code to transform Stories.xml to create a FO that will render a PDF like PageLayout/Demos/OddEvenBreak.pdf.

Solution:

PageLayout/Solutions/Stories.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="/">
	<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
		<fo:layout-master-set>
		
		<fo:simple-page-master master-name="StoriesOdd" 
			page-height="11in" page-width="8.5in" margin=".5in">
			<fo:region-body margin=".5in" />
			<fo:region-before region-name="oddHead" extent=".5in" />
			<fo:region-after region-name="oddFoot" extent=".5in" />
		</fo:simple-page-master>
		
		<fo:simple-page-master master-name="StoriesEven" 
			page-height="11in" page-width="8.5in" margin=".5in">
			<fo:region-body margin=".5in" />
			<fo:region-before region-name="evenHead" extent=".5in" />
			<fo:region-after region-name="evenFoot" extent=".5in" />
		</fo:simple-page-master>
		
		<fo:page-sequence-master master-name="Stories">
			<fo:repeatable-page-master-alternatives>
				<fo:conditional-page-master-reference 
					master-reference="StoriesOdd" odd-or-even="odd" />
				<fo:conditional-page-master-reference 
					master-reference="StoriesEven" odd-or-even="even" />
			</fo:repeatable-page-master-alternatives>
		</fo:page-sequence-master>
			
		</fo:layout-master-set>
		
		<fo:page-sequence master-reference="Stories">
			<fo:static-content flow-name="oddHead">
				<fo:block border-bottom-width="thin" 
					border-bottom-style="solid" border-bottom-color="green"
					font-weight="bold" text-align="outside">
					Story Title
				</fo:block>
			</fo:static-content>
			<fo:static-content flow-name="evenHead">
				<fo:block border-bottom-width="thin" 
					border-bottom-style="solid" border-bottom-color="green"
					font-weight="bold" text-align="outside">
					Book of Stories
				</fo:block>
			</fo:static-content>
			<fo:static-content flow-name="oddFoot">
				<fo:block border-bottom-width="thin" 
					border-bottom-style="solid" border-bottom-color="green"
					font-weight="bold" text-align="outside">
					<fo:page-number/> |
				</fo:block>
			</fo:static-content>
			<fo:static-content flow-name="evenFoot">
				<fo:block border-bottom-width="thin" 
					border-bottom-style="solid" border-bottom-color="green"
					font-weight="bold" text-align="outside">
					| <fo:page-number/>
				</fo:block>
			</fo:static-content>
			<fo:flow flow-name="xsl-region-body">
				<xsl:apply-templates/>
			</fo:flow>
		</fo:page-sequence>
	</fo:root>
</xsl:template>
<xsl:template match="body/div/h2">
	<fo:block font-weight="bold" font-size="larger" break-before="odd-page">
		<xsl:value-of select="."/>
	</fo:block>
</xsl:template>
<xsl:template match="body/div/p">
	<fo:block margin-top="12pt">
		<xsl:apply-templates/>
	</fo:block>
</xsl:template>
<xsl:template match="head | h1 | a"/>
</xsl:stylesheet>

Columns

Creating a column-layout in FO is very easy. Simply add a column-count attribute to the relevant region-body FO. You control the width between columns with the column-gap attribute. The example below illustrates this.

Code Sample:

PageLayout/Demos/Columns.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="StoriesOdd" 
			page-height="11in" page-width="8.5in" margin=".5in" reference-orientation="90">
			<fo:region-body margin=".5in" column-count="3"  column-gap=".5in"/>
			<fo:region-before region-name="oddHead" extent=".5in" />
			<fo:region-after region-name="oddFoot" extent=".5in" />
		</fo:simple-page-master>
		
		<fo:simple-page-master master-name="StoriesEven" 
			page-height="11in" page-width="8.5in" margin=".5in" reference-orientation="90">
			<fo:region-body margin=".5in" column-count="3" column-gap=".5in"/>
			<fo:region-before region-name="evenHead" extent=".5in" />
			<fo:region-after region-name="evenFoot" extent=".5in" />
		</fo:simple-page-master>
---- C O D E   O M I T T E D ----
<fo:flow flow-name="xsl-region-body">
			<fo:block font-weight="bold" font-size="larger" break-before="column">
				POOH GOES VISITING - A.A. Milne
			</fo:block>
---- C O D E   O M I T T E D ----
<fo:block font-weight="bold" font-size="larger" break-before="column">
				A MAD TEA-PARTY - Lewis Carroll
			</fo:block>
---- C O D E   O M I T T E D ----
<fo:block font-weight="bold" font-size="larger" break-before="column">
				THE NAUGHTY BOY - Hans Christian Andersen
			</fo:block>
---- C O D E   O M I T T E D ----
</fo:flow>
		
	</fo:page-sequence>
</fo:root>

The output is shown in PageLayout/Demos/Columns.pdf. Notice that each story title begins at the beginning of a column. We forced this by setting the break-before attribute of the block containing the title to "column". We also set the reference-orientation of the simple-page-master FO to "90" to create a landscape layout.