xslt numbering

Numbering

1. Numbering
2. Numbering a node
3. Numbering some rules of thumb
4. How to number based on the parent
5. Legal Style numbering
6. How to get the childnum of a node into a variable
7. xsl:number and XT
8. Format Number
9. xsl:number example
10. xsl:number vs. format-number
11. Using xsl:number in a table of contents
12. Numbering figures automatically
13. Formatting multiple page references
14. Context for numbering.
15. Numbering items produces wrong number
16. Numbering, problems with from and count attributes
17. Is there a way to do numbering of paragraphs?
18. Numbering Ordinals

1.

Numbering

Jeni Tennison

So you want to have something like:

<first>
  <second>
    <third />
  </second>
  <fourth />
</first>

be numbered as:

1. first
2. second
3. third
4. fourth

Is that right?

To get to the declarative solution to the problem, imagine that you are one of those elements, say 'fourth', you can get the number '4' if you count how many elements start before you in the document. Looking at those elements involves looking along the 'preceding' axis (elements that start and end before you do) and along the 'ancestor' axis (elements that are still open).

So, you can get the number you're after by counting how many elements there are on the preceding and ancestor axes, and adding one to it:

  count(preceding::* | ancestor::*) + 1

For example, to number the nodes as above, then:

<xsl:template match="*">
  <xsl:value-of select="count(preceding::* | ancestor::*) + 1" />
  <xsl:text>. </xsl:text>
  <xsl:value-of select="name()" />
  <xsl:apply-templates />
</xsl:template>

2.

Numbering a node

David Marston

As an aid, here is a set of principles I'm refining:

  1. Use xsl:number to generate numbering for output, not for general counting. (Mnemonic: that's why it's in chapter 7 of the XSLT spec.)

  2. There are well-targeted counting options in xsl:number, so try to let it do the work for you. In other words, avoid value="position()" and its variants.

  3. Start with just plain <xsl:number>, then tinker with count, level, from, value, and format, IN THAT ORDER, to get what you need. (All other attributes come later, to tweak the appearance.)

  4. Ultimately, xsl:number is creating strings. (Mnemonic: it can generate "numbers" with more than one decimal point, or with other separators.)

(Suggestions for refining these principles are welcome.)

3.

Numbering some rules of thumb

David Marston

There are some rules-of-thumb about xsl:number which you can apply here, if you have enough thumbs.

Use xsl:number to generate numbering for output, not for general counting. (Mnemonic: that's why it's in chapter 7 of the XSLT spec.)
There are well-targeted counting options in xsl:number, so try to let it do the work for you.
Start with just plain <xsl:number>, then tinker with count, level, from, position, and format, IN THAT ORDER, to get what you need. (All other attributes come later, to tweak the appearance.) Yes, you could do "value=position() div 2" but that would be highly dependent on the regularity of the structure and the intervening whitespace. If "count=form" is what you want, say so. Actually, that should be the default for your example.
3. Ultimately, xsl:number is creating strings. (Mnemonic: it can generate "numbers" with more than one decimal point, or with other separators.)
If you need position data for other reasons not shown in your example, use separate expressions with position() or count(node-set) to perform numerical calculations. I hope this gives you a fresh outlook on the task.

4.

How to number based on the parent

DaveP


Q expansion: For example, from
 
 <root>
   <level1>
     <level2 />
	 <level2 />
   </level1>
   <level1>
      <level2 />
	  <level2 />
	  <level2 />
  </level1>
 </root>
 
 getting output of the form
 
 1. info
 1. info
 2. info
 2. info
 2. info
I.e. based on the parent node.

Try
<xsl:template match="level1/level2">
<xsl:number count="level1" from="level1"/>
   <xsl:text>info</xsl:text>
</xsl:template>

            

5.

Legal Style numbering

Mark Hayes


How to number recursive <section> elements?

 
 <xsl:number> was made for this.  
 Level="multiple" is particularly useful here.
 
 From this source:
 
 <section>
   <section>
     <section/>
     <section/>
   </section>
   <section>
     <section/>
     <section/>
   </section>
 </section>
 
 This stylesheet:
 
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 version="1.0">
   <xsl:template match="/">
     <xsl:apply-templates/>
   </xsl:template>
   <xsl:template match="section">
     <section>
       <xsl:number level="multiple" format="1.1"/>
       <xsl:apply-templates/>
     </section>
   </xsl:template>
 </xsl:stylesheet>
 
 Produces this output:
 
 <?xml version="1.0" encoding="utf-8"?>
 <section>1
   <section>1.1
     <section>1.1.1</section>
     <section>1.1.2</section>
   </section>
   <section>1.2
     <section>1.2.1</section>
     <section>1.2.2</section>
   </section>
 </section>

            

6.

How to get the childnum of a node into a variable

Nikolai Grigoriev

In most cases, when the current node list is just a list of siblings, you can use position(). In a general case, <xsl:number count="*"/> gives you the childnum as a result tree fragment, regardless of the current node list; to use it in an expression context, you have to paste it into an xsl:variable

7.

xsl:number and XT

Mike Kay


> <xsl:number value="position()" level="single" count="title"/>

If value attribute is specified, level and count are ignored. The above is equivalent to <xsl:value-of select="position()"/>. The only reason for using the value attribute of <xsl:number> is if you want to take advantage of the number formatting capabilities.

8.

Format Number

David Marston

>I get "48064.63999" but I need "48.064,64".
>How could I format the number ?

There are two operations in question, but you may find that format-number does what you want with the rounding. Your instruction would change to something like <xsl:value-of select="format-number(lipr,'##.##0,00')"/> and you would have to make a couple declarations in an xsl:decimal-format element to swap the two separators. Read section 12.3 of the XSLT spec for the whole story. You'll want to set the number of #s and 0s to fit the largest allowable number, and you may need to deal with negative values as well.

To round manually to two decimal places, do this <xsl:value-of select="round(100*lipr) div 100"/>

9.

xsl:number example

Ken Holman


      <section type="title" shownumber="no">
         XML sample
         </section>
         <section type="index" shownumber="yes">
         Definition
                 <definition>
                 XML
                 </definition>
                 <definition>
                 XSL
                 </definition>
         </section>
         <section type="index" shownumber="yes">
         Implementation
         </section>

</document>

T:\ftemp>type test2.xsl

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                 version="1.0">
<xsl:output method="text"/>

<xsl:template match="/">                         <!--root rule-->
   <xsl:apply-templates select="//section"/>
</xsl:template>

<xsl:template match="section">
   <xsl:if test="@shownumber='yes'">
     <xsl:number count="section[@shownumber='yes']"/>
   </xsl:if>
   <xsl:apply-templates/>
</xsl:template>

<xsl:template match="definition">
   <xsl:number count="section[@shownumber='yes']
                     |definition"
               level="multiple" format="1."/>
   <xsl:value-of select="."/>
</xsl:template>

</xsl:stylesheet>

T:\ftemp>xt test2.xml test2.xsl

         XML sample
         1
         Definition
                 1.1.
                 XML

                 1.2.
                 XSL

         2
         Implementation
            

10.

xsl:number vs. format-number

David Marston

Shouldn't these two lines produce the same output ?

     Combination <xsl:value-of select="format-number
($combinationIndex,'I')"/>:

     Combination <xsl:number value="$combinationIndex" format="I"/>:

Using Xalan I get these outputs:

     Combination I1:

     Combination I:

You should be using xsl:number only to generate numbering on outputted items. Yes, <xsl:number value="number-expression" ...> is capable of applying some limited formatting to the expression IF that expression is a positive integer, but format-number() gives much better control. You can use xsl:number to format number-expressions into Roman numerals if you wish, and that might have been your intent in the example you gave, but as a general rule xsl:number is not the tool to format values stored in variables.

You said format-number($combinationIndex,'I') in your example, but format-number uses formatting strings (second argument) that are more pictorial than the limited xsl:number vocabulary. One thing that both do is pass through non-magic strings, like so: format-number(12,'(##)') gives '(12)' <xsl:number value="12" format="(1)"/> gives '(12)' In both cases, the parentheses are non-magic and thus just come out as-is. But I is non-magic in format-number, so you were asking for return of a constant 'I' with nothing representing the value to be formatted! When using format-number(), plan on explicitly marking every decimal place, the decimal point, all commas, etc.

