Skip to content

Instantly share code, notes, and snippets.

@CanOfBees
Forked from xokomola/example-table.xml
Created December 21, 2016 15:15
Show Gist options
  • Save CanOfBees/5f63a13071c72004f0dbcdb287aef049 to your computer and use it in GitHub Desktop.
Save CanOfBees/5f63a13071c72004f0dbcdb287aef049 to your computer and use it in GitHub Desktop.
Create ODS Spreadsheet with XQuery and BaseX
<tables>
<table name="Sheet 1">
<thead>
<tr>
<td>A</td>
<td>B</td>
<td>C</td>
</tr>
</thead>
<tbody>
<tr>
<td>10</td>
<td>20</td>
<td>30</td>
</tr>
</tbody>
</table>
</tables>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0"
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="template" as="xs:string"/>
<xsl:variable name="content" select="doc($template)" as="document-node()"/>
<xsl:template match="/">
<xsl:apply-templates select="$content/office:document-content">
<xsl:with-param name="tables" select="tables/table" tunnel="yes"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="office:spreadsheet">
<xsl:param name="tables" tunnel="yes" as="element(table)*"/>
<xsl:variable name="template" select="table:table[1]"/>
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:for-each select="$tables">
<xsl:apply-templates select="$template">
<xsl:with-param name="table" select="." tunnel="yes"/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="table:table">
<xsl:param name="table" tunnel="yes" as="element(table)"/>
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:attribute name="table:name" select="$table/@name"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="table:table-row"/>
<xsl:template match="table:table-row[2]">
<xsl:param name="table" tunnel="yes"/>
<xsl:call-template name="create-data-rows">
<xsl:with-param name="table" select="$table"/>
<xsl:with-param name="model" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template match="table:table-row[1]">
<xsl:param name="table" tunnel="yes"/>
<xsl:if test="exists($table/thead/tr)">
<xsl:call-template name="create-header-rows">
<xsl:with-param name="table" select="$table"/>
<xsl:with-param name="model" select="."/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="create-header-rows">
<xsl:param name="table" as="element(table)"/>
<xsl:param name="model" as="element(table:table-row)"/>
<xsl:variable name="template-row" select="$model"/>
<xsl:variable name="template-cell" select="$model/table:table-cell[1]"/>
<xsl:for-each select="$table/thead/tr">
<xsl:variable name="row" select="."/>
<xsl:for-each select="$template-row">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:for-each select="$row/td">
<xsl:apply-templates select="$template-cell">
<xsl:with-param name="cell" select="."/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:copy>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
<xsl:template name="create-data-rows">
<xsl:param name="table" as="element(table)"/>
<xsl:param name="model" as="element(table:table-row)"/>
<xsl:variable name="template-row" select="$model"/>
<xsl:variable name="template-cell" select="$model/table:table-cell[1]"/>
<xsl:for-each select="$table/tbody/tr">
<xsl:variable name="row" select="."/>
<xsl:for-each select="$template-row">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:for-each select="$row/td">
<xsl:apply-templates select="$template-cell">
<xsl:with-param name="cell" select="."/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:copy>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
<xsl:template match="table:table-cell">
<xsl:param name="cell"/>
<xsl:copy>
<!-- TODO: may need to 'cook up' a set of default attributes -->
<!-- such as: office:value-type="string" calcext:value-type="string" -->
<xsl:apply-templates select="@* except (@table:number-columns-repeated, @table:number-columns-spanned)"/>
<text:p><xsl:value-of select="$cell"/></text:p>
</xsl:copy>
</xsl:template>
<xsl:template match="@office:value-type">
<xsl:attribute name="office:value-type" select="'string'"/>
</xsl:template>
<xsl:template match="@calcext:value-type">
<xsl:attribute name="calcext:value-type" select="'string'"/>
</xsl:template>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
declare function ods:write(
$tables as element(tables),
$path as xs:string,
$options as map(*))
as empty-sequence()
{
let $template := file:path-to-uri(file:resolve-path(concat(file:base-dir(), 'templates/default.ods')))
let $xsl := file:resolve-path(concat(file:base-dir(), 'ods-tables.xsl'))
let $template-dir := file:create-temp-dir('ods-', '.ods', file:temp-dir())
let $extract archive:extract-to($template-dir, file:read-binary($template))
for $entry in file:list($template-dir, true())
where ends-with(file:path-to-uri($entry),'/content.xml')
let $uri := file:path-to-uri(file:resolve-path(concat($template-dir, '/', $entry)))
let $result := file:write($uri, xslt:transform($tables, $xsl, map { 'template': $uri }))
return
file:write-binary($path, archive:create-from($template-dir))
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment