]> granicus.if.org Git - docbook-dsssl/commitdiff
Checkpointing. This currently does not work as-is. I added support
authorMichael Smith <xmldoc@users.sourceforge.net>
Tue, 8 Nov 2005 06:01:01 +0000 (06:01 +0000)
committerMichael Smith <xmldoc@users.sourceforge.net>
Tue, 8 Nov 2005 06:01:01 +0000 (06:01 +0000)
for processing Rowspan correctly and it seems to work as expected,
but still need to do add some handling to actually generate the
tbl(1) format spec.

This implementation relies on holding a big node-set in memory,
containing "Cell" elements store information about the nature of
each cell (e.g., whether it is a "normal" cell, or the result of a
Rowspan or Colspan). I could not figure out a way to implement
support for Rowspan without ending up storing multiple, duplicate,
Cell elements for each cell. (It gets uniq-ified later using the
EXSLT set:distinct function.) The duplication will probably cause
performance and memory issues with big tables. Maybe I will figure
out later how to do it properly; but for now, this will have to do.

xsl/manpages/table.xsl

index d0ea9936088d9ae0ff4e9d6ce2fb0cb06ae219f1..8b76218d02667c756a42a9fea1db5c6ac6662747 100644 (file)
@@ -1,17 +1,29 @@
 <?xml version='1.0'?>
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                 xmlns:exsl="http://exslt.org/common"
+                xmlns:set="http://exslt.org/sets"
                 version='1.0'>
 
-<!-- ********************************************************************
-     $Id$
-     ********************************************************************
+  <!-- ********************************************************************
+       $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.
+       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.
 
-     ******************************************************************** -->
+       ******************************************************************** -->
+
+  <!-- ==================================================================== -->
+
+  <!-- *  This stylesheet takes HTML output of table content from the -->
+  <!-- *  DocBook HTML stylesheets, and transforms it to tbl(1) markup. -->
+  <!-- * -->
+  <!-- *  See M. E. Lesk, “Tbl – A Program to Format Tables” for details -->
+  <!-- *  on tbl(1) and its markup syntaxt. -->
+  <!-- * -->
+  <!-- *    http://cm.bell-labs.com/cm/cs/doc/76/tbl.ps.gz -->
+  <!-- *    http://www.snake.net/software/troffcvt/tbl.html -->
 
   <!-- ==================================================================== -->
 
@@ -37,7 +49,9 @@
     <xsl:text>allbox;&#10;</xsl:text>
     <!-- * create the table “format” spec, which tells tbl(1) how to -->
     <!-- * format each row and column -->
-    <xsl:apply-templates select="$table//tr" mode="create.table.format" />
+    <xsl:call-template name="create.table.format">
+      <xsl:with-param name="table" select="$table"/>
+    </xsl:call-template>
     <xsl:for-each select="$table//tr">
       <xsl:text>&#10;</xsl:text>
       <!-- * embed a comment to show where each row starts -->
     <xsl:text>.sp&#10;</xsl:text>
   </xsl:template>
 
