xsl-list
[Top] [All Lists]

AW: [xsl] Stylesheet for converting XHTML tables to CALS

2006-03-08 06:51:26
Hi again,

I tried to come up with a solution for the colspan and rowspan problem 
when doing a HTML to CALS table transformation.
All I could think of was a three pass transformation.

1. create additional table cells for each colspan attribute
   (number according to the colspan value)

2. create additional table cells for each rowspan attribute in a preceding row

3. do the transformation an delete previously created cells


Here comes the stylesheet:



<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE xsl:stylesheet [
        <!ENTITY preceding_rowspan_td 
"preceding::xhtml:td[count(preceding-sibling::xhtml:td)=current()/count(preceding-sibling::xhtml:td)+1
 and @rowspan]">
]>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; 
xmlns:ln="http://www.lexisnexis.at/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="xsldoc xs 
ln xhtml" xmlns:doc="http://docbook.org/ns/docbook";>
        <!-- 
===================================================================             
   -->
        <!--                                                                    
                        -->
        <!-- This Stylesheets converts arbitrary XHTML tables into tables 
conforming to the -->
        <!-- OASIS CALS model                                                   
                                -->
        <!--                                                                    
                                        -->
        <!--                                                                    
                -->
        <!-- Author: Roman Huditsch, 
roman(_dot_)huditsch(_at_)lexisnexis(_dot_)at                                  
-->
        <!--                                                                    
                -->
        <!-- 
===================================================================             
   -->
        <xsl:output method="xhtml" version="1.0" encoding="ISO-8859-1"/>
        <xsldoc:author>Roman Huditsch</xsldoc:author>
        <xsldoc:date>March 2, 2006</xsldoc:date>
        <xsldoc:version>Version 0.9</xsldoc:version>
        <xsl:namespace-alias stylesheet-prefix="" result-prefix="xhtml"/>
        <!-- 
===================================================================        -->
        <!-- Per default all existing nodes should be copied into the result 
document -->
        <!-- 
===================================================================        -->
        <xsl:template match="node() | @*" mode="process first-pass second-pass">
                <xsl:copy>
                        <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:copy>
                        <xsl:namespace name="doc" 
select="'http://docbook.org/ns/docbook'"/>
                        <xsl:namespace name="xhtml" 
select="'http://www.w3.org/1999/xhtml'"/>
                        <xsl:apply-templates select="$second-pass" 
mode="process"/>
                </xsl:copy>
        </xsl:template>
        <!-- 
===================================================================        -->
        <!-- Table                                                              
                                -->
        <!-- 
===================================================================        -->
        <xsl:template match="xhtml:table" mode="process">
                <!-- Create param to indicate border handling to child nodes    
-->
                <xsl:param name="border" select="not(starts-with(@border,'0'))" 
tunnel="yes"/>
                <!-- 
===================================================================        -->
                <!-- Continue with processing                                   
                                -->
                <!-- 
===================================================================        -->
                <doc:table>
                        <xsl:apply-templates select="(@border, @width)" 
mode="process"/>
                        <!-- <tgroup> -->
                        <doc:tgroup>
                                <xsl:message select="concat('Max Columns: ', 
ln:max_columns(.))"/>
                                <xsl:call-template name="generate-colspecs">
                                        <xsl:with-param name="max" 
select="ln:max_columns(.)" as="xs:double"/>
                                </xsl:call-template>
                        </doc:tgroup>
                        <!-- <thead> -->
                        <xsl:apply-templates select="xhtml:thead" 
mode="process"/>
                        <!-- <tbody> -->
                        <doc:tbody>
                                <xsl:apply-templates select="xhtml:tr | 
xhtml:tbody/xhtml:tr" mode="process"/>
                        </doc:tbody>
                        <!-- <tfoot> -->
                        <xsl:apply-templates select="xhtml:tfoot" 
mode="process"/>
                </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 &gt; $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(ln: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>
        <!-- 
===================================================================        -->
        <!-- thead, tfoot                                                       
                                -->
        <!-- 
===================================================================        -->
        <xsl:template match="xhtml:thead | xhtml:tfoot" mode="process">
                <xsl:element name="{local-name()}" 
namespace="http://docbook.org/ns/docbook";>
                        <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                                                                 
                        -->
        <!-- 
===================================================================        -->
        <xsl:template match="xhtml:td" mode="process">
                <xsl:variable name="position" 
select="count(preceding-sibling::*) + 1"/>
                <doc:entry colname="col{$position}">
                        <xsl:if test="@colspan &gt; 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 &gt; 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="ln:max_columns" as="xs:double">
                <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[(_at_)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))"/>
                        </xsl:otherwise>
                </xsl:choose>
        </xsl:function>
        <!-- 
