Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SVG output not showing in XHTML #58

Closed
netw0rkf10w opened this issue Jan 26, 2024 · 21 comments
Closed

SVG output not showing in XHTML #58

netw0rkf10w opened this issue Jan 26, 2024 · 21 comments
Labels
Accepted Issue has been reproduced by MathJax team

Comments

@netw0rkf10w
Copy link

I tried using direct/tex2svg-page to output XHTML and found that the images are not showing.

To ensure syntax correctness, I replaced the last line:

console.log(adaptor.outerHTML(adaptor.root(html.document)));

with

console.log(adaptor.serializeXML(adaptor.root(html.document)));

An example of the output SVG is as follows:

<mjx-container class="MathJax" jax="SVG">
        <svg style="vertical-align: 0;" xmlns="http://www.w3.org/2000/svg" width="2.653ex" height="1.932ex" role="img" focusable="false" viewBox="0 -853.7 1172.7 853.7" xmlns:xlink="http://www.w3.org/1999/xlink">
            <g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)">
                <g data-mml-node="math">
                    <g data-mml-node="msup">
                        <g data-mml-node="TeXAtom" data-mjx-texclass="ORD">
                            <g data-mml-node="TeXAtom" data-mjx-texclass="ORD">
                                <g data-mml-node="mi">
                                    <use data-c="211D" xlink:href="#MJX-TEX-D-211D"/>
                                </g>
                            </g>
                        </g>
                        <g data-mml-node="TeXAtom" transform="translate(755,363) scale(0.707)" data-mjx-texclass="ORD">
                            <g data-mml-node="mi">
                                <use data-c="1D451" xlink:href="#MJX-TEX-I-1D451"/>
                            </g>
                        </g>
                    </g>
                </g>
            </g>
        </svg>
        </mjx-container>

Could you please help? Thank you very much in advance!

@dpvc
Copy link
Member

dpvc commented Jan 27, 2024

I suspect that the xlink:href references aren't working properly in XHTML. Try adding fontCache: 'local' to the new SVG() options. If that doesn't work, try fontCache: 'none'. These href attribute are sometimes finicky.

@dpvc dpvc added the Accepted Issue has been reproduced by MathJax team label Jan 27, 2024
@dpvc
Copy link
Member

dpvc commented Jan 27, 2024

Actually, you don't need to set it in the file, you can use the ---fontCache option to specify which to use. So try --fontCache local and see if that works.

@netw0rkf10w
Copy link
Author

@dpvc Indeed, I also had realized that disabling the global caching makes it work but I forgot to mention this in the question. The issue without global cache is that it makes the file size significantly larger (about 5 times for one of my use cases).

I think I found the issue: https://stackoverflow.com/a/77891891/2131200. It looks like adaptor.serializeXML "forgot" to add the namespace to some of the SVGs. Can this be considered a bug?

@dpvc
Copy link
Member

dpvc commented Jan 28, 2024

Thanks for the link to stackoverflow that points out the issue. Yes, this is a bug, and I have made a PR to fix it. In the meantime, you can add

Object.assign(svg, {
  _pageElements: svg.pageElements,
  pageElements(html) {
    const cache = this._pageElements(html);
    if (cache) {
      this.adaptor.setAttribute(cache, 'xmlns', 'http://www.w3.org/2000/svg');
    }
    return cache;
  }
});

to the tex2svg-page script just after the line

const html = mathjax.document(htmlfile, {InputJax: tex, OutputJax: svg});

to patch the SVG output.

@netw0rkf10w
Copy link
Author

@dpvc Works like a charm. Thank you very much!

There are still two issues with adaptor.serializeXML:

  1. It doesn't add the namespace to <html>.
  2. As MathJax v4 stores the raw LaTeX data in the SVG (e.g., data-latex="a < b + c"), when there are > or < in the formulas, then the output XML has syntax errors: Unescaped '<' not allowed in attributes values.

For your convenience, here's an HTML for testing:

<html>
 <head>
  <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
  <title>Test</title>
 </head>
 <body>
    <span>This is some text followed by an equation: $a < b + c$.</span>
 </body>
</html>

dpvc added a commit to mathjax/MathJax-src that referenced this issue Jan 28, 2024
@dpvc
Copy link
Member

dpvc commented Jan 28, 2024

OK, I've made a PR to resolve these two issues. Here is a patch that you can use for now:

First, add

import {LiteParser} from 'mathjax-full/js/adaptors/lite/Parser.js';

