katex 0.10.1 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/CHANGELOG.md +141 -0
  2. package/LICENSE +1 -1
  3. package/README.md +6 -6
  4. package/cli.js +0 -0
  5. package/contrib/auto-render/auto-render.js +12 -3
  6. package/contrib/copy-tex/README.md +3 -5
  7. package/contrib/mathtex-script-type/README.md +12 -14
  8. package/contrib/mhchem/README.md +3 -1
  9. package/contrib/render-a11y-string/render-a11y-string.js +712 -0
  10. package/contrib/render-a11y-string/test/render-a11y-string-spec.js +526 -0
  11. package/dist/README.md +6 -6
  12. package/dist/contrib/auto-render.js +14 -3
  13. package/dist/contrib/auto-render.min.js +1 -1
  14. package/dist/contrib/auto-render.mjs +14 -3
  15. package/dist/contrib/mhchem.min.js +1 -1
  16. package/dist/contrib/render-a11y-string.js +870 -0
  17. package/dist/contrib/render-a11y-string.min.js +1 -0
  18. package/dist/contrib/render-a11y-string.mjs +753 -0
  19. package/dist/fonts/KaTeX_AMS-Regular.ttf +0 -0
  20. package/dist/fonts/KaTeX_AMS-Regular.woff +0 -0
  21. package/dist/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  22. package/dist/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  23. package/dist/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  24. package/dist/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  25. package/dist/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  26. package/dist/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  27. package/dist/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  28. package/dist/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  29. package/dist/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  30. package/dist/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  31. package/dist/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  32. package/dist/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  33. package/dist/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  34. package/dist/fonts/KaTeX_Main-Bold.ttf +0 -0
  35. package/dist/fonts/KaTeX_Main-Bold.woff +0 -0
  36. package/dist/fonts/KaTeX_Main-Bold.woff2 +0 -0
  37. package/dist/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  38. package/dist/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  39. package/dist/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  40. package/dist/fonts/KaTeX_Main-Italic.ttf +0 -0
  41. package/dist/fonts/KaTeX_Main-Italic.woff +0 -0
  42. package/dist/fonts/KaTeX_Main-Italic.woff2 +0 -0
  43. package/dist/fonts/KaTeX_Main-Regular.ttf +0 -0
  44. package/dist/fonts/KaTeX_Main-Regular.woff +0 -0
  45. package/dist/fonts/KaTeX_Main-Regular.woff2 +0 -0
  46. package/dist/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  47. package/dist/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  48. package/dist/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  49. package/dist/fonts/KaTeX_Math-Italic.ttf +0 -0
  50. package/dist/fonts/KaTeX_Math-Italic.woff +0 -0
  51. package/dist/fonts/KaTeX_Math-Italic.woff2 +0 -0
  52. package/dist/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  53. package/dist/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  54. package/dist/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  55. package/dist/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  56. package/dist/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  57. package/dist/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  58. package/dist/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  59. package/dist/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  60. package/dist/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  61. package/dist/fonts/KaTeX_Script-Regular.ttf +0 -0
  62. package/dist/fonts/KaTeX_Script-Regular.woff +0 -0
  63. package/dist/fonts/KaTeX_Script-Regular.woff2 +0 -0
  64. package/dist/fonts/KaTeX_Size1-Regular.ttf +0 -0
  65. package/dist/fonts/KaTeX_Size1-Regular.woff +0 -0
  66. package/dist/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  67. package/dist/fonts/KaTeX_Size2-Regular.ttf +0 -0
  68. package/dist/fonts/KaTeX_Size2-Regular.woff +0 -0
  69. package/dist/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  70. package/dist/fonts/KaTeX_Size3-Regular.ttf +0 -0
  71. package/dist/fonts/KaTeX_Size3-Regular.woff +0 -0
  72. package/dist/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  73. package/dist/fonts/KaTeX_Size4-Regular.ttf +0 -0
  74. package/dist/fonts/KaTeX_Size4-Regular.woff +0 -0
  75. package/dist/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  76. package/dist/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  77. package/dist/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  78. package/dist/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  79. package/dist/katex.css +34 -10
  80. package/dist/katex.js +2906 -2115
  81. package/dist/katex.min.css +1 -1
  82. package/dist/katex.min.js +1 -1
  83. package/dist/katex.mjs +2809 -2020
  84. package/package.json +12 -11
  85. package/src/Lexer.js +1 -0
  86. package/src/MacroExpander.js +39 -10
  87. package/src/Options.js +15 -75
  88. package/src/Parser.js +152 -115
  89. package/src/Settings.js +70 -7
  90. package/src/Token.js +2 -0
  91. package/src/buildCommon.js +24 -90
  92. package/src/buildHTML.js +31 -31
  93. package/src/buildMathML.js +52 -9
  94. package/src/buildTree.js +13 -6
  95. package/src/defineFunction.js +7 -22
  96. package/src/delimiter.js +66 -27
  97. package/src/domTree.js +71 -4
  98. package/src/environments/array.js +235 -25
  99. package/src/fontMetrics.js +11 -2
  100. package/src/functions/accent.js +9 -9
  101. package/src/functions/accentunder.js +2 -2
  102. package/src/functions/arrow.js +15 -5
  103. package/src/functions/color.js +9 -38
  104. package/src/functions/def.js +184 -0
  105. package/src/functions/delimsizing.js +32 -8
  106. package/src/functions/enclose.js +33 -6
  107. package/src/functions/font.js +4 -1
  108. package/src/functions/genfrac.js +39 -27
  109. package/src/functions/horizBrace.js +6 -7
  110. package/src/functions/href.js +16 -0
  111. package/src/functions/html.js +102 -0
  112. package/src/functions/includegraphics.js +153 -0
  113. package/src/functions/lap.js +4 -7
  114. package/src/functions/math.js +1 -5
  115. package/src/functions/mclass.js +41 -2
  116. package/src/functions/op.js +27 -111
  117. package/src/functions/operatorname.js +136 -92
  118. package/src/functions/ordgroup.js +1 -1
  119. package/src/functions/overline.js +3 -2
  120. package/src/functions/phantom.js +5 -2
  121. package/src/functions/raisebox.js +4 -16
  122. package/src/functions/rule.js +20 -9
  123. package/src/functions/styling.js +0 -9
  124. package/src/functions/supsub.js +27 -7
  125. package/src/functions/symbolsOp.js +4 -0
  126. package/src/functions/tag.js +20 -4
  127. package/src/functions/text.js +4 -3
  128. package/src/functions/underline.js +3 -2
  129. package/src/functions/utils/assembleSupSub.js +110 -0
  130. package/src/functions.js +3 -0
  131. package/src/katex.less +45 -9
  132. package/src/macros.js +259 -98
  133. package/src/mathMLTree.js +6 -4
  134. package/src/parseNode.js +37 -57
  135. package/src/stretchy.js +3 -1
  136. package/src/svgGeometry.js +136 -44
  137. package/src/symbols.js +52 -69
  138. package/src/tree.js +2 -2
  139. package/src/types.js +2 -1
  140. package/src/unicodeAccents.js +3 -1
  141. package/src/unicodeSymbols.js +30 -321
  142. package/src/utils.js +10 -0
  143. package/src/wide-character.js +2 -2
  144. package/src/unicodeMake.js +0 -70
