]> git.stg.codes - stg.git/blob - doc/xslt/manpages/table.xsl
Ticket 26. The SETTINGS_IMPL & operator() declaration added.
[stg.git] / doc / xslt / manpages / table.xsl
1 <?xml version="1.0"?>
2 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
3                 xmlns:exsl="http://exslt.org/common"
4                 exclude-result-prefixes="exsl"
5                 version='1.0'>
6
7   <!-- ********************************************************************
8        $Id: table.xsl 8400 2009-04-08 07:44:54Z bobstayton $
9        ********************************************************************
10
11        This file is part of the XSL DocBook Stylesheet distribution.
12        See ../README or http://docbook.sf.net/release/xsl/current/ for
13        copyright and other information.
14
15        ******************************************************************** -->
16   <!--
17   <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl"/>
18   <xsl:param name="tbl.font.title">B</xsl:param>
19   <xsl:param name="tbl.font.headings">B</xsl:param>
20   -->
21   <xsl:param name="tbl.running.header.from.thead" select="0"/>
22   <xsl:param name="tbl.column.separator.char">:</xsl:param>
23
24   <!-- ==================================================================== -->
25
26   <!-- * This stylesheet transforms DocBook and HTML table source into -->
27   <!-- * tbl(1) markup. -->
28   <!-- * -->
29   <!-- * For details on tbl(1) and its markup syntaxt, see M. E. Lesk,-->
30   <!-- * "Tbl - A Program to Format Tables": -->
31   <!-- * -->
32   <!-- *   http://cm.bell-labs.com/7thEdMan/vol2/tbl -->
33   <!-- *   http://cm.bell-labs.com/cm/cs/doc/76/tbl.ps.gz -->
34   <!-- *   http://www.snake.net/software/troffcvt/tbl.html -->
35
36   <xsl:template match="table|informaltable" mode="to.tbl">
37     <!--* the "source" param is an optional param; it can be any -->
38     <!--* string you want to use that gives some indication of the -->
39     <!--* source context for a table; it gets passed down to the named -->
40     <!--* templates that do the actual table processing; this -->
41     <!--* stylesheet currently uses the "source" information for -->
42     <!--* logging purposes -->
43     <xsl:param name="source"/>
44     <xsl:param name="title">
45       <xsl:if test="local-name(.) = 'table'">
46         <xsl:apply-templates select="." mode="object.title.markup.textonly"/>
47       </xsl:if>
48     </xsl:param>
49     <!-- * ============================================================== -->
50     <!-- *    Set global table parameters                                 -->
51     <!-- * ============================================================== -->
52     <!-- * First, set a few parameters based on attributes specified in -->
53     <!-- * the table source. -->
54     <xsl:param name="allbox">
55     <xsl:if test="not(@frame = 'none') and not(@border = '0')">
56       <!-- * By default, put a box around table and between all cells, -->
57       <!-- * unless frame="none" or border="0" -->
58       <xsl:text>allbox </xsl:text>
59     </xsl:if>
60     </xsl:param>
61     <xsl:param name="center">
62     <!-- * If align="center", center the table. Otherwise, tbl(1) -->
63     <!-- * left-aligns it by default; note that there is no support -->
64     <!-- * in tbl(1) for specifying right alignment. -->
65     <xsl:if test="@align = 'center' or tgroup/@align = 'center'">
66       <xsl:text>center </xsl:text>
67     </xsl:if>
68     </xsl:param>
69     <xsl:param name="expand">
70     <!-- * If pgwide="1" or width="100%", then "expand" the table by -->
71     <!-- * making it "as wide as the current line length" (to quote -->
72     <!-- * the tbl(1) guide). -->
73     <xsl:if test="@pgwide = '1' or @width = '100%'">
74       <xsl:text>expand </xsl:text>
75     </xsl:if>
76     </xsl:param>
77
78     <!-- * ============================================================== -->
79     <!-- *    Convert table to HTML                                       -->
80     <!-- * ============================================================== -->
81     <!-- * Process the table by applying the HTML templates from the -->
82     <!-- * DocBook XSL stylesheets to the whole thing; because we don't -->
83     <!-- * override any of the <row>, <entry>, <tr>, <td>, etc. templates, -->
84     <!-- * the templates in the HTML stylesheets (which we import) are -->
85     <!-- * used to process those. -->
86     <xsl:param name="html-table-output">
87       <xsl:choose>
88         <xsl:when test=".//tr">
89           <!-- * If this table has a TR child, it means that it's an -->
90           <!-- * HTML table in the DocBook source, instead of a CALS -->
91           <!-- * table. So we just copy it as-is, while wrapping it -->
92           <!-- * in an element with same name as its original parent. -->
93           <xsl:for-each select="descendant-or-self::table|descendant-or-self::informaltable">
94             <xsl:element name="{local-name(..)}">
95               <table>
96                 <xsl:copy-of select="*"/>
97               </table>
98             </xsl:element>
99           </xsl:for-each>
100         </xsl:when>
101         <xsl:otherwise>
102           <!-- * Otherwise, this is a CALS table in the DocBook source, -->
103           <!-- * so we need to apply the templates in the HTML -->
104           <!-- * stylesheets to transform it into HTML before we do -->
105           <!-- * any further processing of it. -->
106           <xsl:apply-templates/>
107         </xsl:otherwise>
108       </xsl:choose>
109     </xsl:param>
110     <xsl:param name="contents" select="exsl:node-set($html-table-output)"/>
111
112     <!-- ==================================================================== -->
113     <!-- *                       Output the table -->
114     <!-- ==================================================================== -->
115     <!-- * -->
116     <!-- * This is the "driver" part of the code; it calls a series of named
117          * templates (further below) to generate the actual tbl(1) markup, -->
118     <!-- * including the optional "options line", required "format section", -->
119     <!-- * and then the actual contents of the table. -->
120     <!-- * -->
121     <!-- ==================================================================== -->
122
123     <xsl:for-each select="$contents//table">
124       <!-- * ============================================================== -->
125       <!-- *   Output table title                                           -->
126       <!-- * ============================================================== -->
127       <xsl:if test="$title != '' or parent::td">
128         <xsl:text>.sp&#10;</xsl:text>
129         <xsl:call-template name="pinch.together"/>
130         <xsl:text>.</xsl:text>
131         <xsl:value-of select="$tbl.font.title"/>
132         <xsl:text> </xsl:text>
133         <xsl:if test="parent::td">
134           <xsl:text>*[nested&#x2580;table]</xsl:text>
135         </xsl:if>
136         <xsl:value-of select="normalize-space($title)"/>
137         <xsl:text>&#10;</xsl:text>
138       </xsl:if>
139       
140       <!-- * mark the start of the table -->
141       <!-- * "TS" = "table start" -->
142       <xsl:text>.TS</xsl:text>
143       <xsl:if test="thead and $tbl.running.header.from.thead">
144         <!-- * H = "has header" -->
145         <xsl:text> H</xsl:text>
146       </xsl:if>
147       <xsl:text>&#10;</xsl:text>
148
149       <!-- * ============================================================== -->
150       <!-- *   Output "options line"                                         -->
151       <!-- * ============================================================== -->
152       <xsl:variable name="options-line">
153         <xsl:value-of select="$allbox"/>
154         <xsl:value-of select="$center"/>
155         <xsl:value-of select="$expand"/>
156         <xsl:text>tab(</xsl:text>
157         <xsl:value-of select="$tbl.column.separator.char"/>
158         <xsl:text>)</xsl:text>
159       </xsl:variable>
160       <xsl:if test="normalize-space($options-line) != ''">
161         <xsl:value-of select="normalize-space($options-line)"/>
162         <xsl:text>;&#10;</xsl:text>
163       </xsl:if>
164
165       <!-- * ============================================================== -->
166       <!-- *   Output table header rows                                     -->
167       <!-- * ============================================================== -->
168       <xsl:if test="thead">
169         <xsl:call-template name="output.rows">
170           <xsl:with-param name="rows" select="thead/tr"/>
171         </xsl:call-template> 
172         <xsl:text>&#10;</xsl:text>
173
174         <!-- * mark the end of table-header rows -->
175         <xsl:choose>
176           <xsl:when test="$tbl.running.header.from.thead">
177             <!-- * "TH" = "table header end" -->
178             <xsl:text>.TH&#10;</xsl:text>
179           </xsl:when>
180           <xsl:otherwise>
181             <!-- * "T&" = "table continuation" and is meant just as a kind -->
182             <!-- * of convenience macro and is sorta equivalent to a "TE" -->
183             <!-- * (table end) followed immediately by a "TS" (table start); -->
184             <!-- * in this case, it marks the end of a table "subsection" -->
185             <!-- * with header rows, and the start of a subsection with body -->
186             <!-- * rows. It's necessary to output it here because the "TH" -->
187             <!-- * macro is not being output, so there's otherwise no way -->
188             <!-- * for tbl(1) to know we have the table "sectioned". -->
189             <xsl:text>.T&amp;&#10;</xsl:text>
190           </xsl:otherwise>
191         </xsl:choose>
192       </xsl:if>
193       
194       <!-- * ============================================================== -->
195       <!-- *  Output table body rows                                        -->
196       <!-- * ============================================================== -->
197       <!-- * First create node set with all non-thead rows (tbody+tfoot), -->
198       <!-- * but reordered with the tfoot rows at the end of the node set -->
199       <xsl:variable name="rows-set">
200         <xsl:copy-of select="tbody/tr|tr"/>
201         <xsl:copy-of select="tfoot/tr"/>
202       </xsl:variable>
203       <xsl:call-template name="output.rows">
204         <xsl:with-param name="source" select="$source"/>
205         <xsl:with-param name="rows" select="exsl:node-set($rows-set)"/>
206       </xsl:call-template>
207
208       <!-- * mark the end of the table -->
209       <xsl:text>&#10;</xsl:text>
210       <!-- * .TE = "Table End" -->
211       <xsl:text>.TE&#10;</xsl:text>
212       <!-- * put a blank line of space below the table -->
213       <xsl:text>.sp 1&#10;</xsl:text>
214     </xsl:for-each>
215   </xsl:template>
216
217   <!-- ==================================================================== -->
218   <!-- *                        named templates -->
219   <!-- ==================================================================== -->
220   <!-- * -->
221   <!-- * All of the following are named templates that get called directly -->
222   <!-- * or indirectly by the main "driver" part of the code (above) -->
223   <!-- * -->
224   <!-- ==================================================================== -->
225   
226   <xsl:template name="output.rows">
227     <xsl:param name="source"/>
228     <xsl:param name="rows"/>
229     <!-- * ============================================================== -->
230     <!-- *   Flatten row set into simple list of cells                    -->
231     <!-- * ============================================================== -->
232     <!-- * Now we flatten the structure further into just a set of -->
233     <!-- * cells without the row parents. This basically creates a -->
234     <!-- * copy of the entire contents of the original table, but -->
235     <!-- * restructured in such a way that we can more easily generate -->
236     <!-- * the corresponding tbl(1) markup we need to output. -->
237     <xsl:variable name="cells-list">
238       <xsl:call-template name="build.cell.list">
239         <xsl:with-param name="source" select="$source"/>
240         <xsl:with-param name="rows" select="$rows"/>
241       </xsl:call-template>
242     </xsl:variable>
243     <xsl:variable name="cells" select="exsl:node-set($cells-list)"/>
244
245     <!-- * Output the table "format section", which tells tbl(1) how to -->
246     <!-- * format each row and column -->
247     <xsl:call-template name="create.table.format">
248       <xsl:with-param name="cells" select="$cells"/>
249     </xsl:call-template>
250
251     <!--* Output the formatted contents of each cell. -->
252     <xsl:for-each select="$cells/cell">
253       <xsl:call-template name="output.cell"/>
254     </xsl:for-each>
255   </xsl:template>
256
257   <!-- * ============================================================== -->
258   <!-- *    Output the tbl(1)-formatted contents of each cell.            -->
259   <!-- * ============================================================== -->
260   <xsl:template name="output.cell">
261     <xsl:choose>
262       <xsl:when test="preceding-sibling::cell[1]/@row != @row or
263                       not(preceding-sibling::cell)">
264         <!-- * If the value of the "row" attribute on this cell is -->
265         <!-- * different from the value of that on the previous cell, it -->
266         <!-- * means we have a new row. So output a line break (as long -->
267         <!-- * as this isn't the first cell in the table) -->
268         <xsl:text>&#10;</xsl:text>
269       </xsl:when>
270       <xsl:otherwise>
271         <!-- * Otherwise we are not at the start of a new row, so we -->
272         <!-- * output a tab character to delimit the contents of this -->
273         <!-- * cell from the contents of the next one. -->
274         <xsl:value-of select="$tbl.column.separator.char"/>
275       </xsl:otherwise>
276     </xsl:choose>
277     <xsl:choose>
278       <xsl:when test="@type = '^'">
279         <!-- * If this is a dummy cell resulting from the presence of -->
280         <!-- * rowpan attribute in the source, it has no contents, so -->
281         <!-- * we need to handle it differently. -->
282         <xsl:if test="@colspan and @colspan > 1">
283           <!-- * If there is a colspan attribute on this dummy row, then -->
284           <!-- * we need to output a tab character for each column that -->
285           <!-- * it spans. -->
286           <xsl:call-template name="copy-string">
287             <xsl:with-param name="string" select="$tbl.column.separator.char"/>
288             <xsl:with-param name="count">
289               <xsl:value-of select="@colspan - 1"/>
290             </xsl:with-param>
291           </xsl:call-template>
292         </xsl:if>
293       </xsl:when>
294       <xsl:otherwise>
295         <!-- * Otherwise, we have a "real" cell (not a dummy one) with -->
296         <!-- * contents that we need to output, -->
297         <!-- * -->
298         <!-- * The "T{" and "T}" stuff are delimiters to tell tbl(1) that -->
299         <!-- * the delimited contents are "text blocks" that roff -->
300         <!-- * needs to process -->
301         <xsl:text>T{&#10;</xsl:text>
302         <xsl:copy-of select="."/>
303         <xsl:text>&#10;T}</xsl:text>
304       </xsl:otherwise>
305     </xsl:choose>
306   </xsl:template>
307
308   <!-- * ============================================================== -->
309   <!-- *   Build a restructured "cell list" copy of the entire table    -->
310   <!-- * ============================================================== -->
311   <xsl:template name="build.cell.list">
312     <xsl:param name="source"/>
313     <xsl:param name="rows"/>
314     <xsl:param  name="cell-data-unsorted">
315       <!-- * This param collects all the "real" cells from the table, -->
316       <!-- * along with "dummy" rows that we generate for keeping -->
317       <!-- * track of Rowspan instances. -->
318       <xsl:apply-templates select="$rows" mode="cell.list">
319         <xsl:with-param name="source" select="$source"/>
320       </xsl:apply-templates>
321     </xsl:param>
322     <xsl:param  name="cell-data-sorted">
323       <!-- * Sort the cells so that the dummy cells get put where we -->
324       <!-- * need them in the structure. Note that we need to specify -->
325       <!-- * data-type="number" here because the default sorting method -->
326       <!-- * for xsl:sort is "text" (alphabetical). -->
327       <xsl:for-each select="exsl:node-set($cell-data-unsorted)/cell">
328         <xsl:sort select="@row" data-type="number"/>
329         <xsl:sort select="@slot" data-type="number"/>
330         <xsl:copy-of select="."/>
331       </xsl:for-each>
332     </xsl:param>
333     <!-- * Return the sorted cell list -->
334     <xsl:copy-of select="$cell-data-sorted"/>
335   </xsl:template>
336
337   <xsl:template match="tr" mode="cell.list">
338     <xsl:param name="source"/>
339     <xsl:variable name="row">
340       <xsl:value-of select="count(preceding-sibling::tr) + 1"/>
341     </xsl:variable>
342     <xsl:for-each select="td|th">
343       <xsl:call-template name="cell">
344         <xsl:with-param name="source" select="$source"/>
345         <xsl:with-param name="row" select="$row"/>
346         <!-- * pass on the element name so we can select the appropriate -->
347         <!-- * roff font for styling the cell contents -->
348         <xsl:with-param name="class" select="name(.)"/>
349       </xsl:call-template>
350     </xsl:for-each>
351   </xsl:template>
352
353   <xsl:template name="cell">
354     <xsl:param name="source"/>
355     <xsl:param name="row"/>
356     <xsl:param name="class"/>
357     <xsl:param name="slot">
358       <!-- * The "slot" is the horizontal position of this cell (usually -->
359       <!-- * just the same as its column, but not so when it is preceded -->
360       <!-- * by cells that have colspans or cells in preceding rows that -->
361       <!-- * that have rowspans). -->
362       <xsl:value-of select="position()"/>
363     </xsl:param>
364     <!-- * For each real TD cell, create a Cell instance; contents will -->
365     <!-- * be the roff-formatted contents of its original table cell. -->
366     <cell type=""
367           row="{$row}"
368           slot="{$slot}"
369           class="{$class}"
370           colspan="{@colspan}"
371           align="{@align}"
372           valign="{@valign}"
373           >
374       <xsl:choose>
375         <xsl:when test=".//tr">
376           <xsl:call-template name="log.message">
377             <xsl:with-param name="level">Warn</xsl:with-param>
378             <xsl:with-param name="source" select="$source"/>
379             <xsl:with-param name="context-desc">tbl convert</xsl:with-param>
380             <xsl:with-param name="message">
381               <xsl:text>Extracted a nested table</xsl:text>
382             </xsl:with-param>
383           </xsl:call-template>
384           <xsl:text>[\fInested&#x2580;table\fR]*&#10;</xsl:text>
385         </xsl:when>
386         <xsl:otherwise>
387           <!-- * Apply templates to the child contents of this cell, to -->
388           <!-- * transform them into marked-up roff. -->
389           <xsl:variable name="contents">
390             <xsl:apply-templates/>
391           </xsl:variable>
392           <!-- * We now have the contents in roff (plain-text) form, -->
393           <!-- * but we may also still have unnecessary whitespace at -->
394           <!-- * the beginning and/or end of it, so trim it off. -->
395           <xsl:call-template name="trim.text">
396             <xsl:with-param name="contents" select="$contents"/>
397           </xsl:call-template>
398         </xsl:otherwise>
399       </xsl:choose>
400     </cell>
401
402     <!-- * For each instance of a rowspan attribute found, we create N -->
403     <!-- * dummy cells, where N is equal to the value of the rowspan. -->
404     <xsl:if test="@rowspan and @rowspan > 0">
405       <!-- * If this cell is preceded in the same row by cells that -->
406       <!-- * have colspan attributes, then we need to calculate the -->
407       <!-- * "offset" caused by those colspan instances; the formula -->
408       <!-- * is to (1) check for all the preceding cells that have -->
409       <!-- * colspan attributes that are not empty and which have a -->
410       <!-- * value greater than 1, then (2) take the sum of the values -->
411       <!-- * of all those colspan attributes, and subtract from that -->
412       <!-- * the number of such colspan instances found. -->
413       <xsl:variable name="colspan-offset">
414         <xsl:value-of
415             select="sum(preceding-sibling::td[@colspan != ''
416                     and @colspan > 1]/@colspan) -
417                     count(preceding-sibling::td[@colspan != ''
418                     and @colspan > 1]/@colspan)"/>
419       </xsl:variable>
420       <xsl:call-template name="create.dummy.cells">
421         <xsl:with-param name="row" select="$row + 1"/>
422         <!-- * The slot value on each dummy cell must be offset by the -->
423         <!-- * value of $colspan-offset to adjust for preceding colpans -->
424         <xsl:with-param name="slot" select="$slot + $colspan-offset"/>
425         <xsl:with-param name="colspan" select="@colspan"/>
426         <xsl:with-param name="rowspan" select="@rowspan"/>
427       </xsl:call-template>
428     </xsl:if>
429   </xsl:template>
430
431   <xsl:template name="create.dummy.cells">
432     <xsl:param name="row"/>
433     <xsl:param name="slot"/>
434     <xsl:param name="colspan"/>
435     <xsl:param name="rowspan"/>
436     <xsl:choose>
437       <xsl:when test="$rowspan > 1">
438         <!-- * Tail recurse until we have no more rowspans, creating -->
439         <!-- * an empty dummy cell each time. The type value, '^' -->
440         <!-- * is the marker that tbl(1) uses to indicate a -->
441         <!-- * "vertically spanned heading". -->
442         <cell row="{$row}" slot="{$slot}" type="^" colspan="{@colspan}"/>
443         <xsl:call-template name="create.dummy.cells">
444           <xsl:with-param name="row" select="$row + 1"/>
445           <xsl:with-param name="slot" select="$slot"/>
446           <xsl:with-param name="colspan" select="$colspan"/>
447           <xsl:with-param name="rowspan" select="$rowspan - 1"/>
448         </xsl:call-template>
449       </xsl:when>
450     </xsl:choose>
451   </xsl:template>
452
453   <!-- * ============================================================== -->
454   <!-- *    Build the "format section" for the table                    -->
455   <!-- * ============================================================== -->
456   <!-- * Description from the tbl(1) guide: -->
457   <!-- * -->
458   <!-- * "The format section of the table specifies the layout of the -->
459   <!-- * columns.  Each line in this section corresponds to one line of -->
460   <!-- * the table... and each line contains a key-letter for each -->
461   <!-- * column of the table." -->
462   <xsl:template name="create.table.format">
463     <xsl:param name="cells"/>
464     <xsl:apply-templates mode="table.format" select="$cells"/>
465     <!-- * last line of table format section must end with a dot -->
466     <xsl:text>.</xsl:text>
467   </xsl:template>
468
469   <xsl:template match="cell" mode="table.format">
470     <xsl:choose>
471       <xsl:when test="preceding-sibling::cell[1]/@row != @row">
472         <!-- * If the value of the row attribute on this cell is -->
473         <!-- * different from the value of that on the previous cell, it -->
474         <!-- * means we have a new row. So output a line break. -->
475         <xsl:text>&#xa;</xsl:text>
476       </xsl:when>
477       <xsl:otherwise>
478         <!-- * If this isn't the first cell, output a space before it to -->
479         <!-- * separate it from the preceding key letter. -->
480         <xsl:if test="position() != 1">
481           <xsl:text> </xsl:text>
482         </xsl:if>
483       </xsl:otherwise>
484     </xsl:choose>
485     <!-- * Select an appropriate "alignment" key letter based on this -->
486     <!-- * cell's attributes. -->
487     <xsl:choose>
488       <xsl:when test="@type = '^'">
489         <xsl:text>^</xsl:text>
490       </xsl:when>
491       <xsl:when test="@align = 'center'">
492         <xsl:text>c</xsl:text>
493       </xsl:when>
494       <xsl:when test="@align = 'right'">
495         <xsl:text>r</xsl:text>
496       </xsl:when>
497       <xsl:when test="@align = 'char'">
498         <xsl:text>n</xsl:text>
499       </xsl:when>
500       <xsl:otherwise>
501         <!-- * Default to left alignment. -->
502         <xsl:text>l</xsl:text>
503       </xsl:otherwise>
504     </xsl:choose>
505     <!-- * By default, tbl(1) vertically centers cell contents within -->
506     <!-- * their cells; the "t" key latter tells it to top-align the -->
507     <!-- * contents instead. Note that tbl(1) has no options for -->
508     <!-- * bottom or baseline alignment. -->
509     <xsl:if test="@valign = 'top'">
510       <xsl:text>t</xsl:text>
511     </xsl:if>
512     <xsl:if test="@class = 'th'">
513       <!-- * If this is a heading row, generate a font indicator (B or I), -->
514       <!-- * or if the value of $tbl.font.headings is empty, nothing. -->
515       <xsl:value-of select="$tbl.font.headings"/>
516     </xsl:if>
517     <!-- * We only need to deal with colspans whose value is greater -->
518     <!-- * than one (a colspan="1" is the same as having no colspan -->
519     <!-- * attribute at all). -->
520     <xsl:if test="@colspan > 1">
521       <xsl:call-template name="process.colspan">
522         <xsl:with-param name="colspan" select="@colspan - 1"/>
523         <xsl:with-param name="type" select="@type"/>
524       </xsl:call-template>
525     </xsl:if>
526   </xsl:template>
527   
528   <xsl:template name="process.colspan">
529     <xsl:param name="colspan"/>
530     <xsl:param name="type"/>
531     <!-- * Output a space to separate this key letter from preceding one. -->
532     <xsl:text> </xsl:text>
533     <xsl:choose>
534       <xsl:when test="$type = '^'">
535         <!-- * A '^' ("vertically spanned heading" marker) indicates -->
536         <!-- * that the "parent" of this spanned cell is a dummy cell; -->
537         <!-- * in this case, we need to generate a '^' instead of the -->
538         <!-- * normal 's'. -->
539         <xsl:text>^</xsl:text>
540       </xsl:when>
541       <xsl:otherwise>
542         <!-- * s = 'spanned heading' -->
543         <xsl:text>s</xsl:text>
544       </xsl:otherwise>
545     </xsl:choose>
546     <xsl:if test="$colspan > 1">
547       <!-- * Tail recurse until we have no more colspans, outputting -->
548       <!-- * another marker each time. -->
549       <xsl:call-template name="process.colspan">
550         <xsl:with-param name="colspan" select="$colspan - 1"/>
551         <xsl:with-param name="type" select="$type"/>
552       </xsl:call-template>
553     </xsl:if>
554   </xsl:template>
555
556   <!-- * ============================================================== -->
557   <!-- *    colgroup and col                                            -->
558   <!-- * ============================================================== -->
559   <!-- * We currently don't do anything with colgroup. Not sure if it -->
560   <!-- * is widely used enough to bother adding support for it -->
561   <xsl:template match="colgroup"/>
562   <xsl:template match="col"/>
563
564   <!-- * ============================================================== -->
565   <!-- *    table footnotes                                      -->
566   <!-- * ============================================================== -->
567   <xsl:template match="footnote" mode="table.footnote.mode">
568     <xsl:variable name="footnotes" select=".//footnote"/>
569     <xsl:variable name="table.footnotes"
570                   select=".//tgroup//footnote"/>
571     <xsl:value-of select="$man.table.footnotes.divider"/>
572     <xsl:text>&#10;</xsl:text>
573     <xsl:text>.br&#10;</xsl:text>
574     <xsl:apply-templates select="*[1]" mode="footnote.body.number"/>
575     <xsl:apply-templates select="*[position() &gt; 1]"/>
576   </xsl:template>
577
578   <!-- * The following template for footnote.body.number mode was just -->
579   <!-- * lifted from the HTML stylesheets with some minor adjustments -->
580   <xsl:template match="*"  mode="footnote.body.number">
581     <xsl:variable name="name">
582       <xsl:text>ftn.</xsl:text>
583       <xsl:call-template name="object.id">
584         <xsl:with-param name="object" select="ancestor::footnote"/>
585       </xsl:call-template>
586     </xsl:variable>
587     <xsl:variable name="href">
588       <xsl:text>#</xsl:text>
589       <xsl:call-template name="object.id">
590         <xsl:with-param name="object" select="ancestor::footnote"/>
591       </xsl:call-template>
592     </xsl:variable>
593     <xsl:variable name="footnote.mark">
594       <xsl:text>[</xsl:text>
595       <xsl:apply-templates select="ancestor::footnote"
596                            mode="footnote.number"/>
597       <xsl:text>]&#10;</xsl:text>
598     </xsl:variable>
599     <xsl:variable name="html">
600       <xsl:apply-templates select="."/>
601     </xsl:variable>
602     <xsl:choose>
603       <xsl:when test="$exsl.node.set.available != 0">
604         <xsl:variable name="html-nodes" select="exsl:node-set($html)"/>
605         <xsl:choose>
606           <xsl:when test="$html-nodes//p">
607             <xsl:apply-templates select="$html-nodes" mode="insert.html.p">
608               <xsl:with-param name="mark" select="$footnote.mark"/>
609             </xsl:apply-templates>
610           </xsl:when>
611           <xsl:otherwise>
612             <xsl:apply-templates select="$html-nodes" mode="insert.html.text">
613               <xsl:with-param name="mark" select="$footnote.mark"/>
614             </xsl:apply-templates>
615           </xsl:otherwise>
616         </xsl:choose>
617       </xsl:when>
618       <xsl:otherwise>
619         <xsl:copy-of select="$html"/>
620       </xsl:otherwise>
621     </xsl:choose>
622   </xsl:template>
623
624   <!-- * The HTML stylesheets output <sup><a>...</a></sup> around -->
625   <!-- * footnote markers in tables -->
626   <xsl:template match="th/sup">
627     <xsl:apply-templates/>
628   </xsl:template>
629   <xsl:template match="a">
630     <xsl:apply-templates/>
631   </xsl:template>
632
633 </xsl:stylesheet>