Thursday, March 1, 2012

Tutorial - How to setup MathJax locally - "slim" edition

This post is a follow up of the previous MathJax tutorial, but with extra settings to make it "slimmer" as suggested by Davide Cervone, plus using the latest 2.0 release instead of the previous 1.1a version.

Also, in case you are creating multiple projects all using MathJax, I think it might be easier to manage if you "don't" copy the whole MathJax library into your project, as it makes your code base smaller. Of course this comes at the price that if someone copied your project to another machine without MathJax or if it's in different location, there will be some complications - but should be still manageable.

Ok, let's start.


Step 1. Same as before, start a new project in Xcode with single view, I call mine "MathJaxLocal2".


Step 2. Download MathJax from this link, under "Downloads for Local Installation" section, select the link which at this time of writing, says "Current Version: MathJax-2.0 (17.6MB)".


Step 3. Expand the downloaded .ZIP file. My downloaded .ZIP file is called "mathjax-MathJax-v2.0-0-g4a4f535.zip" and the expanded folder is "mathjax-MathJax-c9db6ac". Again to make it easier to differentiate between different version, I renamed the folder to "mathjax-MathJax-v2.0".

I checked the folder size using "Get Info", it says "Size: 21,895,871 bytes (136.6 MB on disk) for 30,963 items". Holy sugar, that is a lot of files!


Step 4. Now the fun part - trim all the fat!! Don't worry if you made a mistake or stuffed up something, as we still have the .ZIP file as backup. The things I deleted are listed below:

Delete everything under the following folders (including the folder itself):
4a. "mathjax-MathJax-v2.0/fonts/HTML-CSS/png"
4b. "mathjax-MathJax-v2.0/unpacked"
4c. "mathjax-MathJax-v2.0/test"
4d. "mathjax-MathJax-v2.0/docs"

Checked the folder size again, it's now "Size: 7,705,058 bytes (9.2 MB on disk) for 739 items". Mumm, much better, but still a long way to go from the "2.5 MB" target? Let's do some more research...

Following the info mentioned here, I also deleted the following 2:

4e. "mathjax-MathJax-v2.0/fonts/HTML-CSS/TeX/svg"
4f. "mathjax-MathJax-v2.0/jax/output/HTML-CSS/fonts/STIX"

Checked the folder size again, now it says "Size: 6,191,493 bytes (7.2 MB on disk) for 492 items"... I could have remove a few more ".js" files but they are all quite small and won't make much difference, plus don't have time to do further research, so will leave it as it is for now. At least we are far below the 20MB limit!


Step 5. Edit the config file "mathjax-MathJax-v2.0/config/default.js" using any text editor

5a. Find this line

imageFont: "TeX",

add "//" at the front to disable it, then add a new line below. don't forget the "," at the end

imageFont: null,

5b. because we deleted "4f." above, I checked the config file and confirmed there's a "imageFont:null" inside the "HTML-CSS" section. So no action required for this step.


Step 6. Next is to add the library to your project. Drag and drop the whole "mathjax-MathJax-v2.0" folder into your Xcode project. At the popup screen as shown below, make sure you "disable" the option "Copy Items into destination group's folder (if needed)". And "select" the "Create folder reference for any added folders". Otherwise the folder structure won't be created properly.


As shown below, still shows the folder in blue colour, same as previous one. So how do we know if it copied the whole library over or just created a reference link?

If you look inside "Finder", first compare with the previous "MathJaxLocal" folder above, you can see there's no "mathjax-MathJax-v2.0" folder in the new "MaxJacLocal2" project, and the folder size is only 53KB. This confirms it's using reference link. Good job!




Step 7. Finally the code change - I am combining all code changing steps into one as it's exactly the same as previous project (except folder name difference in step 7c).

7a. add a UIWebView declaration at the beginning of your UIViewController file.

UIWebView *myWebView;

7b. add the new method that handles writing HTML file locally "writeStringToFile".

-(void)writeStringToFile:(NSString *)dir fileName:(NSString *)strFileName pathName:(NSString *)strPath content:(NSString *)strContent{
    
    NSLog(@" inside writeStringToFile, strPath=%@", strPath);
    
    NSString *path = [dir stringByAppendingPathComponent:strFileName];
    
    NSString *foo0 = @"<html><head><meta name='viewport' content='initial-scale=1.0' />"
"<script type='text/javascript' src='";
    
    NSString *foo1 = @"?config=TeX-AMS-MML_HTMLorMML-full'></script>"
    "</head>"
    "<body>";
    NSString *foo2 = @"</body></html>";
    NSString *fooFinal = [NSString stringWithFormat:@"%@%@%@%@%@",foo0,strPath,foo1,strContent,foo2];
    
    
    NSLog(@"Final content is %@",fooFinal);
    
    [fooFinal writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil];

}


