Create a list of numbered footnotes using XSLT
Content |
Tested on |
Debian (Lenny, Squeeze) |
Ubuntu (Lucid, Precise, Trusty) |
Objective
To create a list of automatically numbered footnotes using XSLT, with matching footnote markers in the text and next to each footnote.
Scenario
Suppose that you want to add support for automatically-numbered footnotes to an XSLT stylesheet.
In the input document, each footnote is represented by a footnote
element that contains the text of the footnote
and is placed at the point in the document to which it refers, for example:
<p>Three is the number to which you will count.<footnote>Armaments 2:9-21</footnote></p>
In the output document, the footnotes should be moved to a list at the end of the page. Each footnote should leave behind a superscripted footnote marker of the form [n] at the location on the page to which it refers. There should be a matching footnote marker next to the relevant footnote, for example:
Three is the number to which you will count.[1]
[1] Armaments 2:9-21
Matching footnote markers should be hyperlinked to each other. The list of footnotes should be represented by an ol
element of class footnote
, and each member of that list should be an li
element of class
footnote
.
(The element types ol
and li
have been chosen because they are semantically correct.
Their default style is unlikely to be appropriate, but this can easily be changed using a suitable CSS stylesheet.)
Method
Overview
The method described here has three steps:
- When they are first encountered, transform each footnote element into a numbered footnote marker.
- At the end of the page body, perform a second pass through the input document searching for footnote elements.
- During the second pass, transform each footnote element into a footnote marker and the corresponding text.
Transform each footnote element into a numbered footnote marker
At the point in the text where the footnote appears in the input document, it should appear only as a footnote marker in the output document.
The template for a footnote
element should not therefore include an apply-templates
instruction, but instead contain the markup needed to construct a footnote marker.
Numbering is performed using the xsl:number
instruction to count the number of footnote elements in the input document
up to and including the current one:
<xsl:number level="any" count="footnote" format="[1]"/>
The level
attribute specifies that the elements counted need not be siblings of the current
footnote
element, but rather, can occur at any level of the input document (higher or lower).
The count
attribute specifies that only footnote
elements are to be counted.
The format
attribute specifies that the value should be written to the output document as a decimal
number in square brackets. (The brackets could have been added later, but it is slightly neater to generate them here.)
To perform the hyperlinking it is necessary to choose an anchor names for each footnote marker. Every anchor name in the document
must be unique, and the easiest way to achieve this is by incorporating the footnote number into the name. The scheme that will be
used here is as follows, where N
is the footnote number:
- Footnote markers that refer to a footnote elsewhere have anchor names of the form
footnoterefN
. - Footnote markers that label an adjacent footnote have anchor names of the form
footnoteN
.
The numbers are generated using the same method as before, except that instead of being written to the output document as text
it must become part of the value of either a name
or an href
attribute.
This is done using an xsl:attribute
instruction, which converts its content into an attribute value.
The resulting template as as follows:
<xsl:template match="footnote"> <a> <xsl:attribute name="name"> <xsl:text>footnoteref</xsl:text><xsl:number level="any" count="footnote" format="1"/> </xsl:attribute> <xsl:attribute name="href"> <xsl:text>#footnote</xsl:text><xsl:number level="any" count="footnote" format="1"/> </xsl:attribute> <sup><xsl:number level="any" count="footnote" format="[1]"/></sup> </a> </xsl:template>
Perform a second pass through the input document searching for footnote elements
By default the xsl:apply-templates
instruction acts on the content of the current node from the input document,
however it can be instructed to act on the result of any XPath expression. This can select any subset of the input document,
including parts that have already been processed.
In this case the requirement is to select all footnote
elements wherever they occur.
This can be done using the XPath expression ‘//footnote
’, meaning elements of type
footnote
that are descended from the root node.
Here is an example of how the relevant instructions could be incorporated into a template for an HTML-style body
element:
<xsl:template match="body"> <body> <xsl:apply-templates/> <ol class="footnotes"> <xsl:apply-templates select="//footnote" mode="footnote"/> </ol> </body> </xsl:template>
Alternatively, it may be preferable for the source document to explicitly indicate where the list of footnotes should be inserted.
The mode
attribute of the apply-templates
instruction is explained below.
Transform each footnote element into a footnote marker and the corresponding text
During the second pass the footnote
elements will be transformed again, but the result must be different from that
obtained during the first pass. Instead of just a footnote marker, the entire footnote must be generated as the content of a list item.
XSLT allows this to be done by passing a mode
argument to the relevant template
and apply-templates
instructions. In this example the mode has been named footnote
:
<xsl:template match="footnote" mode="footnote"> <li> <a> <xsl:attribute name="name"> <xsl:text>footnote</xsl:text><xsl:number level="any" count="footnote" format="1"/> </xsl:attribute> <xsl:attribute name="href"> <xsl:text>#footnoteref</xsl:text><xsl:number level="any" count="footnote" format="1"/> </xsl:attribute> <xsl:number level="any" count="footnote" format="[1]"/> </a> <xsl:text> </xsl:text> <xsl:apply-templates/> </li> </xsl:template>
As previously, the xsl:number
instruction has been used to number both the footnote marker and the hyperlink anchors.
The footnote body is output using an ordinary xsl:apply-templates
instruction so that any stylistic markup within it
is appropriately transformed.
Tags: xslt