]> granicus.if.org Git - docbook-dsssl/commitdiff
Misc files needed to create docbook5ns-xsl from docbook-xsl.
authorBob Stayton <bobs@sagehill.net>
Thu, 18 Jan 2007 06:27:36 +0000 (06:27 +0000)
committerBob Stayton <bobs@sagehill.net>
Thu, 18 Jan 2007 06:27:36 +0000 (06:27 +0000)
releasetools/db5files/README [new file with mode: 0644]
releasetools/db5files/VERSION [new file with mode: 0644]
releasetools/db5files/db5.xsl [new file with mode: 0644]
releasetools/db5files/manpages.table.xsl [new file with mode: 0644]

diff --git a/releasetools/db5files/README b/releasetools/db5files/README
new file mode 100644 (file)
index 0000000..59e8f05
--- /dev/null
@@ -0,0 +1,158 @@
+Candidate DocBook 5 XSL stylesheets
+===========================================
+Bob Stayton
+8 January 2007
+
+This package contains a candidate release of XSL stylesheets
+for processing DocBook 5 documents.  The stylesheets are
+the same as the snapshot stylesheet release of 2006-12-13 except that
+the templates match on elements in the DocBook namespace.
+
+Note, the stylesheets in these directories have
+not yet been converted to use the DocBook 5 namespace:
+
+  slides
+  website
+  wordml
+
+
+Background
+-------------
+DocBook 5 differs from preceding versions of DocBook because
+its elements are in a namespace "http://docbook.org/ns/docbook".
+Because the elements are in a namespace, the regular
+DocBook XSL templates do not match on the elements.
+In XSLT, a match attribute must explicitly specify
+the namespace prefix to match an element in that namespace
+(the default namespace does not apply to pattern matches).
+
+The regular stylesheets are able to process DocBook 5
+documents now, because they preprocess a DB5 document to
+remove the namespace.  When the regular stylesheet
+detects that the root element is in the namespace, it
+processes the document with mode="stripNS" to copy all
+the nodes to a variable, but without the DocBook namespace.
+Then it converts the variable to a nodeset, and processes
+the nodeset with the regular templates.
+
+The alternative approach is to create a set of templates
+that match on the native namespace of DocBook 5 documents.
+These stylesheets do that.
+
+These stylesheets completely mimic the behavior of the
+existing stylesheets.  These are not XSLT 2.0 stylesheets,
+and they do not have any other significant changes than handling
+the namespaced elements.
+
+The two main advantages of these stylesheets are:
+
+a. You can write customization layers using the DocBook namespace.
+
+b. The xml:base of the root element is not lost during processing
+(so things like images and the olink database can be found
+more easily).
+
+
+How these stylesheets were produced
+----------------------------------------
+These stylesheets were created from a DocBook XSL 
+snapshot.  Each xsl file was processed with a
+Perl script and its output placed in a parallel
+directory tree.  Any non-xsl files were simply copied
+into place.
+
+Perl was used because of its excellent regular expression
+handling, and because Perl is able to preserve all whitespace.
+This retains the pretty-printed format that makes
+the stylesheets easier to read and understand.
+
+The stylesheet transforms each XSL match, select, test,
+count, from, use, and elements attribute to add a "d:"
+prefix to each element referenced in the attribute.
+For example:
+
+  <xsl:template match="para">
+
+becomes:
+
+  <xsl:template match="d:para">
+
+In addition, each stylesheet file has a namespace declaration
+added in its root element:
+
+  <xsl:stylesheet  xslns:d="http://docbook.org/ns/docbook"
+
+The combination of these two changes means that the templates
+now recognize DocBook 5 elements in their native namespace.
+
+
+How to use these stylesheets
+--------------------------------
+A DocBook 5 document has the DocBook namespace declaration
+in the root element, along with the version attribute:
+
+  <book xslns="http://docbook.org/ns/docbook" version="5.0">
+
+You can process a DocBook 5 document with these stylesheets
+using any XSLT processor, including xsltproc, Saxon 6 or 8,
+and Xalan.  Use these stylesheets as you would a stylesheet
+from the regular distribution (except for slides, website,
+and wordml).
+
+If you happen to process a DocBook document whose root element
+is without the namespace declaration, the stylesheet
+does not fail.  Rather, it detects that the document
+does not have the namespace, and preprocesses it to add the
+namespace declaration to all elements in the document.
+In a manner similar to stripNS, it copies the elements
+to a variable while adding the namespace, converts the
+variable to a nodeset, and then processes the nodeset with
+the namespace-aware templates.
+
+If the stylesheet encounters an element from your file
+for which the stylesheet has no matching template,
+it reports the unmatched element.  In these stylesheets,
+it also reports the namespace URI that the element has.
+
+
+Customizing these stylesheets
+--------------------------------
+These stylesheets are customized with a customization
+layer in the same manner as for the regular stylesheets,
+with two differences.
+
+When you create a customization layer, you must do two things:
+
+a.  Add the namespace declaration (with a prefix of your choice):
+
+  <xsl:stylesheet  xslns:d="http://docbook.org/ns/docbook"
+
+b.  Use the namespace prefix on all DocBook element names:
+
+  <xsl:template match="d:formalpara">
+
+Be sure to include the namespace prefix on all element
+references, including those in match, select, and test
+attributes, even when using an axis specifier.  Here are
+some examples:
+
+  <xsl:if test="d:title">
+
+  <xsl:apply-templates select="d:title" mode="list.title.mode"/>
+
+  <xsl:apply-templates 
+       select="*[not(self::d:listitem or self::d:title 
+               or self::d:titleabbrev)] | 
+               comment()[not(preceding-sibling::d:listitem)] |
+               processing-instruction()[not(preceding-sibling::d:listitem)]"/>
+
+Failure to add the prefix to an element name will cause
+the stylesheet to silently not match the intended element,
+with consequences that are most likely undesirable.
+
+
+Report any problems
+------------------------
+These are experimental stylesheets and need thorough testing.
+Please report any problems to bobs@sagehill.net.
+
diff --git a/releasetools/db5files/VERSION b/releasetools/db5files/VERSION
new file mode 100644 (file)
index 0000000..e7f0611
--- /dev/null
@@ -0,0 +1,85 @@
+<?xml version='1.0'?> <!-- -*- nxml -*- -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:fm="http://freshmeat.net/projects/freshmeat-submit/"
+               xmlns:sf="http://sourceforge.net/"
+               exclude-result-prefixes="fm sf"
+                version='1.0'>
+
+<xsl:param name="VERSION" select="string(document('')//fm:Version[1])"/>
+<xsl:param name="sf-relid" select="0"/>
+<xsl:strip-space elements="fm:*"/>
+
+<fm:project>
+  <fm:Project>DocBook</fm:Project>
+  <fm:Branch>XSL Stylesheets</fm:Branch>
+  <fm:Version>docbook5ns-snapshot-2007-01-08</fm:Version>
+<!--
+  <fm:License>MIT/X Consortium License</fm:License>
+-->
+  <fm:Release-Focus>
+  <!-- Initial freshmeat announcement -->
+  <!-- Documentation -->
+  <!-- Code cleanup -->
+  Minor feature enhancements
+  <!-- Major feature enhancements -->
+  <!-- Minor bugfixes -->
+  <!-- Major bugfixes -->
+  <!-- Minor security fixes -->
+  <!-- Major security fixes -->
+  </fm:Release-Focus>
+  <fm:Home-Page-URL>http://sourceforge.net/projects/docbook/</fm:Home-Page-URL>
+  <fm:Gzipped-Tar-URL>http://prdownloads.sourceforge.net/docbook/docbook-xsl-{VERSION}.tar.gz?download</fm:Gzipped-Tar-URL>
+  <fm:Zipped-Tar-URL>http://prdownloads.sourceforge.net/docbook/docbook-xsl-{VERSION}.zip?download</fm:Zipped-Tar-URL>
+  <fm:Bzipped-Tar-URL>http://prdownloads.sourceforge.net/docbook/docbook-xsl-{VERSION}.bz2?download</fm:Bzipped-Tar-URL>
+  <fm:Changelog-URL>http://sourceforge.net/project/shownotes.php?release_id={SFRELID}</fm:Changelog-URL>
+  <fm:CVS-URL>http://docbook.cvs.sourceforge.net/docbook/</fm:CVS-URL>
+  <fm:Mailing-List-URL>http://lists.oasis-open.org/archives/docbook-apps/</fm:Mailing-List-URL>
+  <fm:Changes>This is a bug-fix release with a few feature enhancements.
+</fm:Changes>
+</fm:project>
+
+<xsl:template match="/" priority="-100">
+  <xsl:if test="$sf-relid = 0">
+    <xsl:message terminate="yes">
+      <xsl:text>You must specify the sf-relid as a parameter.</xsl:text>
+    </xsl:message>
+  </xsl:if>
+
+  <xsl:apply-templates select="//fm:project"/>
+</xsl:template>
+
+<xsl:template match="fm:project">
+  <xsl:text>&#10;</xsl:text>
+  <xsl:apply-templates/>
+  <xsl:text>&#10;</xsl:text>
+  <xsl:apply-templates select="fm:Changes" mode="text"/>
+</xsl:template>
+
+<xsl:template match="fm:Changes"/>
+
+<xsl:template match="fm:Gzipped-Tar-URL|fm:Zipped-Tar-URL|fm:Bzipped-Tar-URL">
+  <xsl:value-of select="local-name(.)"/>
+  <xsl:text>: </xsl:text>
+  <xsl:value-of select="substring-before(., '{VERSION}')"/>
+  <xsl:value-of select="$VERSION"/>
+  <xsl:value-of select="substring-after(., '{VERSION}')"/>
+  <xsl:text>&#10;</xsl:text>
+</xsl:template>
+
+<xsl:template match="fm:Changelog-URL">
+  <xsl:value-of select="local-name(.)"/>
+  <xsl:text>: </xsl:text>
+  <xsl:value-of select="substring-before(., '{SFRELID}')"/>
+  <xsl:value-of select="$sf-relid"/>
+  <xsl:value-of select="substring-after(., '{SFRELID}')"/>
+  <xsl:text>&#10;</xsl:text>
+</xsl:template>
+
+<xsl:template match="fm:*">
+  <xsl:value-of select="local-name(.)"/>
+  <xsl:text>: </xsl:text>
+  <xsl:value-of select="normalize-space(.)"/>
+  <xsl:text>&#10;</xsl:text>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/releasetools/db5files/db5.xsl b/releasetools/db5files/db5.xsl
new file mode 100644 (file)
index 0000000..bb025f4
--- /dev/null
@@ -0,0 +1,92 @@
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:exsl="http://exslt.org/common"
+                xmlns:fo="http://www.w3.org/1999/XSL/Format"
+                xmlns:xlink="http://www.w3.org/1999/xlink"
+                xmlns:d="http://docbook.org/ns/docbook"
+                xmlns:saxon="http://icl.com/saxon"
+                xmlns:NodeInfo="http://org.apache.xalan.lib.NodeInfo"
+                exclude-result-prefixes="d exsl saxon NodeInfo"
+                version='1.0'>
+
+<!-- Template to add the namespace to non-db5 documents -->
+<xsl:template match="*" mode="addNS">
+  <xsl:element name="{local-name()}" 
+          namespace="http://docbook.org/ns/docbook">
+    <xsl:copy-of select="@*"/>
+    <xsl:apply-templates select="node()" mode="addNS"/>
+  </xsl:element>
+</xsl:template>
+
+<xsl:template name="add-xml-base">
+  <xsl:if test="not(@xml:base)">
+    <xsl:variable name="base">
+      <xsl:choose>
+        <xsl:when test="function-available('saxon:systemId')">
+          <xsl:value-of select="saxon:systemId()"/>
+        </xsl:when>
+        <xsl:when test="function-available('NodeInfo:systemId')">
+          <xsl:value-of select="NodeInfo:systemId()"/>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:message>
+            <xsl:text>WARNING: cannot add @xml:base to node </xsl:text>
+            <xsl:text>set root element.  </xsl:text>
+            <xsl:text>Relative paths may not work.</xsl:text>
+          </xsl:message>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:variable>
+    <!-- debug
+    <xsl:message>base is <xsl:value-of select="$base"/></xsl:message>
+    -->
+    <xsl:if test="$base != ''">
+      <xsl:attribute name="xml:base">
+        <xsl:call-template name="systemIdToBaseURI">
+          <xsl:with-param name="systemId">
+            <!-- file: seems to confuse some processors. -->
+            <xsl:choose>
+              <!-- however, windows paths must use file:///c:/path -->
+              <xsl:when test="starts-with($base, 'file:///') and
+                              substring($base, 10, 1) = ':'">
+                <xsl:value-of select="$base"/>
+              </xsl:when>
+              <xsl:when test="starts-with($base, 'file:/')
+                              and substring($base, 8, 1) = ':'">
+                <xsl:value-of select="concat('file:///', 
+                                      substring-after($base,'file:/'))"/>
+              </xsl:when>
+              <xsl:when test="starts-with($base, 'file:///')">
+                <xsl:value-of select="substring-after($base,'file://')"/>
+              </xsl:when>
+              <xsl:when test="starts-with($base, 'file://')">
+                <xsl:value-of select="substring-after($base,'file:/')"/>
+              </xsl:when>
+              <xsl:when test="starts-with($base, 'file:/')">
+                <xsl:value-of select="substring-after($base,'file:')"/>
+              </xsl:when>
+              <xsl:otherwise>
+                <xsl:value-of select="$base"/>
+              </xsl:otherwise>
+            </xsl:choose>
+          </xsl:with-param>
+        </xsl:call-template>
+      </xsl:attribute>
+    </xsl:if>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template name="systemIdToBaseURI">
+  <xsl:param name="systemId" select="''"/>
+  <xsl:if test="contains($systemId,'/')">
+    <xsl:value-of select="substring-before($systemId,'/')"/>
+    <xsl:text>/</xsl:text>
+    <xsl:call-template name="systemIdToBaseURI">
+      <xsl:with-param name="systemId"
+                      select="substring-after($systemId,'/')"/>
+    </xsl:call-template>
+  </xsl:if>
+</xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/releasetools/db5files/manpages.table.xsl b/releasetools/db5files/manpages.table.xsl
new file mode 100644 (file)
index 0000000..d694e29
--- /dev/null
@@ -0,0 +1,633 @@
+<?xml version="1.0"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:d="http://docbook.org/ns/docbook"
+xmlns:exsl="http://exslt.org/common"
+                exclude-result-prefixes="exsl d"
+                version='1.0'>
+
+  <!-- ********************************************************************
+       $Id$
+       ********************************************************************
+
+       This file is part of the XSL DocBook Stylesheet distribution.
+       See ../README or http://docbook.sf.net/release/xsl/current/ for
+       copyright and other information.
+
+       ******************************************************************** -->
+  <!--
+  <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl"/>
+  <xsl:param name="tbl.font.title">B</xsl:param>
+  <xsl:param name="tbl.font.headings">B</xsl:param>
+  -->
+  <xsl:param name="tbl.running.header.from.thead" select="0"/>
+  <xsl:param name="tbl.column.separator.char">:</xsl:param>
+
+  <!-- ==================================================================== -->
+
+  <!-- * This stylesheet transforms DocBook and HTML table source into -->
+  <!-- * tbl(1) markup. -->
+  <!-- * -->
+  <!-- * For details on tbl(1) and its markup syntaxt, see M. E. Lesk,-->
+  <!-- * "Tbl - A Program to Format Tables": -->
+  <!-- * -->
+  <!-- *   http://cm.bell-labs.com/7thEdMan/vol2/tbl -->
+  <!-- *   http://cm.bell-labs.com/cm/cs/doc/76/tbl.ps.gz -->
+  <!-- *   http://www.snake.net/software/troffcvt/tbl.html -->
+
+  <xsl:template match="d:table|d:informaltable" mode="to.tbl">
+    <!--* the "source" param is an optional param; it can be any -->
+    <!--* string you want to use that gives some indication of the -->
+    <!--* source context for a table; it gets passed down to the named -->
+    <!--* templates that do the actual table processing; this -->
+    <!--* stylesheet currently uses the "source" information for -->
+    <!--* logging purposes -->
+    <xsl:param name="source"/>
+    <xsl:param name="title">
+      <xsl:if test="local-name(.) = 'table'">
+        <xsl:apply-templates select="." mode="object.title.markup.textonly"/>
+      </xsl:if>
+    </xsl:param>
+    <!-- * ============================================================== -->
+    <!-- *    Set global table parameters                                 -->
+    <!-- * ============================================================== -->
+    <!-- * First, set a few parameters based on attributes specified in -->
+    <!-- * the table source. -->
+    <xsl:param name="allbox">
+    <xsl:if test="not(@frame = 'none') and not(@border = '0')">
+      <!-- * By default, put a box around table and between all cells, -->
+      <!-- * unless frame="none" or border="0" -->
+      <xsl:text>allbox </xsl:text>
+    </xsl:if>
+    </xsl:param>
+    <xsl:param name="center">
+    <!-- * If align="center", center the table. Otherwise, tbl(1) -->
+    <!-- * left-aligns it by default; note that there is no support -->
+    <!-- * in tbl(1) for specifying right alignment. -->
+    <xsl:if test="@align = 'center' or d:tgroup/@align = 'center'">
+      <xsl:text>center </xsl:text>
+    </xsl:if>
+    </xsl:param>
+    <xsl:param name="expand">
+    <!-- * If pgwide="1" or width="100%", then "expand" the table by -->
+    <!-- * making it "as wide as the current line length" (to quote -->
+    <!-- * the tbl(1) guide). -->
+    <xsl:if test="@pgwide = '1' or @width = '100%'">
+      <xsl:text>expand </xsl:text>
+    </xsl:if>
+    </xsl:param>
+
+    <!-- * ============================================================== -->
+    <!-- *    Convert table to HTML                                       -->
+    <!-- * ============================================================== -->
+    <!-- * Process the table by applying the HTML templates from the -->
+    <!-- * DocBook XSL stylesheets to the whole thing; because we don't -->
+    <!-- * override any of the <row>, <entry>, <tr>, <td>, etc. templates, -->
+    <!-- * the templates in the HTML stylesheets (which we import) are -->
+    <!-- * used to process those. -->
+    <xsl:param name="html-table-output">
+      <xsl:choose>
+        <xsl:when test=".//d:tr">
+          <!-- * If this table has a TR child, it means that it's an -->
+          <!-- * HTML table in the DocBook source, instead of a CALS -->
+          <!-- * table. So we just copy it as-is, while wrapping it -->
+          <!-- * in an element with same name as its original parent. -->
+          <xsl:for-each select="descendant-or-self::d:table|descendant-or-self::d:informaltable">
+            <xsl:element name="{local-name(..)}">
+              <table>
+                <xsl:copy-of select="*"/>
+              </table>
+            </xsl:element>
+          </xsl:for-each>
+        </xsl:when>
+        <xsl:otherwise>
+          <!-- * Otherwise, this is a CALS table in the DocBook source, -->
+          <!-- * so we need to apply the templates in the HTML -->
+          <!-- * stylesheets to transform it into HTML before we do -->
+          <!-- * any further processing of it. -->
+          <xsl:apply-templates/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:param>
+    <xsl:param name="contents" select="exsl:node-set($html-table-output)"/>
+
+    <!-- ==================================================================== -->
+    <!-- *                       Output the table -->
+    <!-- ==================================================================== -->
+    <!-- * -->
+    <!-- * This is the "driver" part of the code; it calls a series of named
+         * templates (further below) to generate the actual tbl(1) markup, -->
+    <!-- * including the optional "options line", required "format section", -->
+    <!-- * and then the actual contents of the table. -->
+    <!-- * -->
+    <!-- ==================================================================== -->
+
+    <xsl:for-each select="$contents//table">
+      <!-- * ============================================================== -->
+      <!-- *   Output table title                                           -->
+      <!-- * ============================================================== -->
+      <xsl:if test="$title != '' or parent::td">
+        <xsl:text>.PP&#10;</xsl:text>
+        <xsl:text>.</xsl:text>
+        <xsl:value-of select="$tbl.font.title"/>
+        <xsl:text> </xsl:text>
+        <xsl:if test="parent::td">
+          <xsl:text>*[nested&#160;table]</xsl:text>
+        </xsl:if>
+        <xsl:value-of select="normalize-space($title)"/>
+        <xsl:text>&#10;</xsl:text>
+        <xsl:text>.sp -1n&#10;</xsl:text>
+      </xsl:if>
+      
+      <!-- * mark the start of the table -->
+      <!-- * "TS" = "table start" -->
+      <xsl:text>.TS</xsl:text>
+      <xsl:if test="thead and $tbl.running.header.from.thead">
+        <!-- * H = "has header" -->
+        <xsl:text> H</xsl:text>
+      </xsl:if>
+      <xsl:text>&#10;</xsl:text>
+
+      <!-- * ============================================================== -->
+      <!-- *   Output "options line"                                         -->
+      <!-- * ============================================================== -->
+      <xsl:variable name="options-line">
+        <xsl:value-of select="$allbox"/>
+        <xsl:value-of select="$center"/>
+        <xsl:value-of select="$expand"/>
+        <xsl:text>tab(</xsl:text>
+        <xsl:value-of select="$tbl.column.separator.char"/>
+        <xsl:text>)</xsl:text>
+      </xsl:variable>
+      <xsl:if test="normalize-space($options-line) != ''">
+        <xsl:value-of select="normalize-space($options-line)"/>
+        <xsl:text>;&#10;</xsl:text>
+      </xsl:if>
+
+      <!-- * ============================================================== -->
+      <!-- *   Output table header rows                                     -->
+      <!-- * ============================================================== -->
+      <xsl:if test="thead">
+        <xsl:call-template name="output.rows">
+          <xsl:with-param name="rows" select="thead/tr"/>
+        </xsl:call-template> 
+        <xsl:text>&#10;</xsl:text>
+
+        <!-- * mark the end of table-header rows -->
+        <xsl:choose>
+          <xsl:when test="$tbl.running.header.from.thead">
+            <!-- * "TH" = "table header end" -->
+            <xsl:text>.TH&#10;</xsl:text>
+          </xsl:when>
+          <xsl:otherwise>
+            <!-- * "T&" = "table continuation" and is meant just as a kind -->
+            <!-- * of convenience macro and is sorta equivalent to a "TE" -->
+            <!-- * (table end) followed immediately by a "TS" (table start); -->
+            <!-- * in this case, it marks the end of a table "subsection" -->
+            <!-- * with header rows, and the start of a subsection with body -->
+            <!-- * rows. It's necessary to output it here because the "TH" -->
+            <!-- * macro is not being output, so there's otherwise no way -->
+            <!-- * for tbl(1) to know we have the table "sectioned". -->
+            <xsl:text>.T&amp;&#10;</xsl:text>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:if>
+      
+      <!-- * ============================================================== -->
+      <!-- *  Output table body rows                                        -->
+      <!-- * ============================================================== -->
+      <!-- * First create node set with all non-thead rows (tbody+tfoot), -->
+      <!-- * but reordered with the tfoot rows at the end of the node set -->
+      <xsl:variable name="rows-set">
+        <xsl:copy-of select="tbody/tr|tr"/>
+        <xsl:copy-of select="tfoot/tr"/>
+      </xsl:variable>
+      <xsl:call-template name="output.rows">
+        <xsl:with-param name="source" select="$source"/>
+        <xsl:with-param name="rows" select="exsl:node-set($rows-set)"/>
+      </xsl:call-template>
+
+      <!-- * mark the end of the table -->
+      <xsl:text>&#10;</xsl:text>
+      <!-- * .TE = "Table End" -->
+      <xsl:text>.TE&#10;</xsl:text>
+      <!-- * put a blank line of space below the table -->
+      <xsl:text>.sp&#10;</xsl:text>
+    </xsl:for-each>
+  </xsl:template>
+
+  <!-- ==================================================================== -->
+  <!-- *                        named templates -->
+  <!-- ==================================================================== -->
+  <!-- * -->
+  <!-- * All of the following are named templates that get called directly -->
+  <!-- * or indirectly by the main "driver" part of the code (above) -->
+  <!-- * -->
+  <!-- ==================================================================== -->
+  
+  <xsl:template name="output.rows">
+    <xsl:param name="source"/>
+    <xsl:param name="rows"/>
+    <!-- * ============================================================== -->
+    <!-- *   Flatten row set into simple list of cells                    -->
+    <!-- * ============================================================== -->
+    <!-- * Now we flatten the structure further into just a set of -->
+    <!-- * cells without the row parents. This basically creates a -->
+    <!-- * copy of the entire contents of the original table, but -->
+    <!-- * restructured in such a way that we can more easily generate -->
+    <!-- * the corresponding tbl(1) markup we need to output. -->
+    <xsl:variable name="cells-list">
+      <xsl:call-template name="build.cell.list">
+        <xsl:with-param name="source" select="$source"/>
+        <xsl:with-param name="rows" select="$rows"/>
+      </xsl:call-template>
+    </xsl:variable>
+    <xsl:variable name="cells" select="exsl:node-set($cells-list)"/>
+
+    <!-- * Output the table "format section", which tells tbl(1) how to -->
+    <!-- * format each row and column -->
+    <xsl:call-template name="create.table.format">
+      <xsl:with-param name="cells" select="$cells"/>
+    </xsl:call-template>
+
+    <!--* Output the formatted contents of each cell. -->
+    <xsl:for-each select="$cells/cell">
+      <xsl:call-template name="output.cell"/>
+    </xsl:for-each>
+  </xsl:template>
+
+
+  <!-- * ============================================================== -->
+  <!-- *    Output the tbl(1)-formatted contents of each cell.            -->
+  <!-- * ============================================================== -->
+  <xsl:template name="output.cell">
+    <xsl:choose>
+      <xsl:when test="preceding-sibling::cell[1]/@row != @row or
+                      not(preceding-sibling::cell)">
+        <!-- * If the value of the "row" attribute on this cell is -->
+        <!-- * different from the value of that on the previous cell, it -->
+        <!-- * means we have a new row. So output a line break (as long -->
+        <!-- * as this isn't the first cell in the table) -->
+        <xsl:text>&#10;</xsl:text>
+      </xsl:when>
+      <xsl:otherwise>
+        <!-- * Otherwise we are not at the start of a new row, so we -->
+        <!-- * output a tab character to delimit the contents of this -->
+        <!-- * cell from the contents of the next one. -->
+        <xsl:value-of select="$tbl.column.separator.char"/>
+      </xsl:otherwise>
+    </xsl:choose>
+    <xsl:choose>
+      <xsl:when test="@type = '^'">
+        <!-- * If this is a dummy cell resulting from the presence of -->
+        <!-- * rowpan attribute in the source, it has no contents, so -->
+        <!-- * we need to handle it differently. -->
+        <xsl:if test="@colspan and @colspan > 1">
+          <!-- * If there is a colspan attribute on this dummy row, then -->
+          <!-- * we need to output a tab character for each column that -->
+          <!-- * it spans. -->
+          <xsl:call-template name="copy-string">
+            <xsl:with-param name="string" select="$tbl.column.separator.char"/>
+            <xsl:with-param name="count">
+              <xsl:value-of select="@colspan - 1"/>
+            </xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+      </xsl:when>
+      <xsl:otherwise>
+        <!-- * Otherwise, we have a "real" cell (not a dummy one) with -->
+        <!-- * contents that we need to output, -->
+        <!-- * -->
+        <!-- * The "T{" and "T}" stuff are delimiters to tell tbl(1) that -->
+        <!-- * the delimited contents are "text blocks" that roff -->
+        <!-- * needs to process -->
+        <xsl:text>T{&#10;</xsl:text>
+        <xsl:copy-of select="."/>
+        <xsl:text>&#10;T}</xsl:text>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <!-- * ============================================================== -->
+  <!-- *   Build a restructured "cell list" copy of the entire table    -->
+  <!-- * ============================================================== -->
+  <xsl:template name="build.cell.list">
+    <xsl:param name="source"/>
+    <xsl:param name="rows"/>
+    <xsl:param  name="cell-data-unsorted">
+      <!-- * This param collects all the "real" cells from the table, -->
+      <!-- * along with "dummy" rows that we generate for keeping -->
+      <!-- * track of Rowspan instances. -->
+      <xsl:apply-templates select="$rows" mode="cell.list">
+        <xsl:with-param name="source" select="$source"/>
+      </xsl:apply-templates>
+    </xsl:param>
+    <xsl:param  name="cell-data-sorted">
+      <!-- * Sort the cells so that the dummy cells get put where we -->
+      <!-- * need them in the structure. -->
+      <xsl:for-each select="exsl:node-set($cell-data-unsorted)/cell">
+        <xsl:sort select="@row"/>
+        <xsl:sort select="@slot"/>
+        <xsl:copy-of select="."/>
+      </xsl:for-each>
+    </xsl:param>
+    <!-- * Return the sorted cell list -->
+    <xsl:copy-of select="$cell-data-sorted"/>
+  </xsl:template>
+
+  <xsl:template match="tr" mode="cell.list">
+    <xsl:param name="source"/>
+    <xsl:variable name="row">
+      <xsl:value-of select="count(preceding-sibling::tr) + 1"/>
+    </xsl:variable>
+    <xsl:for-each select="td|th">
+      <xsl:call-template name="cell">
+        <xsl:with-param name="source" select="$source"/>
+        <xsl:with-param name="row" select="$row"/>
+        <!-- * pass on the element name so we can select the appropriate -->
+        <!-- * roff font for styling the cell contents -->
+        <xsl:with-param name="class" select="name(.)"/>
+      </xsl:call-template>
+    </xsl:for-each>
+  </xsl:template>
+
+  <xsl:template name="cell">
+    <xsl:param name="source"/>
+    <xsl:param name="row"/>
+    <xsl:param name="class"/>
+    <xsl:param name="slot">
+      <!-- * The "slot" is the horizontal position of this cell (usually -->
+      <!-- * just the same as its column, but not so when it is preceded -->
+      <!-- * by cells that have colspans or cells in preceding rows that -->
+      <!-- * that have rowspans). -->
+      <xsl:value-of select="position()"/>
+    </xsl:param>
+    <!-- * For each real TD cell, create a Cell instance; contents will -->
+    <!-- * be the roff-formatted contents of its original table cell. -->
+    <cell type=""
+          row="{$row}"
+          slot="{$slot}"
+          class="{$class}"
+          colspan="{@colspan}"
+          align="{@align}"
+          valign="{@valign}"
+          >
+      <xsl:choose>
+        <xsl:when test=".//tr">
+          <xsl:call-template name="log.message">
+            <xsl:with-param name="level">Warn</xsl:with-param>
+            <xsl:with-param name="source" select="$source"/>
+            <xsl:with-param name="message">
+              <xsl:text
+              >tbl convert : Extracted a nested table</xsl:text>
+            </xsl:with-param>
+          </xsl:call-template>
+          <xsl:text>[\fInested&#160;table\fR]*&#10;</xsl:text>
+        </xsl:when>
+        <xsl:otherwise>
+          <!-- * Apply templates to the child contents of this cell, to -->
+          <!-- * transform them into marked-up roff. -->
+          <xsl:variable name="contents">
+            <xsl:apply-templates/>
+          </xsl:variable>
+          <!-- * We now have the contents in roff (plain-text) form, -->
+          <!-- * but we may also still have unnecessary whitespace at -->
+          <!-- * the beginning and/or end of it, so trim it off. -->
+          <xsl:call-template name="trim.text">
+            <xsl:with-param name="contents" select="$contents"/>
+          </xsl:call-template>
+        </xsl:otherwise>
+      </xsl:choose>
+    </cell>
+
+    <!-- * For each instance of a rowspan attribute found, we create N -->
+    <!-- * dummy cells, where N is equal to the value of the rowspan. -->
+    <xsl:if test="@rowspan and @rowspan > 0">
+      <!-- * If this cell is preceded in the same row by cells that -->
+      <!-- * have colspan attributes, then we need to calculate the -->
+      <!-- * "offset" caused by those colspan instances; the formula -->
+      <!-- * is to (1) check for all the preceding cells that have -->
+      <!-- * colspan attributes that are not empty and which have a -->
+      <!-- * value greater than 1, then (2) take the sum of the values -->
+      <!-- * of all those colspan attributes, and subtract from that -->
+      <!-- * the number of such colspan instances found. -->
+      <xsl:variable name="colspan-offset">
+        <xsl:value-of
+            select="sum(preceding-sibling::td[@colspan != ''
+                    and @colspan > 1]/@colspan) -
+                    count(preceding-sibling::td[@colspan != ''
+                    and @colspan > 1]/@colspan)"/>
+      </xsl:variable>
+      <xsl:call-template name="create.dummy.cells">
+        <xsl:with-param name="row" select="$row + 1"/>
+        <!-- * The slot value on each dummy cell must be offset by the -->
+        <!-- * value of $colspan-offset to adjust for preceding colpans -->
+        <xsl:with-param name="slot" select="$slot + $colspan-offset"/>
+        <xsl:with-param name="colspan" select="@colspan"/>
+        <xsl:with-param name="rowspan" select="@rowspan"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:template>
+
+  <xsl:template name="create.dummy.cells">
+    <xsl:param name="row"/>
+    <xsl:param name="slot"/>
+    <xsl:param name="colspan"/>
+    <xsl:param name="rowspan"/>
+    <xsl:choose>
+      <xsl:when test="$rowspan > 1">
+        <!-- * Tail recurse until we have no more rowspans, creating -->
+        <!-- * an empty dummy cell each time. The type value, '^' -->
+        <!-- * is the marker that tbl(1) uses for indicates a -->
+        <!-- * "vertically spanned heading". -->
+        <cell row="{$row}" slot="{$slot}" type="^" colspan="{@colspan}"/>
+        <xsl:call-template name="create.dummy.cells">
+          <xsl:with-param name="row" select="$row + 1"/>
+          <xsl:with-param name="slot" select="$slot"/>
+          <xsl:with-param name="colspan" select="$colspan"/>
+          <xsl:with-param name="rowspan" select="$rowspan - 1"/>
+        </xsl:call-template>
+      </xsl:when>
+    </xsl:choose>
+  </xsl:template>
+
+  <!-- * ============================================================== -->
+  <!-- *    Build the "format section" for the table                    -->
+  <!-- * ============================================================== -->
+  <!-- * Description from the tbl(1) guide: -->
+  <!-- * -->
+  <!-- * "The format section of the table specifies the layout of the -->
+  <!-- * columns.  Each line in this section corresponds to one line of -->
+  <!-- * the table... and each line contains a key-letter for each -->
+  <!-- * column of the table." -->
+  <xsl:template name="create.table.format">
+    <xsl:param name="cells"/>
+    <xsl:apply-templates mode="table.format" select="$cells"/>
+    <!-- * last line of table format section must end with a dot -->
+    <xsl:text>.</xsl:text>
+  </xsl:template>
+
+  <xsl:template match="cell" mode="table.format">
+    <xsl:choose>
+      <xsl:when test="preceding-sibling::cell[1]/@row != @row">
+        <!-- * If the value of the row attribute on this cell is -->
+        <!-- * different from the value of that on the previous cell, it -->
+        <!-- * means we have a new row. So output a line break. -->
+        <xsl:text>&#xa;</xsl:text>
+      </xsl:when>
+      <xsl:otherwise>
+        <!-- * If this isn't the first cell, output a space before it to -->
+        <!-- * separate it from the preceding key letter. -->
+        <xsl:if test="position() != 1">
+          <xsl:text> </xsl:text>
+        </xsl:if>
+      </xsl:otherwise>
+    </xsl:choose>
+    <!-- * Select an appropriate "alignment" key letter based on this -->
+    <!-- * cell's attributes. -->
+    <xsl:choose>
+      <xsl:when test="@type = '^'">
+        <xsl:text>^</xsl:text>
+      </xsl:when>
+      <xsl:when test="@align = 'center'">
+        <xsl:text>c</xsl:text>
+      </xsl:when>
+      <xsl:when test="@align = 'right'">
+        <xsl:text>r</xsl:text>
+      </xsl:when>
+      <xsl:when test="@align = 'char'">
+        <xsl:text>n</xsl:text>
+      </xsl:when>
+      <xsl:otherwise>
+        <!-- * Default to left alignment. -->
+        <xsl:text>l</xsl:text>
+      </xsl:otherwise>
+    </xsl:choose>
+    <!-- * By default, tbl(1) vertically centers cell contents within -->
+    <!-- * their cells; the "t" key latter tells it to top-align the -->
+    <!-- * contents instead. Note that tbl(1) has no options for -->
+    <!-- * bottom or baseline alignment. -->
+    <xsl:if test="@valign = 'top'">
+      <xsl:text>t</xsl:text>
+    </xsl:if>
+    <xsl:if test="@class = 'th'">
+      <!-- * If this is a heading row, generate a font indicator (B or I), -->
+      <!-- * or if the value of $tbl.font.headings is empty, nothing. -->
+      <xsl:value-of select="$tbl.font.headings"/>
+    </xsl:if>
+    <!-- * We only need to deal with colspans whose value is greater -->
+    <!-- * than one (a colspan="1" is the same as having no colspan -->
+    <!-- * attribute at all). -->
+    <xsl:if test="@colspan > 1">
+      <xsl:call-template name="process.colspan">
+        <xsl:with-param name="colspan" select="@colspan - 1"/>
+        <xsl:with-param name="type" select="@type"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:template>
+  
+  <xsl:template name="process.colspan">
+    <xsl:param name="colspan"/>
+    <xsl:param name="type"/>
+    <!-- * Output a space to separate this key letter from preceding one. -->
+    <xsl:text> </xsl:text>
+    <xsl:choose>
+      <xsl:when test="$type = '^'">
+        <!-- * A '^' ("vertically spanned heading" marker) indicates -->
+        <!-- * that the "parent" of this spanned cell is a dummy cell; -->
+        <!-- * in this case, we need to generate a '^' instead of the -->
+        <!-- * normal 's'. -->
+        <xsl:text>^</xsl:text>
+      </xsl:when>
+      <xsl:otherwise>
+        <!-- * s = 'spanned heading' -->
+        <xsl:text>s</xsl:text>
+      </xsl:otherwise>
+    </xsl:choose>
+    <xsl:if test="$colspan > 1">
+      <!-- * Tail recurse until we have no more colspans, outputting -->
+      <!-- * another marker each time. -->
+      <xsl:call-template name="process.colspan">
+        <xsl:with-param name="colspan" select="$colspan - 1"/>
+        <xsl:with-param name="type" select="$type"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:template>
+
+  <!-- * ============================================================== -->
+  <!-- *    colgroup and col                                            -->
+  <!-- * ============================================================== -->
+  <!-- * We currently don't do anything with colgroup. Not sure if it -->
+  <!-- * is widely used enough to bother adding support for it -->
+  <xsl:template match="colgroup"/>
+  <xsl:template match="col"/>
+
+  <!-- * ============================================================== -->
+  <!-- *    table footnotes                                      -->
+  <!-- * ============================================================== -->
+  <xsl:template match="d:footnote" mode="table.footnote.mode">
+    <xsl:variable name="footnotes" select=".//d:footnote"/>
+    <xsl:variable name="table.footnotes"
+                  select=".//d:tgroup//d:footnote"/>
+    <xsl:value-of select="$man.table.footnotes.divider"/>
+    <xsl:text>&#10;</xsl:text>
+    <xsl:text>.br&#10;</xsl:text>
+    <xsl:apply-templates select="*[1]" mode="footnote.body.number"/>
+    <xsl:apply-templates select="*[position() &gt; 1]"/>
+  </xsl:template>
+
+  <!-- * The following template for footnote.body.number mode was just -->
+  <!-- * lifted from the HTML stylesheets with some minor adjustments -->
+  <xsl:template match="*"  mode="footnote.body.number">
+    <xsl:variable name="name">
+      <xsl:text>ftn.</xsl:text>
+      <xsl:call-template name="object.id">
+        <xsl:with-param name="object" select="ancestor::d:footnote"/>
+      </xsl:call-template>
+    </xsl:variable>
+    <xsl:variable name="href">
+      <xsl:text>#</xsl:text>
+      <xsl:call-template name="object.id">
+        <xsl:with-param name="object" select="ancestor::d:footnote"/>
+      </xsl:call-template>
+    </xsl:variable>
+    <xsl:variable name="footnote.mark">
+      <xsl:text>[</xsl:text>
+      <xsl:apply-templates select="ancestor::d:footnote"
+                           mode="footnote.number"/>
+      <xsl:text>]&#10;</xsl:text>
+    </xsl:variable>
+    <xsl:variable name="html">
+      <xsl:apply-templates select="."/>
+    </xsl:variable>
+    <xsl:choose>
+      <xsl:when test="function-available('exsl:node-set')">
+        <xsl:variable name="html-nodes" select="exsl:node-set($html)"/>
+        <xsl:choose>
+          <xsl:when test="$html-nodes//p">
+            <xsl:apply-templates select="$html-nodes" mode="insert.html.p">
+              <xsl:with-param name="mark" select="$footnote.mark"/>
+            </xsl:apply-templates>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:apply-templates select="$html-nodes" mode="insert.html.text">
+              <xsl:with-param name="mark" select="$footnote.mark"/>
+            </xsl:apply-templates>
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:copy-of select="$html"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <!-- * The HTML stylesheets output <sup><a>...</a></sup> around -->
+  <!-- * footnote markers in tables -->
+  <xsl:template match="th/sup">
+    <xsl:apply-templates/>
+  </xsl:template>
+  <xsl:template match="a">
+    <xsl:apply-templates/>
+  </xsl:template>
+
+</xsl:stylesheet>