Furthermore, notice that format-number() is a function that returns a string, so additional processing like use of other string functions can be applied. OTOH, xsl:number is an output instruction, just like xsl:value-of.

11.

Using xsl:number in a table of contents

Gary L Peskin

If you nest your toc_num_list directly within the containing topic, you'd be able to use level=multiple easily. This way, toc_num_list would directly be the child of the containing topic (if any) rather than the child of the containing toc_num_list.

For example, your first toc would look like:

<toc>
  <title>
    MAIN COMPONENT 
  </title>
  <break/>
  <toc_num_list>
    <topic toc1="#contactor">
      DC CONTACTOR, 41A296327AM
      <toc_num_list>
        <topic toc1="#func_desc">
          Functional Description
        </topic>
        <topic toc1="#clean">
          Cleaning
        </topic>
        <topic toc1="#lube">
          Lubrication
        </topic>
        <topic toc1="#inspect">
          Inspection
        </topic>
      </toc_num_list>
    </topic>
  </toc_num_list>
</toc>

Then, you'd have to change your "topic" template to add this apply-templates after the </a>:

	<xsl:apply-templates select="toc_num_list"/>

Also, you'd have to change your value-of select from "." to "text()".

This is due to the fact that the count attribute of the xsl:number element is used for two purposes: (1) to select the node of interest in the ancestor-or-self tree that is built and (2) to select the siblings of each node in the tree. In your case, you want to build the ancestor-or-self tree but only count "topic" siblings of either topic nodes or toc_num_list nodes. I don't think <xsl:number> can do that.

However, to answer your original question, if you're unable to change the input XML, the following ugly recursive template should work:

Replace your loc_num_list template with:

<xsl:template match="toc_num_list">
  <xsl:param name="lvl" />		
                  <!-- lvl is levels above this one -->
  <xsl:variable name="sibs" select="count(preceding-sibling::topic)" />
  <xsl:variable name="newparm">		
              <!-- lvl of preceding topic -->
    <xsl:choose>
      <xsl:when test="$sibs = 0" />
      <xsl:when test="$lvl">
        <xsl:value-of select="concat($lvl, '.', $sibs)"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$sibs"/>
      </xsl:otherwise>
   </xsl:choose>
  </xsl:variable>
  
  <ol>
  <xsl:apply-templates>
    <xsl:with-param name="lvl" select="$newparm"/>
  </xsl:apply-templates>
  </ol>

</xsl:template>

And make your topic template look like this:

<xsl:template match="topic">
  <xsl:param name="lvl" />
  <xsl:choose>
    <xsl:when test="$lvl">
      <xsl:value-of select="concat($lvl, '.', count(. |
preceding-sibling::topic))" />
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="count(. | preceding-sibling::topic)" />
    </xsl:otherwise>
  </xsl:choose>
  <a>
    <xsl:attribute name="href"><xsl:value-of
select="@toc1"/></xsl:attribute>
  <xsl:value-of select="."/>
  </a>
  <br/>
</xsl:template>

Jeni Tennison approaches it without modifying the original XML instance.

>I am trying to format my table of contents using xsl:number.
>however the concept is not very clear to me and hence i am unable to get the
>output i want. I hope somebody will be able to help me with this.

Thank you for asking this question - it's given me a good opportunity to play around with xsl:number.

You can do this with just xsl:number, but you need to understand what it's doing to know how. In your first cut at it, you specified:

<xsl:number format="1.1.1"
            level="multiple"
            from="pub/toc/toc_num_list"/>

As you haven't specified a 'count' attribute, the XSLT processor will assume that you are after 'topic' elements (because that's the kind of node that you're looking at). For each topic element that you're look at, it constructs a list of all the ancestor elements for that topic. So let's use 'Installation' as an example:

