1
<article>

<table id="tbl1">
<caption><p>Table 1. Sample Table</p></caption>

<thead>
<tr>
    <td colspan="3">I</td>
    <td colspan="4">II</td>
</tr>

<tr>
    <td>Sl. No.</td>
    <td>Name</td>
    <td>Place</td>
    <td>Subject 1</td>
    <td>Subject 2</td>
    <td>Subject 3</td>
    <td>Grade</td>
</tr>
</thead>

<tbody>
<tr>
    <td rowspan="3">1</td>
    <td colspan="2">Kishan</td>
    <td>95</td>
    <td>96</td>
    <td rowspan="2">97</td>
    <td>A</td>
</tr>

<tr>

    <td>Kishan</td>
    <td>Bangalore</td>
    <td>94</td>
    <td>96</td>

    <td>A</td>
</tr>

<tr>

    <td>Likhith</td>
    <td>Bhadravathi</td>
    <td>94</td>
    <td>94</td>
    <td>99</td>
    <td>A</td>
</tr>
</tbody>
</table>

</article>

Required OutPut: If colspan is 2 is coded in second cell, then third should not be there, next cell name should be "colname="3" (start Index is 0). Same for rowspan, if present row's first cell having rowspan="3", then next two rows should not have colname="0", those next two rows start cells will have the name="1" (start index is 0, thats why 1 means second cell). Please suggest for the XSLT coding two address both rowspan and colspan present in same table.

    <article>

<table id="tbl1">
<caption><p>Table 1. Sample Table</p></caption>

<thead>
<tr>
    <td colname="0:0">I</td>
    <td colname="0:3">II</td>
</tr>

<tr>
    <td colname="1:0">Sl. No.</td>
    <td colname="1:1">Name</td>
    <td colname="1:2">Place</td>
    <td colname="1:3">Subject 1</td>
    <td colname="1:4">Subject 2</td>
    <td colname="1:5">Subject 3</td>
    <td colname="1:6">Grade</td>
</tr>
</thead>


<tbody>
<tr>
    <td colname="2:0">1</td>
    <td colname="2:1">Kishan</td>
    <td colname="2:3">95</td>
    <td colname="2:4">96</td>
    <td colname="2:5">97</td>
    <td colname="2:6">A</td>
</tr>

<tr>

    <td colname="3:1">Kishan</td>
    <td colname="3:2">Bangalore</td>
    <td colname="3:3">94</td>
    <td colname="3:4">96</td>

    <td colname="3:6">A</td>
</tr>

<tr>

    <td colname="4:1">Likhith</td>
    <td colname="4:2">Bhadravathi</td>
    <td colname="4:3">94</td>
    <td colname="4:4">94</td>
    <td colname="4:5">99</td>
    <td colname="4:6">A</td>
</tr>
</tbody>
</table>

</article>

XSLT code from StackOverFlow site:

    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<!--rowspan in table with xslt-->
<xsl:template match="TABLE2">
  <tbody>
    <xsl:call-template name="processRows">
      <xsl:with-param name="rows" select="ROW"/>
    </xsl:call-template>
  </tbody>
</xsl:template>

