我有一个 XML 文档,其中包含一些必须用来创建 LaTeX 文档的元素。有没有办法自动获取每个元素并针对每个元素执行一些 LaTeX 命令?
为了更清楚一点,假设我有如下存储的文章:
<article title="Article title" author='The Author'>
<section title="Section title">
<body>
Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
</body>
</section>
<image source="image/path/foo.png" />
<section title=”Another section title">
<body>
Nam dui ligula, fringilia a,
euismod sodales, sollicitudin
vel, wisi.
</body>
</section>
</article>
我想要创建一个 LaTeX 文档,该文档在编译时会自动创建与我为每篇文章手动执行以下操作时相同的输出:
\chapter{Article title}
by \emph{The Author}
\section{Section title}
Lorem ipsum dolor sit amet,
consectetur adipiscing elit.
\includegraphics[width=\textwidth]{image/path/foo.png}
\section{Another section title}
Nam dui ligula, fringilia a,
euismod sodales, sollicitudin
vel, wisi.
我认为像csvsimple
package 这样的工具可以完成这项工作,但我找不到它。不过,目前我只有 XML 文档,所以我可以尝试所有可能的解决方案,而不会丢失任何已完成的工作。
答案1
您可以使用我的 LuaXML 包,顾名思义,它可以使用 LuaTeX 处理 XML 文档。我将使用我关于Mathml 到 LaTeX处理。这是图书馆,transform-xml.lua
:
-- adapted code from https://github.com/michal-h21/luaxml-mathml
--
local domobject = require "luaxml-domobject"
-- we need to define different actions for XML elements. The default action is
-- to just process child elements and return the result
local function default_action(element)
return process_children(element)
end
-- use template string to place the processed children
local function simple_content(s)
return function(element)
local content = process_children(element)
-- process attrubutes
-- attribute should be marked as @{name}
local expanded = s:gsub("@{(.-)}", function(name)
return element:get_attribute(name) or ""
end)
return string.format(expanded, content)
end
end
local function get_child_element(element, count)
-- return specified child element
local i = 0
for _, el in ipairs(element:get_children()) do
-- count elements
if el:is_element() then
-- return the desired numbered element
i = i + 1
if i == count then return el end
end
end
end
-- actions for particular elements
local actions = {
}
-- add more complicated action
local function add_custom_action(name, fn)
actions[name] = fn
end
-- normal actions
local function add_action(name, template)
actions[name] = simple_content(template)
end
-- convert Unicode characters to TeX sequences
local unicodes = {
[35] = "\\#",
[38] = "\\&",
[60] = "\\textless{}",
[62] = "\\textgreater{}",
[92] = "\\textbackslash{}",
[123] = "\\{",
[125] = "\\}"
}
local function process_text(text)
local t = {}
-- process all Unicode characters and find if they should be replaced
for _, char in utf8.codes(text) do
-- construct new string with replacements or original char
t[#t+1] = unicodes[char] or utf8.char(char)
end
return table.concat(t)
end
function process_children(element)
-- accumulate text from children elements
local t = {}
-- sometimes we may get text node
if type(element) ~= "table" then return element end
for i, elem in ipairs(element:get_children()) do
if elem:is_text() then
-- concat text
t[#t+1] = process_text(elem:get_text())
elseif elem:is_element() then
-- recursivelly process child elements
t[#t+1] = process_tree(elem)
end
end
return table.concat(t)
end
function process_tree(element)
-- find specific action for the element, or use the default action
local element_name = element:get_element_name()
local action = actions[element_name] or default_action
return action(element)
end
function parse_xml(content)
-- parse XML string and process it
local dom = domobject.parse(content)
-- start processing of DOM from the root element
-- return string with TeX content
return process_tree(dom:root_node())
end
local function load_file(filename)
local f = io.open(filename, "r")
local content = f:read("*all")
f:close()
return parse_xml(content)
end
function print_tex(content)
-- we need to replace "\n" characters with calls to tex.sprint
for s in content:gmatch("([^\n]*)") do
tex.sprint(s)
end
end
local M = {
parse_xml = parse_xml,
process_children = process_children,
print_tex = print_tex,
add_action = add_action,
add_custom_action = add_custom_action,
simple_content = simple_content,
load_file = load_file
}
return M
我不会详细描述它,因为它基本上与来自我的另一个答案,只是更加通用。
它提供的主要功能是transform_xml.add_action
。它需要两个参数,第一个是元素的名称,第二个是将插入到文档中的 TeX 模板。
该模板可以使用文本从所有子元素插入%s
文本。您应该在所有可以包含任何文本的元素中使用它。可以使用@{attribute name}
占位符访问属性。
示例模板可能如下所示:
xmltransform.add_action("section", [[\section{@{title}}
%s
\par]])
注意我们使用[[ ... ]]
来指定模板,是为了方便输入换行符和反斜杠。\par
插入段落时也需要使用命令。
我们可以在自定义 TeX 包中指定必要的操作myarticle.sty
:
\ProvidesPackage{myarticle}
\RequirePackage{luacode}
\RequirePackage{graphicx}
\RequirePackage{xparse}
\begin{luacode*}
xmltransform = require "transform_xml.lua"
xmltransform.add_action("article", [[
\chapter{@{title}}
by \emph{@{author}}\par
%s
]])
xmltransform.add_action("section", [[\section{@{title}}
%s
\par]])
xmltransform.add_action("image", '\\includegraphics{@{source}}')
function article_load(filename)
local transformed = xmltransform.load_file(filename)
if transformed then
xmltransform.print_tex(transformed)
else
tex.sprint("Error in parsing of the XML file " .. filename)
end
end
\end{luacode*}
\NewDocumentCommand\processarticle{m}{%
\directlua{article_load("#1")}
}
\endinput
为所有需要的元素(文章、章节、图像)指定操作。当没有为元素指定操作时,例如<body>
,它的内容将被处理并插入到 TeX 文档中。
包还声明了一个新命令\processarticle
。它获取 XML 文件名并使用 对其进行处理transform_xml
。
我们现在可以尝试一个示例文档:
\documentclass{book}
\usepackage{myarticle}
\begin{document}
\processarticle{article.xml}
\end{document}
最终的 PDF 结果如下: