mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-11 22:18:41 +00:00
Merge pull request #292 from kevinb7/fonts-p3_mathml
Adds MathML support for math font commands.
This commit is contained in:
@@ -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
|
||||
* parse level, so size changes can be handled efficiently.
|
||||
*
|
||||
|
@@ -435,6 +435,7 @@ var fontMap = {
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
fontMap: fontMap,
|
||||
makeSymbol: makeSymbol,
|
||||
mathsym: mathsym,
|
||||
makeSpan: makeSpan,
|
||||
|
@@ -5,7 +5,6 @@
|
||||
* called, to produce a final HTML tree.
|
||||
*/
|
||||
|
||||
var Options = require("./Options");
|
||||
var ParseError = require("./ParseError");
|
||||
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
|
||||
* nodes.
|
||||
*/
|
||||
var buildHTML = function(tree, settings) {
|
||||
var buildHTML = function(tree, options) {
|
||||
// buildExpression is destructive, so we need to make a clone
|
||||
// of the incoming tree so that it isn't accidentally changed
|
||||
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
|
||||
var expression = buildExpression(tree, options);
|
||||
var body = makeSpan(["base", options.style.cls()], expression);
|
||||
|
@@ -5,11 +5,14 @@
|
||||
*/
|
||||
|
||||
var buildCommon = require("./buildCommon");
|
||||
var fontMetrics = require("./fontMetrics");
|
||||
var mathMLTree = require("./mathMLTree");
|
||||
var ParseError = require("./ParseError");
|
||||
var symbols = require("./symbols");
|
||||
var utils = require("./utils");
|
||||
|
||||
var makeSpan = buildCommon.makeSpan;
|
||||
var fontMap = buildCommon.fontMap;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* tree. Each function should take a parse group and return a MathML node.
|
||||
*/
|
||||
var groupTypes = {
|
||||
mathord: function(group) {
|
||||
mathord: function(group, options) {
|
||||
var node = new mathMLTree.MathNode(
|
||||
"mi",
|
||||
[makeText(group.value, group.mode)]);
|
||||
|
||||
var variant = getVariant(group, options);
|
||||
if (variant) {
|
||||
node.setAttribute("mathvariant", variant);
|
||||
}
|
||||
return node;
|
||||
},
|
||||
|
||||
textord: function(group) {
|
||||
textord: function(group, options) {
|
||||
var text = makeText(group.value, group.mode);
|
||||
|
||||
var variant = getVariant(group, options) || "normal";
|
||||
|
||||
var node;
|
||||
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]);
|
||||
node.setAttribute("mathvariant", "normal");
|
||||
node.setAttribute("mathvariant", variant);
|
||||
}
|
||||
|
||||
return node;
|
||||
@@ -94,24 +139,24 @@ var groupTypes = {
|
||||
return node;
|
||||
},
|
||||
|
||||
ordgroup: function(group) {
|
||||
var inner = buildExpression(group.value);
|
||||
ordgroup: function(group, options) {
|
||||
var inner = buildExpression(group.value, options);
|
||||
|
||||
var node = new mathMLTree.MathNode("mrow", inner);
|
||||
|
||||
return node;
|
||||
},
|
||||
|
||||
text: function(group) {
|
||||
var inner = buildExpression(group.value.body);
|
||||
text: function(group, options) {
|
||||
var inner = buildExpression(group.value.body, options);
|
||||
|
||||
var node = new mathMLTree.MathNode("mtext", inner);
|
||||
|
||||
return node;
|
||||
},
|
||||
|
||||
color: function(group) {
|
||||
var inner = buildExpression(group.value.value);
|
||||
color: function(group, options) {
|
||||
var inner = buildExpression(group.value.value, options);
|
||||
|
||||
var node = new mathMLTree.MathNode("mstyle", inner);
|
||||
|
||||
@@ -120,15 +165,15 @@ var groupTypes = {
|
||||
return node;
|
||||
},
|
||||
|
||||
supsub: function(group) {
|
||||
var children = [buildGroup(group.value.base)];
|
||||
supsub: function(group, options) {
|
||||
var children = [buildGroup(group.value.base, options)];
|
||||
|
||||
if (group.value.sub) {
|
||||
children.push(buildGroup(group.value.sub));
|
||||
children.push(buildGroup(group.value.sub, options));
|
||||
}
|
||||
|
||||
if (group.value.sup) {
|
||||
children.push(buildGroup(group.value.sup));
|
||||
children.push(buildGroup(group.value.sup, options));
|
||||
}
|
||||
|
||||
var nodeType;
|
||||
@@ -145,11 +190,11 @@ var groupTypes = {
|
||||
return node;
|
||||
},
|
||||
|
||||
genfrac: function(group) {
|
||||
genfrac: function(group, options) {
|
||||
var node = new mathMLTree.MathNode(
|
||||
"mfrac",
|
||||
[buildGroup(group.value.numer),
|
||||
buildGroup(group.value.denom)]);
|
||||
[buildGroup(group.value.numer, options),
|
||||
buildGroup(group.value.denom, options)]);
|
||||
|
||||
if (!group.value.hasBarLine) {
|
||||
node.setAttribute("linethickness", "0px");
|
||||
@@ -186,35 +231,35 @@ var groupTypes = {
|
||||
return node;
|
||||
},
|
||||
|
||||
array: function(group) {
|
||||
array: function(group, options) {
|
||||
return new mathMLTree.MathNode(
|
||||
"mtable", group.value.body.map(function(row) {
|
||||
return new mathMLTree.MathNode(
|
||||
"mtr", row.map(function(cell) {
|
||||
return new mathMLTree.MathNode(
|
||||
"mtd", [buildGroup(cell)]);
|
||||
"mtd", [buildGroup(cell, options)]);
|
||||
}));
|
||||
}));
|
||||
},
|
||||
|
||||
sqrt: function(group) {
|
||||
sqrt: function(group, options) {
|
||||
var node;
|
||||
if (group.value.index) {
|
||||
node = new mathMLTree.MathNode(
|
||||
"mroot", [
|
||||
buildGroup(group.value.body),
|
||||
buildGroup(group.value.index)
|
||||
buildGroup(group.value.body, options),
|
||||
buildGroup(group.value.index, options)
|
||||
]);
|
||||
} else {
|
||||
node = new mathMLTree.MathNode(
|
||||
"msqrt", [buildGroup(group.value.body)]);
|
||||
"msqrt", [buildGroup(group.value.body, options)]);
|
||||
}
|
||||
|
||||
return node;
|
||||
},
|
||||
|
||||
leftright: function(group) {
|
||||
var inner = buildExpression(group.value.body);
|
||||
leftright: function(group, options) {
|
||||
var inner = buildExpression(group.value.body, options);
|
||||
|
||||
if (group.value.left !== ".") {
|
||||
var leftNode = new mathMLTree.MathNode(
|
||||
@@ -239,24 +284,19 @@ var groupTypes = {
|
||||
return outerNode;
|
||||
},
|
||||
|
||||
accent: function(group) {
|
||||
accent: function(group, options) {
|
||||
var accentNode = new mathMLTree.MathNode(
|
||||
"mo", [makeText(group.value.accent, group.mode)]);
|
||||
|
||||
var node = new mathMLTree.MathNode(
|
||||
"mover",
|
||||
[buildGroup(group.value.base),
|
||||
[buildGroup(group.value.base, options),
|
||||
accentNode]);
|
||||
|
||||
node.setAttribute("accent", "true");
|
||||
|
||||
return node;
|
||||
},
|
||||
|
||||
font: function(group) {
|
||||
// pass through so we can render something without throwing
|
||||
return buildGroup(group.value.body);
|
||||
},
|
||||
|
||||
spacing: function(group) {
|
||||
var node;
|
||||
@@ -303,6 +343,11 @@ var groupTypes = {
|
||||
return node;
|
||||
},
|
||||
|
||||
font: function(group, options) {
|
||||
var font = group.value.font;
|
||||
return buildGroup(group.value.body, options.withFont(font));
|
||||
},
|
||||
|
||||
delimsizing: function(group) {
|
||||
var children = [];
|
||||
|
||||
@@ -326,8 +371,8 @@ var groupTypes = {
|
||||
return node;
|
||||
},
|
||||
|
||||
styling: function(group) {
|
||||
var inner = buildExpression(group.value.value, inner);
|
||||
styling: function(group, options) {
|
||||
var inner = buildExpression(group.value.value, options);
|
||||
|
||||
var node = new mathMLTree.MathNode("mstyle", inner);
|
||||
|
||||
@@ -346,28 +391,30 @@ var groupTypes = {
|
||||
return node;
|
||||
},
|
||||
|
||||
sizing: function(group) {
|
||||
var inner = buildExpression(group.value.value);
|
||||
sizing: function(group, options) {
|
||||
var inner = buildExpression(group.value.value, options);
|
||||
|
||||
var node = new mathMLTree.MathNode("mstyle", inner);
|
||||
|
||||
// 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
|
||||
// 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(
|
||||
"mathsize", buildCommon.sizingMultiplier[group.value.size] + "em");
|
||||
|
||||
return node;
|
||||
},
|
||||
|
||||
overline: function(group) {
|
||||
overline: function(group, options) {
|
||||
var operator = new mathMLTree.MathNode(
|
||||
"mo", [new mathMLTree.TextNode("\u203e")]);
|
||||
operator.setAttribute("stretchy", "true");
|
||||
|
||||
var node = new mathMLTree.MathNode(
|
||||
"mover",
|
||||
[buildGroup(group.value.body),
|
||||
[buildGroup(group.value.body, options),
|
||||
operator]);
|
||||
node.setAttribute("accent", "true");
|
||||
|
||||
@@ -382,9 +429,9 @@ var groupTypes = {
|
||||
return node;
|
||||
},
|
||||
|
||||
llap: function(group) {
|
||||
llap: function(group, options) {
|
||||
var node = new mathMLTree.MathNode(
|
||||
"mpadded", [buildGroup(group.value.body)]);
|
||||
"mpadded", [buildGroup(group.value.body, options)]);
|
||||
|
||||
node.setAttribute("lspace", "-1width");
|
||||
node.setAttribute("width", "0px");
|
||||
@@ -392,9 +439,9 @@ var groupTypes = {
|
||||
return node;
|
||||
},
|
||||
|
||||
rlap: function(group) {
|
||||
rlap: function(group, options) {
|
||||
var node = new mathMLTree.MathNode(
|
||||
"mpadded", [buildGroup(group.value.body)]);
|
||||
"mpadded", [buildGroup(group.value.body, options)]);
|
||||
|
||||
node.setAttribute("width", "0px");
|
||||
|
||||
@@ -402,7 +449,7 @@ var groupTypes = {
|
||||
},
|
||||
|
||||
phantom: function(group, options, prev) {
|
||||
var inner = buildExpression(group.value.value);
|
||||
var inner = buildExpression(group.value.value, options);
|
||||
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
|
||||
* previous-node handling.
|
||||
*/
|
||||
var buildExpression = function(expression) {
|
||||
var buildExpression = function(expression, options) {
|
||||
var groups = [];
|
||||
for (var i = 0; i < expression.length; i++) {
|
||||
var group = expression[i];
|
||||
groups.push(buildGroup(group));
|
||||
groups.push(buildGroup(group, options));
|
||||
}
|
||||
return groups;
|
||||
};
|
||||
@@ -425,14 +472,14 @@ var buildExpression = function(expression) {
|
||||
* Takes a group from the parser and calls the appropriate groupTypes function
|
||||
* on it to produce a MathML node.
|
||||
*/
|
||||
var buildGroup = function(group) {
|
||||
var buildGroup = function(group, options) {
|
||||
if (!group) {
|
||||
return new mathMLTree.MathNode("mrow");
|
||||
}
|
||||
|
||||
if (groupTypes[group.type]) {
|
||||
// Call the groupTypes function
|
||||
return groupTypes[group.type](group);
|
||||
return groupTypes[group.type](group, options);
|
||||
} else {
|
||||
throw new ParseError(
|
||||
"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
|
||||
* we can do appropriate styling.
|
||||
*/
|
||||
var buildMathML = function(tree, texExpression, settings) {
|
||||
var expression = buildExpression(tree);
|
||||
var buildMathML = function(tree, texExpression, options) {
|
||||
var expression = buildExpression(tree, options);
|
||||
|
||||
// Wrap up the expression in an mrow so it is presented in the semantics
|
||||
// tag correctly.
|
||||
|
@@ -1,15 +1,30 @@
|
||||
|
||||
var buildHTML = require("./buildHTML");
|
||||
var buildMathML = require("./buildMathML");
|
||||
var buildCommon = require("./buildCommon");
|
||||
var Options = require("./Options");
|
||||
var Settings = require("./Settings");
|
||||
var Style = require("./Style");
|
||||
|
||||
var makeSpan = buildCommon.makeSpan;
|
||||
|
||||
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 ->
|
||||
// ords), so we build the MathML version first.
|
||||
var mathMLNode = buildMathML(tree, expression, settings);
|
||||
var htmlNode = buildHTML(tree, settings);
|
||||
var mathMLNode = buildMathML(tree, expression, options);
|
||||
var htmlNode = buildHTML(tree, options);
|
||||
|
||||
var katexNode = makeSpan(["katex"], [
|
||||
mathMLNode, htmlNode
|
||||
|
Reference in New Issue
Block a user