@@ -1,11 +1,12 @@
1
1
  // @flow
2
2
  import buildCommon from "../buildCommon";
3
+ import Style from "../Style";
3
4
  import defineEnvironment from "../defineEnvironment";
4
5
  import defineFunction from "../defineFunction";
5
6
  import mathMLTree from "../mathMLTree";
6
7
  import ParseError from "../ParseError";
7
8
  import {assertNodeType, assertSymbolNodeType} from "../parseNode";
8
- import {checkNodeType, checkSymbolNodeType} from "../parseNode";
9
+ import {checkSymbolNodeType} from "../parseNode";
9
10
  import {calculateSize} from "../units";
10
11
  import utils from "../utils";
11
12
 
@@ -25,17 +26,20 @@ export type AlignSpec = { type: "separator", separator: string } | {
25
26
  postgap?: number,
26
27
  };
27
28
 
29
+ // Type to indicate column separation in MathML
30
+ export type ColSeparationType = "align" | "alignat" | "small";
31
+
28
32
  function getHLines(parser: Parser): boolean[] {
29
33
  // Return an array. The array length = number of hlines.
30
34
  // Each element in the array tells if the line is dashed.
31
35
  const hlineInfo = [];
32
36
  parser.consumeSpaces();
33
- let nxt = parser.nextToken.text;
37
+ let nxt = parser.fetch().text;
34
38
  while (nxt === "\\hline" || nxt === "\\hdashline") {
35
39
  parser.consume();
36
40
  hlineInfo.push(nxt === "\\hdashline");
37
41
  parser.consumeSpaces();
38
- nxt = parser.nextToken.text;
42
+ nxt = parser.fetch().text;
39
43
  }
40
44
  return hlineInfo;
41
45
  }
