|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import {LineNode, PathNode, SvgNode} from "./domTree"; |
|
import buildCommon from "./buildCommon"; |
|
import mathMLTree from "./mathMLTree"; |
|
import utils from "./utils"; |
|
import {makeEm} from "./units"; |
|
|
|
import type Options from "./Options"; |
|
import type {ParseNode, AnyParseNode} from "./parseNode"; |
|
import type {DomSpan, HtmlDomNode, SvgSpan} from "./domTree"; |
|
|
|
const stretchyCodePoint: {[string]: string} = { |
|
widehat: "^", |
|
widecheck: "ˇ", |
|
widetilde: "~", |
|
utilde: "~", |
|
overleftarrow: "\u2190", |
|
underleftarrow: "\u2190", |
|
xleftarrow: "\u2190", |
|
overrightarrow: "\u2192", |
|
underrightarrow: "\u2192", |
|
xrightarrow: "\u2192", |
|
underbrace: "\u23df", |
|
overbrace: "\u23de", |
|
overgroup: "\u23e0", |
|
undergroup: "\u23e1", |
|
overleftrightarrow: "\u2194", |
|
underleftrightarrow: "\u2194", |
|
xleftrightarrow: "\u2194", |
|
Overrightarrow: "\u21d2", |
|
xRightarrow: "\u21d2", |
|
overleftharpoon: "\u21bc", |
|
xleftharpoonup: "\u21bc", |
|
overrightharpoon: "\u21c0", |
|
xrightharpoonup: "\u21c0", |
|
xLeftarrow: "\u21d0", |
|
xLeftrightarrow: "\u21d4", |
|
xhookleftarrow: "\u21a9", |
|
xhookrightarrow: "\u21aa", |
|
xmapsto: "\u21a6", |
|
xrightharpoondown: "\u21c1", |
|
xleftharpoondown: "\u21bd", |
|
xrightleftharpoons: "\u21cc", |
|
xleftrightharpoons: "\u21cb", |
|
xtwoheadleftarrow: "\u219e", |
|
xtwoheadrightarrow: "\u21a0", |
|
xlongequal: "=", |
|
xtofrom: "\u21c4", |
|
xrightleftarrows: "\u21c4", |
|
xrightequilibrium: "\u21cc", |
|
xleftequilibrium: "\u21cb", |
|
"\\cdrightarrow": "\u2192", |
|
"\\cdleftarrow": "\u2190", |
|
"\\cdlongequal": "=", |
|
}; |
|
|
|
const mathMLnode = function(label: string): mathMLTree.MathNode { |
|
const node = new mathMLTree.MathNode( |
|
"mo", |
|
[new mathMLTree.TextNode(stretchyCodePoint[label.replace(/^\\/, '')])], |
|
); |
|
node.setAttribute("stretchy", "true"); |
|
return node; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const katexImagesData: { |
|
[string]: ([string[], number, number] | [[string], number, number, string]) |
|
} = { |
|
|
|
overrightarrow: [["rightarrow"], 0.888, 522, "xMaxYMin"], |
|
overleftarrow: [["leftarrow"], 0.888, 522, "xMinYMin"], |
|
underrightarrow: [["rightarrow"], 0.888, 522, "xMaxYMin"], |
|
underleftarrow: [["leftarrow"], 0.888, 522, "xMinYMin"], |
|
xrightarrow: [["rightarrow"], 1.469, 522, "xMaxYMin"], |
|
"\\cdrightarrow": [["rightarrow"], 3.0, 522, "xMaxYMin"], |
|
xleftarrow: [["leftarrow"], 1.469, 522, "xMinYMin"], |
|
"\\cdleftarrow": [["leftarrow"], 3.0, 522, "xMinYMin"], |
|
Overrightarrow: [["doublerightarrow"], 0.888, 560, "xMaxYMin"], |
|
xRightarrow: [["doublerightarrow"], 1.526, 560, "xMaxYMin"], |
|
xLeftarrow: [["doubleleftarrow"], 1.526, 560, "xMinYMin"], |
|
overleftharpoon: [["leftharpoon"], 0.888, 522, "xMinYMin"], |
|
xleftharpoonup: [["leftharpoon"], 0.888, 522, "xMinYMin"], |
|
xleftharpoondown: [["leftharpoondown"], 0.888, 522, "xMinYMin"], |
|
overrightharpoon: [["rightharpoon"], 0.888, 522, "xMaxYMin"], |
|
xrightharpoonup: [["rightharpoon"], 0.888, 522, "xMaxYMin"], |
|
xrightharpoondown: [["rightharpoondown"], 0.888, 522, "xMaxYMin"], |
|
xlongequal: [["longequal"], 0.888, 334, "xMinYMin"], |
|
"\\cdlongequal": [["longequal"], 3.0, 334, "xMinYMin"], |
|
xtwoheadleftarrow: [["twoheadleftarrow"], 0.888, 334, "xMinYMin"], |
|
xtwoheadrightarrow: [["twoheadrightarrow"], 0.888, 334, "xMaxYMin"], |
|
|
|
overleftrightarrow: [["leftarrow", "rightarrow"], 0.888, 522], |
|
overbrace: [["leftbrace", "midbrace", "rightbrace"], 1.6, 548], |
|
underbrace: [["leftbraceunder", "midbraceunder", "rightbraceunder"], |
|
1.6, 548], |
|
underleftrightarrow: [["leftarrow", "rightarrow"], 0.888, 522], |
|
xleftrightarrow: [["leftarrow", "rightarrow"], 1.75, 522], |
|
xLeftrightarrow: [["doubleleftarrow", "doublerightarrow"], 1.75, 560], |
|
xrightleftharpoons: [["leftharpoondownplus", "rightharpoonplus"], 1.75, 716], |
|
xleftrightharpoons: [["leftharpoonplus", "rightharpoondownplus"], |
|
1.75, 716], |
|
xhookleftarrow: [["leftarrow", "righthook"], 1.08, 522], |
|
xhookrightarrow: [["lefthook", "rightarrow"], 1.08, 522], |
|
overlinesegment: [["leftlinesegment", "rightlinesegment"], 0.888, 522], |
|
underlinesegment: [["leftlinesegment", "rightlinesegment"], 0.888, 522], |
|
overgroup: [["leftgroup", "rightgroup"], 0.888, 342], |
|
undergroup: [["leftgroupunder", "rightgroupunder"], 0.888, 342], |
|
xmapsto: [["leftmapsto", "rightarrow"], 1.5, 522], |
|
xtofrom: [["leftToFrom", "rightToFrom"], 1.75, 528], |
|
|
|
|
|
|
|
|
|
|
|
xrightleftarrows: [["baraboveleftarrow", "rightarrowabovebar"], 1.75, 901], |
|
xrightequilibrium: [["baraboveshortleftharpoon", |
|
"rightharpoonaboveshortbar"], 1.75, 716], |
|
xleftequilibrium: [["shortbaraboveleftharpoon", |
|
"shortrightharpoonabovebar"], 1.75, 716], |
|
}; |
|
|
|
const groupLength = function(arg: AnyParseNode): number { |
|
if (arg.type === "ordgroup") { |
|
return arg.body.length; |
|
} else { |
|
return 1; |
|
} |
|
}; |
|
|
|
const svgSpan = function( |
|
group: ParseNode<"accent"> | ParseNode<"accentUnder"> | ParseNode<"xArrow"> |
|
| ParseNode<"horizBrace">, |
|
options: Options, |
|
): DomSpan | SvgSpan { |
|
|
|
function buildSvgSpan_(): { |
|
span: DomSpan | SvgSpan, |
|
minWidth: number, |
|
height: number, |
|
} { |
|
let viewBoxWidth = 400000; |
|
const label = group.label.slice(1); |
|
if (utils.contains(["widehat", "widecheck", "widetilde", "utilde"], |
|
label)) { |
|
|
|
|
|
|
|
const grp: ParseNode<"accent"> | ParseNode<"accentUnder"> = group; |
|
|
|
|
|
const numChars = groupLength(grp.base); |
|
let viewBoxHeight; |
|
let pathName; |
|
let height; |
|
|
|
if (numChars > 5) { |
|
if (label === "widehat" || label === "widecheck") { |
|
viewBoxHeight = 420; |
|
viewBoxWidth = 2364; |
|
height = 0.42; |
|
pathName = label + "4"; |
|
} else { |
|
viewBoxHeight = 312; |
|
viewBoxWidth = 2340; |
|
height = 0.34; |
|
pathName = "tilde4"; |
|
} |
|
} else { |
|
const imgIndex = [1, 1, 2, 2, 3, 3][numChars]; |
|
if (label === "widehat" || label === "widecheck") { |
|
viewBoxWidth = [0, 1062, 2364, 2364, 2364][imgIndex]; |
|
viewBoxHeight = [0, 239, 300, 360, 420][imgIndex]; |
|
height = [0, 0.24, 0.3, 0.3, 0.36, 0.42][imgIndex]; |
|
pathName = label + imgIndex; |
|
} else { |
|
viewBoxWidth = [0, 600, 1033, 2339, 2340][imgIndex]; |
|
viewBoxHeight = [0, 260, 286, 306, 312][imgIndex]; |
|
height = [0, 0.26, 0.286, 0.3, 0.306, 0.34][imgIndex]; |
|
pathName = "tilde" + imgIndex; |
|
} |
|
} |
|
const path = new PathNode(pathName); |
|
const svgNode = new SvgNode([path], { |
|
"width": "100%", |
|
"height": makeEm(height), |
|
"viewBox": `0 0 ${viewBoxWidth} ${viewBoxHeight}`, |
|
"preserveAspectRatio": "none", |
|
}); |
|
return { |
|
span: buildCommon.makeSvgSpan([], [svgNode], options), |
|
minWidth: 0, |
|
height, |
|
}; |
|
} else { |
|
const spans = []; |
|
|
|
const data = katexImagesData[label]; |
|
const [paths, minWidth, viewBoxHeight] = data; |
|
const height = viewBoxHeight / 1000; |
|
|
|
const numSvgChildren = paths.length; |
|
let widthClasses; |
|
let aligns; |
|
if (numSvgChildren === 1) { |
|
|
|
const align1: string = data[3]; |
|
widthClasses = ["hide-tail"]; |
|
aligns = [align1]; |
|
} else if (numSvgChildren === 2) { |
|
widthClasses = ["halfarrow-left", "halfarrow-right"]; |
|
aligns = ["xMinYMin", "xMaxYMin"]; |
|
} else if (numSvgChildren === 3) { |
|
widthClasses = ["brace-left", "brace-center", "brace-right"]; |
|
aligns = ["xMinYMin", "xMidYMin", "xMaxYMin"]; |
|
} else { |
|
throw new Error( |
|
`Correct katexImagesData or update code here to support |
|
${numSvgChildren} children.`); |
|
} |
|
|
|
for (let i = 0; i < numSvgChildren; i++) { |
|
const path = new PathNode(paths[i]); |
|
|
|
const svgNode = new SvgNode([path], { |
|
"width": "400em", |
|
"height": makeEm(height), |
|
"viewBox": `0 0 ${viewBoxWidth} ${viewBoxHeight}`, |
|
"preserveAspectRatio": aligns[i] + " slice", |
|
}); |
|
|
|
const span = buildCommon.makeSvgSpan( |
|
[widthClasses[i]], [svgNode], options); |
|
if (numSvgChildren === 1) { |
|
return {span, minWidth, height}; |
|
} else { |
|
span.style.height = makeEm(height); |
|
spans.push(span); |
|
} |
|
} |
|
|
|
return { |
|
span: buildCommon.makeSpan(["stretchy"], spans, options), |
|
minWidth, |
|
height, |
|
}; |
|
} |
|
} |
|
const {span, minWidth, height} = buildSvgSpan_(); |
|
|
|
|
|
|
|
span.height = height; |
|
span.style.height = makeEm(height); |
|
if (minWidth > 0) { |
|
span.style.minWidth = makeEm(minWidth); |
|
} |
|
|
|
return span; |
|
}; |
|
|
|
const encloseSpan = function( |
|
inner: HtmlDomNode, |
|
label: string, |
|
topPad: number, |
|
bottomPad: number, |
|
options: Options, |
|
): DomSpan | SvgSpan { |
|
|
|
let img; |
|
const totalHeight = inner.height + inner.depth + topPad + bottomPad; |
|
|
|
if (/fbox|color|angl/.test(label)) { |
|
img = buildCommon.makeSpan(["stretchy", label], [], options); |
|
|
|
if (label === "fbox") { |
|
const color = options.color && options.getColor(); |
|
if (color) { |
|
img.style.borderColor = color; |
|
} |
|
} |
|
|
|
} else { |
|
|
|
|
|
|
|
|
|
const lines = []; |
|
if (/^[bx]cancel$/.test(label)) { |
|
lines.push(new LineNode({ |
|
"x1": "0", |
|
"y1": "0", |
|
"x2": "100%", |
|
"y2": "100%", |
|
"stroke-width": "0.046em", |
|
})); |
|
} |
|
|
|
if (/^x?cancel$/.test(label)) { |
|
lines.push(new LineNode({ |
|
"x1": "0", |
|
"y1": "100%", |
|
"x2": "100%", |
|
"y2": "0", |
|
"stroke-width": "0.046em", |
|
})); |
|
} |
|
|
|
const svgNode = new SvgNode(lines, { |
|
"width": "100%", |
|
"height": makeEm(totalHeight), |
|
}); |
|
|
|
img = buildCommon.makeSvgSpan([], [svgNode], options); |
|
} |
|
|
|
img.height = totalHeight; |
|
img.style.height = makeEm(totalHeight); |
|
|
|
return img; |
|
}; |
|
|
|
export default { |
|
encloseSpan, |
|
mathMLnode, |
|
svgSpan, |
|
}; |
|
|