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
@@ -0,0 +1,153 @@
1
+ // @flow
2
+ import defineFunction from "../defineFunction";
3
+ import type {Measurement} from "../units";
4
+ import {calculateSize, validUnit} from "../units";
5
+ import ParseError from "../ParseError";
6
+ import {Img} from "../domTree";
7
+ import mathMLTree from "../mathMLTree";
8
+ import {assertNodeType} from "../parseNode";
9
+ import type {CssStyle} from "../domTree";
10
+
11
+ const sizeData = function(str: string): Measurement {
12
+ if (/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(str)) {
13
+ // str is a number with no unit specified.
14
+ // default unit is bp, per graphix package.
15
+ return {number: +str, unit: "bp"};
16
+ } else {
17
+ const match = (/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/).exec(str);
18
+ if (!match) {
19
+ throw new ParseError("Invalid size: '" + str
20
+ + "' in \\includegraphics");
21
+ }
22
+ const data = {
23
+ number: +(match[1] + match[2]), // sign + magnitude, cast to number
24
+ unit: match[3],
25
+ };
26
+ if (!validUnit(data)) {
27
+ throw new ParseError("Invalid unit: '" + data.unit
28
+ + "' in \\includegraphics.");
29
+ }
30
+ return data;
31
+ }
32
+ };
33
+
34
+ defineFunction({
35
+ type: "includegraphics",
36
+ names: ["\\includegraphics"],
37
+ props: {
38
+ numArgs: 1,
39
+ numOptionalArgs: 1,
40
+ argTypes: ["raw", "url"],
41
+ allowedInText: false,
42
+ },
43
+ handler: ({parser}, args, optArgs) => {
44
+ let width = {number: 0, unit: "em"};
45
+ let height = {number: 0.9, unit: "em"}; // sorta character sized.
46
+ let totalheight = {number: 0, unit: "em"};
47
+ let alt = "";
48
+
49
+ if (optArgs[0]) {
50
+ const attributeStr = assertNodeType(optArgs[0], "raw").string;
51
+
52
+ // Parser.js does not parse key/value pairs. We get a string.
53
+ const attributes = attributeStr.split(",");
54
+ for (let i = 0; i < attributes.length; i++) {
55
+ const keyVal = attributes[i].split("=");
56
+ if (keyVal.length === 2) {
57
+ const str = keyVal[1].trim();
58
+ switch (keyVal[0].trim()) {
59
+ case "alt":
60
+ alt = str;
61
+ break;
62
+ case "width":
63
+ width = sizeData(str);
64
+ break;
65
+ case "height":
66
+ height = sizeData(str);
67
+ break;
68
+ case "totalheight":
69
+ totalheight = sizeData(str);
70
+ break;
71
+ default:
72
+ throw new ParseError("Invalid key: '" + keyVal[0] +
73
+ "' in \\includegraphics.");
74
+ }
75
+ }
76
+ }
77
+ }
78
+
79
+ const src = assertNodeType(args[0], "url").url;
80
+
81
+ if (alt === "") {
82
+ // No alt given. Use the file name. Strip away the path.
83
+ alt = src;
84
+ alt = alt.replace(/^.*[\\/]/, '');
85
+ alt = alt.substring(0, alt.lastIndexOf('.'));
86
+ }
87
+
88
+ if (!parser.settings.isTrusted({
89
+ command: "\\includegraphics",
90
+ url: src,
91
+ })) {
92
+ return parser.formatUnsupportedCmd("\\includegraphics");
93
+ }
94
+
95
+ return {
96
+ type: "includegraphics",
97
+ mode: parser.mode,
98
+ alt: alt,
99
+ width: width,
100
+ height: height,
101
+ totalheight: totalheight,
102
+ src: src,
103
+ };
104
+ },
105
+ htmlBuilder: (group, options) => {
106
+ const height = calculateSize(group.height, options);
107
+ let depth = 0;
108
+
109
+ if (group.totalheight.number > 0) {
110
+ depth = calculateSize(group.totalheight, options) - height;
111
+ depth = Number(depth.toFixed(2));
112
+ }
113
+
114
+ let width = 0;
115
+ if (group.width.number > 0) {
116
+ width = calculateSize(group.width, options);
117
+ }
118
+
119
+ const style: CssStyle = {height: height + depth + "em"};
120
+ if (width > 0) {
121
+ style.width = width + "em";
122
+ }
123
+ if (depth > 0) {
124
+ style.verticalAlign = -depth + "em";
125
+ }
126
+
127
+ const node = new Img(group.src, group.alt, style);
128
+ node.height = height;
129
+ node.depth = depth;
130
+
131
+ return node;
132
+ },
133
+ mathmlBuilder: (group, options) => {
134
+ const node = new mathMLTree.MathNode("mglyph", []);
135
+ node.setAttribute("alt", group.alt);
136
+
137
+ const height = calculateSize(group.height, options);
138
+ let depth = 0;
139
+ if (group.totalheight.number > 0) {
140
+ depth = calculateSize(group.totalheight, options) - height;
141
+ depth = depth.toFixed(2);
142
+ node.setAttribute("valign", "-" + depth + "em");
143
+ }
144
+ node.setAttribute("height", height + depth + "em");
145
+
146
+ if (group.width.number > 0) {
147
+ const width = calculateSize(group.width, options);
148
+ node.setAttribute("width", width + "em");
149
+ }
150
+ node.setAttribute("src", group.src);
151
+ return node;
152
+ },
153
+ });
@@ -44,19 +44,16 @@ defineFunction({
44
44
  // two items involved in the lap.
45
45
  // Next, use a strut to set the height of the HTML bounding box.
46
46
  // Otherwise, a tall argument may be misplaced.
47
+ // This code resolved issue #1153
47
48
  const strut = buildCommon.makeSpan(["strut"]);
48
49
  strut.style.height = (node.height + node.depth) + "em";
49
50
  strut.style.verticalAlign = -node.depth + "em";
50
51
  node.children.unshift(strut);
51
52
 
52
53
  // Next, prevent vertical misplacement when next to something tall.
53
- node = buildCommon.makeVList({
54
- positionType: "firstBaseline",
55
- children: [{type: "elem", elem: node}],
56
- }, options);
57
-
58
- // Get the horizontal spacing correct relative to adjacent items.
59
- return buildCommon.makeSpan(["mord"], [node], options);
54
+ // This code resolves issue #1234
55
+ node = buildCommon.makeSpan(["thinbox"], [node], options);
56
+ return buildCommon.makeSpan(["mord", "vbox"], [node], options);
60
57
  },
61
58
  mathmlBuilder: (group, options) => {
62
59
  // mathllap, mathrlap, mathclap
@@ -10,18 +10,14 @@ defineFunction({
10
10
  numArgs: 0,
11
11
  allowedInText: true,
12
12
  allowedInMath: false,
13
- consumeMode: "math",
14
13
  },
15
14
  handler({funcName, parser}, args) {
16
15
  const outerMode = parser.mode;
17
16
  parser.switchMode("math");
18
17
  const close = (funcName === "\\(" ? "\\)" : "$");
19
18
  const body = parser.parseExpression(false, close);
20
- // We can't expand the next symbol after the closing $ until after
21
- // switching modes back. So don't consume within expect.
22
- parser.expect(close, false);
19
+ parser.expect(close);
23
20
  parser.switchMode(outerMode);
24
- parser.consume();
25
21
  return {
26
22
  type: "styling",
27
23
  mode: parser.mode,
@@ -2,6 +2,7 @@
2
2
  import defineFunction, {ordargument} from "../defineFunction";
3
3
  import buildCommon from "../buildCommon";
4
4
  import mathMLTree from "../mathMLTree";
5
+ import utils from "../utils";
5
6
  import type {AnyParseNode} from "../parseNode";
6
7
 
7
8
  import * as html from "../buildHTML";
@@ -17,8 +18,42 @@ function htmlBuilder(group: ParseNode<"mclass">, options) {
17
18
  }
18
19
 
19
20
  function mathmlBuilder(group: ParseNode<"mclass">, options) {
21
+ let node: mathMLTree.MathNode;
20
22
  const inner = mml.buildExpression(group.body, options);
21
- return mathMLTree.newDocumentFragment(inner);
23
+
24
+ if (group.mclass === "minner") {
25
+ return mathMLTree.newDocumentFragment(inner);
26
+ } else if (group.mclass === "mord") {
27
+ if (group.isCharacterBox) {
28
+ node = inner[0];
29
+ node.type = "mi";
30
+ } else {
31
+ node = new mathMLTree.MathNode("mi", inner);
32
+ }
33
+ } else {
34
+ if (group.isCharacterBox) {
35
+ node = inner[0];
36
+ node.type = "mo";
37
+ } else {
38
+ node = new mathMLTree.MathNode("mo", inner);
39
+ }
40
+
41
+ // Set spacing based on what is the most likely adjacent atom type.
42
+ // See TeXbook p170.
43
+ if (group.mclass === "mbin") {
44
+ node.attributes.lspace = "0.22em"; // medium space
45
+ node.attributes.rspace = "0.22em";
46
+ } else if (group.mclass === "mpunct") {
47
+ node.attributes.lspace = "0em";
48
+ node.attributes.rspace = "0.17em"; // thinspace
49
+ } else if (group.mclass === "mopen" || group.mclass === "mclose") {
50
+ node.attributes.lspace = "0em";
51
+ node.attributes.rspace = "0em";
52
+ }
53
+ // MathML <mo> default space is 5/18 em, so <mrel> needs no action.
54
+ // Ref: https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mo
55
+ }
56
+ return node;
22
57
  }
23
58
 
24
59
  // Math class commands except \mathop
@@ -36,8 +71,9 @@ defineFunction({
36
71
  return {
37
72
  type: "mclass",
38
73
  mode: parser.mode,
39
- mclass: "m" + funcName.substr(5),
74
+ mclass: "m" + funcName.substr(5), // TODO(kevinb): don't prefix with 'm'
40
75
  body: ordargument(body),
76
+ isCharacterBox: utils.isCharacterBox(body),
41
77
  };
42
78
  },
43
79
  htmlBuilder,
@@ -71,6 +107,7 @@ defineFunction({
71
107
  mode: parser.mode,
72
108
  mclass: binrelClass(args[0]),
73
109
  body: [args[1]],
110
+ isCharacterBox: utils.isCharacterBox(args[1]),
74
111
  };
75
112
  },
76
113
  });
@@ -99,6 +136,7 @@ defineFunction({
99
136
  mode: baseArg.mode,
100
137
  limits: true,
101
138
  alwaysHandleSupSub: true,
139
+ parentIsSupSub: false,
102
140
  symbol: false,
103
141
  suppressBaseShift: funcName !== "\\stackrel",
104
142
  body: ordargument(baseArg),
@@ -117,6 +155,7 @@ defineFunction({
117
155
  mode: parser.mode,
118
156
  mclass,
119
157
  body: [supsub],
158
+ isCharacterBox: utils.isCharacterBox(supsub),
120
159
  };
121
160
  },
122
161
  htmlBuilder,
@@ -6,7 +6,8 @@ import {SymbolNode} from "../domTree";
6
6
  import * as mathMLTree from "../mathMLTree";
7
7
  import utils from "../utils";
8
8
  import Style from "../Style";
9
- import {assertNodeType, checkNodeType} from "../parseNode";
9
+ import {assembleSupSub} from "./utils/assembleSupSub";
10
+ import {assertNodeType} from "../parseNode";
10
11
 
11
12
  import * as html from "../buildHTML";
12
13
  import * as mml from "../buildMathML";
@@ -14,6 +15,11 @@ import * as mml from "../buildMathML";
14
15
  import type {HtmlBuilderSupSub, MathMLBuilder} from "../defineFunction";
15
16
  import type {ParseNode} from "../parseNode";
16
17
 
18
+ // Most operators have a large successor symbol, but these don't.
19
+ const noSuccessor = [
20
+ "\\smallint",
21
+ ];
22
+
17
23
  // NOTE: Unlike most `htmlBuilder`s, this one handles not only "op", but also
18
24
  // "supsub" since some of them (like \int) can affect super/subscripting.
19
25
  export const htmlBuilder: HtmlBuilderSupSub<"op"> = (grp, options) => {
@@ -22,14 +28,13 @@ export const htmlBuilder: HtmlBuilderSupSub<"op"> = (grp, options) => {
22
28
  let subGroup;
23
29
  let hasLimits = false;
24
30
  let group: ParseNode<"op">;
25
- const supSub = checkNodeType(grp, "supsub");
26
- if (supSub) {
31
+ if (grp.type === "supsub") {
27
32
  // If we have limits, supsub will pass us its group to handle. Pull
28
33
  // out the superscript and subscript and set the group to the op in
29
34
  // its base.
30
- supGroup = supSub.sup;
31
- subGroup = supSub.sub;
32
- group = assertNodeType(supSub.base, "op");
35
+ supGroup = grp.sup;
36
+ subGroup = grp.sub;
37
+ group = assertNodeType(grp.base, "op");
33
38
  hasLimits = true;
34
39
  } else {
35
40
  group = assertNodeType(grp, "op");
@@ -37,11 +42,6 @@ export const htmlBuilder: HtmlBuilderSupSub<"op"> = (grp, options) => {
37
42
 
38
43
  const style = options.style;
39
44
 
40
- // Most operators have a large successor symbol, but these don't.
41
- const noSuccessor = [
42
- "\\smallint",
43
- ];
44
-
45
45
  let large = false;
46
46
  if (style.size === Style.DISPLAY.size &&
47
47
  group.symbol &&
@@ -105,7 +105,7 @@ export const htmlBuilder: HtmlBuilderSupSub<"op"> = (grp, options) => {
105
105
  // operators, like \limsup
106
106
  const output = [];
107
107
  for (let i = 1; i < group.name.length; i++) {
108
- output.push(buildCommon.mathsym(group.name[i], group.mode));
108
+ output.push(buildCommon.mathsym(group.name[i], group.mode, options));
109
109
  }
110
110
  base = buildCommon.makeSpan(["mop"], output, options);
111
111
  }
@@ -131,99 +131,9 @@ export const htmlBuilder: HtmlBuilderSupSub<"op"> = (grp, options) => {
131
131
  }
132
132
 
133
133
  if (hasLimits) {
134
- // IE 8 clips \int if it is in a display: inline-block. We wrap it
135
- // in a new span so it is an inline, and works.
136
- base = buildCommon.makeSpan([], [base]);
137
-
138
- let sub;
139
- let sup;
140
- // We manually have to handle the superscripts and subscripts. This,
141
- // aside from the kern calculations, is copied from supsub.
142
- if (supGroup) {
143
- const elem = html.buildGroup(
144
- supGroup, options.havingStyle(style.sup()), options);
145
-
146
- sup = {
147
- elem,
148
- kern: Math.max(
149
- options.fontMetrics().bigOpSpacing1,
150
- options.fontMetrics().bigOpSpacing3 - elem.depth),
151
- };
152
- }
153
-
154
- if (subGroup) {
155
- const elem = html.buildGroup(
156
- subGroup, options.havingStyle(style.sub()), options);
157
-
158
- sub = {
159
- elem,
160
- kern: Math.max(
161
- options.fontMetrics().bigOpSpacing2,
162
- options.fontMetrics().bigOpSpacing4 - elem.height),
163
- };
164
- }
165
-
166
- // Build the final group as a vlist of the possible subscript, base,
167
- // and possible superscript.
168
- let finalGroup;
169
- if (sup && sub) {
170
- const bottom = options.fontMetrics().bigOpSpacing5 +
171
- sub.elem.height + sub.elem.depth +
172
- sub.kern +
173
- base.depth + baseShift;
174
-
175
- finalGroup = buildCommon.makeVList({
176
- positionType: "bottom",
177
- positionData: bottom,
178
- children: [
179
- {type: "kern", size: options.fontMetrics().bigOpSpacing5},
180
- {type: "elem", elem: sub.elem, marginLeft: -slant + "em"},
181
- {type: "kern", size: sub.kern},
182
- {type: "elem", elem: base},
183
- {type: "kern", size: sup.kern},
184
- {type: "elem", elem: sup.elem, marginLeft: slant + "em"},
185
- {type: "kern", size: options.fontMetrics().bigOpSpacing5},
186
- ],
187
- }, options);
188
- } else if (sub) {
189
- const top = base.height - baseShift;
190
-
191
- // Shift the limits by the slant of the symbol. Note
192
- // that we are supposed to shift the limits by 1/2 of the slant,
193
- // but since we are centering the limits adding a full slant of
194
- // margin will shift by 1/2 that.
195
- finalGroup = buildCommon.makeVList({
196
- positionType: "top",
197
- positionData: top,
198
- children: [
199
- {type: "kern", size: options.fontMetrics().bigOpSpacing5},
200
- {type: "elem", elem: sub.elem, marginLeft: -slant + "em"},
201
- {type: "kern", size: sub.kern},
202
- {type: "elem", elem: base},
203
- ],
204
- }, options);
205
- } else if (sup) {
206
- const bottom = base.depth + baseShift;
207
-
208
- finalGroup = buildCommon.makeVList({
209
- positionType: "bottom",
210
- positionData: bottom,
211
- children: [
212
- {type: "elem", elem: base},
213
- {type: "kern", size: sup.kern},
214
- {type: "elem", elem: sup.elem, marginLeft: slant + "em"},
215
- {type: "kern", size: options.fontMetrics().bigOpSpacing5},
216
- ],
217
- }, options);
218
- } else {
219
- // This case probably shouldn't occur (this would mean the
220
- // supsub was sending us a group with no superscript or
221
- // subscript) but be safe.
222
- return base;
223
- }
134
+ return assembleSupSub(base, supGroup, subGroup, options,
135
+ style, slant, baseShift);
224
136
 
225
- return buildCommon.makeSpan(
226
- ["mop", "op-limits"], [finalGroup], options);
227
137
  } else {
228
138
  if (baseShift) {
229
139
  base.style.position = "relative";
@@ -237,12 +147,13 @@ export const htmlBuilder: HtmlBuilderSupSub<"op"> = (grp, options) => {
237
147
  const mathmlBuilder: MathMLBuilder<"op"> = (group, options) => {
238
148
  let node;
239
149
 
240
- // TODO(emily): handle big operators using the `largeop` attribute
241
-
242
150
  if (group.symbol) {
243
151
  // This is a symbol. Just add the symbol.
244
152
  node = new mathMLTree.MathNode(
245
153
  "mo", [mml.makeText(group.name, group.mode)]);
154
+ if (utils.contains(noSuccessor, group.name)) {
155
+ node.setAttribute("largeop", "false");
156
+ }
246
157
  } else if (group.body) {
247
158
  // This is an operator with children. Add them.
248
159
  node = new mathMLTree.MathNode(
@@ -250,17 +161,17 @@ const mathmlBuilder: MathMLBuilder<"op"> = (group, options) => {
250
161
  } else {
251
162
  // This is a text operator. Add all of the characters from the
252
163
  // operator's name.
253
- // TODO(emily): Add a space in the middle of some of these
254
- // operators, like \limsup.
255
164
  node = new mathMLTree.MathNode(
256
165
  "mi", [new mathMLTree.TextNode(group.name.slice(1))]);
257
-
258
166
  // Append an <mo>&ApplyFunction;</mo>.
259
167
  // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4
260
168
  const operator = new mathMLTree.MathNode("mo",
261
169
  [mml.makeText("\u2061", "text")]);
262
-
263
- return mathMLTree.newDocumentFragment([node, operator]);
170
+ if (group.parentIsSupSub) {
171
+ node = new mathMLTree.MathNode("mo", [node, operator]);
172
+ } else {
173
+ node = mathMLTree.newDocumentFragment([node, operator]);
174
+ }
264
175
  }
265
176
 
266
177
  return node;
@@ -302,6 +213,7 @@ defineFunction({
302
213
  type: "op",
303
214
  mode: parser.mode,
304
215
  limits: true,
216
+ parentIsSupSub: false,
305
217
  symbol: true,
306
218
  name: fName,
307
219
  };
@@ -324,6 +236,7 @@ defineFunction({
324
236
  type: "op",
325
237
  mode: parser.mode,
326
238
  limits: false,
239
+ parentIsSupSub: false,
327
240
  symbol: false,
328
241
  body: ordargument(body),
329
242
  };
@@ -363,6 +276,7 @@ defineFunction({
363
276
  type: "op",
364
277
  mode: parser.mode,
365
278
  limits: false,
279
+ parentIsSupSub: false,
366
280
  symbol: false,
367
281
  name: funcName,
368
282
  };
@@ -385,6 +299,7 @@ defineFunction({
385
299
  type: "op",
386
300
  mode: parser.mode,
387
301
  limits: true,
302
+ parentIsSupSub: false,
388
303
  symbol: false,
389
304
  name: funcName,
390
305
  };
@@ -412,6 +327,7 @@ defineFunction({
412
327
  type: "op",
413
328
  mode: parser.mode,
414
329
  limits: false,
330
+ parentIsSupSub: false,
415
331
  symbol: true,
416
332
  name: fName,
417
333
  };