mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-06 11:48:41 +00:00
Support \oiint and \oiiint (#1430)
* Support \oiint and \oiiint * Fix sub. Add tests * Add phony screenshots * Add real screenshots
This commit is contained in:
@@ -746,7 +746,11 @@ const svgData: {
|
||||
[string]: ([string, number, number])
|
||||
} = {
|
||||
// path, width, height
|
||||
vec: ["vec", 0.471, 0.714], // values from the font glyph
|
||||
vec: ["vec", 0.471, 0.714], // values from the font glyph
|
||||
oiintSize1: ["oiintSize1", 0.957, 0.499], // oval to overlay the integrand
|
||||
oiintSize2: ["oiintSize2", 1.472, 0.659],
|
||||
oiiintSize1: ["oiiintSize1", 1.304, 0.499],
|
||||
oiiintSize2: ["oiiintSize2", 1.98, 0.659],
|
||||
};
|
||||
|
||||
const staticSvg = function(value: string, options: Options): SvgSpan {
|
||||
|
@@ -54,9 +54,39 @@ export const htmlBuilder: HtmlBuilderSupSub<"op"> = (grp, options) => {
|
||||
if (group.value.symbol) {
|
||||
// If this is a symbol, create the symbol.
|
||||
const fontName = large ? "Size2-Regular" : "Size1-Regular";
|
||||
|
||||
let stash = "";
|
||||
if (group.value.body === "\\oiint" || group.value.body === "\\oiiint") {
|
||||
// No font glyphs yet, so use a glyph w/o the oval.
|
||||
// TODO: When font glyphs are available, delete this code.
|
||||
stash = group.value.body.substr(1);
|
||||
// $FlowFixMe
|
||||
group.value.body = stash === "oiint" ? "\\iint" : "\\iiint";
|
||||
}
|
||||
|
||||
base = buildCommon.makeSymbol(
|
||||
group.value.body, fontName, "math", options,
|
||||
["mop", "op-symbol", large ? "large-op" : "small-op"]);
|
||||
|
||||
if (stash.length > 0) {
|
||||
// We're in \oiint or \oiiint. Overlay the oval.
|
||||
// TODO: When font glyphs are available, delete this code.
|
||||
const italic = base.italic;
|
||||
const oval = buildCommon.staticSvg(stash + "Size"
|
||||
+ (large ? "2" : "1"), options);
|
||||
base = buildCommon.makeVList({
|
||||
positionType: "individualShift",
|
||||
children: [
|
||||
{type: "elem", elem: base, shift: 0},
|
||||
{type: "elem", elem: oval, shift: large ? 0.08 : 0},
|
||||
],
|
||||
}, options);
|
||||
// $FlowFixMe
|
||||
group.value.body = "\\" + stash;
|
||||
base.classes.unshift("mop");
|
||||
// $FlowFixMe
|
||||
base.italic = italic;
|
||||
}
|
||||
} else if (group.value.value) {
|
||||
// If this is a list, compose that list.
|
||||
const inner = html.buildExpression(group.value.value, options, true);
|
||||
@@ -81,7 +111,9 @@ export const htmlBuilder: HtmlBuilderSupSub<"op"> = (grp, options) => {
|
||||
// If content of op is a single symbol, shift it vertically.
|
||||
let baseShift = 0;
|
||||
let slant = 0;
|
||||
if (base instanceof domTree.symbolNode && !group.value.suppressBaseShift) {
|
||||
if ((base instanceof domTree.symbolNode
|
||||
|| group.value.body === "\\oiint" || group.value.body === "\\oiiint")
|
||||
&& !group.value.suppressBaseShift) {
|
||||
// We suppress the shift of the base of \overset and \underset. Otherwise,
|
||||
// shift the symbol so its center lies on the axis (rule 13). It
|
||||
// appears that our fonts have the centers of the symbols already
|
||||
@@ -92,6 +124,7 @@ export const htmlBuilder: HtmlBuilderSupSub<"op"> = (grp, options) => {
|
||||
options.fontMetrics().axisHeight;
|
||||
|
||||
// The slant of the symbol is just its italic correction.
|
||||
// $FlowFixMe
|
||||
slant = base.italic;
|
||||
}
|
||||
|
||||
@@ -308,6 +341,8 @@ const singleCharIntegrals: {[string]: string} = {
|
||||
"\u222c": "\\iint",
|
||||
"\u222d": "\\iiint",
|
||||
"\u222e": "\\oint",
|
||||
"\u222f": "\\oiint",
|
||||
"\u2230": "\\oiiint",
|
||||
};
|
||||
|
||||
defineFunction({
|
||||
@@ -379,8 +414,8 @@ defineFunction({
|
||||
defineFunction({
|
||||
type: "op",
|
||||
names: [
|
||||
"\\int", "\\iint", "\\iiint", "\\oint", "\u222b", "\u222c",
|
||||
"\u222d", "\u222e",
|
||||
"\\int", "\\iint", "\\iiint", "\\oint", "\\oiint", "\\oiiint",
|
||||
"\u222b", "\u222c", "\u222d", "\u222e", "\u222f", "\u2230",
|
||||
],
|
||||
props: {
|
||||
numArgs: 0,
|
||||
|
@@ -109,6 +109,22 @@ defineFunctionBuilders({
|
||||
const multiplier = options.sizeMultiplier;
|
||||
const marginRight = (0.5 / metrics.ptPerEm) / multiplier + "em";
|
||||
|
||||
let marginLeft = null;
|
||||
if (subm) {
|
||||
// Subscripts shouldn't be shifted by the base's italic correction.
|
||||
// Account for that by shifting the subscript back the appropriate
|
||||
// amount. Note we only do this when the base is a single symbol.
|
||||
let isOiint = false;
|
||||
if (group.value.base) {
|
||||
isOiint = group.value.base.value.body === "\\oiint" ||
|
||||
group.value.base.value.body === "\\oiiint";
|
||||
}
|
||||
if (base instanceof domTree.symbolNode || isOiint) {
|
||||
// $FlowFixMe
|
||||
marginLeft = -base.italic + "em";
|
||||
}
|
||||
}
|
||||
|
||||
let supsub;
|
||||
if (supm && subm) {
|
||||
supShift = Math.max(
|
||||
@@ -128,11 +144,6 @@ defineFunctionBuilders({
|
||||
}
|
||||
}
|
||||
|
||||
// Subscripts shouldn't be shifted by the base's italic correction.
|
||||
// Account for that by shifting the subscript back the appropriate
|
||||
// amount. Note we only do this when the base is a single symbol.
|
||||
const marginLeft =
|
||||
base instanceof domTree.symbolNode ? -base.italic + "em" : null;
|
||||
const vlistElem = [
|
||||
{type: "elem", elem: subm, shift: subShift, marginRight,
|
||||
marginLeft},
|
||||
@@ -149,9 +160,6 @@ defineFunctionBuilders({
|
||||
subShift, metrics.sub1,
|
||||
subm.height - 0.8 * metrics.xHeight);
|
||||
|
||||
// See comment above about subscripts not being shifted.
|
||||
const marginLeft =
|
||||
base instanceof domTree.symbolNode ? -base.italic + "em" : null;
|
||||
const vlistElem =
|
||||
[{type: "elem", elem: subm, marginLeft, marginRight}];
|
||||
|
||||
|
@@ -160,6 +160,26 @@ c100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14
|
||||
11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0
|
||||
-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z`,
|
||||
|
||||
oiintSize1: `M512.6 71.6c272.6 0 320.3 106.8 320.3 178.2 0 70.8-47.7 177.6
|
||||
-320.3 177.6S193.1 320.6 193.1 249.8c0-71.4 46.9-178.2 319.5-178.2z
|
||||
m368.1 178.2c0-86.4-60.9-215.4-368.1-215.4-306.4 0-367.3 129-367.3 215.4 0 85.8
|
||||
60.9 214.8 367.3 214.8 307.2 0 368.1-129 368.1-214.8z`,
|
||||
|
||||
oiintSize2: `M757.8 100.1c384.7 0 451.1 137.6 451.1 230 0 91.3-66.4 228.8
|
||||
-451.1 228.8-386.3 0-452.7-137.5-452.7-228.8 0-92.4 66.4-230 452.7-230z
|
||||
m502.4 230c0-111.2-82.4-277.2-502.4-277.2s-504 166-504 277.2
|
||||
c0 110 84 276 504 276s502.4-166 502.4-276z`,
|
||||
|
||||
oiiintSize1: `M681.4 71.6c408.9 0 480.5 106.8 480.5 178.2 0 70.8-71.6 177.6
|
||||
-480.5 177.6S202.1 320.6 202.1 249.8c0-71.4 70.5-178.2 479.3-178.2z
|
||||
m525.8 178.2c0-86.4-86.8-215.4-525.7-215.4-437.9 0-524.7 129-524.7 215.4 0
|
||||
85.8 86.8 214.8 524.7 214.8 438.9 0 525.7-129 525.7-214.8z`,
|
||||
|
||||
oiiintSize2: `M1021.2 53c603.6 0 707.8 165.8 707.8 277.2 0 110-104.2 275.8
|
||||
-707.8 275.8-606 0-710.2-165.8-710.2-275.8C311 218.8 415.2 53 1021.2 53z
|
||||
m770.4 277.1c0-131.2-126.4-327.6-770.5-327.6S248.4 198.9 248.4 330.1
|
||||
c0 130 128.8 326.4 772.7 326.4s770.5-196.4 770.5-326.4z`,
|
||||
|
||||
rightarrow: `M0 241v40h399891c-47.3 35.3-84 78-110 128
|
||||
-16.7 32-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20
|
||||
11 8 0 13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7
|
||||
|
@@ -656,6 +656,8 @@ defineSymbol(math, main, op, "\u2a02", "\\bigotimes");
|
||||
defineSymbol(math, main, op, "\u2a01", "\\bigoplus");
|
||||
defineSymbol(math, main, op, "\u2a00", "\\bigodot");
|
||||
defineSymbol(math, main, op, "\u222e", "\\oint");
|
||||
defineSymbol(math, main, op, "\u222f", "\\oiint");
|
||||
defineSymbol(math, main, op, "\u2230", "\\oiiint");
|
||||
defineSymbol(math, main, op, "\u2a06", "\\bigsqcup");
|
||||
defineSymbol(math, main, op, "\u222b", "\\smallint");
|
||||
defineSymbol(text, main, inner, "\u2026", "\\textellipsis");
|
||||
|
@@ -1350,6 +1350,23 @@ describe("A TeX-compliant parser", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("An op symbol builder", function() {
|
||||
it("should not fail", function() {
|
||||
expect("\\int_i^n").toBuild();
|
||||
expect("\\iint_i^n").toBuild();
|
||||
expect("\\iiint_i^n").toBuild();
|
||||
expect("\\int\nolimits_i^n").toBuild();
|
||||
expect("\\iint\nolimits_i^n").toBuild();
|
||||
expect("\\iiint\nolimits_i^n").toBuild();
|
||||
expect("\\oint_i^n").toBuild();
|
||||
expect("\\oiint_i^n").toBuild();
|
||||
expect("\\oiiint_i^n").toBuild();
|
||||
expect("\\oint\nolimits_i^n").toBuild();
|
||||
expect("\\oiint\nolimits_i^n").toBuild();
|
||||
expect("\\oiiint\nolimits_i^n").toBuild();
|
||||
});
|
||||
});
|
||||
|
||||
describe("A style change parser", function() {
|
||||
it("should not fail", function() {
|
||||
expect("\\displaystyle x").toParse();
|
||||
|
BIN
test/screenshotter/images/Integrands-chrome.png
Normal file
BIN
test/screenshotter/images/Integrands-chrome.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
BIN
test/screenshotter/images/Integrands-firefox.png
Normal file
BIN
test/screenshotter/images/Integrands-firefox.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
@@ -132,6 +132,12 @@ GroupMacros:
|
||||
\endExp: \egroup
|
||||
tex: \startExp a+b\endExp
|
||||
HorizontalBraces: \overbrace{\displaystyle{\oint_S{\vec E\cdot\hat n\,\mathrm d a}}}^\text{emf} = \underbrace{\frac{q_{\text{enc}}}{\varepsilon_0}}_{\text{charge}}
|
||||
Integrands: |
|
||||
\begin{array}{l}
|
||||
\displaystyle \int + \oint + \iint + \oiint_i^n \\[0.6em]
|
||||
\displaystyle \iiint + \oiiint + \textstyle \int + \oint_i^n \\[0.6em]
|
||||
\iint + \oiint + \iiint + \oiiint
|
||||
\end{array}
|
||||
KaTeX:
|
||||
tex: \KaTeX
|
||||
nolatex: \KaTeX not supported by LaTeX
|
||||
|
Reference in New Issue
Block a user