multiline.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  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/jax/output/SVG/autoload/multiline.js
  6. *
  7. * Implements the SVG output for <mrow>'s that contain line breaks.
  8. *
  9. * ---------------------------------------------------------------------
  10. *
  11. * Copyright (c) 2011-2013 The MathJax Consortium
  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. MathJax.Hub.Register.StartupHook("SVG Jax Ready",function () {
  26. var VERSION = "2.2";
  27. var MML = MathJax.ElementJax.mml,
  28. SVG = MathJax.OutputJax.SVG,
  29. BBOX = SVG.BBOX;
  30. //
  31. // Penalties for the various line breaks
  32. //
  33. var PENALTY = {
  34. newline: 0,
  35. nobreak: 1000000,
  36. goodbreak: [-200],
  37. badbreak: [+200],
  38. auto: [0],
  39. toobig: 800,
  40. nestfactor: 400,
  41. spacefactor: -100,
  42. spaceoffset: 2,
  43. spacelimit: 1, // spaces larger than this get a penalty boost
  44. fence: 500,
  45. close: 500
  46. };
  47. var ENDVALUES = {linebreakstyle: "after"};
  48. /**************************************************************************/
  49. MML.mrow.Augment({
  50. //
  51. // Handle breaking an mrow into separate lines
  52. //
  53. SVGmultiline: function (svg) {
  54. //
  55. // Find the parent element and mark it as multiline
  56. //
  57. var parent = this;
  58. while (parent.inferred || (parent.parent && parent.parent.type === "mrow" &&
  59. parent.parent.data.length === 1)) {parent = parent.parent}
  60. var isTop = ((parent.type === "math" && parent.Get("display") === "block") ||
  61. parent.type === "mtd");
  62. parent.isMultiline = true;
  63. //
  64. // Default values for the line-breaking parameters
  65. //
  66. var VALUES = this.getValues(
  67. "linebreak","linebreakstyle","lineleading","linebreakmultchar",
  68. "indentalign","indentshift",
  69. "indentalignfirst","indentshiftfirst",
  70. "indentalignlast","indentshiftlast"
  71. );
  72. if (VALUES.linebreakstyle === MML.LINEBREAKSTYLE.INFIXLINEBREAKSTYLE)
  73. {VALUES.linebreakstyle = this.Get("infixlinebreakstyle")}
  74. VALUES.lineleading = SVG.length2em(VALUES.lineleading,1,0.5);
  75. //
  76. // Start with a fresh SVG element
  77. // and make it full width if we are breaking to a specific width
  78. //
  79. svg = this.SVG();
  80. if (SVG.linebreakWidth < SVG.BIGDIMEN) {svg.w = SVG.linebreakWidth}
  81. else {svg.w = SVG.cwidth/SVG.em * 1000}
  82. var state = {
  83. n: 0, Y: 0,
  84. scale: this.SVGgetScale(),
  85. isTop: isTop,
  86. values: {},
  87. VALUES: VALUES
  88. },
  89. align = this.SVGgetAlign(state,{}),
  90. shift = this.SVGgetShift(state,{},align),
  91. start = [],
  92. end = {
  93. index:[], penalty:PENALTY.nobreak,
  94. w:0, W:shift, shift:shift, scanW:shift,
  95. nest: 0
  96. },
  97. broken = false;
  98. //
  99. // Break the expression at its best line breaks
  100. //
  101. while (this.SVGbetterBreak(end,state) &&
  102. (end.scanW >= SVG.linebreakWidth || end.penalty == PENALTY.newline)) {
  103. this.SVGaddLine(svg,start,end.index,state,end.values,broken);
  104. start = end.index.slice(0); broken = true;
  105. align = this.SVGgetAlign(state,end.values);
  106. shift = this.SVGgetShift(state,end.values,align);
  107. if (align === MML.INDENTALIGN.CENTER) {shift = 0}
  108. end.W = end.shift = end.scanW = shift; end.penalty = PENALTY.nobreak;
  109. }
  110. state.isLast = true;
  111. this.SVGaddLine(svg,start,[],state,ENDVALUES,broken);
  112. this.SVGhandleSpace(svg);
  113. this.SVGhandleColor(svg);
  114. svg.isMultiline = true;
  115. this.SVGsaveData(svg);
  116. return svg;
  117. }
  118. });
  119. /**************************************************************************/
  120. MML.mbase.Augment({
  121. SVGlinebreakPenalty: PENALTY,
  122. /****************************************************************/
  123. //
  124. // Locate the next linebreak that is better than the current one
  125. //
  126. SVGbetterBreak: function (info,state) {
  127. if (this.isToken) {return false} // FIXME: handle breaking of token elements
  128. if (this.isEmbellished()) {
  129. info.embellished = this;
  130. return this.CoreMO().SVGbetterBreak(info,state);
  131. }
  132. if (this.linebreakContainer) {return false}
  133. //
  134. // Get the current breakpoint position and other data
  135. //
  136. var index = info.index.slice(0), i = info.index.shift(),
  137. m = this.data.length, W, w, scanW, broken = (info.index.length > 0), better = false;
  138. if (i == null) {i = -1}; if (!broken) {i++; info.W += info.w; info.w = 0}
  139. scanW = info.scanW = info.W; info.nest++;
  140. //
  141. // Look through the line for breakpoints,
  142. // (as long as we are not too far past the breaking width)
  143. //
  144. while (i < m && info.scanW < 1.33*SVG.linebreakWidth) {
  145. if (this.data[i]) {
  146. if (this.data[i].SVGbetterBreak(info,state)) {
  147. better = true; index = [i].concat(info.index); W = info.W; w = info.w;
  148. if (info.penalty === PENALTY.newline) {info.index = index; info.nest--; return true}
  149. }
  150. scanW = (broken ? info.scanW : this.SVGaddWidth(i,info,scanW));
  151. }
  152. info.index = []; i++; broken = false;
  153. }
  154. info.nest--; info.index = index;
  155. if (better) {info.W = W}
  156. return better;
  157. },
  158. SVGaddWidth: function (i,info,scanW) {
  159. if (this.data[i]) {
  160. var svg = this.data[i].SVGdata;
  161. scanW += svg.w + svg.x; if (svg.X) {scanW += svg.X}
  162. info.W = info.scanW = scanW; info.w = 0;
  163. }
  164. return scanW;
  165. },
  166. /****************************************************************/
  167. //
  168. // Create a new line and move the required elements into it
  169. // Position it using proper alignment and indenting
  170. //
  171. SVGaddLine: function (svg,start,end,state,values,broken) {
  172. //
  173. // Create a box for the line, with empty BBox
  174. // fill it with the proper elements,
  175. // and clean up the bbox
  176. //
  177. var line = BBOX();
  178. state.first = broken; state.last = true;
  179. this.SVGmoveLine(start,end,line,state,values);
  180. line.Clean();
  181. //
  182. // Get the alignment and shift values
  183. //
  184. var align = this.SVGgetAlign(state,values),
  185. shift = this.SVGgetShift(state,values,align);
  186. //
  187. // Add in space for the shift
  188. //
  189. if (shift) {
  190. if (align === MML.INDENTALIGN.LEFT) {line.x = shift} else
  191. if (align === MML.INDENTALIGN.RIGHT) {line.w += shift; line.r = line.w}
  192. }
  193. //
  194. // Set the Y offset based on previous depth, leading, and current height
  195. //
  196. if (state.n > 0) {
  197. var LHD = SVG.FONTDATA.baselineskip * state.scale;
  198. var leading = (state.values.lineleading == null ? state.VALUES : state.values).lineleading;
  199. state.Y -= Math.max(LHD,state.d + line.h + leading);
  200. }
  201. //
  202. // Place the new line
  203. //
  204. svg.Align(line,align,0,state.Y);
  205. //
  206. // Save the values needed for the future
  207. //
  208. state.d = line.d; state.values = values; state.n++;
  209. },
  210. /****************************************************************/
  211. //
  212. // Get alignment and shift values from the given data
  213. //
  214. SVGgetAlign: function (state,values) {
  215. var cur = values, prev = state.values, def = state.VALUES, align;
  216. if (state.n === 0) {align = cur.indentalignfirst || prev.indentalignfirst || def.indentalignfirst}
  217. else if (state.isLast) {align = prev.indentalignlast || def.indentalignlast}
  218. else {align = prev.indentalign || def.indentalign}
  219. if (align === MML.INDENTALIGN.INDENTALIGN) {align = prev.indentalign || def.indentalign}
  220. if (align === MML.INDENTALIGN.AUTO) {align = (state.isTop ? this.displayAlign : MML.INDENTALIGN.LEFT)}
  221. return align;
  222. },
  223. SVGgetShift: function (state,values,align) {
  224. if (align === MML.INDENTALIGN.CENTER) {return 0}
  225. var cur = values, prev = state.values, def = state.VALUES, shift;
  226. if (state.n === 0) {shift = cur.indentshiftfirst || prev.indentshiftfirst || def.indentshiftfirst}
  227. else if (state.isLast) {shift = prev.indentshiftlast || def.indentshiftlast}
  228. else {shift = prev.indentshift || def.indentshift}
  229. if (shift === MML.INDENTSHIFT.INDENTSHIFT) {shift = prev.indentshift || def.indentshift}
  230. if (shift === "auto" || shift === "") {shift = (state.isTSop ? this.displayIndent : "0")}
  231. return SVG.length2em(shift,0);
  232. },
  233. /****************************************************************/
  234. //
  235. // Move the selected elements into the new line,
  236. // moving whole items when possible, and parts of ones
  237. // that are split by a line break.
  238. //
  239. SVGmoveLine: function (start,end,svg,state,values) {
  240. var i = start[0], j = end[0];
  241. if (i == null) {i = -1}; if (j == null) {j = this.data.length-1}
  242. if (i === j && start.length > 1) {
  243. //
  244. // If starting and ending in the same element move the subpiece to the new line
  245. //
  246. this.data[i].SVGmoveSlice(start.slice(1),end.slice(1),svg,state,values,"paddingLeft");
  247. } else {
  248. //
  249. // Otherwise, move the remainder of the initial item
  250. // and any others up to the last one
  251. //
  252. var last = state.last; state.last = false;
  253. while (i < j) {
  254. if (this.data[i]) {
  255. if (start.length <= 1) {this.data[i].SVGmove(svg,state,values)}
  256. else {this.data[i].SVGmoveSlice(start.slice(1),[],svg,state,values,"paddingLeft")}
  257. }
  258. i++; state.first = false; start = [];
  259. }
  260. //
  261. // If the last item is complete, move it,
  262. // otherwise move the first part of it up to the split
  263. //
  264. state.last = last;
  265. if (this.data[i]) {
  266. if (end.length <= 1) {this.data[i].SVGmove(svg,state,values)}
  267. else {this.data[i].SVGmoveSlice([],end.slice(1),svg,state,values,"paddingRight")}
  268. }
  269. }
  270. },
  271. /****************************************************************/
  272. //
  273. // Split an element and copy the selected items into the new part
  274. //
  275. SVGmoveSlice: function (start,end,svg,state,values,padding) {
  276. //
  277. // Create a new container for the slice of the element
  278. // Move the selected portion into the slice
  279. //
  280. var slice = BBOX();
  281. this.SVGmoveLine(start,end,slice,state,values);
  282. slice.Clean();
  283. this.SVGhandleColor(slice);
  284. svg.Add(slice,svg.w,0,true);
  285. return slice;
  286. },
  287. /****************************************************************/
  288. //
  289. // Move an element from its original position to its new location in
  290. // a split element or the new line's position
  291. //
  292. SVGmove: function (line,state,values) {
  293. // FIXME: handle linebreakstyle === "duplicate"
  294. // FIXME: handle linebreakmultchar
  295. if (!(state.first || state.last) ||
  296. (state.first && state.values.linebreakstyle === MML.LINEBREAKSTYLE.BEFORE) ||
  297. (state.last && values.linebreakstyle === MML.LINEBREAKSTYLE.AFTER)) {
  298. //
  299. // Recreate output
  300. // Remove padding (if first, remove at right, if last remove at left)
  301. // Add to line
  302. //
  303. var svg = this.toSVG(this.SVGdata.HW,this.SVGdata.D);
  304. if (state.last) {svg.x = 0}
  305. if (state.first || state.nextIsFirst) {delete state.nextIsFirst; if (svg.X) {svg.X = 0}}
  306. line.Add(svg,line.w,0,true);
  307. } else if (state.first) {state.nextIsFirst = true} else {delete state.nextIsFirst}
  308. }
  309. });
  310. /**************************************************************************/
  311. MML.mfenced.Augment({
  312. SVGbetterBreak: function (info,state) {
  313. //
  314. // Get the current breakpoint position and other data
  315. //
  316. var index = info.index.slice(0), i = info.index.shift(),
  317. m = this.data.length, W, w, scanW, broken = (info.index.length > 0), better = false;
  318. if (i == null) {i = -1}; if (!broken) {i++; info.W += info.w; info.w = 0}
  319. scanW = info.scanW = info.W; info.nest++;
  320. //
  321. // Create indices that include the delimiters and separators
  322. //
  323. if (!this.dataI) {
  324. this.dataI = [];
  325. if (this.data.open) {this.dataI.push("open")}
  326. if (m) {this.dataI.push(0)}
  327. for (var j = 1; j < m; j++) {
  328. if (this.data["sep"+j]) {this.dataI.push("sep"+j)}
  329. this.dataI.push(j);
  330. }
  331. if (this.data.close) {this.dataI.push("close")}
  332. }
  333. m = this.dataI.length;
  334. //
  335. // Look through the line for breakpoints, including the open, close, and separators
  336. // (as long as we are not too far past the breaking width)
  337. //
  338. while (i < m && info.scanW < 1.33*SVG.linebreakWidth) {
  339. var k = this.dataI[i];
  340. if (this.data[k]) {
  341. if (this.data[k].SVGbetterBreak(info,state)) {
  342. better = true; index = [i].concat(info.index); W = info.W; w = info.w;
  343. if (info.penalty === PENALTY.newline) {info.index = index; info.nest--; return true}
  344. }
  345. scanW = (broken ? info.scanW : this.SVGaddWidth(i,info,scanW));
  346. }
  347. info.index = []; i++; broken = false;
  348. }
  349. info.nest--; info.index = index;
  350. if (better) {info.W = W; info.w = w}
  351. return better;
  352. },
  353. SVGmoveLine: function (start,end,svg,state,values) {
  354. var i = start[0], j = end[0];
  355. if (i == null) {i = -1}; if (j == null) {j = this.dataI.length-1}
  356. if (i === j && start.length > 1) {
  357. //
  358. // If starting and ending in the same element move the subpiece to the new line
  359. //
  360. this.data[this.dataI[i]].SVGmoveSlice(start.slice(1),end.slice(1),svg,state,values,"paddingLeft");
  361. } else {
  362. //
  363. // Otherwise, move the remainder of the initial item
  364. // and any others (including open and separators) up to the last one
  365. //
  366. var last = state.last; state.last = false; var k = this.dataI[i];
  367. while (i < j) {
  368. if (this.data[k]) {
  369. if (start.length <= 1) {this.data[k].SVGmove(svg,state,values)}
  370. else {this.data[k].SVGmoveSlice(start.slice(1),[],svg,state,values,"paddingLeft")}
  371. }
  372. i++; k = this.dataI[i]; state.first = false; start = [];
  373. }
  374. //
  375. // If the last item is complete, move it
  376. //
  377. state.last = last;
  378. if (this.data[k]) {
  379. if (end.length <= 1) {this.data[k].SVGmove(svg,state,values)}
  380. else {this.data[k].SVGmoveSlice([],end.slice(1),svg,state,values,"paddingRight")}
  381. }
  382. }
  383. }
  384. });
  385. /**************************************************************************/
  386. MML.msubsup.Augment({
  387. SVGbetterBreak: function (info,state) {
  388. if (!this.data[this.base]) {return false}
  389. //
  390. // Get the current breakpoint position and other data
  391. //
  392. var index = info.index.slice(0), i = info.index.shift(),
  393. W, w, scanW, broken = (info.index.length > 0), better = false;
  394. if (!broken) {info.W += info.w; info.w = 0}
  395. scanW = info.scanW = info.W;
  396. //
  397. // Record the width of the base and the super- and subscripts
  398. //
  399. if (i == null) {this.SVGdata.dw = this.SVGdata.w - this.data[this.base].SVGdata.w}
  400. //
  401. // Check if the base can be broken
  402. //
  403. if (this.data[this.base].SVGbetterBreak(info,state)) {
  404. better = true; index = [this.base].concat(info.index); W = info.W; w = info.w;
  405. if (info.penalty === PENALTY.newline) {better = broken = true}
  406. }
  407. //
  408. // Add in the base if it is unbroken, and add the scripts
  409. //
  410. if (!broken) {this.SVGaddWidth(this.base,info,scanW)}
  411. info.scanW += this.SVGdata.dw; info.W = info.scanW;
  412. info.index = []; if (better) {info.W = W; info.w = w; info.index = index}
  413. return better;
  414. },
  415. SVGmoveLine: function (start,end,svg,state,values) {
  416. //
  417. // Move the proper part of the base
  418. //
  419. if (this.data[this.base]) {
  420. if (start.length > 1) {
  421. this.data[this.base].SVGmoveSlice(start.slice(1),end.slice(1),svg,state,values,"paddingLeft");
  422. } else {
  423. if (end.length <= 1) {this.data[this.base].SVGmove(svg,state,values)}
  424. else {this.data[this.base].SVGmoveSlice([],end.slice(1),svg,state,values,"paddingRight")}
  425. }
  426. }
  427. //
  428. // If this is the end, check for super and subscripts, and move those
  429. // by moving the stack tht contains them, and shifting by the amount of the
  430. // base that has been removed. Remove the empty base box from the stack.
  431. //
  432. if (end.length === 0) {
  433. var sup = this.data[this.sup], sub = this.data[this.sub], w = svg.w, data;
  434. if (sup) {data = sup.SVGdata; svg.Add(sup.toSVG(),w+(data.dx||0),data.dy)}
  435. if (sub) {data = sub.SVGdata; svg.Add(sub.toSVG(),w+(data.dx||0),data.dy)}
  436. }
  437. }
  438. });
  439. /**************************************************************************/
  440. MML.mo.Augment({
  441. //
  442. // Override the method for checking line breaks to properly handle <mo>
  443. //
  444. SVGbetterBreak: function (info,state) {
  445. if (info.values && info.values.last === this) {return false}
  446. var values = this.getValues(
  447. "linebreak","linebreakstyle","lineleading","linebreakmultchar",
  448. "indentalign","indentshift",
  449. "indentalignfirst","indentshiftfirst",
  450. "indentalignlast","indentshiftlast",
  451. "texClass", "fence"
  452. );
  453. if (values.linebreakstyle === MML.LINEBREAKSTYLE.INFIXLINEBREAKSTYLE)
  454. {values.linebreakstyle = this.Get("infixlinebreakstyle")}
  455. //
  456. // Adjust nesting by TeX class (helps output that does not include
  457. // mrows for nesting, but can leave these unbalanced.
  458. //
  459. if (values.texClass === MML.TEXCLASS.OPEN) {info.nest++}
  460. if (values.texClass === MML.TEXCLASS.CLOSE) {info.nest--}
  461. //
  462. // Get the default penalty for this location
  463. //
  464. var W = info.scanW, mo = (info.embellished||this); delete info.embellished;
  465. var svg = mo.SVGdata, w = svg.w + svg.x;
  466. if (values.linebreakstyle === MML.LINEBREAKSTYLE.AFTER) {W += w; w = 0}
  467. if (W - info.shift === 0) {return false} // don't break at zero width (FIXME?)
  468. var offset = SVG.linebreakWidth - W;
  469. // adjust offest for explicit first-line indent and align
  470. if (state.n === 0 && (values.indentshiftfirst !== state.VALUES.indentshiftfirst ||
  471. values.indentalignfirst !== state.VALUES.indentalignfirst)) {
  472. var align = this.SVGgetAlign(state,values),
  473. shift = this.SVGgetShift(state,values,align);
  474. offset += (info.shift - shift);
  475. }
  476. //
  477. var penalty = Math.floor(offset / SVG.linebreakWidth * 1000);
  478. if (penalty < 0) {penalty = PENALTY.toobig - 3*penalty}
  479. if (values.fence) {penalty += PENALTY.fence}
  480. if ((values.linebreakstyle === MML.LINEBREAKSTYLE.AFTER &&
  481. values.texClass === MML.TEXCLASS.OPEN) ||
  482. values.texClass === MML.TEXCLASS.CLOSE) {penalty += PENALTY.close}
  483. penalty += info.nest * PENALTY.nestfactor;
  484. //
  485. // Get the penalty for this type of break and
  486. // use it to modify the default penalty
  487. //
  488. var linebreak = PENALTY[values.linebreak||MML.LINEBREAK.AUTO];
  489. if (!(linebreak instanceof Array)) {
  490. // for breaks past the width, don't modify penalty
  491. if (offset >= 0) {penalty = linebreak * info.nest}
  492. } else {penalty = Math.max(1,penalty + linebreak[0] * info.nest)}
  493. //
  494. // If the penalty is no better than the current one, return false
  495. // Otherwise save the data for this breakpoint and return true
  496. //
  497. if (penalty >= info.penalty) {return false}
  498. info.penalty = penalty; info.values = values; info.W = W; info.w = w;
  499. values.lineleading = SVG.length2em(values.lineleading,state.VALUES.lineleading);
  500. values.last = this;
  501. return true;
  502. }
  503. });
  504. /**************************************************************************/
  505. MML.mspace.Augment({
  506. //
  507. // Override the method for checking line breaks to properly handle <mspace>
  508. //
  509. SVGbetterBreak: function (info,state) {
  510. if (info.values && info.values.last === this) {return false}
  511. var values = this.getValues("linebreak");
  512. var linebreakValue = values.linebreak;
  513. if (!linebreakValue || this.hasDimAttr()) {
  514. // The MathML spec says that the linebreak attribute should be ignored
  515. // if any dimensional attribute is set.
  516. linebreakValue = MML.LINEBREAK.AUTO;
  517. }
  518. //
  519. // Get the default penalty for this location
  520. //
  521. var W = info.scanW, svg = this.SVGdata, w = svg.w + svg.x;
  522. if (W - info.shift === 0) {return false} // don't break at zero width (FIXME?)
  523. var offset = SVG.linebreakWidth - W;
  524. //
  525. var penalty = Math.floor(offset / SVG.linebreakWidth * 1000);
  526. if (penalty < 0) {penalty = PENALTY.toobig - 3*penalty}
  527. penalty += info.nest * PENALTY.nestfactor;
  528. //
  529. // Get the penalty for this type of break and
  530. // use it to modify the default penalty
  531. //
  532. var linebreak = PENALTY[linebreakValue];
  533. if (linebreakValue === MML.LINEBREAK.AUTO && w >= PENALTY.spacelimit*1000)
  534. {linebreak = [(w+PENALTY.spaceoffset)*PENALTY.spacefactor]}
  535. if (!(linebreak instanceof Array)) {
  536. // for breaks past the width, don't modify penalty
  537. if (offset >= 0) {penalty = linebreak * info.nest}
  538. } else {penalty = Math.max(1,penalty + linebreak[0] * info.nest)}
  539. //
  540. // If the penalty is no better than the current one, return false
  541. // Otherwise save the data for this breakpoint and return true
  542. //
  543. if (penalty >= info.penalty) {return false}
  544. info.penalty = penalty; info.values = values; info.W = W; info.w = w;
  545. values.lineleading = state.VALUES.lineleading;
  546. values.linebreakstyle = "before"; values.last = this;
  547. return true;
  548. }
  549. });
  550. //
  551. // Hook into the mathchoice extension
  552. //
  553. MathJax.Hub.Register.StartupHook("TeX mathchoice Ready",function () {
  554. MML.TeXmathchoice.Augment({
  555. SVGbetterBreak: function (info,state) {
  556. return this.Core().SVGbetterBreak(info,state);
  557. },
  558. SVGmoveLine: function (start,end,svg,state,values) {
  559. return this.Core().SVGmoveSlice(start,end,svg,state,values);
  560. }
  561. });
  562. });
  563. //
  564. // Have maction process only the selected item
  565. //
  566. MML.maction.Augment({
  567. SVGbetterBreak: function (info,state) {
  568. return this.Core().SVGbetterBreak(info,state);
  569. },
  570. SVGmoveLine: function (start,end,svg,state,values) {
  571. return this.Core().SVGmoveSlice(start,end,svg,state,values);
  572. },
  573. });
  574. //
  575. // Have semantics only do the first element
  576. // (FIXME: do we need to do anything special about annotation-xml?)
  577. //
  578. MML.semantics.Augment({
  579. SVGbetterBreak: function (info,state) {
  580. return (this.data[0] ? this.data[0].SVGbetterBreak(info,state) : false);
  581. },
  582. SVGmoveLine: function (start,end,svg,state,values) {
  583. return (this.data[0] ? this.data[0].SVGmoveSlice(start,end,svg,state,values) : null);
  584. }
  585. });
  586. /**************************************************************************/
  587. MathJax.Hub.Startup.signal.Post("SVG multiline Ready");
  588. MathJax.Ajax.loadComplete(SVG.autoloadDir+"/multiline.js");
  589. });