to the imports section. Then add

Object.assign(LiteParser.prototype, {
  serialize(adaptor, node, xml = false) {
    const SELF_CLOSING = this.constructor.SELF_CLOSING;
    const tag = adaptor.kind(node);
    const attributes = this.allAttributes(adaptor, node, xml).map(
      (x) => x.name + '="' + this.protectAttribute(x.value, xml) + '"'
    ).join(' ');
    const content = this.serializeInner(adaptor, node, xml);
    const html =
      '<' + tag + (attributes ? ' ' + attributes : '')
          + ((!xml || content) && !SELF_CLOSING[tag] ? `>${content}</${tag}>` : xml ? '/>' : '>');
    return html;
  },

  allAttributes(adaptor, node, xml) {
    let attributes = adaptor.allAttributes(node);
    const kind = adaptor.kind(node);
    if (!xml || (kind !== 'svg' && kind !== 'math' && kind !== 'html')) {
      return attributes;
    }
    for (const {name} of attributes) {
      if (name === 'xmlns') {
        return attributes;
      }
    }
    attributes.push({
      name: 'xmlns',
      value: ({
        svg:  'http://www.w3.org/2000/svg',
        math: 'http://www.w3.org/1998/Math/MathML',
        html: 'http://www.w3.org/1999/xhtml'
      })[kind]
    });
    return attributes;
  },

  _protectAttribute: LiteParser.prototype.protectAttribute,
  protectAttribute(text, xml) {
    text = this._protectAttribute(text, xml);
    if (xml) {
      text = text.replace(/</g, '&lt;').replace(/>/g, '&gt;');
    }
    return text;
  }
});

instantiating the LiteAdaptor (const adaptor = liteAdaptor({fontSize: argv.em});).

