Skip to content

Commit

Permalink
Control soft line breaks with a rendering option
Browse files Browse the repository at this point in the history
  • Loading branch information
ronkok committed Sep 5, 2022
1 parent b9f9ddb commit e02155b
Show file tree
Hide file tree
Showing 35 changed files with 735 additions and 433 deletions.
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,19 @@

All notable changes to this project will be documented in this file. This CHANGELOG roughly follows the guidelines from [keepachangelog.com](https://keepachangelog.com/en/1.0.0/).

## [0.8.0] = 2022-09-05

### Breaking Change

- Soft line breaks are now controlled by the `wrap` rendering option.
Temml does this by creating a series of `<math>` elements.

## [0.7.3] = 2022-08-29

## Fixed
### Fixed

- Get (lowered) prime from Temml.woff2 font
- Add a prime character to Temml.woff2

## [0.7.2] = 2022-08-08

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

| Library | Minified JavaScript + CSS |
|:--------------|:-------------------------:|
| Temml | 143 KB |
| Temml | 144 KB |
| MathJax 2.7.5 | 338 KB |
| KaTeX | 280 KB |
| TeXZilla | 168 KB |
Expand All @@ -13,7 +13,7 @@ Temml’s MathML output conforms to both both [MathML 3](https://www.w3.org/TR/M

Temml’s coverage of LaTeX functions is as good as MathJax, slightly better than KaTeX 0.13.0 and substantially better than TeXZilla. See a [detailed coverage comparison](https://temml.org/docs/en/comparison.html).

Temml's [test](https://temml.org/docs/en/supported.html) suite [includes](https://temml.org/tests/mozilla-tests.html) many [rendered](https://temml.org/tests/wiki-tests.html) examples [that](https://temml.org/tests/mhchem-tests.html) are [available](https://temml.org/tests/LaTeXML-tests.html) for viewing.
Temml's test suite include many rendered examples, including the Temml [supported functions page](https://temml.org/docs/en/supported.html) and tests from [Mozilla](https://temml.org/tests/mozilla-tests.html), [Wikipedia](https://temml.org/tests/wiki-tests.html), [mhchem](https://temml.org/tests/mhchem-tests.html), and [LaTeXML](https://temml.org/tests/LaTeXML-tests.html).

Temml's demonstration page is at https://temml.org/

Expand Down
67 changes: 49 additions & 18 deletions dist/temml.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ class Settings {
this.leqno = utils.deflt(options.leqno, false); // boolean
this.errorColor = utils.deflt(options.errorColor, "#b22222"); // string
this.macros = options.macros || {};
this.wrap = utils.deflt(options.wrap, "none"); // "none" | "tex" | "="
this.xml = utils.deflt(options.xml, false); // boolean
this.colorIsTextColor = utils.deflt(options.colorIsTextColor, false); // booelean
this.strict = utils.deflt(options.strict, false); // boolean
Expand Down Expand Up @@ -1760,18 +1761,17 @@ for (let i = 0; i < 10; i++) {
* much of this module.
*/

function setLineBreaks(expression, isDisplayMode, isAnnotated, color = undefined) {
if (color === undefined) {
function setLineBreaks(expression, wrapMode, isDisplayMode, color) {
if (color === undefined && wrapMode !== "none") {
// First, make one pass through the expression and split any color nodes.
const upperLimit = expression.length - 1;
for (let i = upperLimit; i >= 0; i--) {
const node = expression[i];
if (node.type === "mstyle" && node.attributes.mathcolor) {
const color = node.attributes.mathcolor;
const fragment = setLineBreaks(node.children, isDisplayMode, isAnnotated, color);
const fragment = setLineBreaks(node.children, wrapMode, isDisplayMode, color);
if (!(fragment.type && fragment.type !== "mtable")) {
expression.splice(i, 1, ...fragment.children);

}
}
}
Expand All @@ -1782,12 +1782,15 @@ function setLineBreaks(expression, isDisplayMode, isAnnotated, color = undefined
const mtrs = [];
let mrows = [];
let block = [];
let numTopLevelEquals = 0;
let canBeBIN = false; // The first node cannot be an infix binary operator.
for (let i = 0; i < expression.length; i++) {
const node = expression[i];
if (node.type && node.type === "mstyle" && node.attributes.mathcolor) {
// Start a new block. (Insert a soft linebreak.)
mrows.push(new mathMLTree.MathNode(tagName, block));
if (block.length > 0) {
// Start a new block. (Insert a soft linebreak.)
mrows.push(new mathMLTree.MathNode(tagName, block));
}
// Insert the mstyle
mrows.push(node);
block = [];
Expand All @@ -1809,7 +1812,19 @@ function setLineBreaks(expression, isDisplayMode, isAnnotated, color = undefined
continue
}
block.push(node);
if (node.type && node.type === "mo" && !isDisplayMode && !isAnnotated) {
if (node.type && node.type === "mo" && wrapMode === "=") {
if (node.children.length === 1 && node.children[0].text === "=") {
numTopLevelEquals += 1;
if (numTopLevelEquals > 1) {
block.pop();
// Start a new block. (Insert a soft linebreak.)
const element = new mathMLTree.MathNode(tagName, block);
if (color) { element.setAttribute("mathcolor", color); }
mrows.push(element);
block = [node];
}
}
} else if (node.type && node.type === "mo" && wrapMode === "tex") {
// This may be a place for a soft line break.
if (canBeBIN && !node.attributes.form) {
// Check if the following node is a \nobreak text node, e.g. "~""
Expand Down Expand Up @@ -2065,29 +2080,41 @@ function buildMathML(tree, texExpression, style, settings) {
}

const expression = buildExpression(tree, style);
const wrap = (settings.displayMode || settings.annotate) ? "none" : settings.wrap;

const n1 = expression.length === 0 ? null : expression[0];
let wrapper = expression.length === 1 && tag === null && (n1 instanceof MathNode)
&& !(n1.type === "mstyle" && n1.attributes.mathcolor)
? expression[0]
: setLineBreaks(expression, settings.displayMode, settings.annotate);
: expression.length > 1 && wrap === "none"
? new mathMLTree.MathNode("mrow", expression)
: setLineBreaks(expression, wrap, settings.displayMode);

if (tag) {
wrapper = taggedExpression(wrapper, tag, style, settings.leqno);
}

let semantics;
if (settings.annotate) {
// Build a TeX annotation of the source
const annotation = new mathMLTree.MathNode(
"annotation", [new mathMLTree.TextNode(texExpression)]);
annotation.setAttribute("encoding", "application/x-tex");
semantics = new mathMLTree.MathNode("semantics", [wrapper, annotation]);
wrapper = new mathMLTree.MathNode("semantics", [wrapper, annotation]);
}

if (wrap !== "none") {
const maths = [];
for (let i = 0; i < wrapper.children.length; i++) {
const math = new mathMLTree.MathNode("math", [wrapper.children[i]]);
if (settings.xml) {
math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML");
}
maths.push(math);
}
return mathMLTree.newDocumentFragment(maths)
}

const math = settings.annotate
? new mathMLTree.MathNode("math", [semantics])
: new mathMLTree.MathNode("math", [wrapper]);
const math = new mathMLTree.MathNode("math", [wrapper]);

if (settings.xml) {
math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML");
Expand Down Expand Up @@ -3041,8 +3068,7 @@ const mathmlBuilder$9 = (group, style) => {
// Wrap with an <mstyle> element.
const node = wrapWithMstyle(inner);
node.setAttribute("mathcolor", group.color);
// Wrap w/<mrow>. We get better operator spacing that way.
return new mathMLTree.MathNode("mrow", [node])
return node
};

defineFunction({
Expand Down Expand Up @@ -12878,15 +12904,15 @@ class Style {

/* Temml Post Process
* Perform two tasks not done by Temml when it created each individual Temml <math> element.
* Given a block of block,
* Given a block,
* 1. At each AMS auto-numbered environment, assign an id.
* 2. Populate the text contents of each \ref & \eqref
*
* As with other Temml code, this file is released under terms of the MIT license.
* https://mit-license.org/
*/

const version = "0.7.3";
const version = "0.8.0";

function postProcess(block) {
const labelMap = {};
Expand Down Expand Up @@ -12942,11 +12968,16 @@ function postProcess(block) {
*/
let render = function(expression, baseNode, options) {
baseNode.textContent = "";
const alreadyInMathElement = baseNode.tagName === "MATH";
if (alreadyInMathElement) { options.wrap = "none"; }
const math = renderToMathMLTree(expression, options);
if (baseNode.tagName === "MATH") {
if (alreadyInMathElement) {
// The <math> element already exists. Populate it.
baseNode.textContent = "";
math.children.forEach(e => { baseNode.appendChild(e.toNode()); });
} else if (math.children.length > 1) {
baseNode.textContent = "";
math.children.forEach(e => { baseNode.appendChild(e.toNode()); });
} else {
baseNode.appendChild(math.toNode());
}
Expand Down
67 changes: 49 additions & 18 deletions dist/temml.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ var temml = (function () {
this.leqno = utils.deflt(options.leqno, false); // boolean
this.errorColor = utils.deflt(options.errorColor, "#b22222"); // string
this.macros = options.macros || {};
this.wrap = utils.deflt(options.wrap, "none"); // "none" | "tex" | "="
this.xml = utils.deflt(options.xml, false); // boolean
this.colorIsTextColor = utils.deflt(options.colorIsTextColor, false); // booelean
this.strict = utils.deflt(options.strict, false); // boolean
Expand Down Expand Up @@ -1761,18 +1762,17 @@ var temml = (function () {
* much of this module.
*/

function setLineBreaks(expression, isDisplayMode, isAnnotated, color = undefined) {
if (color === undefined) {
function setLineBreaks(expression, wrapMode, isDisplayMode, color) {
if (color === undefined && wrapMode !== "none") {
// First, make one pass through the expression and split any color nodes.
const upperLimit = expression.length - 1;
for (let i = upperLimit; i >= 0; i--) {
const node = expression[i];
if (node.type === "mstyle" && node.attributes.mathcolor) {
const color = node.attributes.mathcolor;
const fragment = setLineBreaks(node.children, isDisplayMode, isAnnotated, color);
const fragment = setLineBreaks(node.children, wrapMode, isDisplayMode, color);
if (!(fragment.type && fragment.type !== "mtable")) {
expression.splice(i, 1, ...fragment.children);

}
}
}
Expand All @@ -1783,12 +1783,15 @@ var temml = (function () {
const mtrs = [];
let mrows = [];
let block = [];
let numTopLevelEquals = 0;
let canBeBIN = false; // The first node cannot be an infix binary operator.
for (let i = 0; i < expression.length; i++) {
const node = expression[i];
if (node.type && node.type === "mstyle" && node.attributes.mathcolor) {
// Start a new block. (Insert a soft linebreak.)
mrows.push(new mathMLTree.MathNode(tagName, block));
if (block.length > 0) {
// Start a new block. (Insert a soft linebreak.)
mrows.push(new mathMLTree.MathNode(tagName, block));
}
// Insert the mstyle
mrows.push(node);
block = [];
Expand All @@ -1810,7 +1813,19 @@ var temml = (function () {
continue
}
block.push(node);
if (node.type && node.type === "mo" && !isDisplayMode && !isAnnotated) {
if (node.type && node.type === "mo" && wrapMode === "=") {
if (node.children.length === 1 && node.children[0].text === "=") {
numTopLevelEquals += 1;
if (numTopLevelEquals > 1) {
block.pop();
// Start a new block. (Insert a soft linebreak.)
const element = new mathMLTree.MathNode(tagName, block);
if (color) { element.setAttribute("mathcolor", color); }
mrows.push(element);
block = [node];
}
}
} else if (node.type && node.type === "mo" && wrapMode === "tex") {
// This may be a place for a soft line break.
if (canBeBIN && !node.attributes.form) {
// Check if the following node is a \nobreak text node, e.g. "~""
Expand Down Expand Up @@ -2066,29 +2081,41 @@ var temml = (function () {
}

const expression = buildExpression(tree, style);
const wrap = (settings.displayMode || settings.annotate) ? "none" : settings.wrap;

const n1 = expression.length === 0 ? null : expression[0];
let wrapper = expression.length === 1 && tag === null && (n1 instanceof MathNode)
&& !(n1.type === "mstyle" && n1.attributes.mathcolor)
? expression[0]
: setLineBreaks(expression, settings.displayMode, settings.annotate);
: expression.length > 1 && wrap === "none"
? new mathMLTree.MathNode("mrow", expression)
: setLineBreaks(expression, wrap, settings.displayMode);

if (tag) {
wrapper = taggedExpression(wrapper, tag, style, settings.leqno);
}

let semantics;
if (settings.annotate) {
// Build a TeX annotation of the source
const annotation = new mathMLTree.MathNode(
"annotation", [new mathMLTree.TextNode(texExpression)]);
annotation.setAttribute("encoding", "application/x-tex");
semantics = new mathMLTree.MathNode("semantics", [wrapper, annotation]);
wrapper = new mathMLTree.MathNode("semantics", [wrapper, annotation]);
}

if (wrap !== "none") {
const maths = [];
for (let i = 0; i < wrapper.children.length; i++) {
const math = new mathMLTree.MathNode("math", [wrapper.children[i]]);
if (settings.xml) {
math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML");
}
maths.push(math);
}
return mathMLTree.newDocumentFragment(maths)
}

const math = settings.annotate
? new mathMLTree.MathNode("math", [semantics])
: new mathMLTree.MathNode("math", [wrapper]);
const math = new mathMLTree.MathNode("math", [wrapper]);

if (settings.xml) {
math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML");
Expand Down Expand Up @@ -3042,8 +3069,7 @@ var temml = (function () {
// Wrap with an <mstyle> element.
const node = wrapWithMstyle(inner);
node.setAttribute("mathcolor", group.color);
// Wrap w/<mrow>. We get better operator spacing that way.
return new mathMLTree.MathNode("mrow", [node])
return node
};

defineFunction({
Expand Down Expand Up @@ -10979,15 +11005,15 @@ var temml = (function () {

/* Temml Post Process
* Perform two tasks not done by Temml when it created each individual Temml <math> element.
* Given a block of block,
* Given a block,
* 1. At each AMS auto-numbered environment, assign an id.
* 2. Populate the text contents of each \ref & \eqref
*
* As with other Temml code, this file is released under terms of the MIT license.
* https://mit-license.org/
*/

const version = "0.7.3";
const version = "0.8.0";

function postProcess(block) {
const labelMap = {};
Expand Down Expand Up @@ -11043,11 +11069,16 @@ var temml = (function () {
*/
let render = function(expression, baseNode, options) {
baseNode.textContent = "";
const alreadyInMathElement = baseNode.tagName === "MATH";
if (alreadyInMathElement) { options.wrap = "none"; }
const math = renderToMathMLTree(expression, options);
if (baseNode.tagName === "MATH") {
if (alreadyInMathElement) {
// The <math> element already exists. Populate it.
baseNode.textContent = "";
math.children.forEach(e => { baseNode.appendChild(e.toNode()); });
} else if (math.children.length > 1) {
baseNode.textContent = "";
math.children.forEach(e => { baseNode.appendChild(e.toNode()); });
} else {
baseNode.appendChild(math.toNode());
}
Expand Down
2 changes: 1 addition & 1 deletion dist/temml.min.js

Large diffs are not rendered by default.

Loading

0 comments on commit e02155b

Please sign in to comment.