看完之后这个惊人的答案,我想知道是否可以做同样的事情,但反过来,即从 luatex 创建一个 LibreOffice 电子表格,将文件中的数据导出tex
到电子表格。
我想到的是:假设我正在用一个文档类排版一份考试的解决方案,该文档类定义了一个将\answer
在以下上下文中使用的宏。
\documentclass{myclass}
\begin{document}
\section{Test 1}
\begin{enumerate}
\item The \answer{definition of entropy} states that \answer{$S = k_B
\ln \Omega$}.
\end{enumerate}
\end{document}
我使用 LibreOffice 电子表格为学生评分。因此,我想luatex
创建(或修改)电子表格,以便自动获得如下屏幕截图所示的结果。
我明白那个LibreOffice 的 lua 接口不存在。因此,我认为有三种方法:
- 从头创建符合
.ods
标准的 zip 文件。 - 使用一些魔法将 LibreOffice API 与 luatex 连接起来(使用LuaJava?)。
- 使用一些 perl 或其他类型的魔法来完成这项工作。
正如@canaaerus 在评论中所建议的那样,我可以使用 csv 文件导出内容tex
,但我希望有一个真正的.ods
开箱即用功能,以便我可以拥有样式信息(例如粗体、居中文本等)和公式。
答案1
编辑 14-08-2012
新版本odsfile
结合了此答案中的想法。现在可以使用以下代码:
\documentclass{article}
\usepackage{lmodern,odsfile,booktabs}
\begin{document}
\loadodsfile{template.ods}
\begin{AddRow}
\AddString{Hello world}{}
\AddNumber{46}{3}
\end{AddRow}
\includespread[template=booktabs,columns=head]
\savespreadsheet
\end{document}
软件包现已包含在内TeX Live
,并且xml
库单独分发http://ctan.org/pkg/luaxml,所以现在它可以被其他包使用了。
这是我使用
lualatex
和开发版本的解决方案ods文件。首先,我创建了从LuaXML
表到的序列化函数xml
。
代码
xml = require("xml-mod")
local t = {html={head = {title = "Hello world"}}} -- Table in format as created with LuaXML parser
print(xml.serialize(t))
输出
<?xml version="1.0" encoding="UTF-8"?>
<html>
<head>
<title>Hello world</title>
</head>
</html>
然后有一个小库用于向 ods 文件添加内容,exams.lua
module(...,package.seeall)
require("odsfile")
xml = require("xml-mod")
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
-- Object for adding new rows
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
end,
insert = function(self, sheet, pos)
local t = {}
local pos = pos or self:findLastRow(sheet)
for i=1, #sheet["table:table-column"] 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
它有一个函数和一个对象。updateZip(zipfile, updatefile)
尝试运行 zip 实用程序,该实用程序必须安装在您的系统中。不幸的是,我的电脑访问权限有些问题,所以它在这里不起作用。相反,我只是使用生成的命令行运行此命令,xml
它就可以正常工作。
newRow() 返回具有一些方法的对象:
addCell(val,attr,pos)
addString(val,attr,pos)
addNumber(val,attr,pos)
这些方法将列添加到行中。addCell
是通用的,不应该使用。attr 是带有属性的数组,您可以将其留空。pos
表示列在行中的位置,也可以是nil
insert(sheet, pos)
此函数将行插入到工作表中,您可以使用指定行位置pos
现在进行一些简单的LaTeX
包装,exams.sty
\ProvidesPackage{exams}
\RequirePackage{luacode}
\begin{luacode}
exams = require("exams")
require("odsfile")
xml = require("xml-mod")
--exams.test()
row = {}
sheet = {}
ods = {}
filename = ""
\end{luacode}
\newcommand\examspreadsheet[2]{%
\luaexec{%
sh = "\luatexluaescapestring{#2}"
if sh == "" then sh = nil end
filename = "\luatexluaescapestring{#1}"
local f = odsfile.load(filename)
ods = f:loadContent()
sheet = odsfile.getTable(ods,sh)
}
}
\newcommand\savespreadsheet{%
\luaexec{exams.updateZip(filename,"content.xml")}
}
\newcommand\answer[1]{%
\begingroup%
\AddRow%
\AddNumber{\theenumi}{}%
\AddString{#1}{}%
\endAddRow%
\endgroup%
#1%
}
\newenvironment{exam}[1]{%
\section{#1}
\AddRow
\AddString{#1}{}
\endAddRow
\enumerate%
}{\endenumerate%
\luaexec{%
f = io.open("content.xml","w")
f:write(xml.serialize(ods.root))
}
}
\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 = exams.newRow()%
}
}{%
\luaexec{%
row:insert(sheet,pos)%
}
}
这个包定义了三个用户命令和一个环境:
\examspreadsheet{odsfile}{sheet}
从文件加载工作表。您可以留空sheet
,然后将选择第一张工作表\answer{text}
在电子表格中添加行\savespreadsheet
保存工作表至ods file
- 环境
exam
中所有的答案都应该在里面,最后,它将文件保存content.xml
在当前目录中。
注意: 的定义中有一些\answer
我找不到的虚假空间。
现在有一些示例文件:
\documentclass{article}
\usepackage{lmodern,exams}
\begin{document}
\examspreadsheet{template.ods}{}
\begin{exam}{Test1}
\item Bla bla \answer{Test of special characters < > \& "}
\item The \answer{definition of entropy} states that \answer{$S = k_B
\ln \Omega$}.
\end{exam}
\savespreadsheet
\end{document}
使用以下方式编译
lualatex --shell-escape sample.tex
如果在控制台输出中收到类似以下错误消息:
zip I/O error: Permission denied
zip error: Could not create output file (was replacing the original zip file)
Updating an ods file.
zip template.ods content.xml
Return code: 15
然后你必须更改ods
文件的文件权限以允许其他用户写入,或者只需运行命令
zip -r template.ods content.xml
结果是openoffice calc
:
答案2
这是一个长评论而不是答案。
在 ConTeXt 中,您可以生成与文档结构对应的 XML。例如,与您的 LaTeX 示例对应的 ConTeXt 文档是:
\setupbackend[export=yes]
\definehighlight[answer] %[style=..., color=...]
\starttext
\startsection[title={Test 1}]
\startitemize[n]
\item The \answer{definition of entropy} states that \answer{$S=k_B ≤ Ω$}
\stopitemize
\stopsection
\stoptext
这里最重要的是\setupbackend
顶部的命令。我不知道你想如何直观地显示\answer
宏的输出,所以我把它留空了。
编译此文件将生成以下\jobname.export
文件:
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<!-- input filename : test -->
<!-- processing date : Thu Jul 26 14:21:19 2012 -->
<!-- context version : 2012.07.19 17:52 -->
<!-- exporter version : 0.30 -->
<document language="en" file="test" date="Thu Jul 26 14:21:19 2012" context="2012.07.19 17:52" version="0.30" xmlns:m="http://www.w3.org/1998/Math/MathML">
<section detail="section" location='aut:1'>
<sectionnumber>1</sectionnumber>
<sectiontitle>Test 1</sectiontitle>
<sectioncontent>
<itemgroup detail="itemize" symbol="n">
<item>
<itemtag>1.</itemtag>
<itemcontent>The <highlight detail="answer">definition of entropy</highlight> states that <highlight detail="answer"><m:math display="inline"><m:mrow><m:mi>