This is a bit long, but the allAttributes method is new, and there was no easy way to call the original serialize()` method and still make the needed fixes.

@dpvc
Copy link
Member

dpvc commented Jan 28, 2024

PS, it also adds missing xmlns attribute to svg nodes, so the previous patch should no longer be needed if you use this one, though it is OK to use both.

@netw0rkf10w
Copy link
Author

@dpvc It worked! Thanks a lot!
On some files though I obtained Error: Cannot find module 'mathjax-modern-font/js/output/fonts/mathjax-modern/svg/dynamic/calligraphic', but this is probably an unrelated issue. I do not see any js folder in my mathjax-modern-font (4.0.0-beta.4), is this normal? Thanks.

@dpvc
Copy link
Member

dpvc commented Jan 29, 2024

Is this with [email protected]? It looks like a reference from an earlier version. But it might be that there is a path that hasn't been updated somewhere in the font configuration code in beta.4. In any case, if you are at beta.4 with mathjax-full, there is a configuration parameter that can be set for the path to the dynamic directory.

@netw0rkf10w
Copy link
Author

Yes that was with [email protected]. Here's my package.json:

{
  "name": "MathJax-demos-node",
  "version": "4.0.0",
  "description": "Demos using MathJax v4 in node",
  "dependencies": {
    "esm": "^3.2.25",
    "mathjax-full": "4.0.0-beta.4",
    "mathjax-modern-font": "^4.0.0-beta.4",
    "yargs": "^17.7.2"
  },
  "devDependencies": {
    "@babel/core": "^7.14.6",
    "@babel/preset-env": "^7.14.5",
    "babel-loader": "^8.2.2",
    "terser-webpack-plugin": "5.3.0",
    "webpack": "5.88.2",
    "webpack-cli": "^5.1.1"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/mathjax/MathJax-demos-node/"
  },
  "keywords": [
    "MathJax",
    "examples",
    "nodejs"
  ],
  "license": "Apache-2.0"
}

I tried removing node_modules and npm install again but that didn't help.

@dpvc
Copy link
Member

dpvc commented Jan 29, 2024

Can you provide your MathJax configuration? I'm not able to find a reference that should lead to that URL, so I'm wondering if there is something in the configuration leading to that.

Also, can you give the traceback from the error message?

@netw0rkf10w
Copy link
Author

@dpvc As you suggested earlier, manually setting the path to the dynamic directory did help:

const svg = new SVG({
    fontCache: argv.fontCache,
    exFactor: argv.ex / argv.em,
    dynamicPrefix: 'mathjax-modern-font/cjs/svg/dynamic'
  });

Thanks!

Removing the line dynamicPrefix: ..., I obtained the following traceback:

Error: Cannot find module '/Users/name/projects/epubs/mathjax/v4/node_modules/mathjax-full/cjs/output/svg/fonts/svg/dynamic/calligraphic'
Require stack:
- /Users/name/projects/epubs/mathjax/v4/node_modules/mathjax-full/cjs/util/asyncLoad/node.js
- /Users/name/projects/epubs/mathjax/v4/direct/tex2svg-page-epub
    at Module._resolveFilename (node:internal/modules/cjs/loader:1149:15)
    at Module._load (node:internal/modules/cjs/loader:990:27)
    at Module.require (node:internal/modules/cjs/loader:1237:19)
    at require (node:internal/modules/helpers:176:18)
    at mathjax_js_1.mathjax.asyncLoad (/Users/name/projects/epubs/mathjax/v4/node_modules/mathjax-full/cjs/util/asyncLoad/node.js:31:16)
    at /Users/name/projects/epubs/mathjax/v4/node_modules/mathjax-full/cjs/util/AsyncLoad.js:10:43
    at new Promise (<anonymous>)
    at asyncLoad (/Users/name/projects/epubs/mathjax/v4/node_modules/mathjax-full/cjs/util/AsyncLoad.js:9:12)
    at MathJaxModernFont.<anonymous> (/Users/name/projects/epubs/mathjax/v4/node_modules/mathjax-full/cjs/output/common/FontData.js:477:68)
    at step (/Users/name/projects/epubs/mathjax/v4/node_modules/mathjax-full/cjs/output/common/FontData.js:44:23) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [
    '/Users/name/projects/epubs/mathjax/v4/node_modules/mathjax-full/cjs/util/asyncLoad/node.js',
    '/Users/name/projects/epubs/mathjax/v4/direct/tex2svg-page-epub'
  ]
}

@netw0rkf10w
Copy link
Author

Sorry please hold on, I think there is some issue with the header of my script, all the import paths start with 'mathjax-full/js/...', which doesn't seem to be good. I'm checking again...

@netw0rkf10w
Copy link
Author

Hmm... Changing all 'mathjax-full/js/...'to 'mathjax-full/cjs/...' didn't make a difference, so the above traceback was good.
What I don't understand is that my script even worked with 'mathjax-full/js/...', which isn't the correct path...

@dpvc
Copy link
Member

dpvc commented Jan 29, 2024

The MathJax package.json is set up to map js to cjs when you use require() to load it, and to mjs when you use import. That way you don't have to worry about the cjs/mjs difference.

Thanks for the stack trace. The dynamicPrefix was the setting I was going to suggest as well. I have things worked out pretty well for use with components, but haven't done as much checking with the direct modules. The default dynamicPrefix probably needs to be adjusted. I'll put it on the list of things to do.

@dpvc
Copy link
Member

dpvc commented Jan 29, 2024

PS, I notice that the path in the error in your message here is different than the one you gave earlier. Did you change anything else that might account for that?

@netw0rkf10w
Copy link
Author

PS, I notice that the path in the error in your message here is different than the one you gave earlier. Did you change anything else that might account for that?

Sorry, the path in that previous message was because I set dynamicPrefix to that wrong path without realizing it (I had copied the code from one of your post in this repo, without changing the path). The last traceback was obtained when there is no dynamicPrefix specified.

@dpvc
Copy link
Member

dpvc commented Jan 29, 2024

Was that other path an alpha.1 path? Just curious, as that is would correspond to the wrong path in the error message.

@netw0rkf10w
Copy link
Author

I don't know which version exactly but definitely from a previous version. It took it from your answer here: #55 (comment)
Thank you!

@dpvc
Copy link
Member

dpvc commented Jan 29, 2024

OK, that explains where the js/output/fonts path was coming from, which was what was confusing me. The directory structure in the beta versions have changed, and that URL is no longer the correct one. Sorry for the confusion there; it was correct at the time the comment was made, but is not correct for beta.4. Your current path is the correct one for that.

@netw0rkf10w
Copy link
Author

Thanks for the clarification! And sorry for the confusion as well, I should have specified that I had copied your code :p

dpvc added a commit to mathjax/MathJax-src that referenced this issue Feb 1, 2024
dpvc added a commit to mathjax/MathJax-src that referenced this issue Feb 6, 2024
@dpvc dpvc closed this as completed Oct 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Accepted Issue has been reproduced by MathJax team
Projects
None yet
Development

No branches or pull requests

2 participants