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>
This commit is contained in:
Ron Kok
2020-11-14 01:45:14 -08:00
committed by GitHub
parent ebd86b90c4
commit 60aecbdfe2
16 changed files with 169 additions and 10 deletions

View File

@@ -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");
}

View File

@@ -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|");

View File

@@ -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>&nbsp;&nbsp;&nbsp;`a & b \\`<br>&nbsp;&nbsp;&nbsp;`\hdashline`<br>&nbsp;&nbsp;&nbsp;`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$||

View File

@@ -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

View File

@@ -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
View 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)
);
},
});

View File

@@ -44,4 +44,3 @@ defineFunction({
return node;
},
});

44
src/functions/vcenter.js Normal file
View 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"]);
},
});

View File

@@ -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,

View File

@@ -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();

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -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 \\