1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
|
-- adapted code from https://github.com/michal-h21/luaxml-mathml
--
local domobject = require "luaxml-domobject"
local cssquery = require "luaxml-cssquery"
-- initialize CSS selector object
local css = cssquery()
-- 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
-- convert Unicode characters to TeX sequences
local unicodes = {
[35] = "\\#",
[36] = "\\$",
[37] = "\\%",
[38] = "\\&",
[60] = "\\textless{}",
[62] = "\\textgreater{}",
[92] = "\\textbackslash{}",
[94] = "\\^",
[95] = "\\_",
[123] = "\\{",
[125] = "\\}"
}
local function process_text(text, parameters)
local parameters = parameters or {}
-- spaces are collapsed by default. set verbatim=true to disable it.
local verbatim = parameters.verbatim
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
local text = table.concat(t)
if not verbatim then
text = text:gsub("(%s%s+)", function(a) return a:sub(1,1) end)
end
return text
end
-- use template string to place the processed children
local function simple_content(s,parameters)
return function(element)
local content = process_children(element,parameters)
-- process attrubutes
-- attribute should be marked as @{name}
local expanded = s:gsub("@{(.-)}", function(name)
return process_text(element:get_attribute(name) or "")
end)
--
return expanded:gsub("%%s", function(a) return content end)
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(selector, fn)
css:add_selector(selector,fn)
end
-- normal actions
local function add_action(selector, template, parameters)
css:add_selector(selector, simple_content(template, parameters))
end
function process_children(element, parameters)
-- 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(), parameters)
elseif elem:is_element() then
-- recursivelly process child elements
t[#t+1] = process_tree(elem)
end
end
return table.concat(t)
end
local function match_css(element)
local selectors = css:match_querylist(element)
if #selectors == 0 then return nil end
-- return function with the highest specificity
return selectors[1].func
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 = match_css(element) 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 process_dom(dom)
return process_tree(dom:root_node())
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,
process_dom = process_dom
}
return M
|