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

[WIP] Modularization of PNG generation #174

Closed
wants to merge 4 commits into from
Closed

[WIP] Modularization of PNG generation #174

wants to merge 4 commits into from

Conversation

pkra
Copy link
Contributor

@pkra pkra commented Feb 18, 2016

  • abstract the png generation
    • mj-single
    • mj-page
  • move batik-specific code into a separate module
  • add librsvg and svg2png based modules.
  • decide on new default
    • update package.json
  • update Readme

This fixes #59.

@pkra
Copy link
Contributor Author

pkra commented Feb 18, 2016

@dpvc @zorkow the main changes are ready for review. I marked it WIP for the organizational details.

@pkra
Copy link
Contributor Author

pkra commented Feb 18, 2016

FYI, the tests fail because Batik won't be installed on Travis at the moment. They pass locally for me.

@pkra
Copy link
Contributor Author

pkra commented Feb 18, 2016

To clarify: the PR currently only modifies mj-single. Once that part is reviewed, I'll reproduce it for mj-page.

@pkra
Copy link
Contributor Author

pkra commented Feb 18, 2016

While the test that's in this PR is universal (in that it succeeds with all three converter modules), I'm wondering if this approach is not actually taking things far enough.

Should the PNG conversion be ripped out completely instead? Just pass the SVG to your own lib and done. It's a bit radical, I admit. But for mj-single it seems sensible; PNG generation is really not mathjax-y.


exports.svg2png = function(svg, result, scale, width, height, callback, check) {
var s = new Readable();
var svgRenderer = new rsvg();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can avoid the stream altogether here since you already have the data to render in svg, so simply:

var svgRender = new rsvg(svg);

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this still allows you to keep the block on L11 (error checking), because the rsvg object is a stream itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Since I'm not too experienced with streams: does L11 catch only errors from the streaming process (which is nice if the string was corrupted somehow) or does it also catch errors that rsvg itself has produced?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the lib's code, both would trigger L11.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @d00rman !

@d00rman
Copy link

d00rman commented Feb 19, 2016

Should the PNG conversion be ripped out completely instead? Just pass the SVG to your own lib and done. It's a bit radical, I admit. But for mj-single it seems sensible; PNG generation is really not mathjax-y.

I'm inclined to think the same. Conversion in itself is not (or should not, IMO) be MathJax' problem. But, a pragmatic solution might be to allow users to specify which converter to use in the init phase. Ideally, we'd want the conversion engine or lib to be a peer dependency of MathJax, i.e. it should not be required by MathJax itself, because, as we see from this PR, that entails a strong dependency on either:

  • a binary module / package; or
  • a large Java-based solution

If there were a really slow, but light dependency that could be used as the sane default, that would be awesome, but I have a feeling that here explaining to users how to achieve the conversion is the right way.

In that context clearly defining an interface for the conversion (which I think has been achieved implicitly in this PR) is a good practice, because that means additional converters can be only an npm install away, as it makes it rather easy to package different converters separately: one can easily imagine lib/mj-batik and lib/mj-librsvg being separate npm packages, and then the user just supplies them to MathJax during the initialisation phase.

@pkra
Copy link
Contributor Author

pkra commented Feb 19, 2016

@d00rman thanks for the comments!

Ideally, we'd want the conversion engine or lib to be a peer dependency of MathJax

I've never used peer dependencies. How would that work?

one can easily imagine lib/mj-batik and lib/mj-librsvg being separate npm packages, and then the user just supplies them to MathJax during the initialisation phase.

I think I agree but I'm not sure. FWIW, I had been thinking a separate module mj-svg2png could look something like

var mj = require('./lib/mj-single');
var svg2png = require('svg2png');

exports.mj2png = function(options, callback) {
  mj.start();
  mj.typeset(options, function(data) {
    var sourceBuffer = new Buffer(data.svg, "utf-8");
    var returnBuffer = svg2png.sync(sourceBuffer, {
      width: data.width,
      height: data.height
    });
    data.png = "data:image/png;base64," + returnBuffer.toString('base64');
    callback(data);
  });
};

(note: just a mockup).

exports.svg2png = function(svg, result, scale, width, height, callback, check) {
var tmpfile = os.tmpdir() + "/mj-svg" + process.pid; // file name prefix to use for temp files
var BatikRasterizerPath = path.resolve(__dirname,'..','batik/batik-rasterizer.jar');
var batikCommands = ['-jar', BatikRasterizerPath, '-dpi', scale*72, tmpfile + '.svg'];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've lost the use of the dpi setting that is part of the current API by changing it to scale*72 here, so that is a breaking change. (Also, the default dpi passed by the command-line tools is based on the ex size, with the default ex of 6 producing a dpi of 96, so this will produce different sized images from the current version. I worked a bit to get the dpi values to do the right thing for the given ex size.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to use a width and height rather than dpi, batik can do that (according to the man page), so it would be possible to use those instead (and change or remove the command line dpi API, or base the scaling off the dpi, e.g., make scale = dpi / 96). In that case, however, I'd only pass width and height and have the scale multiplied in before svg2png is called so that the width and height here are the true sizes that are being requested.

@pkra
Copy link
Contributor Author

pkra commented Feb 22, 2016

@dpvc thanks for the review. But before I fix this, perhaps we can first discuss the larger question of moving the entire PNG workflow out of mathjax-node itself.

@dpvc
Copy link
Member

dpvc commented Feb 22, 2016

I'm OK with removing the PNG stuff (it was a pain to implement), though I suspect you will get some push-back from people currently using it. It might be nice to keep some *2png command-line tools (but with the svg-to-png conversion being done through an external tool in the command-line program rather than in mj-single itself).

@d00rman
Copy link

d00rman commented Feb 29, 2016

I've never used peer dependencies. How would that work?

NPM supports peerDependencies where one can specify the modules that should be included by the application. A big note here is that the peerDependencies sections will disappear with npm 3.x.

The general idea is that an external package provides the svg2png functionality, which is then related to MathJax-node when require()-ing it (or setting a global variable that would be picked up by it).

I think I agree but I'm not sure. FWIW, I had been thinking a separate module mj-svg2png could look something like

var mj = require('./lib/mj-single');
var svg2png = require('svg2png');

exports.mj2png = function(options, callback) {
mj.start();
mj.typeset(options, function(data) {
var sourceBuffer = new Buffer(data.svg, "utf-8");
var returnBuffer = svg2png.sync(sourceBuffer, {
width: data.width,
height: data.height
});
data.png = "data:image/png;base64," + returnBuffer.toString('base64');
callback(data);
});
};
(note: just a mockup).

Yup, something like that 👍

@pkra
Copy link
Contributor Author

pkra commented Feb 29, 2016

A big note here is that the peerDependencies sections will disappear with npm 3.x.

Ah, right. I'm too used to 3.x already, so I got confused when I read up on peerDepdencies. Makes sense now, thank you!

@pkra
Copy link
Contributor Author

pkra commented Apr 11, 2016

Closing in favor of #205

@pkra pkra closed this Apr 11, 2016
@pkra pkra deleted the issue59 branch February 16, 2017 09:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants