DuyTa's picture
Upload folder using huggingface_hub
bc20498 verified
// @flow
import {defineFunctionBuilders} from "../defineFunction";
import buildCommon from "../buildCommon";
import {SymbolNode} from "../domTree";
import mathMLTree from "../mathMLTree";
import utils from "../utils";
import {makeEm} from "../units";
import Style from "../Style";
import * as html from "../buildHTML";
import * as mml from "../buildMathML";
import * as accent from "./accent";
import * as horizBrace from "./horizBrace";
import * as op from "./op";
import * as operatorname from "./operatorname";
import type Options from "../Options";
import type {ParseNode} from "../parseNode";
import type {HtmlBuilder} from "../defineFunction";
import type {MathNodeType} from "../mathMLTree";
/**
* Sometimes, groups perform special rules when they have superscripts or
* subscripts attached to them. This function lets the `supsub` group know that
* Sometimes, groups perform special rules when they have superscripts or
* its inner element should handle the superscripts and subscripts instead of
* handling them itself.
*/
const htmlBuilderDelegate = function(
group: ParseNode<"supsub">,
options: Options,
): ?HtmlBuilder<*> {
const base = group.base;
if (!base) {
return null;
} else if (base.type === "op") {
// Operators handle supsubs differently when they have limits
// (e.g. `\displaystyle\sum_2^3`)
const delegate = base.limits &&
(options.style.size === Style.DISPLAY.size ||
base.alwaysHandleSupSub);
return delegate ? op.htmlBuilder : null;
} else if (base.type === "operatorname") {
const delegate = base.alwaysHandleSupSub &&
(options.style.size === Style.DISPLAY.size || base.limits);
return delegate ? operatorname.htmlBuilder : null;
} else if (base.type === "accent") {
return utils.isCharacterBox(base.base) ? accent.htmlBuilder : null;
} else if (base.type === "horizBrace") {
const isSup = !group.sub;
return isSup === base.isOver ? horizBrace.htmlBuilder : null;
} else {
return null;
}
};
// Super scripts and subscripts, whose precise placement can depend on other
// functions that precede them.
defineFunctionBuilders({
type: "supsub",
htmlBuilder(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.
const builderDelegate = htmlBuilderDelegate(group, options);
if (builderDelegate) {
return builderDelegate(group, options);
}
const {base: valueBase, sup: valueSup, sub: valueSub} = group;
const base = html.buildGroup(valueBase, options);
let supm;
let subm;
const metrics = options.fontMetrics();
// Rule 18a
let supShift = 0;
let subShift = 0;
const isCharacterBox = valueBase && utils.isCharacterBox(valueBase);
if (valueSup) {
const newOptions = options.havingStyle(options.style.sup());
supm = html.buildGroup(valueSup, newOptions, options);
if (!isCharacterBox) {
supShift = base.height - newOptions.fontMetrics().supDrop
* newOptions.sizeMultiplier / options.sizeMultiplier;
}
}
if (valueSub) {
const newOptions = options.havingStyle(options.style.sub());
subm = html.buildGroup(valueSub, newOptions, options);
if (!isCharacterBox) {
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 for use as the marginRight.
const multiplier = options.sizeMultiplier;
const marginRight = makeEm((0.5 / metrics.ptPerEm) / multiplier);
let marginLeft = null;
if (subm) {
// 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.
const isOiint =
group.base && group.base.type === "op" && group.base.name &&
(group.base.name === "\\oiint" || group.base.name === "\\oiiint");
if (base instanceof SymbolNode || isOiint) {
// $FlowFixMe
marginLeft = makeEm(-base.italic);
}
}
let supsub;
if (supm && subm) {
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,
marginLeft},
{type: "elem", elem: supm, shift: -supShift, marginRight},
];
supsub = buildCommon.makeVList({
positionType: "individualShift",
children: vlistElem,
}, options);
} else if (subm) {
// Rule 18b
subShift = Math.max(
subShift, metrics.sub1,
subm.height - 0.8 * metrics.xHeight);
const vlistElem =
[{type: "elem", elem: subm, marginLeft, marginRight}];
supsub = buildCommon.makeVList({
positionType: "shift",
positionData: subShift,
children: vlistElem,
}, options);
} else if (supm) {
// 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 {
throw new Error("supsub must have either sup or sub.");
}
// Wrap the supsub vlist in a span.msupsub to reset text-align.
const mclass = html.getTypeOfDomTree(base, "right") || "mord";
return buildCommon.makeSpan([mclass],
[base, buildCommon.makeSpan(["msupsub"], [supsub])],
options);
},
mathmlBuilder(group, options) {
// Is the inner group a relevant horizonal brace?
let isBrace = false;
let isOver;
let isSup;
if (group.base && group.base.type === "horizBrace") {
isSup = !!group.sup;
if (isSup === group.base.isOver) {
isBrace = true;
isOver = group.base.isOver;
}
}
if (group.base &&
(group.base.type === "op" || group.base.type === "operatorname")) {
group.base.parentIsSupSub = true;
}
const children = [mml.buildGroup(group.base, options)];
if (group.sub) {
children.push(mml.buildGroup(group.sub, options));
}
if (group.sup) {
children.push(mml.buildGroup(group.sup, options));
}
let nodeType: MathNodeType;
if (isBrace) {
nodeType = (isOver ? "mover" : "munder");
} else if (!group.sub) {
const base = group.base;
if (base && base.type === "op" && base.limits &&
(options.style === Style.DISPLAY || base.alwaysHandleSupSub)) {
nodeType = "mover";
} else if (base && base.type === "operatorname" &&
base.alwaysHandleSupSub &&
(base.limits || options.style === Style.DISPLAY)) {
nodeType = "mover";
} else {
nodeType = "msup";
}
} else if (!group.sup) {
const base = group.base;
if (base && base.type === "op" && base.limits &&
(options.style === Style.DISPLAY || base.alwaysHandleSupSub)) {
nodeType = "munder";
} else if (base && base.type === "operatorname" &&
base.alwaysHandleSupSub &&
(base.limits || options.style === Style.DISPLAY)) {
nodeType = "munder";
} else {
nodeType = "msub";
}
} else {
const base = group.base;
if (base && base.type === "op" && base.limits &&
options.style === Style.DISPLAY) {
nodeType = "munderover";
} else if (base && base.type === "operatorname" &&
base.alwaysHandleSupSub &&
(options.style === Style.DISPLAY || base.limits)) {
nodeType = "munderover";
} else {
nodeType = "msubsup";
}
}
return new mathMLTree.MathNode(nodeType, children);
},
});