# Tex4ht generates invalid math in mathjax mode when using dmath

The following MWE generates invalid math in HTML using mathjax mode. This only happens when using dmath*. I am using the solution from Error using \ifdefined\HCode with TeX4ht, but it works in LuaTeX and give below all code needed to reproduce this problem using a MWE.

The Latex come from Mathematica conversion:

 sol = DSolve[y'[x] == 2*y[x]/x + x^3/y[x] + x*Tan[y[x]/x^2], y[x], x]
TeXForm[sol]


Gives

\text{Solve}\left[3 \log (x)-\log \left(y(x) \sin \left(\frac{y(x)}{x^2}\right)+x^2 \cos \left(\frac{y(x)}{x^2}\right)\right)=c_1,y(x)\right]


tex4ht converts the above Latex OK when putting the above inside an equation. But when the Latex is inside dmath it given HTML which is not valid. (can't be rendered by mathjax)

Here is the MWE

\documentclass[12pt]{article}%

\usepackage{amsmath}
\usepackage{breqn}

\begin{document}

$\text{Solve}\left[3 \log (x)-\log \left(y(x) \sin \left(\frac{y(x)}{x^2}\right)+x^2 \cos \left(\frac{y(x)}{x^2}\right)\right)=c_1,y(x)\right]$

\begin{dmath*}
\text{Solve}\left[3 \log (x)-\log \left(y(x) \sin \left(\frac{y(x)}{x^2}\right)+x^2 \cos \left(\frac{y(x)}{x^2}\right)\right)=c_1,y(x)\right]
\end{dmath*}

\end{document}


Compiled using

 make4ht  -ulm default -c ./new.cfg -e ./filter.lua -a debug index.tex "mathjax,htm"


Gives

Here is the raw HTML

<!DOCTYPE html>
<html lang='en-US' xml:lang='en-US'>
<meta charset='utf-8' />
<meta content='TeX4ht (https://tug.org/tex4ht/)' name='generator' />
<meta content='width=device-width,initial-scale=1' name='viewport' />
<link href='index.css' rel='stylesheet' type='text/css' />
<meta content='index.tex' name='src' />
<script>window.MathJax = { tex: { tags: "ams", maxBuffer: 40*1024, packages: {'[+]': ['textmacros']}, environments: { "dgroup*": ["", ""], "dmath*": ["", ""], "dgroup": ["", ""], "dmath": ["", ""] }, macros: { sp: "^", sb: "_", noalign: ["\#1", 1], }, packages: {'[+]': ['noerrors','textmacros']} }, loader: { load: ['[tex]/noerrors','[tex]/textmacros'] } }; </script>
<script async='async' id='MathJax-script' src='https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml-full.js' type='text/javascript'></script>
<!-- l. 11 --><p class='noindent'>\begin {align*}  \text {Solve}\left [3 \log (x)-\log \left (y(x) \sin \left (\frac {y(x)}{x^2}\right )+x^2 \cos \left (\frac {y(x)}{x^2}\right )\right )=c_1,y(x)\right ]  \end {align*}

</p><!-- l. 15 --><p class='noindent'>\begin{align*}  \text {Solve}\left [3 \log (x)-\log \left (y(x) \sin \left (\frac {y(x)}{x^2}\right )+x^2 \cos \left (\frac {y(x)}{x^2}\right )\right )&amp;=c_1,y(x)\right ]  \end{align*}
</p>

</body>
</html>


By trial and error, I found the problem goes away when removing \left[ and right] ! I also noticed that tex4ht generates &amp; in the raw HTML above near the end of the line, when \left[ and right] are present which might be the cause of the problem (need to check).

YES. Confirmed When removing &amp; from the HTML generated, now mathjax renders it OK.

This happens only when using dmath* environment and not normal equation  or normal align* environment.

## Appendix

Here are the files new.cfg and filter.lua used in the above command

new.cfg

\Preamble{xhtml}
%V3.0
\Configure{MathJaxConfig}{{
tex: {
tags: "ams",
maxBuffer: 40*1024,
packages: {'[+]': ['textmacros']},
\detokenize{%
environments: {
"dgroup*": ["", ""],
"dmath*": ["", ""],
"dgroup": ["", ""],
"dmath": ["", ""]
}},
\unexpanded{%
macros: {
sp: "^",
sb: "_",
noalign: ["\#1", 1],
}},
packages: {'[+]': ['noerrors','textmacros']}
},
}
};
}

%Thanks to https://tex.stackexchange.com/questions/620104/error-using-ifdefined-hcode-with-tex4ht-but-it-works-in-luatex/620138?noredirect=1#comment1548405_620138
%IMPORTANT. Need also .config folder in my HOME folder for this to work.
\VerbMath{dgroup*}
\VerbMath{dmath*}
\VerbMath{dgroup}
\VerbMath{dmath}

\begin{document}
\EndPreamble


filter.lua

local filter = require "make4ht-filter"

local function process_options(options)
-- convert [number={foo}] to \tag{foo}
local number = options:match("number%s*=%s*{?(%w+)")
if number then
return "\\tag{" .. number .. "}"
end
return ""
end

local function escape_equal(str)
-- there can be multiple equal characters in the string. we should put the & character
-- just before the one which is not inside any group
-- escape nested equal signs
str = str:gsub("({[^{^}]+)=([^{^}]+})", "%1:EQUAL:%2")
-- replace remaining =
str = str:gsub("=", "&=")
-- return escaped =
str = str:gsub(":EQUAL:", "=")
return str
end

local function make_align(dgroup)
-- change breqn environment contents to align*
-- change = to &=
local dgroup = escape_equal(dgroup)
-- return the fixed text in align* environment
return "\\begin{align*}" ..  dgroup .. "\\end{align*}"
end

local function process_dgroup(s, env_name)
return s:gsub("\\begin%s*{" .. env_name .. "}(.-)\\end%s*{" .. env_name .. "}",
function(dgroup)
-- remove environemnts
-- change dmath[...,number={label}] to \tag{label}
dgroup = dgroup:gsub("\\begin%s*{dmath}%s*(%b[])", process_options)
-- remove all other environemnts
dgroup = dgroup:gsub("\\begin%s*{.-}","")
dgroup = dgroup:gsub("\\end%s*{.-}", "\\\\")
return make_align(dgroup)
end)
end

-- process remaining dmath environments
local function process_dmath(s, env_name)
return s:gsub("\\begin%s*{" .. env_name .. "}(.-)\\end%s*{" .. env_name .. "}",
function(dmath)
-- options can be still here
local dmath = dmath:gsub("^%s*(%b[])", process_options)
return make_align(dmath)
end)
end

local process = filter {
-- find all dgroup* environments and convert them to align*
function(s)
local s = process_dgroup(s,"dgroup%*")
s = process_dgroup(s, "dgroup")
-- process remaining dmath environments in the document
s = process_dmath(s, "dmath%*")
s = process_dmath(s, "dmath")
return s
end
}

-- install filter to match HTML files
Make:match("html?\$", process)


Using TL 2021 on Linux ubuntu 20.04

It seems that the issue is that the &= is inside \left[ ... \right]. It seems like a MathJax issue. The & character should be used inside align* for correct alignment of multiline equations, but it doesn't work in this case. Maybe we can just omit it.

Try this version of the build file:

local filter = require "make4ht-filter"

local function process_options(options)
-- convert [number={foo}] to \tag{foo}
local number = options:match("number%s*=%s*{?(%w+)")
if number then
return "\\tag{" .. number .. "}"
end
return ""
end

local function escape_equal(str)
-- there can be multiple equal characters in the string. we should put the & character
-- just before the one which is not inside any group
-- escape nested equal signs
str = str:gsub("({[^{^}]+)=([^{^}]+})", "%1:EQUAL:%2")
-- replace remaining =
str = str:gsub("=", "& =")
-- return escaped =
str = str:gsub(":EQUAL:", "=")
return str
end

local function make_align(dgroup, new_env)
-- change breqn environment contents to align*
-- change = to &=

local dgroup = dgroup
if not new_env then
dgroup = escape_equal(dgroup) --  <- we don't do this anymore, it leads only to problems
end
local new_env = new_env or  "\\begin{align*}\n%s\n\\end{align*}"
-- return the fixed text in align* environment
-- return "\\begin{".. new_env .."}" ..  dgroup .. "\\end{" .. new_env .. "}"
return string.format(new_env, dgroup)
end

local function process_dgroup(s, env_name)
return s:gsub("\\begin%s*{" .. env_name .. "}(.-)\\end%s*{" .. env_name .. "}",
function(dgroup)
-- remove environemnts
-- change dmath[...,number={label}] to \tag{label}
dgroup = dgroup:gsub("\\begin%s*{dmath}%s*(%b[])", process_options)
-- remove all other environemnts
dgroup = dgroup:gsub("\\begin%s*{.-}","")
dgroup = dgroup:gsub("\\end%s*{.-}", "\\\\")
return make_align(dgroup)
end)
end

-- process remaining dmath environments
local function process_dmath(s, env_name, new_env)
return s:gsub("\\begin%s*{" .. env_name .. "}(.-)\\end%s*{" .. env_name .. "}",
function(dmath)
-- options can be still here
local dmath = dmath:gsub("^%s*(%b[])", process_options)
return make_align(dmath, new_env)
end)
end

local process = filter {
-- find all dgroup* environments and convert them to align*
function(s)
local s = process_dgroup(s,"dgroup%*")
s = process_dgroup(s, "dgroup")
-- process remaining dmath environments in the document
s = process_dmath(s, "dmath%*", "\$\n%s\n\$")
s = process_dmath(s, "dmath", "\$$\n%s\n /extract_tex]") return s end } -- install filter to match HTML files Make:match("html?", process)  The change is that it now don't convert everything to the align* environment, but it follows breqn, so dmath -> equation, and dmath* -> \[ .... It also doesn't escape the equals sign.

This is a resulting HTML:

<!-- l. 10 --><p class='noindent'>$\text {Solve}\left [3 \log (x)-\log \left (y(x) \sin \left (\frac {y(x)}{x^2}\right )+x^2 \cos \left (\frac {y(x)}{x^2}\right )\right )=c_1,y(x)\right ]$
</p><!-- l. 14 --><p class='indent'>   $\text {Solve}\left [3 \log (x)-\log \left (y(x) \sin \left (\frac {y(x)}{x^2}\right )+x^2 \cos \left (\frac {y(x)}{x^2}\right )\right )=c_1,y(x)\right ]$
</p>


And rendered by MathJax:

• It is not a MathJax issue; the LaTeX is incorrect. An ampersand in that position leads to an error in LaTeX as well, since you can't use an ampersand for the outer align environment inside the \left...\right, as the \left...\right act as grouping braces, and the ampersand can't be inside a group. So it seems to be the tex4ht conversion that is at fault. Commented Apr 6, 2022 at 20:00
• Thanks Michale. I have to test your new version on my large file which has thousands of cases to see if it works OK and does not break anything. I will let you know. On the ampersand issue. Mathematica does not generate one from its Latex conversion as can be seen from above. That ampersand seems to have come from TeX4ht itself somehow. Commented Apr 6, 2022 at 20:09
• @DavideCervone thanks for clarification. I've added amperesands in the Lua filter, because I thought that they are useful for the alignment. But they do more harm than good, so I removed them from the updated filter in my answer. Commented Apr 6, 2022 at 21:20
• @Nasser the printing of the code was for debuggging, I've removed it from the updated answer. Commented Apr 7, 2022 at 8:13
• @Nasser I think it works now, at least for your example. try the updated code from this answer. Commented Sep 5, 2023 at 7:27