Showing posts with label MathJax. Show all posts
Showing posts with label MathJax. Show all posts

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.

Sunday, February 26, 2012

Tutorial - How to setup MathJax locally

[Update 02/03/2012] Please also have a look at this post - Tutorial - How to setup MathJax locally - "slim" edition

In this post last year, I talked about how to get MathJax working in your iPhone project. However that requires internet connection as the JavaScript in MathJax needs to connect back to their web site to get all the fonts and details, otherwise all those math formulas won't be displayed properly.

In this post I am going to talk about how I setup my iPhone/iPad project to use MathJax "locally" so that your math formulas can be displayed properly even if there's no Internet connection.


Before you start


A few things you have to be aware of:
1) Nothing is free, to provide this convenience, you have to add the 25+MB MathJax library into your Xcode project
2) As your final application will be over the 20MB limit, your users/customers won't be able to download it directly from the iPhone/iPad device, they have to connect it to a PC/Mac to down your application and future updates.
3) From my observation, I noticed when the JavaScript runs, it still tries to connect to their web site, and then it fall back to use the local copy (as shown in screen dumps below: 1st one says "Loading Web-Font TeX/Main/Regular", 2nd one says "Web-Fonts not available -- using image fonts instead").





If this is not what you expected, and you still want to use MathJax in your application, may be you should just follow the example in the previous MathJax post.


How was it done?


At a high level - using the same technique mentioned in the MathJax post last year, we will first write a HTML file locally and then display it using UIWebView. But the difference this time, is we will change the reference to the JavaScript to point to local directory, so it can fall back locally if internet connection is not available for whatever reason.


What's this JavaScript about?


If you look at any examples from the MathJax web site (for example: this one, or this one) and view source, you will see this link somewhere in the page.

<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/2.0-beta/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</head>

This JavaScript is the main component that makes the whole thing works. Our task is to change the link for it to point to the local copy of MathJax library, that is, change the part highlighted in red.


Steps Involved


1. Start a new project with single view, I am using the name "MathJaxLocal".

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-1.1a.zip (15.4MB)".

3. Expand the downloaded .ZIP file, to make it easier to differentiate between different version, I renamed the folder name to "mathjax-MathJax-v1.1a".

4. Drag and drop the whole "mathjax-MathJax-v1.1a" folder into your Xcode project. At the popup screen as shown below, make sure you click "Copy Items into destination group's folder (if needed)" and  "Create folder reference for any added folders". Otherwise the folder structure won't be created properly.

Please note that it's quite big and might take a few minutes depending on how powerful your machine is, so please be patient.


5. Once it's done, you should see as below a special blue coloured folder with all the MathJax files copied into your project. Feel free to expand it and explore around.


6. Now let's start changing the code, first add a UIWebView declaration. If you are lazy like me, just stick it somewhere in the beginning of your UIViewController file.


UIWebView *myWebView;


7. Next, let's add a new method, which will handle writing HTML file locally, I called it "writeStringToFile". It basically concatenates a few strings into a big one and then write to the specified path with specified filename.


-(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];

}

8.  Finally add the following code into your "viewDidLoad" or whichever method which you think appropriate. Note that I copied 2 examples from their web site, you 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"

     //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-v1.1a"];
    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];



You can now run your application, again it might take a while for 1st run due to the huge file size. The result of 1st example is shown below:


For 2nd example using "MathML", the result is as below:


I noticed there's this warning as shown below saying "Assigning to 'id' from incompatible type 'ViewController *'", which I don't know why as I have seen other examples on the net also doing the same thing - assigning UIWebView.delegate to "self". Let me know if any of you have any idea about this or how to get rid of it.

myWebView.delegate = self;




That's it, hope you find this helpful. And as usual, let me know if you need further information or have any better idea about this. Thanks!


