## Friday, 2 March 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 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/TeX/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>"
"<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 $<mi>a</mi><mo>&#x2260;</mo><mn>0</mn>$,"
"there are two solutions to $" "<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>" "$ 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>"
"[/itex]";
*/

//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;
//this line no longer required
//myWebView.delegate = self;

NSURLRequest* req = [[NSURLRequest alloc] initWithURL:url];

//original request to show MathJax stuffs

//this line no longer required if using ARC
//[req release];

Step 7d no longer required
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 21/03/2012] This step no longer required
[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.

[Update 21/03/2013]
I have shared the source project at this link. Please note that as it's expecting the MathJax files in a separate directory, you might need some changes to make it work as your directory structure might be different from my machine.

Also please note that the latest version of MathJax is v2.1, and the example given above was using v2.0.

[Update 21/03/2013]
I tried MathJax v2.1 on Xcode 4.6 and following the same steps above, got it working on iPhone/iPad 6.1 simulator and on my iPhone 5 as well. Please see this post for other further info.

I think if you have to time to do more research, for example, follow steps mentioned in this post by Davide Cervone you should be able to trim down even more! Have a go and welcomed to share your experience with everyone.

[Update 10/May/2015]

Thanks to everyone who visited this blog. A few friends asked about doing the same example in Swift. Unfortunately I am quite busy with other stuffs at the moment and didn't really spend much time on learning Swift. Therefore not confident that I can provide any good quality Swift example yet.

However, there is this example from source freeze.com which shows an example about "UIWebView load local html" and that is basically what we are doing here. Please try to change the content so that it covers the MathJax stuffs and that should be it.

1. Thanks for the new post. I think this will be very helpful to iPhone and iPad developers who want to use MathJax in their applications.

The other places where you can reduce space is to remove the input jax and output jax that you don't plan to use. So if you are using TeX input and HTML-CSS output, for example, you could remove the jax/input/AsciiMath and jax/input/MathML folders entirely, as well as the jax/output/NativeMML and jax/output/SVG folders. You may want to disable the MathJax contextual menu's Render submenu in that case so that no one tries to change to one of the removed output jax. That is controlled with the showRenderer parameter in the MathMenu block of your configuration (see the config/default.js file).

[It might be worth thinking about using the SVG output rather than the HTML-CSS output, as it is somewhat smaller, and doesn't have to deal with the font issues that the HTML-CSS output does. In that case you can remove the top-level fonts directory entirely; that would be something like 2MB right there. If you don't go that route, you could still get rid of the eot, svg, and one of the otf or woff directories (depending whether the browser reads woff fonts or not; I am not sure).]

You could also remove the jax/output/HTML-CSS/fonts/STIX folder, but then should set availableFonts:["TeX"] in default.js in order to prevent MathJax from trying to use them.

If you are not using one of the combined configuration files but are using default.js instead, then you can remove all the other configuration files in the config directory. That would be another 2.5MB. Since you are running from local files, there should not be much speed difference between using the combined files and using default.js, so it is probably worth getting rid of the combined files.

I think that should get you down to your target size.

2. Hello, have you got problem, with word wrapping?
maybe you can answer on my question.

http://stackoverflow.com/questions/15235077/mathjax-word-wrap

1. Hi, I suspect you might have something missing or in the incorrect order with your HTML file. Instead of everyone trying to combine the CSS/script/body section you provided in different ways and getting different results, it would be less confusing if you can provide the full HTML including everything you used. I have added comment to your stackoverflow post, hope that helps. Cheers

3. Hello Davide, thanks for you comment on the stack. I have a problem. When I removed folders from MathJax I ve got message [Math processing error] on the iPad screen.

I did everything right, just maybe one action could was wrong (on the step five)

this is part of default.js how it looks in my case:

//
imageFont: null,

//
// if you want to prevent the use of image fonts (e.g., you have not
// installed the image fonts on your server). In this case, only
// browsers that support web-based fonts will be able to view your pages
// without having the fonts installed on the client computer. The browsers
// that support web-based fonts include: IE6 and later, Chrome, Safari3.1
// and above, Firefox3.5 and later, and Opera10 and later. Note that
// Firefox3.0 is NOT on this list, so without image fonts, FF3.0 users
// will be required to to download and install either the STIX fonts or the
// MathJax TeX fonts.
//
//imageFont: "TeX",

is this correct?

1. Unfortunately I am not Davide :-)
I think you can reach Davide and other MathJax gurus at this link:

http://www.mathjax.org/contact/

Hope that helps!

2. Sorry Hello Objective-C!, thanks for your response. Can you check you zip archive source. It has not got mathjax-MathJax-v2.0 directory. Maybe because the folder was stored in another folder when you have archived it. Thanks again for help.

One more question how can I disable log message in the bottom left corner?

3. Hi, if you follow the steps you will see that I didn't include the mathjax in the source.

And sorry, I got no idea how to disable the log message in the bottom left corner.

4. Also maybe you have sources. Can you share it with me? I can compare my version and yours. Thanks in advance.

5. Hi, updated with the source link, please have a look and hope that helps. Thanks.

6. Thanks, this was helpful in getting started. - mike

7. Any chance you can show a version that works with Swift? Thanks!

1. Sorry, due to other commitments, I haven't spend much time on learning Swift, and there's currently no plan for that.

Will post it here if there's any update in Swift in the future.

2. I just added some info about an example in Swift from another site above, please have a look and hope that helps. Cheers

8. I have chanced upon your site and this was exactly what I needed - to load MathJax locally to my iOS app. However, I am programming in Swift, and hence need some guidance with Swift. Could you help me with that?

1. Sorry, due to other commitments, I haven't spend much time on learning Swift, and there's currently no plan for that.

Will post it here if there's any update in Swift in the future.

2. I just added some info about an example in Swift from another site above, please have a look and hope that helps. Cheers

9. I have been trying this but no success. I think it is because I have not understood this step.
//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];

Will you please explain this. And where do I write the math part?

1. the "math" part is in the "xContent"

2. It's in the definition/declaration of "xContent" at the top of the code under section 7c.

NSString *xContent = @"......";