Anki doesn't render underscore correctly in LaTeX

I have created one card with this LaTeX code: $(x_n){n=1}^{+\infty}$ e $(y_n){n=1}^{+\infty}$ and instead of rendering what is should be rendering:
image, it is rendering the following:
image

I have been an hour trying to solve this and I can only say that the problem is related with the underscore, because Anki is interpreting it as italics. I need help because all my studies relate on this app and it’s anoying seeing that.

The forum software here is also interpreting underscores as italics. To avoid confusion, when posting your LaTeX code here, you should put it between ` marks (backticks) at each end.

For example:
\(x\)_{n=1}^{+\infty} e \(y_n\)_{n=1}^{+\infty}

I’m not really familiar with LaTeX, but just in case, can you try checking for syntax errors in your LaTeX code, perhaps with some online LaTeX editor webpage like this one

Have you used some note types with markdown support such as Anki-KaTeX-Markdown? It seems to be like the issue of markdown since markdown renders _..._ to <em>...</em> rather than the issue of LaTeX.

If have and if you use markdown-it. I have an approach to solve.

  1. If you don’t need emphasis syntax, use .disable('emphasis')
  2. If you would like to keep the italic/emphasis syntax using *...*, follow steps below(maybe has side-effect, please use with caution!)

Firstly, clone the markdown-it repo.

git clone https://github.com/markdown-it/markdown-it.git

Then modify lib/rules_inline/emphasis.mjs.

vim lib/rules_inline/emphasis.mjs

Find the code below and replace it with the following code.

if (marker !== 0x5F /* _ */ && marker !== 0x2A /* * */) { return false }
if (marker !== 0x2A /* * */) { return false }

Execute npm i to install packages in need.(This requires Node.js installed)

Then install rollup.

npm install --global rollup

Finally, execute the commands below and you will find markdown-it.min.js in dist directory. Then you can replace the original markdown-it.min.js with this version equipped with non-underscore-italic patch.

rollup --config support/rollup.config.mjs

If you don’t want to do so many things, you could ask me to upload my markdown-it.min.js(v14.0.0).

Reference:

  1. markdown-it/markdown-it issue: Option to disable underscore as a italic marker
  2. Rollup documentation: Installation
  3. Rollup documentation: Using Config Files

Thank you for your answer!

I use KaTeX inside of Anki, is your solution still valid in KaTeX?

Absolutely! I’m using KaTeX and markdown based on the templates enhanced from Anki-KaTeX-Markdown.

Here’s my template with 5 fields: “问题”, “答案”, “笔记”, “相关知识” and “标签”.

Front side.

{{#标签}}
<div>
    <img src="_anki.png" height="32" align="left" />
    <img src="_space.png" width="10" align="left" />
</div>

<div class="reason">
    {{标签}}
</div>

<br />
{{/标签}}

<div class="border1">
    <div class="h1 xcolor xleft">
        <span class="ximg"><img src="_space.png" height="24" width="36" /></span>
        问题
    </div>

    <div class="h2 xleft" id="front"><pre>{{问题}}</pre></div>
</div>

<div class="transparent_deck">
    {{Deck}}
</div>

<script src="_Markdown-KaTeX.js"></script>

Back side.

{{#标签}}
<div>
    <img src="_anki.png" height="32" align="left" />
    <img src="_space.png" width="10" align="left" />
</div>

<div class="reason">
    {{标签}}
</div>

<br />
{{/标签}}

<div class="border1">
    <div class="h1 xcolor xleft">
        <span class="ximg"><img src="_space.png" height="24" width="36" /></span>
        问题
    </div>

    <div class="h2 xleft" id="front"><pre>{{问题}}</pre></div>

    {{#答案}}
    <div class="h2 xleft"><hr style="width: 98%; " /></div>

    <div class="h2 xleft" id="back"><pre>{{答案}}</pre></div>
    {{/答案}}
</div>

{{#笔记}}
<br />

<div class="border2">
    <div class="h1 ycolor yleft">
        <span class="yimg"><img src="_space.png" height="24" width="36" /></span>
        笔记
    </div>

    <div class="h2 yleft" id="extra1"><pre>{{笔记}}</pre></div>
</div>
{{/笔记}}

{{#相关知识}}
<br />

<div class="border3">
    <div class="h1 zcolor zleft">
        <span class="zimg"><img src="_space.png" height="24" width="36" /></span>
        相关知识
    </div>

    <div class="h2 zleft" id="extra2"><pre>{{相关知识}}</pre></div>
</div>
{{/相关知识}}

<div class="transparent_deck">{{Deck}}</div>

<script src="_Markdown-KaTeX.js"></script>

_Markdown-KaTeX.js

var getResources = [
    getCSS("_katex.css", "https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/katex.min.css"),
    getScript("_katex.js", "https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/katex.min.js"),
    getScript("_markdown-it.min.js", "https://cdn.jsdelivr.net/npm/markdown-it@13.0.1/dist/markdown-it.min.js"),
    getScript("_markdown-it-mark.js", "https://github.com/markdown-it/markdown-it-mark/blob/master/dist/markdown-it-mark.js"),
    getScript("_auto-render.js", "https://cdn.jsdelivr.net/gh/Jwrede/Anki-KaTeX-Markdown/auto-render-cdn.js"),
    // getCSS("_highlight.css", "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/styles/atom-one-dark-reasonable.min.css"),
    // getScript("_highlight.js", "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/highlight.min.js"),
];

Promise.all(getResources).then(() => getScript("_mhchem.js", "https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/contrib/mhchem.min.js")).then(render).catch(show);

function getScript(path, altURL) {
    return new Promise((resolve, reject) => {
        let script = document.createElement("script");
        script.onload = resolve;
        script.onerror = function() {
            let script_online = document.createElement("script");
            script_online.onload = resolve;
            script_online.onerror = reject;
            script_online.src = altURL;
            document.head.appendChild(script_online);
        }
        script.src = path;
        document.head.appendChild(script);
    })
}

function getCSS(path, altURL) {
    return new Promise((resolve, reject) => {
        var css = document.createElement('link');
        css.setAttribute('rel', 'stylesheet');
        css.type = 'text/css';
        css.onload = resolve;
        css.onerror = function() {
            var css_online = document.createElement('link');
            css_online.setAttribute('rel', 'stylesheet');
            css_online.type = 'text/css';
            css_online.onload = resolve;
            css.onerror = reject;
            css_online.href = altURL;
            document.head.appendChild(css_online);
        }
        css.href = path;
        document.head.appendChild(css);
    });
}

function render() {
    try {renderField("front")}  catch {}
    try {renderField("back")}   catch {}
    try {renderField("extra")}  catch {}
    try {renderField("extra1")} catch {}
    try {renderField("extra2")} catch {}
    show();
}

function renderField(field) {
    renderMath(field);
    markdown(field);
}

function show() {
    try {document.getElementById("front").style.visibility  = "visible"} catch {}
    try {document.getElementById("back").style.visibility   = "visible"} catch {}
    try {document.getElementById("extra").style.visibility  = "visible"} catch {}
    try {document.getElementById("extra1").style.visibility = "visible"} catch {}
    try {document.getElementById("extra2").style.visibility = "visible"} catch {}
}

function renderMath(ID) {
    let text = document.getElementById(ID).innerHTML;
    text = replaceInString(text);
    document.getElementById(ID).textContent = text;
    renderMathInElement(document.getElementById(ID), {
        delimiters:  [
            {left: "$$", right: "$$", display: true},
            {left: "\\[", right: "\\]", display: true},
            {left: "$", right: "$", display: false},
            {left: "\\(", right: "\\)", display: false},
        ],
        trust: true,
        macros: {
// omitted
        },
        throwOnError: false
    });
}

function markdown(ID) {
    // let md = new markdownit({typographer: true, html: true, highlight: function (str, lang) {
    //     if (lang && hljs.getLanguage(lang)) {
    //         try {
    //             return hljs.highlight(str, { language: lang }).value;
    //         } catch (__) {}
    //     }
    //     return ''; // use external default escaping
    // }}).use(markdownItMark);
    let md = new markdownit({typographer: true, html: true}).use(markdownItMark);
    let text = replaceHTMLElementsInString(document.getElementById(ID).innerHTML);
    text = md.render(text);
    document.getElementById(ID).innerHTML = text.replace(/&lt;\/span&gt;/gi, "\\");
}
function replaceInString(str) {
    str = str.replace(/<[\/]?pre[^>]*>/gi, "");
    str = str.replace(/<br\s*[\/]?[^>]*>/gi, "\n");
    str = str.replace(/<div[^>]*>/gi, "\n");
    str = str.replace(/<span class="cloze" data-cloze=".*?" data-ordinal="\d+">|<[\/]?span[^>]*>/gi, "")
    str = str.replace(/<\/div[^>]*>/gi, "\n");
    str = str.replace(/&nbsp;/gi, " ");
    return replaceHTMLElementsInString(str);
}

function replaceHTMLElementsInString(str) {
    str = str.replace(/&tab;/gi, "  ");
    str = str.replace(/&gt;/gi, ">");
    str = str.replace(/&lt;/gi, "<");
    return str.replace(/&amp;/gi, "&");
}

And it works well in my circumstance.

Example:

若 $\bm{A} = \begin{bmatrix} \bm{A}_{11} & \bm{A}_{12} & \cdots & \bm{A}_{1s} \\ \bm{A}_{21} & \bm{A}_{22} & \cdots & \bm{A}_{2s} \\ \vdots & \vdots & \ddots & \vdots \\ \bm{A}_{r1} & \bm{A}_{r2} & \cdots & \bm{A}_{rs} \\ \end{bmatrix}$,则 $\bm{A}^{\mathrm{T}} = \begin{bmatrix} \bm{A}_{11}^{\mathrm{T}} & \bm{A}_{21}^{\mathrm{T}} & \cdots & \bm{A}_{r1}^{\mathrm{T}} \\ \bm{A}_{12}^{\mathrm{T}} & \bm{A}_{22}^{\mathrm{T}} & \cdots & \bm{A}_{r2}^{\mathrm{T}} \\ \vdots & \vdots & \ddots & \vdots \\ \bm{A}_{1s}^{\mathrm{T}} & \bm{A}_{2s}^{\mathrm{T}} & \cdots & \bm{A}_{rs}^{\mathrm{T}} \\ \end{bmatrix}$。

That’s great! However, once I’ve tried to solve it the way you told me to, I don’t know where I’m supposed to put the .disable('emphasis'). Furthermore, the code you wrote about keeping the italic using *...*, I don’t think I can use it because I’m using this anki complement: 1087328706 which led me to this github: Anki-KaTeX-Markdown , and the file where I’m supposed to replace the the code is different. Could you give me more specific support?

Thank you so much!

I currently use the templates modified from this addon so I think you can use it too. Just replace _markdown-it.min.js in collection.media.

You can find my _markdown-it.min.js(v14.0.0) in markdown-it.min.js without underscore syntax (github.com).

For .disable('emphasis'), you can add this after the line below in the definition of the markdown() function.

let md = new markdownit({...}).use(markdownItMark);

turning it into

let md = new markdownit({...}).use(markdownItMark).disable('emphasis');

Done. Now it works perfectly. Thank you so much!

Glad to hear that :smiley:

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.