Merge pull request #292 from kevinb7/fonts-p3_mathml

Adds MathML support for math font commands.
This commit is contained in:
Kevin Barabash 2015-09-01 09:00:16 -06:00
commit c428abca1e
6 changed files with 295 additions and 74 deletions

View File

@ -6,7 +6,7 @@
*/ */
/** /**
* This is the main options class. It contains the style, size, color and font * This is the main options class. It contains the style, size, color, and font
* of the current parse level. It also contains the style and size of the parent * of the current parse level. It also contains the style and size of the parent
* parse level, so size changes can be handled efficiently. * parse level, so size changes can be handled efficiently.
* *

View File

@ -435,6 +435,7 @@ var fontMap = {
}; };
module.exports = { module.exports = {
fontMap: fontMap,
makeSymbol: makeSymbol, makeSymbol: makeSymbol,
mathsym: mathsym, mathsym: mathsym,
makeSpan: makeSpan, makeSpan: makeSpan,

View File

@ -5,7 +5,6 @@
* called, to produce a final HTML tree. * called, to produce a final HTML tree.
*/ */
var Options = require("./Options");
var ParseError = require("./ParseError"); var ParseError = require("./ParseError");
var Style = require("./Style"); var Style = require("./Style");
@ -1330,22 +1329,11 @@ var buildGroup = function(group, options, prev) {
* Take an entire parse tree, and build it into an appropriate set of HTML * Take an entire parse tree, and build it into an appropriate set of HTML
* nodes. * nodes.
*/ */
var buildHTML = function(tree, settings) { var buildHTML = function(tree, options) {
// buildExpression is destructive, so we need to make a clone // buildExpression is destructive, so we need to make a clone
// of the incoming tree so that it isn't accidentally changed // of the incoming tree so that it isn't accidentally changed
tree = JSON.parse(JSON.stringify(tree)); tree = JSON.parse(JSON.stringify(tree));
var startStyle = Style.TEXT;
if (settings.displayMode) {
startStyle = Style.DISPLAY;
}
// Setup the default options
var options = new Options({
style: startStyle,
size: "size5"
});
// Build the expression contained in the tree // Build the expression contained in the tree
var expression = buildExpression(tree, options); var expression = buildExpression(tree, options);
var body = makeSpan(["base", options.style.cls()], expression); var body = makeSpan(["base", options.style.cls()], expression);

View File

@ -5,11 +5,14 @@
*/ */
var buildCommon = require("./buildCommon"); var buildCommon = require("./buildCommon");
var fontMetrics = require("./fontMetrics");
var mathMLTree = require("./mathMLTree"); var mathMLTree = require("./mathMLTree");
var ParseError = require("./ParseError"); var ParseError = require("./ParseError");
var symbols = require("./symbols"); var symbols = require("./symbols");
var utils = require("./utils");
var makeSpan = buildCommon.makeSpan; var makeSpan = buildCommon.makeSpan;
var fontMap = buildCommon.fontMap;
/** /**
* Takes a symbol and converts it into a MathML text node after performing * Takes a symbol and converts it into a MathML text node after performing
@ -23,28 +26,70 @@ var makeText = function(text, mode) {
return new mathMLTree.TextNode(text); return new mathMLTree.TextNode(text);
}; };
/**
* Returns the math variant as a string or null if none is required.
*/
var getVariant = function(group, options) {
var font = options.font;
if (!font) {
return null;
}
var mode = group.mode;
if (font === "mathit") {
return "italic";
}
var value = group.value;
if (utils.contains(["\\imath", "\\jmath"], value)) {
return null;
}
if (symbols[mode][value] && symbols[mode][value].replace) {
value = symbols[mode][value].replace;
}
var fontName = fontMap[font].fontName;
if (fontMetrics.getCharacterMetrics(value, fontName)) {
return fontMap[options.font].variant;
}
return null;
};
/** /**
* Functions for handling the different types of groups found in the parse * Functions for handling the different types of groups found in the parse
* tree. Each function should take a parse group and return a MathML node. * tree. Each function should take a parse group and return a MathML node.
*/ */
var groupTypes = { var groupTypes = {
mathord: function(group) { mathord: function(group, options) {
var node = new mathMLTree.MathNode( var node = new mathMLTree.MathNode(
"mi", "mi",
[makeText(group.value, group.mode)]); [makeText(group.value, group.mode)]);
var variant = getVariant(group, options);
if (variant) {
node.setAttribute("mathvariant", variant);
}
return node; return node;
}, },
textord: function(group) { textord: function(group, options) {
var text = makeText(group.value, group.mode); var text = makeText(group.value, group.mode);
var variant = getVariant(group, options) || "normal";
var node; var node;
if (/[0-9]/.test(group.value)) { 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]); node = new mathMLTree.MathNode("mn", [text]);
if (options.font) {
node.setAttribute("mathvariant", variant);
}
} else { } else {
node = new mathMLTree.MathNode("mi", [text]); node = new mathMLTree.MathNode("mi", [text]);
node.setAttribute("mathvariant", "normal"); node.setAttribute("mathvariant", variant);
} }
return node; return node;
@ -94,24 +139,24 @@ var groupTypes = {
return node; return node;
}, },
ordgroup: function(group) { ordgroup: function(group, options) {
var inner = buildExpression(group.value); var inner = buildExpression(group.value, options);
var node = new mathMLTree.MathNode("mrow", inner); var node = new mathMLTree.MathNode("mrow", inner);
return node; return node;
}, },
text: function(group) { text: function(group, options) {
var inner = buildExpression(group.value.body); var inner = buildExpression(group.value.body, options);
var node = new mathMLTree.MathNode("mtext", inner); var node = new mathMLTree.MathNode("mtext", inner);
return node; return node;
}, },
color: function(group) { color: function(group, options) {
var inner = buildExpression(group.value.value); var inner = buildExpression(group.value.value, options);
var node = new mathMLTree.MathNode("mstyle", inner); var node = new mathMLTree.MathNode("mstyle", inner);
@ -120,15 +165,15 @@ var groupTypes = {
return node; return node;
}, },
supsub: function(group) { supsub: function(group, options) {
var children = [buildGroup(group.value.base)]; var children = [buildGroup(group.value.base, options)];
if (group.value.sub) { if (group.value.sub) {
children.push(buildGroup(group.value.sub)); children.push(buildGroup(group.value.sub, options));
} }
if (group.value.sup) { if (group.value.sup) {
children.push(buildGroup(group.value.sup)); children.push(buildGroup(group.value.sup, options));
} }
var nodeType; var nodeType;
@ -145,11 +190,11 @@ var groupTypes = {
return node; return node;
}, },
genfrac: function(group) { genfrac: function(group, options) {
var node = new mathMLTree.MathNode( var node = new mathMLTree.MathNode(
"mfrac", "mfrac",
[buildGroup(group.value.numer), [buildGroup(group.value.numer, options),
buildGroup(group.value.denom)]); buildGroup(group.value.denom, options)]);
if (!group.value.hasBarLine) { if (!group.value.hasBarLine) {
node.setAttribute("linethickness", "0px"); node.setAttribute("linethickness", "0px");
@ -186,35 +231,35 @@ var groupTypes = {
return node; return node;
}, },
array: function(group) { array: function(group, options) {
return new mathMLTree.MathNode( return new mathMLTree.MathNode(
"mtable", group.value.body.map(function(row) { "mtable", group.value.body.map(function(row) {
return new mathMLTree.MathNode( return new mathMLTree.MathNode(
"mtr", row.map(function(cell) { "mtr", row.map(function(cell) {
return new mathMLTree.MathNode( return new mathMLTree.MathNode(
"mtd", [buildGroup(cell)]); "mtd", [buildGroup(cell, options)]);
})); }));
})); }));
}, },
sqrt: function(group) { sqrt: function(group, options) {
var node; var node;
if (group.value.index) { if (group.value.index) {
node = new mathMLTree.MathNode( node = new mathMLTree.MathNode(
"mroot", [ "mroot", [
buildGroup(group.value.body), buildGroup(group.value.body, options),
buildGroup(group.value.index) buildGroup(group.value.index, options)
]); ]);
} else { } else {
node = new mathMLTree.MathNode( node = new mathMLTree.MathNode(
"msqrt", [buildGroup(group.value.body)]); "msqrt", [buildGroup(group.value.body, options)]);
} }
return node; return node;
}, },
leftright: function(group) { leftright: function(group, options) {
var inner = buildExpression(group.value.body); var inner = buildExpression(group.value.body, options);
if (group.value.left !== ".") { if (group.value.left !== ".") {
var leftNode = new mathMLTree.MathNode( var leftNode = new mathMLTree.MathNode(
@ -239,24 +284,19 @@ var groupTypes = {
return outerNode; return outerNode;
}, },
accent: function(group) { accent: function(group, options) {
var accentNode = new mathMLTree.MathNode( var accentNode = new mathMLTree.MathNode(
"mo", [makeText(group.value.accent, group.mode)]); "mo", [makeText(group.value.accent, group.mode)]);
var node = new mathMLTree.MathNode( var node = new mathMLTree.MathNode(
"mover", "mover",
[buildGroup(group.value.base), [buildGroup(group.value.base, options),
accentNode]); accentNode]);
node.setAttribute("accent", "true"); node.setAttribute("accent", "true");
return node; return node;
}, },
font: function(group) {
// pass through so we can render something without throwing
return buildGroup(group.value.body);
},
spacing: function(group) { spacing: function(group) {
var node; var node;
@ -303,6 +343,11 @@ var groupTypes = {
return node; return node;
}, },
font: function(group, options) {
var font = group.value.font;
return buildGroup(group.value.body, options.withFont(font));
},
delimsizing: function(group) { delimsizing: function(group) {
var children = []; var children = [];
@ -326,8 +371,8 @@ var groupTypes = {
return node; return node;
}, },
styling: function(group) { styling: function(group, options) {
var inner = buildExpression(group.value.value, inner); var inner = buildExpression(group.value.value, options);
var node = new mathMLTree.MathNode("mstyle", inner); var node = new mathMLTree.MathNode("mstyle", inner);
@ -346,28 +391,30 @@ var groupTypes = {
return node; return node;
}, },
sizing: function(group) { sizing: function(group, options) {
var inner = buildExpression(group.value.value); var inner = buildExpression(group.value.value, options);
var node = new mathMLTree.MathNode("mstyle", inner); var node = new mathMLTree.MathNode("mstyle", inner);
// TODO(emily): This doesn't produce the correct size for nested size // TODO(emily): This doesn't produce the correct size for nested size
// changes, because we don't keep state of what style we're currently // changes, because we don't keep state of what style we're currently
// in, so we can't reset the size to normal before changing it. // in, so we can't reset the size to normal before changing it. Now
// that we're passing an options parameter we should be able to fix
// this.
node.setAttribute( node.setAttribute(
"mathsize", buildCommon.sizingMultiplier[group.value.size] + "em"); "mathsize", buildCommon.sizingMultiplier[group.value.size] + "em");
return node; return node;
}, },
overline: function(group) { overline: function(group, options) {
var operator = new mathMLTree.MathNode( var operator = new mathMLTree.MathNode(
"mo", [new mathMLTree.TextNode("\u203e")]); "mo", [new mathMLTree.TextNode("\u203e")]);
operator.setAttribute("stretchy", "true"); operator.setAttribute("stretchy", "true");
var node = new mathMLTree.MathNode( var node = new mathMLTree.MathNode(
"mover", "mover",
[buildGroup(group.value.body), [buildGroup(group.value.body, options),
operator]); operator]);
node.setAttribute("accent", "true"); node.setAttribute("accent", "true");
@ -382,9 +429,9 @@ var groupTypes = {
return node; return node;
}, },
llap: function(group) { llap: function(group, options) {
var node = new mathMLTree.MathNode( var node = new mathMLTree.MathNode(
"mpadded", [buildGroup(group.value.body)]); "mpadded", [buildGroup(group.value.body, options)]);
node.setAttribute("lspace", "-1width"); node.setAttribute("lspace", "-1width");
node.setAttribute("width", "0px"); node.setAttribute("width", "0px");
@ -392,9 +439,9 @@ var groupTypes = {
return node; return node;
}, },
rlap: function(group) { rlap: function(group, options) {
var node = new mathMLTree.MathNode( var node = new mathMLTree.MathNode(
"mpadded", [buildGroup(group.value.body)]); "mpadded", [buildGroup(group.value.body, options)]);
node.setAttribute("width", "0px"); node.setAttribute("width", "0px");
@ -402,7 +449,7 @@ var groupTypes = {
}, },
phantom: function(group, options, prev) { phantom: function(group, options, prev) {
var inner = buildExpression(group.value.value); var inner = buildExpression(group.value.value, options);
return new mathMLTree.MathNode("mphantom", inner); return new mathMLTree.MathNode("mphantom", inner);
} }
}; };
@ -412,11 +459,11 @@ var groupTypes = {
* MathML nodes. A little simpler than the HTML version because we don't do any * MathML nodes. A little simpler than the HTML version because we don't do any
* previous-node handling. * previous-node handling.
*/ */
var buildExpression = function(expression) { var buildExpression = function(expression, options) {
var groups = []; var groups = [];
for (var i = 0; i < expression.length; i++) { for (var i = 0; i < expression.length; i++) {
var group = expression[i]; var group = expression[i];
groups.push(buildGroup(group)); groups.push(buildGroup(group, options));
} }
return groups; return groups;
}; };
@ -425,14 +472,14 @@ var buildExpression = function(expression) {
* Takes a group from the parser and calls the appropriate groupTypes function * Takes a group from the parser and calls the appropriate groupTypes function
* on it to produce a MathML node. * on it to produce a MathML node.
*/ */
var buildGroup = function(group) { var buildGroup = function(group, options) {
if (!group) { if (!group) {
return new mathMLTree.MathNode("mrow"); return new mathMLTree.MathNode("mrow");
} }
if (groupTypes[group.type]) { if (groupTypes[group.type]) {
// Call the groupTypes function // Call the groupTypes function
return groupTypes[group.type](group); return groupTypes[group.type](group, options);
} else { } else {
throw new ParseError( throw new ParseError(
"Got group of unknown type: '" + group.type + "'"); "Got group of unknown type: '" + group.type + "'");
@ -447,8 +494,8 @@ var buildGroup = function(group) {
* Note that we actually return a domTree element with a `<math>` inside it so * Note that we actually return a domTree element with a `<math>` inside it so
* we can do appropriate styling. * we can do appropriate styling.
*/ */
var buildMathML = function(tree, texExpression, settings) { var buildMathML = function(tree, texExpression, options) {
var expression = buildExpression(tree); var expression = buildExpression(tree, options);
// Wrap up the expression in an mrow so it is presented in the semantics // Wrap up the expression in an mrow so it is presented in the semantics
// tag correctly. // tag correctly.

View File

@ -1,15 +1,30 @@
var buildHTML = require("./buildHTML"); var buildHTML = require("./buildHTML");
var buildMathML = require("./buildMathML"); var buildMathML = require("./buildMathML");
var buildCommon = require("./buildCommon"); var buildCommon = require("./buildCommon");
var Options = require("./Options");
var Settings = require("./Settings");
var Style = require("./Style");
var makeSpan = buildCommon.makeSpan; var makeSpan = buildCommon.makeSpan;
var buildTree = function(tree, expression, settings) { var buildTree = function(tree, expression, settings) {
settings = settings || new Settings({});
var startStyle = Style.TEXT;
if (settings.displayMode) {
startStyle = Style.DISPLAY;
}
// Setup the default options
var options = new Options({
style: startStyle,
size: "size5"
});
// `buildHTML` sometimes messes with the parse tree (like turning bins -> // `buildHTML` sometimes messes with the parse tree (like turning bins ->
// ords), so we build the MathML version first. // ords), so we build the MathML version first.
var mathMLNode = buildMathML(tree, expression, settings); var mathMLNode = buildMathML(tree, expression, options);
var htmlNode = buildHTML(tree, settings); var htmlNode = buildHTML(tree, options);
var katexNode = makeSpan(["katex"], [ var katexNode = makeSpan(["katex"], [
mathMLNode, htmlNode mathMLNode, htmlNode

View File

@ -4,27 +4,51 @@
/* global it: false */ /* global it: false */
/* global describe: false */ /* global describe: false */
var buildHTML = require("../src/buildHTML");
var buildMathML = require("../src/buildMathML"); var buildMathML = require("../src/buildMathML");
var buildTree = require("../src/buildTree");
var katex = require("../katex"); var katex = require("../katex");
var ParseError = require("../src/ParseError"); var ParseError = require("../src/ParseError");
var parseTree = require("../src/parseTree"); var parseTree = require("../src/parseTree");
var Options = require("../src/Options");
var Settings = require("../src/Settings"); var Settings = require("../src/Settings");
var Style = require("../src/Style");
var defaultSettings = new Settings({}); var defaultSettings = new Settings({});
var defaultOptions = new Options({
style: Style.TEXT,
size: "size5"
});
var getBuilt = function(expr, settings) { var _getBuilt = function(expr, settings) {
var usedSettings = settings ? settings : defaultSettings; var usedSettings = settings ? settings : defaultSettings;
expect(expr).toBuild(usedSettings);
var parsedTree = parseTree(expr, usedSettings); var parsedTree = parseTree(expr, usedSettings);
var built = buildHTML(parsedTree, usedSettings); var rootNode = buildTree(parsedTree, expr, usedSettings);
// grab the root node of the HTML rendering
var builtHTML = rootNode.children[1];
// Remove the outer .katex and .katex-inner layers // Remove the outer .katex and .katex-inner layers
return built.children[2].children; return builtHTML.children[2].children;
}; };
/**
* Return the root node of the rendered HTML.
* @param expr
* @param settings
* @returns {Object}
*/
var getBuilt = function(expr, settings) {
var usedSettings = settings ? settings : defaultSettings;
expect(expr).toBuild(usedSettings);
return _getBuilt(expr, settings);
};
/**
* Return the root node of the parse tree.
* @param expr
* @param settings
* @returns {Object}
*/
var getParsed = function(expr, settings) { var getParsed = function(expr, settings) {
var usedSettings = settings ? settings : defaultSettings; var usedSettings = settings ? settings : defaultSettings;
@ -104,7 +128,7 @@ beforeEach(function() {
expect(actual).toParse(usedSettings); expect(actual).toParse(usedSettings);
try { try {
buildHTML(parseTree(actual, usedSettings), usedSettings); _getBuilt(actual, settings);
} catch (e) { } catch (e) {
result.pass = false; result.pass = false;
if (e instanceof ParseError) { if (e instanceof ParseError) {
@ -1269,6 +1293,152 @@ describe("An HTML font tree-builder", function () {
}); });
}); });
describe("A MathML font tree-builder", function () {
var contents = "Ax2k\\omega\\Omega\\imath+";
it("should render " + contents + " with the correct mathvariants", function () {
var tree = getParsed(contents);
var markup = buildMathML(tree, contents, defaultOptions).toMarkup();
expect(markup).toContain("<mi>A</mi>");
expect(markup).toContain("<mi>x</mi>");
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
expect(markup).toContain("<mo>+</mo>");
});
it("should render \\mathbb{" + contents + "} with the correct mathvariants", function () {
var tex = "\\mathbb{" + contents + "}";
var tree = getParsed(tex);
var 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("<mi>\u03c9</mi>"); // \omega
expect(markup).toContain("<mi mathvariant=\"normal\">\u03A9</mi>"); // \Omega
expect(markup).toContain("<mi>\u0131</mi>"); // \imath
expect(markup).toContain("<mo>+</mo>");
});
it("should render \\mathrm{" + contents + "} with the correct mathvariants", function () {
var tex = "\\mathrm{" + contents + "}";
var tree = getParsed(tex);
var 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("<mi>\u03c9</mi>"); // \omega
expect(markup).toContain("<mi mathvariant=\"normal\">\u03A9</mi>"); // \Omega
expect(markup).toContain("<mi>\u0131</mi>"); // \imath
expect(markup).toContain("<mo>+</mo>");
});
it("should render \\mathit{" + contents + "} with the correct mathvariants", function () {
var tex = "\\mathit{" + contents + "}";
var tree = getParsed(tex);
var 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("<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("<mo>+</mo>");
});
it("should render \\mathbf{" + contents + "} with the correct mathvariants", function () {
var tex = "\\mathbf{" + contents + "}";
var tree = getParsed(tex);
var markup = buildMathML(tree, tex, defaultOptions).toMarkup();
expect(markup).toContain("<mi mathvariant=\"bold\">A</mi>");
expect(markup).toContain("<mi mathvariant=\"bold\">x</mi>");
expect(markup).toContain("<mn mathvariant=\"bold\">2</mn>");
expect(markup).toContain("<mi>\u03c9</mi>"); // \omega
expect(markup).toContain("<mi mathvariant=\"bold\">\u03A9</mi>"); // \Omega
expect(markup).toContain("<mi>\u0131</mi>"); // \imath
expect(markup).toContain("<mo>+</mo>");
});
it("should render \\mathcal{" + contents + "} with the correct mathvariants", function () {
var tex = "\\mathcal{" + contents + "}";
var tree = getParsed(tex);
var markup = buildMathML(tree, tex, defaultOptions).toMarkup();
expect(markup).toContain("<mi mathvariant=\"script\">A</mi>");
expect(markup).toContain("<mi>x</mi>"); // script is caps only
expect(markup).toContain("<mn mathvariant=\"script\">2</mn>");
// MathJax marks everything below as "script" except \omega
// We don't have these glyphs in "caligraphic" and neither does MathJax
expect(markup).toContain("<mi>\u03c9</mi>"); // \omega
expect(markup).toContain("<mi mathvariant=\"normal\">\u03A9</mi>"); // \Omega
expect(markup).toContain("<mi>\u0131</mi>"); // \imath
expect(markup).toContain("<mo>+</mo>");
});
it("should render \\mathfrak{" + contents + "} with the correct mathvariants", function () {
var tex = "\\mathfrak{" + contents + "}";
var tree = getParsed(tex);
var markup = buildMathML(tree, tex, defaultOptions).toMarkup();
expect(markup).toContain("<mi mathvariant=\"fraktur\">A</mi>");
expect(markup).toContain("<mi mathvariant=\"fraktur\">x</mi>");
expect(markup).toContain("<mn mathvariant=\"fraktur\">2</mn>");
// MathJax marks everything below as "fraktur" except \omega
// We don't have these glyphs in "fraktur" and neither does MathJax
expect(markup).toContain("<mi>\u03c9</mi>"); // \omega
expect(markup).toContain("<mi mathvariant=\"normal\">\u03A9</mi>"); // \Omega
expect(markup).toContain("<mi>\u0131</mi>"); // \imath
expect(markup).toContain("<mo>+</mo>");
});
it("should render \\mathscr{" + contents + "} with the correct mathvariants", function () {
var tex = "\\mathscr{" + contents + "}";
var tree = getParsed(tex);
var markup = buildMathML(tree, tex, defaultOptions).toMarkup();
expect(markup).toContain("<mi mathvariant=\"script\">A</mi>");
// 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("<mi>\u03c9</mi>"); // \omega
expect(markup).toContain("<mi mathvariant=\"normal\">\u03A9</mi>"); // \Omega
expect(markup).toContain("<mi>\u0131</mi>"); // \imath
expect(markup).toContain("<mo>+</mo>");
});
it("should render \\mathsf{" + contents + "} with the correct mathvariants", function () {
var tex = "\\mathsf{" + contents + "}";
var tree = getParsed(tex);
var markup = buildMathML(tree, tex, defaultOptions).toMarkup();
expect(markup).toContain("<mi mathvariant=\"sans-serif\">A</mi>");
expect(markup).toContain("<mi mathvariant=\"sans-serif\">x</mi>");
expect(markup).toContain("<mn mathvariant=\"sans-serif\">2</mn>");
expect(markup).toContain("<mi>\u03c9</mi>"); // \omega
expect(markup).toContain("<mi mathvariant=\"sans-serif\">\u03A9</mi>"); // \Omega
expect(markup).toContain("<mi>\u0131</mi>"); // \imath
expect(markup).toContain("<mo>+</mo>");
});
it("should render a combination of font and color changes", function () {
var tex = "\\color{blue}{\\mathbb R}";
var tree = getParsed(tex);
var markup = buildMathML(tree, tex, defaultOptions).toMarkup();
var node = "<mstyle mathcolor=\"blue\">" +
"<mi mathvariant=\"double-struck\">R</mi>" +
"</mstyle>";
expect(markup).toContain(node);
// reverse the order of the commands
tex = "\\mathbb{\\color{blue}{R}}";
tree = getParsed(tex);
markup = buildMathML(tree, tex, defaultOptions).toMarkup();
node = "<mstyle mathcolor=\"blue\">" +
"<mi mathvariant=\"double-struck\">R</mi>" +
"</mstyle>";
expect(markup).toContain(node);
});
});
describe("A bin builder", function() { describe("A bin builder", function() {
it("should create mbins normally", function() { it("should create mbins normally", function() {
var built = getBuilt("x + y"); var built = getBuilt("x + y");