If you find that the stylesheet parameters used in the DocBook stylesheets are not sufficient for meeting your page design needs, you can create your own custom page masters and use them in your documents.
Before creating custom page masters, it helps to understand the set up of the default page masters so you can see how to fit your customizations in.
In DocBook XSL-FO, a standard set of FO simple-page-master
elements are declared and used. The page masters are grouped into page classes, one for each specialized type of page such as title page or body. The six page classes are listed in the following table. Within each class, there are different page masters to handle the sequence of pages of that type, such as first, left, and right pages. There is a page-sequence-master
element for each page class to set the conditions for sequencing the group of page masters. The following is a list of the built-in page masters.
Table 13.4. DocBook XSL-FO page master names
Page Class | Used by | simple-page-master name | Description |
---|---|---|---|
titlepage | Title pages for
set , part and book elements. | titlepage-first | First title page |
titlepage-odd | Right-hand title pages | ||
titlepage-even | Left-hand title pages | ||
lot | Lists of titles, including table of contents and lists of figures, tables, examples, or procedures. | lot-first | First page of table of contents or list of titles such as figures, tables, examples or procedures that appear at front of book. |
lot-odd | Right-hand list of titles | ||
lot-even | Left-hand list of titles | ||
front | Used by these elements:
dedication preface | front-first | First page of new page sequence in book front matter, used for dedication and preface. |
front-odd | Right-hand front matter page. | ||
front-even | Left-hand front matter page. | ||
body | Used by these elements:
chapter article reference refentry section (if root element) sect1 (if root element) | body-first | First page of new page sequence in body of document, such as chapters. |
body-odd | Right-hand body page. | ||
body-even | Left-hand body page. | ||
back | Used by these elements:
appendix bibliography colophon glossary | back-first | First page of new page sequence in book back matter, which includes appendix, glossary, bibliography, and colophon (but not index). |
back-odd | Right-hand back matter page. | ||
back-even | Left-hand back matter page. | ||
index | Book or set index | index-first | First page of index. |
index-odd | Right-hand index page. | ||
index-even | Left-hand index page. | ||
blank | Blank page that may be automatically inserted at the end of a page sequence to even out the page count. It can be used in all page-sequence-masters. |
In each page class, there is a second set of page masters with the same names except the names all end in -draft
, such as titlepage-first-draft
. These are used when a document is processed in draft mode, which happens when you set the draft.mode
parameter to a value other than no
.
Each of the page-master-sequence
and simple-page-master
elements is defined in the fo/pagesetup.xsl
stylesheet file. The page-master-sequence
elements are used to set the conditions under which different page masters are selected, such as the first page or even page number. The simple-page-master
elements define the margins of the page, using the stylesheet parameters. The following example shows a complete page master declaration:
Example 13.10. Page master declaration
<fo:simple-page-master master-name="titlepage-even"page-width="{$page.width}"
page-height="{$page.height}" margin-top="{$page.margin.top}" margin-bottom="{$page.margin.bottom}" margin-left="{$page.margin.inner}" margin-right="{$page.margin.outer}"> <fo:region-body margin-bottom="{$body.margin.bottom}"
margin-top="{$body.margin.top}" column-count="{$column.count.titlepage}"> </fo:region-body> <fo:region-before region-name="xsl-region-before-even"
extent="{$region.before.extent}" display-align="before"/> <fo:region-after region-name="xsl-region-after-even"
extent="{$region.after.extent}" display-align="after"/> </fo:simple-page-master>
The | |
The attributes set the page size and margins using stylesheet parameter values. | |
The | |
The | |
The |
The DocBook FO stylesheets do not call the page masters directly, but instead call the page sequence masters, which then call the appropriate page masters as needed. The following is an example of a complete page sequence declaration:
Example 13.11. Page sequence declaration
<fo:page-sequence-master master-name="titlepage"><fo:repeatable-page-master-alternatives>
<fo:conditional-page-master-reference master-reference="blank"
blank-or-not-blank="blank"/>
<fo:conditional-page-master-reference master-reference="titlepage-first"
page-position="first"/>
<fo:conditional-page-master-reference master-reference="titlepage-odd" odd-or-even="odd"/> <fo:conditional-page-master-reference master-reference="titlepage-even" odd-or-even="even"/> </fo:repeatable-page-master-alternatives> </fo:page-sequence-master>
The stylesheet calls this master-name when setting up a page sequence for title pages. | |
Wrapper element for a set of conditions that select a particular page master when its conditions are met. | |
Calls the page master named | |
The value of | |
Calls the page master named | |
The value of |
The FO stylesheets let you set up your own page masters while leaving the original default page masters in place. To do so, you create an XSL template named user.pagemasters
in your customization layer. In it, you add as many fo:simple-page-master
and fo:page-sequence-master
elements as you need. The names must be different from the default names, which means each master-name
attribute must be unique and different from those already declared. Then you add any page properties you need as attributes to the simple-page-master
element. The user.pagemasters
template is automatically called by the template that sets up the default page masters. So all you have to do is define it and your page masters will become part of your FO output files, ready to be used in page sequences.
The easiest way to make your own page masters is to cut and paste from the original fo/pagesetup.xsl
stylesheet file to your customization, and then change the name and property attributes. You will most likely want to copy a page-sequence-master
and its related simple-page-master
elements so you can have proper handling of first, left, and right pages.
In your property attributes for your custom page masters, you can refer to any of the existing stylesheet parameter values, or define your own parameters in your customization layer. You can also use any of them in expressions, so you can adjust the regular value by increments. There are many simple-page-master
property attributes that you can add as well. You will need a good FO reference or the XSL-FO specification to know what attribute names and values can be used. Keep in mind that not all property attributes are supported by all XSL-FO processors.
Declaring custom page masters just makes them available to be used, but does not employ them in page sequences. To do that, you have to add some logic to the stylesheets so your custom page masters are selected instead of the default page masters. That logic is put into a template named select.user.pagemaster
that you add to your customization layer. The way this works is as follows:
The DocBook elements that start a page sequence call the template named select.pagemaster
. It is supposed to return a master-name for a page-sequence-master
such as titlepage
or body
. That template's first step is to select the appropriate default page master name for that element.
Then the select.pagemaster
template automatically calls the select.user.pagemaster
template, passing the selected default master name as a parameter.
The default version of the select.user.pagemaster
template simply returns the default page master. But if you customize that template, then it can return the master name of one of your customized page-sequence-master
s in place of the default.
The following is an example of a customized template that selects a new name if for titlepage, otherwise returns the default:
<xsl:template name="select.user.pagemaster"> <xsl:param name="element"/> <xsl:param name="pageclass"/> <xsl:param name="default-pagemaster"/> <!-- Return my customized title page master name if for titlepage, otherwise return the default --> <xsl:choose> <xsl:when test="$default-pagemaster = 'titlepage'"> <xsl:value-of select="'my-new-titlepage'" /> </xsl:when> <xsl:otherwise> <xsl:value-of select="$default-pagemaster"/> </xsl:otherwise> </xsl:choose> </xsl:template>
You could also use the element name or the pageclass parameter value in the conditions for choosing a page master.
There are three levels at which landscape (horizontally oriented) pages can be used:
The entire document is to be rendered on landscape pages. Use the page.orientation
parameter as described in the section “Landscape documents”.
Some chapters or appendixes are to be landscape, in a document that is otherwise portrait. See the section “Landscape page sequence”.
Tables, figures, or other block elements are to be displayed in landscape. See the section “Landscape elements”.
If you want an individual chapter or appendix to be presented in landscape mode, then you can accomplish that with a new page master. In fact, this can be done for any element that generates a page-sequence in XSL-FO:
appendix
article
bibliography*
chapter
colophon
dedication
glossary*
index
preface
refentry*
reference
* Except when it is contained inside an element that generates a page-sequence.
To put an element into a landscape page-sequence, you have to do two things:
Set up a new XSL-FO page-sequence-master with landscape specifications.
Apply the page-sequence-master to your selected element.
To set up a new page-sequence-master, you define it in your customization layer inside a template named user.pagemasters
. Then you customize the template named select.user.pagemaster
to apply it.
There are two methods to handle landscape in a page sequence:
See the following sections for details of each method.
This method of rotating the content of a page-sequence is the easiest. It works best for printed out. That's because it leaves the headers and footers in their portait positions and rotates only the body content on the page. When printed, page numbers and running header and footer titles are in consistent positions on all pages. But when viewed in a PDF browser, the body content will be rotated, and the user will have to select an option in the PDF viewer to unrotate the pages for reading.
In XSL-FO, a page has five primary regions that can hold content. These are the top and bottom margin areas, the left and right margin areas, and the center body area. See the following table.
Location | Region name |
---|---|
Top margin area | fo:region-before |
Bottom margin area | fo:region-after |
Left margin area | fo:region-start |
Right margin area | fo:region-end |
Center body area | fo:region-body |
If your document is set up for text that reads right-to-left as in Hebrew or Arabic, the left area is fo:region-end
and the right area is fo:region-start
.
To use this method, you add a reference-orientation="90"
attribute to the fo:region-body
element in a custom page-master declaration. That rotates only the center body area, leaving the headers and footers in their original locations.
Create a template named user.pagemasters
in your customization layer.
Locate the set of fo:simple-page-master
elements for body
in fo/pagesetup.xsl
. You will need those with master-name
of body-first
, body-odd
, and body-even
. Copy these elements into your user.pagemasters
template.
Change the master-name
values in your copies so they do not conflict with the originals. For example, use landscape-first
instead of body-first
, etc.
In each fo:region-body
element, add the reference-orientation="90"
attribute.
<fo:simple-page-master master-name="landscape-odd"
page-width="{$page.width}"
page-height="{$page.height}"
margin-top="{$page.margin.top}"
margin-bottom="{$page.margin.bottom}"
margin-left="{$margin.left.inner}"
margin-right="{$page.margin.outer}">
<fo:region-body margin-bottom="{$body.margin.bottom}"
margin-top="{$body.margin.top}"
reference-orientation="90"
column-gap="{$column.gap.body}"
column-count="{$column.count.body}">
</fo:region-body>
...
</fo:simple-page-master>
Also in your user.pagemasters
template, copy the fo:page-sequence-master
for body
, and edit it to change the master-name
and to use your new simple page-master names, as follows:
<fo:page-sequence-master master-name="landscape"> <fo:repeatable-page-master-alternatives> <fo:conditional-page-master-reference master-reference="blank" blank-or-not-blank="blank"/> <fo:conditional-page-master-reference master-reference="landscape-first" page-position="first"/> <fo:conditional-page-master-reference master-reference="landscape-odd" odd-or-even="odd"/> <fo:conditional-page-master-reference odd-or-even="even"> <xsl:attribute name="master-reference"> <xsl:choose> <xsl:when test="$double.sided != 0">landscape-even</xsl:when> <xsl:otherwise>landscape-odd</xsl:otherwise> </xsl:choose> </xsl:attribute> </fo:conditional-page-master-reference> </fo:repeatable-page-master-alternatives> </fo:page-sequence-master>
Once the page-sequence-master has been set up this way, you customize the template named select.user.pagemaster
to employ it for certain elements. For example, if you intend to use role="landscape"
on a chapter or appendix element to rotate it, then you would use the following:
<xsl:template name="select.user.pagemaster">
<xsl:param name="element"/>
<xsl:param name="pageclass"/>
<xsl:param name="default-pagemaster"/>
<xsl:choose>
<xsl:when test="@role = 'landscape'">landscape</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$default-pagemaster"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
The select.user.pagemaster
template is automatically called when the stylesheet is selecting a page-master for an element. Normally it just selects the default, but in this case it will select your custom page-master and rotate the content in the body region.
A different approach to landscape output is to change the page dimensions to landscape, without rotating any content. Of course, you can change an entire document to landscape by setting the stylesheet parameter page.orientation="landscape"
. But if you only want a chapter or appendix in landscape, you have to do it with a custom page-master.
When you use this method, none of the content is rotated. When you view the output in a PDF browser, the pages will simply switch from tall to wide, with all the content remaining upright. The headers and footers will still be at the top and bottom, but they will be longer. That output style makes this method the most convenient for users consuming the output using a PDF reader instead of through printed output.
If you print such a PDF document, most (perhaps not all) PDF browsers know to rotate the landscape pages to fit a portrait-oriented printed page. But the headers and footers will also rotate. When the printed pages are assembled into a portrait document, the headers and footers on the landscape pages will be on the sides instead of top and bottom.
To use this method, copy the declaration for body pages into a template named user.pagemasters
in your customization layer and swap the horizontal and vertical dimensions. The following example highlights the changes necessary.
<xsl:template name="user.pagemasters"> <!-- landscape body pages --> <fo:simple-page-master master-name="body-first-landscape" page-width="{$page.height}" page-height="{$page.width}" margin-top="{$margin.left.inner}" margin-bottom="{$page.margin.outer}" margin-left="{$page.margin.bottom}" margin-right="{$page.margin.top}"> <fo:region-body margin-bottom="{$body.margin.bottom}" margin-top="{$body.margin.top}" column-gap="{$column.gap.body}" column-count="{$column.count.body}"> </fo:region-body> <fo:region-before region-name="xsl-region-before-first" extent="{$region.before.extent}" display-align="before"/> <fo:region-after region-name="xsl-region-after-first" extent="{$region.after.extent}" display-align="after"/> </fo:simple-page-master> <fo:simple-page-master master-name="body-odd-landscape" page-width="{$page.height}" page-height="{$page.width}" margin-top="{$margin.left.inner}" margin-bottom="{$page.margin.outer}" margin-left="{$page.margin.bottom}" margin-right="{$page.margin.top}"> <fo:region-body margin-bottom="{$body.margin.bottom}" margin-top="{$body.margin.top}" column-gap="{$column.gap.body}" column-count="{$column.count.body}"> </fo:region-body> <fo:region-before region-name="xsl-region-before-odd" extent="{$region.before.extent}" display-align="before"/> <fo:region-after region-name="xsl-region-after-odd" extent="{$region.after.extent}" display-align="after"/> </fo:simple-page-master> <fo:simple-page-master master-name="body-even-landscape" page-width="{$page.height}" page-height="{$page.width}" margin-top="{$page.margin.outer}" margin-bottom="{$margin.left.inner}" margin-left="$page.margin.bottom}" margin-right="{$page.margin.top}"> <fo:region-body margin-bottom="{$body.margin.bottom}" margin-top="{$body.margin.top}" column-gap="{$column.gap.body}" column-count="{$column.count.body}"> </fo:region-body> <fo:region-before region-name="xsl-region-before-even" extent="{$region.before.extent}" display-align="before"/> <fo:region-after region-name="xsl-region-after-even" extent="{$region.after.extent}" display-align="after"/> </fo:simple-page-master> <!-- blank pages --> <fo:simple-page-master master-name="blank-landscape" page-width="{$page.height}" page-height="{$page.width}" margin-top="{$page.margin.outer}" margin-bottom="{$margin.left.inner}" margin-left="{$page.margin.bottom}" margin-right="{$page.margin.top}"> <fo:region-body display-align="center" margin-bottom="{$body.margin.bottom}" margin-top="{$body.margin.top}"> <xsl:if test="$fop.extensions = 0 and $fop1.extensions = 0"> <xsl:attribute name="region-name">blank-body</xsl:attribute> </xsl:if> </fo:region-body> <fo:region-before region-name="xsl-region-before-blank" extent="{$region.before.extent}" display-align="before"/> <fo:region-after region-name="xsl-region-after-blank" extent="{$region.after.extent}" display-align="after"/> </fo:simple-page-master> <fo:page-sequence-master master-name="body-landscape"> <fo:repeatable-page-master-alternatives> <fo:conditional-page-master-reference master-reference="blank-landscape" blank-or-not-blank="blank"/ <fo:conditional-page-master-reference master-reference="body-first-landscape" page-position="first" <fo:conditional-page-master-reference master-reference="body-odd-landscape" odd-or-even="odd"/> <fo:conditional-page-master-reference odd-or-even="even"> <xsl:attribute name="master-reference"> <xsl:choose> <xsl:when test="$double.sided != 0">body-even-landscape</xsl:when> <xsl:otherwise>body-odd-landscape</xsl:otherwise> </xsl:choose> </xsl:attribute> </fo:conditional-page-master-reference> </fo:repeatable-page-master-alternatives> </fo:page-sequence-master> </xsl:template>
To apply the new body-landscape
master-name, you customize the template named select.user.pagemaster
as described in the section “Rotated body-region”.
Then when you add role="landscape"
to a chapter or appendix element, it will be output in landscape mode to the PDF file. A PDF viewer should display all the pages upright, with the landscape pages shown more wide than tall.
The XSL-FO method of rotating a given element in an otherwise portrait page-sequence is to use the fo:block-container
element. That element accepts a reference-orientation="90"
property to rotate the content relative to the page. This method is used for the following:
A table with an orient="land"
attribute, as described in the section “Landscape tables”.
A table cell with a <?dbfo orientation="90"?>
processing instruction, as described in the section “Cell rotation”.
An image with a custom processing instruction, as described in the section “Landscape images”.
Sometimes the result may not be quite what you expected. When a block-container with rotated content is placed on a portrait page, it is the width rather than the height of the content that determines how much vertical space it takes up. You may need to manually set the width on the DocBook element to get correct formatting.
Long landscape tables are a special problem in XSL-FO. The standard does not support flowing the content of a block-container from one page to the next. It is likely that a table longer than a page will simply be truncated. There is a workaround for this problem, though. G. Ken Holman of Crane Softwrights Ltd. has published a method for creating multipage landscape tables in XSL-FO. His Page Sequence Master Interleave (PSMI) method uses two passes to rearrange the pages in an FO file. PSMI is described at http://www.cranesoftwrights.com/resources/psmi/index.htm.
The top-level elements in DocBook generate one or more page sequences that contain their content. For example, the book template creates page sequences for the titlepage information, the table of contents, and then lets its chapter and appendix elements generate their own page sequences.
You may want to change how a top-level element creates page sequences. For example, an article
element by default creates a single page sequence for all of its content, but you may want to divide that into more than one sequence. You could create a landscape sequence, or a multicolumn sequence for part of the output.
You can use a utility template named page.sequence
to create your own page sequences. You can specify the page master name, the content, and the page numbering style as parameters passed to the template. The following example customizes the template for article
to output the article body in a two-column layout while the titlepage and bibliography are in single column full page width. It uses the existing page master names titlepage
, body
, and back
, but you could substitute your own custom page masters.
Example 13.12. Custom page sequences
<xsl:param name="column.count.titlepage" select="1" /> <xsl:param name="column.count.body" select="2" /> <xsl:param name="column.count.back" select="1" /> <xsl:template match="article"> <xsl:variable name="id"> <xsl:call-template name="object.id"/> </xsl:variable> <xsl:call-template name="page.sequence"> <xsl:with-param name="master-reference">titlepage</xsl:with-param> <xsl:with-param name="content"> <fo:block id="{$id}" xsl:use-attribute-sets="component.titlepage.properties"> <xsl:call-template name="article.titlepage"/> </fo:block> <xsl:variable name="toc.params"> <xsl:call-template name="find.path.params"> <xsl:with-param name="table" select="normalize-space($generate.toc)"/> </xsl:call-template> </xsl:variable> <xsl:if test="contains($toc.params, 'toc')"> <xsl:call-template name="component.toc"> <xsl:with-param name="toc.title.p" select="contains($toc.params, 'title')"/> </xsl:call-template> <xsl:call-template name="component.toc.separator"/> </xsl:if> </xsl:with-param> </xsl:call-template> <xsl:call-template name="page.sequence"> <xsl:with-param name="master-reference">body</xsl:with-param> <xsl:with-param name="content"> <xsl:apply-templates select="*[not(self::bibliography)]"/> </xsl:with-param> </xsl:call-template> <xsl:if test="bibliography"> <xsl:call-template name="page.sequence"> <xsl:with-param name="master-reference">back</xsl:with-param> <xsl:with-param name="content"> <xsl:apply-templates select="bibliography"/> </xsl:with-param> </xsl:call-template> </xsl:if> </xsl:template>
DocBook XSL: The Complete Guide - 4th Edition | PDF version available | Copyright © 2002-2007 Sagehill Enterprises |