UseMathematics and MathJax #274

Closed
opened 2026-01-29 14:32:31 +00:00 by claunia · 6 comments
Owner

Originally created by @RickStrahl on GitHub (Feb 15, 2019).

I'm playing around with Markdig for using Math expressions and am using MathJax, which seems to be the most popular Web based Math library. A number of math equation creation tools all are using this library to power their previews.

The current MarkDig MathExtension works in parsing $$ and $ to <div class="math"> (and <span>) but this turns out to be a problem with MathJax as it expect HTML expressions to start with some sort of block statement.

You can check this out here:

https://www.mathjax.org/#demo

The following does not work:

Simplest thing possible:

<div class="math">
e=mc^2
</div>

but this does:

Simplest thing possible:

<div class="math">
\begin{equation}
e=mc^2
\end{equation}
</div>

MathJax seems pretty popular and is by far the easiest to integrate with easily, so this should probably be supported in some way - either as a separate extension or maybe with a flag on the UseMath() builder extension perhaps.

I played around with creating a custom extension based on the existing MathExtension and the following does seems to work well:

public class HtmlMathJaxBlockRenderer : HtmlObjectRenderer<MathBlock>
    {
        protected override void Write(HtmlRenderer renderer, MathBlock obj)
        {
            bool addBegin = false;
            var firstLine = obj.Lines.ToString();
            if (!firstLine.TrimStart().StartsWith("\\begin{"))
                addBegin = true;

            renderer.EnsureLine();
            renderer.Write("<div").WriteAttributes(obj).Write(">");

            if (addBegin)
                renderer.WriteLine("\\begin{equation}");
            renderer.WriteLeafRawLines(obj, true, true);
            if(addBegin)
                renderer.WriteLine("\n\\end{equation}");

            renderer.WriteLine("</div>");
        }
    }
    public class HtmlMathJaxInlineRenderer : HtmlObjectRenderer<MathInline>
    {
        protected override void Write(HtmlRenderer renderer, MathInline obj)
        {

            bool addBegin = false;
            var firstLine = obj.Content.Text;
            if (!firstLine.TrimStart().StartsWith("\\begin{"))
                addBegin = true;

            renderer.Write("<span").WriteAttributes(obj).Write(">");

            if (addBegin)
                renderer.WriteLine("\\begin{equation}");

            renderer.WriteEscape(ref obj.Content);

            if (addBegin)
                renderer.Write("\n\\end{equation}");

            renderer.Write("</span>");
        }
  } 
 public class MathJaxExtension : MathExtension, IMarkdownExtension
    {
        public void Setup(MarkdownPipelineBuilder pipeline)
        {
            // Adds the inline parser
            if (!pipeline.InlineParsers.Contains<MathInlineParser>())
            {
                pipeline.InlineParsers.Insert(0, new MathInlineParser());
            }

            // Adds the block parser
            if (!pipeline.BlockParsers.Contains<MathBlockParser>())
            {
                // Insert before EmphasisInlineParser to take precedence
                pipeline.BlockParsers.Insert(0, new MathBlockParser());
            }
        }

        public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
        {
            var htmlRenderer = renderer as HtmlRenderer;
            if (htmlRenderer != null)
            {
                if (!htmlRenderer.ObjectRenderers.Contains<HtmlMathJaxInlineRenderer>())
                {
                    htmlRenderer.ObjectRenderers.Insert(0, new HtmlMathJaxInlineRenderer());
                }
                if (!htmlRenderer.ObjectRenderers.Contains<HtmlMathJaxBlockRenderer>())
                {
                    htmlRenderer.ObjectRenderers.Insert(0, new HtmlMathJaxBlockRenderer());
                }
            }
        }
    }

Any thoughts on whether this makes sense to integrate in some way? My code is probably not efficient in the pipeline. Also I think if this sounds like a good idea it would also seem like a good idea to handle raw <div class="html"> blocks in the Markdown and perhaps auto-inject the the begin{} \end{} lines to make it more universal.

