mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-05 19:28:39 +00:00
Format buildHTML groupType definitions in preparation for adding types. (#1206)
* Format buildHTML groupType definitions in preparation for adding types. 1) Collapse groupType definitions (a bunch of functions all of the same type defined on the groupType object) into a single object in preparation for putting types only on the base object. 2) Do minor reformatting to account for the +4 indent above which pushes some lines past 80 characters. 3) Do other minor formatting changes for readability, taking advantage of ES6. * Review fixes.
This commit is contained in:
651
src/buildHTML.js
651
src/buildHTML.js
@@ -63,8 +63,7 @@ export const buildExpression = function(expression, options, isRealGroup,
|
||||
// Parse expressions into `groups`.
|
||||
const rawGroups = [];
|
||||
for (let i = 0; i < expression.length; i++) {
|
||||
const group = expression[i];
|
||||
const output = buildGroup(group, options);
|
||||
const output = buildGroup(expression[i], options);
|
||||
if (output instanceof domTree.documentFragment) {
|
||||
rawGroups.push(...output.children);
|
||||
} else {
|
||||
@@ -216,24 +215,22 @@ export const isLeftTight = function(node) {
|
||||
* handling them itself.
|
||||
*/
|
||||
const shouldHandleSupSub = function(group, options) {
|
||||
if (!group.value.base) {
|
||||
const base = group.value.base;
|
||||
if (!base) {
|
||||
return false;
|
||||
} else if (base.type === "op") {
|
||||
// Operators handle supsubs differently when they have limits
|
||||
// (e.g. `\displaystyle\sum_2^3`)
|
||||
return base.value.limits &&
|
||||
(options.style.size === Style.DISPLAY.size ||
|
||||
base.value.alwaysHandleSupSub);
|
||||
} else if (base.type === "accent") {
|
||||
return utils.isCharacterBox(base.value.base);
|
||||
} else if (base.type === "horizBrace") {
|
||||
const isSup = !group.value.sub;
|
||||
return (isSup === base.value.isOver);
|
||||
} else {
|
||||
const base = group.value.base;
|
||||
if (base.type === "op") {
|
||||
// Operators handle supsubs differently when they have limits
|
||||
// (e.g. `\displaystyle\sum_2^3`)
|
||||
return base.value.limits &&
|
||||
(options.style.size === Style.DISPLAY.size ||
|
||||
base.value.alwaysHandleSupSub);
|
||||
} else if (base.type === "accent") {
|
||||
return utils.isCharacterBox(base.value.base);
|
||||
} else if (base.type === "horizBrace") {
|
||||
const isSup = (group.value.sub ? false : true);
|
||||
return (isSup === base.value.isOver);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -246,374 +243,354 @@ export const makeNullDelimiter = function(options, classes) {
|
||||
* This is a map of group types to the function used to handle that type.
|
||||
* Simpler types come at the beginning, while complicated types come afterwards.
|
||||
*/
|
||||
export const groupTypes = {};
|
||||
export const groupTypes = {
|
||||
mathord: (group, options) => buildCommon.makeOrd(group, options, "mathord"),
|
||||
|
||||
groupTypes.mathord = function(group, options) {
|
||||
return buildCommon.makeOrd(group, options, "mathord");
|
||||
};
|
||||
textord: (group, options) => buildCommon.makeOrd(group, options, "textord"),
|
||||
|
||||
groupTypes.textord = function(group, options) {
|
||||
return buildCommon.makeOrd(group, options, "textord");
|
||||
};
|
||||
bin: (group, options) =>
|
||||
buildCommon.mathsym(group.value, group.mode, options, ["mbin"]),
|
||||
|
||||
groupTypes.bin = function(group, options) {
|
||||
return buildCommon.mathsym(
|
||||
group.value, group.mode, options, ["mbin"]);
|
||||
};
|
||||
rel: (group, options) =>
|
||||
buildCommon.mathsym(group.value, group.mode, options, ["mrel"]),
|
||||
|
||||
groupTypes.rel = function(group, options) {
|
||||
return buildCommon.mathsym(
|
||||
group.value, group.mode, options, ["mrel"]);
|
||||
};
|
||||
open: (group, options) =>
|
||||
buildCommon.mathsym(group.value, group.mode, options, ["mopen"]),
|
||||
|
||||
groupTypes.open = function(group, options) {
|
||||
return buildCommon.mathsym(
|
||||
group.value, group.mode, options, ["mopen"]);
|
||||
};
|
||||
close: (group, options) =>
|
||||
buildCommon.mathsym(group.value, group.mode, options, ["mclose"]),
|
||||
|
||||
groupTypes.close = function(group, options) {
|
||||
return buildCommon.mathsym(
|
||||
group.value, group.mode, options, ["mclose"]);
|
||||
};
|
||||
inner: (group, options) =>
|
||||
buildCommon.mathsym(group.value, group.mode, options, ["minner"]),
|
||||
|
||||
groupTypes.inner = function(group, options) {
|
||||
return buildCommon.mathsym(
|
||||
group.value, group.mode, options, ["minner"]);
|
||||
};
|
||||
punct: (group, options) =>
|
||||
buildCommon.mathsym(group.value, group.mode, options, ["mpunct"]),
|
||||
|
||||
groupTypes.punct = function(group, options) {
|
||||
return buildCommon.mathsym(
|
||||
group.value, group.mode, options, ["mpunct"]);
|
||||
};
|
||||
ordgroup: (group, options) => makeSpan(
|
||||
["mord"], buildExpression(group.value, options, true), options),
|
||||
|
||||
groupTypes.ordgroup = function(group, options) {
|
||||
return makeSpan(["mord"],
|
||||
buildExpression(group.value, options, true),
|
||||
options
|
||||
);
|
||||
};
|
||||
supsub(group, options) {
|
||||
// Superscript and subscripts are handled in the TeXbook on page
|
||||
// 445-446, rules 18(a-f).
|
||||
|
||||
groupTypes.supsub = function(group, options) {
|
||||
// Superscript and subscripts are handled in the TeXbook on page
|
||||
// 445-446, rules 18(a-f).
|
||||
|
||||
// Here is where we defer to the inner group if it should handle
|
||||
// superscripts and subscripts itself.
|
||||
if (shouldHandleSupSub(group, options)) {
|
||||
return groupTypes[group.value.base.type](group, options);
|
||||
}
|
||||
|
||||
const base = buildGroup(group.value.base, options);
|
||||
let supm;
|
||||
let subm;
|
||||
|
||||
const metrics = options.fontMetrics();
|
||||
let newOptions;
|
||||
|
||||
// Rule 18a
|
||||
let supShift = 0;
|
||||
let subShift = 0;
|
||||
|
||||
if (group.value.sup) {
|
||||
newOptions = options.havingStyle(options.style.sup());
|
||||
supm = buildGroup(group.value.sup, newOptions, options);
|
||||
if (!utils.isCharacterBox(group.value.base)) {
|
||||
supShift = base.height - newOptions.fontMetrics().supDrop
|
||||
* newOptions.sizeMultiplier / options.sizeMultiplier;
|
||||
}
|
||||
}
|
||||
|
||||
if (group.value.sub) {
|
||||
newOptions = options.havingStyle(options.style.sub());
|
||||
subm = buildGroup(group.value.sub, newOptions, options);
|
||||
if (!utils.isCharacterBox(group.value.base)) {
|
||||
subShift = base.depth + newOptions.fontMetrics().subDrop
|
||||
* newOptions.sizeMultiplier / options.sizeMultiplier;
|
||||
}
|
||||
}
|
||||
|
||||
// Rule 18c
|
||||
let minSupShift;
|
||||
if (options.style === Style.DISPLAY) {
|
||||
minSupShift = metrics.sup1;
|
||||
} else if (options.style.cramped) {
|
||||
minSupShift = metrics.sup3;
|
||||
} else {
|
||||
minSupShift = metrics.sup2;
|
||||
}
|
||||
|
||||
// scriptspace is a font-size-independent size, so scale it
|
||||
// appropriately
|
||||
const multiplier = options.sizeMultiplier;
|
||||
const scriptspace =
|
||||
(0.5 / metrics.ptPerEm) / multiplier + "em";
|
||||
|
||||
let supsub;
|
||||
if (!group.value.sup) {
|
||||
// Rule 18b
|
||||
subShift = Math.max(
|
||||
subShift, metrics.sub1,
|
||||
subm.height - 0.8 * metrics.xHeight);
|
||||
|
||||
const vlistElem = [{type: "elem", elem: subm, marginRight: scriptspace}];
|
||||
// Subscripts shouldn't be shifted by the base's italic correction.
|
||||
// Account for that by shifting the subscript back the appropriate
|
||||
// amount. Note we only do this when the base is a single symbol.
|
||||
if (base instanceof domTree.symbolNode) {
|
||||
vlistElem[0].marginLeft = -base.italic + "em";
|
||||
// Here is where we defer to the inner group if it should handle
|
||||
// superscripts and subscripts itself.
|
||||
if (shouldHandleSupSub(group, options)) {
|
||||
return groupTypes[group.value.base.type](group, options);
|
||||
}
|
||||
|
||||
supsub = buildCommon.makeVList({
|
||||
positionType: "shift",
|
||||
positionData: subShift,
|
||||
children: vlistElem,
|
||||
}, options);
|
||||
} else if (!group.value.sub) {
|
||||
// Rule 18c, d
|
||||
supShift = Math.max(supShift, minSupShift,
|
||||
supm.depth + 0.25 * metrics.xHeight);
|
||||
const base = buildGroup(group.value.base, options);
|
||||
let supm;
|
||||
let subm;
|
||||
|
||||
supsub = buildCommon.makeVList({
|
||||
positionType: "shift",
|
||||
positionData: -supShift,
|
||||
children: [{type: "elem", elem: supm, marginRight: scriptspace}],
|
||||
}, options);
|
||||
} else {
|
||||
supShift = Math.max(
|
||||
supShift, minSupShift, supm.depth + 0.25 * metrics.xHeight);
|
||||
subShift = Math.max(subShift, metrics.sub2);
|
||||
const metrics = options.fontMetrics();
|
||||
let newOptions;
|
||||
|
||||
const ruleWidth = metrics.defaultRuleThickness;
|
||||
// Rule 18a
|
||||
let supShift = 0;
|
||||
let subShift = 0;
|
||||
|
||||
// Rule 18e
|
||||
if ((supShift - supm.depth) - (subm.height - subShift) <
|
||||
4 * ruleWidth) {
|
||||
subShift = 4 * ruleWidth - (supShift - supm.depth) + subm.height;
|
||||
const psi = 0.8 * metrics.xHeight - (supShift - supm.depth);
|
||||
if (psi > 0) {
|
||||
supShift += psi;
|
||||
subShift -= psi;
|
||||
if (group.value.sup) {
|
||||
newOptions = options.havingStyle(options.style.sup());
|
||||
supm = buildGroup(group.value.sup, newOptions, options);
|
||||
if (!utils.isCharacterBox(group.value.base)) {
|
||||
supShift = base.height - newOptions.fontMetrics().supDrop
|
||||
* newOptions.sizeMultiplier / options.sizeMultiplier;
|
||||
}
|
||||
}
|
||||
|
||||
const vlistElem = [
|
||||
{type: "elem", elem: subm, shift: subShift, marginRight: scriptspace},
|
||||
{type: "elem", elem: supm, shift: -supShift, marginRight: scriptspace},
|
||||
];
|
||||
// See comment above about subscripts not being shifted
|
||||
if (base instanceof domTree.symbolNode) {
|
||||
vlistElem[0].marginLeft = -base.italic + "em";
|
||||
if (group.value.sub) {
|
||||
newOptions = options.havingStyle(options.style.sub());
|
||||
subm = buildGroup(group.value.sub, newOptions, options);
|
||||
if (!utils.isCharacterBox(group.value.base)) {
|
||||
subShift = base.depth + newOptions.fontMetrics().subDrop
|
||||
* newOptions.sizeMultiplier / options.sizeMultiplier;
|
||||
}
|
||||
}
|
||||
|
||||
supsub = buildCommon.makeVList({
|
||||
positionType: "individualShift",
|
||||
children: vlistElem,
|
||||
}, options);
|
||||
}
|
||||
|
||||
// We ensure to wrap the supsub vlist in a span.msupsub to reset text-align
|
||||
const mclass = getTypeOfDomTree(base) || "mord";
|
||||
return makeSpan([mclass],
|
||||
[base, makeSpan(["msupsub"], [supsub])],
|
||||
options);
|
||||
};
|
||||
|
||||
groupTypes.spacing = function(group, options) {
|
||||
if (buildCommon.regularSpace.hasOwnProperty(group.value)) {
|
||||
// Spaces are generated by adding an actual space. Each of these
|
||||
// things has an entry in the symbols table, so these will be turned
|
||||
// into appropriate outputs.
|
||||
if (group.mode === "text") {
|
||||
return buildCommon.makeOrd(group, options, "textord");
|
||||
// Rule 18c
|
||||
let minSupShift;
|
||||
if (options.style === Style.DISPLAY) {
|
||||
minSupShift = metrics.sup1;
|
||||
} else if (options.style.cramped) {
|
||||
minSupShift = metrics.sup3;
|
||||
} else {
|
||||
return makeSpan(["mspace"],
|
||||
[buildCommon.mathsym(group.value, group.mode, options)],
|
||||
options);
|
||||
minSupShift = metrics.sup2;
|
||||
}
|
||||
} else {
|
||||
// Other kinds of spaces are of arbitrary width. We use CSS to
|
||||
// generate these.
|
||||
return makeSpan(
|
||||
["mspace", buildCommon.spacingFunctions[group.value].className],
|
||||
[], options);
|
||||
}
|
||||
};
|
||||
|
||||
groupTypes.horizBrace = function(group, options) {
|
||||
const style = options.style;
|
||||
// scriptspace is a font-size-independent size, so scale it
|
||||
// appropriately for use as the marginRight.
|
||||
const multiplier = options.sizeMultiplier;
|
||||
const marginRight = (0.5 / metrics.ptPerEm) / multiplier + "em";
|
||||
|
||||
const hasSupSub = (group.type === "supsub");
|
||||
let supSubGroup;
|
||||
let newOptions;
|
||||
if (hasSupSub) {
|
||||
// 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.
|
||||
if (group.value.sup) {
|
||||
newOptions = options.havingStyle(style.sup());
|
||||
supSubGroup = buildGroup(group.value.sup, newOptions, options);
|
||||
let supsub;
|
||||
if (!group.value.sup) {
|
||||
// Rule 18b
|
||||
subShift = Math.max(
|
||||
subShift, metrics.sub1,
|
||||
subm.height - 0.8 * metrics.xHeight);
|
||||
|
||||
const vlistElem = [{type: "elem", elem: subm, marginRight}];
|
||||
// Subscripts shouldn't be shifted by the base's italic correction.
|
||||
// Account for that by shifting the subscript back the appropriate
|
||||
// amount. Note we only do this when the base is a single symbol.
|
||||
if (base instanceof domTree.symbolNode) {
|
||||
vlistElem[0].marginLeft = -base.italic + "em";
|
||||
}
|
||||
|
||||
supsub = buildCommon.makeVList({
|
||||
positionType: "shift",
|
||||
positionData: subShift,
|
||||
children: vlistElem,
|
||||
}, options);
|
||||
} else if (!group.value.sub) {
|
||||
// Rule 18c, d
|
||||
supShift = Math.max(supShift, minSupShift,
|
||||
supm.depth + 0.25 * metrics.xHeight);
|
||||
|
||||
supsub = buildCommon.makeVList({
|
||||
positionType: "shift",
|
||||
positionData: -supShift,
|
||||
children: [{type: "elem", elem: supm, marginRight}],
|
||||
}, options);
|
||||
} else {
|
||||
newOptions = options.havingStyle(style.sub());
|
||||
supSubGroup = buildGroup(group.value.sub, newOptions, options);
|
||||
supShift = Math.max(
|
||||
supShift, minSupShift, supm.depth + 0.25 * metrics.xHeight);
|
||||
subShift = Math.max(subShift, metrics.sub2);
|
||||
|
||||
const ruleWidth = metrics.defaultRuleThickness;
|
||||
|
||||
// Rule 18e
|
||||
const maxWidth = 4 * ruleWidth;
|
||||
if ((supShift - supm.depth) - (subm.height - subShift) < maxWidth) {
|
||||
subShift = maxWidth - (supShift - supm.depth) + subm.height;
|
||||
const psi = 0.8 * metrics.xHeight - (supShift - supm.depth);
|
||||
if (psi > 0) {
|
||||
supShift += psi;
|
||||
subShift -= psi;
|
||||
}
|
||||
}
|
||||
|
||||
const vlistElem = [
|
||||
{type: "elem", elem: subm, shift: subShift, marginRight},
|
||||
{type: "elem", elem: supm, shift: -supShift, marginRight},
|
||||
];
|
||||
// See comment above about subscripts not being shifted.
|
||||
if (base instanceof domTree.symbolNode) {
|
||||
vlistElem[0].marginLeft = -base.italic + "em";
|
||||
}
|
||||
|
||||
supsub = buildCommon.makeVList({
|
||||
positionType: "individualShift",
|
||||
children: vlistElem,
|
||||
}, options);
|
||||
}
|
||||
group = group.value.base;
|
||||
}
|
||||
|
||||
// Build the base group
|
||||
const body = buildGroup(
|
||||
group.value.base, options.havingBaseStyle(Style.DISPLAY));
|
||||
// Wrap the supsub vlist in a span.msupsub to reset text-align.
|
||||
const mclass = getTypeOfDomTree(base) || "mord";
|
||||
return makeSpan([mclass],
|
||||
[base, makeSpan(["msupsub"], [supsub])],
|
||||
options);
|
||||
},
|
||||
|
||||
// Create the stretchy element
|
||||
const braceBody = stretchy.svgSpan(group, options);
|
||||
spacing(group, options) {
|
||||
if (buildCommon.regularSpace.hasOwnProperty(group.value)) {
|
||||
// Spaces are generated by adding an actual space. Each of these
|
||||
// things has an entry in the symbols table, so these will be turned
|
||||
// into appropriate outputs.
|
||||
if (group.mode === "text") {
|
||||
return buildCommon.makeOrd(group, options, "textord");
|
||||
} else {
|
||||
return makeSpan(["mspace"],
|
||||
[buildCommon.mathsym(group.value, group.mode, options)],
|
||||
options);
|
||||
}
|
||||
} else {
|
||||
// Other kinds of spaces are of arbitrary width. We use CSS to
|
||||
// generate these.
|
||||
return makeSpan(
|
||||
["mspace", buildCommon.spacingFunctions[group.value].className],
|
||||
[], options);
|
||||
}
|
||||
},
|
||||
|
||||
// Generate the vlist, with the appropriate kerns ┏━━━━━━━━┓
|
||||
// This first vlist contains the subject matter and the brace: equation
|
||||
let vlist;
|
||||
if (group.value.isOver) {
|
||||
vlist = buildCommon.makeVList({
|
||||
positionType: "firstBaseline",
|
||||
children: [
|
||||
{type: "elem", elem: body},
|
||||
{type: "kern", size: 0.1},
|
||||
{type: "elem", elem: braceBody},
|
||||
],
|
||||
}, options);
|
||||
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);
|
||||
vlist.children[0].children[0].children[0].classes.push("svg-align");
|
||||
}
|
||||
horizBrace(group, options) {
|
||||
const style = options.style;
|
||||
|
||||
if (hasSupSub) {
|
||||
// In order 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.
|
||||
const hasSupSub = (group.type === "supsub");
|
||||
let supSubGroup;
|
||||
let newOptions;
|
||||
if (hasSupSub) {
|
||||
// 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.
|
||||
if (group.value.sup) {
|
||||
newOptions = options.havingStyle(style.sup());
|
||||
supSubGroup = buildGroup(group.value.sup, newOptions, options);
|
||||
} else {
|
||||
newOptions = options.havingStyle(style.sub());
|
||||
supSubGroup = buildGroup(group.value.sub, newOptions, options);
|
||||
}
|
||||
group = group.value.base;
|
||||
}
|
||||
|
||||
// note long note long note
|
||||
// ┏━━━━━━━━┓ or ┏━━━┓ not ┏━━━━━━━━━┓
|
||||
// equation eqn eqn
|
||||
// Build the base group
|
||||
const body = buildGroup(
|
||||
group.value.base, options.havingBaseStyle(Style.DISPLAY));
|
||||
|
||||
const vSpan = makeSpan(["mord",
|
||||
(group.value.isOver ? "mover" : "munder")],
|
||||
[vlist], options);
|
||||
// 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.value.isOver) {
|
||||
vlist = buildCommon.makeVList({
|
||||
positionType: "firstBaseline",
|
||||
children: [
|
||||
{type: "elem", elem: vSpan},
|
||||
{type: "kern", size: 0.2},
|
||||
{type: "elem", elem: supSubGroup},
|
||||
{type: "elem", elem: body},
|
||||
{type: "kern", size: 0.1},
|
||||
{type: "elem", elem: braceBody},
|
||||
],
|
||||
}, options);
|
||||
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);
|
||||
vlist.children[0].children[0].children[0].classes.push("svg-align");
|
||||
}
|
||||
|
||||
if (hasSupSub) {
|
||||
// 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 = makeSpan(
|
||||
["mord", (group.value.isOver ? "mover" : "munder")],
|
||||
[vlist], options);
|
||||
|
||||
if (group.value.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,
|
||||
children: [
|
||||
{type: "elem", elem: supSubGroup},
|
||||
{type: "kern", size: 0.2},
|
||||
{type: "elem", elem: vSpan},
|
||||
],
|
||||
}, options);
|
||||
}
|
||||
}
|
||||
|
||||
return makeSpan(["mord", (group.value.isOver ? "mover" : "munder")],
|
||||
[vlist], options);
|
||||
},
|
||||
|
||||
xArrow(group, options) {
|
||||
const style = options.style;
|
||||
|
||||
// Build the argument groups in the appropriate style.
|
||||
// Ref: amsmath.dtx: \hbox{$\scriptstyle\mkern#3mu{#6}\mkern#4mu$}%
|
||||
|
||||
let newOptions = options.havingStyle(style.sup());
|
||||
const upperGroup = buildGroup(group.value.body, newOptions, options);
|
||||
upperGroup.classes.push("x-arrow-pad");
|
||||
|
||||
let lowerGroup;
|
||||
if (group.value.below) {
|
||||
// Build the lower group
|
||||
newOptions = options.havingStyle(style.sub());
|
||||
lowerGroup = buildGroup(group.value.below, newOptions, options);
|
||||
lowerGroup.classes.push("x-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;
|
||||
if (group.value.label === "\\xleftequilibrium") {
|
||||
upperShift -= upperGroup.depth;
|
||||
}
|
||||
|
||||
// Generate the vlist
|
||||
let vlist;
|
||||
if (group.value.below) {
|
||||
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: "bottom",
|
||||
positionData: vSpan.depth + 0.2 + supSubGroup.height,
|
||||
positionType: "individualShift",
|
||||
children: [
|
||||
{type: "elem", elem: supSubGroup},
|
||||
{type: "kern", size: 0.2},
|
||||
{type: "elem", elem: vSpan},
|
||||
{type: "elem", elem: upperGroup, shift: upperShift},
|
||||
{type: "elem", elem: arrowBody, shift: arrowShift},
|
||||
],
|
||||
}, options);
|
||||
}
|
||||
}
|
||||
|
||||
return makeSpan(["mord", (group.value.isOver ? "mover" : "munder")],
|
||||
[vlist], options);
|
||||
};
|
||||
vlist.children[0].children[0].children[1].classes.push("svg-align");
|
||||
|
||||
groupTypes.xArrow = function(group, options) {
|
||||
const style = options.style;
|
||||
return makeSpan(["mrel", "x-arrow"], [vlist], options);
|
||||
},
|
||||
|
||||
// Build the argument groups in the appropriate style.
|
||||
// Ref: amsmath.dtx: \hbox{$\scriptstyle\mkern#3mu{#6}\mkern#4mu$}%
|
||||
mclass(group, options) {
|
||||
const elements = buildExpression(group.value.value, options, true);
|
||||
|
||||
let newOptions = options.havingStyle(style.sup());
|
||||
const upperGroup = buildGroup(group.value.body, newOptions, options);
|
||||
upperGroup.classes.push("x-arrow-pad");
|
||||
return makeSpan([group.value.mclass], elements, options);
|
||||
},
|
||||
|
||||
let lowerGroup;
|
||||
if (group.value.below) {
|
||||
// Build the lower group
|
||||
newOptions = options.havingStyle(style.sub());
|
||||
lowerGroup = buildGroup(group.value.below, newOptions, options);
|
||||
lowerGroup.classes.push("x-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;
|
||||
if (group.value.label === "\\xleftequilibrium") {
|
||||
upperShift -= upperGroup.depth;
|
||||
}
|
||||
|
||||
// Generate the vlist
|
||||
let vlist;
|
||||
if (group.value.below) {
|
||||
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},
|
||||
],
|
||||
raisebox(group, options) {
|
||||
const body = groupTypes.sizing({value: {
|
||||
value: [{
|
||||
type: "text",
|
||||
value: {
|
||||
body: group.value.value,
|
||||
font: "mathrm", // simulate \textrm
|
||||
},
|
||||
}],
|
||||
size: 6, // simulate \normalsize
|
||||
}}, options);
|
||||
const dy = calculateSize(group.value.dy.value, options);
|
||||
return buildCommon.makeVList({
|
||||
positionType: "shift",
|
||||
positionData: -dy,
|
||||
children: [{type: "elem", elem: body}],
|
||||
}, options);
|
||||
} else {
|
||||
vlist = buildCommon.makeVList({
|
||||
positionType: "individualShift",
|
||||
children: [
|
||||
{type: "elem", elem: upperGroup, shift: upperShift},
|
||||
{type: "elem", elem: arrowBody, shift: arrowShift},
|
||||
],
|
||||
}, options);
|
||||
}
|
||||
|
||||
vlist.children[0].children[0].children[1].classes.push("svg-align");
|
||||
|
||||
return makeSpan(["mrel", "x-arrow"], [vlist], options);
|
||||
};
|
||||
|
||||
groupTypes.mclass = function(group, options) {
|
||||
const elements = buildExpression(group.value.value, options, true);
|
||||
|
||||
return makeSpan([group.value.mclass], elements, options);
|
||||
};
|
||||
|
||||
groupTypes.raisebox = function(group, options) {
|
||||
const body = groupTypes.sizing({value: {
|
||||
value: [{
|
||||
type: "text",
|
||||
value: {
|
||||
body: group.value.value,
|
||||
font: "mathrm", // simulate \textrm
|
||||
},
|
||||
}],
|
||||
size: 6, // simulate \normalsize
|
||||
}}, options);
|
||||
const dy = calculateSize(group.value.dy.value, options);
|
||||
return buildCommon.makeVList({
|
||||
positionType: "shift",
|
||||
positionData: -dy,
|
||||
children: [{type: "elem", elem: body}],
|
||||
}, options);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -636,8 +613,8 @@ export const buildGroup = function(group, options, baseOptions) {
|
||||
groupNode = makeSpan(options.sizingClasses(baseOptions),
|
||||
[groupNode], options);
|
||||
|
||||
const multiplier = options.sizeMultiplier /
|
||||
baseOptions.sizeMultiplier;
|
||||
const multiplier =
|
||||
options.sizeMultiplier / baseOptions.sizeMultiplier;
|
||||
|
||||
groupNode.height *= multiplier;
|
||||
groupNode.depth *= multiplier;
|
||||
|
Reference in New Issue
Block a user