-  <xsl:template match="tr" mode="create.table.format">
-    <!-- *  this template creates a “format” spec at the top of the table -->
-    <!-- *  that looks something like the following: -->
+  <!-- ==================================================================== -->
+
+  <xsl:template name="create.table.format">
+    <xsl:param name="table"/>
+    <xsl:param name="row-counter">1</xsl:param>
+    <xsl:param name="total-rows" select="count($table//tr)"/>
+    <xsl:param name="cell-data"/>
+    <!-- * This template calls itself recursivingly until all rows are -->
+    <!-- * processed. It uses the $cell-data param to keep a running -->
+    <!-- * record of the table layout. That is necessary in order to -->
+    <!-- * properly process Rowspan instances. -->
+    <xsl:variable name="new-cell-data">
+      <xsl:call-template name="create.table.format.row">
+        <xsl:with-param name="content" select="$table//tr[$row-counter]"/>
+        <xsl:with-param name="row-counter" select="$row-counter"/>
+        <xsl:with-param name="total-rows" select="$total-rows"/>
+        <xsl:with-param name="cell-data" select="exsl:node-set($cell-data)"/>
+      </xsl:call-template>
+    </xsl:variable>
+    <xsl:choose>
+      <!-- * When the value of row counter  reaches the total number of -->
+      <!-- * rows, it means we have processed all rows; so next step is -->
+      <!-- * to sort and uniq-ify the list of cells we have generated. -->
+      <xsl:when test="$row-counter = $total-rows">
+        <!-- * First, turn $new-cell-data into a node-set so that we -->
+        <!-- * can walk through it. -->
+        <xsl:variable name="cell-list" select="exsl:node-set($new-cell-data)"/>
+        <!-- * Use set:distinct to remove duplicates from the cell list -->
+        <xsl:for-each select="set:distinct($cell-list//cell)">
+          <!-- * Sort the list by row and then by column within row -->
+          <xsl:sort select="@row"/>
+          <xsl:sort select="@column"/>
+          <xsl:message>Cell: Row="<xsl:value-of select="@row"/>", Column="<xsl:value-of select="@column"/>" - value: <xsl:value-of select="."/></xsl:message>
+        </xsl:for-each>
+      </xsl:when>
+      <xsl:otherwise>
+        <!-- * Otherwise, the value of row counter is still less than the -->
+        <!-- * total number of rows, so we increment the row-counter count -->
+        <!-- * and recursively re-call the current template to -->
+        <!-- * process the next row. We pass the cell-data param so that -->
+        <!-- * we will know the layout of rows we have processed so far. -->
+        <xsl:call-template name="create.table.format">
+          <xsl:with-param name="table" select="$table"/>
+          <xsl:with-param name="row-counter" select="$row-counter + 1"/>
+          <xsl:with-param name="cell-data" select="exsl:node-set($new-cell-data)"/>
+        </xsl:call-template>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <!-- ==================================================================== -->
+
+  <xsl:template name="create.table.format.row">
+    <xsl:param name="content"/>
+    <xsl:param name="row-counter"/>
+    <xsl:param name="cell-counter" select="1"/>
+    <xsl:param name="total-rows"/>
+    <xsl:param name="total-cells" select="count($content/*)"/>
+    <xsl:param name="cell-data"/>
+    <!-- * This template calls itself recursivingly until all cells in -->
+    <!-- * a row are processed. It uses the $cell-data param to keep -->
+    <!-- * a running record of the table layout. That is necessary in -->
+    <!-- * order to properly process Rowspan instances. -->
     <!-- * -->