[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>

Wednesday, July 27, 2011

UIWebView with MathJax working!

After a few tests, I was able to get a few weird Math formulas display working in a UIWebView using MathJax as shown in screen dump below.


The codes are based on examples from This page on MathJax site, I stripped out the CSS/JS files and converted into a long NSString as below. Then based on parameter passed on from the caller, concatenated into a long string, write it into a temp HTML file.


    NSString *foo = @"<html><head><meta name='viewport' content='initial-scale=1.0' />"
    "<script type='text/javascript' src='http://www.mathjax.org/wp-includes/js/l10n.js?ver=20101110'></script>"
    "<script type='text/javascript' src='http://www.mathjax.org/wp-content/plugins/syntax-highlighter-mt/scripts/shCore.js'></script>"
    "<script type='text/javascript' src='http://www.mathjax.org/wp-content/plugins/syntax-highlighter-mt/scripts/shAutoloader.js'></script>"
    "<link type='text/css' rel='stylesheet' href='http://www.mathjax.org/wp-content/plugins/syntax-highlighter-mt/styles/shCore.css'/>"
    "<link type='text/css' rel='stylesheet' href='http://www.mathjax.org/wp-content/plugins/syntax-highlighter-mt/styles/shThemeDefault.css'/>"
    "<style type='text/css'>body { background-color: #d2d3d3; }</style>"
    "<script type='text/x-mathjax-config'>"
    "MathJax.Hub.Config({ MMLorHTML: { prefer: {Firefox: \"HTML\"} } });"
    "</script>"
    "<script type='text/javascript' src='http://cdn.mathjax.org/mathjax/1.1-latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML-full'></script>"
    "</head>"
    "<body>";
    
    NSString *foo2 = [foo stringByAppendingString:strContent];
    NSString *foo3 = [foo2 stringByAppendingString:@"</body></html>"];


Then in the main file, it's loaded into an UIWebView, with the content (also extracted from same test page).


    NSString *fName = @"test1.html";
    
    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>";
    
    //write to temp file first
    [self writeStringToFile:tempDir fileName:fName 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;
        
    NSString *path4 = [tempDir stringByAppendingPathComponent:fName];
     
    NSURL* url = [NSURL fileURLWithPath:path4]; 
    NSURLRequest* req = [[NSURLRequest alloc] initWithURL:url]; 
    [myWebView loadRequest:req];
    [self.view addSubview:myWebView];
    [req release];
  
    activityIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activityIndicator.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
activityIndicator.center = self.view.center;
[self.view addSubview: activityIndicator];

Unfortunately, I don't know how to include the whole MathJax into the project yet (the size is 16MB even after compressed into .ZIP file!!!), so although the HTML is first written to local temp directory then loaded back, it still has to go out through Internet to load those .JS files - which might not be the best solution for "off-line" mode operation I guess.

The source project is available below. Note that for the content you have to change every "\" into "\\" otherwise it won't work as that's the escape character.


Source for UIWebViewTest

[Update 1] After tested from local directory, found that to have the whole MathJax fully functioning, you need:
(1) the main "MathJax.js" file, plus
(2) all the files under the 4 sub-directories: "config", "extensions", "fonts" and "jax".
Just that "fonts" one by itself is more than 10MB in size!
Definitely much easier to get everything from Internet then...

[Update 2] Further test shows that as below, the "*foo" can be even further simplified by removing some of the settings in the middle (about 9 lines) and it still works ok (Note: only tested in Safari 5.0.5).

    NSString *foo = @"<html><head><meta name='viewport' content='initial-scale=1.0' />"
    "<script type='text/javascript' src='http://cdn.mathjax.org/mathjax/1.1-latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML-full'></script>"
    "</head>"
    "<body>";

[Update 3] In case you wonder what does the following META tag mean, I will cover that in a separate post.
 "<meta name='viewport' content='initial-scale=1.0' />" 

[Update 4 (26/02/2012)] Please have a look at this new post about how to setup MathJax "Locally".

Friday, July 22, 2011

Any one knows how to create fractions, square roots and other math formulas?

Was looking for ways to create fractions, square roots and other math formulas by code in Objective-C but not very successful...

For example this 'Wolfram Math World' site has lots of stuffs (like the one using .GIF file shown below) but all done using .GIF files, wonder how they create those files?

[UPDATE 1] Saw some discussions about "MathJax", "jsMath" and a few other stuffs on the net, but most of them are about JavaScript libraries, not for Objective-C and none of them seem easy to beginners like me... Might have to park this idea for a while...

[UPDATE 2] Please see this post of mine which I got it working in a UIWebView.