2828var http = require ( 'http' ) ;
2929var fs = require ( 'fs' ) ;
3030var path = require ( 'path' ) ;
31+ var fmt = require ( 'util' ) . format ;
3132var jsdom = require ( "jsdom" ) . jsdom ;
33+ var exec = require ( 'child_process' ) . exec ;
3234var speech = require ( 'speech-rule-engine' ) ;
3335
3436var displayMessages = false ; // don't log Message.Set() calls
@@ -50,6 +52,7 @@ var defaults = {
5052 inputs : [ "AsciiMath" , "TeX" , "MathML" ] , // the inputs formats to support
5153 renderer : "SVG" , // the output format
5254 // ("SVG", "NativeMML", "IMG", "PNG", or "None")
55+ dpi : 144 , // dpi for png image
5356
5457 addPreview : false , // turn turn into a MathJax preview, and keep the jax
5558 removeJax : true , // remove MathJax <script> tags?
@@ -72,11 +75,14 @@ var STATE = {
7275} ;
7376
7477var MathJaxPath = "file://" + require . resolve ( 'MathJax/unpacked/MathJax' ) ;
78+ var BatikRasterizerPath = path . resolve ( __dirname , '..' , 'batik/batik-rasterizer.jar' ) ;
7579var MathJaxConfig ; // configuration for when starting MathJax
7680var MathJax ; // filled in once MathJax is loaded
7781var serverState = STATE . STOPPED ; // nothing loaded yet
7882var timer ; // used to reset MathJax if it runs too long
7983
84+ var tmpfile = "/tmp/mj-single-svg" ; // file name prefix to use for temp files
85+
8086var document , window , content , html ; // the DOM elements
8187
8288var queue = [ ] ; // queue of typesetting requests of the form [data,callback]
@@ -535,6 +541,18 @@ function AdjustSVG() {
535541 //
536542 nodes = document . querySelectorAll ( ".MathJax_SVG[role=textbox], .MathJax_SVG_Display[role=textbox]" ) ;
537543 for ( i = nodes . length - 1 ; i >= 0 ; i -- ) nodes [ i ] . setAttribute ( "role" , "math" ) ;
544+ //
545+ // Fix a problem with jsdom not setting width and height CSS.
546+ //
547+ nodes = document . querySelectorAll ( ".MathJax_SVG > span > span" ) ;
548+ for ( i = nodes . length - 1 ; i >= 0 ; i -- ) {
549+ var child = nodes [ i ] . firstChild ;
550+ SetWH ( nodes [ i ] , child . getAttribute ( "width" ) , child . getAttribute ( "height" ) ) ;
551+ }
552+ //
553+ // Add speech text, if needed
554+ //
555+ if ( data . speakText ) { callback = GetSpeech ( ) }
538556 }
539557 if ( data . renderer === "SVG" ) {
540558 //
@@ -552,18 +570,6 @@ function AdjustSVG() {
552570 styles . innerHTML = STYLES ;
553571 styles . id = "MathJax_SVG_styles" ;
554572 content . insertBefore ( styles , content . firstChild ) ;
555- //
556- // Fix a problem with jsdom not setting width and height CSS.
557- //
558- nodes = document . querySelectorAll ( ".MathJax_SVG > span > span" ) ;
559- for ( i = nodes . length - 1 ; i >= 0 ; i -- ) {
560- var child = nodes [ i ] . firstChild ;
561- SetWH ( nodes [ i ] , child . getAttribute ( "width" ) , child . getAttribute ( "height" ) ) ;
562- }
563- //
564- // Add speech text, if needed
565- //
566- if ( data . speakText ) { callback = GetSpeech ( ) }
567573 }
568574 return callback ;
569575}
@@ -580,11 +586,12 @@ function GetSpeech() {
580586 if ( ! queue ) { queue = MathJax . Callback . Queue ( ) }
581587 return queue . Push ( err . restart , window . Array ( SPEAK , svg ) ) ;
582588 }
589+ jax . speech = speech . processExpression ( mml ) ;
583590 svg . setAttribute ( "role" , "math" ) ;
584591 svg . setAttribute ( "aria-labelledby" , id + "-Title " + id + "-Desc" ) ;
585592 for ( var i = 0 , m = svg . childNodes . length ; i < m ; i ++ )
586593 svg . childNodes [ i ] . setAttribute ( "aria-hidden" , true ) ;
587- var node = MathJax . HTML . Element ( "desc" , { id :id + "-Desc" } , [ speech . processExpression ( mml ) ] ) ;
594+ var node = MathJax . HTML . Element ( "desc" , { id :id + "-Desc" } , [ jax . speech ] ) ;
588595 svg . insertBefore ( node , svg . firstChild ) ;
589596 node = MathJax . HTML . Element ( "title" , { id :id + "-Title" } , [ "Equation" ] ) ;
590597 svg . insertBefore ( node , svg . firstChild ) ;
@@ -612,7 +619,7 @@ function AdjustMML() {
612619//
613620function MakeIMG ( ) {
614621 if ( data . renderer === "IMG" ) {
615- var n = 0 , nodes = document . getElementsByClassName ( "MathJax_SVG" ) ;
622+ var nodes = document . getElementsByClassName ( "MathJax_SVG" ) ;
616623 for ( var i = nodes . length - 1 ; i >= 0 ; i -- ) {
617624 var svg = nodes [ i ] . getElementsByTagName ( "svg" ) [ 0 ] ;
618625 var w = svg . getAttribute ( "width" ) , h = svg . getAttribute ( "height" ) ;
@@ -626,6 +633,7 @@ function MakeIMG() {
626633 className : "MathJax_SVG_IMG" ,
627634 } ) ;
628635 SetWH ( img , w , h ) ; // work around jsdom bug with width and height CSS
636+ if ( data . speakText ) img . setAttribute ( "alt" , MathJax . Hub . getJaxFor ( nodes [ i ] ) . speech ) ;
629637 nodes [ i ] . parentNode . replaceChild ( img , nodes [ i ] ) ;
630638 }
631639 }
@@ -635,6 +643,47 @@ function MakeIMG() {
635643// Make PNG images and attach them to IMG tags
636644//
637645function MakePNG ( ) {
646+ if ( data . renderer === "PNG" ) {
647+ var synch = MathJax . Callback ( function ( ) { } ) ; // for synchronization with MathJax
648+ var batikCommand = fmt ( "java -jar %s -dpi %d '%s.svg'" , BatikRasterizerPath , data . dpi , tmpfile ) ;
649+ var tmpSVG = tmpfile + ".svg" , tmpPNG = tmpfile + ".png" ;
650+ var nodes = document . getElementsByClassName ( "MathJax_SVG" ) ;
651+ var check = function ( err ) { if ( err ) { AddError ( err . message ) ; return true } }
652+ var PNG = function ( i ) {
653+ if ( i < 0 ) return synch ( ) ; // signal everything is done
654+ var svg = nodes [ i ] . getElementsByTagName ( "svg" ) [ 0 ] ;
655+ var w = svg . getAttribute ( "width" ) , h = svg . getAttribute ( "height" ) ;
656+ var css = svg . style . cssText + " width:" + w + "; height:" + h ;
657+ svg . style . cssText = "" ; // this will be handled by the <img> tag instead
658+ svg . setAttribute ( "xmlns" , "http://www.w3.org/2000/svg" ) ;
659+ svg = [
660+ '<?xml version="1.0" standalone="no"?>' ,
661+ '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">' ,
662+ svg . outerHTML
663+ ] . join ( "\n" ) ;
664+ fs . writeFile ( tmpSVG , svg , function ( err ) {
665+ if ( check ( err ) ) return PNG ( i - 1 ) ;
666+ exec ( batikCommand , function ( err , stdout , stderr ) {
667+ if ( check ( err ) ) { fs . unlinkSync ( tmpSVG ) ; return PNG ( i - 1 ) }
668+ fs . readFile ( tmpPNG , null , function ( err , buffer ) {
669+ if ( ! check ( err ) ) {
670+ var img = MathJax . HTML . Element ( "img" , {
671+ src :"data:image/png;base64," + buffer . toString ( 'base64' ) ,
672+ style :{ cssText :css } , className : "MathJax_PNG_IMG" ,
673+ } ) ;
674+ SetWH ( img , w , h ) ; // work around jsdom bug with width and height CSS
675+ if ( data . speakText ) img . setAttribute ( "alt" , MathJax . Hub . getJaxFor ( nodes [ i ] ) . speech ) ;
676+ nodes [ i ] . parentNode . replaceChild ( img , nodes [ i ] ) ;
677+ }
678+ fs . unlinkSync ( tmpSVG ) ; fs . unlinkSync ( tmpPNG ) ;
679+ PNG ( i - 1 ) ;
680+ } ) ;
681+ } ) ;
682+ } ) ;
683+ }
684+ PNG ( nodes . length - 1 ) ;
685+ }
686+ return synch ;
638687}
639688
640689//
0 commit comments