You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
125 lines
4.3 KiB
125 lines
4.3 KiB
// @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)]); |
|
}, |
|
});
|
|
|