==============================================================================  
                                           -->
        <!-- Function for searching the maximun width for a column - Input: 
Context Element, Output: Maximum Double             -->
        <!-- 
==============================================================================  
                                           -->
        <xsl:function name="ln: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:td[(_at_)id=('rowspan', 
'colspan')]" mode="process"/>
        <!-- 
===================================================================        -->
        <!-- First Pass - create empty cells for colspans                       
                -->
        <!-- 
===================================================================        -->
        <xsl:template match="xhtml:*[(_at_)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[&preceding_rowspan_td;]" 
mode="second-pass">
                <xsl:variable name="rowDiff" 
select="parent::xhtml:tr/count(preceding-sibling::*)+1 - 
(&preceding_rowspan_td;[1]/parent::xhtml:tr/count(preceding-sibling::*)+1)" 
as="xs:integer"/>
                <xsl:variable name="rowspan" 
select="&preceding_rowspan_td;[1]/@rowspan" as="xs:integer"/>
                <xsl:copy>
                        <xsl:copy-of select="@*"/>
                        <xsl:apply-templates mode="second-pass"/>
                </xsl:copy>
                <xsl:if test="$rowDiff &lt; $rowspan">
                        <xsl:for-each select="1 to ($rowspan - 1)">
                                <td id="rowspan"/>
                        </xsl:for-each>
                </xsl:if>
        </xsl:template>
</xsl:stylesheet> 


Every feedback is welcome!

wbr,
Roman




-----Ursprüngliche Nachricht-----
Von: David Carlisle [mailto:davidc(_at_)nag(_dot_)co(_dot_)uk] 
Gesendet: Montag, 06. März 2006 13:03
An: xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com
Betreff: Re: [xsl] Stylesheet for converting XHTML tables to CALS


I just finished my first attempt to transform XHTML tables 
into tables 
conforming to the CALS table model.
Attached is my XSLT 2.0 stylesheet. Every feedback is 
heartily welcome
:)

You appear to be generating elements in the xhtml namespace 
they should probably be in no-namespace or the new docbook 5 
namespace or something, seeing as <entry>  etc are not xhtml.

<xsl:param name="border" select="if(starts-with(@border,
'0')) then(xs:boolean(0)) else(xs:boolean(1))" as="xs:boolean"
tunnel="yes"/>

you are starting with a boolean value
starts-with(@border,'0')
then if it is true, taking an integer literal and coercing it 
to a boolean. If you need boolean values you can just use 
true() and false() but here you just need <xsl:param 
name="border" select="not(starts-with(@border,'0'))"/>


<xsl:apply-templates
select="xhtml:tr[not(parent::*[local-name()=('thead', 
'tbody', 'tfoot')])] | xhtml:tbody/xhtml:tr"/>

It's best not to use local-name() in such tests but just to 
use name tests (which are namespace aware) however in this 
case the current element is <table> so the parent of every 
element selected by xhtml:tr is table and so the filter 
testing on local-name is not doing anything.
so it could be

select="xhtml:tr| xhtml:tbody/xhtml:tr"/>


In 

  <xsl:template match="xhtml:th | xhtml:td">
              <xsl:variable name="position"
  select="count(preceding-sibling::*) + 1"/>
              <entry>
                      <xsl:if test="@colspan &gt; 1">
                              <xsl:attribute name="namest">
                                      <xsl:value-of
  select="concat('col',$position)"/>

don't you need to take account of any colspan attributes in 
earlier columns, and rowspan attributes in earlier rows in 
order to know which coulmn an entry in a table corresponds 
to? (This is the hardest part of switching between html and 
cals tables). In the above you are assuming that the second 
td entry in a row is corresponding to the second column, but 
this is not the case if the first entry spans columns, or if 
an entry in an earlier row spans into the first cell of this row.

David

______________________________________________________________
__________
This e-mail has been scanned for all viruses by Star. The 
service is powered by MessageLabs. For more information on a 
proactive anti-virus service working around the clock, around 
the globe, visit:
http://www.star.net.uk
______________________________________________________________
__________

--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: 
<mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--



--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--

<Prev in Thread] Current Thread [Next in Thread>
  • AW: [xsl] Stylesheet for converting XHTML tables to CALS, Huditsch, Roman \(LNG-VIE\) <=