<toc>
  <title>SUB-COMPONENTS</title>
  <break/>
  <toc_num_list>
    <topic toc1="#arcchute">ARC CHUTE</topic>
    <toc_num_list>
      <topic toc1="#arc_insp">Inspection</topic>
      <topic toc1="#arc_maint">Routine Maintenance</topic>
      <toc_num_list>
        <topic toc1="#arc_rem">Removal</topic>
        <topic toc1="#arc_inst">Installation</topic>
        <break/>
      </toc_num_list>
    </toc_num_list>
    ...
  </toc_num_list>
</toc>

The ancestor hierarchy for 'Installation' looks like:

<toc>
  <toc_num_list>
    <toc_num_list>
      <toc_num_list>
        <topic toc1="#arc_inst">Installation</topic>
      </toc_num_list>
    </toc_num_list>
  </toc_num_list>
</toc>

The XSLT processor looks for 'topic' elements within that hierarchy, and the only 'topic' element it can find is the Installation topic itself. That's why you only get single numbers.

The XSLT processor needs to understand the hierarchical structure of your XML in order to number it properly. You can tell it a little about the hierarchical structure of your document by saying that 'toc_num_list' elements are involved in that hierarchy, and that it should count them as well. You tell it that by setting the 'count' attribute on xsl:number to include 'toc_num_list' elements as well as 'topic' elements:

<xsl:number format="1.1.1"
            level="multiple"
            from="pub/toc/toc_num_list"
            count="topic | toc_num_list" />

However, this gives you the output (extract):

	1 ARC CHUTE 
			2.1 Inspection 
			2.2 Routine Maintenance 
				2.3.1 Removal 
				2.3.2 Installation 

The reason is that when the XSLT processor tries to find a number for a toc_num_list it does so by counting all the topics (and toc_num_lists) before it, and adding one to the result. For example, in numbering the toc_num_list that's the parent of 'Installation', it counts 'Inspection'=1, 'Routine Maintenance'=2, so this toc_num_list=3.

What we want to do when it comes to counting is ignore the 'topic' that immediately precedes the 'toc_num_list'. In other words, we're not interested in a 'topic' element whose immediate following sibling is a 'toc_num_list' element:

<xsl:number format="1.1.1"
            level="multiple"
            from="pub/toc/toc_num_list"
            count="topic[not(following-sibling::*[1][name() =
'toc_num_list'])] 
                   | toc_num_list" />

As expected, this gives you the output (extract):

	ARC CHUTE 
			2.1 Inspection 
			2 Routine Maintenance 
				2.3.1 Removal 
				2.3.2 Installation 

It declines to number any of those 'topics' that immediately precede a toc_num_list (as expected). We *do* want those to be numbered, but only when they are the current topic. So, we have to set a variable that holds the id of the current topic (I've used generate-id() for generality but you could use '@toc1' instead) and select nodes that match that id as well:

<xsl:template match="topic">
  <xsl:variable name="topic-id" select="generate-id()" />
  <xsl:number format="1.1"
              level="multiple"
              from="toc/toc_num_list"
              count="topic[generate-id() = $topic-id or
                           not(following-sibling::*[1][name() = 
                                                       'toc_num_list'])] | 
                     toc_num_list/>
  <a href="{@toc1}">
    <xsl:value-of select="."/>
  </a>
 <br/>
</xsl:template>

This gives the numbering you're after in SAXON, Xalan and MSXML (July).

I've made a few other minor changes in the template above:

1. format="1.1" - you don't need to have '1.1.1' as the XSLT processor will automatically extend the format expression to that.
2. from="toc/toc_num_list" - you don't need to specify 'pub' as being a parent of 'toc' as all 'toc's are children of 'pub'.
3. using attribute value template to set the href attribute on the link - it's just shorter, and you're not doing anything complex in identifying what to link to, so there's no need to use xsl:attribute.

I hope that this helps - it's certainly helped me :)

12.

Numbering figures automatically

Sebastian Rahtz

With XML

<figure file="pic.gif" />

My problem is that I may have more than I references to the same figure.

 > The XSL code has a "count()" 
 > command that counts the preceding <figure> elements and then assigns the 
 > next number to the current <figure> element. 

um, this is not the right way to do it. XSLT has a MUCH simpler builtin method, xsl:number:

