# Error using \ifdefined\HCode with TeX4ht, but it works in LuaTeX

I now use the breqn package in LuaTeX mode only. Not in TeX4ht mode since it does not work there and is not needed. So I load breqn only when not in TeX4ht mode and use \ifdefined\HCode to make sure it is used only when compiling to PDF and not HTML.

I found some issue where TeX4ht still sees {dmath*} and dgroup which are commands from breqn, even though I made sure by using \ifdefined\HCode these only can be seen when compiling to PDF.

Compiling to PDF doesn't show any error at all. But compiling to HTML gives an error.

Here is an MWE:

\documentclass[12pt]{book}
\usepackage{amsmath}
\ifdefined\HCode %only load breaqn when not in TeX4ht mode
\else
\usepackage{breqn}
\fi

\begin{document}
\ifdefined\HCode
\begin{align*}
\else
\begin{dgroup*}
\fi

\ifdefined\HCode
A &= B
\else
\begin{dmath*}
A = B
\end{dmath*}     %error is around here
\fi

\ifdefined\HCode
\end{align*}
\else
\end{dgroup*}
\fi
\end{document}


Compiling using lualatex foo2.tex doesn't give any error and correct output.

But

make4ht -ulm default -a debug foo2.tex "mathjax,htm"


gives

(/usr/local/texlive/2021/texmf-dist/tex/generic/tex4ht/html5.4ht)) (./foo2.aux)
(/usr/local/texlive/2021/texmf-dist/tex/latex/base/ts1cmr.fd)

! LaTeX Error: Environment dgroup* undefined.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return>  for immediate help.
...

l.31 \end{dmath*}


I had to do it this way. It is a long story, but my question is: Why does the above work correctly with LuaTeX, but not with TeX4ht? Am I doing something wrong? If yes, then how come LuaTeX is happy with it, but not TeX4ht? Is there a way to make tex4ht compile the above?

I am using TeX Live (TL) 2021, and it was updated about two weeks ago. On Linux.

Update oct 25, 2021

I found an issue with proposed solution. It does not work with multiple dmath* inside dgroup*. It only works with one dmath* inside dgroup*. But if I have one dmath*, I really do not need dgroup*.

Since hard to show all this in a comment to the answer, I show it here.

Here is MWE

\documentclass[12pt]{book}
\usepackage{amsmath}
\usepackage{breqn}
\begin{document}

\begin{dgroup*}
\begin{dmath*}
A = B
\end{dmath*}
\begin{dmath*}
C = D
\end{dmath*}
\end{dgroup*}
\end{document}


Using same .cfg file shown in the answer, and compiled using

make4ht -ulm default -a debug -c ./new.cfg test_breqn.tex "mathjax,htm"


gives this HTML

The second dmath* needs to be on new line. The HTML is

<!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' />
<meta content='test_breqn.tex' name='src' />
<script>window.MathJax = { tex: { tags: "ams", environments: { "dgroup*": ["", ""], "dmath*": ["", ""], }  } }</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. 13 --><p class='noindent'>\begin {dgroup*} \begin {dmath*} A = B \end {dmath*} \begin {dmath*} C = D \end {dmath*} \end {dgroup*}
</p>

</body>
</html>


I hope there is a way around this.

• \ifdefined\HCode \begin{align*} \else \begin{dgroup*} \fi can't really work, you need the condition around the entire environment Oct 24 at 22:01

Edit:

You can also use make4ht filters to edit the math content in the HTML file to get something that MathJax supports. For example, the following build file converts dgroup* environment to align*, and it also changes the math content to suitable form:

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)


It produces a following HTML code:

<p class='noindent'>test 1, OK
</p><!-- l. 15 --><p class='indent'>   \begin{align*}  A &amp;= B \\  C &amp;= D \\ \end{align*}
</p><!-- l. 17 --><p class='indent'>   test 2 FAIL
</p><!-- l. 26 --><p class='indent'>   \begin{align*}  A &amp;= B \\ \tag{1} C &amp;= D \\ \end{align*}
</p><!-- l. 29 --><p class='indent'>   test 3 OK
</p><!-- l. 33 --><p class='indent'>   \begin{align*} A &amp;= B \end{align*}
</p><!-- l. 35 --><p class='indent'>   test 4 FAIL
</p><!-- l. 39 --><p class='indent'>   \begin{align*}\tag{1} A &amp;= B \end{align*}
</p><!-- l. 41 --><p class='indent'>   test 5 OK
</p><!-- l. 45 --><p class='indent'>   \begin{align*} A &amp;= B \end{align*}
</p>


Which is displayed by MathJax like this:

Alternatively, you can add support for your math environments to MathJax, using this config file:

\Preamble{xhtml}
\Configure{MathJaxConfig}{{
tex: {
tags: "ams",
\detokenize{%
environments: {
"dgroup*": ["", ""],
"dmath*": ["", ""],
}
}
}
}}
\VerbMath{dgroup*}
\begin{document}
\EndPreamble


The \VerbMath{dgroup*} command ensures that TeX4ht passes the whole content of the dgroup* environment to the HTML file. You then need to configure MathJax to support both dgroup* and dmath* environments. This is thanks to:

  environments: {
"dgroup*": ["", ""],
"dmath*": ["", ""],
}


I've found that you don't need to provide any other code, MathJax just switches to the display math mode and prints the content.

Your TeX file can be then simplified:

\documentclass[12pt]{book}
\usepackage{amsmath}
\usepackage{breqn}

\begin{document}
\begin{dgroup*}
\begin{dmath*}
A = B
\end{dmath*}     %error is around here
\end{dgroup*}
\end{document}


And this is the result:

• @Nasser should give you the tick:-) Oct 25 at 10:06
• Thanks! This is much easier so I do not have to duplicate code. I will test it more on my main file. Oct 25 at 11:43
• @Nasser yes, it is easier. you should try to avoid the \ifdefined\HCode in your documents, try to add support for unsupported commands instead :) Oct 25 at 12:06
• hi Michal. I found a problem. Please see updated answer. I hope there is a magic code you have that can fix this, otherwise, it will not really work. I still need to test dmath and dgroup with [number=label] which give labels to equations in breqn. But one thing at a time :) Oct 25 at 13:05
• @Nasser you can post-process the HTML file using make4ht filters and fix some things here. I've updated my answer to show this. Oct 25 at 20:35

If \Hcode is defined then \begin{align*} will be executed and as a first action this will grab all the code up to \end{align*}. This body is executed multiple times, once with a measuring pass and then a main typesetting pass. The body collected will be

\else
\begin{dgroup*}
\fi

\ifdefined\HCode
A &= B
\else
\begin{dmath*}
A = B
\end{dmath*}     %error is around here
\fi

\ifdefined\HCode


and the if conditionals will be unmatched. You need the conditional to be around the entire environment.