mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-13 23:18:38 +00:00
Support {smallmatrix}, {subarray}, and \substack (#1969)
* Support {smallmatrix}, {subarray}, and \substack * Fix lint errors * Update docs. Screenshots step 1. * Screenshots step 2 * Screenshots step 3 * Pick up review comments * Fixed arraycolsep for {smallmatrix} * Fixed lint error * Updated screenshots
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
// @flow
|
||||
import buildCommon from "../buildCommon";
|
||||
import Style from "../Style";
|
||||
import defineEnvironment from "../defineEnvironment";
|
||||
import defineFunction from "../defineFunction";
|
||||
import mathMLTree from "../mathMLTree";
|
||||
@@ -26,7 +27,7 @@ export type AlignSpec = { type: "separator", separator: string } | {
|
||||
};
|
||||
|
||||
// Type to indicate column separation in MathML
|
||||
export type ColSeparationType = "align" | "alignat";
|
||||
export type ColSeparationType = "align" | "alignat" | "small";
|
||||
|
||||
function getHLines(parser: Parser): boolean[] {
|
||||
// Return an array. The array length = number of hlines.
|
||||
@@ -175,7 +176,16 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
|
||||
|
||||
// Horizontal spacing
|
||||
const pt = 1 / options.fontMetrics().ptPerEm;
|
||||
const arraycolsep = 5 * pt; // \arraycolsep in article.cls
|
||||
let arraycolsep = 5 * pt; // default value, i.e. \arraycolsep in article.cls
|
||||
if (group.colSeparationType && group.colSeparationType === "small") {
|
||||
// We're in a {smallmatrix}. Default column space is \thickspace,
|
||||
// i.e. 5/18em = 0.2778em, per amsmath.dtx for {smallmatrix}.
|
||||
// But that needs adjustment because LaTeX applies \scriptstyle to the
|
||||
// entire array, including the colspace, but this function applies
|
||||
// \scriptstyle only inside each element.
|
||||
const localMultiplier = options.havingStyle(Style.SCRIPT).sizeMultiplier;
|
||||
arraycolsep = 0.2778 * (localMultiplier / options.sizeMultiplier);
|
||||
}
|
||||
|
||||
// Vertical spacing
|
||||
const baselineskip = 12 * pt; // see size10.clo
|
||||
@@ -379,7 +389,7 @@ const alignMap = {
|
||||
};
|
||||
|
||||
const mathmlBuilder: MathMLBuilder<"array"> = function(group, options) {
|
||||
const table = new mathMLTree.MathNode(
|
||||
let table = new mathMLTree.MathNode(
|
||||
"mtable", group.body.map(function(row) {
|
||||
return new mathMLTree.MathNode(
|
||||
"mtr", row.map(function(cell) {
|
||||
@@ -401,7 +411,9 @@ const mathmlBuilder: MathMLBuilder<"array"> = function(group, options) {
|
||||
|
||||
// The 0.16 and 0.09 values are found emprically. They produce an array
|
||||
// similar to LaTeX and in which content does not interfere with \hines.
|
||||
const gap = 0.16 + group.arraystretch - 1 + (group.addJot ? 0.09 : 0);
|
||||
const gap = (group.arraystretch === 0.5)
|
||||
? 0.1 // {smallmatrix}, {subarray}
|
||||
: 0.16 + group.arraystretch - 1 + (group.addJot ? 0.09 : 0);
|
||||
table.setAttribute("rowspacing", gap + "em");
|
||||
|
||||
// MathML table lines go only between cells.
|
||||
@@ -463,6 +475,8 @@ const mathmlBuilder: MathMLBuilder<"array"> = function(group, options) {
|
||||
table.setAttribute("columnspacing", spacing.trim());
|
||||
} else if (group.colSeparationType === "alignat") {
|
||||
table.setAttribute("columnspacing", "0em");
|
||||
} else if (group.colSeparationType === "small") {
|
||||
table.setAttribute("columnspacing", "0.2778em");
|
||||
} else {
|
||||
table.setAttribute("columnspacing", "1em");
|
||||
}
|
||||
@@ -484,13 +498,18 @@ const mathmlBuilder: MathMLBuilder<"array"> = function(group, options) {
|
||||
table.setAttribute("rowlines", rowLines.trim());
|
||||
}
|
||||
|
||||
if (menclose === "") {
|
||||
return table;
|
||||
} else {
|
||||
const wrapper = new mathMLTree.MathNode("menclose", [table]);
|
||||
wrapper.setAttribute("notation", menclose.trim());
|
||||
return wrapper;
|
||||
if (menclose !== "") {
|
||||
table = new mathMLTree.MathNode("menclose", [table]);
|
||||
table.setAttribute("notation", menclose.trim());
|
||||
}
|
||||
|
||||
if (group.arraystretch && group.arraystretch < 1) {
|
||||
// A small array. Wrap in scriptstyle so row gap is not too large.
|
||||
table = new mathMLTree.MathNode("mstyle", [table]);
|
||||
table.setAttribute("scriptlevel", "1");
|
||||
}
|
||||
|
||||
return table;
|
||||
};
|
||||
|
||||
// Convenience function for aligned and alignedat environments.
|
||||
@@ -656,6 +675,63 @@ defineEnvironment({
|
||||
mathmlBuilder,
|
||||
});
|
||||
|
||||
defineEnvironment({
|
||||
type: "array",
|
||||
names: ["smallmatrix"],
|
||||
props: {
|
||||
numArgs: 0,
|
||||
},
|
||||
handler(context) {
|
||||
const payload = {arraystretch: 0.5};
|
||||
const res = parseArray(context.parser, payload, "script");
|
||||
res.colSeparationType = "small";
|
||||
return res;
|
||||
},
|
||||
htmlBuilder,
|
||||
mathmlBuilder,
|
||||
});
|
||||
|
||||
defineEnvironment({
|
||||
type: "array",
|
||||
names: ["subarray"],
|
||||
props: {
|
||||
numArgs: 1,
|
||||
},
|
||||
handler(context, args) {
|
||||
// Parsing of {subarray} is similar to {array}
|
||||
const symNode = checkSymbolNodeType(args[0]);
|
||||
const colalign: AnyParseNode[] =
|
||||
symNode ? [args[0]] : assertNodeType(args[0], "ordgroup").body;
|
||||
const cols = colalign.map(function(nde) {
|
||||
const node = assertSymbolNodeType(nde);
|
||||
const ca = node.text;
|
||||
// {subarray} only recognizes "l" & "c"
|
||||
if ("lc".indexOf(ca) !== -1) {
|
||||
return {
|
||||
type: "align",
|
||||
align: ca,
|
||||
};
|
||||
}
|
||||
throw new ParseError("Unknown column alignment: " + ca, nde);
|
||||
});
|
||||
if (cols.length > 1) {
|
||||
throw new ParseError("{subarray} can contain only one column");
|
||||
}
|
||||
let res = {
|
||||
cols,
|
||||
hskipBeforeAndAfter: false,
|
||||
arraystretch: 0.5,
|
||||
};
|
||||
res = parseArray(context.parser, res, "script");
|
||||
if (res.body[0].length > 1) {
|
||||
throw new ParseError("{subarray} can contain only one column");
|
||||
}
|
||||
return res;
|
||||
},
|
||||
htmlBuilder,
|
||||
mathmlBuilder,
|
||||
});
|
||||
|
||||
// A cases environment (in amsmath.sty) is almost equivalent to
|
||||
// \def\arraystretch{1.2}%
|
||||
// \left\{\begin{array}{@{}l@{\quad}l@{}} … \end{array}\right.
|
||||
|
@@ -426,6 +426,9 @@ defineMacro("\\varPhi", "\\mathit{\\Phi}");
|
||||
defineMacro("\\varPsi", "\\mathit{\\Psi}");
|
||||
defineMacro("\\varOmega", "\\mathit{\\Omega}");
|
||||
|
||||
//\newcommand{\substack}[1]{\subarray{c}#1\endsubarray}
|
||||
defineMacro("\\substack", "\\begin{subarray}{c}#1\\end{subarray}");
|
||||
|
||||
// \renewcommand{\colon}{\nobreak\mskip2mu\mathpunct{}\nonscript
|
||||
// \mkern-\thinmuskip{:}\mskip6muplus1mu\relax}
|
||||
defineMacro("\\colon", "\\nobreak\\mskip2mu\\mathpunct{}" +
|
||||
|
Reference in New Issue
Block a user