Originally created by @RickStrahl on GitHub (Feb 15, 2019). I'm playing around with Markdig for using Math expressions and am using[ MathJax](https://www.mathjax.org/), which seems to be the most popular Web based Math library. A number of math equation creation tools all are using this library to power their previews. The current MarkDig `MathExtension` works in parsing `$$` and `$` to `<div class="math">` (and `<span>`) but this turns out to be a problem with MathJax as it expect HTML expressions to start with some sort of block statement. You can check this out here: https://www.mathjax.org/#demo The following does not work: ```html Simplest thing possible: <div class="math"> e=mc^2 </div> ``` but this does: ```html Simplest thing possible: <div class="math"> \begin{equation} e=mc^2 \end{equation} </div> ``` MathJax seems pretty popular and is by far the easiest to integrate with easily, so this should probably be supported in some way - either as a separate extension or maybe with a flag on the `UseMath()` builder extension perhaps. I played around with creating a custom extension based on the existing `MathExtension` and the following does seems to work well: ```cs public class HtmlMathJaxBlockRenderer : HtmlObjectRenderer<MathBlock> { protected override void Write(HtmlRenderer renderer, MathBlock obj) { bool addBegin = false; var firstLine = obj.Lines.ToString(); if (!firstLine.TrimStart().StartsWith("\\begin{")) addBegin = true; renderer.EnsureLine(); renderer.Write("<div").WriteAttributes(obj).Write(">"); if (addBegin) renderer.WriteLine("\\begin{equation}"); renderer.WriteLeafRawLines(obj, true, true); if(addBegin) renderer.WriteLine("\n\\end{equation}"); renderer.WriteLine("</div>"); } } ``` ```cs public class HtmlMathJaxInlineRenderer : HtmlObjectRenderer<MathInline> { protected override void Write(HtmlRenderer renderer, MathInline obj) { bool addBegin = false; var firstLine = obj.Content.Text; if (!firstLine.TrimStart().StartsWith("\\begin{")) addBegin = true; renderer.Write("<span").WriteAttributes(obj).Write(">"); if (addBegin) renderer.WriteLine("\\begin{equation}"); renderer.WriteEscape(ref obj.Content); if (addBegin) renderer.Write("\n\\end{equation}"); renderer.Write("</span>"); } } ``` ```cs public class MathJaxExtension : MathExtension, IMarkdownExtension { public void Setup(MarkdownPipelineBuilder pipeline) { // Adds the inline parser if (!pipeline.InlineParsers.Contains<MathInlineParser>()) { pipeline.InlineParsers.Insert(0, new MathInlineParser()); } // Adds the block parser if (!pipeline.BlockParsers.Contains<MathBlockParser>()) { // Insert before EmphasisInlineParser to take precedence pipeline.BlockParsers.Insert(0, new MathBlockParser()); } } public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) { var htmlRenderer = renderer as HtmlRenderer; if (htmlRenderer != null) { if (!htmlRenderer.ObjectRenderers.Contains<HtmlMathJaxInlineRenderer>()) { htmlRenderer.ObjectRenderers.Insert(0, new HtmlMathJaxInlineRenderer()); } if (!htmlRenderer.ObjectRenderers.Contains<HtmlMathJaxBlockRenderer>()) { htmlRenderer.ObjectRenderers.Insert(0, new HtmlMathJaxBlockRenderer()); } } } } ``` Any thoughts on whether this makes sense to integrate in some way? My code is probably not efficient in the pipeline. Also I think if this sounds like a good idea it would also seem like a good idea to handle raw `<div class="html">` blocks in the Markdown and perhaps auto-inject the the `begin{}` `\end{}` lines to make it more universal.
Author
Owner

@xoofx commented on GitHub (Feb 15, 2019):

I would usually expect that it requires some JS side scripting to handle the particularities of these libraries... so not sure I would like to have this in Markdig... I don't know, I haven't dig into the integration with these math libraries. Katex which is also quite popular has a different way of handling this... both would require a different JS integration to make it working with markdig.

