summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2012-08-13 22:42:32 +0000
committerKarl Berry <karl@freefriends.org>2012-08-13 22:42:32 +0000
commit06c486e19ae6f98bf02be91b7d502e940c596d20 (patch)
treecfe172c44e8b94c2c4511228c0eeef9c4ecde63c
parent891d790aa1dafff5340c181e3dce3332526e4834 (diff)
new (semi-old) luatex package odsfile (13aug12)
git-svn-id: svn://tug.org/texlive/trunk@27395 c570f23f-e606-0410-a88d-b1316a301751
-rw-r--r--Master/texmf-dist/doc/lualatex/odsfile/README53
-rw-r--r--Master/texmf-dist/doc/lualatex/odsfile/odsfile.pdfbin0 -> 185396 bytes
-rw-r--r--Master/texmf-dist/doc/lualatex/odsfile/odsfile.tex342
-rw-r--r--Master/texmf-dist/doc/lualatex/odsfile/pokus.odsbin0 -> 7533 bytes
-rw-r--r--Master/texmf-dist/tex/lualatex/odsfile/odsfile.lua178
-rw-r--r--Master/texmf-dist/tex/lualatex/odsfile/odsfile.sty180
-rwxr-xr-xMaster/tlpkg/bin/tlpkg-ctan-check2
-rw-r--r--Master/tlpkg/tlpsrc/collection-luatex.tlpsrc1
-rw-r--r--Master/tlpkg/tlpsrc/odsfile.tlpsrc0
9 files changed, 755 insertions, 1 deletions
diff --git a/Master/texmf-dist/doc/lualatex/odsfile/README b/Master/texmf-dist/doc/lualatex/odsfile/README
new file mode 100644
index 00000000000..50ba5f3333d
--- /dev/null
+++ b/Master/texmf-dist/doc/lualatex/odsfile/README
@@ -0,0 +1,53 @@
+Introduction
+------------
+
+The odsfile is LuaLaTeX package and lua library for working with
+opendocument spreadsheet (ods) documents from Open/Libre Office Calc.
+It is possible to select cells from sheets and typeset them as LaTeX tables,
+it is also possible to pass these data to LaTeX macros and make some plots,
+for example.
+
+There is also simple interface to add data to existing spreadsheets. This
+functionality requires external zip utility.
+
+This package uses LuaTeX's zip library and scripting to read these files.
+It also requires LuaXML library, which is available to download from
+CTAN site.
+
+Because of lua scripting, it is not possible to use this package with pdfLaTeX
+or XeLaTeX. On the other side, odsfile.lua library can be used from PlainTeX,
+ConTeXt or pure lua scripts.
+
+It may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this license or
+(at your option) any later version. The latest version of this license is in
+
+ http://www.latex-project.org/lppl.txt
+
+and version 1.3 or later is part of all distributions of LaTeX version
+2005/12/01 or later.
+
+Basic usage
+-----------
+
+\usepackage{odsfile,booktabs}
+
+...
+
+\includespread[\begin{tabular}{l l}
+\includespread[file=pokus.ods]
+\end{tabular}
+
+...
+
+You can also use templates to specify tabular properties automaticaly
+\includespread[columns=top,template=booktabs,range=a3:c5]
+
+Author
+------
+Michal Hoftich
+Email: michal.h21@gmail.com
+
+If you are interested in the process of development you may observe
+
+ https://github.com/michal-h21/odsfile
diff --git a/Master/texmf-dist/doc/lualatex/odsfile/odsfile.pdf b/Master/texmf-dist/doc/lualatex/odsfile/odsfile.pdf
new file mode 100644
index 00000000000..c5df9e1ba0f
--- /dev/null
+++ b/Master/texmf-dist/doc/lualatex/odsfile/odsfile.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/lualatex/odsfile/odsfile.tex b/Master/texmf-dist/doc/lualatex/odsfile/odsfile.tex
new file mode 100644
index 00000000000..e1fb2b46118
--- /dev/null
+++ b/Master/texmf-dist/doc/lualatex/odsfile/odsfile.tex
@@ -0,0 +1,342 @@
+\documentclass{ltxdoc}
+\usepackage{odsfile,tgschola,metalogo,hyperref,xspace,microtype,showexpl,booktabs,url}
+\author{Michal Hoftich (\url{michal.h21@gmail.com})}
+\title{The \textsf{odsfile} package:\\
+accessing of the \textsf{opendocument spreadsheet} from \LaTeX{} documents\thanks{Version 0.2, last revisited 2012-08-12.}
+}
+\usepackage[english]{babel}
+\lstloadlanguages{[LaTeX]Tex}
+\lstset{%
+ basicstyle=\ttfamily,
+ commentstyle=\itshape\ttfamily,
+ showspaces=false,
+ showstringspaces=false,
+ breaklines=true,
+ breakautoindent=true,
+ captionpos=t
+}
+
+\newcommand\ods{\textsf{ods}\xspace}
+
+\begin{document}
+\maketitle
+
+\tableofcontents
+
+\section{Introduction}
+
+This is \LuaLaTeX{} package and \textsf{lua} library for working with
+\textsf{opendocument spreadsheet} (\ods) documents from Open/Libre Office Calc.
+Their contents can be read as \LaTeX{} tables, can be pass to macros,
+ you can also add new data to existing spreadsheets.
+
+\ods format consist of number of |xml| files packed in the |zip| file.
+This package uses \LuaTeX's zip library, LuaXML library\footnote{Pure |lua| library
+for working with |xml| files, it is available form CTAN or \url{https://github.com/michal-h21/LuaXML}} and lua scripting to read |xml| content from this archive,
+which means that it is not possible to use this package with pdf\LaTeX{} or \XeLaTeX.
+On the other side, |odsfile.lua| library can be used from Plain\TeX,
+Con\TeX{}t or pure |lua| scripts.
+
+Creation of this package was motivated by question\footnote{\url{http://tex.stackexchange.com/questions/60378/insert-libreoffice-table-as-input}} on site \url{http://tex.stackexchange.com/}. Development version of the package can be found at \url{https://github.com/michal-h21/odsfile}, all contributions and comments are welcome.
+\section{Usage}
+
+You can load |odsfile| classically with
+\begin{verbatim}
+\usepackage{odsfile}
+\end{verbatim}
+There are macros:
+\begin{itemize}
+\item \cmd{\includespread}
+\item \cmd{\tabletemplate}
+\item \cmd{\loadodsfile}
+\item \cmd{\savespreadsheet}
+\item |AddRow| environment
+\end{itemize}
+
+\noindent Main command is\marginpar{\cmd{\includespread}} \cmd{\includespread}. It's syntax is:\\
+\cmd{\includespread}\oarg{key-value list}
+
+Options are:
+
+\begin{description}
+\item[file] Filename of file to be loaded. You should specify this only on first use of \cmd{\includespread}.
+\item[sheet] Name of sheet to be loaded. If it's not specified on first use of \cmd{\includespread}, then first sheet from the file is loaded. The sheet remains selected until another use of |sheet|.
+\begin{LTXexample}
+\begin{tabular}{l l}
+\includespread[file=pokus.ods,sheet=List2]
+\end{tabular}
+\end{LTXexample}
+\item[range] Selects range from table to be inserted. Range is specified in format similar to spreadsheet processors, like |a2:c4|, selecting cells starting at first column, second row and ending and third column, fourth row.
+\begin{LTXexample}
+\begin{tabular}{lll}
+\includespread[sheet=List1,range=a2:c4]
+\end{tabular}
+\end{LTXexample}
+You can omit some or both of the numbers:
+\begin{LTXexample}
+\begin{tabular}{lll}
+\includespread[range=a:c4]
+\end{tabular}
+\end{LTXexample}
+
+\begin{LTXexample}
+\begin{tabular}{ll}
+\includespread[range=a:b]
+\end{tabular}
+\end{LTXexample}
+
+\begin{LTXexample}
+\begin{tabular}{ll}
+\includespread[range=b2:c]
+\end{tabular}
+\end{LTXexample}
+\item[columns] Column heading specification. It can be either |head|, |top|, or comma separated list of values.
+\begin{description}
+\item[top] Use as headers first line from the table.
+\begin{LTXexample}
+\begin{tabular}{ll}
+\includespread[range=b3:c5,columns=top]
+\end{tabular}
+\end{LTXexample}
+Note that if you include whole table, first line is included twice:
+\begin{LTXexample}
+\begin{tabular}{lll}
+\includespread[columns=top]
+\end{tabular}
+\end{LTXexample}
+in this case you can use
+\item[head] use first row from selection as headings.
+\begin{LTXexample}
+\begin{tabular}{lll}
+\includespread[columns=head,range=a:c3]
+\end{tabular}
+\end{LTXexample}
+\item[manually specified list] If column headings are not specified in the file, you can set them manually.
+\begin{LTXexample}
+\begin{tabular}{ll}
+\includespread[columns=head,columns={title,amount},sheet=List2]
+\end{tabular}
+\end{LTXexample}
+\end{description}
+
+\item[rowseparator] Rows are normally separated with newlines, if you really want, you can separate them with hlines.
+
+Possible values:
+\begin{description}
+\item[tableline (default)] Inserts |\\| character
+\item[hline] Inserts |\\ \\hline|
+\item[newline] Inserts blank line
+\item[user specified separator] useful in conjunction with \ref{it:rowtemplate}
+(p. \pageref{it:rowtemplate}), for example if you want to include sheets
+as plaintext or input for plotting functions.
+\end{description}
+\begin{LTXexample}
+\begin{tabular}{lll}
+\includespread[columns=top,sheet=List1,rowseparator=hline,range=a2:b5]
+\end{tabular}
+\end{LTXexample}
+
+\item[template] Templates are simple mechanism to insert whole tabular environment with column specification. All columns are aligned to the left, if you want to do more advanced stuff with column specifications, you must enter them manually as in all previous examples.
+\begin{LTXexample}
+\includespread[columns=top,template=booktabs,range=a3]
+\end{LTXexample}
+For more info about templates, see next section \ref{sec:tpl}
+
+\item[rowtemplate]
+\makeatletter
+\edef\@currentlabel{rowtemplate}
+\label{it:rowtemplate} Enables to convert tabular data to something different than \LaTeX\ tables.
+Syntax for rowtemplates is similar to the table templates, variables are inserted with |-{number}|, where
+number is the position of the cell from beginning of the selection.
+\makeatother
+\begin{LTXexample}
+\includespread[range=a2:b,rowseparator={,\ },rowtemplate={\textit{-{1}}:-{2}}]
+\end{LTXexample}
+\end{description}
+
+\section{Templates}\label{sec:tpl}
+
+If you don't want to specify tabular environment by hand, you can use simple templating mechanism to insert tabular environment by hand.
+
+Templates are defined with macro\marginpar{\cmd{\tabletemplate}}\\
+\cmd{\tabletemplate}\marg{template name}\marg{template code}\\
+there is default template:
+\begin{verbatim}
+\tabletemplate{default}{-{colheading}-{rowsep}-{content}}
+\end{verbatim}
+
+Code |-{variable name}| inserts one of the following variables:
+
+\begin{description}
+\item[coltypes] This is code to be inserted in |\begin{tabular}{coltypes}|. In current version, it inserts |l| for left alignment column, for all columns of inserted table.
+It should be possible to use more intelligent method based on types of column content, or \ods styles, maybe in future versions some of them will be used. If you want other alignment of columns now, you have to specify |\begin{tabular}{column types}| manually.
+\item[colheading] Column headings.
+\item[rowsep] It inserts row separator defined with |rowsepartor| key of |\includespread|. It is used in default style to delimit column headings and table contents.
+\item[content] Tabular data.
+\end{description}
+
+\paragraph{More powerful template for the \textsc{booktabs} package}
+
+\begin{verbatim}
+\tabletemplate{booktabs}{%
+\begin{tabular}{-{coltypes}}
+\toprule
+-{colheading}
+\midrule
+-{content}
+\\ \bottomrule
+\end{tabular}
+}
+\end{verbatim}
+
+\section{Adding data}
+
+There is simple interface for adding new rows to the spreadsheet.
+
+|AddRow|\oarg{row number}\marginpar{|AddRow|} environment for adding new row to the current sheet. Optional argument \oarg{row number} specifies where it should be inserted, if blank, it will be inserted at end.
+
+Inside |AddRow|, you can use
+\begin{itemize}
+ \item \cmd{\AddString}\marg{text}\marg{position}
+ \item \cmd{\AddNumber}\marg{number}\marg{position}
+\end{itemize}
+Position specifies cell, where data should be added, if you leave it blank, it will be laced next to the previous one.
+
+\begin{LTXexample}
+\includespread[columns=head,template=booktabs]
+\begin{AddRow}
+ \AddString{last row}{}
+\end{AddRow}
+\begin{AddRow}[3]
+ \AddString{third row}{}
+ \AddNumber{22}{3}
+\end{AddRow}
+\includespread[columns=head,template=booktabs]
+\end{LTXexample}
+
+\section{Loading and saving of the \ods file}
+
+You can explicitly load \ods file with \marginpar{\cmd{\loadodsfile}}\cmd{\loadodsfile}\oarg{key val list}\marg{filename}. This can be useful, if you only want to write some data to the file, otherwise it is better to use \cmd{\includespread}.
+
+For saving spreadsheets modified with |AddRow|, you can use \cmd{\savespreadsheet}\marginpar{\cmd{\savespreadsheet}}. This command uses call to external |zip| utility, so you should have installed it and you have to call lua\LaTeX with |lualatex --shell-escape filename|. Lua\LaTeX also must have write permissions for accessing the \ods file. This command creates file |content.xml| in the current directory, so if externall call fails, you can run
+\begin{verbatim}
+zip -r filename.ods content.xml
+\end{verbatim}
+by hand.
+\section{Lua library}
+
+
+The |lua| library uses |luazip| library integrated to \LuaTeX{} and |LuaXML|\footnote{\url{https://github.com/michal-h21/LuaXML}}, pure |lua| library for working with |XML| files.
+
+To use library in your code, you can use:
+
+\begin{verbatim}
+require("odsfile")
+\end{verbatim}
+
+Function |odsfile.load(filename)| returns |odsfile| object, with |loadContent()| method, which returns |lua| table representing |content.xml| file. We can select sheet from the spreadsheet with |odsfile.getTable(xmlobject,sheet_name)|. If we omit |sheet_name|, first sheet from spreadsheet is selected.
+
+Data from sheet can be read with |odsfile.tableValues(sheet, x1, y1, x2, y2)|. |x1 - y2| are range to be selected, they can be |nil|, in which case whole rows and cells are selected. For converting of standard range expressions of form |a1:b2| to this representation, function |odsfile.getRange(range)| can be used.
+
+\paragraph{Example usage -- file \textsf{odsexample.lua}}
+
+\begin{verbatim}
+require "odsfile"
+
+-- Helper function to print structure of the table
+function printable(tb, level)
+ level = level or 1
+ local spaces = string.rep(' ', level*2)
+ for k,v in pairs(tb) do
+ if type(v) ~= "table" then
+ print(spaces .. k..'='..v)
+ else
+ print(spaces .. k)
+ level = level + 1
+ printable(v, level)
+ end
+ end
+end
+
+local ods = odsfile.load("filename.ods")
+local f, e = ods:loadContent()
+
+-- Get First sheet from the table
+body= odsfile.getTable(f)
+-- Print structure of the range a4:b5
+printable(odsfile.tableValues(body,odsfile.getRange("a4:b5")))
+\end{verbatim}
+
+Run the example with |texlua odsexample.lua| from the command line, you should get following result:
+
+\begin{verbatim}
+ 1
+ 1
+ value=AA
+ attr
+ office:value-type=string
+ 2
+ value=3
+ attr
+ office:value-type=float
+ office:value=3
+ 2
+ 1
+ value=BB
+ attr
+ office:value-type=string
+ 2
+ value=4
+ attr
+ office:value-type=float
+ office:value=4
+\end{verbatim}
+
+To convert this structure to \LaTeX{} tabular code, you can use following function:
+
+\begin{verbatim}
+function tableToTabular(values)
+ local rowValues = function(row)
+ local t={}
+ for _,column in pairs(row) do table.insert(t,column.value) end
+ return t
+ end
+ content = {}
+ for i,row in pairs(values) do
+ table.insert(content,table.concat(rowValues(row)," & "))
+ end
+ return table.concat(content,"\\\\\n")
+end
+-- Now use it with objects from previous example
+print(tableToTabular(odsfile.tableValues(body)))
+\end{verbatim}
+
+This example yields
+
+\begin{verbatim}
+Label & Position & Count\\
+Hello & 1 & 3\\
+World & 2 & 4\\
+AA & 3 & 5\\
+BB & 4 & 6\\
+CC & 5 & 7
+\end{verbatim}
+
+
+
+\section{Changes}
+
+\begin{description}
+\item[v0.2]
+\begin{itemize}
+ \item LuaXML is now distributed as separate library, so other projects can use it.
+ \item New |AddRow| environment for adding data to the \ods file
+ \item New command |\savespreadsheet| for saving \ods file
+ \item Bug fixes: corrected loading of the sheets, corrected behaviour of blank cell
+\end{itemize}
+\item[v0.1] First version
+\end{description}
+
+
+
+\end{document}
diff --git a/Master/texmf-dist/doc/lualatex/odsfile/pokus.ods b/Master/texmf-dist/doc/lualatex/odsfile/pokus.ods
new file mode 100644
index 00000000000..d2c6762ffba
--- /dev/null
+++ b/Master/texmf-dist/doc/lualatex/odsfile/pokus.ods
Binary files differ
diff --git a/Master/texmf-dist/tex/lualatex/odsfile/odsfile.lua b/Master/texmf-dist/tex/lualatex/odsfile/odsfile.lua
new file mode 100644
index 00000000000..d305fb48a5f
--- /dev/null
+++ b/Master/texmf-dist/tex/lualatex/odsfile/odsfile.lua
@@ -0,0 +1,178 @@
+module(...,package.seeall)
+require "zip"
+xmlparser = require ("luaxml-mod-xml")
+handler = require("luaxml-mod-handler")
+
+
+function load(filename)
+ local p = {
+ file = zip.open(filename),
+ content_file_name = "content.xml",
+ loadContent = function(self,filename)
+ local treehandler = handler.simpleTreeHandler()
+ local filename = filename or self.content_file_name
+ local xmlfile = self.file:open(filename)
+ local text = xmlfile:read("*a")
+ local xml = xmlparser.xmlParser(treehandler)
+ xml:parse(text)
+ return treehandler
+ end
+ }
+ return p
+end
+
+function getTable(x,table_name)
+ local tables = x.root["office:document-content"]["office:body"]["office:spreadsheet"]["table:table"]
+ if #tables > 1 then
+ if type(tables) == "table" and table_name ~= nil then
+ for k,v in pairs(tables) do
+ if(v["_attr"]["table:name"]==table_name) then
+ return v, k
+ end
+ end
+ elseif type(tables) == "table" and table_name == nil then
+ return tables[1], 1
+ else
+ return tables
+ end
+ else
+ return tables
+ end
+end
+
+function tableValues(tbl,x1,y1,x2,y2)
+ local t= {}
+ if type(tbl["table:table-row"])=="table" then
+ local rows = table_slice(tbl["table:table-row"],y1,y2)
+ for k,v in pairs(rows) do
+ -- In every sheet, there are two rows with no data at the bottom, we need to strip them
+ if(v["_attr"] and v["_attr"]["table:number-rows-repeated"] and tonumber(v["_attr"]["table:number-rows-repeated"])>10000) then break end
+ local j = {}
+ if #v["table:table-cell"] > 1 then
+ local r = table_slice(v["table:table-cell"],x1,x2)
+ for p,n in pairs(r) do
+ table.insert(j,{value=n["text:p"] or "",attr=n["_attr"]})
+ end
+ else
+ local p = {value=v["table:table-cell"]["text:p"],attr=v["table:table-cell"]["_attr"]}
+ table.insert(j,p)
+ end
+ table.insert(t,j)
+ end
+ end
+ return t
+end
+
+function getRange(range)
+ local r = range:lower()
+ local function getNumber(s)
+ if s == "" or s == nil then return nil end
+ local f,ex = 0,0
+ for i in string.gmatch(s:reverse(),"(.)") do
+ f = f + (i:byte()-96) * 26 ^ ex
+ ex = ex + 1
+ end
+ return f
+ end
+ for x1,y1,x2,y2 in r:gmatch("(%a*)(%d*):*(%a*)(%d*)") do
+ return getNumber(x1),tonumber(y1),getNumber(x2),tonumber(y2)
+ --print(string.format("%s, %s, %s, %s",getNumber(x1),y1,getNumber(x2),y2))
+ end
+end
+
+function table_slice (values,i1,i2)
+ -- Function from http://snippets.luacode.org/snippets/Table_Slice_116
+ local res = {}
+ local n = #values
+ -- default values for range
+ i1 = i1 or 1
+ i2 = i2 or n
+ if i2 < 0 then
+ i2 = n + i2 + 1
+ elseif i2 > n then
+ i2 = n
+ end
+ if i1 < 1 or i1 > n then
+ return {}
+ end
+ local k = 1
+ for i = i1,i2 do
+ res[k] = values[i]
+ k = k + 1
+ end
+ return res
+end
+
+function interp(s, tab)
+ return (s:gsub('(-%b{})',
+ function(w)
+ s = w:sub(3, -2)
+ s = tonumber(s) or s
+ return tab[s] or w
+ end)
+ )
+end
+
+
+-- Interface for adding new rows to the spreadsheet
+
+function newRow()
+ local p = {
+ pos = 0,
+ cells = {},
+ -- Generic function for inserting cell
+ addCell = function(self,val, attr,pos)
+ if pos then
+ table.insert(self.cells,pos,{["text:p"] = val, ["_attr"] = attr})
+ self.pos = pos
+ else
+ self.pos = self.pos + 1
+ table.insert(self.cells,self.pos,{["text:p"] = val, ["_attr"] = attr})
+ end
+ end,
+ addString = function(self,s,attr,pos)
+ local attr = attr or {}
+ attr["office:value-type"] = "string"
+ self:addCell(s,attr,pos)
+ end,
+ addFloat = function(self,i,attr,pos)
+ local attr = attr or {}
+ local s = tonumber(i) or 0
+ s = tostring(s)
+ attr["office:value-type"] = "float"
+ attr["office:value"] = s
+ self:addCell(s,attr,pos)
+ end,
+ findLastRow = function(self,sheet)
+ for i= #sheet["table:table-row"],1,-1 do
+ if sheet["table:table-row"][i]["_attr"]["table:number-rows-repeated"] then
+ return i
+ end
+ end
+ return #sheet["table:table-row"]+1
+ end,
+ insert = function(self, sheet, pos)
+ local t = {}
+ local pos = pos or self:findLastRow(sheet)
+ print("pos je: ",pos)
+ if sheet["table:table-column"]["_attr"] and sheet["table:table-column"]["_attr"]["table:number-columns-repeated"] then
+ table_columns = sheet["table:table-column"]["_attr"]["table:number-columns-repeated"]
+ else
+ table_columns = #sheet["table:table-column"]
+ end
+ for i=1, table_columns do
+ table.insert(t,self.cells[i] or {})
+ end
+ t = {["table:table-cell"]=t}
+ table.insert(sheet["table:table-row"],pos,t)
+ end
+ }
+ return p
+end
+
+
+-- function for updateing the archive. Depends on external zip utility
+function updateZip(zipfile, updatefile)
+ local command = string.format("zip %s %s",zipfile, updatefile)
+ print ("Updating an ods file.\n" ..command .."\n Return code: ", os.execute(command))
+end
diff --git a/Master/texmf-dist/tex/lualatex/odsfile/odsfile.sty b/Master/texmf-dist/tex/lualatex/odsfile/odsfile.sty
new file mode 100644
index 00000000000..b4c7f617e18
--- /dev/null
+++ b/Master/texmf-dist/tex/lualatex/odsfile/odsfile.sty
@@ -0,0 +1,180 @@
+\ProvidesPackage{odsfile.sty}
+\RequirePackage{luacode,xkeyval}
+
+%keyval keys
+
+\define@key{includespread}{file}{\loadodsfile{#1}}
+\define@key{includespread}{sheet}{\luaexec{sheetname = "\luatexluaescapestring{#1}"}}
+\define@key{includespread}{range}{\luaexec{%
+local x1,y1,x2,y2 = odsreader.getRange("\luatexluaescapestring{#1}")%
+range = {x1,y1,x2,y2}%
+}}
+\define@key{includespread}{template}{\luaexec{currenttemplate="\luatexluaescapestring{#1}"}}
+\define@key{includespread}{rowtemplate}{\luaexec{rowtemplate="\luatexluaescapestring{\unexpanded{#1}}"}}
+
+\define@choicekey*+{includespread}{rowseparator}[\val\nr]{tableline,hline,newline}[tableline]%
+{%
+\ifcase\nr\relax%
+ \luaexec{rowseparator=""}%
+\or%
+ \luaexec{rowseparator="\\hline "}%
+\or
+\luaexec{%
+rowseparator=[[\\n]]
+}
+\fi%
+}{%
+ \luaexec{rowseparator="\luatexluaescapestring{#1}"}
+}
+
+\define@choicekey*+{includespread}{columns}[\val\nr]{head,top,none}{%
+\ifcase\nr\relax%
+ \luaexec{columns=1}%
+\or%
+ \luaexec{columns=2}%
+\fi%
+}{%
+\luaexec{%
+local function split(s,sep)
+ local sep, fields = sep or ":", {}
+ local pattern = string.format("([^\%s]+)", sep)
+ s:gsub(pattern, function(c) fields[\#fields+1] = c end)
+ return fields
+end
+local s="\luatexluaescapestring{#1}"
+columns = split(s,",")
+}%
+}{}%
+
+
+% Variable initialization and helper functions
+\begin{luacode*}
+odsreader = require("odsfile")
+odsfile = nil
+sheetname = nil
+range = {nil,nil,nil,nil}
+columns = nil
+templates = {}
+row = {}
+body = nil
+odsfilename = ""
+currenttemplate = nil
+rowtemplate=nil
+\end{luacode*}
+
+\newcommand\loadodsfile[2][]{%
+ \setkeys{includespread}{#1}%
+ \luaexec{%
+ odsfilename = "\luatexluaescapestring{#2}"%
+ local ods = odsreader.load(odsfilename)%
+ odsfile, e = ods:loadContent()%
+ }%
+}
+
+\newcommand\tabletemplate[2]{%
+ \luaexec{%
+ templates["#1"]="\luatexluaescapestring{\unexpanded{#2}}"
+ }
+}
+
+\newcommand\includespread[1][]{%
+ \luaexec{%
+ range = {nil,nil,nil,nil}
+ rowseparator = ""
+ columns=nil
+ currenttemplate = nil
+ rowtemplate = nil
+ }%
+ \setkeys{includespread}{#1}%
+ \luaexec{%
+ body = odsreader.getTable(odsfile,sheetname)
+ local values = odsreader.tableValues(body,range[1],range[2],range[3],range[4])
+ %-- Conversion of odsfile table values to LaTeX tabular
+ local rowValues = function(row)
+ local t={}
+ for _,column in pairs(row) do table.insert(t,column.value) end
+ return t
+ end
+ if rowtemplate == nil then
+ currenttemplate = currenttemplate or "default"
+ content = {}
+ for i,row in pairs(values) do
+ table.insert(content,table.concat(rowValues(row)," & "))
+ end
+ %-- Column headings handling
+ local colheading=""
+ if type(columns) == "number" and columns == 1 then
+ columns = rowValues(values[1])
+ content = odsreader.table_slice(content,2,nil)
+ elseif type(columns) == "number" and columns == 2 then
+ local t = odsreader.tableValues(body,range[1],1,range[3],2)
+ columns = rowValues(t[1])
+ end
+ if type(columns) == "table" then colheading = table.concat(columns," & ") .. "\\\\" end
+ coltypes = ""
+ if type(content)== "table" then coltypes= string.rep("l",\#content[1]) end
+ tex.print(odsreader.interp(templates[currenttemplate],{content=table.concat(content, "\\\\ "..rowseparator),coltypes=coltypes,colheading=colheading,rowsep=rowseparator}))
+ else
+ local content = {}
+ currenttemplate = currenttemplate or "empty"
+ for _,row in pairs(values) do
+ table.insert(content,odsreader.interp(rowtemplate,rowValues(row)))
+ end
+ content = table.concat(content,rowseparator)
+ tex.sprint(odsreader.interp(templates[currenttemplate],{content=content}))
+ end
+ }
+}
+
+\tabletemplate{empty}{-{content}}
+
+\tabletemplate{default}{-{colheading}-{rowsep}-{content}}
+
+\tabletemplate{booktabs}{%
+\begin{tabular}{-{coltypes}}
+\toprule
+-{colheading}
+\midrule
+-{content}
+\\ \bottomrule
+\end{tabular}
+}
+
+% Interface for adding of new rows
+
+\newenvironment{AddRow}[1][]{%
+\def\AddString##1##2{%
+\luaexec{%
+local pos = "\luatexluaescapestring{##2}"%
+if pos == "" then pos = nil end; row:addString("\luatexluaescapestring{\unexpanded{##1}}",nil,pos)%
+}%
+}%
+\def\AddNumber##1##2{%
+\luaexec{%
+local pos = "\luatexluaescapestring{##2}"%
+if pos == "" then pos = nil end; row:addFloat("\luatexluaescapestring{##1}",nil,pos)%
+}%
+}%
+\luaexec{%
+pos = "\luatexluaescapestring{#1}"%
+if pos == "" then pos = nil end; row = odsreader.newRow()%
+}%
+}{%
+\luaexec{%
+body = body or odsreader.getTable(odsfile)
+row:insert(body,pos)%
+}%
+}
+
+% Interface for saving the spreadsheet
+
+\newcommand\savespreadsheet{%
+\luaexec{%
+ local xml = require("luaxml-mod-xml")
+ f = io.open("content.xml","w")%
+ f:write(xml.serialize(odsfile.root))%
+ f:close()%
+ odsreader.updateZip(odsfilename,"content.xml")%
+}%
+}
+\endinput
diff --git a/Master/tlpkg/bin/tlpkg-ctan-check b/Master/tlpkg/bin/tlpkg-ctan-check
index 0d5de27a9d0..c9ad51dc064 100755
--- a/Master/tlpkg/bin/tlpkg-ctan-check
+++ b/Master/tlpkg/bin/tlpkg-ctan-check
@@ -293,7 +293,7 @@ my @TLP_working = qw(
nomencl nomentbl nonfloat nonumonpart nopageno nostarch notes
notes2bib notoccite nowidow
nrc ntgclass ntheorem ntheorem-vn nuc numericplots numname numprint
- oberdiek objectz ocherokee ocr-b ocr-b-outline ocr-latex octavo ofs
+ oberdiek objectz ocherokee ocr-b ocr-b-outline ocr-latex octavo odsfile ofs
ogham oldlatin oldstandard oldstyle
onlyamsmath onrannual opcit opensans opteng optional
ordinalpt orkhun ot-tableau othello othelloboard
diff --git a/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc b/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
index 63412456e54..51972b69ac7 100644
--- a/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
+++ b/Master/tlpkg/tlpsrc/collection-luatex.tlpsrc
@@ -25,4 +25,5 @@ depend luasseq
depend luatexbase
depend luatextra
depend luaxml
+depend odsfile
depend showhyphens
diff --git a/Master/tlpkg/tlpsrc/odsfile.tlpsrc b/Master/tlpkg/tlpsrc/odsfile.tlpsrc
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/Master/tlpkg/tlpsrc/odsfile.tlpsrc