@@ -48,11 +52,12 @@ function getHLines(parser: Parser): boolean[] {
48
52
  */
49
53
  function parseArray(
50
54
  parser: Parser,
51
- {hskipBeforeAndAfter, addJot, cols, arraystretch}: {|
55
+ {hskipBeforeAndAfter, addJot, cols, arraystretch, colSeparationType}: {|
52
56
  hskipBeforeAndAfter?: boolean,
53
57
  addJot?: boolean,
54
58
  cols?: AlignSpec[],
55
59
  arraystretch?: number,
60
+ colSeparationType?: ColSeparationType,
56
61
  |},
57
62
  style: StyleStr,
58
63
  ): ParseNode<"array"> {
@@ -74,6 +79,9 @@ function parseArray(
74
79
  }
75
80
  }
76
81
 
82
+ // Start group for first cell
83
+ parser.gullet.beginGroup();
84
+
77
85
  let row = [];
78
86
  const body = [row];
79
87
  const rowGaps = [];
@@ -83,7 +91,11 @@ function parseArray(
83
91
  hLinesBeforeRow.push(getHLines(parser));
84
92
 
85
93
  while (true) { // eslint-disable-line no-constant-condition
94
+ // Parse each cell in its own group (namespace)
86
95
  let cell = parser.parseExpression(false, "\\cr");
96
+ parser.gullet.endGroup();
97
+ parser.gullet.beginGroup();
98
+
87
99
  cell = {
88
100
  type: "ordgroup",
89
101
  mode: parser.mode,
@@ -98,7 +110,7 @@ function parseArray(
98
110
  };
99
111
  }
100
112
  row.push(cell);
101
- const next = parser.nextToken.text;
113
+ const next = parser.fetch().text;
102
114
  if (next === "&") {
103
115
  parser.consume();
104
116
  } else if (next === "\\end") {
@@ -127,7 +139,12 @@ function parseArray(
127
139
  parser.nextToken);
128
140
  }
129
141
  }
142
+
143
+ // End cell group
144
+ parser.gullet.endGroup();
145
+ // End array group defining \\
130
146
  parser.gullet.endGroup();
147
+
131
148
  return {
132
149
  type: "array",
133
150
  mode: parser.mode,
@@ -138,6 +155,7 @@ function parseArray(
138
155
  rowGaps,
139
156
  hskipBeforeAndAfter,
140
157
  hLinesBeforeRow,
158
+ colSeparationType,
141
159
  };
142
160
  }
143
161
 
@@ -168,9 +186,24 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
168
186
  let body = new Array(nr);
169
187
  const hlines = [];
170
188
 
189
+ const ruleThickness = Math.max(
190
+ // From LaTeX \showthe\arrayrulewidth. Equals 0.04 em.
191
+ (options.fontMetrics().arrayRuleWidth),
192
+ options.minRuleThickness, // User override.
193
+ );
194
+
171
195
  // Horizontal spacing
172
196
  const pt = 1 / options.fontMetrics().ptPerEm;
173
- const arraycolsep = 5 * pt; // \arraycolsep in article.cls
197
+ let arraycolsep = 5 * pt; // default value, i.e. \arraycolsep in article.cls
198
+ if (group.colSeparationType && group.colSeparationType === "small") {
199
+ // We're in a {smallmatrix}. Default column space is \thickspace,
200
+ // i.e. 5/18em = 0.2778em, per amsmath.dtx for {smallmatrix}.
201
+ // But that needs adjustment because LaTeX applies \scriptstyle to the
202
+ // entire array, including the colspace, but this function applies
203
+ // \scriptstyle only inside each element.
204
+ const localMultiplier = options.havingStyle(Style.SCRIPT).sizeMultiplier;
205
+ arraycolsep = 0.2778 * (localMultiplier / options.sizeMultiplier);
206
+ }
174
207
 
175
208
  // Vertical spacing
176
209
  const baselineskip = 12 * pt; // see size10.clo
@@ -269,20 +302,15 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
269
302
  cols.push(colSep);
270
303
  }
271
304
 
272
- if (colDescr.separator === "|") {
305
+ if (colDescr.separator === "|" || colDescr.separator === ":") {
306
+ const lineType = (colDescr.separator === "|") ? "solid" : "dashed";
273
307
  const separator = buildCommon.makeSpan(
274
308
  ["vertical-separator"], [], options
275
309
  );
276
310
  separator.style.height = totalHeight + "em";
277
- separator.style.verticalAlign =
278
- -(totalHeight - offset) + "em";
279
-
280
- cols.push(separator);
281
- } else if (colDescr.separator === ":") {
282
- const separator = buildCommon.makeSpan(
283
- ["vertical-separator", "vs-dashed"], [], options
284
- );
285
- separator.style.height = totalHeight + "em";
311
+ separator.style.borderRightWidth = `${ruleThickness}em`;
312
+ separator.style.borderRightStyle = lineType;
313
+ separator.style.margin = `0 -${ruleThickness / 2}em`;
286
314
  separator.style.verticalAlign =
287
315
  -(totalHeight - offset) + "em";
288
316
 
@@ -346,8 +374,9 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
346
374
 
347
375
  // Add \hline(s), if any.
348
376
  if (hlines.length > 0) {
349
- const line = buildCommon.makeLineSpan("hline", options, 0.05);
350
- const dashes = buildCommon.makeLineSpan("hdashline", options, 0.05);
377
+ const line = buildCommon.makeLineSpan("hline", options, ruleThickness);
378
+ const dashes = buildCommon.makeLineSpan("hdashline", options,
379
+ ruleThickness);
351
380
  const vListElems = [{type: "elem", elem: body, shift: 0}];
352
381
  while (hlines.length > 0) {
353
382
  const hline = hlines.pop();
@@ -367,8 +396,14 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
367
396
  return buildCommon.makeSpan(["mord"], [body], options);
368
397
  };
369
398
 
399
+ const alignMap = {
400
+ c: "center ",
401
+ l: "left ",
402
+ r: "right ",
403
+ };
404
+
370
405
  const mathmlBuilder: MathMLBuilder<"array"> = function(group, options) {
371
- return new mathMLTree.MathNode(
406
+ let table = new mathMLTree.MathNode(
372
407
  "mtable", group.body.map(function(row) {
373
408
  return new mathMLTree.MathNode(
374
409
  "mtr", row.map(function(cell) {
@@ -376,6 +411,119 @@ const mathmlBuilder: MathMLBuilder<"array"> = function(group, options) {
376
411
  "mtd", [mml.buildGroup(cell, options)]);
377
412
  }));
378
413
  }));
414
+
415
+ // Set column alignment, row spacing, column spacing, and
416
+ // array lines by setting attributes on the table element.
417
+
418
+ // Set the row spacing. In MathML, we specify a gap distance.
419
+ // We do not use rowGap[] because MathML automatically increases
420
+ // cell height with the height/depth of the element content.
421
+
422
+ // LaTeX \arraystretch multiplies the row baseline-to-baseline distance.
423
+ // We simulate this by adding (arraystretch - 1)em to the gap. This
424
+ // does a reasonable job of adjusting arrays containing 1 em tall content.
425
+
426
+ // The 0.16 and 0.09 values are found emprically. They produce an array
427
+ // similar to LaTeX and in which content does not interfere with \hines.
428
+ const gap = (group.arraystretch === 0.5)
429
+ ? 0.1 // {smallmatrix}, {subarray}
430
+ : 0.16 + group.arraystretch - 1 + (group.addJot ? 0.09 : 0);
431
+ table.setAttribute("rowspacing", gap + "em");
432
+
433
+ // MathML table lines go only between cells.
434
+ // To place a line on an edge we'll use <menclose>, if necessary.
435
+ let menclose = "";
436
+ let align = "";
437
+
438
+ if (group.cols && group.cols.length > 0) {
439
+ // Find column alignment, column spacing, and vertical lines.
440
+ const cols = group.cols;
441
+ let columnLines = "";
442
+ let prevTypeWasAlign = false;
443
+ let iStart = 0;
444
+ let iEnd = cols.length;
445
+
446
+ if (cols[0].type === "separator") {
447
+ menclose += "top ";
448
+ iStart = 1;
449
+ }
450
+ if (cols[cols.length - 1].type === "separator") {
451
+ menclose += "bottom ";
452
+ iEnd -= 1;
453
+ }
454
+
455
+ for (let i = iStart; i < iEnd; i++) {
456
+ if (cols[i].type === "align") {
457
+ align += alignMap[cols[i].align];
458
+
459
+ if (prevTypeWasAlign) {
460
+ columnLines += "none ";
461
+ }
462
+ prevTypeWasAlign = true;
463
+ } else if (cols[i].type === "separator") {
464
+ // MathML accepts only single lines between cells.
465
+ // So we read only the first of consecutive separators.
466
+ if (prevTypeWasAlign) {
467
+ columnLines += cols[i].separator === "|"
468
+ ? "solid "
469
+ : "dashed ";
470
+ prevTypeWasAlign = false;
471
+ }
472
+ }
473
+ }
474
+
475
+ table.setAttribute("columnalign", align.trim());
476
+
477
+ if (/[sd]/.test(columnLines)) {
478
+ table.setAttribute("columnlines", columnLines.trim());
479
+ }
480
+ }
481
+
482
+ // Set column spacing.
483
+ if (group.colSeparationType === "align") {
484
+ const cols = group.cols || [];
485
+ let spacing = "";
486
+ for (let i = 1; i < cols.length; i++) {
487
+ spacing += i % 2 ? "0em " : "1em ";
488
+ }
489
+ table.setAttribute("columnspacing", spacing.trim());
490
+ } else if (group.colSeparationType === "alignat") {
491
+ table.setAttribute("columnspacing", "0em");
492
+ } else if (group.colSeparationType === "small") {
493
+ table.setAttribute("columnspacing", "0.2778em");
494
+ } else {
495
+ table.setAttribute("columnspacing", "1em");
496
+ }
497
+
498
+ // Address \hline and \hdashline
499
+ let rowLines = "";
500
+ const hlines = group.hLinesBeforeRow;
501
+
502
+ menclose += hlines[0].length > 0 ? "left " : "";
503
+ menclose += hlines[hlines.length - 1].length > 0 ? "right " : "";
504
+
505
+ for (let i = 1; i < hlines.length - 1; i++) {
506
+ rowLines += (hlines[i].length === 0)
507
+ ? "none "
508
+ // MathML accepts only a single line between rows. Read one element.
509
+ : hlines[i][0] ? "dashed " : "solid ";
510
+ }
511
+ if (/[sd]/.test(rowLines)) {
512
+ table.setAttribute("rowlines", rowLines.trim());
513
+ }
514
+
515
+ if (menclose !== "") {
516
+ table = new mathMLTree.MathNode("menclose", [table]);
517
+ table.setAttribute("notation", menclose.trim());
518
+ }
519
+
520
+ if (group.arraystretch && group.arraystretch < 1) {
521
+ // A small array. Wrap in scriptstyle so row gap is not too large.
522
+ table = new mathMLTree.MathNode("mstyle", [table]);
523
+ table.setAttribute("scriptlevel", "1");
524
+ }
525
+
526
+ return table;
379
527
  };
380
528
 
381
529
  // Convenience function for aligned and alignedat environments.
@@ -399,11 +547,10 @@ const alignedHandler = function(context, args) {
399
547
  mode: context.mode,
400
548
  body: [],
401
549
  };
402
- const ordgroup = checkNodeType(args[0], "ordgroup");
403
- if (ordgroup) {
550
+ if (args[0] && args[0].type === "ordgroup") {
404
551
  let arg0 = "";
405
- for (let i = 0; i < ordgroup.body.length; i++) {
406
- const textord = assertNodeType(ordgroup.body[i], "textord");
552
+ for (let i = 0; i < args[0].body.length; i++) {
553
+ const textord = assertNodeType(args[0].body[i], "textord");
407
554
  arg0 += textord.text;
408
555
  }
409
556
  numMaths = Number(arg0);
@@ -448,6 +595,7 @@ const alignedHandler = function(context, args) {
448
595
  postgap: 0,
449
596
  };
450
597
  }
598
+ res.colSeparationType = isAligned ? "align" : "alignat";
451
599
  return res;
452
600
  };
453
601
 
@@ -534,22 +682,83 @@ defineEnvironment({
534
682
  body: [res],
535
683
  left: delimiters[0],
536
684
  right: delimiters[1],
685
+ rightColor: undefined, // \right uninfluenced by \color in array
537
686
  } : res;
538
687
  },
539
688
  htmlBuilder,
540
689
  mathmlBuilder,
541
690
  });
542
691
 
692
+ defineEnvironment({
693
+ type: "array",
694
+ names: ["smallmatrix"],
695
+ props: {
696
+ numArgs: 0,
697
+ },
698
+ handler(context) {
699
+ const payload = {arraystretch: 0.5};
700
+ const res = parseArray(context.parser, payload, "script");
701
+ res.colSeparationType = "small";
702
+ return res;
703
+ },
704
+ htmlBuilder,
705
+ mathmlBuilder,
706
+ });
707
+
708
+ defineEnvironment({
709
+ type: "array",
710
+ names: ["subarray"],
711
+ props: {
712
+ numArgs: 1,
713
+ },
714
+ handler(context, args) {
715
+ // Parsing of {subarray} is similar to {array}
716
+ const symNode = checkSymbolNodeType(args[0]);
717
+ const colalign: AnyParseNode[] =
718
+ symNode ? [args[0]] : assertNodeType(args[0], "ordgroup").body;
719
+ const cols = colalign.map(function(nde) {
720
+ const node = assertSymbolNodeType(nde);
721
+ const ca = node.text;
722
+ // {subarray} only recognizes "l" & "c"
723
+ if ("lc".indexOf(ca) !== -1) {
724
+ return {
725
+ type: "align",
726
+ align: ca,
727
+ };
728
+ }
729
+ throw new ParseError("Unknown column alignment: " + ca, nde);
730
+ });
731
+ if (cols.length > 1) {
732
+ throw new ParseError("{subarray} can contain only one column");
733
+ }
734
+ let res = {
735
+ cols,
736
+ hskipBeforeAndAfter: false,
737
+ arraystretch: 0.5,
738
+ };
739
+ res = parseArray(context.parser, res, "script");
740
+ if (res.body.length > 0 && res.body[0].length > 1) {
741
+ throw new ParseError("{subarray} can contain only one column");
742
+ }
743
+ return res;
744
+ },
745
+ htmlBuilder,
746
+ mathmlBuilder,
747
+ });
748
+
543
749
  // A cases environment (in amsmath.sty) is almost equivalent to
544
750
  // \def\arraystretch{1.2}%
545
751
  // \left\{\begin{array}{@{}l@{\quad}l@{}} … \end{array}\right.
546
752
  // {dcases} is a {cases} environment where cells are set in \displaystyle,
547
753
  // as defined in mathtools.sty.
754
+ // {rcases} is another mathtools environment. It's brace is on the right side.
548
755
  defineEnvironment({
549
756
  type: "array",
550
757
  names: [
551
758
  "cases",
552
759
  "dcases",
760
+ "rcases",
761
+ "drcases",
553
762
  ],
554
763
  props: {
555
764
  numArgs: 0,
@@ -579,8 +788,9 @@ defineEnvironment({
579
788
  type: "leftright",
580
789
  mode: context.mode,
581
790
  body: [res],
582
- left: "\\{",
583
- right: ".",
791
+ left: context.envName.indexOf("r") > -1 ? "." : "\\{",
792
+ right: context.envName.indexOf("r") > -1 ? "\\}" : ".",
793
+ rightColor: undefined,
584
794
  };
585
795
  },
586
796
  htmlBuilder,
@@ -82,6 +82,14 @@ const sigmasAndXis = {
82
82
  // The space between adjacent `|` columns in an array definition. From
83
83
  // `\showthe\doublerulesep` in LaTeX. Equals 2.0 / ptPerEm.
84
84
  doubleRuleSep: [0.2, 0.2, 0.2],
85
+
86
+ // The width of separator lines in {array} environments. From
87
+ // `\showthe\arrayrulewidth` in LaTeX. Equals 0.4 / ptPerEm.
88
+ arrayRuleWidth: [0.04, 0.04, 0.04],
89
+
90
+ // Two values from LaTeX source2e:
91
+ fboxsep: [0.3, 0.3, 0.3], // 3 pt / ptPerEm
92
+ fboxrule: [0.04, 0.04, 0.04], // 0.4 pt / ptPerEm
85
93
  };
86
94
 
87
95
  // This map contains a mapping from font name and character code to character
@@ -211,10 +219,11 @@ export function getCharacterMetrics(
211
219
  throw new Error(`Font metrics not found for font: ${font}.`);
212
220
  }
213
221
  let ch = character.charCodeAt(0);
214
- if (character[0] in extraCharacterMap) {
222
+ let metrics = metricMap[font][ch];
223
+ if (!metrics && character[0] in extraCharacterMap) {
215
224
  ch = extraCharacterMap[character[0]].charCodeAt(0);
225
+ metrics = metricMap[font][ch];
216
226
  }
217
- let metrics = metricMap[font][ch];
218
227
 
219
228
  if (!metrics && mode === 'text') {
220
229
  // We don't typically have font metrics for Asian scripts.
@@ -4,7 +4,7 @@ import buildCommon from "../buildCommon";
4
4
  import mathMLTree from "../mathMLTree";
5
5
  import utils from "../utils";
6
6
  import stretchy from "../stretchy";
7
- import {assertNodeType, checkNodeType} from "../parseNode";
7
+ import {assertNodeType} from "../parseNode";
8
8
  import {assertSpan, assertSymbolDomNode} from "../domTree";
9
9
 
10
10
  import * as html from "../buildHTML";
@@ -20,9 +20,8 @@ export const htmlBuilder: HtmlBuilderSupSub<"accent"> = (grp, options) => {
20
20
  let base: AnyParseNode;
21
21
  let group: ParseNode<"accent">;
22
22
 
23
- const supSub: ?ParseNode<"supsub"> = checkNodeType(grp, "supsub");
24
23
  let supSubGroup;
25
- if (supSub) {
24
+ if (grp && grp.type === "supsub") {
26
25
  // If our base is a character box, and we have superscripts and
27
26
  // subscripts, the supsub will defer to us. In particular, we want
28
27
  // to attach the superscripts and subscripts to the inner body (so
@@ -32,18 +31,18 @@ export const htmlBuilder: HtmlBuilderSupSub<"accent"> = (grp, options) => {
32
31
  // rendering that, while keeping track of where the accent is.
33
32
 
34
33
  // The real accent group is the base of the supsub group
35
- group = assertNodeType(supSub.base, "accent");
34
+ group = assertNodeType(grp.base, "accent");
36
35
  // The character box is the base of the accent group
37
36
  base = group.base;
38
37
  // Stick the character box into the base of the supsub group
39
- supSub.base = base;
38
+ grp.base = base;
40
39
 
41
40
  // Rerender the supsub group with its new base, and store that
42
41
  // result.
43
- supSubGroup = assertSpan(html.buildGroup(supSub, options));
42
+ supSubGroup = assertSpan(html.buildGroup(grp, options));
44
43
 
45
44
  // reset original base
46
- supSub.base = group;
45
+ grp.base = group;
47
46
  } else {
48
47
  group = assertNodeType(grp, "accent");
49
48
  base = group.base;
@@ -94,8 +93,9 @@ export const htmlBuilder: HtmlBuilderSupSub<"accent"> = (grp, options) => {
94
93
  accent = buildCommon.staticSvg("vec", options);
95
94
  width = buildCommon.svgData.vec[1];
96
95
  } else {
97
- accent = buildCommon.makeSymbol(
98
- group.label, "Main-Regular", group.mode, options);
96
+ accent = buildCommon.makeOrd({mode: group.mode, text: group.label},
97
+ options, "textord");
98
+ accent = assertSymbolDomNode(accent);
99
99
  // Remove the italic correction of the accent, because it only serves to
100
100
  // shift the accent over to a place we don't want.
101
101
  accent.italic = 0;
@@ -37,8 +37,8 @@ defineFunction({
37
37
 
38
38
  // Generate the vlist, with the appropriate kerns
39
39
  const vlist = buildCommon.makeVList({
40
- positionType: "bottom",
41
- positionData: accentBody.height + kern,
40
+ positionType: "top",
41
+ positionData: innerGroup.height,
42
42
  children: [
43
43
  {type: "elem", elem: accentBody, wrapperClasses: ["svg-align"]},
44
44
  {type: "kern", size: kern},
@@ -9,6 +9,14 @@ import * as mml from "../buildMathML";
9
9
 
10
10
  import type {ParseNode} from "../parseNode";
11
11
 
12
+ // Helper function
13
+ const paddedNode = group => {
14
+ const node = new mathMLTree.MathNode("mpadded", group ? [group] : []);
15
+ node.setAttribute("width", "+0.6em");
16
+ node.setAttribute("lspace", "0.3em");
17
+ return node;
18
+ };
19
+
12
20
  // Stretchy arrows with an optional argument
13
21
  defineFunction({
14
22
  type: "xArrow",
@@ -105,12 +113,11 @@ defineFunction({
105
113
  mathmlBuilder(group, options) {
106
114
  const arrowNode = stretchy.mathMLnode(group.label);
107
115
  let node;
108
- let lowerNode;
109
116
 
110
117
  if (group.body) {
111
- const upperNode = mml.buildGroup(group.body, options);
118
+ const upperNode = paddedNode(mml.buildGroup(group.body, options));
112
119
  if (group.below) {
113
- lowerNode = mml.buildGroup(group.below, options);
120
+ const lowerNode = paddedNode(mml.buildGroup(group.below, options));
114
121
  node = new mathMLTree.MathNode(
115
122
  "munderover", [arrowNode, lowerNode, upperNode]
116
123
  );
@@ -118,10 +125,13 @@ defineFunction({
118
125
  node = new mathMLTree.MathNode("mover", [arrowNode, upperNode]);
119
126
  }
120
127
  } else if (group.below) {
121
- lowerNode = mml.buildGroup(group.below, options);
128
+ const lowerNode = paddedNode(mml.buildGroup(group.below, options));
122
129
  node = new mathMLTree.MathNode("munder", [arrowNode, lowerNode]);
123
130
  } else {
124
- node = new mathMLTree.MathNode("mover", [arrowNode]);
131
+ // This should never happen.
132
+ // Parser.js throws an error if there is no argument.
133
+ node = paddedNode();
134
+ node = new mathMLTree.MathNode("mover", [arrowNode, node]);
125
135
  }
126
136
  return node;
127
137
  },
@@ -22,7 +22,8 @@ const htmlBuilder = (group, options) => {
22
22
  };
23
23
 
24
24
  const mathmlBuilder = (group, options) => {
25
- const inner = mml.buildExpression(group.body, options);
25
+ const inner = mml.buildExpression(group.body,
26
+ options.withColor(group.color));
26
27
 
27
28
  const node = new mathMLTree.MathNode("mstyle", inner);
28
29
 
@@ -54,42 +55,6 @@ defineFunction({
54
55
  mathmlBuilder,
55
56
  });
56
57
 
57
- // TODO(kevinb): define these using macros
58
- defineFunction({
59
- type: "color",
60
- names: [
61
- "\\blue", "\\orange", "\\pink", "\\red",
62
- "\\green", "\\gray", "\\purple",
63
- "\\blueA", "\\blueB", "\\blueC", "\\blueD", "\\blueE",
64
- "\\tealA", "\\tealB", "\\tealC", "\\tealD", "\\tealE",
65
- "\\greenA", "\\greenB", "\\greenC", "\\greenD", "\\greenE",
66
- "\\goldA", "\\goldB", "\\goldC", "\\goldD", "\\goldE",
67
- "\\redA", "\\redB", "\\redC", "\\redD", "\\redE",
68
- "\\maroonA", "\\maroonB", "\\maroonC", "\\maroonD", "\\maroonE",
69
- "\\purpleA", "\\purpleB", "\\purpleC", "\\purpleD", "\\purpleE",
70
- "\\mintA", "\\mintB", "\\mintC",
71
- "\\grayA", "\\grayB", "\\grayC", "\\grayD", "\\grayE",
72
- "\\grayF", "\\grayG", "\\grayH", "\\grayI",
73
- "\\kaBlue", "\\kaGreen",
74
- ],
75
- props: {
76
- numArgs: 1,
77
- allowedInText: true,
78
- greediness: 3,
79
- },
80
- handler({parser, funcName}, args) {
81
- const body = args[0];
82
- return {
83
- type: "color",
84
- mode: parser.mode,
85
- color: "katex-" + funcName.slice(1),
86
- body: ordargument(body),
87
- };
88
- },
89
- htmlBuilder,
90
- mathmlBuilder,
91
- });
92
-
93
58
  defineFunction({
94
59
  type: "color",
95
60
  names: ["\\color"],
@@ -102,7 +67,13 @@ defineFunction({
102
67
  handler({parser, breakOnTokenText}, args) {
103
68
  const color = assertNodeType(args[0], "color-token").color;
104
69
 
105
- // If we see a styling function, parse out the implicit body
70
+ // Set macro \current@color in current namespace to store the current
71
+ // color, mimicking the behavior of color.sty.
72
+ // This is currently used just to correctly color a \right
73
+ // that follows a \color command.
74
+ parser.gullet.macros.set("\\current@color", color);
75
+
76
+ // Parse out the implicit body that should be colored.
106
77
  const body = parser.parseExpression(true, breakOnTokenText);
107
78
 
108
79
  return {