@xoofx commented on GitHub (Feb 15, 2019): I would usually expect that it requires some JS side scripting to handle the particularities of these libraries... so not sure I would like to have this in Markdig... I don't know, I haven't dig into the integration with these math libraries. [Katex](https://katex.org) which is also quite popular has a different way of handling this... both would require a different JS integration to make it working with markdig.
Author
Owner

@stevehurcombe commented on GitHub (Feb 15, 2019):

Personally speaking, if we wanted to support Math rendering then adding the JS wouldn't be an issue. Markdig's role is to generate the basic HTML framework from the markdown source...unless I'm missing something...what would the concern be? I wouldn't expect Markdig to output the JS.

@stevehurcombe commented on GitHub (Feb 15, 2019): Personally speaking, if we wanted to support Math rendering then adding the JS wouldn't be an issue. Markdig's role is to generate the basic HTML framework from the markdown source...unless I'm missing something...what would the concern be? I wouldn't expect Markdig to output the JS.
Author
Owner

@RickStrahl commented on GitHub (Feb 16, 2019):

@xoofx Yes no matter what some sort of client side library handling is required, but it would be nice you can pick up common examples and they just worked. I've been working of the samples from here:

https://en.wikibooks.org/wiki/LaTeX/Mathematics

and using MathJax those work with a few exceptions (probably due to some missing extensions that have to be configured).

Using KaTex almost none of those work.

I'm not sure what the solution is, but looking at this from an outsider's POV it looks to me like most tools output to MathJax so that's going to be the one that most people work with and it matches the wiki pages I referenced above as well.

@stevehurcombe There are several issues and for the most part Markdig already addresses the most important ones. Math expressions have to be fenced and left alone otherwise the equations are fixed up as Markdown text which breaks the equations. UseMathematics deals with this by fencing the block. As it is there is at least one extension that interferes with the Math functionality (customAttributes I believe) although I think order can fix that problem.

The main issue I bring up here is that each library treats the equations a little different. My point wasn't to including JS directly but supporting syntax that is most commonly used. Unfortunately after playing around with KaTex today I see that its base syntax for the equation structure is different than the other Math libraries I've looked at.

For MathJax specifically - it requires some sort of block expressions for math blocks so anything that doesn't have needs to add \begin{equation} and \end{equation}. Per the Latext Math wiki that's perfectly legal for any expression, but KaTex doesn't support that at all.

Please note I'm not trying to advocate one way or another here, just trying to get a feel what the most likely use cases will be. Like most of us here most likely, I'm just enabling the feature and not directly using it myself so most of this is probably blowing hot air :-) But it does seem to me that MathTex is the closest thing to official syntax and out of the box that doesn't work with UseMathematics().

FWIW, for now I've solved this by creating a custom extension that does what I need to by just making very basic changes to the existing MathExtension (just adding the structure lines if there are none). That works fine and I'm Ok with that. Just curious if there's a broader need to deal with this issue of different libraries for rendering.

@RickStrahl commented on GitHub (Feb 16, 2019): @xoofx Yes no matter what some sort of client side library handling is required, but it would be nice you can pick up common examples and they just worked. I've been working of the samples from here: https://en.wikibooks.org/wiki/LaTeX/Mathematics and using MathJax those work with a few exceptions (probably due to some missing extensions that have to be configured). Using KaTex almost none of those work. I'm not sure what the solution is, but looking at this from an outsider's POV it looks to me like most tools output to MathJax so that's going to be the one that most people work with and it matches the wiki pages I referenced above as well. @stevehurcombe There are several issues and for the most part Markdig already addresses the most important ones. Math expressions have to be fenced and left alone otherwise the equations are fixed up as Markdown text which breaks the equations. UseMathematics deals with this by fencing the block. As it is there is at least one extension that interferes with the Math functionality (customAttributes I believe) although I think order can fix that problem. The main issue I bring up here is that each library treats the equations a little different. My point wasn't to including JS directly but supporting syntax that is most commonly used. Unfortunately after playing around with KaTex today I see that its base syntax for the equation structure is different than the other Math libraries I've looked at. For MathJax specifically - it requires some sort of block expressions for math blocks so anything that doesn't have needs to add `\begin{equation}` and `\end{equation}`. Per the Latext Math wiki that's perfectly legal for any expression, but KaTex doesn't support that at all. Please note I'm not trying to advocate one way or another here, just trying to get a feel what the most likely use cases will be. Like most of us here most likely, I'm just enabling the feature and not directly using it myself so most of this is probably blowing hot air :-) But it does seem to me that MathTex is the closest thing to official syntax and out of the box that doesn't work with `UseMathematics()`. FWIW, for now I've solved this by creating a custom extension that does what I need to by just making very basic changes to the existing MathExtension (just adding the structure lines if there are none). That works fine and I'm Ok with that. Just curious if there's a broader need to deal with this issue of different libraries for rendering.
Author
Owner

