MatchWebFonts.js 12 KB

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