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,184 @@
1
+ //@flow
2
+ import defineFunction from "../defineFunction";
3
+ import ParseError from "../ParseError";
4
+ import {assertNodeType} from "../parseNode";
5
+
6
+ const globalMap = {
7
+ "\\global": "\\global",
8
+ "\\long": "\\\\globallong",
9
+ "\\\\globallong": "\\\\globallong",
10
+ "\\def": "\\gdef",
11
+ "\\gdef": "\\gdef",
12
+ "\\edef": "\\xdef",
13
+ "\\xdef": "\\xdef",
14
+ "\\let": "\\\\globallet",
15
+ "\\futurelet": "\\\\globalfuture",
16
+ };
17
+
18
+ const checkControlSequence = (tok) => {
19
+ const name = tok.text;
20
+ if (/^(?:[\\{}$&#^_]|EOF)$/.test(name)) {
21
+ throw new ParseError("Expected a control sequence", tok);
22
+ }
23
+ return name;
24
+ };
25
+
26
+ const getRHS = (parser) => {
27
+ let tok = parser.gullet.popToken();
28
+ if (tok.text === "=") { // consume optional equals
29
+ tok = parser.gullet.popToken();
30
+ if (tok.text === " ") { // consume one optional space
31
+ tok = parser.gullet.popToken();
32
+ }
33
+ }
34
+ return tok;
35
+ };
36
+
37
+ const letCommand = (parser, name, tok, global) => {
38
+ let macro = parser.gullet.macros.get(tok.text);
39
+ if (macro == null) {
40
+ // don't expand it later even if a macro with the same name is defined
41
+ // e.g., \let\foo=\frac \def\frac{\relax} \frac12
42
+ tok.noexpand = true;
43
+ macro = {
44
+ tokens: [tok],
45
+ numArgs: 0,
46
+ // reproduce the same behavior in expansion
47
+ unexpandable: !parser.gullet.isExpandable(tok.text),
48
+ };
49
+ }
50
+ parser.gullet.macros.set(name, macro, global);
51
+ };
52
+
53
+ // <assignment> -> <non-macro assignment>|<macro assignment>
54
+ // <non-macro assignment> -> <simple assignment>|\global<non-macro assignment>
55
+ // <macro assignment> -> <definition>|<prefix><macro assignment>
56
+ // <prefix> -> \global|\long|\outer
57
+ defineFunction({
58
+ type: "internal",
59
+ names: [
60
+ "\\global", "\\long",
61
+ "\\\\globallong", // can’t be entered directly
62
+ ],
63
+ props: {
64
+ numArgs: 0,
65
+ allowedInText: true,
66
+ },
67
+ handler({parser, funcName}) {
68
+ parser.consumeSpaces();
69
+ const token = parser.fetch();
70
+ if (globalMap[token.text]) {
71
+ // KaTeX doesn't have \par, so ignore \long
72
+ if (funcName === "\\global" || funcName === "\\\\globallong") {
73
+ token.text = globalMap[token.text];
74
+ }
75
+ return assertNodeType(parser.parseFunction(), "internal");
76
+ }
77
+ throw new ParseError(`Invalid token after macro prefix`, token);
78
+ },
79
+ });
80
+
81
+ // Basic support for macro definitions: \def, \gdef, \edef, \xdef
82
+ // <definition> -> <def><control sequence><definition text>
83
+ // <def> -> \def|\gdef|\edef|\xdef
84
+ // <definition text> -> <parameter text><left brace><balanced text><right brace>
85
+ defineFunction({
86
+ type: "internal",
87
+ names: ["\\def", "\\gdef", "\\edef", "\\xdef"],
88
+ props: {
89
+ numArgs: 0,
90
+ allowedInText: true,
91
+ },
92
+ handler({parser, funcName}) {
93
+ let arg = parser.gullet.consumeArgs(1)[0];
94
+ if (arg.length !== 1) {
95
+ throw new ParseError("\\gdef's first argument must be a macro name");
96
+ }
97
+ const name = arg[0].text;
98
+ // Count argument specifiers, and check they are in the order #1 #2 ...
99
+ let numArgs = 0;
100
+ arg = parser.gullet.consumeArgs(1)[0];
101
+ while (arg.length === 1 && arg[0].text === "#") {
102
+ arg = parser.gullet.consumeArgs(1)[0];
103
+ if (arg.length !== 1) {
104
+ throw new ParseError(
105
+ `Invalid argument number length "${arg.length}"`);
106
+ }
107
+ if (!(/^[1-9]$/.test(arg[0].text))) {
108
+ throw new ParseError(
109
+ `Invalid argument number "${arg[0].text}"`);
110
+ }
111
+ numArgs++;
112
+ if (parseInt(arg[0].text) !== numArgs) {
113
+ throw new ParseError(
114
+ `Argument number "${arg[0].text}" out of order`);
115
+ }
116
+ arg = parser.gullet.consumeArgs(1)[0];
117
+ }
118
+ if (funcName === "\\edef" || funcName === "\\xdef") {
119
+ arg = parser.gullet.expandTokens(arg);
120
+ arg.reverse(); // to fit in with stack order
121
+ }
122
+ // Final arg is the expansion of the macro
123
+ parser.gullet.macros.set(name, {
124
+ tokens: arg,
125
+ numArgs,
126
+ }, funcName === globalMap[funcName]);
127
+
128
+ return {
129
+ type: "internal",
130
+ mode: parser.mode,
131
+ };
132
+ },
133
+ });
134
+
135
+ // <simple assignment> -> <let assignment>
136
+ // <let assignment> -> \futurelet<control sequence><token><token>
137
+ // | \let<control sequence><equals><one optional space><token>
138
+ // <equals> -> <optional spaces>|<optional spaces>=
139
+ defineFunction({
140
+ type: "internal",
141
+ names: [
142
+ "\\let",
143
+ "\\\\globallet", // can’t be entered directly
144
+ ],
145
+ props: {
146
+ numArgs: 0,
147
+ allowedInText: true,
148
+ },
149
+ handler({parser, funcName}) {
150
+ const name = checkControlSequence(parser.gullet.popToken());
151
+ parser.gullet.consumeSpaces();
152
+ const tok = getRHS(parser);
153
+ letCommand(parser, name, tok, funcName === "\\\\globallet");
154
+ return {
155
+ type: "internal",
156
+ mode: parser.mode,
157
+ };
158
+ },
159
+ });
160
+
161
+ // ref: https://www.tug.org/TUGboat/tb09-3/tb22bechtolsheim.pdf
162
+ defineFunction({
163
+ type: "internal",
164
+ names: [
165
+ "\\futurelet",
166
+ "\\\\globalfuture", // can’t be entered directly
167
+ ],
168
+ props: {
169
+ numArgs: 0,
170
+ allowedInText: true,
171
+ },
172
+ handler({parser, funcName}) {
173
+ const name = checkControlSequence(parser.gullet.popToken());
174
+ const middle = parser.gullet.popToken();
175
+ const tok = parser.gullet.popToken();
176
+ letCommand(parser, name, tok, funcName === "\\\\globalfuture");
177
+ parser.gullet.pushToken(tok);
178
+ parser.gullet.pushToken(middle);
179
+ return {
180
+ type: "internal",
181
+ mode: parser.mode,
182
+ };
183
+ },
184
+ });
@@ -62,11 +62,12 @@ function checkDelimiter(
62
62
  const symDelim = checkSymbolNodeType(delim);
63
63
  if (symDelim && utils.contains(delimiters, symDelim.text)) {
64
64
  return symDelim;
65
- } else {
65
+ } else if (symDelim) {
66
66
  throw new ParseError(
67
- "Invalid delimiter: '" +
68
- (symDelim ? symDelim.text : JSON.stringify(delim)) +
69
- "' after '" + context.funcName + "'", delim);
67
+ `Invalid delimiter '${symDelim.text}' after '${context.funcName}'`,
68
+ delim);
69
+ } else {
70
+ throw new ParseError(`Invalid delimiter type '${delim.type}'`, delim);
70
71
  }
71
72
  }
72
73
 
@@ -145,10 +146,16 @@ defineFunction({
145
146
  // \left case below triggers parsing of \right in
146
147
  // `const right = parser.parseFunction();`
147
148
  // uses this return value.
149
+ const color = context.parser.gullet.macros.get("\\current@color");
150
+ if (color && typeof color !== "string") {
151
+ throw new ParseError(
152
+ "\\current@color set to non-string in \\right");
153
+ }
148
154
  return {
149
155
  type: "leftright-right",
150
156
  mode: context.parser.mode,
151
157
  delim: checkDelimiter(args[0], context).text,
158
+ color, // undefined if not set via \color
152
159
  };
153
160
  },
154
161
  });
