Subject: [xsl] xhtml2cals table stylesheet From: "Roman Huditsch" <roman.huditsch@xxxxxx> Date: Fri, 27 Mar 2009 17:39:34 +0100 |
Hello xsl-list, During the last couple of years I pretty often had to transform tables from XHTML to CALS and the other way around. Since I couldn't find something usefull for doing the XHTML to CALS way (the other direction is pretty well covered by the docbook stylesheets), I tried to come up with an XSLT stylesheet myself. Since that was already some years ago and - as far as I can remember - there was a bug dealing with rowspans and colspans appearing on the same table cell, I reworked it and would like to ask you for your extremely valuable input. I am sure that there might be some XPaths that could be more elegant and smarter. I tested the stylesheet with various more or less complex tables and it seems to work as expected. What's still missing, though, is an XSpec description for some more test cases. But that's still to come. Thanks a lot, Roman Here it comes: <?xml version="1.0" encoding="ISO-8859-1"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:rh="http://www.huditsch.bkf.at/xslt/xhtml-cals" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsldoc="http://www.bacman.net/XSLdoc" xmlns:xhtml="http://www.w3.org/1999/xhtml" exclude-result-prefixes="#all" xmlns:doc="http://docbook.org/ns/docbook"> <xsl:output method="xhtml" version="1.0" encoding="ISO-8859-1" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" /> <xsldoc:author>Roman Huditsch</xsldoc:author> <xsldoc:date>March 27th, 2009</xsldoc:date> <xsldoc:version>Version 1.0</xsldoc:version> <!-- =================================================================== --> <!-- Per default all existing nodes should be copied into the result document --> <!-- =================================================================== --> <xsl:template match="node() | @*" mode="process first-pass second-pass"> <xsl:copy copy-namespaces="no"> <xsl:apply-templates select="@* | node()" mode="#current"/> </xsl:copy> </xsl:template> <!-- =================================================================== --> <!-- HTML --> <!-- =================================================================== --> <xsl:template match="xhtml:html"> <!-- =================================================================== --> <!-- First Pass - Create a variable with "expanded" table cells based on @colspan --> <!-- =================================================================== --> <xsl:variable name="first-pass" as="element()+"> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:apply-templates mode="first-pass"/> </xsl:copy> </xsl:variable> <!--xsl:message select="$first-pass"/--> <!-- =================================================================== --> <!-- Second Pass - Create a variable with "expanded" table cells based on @rowspan --> <!-- =================================================================== --> <xsl:variable name="second-pass" as="element()+"> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:apply-templates select="$first-pass/*" mode="second-pass"/> </xsl:copy> </xsl:variable> <!--xsl:message select="$second-pass"/--> <xsl:copy> <xsl:namespace name="doc" select="'http://docbook.org/ns/docbook'"/> <xsl:apply-templates select="$second-pass/*" mode="process"/> </xsl:copy> </xsl:template> <!-- =================================================================== --> <!-- Table --> <!-- =================================================================== --> <xsl:template match="xhtml:table" mode="process"> <!-- =================================================================== --> <!-- Continue with processing --> <!-- =================================================================== --> <doc:table> <xsl:apply-templates select="(@border, @width)" mode="process"/> <!-- <tgroup> --> <doc:tgroup cols="{rh:max_columns(.)}"> <xsl:message select="concat('Max Columns: ', rh:max_columns(.))"/> <xsl:call-template name="generate-colspecs"> <xsl:with-param name="max" select="rh:max_columns(.)" as="xs:double"/> <!-- Param to indicate border handling to child nodes --> <xsl:with-param name="border" select="not(starts-with(@border,'0'))" tunnel="yes" as="xs:boolean"/> </xsl:call-template> <!-- <thead> --> <xsl:apply-templates select="xhtml:thead" mode="process"> <!-- Param to indicate border handling to child nodes --> <xsl:with-param name="border" select="not(starts-with(@border,'0'))" tunnel="yes" as="xs:boolean"/> </xsl:apply-templates> <!-- <tbody> --> <doc:tbody> <xsl:apply-templates select="xhtml:tr | xhtml:tbody/xhtml:tr" mode="process"> <!-- Param to indicate border handling to child nodes --> <xsl:with-param name="border" select="not(starts-with(@border,'0'))" tunnel="yes" as="xs:boolean"/> </xsl:apply-templates> </doc:tbody> <!-- <tfoot> --> <xsl:apply-templates select="xhtml:tfoot" mode="process"> <!-- Param to indicate border handling to child nodes --> <xsl:with-param name="border" select="not(starts-with(@border,'0'))" tunnel="yes" as="xs:boolean"/> </xsl:apply-templates> </doc:tgroup> </doc:table> </xsl:template> <!-- =================================================================== --> <!-- Table attributes --> <!-- =================================================================== --> <xsl:template match="@border" mode="process"> <xsl:attribute name="frame"> <xsl:value-of select="if(starts-with(., '0')) then('none') else('all')"/> </xsl:attribute> </xsl:template> <xsl:template match="xhtml:table/@width" mode="process"> <xsl:attribute name="pgwide"> <xsl:value-of select="if(.='100%') then('0') else('1')"/> </xsl:attribute> </xsl:template> <!-- =================================================================== --> <!-- colspec --> <!-- =================================================================== --> <xsl:template name="generate-colspecs"> <xsl:param name="border" tunnel="yes"/> <xsl:param name="max" as="xs:double"/> <xsl:param name="count" select="1" as="xs:double"/> <xsl:choose> <xsl:when test="$count > $max"/> <xsl:otherwise> <doc:colspec colnum="{$count}" colname="{concat('col', $count)}" colsep="{if($border) then('1') else('0')}"> <xsl:choose> <xsl:when test="xhtml:colgroup/xhtml:col[$count]/@width"> <xsl:apply-templates select="xhtml:colgroup/xhtml:col[$count]/@width" mode="process"/> </xsl:when> <xsl:when test="( ./(*/* | *)/xhtml:td[$count] | ./(*/* | *)/xhtml:th[$count])/@width"> <xsl:attribute name="colwidth"> <xsl:value-of select="concat(rh:max_width(., $count), '*')"/> </xsl:attribute> </xsl:when> </xsl:choose> </doc:colspec> <xsl:call-template name="generate-colspecs"> <xsl:with-param name="max" select="$max"/> <xsl:with-param name="count" select="$count + 1"/> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template match="xhtml:col/@width" mode="process"> <xsl:attribute name="colwidth"> <xsl:value-of select="."/> </xsl:attribute> </xsl:template> <!-- =================================================================== --> <!-- thead, tfoot --> <!-- =================================================================== --> <xsl:template match="xhtml:thead | xhtml:tfoot" mode="process"> <xsl:element name="doc:{local-name()}"> <xsl:copy-of select="@valign"/> <xsl:apply-templates mode="process"/> </xsl:element> </xsl:template> <!-- =================================================================== --> <!-- TR --> <!-- =================================================================== --> <xsl:template match="xhtml:tr" mode="process"> <xsl:param name="border" tunnel="yes"/> <doc:row rowsep="{if($border) then('1') else('0')}"> <xsl:copy-of select="@valign"/> <xsl:apply-templates mode="process"/> </doc:row> </xsl:template> <!-- =================================================================== --> <!-- TD | TH --> <!-- =================================================================== --> <xsl:template match="xhtml:td | xhtml:th" mode="process"> <xsl:variable name="position" select="count(preceding-sibling::*) + 1"/> <doc:entry colname="col{$position}"> <xsl:if test="@colspan > 1"> <xsl:attribute name="namest"> <xsl:value-of select="concat('col',$position)"/> </xsl:attribute> <xsl:attribute name="nameend"> <xsl:value-of select="concat('col',$position + number(@colspan) - 1)"/> </xsl:attribute> </xsl:if> <xsl:if test="@rowspan > 1"> <xsl:attribute name="morerows"> <xsl:value-of select="number(@rowspan) - 1"/> </xsl:attribute> </xsl:if> <xsl:copy-of select="@align"/> <xsl:apply-templates mode="process"/> </doc:entry> </xsl:template> <!-- ======================================================================== ================ --> <!-- Function for counting the number of columns - Input: Context Element, Output: Maximum Double --> <!-- ======================================================================== ================ --> <xsl:function name="rh:max_columns" as="xs:integer"> <xsl:param name="context" as="element()"/> <xsl:choose> <xsl:when test="$context/xhtml:colgroup[not(@span)]"> <xsl:sequence select="count($context/xhtml:colgroup/xhtml:col)"/> </xsl:when> <xsl:when test="$context/xhtml:colgroup[@span]"> <xsl:sequence select="$context/xhtml:colgroup/@span"/> </xsl:when> <xsl:otherwise> <xsl:sequence select="max(for $x in ($context | $context/* )/xhtml:tr return count($x/xhtml:td | $x/xhtml:th))"/> </xsl:otherwise> </xsl:choose> </xsl:function> <!-- ======================================================================== ================ --> <!-- Function for searching the maximun width for a column - Input: Context Element, Output: Maximum Double --> <!-- ======================================================================== ================ --> <xsl:function name="rh:max_width" as="xs:double"> <xsl:param name="context" as="element()"/> <xsl:param name="count" as="xs:double"/> <xsl:sequence select="max(for $x in ($context/* | $context/*/* )/(xhtml:td[$count] | xhtml:th[$count])/@width return (if($x castable as xs:double) then($x) else(replace($x, '[a-z%]', ''))))"/> </xsl:function> <!-- =================================================================== --> <!-- Suppressed elements --> <!-- =================================================================== --> <xsl:template match="xhtml:colgroup | xhtml:*[@id=('rowspan', 'colspan')]" mode="process"/> <!-- =================================================================== --> <!-- First Pass - create empty cells for colspans --> <!-- =================================================================== --> <xsl:template match="xhtml:*[@colspan]" mode="first-pass"> <xhtml:td> <xsl:copy-of select="@*"/> <xsl:apply-templates mode="first-pass"/> </xhtml:td> <xsl:for-each select="1 to (xs:integer(@colspan)-1)"> <xhtml:td id="colspan"/> </xsl:for-each> </xsl:template> <!-- =================================================================== --> <!-- Second Pass - create empty cells for rowspans --> <!-- =================================================================== --> <xsl:template match="xhtml:td[not(@rowspan)] | xhtml:th[not(@rowspan)]" mode="second-pass"> <!-- what is my current position in the row? --> <xsl:variable name="currentPos" select="count(preceding-sibling::*)" as="xs:integer"/> <!-- what is the position of the next previous cell with a rowspan attribute in its row? That can also be 0, if there is no rowspan before --> <xsl:variable name="rowspanBeforePos" select="preceding::*[self::xhtml:td or self::xhtml:th][@rowspan][1]/count(preceding-sibling::*)" as="xs:integer?"/> <!-- calculate the difference between the row with the rowspan and my current row --> <xsl:variable name="rowDiff" select="parent::xhtml:tr/count(preceding-sibling::*) - preceding::*[@rowspan][1]/parent::xhtml:tr/count(preceding-sibling::*)" as="xs:integer?"/> <!-- number of rows to span --> <xsl:variable name="rowSpan" select="preceding::xhtml:*[@rowspan][1]/@rowspan" as="xs:integer?"/> <!-- It's possible that there is a colspan on the row spanning cell, too, that must be taken into account --> <xsl:variable name="colSpan" select="preceding::xhtml:*[@rowspan][1]/@colspan" as="xs:integer?"/> <!-- create empty cell for the rowspan --> <xsl:if test="($rowDiff < $rowSpan) and $currentPos=$rowspanBeforePos"> <xhtml:td id="rowspan"/> <xsl:for-each select="2 to $colSpan"> <xhtml:td id="rowspan"/> </xsl:for-each> </xsl:if> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:apply-templates mode="second-pass"/> </xsl:copy> </xsl:template> </xsl:stylesheet> _____ Ich verwende die kostenlose Version von SPAMfighter <http://www.spamfighter.com/lde> , die bisher 8421 Spammails entfernt und mir so eine Menge Zeit gespart hat. Rund 6 Millionen Leute nutzen SPAMfighter schon
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
Re: [xsl] ancestor/subsequent desce, Michael Ludwig | Thread | [xsl] xhtml2cals table stylesheet, Roman Huditsch |
Re: [xsl] ancestor/subsequent desce, Michael Ludwig | Date | [xsl] Numbering new nodes using con, Michael Ludwig |
Month |