<xsl:template match="figure">
 Figure  <xsl:number level="any"/>
 ......
</xsl:template>
 > Then I feel the need to refer back 
 > to "Figure 1". 

I am afraid my reaction is "I would not start from here". I would assign a unique ID to each real <figure>, and if I wanted to refer again to Figure 1, I'd use a new element <figureref id="whatever"/>, which matched <figure id="whatever">. Then

<xsl:template match="figref">
 see Figure <xsl:apply-templates select="id(@idref)" mode="xref"/>
</xsl:template>

<xsl:template match="figure" mode="xref">
 Figure  <xsl:number level="any"/
</xsl:template>

13.

Formatting multiple page references

Jeni Tennison



> I want to add a feature so that pages 12, 13,
> 14 is written 12-14. How is that achieved?

Since the number of entries for a particular word is likely to be fairly small, I'd probably do it as follows:

First, create a variable to hold all the word elements that you're interested in:

  <xsl:variable name="entries" select="word[. = current()]" />

Then iterate over them as you are at the moment, in sorted order:

  <xsl:for-each select="$entries">
    <xsl:sort select="@page" data-type="number" />
    ...
  </xsl:for-each>

But within xsl:for-each, test whether there are other entries whose page is one less than and one more than this one. If there's an entry whose page is one less than this one, and an entry whose page is one more than this one, then this one can be skipped over. If there's an entry whose page is one less than this one, but not an entry whose page is one more than this one, then you need to put the hyphen before the page number. If there's not a page whose number is one less than this one (and it's not the first page) then you need to give a comma before the page number. So you get:

  <xsl:for-each select="$entries">
    <xsl:sort select="@page" data-type="number" />
    <xsl:choose>
      <xsl:when test="$entries/@page = @page - 1">
        <xsl:if test="not($entries/@page = @page + 1)">
          <xsl:value-of select="concat('-', @page)" />
        </xsl:if>
      </xsl:when>
      <xsl:otherwise>
        <xsl:if test="position() != 1">, </xsl:if>
        <xsl:value-of select="@page" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:for-each>

Note that this means that consecutive page numbers are linked with a dash as well, so you get 12-13 rather than 12, 13. If you want them to be separated by a comma, then you also need to test whether there's an entry whose page is *two* less than the current page, but the general model is the same.

If there were lots of entries, I'd be tempted to go with a solution where you create a new result tree holding the page numbers in ascending order, and then used the following-sibling and preceding-sibling axes to test what the other page numbers were.

14.

Context for numbering.

Wendell Piez

(Ednote: Rather a long question, but puts it into context) I am trying to number figures consecutively through a document. I have a named template that handles generating the display text for figure captions. My intent is to re-use it for both the figure itself and cross references to figures:

<xsl:template name="figure-caption-text">
  <xsl:text>Figure </xsl:text
    ><xsl:number 
          count="Figure"
          level="any"
    /><xsl:text>. </xsl:text
  ><xsl:apply-templates/>
</xsl:template>

When this template is called from the template for Figure/Caption, I get the expected correct numbering (Figure 1, Figure 2, etc.):

<xsl:template match="Figure/Caption">
  <fo:block
      font-weight="bold">
    <xsl:call-template name="figure-caption-text"/>    
  </fo:block>
</xsl:template>

However, when I call it for the cross ref, I always get the value "1":

<xsl:template match="Figure" mode="xref">
  <xsl:apply-templates select="Caption" mode="xref"/>
</xsl:template>

<xsl:template match="Figure/Caption" mode="xref">
  <fo:inline 
      font-style="italic">
    <xsl:call-template name="figure-caption-text"/>
  </fo:inline>
</xsl:template>

Clearly I'm missing something. I didn't see anything in the spec or the FAQ explained this behavior--everything implies that the numbering is with respect to the source tree, not, for example, the current node list as for position().

Here's the template for CrossRef:

<xsl:template match="CrossRef">
  <xsl:variable name="target-id">
    <xsl:value-of select="@refsub"/>
  </xsl:variable>
  <xsl:variable name="target-element">
    <xsl:copy-of select="key('ids', $target-id)"/>
  </xsl:variable>
  <xsl:apply-templates select="$target-element" mode="xref"/>
