Skip to content

Commit ff06343

Browse files
committed
Merge pull request #598 from dpvc/issue558-loop
Improve width computation for MathML elements (issue #558)
2 parents 37c282a + 8599abb commit ff06343

File tree

7 files changed

+386
-27
lines changed

7 files changed

+386
-27
lines changed

unpacked/MathJax.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -789,7 +789,7 @@ MathJax.fileversion = "2.2";
789789
check = BASE.Callback(check);
790790
check.execute = this.execute; check.time = this.time;
791791
check.STATUS = AJAX.STATUS; check.timeout = timeout || AJAX.timeout;
792-
check.delay = check.total = 0;
792+
check.delay = check.total = delay || 0;
793793
if (delay) {setTimeout(check,delay)} else {check()}
794794
},
795795
//
@@ -2251,6 +2251,12 @@ MathJax.Hub = {
22512251
},
22522252

22532253
elementScripts: function (element) {
2254+
if (element instanceof Array) {
2255+
var scripts = [];
2256+
for (var i = 0, m = element.length; i < m; i++)
2257+
{scripts.push.apply(scripts,this.elementScripts(element[i]))}
2258+
return scripts;
2259+
}
22542260
if (typeof(element) === 'string') {element = document.getElementById(element)}
22552261
if (!document.body) {document.body = document.getElementsByTagName("body")[0]}
22562262
if (element == null) {element = document.body}

unpacked/extensions/MatchWebFonts.js

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
2+
/* vim: set ts=2 et sw=2 tw=80: */
3+
4+
/*************************************************************
5+
*
6+
* MathJax/extensions/MatchWebFonts.js
7+
*
8+
* Adds code to the output jax so that if web fonts are used on the page,
9+
* MathJax will be able to detect their arrival and update the math to
10+
* accommodate the change in font. For the NativeMML output, this works
11+
* both for web fonts in main text, and for web fonts in the math as well.
12+
*
13+
* ---------------------------------------------------------------------
14+
*
15+
* Copyright (c) 2013 The MathJax Consortium
16+
*
17+
* Licensed under the Apache License, Version 2.0 (the "License");
18+
* you may not use this file except in compliance with the License.
19+
* You may obtain a copy of the License at
20+
*
21+
* http://www.apache.org/licenses/LICENSE-2.0
22+
*
23+
* Unless required by applicable law or agreed to in writing, software
24+
* distributed under the License is distributed on an "AS IS" BASIS,
25+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26+
* See the License for the specific language governing permissions and
27+
* limitations under the License.
28+
*/
29+
30+
(function (HUB,AJAX) {
31+
var VERSION = "2.2";
32+
33+
var CONFIG = MathJax.Hub.CombineConfig("MatchWebFonts",{
34+
matchFor: {
35+
"HTML-CSS": true,
36+
NativeMML: true,
37+
SVG: true
38+
},
39+
fontCheckDelay: 500, // initial delay for the first check for web fonts
40+
fontCheckTimeout: 15 * 1000, // how long to keep looking for fonts (15 seconds)
41+
});
42+
43+
var MATCH = MathJax.Extension.MatchWebFonts = {
44+
version: VERSION,
45+
config: CONFIG
46+
};
47+
48+
HUB.Register.StartupHook("HTML-CSS Jax Ready",function () {
49+
var HTMLCSS = MathJax.OutputJax["HTML-CSS"];
50+
var POSTTRANSLATE = HTMLCSS.postTranslate;
51+
52+
HTMLCSS.Augment({
53+
postTranslate: function (state,partial) {
54+
if (!partial && CONFIG.matchFor["HTML-CSS"] && this.config.matchFontHeight) {
55+
//
56+
// Check for changes in the web fonts that might affect the font
57+
// size for math elements. This is a periodic check that goes on
58+
// until a timeout is reached.
59+
//
60+
AJAX.timer.start(AJAX,["checkFonts",this,state.jax[this.id]],
61+
CONFIG.fontCheckDelay,CONFIG.fontCheckTimeout);
62+
}
63+
return POSTTRANSLATE.apply(this,arguments); // do the original function
64+
},
65+
66+
checkFonts: function (check,scripts) {
67+
if (check.time(function () {})) return;
68+
var size = [], i, m, retry = false;
69+
//
70+
// Add the elements used for testing ex and em sizes
71+
//
72+
for (i = 0, m = scripts.length; i < m; i++) {
73+
script = scripts[i];
74+
if (script.parentNode && script.MathJax.elementJax) {
75+
script.parentNode.insertBefore(this.EmExSpan.cloneNode(true),script);
76+
}
77+
}
78+
//
79+
// Check to see if anything has changed
80+
//
81+
for (i = 0, m = scripts.length; i < m; i++) {
82+
script = scripts[i]; if (!script.parentNode) continue; retry = true;
83+
var jax = script.MathJax.elementJax; if (!jax) continue;
84+
var span = document.getElementById(jax.inputID+"-Frame");
85+
//
86+
// Check if ex or mex has changed
87+
//
88+
var test = script.previousSibling;
89+
var ex = test.firstChild.offsetHeight/60;
90+
var em = test.lastChild.lastChild.offsetHeight/60;
91+
if (ex === 0 || ex === "NaN") {ex = this.defaultEx; em = this.defaultEm}
92+
if (ex !== jax.HTMLCSS.ex || em !== jax.HTMLCSS.em) {
93+
var scale = ex/this.TeX.x_height/em;
94+
scale = Math.floor(Math.max(this.config.minScaleAdjust/100,scale)*this.config.scale);
95+
if (scale/100 !== jax.scale) {size.push(script); scripts[i] = {}}
96+
}
97+
}
98+
//
99+
// Remove markers
100+
//
101+
for (i = 0, m = scripts.length; i < m; i++) {
102+
script = scripts[i];
103+
if (script.parentNode && script.MathJax.elementJax) {
104+
script.parentNode.removeChild(script.previousSibling);
105+
}
106+
}
107+
//
108+
// Rerender the changed items
109+
//
110+
if (size.length) {HUB.Queue(["Rerender",HUB,[size],{}])}
111+
//
112+
// Try again later
113+
//
114+
if (retry) {setTimeout(check,check.delay)}
115+
}
116+
});
117+
});
118+
119+
HUB.Register.StartupHook("SVG Jax Ready",function () {
120+
var SVG = MathJax.OutputJax.SVG;
121+
var POSTTRANSLATE = SVG.postTranslate;
122+
123+
SVG.Augment({
124+
postTranslate: function (state,partial) {
125+
if (!partial && CONFIG.matchFor.SVG) {
126+
//
127+
// Check for changes in the web fonts that might affect the font
128+
// size for math elements. This is a periodic check that goes on
129+
// until a timeout is reached.
130+
//
131+
AJAX.timer.start(AJAX,["checkFonts",this,state.jax[this.id]],
132+
CONFIG.fontCheckDelay,CONFIG.fontCheckTimeout);
133+
}
134+
return POSTTRANSLATE.apply(this,arguments); // do the original function
135+
},
136+
137+
checkFonts: function (check,scripts) {
138+
if (check.time(function () {})) return;
139+
var size = [], i, m, retry = false;
140+
//
141+
// Add the elements used for testing ex and em sizes
142+
//
143+
for (i = 0, m = scripts.length; i < m; i++) {
144+
script = scripts[i];
145+
if (script.parentNode && script.MathJax.elementJax) {
146+
script.parentNode.insertBefore(this.ExSpan.cloneNode(true),script);
147+
}
148+
}
149+
//
150+
// Check to see if anything has changed
151+
//
152+
for (i = 0, m = scripts.length; i < m; i++) {
153+
script = scripts[i]; if (!script.parentNode) continue; retry = true;
154+
var jax = script.MathJax.elementJax; if (!jax) continue;
155+
var span = document.getElementById(jax.inputID+"-Frame");
156+
//
157+
// Check if ex or mex has changed
158+
//
159+
var test = script.previousSibling;
160+
var ex = test.firstChild.offsetHeight/60;
161+
if (ex === 0 || ex === "NaN") {ex = this.defaultEx; em = this.defaultEm}
162+
if (ex !== jax.SVG.ex) {size.push(script); scripts[i] = {}}
163+
}
164+
//
165+
// Remove markers
166+
//
167+
for (i = 0, m = scripts.length; i < m; i++) {
168+
script = scripts[i];
169+
if (script.parentNode && script.MathJax.elementJax) {
170+
script.parentNode.removeChild(script.previousSibling);
171+
}
172+
}
173+
//
174+
// Rerender the changed items
175+
//
176+
if (size.length) {HUB.Queue(["Rerender",HUB,[size],{}])}
177+
//
178+
// Try again later (if not all the scripts are null)
179+
//
180+
181+
if (retry) setTimeout(check,check.delay);
182+
}
183+
});
184+
});
185+
186+
HUB.Register.StartupHook("NativeMML Jax Ready",function () {
187+
var nMML = MathJax.OutputJax.NativeMML;
188+
var POSTTRANSLATE = nMML.postTranslate;
189+
190+
nMML.Augment({
191+
postTranslate: function (state) {
192+
if (!HUB.Browser.isMSIE && CONFIG.matchFor.NativeMML) {
193+
//
194+
// Check for changes in the web fonts that might affect the sizes
195+
// of math elements. This is a periodic check that goes on until
196+
// a timeout is reached.
197+
//
198+
AJAX.timer.start(AJAX,["checkFonts",this,state.jax[this.id]],
199+
CONFIG.fontCheckDelay,CONFIG.fontCheckTimeout);
200+
}
201+
POSTTRANSLATE.apply(this,arguments); // do the original routine
202+
},
203+
204+
//
205+
// Check to see if web fonts have been loaded that change the ex size
206+
// of the surrounding font, the ex size within the math, or the widths
207+
// of math elements. We do this by rechecking the ex and mex sizes
208+
// (to see if the font scaling needs adjusting) and by checking the
209+
// size of the inner mrow of math elements and mtd elements. The
210+
// sizes of these have been stored in the NativeMML object of the
211+
// element jax so that we can check for them here.
212+
//
213+
checkFonts: function (check,scripts) {
214+
if (check.time(function () {})) return;
215+
var adjust = [], mtd = [], size = [], i, m, script;
216+
//
217+
// Add the elements used for testing ex and em sizes
218+
//
219+
for (i = 0, m = scripts.length; i < m; i++) {
220+
script = scripts[i];
221+
if (script.parentNode && script.MathJax.elementJax) {
222+
script.parentNode.insertBefore(this.EmExSpan.cloneNode(true),script);
223+
}
224+
}
225+
//
226+
// Check to see if anything has changed
227+
//
228+
for (i = 0, m = scripts.length; i < m; i++) {
229+
script = scripts[i]; if (!script.parentNode) continue;
230+
var jax = script.MathJax.elementJax; if (!jax) continue;
231+
var span = document.getElementById(jax.inputID+"-Frame");
232+
var math = span.getElementsByTagName("math")[0]; if (!math) continue;
233+
jax = jax.NativeMML;
234+
//
235+
// Check if ex or mex has changed
236+
//
237+
var test = script.previousSibling;
238+
var ex = test.firstChild.offsetWidth/60;
239+
var mex = test.lastChild.offsetWidth/60;
240+
if (ex === 0 || ex === "NaN") {ex = this.defaultEx; mex = this.defaultMEx}
241+
var newEx = (ex !== jax.ex);
242+
if (newEx || mex != jax.mex) {
243+
var scale = (this.config.matchFontHeight && mex > 1 ? ex/mex : 1);
244+
scale = Math.floor(Math.max(this.config.minScaleAdjust/100,scale) * this.config.scale);
245+
if (scale/100 !== jax.scale) {size.push([span.style,scale])}
246+
jax.scale = scale/100; jax.fontScale = scale+"%"; jax.ex = ex; jax.mex = mex;
247+
}
248+
249+
//
250+
// Check width of math elements
251+
//
252+
if ("scrollWidth" in jax && (newEx || jax.scrollWidth !== math.firstChild.scrollWidth)) {
253+
jax.scrollWidth = math.firstChild.scrollWidth;
254+
adjust.push([math.parentNode.style,jax.scrollWidth/jax.ex/jax.scale]);
255+
}
256+
//
257+
// Check widths of mtd elements
258+
//
259+
if (math.MathJaxMtds) {
260+
for (j = 0, n = math.MathJaxMtds.length; j < n; j++) {
261+
if (!math.MathJaxMtds[j].parentNode) continue;
262+
if (newEx || math.MathJaxMtds[j].firstChild.scrollWidth !== jax.mtds[j]) {
263+
jax.mtds[j] = math.MathJaxMtds[j].firstChild.scrollWidth;
264+
mtd.push([math.MathJaxMtds[j],jax.mtds[j]/jax.ex]);
265+
}
266+
}
267+
}
268+
}
269+
//
270+
// Remove markers
271+
//
272+
for (i = 0, m = scripts.length; i < m; i++) {
273+
script = scripts[i];
274+
if (script.parentNode && script.MathJax.elementJax) {
275+
script.parentNode.removeChild(script.previousSibling);
276+
}
277+
}
278+
//
279+
// Adjust scaling factor
280+
//
281+
for (i = 0, m = size.length; i < m; i++) {
282+
size[i][0].fontSize = size[i][1] + "%";
283+
}
284+
//
285+
// Adjust width of spans containing math elements that have changed
286+
//
287+
for (i = 0, m = adjust.length; i < m; i++) {
288+
adjust[i][0].width = adjust[i][1].toFixed(3)+"ex";
289+
}
290+
//
291+
// Adjust widths of mtd elements that have changed
292+
//
293+
for (i = 0, m = mtd.length; i < m; i++) {
294+
var style = mtd[i][0].getAttribute("style");
295+
style = style.replace(/(($|;)\s*min-width:).*?ex/,"$1 "+mtd[i][1].toFixed(3)+"ex");
296+
mtd[i][0].setAttribute("style",style);
297+
}
298+
//
299+
// Try again later
300+
//
301+
setTimeout(check,check.delay);
302+
}
303+
});
304+
});
305+
306+
HUB.Startup.signal.Post("MathWebFont Extension Ready");
307+
AJAX.loadComplete("[MathJax]/extensions/MatchWebFonts.js");
308+
309+
})(MathJax.Hub,MathJax.Ajax);

unpacked/jax/output/HTML-CSS/config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ MathJax.OutputJax["HTML-CSS"] = MathJax.OutputJax({
3535
webfontDir: MathJax.OutputJax.fontDir + "/HTML-CSS", // font name added later
3636

3737
config: {
38+
matchFontHeight: true, // try to match math font height to surrounding font?
3839
scale: 100, minScaleAdjust: 50, // global math scaling factor, and minimum adjusted scale factor
3940
availableFonts: ["STIX","TeX"], // list of local fonts to check for
4041
preferredFont: "TeX", // preferred local font (TeX or STIX)

unpacked/jax/output/HTML-CSS/jax.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,8 @@
534534
ex = this.defaultEx; em = this.defaultEm;
535535
if (relwidth) {maxwidth = this.defaultWidth}
536536
}
537-
scale = Math.floor(Math.max(this.config.minScaleAdjust/100,(ex/this.TeX.x_height)/em) * this.config.scale);
537+
scale = (this.config.matchFontHeight ? ex/this.TeX.x_height/em : 1);
538+
scale = Math.floor(Math.max(this.config.minScaleAdjust/100,scale)*this.config.scale);
538539
jax.HTMLCSS.scale = scale/100; jax.HTMLCSS.fontSize = scale+"%";
539540
jax.HTMLCSS.em = jax.HTMLCSS.outerEm = em; this.em = em * scale/100; jax.HTMLCSS.ex = ex;
540541
jax.HTMLCSS.lineWidth = (linebreak ? this.length2em(width,1,maxwidth/this.em) : 1000000);
@@ -584,7 +585,7 @@
584585
this.em = MML.mbase.prototype.em = jax.HTMLCSS.em * jax.HTMLCSS.scale;
585586
this.outerEm = jax.HTMLCSS.em; this.scale = jax.HTMLCSS.scale;
586587
this.linebreakWidth = jax.HTMLCSS.lineWidth;
587-
span.style.fontSize = jax.HTMLCSS.fontSize;
588+
if (this.scale !== 1) {span.style.fontSize = jax.HTMLCSS.fontSize}
588589
//
589590
// Typeset the math
590591
//
@@ -617,14 +618,14 @@
617618
//
618619
state.HTMLCSSeqn += (state.i - state.HTMLCSSi); state.HTMLCSSi = state.i;
619620
if (state.HTMLCSSeqn >= state.HTMLCSSlast + state.HTMLCSSchunk) {
620-
this.postTranslate(state);
621+
this.postTranslate(state,true);
621622
state.HTMLCSSchunk = Math.floor(state.HTMLCSSchunk*this.config.EqnChunkFactor);
622623
state.HTMLCSSdelay = true; // delay if there are more scripts
623624
}
624625
}
625626
},
626627

627-
postTranslate: function (state) {
628+
postTranslate: function (state,partial) {
628629
var scripts = state.jax[this.id];
629630
if (!this.hideProcessedMath) return;
630631
//
@@ -650,7 +651,7 @@
650651
}
651652
if (this.forceReflow) {
652653
// WebKit can misplace some elements that should wrap to the next line
653-
// but gets them right ona reflow, so force reflow by toggling a stylesheet
654+
// but gets them right on a reflow, so force reflow by toggling a stylesheet
654655
var sheet = (document.styleSheets||[])[0]||{};
655656
sheet.disabled = true; sheet.disabled = false;
656657
}
@@ -659,7 +660,7 @@
659660
//
660661
state.HTMLCSSlast = state.HTMLCSSeqn;
661662
},
662-
663+
663664
getJaxFromMath: function (math) {
664665
if (math.parentNode.className === "MathJax_Display") {math = math.parentNode}
665666
do {math = math.nextSibling} while (math && math.nodeName.toLowerCase() !== "script");

0 commit comments

Comments
 (0)