re-add mathml.js
[lambda.git] / jsMath / plugins / spriteImageFonts.js
1 /*
2  *  plugins/spriteImageFonts.js
3  *  
4  *  Part of the jsMath package for mathematics on the web.
5  *
6  *  This file makes jsMath use single files for the image fonts
7  *  rather than individual images for each character.
8  *
9  *  ---------------------------------------------------------------------
10  *
11  *  Copyright 2006 by Davide P. Cervone
12  * 
13  *  Licensed under the Apache License, Version 2.0 (the "License");
14  *  you may not use this file except in compliance with the License.
15  *  You may obtain a copy of the License at
16  * 
17  *      http://www.apache.org/licenses/LICENSE-2.0
18  * 
19  *  Unless required by applicable law or agreed to in writing, software
20  *  distributed under the License is distributed on an "AS IS" BASIS,
21  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22  *  See the License for the specific language governing permissions and
23  *  limitations under the License.
24  */
25
26 if (!window.jsMath) {window.jsMath = {}}
27
28 jsMath.spriteImageFonts = {version: "1.1"};
29
30 if (!jsMath.styles) {jsMath.styles = []}
31
32 jsMath.styles['.typeset .img'] = {
33    position: 'relative',
34    display: 'inline-block',
35    overflow: 'hidden',
36   'background-repeat': 'no-repeat'
37 };
38 jsMath.styles['.typeset .img .xy'] = {
39   position: 'relative',
40   left: '0px', top: '0px'
41 };
42
43 // for Mozilla
44 jsMath.styles['.typeset .mimg'] = {position: 'relative'};
45 jsMath.styles['.typeset .mimg .size'] = {display: '-moz-inline-box'};
46 jsMath.styles['.typeset .mimg .wh'] = {
47   position: 'absolute',
48   left: '0px', top: '0px',
49   overflow: 'hidden'
50 };
51 jsMath.styles['.typeset .mimg .xy'] = {
52   position: 'relative',
53   left: '0px', top: '0px'
54 };
55 // for MSIE
56 jsMath.styles['.typeset .mimg .h'] = {
57   position: 'relative',
58   display: 'inline-block',
59   width: '0px'
60 };
61
62 /*
63  *  Replace the TeXIMG function with one that uses the sprite fonts
64  */
65 if (!jsMath.Box) {jsMath.Box = {}}
66   jsMath.Box.TeXIMG = function (font,C,size) {
67     var c = jsMath.TeX[font][C];
68     if (c.img.reload && jsMath.Img[c.img.reload][font].loaded == 1)
69       {delete c.img.reload; c.img.size = null}
70     if (c.img.size != null && c.img.size == size &&
71         c.img.best != null && c.img.best == jsMath.Img.best) return;
72     var mustScale = (jsMath.Img.scale != 1);
73     var id = jsMath.Img.best + size - 4;
74     if (id < 0) {id = 0; mustScale = 1} else
75     if (id >= jsMath.Img.fonts.length) {id = jsMath.Img.fonts.length-1; mustScale = 1}
76     var imgFont = jsMath.Img[jsMath.Img.fonts[id]][font];
77     if (!imgFont.loaded && jsMath.Browser.waitForImages) {
78       // store information so several fonts can be loaded at once
79       jsMath.Img.mustLoad[jsMath.Img.mustLoad.length] = [font,jsMath.Img.fonts[id]];
80       imgFont.loaded = -1;
81     }
82     var img = imgFont[C];
83     var scale = 1/jsMath.Img.w[jsMath.Img.fonts[id]];
84     if (id != jsMath.Img.best + size - 4) {
85       if (c.w != null) {scale = c.w/img[0]} else {
86         scale *= jsMath.Img.fonts[size]/jsMath.Img.fonts[4]
87               *  jsMath.Img.fonts[jsMath.Img.best]/jsMath.Img.fonts[id];
88       }
89     }
90
91     // get the metrics for the character glyph
92     var bScale = jsMath.Browser.imgScale;
93     if (img[3] == null) {img[3] = 0}
94     var w = (img[0]-img[3])*scale; var h = img[1]*scale; var d = -img[2]*scale;
95     var x = img[3]-imgFont.x[C%16]; var y = img[1]-img[2]-imgFont.y[Math.floor(C/16)];
96     var wh; var xy; var v;
97     var ladjust = ""; var resize = ""; var vadjust; var wadjust;
98
99     if ((mustScale || jsMath.Controls.cookie.scaleImg) && !jsMath.Browser.operaImageFonts) {
100       w += 2/jsMath.em; h += 2/jsMath.em; d -= 1/jsMath.em; y += 1; x += 1; // try to adjust for rounding errors
101       resize = "width:"+jsMath.HTML.Em(imgFont.wh[0]*scale*bScale)+";";
102       wh = "width:"+jsMath.HTML.Em(w*bScale)+";height:"+jsMath.HTML.Em(h*bScale)+";";
103       xy = "left:"+jsMath.HTML.Em(x*scale*bScale)+";top:"+jsMath.HTML.Em(y*scale*bScale)+";";
104       vadjust = "vertical-align:"+jsMath.HTML.Em(d*bScale)+";";
105       v = jsMath.HTML.Em(h+d);
106       if (img[3]) {ladjust = "margin-left:"+jsMath.HTML.Em(-img[3]*scale*bScale)+";"}
107     } else {
108       if (jsMath.Browser.msieAlphaBug && jsMath.Controls.cookie.alpha) {
109         resize = "height:"+(imgFont.wh[1]*jsMath.Browser.imgScale)+"px;"
110                + "width:"+(imgFont.wh[0]*jsMath.Browser.imgScale)+"px;";
111       }
112       wh = "width:"+img[0]+"px; height:"+img[1]+"px;";
113       xy = "left:"+x+"px; top:"+y+"px;";
114       vadjust = "vertical-align:"+(-img[2])+"px;"; v = (img[1]-img[2])+"px";
115       if (img[3]) {ladjust = "margin-left:"+(-img[3])+"px;"}
116     }
117
118     wadjust = (c.w == null || Math.abs(c.w-w) < .01)? "" : " margin-right:"+jsMath.HTML.Em(c.w-w)+';';
119     if (img[2] == 0 || jsMath.Browser.valignBug) {vadjust = ""}
120
121     // get the image
122     var URL = jsMath.Img.URL(font,jsMath.Img.fonts[id],C); var IMG;
123     if (jsMath.Browser.msieAlphaBug && jsMath.Controls.cookie.alpha) {
124       IMG = '<span class="xy" style="'+xy+'">'
125           +   '<img src="'+jsMath.blank+'" style="' + resize
126           +      'filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='
127           +      "'" + URL + "', sizingMethod='scale'" + ');" />'
128           + '</span>';
129     } else {
130       IMG = '<img src="'+URL+'" class="xy" style="'+xy+resize+'" />';
131     }
132     if (imgFont.loaded == -1) {IMG = ""; c.img.reload = jsMath.Img.fonts[id]}
133
134     // get the HTML for cropping the image
135     c.c = this.IMG(IMG,wh,vadjust,wadjust,ladjust,v,x,y,URL);
136     c.tclass = "normal";
137     c.img.bh = h+d; c.img.bd = -d;
138     c.img.size = size; c.img.best = jsMath.Img.best;
139   };
140
141   /*
142    *  Default uses inline-box containing an image
143    */
144   jsMath.Box.IMG = function (IMG,wh,vadjust,wadjust,ladjust,v,x,y,URL) {
145     return '<span class="img" style="'+wh+vadjust+wadjust+ladjust+'">'
146              + IMG + '</span>' + jsMath.Browser.msieImgFontBBoxFix;
147   };
148
149   /*
150    *  Opera bug in inline-block alignment forces use of background image
151    */
152   jsMath.Box.IMG_opera = function (IMG,wh,vadjust,wadjust,ladjust,v,x,y,URL) {
153     var html = '<span class="img" style="background-image: url('+URL+');'
154              +    wh + vadjust + 'background-position: '+x+'px '+y+'px"></span>';
155     if (wadjust || ladjust)
156       {html = '<span style="'+wadjust+ladjust+'">' + html + '</span>'}
157     return html;
158   };
159
160   /*
161    *  Mozilla's -moz-inline-box has top aligned with baseline, so adjust
162    */
163   jsMath.Box.IMG_mozilla = function (IMG,wh,vadjust,wadjust,ladjust,v,x,y,URL) {
164     vadjust = "vertical-align:"+v+";";
165     var html = '<span class="mimg" style="'+vadjust+'">'
166              +   '<span class="size" style="'+wh+'"></span>'
167              +   '<span class="wh" style="'+wh+'">' + IMG + '</span>'
168              + '</span>';
169     if (wadjust || ladjust)
170       {html = '<span style="'+wadjust+ladjust+'">' + html + '</span>'}
171     return html;
172   };
173
174   /*
175    *  MSIE screws up vadjust on inline-box elements, so use absolute
176    *  positioning and an extra span to set the width and height
177    */
178   jsMath.Box.IMG_msie = function (IMG,wh,vadjust,wadjust,ladjust,v,x,y,URL) {
179     var html = '<span class="mimg">'
180              +   '<span class="h" style="height:'+v+'">'
181              +     '<span class="wh" style="'+wh+'">' + IMG + '</span>'
182              +   '</span>'
183              +   '<span class="img" style="'+wh+vadjust+'"></span>'
184              + '</span>';
185     if (wadjust || ladjust) {
186       html = jsMath.Browser.msieSpaceFix
187            +    '<span style="'+wadjust+ladjust+'">' + html + '</span>';
188     }
189     return html;
190   };
191
192 if (!jsMath.Img) {jsMath.Img = {}}
193
194   /*
195    *  Called by the extra-font definition files to add an image font
196    *  into the mix (save offset data and image size)
197    */
198   jsMath.Img.AddFont = function (size,def) {
199     if (!jsMath.Img[size]) {jsMath.Img[size] = {}};
200     for (var font in def) {
201       def[font].x = def[font][128]; def[font].y = def[font][129];
202       def[font].wh = def[font][130];
203       delete def[font][128]; delete def[font][129]; delete def[font][130];
204     }
205     jsMath.Add(jsMath.Img[size],def);
206   };
207
208   /*
209    *  Get URL to directory for given font and size, based on the
210    *  user's alpha/plain setting
211    */
212   jsMath.Img.URL = function (name,size) {
213     if (size == null) {return this.root+name+'/font.js'}
214     var type = (jsMath.Controls.cookie.alpha) ? '/alpha/': '/plain/';
215     return this.root+name+type+size+'.png';
216   };
217
218   /*
219    *  Laod the data for an image font
220    */
221   jsMath.Img.LoadFont = function (name) {
222     if (jsMath.browser == 'OmniWeb' && !jsMath.Browser.hasInlineBlock) {
223       jsMath.noImgFonts = 1;
224       jsMath.Font.Check();
225       return;
226     }
227     if (!this.loaded) this.Init();
228     jsMath.Setup.Script(this.URL(name));
229   };
230   
231   /*
232    *  Setup for print mode
233    */
234   jsMath.Img.Init = function () {
235     if ((jsMath.Controls.cookie.print || jsMath.Controls.cookie.stayhires) && !jsMath.Browser.operaImgFonts) {
236       jsMath.Controls.cookie.print = jsMath.Controls.cookie.stayhires;
237       this.factor *= 3;
238       if (!jsMath.Controls.isLocalCookie || !jsMath.Global.isLocal) {jsMath.Controls.SetCookie(0)}
239       if (jsMath.Browser.alphaPrintBug) {jsMath.Controls.cookie.alpha = 0}
240     }
241     this.loaded = 1;
242     jsMath.Img.root = jsMath.root + "fonts-sprite/";
243     jsMath.version += "-sp" + jsMath.spriteImageFonts.version;
244   };
245
246
247 if (!jsMath.Browser) {jsMath.Browser = {}}
248   /*
249    * Hook into initialization routine
250    */
251   jsMath.Browser.Init = function () {
252     jsMath.browser = 'unknown';
253     this.TestInlineBlock();
254     this.TestSpanHeight();
255     this.TestRenameOK();
256     this.TestStyleChange();
257
258     this.MSIE();
259     this.Mozilla();
260     this.Opera();
261     this.OmniWeb();
262     this.Safari();
263     this.Konqueror();
264     
265     this.ImgFontInit();
266     
267     //
268     // Change some routines depending on the browser
269     // 
270     if (this.allowAbsoluteDelim) {
271       jsMath.Box.DelimExtend = jsMath.Box.DelimExtendAbsolute;
272       jsMath.Box.Layout = jsMath.Box.LayoutAbsolute;
273     } else {
274       jsMath.Box.DelimExtend = jsMath.Box.DelimExtendRelative;
275       jsMath.Box.Layout = jsMath.Box.LayoutRelative;
276     }
277     
278     if (this.separateSkips) {
279       jsMath.HTML.Place = jsMath.HTML.PlaceSeparateSkips;
280       jsMath.Typeset.prototype.Place = jsMath.Typeset.prototype.PlaceSeparateSkips;
281     }
282   },
283
284   /*
285    *  These should be part of the regular browser
286    *  test functions
287    */
288   jsMath.Browser.ImgFontInit = function () {
289     this.msieImgFontBBoxFix = '';
290     if (jsMath.browser == 'Mozilla') {
291       if (!jsMath.Browser.VersionAtLeast("3.0")) {jsMath.Box.IMG = jsMath.Box.IMG_mozilla}
292     } else if (jsMath.browser == 'Opera') {
293       if (!jsMath.Browser.VersionAtLeast("9.50")) {
294         this.operaImageFonts = 1;
295         jsMath.Box.IMG = jsMath.Box.IMG_opera;
296       }
297     } else if (jsMath.browser == 'MSIE') {
298       if (jsMath.platform == 'mac') {
299         this.msieImgFontBBoxFix = '<span style="display:none">x</span>'
300       } else {
301         jsMath.Parser.prototype.oldTypeset = jsMath.Parser.prototype.Typeset;
302         jsMath.Parser.prototype.Typeset = jsMath.Parser.prototype.msieTypeset;
303         jsMath.Img.mustLoad = [];
304         this.msieImageFonts = 1;
305         jsMath.Controls.defaults.alpha = 0;
306         if (!jsMath.Controls.userSet.alpha) {jsMath.Controls.cookie.alpha = 0}
307         jsMath.Box.IMG = jsMath.Box.IMG_msie;
308       }
309     }
310   };
311
312 if (!jsMath.Parser) {jsMath.Parser = {}}
313 if (!jsMath.Parser.prototype) {jsMath.Parser.prototype = {}}
314   /*
315    *  Handle loading of image files needed for this equation
316    *  (avoids MSIE bug where it will request the image more
317    *  than once if it is used more than once before it is
318    *  loaded.)
319    */
320   jsMath.Parser.prototype.msieTypeset = function () {
321     var HTML = this.oldTypeset();
322     if (jsMath.Img.mustLoad.length > 0) {
323       for (var i = 0; i < jsMath.Img.mustLoad.length; i++) {
324         var IMG = jsMath.Img.URL(jsMath.Img.mustLoad[i][0],jsMath.Img.mustLoad[i][1]);
325         jsMath.Script.WaitForImage(IMG);
326         jsMath.Img[jsMath.Img.mustLoad[i][1]][jsMath.Img.mustLoad[i][0]].loaded = 1;
327       }
328       jsMath.Img.mustLoad = [];
329       jsMath.Translate.restart = 1;
330       throw "restart";
331     }
332     return HTML;
333   };
334
335 /*
336  *  Override the control panel calls in order to
337  *  disable scaling in Opera.
338  */
339 if (!jsMath.Controls) {jsMath.Controls = {}}
340   /*
341    *  Add Opera calls to loading of control panel
342    */
343   jsMath.Controls.Panel = function () {
344     jsMath.Translate.Cancel();
345     if (this.loaded) {
346       this.Main();
347     } else {
348       jsMath.Script.delayedLoad(jsMath.root+"jsMath-controls.html");
349       jsMath.Script.Push(this,"OperaInit");
350     }
351   };
352   
353   /*
354    *  Disable hi-res fonts and image scaling in Opera
355    */
356   jsMath.Controls.OperaMain = function (init) {
357     if (!init) {this.OldMain()}
358     jsMath.Element("_resolution").disabled = true;
359   };
360   
361   jsMath.Controls.OperaOptions = function () {
362     this.OldOptions();
363     jsMath.Element("_scaleImg").disabled = true;
364     jsMath.Element("_scaleImgText").className = "disabled";
365   };
366   
367   jsMath.Controls.OperaInit = function () {
368     if (!jsMath.Browser.operaImageFonts) return;
369     this.OldMain = this.Main; this.Main = this.OperaMain;
370     this.OldOptions = this.Options; this.Options = this.OperaOptions;
371     this.OperaMain(1);
372   };