</xsl:template>

target-element is a Figure element. I know I'm getting the right target element because the caption text is correct.

Wendell Piez replied:

Ah, it's interesting you're able to apply templates to $target-element, since it's not actually a node in your source, it's a result-tree-fragment. (In XSLT 1.0 you shouldn't be able to do this.)

Try setting your variable as

   <xsl:variable name="target-element" select="key('ids', $target-id)"/>

to get it to be be, truly, the node in your source, and not a copy (which loses the context).

15.

Numbering items produces wrong number

Wendell Piez



I am trying to number figures consecutively through a document. I have a named template that handles generating the display text for figure captions. My intent is to re-use it for both the figure itself and cross references to figures:

<xsl:template name="figure-caption-text">
  <xsl:text>Figure </xsl:text
    ><xsl:number 
          count="Figure"
          level="any"
    /><xsl:text>. </xsl:text
  ><xsl:apply-templates/>
</xsl:template>

When this template is called from the template for Figure/Caption, I get the expected correct numbering (Figure 1, Figure 2, etc.):

<xsl:template match="Figure/Caption">
  <fo:block
      font-weight="bold">
    <xsl:call-template name="figure-caption-text"/>    
  </fo:block>
</xsl:template>

However, when I call it for the cross ref, I always get the value "1":

<xsl:template match="Figure" mode="xref">
  <xsl:apply-templates select="Caption" mode="xref"/>
</xsl:template>

<xsl:template match="Figure/Caption" mode="xref">
  <fo:inline 
      font-style="italic">
    <xsl:call-template name="figure-caption-text"/>
  </fo:inline>
</xsl:template>

Clearly I'm missing something. I didn't see anything in the spec or the FAQ explained this behavior--everything implies that the numbering is with respect to the source tree, not, for example, the current node list as for position().

Here's the template for CrossRef:

<xsl:template match="CrossRef">
  <xsl:variable name="target-id">
    <xsl:value-of select="@refsub"/>
  </xsl:variable>
  <xsl:variable name="target-element">
    <xsl:copy-of select="key('ids', $target-id)"/>
  </xsl:variable>
  <xsl:apply-templates select="$target-element" mode="xref"/>
</xsl:template>

target-element is a Figure element. I know I'm getting the right target element because the caption text is correct.

Wendell Piez replied:

Ah, it's interesting you're able to apply templates to $target-element, since it's not actually a node in your source, it's a result-tree-fragment. (In XSLT 1.0 you shouldn't be able to do this.)

Try setting your variable as

   <xsl:variable name="target-element" select="key('ids', $target-id)"/>

to get it to be be, truly, the node in your source, and not a copy (which loses the context).

16.

Numbering, problems with from and count attributes

Jeni Tennison



> Do you know what's the difference between <xsl:number level="single" 
> from="*" count="SEC" format="1. "/> and <xsl:number level="single" 
> from="." count="SEC" format="1. "/>
>
> The second one sometimes does not work by giving error message:
>
> XSLException Type is: XPathParserException Message is: Expected node 
> test.
> pattern = '.'
> Remaining tokens: ('.')

The value of the from attribute should be an *XSLT pattern* -- the same kind of value as that held in the match attribute of <xsl:template>. The purpose of patterns is to test nodes based on their kind, name, ancestry and so on. In this case, the pattern held by the from attribute is used to test all the ancestors of the node you're on (the context node), and you get numbering within the closest ancestor that matches that pattern. In this case, the pattern "*" matches all elements, so you get numbering within the parent element of the node you're on (which is presumably a <SEC> element).

The value "." is a *XPath expression* (that selects the context node). It isn't a legal XSLT pattern, which is why you're getting the error message.

17.

Is there a way to do numbering of paragraphs?

Jens Stavnstrup

Here is one attempt, I have been using. Both methods depends on the global parameter

<xsl:param name="use.para.numbering" select="1"/>

The algorithm make a number of assumsions, which you might have to correct for:

- you are using a book as the root element
- you will never have more than 999 paragraphs.
- you are only using sect1-sect5 elements in your chapter

Ednote: This assumes one docbook structure. Replace the wrapper element names with those you use.

For HTML/XHTML use:

<xsl:template match="chapter/para
                     |appendix/para
                     |sect1/para
                     |sect2/para
                     |sect3/para
                     |sect4/para
                     |sect5/para">
  <p>
    <xsl:if test="$use.para.numbering != 0">
      <xsl:text></xsl:text>
      <xsl:number from="book"
                 count="para[parent::chapter or
                             parent::appendix or
                             parent::sect1 or
                             parent::sect2 or
                             parent::sect3 or
                             parent::sect4 or
                             parent::sect5
                               ]" format="1" level="any"/>
      <xsl:text>. </xsl:text>
    </xsl:if>
    <xsl:call-template name="anchor"/>
    <xsl:apply-templates/>
  </p>
</xsl:template>

For FO use:

<xsl:template match="chapter/para
                     |appendix/para
                     |sect1/para
                     |sect2/para
                     |sect3/para
                     |sect4/para
                     |sect5/para">
  <fo:block xsl:use-attribute-sets="normal.para.spacing">
    <xsl:if test="$use.para.numbering != 0">
      <xsl:number from="book"
                 count="para[parent::chapter or
                               parent::appendix or
                               parent::sect1 or
                               parent::sect2 or
                               parent::sect3 or
                               parent::sect4 or
                               parent::sect5]" format="111" level="any"/>
      <xsl:text>. </xsl:text>
    </xsl:if>
    <xsl:apply-templates/>
  </fo:block>
