local unimath_symbols = require("unimath_symbols") local convert_char = unimath_symbols.convert_char local converters = {} local function convert(n) local id = n.id local type = node.type(id) local typeconv = converters[type] if typeconv then return typeconv(n) or "" else texio.write_nl("tag_math warning: no conversion available for " .. type) return "" 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 local result = convert(n.nucleus) local subtype = node.subtypes(n.id)[n.subtype] if subtype == "oplimits" then result = result .. "\\limits" elseif subtype == "opdisplaylimits" then result = result .. "\\displaylimits" end if n.sub then result = result .. "_{" .. convert(n.sub) .. "}" end if n.sup then result = result .. "^{" .. convert(n.sup) .. "}" end return result end function converters.math_char(n) return convert_char(n.char) end function converters.sub_mlist(n) local result = "" for n in node.traverse(n.head) do result = result .. convert(n) end return result end function converters.fence(n, subtype) local subtype = node.subtypes(n.id)[n.subtype] local leftright = { left = "\\left", right = "\\right" } local result if n.delim.small_char ~= 0 then result = convert_char(n.delim.small_char) elseif n.delim.large_char ~= 0 then result = convert_char(n.delim.large_char) else result = "." end return leftright[subtype] .. result end function converters.fraction(n) local num = convert(n.num) local denom = convert(n.denom) return "\\frac{" .. num .. "}{" .. denom .. "}" end function converters.radical(n) local result = "\\sqrt{" .. convert(n.nucleus) .. "}" if n.sub then result = result .. "_{" .. convert(n.sub) .. "}" end if n.sup then result = result .. "^{" .. convert(n.sup) .. "}" end return result end function converters.style(n) return "\\" .. n.style .. "style" end function converters.accent(n) local result = convert(n.nucleus) if n.accent then result = convert(n.accent) .. "{" .. result .. "}" end if n.bot_accent then result = convert(n.bot_accent) .. "{" .. result .. "}" end if n.sub then result = result .. "_{" .. convert(n.sub) .. "}" end if n.sup then result = result .. "^{" .. convert(n.sup) .. "}" end return result end function converters.glue(n) -- FIXME: any glue is treated like space return " " end function converters.kern(n) -- FIXME: any kern is just dropped return "" end local function callback(head, display_type, need_penalties) local text = {} for n in node.traverse(head) do text[#text + 1] = convert(n) end -- concatenate, escape, and remove quotes local actual_text = string.sub(string.format("%q", table.concat(text)), 2, -2) if display_type == "display" then actual_text = "\\\$" .. actual_text .. "\\\$" elseif display_type == "text" then actual_text = "\\\$$" .. actual_text .. "\\\$$" end local BDC = node.new("whatsit", "pdf_literal") BDC.data = "/Span <> BDC" BDC.mode = 2 head = node.insert_before(head, head, BDC) local EMC = node.new("whatsit", "pdf_literal") EMC.data = "EMC" EMC.mode = 2 head = node.insert_after(head, node.tail(head), EMC) return head end return { callback = callback }
 \pdfvariable compresslevel 0 \pdfvariable objcompresslevel 0 \documentclass{article} \pagestyle{empty} \usepackage{amsmath} \usepackage{unicode-math} \DeclareMathOperator\Res{Res} \directlua{tag_math = require("tag_math")} \AtBeginDocument{\directlua{ luatexbase.add_to_callback("mlist_to_hlist", function(head, display_type, need_penalties) head = tag_math.callback(head, display_type, need_penalties) return node.mlist_to_hlist(head, display_type, need_penalties) end, "tag_math") }} \begin{document} $\frac{1}{2\pi i} \int\limits_\gamma f\left(x^{\symbf{N}\in\mathbb{C}^{N\times 10}}\right) = \sum_{k=1}^m n(\gamma;a_k)\Res(f;a_k)\,.$ $\frac{1}{\sqrt{2\pi}^2 i} \int\limits_\gamma f\left(x^{\symbf{N}\in\mathbb{C}^{N\times 10}}\right) = \sum_{k=1}^m n(\gamma;a_k)\Res(f;a_k)\,.$ $a^2 + b^2 = c^2$ $$a^2 + b^2 = c^2$$ $$a^2 + b^2 = c^2$$ $a^2 + b^2 = c^2$ % tag_math warning: no conversion available for sub_box % That is because \eqno is a \hbox in math mode \begin{equation} a^2 + b^2 = c^2 \end{equation} % Ideas for handling environments: % % Instead of flushing /ActualText for each mlist immediately, insert whatsits % to delimit the environment and insert a whatsit at the head or tail with the % /ActualText inside for each mlist. Then in post_linebreak_filter (or % earlier?), scan for environment and the nested /ActualText whatsits and % assemble a single BDC and EMC whatsit from them. % Clear downside: this is a two stage process which requires extensive % bookkeeping across callbacks. \begin{align} a^2 + b^2 &= c^2 \\ a^2 + b^2 &= c^2 \end{align} % Accents % Why does this appear *before* the equation environment in the PDF??? $\threeunderdot{\hat{x}_a^b}_c^d$ \end{document}