File size: 5,906 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 139 140 141 142 143 144 145 |
// @flow
import defineFunction from "../defineFunction";
import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import stretchy from "../stretchy";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import type {ParseNode} from "../parseNode";
// Helper function
const paddedNode = group => {
const node = new mathMLTree.MathNode("mpadded", group ? [group] : []);
node.setAttribute("width", "+0.6em");
node.setAttribute("lspace", "0.3em");
return node;
};
// Stretchy arrows with an optional argument
defineFunction({
type: "xArrow",
names: [
"\\xleftarrow", "\\xrightarrow", "\\xLeftarrow", "\\xRightarrow",
"\\xleftrightarrow", "\\xLeftrightarrow", "\\xhookleftarrow",
"\\xhookrightarrow", "\\xmapsto", "\\xrightharpoondown",
"\\xrightharpoonup", "\\xleftharpoondown", "\\xleftharpoonup",
"\\xrightleftharpoons", "\\xleftrightharpoons", "\\xlongequal",
"\\xtwoheadrightarrow", "\\xtwoheadleftarrow", "\\xtofrom",
// The next 3 functions are here to support the mhchem extension.
// Direct use of these functions is discouraged and may break someday.
"\\xrightleftarrows", "\\xrightequilibrium", "\\xleftequilibrium",
// The next 3 functions are here only to support the {CD} environment.
"\\\\cdrightarrow", "\\\\cdleftarrow", "\\\\cdlongequal",
],
props: {
numArgs: 1,
numOptionalArgs: 1,
},
handler({parser, funcName}, args, optArgs) {
return {
type: "xArrow",
mode: parser.mode,
label: funcName,
body: args[0],
below: optArgs[0],
};
},
// Flow is unable to correctly infer the type of `group`, even though it's
// unambiguously determined from the passed-in `type` above.
htmlBuilder(group: ParseNode<"xArrow">, options) {
const style = options.style;
// Build the argument groups in the appropriate style.
// Ref: amsmath.dtx: \hbox{$\scriptstyle\mkern#3mu{#6}\mkern#4mu$}%
// Some groups can return document fragments. Handle those by wrapping
// them in a span.
let newOptions = options.havingStyle(style.sup());
const upperGroup = buildCommon.wrapFragment(
html.buildGroup(group.body, newOptions, options), options);
const arrowPrefix = group.label.slice(0, 2) === "\\x" ? "x" : "cd";
upperGroup.classes.push(arrowPrefix + "-arrow-pad");
let lowerGroup;
if (group.below) {
// Build the lower group
newOptions = options.havingStyle(style.sub());
lowerGroup = buildCommon.wrapFragment(
html.buildGroup(group.below, newOptions, options), options);
lowerGroup.classes.push(arrowPrefix + "-arrow-pad");
}
const arrowBody = stretchy.svgSpan(group, options);
// Re shift: Note that stretchy.svgSpan returned arrowBody.depth = 0.
// The point we want on the math axis is at 0.5 * arrowBody.height.
const arrowShift = -options.fontMetrics().axisHeight +
0.5 * arrowBody.height;
// 2 mu kern. Ref: amsmath.dtx: #7\if0#2\else\mkern#2mu\fi
let upperShift = -options.fontMetrics().axisHeight
- 0.5 * arrowBody.height - 0.111; // 0.111 em = 2 mu
if (upperGroup.depth > 0.25 || group.label === "\\xleftequilibrium") {
upperShift -= upperGroup.depth; // shift up if depth encroaches
}
// Generate the vlist
let vlist;
if (lowerGroup) {
const lowerShift = -options.fontMetrics().axisHeight
+ lowerGroup.height + 0.5 * arrowBody.height
+ 0.111;
vlist = buildCommon.makeVList({
positionType: "individualShift",
children: [
{type: "elem", elem: upperGroup, shift: upperShift},
{type: "elem", elem: arrowBody, shift: arrowShift},
{type: "elem", elem: lowerGroup, shift: lowerShift},
],
}, options);
} else {
vlist = buildCommon.makeVList({
positionType: "individualShift",
children: [
{type: "elem", elem: upperGroup, shift: upperShift},
{type: "elem", elem: arrowBody, shift: arrowShift},
],
}, options);
}
// $FlowFixMe: Replace this with passing "svg-align" into makeVList.
vlist.children[0].children[0].children[1].classes.push("svg-align");
return buildCommon.makeSpan(["mrel", "x-arrow"], [vlist], options);
},
mathmlBuilder(group, options) {
const arrowNode = stretchy.mathMLnode(group.label);
arrowNode.setAttribute(
"minsize", group.label.charAt(0) === "x" ? "1.75em" : "3.0em"
);
let node;
if (group.body) {
const upperNode = paddedNode(mml.buildGroup(group.body, options));
if (group.below) {
const lowerNode = paddedNode(mml.buildGroup(group.below, options));
node = new mathMLTree.MathNode(
"munderover", [arrowNode, lowerNode, upperNode]
);
} else {
node = new mathMLTree.MathNode("mover", [arrowNode, upperNode]);
}
} else if (group.below) {
const lowerNode = paddedNode(mml.buildGroup(group.below, options));
node = new mathMLTree.MathNode("munder", [arrowNode, lowerNode]);
} else {
// This should never happen.
// Parser.js throws an error if there is no argument.
node = paddedNode();
node = new mathMLTree.MathNode("mover", [arrowNode, node]);
}
return node;
},
});
|