-    <!-- *   cfB cfB cfB cfB -->
-    <!-- *   l l s l -->
-    <!-- *   l l l l -->
+    <!-- * First copy in all cell data we have so far -->
+    <xsl:copy-of select="$cell-data"/>
+    <xsl:choose>
+      <!-- * When the value of cell counter is less than or equal to the -->
+      <!-- * total number of cells in the row, it means we still have -->
+      <!-- * cells to process, so we call the template for generating -->
+      <!-- * cell format data. -->
+      <xsl:when test="$cell-counter &lt;= $total-cells">
+        <xsl:variable name="new-cell-data">
+          <xsl:call-template name="create.table.format.cell">
+            <xsl:with-param name="content" select="$content"/>
+            <xsl:with-param name="cell-content" select="$content[1]/*[$cell-counter]"/>
+            <xsl:with-param name="row-counter" select="$row-counter"/>
+            <xsl:with-param name="cell-counter" select="$cell-counter"/>
+            <xsl:with-param name="cell-data" select="exsl:node-set($cell-data)"/>
+          </xsl:call-template>
+        </xsl:variable>
+        <!-- * Now we increment the cell-counter count and recursively -->
+        <!-- * re-call the current template to process the next cell. -->
+        <xsl:call-template name="create.table.format.row">
+          <xsl:with-param name="content" select="$content[1]"/>
+          <xsl:with-param name="row-counter" select="$row-counter"/>
+          <xsl:with-param name="cell-counter" select="$cell-counter + 1"/>
+          <xsl:with-param name="cell-data" select="exsl:node-set($new-cell-data)"/>
+        </xsl:call-template>
+      </xsl:when>
+      <!-- * Otherwise, the value of the cell counter exceeds the total number -->
+      <!-- * of cells, meaning that we have run out of cells to process, so we -->
+      <!-- * don’t do anything more – just return to the calling template. -->
+      <xsl:otherwise/>
+    </xsl:choose>
+  </xsl:template>
+
+  <!-- ==================================================================== -->
+
+  <xsl:template name="create.table.format.cell">
+    <xsl:param name="content"/>
+    <xsl:param name="cell-content"/>
+    <xsl:param name="row-counter"/>
+    <xsl:param name="cell-counter" select="1"/>
+    <xsl:param name="cell-data"/>
+    <xsl:param name="column" select="$cell-counter"/>
+    <!-- * This template processes each pair of row and column -->
+    <!-- * coordinates and generates a “cell” with appropriate data for -->
+    <!-- * each. It checks to see if a cell has Rowspan and/or Colspan -->
+    <!-- * attributes. If so, it generates cells for those also. -->
+    <!-- * -->
+    <!-- * For each pair of cell coordinates, it checks the $cell-data -->
+    <!-- * list to see if we have already generated a cell at those -->
+    <!-- * coordinates (as a result of auto-generating one for a Colspan -->
+    <!-- * or Rowspan instance); if we have, it does not generate a new -->
+    <!-- * cell, but shifts the cell numbering for the rest of the cells -->
+    <!-- * over as needed. -->
+    <xsl:choose>
+      <!-- * Check to see if we already have an existing cell at the -->
+      <!-- * current coordinates. -->
+      <xsl:when test="$cell-data//cell[@row = $row-counter and @column = $cell-counter]">
+        <!-- * If we already have a cell here, increment cell counter -->
+        <!-- * and then recursively call create.table.format.cell to -->
+        <!-- * check the next column, until we find an “open” column. -->
+        <xsl:call-template name="create.table.format.cell">
+          <xsl:with-param name="content" select="$content"/>
+          <xsl:with-param name="cell-content" select="$content[1]/*[$cell-counter + 1]"/>
+          <xsl:with-param name="row-counter" select="$row-counter"/>
+          <xsl:with-param name="cell-counter" select="$cell-counter + 1"/>
+          <xsl:with-param name="cell-data" select="$cell-data"/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:otherwise>
+        <!-- * Otherwise, we don’t have a existing cell at these -->
+        <!-- * coordinates, so we need to generate one. -->
+        <!-- * -->
+        <!-- * ****************** HACK ALERT ************************* -->
+        <!-- * We first re-copy the whole cell-data list. This results -->
+        <!-- * in a potentially huge number of duplicates in the -->
+        <!-- * list. But I have not found a way around it. -Mike -->
+        <xsl:copy-of select="$cell-data"/>
+        <!-- * Now call the template that actually generates the Cell -->
+        <!-- * instance for each “real” cell. -->
+        <xsl:call-template name="cell">
+          <xsl:with-param name="row" select="$row-counter"/>
+          <xsl:with-param name="column" select="$cell-counter"/>
+        </xsl:call-template>
+        <!-- * Next, if we find a Colspan attribute in the source, -->
+        <!-- * call the template that generates the bogus Cell -->
+        <!-- * instances we need for outputting appropriate tbl(1) -->
+        <!-- * markup we need for output of Colspan instances. -->
+        <xsl:if test="$cell-content/@colspan">
+          <xsl:call-template name="colspan">
+            <xsl:with-param name="count" select="$cell-content/@colspan"/>
+            <xsl:with-param name="row" select="$row-counter"/>
+            <xsl:with-param name="column" select="$cell-counter + 1"/>
+          </xsl:call-template>
+        </xsl:if>
+        <xsl:if test="$cell-content/@rowspan">
+          <!-- * Finally, if we find a Rowspan attribute in the source, -->
+          <!-- * we first call a rowspans (plural) template, because we -->
+          <!-- * need to check and see if there is also a Colspan -->
+          <!-- * attribute on the same element; if there is, we have to -->
+          <!-- * generate N number of bogus Cell instances for each -->
+          <!-- * Rowspan, where N is the value of the Colspan attribute. -->
+          <xsl:call-template name="rowspans">
+            <xsl:with-param name="count" select="$cell-content/@rowspan - 1"/>
+            <xsl:with-param name="row" select="$row-counter + 1"/>
+            <xsl:with-param name="column" select="$cell-counter"/>
+            <xsl:with-param name="iterations">
+              <xsl:choose>
+                <!-- * If this element actually has a Colspan attribute, -->
+                <!-- * we use that value. -->
+                <xsl:when test="$cell-content/@colspan">
+                  <xsl:value-of select="$cell-content/@colspan"/>
+                </xsl:when>
+                <!-- * Otherwise, we only do one iteration -->
+                <xsl:otherwise>1</xsl:otherwise>
+              </xsl:choose>
+            </xsl:with-param>
+          </xsl:call-template>
+        </xsl:if>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <!-- ==================================================================== -->
+
+  <xsl:template name="rowspans">
+    <xsl:param name="count"/>
+    <xsl:param name="iterations"/>
+    <xsl:param name="row"/>
+    <xsl:param name="column"/>
+    <!-- * This is a special template needed to deal with cases where an -->
+    <!-- * element has both Rowspan and Colspan attributes. -->
     <!-- * -->
-    <xsl:for-each select="td|th">
+    <!-- * This template calls the rowspan (singular) template N -->
+    <!-- * number of times, where N=$iterations; it does that by -->
+    <!-- * recursively calling itself. -->
+    <xsl:choose>
+      <!-- * When the value of $iterations reaches zero, we stop and -->
+      <!-- * return to the calling template. -->
+      <xsl:when test="$iterations = 0"/> <!-- * do nothing -->
+      <xsl:otherwise>
+             Otherwise, the value os $iterations is still non-zero, so
+             we call the rowspan template to generate the bogus Cell
+             instances we need for outputting appropriate tbl(1)
+             markup we need for Rowspan instances.
+        <xsl:call-template name="rowspan">
+          <xsl:with-param name="count" select="$count"/>
+          <xsl:with-param name="row" select="$row"/>
+          <xsl:with-param name="column" select="$column"/>
+        </xsl:call-template>
+        <xsl:call-template name="rowspans">
+          <xsl:with-param name="count" select="$count"/>
+          <xsl:with-param name="iterations" select="$iterations - 1"/>
+          <xsl:with-param name="row" select="$row"/>
+          <xsl:with-param name="column" select="$column + 1"/>
+        </xsl:call-template>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <!-- ==================================================================== -->
+
+  <!-- * The following templates generate the actual Cell instances that -->
+  <!-- * we need for assembling the tbel(1) table-format spec. -->
+
+  <xsl:template name="cell">
+    <xsl:param name="row"/>
+    <xsl:param name="column"/>
+    <!-- * This template gets called once for each real table cell in -->
+    <!-- * the source. -->
+    <cell>
+      <!-- * Add row and column attributes, plus row and column data -->
+      <!-- * in the string value of the element. -->
+      <xsl:call-template name="row-column-data">
+        <xsl:with-param name="row" select="$row"/>
+        <xsl:with-param name="column" select="$column"/>
+      </xsl:call-template>
       <xsl:choose>
         <xsl:when test="local-name(.) = 'th'">
-          <!-- * c = "center"; fB = "bold" -->
-          <xsl:text>cfB</xsl:text>
+          <!-- * h = "heading" (pseudo-markup; we change to "cfB" later) -->
+          <xsl:text>h</xsl:text>
         </xsl:when>
         <xsl:otherwise>
           <!-- * l = "left" -->
           <xsl:text>l</xsl:text>
         </xsl:otherwise>
       </xsl:choose>
-      <!-- * if <td> source for this cell has a “colspan” attribute, we -->
-      <!-- * need to tell tbl(1) to format the corresponding cell as a -->
-      <!-- * “spanned heading”; we do this by generating an "s" format -->
-      <!-- * letter n-1 times, where n is hte value of the colspan attr -->
-      <xsl:if test="@colspan and @colspan > 1">
-        <!-- put a space before each additional format letter -->
-        <xsl:text> </xsl:text>
-        <xsl:call-template name="copy-string">
-          <!-- * s = “spanned heading” -->
-          <xsl:with-param name="string" select="'s'"/>
-          <xsl:with-param name="count">
-            <xsl:value-of select="@colspan - 1"/>
-          </xsl:with-param>
+    </cell>
+  </xsl:template>
+
+  <xsl:template name="colspan">
+    <xsl:param name="count"/>
+    <xsl:param name="row"/>
+    <xsl:param name="column"/>
+    <!-- * This template generates a bogus Cell instance for the number -->
+    <!-- * of columns specified in the Colspan attribute. It does that -->
+    <!-- * by recursively calling itself. -->
+    <xsl:choose>
+      <!-- * If count has reached 1, we have no more columns to process, -->
+      <!-- * so we stop and return to the calling template. -->
+      <xsl:when test="$count = 1"/> <!-- * do nothing -->
+      <xsl:otherwise>
+        <!-- * Otherwise, count has not reached 1, so we need to -->
+        <!-- * generate a Cell. -->
+        <cell>
+          <!-- * Add row and column attributes, plus row and column data -->
+          <!-- * in the string value of the element. -->
+          <xsl:call-template name="row-column-data">
+            <xsl:with-param name="row" select="$row"/>
+            <xsl:with-param name="column" select="$column"/>
+          </xsl:call-template>
+          <!-- * s  = "spanned heading" -->
+          <xsl:text>s</xsl:text>
+        </cell>
+        <!-- * Decrement the count, increment the column number, and -->
+        <!-- * recursively call the current template with those values -->
+        <!-- * in order to generate the next Cell for this Colspan. -->
+        <xsl:call-template name="colspan">
+          <xsl:with-param name="count" select="$count - 1"/>
+          <xsl:with-param name="row" select="$row"/>
+          <xsl:with-param name="column" select="$column + 1"/>
         </xsl:call-template>
-      </xsl:if>
-      <xsl:choose>
-        <!-- * output a space after each format letter except -->
-        <!-- * the last one in the row -->
-        <xsl:when test="position() = last()"/> <!-- do nothing -->
-        <xsl:otherwise>
-          <xsl:text> </xsl:text>
-        </xsl:otherwise>
-      </xsl:choose>
-    </xsl:for-each>
-    <!-- * the following dot signals end of table "format" spec -->
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <xsl:template name="rowspan">
+    <xsl:param name="count"/>
+    <xsl:param name="row"/>
+    <xsl:param name="column"/>
+    <!-- * This template generates bogus Cell instances for the number -->
+    <!-- * of rows specified in the Rowspan attribute. It does that -->
+    <!-- * by recursively calling itself. -->
     <xsl:choose>
-      <xsl:when test="position() = last()">
-      <xsl:text> .&#10;</xsl:text>
-    </xsl:when>
-    <xsl:otherwise>
-      <xsl:text>&#10;</xsl:text>
-    </xsl:otherwise>
+      <!-- * If count has reached 1, we have no more rows to process, -->
+      <!-- * so we stop and return to the calling template. -->
+      <xsl:when test="$count = 0"/> <!-- * do nothing -->
+      <xsl:otherwise>
+        <!-- * Otherwise, count has not reached 1, so we need to -->
+        <!-- * generate a Cell. -->
+        <cell>
+          <!-- * Add row and column attributes, plus row and column data -->
+          <!-- * in the string value of the element. -->
+          <xsl:call-template name="row-column-data">
+            <xsl:with-param name="row" select="$row"/>
+            <xsl:with-param name="column" select="$column"/>
+          </xsl:call-template>
+          <!-- * ^  = "vertically spanned heading" -->
+          <xsl:text>^</xsl:text>
+        </cell>
+        <!-- * Decrement the count, increment the row number, and -->
+        <!-- * recursively call the current template with those values -->
+        <!-- * in order to generate the next Cell for this Rowspan. -->
+        <xsl:call-template name="rowspan">
+          <xsl:with-param name="count" select="$count - 1"/>
+          <xsl:with-param name="row" select="$row + 1"/>
+          <xsl:with-param name="column" select="$column"/>
+        </xsl:call-template>
+      </xsl:otherwise>
     </xsl:choose>
   </xsl:template>
 
+  <xsl:template name="row-column-data">
+    <xsl:param name="row"/>
+    <xsl:param name="column"/>
+    <!-- * Generate row and column attributes for this Cell -->
+    <xsl:attribute name="row"><xsl:value-of select="$row"/></xsl:attribute>
+    <xsl:attribute name="column"><xsl:value-of select="$column"/></xsl:attribute>
+    <!-- * Generate this first part of the three-part value of this -->
+    <!-- * Cell; the three-part format is: -->
+    <!-- * -->
+    <!-- * $row,$column,[tbl(1) format letter] -->
+    <!-- * -->
+    <!-- * The reason we put the row and column numbers into the value -->
+    <!-- * of the Cell is so that we can remove duplicates from the -->
+    <!-- * cell list later (as far as why the duplicates are there to -->
+    <!-- * begin with, see the HACK ALERT above. -->
+    <xsl:value-of select="$row"/>
+    <xsl:text>,</xsl:text>
+    <xsl:value-of select="$column"/>
+    <xsl:text>,</xsl:text>
+  </xsl:template>
+
 </xsl:stylesheet>