feat: Support \vcenter and \hbox (#2452)
* Support \vcenter, \raise, \lower, and \hbox * Update screenshots * Edit docs for strict and hbox to * Fix typo for \hbox to * Update Safari screenshot * Augment docs for \vcentcolon * Edit vcenter MathML comment. * Remove pointless class from vcenter MathML * Withdraw \raise and \lower * Updatae Chrome and Firefox screenshots * Update Safari screenshot * Delete allowedInArgument setting Co-authored-by: ylemkimon <y@ylem.kim> * Update Chrome and Firefox screenshots * Update Chrome and Firefox screenshots take 2 * Update screenshot Co-authored-by: ylemkimon <y@ylem.kim>
@@ -327,6 +327,11 @@ const handleObject = (
|
||||
break;
|
||||
}
|
||||
|
||||
case "hbox": {
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "kern": {
|
||||
// No op: we don't attempt to present kerning information
|
||||
// to the screen reader.
|
||||
@@ -546,6 +551,11 @@ const handleObject = (
|
||||
`KaTeX-a11y: enclose node with ${tree.label} not supported yet`);
|
||||
}
|
||||
|
||||
case "vcenter": {
|
||||
buildA11yStrings(tree.body, a11yStrings, atomType);
|
||||
break;
|
||||
}
|
||||
|
||||
case "vphantom": {
|
||||
throw new Error("KaTeX-a11y: vphantom not implemented yet");
|
||||
}
|
||||
|
@@ -272,6 +272,13 @@ describe("renderA11yString", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("hbox", () => {
|
||||
test("\\hbox", () => {
|
||||
const result = renderA11yString("x+\\hbox{y}");
|
||||
expect(result).toMatchInlineSnapshot(`"x, plus, y"`);
|
||||
});
|
||||
});
|
||||
|
||||
describe("inner", () => {
|
||||
test("\\ldots", () => {
|
||||
const result = renderA11yString("\\ldots");
|
||||
@@ -524,6 +531,13 @@ describe("renderA11yString", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("vcenter", () => {
|
||||
test("\\vcenter", () => {
|
||||
const result = renderA11yString("x+\\vcenter{y}");
|
||||
expect(result).toMatchInlineSnapshot(`"x, plus, y"`);
|
||||
});
|
||||
});
|
||||
|
||||
describe("verb", () => {
|
||||
test("\\verb", () => {
|
||||
const result = renderA11yString("\\verb|hello|");
|
||||
|
@@ -456,7 +456,8 @@ use `\ce` instead|
|
||||
|\harr|$\harr$||
|
||||
|\hat|$\hat{\theta}$|`\hat{\theta}`|
|
||||
|\hbar|$\hbar$||
|
||||
|\hbox|<span style="color:firebrick;">Not supported</span>||
|
||||
|\hbox|$\hbox{$x^2$}$|`\hbox{$x^2$}`|
|
||||
|\hbox to <dimen>| <span style="color:firebrick;">Not supported</span> ||
|
||||
|\hdashline|$\begin{matrix}a&b\\ \hdashline c &d\end{matrix}$|`\begin{matrix}`<br> `a & b \\`<br> `\hdashline`<br> `c & d`<br>`\end{matrix}`|
|
||||
|\hearts|$\hearts$||
|
||||
|\heartsuit|$\heartsuit$||
|
||||
@@ -865,7 +866,7 @@ use `\ce` instead|
|
||||
|\R|$\R$||
|
||||
|\r|$\text{\r{a}}$|`\text{\r{a}}`|
|
||||
|\raise|<span style="color:firebrick;">Not supported</span>|see `\raisebox`|
|
||||
|\raisebox|$h\raisebox{2pt}{ighe}r$|`h\raisebox{2pt}{ighe}r`|
|
||||
|\raisebox|$h\raisebox{2pt}{ighe}r$|`h\raisebox{2pt}{$ighe$}r`|
|
||||
|\rang|$\langle A\rang$|`\langle A\rang`|
|
||||
|\rangle|$\langle A\rangle$|`\langle A\rangle`|
|
||||
|\Rarr|$\Rarr$||
|
||||
@@ -1176,8 +1177,9 @@ use `\ce` instead|
|
||||
|\vartriangleright|$\vartriangleright$||
|
||||
|\varUpsilon|$\varUpsilon$||
|
||||
|\varXi|$\varXi$||
|
||||
|\vcentcolon|$\vcentcolon$||
|
||||
|\vcenter|<span style="color:firebrick;">Not supported</span>||
|
||||
|\vcentcolon|$\mathrel{\vcentcolon =}$|`\mathrel{\vcentcolon =}`|
|
||||
|\vcenter|$a+\left(\vcenter{\frac{\frac a b}c}\right)$|`a+\left(\vcenter{\hbox{$\frac{\frac a b}c$}}\right)`<br>TeX (strict) syntax|
|
||||
|\vcenter|$a+\left(\vcenter{\frac{\frac a b}c}\right)$|`a+\left(\vcenter{\frac{\frac a b}c}\right)`<br>non-strict syntax|
|
||||
|\Vdash|$\Vdash$||
|
||||
|\vDash|$\vDash$||
|
||||
|\vdash|$\vdash$||
|
||||
|
@@ -236,11 +236,14 @@ In display math, KaTeX does not insert automatic line breaks. It ignores display
|
||||
|
||||
||||
|
||||
|:--------------|:----------------------------------------|:-----
|
||||
|$x_n$ `x_n` |$\stackrel{!}{=}$ `\stackrel{!}{=}` |$a \atop b$ `a \atop b`
|
||||
|$e^x$ `e^x` |$\overset{!}{=}$ `\overset{!}{=}` |$a\raisebox{0.25em}{b}c$ `a\raisebox{0.25em}{b}c`
|
||||
|$_u^o $ `_u^o `|$\underset{!}{=}$ `\underset{!}{=}` | $$\sum_{\substack{0<i<m\\0<j<n}}$$ `\sum_{\substack{0<i<m\\0<j<n}}`
|
||||
|$x_n$ `x_n` |$\stackrel{!}{=}$ `\stackrel{!}{=}`| $a \atop b$ `a \atop b`
|
||||
|$e^x$ `e^x` |$\overset{!}{=}$ `\overset{!}{=}` | $a\raisebox{0.25em}{$b$}c$ `a\raisebox{0.25em}{$b$}c`
|
||||
|$_u^o $ `_u^o `| $\underset{!}{=}$ `\underset{!}{=}` | $a+\left(\vcenter{\frac{\frac a b}c}\right)$ `a+\left(\vcenter{\hbox{$\frac{\frac a b}c$}}\right)`
|
||||
||| $$\sum_{\substack{0<i<m\\0<j<n}}$$ `\sum_{\substack{0<i<m\\0<j<n}}`
|
||||
|
||||
The second argument of `\raisebox` can contain math if it is nested within `$…$` delimiters, as in `\raisebox{0.25em}{$\frac a b$}`
|
||||
`\raisebox` and `\hbox` put their argument into text mode. To raise math, nest `$…$` delimiters inside the argument as shown above.
|
||||
|
||||
`\vcenter` can be written without an `\hbox` if the `strict` rendering option is *false*. In that case, omit the nested `$…$` delimiters.
|
||||
|
||||
### Overlap and Spacing
|
||||
|
||||
|
@@ -21,6 +21,7 @@ import "./functions/font";
|
||||
import "./functions/genfrac";
|
||||
import "./functions/horizBrace";
|
||||
import "./functions/href";
|
||||
import "./functions/hbox";
|
||||
import "./functions/html";
|
||||
import "./functions/htmlmathml";
|
||||
import "./functions/includegraphics";
|
||||
@@ -47,4 +48,5 @@ import "./functions/symbolsSpacing";
|
||||
import "./functions/tag";
|
||||
import "./functions/text";
|
||||
import "./functions/underline";
|
||||
import "./functions/vcenter";
|
||||
import "./functions/verb";
|
||||
|
39
src/functions/hbox.js
Normal file
@@ -0,0 +1,39 @@
|
||||
// @flow
|
||||
import defineFunction, {ordargument} from "../defineFunction";
|
||||
import buildCommon from "../buildCommon";
|
||||
import mathMLTree from "../mathMLTree";
|
||||
|
||||
import * as html from "../buildHTML";
|
||||
import * as mml from "../buildMathML";
|
||||
|
||||
// \hbox is provided for compatibility with LaTeX \vcenter.
|
||||
// In LaTeX, \vcenter can act only on a box, as in
|
||||
// \vcenter{\hbox{$\frac{a+b}{\dfrac{c}{d}}$}}
|
||||
// This function by itself doesn't do anything but prevent a soft line break.
|
||||
|
||||
defineFunction({
|
||||
type: "hbox",
|
||||
names: ["\\hbox"],
|
||||
props: {
|
||||
numArgs: 1,
|
||||
argTypes: ["text"],
|
||||
allowedInText: true,
|
||||
primitive: true,
|
||||
},
|
||||
handler({parser}, args) {
|
||||
return {
|
||||
type: "hbox",
|
||||
mode: parser.mode,
|
||||
body: ordargument(args[0]),
|
||||
};
|
||||
},
|
||||
htmlBuilder(group, options) {
|
||||
const elements = html.buildExpression(group.body, options, false);
|
||||
return buildCommon.makeFragment(elements);
|
||||
},
|
||||
mathmlBuilder(group, options) {
|
||||
return new mathMLTree.MathNode(
|
||||
"mrow", mml.buildExpression(group.body, options)
|
||||
);
|
||||
},
|
||||
});
|
@@ -44,4 +44,3 @@ defineFunction({
|
||||
return node;
|
||||
},
|
||||
});
|
||||
|
||||
|
44
src/functions/vcenter.js
Normal file
@@ -0,0 +1,44 @@
|
||||
// @flow
|
||||
import defineFunction from "../defineFunction";
|
||||
import buildCommon from "../buildCommon";
|
||||
import mathMLTree from "../mathMLTree";
|
||||
|
||||
import * as html from "../buildHTML";
|
||||
import * as mml from "../buildMathML";
|
||||
|
||||
// \vcenter: Vertically center the argument group on the math axis.
|
||||
|
||||
defineFunction({
|
||||
type: "vcenter",
|
||||
names: ["\\vcenter"],
|
||||
props: {
|
||||
numArgs: 1,
|
||||
argTypes: ["original"], // In LaTeX, \vcenter can act only on a box.
|
||||
allowedInText: false,
|
||||
},
|
||||
handler({parser}, args) {
|
||||
return {
|
||||
type: "vcenter",
|
||||
mode: parser.mode,
|
||||
body: args[0],
|
||||
};
|
||||
},
|
||||
htmlBuilder(group, options) {
|
||||
const body = html.buildGroup(group.body, options);
|
||||
const axisHeight = options.fontMetrics().axisHeight;
|
||||
const dy = 0.5 * ((body.height - axisHeight) - (body.depth + axisHeight));
|
||||
return buildCommon.makeVList({
|
||||
positionType: "shift",
|
||||
positionData: dy,
|
||||
children: [{type: "elem", elem: body}],
|
||||
}, options);
|
||||
},
|
||||
mathmlBuilder(group, options) {
|
||||
// There is no way to do this in MathML.
|
||||
// Write a class as a breadcrumb in case some post-processor wants
|
||||
// to perform a vcenter adjustment.
|
||||
return new mathMLTree.MathNode(
|
||||
"mpadded", [mml.buildGroup(group.body, options)], ["vcenter"]);
|
||||
},
|
||||
});
|
||||
|
@@ -256,6 +256,12 @@ type ParseNodeTypes = {
|
||||
size: StyleStr | "auto",
|
||||
barSize: Measurement | null,
|
||||
|},
|
||||
"hbox": {|
|
||||
type: "hbox",
|
||||
mode: Mode,
|
||||
loc?: ?SourceLocation,
|
||||
body: AnyParseNode[],
|
||||
|},
|
||||
"horizBrace": {|
|
||||
type: "horizBrace",
|
||||
mode: Mode,
|
||||
@@ -436,6 +442,12 @@ type ParseNodeTypes = {
|
||||
loc?: ?SourceLocation,
|
||||
body: AnyParseNode,
|
||||
|},
|
||||
"vcenter": {|
|
||||
type: "vcenter",
|
||||
mode: Mode,
|
||||
loc?: ?SourceLocation,
|
||||
body: AnyParseNode,
|
||||
|},
|
||||
"xArrow": {|
|
||||
type: "xArrow",
|
||||
mode: Mode,
|
||||
|
@@ -1642,6 +1642,36 @@ describe("A \\pmb builder", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("A raise parser", function() {
|
||||
it("should parse and build text in \\raisebox", function() {
|
||||
expect("\\raisebox{5pt}{text}").toBuild(strictSettings);
|
||||
expect("\\raisebox{-5pt}{text}").toBuild(strictSettings);
|
||||
});
|
||||
|
||||
it("should parse and build math in non-strict \\vcenter", function() {
|
||||
expect("\\vcenter{\\frac a b}").toBuild(nonstrictSettings);
|
||||
});
|
||||
|
||||
it("should fail to parse math in \\raisebox", function() {
|
||||
expect("\\raisebox{5pt}{\\frac a b}").not.toParse(nonstrictSettings);
|
||||
expect("\\raisebox{-5pt}{\\frac a b}").not.toParse(nonstrictSettings);
|
||||
});
|
||||
|
||||
it("should fail to parse math in an \\hbox", function() {
|
||||
expect("\\hbox{\\frac a b}").not.toParse(nonstrictSettings);
|
||||
});
|
||||
|
||||
it("should fail to build, given an unbraced length", function() {
|
||||
expect("\\raisebox5pt{text}").not.toBuild(strictSettings);
|
||||
expect("\\raisebox-5pt{text}").not.toBuild(strictSettings);
|
||||
});
|
||||
|
||||
|
||||
it("should build math in an hbox when math mode is set", function() {
|
||||
expect("a + \\vcenter{\\hbox{$\\frac{\\frac a b}c$}}").toBuild(strictSettings);
|
||||
});
|
||||
});
|
||||
|
||||
describe("A comment parser", function() {
|
||||
it("should parse comments at the end of a line", () => {
|
||||
expect("a^2 + b^2 = c^2 % Pythagoras' Theorem\n").toParse();
|
||||
|
BIN
test/screenshotter/images/CD-chrome.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
test/screenshotter/images/CD-firefox.png
Normal file
After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 16 KiB |
@@ -323,7 +323,11 @@ Phase: 120\text{V}\phase{-78.2^\circ}\;\Large\phase{78.2^\circ}
|
||||
Pmb: \mu\pmb{\mu}\pmb{=}\mu\pmb{+}\mu
|
||||
PrimeSpacing: f'+f_2'+f^{f'}
|
||||
PrimeSuper: x'^2+x'''^2+x'^2_3+x_3'^2
|
||||
Raisebox: \frac{a}{a\raisebox{0.5em}{b}} \cdot \frac{a\raisebox{-0.5em}{b}}{a} \cdot \sqrt{a\raisebox{0.5em}{b}} \cdot \sqrt{a\raisebox{-0.5em}{b}} \cdot \sqrt{a\raisebox{0.5em}{b}\raisebox{-0.5em}{b}}
|
||||
Raisebox:
|
||||
\begin{matrix}
|
||||
\frac{a}{a\raisebox{0.5em}{b}} \cdot \frac{a\raisebox{-0.5em}{b}}{a} \cdot \sqrt{a\raisebox{0.5em}{b}} \cdot \sqrt{a\raisebox{-0.5em}{b}} \cdot \sqrt{a\raisebox{0.5em}{b}\raisebox{-0.5em}{b}} \\[2em]
|
||||
a + \left(\vcenter{\hbox{$\frac{a+b}{\dfrac{c}{d}}$}}\right)
|
||||
\end {matrix}
|
||||
ReactionArrows: |
|
||||
\begin{matrix}
|
||||
A \xrightleftarrows{} B \xrightequilibrium{} C \xleftequilibrium{} D \\
|
||||
|