Printing from XML: An Introduction to XSL-FO
by Dave Pawson
|
Pages: 1, 2
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
version="1.0">
<xsl:output method="xml"/>
<xsl:template match="/">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master
master-name="simple"
page-height ="29.7cm" [1]
page-width ="21cm"
margin-left ="2.5cm"
margin-right ="2.5cm">
<fo:region-body margin-top="3cm"/> [2]
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence
master-reference="simple"> [3]
<fo:flow
flow-name="xsl-region-body"> [4]
<xsl:apply-templates/> [5]
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
<xsl:template match="document"> [6]
<fo:block>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="head"> [7]
<fo:block>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="para"> [8]
<fo:block>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="em"> [9]
<fo:inline font-style="italic">
<xsl:apply-templates/>
</fo:inline>
</xsl:template>
<xsl:template match="*"> [10]
<fo:block background-color="red">
<xsl:apply-templates/>
</fo:block>
</xsl:template>
</xsl:stylesheet>
The Source Document
Before explaining the structure, the source document for which we are
designing this stylesheet should be mentioned. I'm assuming a feed from a
document class which has 4 elements, with the structure as shown
below. I've kept it simple because it represents the vast majority of XML
content meant for an XSL-FO document. It contains only two block items
(head and para) and a single inline item
(em).
Our document is contained in an outer document element,
and a mix of head and para elements which
contain some emphasis:
<document>
<head>My very first xsl-fo document</head>
<para>has an <em>important</em> paragraph inside it</para>
</document>
A page size is specified at [1], using European sizes. Change these to your local paper size if it's different. I've added margins since content which extends to the edges of the page is unsightly.
At [2] I've added a top margin to the main region of the
page. [3] and [4] are as before. At [5] we have a
crucial difference: at this point, where previously I simply said
"content", I now use the facilities of XSLT to instruct the XSLT engine to
process the input document. At [6] the XSLT engine processes the
document element of the input XML file by outputting an
fo:block element, inside which all remaining content is
placed. Since blocks can be nested quite happily in XSL-FO this isn't a
problem. What it does do is ensure that any content which leaks -- that
is, isn't handled explicitly by the stylesheet -- is still in a block.
At [7], [8], and [9] I'm back in the normal world of XML and XSLT. Matching a source document element and outputting an appropriate element from the XSL-FO vocabulary. The first two are identical and just need decorating, the latter is slightly different in that it is an inline formatting object and produces italic output.
[10] is a catch-all to show (in the output) which elements, if any, are not styled. Once styling is applied to all elements nothing will be processed by this template. It's good as a debugging option during development.
This stylesheet introduces two new elements. The first is the
fo:block element, used for many elements in the
stylesheet. This is the basic layout element which is used to wrap
content; think of it as a p element in HTML.
The fo:inline element is a container for inline elements
in XSL-FO. Each of these two elements has a whole range of properties,
expressed syntactically as attributes, which are used to decorate the
content that they wrap.
Starting New Pages
Let's extend the source document structure to include a section which should have a new page start point. So now the document might look like this:
<document>
<section>
<head>My very first xsl-fo document</head>
<para>has an <em>important</em> paragraph inside it</para>
</section>
<section>
<head>The second section, starting on a new page </head>
<para>Some content in the second section</para>
</section>
</document>
Now I need to style this addition, using one of the available properties of a block.
<xsl:template match="section">
<fo:block break-before="page">
<xsl:apply-templates/>
</fo:block>
</xsl:template>
This tells the XSL-FO formatting engine to create a new page when it hits a section. All the content of that section is processed within that block. To make the head element stand out, I'll also improve the appearance by choosing a larger, bold font size and by adding a little space after the content.
<xsl:template match="head">
<fo:block font-size="14pt"
font-weight="bold"
space-after="1cm"
space-after.conditionality = 'retain'
>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
That's it. To review: processing is a two stage process at its simplest. Give your source document and the above XSLT stylesheet to an XSLT processor, and the output should be a valid XSL-FO document. This can then be fed to an XSL-FO engine -- RenderX or Antenna House (both commercial, with trial options) or to PassiveTeX or FOP (non-commercial offerings).
You can download the files developed in this article here: xsl-fo-assets.zip.
Related Reading
|