<xsl:template name="processRows">
  <xsl:param name="rows"/>
  <xsl:param name="index" select="1"/>
  <!-- Bit vector for the columns -->
  <xsl:param name="col1" select="0"/>
  <xsl:param name="col2" select="0"/>
  <xsl:param name="col3" select="0"/>
  <xsl:param name="col4" select="0"/>
  <xsl:param name="col5" select="0"/>
  <xsl:param name="col6" select="0"/>

  <xsl:variable name="cellsBefore2">
    <xsl:choose>
      <xsl:when test="$col1 > 0">0</xsl:when>
      <xsl:otherwise>1</xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <xsl:variable name="cellsBefore3">
    <xsl:choose>
      <xsl:when test="$col2 > 0">
        <xsl:value-of select="$cellsBefore2"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$cellsBefore2 + 1"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

   <xsl:variable name="cellsBefore4">
    <xsl:choose>
       <xsl:when test="$col3 > 0">
        <xsl:value-of select="$cellsBefore3"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$cellsBefore3 + 1"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

   <xsl:variable name="cellsBefore5">
    <xsl:choose>
       <xsl:when test="$col4 > 0">
        <xsl:value-of select="$cellsBefore4"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$cellsBefore4 + 1"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:variable name="cellsBefore6">
    <xsl:choose>
       <xsl:when test="$col5 > 0">
        <xsl:value-of select="$cellsBefore5"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$cellsBefore5 + 1"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>



  <row>
    <xsl:if test="$col1 = 0">
      <entry colname="1">
        <xsl:value-of select="$rows[$index]/CELL[1]/text()"/>
      </entry>
    </xsl:if>
    <xsl:if test="$col2 = 0">
      <entry colname="2">
        <xsl:value-of select="$rows[$index]/CELL[$cellsBefore2 + 1]/text()"/>
      </entry>
    </xsl:if>
    <xsl:if test="$col3 = 0">
      <entry colname="3">
        <xsl:value-of select="$rows[$index]/CELL[$cellsBefore3 + 1]/text()"/>
      </entry>
    </xsl:if>
    <xsl:if test="$col4 = 0">
      <entry colname="4">
        <xsl:value-of select="$rows[$index]/CELL[$cellsBefore4 + 1]/text()"/>
      </entry>
    </xsl:if>
     <xsl:if test="$col5 = 0">
      <entry colname="5">
        <xsl:value-of select="$rows[$index]/CELL[$cellsBefore5 + 1]/text()"/>
      </entry>
    </xsl:if>
     <xsl:if test="$col6 = 0">
      <entry colname="6">
        <xsl:value-of select="$rows[$index]/CELL[$cellsBefore6 + 1]/text()"/>
      </entry>
    </xsl:if>


  </row>
  <xsl:if test="$index &lt; count($rows)">
    <xsl:call-template name="processRows">
      <xsl:with-param name="rows" select="$rows"/>
      <xsl:with-param name="index" select="$index + 1"/>
      <xsl:with-param name="col1">
        <xsl:choose>
          <xsl:when test="$col1 > 0">
            <xsl:value-of select="$col1 - 1"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="number($rows[$index]/CELL[1]/@ROWSPAN) - 1"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:with-param>
      <xsl:with-param name="col2">
        <xsl:choose>
          <xsl:when test="$col2 > 0">
            <xsl:value-of select="$col2 - 1"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="number($rows[$index]/CELL[$cellsBefore2 + 1]/@ROWSPAN) - 1"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:with-param>
      <xsl:with-param name="col3">
        <xsl:choose>
          <xsl:when test="$col3 > 0">
            <xsl:value-of select="$col3 - 1"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="number($rows[$index]/CELL[$cellsBefore3 + 1]/@ROWSPAN) - 1"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:with-param>
      <xsl:with-param name="col4">
        <xsl:choose>
          <xsl:when test="$col4 > 0">
            <xsl:value-of select="$col4 - 1"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="number($rows[$index]/CELL[$cellsBefore4 + 1]/@ROWSPAN) - 1"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:with-param>
      <xsl:with-param name="col5">
        <xsl:choose>
          <xsl:when test="$col5 > 0">
            <xsl:value-of select="$col5 - 1"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="number($rows[$index]/CELL[$cellsBefore5 + 1]/@ROWSPAN) - 1"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:with-param>
       <xsl:with-param name="col6">
        <xsl:choose>
          <xsl:when test="$col6 > 0">
            <xsl:value-of select="$col6 - 1"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="number($rows[$index]/CELL[$cellsBefore6 + 1]/@ROWSPAN) - 1"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:with-param>


    </xsl:call-template>
  </xsl:if>
</xsl:template>
</xsl:stylesheet> 
Rudramuni TP
  • 1,268
  • 2
  • 16
  • 26
  • Please also show your XSLT code so that we can tell you where you went wrong. – Mathias Müller Feb 06 '14 at 16:47
  • My coding nearly 500 line, where I used static code, checking with previous row's each and every cell, whether it is having 'rowspan' or not, by that I addressed nearly 12 previous rows rowspan info. I got one sample from STACKOVERFLOW, where it is applicable only for ROWSPAN not for COLSPAN. Can I give that example. – Rudramuni TP Feb 06 '14 at 16:53
  • XSLT 2.0 or 1.0, and if 1.0 which processor are you using? I suspect the best way to attack this will be a tail recursive template that passes a parameter from one row to the next holding the current set of rowspan details, and that will require an extension function if you're limited to XSLT 1.0. See [this answer](http://stackoverflow.com/a/17838743/592139) for a similar approach I've suggested in the past. – Ian Roberts Feb 06 '14 at 16:58
  • I placed one more sample XSLT above, there only ROWSPAN issues are addressed. Please suggest for the COLSPAN too. – Rudramuni TP Feb 06 '14 at 17:02

1 Answers1

7

This is not at all simple. Basically, you are asking how to render an HTML table visually, by positioning each cell on a (equi-spaced) x-y grid of rows and columns.

This is complex, because the position of each cell depends not only on the width (colspan) of the preceding cells in the same row, but also on the position of cells in preceding rows that span more than one row. This position is not known before the preceding cells themselves have been processed - so this is a giant render-as-you-go cascading operation.

Due to this complexity, I suggest solving the basic problem in isolation first, before introducing additional constraints (e.g. separate header rows or numbers starting from 0).

For testing, I have used the following table1 as the input:

<table border="1">
    <tr>
        <td>Column 1</td>
        <td>Column 2</td>
        <td>Column 3</td>
    </tr>
    <tr>
        <td rowspan="2">A</td>
        <td colspan="2">B</td>
    </tr>
    <tr>
        <td>C</td>
        <td>D</td>
    </tr>
    <tr>
        <td>E</td>
        <td rowspan="2" colspan="2">F</td>
    </tr>
    <tr>
        <td>G</td>
    </tr>
    <tr>
        <td colspan="3">H</td>
    </tr>
</table>

Applying the folowing stylesheet:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/table">
    <table>
        <xsl:text>&#10;</xsl:text>
        <xsl:call-template name="generate-rows">
            <xsl:with-param name="current-row" select="1"/>
            <xsl:with-param name="last-row" select="count(tr)"/>
        </xsl:call-template>
    </table>
</xsl:template>


<xsl:template name="generate-rows">
    <xsl:param name="current-row"/>
    <xsl:param name="last-row"/>
    <xsl:param name="result" select="some-dummy-node-to-make-this-a-node"/>

    <!-- append current-row to previous result -->
    <xsl:variable name="new-result">
        <xsl:copy-of select="$result"/>
        <row num="{$current-row}">
            <xsl:text>&#10;</xsl:text>
            <!-- generate cells for current-row -->
            <xsl:call-template name="generate-cells">
                <xsl:with-param name="current-row" select="$current-row"/>
                <xsl:with-param name="current-cell" select="1"/>
                <xsl:with-param name="x" select="1"/>
                <xsl:with-param name="last-cell" select="count(tr[$current-row]/td)"/>
                <xsl:with-param name="previous-rows" select="$result"/>
            </xsl:call-template>
        </row>
        <xsl:text>&#10;</xsl:text>
    </xsl:variable>

    <xsl:choose>
        <xsl:when test="$current-row &lt; $last-row">
            <!-- recursive call -->
            <xsl:call-template name="generate-rows">
                <xsl:with-param name="current-row" select="$current-row + 1"/>
                <xsl:with-param name="last-row" select="$last-row"/>
                <xsl:with-param name="result" select="$new-result"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <!-- return result -->
            <xsl:copy-of select="$new-result"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>


<xsl:template name="generate-cells">
    <xsl:param name="current-row"/>
    <xsl:param name="current-cell"/>
    <xsl:param name="x"/>
    <xsl:param name="last-cell"/>
    <xsl:param name="previous-rows"/>

    <xsl:variable name="my-cell" select="tr[$current-row]/td[$current-cell]" />

    <xsl:choose>
        <!-- if there's a collision, move one place to the right -->
        <xsl:when test="exsl:node-set($previous-rows)/row/cell[@x &lt;= $x and @x + @width > $x and @y + @height > $current-row]">
            <xsl:call-template name="generate-cells">
                <xsl:with-param name="current-row" select="$current-row"/>
                <xsl:with-param name="current-cell" select="$current-cell"/>
                <xsl:with-param name="x" select="$x + 1"/>
                <xsl:with-param name="last-cell" select="$last-cell"/>
                <xsl:with-param name="previous-rows" select="$previous-rows"/>
            </xsl:call-template>
        </xsl:when>

        <xsl:otherwise>     
            <xsl:variable name="width">
                <xsl:choose>
                    <xsl:when test="$my-cell/@colspan">
                        <xsl:value-of select="$my-cell/@colspan"/>
                    </xsl:when>
                    <xsl:otherwise>1</xsl:otherwise>
                </xsl:choose>
            </xsl:variable>

             <xsl:variable name="height">
                <xsl:choose>
                    <xsl:when test="$my-cell/@rowspan">
                        <xsl:value-of select="$my-cell/@rowspan"/>
                    </xsl:when>
                    <xsl:otherwise>1</xsl:otherwise>
                </xsl:choose>
             </xsl:variable>

            <xsl:text>&#9;</xsl:text>
             <cell num="{$current-cell}" y="{$current-row}" x="{$x}" width="{$width}" height="{$height}">
                 <xsl:value-of select="$my-cell"/>
             </cell>
            <xsl:text>&#10;</xsl:text>

            <xsl:if test="$current-cell &lt; $last-cell">
                <xsl:call-template name="generate-cells">
                    <xsl:with-param name="current-row" select="$current-row"/>
                    <xsl:with-param name="current-cell" select="$current-cell + 1"/>
                    <xsl:with-param name="x" select="$x + $width"/>
                    <xsl:with-param name="last-cell" select="count(tr[$current-row]/td)"/>
                    <xsl:with-param name="previous-rows" select="$previous-rows"/>
                </xsl:call-template>
            </xsl:if>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>

