// @flow import buildCommon from "../../buildCommon"; import * as html from "../../buildHTML"; import utils from "../../utils"; import type {StyleInterface} from "../../Style"; import type Options from "../../Options"; import type {DomSpan, SymbolNode} from "../../domTree"; import type {AnyParseNode} from "../../parseNode"; import {makeEm} from "../../units"; // For an operator with limits, assemble the base, sup, and sub into a span. export const assembleSupSub = ( base: DomSpan | SymbolNode, supGroup: ?AnyParseNode, subGroup: ?AnyParseNode, options: Options, style: StyleInterface, slant: number, baseShift: number, ): DomSpan => { base = buildCommon.makeSpan([], [base]); const subIsSingleCharacter = subGroup && utils.isCharacterBox(subGroup); let sub; let sup; // We manually have to handle the superscripts and subscripts. This, // aside from the kern calculations, is copied from supsub. if (supGroup) { const elem = html.buildGroup( supGroup, options.havingStyle(style.sup()), options); sup = { elem, kern: Math.max( options.fontMetrics().bigOpSpacing1, options.fontMetrics().bigOpSpacing3 - elem.depth), }; } if (subGroup) { const elem = html.buildGroup( subGroup, options.havingStyle(style.sub()), options); sub = { elem, kern: Math.max( options.fontMetrics().bigOpSpacing2, options.fontMetrics().bigOpSpacing4 - elem.height), }; } // Build the final group as a vlist of the possible subscript, base, // and possible superscript. let finalGroup; if (sup && sub) { const bottom = options.fontMetrics().bigOpSpacing5 + sub.elem.height + sub.elem.depth + sub.kern + base.depth + baseShift; finalGroup = buildCommon.makeVList({ positionType: "bottom", positionData: bottom, children: [ {type: "kern", size: options.fontMetrics().bigOpSpacing5}, {type: "elem", elem: sub.elem, marginLeft: makeEm(-slant)}, {type: "kern", size: sub.kern}, {type: "elem", elem: base}, {type: "kern", size: sup.kern}, {type: "elem", elem: sup.elem, marginLeft: makeEm(slant)}, {type: "kern", size: options.fontMetrics().bigOpSpacing5}, ], }, options); } else if (sub) { const top = base.height - baseShift; // Shift the limits by the slant of the symbol. Note // that we are supposed to shift the limits by 1/2 of the slant, // but since we are centering the limits adding a full slant of // margin will shift by 1/2 that. finalGroup = buildCommon.makeVList({ positionType: "top", positionData: top, children: [ {type: "kern", size: options.fontMetrics().bigOpSpacing5}, {type: "elem", elem: sub.elem, marginLeft: makeEm(-slant)}, {type: "kern", size: sub.kern}, {type: "elem", elem: base}, ], }, options); } else if (sup) { const bottom = base.depth + baseShift; finalGroup = buildCommon.makeVList({ positionType: "bottom", positionData: bottom, children: [ {type: "elem", elem: base}, {type: "kern", size: sup.kern}, {type: "elem", elem: sup.elem, marginLeft: makeEm(slant)}, {type: "kern", size: options.fontMetrics().bigOpSpacing5}, ], }, options); } else { // This case probably shouldn't occur (this would mean the // supsub was sending us a group with no superscript or // subscript) but be safe. return base; } const parts = [finalGroup]; if (sub && slant !== 0 && !subIsSingleCharacter) { // A negative margin-left was applied to the lower limit. // Avoid an overlap by placing a spacer on the left on the group. const spacer = buildCommon.makeSpan(["mspace"], [], options); spacer.style.marginRight = makeEm(slant); parts.unshift(spacer); } return buildCommon.makeSpan(["mop", "op-limits"], parts, options); };