mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-11 22:18:41 +00:00
extract accent and accentunder into their own files (#1048)
* extract accent and accentunder into their own files * cleanup how we apply custom styles to accentBody wrappers * address feedback from code review * fix typo * define a flow type for a CSS style * clone 'style' object passed to methods in domTree * forgot that we don't support object spread yet
This commit is contained in:
172
src/buildHTML.js
172
src/buildHTML.js
@@ -459,155 +459,6 @@ groupTypes.font = function(group, options) {
|
||||
return buildGroup(group.value.body, options.withFontFamily(font));
|
||||
};
|
||||
|
||||
groupTypes.accent = function(group, options) {
|
||||
// Accents are handled in the TeXbook pg. 443, rule 12.
|
||||
let base = group.value.base;
|
||||
|
||||
let supsubGroup;
|
||||
if (group.type === "supsub") {
|
||||
// If our base is a character box, and we have superscripts and
|
||||
// subscripts, the supsub will defer to us. In particular, we want
|
||||
// to attach the superscripts and subscripts to the inner body (so
|
||||
// that the position of the superscripts and subscripts won't be
|
||||
// affected by the height of the accent). We accomplish this by
|
||||
// sticking the base of the accent into the base of the supsub, and
|
||||
// rendering that, while keeping track of where the accent is.
|
||||
|
||||
// The supsub group is the group that was passed in
|
||||
const supsub = group;
|
||||
// The real accent group is the base of the supsub group
|
||||
group = supsub.value.base;
|
||||
// The character box is the base of the accent group
|
||||
base = group.value.base;
|
||||
// Stick the character box into the base of the supsub group
|
||||
supsub.value.base = base;
|
||||
|
||||
// Rerender the supsub group with its new base, and store that
|
||||
// result.
|
||||
supsubGroup = buildGroup(supsub, options);
|
||||
}
|
||||
|
||||
// Build the base group
|
||||
const body = buildGroup(base, options.havingCrampedStyle());
|
||||
|
||||
// Does the accent need to shift for the skew of a character?
|
||||
const mustShift = group.value.isShifty && utils.isCharacterBox(base);
|
||||
|
||||
// Calculate the skew of the accent. This is based on the line "If the
|
||||
// nucleus is not a single character, let s = 0; otherwise set s to the
|
||||
// kern amount for the nucleus followed by the \skewchar of its font."
|
||||
// Note that our skew metrics are just the kern between each character
|
||||
// and the skewchar.
|
||||
let skew = 0;
|
||||
if (mustShift) {
|
||||
// If the base is a character box, then we want the skew of the
|
||||
// innermost character. To do that, we find the innermost character:
|
||||
const baseChar = utils.getBaseElem(base);
|
||||
// Then, we render its group to get the symbol inside it
|
||||
const baseGroup = buildGroup(baseChar, options.havingCrampedStyle());
|
||||
// Finally, we pull the skew off of the symbol.
|
||||
skew = baseGroup.skew;
|
||||
// Note that we now throw away baseGroup, because the layers we
|
||||
// removed with getBaseElem might contain things like \color which
|
||||
// we can't get rid of.
|
||||
// TODO(emily): Find a better way to get the skew
|
||||
}
|
||||
|
||||
// calculate the amount of space between the body and the accent
|
||||
const clearance = Math.min(
|
||||
body.height,
|
||||
options.fontMetrics().xHeight);
|
||||
|
||||
// Build the accent
|
||||
let accentBody;
|
||||
if (!group.value.isStretchy) {
|
||||
let accent;
|
||||
if (group.value.label === "\\vec") {
|
||||
// Before version 0.9, \vec used the combining font glyph U+20D7.
|
||||
// But browsers, especially Safari, are not consistent in how they
|
||||
// render combining characters when not preceded by a character.
|
||||
// So now we use an SVG.
|
||||
// If Safari reforms, we should consider reverting to the glyph.
|
||||
accent = buildCommon.staticSvg("vec", options);
|
||||
accent.width = parseFloat(accent.style.width);
|
||||
} else {
|
||||
accent = buildCommon.makeSymbol(
|
||||
group.value.label, "Main-Regular", group.mode, options);
|
||||
}
|
||||
// Remove the italic correction of the accent, because it only serves to
|
||||
// shift the accent over to a place we don't want.
|
||||
accent.italic = 0;
|
||||
|
||||
accentBody = makeSpan(["accent-body"], [accent]);
|
||||
|
||||
// CSS defines `.katex .accent .accent-body { width: 0 }`
|
||||
// so that the accent doesn't contribute to the bounding box.
|
||||
// We need to shift the character by its width (effectively half
|
||||
// its width) to compensate.
|
||||
let left = -accent.width / 2;
|
||||
|
||||
// Shift the accent over by the skew.
|
||||
left += skew;
|
||||
|
||||
// The \H character that the fonts use is a combining character, and
|
||||
// thus shows up much too far to the left. To account for this, we add
|
||||
// a manual shift of the width of one space.
|
||||
// TODO(emily): Fix this in a better way, like by changing the font
|
||||
if (group.value.label === '\\H') {
|
||||
left += 0.5; // twice width of space, or width of accent
|
||||
}
|
||||
|
||||
accentBody.style.left = left + "em";
|
||||
|
||||
accentBody = buildCommon.makeVList({
|
||||
positionType: "firstBaseline",
|
||||
children: [
|
||||
{type: "elem", elem: body},
|
||||
{type: "kern", size: -clearance},
|
||||
{type: "elem", elem: accentBody},
|
||||
],
|
||||
}, options);
|
||||
|
||||
} else {
|
||||
accentBody = stretchy.svgSpan(group, options);
|
||||
|
||||
accentBody = buildCommon.makeVList({
|
||||
positionType: "firstBaseline",
|
||||
children: [
|
||||
{type: "elem", elem: body},
|
||||
{type: "elem", elem: accentBody},
|
||||
],
|
||||
}, options);
|
||||
|
||||
const styleSpan = accentBody.children[0].children[0].children[1];
|
||||
styleSpan.classes.push("svg-align"); // text-align: left;
|
||||
if (skew > 0) {
|
||||
// Shorten the accent and nudge it to the right.
|
||||
styleSpan.style.width = `calc(100% - ${2 * skew}em)`;
|
||||
styleSpan.style.marginLeft = (2 * skew) + "em";
|
||||
}
|
||||
}
|
||||
|
||||
const accentWrap = makeSpan(["mord", "accent"], [accentBody], options);
|
||||
|
||||
if (supsubGroup) {
|
||||
// Here, we replace the "base" child of the supsub with our newly
|
||||
// generated accent.
|
||||
supsubGroup.children[0] = accentWrap;
|
||||
|
||||
// Since we don't rerun the height calculation after replacing the
|
||||
// accent, we manually recalculate height.
|
||||
supsubGroup.height = Math.max(accentWrap.height, supsubGroup.height);
|
||||
|
||||
// Accents should always be ords, even when their innards are not.
|
||||
supsubGroup.classes[0] = "mord";
|
||||
|
||||
return supsubGroup;
|
||||
} else {
|
||||
return accentWrap;
|
||||
}
|
||||
};
|
||||
|
||||
groupTypes.horizBrace = function(group, options) {
|
||||
const style = options.style;
|
||||
|
||||
@@ -700,29 +551,6 @@ groupTypes.horizBrace = function(group, options) {
|
||||
[vlist], options);
|
||||
};
|
||||
|
||||
groupTypes.accentUnder = function(group, options) {
|
||||
// Treat under accents much like underlines.
|
||||
const innerGroup = buildGroup(group.value.base, options);
|
||||
|
||||
const accentBody = stretchy.svgSpan(group, options);
|
||||
const kern = (/tilde/.test(group.value.label) ? 0.12 : 0);
|
||||
|
||||
// Generate the vlist, with the appropriate kerns
|
||||
const vlist = buildCommon.makeVList({
|
||||
positionType: "bottom",
|
||||
positionData: accentBody.height + kern,
|
||||
children: [
|
||||
{type: "elem", elem: accentBody},
|
||||
{type: "kern", size: kern},
|
||||
{type: "elem", elem: innerGroup},
|
||||
],
|
||||
}, options);
|
||||
|
||||
vlist.children[0].children[0].children[0].classes.push("svg-align");
|
||||
|
||||
return makeSpan(["mord", "accentunder"], [vlist], options);
|
||||
};
|
||||
|
||||
groupTypes.xArrow = function(group, options) {
|
||||
const style = options.style;
|
||||
|
||||
|
Reference in New Issue
Block a user