For a certain output format (not HTML-like), I need to convert HTML tables to 'square' tables, where each colspan
and rowspan
is not only indicated in the parent cell, but also followed by the correct number of empty cells.
For example, the simple HTML table
<table>
<tr>
<th>test</th>
<th colspan="2">span 1/2</th>
<th colspan="3">span 2/2</th>
</tr>
<tr>
<td>col 1</td>
<td>col 2</td>
<td>col 3</td>
<td>col 4</td>
<td>col 5</td>
<td>col 6</td>
</tr>
</table>
should be translated to
<table>
<tr>
<th>test</th>
<th colspan="2">span 1/2</th>
<th /> <!-- < empty cell added -->
<th colspan="3">span 2/2</th>
<th /> <!-- < empty cell added -->
</tr>
..
(note: the output format uses a very different syntax, this is for clarity only!)
and, similarly, rowspans should be propagated to next <tr>
lines:
<table><tr><td rowspan="3" /><td rowspan="2" /><td /></tr>
<tr><td>data</td></tr>
<tr><td>data</td><td>data</td></tr>
</table>
which should come out as
<table>
<tr><td /><td /><td /></tr>
<tr><td /><td /><td>data</td></tr> <!-- 2 empty cells added -->
<tr><td /><td>data</td><td>data</td></tr> <!-- 1 empty cell added -->
<table>
Handling colspan
is straightforward:
<xsl:template name="add-empty">
<xsl:param name="repeat" />
<xsl:if test="$repeat > 1">
<td class="empty" />
<xsl:call-template name="add-empty">
<xsl:with-param name="repeat" select="$repeat - 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="th|td">
<td>
<xsl:apply-templates />
</td>
<xsl:if test="@colspan">
<xsl:call-template name="add-empty">
<xsl:with-param name="repeat" select="@colspan" />
</xsl:call-template>
</xsl:if>
</xsl:template>
This will add single th
or td
, check each one's colspan
, and insert as many empty cells as needed with a recursive call to the template add-empty
. The class attribute empty
is for debugging only.
The problem is in the rowspan
s. For this to work properly, it needs scanning over every previous tr
and keep a count of which columns need to be empty. That iteration would be something like
<xsl:if test="position() > 1">
<xsl:variable name="currentRow" select="position()" />
<xsl:for-each select="../tr[position() < $currentRow]">
<xsl:message>testing <xsl:value-of select="." /></xsl:message>
</xsl:for-each>
</xsl:if>
– it does not need to be called on the first row, because for that only colspan
s need adding. The question, then, is two-fold: how would I build the cell set list to add up to a correct set for the current row? And with such a list, how can I iterate over both this list (which is as long as the total number of columns in the table) and each row's th|td
elements?
The latter is a problem because I can iterate over either the cell set using something like
<xsl:for-each select="1 to string-length(cell-set)">
<xsl:if test="substring($cell-set, ., 1) = 'E'>
.. empty ..
...
</xsl:for-each>
(if cell-set
is a string), or over the 'current' tr
contents using
<xsl:for-each select="th|td">
..
in which case there is no direct relation to the contents of cell-set
. With the first, I don't know which index of td|th
to insert, with the second I don't know when to insert a blank.