[luatex] Processing every letter in math

Henri Menke henri at henrimenke.de
Tue Mar 15 20:03:30 CET 2022


On Tue, 2022-03-15 at 18:16 +0100, Javier Bezos wrote:
> How can I process every single letter in math? Processing
> glyph nodes in text is more or less trivial, and to some
> extent even in-line math (surrounded by 'math' type nodes),
> but processing math, including displays, sub/superscripts,
> fractions, radicals. etc.? From the manual I think the
> solution must be the node.mlist_to_hlist and the
> corresponding callback, and this is what I’ve achieved
> so far:
> 
> -----------------------
> \directlua{
> 
> function math_tweak(head, d, p)
>    head = node.mlist_to_hlist(head, d, p)
>    for item in node.traverse(head) do
>      if item.id == node.id'glyph' then
>        item.char = 88
>      end
>    end
>    return head, d, p
> end
> 
> callback.register('mlist_to_hlist', math_tweak)
> 
> }
> 
> $a$ ${b\over c}$ $\sqrt{d}$ $e$
> 
> $$f$$
> 
> \bye
> ------------------------
> 
> Here, only ‘a’, ‘e’ and ‘f’ are converted to ‘X’ (= 88).
> 
> With a branch for item.id == 0 calling recursively the
> function sub/superscripts are processed, but that’s not
> quite correct.

You want to process the nodes *before* converting the mlist to a hlist.
Once the conversion has been done there is lots of layouting stuff that
gets in the way of sensibly processing the formula. The mlist
representation is much more structured and easier to work with. But
also then you have to take care that you treat all nodes correctly and
descend into sublists:

   \directlua{
   
   local converters = {}
   
   local function convert(n)
       local id = n.id
       local type = node.type(id)
   
       local typeconv = converters[type]
       if typeconv then
           typeconv(n)
       else
           texio.write_nl("Warning: no conversion available for " .. type)
       end
   end
   
   function converters.noad(n)
       if not (n.nucleus.head or n.nucleus.char) then
           % This is a thing, e.g. ${}$ is just an empty noad
           return
       end
       convert(n.nucleus)
   
       if n.sub then
           convert(n.sub)
       end
       if n.sup then
           convert(n.sup)
       end
   end
   
   function converters.math_char(n)
       n.char = utf8.codepoint("X")
   end
   
   function converters.sub_mlist(n)
       for n in node.traverse(n.head) do
           convert(n)
       end
   end
   
   function converters.fraction(n)
       convert(n.num)
       convert(n.denom)
   end
   
   function converters.radical(n)
       convert(n.nucleus)
   
       if n.sub then
           convert(n.sub)
       end
       if n.sup then
           convert(n.sup)
       end
   end
   
   function converters.accent(n)
       convert(n.nucleus)
       if n.accent then
           convert(n.accent)
       end
       if n.bot_accent then
           convert(n.bot_accent)
       end
       if n.sub then
           convert(n.sub)
       end
       if n.sup then
           convert(n.sup)
       end
   end
   
   local function math_tweak(head, style,penalties)
       for n in node.traverse(head) do
           convert(n)
       end
       return node.mlist_to_hlist(head, style, penalties)
   end
   
   callback.register('mlist_to_hlist', math_tweak)
   
   }
   
   $a \left(b\right)$
   
   $a$ ${b\over c}$ $\sqrt{d}$ $e$
   
   $$\hat{f}$$
   
   \bye
   
Cheers, Henri


> 
> Javier
> 



More information about the luatex mailing list.