File size: 4,419 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
// @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);
};