File size: 4,992 Bytes
bc20498 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
// @flow
import defineFunction from "../defineFunction";
import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import stretchy from "../stretchy";
import Style from "../Style";
import {assertNodeType} from "../parseNode";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import type {HtmlBuilderSupSub, MathMLBuilder} from "../defineFunction";
import type {ParseNode} from "../parseNode";
// NOTE: Unlike most `htmlBuilder`s, this one handles not only "horizBrace", but
// also "supsub" since an over/underbrace can affect super/subscripting.
export const htmlBuilder: HtmlBuilderSupSub<"horizBrace"> = (grp, options) => {
const style = options.style;
// Pull out the `ParseNode<"horizBrace">` if `grp` is a "supsub" node.
let supSubGroup;
let group: ParseNode<"horizBrace">;
if (grp.type === "supsub") {
// Ref: LaTeX source2e: }}}}\limits}
// i.e. LaTeX treats the brace similar to an op and passes it
// with \limits, so we need to assign supsub style.
supSubGroup = grp.sup ?
html.buildGroup(grp.sup, options.havingStyle(style.sup()), options) :
html.buildGroup(grp.sub, options.havingStyle(style.sub()), options);
group = assertNodeType(grp.base, "horizBrace");
} else {
group = assertNodeType(grp, "horizBrace");
}
// Build the base group
const body = html.buildGroup(
group.base, options.havingBaseStyle(Style.DISPLAY));
// Create the stretchy element
const braceBody = stretchy.svgSpan(group, options);
// Generate the vlist, with the appropriate kerns ββββββββββ
// This first vlist contains the content and the brace: equation
let vlist;
if (group.isOver) {
vlist = buildCommon.makeVList({
positionType: "firstBaseline",
children: [
{type: "elem", elem: body},
{type: "kern", size: 0.1},
{type: "elem", elem: braceBody},
],
}, options);
// $FlowFixMe: Replace this with passing "svg-align" into makeVList.
vlist.children[0].children[0].children[1].classes.push("svg-align");
} else {
vlist = buildCommon.makeVList({
positionType: "bottom",
positionData: body.depth + 0.1 + braceBody.height,
children: [
{type: "elem", elem: braceBody},
{type: "kern", size: 0.1},
{type: "elem", elem: body},
],
}, options);
// $FlowFixMe: Replace this with passing "svg-align" into makeVList.
vlist.children[0].children[0].children[0].classes.push("svg-align");
}
if (supSubGroup) {
// To write the supsub, wrap the first vlist in another vlist:
// They can't all go in the same vlist, because the note might be
// wider than the equation. We want the equation to control the
// brace width.
// note long note long note
// ββββββββββ or βββββ not βββββββββββ
// equation eqn eqn
const vSpan = buildCommon.makeSpan(
["mord", (group.isOver ? "mover" : "munder")],
[vlist], options);
if (group.isOver) {
vlist = buildCommon.makeVList({
positionType: "firstBaseline",
children: [
{type: "elem", elem: vSpan},
{type: "kern", size: 0.2},
{type: "elem", elem: supSubGroup},
],
}, options);
} else {
vlist = buildCommon.makeVList({
positionType: "bottom",
positionData: vSpan.depth + 0.2 + supSubGroup.height +
supSubGroup.depth,
children: [
{type: "elem", elem: supSubGroup},
{type: "kern", size: 0.2},
{type: "elem", elem: vSpan},
],
}, options);
}
}
return buildCommon.makeSpan(
["mord", (group.isOver ? "mover" : "munder")], [vlist], options);
};
const mathmlBuilder: MathMLBuilder<"horizBrace"> = (group, options) => {
const accentNode = stretchy.mathMLnode(group.label);
return new mathMLTree.MathNode(
(group.isOver ? "mover" : "munder"),
[mml.buildGroup(group.base, options), accentNode]
);
};
// Horizontal stretchy braces
defineFunction({
type: "horizBrace",
names: ["\\overbrace", "\\underbrace"],
props: {
numArgs: 1,
},
handler({parser, funcName}, args) {
return {
type: "horizBrace",
mode: parser.mode,
label: funcName,
isOver: /^\\over/.test(funcName),
base: args[0],
};
},
htmlBuilder,
mathmlBuilder,
});
|