</xsl:template>

18.

Numbering Ordinals

Abel Braaksma



>Is there  an elegant
> way to format position() like:
> 1st 2nd 3rd and so on. I've looked through google for a format that
> works with xsl:number, but haven't been able to find anything. I know
> I could always do a long <xsl:choose> statement, but it feels wrong.
> Anyway here's some samples
>
> XML
>
> <root>
>  <num>foo</num>
>  <num>bar</num>
> </root>
>
> Output
>
> <root>
> <num>1st</num>
> <num>2nd</num>
> </root>

Someone already pointed out that 11..13 are exceptions to the mod 10 rule. This is the simplest I could think of and it works up to 1000 at least (didn't check every number though) and it may even work for higher numbers. Lucky thing: not a huge <xsl:choose>, just a tiny one.

Mark the line that says:

<xsl:with-param name="top" select="200" />

Set it to any value to extend the list. Every number from 1 to $top is output. You prob. only need the tiny piece that is called "ordinal". The stylesheet works with any input, as it does not do anything with the input.

Have fun with it ;)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
   <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

   <xsl:template match="/" >
       <ordinals>
           <xsl:call-template name="sequence">
               <xsl:with-param name="top" select="200" />
           </xsl:call-template>
       </ordinals>
   </xsl:template>

   <!-- returns a node set of ord-nodes with all
        numbers up to a given top -->
   <xsl:template name="sequence" >
       <xsl:param name="top" />
       <xsl:if test="$top &gt; 1" >
           <xsl:call-template name="sequence">
               <xsl:with-param name="top" select="$top - 1" />
           </xsl:call-template>
       </xsl:if>
       <ord>
           <xsl:value-of select="$top" />
           <xsl:call-template name="ordinal">
               <xsl:with-param name="number" select="$top" />
           </xsl:call-template>
       </ord>
   </xsl:template>

   <!-- gets ordinal of a number in english -->
   <xsl:template name="ordinal">
       <xsl:param name="number" />
       <xsl:choose>
           <xsl:when test="$number mod 100 = 11">th</xsl:when>
           <xsl:when test="$number mod 100 = 12">th</xsl:when>
           <xsl:when test="$number mod 100 = 13">th</xsl:when>
           <xsl:when test="$number mod 10 = 3">rd</xsl:when>
           <xsl:when test="$number mod 10 = 2">nd</xsl:when>
           <xsl:when test="$number mod 10 = 1">st</xsl:when>
           <xsl:otherwise>th</xsl:otherwise>
       </xsl:choose>
   </xsl:template>
</xsl:stylesheet>