File size: 4,391 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
// @flow
import defineFunction from "../defineFunction";
import buildCommon from "../buildCommon";
import mathMLTree from "../mathMLTree";
import delimiter from "../delimiter";
import Style from "../Style";
import {makeEm} from "../units";

import * as html from "../buildHTML";
import * as mml from "../buildMathML";

defineFunction({
    type: "sqrt",
    names: ["\\sqrt"],
    props: {
        numArgs: 1,
        numOptionalArgs: 1,
    },
    handler({parser}, args, optArgs) {
        const index = optArgs[0];
        const body = args[0];
        return {
            type: "sqrt",
            mode: parser.mode,
            body,
            index,
        };
    },
    htmlBuilder(group, options) {
        // Square roots are handled in the TeXbook pg. 443, Rule 11.

        // First, we do the same steps as in overline to build the inner group
        // and line
        let inner = html.buildGroup(group.body, options.havingCrampedStyle());
        if (inner.height === 0) {
            // Render a small surd.
            inner.height = options.fontMetrics().xHeight;
        }

        // Some groups can return document fragments.  Handle those by wrapping
        // them in a span.
        inner = buildCommon.wrapFragment(inner, options);

        // Calculate the minimum size for the \surd delimiter
        const metrics = options.fontMetrics();
        const theta = metrics.defaultRuleThickness;

        let phi = theta;
        if (options.style.id < Style.TEXT.id) {
            phi = options.fontMetrics().xHeight;
        }

        // Calculate the clearance between the body and line
        let lineClearance = theta + phi / 4;

        const minDelimiterHeight = (inner.height + inner.depth +
            lineClearance + theta);

        // Create a sqrt SVG of the required minimum size
        const {span: img, ruleWidth, advanceWidth} =
            delimiter.sqrtImage(minDelimiterHeight, options);

        const delimDepth = img.height - ruleWidth;

        // Adjust the clearance based on the delimiter size
        if (delimDepth > inner.height + inner.depth + lineClearance) {
            lineClearance =
                (lineClearance + delimDepth - inner.height - inner.depth) / 2;
        }

        // Shift the sqrt image
        const imgShift = img.height - inner.height - lineClearance - ruleWidth;

        inner.style.paddingLeft = makeEm(advanceWidth);

        // Overlay the image and the argument.
        const body = buildCommon.makeVList({
            positionType: "firstBaseline",
            children: [
                {type: "elem", elem: inner, wrapperClasses: ["svg-align"]},
                {type: "kern", size: -(inner.height + imgShift)},
                {type: "elem", elem: img},
                {type: "kern", size: ruleWidth},
            ],
        }, options);

        if (!group.index) {
            return buildCommon.makeSpan(["mord", "sqrt"], [body], options);
        } else {
            // Handle the optional root index

            // The index is always in scriptscript style
            const newOptions = options.havingStyle(Style.SCRIPTSCRIPT);
            const rootm = html.buildGroup(group.index, newOptions, options);

            // The amount the index is shifted by. This is taken from the TeX
            // source, in the definition of `\r@@t`.
            const toShift = 0.6 * (body.height - body.depth);

            // Build a VList with the superscript shifted up correctly
            const rootVList = buildCommon.makeVList({
                positionType: "shift",
                positionData: -toShift,
                children: [{type: "elem", elem: rootm}],
            }, options);
            // Add a class surrounding it so we can add on the appropriate
            // kerning
            const rootVListWrap = buildCommon.makeSpan(["root"], [rootVList]);

            return buildCommon.makeSpan(["mord", "sqrt"],
                [rootVListWrap, body], options);
        }
    },
    mathmlBuilder(group, options) {
        const {body, index} = group;
        return index ?
            new mathMLTree.MathNode(
                "mroot", [
                    mml.buildGroup(body, options),
                    mml.buildGroup(index, options),
                ]) :
            new mathMLTree.MathNode(
                "msqrt", [mml.buildGroup(body, options)]);
    },
});