katex 0.16.45 → 0.16.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/contrib/copy-tex/README.md +2 -2
- package/contrib/mathtex-script-type/README.md +5 -5
- package/contrib/mathtex-script-type/mathtex-script-type.js +2 -1
- package/contrib/mhchem/README.md +1 -1
- package/contrib/render-a11y-string/render-a11y-string.ts +7 -5
- package/contrib/render-a11y-string/test/render-a11y-string-spec.ts +0 -1
- package/dist/README.md +3 -3
- package/dist/contrib/mathtex-script-type.js +2 -1
- package/dist/contrib/mathtex-script-type.min.js +1 -1
- package/dist/contrib/mathtex-script-type.mjs +2 -1
- package/dist/contrib/render-a11y-string.js +47 -8
- package/dist/contrib/render-a11y-string.min.js +1 -1
- package/dist/contrib/render-a11y-string.mjs +27 -2
- package/dist/katex-swap.css +5 -2
- package/dist/katex-swap.min.css +1 -1
- package/dist/katex.css +5 -2
- package/dist/katex.js +520 -316
- package/dist/katex.min.css +1 -1
- package/dist/katex.min.js +1 -1
- package/dist/katex.mjs +492 -321
- package/package.json +15 -16
- package/src/Options.ts +29 -28
- package/src/Parser.ts +12 -15
- package/src/Settings.ts +177 -60
- package/src/atoms.ts +33 -0
- package/src/buildCommon.ts +54 -46
- package/src/buildHTML.ts +4 -3
- package/src/buildMathML.ts +54 -47
- package/src/defineEnvironment.ts +1 -1
- package/src/defineFunction.ts +10 -3
- package/src/delimiter.ts +17 -13
- package/src/domTree.ts +28 -23
- package/src/environments/array.ts +12 -6
- package/src/environments/cd.ts +9 -2
- package/src/fontMetrics.ts +10 -23
- package/src/fontMetricsData.d.ts +6 -1
- package/src/functions/arrow.ts +4 -5
- package/src/functions/delimsizing.ts +22 -16
- package/src/functions/enclose.ts +6 -6
- package/src/functions/environment.ts +7 -2
- package/src/functions/font.ts +13 -8
- package/src/functions/hbox.ts +2 -2
- package/src/functions/horizBrace.ts +4 -6
- package/src/functions/math.ts +1 -0
- package/src/functions/op.ts +10 -5
- package/src/functions/smash.ts +1 -1
- package/src/functions/styling.ts +17 -5
- package/src/functions/supsub.ts +6 -3
- package/src/functions/text.ts +7 -3
- package/src/parseNode.ts +7 -5
- package/src/stretchy.ts +14 -14
- package/src/styles/katex.scss +3 -1
- package/src/svgGeometry.ts +2 -2
- package/src/symbols.ts +11 -26
- package/src/tree.ts +11 -5
- package/src/types/fonts.ts +73 -0
- package/src/{types.ts → types/index.ts} +4 -10
- package/src/utils.ts +0 -1
- package/src/wide-character.ts +101 -55
package/src/environments/cd.ts
CHANGED
|
@@ -25,7 +25,13 @@ const newCell = (): ParseNode<"styling"> => {
|
|
|
25
25
|
// The parseTree from this module must be constructed like the
|
|
26
26
|
// one created by parseArray(), so an empty CD cell must
|
|
27
27
|
// be a ParseNode<"styling">. And CD is always displaystyle.
|
|
28
|
-
return {
|
|
28
|
+
return {
|
|
29
|
+
type: "styling",
|
|
30
|
+
body: [],
|
|
31
|
+
mode: "math",
|
|
32
|
+
style: "display",
|
|
33
|
+
resetFont: true,
|
|
34
|
+
};
|
|
29
35
|
};
|
|
30
36
|
|
|
31
37
|
const isStartOfArrow = (node: AnyParseNode) => {
|
|
@@ -90,7 +96,7 @@ export function parseCD(parser: Parser): ParseNode<"array"> {
|
|
|
90
96
|
parser.gullet.beginGroup();
|
|
91
97
|
parser.gullet.macros.set("\\cr", "\\\\\\relax");
|
|
92
98
|
parser.gullet.beginGroup();
|
|
93
|
-
while (true) {
|
|
99
|
+
while (true) {
|
|
94
100
|
// Get the parse nodes for the next row.
|
|
95
101
|
parsedRows.push(parser.parseExpression(false, "\\\\"));
|
|
96
102
|
parser.gullet.endGroup();
|
|
@@ -183,6 +189,7 @@ export function parseCD(parser: Parser): ParseNode<"array"> {
|
|
|
183
189
|
body: [arrow],
|
|
184
190
|
mode: "math",
|
|
185
191
|
style: "display", // CD is always displaystyle.
|
|
192
|
+
resetFont: true,
|
|
186
193
|
};
|
|
187
194
|
row.push(wrappedArrow);
|
|
188
195
|
// In CD's syntax, cells are implicit. That is, everything that
|
package/src/fontMetrics.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import {supportedCodepoint} from "./unicodeScripts";
|
|
2
2
|
|
|
3
3
|
import type {Mode} from "./types";
|
|
4
|
+
import type {CharacterMetrics, CharacterMetricsTuple, FontMetrics} from "./types/fonts";
|
|
5
|
+
// This map contains a mapping from font name and character code to character
|
|
6
|
+
// metrics, including height, depth, italic correction, and skew (kern from the
|
|
7
|
+
// character to the corresponding \skewchar)
|
|
8
|
+
// This map is generated via `make metrics`. It should not be changed manually.
|
|
9
|
+
import metricMap from "./fontMetricsData";
|
|
4
10
|
|
|
5
11
|
/**
|
|
6
12
|
* This file contains metrics regarding fonts and individual symbols. The sigma
|
|
@@ -91,12 +97,6 @@ const sigmasAndXis: Record<string, [number, number, number]> = {
|
|
|
91
97
|
fboxrule: [0.04, 0.04, 0.04], // 0.4 pt / ptPerEm
|
|
92
98
|
};
|
|
93
99
|
|
|
94
|
-
// This map contains a mapping from font name and character code to character
|
|
95
|
-
// metrics, including height, depth, italic correction, and skew (kern from the
|
|
96
|
-
// character to the corresponding \skewchar)
|
|
97
|
-
// This map is generated via `make metrics`. It should not be changed manually.
|
|
98
|
-
import metricMap from "./fontMetricsData";
|
|
99
|
-
|
|
100
100
|
// These are very rough approximations. We default to Times New Roman which
|
|
101
101
|
// should have Latin-1 and Cyrillic characters, but may not depending on the
|
|
102
102
|
// operating system. The metrics do not account for extra height from the
|
|
@@ -180,23 +180,14 @@ const extraCharacterMap: Record<string, string> = {
|
|
|
180
180
|
'я': 'r',
|
|
181
181
|
};
|
|
182
182
|
|
|
183
|
-
export type CharacterMetrics = {
|
|
184
|
-
depth: number;
|
|
185
|
-
height: number;
|
|
186
|
-
italic: number;
|
|
187
|
-
skew: number;
|
|
188
|
-
width: number;
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
export type MetricMap = {
|
|
192
|
-
[key: string]: [number, number, number, number, number];
|
|
193
|
-
};
|
|
194
|
-
|
|
195
183
|
/**
|
|
196
184
|
* This function adds new font metrics to default metricMap
|
|
197
185
|
* It can also override existing metrics
|
|
198
186
|
*/
|
|
199
|
-
export function setFontMetrics(
|
|
187
|
+
export function setFontMetrics(
|
|
188
|
+
fontName: string,
|
|
189
|
+
metrics: {[key: string]: CharacterMetricsTuple}
|
|
190
|
+
) {
|
|
200
191
|
metricMap[fontName] = metrics;
|
|
201
192
|
}
|
|
202
193
|
|
|
@@ -248,10 +239,6 @@ export function getCharacterMetrics(
|
|
|
248
239
|
}
|
|
249
240
|
|
|
250
241
|
type FontSizeIndex = 0 | 1 | 2;
|
|
251
|
-
export type FontMetrics = {
|
|
252
|
-
cssEmPerMu: number;
|
|
253
|
-
[key: string]: number;
|
|
254
|
-
};
|
|
255
242
|
const fontMetricsBySizeIndex: Partial<Record<FontSizeIndex, FontMetrics>> = {};
|
|
256
243
|
|
|
257
244
|
/**
|
package/src/fontMetricsData.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import type {CharacterMetricsTuple, FontName} from "./types/fonts";
|
|
2
|
+
|
|
3
|
+
type FontMetrics = Record<number, CharacterMetricsTuple>;
|
|
4
|
+
|
|
5
|
+
declare const fontMetricsData: Record<FontName, FontMetrics> &
|
|
6
|
+
Record<string, FontMetrics>;
|
|
2
7
|
|
|
3
8
|
export default fontMetricsData;
|
package/src/functions/arrow.ts
CHANGED
|
@@ -92,7 +92,8 @@ defineFunction({
|
|
|
92
92
|
positionType: "individualShift",
|
|
93
93
|
children: [
|
|
94
94
|
{type: "elem", elem: upperGroup, shift: upperShift},
|
|
95
|
-
{type: "elem", elem: arrowBody, shift: arrowShift
|
|
95
|
+
{type: "elem", elem: arrowBody, shift: arrowShift,
|
|
96
|
+
wrapperClasses: ["svg-align"]},
|
|
96
97
|
{type: "elem", elem: lowerGroup, shift: lowerShift},
|
|
97
98
|
],
|
|
98
99
|
}, options);
|
|
@@ -101,14 +102,12 @@ defineFunction({
|
|
|
101
102
|
positionType: "individualShift",
|
|
102
103
|
children: [
|
|
103
104
|
{type: "elem", elem: upperGroup, shift: upperShift},
|
|
104
|
-
{type: "elem", elem: arrowBody, shift: arrowShift
|
|
105
|
+
{type: "elem", elem: arrowBody, shift: arrowShift,
|
|
106
|
+
wrapperClasses: ["svg-align"]},
|
|
105
107
|
],
|
|
106
108
|
}, options);
|
|
107
109
|
}
|
|
108
110
|
|
|
109
|
-
// TODO(ts): Replace this with passing "svg-align" into makeVList.
|
|
110
|
-
(vlist as any).children[0].children[0].children[1].classes.push("svg-align");
|
|
111
|
-
|
|
112
111
|
return makeSpan(["mrel", "x-arrow"], [vlist], options);
|
|
113
112
|
},
|
|
114
113
|
mathmlBuilder(group, options) {
|
|
@@ -12,6 +12,7 @@ import * as mml from "../buildMathML";
|
|
|
12
12
|
import type Options from "../Options";
|
|
13
13
|
import type {AnyParseNode, ParseNode, SymbolParseNode} from "../parseNode";
|
|
14
14
|
import type {FunctionContext} from "../defineFunction";
|
|
15
|
+
import type {HtmlDomNode} from "../domTree";
|
|
15
16
|
|
|
16
17
|
// Extra data needed for the delimiter handler down below
|
|
17
18
|
const delimiterSizes: Record<string, {
|
|
@@ -56,6 +57,16 @@ const delimiters = new Set([
|
|
|
56
57
|
|
|
57
58
|
type IsMiddle = {delim: string, options: Options};
|
|
58
59
|
|
|
60
|
+
/**
|
|
61
|
+
* An HtmlDomNode that carries an `isMiddle` property, used by the
|
|
62
|
+
* \middle command to communicate delimiter info to the \left/\right builder.
|
|
63
|
+
*/
|
|
64
|
+
type MiddleDelimNode = HtmlDomNode & {isMiddle: IsMiddle};
|
|
65
|
+
|
|
66
|
+
function isMiddleDelimNode(node: HtmlDomNode): node is MiddleDelimNode {
|
|
67
|
+
return 'isMiddle' in node;
|
|
68
|
+
}
|
|
69
|
+
|
|
59
70
|
// Delimiter functions
|
|
60
71
|
function checkDelimiter(
|
|
61
72
|
delim: AnyParseNode,
|
|
@@ -209,10 +220,8 @@ defineFunction({
|
|
|
209
220
|
|
|
210
221
|
// Calculate its height and depth
|
|
211
222
|
for (let i = 0; i < inner.length; i++) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
// TODO(ts)
|
|
215
|
-
if ((inner[i] as any).isMiddle) {
|
|
223
|
+
const node = inner[i];
|
|
224
|
+
if (isMiddleDelimNode(node)) {
|
|
216
225
|
hadMiddle = true;
|
|
217
226
|
} else {
|
|
218
227
|
innerHeight = Math.max(inner[i].height, innerHeight);
|
|
@@ -244,11 +253,8 @@ defineFunction({
|
|
|
244
253
|
if (hadMiddle) {
|
|
245
254
|
for (let i = 1; i < inner.length; i++) {
|
|
246
255
|
const middleDelim = inner[i];
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
// TODO(ts)
|
|
250
|
-
const isMiddle: IsMiddle = (middleDelim as any).isMiddle;
|
|
251
|
-
if (isMiddle) {
|
|
256
|
+
if (isMiddleDelimNode(middleDelim)) {
|
|
257
|
+
const isMiddle = middleDelim.isMiddle;
|
|
252
258
|
// Apply the options that were active when \middle was called
|
|
253
259
|
inner[i] = makeLeftRightDelim(
|
|
254
260
|
isMiddle.delim, innerHeight, innerDepth,
|
|
@@ -331,13 +337,13 @@ defineFunction({
|
|
|
331
337
|
group.delim, 1, options,
|
|
332
338
|
group.mode, []);
|
|
333
339
|
|
|
334
|
-
|
|
335
|
-
//
|
|
336
|
-
//
|
|
337
|
-
//
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
340
|
+
// Patch an ad-hoc property onto the node so the \left/\right
|
|
341
|
+
// builder can reconstruct appropriately sized middle delimiters.
|
|
342
|
+
// isMiddle is not part of HtmlDomNode; the read side uses
|
|
343
|
+
// isMiddleDelimNode() to check before accessing.
|
|
344
|
+
(middleDelim as unknown as MiddleDelimNode).isMiddle = {
|
|
345
|
+
delim: group.delim, options,
|
|
346
|
+
};
|
|
341
347
|
}
|
|
342
348
|
return middleDelim;
|
|
343
349
|
},
|
package/src/functions/enclose.ts
CHANGED
|
@@ -22,7 +22,7 @@ const htmlBuilder: HtmlBuilder<"enclose"> = (group, options) => {
|
|
|
22
22
|
const label = group.label.slice(1);
|
|
23
23
|
let scale = options.sizeMultiplier;
|
|
24
24
|
let img;
|
|
25
|
-
let imgShift
|
|
25
|
+
let imgShift;
|
|
26
26
|
|
|
27
27
|
// In the LaTeX cancel package, line geometry is slightly different
|
|
28
28
|
// depending on whether the subject is wider than it is tall, or vice versa.
|
|
@@ -76,8 +76,8 @@ const htmlBuilder: HtmlBuilder<"enclose"> = (group, options) => {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// Add vertical padding
|
|
79
|
-
let topPad
|
|
80
|
-
let bottomPad
|
|
79
|
+
let topPad;
|
|
80
|
+
let bottomPad;
|
|
81
81
|
let ruleThickness = 0;
|
|
82
82
|
// ref: cancel package: \advance\totalheight2\p@ % "+2"
|
|
83
83
|
if (/box/.test(label)) {
|
|
@@ -165,7 +165,7 @@ const htmlBuilder: HtmlBuilder<"enclose"> = (group, options) => {
|
|
|
165
165
|
};
|
|
166
166
|
|
|
167
167
|
const mathmlBuilder: MathMLBuilder<"enclose"> = (group, options) => {
|
|
168
|
-
let fboxsep
|
|
168
|
+
let fboxsep;
|
|
169
169
|
const node = new MathNode(
|
|
170
170
|
group.label.includes("colorbox") ? "mpadded" : "menclose",
|
|
171
171
|
[mml.buildGroup(group.body, options)]
|
|
@@ -223,7 +223,7 @@ defineFunction({
|
|
|
223
223
|
props: {
|
|
224
224
|
numArgs: 2,
|
|
225
225
|
allowedInText: true,
|
|
226
|
-
argTypes: ["color", "
|
|
226
|
+
argTypes: ["color", "hbox"],
|
|
227
227
|
},
|
|
228
228
|
handler({parser, funcName}, args, optArgs) {
|
|
229
229
|
const color = assertNodeType(args[0], "color-token").color;
|
|
@@ -246,7 +246,7 @@ defineFunction({
|
|
|
246
246
|
props: {
|
|
247
247
|
numArgs: 3,
|
|
248
248
|
allowedInText: true,
|
|
249
|
-
argTypes: ["color", "color", "
|
|
249
|
+
argTypes: ["color", "color", "hbox"],
|
|
250
250
|
},
|
|
251
251
|
handler({parser, funcName}, args, optArgs) {
|
|
252
252
|
const borderColor = assertNodeType(args[0], "color-token").color;
|
|
@@ -3,6 +3,8 @@ import ParseError from "../ParseError";
|
|
|
3
3
|
import {assertNodeType} from "../parseNode";
|
|
4
4
|
import environments from "../environments";
|
|
5
5
|
|
|
6
|
+
import type {ParseNode} from "../parseNode";
|
|
7
|
+
|
|
6
8
|
// Environment delimiters. HTML/MathML rendering is defined in the corresponding
|
|
7
9
|
// defineEnvironment definitions.
|
|
8
10
|
defineFunction({
|
|
@@ -47,8 +49,11 @@ defineFunction({
|
|
|
47
49
|
`Mismatch: \\begin{${envName}} matched by \\end{${end.name}}`,
|
|
48
50
|
endNameToken);
|
|
49
51
|
}
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
+
// env.handler returns the specific node type (e.g. "array"),
|
|
53
|
+
// not "environment". This cast is unavoidable: defineFunction
|
|
54
|
+
// requires the handler to return ParseNode<"environment"> but
|
|
55
|
+
// \begin delegates to environment handlers with different types.
|
|
56
|
+
return result as unknown as ParseNode<"environment">;
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
return {
|
package/src/functions/font.ts
CHANGED
|
@@ -9,6 +9,7 @@ import * as mml from "../buildMathML";
|
|
|
9
9
|
|
|
10
10
|
import type Options from "../Options";
|
|
11
11
|
import type {ParseNode} from "../parseNode";
|
|
12
|
+
import type {Slice1} from "../types";
|
|
12
13
|
|
|
13
14
|
const htmlBuilder = (group: ParseNode<"font">, options: Options) => {
|
|
14
15
|
const font = group.font;
|
|
@@ -22,12 +23,17 @@ const mathmlBuilder = (group: ParseNode<"font">, options: Options) => {
|
|
|
22
23
|
return mml.buildGroup(group.body, newOptions);
|
|
23
24
|
};
|
|
24
25
|
|
|
25
|
-
const fontAliases
|
|
26
|
+
const fontAliases = {
|
|
26
27
|
"\\Bbb": "\\mathbb",
|
|
27
28
|
"\\bold": "\\mathbf",
|
|
28
29
|
"\\frak": "\\mathfrak",
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
} as const;
|
|
31
|
+
|
|
32
|
+
type OldFontCommands = "\\rm" | "\\sf" | "\\tt" | "\\bf" | "\\it" | "\\cal";
|
|
33
|
+
type FontCommands =
|
|
34
|
+
"\\mathrm" | "\\mathit" | "\\mathbf" | "\\mathnormal" | "\\mathsfit" |
|
|
35
|
+
"\\mathbb" | "\\mathcal" | "\\mathfrak" | "\\mathscr" | "\\mathsf" |
|
|
36
|
+
"\\mathtt";
|
|
31
37
|
|
|
32
38
|
defineFunction({
|
|
33
39
|
type: "font",
|
|
@@ -41,7 +47,7 @@ defineFunction({
|
|
|
41
47
|
|
|
42
48
|
// aliases, except \bm defined below
|
|
43
49
|
"\\Bbb", "\\bold", "\\frak",
|
|
44
|
-
],
|
|
50
|
+
] satisfies (FontCommands | keyof typeof fontAliases)[],
|
|
45
51
|
props: {
|
|
46
52
|
numArgs: 1,
|
|
47
53
|
allowedInArgument: true,
|
|
@@ -50,12 +56,12 @@ defineFunction({
|
|
|
50
56
|
const body = normalizeArgument(args[0]);
|
|
51
57
|
let func = funcName;
|
|
52
58
|
if (func in fontAliases) {
|
|
53
|
-
func = fontAliases[func];
|
|
59
|
+
func = fontAliases[func as keyof typeof fontAliases];
|
|
54
60
|
}
|
|
55
61
|
return {
|
|
56
62
|
type: "font",
|
|
57
63
|
mode: parser.mode,
|
|
58
|
-
font: func.slice(1)
|
|
64
|
+
font: func.slice(1) as Slice1<FontCommands>,
|
|
59
65
|
body,
|
|
60
66
|
};
|
|
61
67
|
},
|
|
@@ -101,12 +107,11 @@ defineFunction({
|
|
|
101
107
|
handler: ({parser, funcName, breakOnTokenText}, args) => {
|
|
102
108
|
const {mode} = parser;
|
|
103
109
|
const body = parser.parseExpression(true, breakOnTokenText);
|
|
104
|
-
const style = `math${funcName.slice(1)}`;
|
|
105
110
|
|
|
106
111
|
return {
|
|
107
112
|
type: "font",
|
|
108
113
|
mode: mode,
|
|
109
|
-
font:
|
|
114
|
+
font: `math${funcName.slice(1) as Slice1<OldFontCommands>}`,
|
|
110
115
|
body: {
|
|
111
116
|
type: "ordgroup",
|
|
112
117
|
mode: parser.mode,
|
package/src/functions/hbox.ts
CHANGED
|
@@ -27,12 +27,12 @@ defineFunction({
|
|
|
27
27
|
};
|
|
28
28
|
},
|
|
29
29
|
htmlBuilder(group, options) {
|
|
30
|
-
const elements = html.buildExpression(group.body, options, false);
|
|
30
|
+
const elements = html.buildExpression(group.body, options.withFont(''), false);
|
|
31
31
|
return makeFragment(elements);
|
|
32
32
|
},
|
|
33
33
|
mathmlBuilder(group, options) {
|
|
34
34
|
return new MathNode(
|
|
35
|
-
"mrow", mml.buildExpression(group.body, options)
|
|
35
|
+
"mrow", mml.buildExpression(group.body, options.withFont(''))
|
|
36
36
|
);
|
|
37
37
|
},
|
|
38
38
|
});
|
|
@@ -47,23 +47,21 @@ export const htmlBuilder: HtmlBuilderSupSub<"horizBrace"> = (grp, options) => {
|
|
|
47
47
|
children: [
|
|
48
48
|
{type: "elem", elem: body},
|
|
49
49
|
{type: "kern", size: 0.1},
|
|
50
|
-
{type: "elem", elem: braceBody
|
|
50
|
+
{type: "elem", elem: braceBody,
|
|
51
|
+
wrapperClasses: ["svg-align"]},
|
|
51
52
|
],
|
|
52
53
|
}, options);
|
|
53
|
-
// TODO(ts): Replace this with passing "svg-align" into makeVList.
|
|
54
|
-
(vlist as any).children[0].children[0].children[1].classes.push("svg-align");
|
|
55
54
|
} else {
|
|
56
55
|
vlist = makeVList({
|
|
57
56
|
positionType: "bottom",
|
|
58
57
|
positionData: body.depth + 0.1 + braceBody.height,
|
|
59
58
|
children: [
|
|
60
|
-
{type: "elem", elem: braceBody
|
|
59
|
+
{type: "elem", elem: braceBody,
|
|
60
|
+
wrapperClasses: ["svg-align"]},
|
|
61
61
|
{type: "kern", size: 0.1},
|
|
62
62
|
{type: "elem", elem: body},
|
|
63
63
|
],
|
|
64
64
|
}, options);
|
|
65
|
-
// TODO(ts): Replace this with passing "svg-align" into makeVList.
|
|
66
|
-
(vlist as any).children[0].children[0].children[0].classes.push("svg-align");
|
|
67
65
|
}
|
|
68
66
|
|
|
69
67
|
if (supSubGroup) {
|
package/src/functions/math.ts
CHANGED
package/src/functions/op.ts
CHANGED
|
@@ -51,6 +51,9 @@ export const htmlBuilder: HtmlBuilderSupSub<"op"> = (grp, options) => {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
let base;
|
|
54
|
+
// Italic correction from the symbol glyph, captured before the symbol
|
|
55
|
+
// may be wrapped in a vlist (for \oiint/\oiiint). Stays 0 for non-symbol ops.
|
|
56
|
+
let symbolItalic;
|
|
54
57
|
if (group.symbol) {
|
|
55
58
|
// If this is a symbol, create the symbol.
|
|
56
59
|
const fontName = large ? "Size2-Regular" : "Size1-Regular";
|
|
@@ -66,11 +69,11 @@ export const htmlBuilder: HtmlBuilderSupSub<"op"> = (grp, options) => {
|
|
|
66
69
|
base = makeSymbol(
|
|
67
70
|
group.name, fontName, "math", options,
|
|
68
71
|
["mop", "op-symbol", large ? "large-op" : "small-op"]);
|
|
72
|
+
symbolItalic = base.italic;
|
|
69
73
|
|
|
70
74
|
if (stash.length > 0) {
|
|
71
75
|
// We're in \oiint or \oiiint. Overlay the oval.
|
|
72
76
|
// TODO: When font glyphs are available, delete this code.
|
|
73
|
-
const italic = base.italic;
|
|
74
77
|
const oval = staticSvg(stash + "Size"
|
|
75
78
|
+ (large ? "2" : "1"), options);
|
|
76
79
|
base = makeVList({
|
|
@@ -82,8 +85,9 @@ export const htmlBuilder: HtmlBuilderSupSub<"op"> = (grp, options) => {
|
|
|
82
85
|
}, options);
|
|
83
86
|
group.name = "\\" + stash;
|
|
84
87
|
base.classes.unshift("mop");
|
|
85
|
-
//
|
|
86
|
-
|
|
88
|
+
// Carry the italic correction from the original symbol to the
|
|
89
|
+
// vlist wrapper so supsub can use it for subscript positioning.
|
|
90
|
+
base.italic = symbolItalic;
|
|
87
91
|
}
|
|
88
92
|
} else if (group.body) {
|
|
89
93
|
// If this is a list, compose that list.
|
|
@@ -120,8 +124,9 @@ export const htmlBuilder: HtmlBuilderSupSub<"op"> = (grp, options) => {
|
|
|
120
124
|
options.fontMetrics().axisHeight;
|
|
121
125
|
|
|
122
126
|
// The slant of the symbol is just its italic correction.
|
|
123
|
-
//
|
|
124
|
-
|
|
127
|
+
// SymbolNode carries .italic natively; Span (for \oiint/\oiiint)
|
|
128
|
+
// only has it set when nonzero, so default to 0.
|
|
129
|
+
slant = base.italic ?? 0;
|
|
125
130
|
}
|
|
126
131
|
|
|
127
132
|
if (hasLimits) {
|
package/src/functions/smash.ts
CHANGED
|
@@ -23,7 +23,7 @@ defineFunction({
|
|
|
23
23
|
// Optional [tb] argument is engaged.
|
|
24
24
|
// ref: amsmath: \renewcommand{\smash}[1][tb]{%
|
|
25
25
|
// def\mb@t{\ht}\def\mb@b{\dp}\def\mb@tb{\ht\z@\z@\dp}%
|
|
26
|
-
let letter
|
|
26
|
+
let letter;
|
|
27
27
|
for (let i = 0; i < tbArg.body.length; ++i) {
|
|
28
28
|
const node = tbArg.body[i];
|
|
29
29
|
letter = assertSymbolNodeType(node).text;
|
package/src/functions/styling.ts
CHANGED
|
@@ -6,13 +6,17 @@ import {sizingGroup} from "./sizing";
|
|
|
6
6
|
import * as mml from "../buildMathML";
|
|
7
7
|
import type {StyleStr} from "../types";
|
|
8
8
|
|
|
9
|
-
const styleMap = {
|
|
9
|
+
const styleMap: Record<StyleStr, typeof Style.DISPLAY> = {
|
|
10
10
|
"display": Style.DISPLAY,
|
|
11
11
|
"text": Style.TEXT,
|
|
12
12
|
"script": Style.SCRIPT,
|
|
13
13
|
"scriptscript": Style.SCRIPTSCRIPT,
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
+
function isStyleStr(s: string): s is StyleStr {
|
|
17
|
+
return s in styleMap;
|
|
18
|
+
}
|
|
19
|
+
|
|
16
20
|
defineFunction({
|
|
17
21
|
type: "styling",
|
|
18
22
|
names: [
|
|
@@ -30,8 +34,10 @@ defineFunction({
|
|
|
30
34
|
|
|
31
35
|
// TODO: Refactor to avoid duplicating styleMap in multiple places (e.g.
|
|
32
36
|
// here and in buildHTML and de-dupe the enumeration of all the styles).
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
const style = funcName.slice(1, funcName.length - 5);
|
|
38
|
+
if (!isStyleStr(style)) {
|
|
39
|
+
throw new Error(`Unknown style: ${style}`);
|
|
40
|
+
}
|
|
35
41
|
return {
|
|
36
42
|
type: "styling",
|
|
37
43
|
mode: parser.mode,
|
|
@@ -44,13 +50,19 @@ defineFunction({
|
|
|
44
50
|
htmlBuilder(group, options) {
|
|
45
51
|
// Style changes are handled in the TeXbook on pg. 442, Rule 3.
|
|
46
52
|
const newStyle = styleMap[group.style];
|
|
47
|
-
|
|
53
|
+
let newOptions = options.havingStyle(newStyle);
|
|
54
|
+
if (group.resetFont) {
|
|
55
|
+
newOptions = newOptions.withFont('');
|
|
56
|
+
}
|
|
48
57
|
return sizingGroup(group.body, newOptions, options);
|
|
49
58
|
},
|
|
50
59
|
mathmlBuilder(group, options) {
|
|
51
60
|
// Figure out what style we're changing to.
|
|
52
61
|
const newStyle = styleMap[group.style];
|
|
53
|
-
|
|
62
|
+
let newOptions = options.havingStyle(newStyle);
|
|
63
|
+
if (group.resetFont) {
|
|
64
|
+
newOptions = newOptions.withFont('');
|
|
65
|
+
}
|
|
54
66
|
|
|
55
67
|
const inner = mml.buildExpression(group.body, newOptions);
|
|
56
68
|
|
package/src/functions/supsub.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {defineFunctionBuilders} from "../defineFunction";
|
|
2
2
|
import {makeSpan, makeVList} from "../buildCommon";
|
|
3
|
-
import {SymbolNode} from "../domTree";
|
|
3
|
+
import {Span, SymbolNode, type HtmlDomNode} from "../domTree";
|
|
4
4
|
import {isCharacterBox} from "../utils";
|
|
5
5
|
import {MathNode} from "../mathMLTree";
|
|
6
6
|
import {makeEm} from "../units";
|
|
@@ -28,6 +28,7 @@ import type {MathNodeType} from "../mathMLTree";
|
|
|
28
28
|
const htmlBuilderDelegate = function(
|
|
29
29
|
group: ParseNode<"supsub">,
|
|
30
30
|
options: Options,
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
32
|
): HtmlBuilder<any> | null | undefined {
|
|
32
33
|
const base = group.base;
|
|
33
34
|
if (!base) {
|
|
@@ -122,8 +123,10 @@ defineFunctionBuilders({
|
|
|
122
123
|
group.base && group.base.type === "op" && group.base.name &&
|
|
123
124
|
(group.base.name === "\\oiint" || group.base.name === "\\oiiint");
|
|
124
125
|
if (base instanceof SymbolNode || isOiint) {
|
|
125
|
-
//
|
|
126
|
-
|
|
126
|
+
// SymbolNode has .italic natively; for \oiint/\oiiint the
|
|
127
|
+
// op builder stores .italic on the wrapping Span.
|
|
128
|
+
marginLeft = makeEm(
|
|
129
|
+
-((base as SymbolNode | Span<HtmlDomNode>).italic ?? 0));
|
|
127
130
|
}
|
|
128
131
|
}
|
|
129
132
|
|
package/src/functions/text.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import defineFunction, {ordargument} from "../defineFunction";
|
|
2
|
+
import type {TextFont} from "../types/fonts";
|
|
2
3
|
import {makeSpan} from "../buildCommon";
|
|
3
4
|
|
|
4
5
|
import * as html from "../buildHTML";
|
|
@@ -7,9 +8,12 @@ import type Options from "../Options";
|
|
|
7
8
|
import type {ParseNode} from "../parseNode";
|
|
8
9
|
|
|
9
10
|
// Non-mathy text, possibly in a font
|
|
10
|
-
const textFontFamilies: Record<string,
|
|
11
|
-
"\\text": undefined,
|
|
12
|
-
"\\
|
|
11
|
+
const textFontFamilies: Record<string, TextFont | undefined> = {
|
|
12
|
+
"\\text": undefined,
|
|
13
|
+
"\\textrm": "textrm",
|
|
14
|
+
"\\textsf": "textsf",
|
|
15
|
+
"\\texttt": "texttt",
|
|
16
|
+
"\\textnormal": "textrm",
|
|
13
17
|
};
|
|
14
18
|
|
|
15
19
|
const textFontWeights: Record<string, "textbf" | "textmd"> = {
|
package/src/parseNode.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {NON_ATOMS} from "./
|
|
1
|
+
import {NON_ATOMS} from "./atoms";
|
|
2
|
+
import type {Atom} from "./atoms";
|
|
2
3
|
import type SourceLocation from "./SourceLocation";
|
|
3
4
|
import type {AlignSpec, ColSeparationType} from "./environments/array";
|
|
4
|
-
import type {
|
|
5
|
-
import type {
|
|
5
|
+
import type {DelimiterSize, Mode, StyleStr} from "./types";
|
|
6
|
+
import type {MathFont} from "./types/fonts";
|
|
6
7
|
import type {Token} from "./Token";
|
|
7
8
|
import type {Measurement} from "./units";
|
|
8
9
|
export type NodeType = keyof ParseNodeTypes;
|
|
@@ -122,6 +123,7 @@ type ParseNodeTypes = {
|
|
|
122
123
|
mode: Mode;
|
|
123
124
|
loc?: SourceLocation | null | undefined;
|
|
124
125
|
style: StyleStr;
|
|
126
|
+
resetFont?: boolean;
|
|
125
127
|
body: AnyParseNode[];
|
|
126
128
|
};
|
|
127
129
|
"supsub": {
|
|
@@ -231,7 +233,7 @@ type ParseNodeTypes = {
|
|
|
231
233
|
type: "delimsizing";
|
|
232
234
|
mode: Mode;
|
|
233
235
|
loc?: SourceLocation | null | undefined;
|
|
234
|
-
size:
|
|
236
|
+
size: DelimiterSize;
|
|
235
237
|
mclass: "mopen" | "mclose" | "mrel" | "mord";
|
|
236
238
|
delim: string;
|
|
237
239
|
};
|
|
@@ -255,7 +257,7 @@ type ParseNodeTypes = {
|
|
|
255
257
|
type: "font";
|
|
256
258
|
mode: Mode;
|
|
257
259
|
loc?: SourceLocation | null | undefined;
|
|
258
|
-
font:
|
|
260
|
+
font: Exclude<MathFont, "">;
|
|
259
261
|
body: AnyParseNode;
|
|
260
262
|
};
|
|
261
263
|
"genfrac": {
|
package/src/stretchy.ts
CHANGED
|
@@ -103,9 +103,9 @@ export const stretchyMathML = function(label: string): MathNode {
|
|
|
103
103
|
// That is, inside the font, that arrowhead is 522 units tall, which
|
|
104
104
|
// corresponds to 0.522 em inside the document.
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
} = {
|
|
106
|
+
type SvgData = [string[], number, number, string?];
|
|
107
|
+
|
|
108
|
+
const katexImagesData: {[key: string]: SvgData} = {
|
|
109
109
|
// path(s), minWidth, height, align
|
|
110
110
|
overrightarrow: [["rightarrow"], 0.888, 522, "xMaxYMin"],
|
|
111
111
|
overleftarrow: [["leftarrow"], 0.888, 522, "xMinYMin"],
|
|
@@ -177,15 +177,11 @@ export const stretchySvg = function(
|
|
|
177
177
|
} {
|
|
178
178
|
let viewBoxWidth = 400000; // default
|
|
179
179
|
const label = group.label.slice(1);
|
|
180
|
-
if (wideAccentLabels.has(label)) {
|
|
181
|
-
// Each type in the `if` statement corresponds to one of the ParseNode
|
|
182
|
-
// types below. This narrowing is required to access `grp.base`.
|
|
183
|
-
// TODO(ts)
|
|
184
|
-
const grp = group as ParseNode<"accent"> | ParseNode<"accentUnder">;
|
|
180
|
+
if (wideAccentLabels.has(label) && 'base' in group) {
|
|
185
181
|
// There are four SVG images available for each function.
|
|
186
182
|
// Choose a taller image when there are more characters.
|
|
187
|
-
const numChars =
|
|
188
|
-
|
|
183
|
+
const numChars = group.base.type === "ordgroup" ?
|
|
184
|
+
group.base.body.length : 1;
|
|
189
185
|
let viewBoxHeight;
|
|
190
186
|
let pathName;
|
|
191
187
|
let height;
|
|
@@ -232,6 +228,9 @@ export const stretchySvg = function(
|
|
|
232
228
|
const spans = [];
|
|
233
229
|
|
|
234
230
|
const data = katexImagesData[label];
|
|
231
|
+
if (!data) {
|
|
232
|
+
throw new Error(`No SVG data for "${label}".`);
|
|
233
|
+
}
|
|
235
234
|
const [paths, minWidth, viewBoxHeight] = data;
|
|
236
235
|
const height = viewBoxHeight / 1000;
|
|
237
236
|
|
|
@@ -239,11 +238,12 @@ export const stretchySvg = function(
|
|
|
239
238
|
let widthClasses;
|
|
240
239
|
let aligns;
|
|
241
240
|
if (numSvgChildren === 1) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
241
|
+
if (data.length !== 4) {
|
|
242
|
+
throw new Error(
|
|
243
|
+
`Expected 4-tuple for single-path SVG data "${label}".`);
|
|
244
|
+
}
|
|
245
245
|
widthClasses = ["hide-tail"];
|
|
246
|
-
aligns = [
|
|
246
|
+
aligns = [data[3]];
|
|
247
247
|
} else if (numSvgChildren === 2) {
|
|
248
248
|
widthClasses = ["halfarrow-left", "halfarrow-right"];
|
|
249
249
|
aligns = ["xMinYMin", "xMaxYMin"];
|