UseMathematics and MathJax #272

Open
opened 2026-01-29 14:32:28 +00:00 by claunia · 0 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.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/markdig#272