I have been looking for a suitable solution for conversion of an HTML table to LaTeX. I have found the following questions which is similar to my requirement:
XML table to LaTeX
Converting XHTML table to LaTeX using XSLT
But both of these were not 100% helpful with my current requirement, as it does not involve in conversion of content with both colspan and rowspan.
The input HTML would look like:
<html>
<head>
</head>
<body>
<table border="1">
<tr>
<td>This</td>
<td>is</td>
<td>a</td>
<td>test</td>
</tr>
<tr>
<td colspan="2">This is</td>
<td>a</td>
<td>test</td>
</tr>
<tr>
<td>This</td>
<td>is</td>
<td colspan="2">a test</td>
</tr>
<tr>
<td rowspan="2">This</td>
<td colspan="2">is a</td>
<td rowspan="2">test</td>
</tr>
<tr>
<td>is</td>
<td>a</td>
</tr>
</table>
</body>
</html>
The LaTeX code I expect as the output is:
\documentclass{standalone}
\usepackage{multirow}
\begin{document}
\begin{tabular}{*{4}{|l}|}
\hline
This & is & a & test\\
\hline
\multicolumn{2}{|l|}{This is} & a & test \\
\hline
This & is & \multicolumn{2}{|l|}{a test} \\
\hline
\multirow{2}*{This} & \multicolumn{2}{|l|}{is a} & \multirow{2}*{test} \\
\cline{2-3}
& is & a & \\
\hline
\end{tabular}
\end{document}
I took the solution given by #DavidCarlisle in Converting XHTML table to LaTeX using XSLT and modified as given below:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/">
\documentclass{standalone}
\usepackage{multirow}
\begin{document}
<xsl:apply-templates/>
\end{document}
</xsl:template>
<xsl:template match="table">
<xsl:variable name="noc" select="max(tr/sum(td/(#colspan/number(.),1)[1]))"/>
<xsl:text>\begin{tabular}{*{</xsl:text>
<xsl:value-of select="$noc"/>
<xsl:text>}{|l}|}
</xsl:text>
<xsl:apply-templates select="tr[1]">
<xsl:with-param name="rspans" select="for $i in 1 to xs:integer($noc) return 0"/>
</xsl:apply-templates>
<xsl:text>\end{tabular}</xsl:text>
</xsl:template>
<xsl:template match="tr">
<xsl:param name="rspans"/>
<xsl:text/>% [<xsl:value-of select="$rspans"/>]
<xsl:variable name="tr" select="."/>
<xsl:for-each select="$rspans">
<xsl:variable name="c" select="position()"/>
<xsl:variable name="td" select="$tr/td[count($rspans[position() <=$c][.=0])]"/>
<xsl:if test=".=0">
<xsl:if test="$td/#rowspan[. > 1]">
<xsl:text>\multirow{</xsl:text>
<xsl:value-of select="$td/#rowspan"/>
<xsl:text>}{*}{</xsl:text>
</xsl:if>
<xsl:if test="$td/#colspan[. > 1]">
<xsl:text>\multicolumn{</xsl:text>
<xsl:value-of select="$td/#colspan"/>
<xsl:text>}{|l|}{</xsl:text>
</xsl:if>
<xsl:apply-templates select="$td"/>
<xsl:if test="$td/#colspan[. > 1]">}</xsl:if>
<xsl:if test="$td/#rowspan[. > 1]">}</xsl:if>
</xsl:if>
<xsl:choose>
<xsl:when test=". >1 and position()=last()">&\\\hline
</xsl:when>
<xsl:when test="position()=last()">\\\hline
</xsl:when>
<xsl:otherwise>&</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:apply-templates select="following-sibling::tr[1]">
<xsl:with-param name="rspans" select="for $c in 1 to count($rspans)
return
($rspans[$c] +
td[count($rspans[position() <=$c][.=0])]/(#rowspan,1)[1]
-1)"/>
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
The resulting LaTeX code contains following problems:
I am not able to put the \cline{...} command properly. \cline{..} is reqired if there is a row spanned in the current row. Otherwise \hline is required.
In the second line there is an extra & (ampersand) symbol appearing at the end just before \\\hline.
In the last row, third column (content "a") is missing.
Is there any solution for this problem.
Related
`input html table:
<table>
<tr>
<td>First Name:</td>
<td>Bill Gates</td>
</tr>
<tr>
<td rowspan="2">Telephone:</td>
<td>5</td>
</tr>
<tr>
<td>7</td>
</tr>
<tr>
<td>4545</td>
<td>5656</td>
</tr>
<tr>
<td>q56</td>
<td>q566</td>
</tr>
</table>
My xslt program:
<xsl:template match="table">
<xsl:variable name="noc" select="max(tr/sum(td/(#colspan/number(.),1)[1]))"/>
<xsl:text>\begin{tabular}{*{</xsl:text>
<xsl:value-of select="$noc"/>
<xsl:text>}{|l}|}
</xsl:text>
<xsl:apply-templates select="tr[1]">
<xsl:with-param name="rspans" select="for $i in 1 to xs:integer($noc) return 0"/>
</xsl:apply-templates>
<xsl:text>\end{tabular}</xsl:text>
</xsl:template>
<xsl:template match="tr">
<xsl:param name="rspans"/>
<xsl:text/>% [<xsl:value-of select="$rspans"/>]
<xsl:variable name="tr" select="."/>
<xsl:for-each select="$rspans">
<xsl:variable name="c" select="position()"/>
<xsl:variable name="td" select="$tr/td[count($rspans[position() <=$c][.=0])]"/>
<xsl:if test=".=0">
<xsl:if test="$td/#rowspan[. > 1]">
<xsl:text>\multirow{</xsl:text>
<xsl:value-of select="$td/#rowspan"/>
<xsl:text>}{*}{</xsl:text>
</xsl:if>
<xsl:if test="$td/#colspan[. > 1]">
<xsl:text>\multicolumn{</xsl:text>
<xsl:value-of select="$td/#colspan"/>
<xsl:text>}{|l|}{</xsl:text>
</xsl:if>
<xsl:apply-templates select="$td"/>
<xsl:if test="$td/#colspan[. > 1]">}</xsl:if>
<xsl:if test="$td/#rowspan[. > 1]">}</xsl:if>
</xsl:if>
<xsl:choose>
<xsl:when test=". >1 and position()=last()">&\\
</xsl:when>
<xsl:when test="position()=last()">\\
</xsl:when>
<xsl:otherwise>&</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:apply-templates select="following-sibling::tr[1]">
<xsl:with-param name="rspans" select="for $c in 1 to count($rspans)
return
($rspans[$c] +
td[count($rspans[position() <=$c][.=0])]/(#rowspan,1)[1]
-1)"/>
</xsl:apply-templates>
</xsl:template>
I am getting tex file like this
\begin{tabular}{*{2}{|l}|}
% [0 0]
First Name:&Bill Gates\\
% [0 0]
\multirow{2}{*}{Telephone:}&5\\
% [1 0]
&7\\
% [0]
4545\\
% [0]
q56\\
\end{tabular}
In input html rowspan is 2 value, it takes only 2 rows. But in our coding its take throughout after rowspan and not printing properly in tr.
You can see our tex code its not printing last two tr tag in properly.
Please give solution of rowspan convertion.
Im new to XSLT and My XML is below
<Aus>
<au>
<ele>
<auid>Au1</auid>
<cid>C1</cid>
<fn>F1</fn>
<sn>S1</sn>
<dept>D1</dept>
</ele>
<ele>
<auid>Au2</auid>
<cid>C2</cid>
<fn>F2</fn>
<sn>S2</sn>
<dept>D2</dept>
</ele>
<ele>
<auid>Au3</auid>
<cid>C3</cid>
<fn>F3</fn>
<sn>S3</sn>
<dept>D4</dept>
</ele>..............
</au>
</Aus>
I want the output like below in html view using XSLT conversion
but XSLT code should be simple to identify next columns by position increment. Please help me.
My current code is
<xsl:for-each select="//Aus/au">
<table>
<tr>
<td><xsl:value-of select="ele[1]/auid"/></td><td><xsl:value-of select="ele[2]/auid"/></td><td><xsl:value-of select="ele[3]/auid"/></td>
</tr>
<tr>
<td><xsl:value-of select="ele[1]/cid"/></td><td><xsl:value-of select="ele[2]/cid"/></td><td><xsl:value-of select="ele[3]/cid"/></td>
</tr>
..........
</table>
</xsl:for-each>
I would do it like this:
<xsl:template match="Aus/au">
<table>
<tbody>
<xsl:apply-templates select="ele[1]/*" mode="row"/>
</tbody>
</table>
</xsl:template>
<xsl:template match="ele/*" mode="row">
<tr>
<xsl:variable name="pos" select="position()"/>
<xsl:apply-templates select="../../ele/*[$pos]"/>
</tr>
</xsl:template>
<xsl:template match="ele/*">
<td>
<xsl:value-of select="."/>
</td>
</xsl:template>
https://xsltfiddle.liberty-development.net/gVhEaiK
The sample you linked in your comment seems to have more complex input data as it seems there are nested elements, also there seem to be lots of elements without data; however, the templates could be adapted to e.g.
<xsl:template match="authorDetails/authors">
<table>
<tbody>
<xsl:apply-templates
select="element[1]/descendant::*[not(*)]" mode="row"/>
</tbody>
</table>
</xsl:template>
<xsl:template match="element//*" mode="row">
<tr>
<th>
<xsl:value-of select="local-name()"/>
</th>
<xsl:variable name="pos" select="position()"/>
<xsl:apply-templates select="ancestor::authors/element/descendant::*[not(*)][$pos]"/>
</tr>
</xsl:template>
<xsl:template match="element//*">
<td>
<xsl:value-of select="."/>
</td>
</xsl:template>
Example: https://xsltfiddle.liberty-development.net/gVhEaiK/5
I need to perform a cyclical sequence within another public secuanci and get the data of each person and each status, the problem that I have with the code is that it does not iterate the data that I am delivering. I need help in the second for-each when I have empty fields and then I can work with Word content controls
XML file
<?xml version="1.0"?>
<emailList>
<person>
<name>name 1</name>
<email>g#gmail.com</email>
<status>
<active>1</active>
<active>2</active>
<active>3</active>
</status>
</person>
<person>
<name>name 2</name>
<email>n#hotmail.com</email>
<status>
<active>4</active>
<active>5</active>
</status>
</person>
</emailList>
XSL file
<xsl:stylesheet version="1.0">
<html xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xsl:version="1.0">
<head>
<title>Email Listing</title>
</head>
<body>
<table>
<tr>
<th>Name</th>
<th>E-mail Address</th>
<th>Status</th>
</tr>
<xsl:for-each select="emailList/person">
<tr>
<td><xsl:value-of select="name"/></td>
<td><xsl:value-of select="email"/></td>
<td>
<xsl:for-each select="emailList/person/status">
<xsl:value-of select="active"/>
</xsl:for-each>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
You inner xsl:for-each will be relative to the outer one, which is selecting person.
Try changing it to this...
<xsl:for-each select="status/active">
<xsl:value-of select="."/>
</xsl:for-each>
Where . selects the current node.
If you wanted to separate the values by commas, you could do this....
<xsl:for-each select="status/active">
<xsl:if test="position() > 1">,</xsl:if>
<xsl:value-of select="."/>
</xsl:for-each>
Better still, upgrade to XSLT 2.0 and do away with this xsl:for-each entirely..
<td>
<xsl:value-of select="status/active" separator="," />
</td>
<xsl:template match="/">
<html>
<head>
<title>Email Listing</title>
</head>
<body>
<table>
<tr>
<th>Name</th>
<th>E-mail Address</th>
<th>Status</th>
</tr>
<xsl:for-each select="emailList/person">
<tr>
<td><xsl:value-of select="name"/></td>
<td><xsl:value-of select="email"/></td>
<td>
<xsl:for-each select="status">
<xsl:value-of select="active"/>
</xsl:for-each>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
I have following xml document:
...
<x>
<symptom><descr></descr></symptom>
<cause></cause>
<solution></solution>
<cause></cause>
<solution></solution>
</x>
...
In my document I have several <x>
In each <x> I have only one <symptom> and n <cause> and <solution> whereby the amount of <cause> and <solution> is always the same.
I want to get following autmatically generated structure:
<table>
<tr>
<td rowspan=count(cause)><xsl:value-of select="symptom/descr"></td>
<td><xsl:value-of select="cause"></td>
<td><xsl:value-of select="symptom"></td>
<tr>
<tr>
<td><xsl:value-of select="cause"></td>
<td><xsl:value-of select="symptom"></td>
<tr>
...
</table>
I tried following code, which I know is totally wrong. But I'm stuck since several hours and couldn't find any good solution on the internet.
<xsl:for-each select="cause">
<tr>
<td rowspan="count(.)">
<xsl:value-of select="../descr[1]"/>
</td>
<td>
<xsl:value-of select="."/>
</td>
<xsl:for-each select="../solution">
<td>
<xsl:value-of select="."/>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
You're on the right lines with one tr per cause, how about this:
<xsl:template match="x">
<table>
<xsl:for-each select="cause">
<!-- the index of this cause within the list of causes in the current x -->
<xsl:variable name="pos" select="position()" />
<tr>
<!-- first cause - create the spanning symptom cell -->
<xsl:if test="$pos = 1">
<td rowspan="{last()}"><xsl:value-of select="../symptom/descr"/></td>
</xsl:if>
<!-- this cause -->
<td><xsl:value-of select="." /></td>
<!-- the matching solution -->
<td><xsl:value-of select="../solution[$pos]" /></td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
A trick here is the last() function, which returns the total number of nodes that the current for-each (or apply-templates) is processing, which in this case is precisely the number of rows you want to span.
This is based on the assumption that you want as result a table with following structure: Given an example input XML
<x>
<symptom>
<descr>
Description
</descr>
</symptom>
<cause>
Cause 1
</cause>
<solution>
Solution 1
</solution>
<cause>
Cause 2
</cause>
<solution>
Solution 2
</solution>
</x>
I assume you want following table:
<table>
<tr>
<td rowspan="2">Description</td>
<td>Cause 1</td>
<td>Solution 1</td>
</tr>
<tr>
<td>Cause 2</td>
<td>Solution 2</td>
</tr>
</table>
This could be done with following XSLT:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="x">
<table>
<xsl:for-each select="cause">
<xsl:apply-templates select="." mode="row">
<xsl:with-param name="amount" select="count(../cause)"/>
<xsl:with-param name="position" select="position()"/>
</xsl:apply-templates>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template match="cause" mode="row">
<xsl:param name="amount"/>
<xsl:param name="position"/>
<tr>
<xsl:if test="$position = 1">
<td rowspan="{$amount}">
<xsl:value-of select="//symptom/descr"/>
</td>
</xsl:if>
<td>
<xsl:value-of select="."/>
</td>
<td>
<xsl:value-of select="following-sibling::solution"/>
</td>
</tr>
</xsl:template>
</xsl:transform>
For each cause a row is created by applying the template
<xsl:template match="cause" mode="row">
with the amount of rows and the the position of the current cause as parameters. If the position is 1, the description is written as value in a td with the amount of cause as value of rowspan.
Each row contains the value of the current cause:
<td>
<xsl:value-of select="."/>
</td>
and the value of the solution at the same position (the solution which is the following-sibling of the current cause):
<td>
<xsl:value-of select="following-sibling::solution"/>
</td>
In brief, my problem is that I want to loop through the contents of one child among identical children, but when I try to do so, it loops through the contents of all the children, giving me as many duplicates of the data as there are children.
Example code:
Input.xml
<?xml version="1.0"?>
<base>
<item name="item1">
<foo>
<type1>1</type1>
<type2>2</type2>
</foo>
<bar>
<type3>3</type3>
</bar>
</item>
<item name="item2">
<foo>
<type1>4</type1>
<type2>5</type2>
</foo>
<bar>
<type3></type3>
<!-- NOTE! This value is missing. Therefore we must put a blank value in the table-->
</bar>
</item>
<item name="item3">
<foo>
<type1>7</type1>
<type2></type2>
<!-- NOTE! This value is missing. Therefore we must put a blank value in the table-->
</foo>
<bar>
<type3>9</type3>
</bar>
</item>
</base>
tableMaker.xsl
<?xml version="1.0"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:cyg="http://CygNetSCADA.com/Schemas/CygNetEnterpriseObjects730">
<xsl:output method="html" encoding="UTF-8" />
<xsl:template match="/">
<html>
<body>
<table border="1">
<tr>
<th>Name</th>
<xsl:for-each select="base/item/*/*">
<th>
<xsl:value-of select="local-name()"/>
</th>
</xsl:for-each>
</tr>
<xsl:for-each select="base/item">
<tr>
<th>
<xsl:value-of select="#name"/>
</th>
<xsl:for-each select="foo/*">
<xsl:choose>
<xsl:when test=".[node()]">
<td>
<xsl:value-of select="." />
</td>
</xsl:when>
<xsl:otherwise>
<td />
<!-- This is for that empty value in item3 -->
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:for-each select="bar/*">
<xsl:choose>
<xsl:when test=".[node()]">
<td>
<xsl:value-of select="." />
</td>
</xsl:when>
<xsl:otherwise>
<td />
<!-- This is for that empty value in item2 -->
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
What happens in this example is that the generated HTML has 10 columns - one for "name", and "type1", "type2", "type3" three times (three times for the three elements; if there were 4 elements in my input there would be 3 more columns). I only want "type1", "type2", and "type3" to display once in the column. How might I go about doing this?
All help is appreciated and thanks in advance!
You're using XSL 2.0 so presumably you have access to the xsl:for-each-group element. It's what you need in this situation:
<tr>
<th>Name</th>
<xsl:for-each-group select="base/item/*/*" group-by="local-name()">
<th>
<xsl:value-of select="current-grouping-key()"/>
</th>
</xsl:for-each>
</tr>