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

move PNG generation out of mathjax-node #205

Closed
pkra opened this issue Apr 11, 2016 · 17 comments
Closed

move PNG generation out of mathjax-node #205

pkra opened this issue Apr 11, 2016 · 17 comments

Comments

@pkra
Copy link
Contributor

pkra commented Apr 11, 2016

As per F2F, the PNG generation will be moved out of mathjax-node; there will be alternative modules to use.

Cf. the discussion in #174; see also #191.

@pkra
Copy link
Contributor Author

pkra commented Apr 14, 2016

From F2F: we will be adding width and height to the returned data (not just as an option).

@pkra
Copy link
Contributor Author

pkra commented Apr 14, 2016

While working on this, I've been wondering about structure of mj-single and the SVG modifications.

The last two seem like they should be dropped alongside the PNG (since they only come into play there). Any thoughts?

I'm also wondering if we should expose both the "HTML5" svg data alongside the namespaced version. Would anyone benefit from having the "HTML5" svg data?

@pkra
Copy link
Contributor Author

pkra commented Apr 15, 2016

I'm also wondering if we should expose both the "HTML5" svg data alongside the namespaced version. Would anyone benefit from having the "HTML5" svg data?

Or perhaps we should have a more general "standalone (prefixed?)" option affecting both MathML and SVG (and perhaps even CommonHTML?) output. Or maybe I'm just overthinking this 😉

@dpvc
Copy link
Member

dpvc commented Apr 17, 2016

The <img> tag is actually for an SVG file, not a PNG file, so the file version of the SVG is used for that as well as the PNG file. But perhaps you don't want to produce the SVG <img> tag any longer and want to push that to the surrounding tool.

One of the difficulties of pushing this off to the application is the copying of the style attributes from the <svg> to the <img> tag. If we are returning just a string for the svg (as we do now), then it is harder to get the style using string manipulation rather than DOM element's methods, and that is less reliable. But if we return the DOM element rather than a string, then that means the application is now responsible for prettying it up as we currently do.

One solution might be to provide service routines to do some of this so that the application can call them as needed.

@dpvc
Copy link
Member

dpvc commented Apr 17, 2016

I'm also wondering if we should expose both the "HTML5" svg data alongside the namespaced version. Would anyone benefit from having the "HTML5" svg data?

I'm not sure I understand what you are suggesting. Can you be more clear about the difference you have in mind?

Or perhaps we should have a more general "standalone (prefixed?)" option affecting both MathML and SVG (and perhaps even CommonHTML?) output.

Again, I'm not clear on what you are suggesting. Can you give more details about what is included (and not included) in each form?

@pkra
Copy link
Contributor Author

pkra commented Apr 18, 2016

The tag is actually for an SVG file, not a PNG file, so the file version of the SVG is used for that as well as the PNG file.

Right.

But perhaps you don't want to produce the SVG <img> tag any longer and want to push that to the surrounding tool.

I think creating an image tag is something that's better done outside mathjax-node. As a developer, I would frequently want to modify the img tag (add e.g., classes, data-attributes, wrapping elements).

One of the difficulties of pushing this off to the application is the copying of the style attributes from the to the

Right. Which is why I add the dimensions and styles alongside the SVG in the result.

One solution might be to provide service routines to do some of this so that the application can call them as needed.

Right. As we discussed on the last F2F, maybe exposing the SVG as an (htmlparser2?) object is useful more generally.

I'm not sure I understand what you are suggesting. Can you be more clear about the difference you have in mind?

The SVG data before/after adding namespaces and xlink prefixes (and prettying).

Again, I'm not clear on what you are suggesting. Can you give more details about what is included (and not included) in each form?

I don't really want any of this as part of mathjax-node but for completeness: just like the current code makes the SVG usable as a standalone document, it could do the same for MathML and HTML.

@dpvc
Copy link
Member

dpvc commented Apr 18, 2016

I think creating an image tag is something that's better done outside mathjax-node. As a developer, I would frequently want to modify the img tag (add e.g., classes, data-attributes, wrapping elements).

