You can use my LuaXML package, which as it's name suggests can process XML documents using LuaTeX. I would create a simple Lua module that can process your document, using modified version of code from my answer about Mathml to LaTeX processing. This is the library, 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
I will not describe it in much detail, as it is basically the same as from my other answer, it is only more general.
The main function it provides is transform_xml.add_action
. It takes two parameters, the first is name of the element, the second is a TeX template that will be inserted in your document.
The template can insert text from all children elements using %s
text. You should use it in all elements that can contain any text. Attributes can be accessed using @{attribute name}
placeholder.
Sample template may look like this:
xmltransform.add_action("section", [[\section{@{title}}
%s
\par]])
Note that we use [[ ... ]]
to specify the template, in order to make it easier to input newlines and backslashes. You also need to use \par
command when you want to insert paragraph.
We can specify necessary actions in custom TeX package, 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
Actions are specified for all elements that need it (article, section, image). When no action is specified for a element, like <body>
, it's content is just processed and inserted to the TeX document.
Package also declares a new command, \processarticle
. It takes XML file name and process it with transform_xml
.
We can now try a sample document:
\documentclass{book}
\usepackage{myarticle}
\begin{document}
\processarticle{article.xml}
\end{document}
And this is the resulting PDF:
XMLTeX
else try with the conversion fromXML
toLaTeX
by usingXSLT
...