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/buildCommon.ts
CHANGED
|
@@ -13,11 +13,18 @@ import {DocumentFragment} from "./tree";
|
|
|
13
13
|
|
|
14
14
|
import type Options from "./Options";
|
|
15
15
|
import type {ParseNode} from "./parseNode";
|
|
16
|
-
import type {
|
|
17
|
-
import type {FontVariant, Mode} from "./types";
|
|
16
|
+
import type {Mode} from "./types";
|
|
18
17
|
import type {documentFragment as HtmlDocumentFragment} from "./domTree";
|
|
19
18
|
import type {HtmlDomNode, DomSpan, SvgSpan, CssStyle} from "./domTree";
|
|
20
19
|
import type {Measurement} from "./units";
|
|
20
|
+
import type {
|
|
21
|
+
CharacterMetrics,
|
|
22
|
+
FontName,
|
|
23
|
+
FontShape,
|
|
24
|
+
FontVariant,
|
|
25
|
+
FontWeight,
|
|
26
|
+
TextFont,
|
|
27
|
+
} from "./types/fonts";
|
|
21
28
|
|
|
22
29
|
/**
|
|
23
30
|
* Looks up the given symbol in fontMetrics, after applying any symbol
|
|
@@ -25,8 +32,7 @@ import type {Measurement} from "./units";
|
|
|
25
32
|
*/
|
|
26
33
|
const lookupSymbol = function(
|
|
27
34
|
value: string,
|
|
28
|
-
|
|
29
|
-
fontName: string,
|
|
35
|
+
fontName: FontName,
|
|
30
36
|
mode: Mode,
|
|
31
37
|
): {
|
|
32
38
|
value: string;
|
|
@@ -58,7 +64,7 @@ const lookupSymbol = function(
|
|
|
58
64
|
*/
|
|
59
65
|
export const makeSymbol = function(
|
|
60
66
|
value: string,
|
|
61
|
-
fontName:
|
|
67
|
+
fontName: FontName,
|
|
62
68
|
mode: Mode,
|
|
63
69
|
options?: Options,
|
|
64
70
|
classes?: string[],
|
|
@@ -132,18 +138,18 @@ export const mathsym = function(
|
|
|
132
138
|
* depending on the symbol. Use this function instead of fontMap for font
|
|
133
139
|
* "boldsymbol".
|
|
134
140
|
*/
|
|
135
|
-
const
|
|
141
|
+
const boldSymbol = function(
|
|
136
142
|
value: string,
|
|
137
143
|
mode: Mode,
|
|
138
|
-
options: Options,
|
|
139
|
-
classes: string[],
|
|
140
144
|
type: "mathord" | "textord",
|
|
141
145
|
): {
|
|
142
|
-
fontName:
|
|
143
|
-
fontClass:
|
|
146
|
+
fontName: "Math-BoldItalic" | "Main-Bold";
|
|
147
|
+
fontClass: "boldsymbol" | "mathbf";
|
|
144
148
|
} {
|
|
145
|
-
if (
|
|
146
|
-
|
|
149
|
+
if (
|
|
150
|
+
type !== "textord" &&
|
|
151
|
+
lookupSymbol(value, "Math-BoldItalic", mode).metrics
|
|
152
|
+
) {
|
|
147
153
|
return {
|
|
148
154
|
fontName: "Math-BoldItalic",
|
|
149
155
|
fontClass: "boldsymbol",
|
|
@@ -168,35 +174,39 @@ export const makeOrd = function<NODETYPE extends "spacing" | "mathord" | "textor
|
|
|
168
174
|
): HtmlDocumentFragment | SymbolNode {
|
|
169
175
|
const mode = group.mode;
|
|
170
176
|
const text = group.text;
|
|
171
|
-
|
|
172
177
|
const classes = ["mord"];
|
|
178
|
+
const {font, fontFamily, fontWeight, fontShape} = options;
|
|
173
179
|
|
|
174
180
|
// Math mode or Old font (i.e. \rm)
|
|
175
|
-
const
|
|
176
|
-
const fontOrFamily =
|
|
177
|
-
let wideFontName = "";
|
|
181
|
+
const useFont = mode === "math" || (mode === "text" && !!font);
|
|
182
|
+
const fontOrFamily = useFont ? font : fontFamily;
|
|
183
|
+
let wideFontName: FontName | "" = "";
|
|
178
184
|
let wideFontClass = "";
|
|
179
185
|
if (text.charCodeAt(0) === 0xD835) {
|
|
180
|
-
|
|
186
|
+
const wideCharData = wideCharacterFont(text);
|
|
187
|
+
wideFontName = wideCharData.font;
|
|
188
|
+
wideFontClass = wideCharData[`${mode}Class`];
|
|
181
189
|
}
|
|
182
|
-
if (wideFontName
|
|
190
|
+
if (wideFontName) {
|
|
183
191
|
// surrogate pairs get special treatment
|
|
184
|
-
return makeSymbol(text, wideFontName, mode, options,
|
|
185
|
-
classes.concat(wideFontClass));
|
|
192
|
+
return makeSymbol(text, wideFontName, mode, options, classes.concat(wideFontClass));
|
|
186
193
|
} else if (fontOrFamily) {
|
|
187
194
|
let fontName;
|
|
188
195
|
let fontClasses;
|
|
189
196
|
if (fontOrFamily === "boldsymbol") {
|
|
190
|
-
const fontData =
|
|
197
|
+
const fontData = boldSymbol(text, mode, type);
|
|
191
198
|
fontName = fontData.fontName;
|
|
192
199
|
fontClasses = [fontData.fontClass];
|
|
193
|
-
} else if (
|
|
194
|
-
fontName = fontMap[
|
|
195
|
-
fontClasses = [
|
|
200
|
+
} else if (useFont) {
|
|
201
|
+
fontName = fontMap[font].fontName;
|
|
202
|
+
fontClasses = [font];
|
|
196
203
|
} else {
|
|
197
|
-
fontName = retrieveTextFontName(
|
|
198
|
-
|
|
199
|
-
|
|
204
|
+
fontName = retrieveTextFontName(
|
|
205
|
+
fontFamily,
|
|
206
|
+
fontWeight,
|
|
207
|
+
fontShape,
|
|
208
|
+
);
|
|
209
|
+
fontClasses = [fontFamily, fontWeight, fontShape];
|
|
200
210
|
}
|
|
201
211
|
|
|
202
212
|
if (lookupSymbol(text, fontName, mode).metrics) {
|
|
@@ -221,24 +231,21 @@ export const makeOrd = function<NODETYPE extends "spacing" | "mathord" | "textor
|
|
|
221
231
|
} else if (type === "textord") {
|
|
222
232
|
const font = symbols[mode][text] && symbols[mode][text].font;
|
|
223
233
|
if (font === "ams") {
|
|
224
|
-
const fontName = retrieveTextFontName("amsrm",
|
|
225
|
-
options.fontShape);
|
|
234
|
+
const fontName = retrieveTextFontName("amsrm", fontWeight, fontShape);
|
|
226
235
|
return makeSymbol(
|
|
227
236
|
text, fontName, mode, options,
|
|
228
|
-
classes.concat("amsrm",
|
|
237
|
+
classes.concat("amsrm", fontWeight, fontShape));
|
|
229
238
|
} else if (font === "main" || !font) {
|
|
230
|
-
const fontName = retrieveTextFontName("textrm",
|
|
231
|
-
options.fontShape);
|
|
239
|
+
const fontName = retrieveTextFontName("textrm", fontWeight, fontShape);
|
|
232
240
|
return makeSymbol(
|
|
233
241
|
text, fontName, mode, options,
|
|
234
|
-
classes.concat(
|
|
242
|
+
classes.concat(fontWeight, fontShape));
|
|
235
243
|
} else { // fonts added by plugins
|
|
236
|
-
const fontName = retrieveTextFontName(font,
|
|
237
|
-
options.fontShape);
|
|
244
|
+
const fontName = retrieveTextFontName(font, fontWeight, fontShape);
|
|
238
245
|
// We add font name as a css class
|
|
239
246
|
return makeSymbol(
|
|
240
247
|
text, fontName, mode, options,
|
|
241
|
-
classes.concat(fontName,
|
|
248
|
+
classes.concat(fontName, fontWeight, fontShape));
|
|
242
249
|
}
|
|
243
250
|
} else {
|
|
244
251
|
throw new Error("unexpected type: " + type + " in makeOrd");
|
|
@@ -643,12 +650,14 @@ export const makeGlue = (measurement: Measurement, options: Options): DomSpan =>
|
|
|
643
650
|
};
|
|
644
651
|
|
|
645
652
|
// Takes font options, and returns the appropriate fontLookup name
|
|
646
|
-
const retrieveTextFontName =
|
|
647
|
-
fontFamily:
|
|
648
|
-
fontWeight:
|
|
649
|
-
fontShape:
|
|
650
|
-
):
|
|
651
|
-
let baseFontName
|
|
653
|
+
const retrieveTextFontName = (
|
|
654
|
+
fontFamily: TextFont,
|
|
655
|
+
fontWeight: FontWeight,
|
|
656
|
+
fontShape: FontShape,
|
|
657
|
+
): FontName => {
|
|
658
|
+
let baseFontName: "AMS" | "Main" | "SansSerif" | "Typewriter" | TextFont;
|
|
659
|
+
let fontStylesName: "BoldItalic" | "Bold" | "Italic" | "Regular";
|
|
660
|
+
|
|
652
661
|
switch (fontFamily) {
|
|
653
662
|
case "amsrm":
|
|
654
663
|
baseFontName = "AMS";
|
|
@@ -666,18 +675,17 @@ const retrieveTextFontName = function(
|
|
|
666
675
|
baseFontName = fontFamily; // use fonts added by a plugin
|
|
667
676
|
}
|
|
668
677
|
|
|
669
|
-
let fontStylesName;
|
|
670
678
|
if (fontWeight === "textbf" && fontShape === "textit") {
|
|
671
679
|
fontStylesName = "BoldItalic";
|
|
672
680
|
} else if (fontWeight === "textbf") {
|
|
673
681
|
fontStylesName = "Bold";
|
|
674
|
-
} else if (
|
|
682
|
+
} else if (fontShape === "textit") {
|
|
675
683
|
fontStylesName = "Italic";
|
|
676
684
|
} else {
|
|
677
685
|
fontStylesName = "Regular";
|
|
678
686
|
}
|
|
679
687
|
|
|
680
|
-
return `${baseFontName}-${fontStylesName}
|
|
688
|
+
return `${baseFontName}-${fontStylesName}` as FontName;
|
|
681
689
|
};
|
|
682
690
|
|
|
683
691
|
/**
|
|
@@ -688,7 +696,7 @@ const retrieveTextFontName = function(
|
|
|
688
696
|
// A map between tex font commands an MathML mathvariant attribute values
|
|
689
697
|
export const fontMap: Record<string, {
|
|
690
698
|
variant: FontVariant;
|
|
691
|
-
fontName:
|
|
699
|
+
fontName: FontName;
|
|
692
700
|
}> = {
|
|
693
701
|
// styles
|
|
694
702
|
"mathbf": {
|
package/src/buildHTML.ts
CHANGED
|
@@ -159,7 +159,8 @@ const traverseNonSpaceNodes = function(
|
|
|
159
159
|
const partialGroup = checkPartialGroup(node);
|
|
160
160
|
|
|
161
161
|
if (partialGroup) { // Recursive DFS
|
|
162
|
-
// TODO(ts):
|
|
162
|
+
// TODO(ts): partialGroup.children is ReadonlyArray but this
|
|
163
|
+
// function mutates the array (insertAfter splices into it).
|
|
163
164
|
traverseNonSpaceNodes(partialGroup.children as HtmlDomNode[],
|
|
164
165
|
callback, prev, null, isRoot);
|
|
165
166
|
continue;
|
|
@@ -266,8 +267,8 @@ export const buildGroup = function(
|
|
|
266
267
|
}
|
|
267
268
|
|
|
268
269
|
if (groupBuilders[group.type]) {
|
|
269
|
-
//
|
|
270
|
-
//
|
|
270
|
+
// TODO(ts): groupBuilders is Record<string, HtmlBuilder<any>>;
|
|
271
|
+
// a type-safe registry would need a mapped type keyed by NodeType.
|
|
271
272
|
let groupNode: HtmlDomNode = groupBuilders[group.type](group, options);
|
|
272
273
|
|
|
273
274
|
// If the size changed between the parent and the current group, account
|
package/src/buildMathML.ts
CHANGED
|
@@ -13,9 +13,10 @@ import {MathNode, TextNode} from "./mathMLTree";
|
|
|
13
13
|
|
|
14
14
|
import type Options from "./Options";
|
|
15
15
|
import type {AnyParseNode, SymbolParseNode} from "./parseNode";
|
|
16
|
-
import type {DomSpan} from "./domTree";
|
|
16
|
+
import type {DomSpan, HtmlDomNode} from "./domTree";
|
|
17
17
|
import type {MathDomNode} from "./mathMLTree";
|
|
18
|
-
import type {
|
|
18
|
+
import type {Mode} from "./types";
|
|
19
|
+
import type {FontVariant, MathFont} from "./types/fonts";
|
|
19
20
|
|
|
20
21
|
const noVariantSymbols = new Set(["\\imath", "\\jmath"]);
|
|
21
22
|
const rowLikeTypes = new Set(["mrow", "mtable"]);
|
|
@@ -52,36 +53,53 @@ export const makeRow = function(body: MathDomNode[]): MathDomNode {
|
|
|
52
53
|
}
|
|
53
54
|
};
|
|
54
55
|
|
|
56
|
+
const mathFontVariants: Partial<
|
|
57
|
+
Record<MathFont, FontVariant | ((group: SymbolParseNode) => FontVariant)>
|
|
58
|
+
> = {
|
|
59
|
+
mathit: "italic",
|
|
60
|
+
boldsymbol: (group) => (group.type === "textord" ? "bold" : "bold-italic"),
|
|
61
|
+
mathbf: "bold",
|
|
62
|
+
mathbb: "double-struck",
|
|
63
|
+
mathsfit: "sans-serif-italic",
|
|
64
|
+
mathfrak: "fraktur",
|
|
65
|
+
mathscr: "script",
|
|
66
|
+
mathcal: "script",
|
|
67
|
+
mathsf: "sans-serif",
|
|
68
|
+
mathtt: "monospace",
|
|
69
|
+
};
|
|
70
|
+
|
|
55
71
|
/**
|
|
56
72
|
* Returns the math variant as a string or null if none is required.
|
|
57
73
|
*/
|
|
58
|
-
export const getVariant =
|
|
74
|
+
export const getVariant = (
|
|
59
75
|
group: SymbolParseNode,
|
|
60
76
|
options: Options,
|
|
61
|
-
): FontVariant | null | undefined {
|
|
77
|
+
): FontVariant | null | undefined => {
|
|
62
78
|
// Handle \text... font specifiers as best we can.
|
|
63
79
|
// MathML has a limited list of allowable mathvariant specifiers; see
|
|
64
80
|
// https://www.w3.org/TR/MathML3/chapter3.html#presm.commatt
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (options.
|
|
69
|
-
options.
|
|
70
|
-
|
|
81
|
+
if (group.mode === "text") {
|
|
82
|
+
if (options.fontFamily === "texttt") {
|
|
83
|
+
return "monospace";
|
|
84
|
+
} else if (options.fontFamily === "textsf") {
|
|
85
|
+
if (options.fontShape === "textit" &&
|
|
86
|
+
options.fontWeight === "textbf") {
|
|
87
|
+
return "sans-serif-bold-italic";
|
|
88
|
+
} else if (options.fontShape === "textit") {
|
|
89
|
+
return "sans-serif-italic";
|
|
90
|
+
} else if (options.fontWeight === "textbf") {
|
|
91
|
+
return "bold-sans-serif";
|
|
92
|
+
} else {
|
|
93
|
+
return "sans-serif";
|
|
94
|
+
}
|
|
95
|
+
} else if (options.fontShape === "textit" &&
|
|
96
|
+
options.fontWeight === "textbf") {
|
|
97
|
+
return "bold-italic";
|
|
71
98
|
} else if (options.fontShape === "textit") {
|
|
72
|
-
return "
|
|
99
|
+
return "italic";
|
|
73
100
|
} else if (options.fontWeight === "textbf") {
|
|
74
|
-
return "bold
|
|
75
|
-
} else {
|
|
76
|
-
return "sans-serif";
|
|
101
|
+
return "bold";
|
|
77
102
|
}
|
|
78
|
-
} else if (options.fontShape === "textit" &&
|
|
79
|
-
options.fontWeight === "textbf") {
|
|
80
|
-
return "bold-italic";
|
|
81
|
-
} else if (options.fontShape === "textit") {
|
|
82
|
-
return "italic";
|
|
83
|
-
} else if (options.fontWeight === "textbf") {
|
|
84
|
-
return "bold";
|
|
85
103
|
}
|
|
86
104
|
|
|
87
105
|
const font = options.font;
|
|
@@ -90,25 +108,12 @@ export const getVariant = function(
|
|
|
90
108
|
}
|
|
91
109
|
|
|
92
110
|
const mode = group.mode;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
} else if (font === "mathbb") {
|
|
100
|
-
return "double-struck";
|
|
101
|
-
} else if (font === "mathsfit") {
|
|
102
|
-
return "sans-serif-italic";
|
|
103
|
-
} else if (font === "mathfrak") {
|
|
104
|
-
return "fraktur";
|
|
105
|
-
} else if (font === "mathscr" || font === "mathcal") {
|
|
106
|
-
// MathML makes no distinction between script and calligraphic
|
|
107
|
-
return "script";
|
|
108
|
-
} else if (font === "mathsf") {
|
|
109
|
-
return "sans-serif";
|
|
110
|
-
} else if (font === "mathtt") {
|
|
111
|
-
return "monospace";
|
|
111
|
+
const mathVariant = mathFontVariants[font];
|
|
112
|
+
|
|
113
|
+
if (mathVariant) {
|
|
114
|
+
return typeof mathVariant === "function"
|
|
115
|
+
? mathVariant(group)
|
|
116
|
+
: mathVariant;
|
|
112
117
|
}
|
|
113
118
|
|
|
114
119
|
let text = group.text;
|
|
@@ -257,11 +262,10 @@ export const buildGroup = function(
|
|
|
257
262
|
}
|
|
258
263
|
|
|
259
264
|
if (groupBuilders[group.type]) {
|
|
260
|
-
//
|
|
261
|
-
//
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
return result as MathNode;
|
|
265
|
+
// TODO(ts): MathMLBuilder returns MathDomNode but all concrete
|
|
266
|
+
// builders return MathNode. Widening the return type here would
|
|
267
|
+
// require updating all callers that assume MathNode.
|
|
268
|
+
return groupBuilders[group.type](group, options) as MathNode;
|
|
265
269
|
} else {
|
|
266
270
|
throw new ParseError(
|
|
267
271
|
"Got group of unknown type: '" + group.type + "'");
|
|
@@ -319,7 +323,10 @@ export default function buildMathML(
|
|
|
319
323
|
// NOTE: The span class is not typed to have <math> nodes as children, and
|
|
320
324
|
// we don't want to make the children type more generic since the children
|
|
321
325
|
// of span are expected to have more fields in `buildHtml` contexts.
|
|
326
|
+
// The MathNode implements VirtualNode (toNode/toMarkup) which is all that
|
|
327
|
+
// Span needs from its children for rendering.
|
|
328
|
+
// TODO(ts): Span's child type is HtmlDomNode, but MathNode only implements
|
|
329
|
+
// VirtualNode. The double-cast acknowledges this architectural limitation.
|
|
322
330
|
const wrapperClass = forMathmlOnly ? "katex" : "katex-mathml";
|
|
323
|
-
|
|
324
|
-
return makeSpan([wrapperClass], [math as any]);
|
|
331
|
+
return makeSpan([wrapperClass], [math as unknown as HtmlDomNode]);
|
|
325
332
|
}
|
package/src/defineEnvironment.ts
CHANGED
|
@@ -62,7 +62,7 @@ export type EnvSpec<NODETYPE extends NodeType> = {
|
|
|
62
62
|
* `environments.js` exports this same dictionary again and makes it public.
|
|
63
63
|
* `Parser.js` requires this dictionary via `environments.js`.
|
|
64
64
|
*/
|
|
65
|
-
export const _environments: Record<string, EnvSpec<
|
|
65
|
+
export const _environments: Record<string, EnvSpec<NodeType>> = {};
|
|
66
66
|
|
|
67
67
|
type EnvDefSpec<NODETYPE extends NodeType> = {
|
|
68
68
|
// Unique string to differentiate parse nodes.
|
package/src/defineFunction.ts
CHANGED
|
@@ -130,7 +130,7 @@ export type FunctionSpec<NODETYPE extends NodeType> = {
|
|
|
130
130
|
// _functions is typed FunctionSpec<*> (it stores all TeX function specs).
|
|
131
131
|
|
|
132
132
|
// Must be specified unless it's handled directly in the parser.
|
|
133
|
-
handler: FunctionHandler<
|
|
133
|
+
handler: FunctionHandler<NodeType> | null | undefined;
|
|
134
134
|
};
|
|
135
135
|
|
|
136
136
|
/**
|
|
@@ -138,18 +138,25 @@ export type FunctionSpec<NODETYPE extends NodeType> = {
|
|
|
138
138
|
* `functions.js` just exports this same dictionary again and makes it public.
|
|
139
139
|
* `Parser.js` requires this dictionary.
|
|
140
140
|
*/
|
|
141
|
-
export const _functions: Record<string, FunctionSpec<
|
|
141
|
+
export const _functions: Record<string, FunctionSpec<NodeType>> = {};
|
|
142
142
|
|
|
143
143
|
/**
|
|
144
144
|
* All HTML builders. Should be only used in the `define*` and the `build*ML`
|
|
145
145
|
* functions.
|
|
146
|
+
*
|
|
147
|
+
* Builders for different node types are stored side by side, but
|
|
148
|
+
* `HtmlBuilder<T>` is contravariant in `T`, so there is no single type
|
|
149
|
+
* argument that makes storing/retrieving them typecheck. `any` is used
|
|
150
|
+
* as an existential-quantifier escape hatch.
|
|
146
151
|
*/
|
|
152
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
147
153
|
export const _htmlGroupBuilders: Record<string, HtmlBuilder<any>> = {};
|
|
148
154
|
|
|
149
155
|
/**
|
|
150
156
|
* All MathML builders. Should be only used in the `define*` and the `build*ML`
|
|
151
|
-
* functions.
|
|
157
|
+
* functions. See `_htmlGroupBuilders` above for the rationale behind `any`.
|
|
152
158
|
*/
|
|
159
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
153
160
|
export const _mathmlGroupBuilders: Record<string, MathMLBuilder<any>> = {};
|
|
154
161
|
|
|
155
162
|
export default function defineFunction<NODETYPE extends NodeType>({
|
package/src/delimiter.ts
CHANGED
|
@@ -32,11 +32,11 @@ import {makeEm} from "./units";
|
|
|
32
32
|
import fontMetricsData from "./fontMetricsData";
|
|
33
33
|
|
|
34
34
|
import type Options from "./Options";
|
|
35
|
-
import type {CharacterMetrics} from "./fontMetrics";
|
|
36
35
|
import type {HtmlDomNode, DomSpan, SvgSpan} from "./domTree";
|
|
37
|
-
import type {Mode} from "./types";
|
|
36
|
+
import type {DelimiterSize, Mode} from "./types";
|
|
38
37
|
import type {StyleInterface} from "./Style";
|
|
39
38
|
import type {VListElem} from "./buildCommon";
|
|
39
|
+
import type {CharacterMetrics, FontName} from "./types/fonts";
|
|
40
40
|
|
|
41
41
|
type StackedDelimiterFont = "Size1-Regular" | "Size4-Regular";
|
|
42
42
|
|
|
@@ -125,12 +125,11 @@ const makeSmallDelim = function(
|
|
|
125
125
|
*/
|
|
126
126
|
const mathrmSize = function(
|
|
127
127
|
value: string,
|
|
128
|
-
size:
|
|
128
|
+
size: DelimiterSize,
|
|
129
129
|
mode: Mode,
|
|
130
130
|
options: Options,
|
|
131
131
|
): SymbolNode {
|
|
132
|
-
return makeSymbol(value,
|
|
133
|
-
mode, options);
|
|
132
|
+
return makeSymbol(value, `Size${size}-Regular`, mode, options);
|
|
134
133
|
};
|
|
135
134
|
|
|
136
135
|
/**
|
|
@@ -138,7 +137,7 @@ const mathrmSize = function(
|
|
|
138
137
|
* Size3, or Size4 fonts. It is always rendered in textstyle.
|
|
139
138
|
*/
|
|
140
139
|
const makeLargeDelim = function(delim: string,
|
|
141
|
-
size:
|
|
140
|
+
size: DelimiterSize,
|
|
142
141
|
center: boolean,
|
|
143
142
|
options: Options,
|
|
144
143
|
mode: Mode,
|
|
@@ -507,9 +506,9 @@ export const makeSqrtImage = function(
|
|
|
507
506
|
|
|
508
507
|
// Create a span containing an SVG image of a sqrt symbol.
|
|
509
508
|
let span;
|
|
510
|
-
let spanHeight
|
|
511
|
-
let texHeight
|
|
512
|
-
let viewBoxHeight
|
|
509
|
+
let spanHeight;
|
|
510
|
+
let texHeight;
|
|
511
|
+
let viewBoxHeight;
|
|
513
512
|
let advanceWidth;
|
|
514
513
|
|
|
515
514
|
// We create viewBoxes with 80 units of "padding" above each surd.
|
|
@@ -608,7 +607,7 @@ export const sizeToMaxHeight = [0, 1.2, 1.8, 2.4, 3.0];
|
|
|
608
607
|
*/
|
|
609
608
|
export const makeSizedDelim = function(
|
|
610
609
|
delim: string,
|
|
611
|
-
size:
|
|
610
|
+
size: DelimiterSize,
|
|
612
611
|
options: Options,
|
|
613
612
|
mode: Mode,
|
|
614
613
|
classes: string[],
|
|
@@ -621,12 +620,17 @@ export const makeSizedDelim = function(
|
|
|
621
620
|
}
|
|
622
621
|
|
|
623
622
|
// Sized delimiters are never centered.
|
|
624
|
-
if (stackLargeDelimiters.has(delim) ||
|
|
625
|
-
stackNeverDelimiters.has(delim)) {
|
|
623
|
+
if (stackLargeDelimiters.has(delim) || stackNeverDelimiters.has(delim)) {
|
|
626
624
|
return makeLargeDelim(delim, size, false, options, mode, classes);
|
|
627
625
|
} else if (stackAlwaysDelimiters.has(delim)) {
|
|
628
626
|
return makeStackedDelim(
|
|
629
|
-
delim,
|
|
627
|
+
delim,
|
|
628
|
+
sizeToMaxHeight[size],
|
|
629
|
+
false,
|
|
630
|
+
options,
|
|
631
|
+
mode,
|
|
632
|
+
classes,
|
|
633
|
+
);
|
|
630
634
|
} else {
|
|
631
635
|
throw new ParseError("Illegal delimiter: '" + delim + "'");
|
|
632
636
|
}
|
package/src/domTree.ts
CHANGED
|
@@ -28,6 +28,21 @@ export const createClass = function(classes: string[]): string {
|
|
|
28
28
|
return classes.filter(cls => cls).join(" ");
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Serialize a CssStyle object into a semicolon-delimited inline-style string
|
|
33
|
+
* (hyphenating camelCase property names). Returns "" when no property is set.
|
|
34
|
+
*/
|
|
35
|
+
const cssStyleToString = function(style: CssStyle): string {
|
|
36
|
+
let styles = "";
|
|
37
|
+
for (const key of Object.keys(style) as Array<keyof CssStyle>) {
|
|
38
|
+
const value = style[key];
|
|
39
|
+
if (value !== undefined) {
|
|
40
|
+
styles += `${hyphenate(key)}:${value};`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return styles;
|
|
44
|
+
};
|
|
45
|
+
|
|
31
46
|
type InitNodeData = {
|
|
32
47
|
classes: string[];
|
|
33
48
|
attributes: Record<string, string>;
|
|
@@ -74,9 +89,7 @@ const toNode = function(this: HtmlNodeData, tagName: string): HTMLElement {
|
|
|
74
89
|
node.className = createClass(this.classes);
|
|
75
90
|
|
|
76
91
|
// Apply inline styles
|
|
77
|
-
|
|
78
|
-
(node.style as any)[key] = this.style[key];
|
|
79
|
-
}
|
|
92
|
+
Object.assign(node.style, this.style);
|
|
80
93
|
|
|
81
94
|
// Apply attributes
|
|
82
95
|
for (const attr of Object.keys(this.attributes)) {
|
|
@@ -112,13 +125,7 @@ const toMarkup = function(this: HtmlNodeData, tagName: string): string {
|
|
|
112
125
|
markup += ` class="${escape(createClass(this.classes))}"`;
|
|
113
126
|
}
|
|
114
127
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
// Add the styles, after hyphenation
|
|
118
|
-
for (const key of Object.keys(this.style) as Array<keyof CssStyle>) {
|
|
119
|
-
styles += `${hyphenate(key)}:${this.style[key]};`;
|
|
120
|
-
}
|
|
121
|
-
|
|
128
|
+
const styles = cssStyleToString(this.style);
|
|
122
129
|
if (styles) {
|
|
123
130
|
markup += ` style="${escape(styles)}"`;
|
|
124
131
|
}
|
|
@@ -211,6 +218,12 @@ export class Span<ChildType extends VirtualNode> implements HtmlDomNode {
|
|
|
211
218
|
width: number | null | undefined;
|
|
212
219
|
maxFontSize!: number;
|
|
213
220
|
style!: CssStyle;
|
|
221
|
+
/**
|
|
222
|
+
* Italic correction carried over from a SymbolNode when the symbol is
|
|
223
|
+
* wrapped in a vlist (e.g. \oiint / \oiiint). Read by supsub to adjust
|
|
224
|
+
* subscript positioning. Only set when nonzero; use `?? 0` at read sites.
|
|
225
|
+
*/
|
|
226
|
+
italic?: number;
|
|
214
227
|
|
|
215
228
|
constructor(
|
|
216
229
|
classes?: string[],
|
|
@@ -322,9 +335,7 @@ export class Img implements VirtualNode {
|
|
|
322
335
|
node.className = "mord";
|
|
323
336
|
|
|
324
337
|
// Apply inline styles
|
|
325
|
-
|
|
326
|
-
(node.style as any)[key] = this.style[key];
|
|
327
|
-
}
|
|
338
|
+
Object.assign(node.style, this.style);
|
|
328
339
|
|
|
329
340
|
return node;
|
|
330
341
|
}
|
|
@@ -333,11 +344,7 @@ export class Img implements VirtualNode {
|
|
|
333
344
|
let markup = `<img src="${escape(this.src)}"` +
|
|
334
345
|
` alt="${escape(this.alt)}"`;
|
|
335
346
|
|
|
336
|
-
|
|
337
|
-
let styles = "";
|
|
338
|
-
for (const key of Object.keys(this.style) as Array<keyof CssStyle>) {
|
|
339
|
-
styles += `${hyphenate(key)}:${this.style[key]};`;
|
|
340
|
-
}
|
|
347
|
+
const styles = cssStyleToString(this.style);
|
|
341
348
|
if (styles) {
|
|
342
349
|
markup += ` style="${escape(styles)}"`;
|
|
343
350
|
}
|
|
@@ -430,9 +437,9 @@ export class SymbolNode implements HtmlDomNode {
|
|
|
430
437
|
span.className = createClass(this.classes);
|
|
431
438
|
}
|
|
432
439
|
|
|
433
|
-
|
|
440
|
+
if (Object.keys(this.style).length > 0) {
|
|
434
441
|
span = span || document.createElement("span");
|
|
435
|
-
(span.style
|
|
442
|
+
Object.assign(span.style, this.style);
|
|
436
443
|
}
|
|
437
444
|
|
|
438
445
|
if (span) {
|
|
@@ -465,9 +472,7 @@ export class SymbolNode implements HtmlDomNode {
|
|
|
465
472
|
if (this.italic > 0) {
|
|
466
473
|
styles += `margin-right:${makeEm(this.italic)};`;
|
|
467
474
|
}
|
|
468
|
-
|
|
469
|
-
styles += hyphenate(key) + ":" + this.style[key] + ";";
|
|
470
|
-
}
|
|
475
|
+
styles += cssStyleToString(this.style);
|
|
471
476
|
|
|
472
477
|
if (styles) {
|
|
473
478
|
needsSpan = true;
|
|
@@ -168,7 +168,7 @@ function parseArray(
|
|
|
168
168
|
// Test for \hline at the top of the array.
|
|
169
169
|
hLinesBeforeRow.push(getHLines(parser));
|
|
170
170
|
|
|
171
|
-
while (true) {
|
|
171
|
+
while (true) {
|
|
172
172
|
// Parse each cell in its own group (namespace)
|
|
173
173
|
const cellBody = parser.parseExpression(false, singleRow ? "\\end" : "\\\\");
|
|
174
174
|
parser.gullet.endGroup();
|
|
@@ -183,6 +183,7 @@ function parseArray(
|
|
|
183
183
|
type: "styling",
|
|
184
184
|
mode: parser.mode,
|
|
185
185
|
style,
|
|
186
|
+
resetFont: true,
|
|
186
187
|
body: [cell],
|
|
187
188
|
};
|
|
188
189
|
}
|
|
@@ -275,7 +276,7 @@ function dCellStyle(envName: string): StyleStr {
|
|
|
275
276
|
}
|
|
276
277
|
|
|
277
278
|
type Outrow = {
|
|
278
|
-
|
|
279
|
+
cells: HtmlDomNode[];
|
|
279
280
|
height: number;
|
|
280
281
|
depth: number;
|
|
281
282
|
pos: number;
|
|
@@ -287,7 +288,7 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
|
|
|
287
288
|
const nr = group.body.length;
|
|
288
289
|
const hLinesBeforeRow = group.hLinesBeforeRow;
|
|
289
290
|
let nc = 0;
|
|
290
|
-
const body = new Array(nr);
|
|
291
|
+
const body: Outrow[] = new Array(nr);
|
|
291
292
|
const hlines: Array<{pos: number; isDashed: boolean}> = [];
|
|
292
293
|
const ruleThickness = Math.max(
|
|
293
294
|
// From LaTeX \showthe\arrayrulewidth. Equals 0.04 em.
|
|
@@ -341,7 +342,12 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
|
|
|
341
342
|
nc = inrow.length;
|
|
342
343
|
}
|
|
343
344
|
|
|
344
|
-
const outrow: Outrow =
|
|
345
|
+
const outrow: Outrow = {
|
|
346
|
+
cells: new Array<HtmlDomNode>(inrow.length),
|
|
347
|
+
height: 0,
|
|
348
|
+
depth: 0,
|
|
349
|
+
pos: 0,
|
|
350
|
+
};
|
|
345
351
|
for (c = 0; c < inrow.length; ++c) {
|
|
346
352
|
const elt = html.buildGroup(inrow[c], options);
|
|
347
353
|
if (depth < elt.depth) {
|
|
@@ -350,7 +356,7 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
|
|
|
350
356
|
if (height < elt.height) {
|
|
351
357
|
height = elt.height;
|
|
352
358
|
}
|
|
353
|
-
outrow[c] = elt;
|
|
359
|
+
outrow.cells[c] = elt;
|
|
354
360
|
}
|
|
355
361
|
|
|
356
362
|
const rowGap = group.rowGaps[r];
|
|
@@ -480,7 +486,7 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
|
|
|
480
486
|
}> = [];
|
|
481
487
|
for (r = 0; r < nr; ++r) {
|
|
482
488
|
const row = body[r];
|
|
483
|
-
const elem = row[c];
|
|
489
|
+
const elem = row.cells[c];
|
|
484
490
|
if (!elem) {
|
|
485
491
|
continue;
|
|
486
492
|
}
|