8.6 A Bigger Example: DocBook

The last example was relatively simple, lacking complex structures like lists, tables and footnotes. It also was quite short, taking up only one page. Clearly, this lack of a challenge is an insult to any self-respecting XSL formatter. So let us set our sights on something more challenging.

Let us return to our old friend DocBook for inspiration. We will include support for most of the common elements you would find in technical prose, including some objects we have not covered yet, such as tables, lists, and footnotes. To flex the muscles of pagination, we will also explore the use of different page masters and conditional master sequences.

To save you from having to look at a huge, monolithic listing, I'll break up the stylesheet into manageable chunks interspersed with friendly narrative.

8.6.1 Page Masters

We will start by setting up the page masters. This is a very verbose piece of the stylesheet, since we want to create a page master for each type of page. I chose to cover these types:

  1. Lefthand (verso) page starting a chapter. It happens to have an even numbered page number, which we will use to identify it in the page sequence master. The layout will include a header that shows the chapter title and number and a body that is pushed down further than a non-starting page.

  2. Righthand (recto) page starting a chapter. The main difference is that its page number is odd, and the header is right-justified instead of justified on the left.

  3. Verso page that does not start a chapter. It has a plain header and body that reaches up almost to the top.

  4. Recto page that does not start a chapter. Again, the distinctive layout involves right-justifying header and footer.

Below is the stylesheet portion containing page master definitions. The template matching the root node in the source document calls a named template that sets up the page masters and also the page sequence masters.

<?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">

<!--        ROOT RULE
            Set up page layout and main flow.
-->

<xsl:template match="/">
  <fo:root>
    <fo:layout-master-set>
      <xsl:call-template name="page-masters"/>
      <xsl:call-template name="page-sequence-masters"/>
    </fo:layout-master-set>
    <xsl:apply-templates/>
  </fo:root>
</xsl:template>

<!--        PAGE MASTERS
            Where we define layout and regions for page types.
-->

<xsl:template name="page-masters">

<!--
Even-numbered first page of a chapter.
-->
  <fo:simple-page-master master-name="chapter-first-verso"
    page-height="9in"
    page-width="7in"
    margin-left="1in"
    margin-right="1in"
    margin-top="0.5in"
    margin-bottom="0.4in"
 >

    <!-- body -->
    <fo:region-body 
      margin-top="2.5in"
      margin-bottom="0.6in"
    />

    <!-- header -->
    <fo:region-before region-name="region-first-verso-before"
      extent="1in"
    />

    <!-- footer -->
    <fo:region-after region-name="region-first-verso-after"
      extent="0.5in"
    />
  </fo:simple-page-master>

<!--
Odd-numbered first page of a chapter.
-->
  <fo:simple-page-master master-name="chapter-first-recto"
    page-height="9in"
    page-width="7in"
    margin-left="1in"
    margin-right="1in"
    margin-top="0.5in"
    margin-bottom="0.4in"
 >
    <fo:region-body 
      margin-top="2.5in"
      margin-bottom="0.6in"
    />
    <fo:region-before region-name="region-first-recto-before"
      extent="1in"
    />
    <fo:region-after region-name="region-first-recto-after"
      extent="0.5in"
    />
  </fo:simple-page-master>

<!--
Even-numbered page of a chapter not including the first.
-->
  <fo:simple-page-master master-name="chapter-other-verso"
    page-height="9in"
    page-width="7in"
    margin-left="1in"
    margin-right="1in"
    margin-top="0.5in"
    margin-bottom="0.4in"
 >
    <fo:region-body 
      margin-bottom="0.6in"
    />
    <fo:region-after region-name="region-other-verso-after"
      extent="0.5in"
    />
  </fo:simple-page-master>

<!--
Odd-numbered first page of a chapter other than the first.
-->
  <fo:simple-page-master master-name="chapter-other-recto"
    page-height="9in"
    page-width="7in"
    margin-left="1in"
    margin-right="1in"
    margin-top="0.5in"
    margin-bottom="0.4in"
 >
    <fo:region-body 
      margin-bottom="0.6in"
    />
    <fo:region-after region-name="region-other-recto-after"
      extent="0.5in"
    />
  </fo:simple-page-master>

