1 |
|
2 | import defineFunction from "../defineFunction";
|
3 | import buildCommon from "../buildCommon";
|
4 | import mathMLTree from "../mathMLTree";
|
5 | import delimiter from "../delimiter";
|
6 | import Style from "../Style";
|
7 |
|
8 | import * as html from "../buildHTML";
|
9 | import * as mml from "../buildMathML";
|
10 |
|
11 | defineFunction({
|
12 | type: "sqrt",
|
13 | names: ["\\sqrt"],
|
14 | props: {
|
15 | numArgs: 1,
|
16 | numOptionalArgs: 1,
|
17 | },
|
18 | handler({parser}, args, optArgs) {
|
19 | const index = optArgs[0];
|
20 | const body = args[0];
|
21 | return {
|
22 | type: "sqrt",
|
23 | mode: parser.mode,
|
24 | body,
|
25 | index,
|
26 | };
|
27 | },
|
28 | htmlBuilder(group, options) {
|
29 |
|
30 |
|
31 |
|
32 |
|
33 | let inner = html.buildGroup(group.body, options.havingCrampedStyle());
|
34 | if (inner.height === 0) {
|
35 |
|
36 | inner.height = options.fontMetrics().xHeight;
|
37 | }
|
38 |
|
39 |
|
40 |
|
41 | inner = buildCommon.wrapFragment(inner, options);
|
42 |
|
43 |
|
44 | const metrics = options.fontMetrics();
|
45 | const theta = metrics.defaultRuleThickness;
|
46 |
|
47 | let phi = theta;
|
48 | if (options.style.id < Style.TEXT.id) {
|
49 | phi = options.fontMetrics().xHeight;
|
50 | }
|
51 |
|
52 |
|
53 | let lineClearance = theta + phi / 4;
|
54 |
|
55 | const minDelimiterHeight = (inner.height + inner.depth +
|
56 | lineClearance + theta);
|
57 |
|
58 |
|
59 | const {span: img, ruleWidth, advanceWidth} =
|
60 | delimiter.sqrtImage(minDelimiterHeight, options);
|
61 |
|
62 | const delimDepth = img.height - ruleWidth;
|
63 |
|
64 |
|
65 | if (delimDepth > inner.height + inner.depth + lineClearance) {
|
66 | lineClearance =
|
67 | (lineClearance + delimDepth - inner.height - inner.depth) / 2;
|
68 | }
|
69 |
|
70 |
|
71 | const imgShift = img.height - inner.height - lineClearance - ruleWidth;
|
72 |
|
73 | inner.style.paddingLeft = advanceWidth + "em";
|
74 |
|
75 |
|
76 | const body = buildCommon.makeVList({
|
77 | positionType: "firstBaseline",
|
78 | children: [
|
79 | {type: "elem", elem: inner, wrapperClasses: ["svg-align"]},
|
80 | {type: "kern", size: -(inner.height + imgShift)},
|
81 | {type: "elem", elem: img},
|
82 | {type: "kern", size: ruleWidth},
|
83 | ],
|
84 | }, options);
|
85 |
|
86 | if (!group.index) {
|
87 | return buildCommon.makeSpan(["mord", "sqrt"], [body], options);
|
88 | } else {
|
89 |
|
90 |
|
91 |
|
92 | const newOptions = options.havingStyle(Style.SCRIPTSCRIPT);
|
93 | const rootm = html.buildGroup(group.index, newOptions, options);
|
94 |
|
95 |
|
96 |
|
97 | const toShift = 0.6 * (body.height - body.depth);
|
98 |
|
99 |
|
100 | const rootVList = buildCommon.makeVList({
|
101 | positionType: "shift",
|
102 | positionData: -toShift,
|
103 | children: [{type: "elem", elem: rootm}],
|
104 | }, options);
|
105 |
|
106 |
|
107 | const rootVListWrap = buildCommon.makeSpan(["root"], [rootVList]);
|
108 |
|
109 | return buildCommon.makeSpan(["mord", "sqrt"],
|
110 | [rootVListWrap, body], options);
|
111 | }
|
112 | },
|
113 | mathmlBuilder(group, options) {
|
114 | const {body, index} = group;
|
115 | return index ?
|
116 | new mathMLTree.MathNode(
|
117 | "mroot", [
|
118 | mml.buildGroup(body, options),
|
119 | mml.buildGroup(index, options),
|
120 | ]) :
|
121 | new mathMLTree.MathNode(
|
122 | "msqrt", [mml.buildGroup(body, options)]);
|
123 | },
|
124 | });
|