If course, if we returned a DOM element (like we are considering for the SVG element itself), it would be easy to do those things.

Which is why I add the dimensions and styles alongside the SVG in the result.

I had forgotten that you included the CSS text. Of course, if we return the SVG DOM element, then it is easy to get those values from the node itself, and so they may not need to be added to the result in that case.

The SVG data before/after adding namespaces and xlink prefixes (and prettying).

I'm more and inclined to think that the DOM node is the thing that should be returned. Then the outer application can serialize it as they want, and we can provide functions to call that do the prettifying and xlink adjustments if we want.

just like the current code makes the SVG usable as a standalone document, it could do the same for MathML and HTML.

Do you mean adding the <?xml version="1.0" standalone="no"?> and <!DOCTYPE ...> to the top of the file? And for the HTML output, adding an <html>, <head>, and <body> tags (with the CSS in a <style> element in the head)?

Again, it seems like service routines to do this given a result object might be the way, rather than returning two forms based on more flags.

@pkra
Copy link
Contributor Author

pkra commented Apr 18, 2016

I'm more and inclined to think that the DOM node is the thing that should be returned.

Me too but I don't know what would count as a good DOM object in nodejs land. Of course for now we're stuck with jsdom (which I think means parse5).

We should research a few libraries, I guess (also for MathJax v3). Good starting points might be parse5 (jsdom) but also htmlparser2 (e.g., used by cheerio).

The only downside I can see is that an application using mathjax-node would need a minimal amount of understanding over the relevant structure (e.g., so as to serialize it).

Do you mean adding [...]

Pretty much.

[...] it seems like service routines to do this given a result object might be the wa

I second that.

@pkra
Copy link
Contributor Author

pkra commented Apr 20, 2016

I've moved the question of returning an object to #219.

So from my questions, this leaves only the question regarding the xlink prefixes added in https://github.com/mathjax/MathJax-node/blob/master/lib/mj-single.js#L587-L593.