Notice how I had to choose distinctive names for the page masters as well as the regions inside them, except for the body regions. Why not name the body regions? They get the implicit name xsl-region-body which is good enough because the text will flow inside it the same way no matter what the page type.

8.6.2 Page Sequence Masters

Now we define the page sequence masters. Well, there is only one sequence. When I first wrote this example, I had two. One was used for chapters, and another for front matter. The front matter includes dedication, preface, foreword, and other junk at the beginning of the book. The distinction was necessary because these pages are typically numbered with Roman numerals, and the page count resets to 1 at the first chapter. However, this example was so large it would have caused many trees to die unnecessarily for the extra pages. I did the right (green) thing and left it out.

The sequence master uses a repeatable-page-master-alternatives FO to hold a reference for each of the possible page types. Remember, there are four of them. The conditional-page-master-reference FO has attributes page-position and odd-or-even to determine which page type to select:

<xsl:template name="page-sequence-masters">
  <fo:page-sequence-master master-name="chapter" 
    initial-page-number="1"
    format="1"
 >
    <fo:repeatable-page-master-alternatives>
      <fo:conditional-page-master-reference
        master-reference="chapter-other-verso"
        page-position="rest"
        odd-or-even="even"
      />
      <fo:conditional-page-master-reference
        master-reference="chapter-other-recto"
        page-position="rest"
        odd-or-even="odd"
      />
      <fo:conditional-page-master-reference
        master-reference="chapter-first-verso"
        page-position="first"
        odd-or-even="even"
      />
      <fo:conditional-page-master-reference
        master-reference="chapter-first-recto"
        page-position="first"
        odd-or-even="odd"
      />
    </fo:repeatable-page-master-alternatives>
  </fo:page-sequence-master>
</xsl:template>

8.6.3 Top-Level Elements and Flows

I like to structure my XSLT stylesheets with the outermost container elements first. So the next two templates match book and its children which include chapter, preface, and the like. The second template includes the flow and static-flow FOs. Recall that static flows are used for small, single-page items like headers and footers.

<xsl:template match="book">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="chapter | appendix | glossary | 
                     bibliography | index | colophon |
                     preface | dedication | foreword | toc">
  <fo:page-sequence 
    master-reference="chapter"
    force-page-count="odd"
 >

    <!-- START OF CHAPTER ON ODD PAGE -->

    <fo:static-content flow-name="region-first-verso-before">
      <fo:block
        font-family="myriad, verdana, sans-serif"
        font-weight="bold"
        font-size="12pt"
        space-after="2mm"
     >
        <xsl:choose>
          <xsl:when test="self::chapter">
            <xsl:text>CHAPTER </xsl:text>
            <xsl:call-template name="chapnum"/>
          </xsl:when>
          <xsl:when test="self::appendix">
            <xsl:text>APPENDIX </xsl:text>
            <xsl:call-template name="chapnum"/>
          </xsl:when>
        </xsl:choose>
      </fo:block>
      <fo:block
        border-top="solid 1pt black"
        padding-before="2mm"
        font-family="myriad, verdana, sans-serif"
        font-weight="bold"
        font-size="18pt"
     >
        <xsl:apply-templates select="title"/>
      </fo:block>
    </fo:static-content>

    <fo:static-content flow-name="region-first-verso-after">
      <fo:block
        border-top="solid 1pt black"
        padding-before="2mm"
        font-family="myriad, verdana, sans-serif"
        font-weight="bold"
        font-size="8pt"
        text-align="left"
     >
        <fo:page-number/>
      </fo:block>
    </fo:static-content>

    <!-- START OF CHAPTER ON EVEN PAGE -->

    <fo:static-content flow-name="region-first-recto-before">
      <fo:block
        font-family="myriad, verdana, sans-serif"
        font-weight="bold"
        font-size="12pt"
        text-align="right"
        space-after="2mm"
     >
        <xsl:choose>
          <xsl:when test="self::chapter">
            <xsl:text>CHAPTER </xsl:text>
            <xsl:call-template name="chapnum"/>
          </xsl:when>
          <xsl:when test="self::appendix">
            <xsl:text>APPENDIX </xsl:text>
            <xsl:call-template name="chapnum"/>
          </xsl:when>
        </xsl:choose>
      </fo:block>
      <fo:block
        border-top="solid 1pt black"
        padding-before="2mm"
        font-family="myriad, verdana, sans-serif"
        font-weight="bold"
        font-size="18pt"
        text-align="right"
     >
        <xsl:apply-templates select="title"/>
      </fo:block>
    </fo:static-content>

    <fo:static-content flow-name="region-first-recto-after">
      <fo:block
        border-top="solid 1pt black"
        padding-before="2mm"
        font-family="myriad, verdana, sans-serif"
        font-weight="bold"
        font-size="8pt"
        text-align="right"
     >
        <fo:page-number/>
      </fo:block>
    </fo:static-content>

    <!-- REST OF CHAPTER ON ODD PAGE -->

    <fo:static-content flow-name="region-other-verso-after">
      <fo:block
        border-top="solid 1pt black"
        padding-before="2mm"
        font-family="myriad, verdana, sans-serif"
        font-weight="bold"
        font-size="8pt"
        text-align="left"
     >
        <fo:page-number/>
        <fo:inline
          font-size="12pt"
       >
          <xsl:text> | </xsl:text>
        </fo:inline>
        <xsl:choose>
          <xsl:when test="self::chapter">
            <xsl:text>Chapter </xsl:text>
            <xsl:call-template name="chapnum"/>
          </xsl:when>
          <xsl:when test="self::appendix">
            <xsl:text>Appendix </xsl:text>
            <xsl:call-template name="chapnum"/>
          </xsl:when>
        </xsl:choose>
        <xsl:text>: </xsl:text>
        <xsl:value-of select="title"/>
      </fo:block>
    </fo:static-content>

    <!-- REST OF CHAPTER ON EVEN PAGE -->

    <fo:static-content flow-name="region-other-recto-after">
      <fo:block
        border-top="solid 1pt black"
        padding-before="2mm"
        font-family="myriad, verdana, sans-serif"
        font-weight="bold"
        font-size="8pt"
        text-align="right"
     >
        <fo:page-number/>
      </fo:block>
    </fo:static-content>

    <!-- REGULAR BODY FLOW -->
    <fo:flow flow-name="xsl-region-body">
      <fo:block
        font-size="10pt"
        font-family="garamond, palatino, serif"
        break-before="page"
     >
        <xsl:apply-templates select="*[not(self::title)]"/>
      </fo:block>
    </fo:flow>

  </fo:page-sequence>
</xsl:template>

<xsl:template match="simplesect | partintro | sect1 | sect2 | sect3">
  <xsl:apply-templates/>
</xsl:template>

8.6.4 Simple Blocks

The next crop of templates are for more mundane blocks, namely heads of chapters, tables, figures, and so on. One interestingly named template, called chapnum, determines how to label a chapter-level element. If it is called from the context of a chapter, it will just return a number. But from inside an appendix, the function will return a letter.

<xsl:template match="book/title"/>

<xsl:template match="chapter/title | appendix/title | preface/title">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="sect1/title">
  <fo:block
    font-family="myriad, verdana, sans-serif"
    font-size="18pt"
    font-weight="bold"
    space-before="8mm"
    space-after="4mm"
 >
    <xsl:apply-templates/>
  </fo:block>
</xsl:template>

<xsl:template match="sect2/title">
  <fo:block
    font-family="myriad, verdana, sans-serif"
    font-size="16pt"
    font-weight="bold"
    space-before="8mm"
    space-after="4mm"
 >
    <xsl:apply-templates/>
  </fo:block>
