The following XSL customization inserts the headers attribute in HTML table cells. It only supports header rows, not row headers in one of the columns.
Sample DocBook XML
<informaltable tabstyle="content">
<tgroup cols="4">
<colspec colwidth="630*" colname="col1" /> <colspec colwidth="2970*" colname="col2" /> <colspec colwidth="1350*" colname="col3" /> <colspec colwidth="3240*" colname="col4" /> <thead>
<row>
<entry colname="col1">
<para role="atgtablebody">A</para>
</entry> <entry colname="col2">
<para role="atgtablebody">Quick Brown Fox Jumps</para>
</entry> <entry colname="col3">
<para role="atgtablebody">Over</para>
</entry> <entry colname="col4">
<para role="atgtablebody">The Lazy Dog</para>
</entry>
</row>
</thead> <tbody>
<row>
<entry colname="col1">
<para role="atgtablebody">B</para>
</entry> <entry colname="col2">
<para role="atgtablebody">How high?</para>
</entry> <entry colname="col3">
<para role="atgtablebody">Under</para>
</entry> <entry colname="col4">
<para role="atgtablebody">The long day</para>
</entry>
</row> <row>
<entry colname="col1">
<para role="atgtablebody">C</para>
</entry> <entry colname="col2">
<para role="atgtablebody">Why?</para>
</entry> <entry colname="col3">
<para role="atgtablebody">Through</para>
</entry> <entry colname="col4">
<para role="atgtablebody">The leering dentist</para>
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
Customized DocBook XSL
<xsl:template match="d:entry|d:entrytbl" name="entry">
- [snip]
<xsl:if test="$charoff != ">
<xsl:attribute name="charoff">
<xsl:value-of select="$charoff"/>
</xsl:attribute>
</xsl:if>
<xsl:choose>
<xsl:when test="$cellgi = 'th'">
<xsl:attribute name="id">
<xsl:text>colhead</xsl:text> <xsl:value-of select="generate-id()"/>
</xsl:attribute>
</xsl:when> <xsl:when test="$cellgi = 'td'">
<xsl:if test="ancestor::*[@tabstyle='content']">
<xsl:attribute name="headers">
<xsl:choose>
<xsl:when test="@colname">
<xsl:call-template name="findHeaderCell">
<xsl:with-param name="colname" select="@colname" />
</xsl:call-template>
</xsl:when> <xsl:when test="@nameend">
<xsl:call-template name="findMultipleHeaderCells">
<xsl:with-param name="namest" select="@namest" /> <xsl:with-param name="nameend" select="@nameend" />
</xsl:call-template>
</xsl:when> <xsl:otherwise>
<xsl:message>ERROR: attempting to add a headers attribute to a table cell that has no colname or nameend attribute.</xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:if>
</xsl:when> <xsl:otherwise>
<xsl:message>ERROR: The cell type <xsl:value-of select="$cellgi" /> is neither th nor td.</xsl:message>
</xsl:otherwise>
</xsl:choose> [snip]
</xsl:template>
<xsl:template name="findHeaderCell">
<xsl:param name="colname" /> <xsl:choose>
<xsl:when test="../../../d:thead/d:row/d:entry[@colname=$colname]">
<xsl:text>colhead</xsl:text> <xsl:value-of select="generate-id(../../../d:thead/d:row/d:entry[@colname=$colname])"/>
</xsl:when> <xsl:when test="../../../d:thead/d:row/d:entry[@namest=$colname]">
<xsl:text>colhead</xsl:text> <xsl:value-of select="generate-id(../../../d:thead/d:row/d:entry[@namest=$colname])"/>
</xsl:when> <xsl:when test="../../../d:colspec[@colname=$colname]">
<xsl:apply-templates select="../../../d:colspec[@colname=$colname]" mode="findColspecWithMatch" />
</xsl:when> <xsl:otherwise>
<xsl:message>ERROR: No table header cells nor colspecs match the current table body cell. My value is: <xsl:value-of select="."/></xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="findMultipleHeaderCells">
<xsl:param name="namest" /> <xsl:param name="nameend" /> <xsl:param name="currentColname" select="$nameend" /> <xsl:param name="previousMatches" /> <!-- Find the header cell for the current column. Write its value. --> <xsl:variable name="newValue">
<xsl:call-template name="findHeaderCell">
<xsl:with-param name="colname" select="$currentColname" />
</xsl:call-template> <xsl:text> </xsl:text> <xsl:value-of select="$previousMatches" />
</xsl:variable>
<!-- If the currentColname does not match the namest, find the colspec element
- preceding the one that has a colname matching the currentColname. Call this
template with its colname in the currentColname param. -->
<xsl:choose>
<xsl:when test="$currentColname != $namest">
<xsl:call-template name="findMultipleHeaderCells">
<xsl:with-param name="currentColname">
<xsl:apply-templates select="../../../d:colspec[@colname=$currentColname]" mode="findPrecedingColname" />
</xsl:with-param> <xsl:with-param name="namest" select="$namest" /> <xsl:with-param name="nameend" select="$nameend" /> <xsl:with-param name="previousMatches" select="$newValue" />
</xsl:call-template>
</xsl:when> <xsl:otherwise>
<xsl:value-of select="$newValue" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Sample HTML Output
<table border="1">
<colgroup>
<col width="7%" class="col1" /> <col width="37%" class="col2" /> <col width="16%" class="col3" /> <col width="40%" class="col4" />
</colgroup> <thead>
<tr>
<th id="colheadd0e171">
<p class="atgtablebody">A</p>
</th> <th id="colheadd0e174">
<p class="atgtablebody">Quick Brown Fox Jumps</p>
</th> <th id="colheadd0e177">
<p class="atgtablebody">Over</p>
</th> <th id="colheadd0e180">
<p class="atgtablebody">The Lazy Dog</p>
</th>
</tr>
</thead> <tbody>
<tr>
<td headers="colheadd0e171">
<p class="atgtablebody">B</p>
</td> <td headers="colheadd0e174">
<p class="atgtablebody">How high?</p>
</td> <td headers="colheadd0e177">
<p class="atgtablebody">Under</p>
</td> <td headers="colheadd0e180">
<p class="atgtablebody">The long day</p>
</td>
</tr> <tr>
<td headers="colheadd0e171">
<p class="atgtablebody">C</p>
</td> <td headers="colheadd0e174">
<p class="atgtablebody">Why?</p>
</td> <td headers="colheadd0e177">
<p class="atgtablebody">Through</p>
</td> <td headers="colheadd0e180">
<p class="atgtablebody">The leering dentist</p>
</td>
</tr>
</tbody>
</table>
