mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-05 03:08:40 +00:00
Implement $...$ within \text via styling node (#637)
This commit is contained in:
committed by
Kevin Barabash
parent
9913242245
commit
7fdb1eed81
@@ -507,6 +507,20 @@ Parser.prototype.parseImplicitGroup = function() {
|
||||
body: new ParseNode("ordgroup", body, this.mode),
|
||||
}, this.mode);
|
||||
}
|
||||
} else if (func === "$") {
|
||||
if (this.mode === "math") {
|
||||
throw new ParseError("$ within math mode");
|
||||
}
|
||||
this.consume();
|
||||
const outerMode = this.mode;
|
||||
this.switchMode("math");
|
||||
const body = this.parseExpression(false, "$");
|
||||
this.expect("$", true);
|
||||
this.switchMode(outerMode);
|
||||
return new ParseNode("styling", {
|
||||
style: "text",
|
||||
value: body,
|
||||
}, "math");
|
||||
} else {
|
||||
// Defer to parseFunction if it's not a function we handle
|
||||
return this.parseFunction(start);
|
||||
@@ -878,6 +892,10 @@ Parser.prototype.parseSymbol = function() {
|
||||
return new ParseFuncOrArgument(
|
||||
new ParseNode("textord", nucleus.text, this.mode, nucleus),
|
||||
false, nucleus);
|
||||
} else if (nucleus.text === "$") {
|
||||
return new ParseFuncOrArgument(
|
||||
nucleus.text,
|
||||
false, nucleus);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@@ -63,13 +63,19 @@ const getVariant = function(group, options) {
|
||||
*/
|
||||
const groupTypes = {};
|
||||
|
||||
const defaultVariant = {
|
||||
"mi": "italic",
|
||||
"mn": "normal",
|
||||
"mtext": "normal",
|
||||
};
|
||||
|
||||
groupTypes.mathord = function(group, options) {
|
||||
const node = new mathMLTree.MathNode(
|
||||
"mi",
|
||||
[makeText(group.value, group.mode)]);
|
||||
|
||||
const variant = getVariant(group, options);
|
||||
if (variant) {
|
||||
const variant = getVariant(group, options) || "italic";
|
||||
if (variant !== defaultVariant[node.type]) {
|
||||
node.setAttribute("mathvariant", variant);
|
||||
}
|
||||
return node;
|
||||
@@ -81,15 +87,16 @@ groupTypes.textord = function(group, options) {
|
||||
const variant = getVariant(group, options) || "normal";
|
||||
|
||||
let node;
|
||||
if (/[0-9]/.test(group.value)) {
|
||||
if (group.mode === 'text') {
|
||||
node = new mathMLTree.MathNode("mtext", [text]);
|
||||
} else if (/[0-9]/.test(group.value)) {
|
||||
// TODO(kevinb) merge adjacent <mn> nodes
|
||||
// do it as a post processing step
|
||||
node = new mathMLTree.MathNode("mn", [text]);
|
||||
if (options.font) {
|
||||
node.setAttribute("mathvariant", variant);
|
||||
}
|
||||
} else {
|
||||
node = new mathMLTree.MathNode("mi", [text]);
|
||||
}
|
||||
if (variant !== defaultVariant[node.type]) {
|
||||
node.setAttribute("mathvariant", variant);
|
||||
}
|
||||
|
||||
@@ -149,11 +156,32 @@ groupTypes.ordgroup = function(group, options) {
|
||||
};
|
||||
|
||||
groupTypes.text = function(group, options) {
|
||||
const inner = buildExpression(group.value.body, options);
|
||||
const body = group.value.body;
|
||||
|
||||
const node = new mathMLTree.MathNode("mtext", inner);
|
||||
// Convert each element of the body into MathML, and combine consecutive
|
||||
// <mtext> outputs into a single <mtext> tag. In this way, we don't
|
||||
// nest non-text items (e.g., $nested-math$) within an <mtext>.
|
||||
const inner = [];
|
||||
let currentText = null;
|
||||
for (let i = 0; i < body.length; i++) {
|
||||
const group = buildGroup(body[i], options);
|
||||
if (group.type === 'mtext' && currentText != null) {
|
||||
Array.prototype.push.apply(currentText.children, group.children);
|
||||
} else {
|
||||
inner.push(group);
|
||||
if (group.type === 'mtext') {
|
||||
currentText = group;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
// If there is a single tag in the end (presumably <mtext>),
|
||||
// just return it. Otherwise, wrap them in an <mrow>.
|
||||
if (inner.length === 1) {
|
||||
return inner[0];
|
||||
} else {
|
||||
return new mathMLTree.MathNode("mrow", inner);
|
||||
}
|
||||
};
|
||||
|
||||
groupTypes.color = function(group, options) {
|
||||
|
@@ -732,6 +732,7 @@ describe("A text parser", function() {
|
||||
const badTextExpression = "\\text{a b%}";
|
||||
const badFunctionExpression = "\\text{\\sqrt{x}}";
|
||||
const mathTokenAfterText = "\\text{sin}^2";
|
||||
const textWithEmbeddedMath = "\\text{graph: $y = mx + b$}";
|
||||
|
||||
it("should not fail", function() {
|
||||
expect(textExpression).toParse();
|
||||
@@ -789,6 +790,10 @@ describe("A text parser", function() {
|
||||
parse.value.body.map(function(n) { return n.value; }).join("")
|
||||
).toBe("moo");
|
||||
});
|
||||
|
||||
it("should parse math within text group", function() {
|
||||
expect(textWithEmbeddedMath).toParse();
|
||||
});
|
||||
});
|
||||
|
||||
describe("A color parser", function() {
|
||||
@@ -1539,7 +1544,7 @@ describe("A MathML font tree-builder", function() {
|
||||
const markup = buildMathML(tree, tex, defaultOptions).toMarkup();
|
||||
expect(markup).toContain("<mi mathvariant=\"double-struck\">A</mi>");
|
||||
expect(markup).toContain("<mi>x</mi>");
|
||||
expect(markup).toContain("<mn mathvariant=\"normal\">2</mn>");
|
||||
expect(markup).toContain("<mn>2</mn>");
|
||||
expect(markup).toContain("<mi>\u03c9</mi>"); // \omega
|
||||
expect(markup).toContain("<mi mathvariant=\"normal\">\u03A9</mi>"); // \Omega
|
||||
expect(markup).toContain("<mi>\u0131</mi>"); // \imath
|
||||
@@ -1552,7 +1557,7 @@ describe("A MathML font tree-builder", function() {
|
||||
const markup = buildMathML(tree, tex, defaultOptions).toMarkup();
|
||||
expect(markup).toContain("<mi mathvariant=\"normal\">A</mi>");
|
||||
expect(markup).toContain("<mi mathvariant=\"normal\">x</mi>");
|
||||
expect(markup).toContain("<mn mathvariant=\"normal\">2</mn>");
|
||||
expect(markup).toContain("<mn>2</mn>");
|
||||
expect(markup).toContain("<mi>\u03c9</mi>"); // \omega
|
||||
expect(markup).toContain("<mi mathvariant=\"normal\">\u03A9</mi>"); // \Omega
|
||||
expect(markup).toContain("<mi>\u0131</mi>"); // \imath
|
||||
@@ -1563,12 +1568,12 @@ describe("A MathML font tree-builder", function() {
|
||||
const tex = "\\mathit{" + contents + "}";
|
||||
const tree = getParsed(tex);
|
||||
const markup = buildMathML(tree, tex, defaultOptions).toMarkup();
|
||||
expect(markup).toContain("<mi mathvariant=\"italic\">A</mi>");
|
||||
expect(markup).toContain("<mi mathvariant=\"italic\">x</mi>");
|
||||
expect(markup).toContain("<mi>A</mi>");
|
||||
expect(markup).toContain("<mi>x</mi>");
|
||||
expect(markup).toContain("<mn mathvariant=\"italic\">2</mn>");
|
||||
expect(markup).toContain("<mi mathvariant=\"italic\">\u03c9</mi>"); // \omega
|
||||
expect(markup).toContain("<mi mathvariant=\"italic\">\u03A9</mi>"); // \Omega
|
||||
expect(markup).toContain("<mi mathvariant=\"italic\">\u0131</mi>"); // \imath
|
||||
expect(markup).toContain("<mi>\u03c9</mi>"); // \omega
|
||||
expect(markup).toContain("<mi>\u03A9</mi>"); // \Omega
|
||||
expect(markup).toContain("<mi>\u0131</mi>"); // \imath
|
||||
expect(markup).toContain("<mo>+</mo>");
|
||||
});
|
||||
|
||||
@@ -1623,7 +1628,7 @@ describe("A MathML font tree-builder", function() {
|
||||
// MathJax marks everything below as "script" except \omega
|
||||
// We don't have these glyphs in "script" and neither does MathJax
|
||||
expect(markup).toContain("<mi>x</mi>");
|
||||
expect(markup).toContain("<mn mathvariant=\"normal\">2</mn>");
|
||||
expect(markup).toContain("<mn>2</mn>");
|
||||
expect(markup).toContain("<mi>\u03c9</mi>"); // \omega
|
||||
expect(markup).toContain("<mi mathvariant=\"normal\">\u03A9</mi>"); // \Omega
|
||||
expect(markup).toContain("<mi>\u0131</mi>"); // \imath
|
||||
@@ -1661,6 +1666,22 @@ describe("A MathML font tree-builder", function() {
|
||||
"</mstyle>";
|
||||
expect(markup).toContain(node);
|
||||
});
|
||||
|
||||
it("should render text as <mtext>", function() {
|
||||
const tex = "\\text{for }";
|
||||
const tree = getParsed(tex);
|
||||
const markup = buildMathML(tree, tex, defaultOptions).toMarkup();
|
||||
expect(markup).toContain("<mtext>for\u00a0</mtext>");
|
||||
});
|
||||
|
||||
it("should render math within text as side-by-side children", function() {
|
||||
const tex = "\\text{graph: $y = mx + b$}";
|
||||
const tree = getParsed(tex);
|
||||
const markup = buildMathML(tree, tex, defaultOptions).toMarkup();
|
||||
expect(markup).toContain("<mrow><mtext>graph:\u00a0</mtext>");
|
||||
expect(markup).toContain(
|
||||
"<mi>y</mi><mo>=</mo><mi>m</mi><mi>x</mi><mo>+</mo><mi>b</mi>");
|
||||
});
|
||||
});
|
||||
|
||||
describe("A bin builder", function() {
|
||||
|
BIN
test/screenshotter/images/TextWithMath-chrome.png
Normal file
BIN
test/screenshotter/images/TextWithMath-chrome.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.8 KiB |
BIN
test/screenshotter/images/TextWithMath-firefox.png
Normal file
BIN
test/screenshotter/images/TextWithMath-firefox.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.5 KiB |
@@ -170,6 +170,7 @@ Symbols1: |
|
||||
\maltese\degree\pounds\$
|
||||
\text{\maltese\degree\pounds\textdollar}
|
||||
Text: \frac{a}{b}\text{c~ {ab} \ e}+fg
|
||||
TextWithMath: \text{for $a < b$ and $ c < d $}.
|
||||
Unicode: \begin{matrix}\text{ÀàÇçÉéÏïÖöÛû} \\ \text{БГДЖЗЙЛФЦШЫЮЯ} \\ \text{여보세요} \\ \text{私はバナナです} \end{matrix}
|
||||
UnsupportedCmds:
|
||||
tex: \err\,\frac\fracerr3\,2^\superr_\suberr\,\sqrt\sqrterr
|
||||
|
Reference in New Issue
Block a user