@xoofx commented on GitHub (Feb 16, 2019):

Judging from babelmark, it seems that Pandoc and multimarkdown are both enclosing the Latex inline for example with \( and \) and looking at MathJax doc here it seems it is quite straightforward to work with that. For block (so $$....$$), it seems that it would be \[ and \]
I would be fine with to output this markers as it seems to match what others markdown engine are doing.
Would it be ok?

@xoofx commented on GitHub (Feb 16, 2019): Judging from [babelmark](https://babelmark.github.io/?text=This+is+a+function+%24\forall+x+\in+X%2C+\quad+\exists+y+\leq+\epsilon%24), it seems that Pandoc and multimarkdown are both enclosing the Latex inline for example with `\(` and `\)` and looking at [MathJax doc here](https://docs.mathjax.org/en/latest/start.html#tex-and-latex-input) it seems it is quite straightforward to work with that. For block (so `$$`....`$$`), it seems that it would be `\[` and `\]` I would be fine with to output this markers as it seems to match what others markdown engine are doing. Would it be ok?
Author
Owner

@RickStrahl commented on GitHub (Feb 22, 2019):

Yes I think that'll work.

I checked with my custom extension and changed the code to remove the \begin{equation} and used \( and \) like this:

 public class HtmlMathJaxBlockRenderer : HtmlObjectRenderer<MathBlock>
    {
        protected override void Write(HtmlRenderer renderer, MathBlock obj)
        {
            bool addBegin = false;
            var firstLine = obj.Lines.ToString();            
            addBegin = true;

            renderer.EnsureLine();
            renderer.Write("<div").WriteAttributes(obj).Write(">");

            if (addBegin)
                renderer.WriteLine("\\(");
            renderer.WriteLeafRawLines(obj, true, true);
            if(addBegin)
                renderer.WriteLine("\n\\)");

            renderer.WriteLine("</div>");
        }
    }

and that works to render all the expressions in my sample page I've been using for testing.

So I think if the MarkDig MathExtension adds those start tags then the custom extension would not be needed which would be awesome.

@RickStrahl commented on GitHub (Feb 22, 2019): Yes I think that'll work. I checked with my custom extension and changed the code to remove the `\begin{equation}` and used `\(` and `\)` like this: ```cs public class HtmlMathJaxBlockRenderer : HtmlObjectRenderer<MathBlock> { protected override void Write(HtmlRenderer renderer, MathBlock obj) { bool addBegin = false; var firstLine = obj.Lines.ToString(); addBegin = true; renderer.EnsureLine(); renderer.Write("<div").WriteAttributes(obj).Write(">"); if (addBegin) renderer.WriteLine("\\("); renderer.WriteLeafRawLines(obj, true, true); if(addBegin) renderer.WriteLine("\n\\)"); renderer.WriteLine("</div>"); } } ``` and that works to render all the expressions in my sample page I've been using for testing. So I think if the MarkDig MathExtension adds those start tags then the custom extension would not be needed which would be awesome.
Author
Owner

@RickStrahl commented on GitHub (Feb 26, 2019):

Thanks for making this happen so quickly. Moved 0.16 into Markdown Monster today and could dump my temporary custom extension and everything in my tests and samples still works which is awesome - much cleaner this way.

@RickStrahl commented on GitHub (Feb 26, 2019): Thanks for making this happen so quickly. Moved 0.16 into Markdown Monster today and could dump my temporary custom extension and everything in my tests and samples still works which is awesome - much cleaner this way.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/markdig#274