Lua script in separate LaTeX Style File

cross-referencingluacodeluatex

I have successfully loaded XML in LuaLaTeX with help of Michal.h21. Now I would like to store all the customized defined Lua Script in a separate LaTeX Style file, like bibr.sty, so that I will call just \usepackage{bibr} in the LaTeX file.

In the bibr.sty file all the customized defined lua script need to be defined.

My BIBR Cross-Links XML file is below:

\documentclass{article}
\usepackage{luacode}
\usepackage{natbib}

\begin{document}

\begin{luacode*}
local domobject = require "luaxml-domobject"
local transform = require "luaxml-transform"
sample = [[
<?xml version="1.0" encoding="utf-8"?>
<art>
<title>Scattering of flexural waves an electric current</title>
<para>The first <xref ref-type="section" rid="Sec1">Section 1</xref> description and correlation between gait impairment and hydrocephalus were made in 1965 by Hakim and Adam (<xref ref-type="bibr" rid="B1">1</xref>). The etiology of idiopathic normal pressure hydrocephalus (iNPH) has not yet been entirely understood (<xref ref-type="bibr" rid="B2">2</xref>–<xref ref-type="bibr" rid="B5">5</xref>). In elderly patients, other conditions such as spinal canal stenosis, Parkinson's disease, and polyneuropathy may influence the gait negatively.</para>
</art>]]

local dom = domobject.parse(sample)
local function process_instructions(el)

