diff --git a/src/buildHTML.js b/src/buildHTML.js
index a13b28bb9a..a106bb9d7d 100644
--- a/src/buildHTML.js
+++ b/src/buildHTML.js
@@ -9,7 +9,7 @@
import ParseError from "./ParseError";
import Style from "./Style";
import buildCommon from "./buildCommon";
-import {Span, Anchor} from "./domTree";
+import {Span, Anchor, SymbolNode} from "./domTree";
import utils from "./utils";
import {spacings, tightSpacings} from "./spacingData";
import {_htmlGroupBuilders as groupBuilders} from "./defineFunction";
@@ -310,7 +310,11 @@ function buildHTMLUnbreakable(children, options) {
* Take an entire parse tree, and build it into an appropriate set of HTML
* nodes.
*/
-export default function buildHTML(tree: AnyParseNode[], options: Options): DomSpan {
+export default function buildHTML(
+ tree: AnyParseNode[],
+ options: Options,
+ isDisplayMode: boolean,
+): DomSpan {
// Strip off outer tag wrapper for processing below.
let tag = null;
if (tree.length === 1 && tree[0].type === "tag") {
@@ -386,7 +390,24 @@ export default function buildHTML(tree: AnyParseNode[], options: Options): DomSp
children.push(eqnNum);
}
- const htmlNode = makeSpan(["katex-html"], children);
+ // Add spacers _between_ unbreakable subtrees to allow for line breaks in
+ // inline documents, but not allowing a soft break before a punctuation
+ // in text _immediately_ following the inline math element.
+ //
+ // See https://github.com/KaTeX/KaTeX/issues/1233 for discussion.
+ const childrenWithSpacers = [];
+ for (let i = 0; i < children.length; i++) {
+ if (!isDisplayMode && i > 0) {
+ const spacer = new SymbolNode(
+ "\u200b", undefined, undefined, undefined, undefined, undefined,
+ ["spacer"],
+ );
+ childrenWithSpacers.push(spacer);
+ }
+ childrenWithSpacers.push(children[i]);
+ }
+
+ const htmlNode = makeSpan(["katex-html"], childrenWithSpacers);
htmlNode.setAttribute("aria-hidden", "true");
// Adjust the strut of the tag to be the maximum height of all children
diff --git a/src/buildTree.js b/src/buildTree.js
index d7ad29c7e2..e4e94f3e45 100644
--- a/src/buildTree.js
+++ b/src/buildTree.js
@@ -41,12 +41,12 @@ export const buildTree = function(
if (settings.output === "mathml") {
return buildMathML(tree, expression, options, settings.displayMode, true);
} else if (settings.output === "html") {
- const htmlNode = buildHTML(tree, options);
+ const htmlNode = buildHTML(tree, options, settings.displayMode);
katexNode = buildCommon.makeSpan(["katex"], [htmlNode]);
} else {
const mathMLNode = buildMathML(tree, expression, options,
settings.displayMode, false);
- const htmlNode = buildHTML(tree, options);
+ const htmlNode = buildHTML(tree, options, settings.displayMode);
katexNode = buildCommon.makeSpan(["katex"], [mathMLNode, htmlNode]);
}
@@ -59,7 +59,7 @@ export const buildHTMLTree = function(
settings: Settings,
): DomSpan {
const options = optionsFromSettings(settings);
- const htmlNode = buildHTML(tree, options);
+ const htmlNode = buildHTML(tree, options, settings.displayMode);
const katexNode = buildCommon.makeSpan(["katex"], [htmlNode]);
return displayWrap(katexNode, settings);
};
diff --git a/src/katex.less b/src/katex.less
index e2fe90f4fd..801f1d5f39 100644
--- a/src/katex.less
+++ b/src/katex.less
@@ -17,6 +17,9 @@
// Prevent a rendering bug that misplaces \vec in Chrome.
text-rendering: auto;
+ /* Do not wrap math elements, unless in an inline spacer. */
+ white-space: nowrap;
+
* {
// Prevent background resetting on elements in Windows's high-contrast
// mode, while still allowing background/foreground setting on root .katex
@@ -47,6 +50,11 @@
> .newline {
display: block;
}
+
+ /* Element containing ​ between .base elements */
+ > .spacer {
+ white-space: normal;
+ }
}
.base {
@@ -645,7 +653,6 @@
> .katex {
display: block;
text-align: center;
- white-space: nowrap;
> .katex-html {
display: block;
diff --git a/test/__snapshots__/katex-spec.js.snap b/test/__snapshots__/katex-spec.js.snap
index 8cd15a12ec..6c2499903e 100755
--- a/test/__snapshots__/katex-spec.js.snap
+++ b/test/__snapshots__/katex-spec.js.snap
@@ -1186,8 +1186,14 @@ exports[`Newlines via \\\\ and \\newline \\\\ causes newline, even after mrel an
=
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
node.classes.indexOf("strut") < 0));
+ const span = builtHTML.children[i];
+ if (span.classes.indexOf("spacer") < 0) {
+ children.push(...span.children.filter(
+ (node) => node.classes.indexOf("strut") < 0));
+ }
}
return children;
}
diff --git a/test/katex-spec.js b/test/katex-spec.js
index c926229b24..26961c060f 100644
--- a/test/katex-spec.js
+++ b/test/katex-spec.js
@@ -3893,8 +3893,11 @@ describe("Newlines via \\\\ and \\newline", function() {
const markup = katex.renderToString(r`M = \\ a + \\ b \\ c`);
// Ensure newlines appear outside base spans (because, in this regexp,
// base span occurs immediately after each newline span).
- expect(markup).toMatch(
- /(.*?<\/span><\/span>){3}/);
+ const base = `.*?<\\/span>`;
+ const newline = `<\\/span>`;
+ const spacer = `\u200b`;
+ const pattern = new RegExp(`(${base}${spacer}${newline}${spacer}){3}${base}`);
+ expect(markup).toMatch(pattern);
expect(markup).toMatchSnapshot();
});
});