mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-08 12:38:39 +00:00
extract overline, underline, and rule into their own files (#999)
This commit is contained in:
@@ -9,6 +9,7 @@ import domTree from "./domTree";
|
|||||||
import fontMetrics from "./fontMetrics";
|
import fontMetrics from "./fontMetrics";
|
||||||
import symbols from "./symbols";
|
import symbols from "./symbols";
|
||||||
import utils from "./utils";
|
import utils from "./utils";
|
||||||
|
import stretchy from "./stretchy";
|
||||||
|
|
||||||
import type Options from "./Options";
|
import type Options from "./Options";
|
||||||
import type ParseNode from "./ParseNode";
|
import type ParseNode from "./ParseNode";
|
||||||
@@ -274,6 +275,19 @@ const makeSpan = function(
|
|||||||
return span;
|
return span;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const makeLineSpan = function(
|
||||||
|
className: string,
|
||||||
|
options: Options,
|
||||||
|
) {
|
||||||
|
// Fill the entire span instead of just a border. That way, the min-height
|
||||||
|
// value in katex.less will ensure that at least one screen pixel displays.
|
||||||
|
const line = stretchy.ruleSpan(className, options);
|
||||||
|
line.height = options.fontMetrics().defaultRuleThickness;
|
||||||
|
line.style.height = line.height + "em";
|
||||||
|
line.maxFontSize = 1.0;
|
||||||
|
return line;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes an anchor with the given href, list of classes, list of children,
|
* Makes an anchor with the given href, list of classes, list of children,
|
||||||
* and options.
|
* and options.
|
||||||
@@ -612,6 +626,7 @@ export default {
|
|||||||
makeSymbol,
|
makeSymbol,
|
||||||
mathsym,
|
mathsym,
|
||||||
makeSpan,
|
makeSpan,
|
||||||
|
makeLineSpan,
|
||||||
makeAnchor,
|
makeAnchor,
|
||||||
makeFragment,
|
makeFragment,
|
||||||
makeVList,
|
makeVList,
|
||||||
|
@@ -474,63 +474,6 @@ groupTypes.spacing = function(group, options) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const makeLineSpan = function(className, options, thickness) {
|
|
||||||
// Fill the entire span instead of just a border. That way, the min-height
|
|
||||||
// value in katex.less will ensure that at least one screen pixel displays.
|
|
||||||
const line = stretchy.ruleSpan(className, options);
|
|
||||||
line.height = thickness || options.fontMetrics().defaultRuleThickness;
|
|
||||||
line.style.height = line.height + "em";
|
|
||||||
line.maxFontSize = 1.0;
|
|
||||||
return line;
|
|
||||||
};
|
|
||||||
|
|
||||||
groupTypes.overline = function(group, options) {
|
|
||||||
// Overlines are handled in the TeXbook pg 443, Rule 9.
|
|
||||||
|
|
||||||
// Build the inner group in the cramped style.
|
|
||||||
const innerGroup = buildGroup(group.value.body,
|
|
||||||
options.havingCrampedStyle());
|
|
||||||
|
|
||||||
// Create the line above the body
|
|
||||||
const line = makeLineSpan("overline-line", options);
|
|
||||||
|
|
||||||
// Generate the vlist, with the appropriate kerns
|
|
||||||
const vlist = buildCommon.makeVList({
|
|
||||||
positionType: "firstBaseline",
|
|
||||||
children: [
|
|
||||||
{type: "elem", elem: innerGroup},
|
|
||||||
{type: "kern", size: 3 * line.height},
|
|
||||||
{type: "elem", elem: line},
|
|
||||||
{type: "kern", size: line.height},
|
|
||||||
],
|
|
||||||
}, options);
|
|
||||||
|
|
||||||
return makeSpan(["mord", "overline"], [vlist], options);
|
|
||||||
};
|
|
||||||
|
|
||||||
groupTypes.underline = function(group, options) {
|
|
||||||
// Underlines are handled in the TeXbook pg 443, Rule 10.
|
|
||||||
// Build the inner group.
|
|
||||||
const innerGroup = buildGroup(group.value.body, options);
|
|
||||||
|
|
||||||
// Create the line above the body
|
|
||||||
const line = makeLineSpan("underline-line", options);
|
|
||||||
|
|
||||||
// Generate the vlist, with the appropriate kerns
|
|
||||||
const vlist = buildCommon.makeVList({
|
|
||||||
positionType: "top",
|
|
||||||
positionData: innerGroup.height,
|
|
||||||
children: [
|
|
||||||
{type: "kern", size: line.height},
|
|
||||||
{type: "elem", elem: line},
|
|
||||||
{type: "kern", size: 3 * line.height},
|
|
||||||
{type: "elem", elem: innerGroup},
|
|
||||||
],
|
|
||||||
}, options);
|
|
||||||
|
|
||||||
return makeSpan(["mord", "underline"], [vlist], options);
|
|
||||||
};
|
|
||||||
|
|
||||||
groupTypes.sqrt = function(group, options) {
|
groupTypes.sqrt = function(group, options) {
|
||||||
// Square roots are handled in the TeXbook pg. 443, Rule 11.
|
// Square roots are handled in the TeXbook pg. 443, Rule 11.
|
||||||
|
|
||||||
@@ -705,36 +648,6 @@ groupTypes.verb = function(group, options) {
|
|||||||
body, newOptions);
|
body, newOptions);
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.rule = function(group, options) {
|
|
||||||
// Make an empty span for the rule
|
|
||||||
const rule = makeSpan(["mord", "rule"], [], options);
|
|
||||||
|
|
||||||
// Calculate the shift, width, and height of the rule, and account for units
|
|
||||||
let shift = 0;
|
|
||||||
if (group.value.shift) {
|
|
||||||
shift = calculateSize(group.value.shift, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
const width = calculateSize(group.value.width, options);
|
|
||||||
const height = calculateSize(group.value.height, options);
|
|
||||||
|
|
||||||
// Style the rule to the right size
|
|
||||||
rule.style.borderRightWidth = width + "em";
|
|
||||||
rule.style.borderTopWidth = height + "em";
|
|
||||||
rule.style.bottom = shift + "em";
|
|
||||||
|
|
||||||
// Record the height and width
|
|
||||||
rule.width = width;
|
|
||||||
rule.height = height + shift;
|
|
||||||
rule.depth = -shift;
|
|
||||||
// Font size is the number large enough that the browser will
|
|
||||||
// reserve at least `absHeight` space above the baseline.
|
|
||||||
// The 1.125 factor was empirically determined
|
|
||||||
rule.maxFontSize = height * 1.125 * options.sizeMultiplier;
|
|
||||||
|
|
||||||
return rule;
|
|
||||||
};
|
|
||||||
|
|
||||||
groupTypes.accent = function(group, options) {
|
groupTypes.accent = function(group, options) {
|
||||||
// Accents are handled in the TeXbook pg. 443, rule 12.
|
// Accents are handled in the TeXbook pg. 443, rule 12.
|
||||||
let base = group.value.base;
|
let base = group.value.base;
|
||||||
|
@@ -359,32 +359,6 @@ groupTypes.verb = function(group, options) {
|
|||||||
return node;
|
return node;
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.overline = function(group, options) {
|
|
||||||
const operator = new mathMLTree.MathNode(
|
|
||||||
"mo", [new mathMLTree.TextNode("\u203e")]);
|
|
||||||
operator.setAttribute("stretchy", "true");
|
|
||||||
|
|
||||||
const node = new mathMLTree.MathNode(
|
|
||||||
"mover",
|
|
||||||
[buildGroup(group.value.body, options), operator]);
|
|
||||||
node.setAttribute("accent", "true");
|
|
||||||
|
|
||||||
return node;
|
|
||||||
};
|
|
||||||
|
|
||||||
groupTypes.underline = function(group, options) {
|
|
||||||
const operator = new mathMLTree.MathNode(
|
|
||||||
"mo", [new mathMLTree.TextNode("\u203e")]);
|
|
||||||
operator.setAttribute("stretchy", "true");
|
|
||||||
|
|
||||||
const node = new mathMLTree.MathNode(
|
|
||||||
"munder",
|
|
||||||
[buildGroup(group.value.body, options), operator]);
|
|
||||||
node.setAttribute("accentunder", "true");
|
|
||||||
|
|
||||||
return node;
|
|
||||||
};
|
|
||||||
|
|
||||||
groupTypes.accentUnder = function(group, options) {
|
groupTypes.accentUnder = function(group, options) {
|
||||||
const accentNode = stretchy.mathMLnode(group.value.label);
|
const accentNode = stretchy.mathMLnode(group.value.label);
|
||||||
const node = new mathMLTree.MathNode(
|
const node = new mathMLTree.MathNode(
|
||||||
@@ -460,14 +434,6 @@ groupTypes.xArrow = function(group, options) {
|
|||||||
return node;
|
return node;
|
||||||
};
|
};
|
||||||
|
|
||||||
groupTypes.rule = function(group) {
|
|
||||||
// TODO(emily): Figure out if there's an actual way to draw black boxes
|
|
||||||
// in MathML.
|
|
||||||
const node = new mathMLTree.MathNode("mrow");
|
|
||||||
|
|
||||||
return node;
|
|
||||||
};
|
|
||||||
|
|
||||||
groupTypes.mclass = function(group, options) {
|
groupTypes.mclass = function(group, options) {
|
||||||
const inner = buildExpression(group.value.value, options);
|
const inner = buildExpression(group.value.value, options);
|
||||||
return new mathMLTree.MathNode("mstyle", inner);
|
return new mathMLTree.MathNode("mstyle", inner);
|
||||||
|
@@ -59,6 +59,7 @@ class span implements CombinableDomNode {
|
|||||||
children: DomChildNode[];
|
children: DomChildNode[];
|
||||||
height: number;
|
height: number;
|
||||||
depth: number;
|
depth: number;
|
||||||
|
width: ?number;
|
||||||
maxFontSize: number;
|
maxFontSize: number;
|
||||||
style: {[string]: string};
|
style: {[string]: string};
|
||||||
attributes: {[string]: string};
|
attributes: {[string]: string};
|
||||||
|
@@ -124,44 +124,11 @@ defineFunction(["\\fcolorbox"], {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// An overline
|
import "./functions/overline";
|
||||||
defineFunction(["\\overline"], {
|
|
||||||
numArgs: 1,
|
|
||||||
}, function(context, args) {
|
|
||||||
const body = args[0];
|
|
||||||
return {
|
|
||||||
type: "overline",
|
|
||||||
body: body,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// An underline
|
import "./functions/underline";
|
||||||
defineFunction(["\\underline"], {
|
|
||||||
numArgs: 1,
|
|
||||||
}, function(context, args) {
|
|
||||||
const body = args[0];
|
|
||||||
return {
|
|
||||||
type: "underline",
|
|
||||||
body: body,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// A box of the width and height
|
import "./functions/rule";
|
||||||
defineFunction(["\\rule"], {
|
|
||||||
numArgs: 2,
|
|
||||||
numOptionalArgs: 1,
|
|
||||||
argTypes: ["size", "size", "size"],
|
|
||||||
}, function(context, args, optArgs) {
|
|
||||||
const shift = optArgs[0];
|
|
||||||
const width = args[0];
|
|
||||||
const height = args[1];
|
|
||||||
return {
|
|
||||||
type: "rule",
|
|
||||||
shift: shift && shift.value,
|
|
||||||
width: width.value,
|
|
||||||
height: height.value,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
import "./functions/kern";
|
import "./functions/kern";
|
||||||
|
|
||||||
|
@@ -93,7 +93,7 @@ defineFunction({
|
|||||||
let ruleWidth;
|
let ruleWidth;
|
||||||
let ruleSpacing;
|
let ruleSpacing;
|
||||||
if (group.value.hasBarLine) {
|
if (group.value.hasBarLine) {
|
||||||
rule = html.makeLineSpan("frac-line", options);
|
rule = buildCommon.makeLineSpan("frac-line", options);
|
||||||
ruleWidth = rule.height;
|
ruleWidth = rule.height;
|
||||||
ruleSpacing = rule.height;
|
ruleSpacing = rule.height;
|
||||||
} else {
|
} else {
|
||||||
|
57
src/functions/overline.js
Normal file
57
src/functions/overline.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// @flow
|
||||||
|
import defineFunction from "../defineFunction";
|
||||||
|
import buildCommon from "../buildCommon";
|
||||||
|
import mathMLTree from "../mathMLTree";
|
||||||
|
|
||||||
|
import * as html from "../buildHTML";
|
||||||
|
import * as mml from "../buildMathML";
|
||||||
|
|
||||||
|
defineFunction({
|
||||||
|
type: "overline",
|
||||||
|
names: ["\\overline"],
|
||||||
|
props: {
|
||||||
|
numArgs: 1,
|
||||||
|
},
|
||||||
|
handler(context, args) {
|
||||||
|
const body = args[0];
|
||||||
|
return {
|
||||||
|
type: "overline",
|
||||||
|
body: body,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
htmlBuilder(group, options) {
|
||||||
|
// Overlines are handled in the TeXbook pg 443, Rule 9.
|
||||||
|
|
||||||
|
// Build the inner group in the cramped style.
|
||||||
|
const innerGroup = html.buildGroup(group.value.body,
|
||||||
|
options.havingCrampedStyle());
|
||||||
|
|
||||||
|
// Create the line above the body
|
||||||
|
const line = buildCommon.makeLineSpan("overline-line", options);
|
||||||
|
|
||||||
|
// Generate the vlist, with the appropriate kerns
|
||||||
|
const vlist = buildCommon.makeVList({
|
||||||
|
positionType: "firstBaseline",
|
||||||
|
children: [
|
||||||
|
{type: "elem", elem: innerGroup},
|
||||||
|
{type: "kern", size: 3 * line.height},
|
||||||
|
{type: "elem", elem: line},
|
||||||
|
{type: "kern", size: line.height},
|
||||||
|
],
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
return buildCommon.makeSpan(["mord", "overline"], [vlist], options);
|
||||||
|
},
|
||||||
|
mathmlBuilder(group, options) {
|
||||||
|
const operator = new mathMLTree.MathNode(
|
||||||
|
"mo", [new mathMLTree.TextNode("\u203e")]);
|
||||||
|
operator.setAttribute("stretchy", "true");
|
||||||
|
|
||||||
|
const node = new mathMLTree.MathNode(
|
||||||
|
"mover",
|
||||||
|
[mml.buildGroup(group.value.body, options), operator]);
|
||||||
|
node.setAttribute("accent", "true");
|
||||||
|
|
||||||
|
return node;
|
||||||
|
},
|
||||||
|
});
|
62
src/functions/rule.js
Normal file
62
src/functions/rule.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
// @flow
|
||||||
|
import buildCommon from "../buildCommon";
|
||||||
|
import defineFunction from "../defineFunction";
|
||||||
|
import mathMLTree from "../mathMLTree";
|
||||||
|
import {calculateSize} from "../units";
|
||||||
|
|
||||||
|
defineFunction({
|
||||||
|
type: "rule",
|
||||||
|
names: ["\\rule"],
|
||||||
|
props: {
|
||||||
|
numArgs: 2,
|
||||||
|
numOptionalArgs: 1,
|
||||||
|
argTypes: ["size", "size", "size"],
|
||||||
|
},
|
||||||
|
handler(context, args, optArgs) {
|
||||||
|
const shift = optArgs[0];
|
||||||
|
const width = args[0];
|
||||||
|
const height = args[1];
|
||||||
|
return {
|
||||||
|
type: "rule",
|
||||||
|
shift: shift && shift.value,
|
||||||
|
width: width.value,
|
||||||
|
height: height.value,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
htmlBuilder(group, options) {
|
||||||
|
// Make an empty span for the rule
|
||||||
|
const rule = buildCommon.makeSpan(["mord", "rule"], [], options);
|
||||||
|
|
||||||
|
// Calculate the shift, width, and height of the rule, and account for units
|
||||||
|
let shift = 0;
|
||||||
|
if (group.value.shift) {
|
||||||
|
shift = calculateSize(group.value.shift, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
const width = calculateSize(group.value.width, options);
|
||||||
|
const height = calculateSize(group.value.height, options);
|
||||||
|
|
||||||
|
// Style the rule to the right size
|
||||||
|
rule.style.borderRightWidth = width + "em";
|
||||||
|
rule.style.borderTopWidth = height + "em";
|
||||||
|
rule.style.bottom = shift + "em";
|
||||||
|
|
||||||
|
// Record the height and width
|
||||||
|
rule.width = width;
|
||||||
|
rule.height = height + shift;
|
||||||
|
rule.depth = -shift;
|
||||||
|
// Font size is the number large enough that the browser will
|
||||||
|
// reserve at least `absHeight` space above the baseline.
|
||||||
|
// The 1.125 factor was empirically determined
|
||||||
|
rule.maxFontSize = height * 1.125 * options.sizeMultiplier;
|
||||||
|
|
||||||
|
return rule;
|
||||||
|
},
|
||||||
|
mathmlBuilder(group, options) {
|
||||||
|
// TODO(emily): Figure out if there's an actual way to draw black boxes
|
||||||
|
// in MathML.
|
||||||
|
const node = new mathMLTree.MathNode("mrow");
|
||||||
|
|
||||||
|
return node;
|
||||||
|
},
|
||||||
|
});
|
56
src/functions/underline.js
Normal file
56
src/functions/underline.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// @flow
|
||||||
|
import defineFunction from "../defineFunction";
|
||||||
|
import buildCommon from "../buildCommon";
|
||||||
|
import mathMLTree from "../mathMLTree";
|
||||||
|
|
||||||
|
import * as html from "../buildHTML";
|
||||||
|
import * as mml from "../buildMathML";
|
||||||
|
|
||||||
|
defineFunction({
|
||||||
|
type: "underline",
|
||||||
|
names: ["\\underline"],
|
||||||
|
props: {
|
||||||
|
numArgs: 1,
|
||||||
|
},
|
||||||
|
handler(context, args) {
|
||||||
|
const body = args[0];
|
||||||
|
return {
|
||||||
|
type: "underline",
|
||||||
|
body: body,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
htmlBuilder(group, options) {
|
||||||
|
// Underlines are handled in the TeXbook pg 443, Rule 10.
|
||||||
|
// Build the inner group.
|
||||||
|
const innerGroup = html.buildGroup(group.value.body, options);
|
||||||
|
|
||||||
|
// Create the line above the body
|
||||||
|
const line = buildCommon.makeLineSpan("underline-line", options);
|
||||||
|
|
||||||
|
// Generate the vlist, with the appropriate kerns
|
||||||
|
const vlist = buildCommon.makeVList({
|
||||||
|
positionType: "top",
|
||||||
|
positionData: innerGroup.height,
|
||||||
|
children: [
|
||||||
|
{type: "kern", size: line.height},
|
||||||
|
{type: "elem", elem: line},
|
||||||
|
{type: "kern", size: 3 * line.height},
|
||||||
|
{type: "elem", elem: innerGroup},
|
||||||
|
],
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
return buildCommon.makeSpan(["mord", "underline"], [vlist], options);
|
||||||
|
},
|
||||||
|
mathmlBuilder(group, options) {
|
||||||
|
const operator = new mathMLTree.MathNode(
|
||||||
|
"mo", [new mathMLTree.TextNode("\u203e")]);
|
||||||
|
operator.setAttribute("stretchy", "true");
|
||||||
|
|
||||||
|
const node = new mathMLTree.MathNode(
|
||||||
|
"munder",
|
||||||
|
[mml.buildGroup(group.value.body, options), operator]);
|
||||||
|
node.setAttribute("accentunder", "true");
|
||||||
|
|
||||||
|
return node;
|
||||||
|
},
|
||||||
|
});
|
Reference in New Issue
Block a user