summaryrefslogtreecommitdiff
path: root/macros/luatex/generic/luaxml/luaxml-transform.lua
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2020-12-21 03:01:35 +0000
committerNorbert Preining <norbert@preining.info>2020-12-21 03:01:35 +0000
commit4cc7922324b7a08ba0418e8f245cf1dcfa1891ac (patch)
treecd90cb6c072285e779f7555a85966b81a8757e15 /macros/luatex/generic/luaxml/luaxml-transform.lua
parent2d9d44100d72263a7df34c12aaa89dbe6ee6fe0c (diff)
CTAN sync 202012210301
Diffstat (limited to 'macros/luatex/generic/luaxml/luaxml-transform.lua')
-rw-r--r--macros/luatex/generic/luaxml/luaxml-transform.lua161
1 files changed, 161 insertions, 0 deletions
diff --git a/macros/luatex/generic/luaxml/luaxml-transform.lua b/macros/luatex/generic/luaxml/luaxml-transform.lua
new file mode 100644
index 0000000000..1bf5077a38
--- /dev/null
+++ b/macros/luatex/generic/luaxml/luaxml-transform.lua
@@ -0,0 +1,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