I'm not sure if they're still needed (or sensible, we don't add the namespace, do we?) but this is more a 1/10 for me.

So unless we change that, the PR is ready.

@pkra
Copy link
Contributor Author

pkra commented Apr 29, 2016

Blargh. I had posted the mock module on the wrong issues so @dpvc left his thorough review there as well. Check #206, comments 2--4. Will repost when I come back to this.

@pkra
Copy link
Contributor Author

pkra commented May 25, 2016

So here's my simple example (erroneously posted to #206) addressing (I hope) the comments from @dpvc (from that thread). Again, this is meant to be a simple example for people to build on, not a complete replacement for the batik integration. For example, I could easily imagine people wanting to increase the dimensions of the png (svg2png doesn't have any settings so you'd have to increase the dimensions manually which is somewhat tricky since MathJax provides ex values).

Anyway, I hope this might be a sufficient example for now.

var mjAPI = require("mj-single.js");
var svg2png = require('svg2png');
mjAPI.start();

function createPNG(result, callback){
  var sourceBuffer = new Buffer(result.svg, "utf-8");
  svg2png(sourceBuffer).then(function(buffer){
    result.png = "data:image/png;base64," + buffer.toString('base64');
    return callback(result);
  })
};

exports.math2png = function(options, callback){
  var mjConfig = options.config;
  var typesetOptions = options.typeset;

  // make sure SVG output will be generated and disable mml and html
  typesetOptions.svg = true;
  typesetOptions.mml = false;
  typesetOptions.html = false;

  mjAPI.config(mjConfig);
  mjAPI.typeset(typesetOptions, function(result){
    if (result.errors) return result.errors;
    createPNG(result, callback);
  });
}

Usage:

var math2png = require("math2png.js").math2png;
var options = {
  config: {
  },
  typeset: {
    math: "x^2",
    format: "TeX"
  },
  png: {}
}
math2png(options, function(result){
  console.log(result.png);
  console.log("style='" + result.style + "width:" + result.width + "; height:" + result.height +";'");
});

Note: this depends on the changes from PR #213 (width and height).

@dpvc
Copy link
Member

dpvc commented May 27, 2016

A couple of small comments, here.

Some configuration options for mjAPI.config() must be set before mjAPI.start() is called (e.g., the MathJax, extensions, and fontURL options), so you will not be able to specify these options in your setup, since mjAPI.start() is performed immediately when the module is loaded. Since mjAPI.typeset() will perform a start() automatically, you could just leave out the start() all together, which would allow you to configure all the options. Note, however, that once the first math2png() is called, you will not be able to change those options. It might be better not to pass config options to math2png but rather export another function for configuring the module.

Your options variable has a png block, but that is never used in your code. Perhaps you have that for future use?

In math2png, when you set typesetOptions.*, you are not just changing the local version, but also the original object passed in by the user. So your routine modifies the user's object. That is not an expected result of calling your function, so it might be better to make a copy of the typeset options that were passed to you instead, and then modify that with your values for svg, mml, etc.

In your callback to the typeset() call, if there are errors, you return result.errors. This does nothing, however, since the return value of the typeset() callback is never used (this was in partially corrected code in my point 5, but I pointed out that this was not useful in point 7). Furthermore, this means the user's callback will never get called; but since the user is relying on the callback for his own synchronization with your code, that is probably a bad idea. You probably want

    if (result.errors) {
      callback(result);
    } else {
      createPNG(result, callback);
    }

and let the user check for result.errors (or for result.png being null). Or you could do callback({errors: result.errors}) if you won't want to give the full results object.

Alternatively, your callback could be passed a success/failure value in addition to the results object. Or you could require two callbacks, one for success and one for failure.

@pkra
Copy link
Contributor Author

pkra commented May 30, 2016

Thanks for the comments!

First off, let me move the code to a gist at https://gist.github.com/pkra/c60098af5c1d8c37473416caad0418f6

Some configuration options for mjAPI.config() must be set before mjAPI.start() is called (e.g., the MathJax, extensions, and fontURL options), so you will not be able to specify these options in your setup, since mjAPI.start() is performed immediately when the module is loaded. Since mjAPI.typeset() will perform a start() automatically, you could just leave out the start() all together, which would allow you to configure all the options. Note, however, that once the first math2png() is called, you will not be able to change those options. It might be better not to pass config options to math2png but rather export another function for configuring the module.

I guess I wasn't really aware that some options will "stick" (though it was clear as soon as I read it). Questions:

  • Are MathJax, extensions, and fontURL the only relevant options here? (I'd like to start documenting things a bit better.)
  • It seems to me we might want to add a reset (and stop) method for these for v1.0. What do you think?

Your options variable has a png block, but that is never used in your code. Perhaps you have that for future use?

No, it was just a copied from an older version. (Though I expect that svg-to-png converters might want those or a more advanced module that allows scaling would need options like this.)

it might be better to make a copy of the typeset options that were passed to you instead,

Ouch; fixed.

this does nothing, however, since the return value of the typeset() callback is never used (this was in partially corrected code in my point 5, but I pointed out that this was not useful in point 7)

Another Ouch -- fixed. Sorry for missing that among the earlier comments.

@dpvc
Copy link
Member

dpvc commented Jun 22, 2016

==> Merged.

@dpvc dpvc added the Fixed label Jun 22, 2016
@pkra
Copy link
Contributor Author

pkra commented Nov 22, 2016

I've created another wrapper based on svg2png that can be used as a drop-in for mathjax-node to generate PNG content.

@federicosan
Copy link

Hi, I am having trouble getting the SVG output from MathJax-node to be included into pdf files with pdfmake that uses svg-to-PDFKIT, so I thought of making the SVG output into a png, I've noticed MathJax-node does not support this anymore, could you please point out how this could be done? Would I need to pipe MathJax-node's result into some other function that converts it into PNG, would you know any that would work with MathJax-node's outputs? Thank you

@dpvc
Copy link
Member

dpvc commented Jan 9, 2020

@federicosan, the link in the comment just above yours gives the sag-to-png functionality.

Alternatively, the issue you are probably facing is the <use> elements in the svg output. Try using the --nocache option to prevent the inclusion of <use> elements. That worked for me in the pdfmake playground.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants