mathjax 3 + xyJax + fabric.js (SCG only) _ migration to mathax 4

57 views
Skip to first unread message

jox joe

unread,
Jan 5, 2026, 9:28:44 AMJan 5
to MathJax Development
Hello Davide,

001: ************
As a result of your support, I can use the following packages together:
1/ mathjax 3
2/ xyJax (mathjax 3 extension)
3/ fabric.js
The mathjax3 SVG output is used which is further processed by fabric.js.
I can modify and recompile the source code of  mathjax 3 and  xyJax 3.  

002: ************ 
I'd like to do the same but with MathJax 4.1.0.
I can compile  xyJax 3 and mathjax 4.1.0 without an error message. 
If I include both in the HTML based on the instruction of  xyJax 3 
I get the following error message:
Unknow environment "xy" 

003: *************
My request:
Please give me a hint how to modify the  xyJax 3 source to meet
the requirement of MathJax 4.
I am studying the  xyJax 3 source and the MathJax 4 document now.
Thank you.



dp...@mathjax.org

unread,
Jan 5, 2026, 3:13:36 PMJan 5
to mathj...@googlegroups.com
I can compile  xyJax 3 and mathjax 4.1.0 without an error message. 
If I include both in the HTML based on the instruction of  xyJax 3 
I get the following error message:
Unknow environment "xy" 

If you look in the developer's console, you will probably see additional errors where xypic.js is failing.

Please give me a hint how to modify the  xyJax 3 source to meet
the requirement of MathJax 4.
I am studying the  xyJax 3 source and the MathJax 4 document now.

I suspect the main issue is the change from SymbolMap to TokenMap and Symbol to Token.

But it looks like someone has already done the work recently:


Perhaps that will work for you.

Davide


jox joe

unread,
Jan 10, 2026, 12:04:04 PMJan 10
to MathJax Development
Hi Davide,
thank you for your feedback.

001******************
**********************
Mathjax3+img+xyJax works properly

002******************
**********************
Mathjax4+img+xyJax works properly if I use a browser (Firefox, Chrome) 

But the following code is not working: 

////BEGIN code
global.MathJax = {
  options: {}
  ,
  tex: {
    packages: {
      '[+]': ['xypic', 'img']
    }
    ,
    processEnvironments: true
    ,
    maxBuffer: 200 * 1024
    ,
    maxMacros: 40000
    ,
    noundefined: {
      color: 'red',
      background: '',
      size: ''
    }//END , noundefined:
    ,

    formatError(_, error) {
      throw error;
    }
    ,
    macros: {
      //
      RR: ['{\\bf{R}}']
      ,
      bold: ['\\boldsymbol{#1}', 1]  
      ,
      ddx: ['\\frac{d#2}{d#1}', 2, 'x']
      ,
      abc: ['(#1)', 1, [null, '\\cba']]
      ,
      ud: ['{#1^ \\dagger}', 1]
      ,
      Tr: ['{\\mathrm{Tr}}']
      ,
      bold2: ['{\\bf #1}', 1]
      ,
      c1y: ['{\\color{yellow}{#1}}']
      ,
      cy: ['{\\color{yellow}#1}']
      ,
      ktc: ['{\\color{yellow}{\\boldsymbol{\\mathsf{#1}}}}', 1]
      ,
      b: ['{\\boldsymbol{\\mathsf{#1}}}', 1]
     
    } //END , macros:

  } //END , tex:
  ,
  svg: {
    fontCache: 'local'
  }

}; //END MathJax =


var init_js_1 = require("/mathjax/components/cjs/startup/init.js");
var loader_js_1 = require("/mathjax/components/cjs/loader/loader.js");
require("/mathjax/components/cjs/core/core.js");
require("/mathjax/components/cjs/input/tex/tex.js");
var svg_js_1 = require("/mathjax/components/cjs/output/svg/svg.js");
require('/mathjax/cjs/adaptors/browserAdaptor.js');
require('/mathjax/cjs/ui/menu/Menu.js');
require('/mathjax4/build/xypic.js');
require('/mathjax4/mathjax-img-main/browser/img.js');
loader_js_1.Loader.preLoaded('init', 'loader', 'startup', 'core', 'input/tex', 'output/svg', 'img', 'xypic');
loader_js_1.Loader.saveVersion('tex-svg');
(0, svg_js_1.loadFont)(init_js_1.startup, true);

//this creates the svg
function _tex2svg(tex, callback, _errCallback) {
  MathJax.tex2svgPromise(tex, {
    display: false,
    em: 16 // em-size in pixels
    ,
    ex: 8 // ex-size in pixels
    ,
    containerWidth: 80 * 16
  }).then(function (node) {
    callback(node); //  ORIGINAL callback(node.firstElementChild);
    //callback(node.firstElementChild);  
  }).catch(function (err) {
    _errCallback(err.message);
  });
}
////END code

My workflow:
- the MathJax generates the SVG 
- this will be processed by fabric.js
   and passed to the browser

Please let me how to set up Mathjax 4 properly
for this use case.
I use the CJS version because the browserify bundler is used.
Later I can try and convert es6 to es5 (babel+ browserify ).

Thank you.

Davide Cervone

unread,
Jan 10, 2026, 4:45:42 PMJan 10
to mathj...@googlegroups.com
"the following code is not working" is not very specific about what is happening for you.  Are there error messages?  Is SVG output produced, but doesn't get displayed properly?  Is it a problem with specific expressions, or all expressions?

If I understand properly, this will be run int he browser, right?  (Not in node on a server.)

I haven't tried to run this code without knowing the details I ask about above, but I do see one important configuration issue.  Because you are packaging your own copy of MathJax, you will need to tell MathJax where to find its other files (like font data, tex extensions, etc), so you will need to add something like

loader: {
  paths: {
    mathjax: '/mathjax',
  }
},

to your configuration (you may need to give the full path with https://... for this).

Because MathJax v4 has a choice of multiple fonts, it is not packaged with one itself.  So if you are hosting your own copy of MathJax, you will likely want to have your own copy of the font you are using (the default is `mathjax-newcm`);  If you used npm or pnpm to install your copy of MathJax v4, it should have loaded @mathjax/mathjax-newcm-font, which will be in your node_modules/mathjax folder.  Make that available and add a path 

loader: {
  paths: {
    mathjax: '/mathjax',
    'mathjax-newcm': '/mathjax-newcm-font'
  }
},

or wherever you put the font.

An unimportant detail is the you don't need to list `init` in the Loader.preLoaded() call, as it is not a component on its own (this just says which components you have loaded).

Those are the things I see immediately without actually running the code.

Davide


--
You received this message because you are subscribed to the Google Groups "MathJax Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mathjax-dev...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/mathjax-dev/f9162c12-fd89-4ece-905f-d344da811263n%40googlegroups.com.

jox joe

unread,
Jan 11, 2026, 11:42:06 AMJan 11
to MathJax Development
001: ************* I added this:

    loader: {
      paths: {
        mathjax: '/mathjax',
      }
    },


002: *************** I added this:
    loader: {
      paths: {
        mathjax: '/mathjax',
        'mathjax-newcm': '/mathjax-newcm-font'
      }
    },


003: *************** I removed this:
`init`

004: ***********************
/mathjax/components/cjs/loader/loader.js";
//Uncaught TypeError: Cannot read properties of undefined (reading 'preLoaded')
Reason: this "loader.js" doesn't contain preload function

This is working:
mathjax/cjs/components/loader.js





*******************************************************************
Now if I use mathjax4+img+ xyJax  I get the following error messages:
(These worked in mathJax 3+img+xyJax properly. )

001: *************
//BEGIN code

function _tex2svg(tex, callback, _errCallback) {
MathJax.tex2svgPromise(tex, {
display: false
, em: 16 // em-size in pixels

, ex: 8 // ex-size in pixels
, containerWidth: 80 * 16
}).then(function (node) {
callback(node);  //  ORIGINAL callback(node.firstElementChild);
          //callback(node.firstElementChild);  
}).catch(function (err) {
_errCallback(err.message);
});
}
//END code

This generates the following error message:
wb-ver-1768133149000.js:15468 Uncaught (in promise) TypeError: _errCallback is not a function


002: *************
/mathjax/input/tex/extensions/bbox.js net::ERR_ABORTED 404 (Not Found)

003: *************
/mathjax/input/tex/extensions/bbox.js net::ERR_ABORTED 404 (Not Found)


004: *************
/mathjax/input/tex/extensions/boldsymbol.js net::ERR_ABORTED 404 (Not Found)


005: **************
wb-ver-1768133149000.js:91444 Can't load '[mathjax-newcm]/svg/dynamic/sans-serif.js':
No mathjax.asyncLoad method specified

****************************
****************************
****************************
>If I understand properly, this will be run int he browser, right?  (Not in node on a server.)
Yes, in the browser.
The Javascript code of the framework including Mathjax4, img extension and xyJax
will be bundled with browserify. The codes above are from your CJS version.
I use the SVG output of Mathjax 4 but not directly because it will be converted by fabric.js
to SVG again which will be displayed by the browser.

Further testing is in progress.
My plan is to focus on Mathjax 4 (and not on Mathjax 3).
Joe

jox joe

unread,
Jan 12, 2026, 10:24:00 AMJan 12
to MathJax Development

001:********************
Linebreak:
This is working:
\begin{align}
&line01\\
&line02 
\end{align}

This is not working:
line01\\
line02 

If I use them directly in a browser (tex-svg.js)
both are working as expected.

002: *******************
I can't use the \boldsymbol
and \mathsf macros which means they
have no effect on the letters. 

If I use them directly in a browser (tex-svg.js)
both are working as expected.

003: ********************
Question:
The new default font in Matjax 4
is better than the Mathjax 3 default font.
In case of existing material, should I change
the tex string manually everywhere?
(backward compatibility: will be the final document
the same? )

004: ********************
If I use the SVG output is it guaranteed
the the local font installed on the client
won't be used?
I want to see the same document
on all clients/browsers. 

Further testing and research are in progress.

Thank you.

Davide Cervone

unread,
Jan 12, 2026, 2:16:12 PMJan 12
to mathj...@googlegroups.com
/mathjax/components/cjs/loader/loader.js";
//Uncaught TypeError: Cannot read properties of undefined (reading 'preLoaded')
Reason: this "loader.js" doesn't contain preload function

This is not quite the actual reason.  The error message actually indicates that loader_js_1.Loader is undefined, not that it doesn't contain preLoaded.  That is because you have imported the wrong file, as I think you figured out.

This is working:
mathjax/cjs/components/loader.js

Yes, that is where you should be importing Loader from.  I'm sorry that I missed that when looking through your code.

001: *************
//BEGIN code
function _tex2svg(tex, callback, _errCallback) {
MathJax.tex2svgPromise(tex, {
display: false
, em: 16 // em-size in pixels
, ex: 8 // ex-size in pixels
, containerWidth: 80 * 16
}).then(function (node) {
callback(node);  //  ORIGINAL callback(node.firstElementChild); 
          //callback(node.firstElementChild);  
}).catch(function (err) {
_errCallback(err.message);
});
}
//END code

This generates the following error message:
wb-ver-1768133149000.js:15468 Uncaught (in promise) TypeError: _errCallback is not a function

That suggests that the code (not shown) where you call _tex2svg() is either not being passed the third argument, or that third argument is not a function object.  Since that is not in the code that you have given, I can't tell you which it is.  In any case, the catch() that is trying to process an error from the MathJax.tex2svgPromise() call is not able to do so.  You might try inserting a console.error(err.message) call before the _errCallback() call to see the actual error message if you can't fix the _tex2svg() call.


002: *************
/mathjax/input/tex/extensions/bbox.js net::ERR_ABORTED 404 (Not Found)

003: *************
/mathjax/input/tex/extensions/bbox.js net::ERR_ABORTED 404 (Not Found)

004: *************
/mathjax/input/tex/extensions/boldsymbol.js net::ERR_ABORTED 404 (Not Found)

These are all due to the mathjax path being incorrect.  It should have been

    loader: {
      paths: {
        mathjax: '/mathjax/bundle',
      }
    },


so that is my fault.  I wasn't sure what the contents of your /mathjax directory was, but should have guess this.


005: **************
wb-ver-1768133149000.js:91444 Can't load '[mathjax-newcm]/svg/dynamic/sans-serif.js':
No mathjax.asyncLoad method specified

The mathjax.asyncLoad method is supposed to be set up by the components/cjs/core/core.js file when it is loaded, so I am surprised to see this.  Try adding

var mathjax = require('/mathjax/cjs/mathjax.js');
console.log(Array.from(Object.keys(mathjax)));

right after the line that requires core.js and see whether asyncLoad is among the values displayed.

Davide


Davide Cervone

unread,
Jan 12, 2026, 2:40:57 PMJan 12
to mathj...@googlegroups.com
001:********************
Linebreak:
This is working:
\begin{align}
&line01\\
&line02 
\end{align}

This is not working:
line01\\
line02 

Again, what does "not working" mean?  Do you get an error message?  No output?  Wrong output?  You have given no useful information.

Also, you don't have math delimiters around these two lines, so it is not clear if you have these in math mode or not.  MathJax only processes, math, not regular text, so you would need to use something like

\[
  line01\\
  line02
\]

for this to create a line break from MathJax.

Finally, if you are passing a literal text string, remember that you need to double backslashes.  E.g., 

_tex2svg("line01\\\\line02", ..., ...);

Again, it is hard to provide meaningful advice from the information given.

002: *******************
I can't use the \boldsymbol
and \mathsf macros which means they
have no effect on the letters. 

Do you get any error messages in the browser's developer's console?  In particular, messages about fonts or extensions not being loaded?

Are the macros shown in red, or do they just "disappear"?

If you use the MathJax contextual menu to "Show Math As -> MathML Code" do the characters in \mathsf get marked by mathvariant="sans-serif", and the ones in \boldsymbol get marked with mathvariant="bold"?

003: ********************
Question:
The new default font in Matjax 4
is better than the Mathjax 3 default font.

I'm glad you think so.

In case of existing material, should I change
the tex string manually everywhere?

I don't understand the question.  Changing the font doesn't require any changes to the original tex strings.  So I'm not sure what you are asking.

(backward compatibility: will be the final document
the same? )

The new font has slightly different metrics (the characters are a bit "thinner"), so the spacing will not be exactly the same.  Plus MathJax v4 has fixed some layout issues from v3.  So there may be some effect on the document'e line breaking or pagination from that.

Also, some situations that are errors in LaTeX are now being reported in v4 that were not in v3, so if you have some bad LaTeX in your documents, that may be reported now when it wasn't before.  This is mostly concerning improper nesting of AMS environments (like using \begin{align}...\end{align} inside an expression instead of \begin{aligned}...\end{aligned}).  So there may be some expressions that need updating.

004: ********************
If I use the SVG output is it guaranteed
the the local font installed on the client
won't be used?

As long as you only use characters that are in the mathjax-newcm font, then no client fonts will be used.  But if you use characters that aren't in mathjax-newcm, then those will be taken from local fonts, and the results can vary from user to user.  Fortunately, mathjax-newcm has a much wider coverage than the original mathjax-tex font, so the likelihood of that occurring is much reduced from v3.

Davide

jox joe

unread,
Jan 13, 2026, 8:34:28 AMJan 13
to MathJax Development


A/ symbolic link is not working in this environment
*****************************************************
*****************************************************
I modified the files manually. Please check them.
..... means, full path was provided

001: mathjax/components/cjs/startup/hasown.js
var global_js_1 = require("...../mathjax/cjs/components/global.js");

002: mathjax/components/cjs/startup/init.js
var global_js_1 = require("...../mathjax/cjs/components/global.js");
var loader_js_1 = require("...../mathjax/cjs/components/loader.js");

003: mathjax/components/cjs/input/tex/tex.js
var loader_js_1 = require("...../mathjax/cjs/components/loader.js");

004: mathjax/components/cjs/input/tex/register.js
var Options_js_1 = require("...../mathjax/cjs/util/Options.js");

005: mathjax/components/cjs/loader/loader.js
var loader_js_1 = require("...../mathjax/cjs/components/loader.js");
var global_js_1 = require("...../mathjax/cjs/components/global.js");

006: mathjax/components/cjs/output/util.js
var global_js_1 = require("...../mathjax/cjs/components/global.js");
var package_js_1 = require("...../mathjax/cjs/components/package.js");
var context_js_1 = require("...../mathjax/cjs/util/context.js");

007: mathjax/components/cjs/output/svg/svg.js
var svg_js_1 = require("...../mathjax/cjs/output/svg.js");
var DefaultFont_js_1 = require("...../mathjax/cjs/output/svg/DefaultFont.js");

008: mathjax/components/cjs/core/core.js
var HTMLHandler_js_1 = require("...../mathjax/cjs/handlers/html/HTMLHandler.js");
var browserAdaptor_js_1 = require("...../mathjax/cjs/adaptors/browserAdaptor.js");

009: mathjax/cjs/ui/menu/mj-context-menu.js
var context_menu_js_1 = require("...../mathjax/node_modules/mj-context-menu/cjs/context_menu.js");
var sub_menu_js_1 = require("...../mathjax/node_modules/mj-context-menu/cjs/sub_menu.js");
var item_submenu_js_1 = require("...../mathjax/node_modules/mj-context-menu/cjs/item_submenu.js");
var item_radio_js_1 = require("...../mathjax/node_modules/mj-context-menu/cjs/item_radio.js");
var item_rule_js_1 = require("...../mathjax/node_modules/mj-context-menu/cjs/item_rule.js");
var parser_factory_js_1 = require("...../mathjax/node_modules/mj-context-menu/cjs/parser_factory.js");
var parse_js_1 = require("...../mathjax/node_modules/mj-context-menu/cjs/parse.js");
exports.CssStyles = __importStar(require("...../mathjax/node_modules/mj-context-menu/cjs/css_util.js"));
var html_classes_js_1 = require("...../mathjax/node_modules/mj-context-menu/cjs/html_classes.js");

010: mathjax/cjs/components/loader.js
var root_js_1 = require("...../mathjax/cjs/components/cjs/root.js");

011: mathjax/cjs/output/svg/DefaultFont.js
var default_js_1 = require("...../mathjax/node_modules/@mathjax/src/node_modules/@mathjax/mathjax-newcm-font/cjs/svg/default.js");

B/ some error messages 
*****************************************************
*****************************************************
(existing material, all worked in Matjax 3)
I can give you more details later.
+++
\boldsymbol(abc) *** \boldsymbol(abc) *** no error message
+++
\mathsf(abc)     *** no output *** dynamic file 'sans-serif' failed to load
+++
\unicode{x25B6} ***  \unicodex25B6 *** _tex2svg  Extension "unicode" failed to load
+++
  _tex2svg  Extension "bbox" failed to load
+++
 _tex2svg  Erroneous nesting of equation structures
+++
_tex2svg  Extension "boldsymbol" failed to load
+++
wb-ver-1768259731000.js:91492 Can't load '[mathjax-newcm]/svg/dynamic/sans-serif.js': No mathjax.asyncLoad method specified
+++
_tex2svg  Extension "color" failed to load
+++
\style rotation and scale is not working *** no error message
\style{transform: scale(1.0 , 1.0) rotate(90)}{jjj}
OUTPUT: style is red \style{transform: scale(1.0 , 1.0) rotate(90)}{jjj}
+++

Thank you.
joe

jox joe

unread,
Jan 14, 2026, 8:51:19 AMJan 14
to MathJax Development
Hi Davide,
I think some files were not properly transferred to my other machine.
Investigation is in progress.
Joe

jox joe

unread,
Jan 14, 2026, 4:35:51 PMJan 14
to MathJax Development
These are the error messages.
Actually I can use mathjax but certain functions are not available.
(\boldsymbol * \style * \mathsf * linebreak with \\ )
xyJax works properly if I dont use mathjax instruction within the xyjax block
*****************************************************
*****************************************************
(existing material, all worked in Mathjax 3)
+++
\boldsymbol(abc) *** \boldsymbol(abc) *** no error message
+++
\mathsf(abc)     *** no output *** dynamic file 'sans-serif' failed to load
+++
\unicode{x25B6} ***  \unicodex25B6 *** _tex2svg  Extension "unicode" failed to load
+++
  _tex2svg  Extension "bbox" failed to load
+++
 _tex2svg  Erroneous nesting of equation structures
+++
_tex2svg  Extension "boldsymbol" failed to load
+++
wb-ver-1768259731000.js:91492 Can't load '[mathjax-newcm]/svg/dynamic/sans-serif.js': No mathjax.asyncLoad method specified
+++
_tex2svg  Extension "color" failed to load
+++
\style rotation and scale is not working *** no error message
\style{transform: scale(1.0 , 1.0) rotate(90)}{jjj}
OUTPUT: style is red \style{transform: scale(1.0 , 1.0) rotate(90)}{jjj}
+++

Linebreak:
This is working:
\begin{align}
&line01\\
&line02 
\end{align}

This is not working:
line01\\
line02 
No error message. Only line01 is displayed.

Linebreak in text mode works as expected.
\text{
gffggf\\
dgdfgdfgdf}

++++++++
/img extension: I haven't tested it yet.

Thank you.
joe

Davide Cervone

unread,
Jan 17, 2026, 6:17:31 AMJan 17
to mathj...@googlegroups.com
A/ symbolic link is not working in this environment

There are no symbolic links.  There is, however, an "imports" block in the package.json file that tells node to redirect pseudo-paths like "#js" to their actual locations (which are different in the cjs directory than in the mjs one).  Javascript binders like webpack are supposed to take the imports block into account, so you should not have to modify the MathJax code to have these take effect.  That does require that you have the package.json file in place, so don't remove that or alter its contents.

011: mathjax/cjs/output/svg/DefaultFont.js
var default_js_1 = require("...../mathjax/node_modules/@mathjax/src/node_modules/@mathjax/mathjax-newcm-font/cjs/svg/default.js");

This one doesn't look right to me.  I would expect it to be

var default_js_1 = require("...../mathjax/node_modules/@mathjax/mathjax-newcm-font/cjs/svg/default.js");


B/ some error messages 

It looks like most of these are due to not being able to load the required extensions. Did you change the mathjax path as I suggested in a previous message?

        loader: {
      paths: {
        mathjax: '/mathjax/bundle',
      }
    },

I suspect that that is the problem.  In addition to the MathJax error indicating that a file failed to load, you should getting a warning from the browser about that file as well.  Check the file's URL path carefully and see if that file actually exists on your server.  If the path isn't correct, then that will help you decide how the ladder path needs to be adjusted.

 _tex2svg  Erroneous nesting of equation structures

I mentioned that you might see this message if you have misused certain AMS environments.  Version 4 corrected some situations that were being incorrectly allowed in v3.

wb-ver-1768259731000.js:91492 Can't load '[mathjax-newcm]/svg/dynamic/sans-serif.js': No mathjax.asyncLoad method specified

Did you try the debugging console.log that I recommended in a previous message?  The asyncLoad command is supposed to be set up in the core.js file that you are loading, so I asked you to check that that was occurring using

var mathjax = require('/mathjax/cjs/mathjax.js');
console.log(Array.from(Object.keys(mathjax)));

right after the require() call that loads core.js.

Davide

Davide Cervone

unread,
Jan 17, 2026, 6:32:21 AMJan 17
to mathj...@googlegroups.com
These are the error messages.

See my previous message about these.  It looks like this is due to files no loading, which in turns is probably du e to an infract loader path for mathjax.

This is not working:
line01\\
line02 
No error message. Only line01 is displayed.

For in-line expressions in v4, in order to allow line breaks within the math, SVG output is broken into multiple smaller SVGs at the potential (or explicit) breakpoints so that the browser can break the in-line math wherever it needs to.  Your tex2svg code is probably only returning the first of these.  

To handle breaks in in-line math, you either need to process multiple SVG elements, or configure MathJax to not do automatic in-line breaks, which will force MathJax to produce a single SVG for this case. This is done using the "linebreaks" block in the "output" section of your MathJax configuration:

MathJax = {
  output: {
    linebreaks: {
      inline: false
    }
  }
}

Give that a try.
Reply all
Reply to author
Forward
0 new messages