diff options
Diffstat (limited to 'macros/luatex/generic/luaxml/luaxml-transform.lua')
-rw-r--r-- | macros/luatex/generic/luaxml/luaxml-transform.lua | 281 |
1 files changed, 225 insertions, 56 deletions
diff --git a/macros/luatex/generic/luaxml/luaxml-transform.lua b/macros/luatex/generic/luaxml/luaxml-transform.lua index 1bf5077a38..9ea4595ea7 100644 --- a/macros/luatex/generic/luaxml/luaxml-transform.lua +++ b/macros/luatex/generic/luaxml/luaxml-transform.lua @@ -1,15 +1,14 @@ --- adapted code from https://github.com/michal-h21/luaxml-mathml +--- XML transformation module for LuaXML +-- @module luaxml-tranform +-- @author Michal Hoftich <michal.h21@gmail.com + +-- code originaly comes from 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 = { @@ -26,6 +25,15 @@ local unicodes = { [125] = "\\}" } +local function match_css(element,csspar) + local css = csspar or css + local selectors = css:match_querylist(element) + if #selectors == 0 then return nil end + -- return function with the highest specificity + return selectors[1].func +end + + local function process_text(text, parameters) local parameters = parameters or {} -- spaces are collapsed by default. set verbatim=true to disable it. @@ -37,26 +45,59 @@ local function process_text(text, parameters) t[#t+1] = unicodes[char] or utf8.char(char) end local text = table.concat(t) - if not verbatim then + if parameters.collapse_newlines==true then + text = text:gsub("\n", " ") + end + -- verbatim can be set in parameters table. it prevent collapsing of spaces. + if not parameters.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) +-- this function is initialized later, I need the declaration here +-- to prevent Lua run-time error +local process_tree + +--- Transform DOM element and it's children +-- @param element DOM element +-- @param parameters Table with settings +-- @return Transformed string +local 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 +-- 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 + +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 + + +--- return nth child element +-- @param element DOM element to be processed +-- @param count Number of child element that should be returned +-- @return DOM object, or nil if it cannot be found local function get_child_element(element, count) -- return specified child element local i = 0 @@ -70,54 +111,81 @@ local function get_child_element(element, count) end end + +--- Default transforming function. It replaces %s by text content of the +-- element, and @ {attrname} by value of an attribute +-- @param s Template string +-- @param parameters Table with settings +-- @return transforming function +local function simple_content(s,parameters) + return function(element) + local content = process_children(element,parameters) + local parameters = parameters or {} + -- process attrubutes + -- attribute should be marked as @{name} + local expanded = s:gsub("@{(.-)}", function(name) + return process_text(element:get_attribute(name) or "", parameters) + end) + -- process child elements + expanded = expanded:gsub("@<(.-)>",function(name) + -- @<.> return element's content + if name == "." then + return content + -- @<1> returns first child element's content + elseif name:match("^[0-9]+$") then + local child = get_child_element(element, tonumber(name)) + if child then + return process_children(child, parameters) + end + -- @<CSS selector> returns contents of matched selectors + else + local t = {} + for _, child in ipairs(element:query_selector(name)) do + t[#t+1] = process_children(child, parameters) + end + -- concetanate results, separated by parameters.separator + local separator = parameters.separator or "" + return table.concat(t, separator) + end + end) + + return expanded:gsub("%%s", function(a) return content end) + end +end + + + -- actions for particular elements local actions = { } --- add more complicated action -local function add_custom_action(selector, fn) +--- Use function to transform selected element +-- @param selector CSS selector for the matching element +-- @param fn Function that transforms the selected DOM element. +-- @param csspar cssquery object. Default is set by the library, so it is not necessary to use. +local function add_custom_action(selector, fn, csspar) + local css = csspar or css css:add_selector(selector,fn) end --- normal actions -local function add_action(selector, template, parameters) +--- Use template to transform selected template +-- @param selector CSS selector for the matching element +-- @param template String template +-- @param parameters Table with extra parameters. Use "verbatim=true" to keep spacing in the processed text. +-- @param csspar cssquery object. Default is set by the library, so it is not necessary to use. +local function add_action(selector, template, parameters, csspar) + local css = csspar or css 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) +--- Transform XML string +-- @param content String with XML content +-- @return transformed string +local function parse_xml(content) -- parse XML string and process it local dom = domobject.parse(content) -- start processing of DOM from the root element @@ -126,6 +194,9 @@ function parse_xml(content) end +--- Transform XML file +-- @param filename XML file name +-- @return transformed string local function load_file(filename) local f = io.open(filename, "r") local content = f:read("*all") @@ -133,12 +204,16 @@ local function load_file(filename) return parse_xml(content) end -function process_dom(dom) +--- Transform XML DOM object +-- @param dom DOM object +-- @return transformed string +local function process_dom(dom) return process_tree(dom:root_node()) end - -function print_tex(content) +--- print transformed file to PDF using LuaTeX functions +-- @param content String to be printed +local 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) @@ -146,15 +221,109 @@ function print_tex(content) end +-- make Transformer objec +local Transformer + +--- Make new Transformer object +-- @return Tranformer object +local function new() + local self = setmetatable({}, Transformer) + self.css = cssquery() + -- use the default unicodes table that escapes special LaTeX characters + self.unicodes = unicodes + return self +end + +--- @type Transformer +Transformer = {} +Transformer.__index = Transformer +-- the library uses shared css variable. in order to support multiple transformers, +-- we need to save the original state, set the self.css variable as the global variable +-- execute library function and then set the original function back +-- +function Transformer:save_css() + self.old_css = css + css = self.css + -- we must also save unicodes table + self.old_unicodes = unicodes + unicodes = self.unicodes +end + +function Transformer:restore_css() + css = self.old_css + unicodes = self.old_unicodes +end + +--- add a new template +-- @param selector CSS selector that should be matched +-- @param template use %s for element's text, and @ {name} to access attribute "name" +-- @param parameters table with extra parameters +-- @see add_action +function Transformer:add_action(selector, template, parameters ) + add_action(selector, template, parameters, self.css) +end + +--- Use function for transformation +-- @param selector CSS selector that should be matched +-- @param fn DOM transforming function +-- @see add_custom_action +function Transformer:add_custom_action(selector, fn ) + add_custom_action(selector, fn, self.css) +end + +-- all methods that use transformation functions must +-- correctly handle the cssquery object that this library uses + +--- Parse XML string +-- @param content String with XML content +-- @return transformed string +-- @see parse_xml +function Transformer:parse_xml(content) + self:save_css() + local result = parse_xml(content) + self:restore_css() + return result +end + +-- make method for load_file function + +--- Transform XML file +-- @param filename XML file name +-- @return transformed string +-- @see load_file +function Transformer:load_file(filename) + self:save_css() + local result = load_file(filename) + self:restore_css() + return result +end + +-- make method for process_dom function + +--- Transform XML DOM object +-- @param dom DOM object +-- @return transformed string +-- @see process_dom +function Transformer:process_dom(dom) + self:save_css() + local result = process_dom(dom) + self:restore_css() + return result +end + + +--- @export local M = { parse_xml = parse_xml, process_children = process_children, + get_child_element= get_child_element, 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 + process_dom = process_dom, + new = new } |