katex 0.10.0-rc → 0.10.2
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.
- package/CHANGELOG.md +214 -126
- package/README.md +18 -17
- package/cli.js +5 -1
- package/contrib/auto-render/README.md +1 -1
- package/contrib/auto-render/auto-render.js +4 -1
- package/contrib/auto-render/test/auto-render-spec.js +17 -0
- package/contrib/copy-tex/README.md +8 -2
- package/contrib/copy-tex/copy-tex.js +0 -1
- package/contrib/copy-tex/copy-tex.webpack.js +6 -0
- package/contrib/mathtex-script-type/README.md +10 -6
- package/contrib/mhchem/README.md +19 -0
- package/contrib/mhchem/mhchem.js +1695 -0
- package/contrib/mhchem/mhchem.patch +235 -0
- package/dist/README.md +18 -17
- package/dist/contrib/auto-render.js +179 -161
- package/dist/contrib/auto-render.min.js +1 -1
- package/dist/contrib/auto-render.mjs +215 -0
- package/dist/contrib/copy-tex.js +84 -62
- package/dist/contrib/copy-tex.min.css +1 -1
- package/dist/contrib/copy-tex.min.js +1 -1
- package/dist/contrib/copy-tex.mjs +85 -0
- package/dist/contrib/mathtex-script-type.js +17 -14
- package/dist/contrib/mathtex-script-type.mjs +24 -0
- package/dist/contrib/mhchem.js +3241 -0
- package/dist/contrib/mhchem.min.js +1 -0
- package/dist/contrib/mhchem.mjs +3109 -0
- package/dist/fonts/KaTeX_AMS-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_AMS-Regular.woff +0 -0
- package/dist/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- package/dist/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- package/dist/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/dist/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Main-Bold.ttf +0 -0
- package/dist/fonts/KaTeX_Main-Bold.woff +0 -0
- package/dist/fonts/KaTeX_Main-Bold.woff2 +0 -0
- package/dist/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- package/dist/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- package/dist/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/dist/fonts/KaTeX_Main-Italic.ttf +0 -0
- package/dist/fonts/KaTeX_Main-Italic.woff +0 -0
- package/dist/fonts/KaTeX_Main-Italic.woff2 +0 -0
- package/dist/fonts/KaTeX_Main-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Main-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Main-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- package/dist/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- package/dist/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/dist/fonts/KaTeX_Math-Italic.ttf +0 -0
- package/dist/fonts/KaTeX_Math-Italic.woff +0 -0
- package/dist/fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/dist/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- package/dist/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- package/dist/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/dist/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- package/dist/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- package/dist/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/dist/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- package/dist/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Script-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Script-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Script-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Size1-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Size1-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Size2-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Size2-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Size3-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Size3-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Size4-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Size4-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/dist/katex.css +24 -9
- package/dist/katex.js +13295 -12413
- package/dist/katex.min.css +1 -1
- package/dist/katex.min.js +1 -1
- package/dist/katex.mjs +13388 -11826
- package/katex.js +1 -2
- package/package.json +60 -48
- package/src/Lexer.js +25 -25
- package/src/MacroExpander.js +0 -1
- package/src/Options.js +11 -75
- package/src/Parser.js +231 -313
- package/src/Settings.js +6 -0
- package/src/buildCommon.js +140 -103
- package/src/buildHTML.js +125 -121
- package/src/buildMathML.js +14 -4
- package/src/buildTree.js +16 -10
- package/src/delimiter.js +4 -3
- package/src/domTree.js +91 -44
- package/src/environments/array.js +120 -7
- package/src/fontMetrics.js +3 -2
- package/src/functions/arrow.js +21 -7
- package/src/functions/color.js +2 -37
- package/src/functions/delimsizing.js +18 -11
- package/src/functions/enclose.js +19 -4
- package/src/functions/environment.js +35 -4
- package/src/functions/font.js +1 -2
- package/src/functions/genfrac.js +35 -20
- package/src/functions/href.js +5 -3
- package/src/functions/includegraphics.js +146 -0
- package/src/functions/mclass.js +1 -0
- package/src/functions/op.js +21 -32
- package/src/functions/operatorname.js +1 -2
- package/src/functions/ordgroup.js +4 -0
- package/src/functions/phantom.js +7 -3
- package/src/functions/rule.js +20 -9
- package/src/functions/sizing.js +2 -4
- package/src/functions/smash.js +5 -2
- package/src/functions/sqrt.js +1 -4
- package/src/functions/styling.js +0 -1
- package/src/functions/supsub.js +6 -2
- package/src/functions/symbolsOp.js +4 -0
- package/src/functions/symbolsSpacing.js +29 -6
- package/src/functions/tag.js +20 -4
- package/src/functions/text.js +6 -4
- package/src/functions/verb.js +16 -4
- package/src/functions.js +2 -0
- package/src/katex.less +35 -12
- package/src/macros.js +161 -36
- package/src/mathMLTree.js +17 -19
- package/src/parseNode.js +27 -1
- package/src/stretchy.js +3 -1
- package/src/svgGeometry.js +1 -1
- package/src/symbols.js +39 -17
- package/src/tree.js +0 -4
- package/src/types.js +4 -3
- package/src/unicodeMake.js +1 -1
- package/src/utils.js +1 -62
- package/src/wide-character.js +2 -2
|
@@ -25,6 +25,9 @@ export type AlignSpec = { type: "separator", separator: string } | {
|
|
|
25
25
|
postgap?: number,
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
+
// Type to indicate column separation in MathML
|
|
29
|
+
export type ColSeparationType = "align" | "alignat";
|
|
30
|
+
|
|
28
31
|
function getHLines(parser: Parser): boolean[] {
|
|
29
32
|
// Return an array. The array length = number of hlines.
|
|
30
33
|
// Each element in the array tells if the line is dashed.
|
|
@@ -48,11 +51,12 @@ function getHLines(parser: Parser): boolean[] {
|
|
|
48
51
|
*/
|
|
49
52
|
function parseArray(
|
|
50
53
|
parser: Parser,
|
|
51
|
-
{hskipBeforeAndAfter, addJot, cols, arraystretch}: {|
|
|
54
|
+
{hskipBeforeAndAfter, addJot, cols, arraystretch, colSeparationType}: {|
|
|
52
55
|
hskipBeforeAndAfter?: boolean,
|
|
53
56
|
addJot?: boolean,
|
|
54
57
|
cols?: AlignSpec[],
|
|
55
58
|
arraystretch?: number,
|
|
59
|
+
colSeparationType?: ColSeparationType,
|
|
56
60
|
|},
|
|
57
61
|
style: StyleStr,
|
|
58
62
|
): ParseNode<"array"> {
|
|
@@ -114,11 +118,8 @@ function parseArray(
|
|
|
114
118
|
}
|
|
115
119
|
break;
|
|
116
120
|
} else if (next === "\\cr") {
|
|
117
|
-
const cr = parser.parseFunction();
|
|
118
|
-
|
|
119
|
-
throw new ParseError(`Failed to parse function after ${next}`);
|
|
120
|
-
}
|
|
121
|
-
rowGaps.push(assertNodeType(cr, "cr").size);
|
|
121
|
+
const cr = assertNodeType(parser.parseFunction(), "cr");
|
|
122
|
+
rowGaps.push(cr.size);
|
|
122
123
|
|
|
123
124
|
// check for \hline(s) following the row separator
|
|
124
125
|
hLinesBeforeRow.push(getHLines(parser));
|
|
@@ -141,6 +142,7 @@ function parseArray(
|
|
|
141
142
|
rowGaps,
|
|
142
143
|
hskipBeforeAndAfter,
|
|
143
144
|
hLinesBeforeRow,
|
|
145
|
+
colSeparationType,
|
|
144
146
|
};
|
|
145
147
|
}
|
|
146
148
|
|
|
@@ -370,8 +372,14 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
|
|
|
370
372
|
return buildCommon.makeSpan(["mord"], [body], options);
|
|
371
373
|
};
|
|
372
374
|
|
|
375
|
+
const alignMap = {
|
|
376
|
+
c: "center ",
|
|
377
|
+
l: "left ",
|
|
378
|
+
r: "right ",
|
|
379
|
+
};
|
|
380
|
+
|
|
373
381
|
const mathmlBuilder: MathMLBuilder<"array"> = function(group, options) {
|
|
374
|
-
|
|
382
|
+
const table = new mathMLTree.MathNode(
|
|
375
383
|
"mtable", group.body.map(function(row) {
|
|
376
384
|
return new mathMLTree.MathNode(
|
|
377
385
|
"mtr", row.map(function(cell) {
|
|
@@ -379,6 +387,110 @@ const mathmlBuilder: MathMLBuilder<"array"> = function(group, options) {
|
|
|
379
387
|
"mtd", [mml.buildGroup(cell, options)]);
|
|
380
388
|
}));
|
|
381
389
|
}));
|
|
390
|
+
|
|
391
|
+
// Set column alignment, row spacing, column spacing, and
|
|
392
|
+
// array lines by setting attributes on the table element.
|
|
393
|
+
|
|
394
|
+
// Set the row spacing. In MathML, we specify a gap distance.
|
|
395
|
+
// We do not use rowGap[] because MathML automatically increases
|
|
396
|
+
// cell height with the height/depth of the element content.
|
|
397
|
+
|
|
398
|
+
// LaTeX \arraystretch multiplies the row baseline-to-baseline distance.
|
|
399
|
+
// We simulate this by adding (arraystretch - 1)em to the gap. This
|
|
400
|
+
// does a reasonable job of adjusting arrays containing 1 em tall content.
|
|
401
|
+
|
|
402
|
+
// The 0.16 and 0.09 values are found emprically. They produce an array
|
|
403
|
+
// similar to LaTeX and in which content does not interfere with \hines.
|
|
404
|
+
const gap = 0.16 + group.arraystretch - 1 + (group.addJot ? 0.09 : 0);
|
|
405
|
+
table.setAttribute("rowspacing", gap + "em");
|
|
406
|
+
|
|
407
|
+
// MathML table lines go only between cells.
|
|
408
|
+
// To place a line on an edge we'll use <menclose>, if necessary.
|
|
409
|
+
let menclose = "";
|
|
410
|
+
let align = "";
|
|
411
|
+
|
|
412
|
+
if (group.cols) {
|
|
413
|
+
// Find column alignment, column spacing, and vertical lines.
|
|
414
|
+
const cols = group.cols;
|
|
415
|
+
let columnLines = "";
|
|
416
|
+
let prevTypeWasAlign = false;
|
|
417
|
+
let iStart = 0;
|
|
418
|
+
let iEnd = cols.length;
|
|
419
|
+
|
|
420
|
+
if (cols[0].type === "separator") {
|
|
421
|
+
menclose += "top ";
|
|
422
|
+
iStart = 1;
|
|
423
|
+
}
|
|
424
|
+
if (cols[cols.length - 1].type === "separator") {
|
|
425
|
+
menclose += "bottom ";
|
|
426
|
+
iEnd -= 1;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
for (let i = iStart; i < iEnd; i++) {
|
|
430
|
+
if (cols[i].type === "align") {
|
|
431
|
+
align += alignMap[cols[i].align];
|
|
432
|
+
|
|
433
|
+
if (prevTypeWasAlign) {
|
|
434
|
+
columnLines += "none ";
|
|
435
|
+
}
|
|
436
|
+
prevTypeWasAlign = true;
|
|
437
|
+
} else if (cols[i].type === "separator") {
|
|
438
|
+
// MathML accepts only single lines between cells.
|
|
439
|
+
// So we read only the first of consecutive separators.
|
|
440
|
+
if (prevTypeWasAlign) {
|
|
441
|
+
columnLines += cols[i].separator === "|"
|
|
442
|
+
? "solid "
|
|
443
|
+
: "dashed ";
|
|
444
|
+
prevTypeWasAlign = false;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
table.setAttribute("columnalign", align.trim());
|
|
450
|
+
|
|
451
|
+
if (/[sd]/.test(columnLines)) {
|
|
452
|
+
table.setAttribute("columnlines", columnLines.trim());
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Set column spacing.
|
|
457
|
+
if (group.colSeparationType === "align") {
|
|
458
|
+
const cols = group.cols || [];
|
|
459
|
+
let spacing = "";
|
|
460
|
+
for (let i = 1; i < cols.length; i++) {
|
|
461
|
+
spacing += i % 2 ? "0em " : "1em ";
|
|
462
|
+
}
|
|
463
|
+
table.setAttribute("columnspacing", spacing.trim());
|
|
464
|
+
} else if (group.colSeparationType === "alignat") {
|
|
465
|
+
table.setAttribute("columnspacing", "0em");
|
|
466
|
+
} else {
|
|
467
|
+
table.setAttribute("columnspacing", "1em");
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Address \hline and \hdashline
|
|
471
|
+
let rowLines = "";
|
|
472
|
+
const hlines = group.hLinesBeforeRow;
|
|
473
|
+
|
|
474
|
+
menclose += hlines[0].length > 0 ? "left " : "";
|
|
475
|
+
menclose += hlines[hlines.length - 1].length > 0 ? "right " : "";
|
|
476
|
+
|
|
477
|
+
for (let i = 1; i < hlines.length - 1; i++) {
|
|
478
|
+
rowLines += (hlines[i].length === 0)
|
|
479
|
+
? "none "
|
|
480
|
+
// MathML accepts only a single line between rows. Read one element.
|
|
481
|
+
: hlines[i][0] ? "dashed " : "solid ";
|
|
482
|
+
}
|
|
483
|
+
if (/[sd]/.test(rowLines)) {
|
|
484
|
+
table.setAttribute("rowlines", rowLines.trim());
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (menclose === "") {
|
|
488
|
+
return table;
|
|
489
|
+
} else {
|
|
490
|
+
const wrapper = new mathMLTree.MathNode("menclose", [table]);
|
|
491
|
+
wrapper.setAttribute("notation", menclose.trim());
|
|
492
|
+
return wrapper;
|
|
493
|
+
}
|
|
382
494
|
};
|
|
383
495
|
|
|
384
496
|
// Convenience function for aligned and alignedat environments.
|
|
@@ -451,6 +563,7 @@ const alignedHandler = function(context, args) {
|
|
|
451
563
|
postgap: 0,
|
|
452
564
|
};
|
|
453
565
|
}
|
|
566
|
+
res.colSeparationType = isAligned ? "align" : "alignat";
|
|
454
567
|
return res;
|
|
455
568
|
};
|
|
456
569
|
|
package/src/fontMetrics.js
CHANGED
|
@@ -211,10 +211,11 @@ export function getCharacterMetrics(
|
|
|
211
211
|
throw new Error(`Font metrics not found for font: ${font}.`);
|
|
212
212
|
}
|
|
213
213
|
let ch = character.charCodeAt(0);
|
|
214
|
-
|
|
214
|
+
let metrics = metricMap[font][ch];
|
|
215
|
+
if (!metrics && character[0] in extraCharacterMap) {
|
|
215
216
|
ch = extraCharacterMap[character[0]].charCodeAt(0);
|
|
217
|
+
metrics = metricMap[font][ch];
|
|
216
218
|
}
|
|
217
|
-
let metrics = metricMap[font][ch];
|
|
218
219
|
|
|
219
220
|
if (!metrics && mode === 'text') {
|
|
220
221
|
// We don't typically have font metrics for Asian scripts.
|
package/src/functions/arrow.js
CHANGED
|
@@ -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",
|
|
@@ -44,15 +52,19 @@ defineFunction({
|
|
|
44
52
|
// Build the argument groups in the appropriate style.
|
|
45
53
|
// Ref: amsmath.dtx: \hbox{$\scriptstyle\mkern#3mu{#6}\mkern#4mu$}%
|
|
46
54
|
|
|
55
|
+
// Some groups can return document fragments. Handle those by wrapping
|
|
56
|
+
// them in a span.
|
|
47
57
|
let newOptions = options.havingStyle(style.sup());
|
|
48
|
-
const upperGroup =
|
|
58
|
+
const upperGroup = buildCommon.wrapFragment(
|
|
59
|
+
html.buildGroup(group.body, newOptions, options), options);
|
|
49
60
|
upperGroup.classes.push("x-arrow-pad");
|
|
50
61
|
|
|
51
62
|
let lowerGroup;
|
|
52
63
|
if (group.below) {
|
|
53
64
|
// Build the lower group
|
|
54
65
|
newOptions = options.havingStyle(style.sub());
|
|
55
|
-
lowerGroup =
|
|
66
|
+
lowerGroup = buildCommon.wrapFragment(
|
|
67
|
+
html.buildGroup(group.below, newOptions, options), options);
|
|
56
68
|
lowerGroup.classes.push("x-arrow-pad");
|
|
57
69
|
}
|
|
58
70
|
|
|
@@ -101,12 +113,11 @@ defineFunction({
|
|
|
101
113
|
mathmlBuilder(group, options) {
|
|
102
114
|
const arrowNode = stretchy.mathMLnode(group.label);
|
|
103
115
|
let node;
|
|
104
|
-
let lowerNode;
|
|
105
116
|
|
|
106
117
|
if (group.body) {
|
|
107
|
-
const upperNode = mml.buildGroup(group.body, options);
|
|
118
|
+
const upperNode = paddedNode(mml.buildGroup(group.body, options));
|
|
108
119
|
if (group.below) {
|
|
109
|
-
lowerNode = mml.buildGroup(group.below, options);
|
|
120
|
+
const lowerNode = paddedNode(mml.buildGroup(group.below, options));
|
|
110
121
|
node = new mathMLTree.MathNode(
|
|
111
122
|
"munderover", [arrowNode, lowerNode, upperNode]
|
|
112
123
|
);
|
|
@@ -114,10 +125,13 @@ defineFunction({
|
|
|
114
125
|
node = new mathMLTree.MathNode("mover", [arrowNode, upperNode]);
|
|
115
126
|
}
|
|
116
127
|
} else if (group.below) {
|
|
117
|
-
lowerNode = mml.buildGroup(group.below, options);
|
|
128
|
+
const lowerNode = paddedNode(mml.buildGroup(group.below, options));
|
|
118
129
|
node = new mathMLTree.MathNode("munder", [arrowNode, lowerNode]);
|
|
119
130
|
} else {
|
|
120
|
-
|
|
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]);
|
|
121
135
|
}
|
|
122
136
|
return node;
|
|
123
137
|
},
|
package/src/functions/color.js
CHANGED
|
@@ -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,
|
|
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"],
|
|
@@ -35,7 +35,8 @@ const delimiterSizes = {
|
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
const delimiters = [
|
|
38
|
-
"(", "
|
|
38
|
+
"(", "\\lparen", ")", "\\rparen",
|
|
39
|
+
"[", "\\lbrack", "]", "\\rbrack",
|
|
39
40
|
"\\{", "\\lbrace", "\\}", "\\rbrace",
|
|
40
41
|
"\\lfloor", "\\rfloor", "\u230a", "\u230b",
|
|
41
42
|
"\\lceil", "\\rceil", "\u2308", "\u2309",
|
|
@@ -64,8 +65,7 @@ function checkDelimiter(
|
|
|
64
65
|
} else {
|
|
65
66
|
throw new ParseError(
|
|
66
67
|
"Invalid delimiter: '" +
|
|
67
|
-
|
|
68
|
-
(symDelim ? symDelim.text : JSON["stringify"](delim)) +
|
|
68
|
+
(symDelim ? symDelim.text : JSON.stringify(delim)) +
|
|
69
69
|
"' after '" + context.funcName + "'", delim);
|
|
70
70
|
}
|
|
71
71
|
}
|
|
@@ -171,23 +171,20 @@ defineFunction({
|
|
|
171
171
|
--parser.leftrightDepth;
|
|
172
172
|
// Check the next token
|
|
173
173
|
parser.expect("\\right", false);
|
|
174
|
-
const right = parser.parseFunction();
|
|
175
|
-
if (!right) {
|
|
176
|
-
throw new ParseError('failed to parse function after \\right');
|
|
177
|
-
}
|
|
174
|
+
const right = assertNodeType(parser.parseFunction(), "leftright-right");
|
|
178
175
|
return {
|
|
179
176
|
type: "leftright",
|
|
180
177
|
mode: parser.mode,
|
|
181
178
|
body,
|
|
182
179
|
left: delim.text,
|
|
183
|
-
right:
|
|
180
|
+
right: right.delim,
|
|
184
181
|
};
|
|
185
182
|
},
|
|
186
183
|
htmlBuilder: (group, options) => {
|
|
187
184
|
assertParsed(group);
|
|
188
185
|
// Build the inner expression
|
|
189
186
|
const inner = html.buildExpression(group.body, options, true,
|
|
190
|
-
[
|
|
187
|
+
["mopen", "mclose"]);
|
|
191
188
|
|
|
192
189
|
let innerHeight = 0;
|
|
193
190
|
let innerDepth = 0;
|
|
@@ -321,9 +318,19 @@ defineFunction({
|
|
|
321
318
|
return middleDelim;
|
|
322
319
|
},
|
|
323
320
|
mathmlBuilder: (group, options) => {
|
|
324
|
-
|
|
325
|
-
|
|
321
|
+
// A Firefox \middle will strech a character vertically only if it
|
|
322
|
+
// is in the fence part of the operator dictionary at:
|
|
323
|
+
// https://www.w3.org/TR/MathML3/appendixc.html.
|
|
324
|
+
// So we need to avoid U+2223 and use plain "|" instead.
|
|
325
|
+
const textNode = (group.delim === "\\vert" || group.delim === "|")
|
|
326
|
+
? mml.makeText("|", "text")
|
|
327
|
+
: mml.makeText(group.delim, group.mode);
|
|
328
|
+
const middleNode = new mathMLTree.MathNode("mo", [textNode]);
|
|
326
329
|
middleNode.setAttribute("fence", "true");
|
|
330
|
+
// MathML gives 5/18em spacing to each <mo> element.
|
|
331
|
+
// \middle should get delimiter spacing instead.
|
|
332
|
+
middleNode.setAttribute("lspace", "0.05em");
|
|
333
|
+
middleNode.setAttribute("rspace", "0.05em");
|
|
327
334
|
return middleNode;
|
|
328
335
|
},
|
|
329
336
|
});
|
package/src/functions/enclose.js
CHANGED
|
@@ -12,7 +12,10 @@ import * as mml from "../buildMathML";
|
|
|
12
12
|
|
|
13
13
|
const htmlBuilder = (group, options) => {
|
|
14
14
|
// \cancel, \bcancel, \xcancel, \sout, \fbox, \colorbox, \fcolorbox
|
|
15
|
-
|
|
15
|
+
// Some groups can return document fragments. Handle those by wrapping
|
|
16
|
+
// them in a span.
|
|
17
|
+
const inner = buildCommon.wrapFragment(
|
|
18
|
+
html.buildGroup(group.body, options), options);
|
|
16
19
|
|
|
17
20
|
const label = group.label.substr(1);
|
|
18
21
|
const scale = options.sizeMultiplier;
|
|
@@ -109,7 +112,9 @@ const htmlBuilder = (group, options) => {
|
|
|
109
112
|
|
|
110
113
|
const mathmlBuilder = (group, options) => {
|
|
111
114
|
const node = new mathMLTree.MathNode(
|
|
112
|
-
|
|
115
|
+
(group.label.indexOf("colorbox") > -1) ? "mpadded" : "menclose",
|
|
116
|
+
[mml.buildGroup(group.body, options)]
|
|
117
|
+
);
|
|
113
118
|
switch (group.label) {
|
|
114
119
|
case "\\cancel":
|
|
115
120
|
node.setAttribute("notation", "updiagonalstrike");
|
|
@@ -124,8 +129,18 @@ const mathmlBuilder = (group, options) => {
|
|
|
124
129
|
node.setAttribute("notation", "box");
|
|
125
130
|
break;
|
|
126
131
|
case "\\fcolorbox":
|
|
127
|
-
|
|
128
|
-
|
|
132
|
+
case "\\colorbox":
|
|
133
|
+
// <menclose> doesn't have a good notation option. So use <mpadded>
|
|
134
|
+
// instead. Set some attributes that come included with <menclose>.
|
|
135
|
+
node.setAttribute("width", "+6pt");
|
|
136
|
+
node.setAttribute("height", "+6pt");
|
|
137
|
+
node.setAttribute("lspace", "3pt"); // LaTeX source2e: \fboxsep = 3pt
|
|
138
|
+
node.setAttribute("voffset", "3pt");
|
|
139
|
+
if (group.label === "\\fcolorbox") {
|
|
140
|
+
const thk = options.fontMetrics().defaultRuleThickness;
|
|
141
|
+
node.setAttribute("style", "border: " + thk + "em solid " +
|
|
142
|
+
String(group.borderColor));
|
|
143
|
+
}
|
|
129
144
|
break;
|
|
130
145
|
case "\\xcancel":
|
|
131
146
|
node.setAttribute("notation", "updiagonalstrike downdiagonalstrike");
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
import defineFunction from "../defineFunction";
|
|
3
3
|
import ParseError from "../ParseError";
|
|
4
4
|
import {assertNodeType} from "../parseNode";
|
|
5
|
+
import environments from "../environments";
|
|
5
6
|
|
|
6
7
|
// Environment delimiters. HTML/MathML rendering is defined in the corresponding
|
|
7
8
|
// defineEnvironment definitions.
|
|
9
|
+
// $FlowFixMe, "environment" handler returns an environment ParseNode
|
|
8
10
|
defineFunction({
|
|
9
11
|
type: "environment",
|
|
10
12
|
names: ["\\begin", "\\end"],
|
|
@@ -12,19 +14,48 @@ defineFunction({
|
|
|
12
14
|
numArgs: 1,
|
|
13
15
|
argTypes: ["text"],
|
|
14
16
|
},
|
|
15
|
-
handler({parser}, args) {
|
|
17
|
+
handler({parser, funcName}, args) {
|
|
16
18
|
const nameGroup = args[0];
|
|
17
19
|
if (nameGroup.type !== "ordgroup") {
|
|
18
20
|
throw new ParseError("Invalid environment name", nameGroup);
|
|
19
21
|
}
|
|
20
|
-
let
|
|
22
|
+
let envName = "";
|
|
21
23
|
for (let i = 0; i < nameGroup.body.length; ++i) {
|
|
22
|
-
|
|
24
|
+
envName += assertNodeType(nameGroup.body[i], "textord").text;
|
|
23
25
|
}
|
|
26
|
+
|
|
27
|
+
if (funcName === "\\begin") {
|
|
28
|
+
// begin...end is similar to left...right
|
|
29
|
+
if (!environments.hasOwnProperty(envName)) {
|
|
30
|
+
throw new ParseError(
|
|
31
|
+
"No such environment: " + envName, nameGroup);
|
|
32
|
+
}
|
|
33
|
+
// Build the environment object. Arguments and other information will
|
|
34
|
+
// be made available to the begin and end methods using properties.
|
|
35
|
+
const env = environments[envName];
|
|
36
|
+
const {args, optArgs} =
|
|
37
|
+
parser.parseArguments("\\begin{" + envName + "}", env);
|
|
38
|
+
const context = {
|
|
39
|
+
mode: parser.mode,
|
|
40
|
+
envName,
|
|
41
|
+
parser,
|
|
42
|
+
};
|
|
43
|
+
const result = env.handler(context, args, optArgs);
|
|
44
|
+
parser.expect("\\end", false);
|
|
45
|
+
const endNameToken = parser.nextToken;
|
|
46
|
+
const end = assertNodeType(parser.parseFunction(), "environment");
|
|
47
|
+
if (end.name !== envName) {
|
|
48
|
+
throw new ParseError(
|
|
49
|
+
`Mismatch: \\begin{${envName}} matched by \\end{${end.name}}`,
|
|
50
|
+
endNameToken);
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
|
|
24
55
|
return {
|
|
25
56
|
type: "environment",
|
|
26
57
|
mode: parser.mode,
|
|
27
|
-
name,
|
|
58
|
+
name: envName,
|
|
28
59
|
nameGroup,
|
|
29
60
|
};
|
|
30
61
|
},
|
package/src/functions/font.js
CHANGED
|
@@ -32,7 +32,7 @@ defineFunction({
|
|
|
32
32
|
type: "font",
|
|
33
33
|
names: [
|
|
34
34
|
// styles, except \boldsymbol defined below
|
|
35
|
-
"\\mathrm", "\\mathit", "\\mathbf",
|
|
35
|
+
"\\mathrm", "\\mathit", "\\mathbf", "\\mathnormal",
|
|
36
36
|
|
|
37
37
|
// families
|
|
38
38
|
"\\mathbb", "\\mathcal", "\\mathfrak", "\\mathscr", "\\mathsf",
|
|
@@ -99,7 +99,6 @@ defineFunction({
|
|
|
99
99
|
},
|
|
100
100
|
handler: ({parser, funcName, breakOnTokenText}, args) => {
|
|
101
101
|
const {mode} = parser;
|
|
102
|
-
parser.consumeSpaces();
|
|
103
102
|
const body = parser.parseExpression(true, breakOnTokenText);
|
|
104
103
|
const style = `math${funcName.slice(1)}`;
|
|
105
104
|
|
package/src/functions/genfrac.js
CHANGED
|
@@ -11,22 +11,29 @@ import * as html from "../buildHTML";
|
|
|
11
11
|
import * as mml from "../buildMathML";
|
|
12
12
|
import {calculateSize} from "../units";
|
|
13
13
|
|
|
14
|
-
const
|
|
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 =
|
|
19
|
-
if (
|
|
20
|
-
style
|
|
21
|
-
|
|
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 (
|
|
26
|
+
} else if (size === "script") {
|
|
26
27
|
style = Style.SCRIPT;
|
|
27
|
-
} else if (
|
|
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
|
-
|
|
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",
|
|
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",
|
|
226
|
+
"mo",
|
|
227
|
+
[new mathMLTree.TextNode(group.rightDelim.replace("\\", ""))]
|
|
228
|
+
);
|
|
210
229
|
|
|
211
230
|
rightOp.setAttribute("fence", "true");
|
|
212
231
|
|
|
@@ -363,21 +382,17 @@ 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], "
|
|
385
|
+
let leftNode = checkNodeType(args[0], "atom");
|
|
367
386
|
if (leftNode) {
|
|
368
|
-
leftNode = assertAtomFamily(leftNode.body[0], "open");
|
|
369
|
-
} else {
|
|
370
387
|
leftNode = assertAtomFamily(args[0], "open");
|
|
371
388
|
}
|
|
372
|
-
const leftDelim = delimFromValue(leftNode.text);
|
|
389
|
+
const leftDelim = leftNode ? delimFromValue(leftNode.text) : null;
|
|
373
390
|
|
|
374
|
-
let rightNode = checkNodeType(args[1], "
|
|
391
|
+
let rightNode = checkNodeType(args[1], "atom");
|
|
375
392
|
if (rightNode) {
|
|
376
|
-
rightNode = assertAtomFamily(rightNode.body[0], "close");
|
|
377
|
-
} else {
|
|
378
393
|
rightNode = assertAtomFamily(args[1], "close");
|
|
379
394
|
}
|
|
380
|
-
const rightDelim = delimFromValue(rightNode.text);
|
|
395
|
+
const rightDelim = rightNode ? delimFromValue(rightNode.text) : null;
|
|
381
396
|
|
|
382
397
|
const barNode = assertNodeType(args[2], "size");
|
|
383
398
|
let hasBarLine;
|
package/src/functions/href.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import defineFunction, {ordargument} from "../defineFunction";
|
|
3
3
|
import buildCommon from "../buildCommon";
|
|
4
4
|
import {assertNodeType} from "../parseNode";
|
|
5
|
-
import {assertType} from "../utils";
|
|
6
5
|
import {MathNode} from "../mathMLTree";
|
|
7
6
|
|
|
8
7
|
import * as html from "../buildHTML";
|
|
@@ -31,8 +30,11 @@ defineFunction({
|
|
|
31
30
|
return buildCommon.makeAnchor(group.href, [], elements, options);
|
|
32
31
|
},
|
|
33
32
|
mathmlBuilder: (group, options) => {
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
let math = mml.buildExpressionRow(group.body, options);
|
|
34
|
+
if (!(math instanceof MathNode)) {
|
|
35
|
+
math = new MathNode("mrow", [math]);
|
|
36
|
+
}
|
|
37
|
+
math.setAttribute("href", group.href);
|
|
36
38
|
return math;
|
|
37
39
|
},
|
|
38
40
|
});
|