2

I have successfully loaded XML in LuaLaTeX. But how to load MathML. Any Lua Modules are available?

My LaTeX file:

\documentclass{article}
\usepackage{luacode}
\begin{luacode*}
xml = require('luaxml-mod-xml')
handler = require('luaxml-mod-handler')
\end{luacode*}
\begin{document}
\begin{luacode*}
sample = [[
<?xml version="1.0" encoding="utf-8"?>
<art>
<title>Scattering of flexural waves an electric current</title>
<para>Smart testing structures are components <math display="inline" overflow="scroll"><mrow><mi>G</mi><mo>&#x3d;</mo><mrow><mo stretchy="false">(</mo><mi>V</mi><mo>,</mo><mi>E</mi><mo stretchy="false">)</mo></mrow></mrow></math> used in engineering applications that are capable of sensing or reacting to their environment in a predictable and desired manner. In addition to carrying mechanical loads, smart structures may alleviate vibration, reduce acoustic noise, change their mechanical properties as required or monitor their own condition.</para>
</art>
]]
XMLIn = handler.simpleTreeHandler()
x = xml.xmlParser(XMLIn)
x:parse(sample)
tex.sprint(XMLIn.root["art"]["title"])
tex.sprint(XMLIn.root["art"]["para"])
\end{luacode*}
\end{document}
  • 2
    ConTeXt supports MathML, in case LuaLaTeX doesn't make the trick for you – user193767 Jun 8 at 17:10
3

The LuaXML library provides more high level library to process XML, luaxml-domobject. It can create DOM structure similar to HTML DOM in JavaScript.

I've created a simple library that can process XML you provided. It doesn't support full MathML, but it isn't hard to add support for additional elements. Here is the full code, process_xml.lua:

kpse.set_program_name "luatex"
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)
    return string.format(s, content)
  end
end

local function math_action(element)
  local content = process_children(element)
  local display = element:get_attribute("display") or "inline"
  local template = "$%s$" 
  if display == "block" then template = "\n\\[\n%s\n\\]\n" end
  return string.format(template, content)
end

local mathvariant_templates = {
  normal = "\\mathrm{%s}",
  identifier = "\\operatorname{%s}" -- this needs amsmath package
  -- there are lot more, see https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mi
}

local function mi_action(element)
  local content = process_children(element)
  -- how should be <mi> rendered is based on the length.
  -- one character should be rendered in italic, two and more characters
  -- act like identifier like \sin
  local implicit_mathvariant = utf8.len(content) > 1 and "identifier" or "italic"
  -- the rendering can be also based on the mathvariant attribute
  local mathvariant = element:get_attribute("mathvariant") or implicit_mathvariant
  local template = mathvariant_templates[mathvariant] or "%s"
  return string.format(template, content)

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

local function frac_action(element)
  -- <mfrac> should have two children, we need to process them separatelly
  local numerator = process_children(get_child_element(element, 1))
  local denominator = process_children(get_child_element(element, 2))
  return string.format("\\frac{%s}{%s}", numerator, denominator)
end

-- actions for particular elements
local actions = {
  title = simple_content("\\section{%s}\n"),
  para = simple_content("%s\n\\par"),
  math = math_action,
  mi = mi_action,
  mfrac = frac_action, -- example of element that needs to process the children separatelly
  -- here you can add more elements, like <mo> etc.
}

-- convert Unicode characters to TeX sequences
local unicodes = {
   [960] = "\\pi{}"
}

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


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,
  print_tex = print_tex
}

return M

It provides two main functions, parse_xml, which converts XML string to TeX string, and print_tex, which will print the TeX string to the document. You cannot use tex.sprint directly, because characters for newlines are not handled correctly.

You can configure three tables:

mathvariant_templates - this table provides configuration for <mi> elements mathvariant attributes. It should contain templates for string.format function. This is the default:

local mathvariant_templates = {
  normal = "\\mathrm{%s}",
  identifier = "\\operatorname{%s}" -- this needs amsmath package
  -- there are lot more, see https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mi
}

unicodes - Unicode codepoints of characters that should be replaced with LaTeX macros. XML entites will be replaced by Unicode by LuaXML, so you should place things like \pi etc. here. This is the default:

-- convert Unicode characters to TeX sequences
local unicodes = {
   [960] = "\\pi{}"
}

The most important configuration is actions. This should handle elements. All contents of element are proceeded by default, so you need to configure only elements that you want to take a special action.

I've added few helper functions:

simple_content takes string.format template and replaces %s character with content of element's children.

More advanced functions like math_action or mi_action can decide what to do by analyzing of the content or element attributes.

Some elements take fixed number of child elements, like <mfrac>, which should have two children. We want to transfrom this to \frac macro, which takes two arguments. In this case, we need to process the two child elements separately. The frac_action function handles this. You will need similar functions for some other MathML elements that I didn't implement.

This is the default content of actions:

-- actions for particular elements
local actions = {
  title = simple_content("\\section{%s}\n"),
  para = simple_content("%s\n\\par"),
  math = math_action,
  mi = mi_action,
  mfrac = frac_action, -- example of element that needs to process the children separatelly
  -- here you can add more elements, like <mo> etc.
}

Here is a sample TeX file:

\documentclass{article}
\usepackage{amsmath}
\usepackage{luacode}
\begin{document}
\begin{luacode*}
local process_xml = require "process_xml"    
sample = [[
<?xml version="1.0" encoding="utf-8"?>
<art>
<title>Scattering of flexural waves an electric current</title>
<para>Smart testing structures are components <math display="inline" overflow="scroll"><mrow><mi>G</mi><mo>&#x3d;</mo><mrow><mo stretchy="false">(</mo><mi>V</mi><mo>,</mo><mi>E</mi><mo stretchy="false">)</mo></mrow></mrow></math> used in engineering applications that are capable of sensing or reacting to their environment in a predictable and desired manner. In addition to carrying mechanical loads, smart structures may alleviate vibration, reduce acoustic noise, change their mechanical properties as required or monitor their own condition.</para>
<para>
More examples: <math>
  <mi>sin</mi><mi>x</mi>
</math> or Pi: <math><mi>&pi;</mi></math>
</para>
<math display="block">
<mfrac><mi>a</mi><mi>b</mi></mfrac>
</math>
</art>
]]
process_xml.print_tex(process_xml.parse_xml(sample))
\end{luacode*}
\end{document}

It is rendered in this way:

enter image description here

| improve this answer | |
  • It's possible to load ConTeXT modules in Lua?. ConTeXT command \usemodule[mathml] – Balaji Jun 9 at 14:58
  • @Balaji I don't have any experience with Context modules, but it shouldn't be problem. You will only need to provide Context commands in the templates. LaTeX commands are used in my example, which cannot work in Context. – michal.h21 Jun 9 at 19:09
  • @Balaji I don't know anything about ConTeXt modules unfortunately. You may try to ask on ConTeXt mailing list, it is quite active. – michal.h21 Jun 11 at 10:53
  • How to actions when using rece-date element in Lua? I have tried rece%-date and it's not working. Please advise or can i post separate question? – Balaji Jul 12 at 7:52
  • @Balaji I am afraid that I don't understand what you mean be rece-date. Is it some custom XML element? – michal.h21 Jul 13 at 13:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.