</xsl:template>

<xsl:template match="example/title">
  <fo:block
    font-size="8pt"
    font-style="italic"
    space-after="2mm"
    keep-with-next.within-page="always"
 >
    <xsl:text>Example </xsl:text>
    <xsl:call-template name="chapnum"/>
    <xsl:text>-</xsl:text>
    <xsl:number format="1. " count="example" 
        from="chapter|appendix" level="any"/>
    <xsl:apply-templates/>
  </fo:block>
</xsl:template>

<!--
Return the chapter number or appendix letter. If the element contains
a label attribute, use that. (Useful if you want to split a document
up into multiple documents.
-->
<xsl:template name="chapnum">
  <xsl:choose>
    <xsl:when test="ancestor-or-self::chapter/@label">
      <xsl:value-of select="ancestor-or-self::chapter/@label"/>
    </xsl:when>
    <xsl:when test="ancestor-or-self::appendix/@label">
      <xsl:value-of select="ancestor-or-self::appendix/@label"/>
    </xsl:when>
    <xsl:when test="ancestor-or-self::chapter">
      <xsl:number format="1" value="count(preceding::chapter)+1"/>
    </xsl:when>
    <xsl:when test="ancestor-or-self::appendix">
      <xsl:number format="A" value="count(preceding::appendix)+1"/>
    </xsl:when>
    <xsl:when test="ancestor-or-self::preface">
      <xsl:text>0</xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <xsl:text>?</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="figure/title">
  <fo:block
    font-size="8pt"
    font-style="italic"
    space-after="2mm"
    keep-with-next.within-page="always"
 >
    <xsl:text>Figure </xsl:text>
    <xsl:call-template name="chapnum"/>
    <xsl:text>-</xsl:text>
    <xsl:number format="1" count="figure" 
        from="chapter|appendix" level="any"/>
    <xsl:text>. </xsl:text>
    <xsl:apply-templates/>
  </fo:block>
</xsl:template>

<!-- Catch-all: just a small head -->
<xsl:template match="title">
  <fo:block
    font-size="12pt"
    font-weight="bold"
    space-after="4mm"
 >
    <xsl:apply-templates/>
  </fo:block>
</xsl:template>

Next follow a bunch of paragraph-level blocks.

<xsl:template match="para">
  <fo:block
    space-after="3mm"
    text-align="justify"
 >
    <xsl:apply-templates/>
  </fo:block>
</xsl:template>

<xsl:template match="programlisting | screen">
  <fo:block
    space-after="5mm"
    font-family="monoco, monospace"
    font-size="8pt"
    wrap-option="no-wrap"
 >
    <xsl:apply-templates/>
  </fo:block>
</xsl:template>

<xsl:template match="remark">
  <fo:block
    margin-left="2em"
    margin-right="2em"
    space-after="1em"
    color="blue"
    font-weight="bold"
 >
    <xsl:apply-templates/>
  </fo:block>
</xsl:template>

<!-- Container of graphic and caption. Normally <fo:external-graphic>
     is treated as an inline, so we have to put it inside a block.
-->
<xsl:template match="figure">
  <fo:block 
    text-align="center"
    width="4in"
    height="3in"
    border="solid 1pt black"
    background-color="silver"
 >
    <fo:external-graphic>
      <xsl:attribute name="src">
        <xsl:value-of select="graphic/@fileref"/>
      </xsl:attribute>
    </fo:external-graphic>
  </fo:block>
  <fo:block 
    space-before="4mm" 
 >
    <xsl:apply-templates select="title"/>
  </fo:block>
</xsl:template>

<xsl:template match="example">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="sidebar">
  <fo:block
    background-color="silver"
    padding="1em"
    margin="2em"
 >
    <xsl:apply-templates/>
  </fo:block>
</xsl:template>

<xsl:template match="note | tip | warning">
  <fo:block
    margin-left="20mm"
    margin-right="10mm"
 >
    <xsl:apply-templates/>
  </fo:block>
</xsl:template>

8.6.5 Lists

Lists are more complex than your average block. They contain a sequence of list items, with an optional label.

You can think of a really simple list as a bunch of small paragraphs. However, lists are often more complicated than that. In numbered lists, the item has a number or letter for a label. Bulleted lists use some kind of symbol for a label. A label may even be a piece of text, such as in a term definition list.

XSL uses four FOs to put together a list. The root of the list is list-block, a container of list-item objects. Each list-item has two children, a list-item-label and list-item-body. Figure 8-9 diagrams this list structure.

Figure 8-9. The structure of lists
figs/lx2_0809.gif

list-block has two parameters for fudging the geometry of list-items. provisional-distance-between-starts sets the preferred distance between the start of the label and the item body. The space between the end of the label and the item body is controlled by the property provisional-label-separation.

In list-item-label, you can set the parameter end-indent to label-end(). This function returns the indent of the item body. Similarly, you can set the parameter start-indent in list-item-body to body-start(), a function that returns the indent of the start of the body of the list item. These values are calculated based on the property provisional-distance-between-starts. You don't have to use them, but without them you run the risk of the label overlapping the body or not being able to fit into the space allotted to it.

The following part of the stylesheet creates a numbered list.

<xsl:template match="orderedlist">
  <fo:list-block
    margin-left="5mm"
    space-before="5mm"
    space-after="5mm"
    provisional-distance-between-starts="15mm"
    provisional-label-separation="5mm"
 >
    <xsl:apply-templates/>
  </fo:list-block>
</xsl:template>

<xsl:template match="orderedlist/listitem">
  <fo:list-item>
    <fo:list-item-label start-indent="5mm" end-indent="label-end()">
      <fo:block>
        <xsl:number format="1."/>
      </fo:block>
    </fo:list-item-label>
    <fo:list-item-body start-indent="body-start()">
      <fo:block>
        <xsl:apply-templates/>
      </fo:block>
    </fo:list-item-body>
  </fo:list-item>
</xsl:template>

To format a bulleted list, simply replace the number with some other character:

<xsl:template match="itemizedlist">
  <fo:list-block
    margin-left="5mm"
    space-before="5mm"
    space-after="5mm"
    provisional-distance-between-starts="15mm"
    provisional-label-separation="5mm"
 >
    <xsl:apply-templates/>
  </fo:list-block>
</xsl:template>

<xsl:template match="itemizedlist/listitem">
  <fo:list-item>
    <fo:list-item-label start-indent="5mm" end-indent="label-end()">
      <fo:block>·lt;/fo:block>
    </fo:list-item-label>
    <fo:list-item-body start-indent="body-start()">
      <fo:block>
        <xsl:apply-templates/>
      </fo:block>
    </fo:list-item-body>
  </fo:list-item>
</xsl:template>

Following is a term definition list. I wanted the terms and definitions to appear on separate lines, so I decided to forgo the traditional list FOs.

<xsl:template match="variablelist">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="varlistentry">
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="varlistentry/term">
  <fo:block
    font-weight="bold"
    space-after="2mm"
    keep-with-next.within-page="2"
 >
    <xsl:apply-templates/>
  </fo:block>
</xsl:template>

<xsl:template match="varlistentry/listitem">
  <fo:block
    margin-left="10mm"
    space-after="2mm"
 >
    <xsl:apply-templates/>
  </fo:block>
</xsl:template>

8.6.6 Footnotes

Footnotes are the strangest objects yet. Even though in the DocBook source the content is embedded in the narrative, in the presentation it pops out of the flow and drops to the bottom of the page. A footnote also leaves behind a referential symbol to bring it to the attention of the reader. Because of this unusual behavior, XSL has devoted an FO specifically to footnotes.

The following template shows how to set up a formatting object for a footnote. The first child of the object is an inline FO describing what to place in the main text as an indication of the footnote's existence (in this case, the number of the footnote in the chapter). The second child contains the footnote body which will be positioned at the bottom of the page. For convenience, I've used some list-related elements to help format the footnote text with a number.

<xsl:template match="footnote">
  <fo:footnote>
    <fo:inline
      font-size="70%"
      vertical-align="super"
   >
      <xsl:number count="footnote" from="chapter" level="any" format="1"/>
    </fo:inline>
    <fo:footnote-body>
      <fo:list-block>
        <fo:list-item
          space-before="5mm"
       >
          <fo:list-item-label end-indent="label-end()">
            <fo:block
              font-size="7pt"
           >
              <xsl:number count="footnote" from="chapter" 
                level="any" format="1"/>
            </fo:block>
          </fo:list-item-label>
          <fo:list-item-body start-indent="body-start()">
            <fo:block
              font-size="7pt"
           >
              <xsl:apply-templates/>
            </fo:block>
          </fo:list-item-body>
        </fo:list-item>
      </fo:list-block>
    </fo:footnote-body>
  </fo:footnote>
</xsl:template>

8.6.7 Tables

Tables are very intuitive in XSL. Virtually anything you want to do in a table is possible. The following FO types are used to build tables.

table-and-caption

A block encapsulating a table and its caption.

table-caption

A block containing text describing the table.

table

The container of basic table components. Its children include a number of table-column objects, an optional table-header, an optional table-footer, and at least one table-body.

table-column

An informational element describing details about a column, such as its width. Set the column-number property to a positive integer to refer to a specific column. (A column is a stack of cells perpendicular to the inline propagation direction).

table-body

A collection of table-row objects. Rows stack in the block progression direction.

table-header

An optional container of rows or cells that must appear before the body. This is traditionally where column heads reside.

table-footer

Like table-header, except that it must appear after the body. Typically used for things like column totals.

table-row

A block containing some number of table-cells. Usually, the number of cells is constant for all rows in the table, and the formatter will attempt to align them so that their edges parallel to the block progression direction line up. These lined up cells are called columns and a table-column object can be used to define group behavior of each column.

table-cell

The innermost table component. Cells stack along the inline progression direction inside rows. A cell may take up more than one column width or row height by setting the number-columns-spanned or number-rows-spanned properties to a value greater than 1.

Figure 8-10 outlines the structure of a typical table.

Figure 8-10. The structure of tables
figs/lx2_0810.gif

Properties common to all the table components include height and width which are useful in tuning the size of the areas they control. Border properties also apply, making it easy to create rules.

The following XSLT templates are my attempt to handle CALS tables (the standard adopted by DocBook). They allow for tables that have captions, but no cell spans are allowed.

<xsl:template match="table">
  <fo:table-with-caption>
    <xsl:apply-templates/>
  </fo:table-with-caption>
</xsl:template>

<xsl:template match="table/title">
  <fo:table-caption
    font-size="8pt"
    font-style="italic"
    space-after="2mm"
 >
    <xsl:text>Table </xsl:text>
    <xsl:call-template name="chapnum"/>
    <xsl:text>-</xsl:text>
    <xsl:number format="1" count="table" 
        from="chapter|appendix" level="any"/>
    <xsl:text>. </xsl:text>
    <xsl:apply-templates/>
  </fo:table-caption>
</xsl:template>

<xsl:template match="tgroup">
  <fo:table 
    table-layout="fixed" 
    width="4.7in"
    space-after="4mm"
    margin-left="4mm"
    border-bottom="dotted 1pt gray"
 >
    <xsl:apply-templates/>
  </fo:table>
</xsl:template>

<xsl:template match="colspec">
  <fo:table-column
    column-number="{count(preceding-sibling::colspec) + 1}"
    column-width="{@colwidth}" />
</xsl:template>

<xsl:template match="thead">
  <fo:table-header
      background-color="silver"
      font-weight="bold"
 >
    <xsl:apply-templates/>
  </fo:table-header>
</xsl:template>

<xsl:template match="tbody">
  <fo:table-body>
    <xsl:apply-templates/>
  </fo:table-body>
</xsl:template>

<xsl:template match="tfoot">
  <fo:table-footer>
    <xsl:apply-templates/>
  </fo:table-footer>
</xsl:template>

<xsl:template match="row">
  <fo:table-row>
    <xsl:apply-templates/>
  </fo:table-row>
</xsl:template>

<xsl:template match="entry">
  <fo:table-cell>
    <fo:block
      font-family="verdana, sans-serif"
      padding-top="1mm"
      padding-bottom="1mm"
      padding-left="3mm"
      padding-right="3mm"
      text-align="left"
   >
      <xsl:apply-templates/>
    </fo:block>
  </fo:table-cell>
</xsl:template>

8.6.8 Inlines

Finally, we can define the templates for inline elements. In this particular stylesheet, they fall into a few categories of presentation: italic, monospace, subscript, superscript, and small caps.

<xsl:template match="citetitle | command | emphasis | filename | 
                     firstterm | foreignphrase | replaceable">
  <fo:inline font-style="italic">
    <xsl:apply-templates/>
  </fo:inline>
</xsl:template>

<xsl:template match="function | literal | option | parameter | 
                     returnvalue | symbol | type">
  <fo:inline font-family="monoco, monospace" font-size="9pt">
    <xsl:apply-templates/>
  </fo:inline>
</xsl:template>

<!-- Note use of vertical-align property to lower the font baseline.
     Also using relative size to make it smaller. -->
<xsl:template match="subscript">
  <fo:inline 
    font-size="60%" 
    vertical-align="sub"
  >
    <xsl:apply-templates/>
  </fo:inline>
</xsl:template>

<xsl:template match="superscript">
  <fo:inline 
    font-size="60%" 
    vertical-align="super"
  >
    <xsl:apply-templates/>
  </fo:inline>
</xsl:template>

<!-- Font-variant property here to set all-caps for acronyms. -->
<xsl:template match="acronym">
  <fo:inline font-variant="small-caps">
    <xsl:apply-templates/>
  </fo:inline>
</xsl:template>

</xsl:stylesheet>

8.6.9 Results

As a source document, I chose to revisit Example 3-6. It's long enough to span several pages and contains some complex objects, such as tables, lists, and footnotes. And as a bonus, it includes chapters and sections with titles that need to be differentiated stylistically.

Figure 8-11 is a screenshot of a page with a variety of block and inline areas. There is a section head on the top left, an inline style on the top right, a program listing on the bottom, and a paragraph in the middle. The paragraph contains a footnote marker.

Figure 8-11. A screenshot of the middle of the page
figs/lx2_0811.gif

Figure 8-12 shows the bottom of the same page. At the top of the screenshot is the body of the footnote previously mentioned. It is at the bottom of the body flow. Under it is the static flow area of the footer, which contains a generated page number and the chapter title.

Figure 8-12. A screenshot of a footer
figs/lx2_0812.gif

In Figure 8-13 we see a close-up of a table. The table header area is shaded gray with bold text.

Figure 8-13. A screenshot of a table
figs/lx2_0813.gif

Finally, a view of a whole page appears in Figure 8-14. Of note here are the chapter opener header, a bulleted list, and a comment in italic.

Figure 8-14. A screenshot of the whole page
figs/lx2_0814.gif

Overall, my experience with XSL-FO was very positive. I found it easy to learn and 95% of the things I wanted to do were straightforward. A few irritating wrinkles arose because the formatter I used, FOP, is still a beta version and is missing some functionality. For example, I could not figure out how to get Roman numerals for the page number references in the preface. Tables are limited to fixed-width format in the version I used, but this is documented and I'm prepared to accept it for my purposes.

For a free system, I'm quite impressed with FOP. I cannot recommend it to companies that want professional-quality typesetting yet, but it's good for turning my XML copy into something readable with a reasonably accurate page count. I certainly expect FOP to evolve into a stronger product in coming years. High-end systems have more complete implementation as well as technical support and nice GUI interfaces. Of course, those advantages come at a price.