7c. last change is add the following code into your "viewDidLoad" or whichever method which you think appropriate. Same as before, 2 examples from MathJax web site included, can change it to see the differences. 1st one is for "TEX" and there's more work as you have to change all the single slash "\" to double slashes "\\", the 2nd one is for "MathML". Please note that the folder name has changed...

     //content copied from http://www.mathjax.org/demos/tex-samples/
     NSString *xContent = @"<p>\\["
     "\\left( \\sum_{k=1}^n a_k b_k \\right)^{\\!\\!2} \\leq"
     "\\left( \\sum_{k=1}^n a_k^2 \\right) \\left( \\sum_{k=1}^n b_k^2 \\right)"
     "\\]</p>"
     "<BR/>"
     "<p>\\["
     "\\frac{1}{(\\sqrt{\\phi \\sqrt{5}}-\\phi) e^{\\frac25 \\pi}} ="
     "1+\\frac{e^{-2\\pi}} {1+\\frac{e^{-4\\pi}} {1+\\frac{e^{-6\\pi}}"
     "|{1+\\frac{e^{-8\\pi}} {1+\\ldots} } } }"
     "\\]</p>";
     
    /*
    NSString *xContent =@"When <math><mi>a</mi><mo>&#x2260;</mo><mn>0</mn></math>,"
    "there are two solutions to <math>"
    "<mi>a</mi><msup><mi>x</mi><mn>2</mn></msup>"
    "<mo>+</mo> <mi>b</mi><mi>x</mi>"
    "<mo>+</mo> <mi>c</mi> <mo>=</mo> <mn>0</mn>"
    "</math> and they are"
    "<math mode='display'>"
    "<mi>x</mi> <mo>=</mo>"
    "<mrow>"
    "<mfrac>"
    "<mrow>"
    "<mo>&#x2212;</mo>"
    "<mi>b</mi>"
    "<mo>&#x00B1;</mo>"
    "<msqrt>"
    "<msup><mi>b</mi><mn>2</mn></msup>"
    "<mo>&#x2212;</mo>"
    "<mn>4</mn><mi>a</mi><mi>c</mi>"
    "</msqrt>"
    "</mrow>"
    "<mrow> <mn>2</mn><mi>a</mi> </mrow>"
    "</mfrac>"
    "</mrow>"
    "<mtext>.</mtext>"
    "</math>";
     */
    
    //temp file filename
    NSString *tmpFileName = @"test1.html";
    
    //temp dir
    NSString *tempDir = NSTemporaryDirectory();
    NSLog(@"tempDirectory: %@",tempDir);
    
    //create NSURL
    NSString *path4 = [tempDir stringByAppendingPathComponent:tmpFileName];
    NSURL* url = [NSURL fileURLWithPath:path4]; 
    NSLog(@"Path=%@, url=%@",path4,url);
    
    //setup HTML file contents
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"MathJax" ofType:@"js" inDirectory:@"mathjax-MathJax-v2.0"];
    NSLog(@"filePath = %@",filePath);
    
    //write to temp file "tempDir/tmpFileName", set MathJax JavaScript to use "filePath" as directory, add "xContent" as content of HTML file
    [self writeStringToFile:tempDir fileName:tmpFileName pathName:filePath content:xContent];
    
    //create UIWebView
    CGRect webRect = CGRectMake(10,10,300,400);
    myWebView =[[UIWebView alloc] initWithFrame:webRect];
    myWebView.scalesPageToFit = YES;
    myWebView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
myWebView.delegate = self;
    
    
    NSURLRequest* req = [[NSURLRequest alloc] initWithURL:url]; 
    
    //original request to show MathJax stuffs
    [myWebView loadRequest:req];
    
    [self.view addSubview:myWebView];
    [req release];

7d. Change the "ViewController.h" to add the "<UIWebViewDelegate>" at the end. Without this, you will receive a warning about the "Assigning to 'id' from incompatible type 'ViewController *'".


@interface ViewController : UIViewController <UIWebViewDelegate>



That's it, you can now run the project. The speed was extremely fast - definitely within a second - much, much faster than the previous one.

A few screen dumps shown below. First the iPhone one as before:

I also tried device rotation - in landscape mode as shown below:

Then another one I didn't try last time - the retina display version of iPhone - both portrait and landscape.


And yet another one I didn't do last time, iPad test!

Then the iPad landscape test.... Urhhh... Huston, we have a problem. The bottom part was cut off??!!

Something wrong somewhere, as it's not happening in iPhone... Strange...

Later, I found the problem. It's all because of this line:

CGRect webRect = CGRectMake(10,10,300,400);


After I changed it to bigger size:


CGRect webRect = CGRectMake(10,10,self.view.bounds.size.width,self.view.bounds.size.height);


Everything works... Yeah! Mission accomplished!




Ok, I was too excited with the excellent result... Calm down a bit...

A few things:

1) I still get that annoying warning "Assigning to 'id' from incompatible type 'ViewController *'"...

2) I didn't try the MathML example as it's 12:37am now, still have to go to work tomorrow morning... Will leave that with you...

As usual, hope you learned something from this tutorial and let me know if you have any questions or any better ideas (e.g. to cut it down to 2.5MB or get rid of that warning...)!

Off to bed now...z.z.z.z.z.z....

[Update 02/03/2012] I found the fix to get rid of the warning. Just need to change this line in "ViewController.h" header file

@interface ViewController : UIViewController

to this

@interface ViewController : UIViewController <UIWebViewDelegate>

Will also update the steps above (step 7d added) to cover this.

0 comments:

Post a Comment