Using XSL Formatting Objects, Part 2
by J. David Eisenberg
|
Pages: 1, 2
Using the list model to create a definition list where the terms
and their definitions share the line space requires incredibly complex
XSLT. (You can see it in the XSL specification, section 6.8.1.1.)
Instead, we'll put the terms and definitions on separate lines, as HTML
is ordinarily rendered.
<xsl:template match="dl">
<fo:block space-before="0.25em" space-after="0.25em">
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="dt">
<fo:block><xsl:apply-templates/></fo:block>
</xsl:template>
<xsl:template match="dd">
<fo:block start-indent="2em">
<xsl:apply-templates/>
</fo:block>
</xsl:template>
Here's a portion of the booklet, showing an ordered list and a
definition list. Note that the text flows from one page to the next
without our having to do anything special.
When we get to verbs, we'll have to show the classical conjugation table
below.
| Singular | Plural |
| yo canto | nosotros cantamos |
| tú cantas | vosotros cantáis |
él canta ella canta |
ellos cantan ellas cantan |
An XSL Formatting Objects Table has elements in this hierarchy
<fo:table-and-caption>
<fo:table-caption>
<fo:table>
<fo:table-column>
<fo:table-header>
<fo:table-row>
<fo:table-cell>
<fo:table-body>
<fo:table-row>
<fo:table-cell>
<fo:table-footer>
<fo:table-row>
<fo:table-cell>
The <fo:table> corresponds to the HTML
<table> tag; <fo:table-body> corresponds
to <tbody>. The only addition of note is the
<fo:table-column> specifier, which allows you to
specify how wide each column in your table will be. You can use this
tag to specify characteristics of cells that have the same column and
span. In the current (January 2001) implementation of FOP, the
<table-and-caption> element is not implemented. You are
required to specify column widths with the
column-width attribute in the
<fo:table-column> element. FOP does not automatically
figure out how wide your table is.
The following XSLT is written for simple tables, and it assumes all
column widths are specified in the first table row, and that all
widths are in pixels. It also assumes that there are 72 pixels per
inch. It doesn't handle column or row spans. Take a deep breath,
though, as it's still fairly lengthy.
<!-- when table-and-caption is supported, that will be the
wrapper for this template -->
<xsl:template match="table">
<xsl:apply-templates/>
</xsl:template>
<!--
find the width= attribute of all the <th> and <td>
elements in the first <tr> of this table. They are
in pixels, so divide by 72 to get inches
-->
<xsl:template match="tbody">
<fo:table>
<xsl:for-each select="tr[1]/th|tr[1]/td">
<fo:table-column>
<xsl:attribute name="column-width"><xsl:value-of
select="floor(@width div 72)"/>in</xsl:attribute>
</fo:table-column>
</xsl:for-each>
<fo:table-body>
<xsl:apply-templates />
</fo:table-body>
</fo:table>
</xsl:template>
<!-- this one's easy; <tr> corresponds to <fo:table-row> -->
<xsl:template match="tr">
<fo:table-row> <xsl:apply-templates/> </fo:table-row>
</xsl:template>
<!--
Handle table header cells. They should be bold
and centered by default. Look back at the containing
<table> tag to see if a border width was specified.
-->
<xsl:template match="th">
<fo:table-cell font-weight="bold" text-align="center">
<xsl:if test="ancestor::table[1]/@border > 0">
<xsl:attribute name="border-style">solid</xsl:attribute>
<xsl:attribute name="border-width">1pt</xsl:attribute>
</xsl:if>
<fo:block>
<xsl:apply-templates/>
</fo:block>
</fo:table-cell>
</xsl:template>
<!--
Handle table data cells. Look back at the containing
<table> tag to see if a border width was specified.
-->
<xsl:template match="td">
<fo:table-cell>
<xsl:if test="ancestor::table/@border > 0">
<xsl:attribute name="border-style">solid</xsl:attribute>
<xsl:attribute name="border-width">1pt</xsl:attribute>
</xsl:if>
<fo:block>
<!-- set alignment to match that of <td> tag -->
<xsl:choose>
<xsl:when test="@align='left'">
<xsl:attribute name="text-align">start</xsl:attribute>
</xsl:when>
<xsl:when test="@align='center'">
<xsl:attribute name="text-align">center</xsl:attribute>
</xsl:when>
<xsl:when test="@align='right'">
<xsl:attribute name="text-align">end</xsl:attribute>
</xsl:when>
</xsl:choose>
<xsl:apply-templates/>
</fo:block>
</fo:table-cell>
</xsl:template>
The third person entries require a <br /> tag, which is
translated into FO this way:
<xsl:template match="br">
<fo:block><xsl:text>
</xsl:text></fo:block>
</xsl:template>
And the resulting table looks like
As you have seen, the combination of XSLT and FO allows you to
convert your XHTML or other XML documents to a format designed for
print. These articles only begin to cover the layout possibilities
that XSL Formatting Objects give you. For more information about
XSL:FO, see Elliotte
Rusty Harold's XML Bible, Chapter 15.
You can get all the files (ZIP or TGZ) used in these
articles, including an XSL file that handles more aspects of
converting HTML to FO, and that was used to convert these articles to
PDF format.