@@ -178,6 +185,7 @@ defineFunction({
178
185
  body,
179
186
  left: delim.text,
180
187
  right: right.delim,
188
+ rightColor: right.color,
181
189
  };
182
190
  },
183
191
  htmlBuilder: (group, options) => {
@@ -241,12 +249,14 @@ defineFunction({
241
249
  }
242
250
 
243
251
  let rightDelim;
244
- // Same for the right delimiter
252
+ // Same for the right delimiter, but using color specified by \color
245
253
  if (group.right === ".") {
246
254
  rightDelim = html.makeNullDelimiter(options, ["mclose"]);
247
255
  } else {
256
+ const colorOptions = group.rightColor ?
257
+ options.withColor(group.rightColor) : options;
248
258
  rightDelim = delimiter.leftRightDelim(
249
- group.right, innerHeight, innerDepth, options,
259
+ group.right, innerHeight, innerDepth, colorOptions,
250
260
  group.mode, ["mclose"]);
251
261
  }
252
262
  // Add it to the end of the expression.
@@ -273,6 +283,10 @@ defineFunction({
273
283
 
274
284
  rightNode.setAttribute("fence", "true");
275
285
 
286
+ if (group.rightColor) {
287
+ rightNode.setAttribute("mathcolor", group.rightColor);
288
+ }
289
+
276
290
  inner.push(rightNode);
277
291
  }
278
292
 
@@ -318,9 +332,19 @@ defineFunction({
318
332
  return middleDelim;
319
333
  },
320
334
  mathmlBuilder: (group, options) => {
321
- const middleNode = new mathMLTree.MathNode(
322
- "mo", [mml.makeText(group.delim, group.mode)]);
335
+ // A Firefox \middle will strech a character vertically only if it
336
+ // is in the fence part of the operator dictionary at:
337
+ // https://www.w3.org/TR/MathML3/appendixc.html.
338
+ // So we need to avoid U+2223 and use plain "|" instead.
339
+ const textNode = (group.delim === "\\vert" || group.delim === "|")
340
+ ? mml.makeText("|", "text")
341
+ : mml.makeText(group.delim, group.mode);
342
+ const middleNode = new mathMLTree.MathNode("mo", [textNode]);
323
343
  middleNode.setAttribute("fence", "true");
344
+ // MathML gives 5/18em spacing to each <mo> element.
345
+ // \middle should get delimiter spacing instead.
346
+ middleNode.setAttribute("lspace", "0.05em");
347
+ middleNode.setAttribute("rspace", "0.05em");
324
348
  return middleNode;
325
349
  },
326
350
  });
@@ -46,15 +46,24 @@ const htmlBuilder = (group, options) => {
46
46
 
47
47
  // Add vertical padding
48
48
  let vertPad = 0;
49
- // ref: LaTeX source2e: \fboxsep = 3pt; \fboxrule = .4pt
49
+ let ruleThickness = 0;
50
50
  // ref: cancel package: \advance\totalheight2\p@ % "+2"
51
51
  if (/box/.test(label)) {
52
- vertPad = label === "colorbox" ? 0.3 : 0.34;
52
+ ruleThickness = Math.max(
53
+ options.fontMetrics().fboxrule, // default
54
+ options.minRuleThickness, // User override.
55
+ );
56
+ vertPad = options.fontMetrics().fboxsep +
57
+ (label === "colorbox" ? 0 : ruleThickness);
53
58
  } else {
54
59
  vertPad = isSingleChar ? 0.2 : 0;
55
60
  }
56
61
 
57
62
  img = stretchy.encloseSpan(inner, label, vertPad, options);
63
+ if (/fbox|boxed|fcolorbox/.test(label)) {
64
+ img.style.borderStyle = "solid";
65
+ img.style.borderWidth = `${ruleThickness}em`;
66
+ }
58
67
  imgShift = inner.depth + vertPad;
59
68
 
60
69
  if (group.backgroundColor) {
@@ -111,8 +120,11 @@ const htmlBuilder = (group, options) => {
111
120
  };
112
121
 
113
122
  const mathmlBuilder = (group, options) => {
123
+ let fboxsep = 0;
114
124
  const node = new mathMLTree.MathNode(
115
- "menclose", [mml.buildGroup(group.body, options)]);
125
+ (group.label.indexOf("colorbox") > -1) ? "mpadded" : "menclose",
126
+ [mml.buildGroup(group.body, options)]
127
+ );
116
128
  switch (group.label) {
117
129
  case "\\cancel":
118
130
  node.setAttribute("notation", "updiagonalstrike");
@@ -127,8 +139,23 @@ const mathmlBuilder = (group, options) => {
127
139
  node.setAttribute("notation", "box");
128
140
  break;
129
141
  case "\\fcolorbox":
130
- // TODO(ron): I don't know any way to set the border color.
131
- node.setAttribute("notation", "box");
142
+ case "\\colorbox":
143
+ // <menclose> doesn't have a good notation option. So use <mpadded>
144
+ // instead. Set some attributes that come included with <menclose>.
145
+ fboxsep = options.fontMetrics().fboxsep *
146
+ options.fontMetrics().ptPerEm;
147
+ node.setAttribute("width", `+${2 * fboxsep}pt`);
148
+ node.setAttribute("height", `+${2 * fboxsep}pt`);
149
+ node.setAttribute("lspace", `${fboxsep}pt`); //
150
+ node.setAttribute("voffset", `${fboxsep}pt`);
151
+ if (group.label === "\\fcolorbox") {
152
+ const thk = Math.max(
153
+ options.fontMetrics().fboxrule, // default
154
+ options.minRuleThickness, // user override
155
+ );
156
+ node.setAttribute("style", "border: " + thk + "em solid " +
157
+ String(group.borderColor));
158
+ }
132
159
  break;
133
160
  case "\\xcancel":
134
161
  node.setAttribute("notation", "updiagonalstrike downdiagonalstrike");
@@ -195,7 +222,7 @@ defineFunction({
195
222
  names: ["\\fbox"],
196
223
  props: {
197
224
  numArgs: 1,
198
- argTypes: ["text"],
225
+ argTypes: ["hbox"],
199
226
  allowedInText: true,
200
227
  },
201
228
  handler({parser}, args) {
@@ -3,6 +3,7 @@
3
3
 
4
4
  import {binrelClass} from "./mclass";
5
5
  import defineFunction from "../defineFunction";
6
+ import utils from "../utils";
6
7
 
7
8
  import * as html from "../buildHTML";
8
9
  import * as mml from "../buildMathML";
@@ -71,6 +72,7 @@ defineFunction({
71
72
  },
72
73
  handler: ({parser}, args) => {
73
74
  const body = args[0];
75
+ const isCharacterBox = utils.isCharacterBox(body);
74
76
  // amsbsy.sty's \boldsymbol uses \binrel spacing to inherit the
75
77
  // argument's bin|rel|ord status
76
78
  return {
@@ -85,6 +87,7 @@ defineFunction({
85
87
  body,
86
88
  },
87
89
  ],
90
+ isCharacterBox: isCharacterBox,
88
91
  };
89
92
  },
90
93
  });
@@ -92,7 +95,7 @@ defineFunction({
92
95
  // Old font changing functions
93
96
  defineFunction({
94
97
  type: "font",
95
- names: ["\\rm", "\\sf", "\\tt", "\\bf", "\\it"],
98
+ names: ["\\rm", "\\sf", "\\tt", "\\bf", "\\it", "\\cal"],
96
99
  props: {
97
100
  numArgs: 0,
98
101
  allowedInText: true,
@@ -4,29 +4,36 @@ import buildCommon from "../buildCommon";
4
4
  import delimiter from "../delimiter";
5
5
  import mathMLTree from "../mathMLTree";
6
6
  import Style from "../Style";
7
- import {assertNodeType, assertAtomFamily, checkNodeType} from "../parseNode";
7
+ import {assertNodeType} from "../parseNode";
8
8
  import {assert} from "../utils";
9
9
 
10
10
  import * as html from "../buildHTML";
11
11
  import * as mml from "../buildMathML";
12
12
  import {calculateSize} from "../units";
13
13
 
14
- const htmlBuilder = (group, options) => {
15
- // Fractions are handled in the TeXbook on pages 444-445, rules 15(a-e).
14
+ const adjustStyle = (size, originalStyle) => {
16
15
  // Figure out what style this fraction should be in based on the
17
16
  // function used
18
- let style = options.style;
19
- if (group.size === "display") {
20
- style = Style.DISPLAY;
21
- } else if (group.size === "text" &&
17
+ let style = originalStyle;
18
+ if (size === "display") {
19
+ // Get display style as a default.
20
+ // If incoming style is sub/sup, use style.text() to get correct size.
21
+ style = style.id >= Style.SCRIPT.id ? style.text() : Style.DISPLAY;
22
+ } else if (size === "text" &&
22
23
  style.size === Style.DISPLAY.size) {
23
24
  // We're in a \tfrac but incoming style is displaystyle, so:
24
25
  style = Style.TEXT;
25
- } else if (group.size === "script") {
26
+ } else if (size === "script") {
26
27
  style = Style.SCRIPT;
27
- } else if (group.size === "scriptscript") {
28
+ } else if (size === "scriptscript") {
28
29
  style = Style.SCRIPTSCRIPT;
29
30
  }
31
+ return style;
32
+ };
33
+
34
+ const htmlBuilder = (group, options) => {
35
+ // Fractions are handled in the TeXbook on pages 444-445, rules 15(a-e).
36
+ const style = adjustStyle(group.size, options.style);
30
37
 
31
38
  const nstyle = style.fracNum();
32
39
  const dstyle = style.fracDen();
@@ -69,7 +76,7 @@ const htmlBuilder = (group, options) => {
69
76
  let numShift;
70
77
  let clearance;
71
78
  let denomShift;
72
- if (style.size === Style.DISPLAY.size) {
79
+ if (style.size === Style.DISPLAY.size || group.size === "display") {
73
80
  numShift = options.fontMetrics().num1;
74
81
  if (ruleWidth > 0) {
75
82
  clearance = 3 * ruleSpacing;
@@ -176,7 +183,7 @@ const htmlBuilder = (group, options) => {
176
183
  };
177
184
 
178
185
  const mathmlBuilder = (group, options) => {
179
- const node = new mathMLTree.MathNode(
186
+ let node = new mathMLTree.MathNode(
180
187
  "mfrac",
181
188
  [
182
189
  mml.buildGroup(group.numer, options),
@@ -190,12 +197,22 @@ const mathmlBuilder = (group, options) => {
190
197
  node.setAttribute("linethickness", ruleWidth + "em");
191
198
  }
192
199
 
200
+ const style = adjustStyle(group.size, options.style);
201
+ if (style.size !== options.style.size) {
202
+ node = new mathMLTree.MathNode("mstyle", [node]);
203
+ const isDisplay = (style.size === Style.DISPLAY.size) ? "true" : "false";
204
+ node.setAttribute("displaystyle", isDisplay);
205
+ node.setAttribute("scriptlevel", "0");
206
+ }
207
+
193
208
  if (group.leftDelim != null || group.rightDelim != null) {
194
209
  const withDelims = [];
195
210
 
196
211
  if (group.leftDelim != null) {
197
212
  const leftOp = new mathMLTree.MathNode(
198
- "mo", [new mathMLTree.TextNode(group.leftDelim)]);
213
+ "mo",
214
+ [new mathMLTree.TextNode(group.leftDelim.replace("\\", ""))]
215
+ );
199
216
 
200
217
  leftOp.setAttribute("fence", "true");
201
218
 
@@ -206,7 +223,9 @@ const mathmlBuilder = (group, options) => {
206
223
 
207
224
  if (group.rightDelim != null) {
208
225
  const rightOp = new mathMLTree.MathNode(
209
- "mo", [new mathMLTree.TextNode(group.rightDelim)]);
226
+ "mo",
227
+ [new mathMLTree.TextNode(group.rightDelim.replace("\\", ""))]
228
+ );
210
229
 
211
230
  rightOp.setAttribute("fence", "true");
212
231
 
@@ -363,17 +382,10 @@ defineFunction({
363
382
  const denom = args[5];
364
383
 
365
384
  // Look into the parse nodes to get the desired delimiters.
366
- let leftNode = checkNodeType(args[0], "atom");
367
- if (leftNode) {
368
- leftNode = assertAtomFamily(args[0], "open");
369
- }
370
- const leftDelim = leftNode ? delimFromValue(leftNode.text) : null;
371
-
372
- let rightNode = checkNodeType(args[1], "atom");
373
- if (rightNode) {
374
- rightNode = assertAtomFamily(args[1], "close");
375
- }
376
- const rightDelim = rightNode ? delimFromValue(rightNode.text) : null;
385
+ const leftDelim = args[0].type === "atom" && args[0].family === "open"
386
+ ? delimFromValue(args[0].text) : null;
387
+ const rightDelim = args[1].type === "atom" && args[1].family === "close"
388
+ ? delimFromValue(args[1].text) : null;
377
389
 
378
390
  const barNode = assertNodeType(args[2], "size");
379
391
  let hasBarLine;
@@ -390,14 +402,14 @@ defineFunction({
390
402
 
391
403
  // Find out if we want displaystyle, textstyle, etc.
392
404
  let size = "auto";
393
- let styl = checkNodeType(args[3], "ordgroup");
394
- if (styl) {
405
+ let styl = args[3];
406
+ if (styl.type === "ordgroup") {
395
407
  if (styl.body.length > 0) {
396
408
  const textOrd = assertNodeType(styl.body[0], "textord");
397
409
  size = stylArray[Number(textOrd.text)];
398
410
  }
399
411
  } else {
400
- styl = assertNodeType(args[3], "textord");
412
+ styl = assertNodeType(styl, "textord");
401
413
  size = stylArray[Number(styl.text)];
402
414
  }
403
415
 
@@ -4,7 +4,7 @@ import buildCommon from "../buildCommon";
4
4
  import mathMLTree from "../mathMLTree";
5
5
  import stretchy from "../stretchy";
6
6
  import Style from "../Style";
7
- import {assertNodeType, checkNodeType} from "../parseNode";
7
+ import {assertNodeType} from "../parseNode";
8
8
 
9
9
  import * as html from "../buildHTML";
10
10
  import * as mml from "../buildMathML";
@@ -20,15 +20,14 @@ export const htmlBuilder: HtmlBuilderSupSub<"horizBrace"> = (grp, options) => {
20
20
  // Pull out the `ParseNode<"horizBrace">` if `grp` is a "supsub" node.
21
21
  let supSubGroup;
22
22
  let group: ParseNode<"horizBrace">;
23
- const supSub = checkNodeType(grp, "supsub");
24
- if (supSub) {
23
+ if (grp.type === "supsub") {
25
24
  // Ref: LaTeX source2e: }}}}\limits}
26
25
  // i.e. LaTeX treats the brace similar to an op and passes it
27
26
  // with \limits, so we need to assign supsub style.
28
- supSubGroup = supSub.sup ?
29
- html.buildGroup(supSub.sup, options.havingStyle(style.sup()), options) :
30
- html.buildGroup(supSub.sub, options.havingStyle(style.sub()), options);
31
- group = assertNodeType(supSub.base, "horizBrace");
27
+ supSubGroup = grp.sup ?
28
+ html.buildGroup(grp.sup, options.havingStyle(style.sup()), options) :
29
+ html.buildGroup(grp.sub, options.havingStyle(style.sub()), options);
30
+ group = assertNodeType(grp.base, "horizBrace");
32
31
  } else {
33
32
  group = assertNodeType(grp, "horizBrace");
34
33
  }
@@ -18,6 +18,14 @@ defineFunction({
18
18
  handler: ({parser}, args) => {
19
19
  const body = args[1];
20
20
  const href = assertNodeType(args[0], "url").url;
21
+
22
+ if (!parser.settings.isTrusted({
23
+ command: "\\href",
24
+ url: href,
25
+ })) {
26
+ return parser.formatUnsupportedCmd("\\href");
27
+ }
28
+
21
29
  return {
22
30
  type: "href",
23
31
  mode: parser.mode,
@@ -49,6 +57,14 @@ defineFunction({
49
57
  },
50
58
  handler: ({parser}, args) => {
51
59
  const href = assertNodeType(args[0], "url").url;
60
+
61
+ if (!parser.settings.isTrusted({
62
+ command: "\\url",
63
+ url: href,
64
+ })) {
65
+ return parser.formatUnsupportedCmd("\\url");
66
+ }
67
+
52
68
  const chars = [];
53
69
  for (let i = 0; i < href.length; i++) {
54
70
  let c = href[i];
@@ -0,0 +1,102 @@
1
+ // @flow
2
+ import defineFunction, {ordargument} from "../defineFunction";
3
+ import buildCommon from "../buildCommon";
4
+ import {assertNodeType} from "../parseNode";
5
+ import ParseError from "../ParseError";
6
+
7
+ import * as html from "../buildHTML";
8
+ import * as mml from "../buildMathML";
9
+
10
+ defineFunction({
11
+ type: "html",
12
+ names: ["\\htmlClass", "\\htmlId", "\\htmlStyle", "\\htmlData"],
13
+ props: {
14
+ numArgs: 2,
15
+ argTypes: ["raw", "original"],
16
+ allowedInText: true,
17
+ },
18
+ handler: ({parser, funcName, token}, args) => {
19
+ const value = assertNodeType(args[0], "raw").string;
20
+ const body = args[1];
21
+
22
+ if (parser.settings.strict) {
23
+ parser.settings.reportNonstrict("htmlExtension",
24
+ "HTML extension is disabled on strict mode");
25
+ }
26
+
27
+ let trustContext;
28
+ const attributes = {};
29
+
30
+ switch (funcName) {
31
+ case "\\htmlClass":
32
+ attributes.class = value;
33
+ trustContext = {
34
+ command: "\\htmlClass",
35
+ class: value,
36
+ };
37
+ break;
38
+ case "\\htmlId":
39
+ attributes.id = value;
40
+ trustContext = {
41
+ command: "\\htmlId",
42
+ id: value,
43
+ };
44
+ break;
45
+ case "\\htmlStyle":
46
+ attributes.style = value;
47
+ trustContext = {
48
+ command: "\\htmlStyle",
49
+ style: value,
50
+ };
51
+ break;
52
+ case "\\htmlData": {
53
+ const data = value.split(",");
54
+ for (let i = 0; i < data.length; i++) {
55
+ const keyVal = data[i].split("=");
56
+ if (keyVal.length !== 2) {
57
+ throw new ParseError(
58
+ "Error parsing key-value for \\htmlData");
59
+ }
60
+ attributes["data-" + keyVal[0].trim()] = keyVal[1].trim();
61
+ }
62
+
63
+ trustContext = {
64
+ command: "\\htmlData",
65
+ attributes,
66
+ };
67
+ break;
68
+ }
69
+ default:
70
+ throw new Error("Unrecognized html command");
71
+ }
72
+
73
+ if (!parser.settings.isTrusted(trustContext)) {
74
+ return parser.formatUnsupportedCmd(funcName);
75
+ }
76
+ return {
77
+ type: "html",
78
+ mode: parser.mode,
79
+ attributes,
80
+ body: ordargument(body),
81
+ };
82
+ },
83
+ htmlBuilder: (group, options) => {
84
+ const elements = html.buildExpression(group.body, options, false);
85
+
86
+ const classes = ["enclosing"];
87
+ if (group.attributes.class) {
88
+ classes.push(...group.attributes.class.trim().split(/\s+/));
89
+ }
90
+
91
+ const span = buildCommon.makeSpan(classes, elements, options);
92
+ for (const attr in group.attributes) {
93
+ if (attr !== "class" && group.attributes.hasOwnProperty(attr)) {
94
+ span.setAttribute(attr, group.attributes[attr]);
95
+ }
96
+ }
97
+ return span;
98
+ },
99
+ mathmlBuilder: (group, options) => {
100
+ return mml.buildExpressionRow(group.body, options);
101
+ },
102
+ });