|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import buildCommon from "./buildCommon"; |
|
import {getCharacterMetrics} from "./fontMetrics"; |
|
import mathMLTree from "./mathMLTree"; |
|
import ParseError from "./ParseError"; |
|
import symbols, {ligatures} from "./symbols"; |
|
import utils from "./utils"; |
|
import {_mathmlGroupBuilders as groupBuilders} from "./defineFunction"; |
|
import {MathNode, TextNode} from "./mathMLTree"; |
|
|
|
import type Options from "./Options"; |
|
import type {AnyParseNode, SymbolParseNode} from "./parseNode"; |
|
import type {DomSpan} from "./domTree"; |
|
import type {MathDomNode} from "./mathMLTree"; |
|
import type {FontVariant, Mode} from "./types"; |
|
|
|
|
|
|
|
|
|
|
|
export const makeText = function( |
|
text: string, |
|
mode: Mode, |
|
options?: Options, |
|
): TextNode { |
|
if (symbols[mode][text] && symbols[mode][text].replace && |
|
text.charCodeAt(0) !== 0xD835 && |
|
!(ligatures.hasOwnProperty(text) && options && |
|
((options.fontFamily && options.fontFamily.slice(4, 6) === "tt") || |
|
(options.font && options.font.slice(4, 6) === "tt")))) { |
|
text = symbols[mode][text].replace; |
|
} |
|
|
|
return new mathMLTree.TextNode(text); |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
export const makeRow = function(body: $ReadOnlyArray<MathDomNode>): MathDomNode { |
|
if (body.length === 1) { |
|
return body[0]; |
|
} else { |
|
return new mathMLTree.MathNode("mrow", body); |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
export const getVariant = function( |
|
group: SymbolParseNode, |
|
options: Options, |
|
): ?FontVariant { |
|
|
|
|
|
|
|
if (options.fontFamily === "texttt") { |
|
return "monospace"; |
|
} else if (options.fontFamily === "textsf") { |
|
if (options.fontShape === "textit" && |
|
options.fontWeight === "textbf") { |
|
return "sans-serif-bold-italic"; |
|
} else if (options.fontShape === "textit") { |
|
return "sans-serif-italic"; |
|
} else if (options.fontWeight === "textbf") { |
|
return "bold-sans-serif"; |
|
} else { |
|
return "sans-serif"; |
|
} |
|
} else if (options.fontShape === "textit" && |
|
options.fontWeight === "textbf") { |
|
return "bold-italic"; |
|
} else if (options.fontShape === "textit") { |
|
return "italic"; |
|
} else if (options.fontWeight === "textbf") { |
|
return "bold"; |
|
} |
|
|
|
const font = options.font; |
|
if (!font || font === "mathnormal") { |
|
return null; |
|
} |
|
|
|
const mode = group.mode; |
|
if (font === "mathit") { |
|
return "italic"; |
|
} else if (font === "boldsymbol") { |
|
return group.type === "textord" ? "bold" : "bold-italic"; |
|
} else if (font === "mathbf") { |
|
return "bold"; |
|
} else if (font === "mathbb") { |
|
return "double-struck"; |
|
} else if (font === "mathfrak") { |
|
return "fraktur"; |
|
} else if (font === "mathscr" || font === "mathcal") { |
|
|
|
return "script"; |
|
} else if (font === "mathsf") { |
|
return "sans-serif"; |
|
} else if (font === "mathtt") { |
|
return "monospace"; |
|
} |
|
|
|
let text = group.text; |
|
if (utils.contains(["\\imath", "\\jmath"], text)) { |
|
return null; |
|
} |
|
|
|
if (symbols[mode][text] && symbols[mode][text].replace) { |
|
text = symbols[mode][text].replace; |
|
} |
|
|
|
const fontName = buildCommon.fontMap[font].fontName; |
|
if (getCharacterMetrics(text, fontName, mode)) { |
|
return buildCommon.fontMap[font].variant; |
|
} |
|
|
|
return null; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
export const buildExpression = function( |
|
expression: AnyParseNode[], |
|
options: Options, |
|
isOrdgroup?: boolean, |
|
): MathNode[] { |
|
if (expression.length === 1) { |
|
const group = buildGroup(expression[0], options); |
|
if (isOrdgroup && group instanceof MathNode && group.type === "mo") { |
|
|
|
|
|
group.setAttribute("lspace", "0em"); |
|
group.setAttribute("rspace", "0em"); |
|
} |
|
return [group]; |
|
} |
|
|
|
const groups = []; |
|
let lastGroup; |
|
for (let i = 0; i < expression.length; i++) { |
|
const group = buildGroup(expression[i], options); |
|
if (group instanceof MathNode && lastGroup instanceof MathNode) { |
|
|
|
if (group.type === 'mtext' && lastGroup.type === 'mtext' |
|
&& group.getAttribute('mathvariant') === |
|
lastGroup.getAttribute('mathvariant')) { |
|
lastGroup.children.push(...group.children); |
|
continue; |
|
|
|
} else if (group.type === 'mn' && lastGroup.type === 'mn') { |
|
lastGroup.children.push(...group.children); |
|
continue; |
|
|
|
} else if (group.type === 'mi' && group.children.length === 1 && |
|
lastGroup.type === 'mn') { |
|
const child = group.children[0]; |
|
if (child instanceof TextNode && child.text === '.') { |
|
lastGroup.children.push(...group.children); |
|
continue; |
|
} |
|
} else if (lastGroup.type === 'mi' && lastGroup.children.length === 1) { |
|
const lastChild = lastGroup.children[0]; |
|
if (lastChild instanceof TextNode && lastChild.text === '\u0338' && |
|
(group.type === 'mo' || group.type === 'mi' || |
|
group.type === 'mn')) { |
|
const child = group.children[0]; |
|
if (child instanceof TextNode && child.text.length > 0) { |
|
|
|
child.text = child.text.slice(0, 1) + "\u0338" + |
|
child.text.slice(1); |
|
groups.pop(); |
|
} |
|
} |
|
} |
|
} |
|
groups.push(group); |
|
lastGroup = group; |
|
} |
|
return groups; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
export const buildExpressionRow = function( |
|
expression: AnyParseNode[], |
|
options: Options, |
|
isOrdgroup?: boolean, |
|
): MathDomNode { |
|
return makeRow(buildExpression(expression, options, isOrdgroup)); |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
export const buildGroup = function( |
|
group: ?AnyParseNode, |
|
options: Options, |
|
): MathNode { |
|
if (!group) { |
|
return new mathMLTree.MathNode("mrow"); |
|
} |
|
|
|
if (groupBuilders[group.type]) { |
|
|
|
|
|
const result: MathDomNode = groupBuilders[group.type](group, options); |
|
|
|
return result; |
|
} else { |
|
throw new ParseError( |
|
"Got group of unknown type: '" + group.type + "'"); |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export default function buildMathML( |
|
tree: AnyParseNode[], |
|
texExpression: string, |
|
options: Options, |
|
isDisplayMode: boolean, |
|
forMathmlOnly: boolean, |
|
): DomSpan { |
|
const expression = buildExpression(tree, options); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let wrapper; |
|
if (expression.length === 1 && expression[0] instanceof MathNode && |
|
utils.contains(["mrow", "mtable"], expression[0].type)) { |
|
wrapper = expression[0]; |
|
} else { |
|
wrapper = new mathMLTree.MathNode("mrow", expression); |
|
} |
|
|
|
|
|
const annotation = new mathMLTree.MathNode( |
|
"annotation", [new mathMLTree.TextNode(texExpression)]); |
|
|
|
annotation.setAttribute("encoding", "application/x-tex"); |
|
|
|
const semantics = new mathMLTree.MathNode( |
|
"semantics", [wrapper, annotation]); |
|
|
|
const math = new mathMLTree.MathNode("math", [semantics]); |
|
math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML"); |
|
if (isDisplayMode) { |
|
math.setAttribute("display", "block"); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
const wrapperClass = forMathmlOnly ? "katex" : "katex-mathml"; |
|
|
|
return buildCommon.makeSpan([wrapperClass], [math]); |
|
} |
|
|