这个问题导致了一个新的方案的出现:
odsfile
假设您有一个localc
电子表格myspread.ods
,并且想要将范围的内容H4:I40
作为表格插入到您的 latex 文档中。我知道我可以导出表格或只是进行一些复制和粘贴。但是,是否可以编写如下代码:
\includespread{file=myspread.ods,range=H4:I40}
然后,当相应的电子表格发生变化时,我的文档中的表格LaTeX
也会发生变化。
如果能够将表的全局外观属性作为可选参数进行修改就好了\includespread
。
答案1
编辑:现在有 LaTeX 包可供测试https://github.com/michal-h21/odsfile
在我将其发布到 CTAN 之前,非常欢迎对文档文件中的样式/语法/拼写的任何评论,以及对源代码的评论。
有使用luatex的zip
库和纯lua xml处理库的解决方案LuaXML,您应该将其安装到与 相同的目录中odsfile.lua
。
ods
xml
格式由文件中打包的文件数组成zip
。数据包含在 content.xml
文件中。其结构如下:
<?xml version="1.0" encoding="UTF-8"?>
<office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" ... >
<office:scripts/>
<office:font-face-decls>
...
</office:font-face-decls>
<office:automatic-styles>
...
</office:automatic-styles>
<office:body>
<office:spreadsheet>
<table:table table:name="List1" table:style-name="ta1" table:print="false">
<table:table-column table:style-name="co1" table:number-columns-repeated="3" table:default-cell-style-name="Default"/>
<table:table-row table:style-name="ro1">
<table:table-cell office:value-type="string">
<text:p>Hello</text:p>
</table:table-cell>
<table:table-cell office:value-type="float" office:value="1">
<text:p>1</text:p>
</table:table-cell>
<table:table-cell table:formula="of:=[.B1]+2" office:value-type="float" office:value="3">
<text:p>3</text:p>
</table:table-cell>
</table:table-row>
...
</table:table>
<table:table table:name="List2" table:style-name="ta1" table:print="false">
...
</table:table>
</office:spreadsheet>
</office:body>
</office:document-content>
从我创建的示例文件来看,工作表数据似乎保存在office:document-content / office:body / office:spreadsheet / table:table
路径中。表格行保存为,table:table-row
单元格保存为table:table-cell
。每个单元格都设置了其内容的数据类型,如果单元格包含公式,则保存在table:formula
属性中。在每种情况下,准备打印的计算值都包含在 的子元素中table-cell
。text:p
我希望这个结构也能用与我的不同的版本创建Open/Libre Office Calc
,否则代码将无法正常工作 :(
有了LuaXML
,我们可以转换xml
成lua
表格,然后可以使用标准lua
技术轻松处理。
图书馆odsfile.lua
module(...,package.seeall)
require "zip"
dofile("xml.lua")
dofile("handler.lua")
function load(filename)
local p = {
file = zip.open(filename),
content_file_name = "content.xml",
loadContent = function(self,filename)
local treehandler = simpleTreeHandler()
local filename = filename or self.content_file_name
local xmlfile = self.file:open(filename)
local text = xmlfile:read("*a")
local xml = 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 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
end
end
elseif type(tables) == "table" and table_name == nil then
return tables[1]
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
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"],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
函数odsfile.load(filename)
返回odsfile
对象,loadContent()
方法返回lua
表示content.xml
文件的表。我们可以使用 从电子表格中选择工作表odsfile.getTable(xmlobject,sheet_name)
。如果我们省略sheet_name
,则选择电子表格中的第一个工作表。
可以使用 读取工作表中的数据odsfile.tableValues(sheet, x1, y1, x2, y2)
。x1 - y2
是要选择的范围,它们可以为空,在这种情况下会选择整个行和单元格。 要将标准的表格范围表达式转换a1:b2
为此表示形式,odsfile.getRange(range)
可以使用函数。
现在有一些LaTeX
界面。odsfile.sty
\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}%
}}
\begin{luacode*}
odsreader = require("odsfile")
odsfile = nil
sheetname = nil
range = {nil,nil,nil,nil}
\end{luacode*}
\newcommand\loadodsfile[2][]{%
\setkeys{includespread}{#1}%
\luaexec{%
odsfile = "\luatexluaescapestring{#2}"%
local ods = odsreader.load(odsfile)%
odsfile, e = ods:loadContent()%
}%
}
\newcommand\includespread[1]{%
\luaexec{range = {nil,nil,nil,nil}}%
\setkeys{includespread}{#1}%
\luaexec{%
local body = odsreader.getTable(odsfile,sheetname)
local values =odsreader.tableValues(body,range[1],range[2],range[3],range[4])
local rowValues = function(row)
local t={}
for _,column in pairs(row) do table.insert(t,column.value) end
return t
end
for i,row in pairs(values) do
tex.sprint(table.concat(rowValues(row)," & ").."\\\\")
end
}
}
\endinput
在这个版本中,您必须手动指定表列,但分析例如第一行的内容并从中生成列规范应该并不困难。
使用示例:
\documentclass{article}
\usepackage{odsfile,lmodern}
\begin{document}
\pagestyle{empty}
Inserting range from sheet
\begin{tabular}{l l}
\includespread{file=pokus.ods,sheet=List1,range=a2:b4}
\end{tabular}
Including of whole sheet
\begin{tabular}{l l l}
\includespread{}
\end{tabular}
Inserting second sheet
\begin{tabular}{l}
\includespread{file=pokus.ods,sheet=List2}
\end{tabular}
\end{document}
答案2
您可以通过 \write18 传递命令管道来将 document.xml 解压缩到 stdin 并放入 lxprintf (LTxml 套件的一部分)来实现此目的 http://www.ltg.ed.ac.uk/software/ltxml/它允许您从 XML 中提取内容),然后通过 awk 或其他方式处理结果。或者直接使用 Perl/Tcl/Python。