produces the following result:

<?xml version="1.0" encoding="UTF-8"?>
<table>
<row num="1">
    <cell num="1" y="1" x="1" width="1" height="1">Column 1</cell>
    <cell num="2" y="1" x="2" width="1" height="1">Column 2</cell>
    <cell num="3" y="1" x="3" width="1" height="1">Column 3</cell>
</row>
<row num="2">
    <cell num="1" y="2" x="1" width="1" height="2">A</cell>
    <cell num="2" y="2" x="2" width="2" height="1">B</cell>
</row>
<row num="3">
    <cell num="1" y="3" x="2" width="1" height="1">C</cell>
    <cell num="2" y="3" x="3" width="1" height="1">D</cell>
</row>
<row num="4">
    <cell num="1" y="4" x="1" width="1" height="1">E</cell>
    <cell num="2" y="4" x="2" width="2" height="2">F</cell>
</row>
<row num="5">
    <cell num="1" y="5" x="1" width="1" height="1">G</cell>
</row>
<row num="6">
    <cell num="1" y="6" x="1" width="3" height="1">H</cell>
</row>
</table>

As you can see, the x-y positioning of each cell corresponds to the visual rendering of the original table in a browser:

enter image description here

--
1. From http://en.wikipedia.org/wiki/Help:Table


For XSLT 2.0:

Change the stylesheet declaration to:

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

Change line #66 to:

<xsl:when test="$previous-rows/row/cell[@x &lt;= $x and @x + @width > $x and @y + @height > $current-row]">
michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
  • Thanks for suggestion, I am getting error at 68th line of suggested XSLT. – Rudramuni TP Dec 04 '14 at 08:54
  • I am getting Error at ''. – Rudramuni TP Dec 04 '14 at 08:57
  • Saxon 9he, Error message - Cannot find a matching 1-argument function named {http://exslt.org/common}node-set() at xsl:call-template name="generate-cells" (file:/D:/Rudramuni/XSLT/Samples/Functions/RowSpanFinal/RowSpan.xsl#29) at xsl:call-template name="generate-rows" (file:/D:/Rudramuni/XSLT/Samples/Functions/RowSpanFinal/RowSpan.xsl#10) – Rudramuni TP Dec 04 '14 at 10:02
  • The above code is XSLT 1.0. For XSLT 2.0, just remove the exsl:node-set() function (or construct a dummy one). I'll post an update, if you need it. – michael.hor257k Dec 04 '14 at 10:23
  • If I remove as suggested, third row elements are not shifting as required. Please, can you help me with XSLT2 code. – Rudramuni TP Dec 04 '14 at 10:37
  • Please note: I have made a change in line #19 of the original XSLT 1.0 code (which solved a problem when running with Saxon 6.5), and added two modifications you should make for XSLT 2.0. These three together should be it, hopefully. – michael.hor257k Dec 04 '14 at 11:45
  • Now, Error on line 66 - XPTY0019: Required item type of first operand of '/' is node(); supplied value has item type xs:string at xsl:call-template name="generate-cells" (file:/D:/Rudramuni/XSLT/Samples/Fu nctions/RowSpanFinal/RowSpan.xsl#27) at xsl:call-template name="generate-rows" (file:/D:/Rudramuni/XSLT/Samples/Fun ctions/RowSpanFinal/RowSpan.xsl#8) – Rudramuni TP Dec 04 '14 at 11:56
  • With Saxon8.jar, it is working. But Saxon9h above error i am getting. – Rudramuni TP Dec 04 '14 at 12:02
  • I have tested this successfully with Saxon 8.9, 9.3 and 9.5. Please copy the **exact** code from here and see if the problem persists: http://xsltransform.net/bFDb2C3 – michael.hor257k Dec 04 '14 at 12:06
  • It is working, sorry now I copied Complete code and altered as suggested, working perfectly. – Rudramuni TP Dec 04 '14 at 12:06
  • C, like this any child elements, then it is not working. Can I change value-of select to APPLY-TEMPLATES? Please suggest. – Rudramuni TP Dec 04 '14 at 12:08
  • Yes, I believe so (provided you *have* templates to apply..). – michael.hor257k Dec 04 '14 at 12:12
  • 97th Line, altered as , but unable to get for C – Rudramuni TP Dec 04 '14 at 12:25
  • 1
    Come now, this is basic XSLT and you know much better than that: you need to add the *identity transform* template to the stylesheet. Otherwise you'll be only applying the built-in template that copies (only) text. – michael.hor257k Dec 04 '14 at 12:31
  • Excellent Sir. Sorry for asking this type of question. You identified the area of need so quickly. By your suggestion long time pending TABLE ROWSPAN issue solved. Thanks a lot for your help. – Rudramuni TP Dec 04 '14 at 12:39