-- try to find <xref> followed by dash and another <xref>
-- we then expand rid attributes
for _, xref in ipairs(dom:query_selector("xref[ref-type='bibr']")) do
  local first = xref:get_sibling_node(1)
  local second = xref:get_sibling_node(2)
  if first and second and
     first:is_text() and first:get_text() == "–" and -- next node must be "–" text
     second:is_element() and second:get_element_name() == "xref" -- second element must be <xref>
  then
    local rid1 = xref:get_attribute("rid") or ""
    local rid2 = second:get_attribute("rid") or ""
    local prefix1, number1 = rid1:match("(%a+)(%d+)")
    local prefix2, number2 = rid2:match("(%a+)(%d+)")
    -- expand only if prefixes match each other
    if prefix1 and prefix2 and prefix1 == prefix2 then
      -- expand numbers
      local t = {}
      for i = number1, number2 do
        t[#t+1] = prefix1 .. math.floor(i)
      end
      -- save expanded numbers
      xref:set_attribute("rid", table.concat(t, ","))
      -- remove unnecessary elements
      first:remove_node()
      second:remove_node()
    end
  end

  
end
end

process_instructions(dom:root_node())

local transformer = transform.new()
transformer:add_action("title", "\\section{@<.>}")
transformer:add_action("para", "@<.>\\par")
transformer:add_action("xref[ref-type='bibr']", "\\citep{@{rid}}")
-- handle the processing instruction
transformer:add_custom_action("lualatex-instruction",
    function(el)
        return el:get_attribute("text")
    end)
local result = transformer:process_dom(dom)
-- Print Output Result in Command-Prompt/Terminal
print(result)
-- Print Output Result in PDF uncomment below
--transform.print_tex(result)
\end{luacode*}
\end{document}

How to achieve this?

Best Answer

As David suggested, it is a good idea to move the Lua code to a standalone file, to prevent some interaction issues between TeX and Lua. It could look like this, bibr.lua:

local M = {}
local domobject = require "luaxml-domobject"
local transform = require "luaxml-transform"

local function process_instructions(el)
  for _, child in ipairs(el:get_children()) do
    local ntype = child:get_node_type()
    if ntype == "PI" and child._name=="LuaLaTeX" then
      local text = child._attr[ "_text" ]
      local newel = el:create_element("lualatex-instruction", {text = text})
      child:replace_node(newel)
    end
    if child:is_element() then
      process_instructions(child)
    end
  end
end

function M.process(sample)
  local dom = domobject.parse(sample)
  -- try to find <xref> followed by dash and another <xref>
  -- we then expand rid attributes
  for _, xref in ipairs(dom:query_selector("xref[ref-type='bibr']")) do
    local first = xref:get_sibling_node(1)
    local second = xref:get_sibling_node(2)
    if first and second and
      first:is_text() and first:get_text() == "–" and -- next node must be "–" text
      second:is_element() and second:get_element_name() == "xref" -- second element must be <xref>
    then
      local rid1 = xref:get_attribute("rid") or ""
      local rid2 = second:get_attribute("rid") or ""
      local prefix1, number1 = rid1:match("(%a+)(%d+)")
      local prefix2, number2 = rid2:match("(%a+)(%d+)")
      -- expand only if prefixes match each other
      if prefix1 and prefix2 and prefix1 == prefix2 then
        -- expand numbers
        local t = {}
        for i = number1, number2 do
          t[#t+1] = prefix1 .. math.floor(i)
        end
        -- save expanded numbers
        xref:set_attribute("rid", table.concat(t, ","))
        -- remove unnecessary elements
        first:remove_node()
        second:remove_node()
      end
    end


  end

  process_instructions(dom:root_node())

  local transformer = transform.new()
  transformer:add_action("title", "\\section{@<.>}")
  transformer:add_action("para", "@<.>\\par")
  transformer:add_action("xref[ref-type='bibr']", "\\citep{@{rid}}")
  -- handle the processing instruction
  transformer:add_custom_action("lualatex-instruction",
  function(el)
    return el:get_attribute("text")
  end)
  local result = transformer:process_dom(dom)
  print(result)
  transform.print_tex(result)
end

function M.process_file(filename)
  local f = io.open(filename, "r")
  if not f then return nil, "Cannot open file: " .. filename end
  local content = f:read("*all")
  f:close()
  M.process(content)
end

return M

I've moved the code that was originally in the main body of the luacode environment to a function M.process. The M table is returned from this library, so it will enable you to use this code in this way:

 local bibr = require "bibr"
 bibr.process("xml code...")

I've also made another function, M.process_file, which will transform XML file, so you don't need to pass the XML code to your document.

You then need a LaTeX package that will provide commands that can be used in your documents. It can look like this, bibr.sty:

\ProvidesPackage{bibr}

\NewDocumentEnvironment{processbibr}{+b}{%
  \directlua{
    local bibr = require "bibr"
    bibr.process([[\unexpanded{#1}]])
  }
}{}

\NewDocumentCommand\processbibrfile{m}{%
  \directlua{%
    local bibr = require "bibr"
    bibr.process_file("#1")
  }
}

\endinput

It provides processbibr environment and \processbibrfile command. The environment can be used if you want to put the XML code in the document, the command for including external XML files.

It can be used like this:

\documentclass{article}
\usepackage{natbib}
\usepackage{bibr}
\begin{document}
\begin{processbibr}
<?xml version="1.0" encoding="utf-8"?>
<art>
<title>Scattering of flexural waves an electric current</title>
<para>Smart testing structures are $a+b$ components Reduce acoustic noise used in engineering applications that are capable of sensing or reacting to their environment change <?LuaLaTeX Sample XXX1?> their mechanical properties. in a predictable and desired manner. In addition to carrying mechanical loads, <?LuaLaTeX \hspace*{12pt}Abc?> alleviate vibration, reduce acoustic noise, change their mechanical properties as required or monitor their own condition.</para>
<para>The first <xref ref-type="section" rid="Sec1">Section 1</xref> description and correlation between gait impairment and hydrocephalus were made in 1965 by Hakim and Adam (<xref ref-type="bibr" rid="B1">1</xref>). The etiology of idiopathic normal pressure hydrocephalus (iNPH) has not yet been entirely understood (<xref ref-type="bibr" rid="B2">2</xref>&#x02013;<xref ref-type="bibr" rid="B5">5</xref>). In elderly patients, other conditions such as spinal canal stenosis, Parkinson&#x00027;s disease, and polyneuropathy may influence the gait negatively.</para>
</art>
\end{processbibr}

\processbibrfile{test.xml}
\end{document}

In general, I think it is better to use \processbibrfile, as you shouldn't run into strange errors that can happen when you include the XML file directly to your TeX file.

This is the result:

enter image description here

Related Question