mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-05 03:08:40 +00:00
Improve MathML for classes (#1929)
* Improve MathML for classes * Fix lint errors * Fix another lint error * Simplify MathML resulting from single character box * Fix lint errors * make some of the arrays in our html/mathml helper functions readonly
This commit is contained in:
@@ -68,7 +68,7 @@ export const buildExpression = function(
|
||||
for (let i = 0; i < expression.length; i++) {
|
||||
const output = buildGroup(expression[i], options);
|
||||
if (output instanceof DocumentFragment) {
|
||||
const children: HtmlDomNode[] = output.children;
|
||||
const children: $ReadOnlyArray<HtmlDomNode> = output.children;
|
||||
groups.push(...children);
|
||||
} else {
|
||||
groups.push(output);
|
||||
@@ -154,6 +154,7 @@ const traverseNonSpaceNodes = function(
|
||||
const node = nodes[i];
|
||||
const partialGroup = checkPartialGroup(node);
|
||||
if (partialGroup) { // Recursive DFS
|
||||
// $FlowFixMe: make nodes a $ReadOnlyArray by returning a new array
|
||||
traverseNonSpaceNodes(partialGroup.children, callback, prev);
|
||||
continue;
|
||||
}
|
||||
|
@@ -44,7 +44,7 @@ export const makeText = function(
|
||||
* Wrap the given array of nodes in an <mrow> node if needed, i.e.,
|
||||
* unless the array has length 1. Always returns a single node.
|
||||
*/
|
||||
export const makeRow = function(body: MathDomNode[]): MathDomNode {
|
||||
export const makeRow = function(body: $ReadOnlyArray<MathDomNode>): MathDomNode {
|
||||
if (body.length === 1) {
|
||||
return body[0];
|
||||
} else {
|
||||
@@ -134,7 +134,19 @@ export const getVariant = function(
|
||||
export const buildExpression = function(
|
||||
expression: AnyParseNode[],
|
||||
options: Options,
|
||||
): MathDomNode[] {
|
||||
isOrdgroup?: boolean,
|
||||
): MathNode[] {
|
||||
if (expression.length === 1) {
|
||||
const group = buildGroup(expression[0], options);
|
||||
if (isOrdgroup && group instanceof MathNode && group.type === "mo") {
|
||||
// When TeX writers want to suppress spacing on an operator,
|
||||
// they often put the operator by itself inside braces.
|
||||
group.setAttribute("lspace", "0em");
|
||||
group.setAttribute("rspace", "0em");
|
||||
}
|
||||
return [group];
|
||||
}
|
||||
|
||||
const groups = [];
|
||||
let lastGroup;
|
||||
for (let i = 0; i < expression.length; i++) {
|
||||
@@ -186,8 +198,9 @@ export const buildExpression = function(
|
||||
export const buildExpressionRow = function(
|
||||
expression: AnyParseNode[],
|
||||
options: Options,
|
||||
isOrdgroup?: boolean,
|
||||
): MathDomNode {
|
||||
return makeRow(buildExpression(expression, options));
|
||||
return makeRow(buildExpression(expression, options, isOrdgroup));
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -197,7 +210,7 @@ export const buildExpressionRow = function(
|
||||
export const buildGroup = function(
|
||||
group: ?AnyParseNode,
|
||||
options: Options,
|
||||
): MathDomNode {
|
||||
): MathNode {
|
||||
if (!group) {
|
||||
return new mathMLTree.MathNode("mrow");
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@
|
||||
|
||||
import {binrelClass} from "./mclass";
|
||||
import defineFunction from "../defineFunction";
|
||||
import utils from "../utils";
|
||||
|
||||
import * as html from "../buildHTML";
|
||||
import * as mml from "../buildMathML";
|
||||
@@ -71,6 +72,7 @@ defineFunction({
|
||||
},
|
||||
handler: ({parser}, args) => {
|
||||
const body = args[0];
|
||||
const isCharacterBox = utils.isCharacterBox(body);
|
||||
// amsbsy.sty's \boldsymbol uses \binrel spacing to inherit the
|
||||
// argument's bin|rel|ord status
|
||||
return {
|
||||
@@ -85,6 +87,7 @@ defineFunction({
|
||||
body,
|
||||
},
|
||||
],
|
||||
isCharacterBox: isCharacterBox,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
@@ -2,6 +2,7 @@
|
||||
import defineFunction, {ordargument} from "../defineFunction";
|
||||
import buildCommon from "../buildCommon";
|
||||
import mathMLTree from "../mathMLTree";
|
||||
import utils from "../utils";
|
||||
import type {AnyParseNode} from "../parseNode";
|
||||
|
||||
import * as html from "../buildHTML";
|
||||
@@ -17,8 +18,42 @@ function htmlBuilder(group: ParseNode<"mclass">, options) {
|
||||
}
|
||||
|
||||
function mathmlBuilder(group: ParseNode<"mclass">, options) {
|
||||
let node: mathMLTree.MathNode;
|
||||
const inner = mml.buildExpression(group.body, options);
|
||||
return mathMLTree.newDocumentFragment(inner);
|
||||
|
||||
if (group.mclass === "minner") {
|
||||
return mathMLTree.newDocumentFragment(inner);
|
||||
} else if (group.mclass === "mord") {
|
||||
if (group.isCharacterBox) {
|
||||
node = inner[0];
|
||||
node.type = "mi";
|
||||
} else {
|
||||
node = new mathMLTree.MathNode("mi", inner);
|
||||
}
|
||||
} else {
|
||||
if (group.isCharacterBox) {
|
||||
node = inner[0];
|
||||
node.type = "mo";
|
||||
} else {
|
||||
node = new mathMLTree.MathNode("mo", inner);
|
||||
}
|
||||
|
||||
// Set spacing based on what is the most likely adjacent atom type.
|
||||
// See TeXbook p170.
|
||||
if (group.mclass === "mbin") {
|
||||
node.attributes.lspace = "0.22em"; // medium space
|
||||
node.attributes.rspace = "0.22em";
|
||||
} else if (group.mclass === "mpunct") {
|
||||
node.attributes.lspace = "0em";
|
||||
node.attributes.rspace = "0.17em"; // thinspace
|
||||
} else if (group.mclass === "mopen" || group.mclass === "mclose") {
|
||||
node.attributes.lspace = "0em";
|
||||
node.attributes.rspace = "0em";
|
||||
}
|
||||
// MathML <mo> default space is 5/18 em, so <mrel> needs no action.
|
||||
// Ref: https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mo
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
// Math class commands except \mathop
|
||||
@@ -38,6 +73,7 @@ defineFunction({
|
||||
mode: parser.mode,
|
||||
mclass: "m" + funcName.substr(5),
|
||||
body: ordargument(body),
|
||||
isCharacterBox: utils.isCharacterBox(body),
|
||||
};
|
||||
},
|
||||
htmlBuilder,
|
||||
@@ -71,6 +107,7 @@ defineFunction({
|
||||
mode: parser.mode,
|
||||
mclass: binrelClass(args[0]),
|
||||
body: [args[1]],
|
||||
isCharacterBox: utils.isCharacterBox(args[1]),
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -118,6 +155,7 @@ defineFunction({
|
||||
mode: parser.mode,
|
||||
mclass,
|
||||
body: [supsub],
|
||||
isCharacterBox: utils.isCharacterBox(supsub),
|
||||
};
|
||||
},
|
||||
htmlBuilder,
|
||||
|
@@ -16,7 +16,7 @@ defineFunctionBuilders({
|
||||
["mord"], html.buildExpression(group.body, options, true), options);
|
||||
},
|
||||
mathmlBuilder(group, options) {
|
||||
return mml.buildExpressionRow(group.body, options);
|
||||
return mml.buildExpressionRow(group.body, options, true);
|
||||
},
|
||||
});
|
||||
|
||||
|
@@ -32,7 +32,9 @@ export interface MathDomNode extends VirtualNode {
|
||||
}
|
||||
|
||||
export type documentFragment = DocumentFragment<MathDomNode>;
|
||||
export function newDocumentFragment(children: MathDomNode[]): documentFragment {
|
||||
export function newDocumentFragment(
|
||||
children: $ReadOnlyArray<MathDomNode>
|
||||
): documentFragment {
|
||||
return new DocumentFragment(children);
|
||||
}
|
||||
|
||||
@@ -44,9 +46,9 @@ export function newDocumentFragment(children: MathDomNode[]): documentFragment {
|
||||
export class MathNode implements MathDomNode {
|
||||
type: MathNodeType;
|
||||
attributes: {[string]: string};
|
||||
children: MathDomNode[];
|
||||
children: $ReadOnlyArray<MathDomNode>;
|
||||
|
||||
constructor(type: MathNodeType, children?: MathDomNode[]) {
|
||||
constructor(type: MathNodeType, children?: $ReadOnlyArray<MathDomNode>) {
|
||||
this.type = type;
|
||||
this.attributes = {};
|
||||
this.children = children || [];
|
||||
|
@@ -348,6 +348,7 @@ type ParseNodeTypes = {
|
||||
loc?: ?SourceLocation,
|
||||
mclass: string,
|
||||
body: AnyParseNode[],
|
||||
isCharacterBox: boolean,
|
||||
|},
|
||||
"operatorname": {|
|
||||
type: "operatorname",
|
||||
|
@@ -20,7 +20,7 @@ export interface VirtualNode {
|
||||
*/
|
||||
export class DocumentFragment<ChildType: VirtualNode>
|
||||
implements HtmlDomNode, MathDomNode {
|
||||
children: ChildType[];
|
||||
children: $ReadOnlyArray<ChildType>;
|
||||
// HtmlDomNode
|
||||
classes: string[];
|
||||
height: number;
|
||||
@@ -28,7 +28,7 @@ export class DocumentFragment<ChildType: VirtualNode>
|
||||
maxFontSize: number;
|
||||
style: CssStyle; // Never used; needed for satisfying interface.
|
||||
|
||||
constructor(children: ChildType[]) {
|
||||
constructor(children: $ReadOnlyArray<ChildType>) {
|
||||
this.children = children;
|
||||
this.classes = [];
|
||||
this.height = 0;
|
||||
|
@@ -8,15 +8,15 @@ exports[`A MathML builder \\html@mathml makes clean symbols 1`] = `
|
||||
<mtext>
|
||||
©
|
||||
</mtext>
|
||||
<mi mathvariant="normal">
|
||||
<mo mathvariant="normal">
|
||||
≠
|
||||
</mi>
|
||||
<mi mathvariant="normal">
|
||||
</mo>
|
||||
<mo mathvariant="normal">
|
||||
∉
|
||||
</mi>
|
||||
<mi mathvariant="normal">
|
||||
</mo>
|
||||
<mo mathvariant="normal">
|
||||
≘
|
||||
</mi>
|
||||
</mo>
|
||||
<mtext>
|
||||
KaTeX
|
||||
</mtext>
|
||||
@@ -306,7 +306,10 @@ exports[`A MathML builder should make prime operators into <mo> nodes 1`] = `
|
||||
<mi>
|
||||
f
|
||||
</mi>
|
||||
<mo mathvariant="normal">
|
||||
<mo mathvariant="normal"
|
||||
lspace="0em"
|
||||
rspace="0em"
|
||||
>
|
||||
′
|
||||
</mo>
|
||||
</msup>
|
||||
@@ -394,32 +397,34 @@ exports[`A MathML builder should render boldsymbol with the correct mathvariants
|
||||
<math xmlns="http://www.w3.org/1998/Math/MathML">
|
||||
<semantics>
|
||||
<mrow>
|
||||
<mrow>
|
||||
<mi mathvariant="bold-italic">
|
||||
A
|
||||
</mi>
|
||||
<mi mathvariant="bold-italic">
|
||||
x
|
||||
</mi>
|
||||
<mn mathvariant="bold-italic">
|
||||
2
|
||||
</mn>
|
||||
<mi mathvariant="bold-italic">
|
||||
k
|
||||
</mi>
|
||||
<mi mathvariant="bold-italic">
|
||||
ω
|
||||
</mi>
|
||||
<mi mathvariant="bold-italic">
|
||||
Ω
|
||||
</mi>
|
||||
<mi mathvariant="bold-italic">
|
||||
ı
|
||||
</mi>
|
||||
<mo mathvariant="bold-italic">
|
||||
+
|
||||
</mo>
|
||||
</mrow>
|
||||
<mi>
|
||||
<mrow>
|
||||
<mi mathvariant="bold-italic">
|
||||
A
|
||||
</mi>
|
||||
<mi mathvariant="bold-italic">
|
||||
x
|
||||
</mi>
|
||||
<mn mathvariant="bold-italic">
|
||||
2
|
||||
</mn>
|
||||
<mi mathvariant="bold-italic">
|
||||
k
|
||||
</mi>
|
||||
<mi mathvariant="bold-italic">
|
||||
ω
|
||||
</mi>
|
||||
<mi mathvariant="bold-italic">
|
||||
Ω
|
||||
</mi>
|
||||
<mi mathvariant="bold-italic">
|
||||
ı
|
||||
</mi>
|
||||
<mo mathvariant="bold-italic">
|
||||
+
|
||||
</mo>
|
||||
</mrow>
|
||||
</mi>
|
||||
</mrow>
|
||||
<annotation encoding="application/x-tex">
|
||||
\\boldsymbol{Ax2k\\omega\\Omega\\imath+}
|
||||
|
Reference in New Issue
Block a user