temml 0.11.0 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,7 +11,7 @@
11
11
  * https://mit-license.org/
12
12
  */
13
13
 
14
- const version = "0.11.00";
14
+ const version = "0.11.02";
15
15
 
16
16
  function postProcess(block) {
17
17
  const labelMap = {};
@@ -72,6 +72,7 @@
72
72
  mtext.appendChild(document.createTextNode(str));
73
73
  const math = document.createElementNS("http://www.w3.org/1998/Math/MathML", "math");
74
74
  math.appendChild(mtext);
75
+ ref.textContent = '';
75
76
  ref.appendChild(math);
76
77
  });
77
78
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "temml",
3
- "version": "0.11.00",
3
+ "version": "0.11.02",
4
4
  "description": "TeX to MathML conversion in JavaScript.",
5
5
  "main": "dist/temml.js",
6
6
  "engines": {
package/src/ParseError.js CHANGED
@@ -27,7 +27,7 @@ class ParseError {
27
27
  if (start === input.length) {
28
28
  error += " at end of input: ";
29
29
  } else {
30
- error += " at position " + (start + 1) + ": ";
30
+ error += " at position " + (start + 1) + ": \n";
31
31
  }
32
32
 
33
33
  // Underline token in question using combining underscores
package/src/Parser.js CHANGED
@@ -17,6 +17,7 @@ import unicodeSymbols from /*preval*/ "./unicodeSymbols";
17
17
 
18
18
  const binLeftCancellers = ["bin", "op", "open", "punct", "rel"];
19
19
  const sizeRegEx = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/
20
+ const textRegEx = /^ *\\text/
20
21
 
21
22
  /**
22
23
  * This file contains the parser used to parse out a TeX expression from the
@@ -926,7 +927,12 @@ export default class Parser {
926
927
  family,
927
928
  loc,
928
929
  text
929
- };
930
+ }
931
+ if ((family === "rel" || family === "bin") && this.prevAtomType === "text") {
932
+ if (textRegEx.test(loc.lexer.input.slice(loc.end))) {
933
+ s.needsSpacing = true // Fix a MathML bug.
934
+ }
935
+ }
930
936
  } else {
931
937
  if (asciiFromScript[text]) {
932
938
  // Unicode 14 disambiguates chancery from roundhand.
@@ -1,5 +1,6 @@
1
1
  import defineEnvironment from "../defineEnvironment";
2
2
  import { parseCD } from "./cd";
3
+ import { bordermatrixParseTree } from "./borderTree.js"
3
4
  import defineFunction from "../defineFunction";
4
5
  import mathMLTree from "../mathMLTree";
5
6
  import { Span } from "../domTree"
@@ -103,6 +104,7 @@ function parseArray(
103
104
  },
104
105
  scriptLevel
105
106
  ) {
107
+ const endToken = envClasses && envClasses.includes("bordermatrix") ? "}" : "\\end"
106
108
  parser.gullet.beginGroup();
107
109
  if (!singleRow) {
108
110
  // \cr is equivalent to \\ without the optional size argument (see below)
@@ -176,7 +178,7 @@ function parseArray(
176
178
  }
177
179
  }
178
180
  parser.consume();
179
- } else if (next === "\\end") {
181
+ } else if (next === endToken) {
180
182
  endRow()
181
183
  // Arrays terminate newlines with `\crcr` which consumes a `\cr` if
182
184
  // the last line is empty. However, AMS environments keep the
@@ -213,7 +215,7 @@ function parseArray(
213
215
  body.push(row);
214
216
  beginRow();
215
217
  } else {
216
- throw new ParseError("Expected & or \\\\ or \\cr or \\end", parser.nextToken);
218
+ throw new ParseError("Expected & or \\\\ or \\cr or " + endToken, parser.nextToken);
217
219
  }
218
220
  }
219
221
 
@@ -346,23 +348,44 @@ const mathmlBuilder = function(group, style) {
346
348
  })
347
349
  }
348
350
  }
349
- tbl.push(mtr);
351
+
352
+ // Check for \hphantom \from \bordermatrix
353
+ let mustSquashRow = true
354
+ for (let j = 0; j < mtr.children.length; j++) {
355
+ const child = mtr.children[j].children[0];
356
+ if (!(child && child.type === "mpadded" && child.attributes.height === "0px")) {
357
+ mustSquashRow = false
358
+ break
359
+ }
360
+ }
361
+ if (mustSquashRow) {
362
+ // All the cell contents are \hphantom. Squash the padding.
363
+ for (let j = 0; j < mtr.children.length; j++) {
364
+ mtr.children[j].style.paddingTop = "0"
365
+ mtr.children[j].style.paddingBottom = "0"
366
+ }
367
+ }
368
+
369
+ tbl.push(mtr)
350
370
  }
351
371
 
352
- if (group.envClasses.length > 0) {
353
- if (group.arraystretch && group.arraystretch !== 1) {
354
- // In LaTeX, \arraystretch is a factor applied to a 12pt strut height.
355
- // It defines a baseline to baseline distance.
356
- // Here, we do an approximation of that approach.
357
- const pad = String(1.4 * group.arraystretch - 0.8) + "ex"
358
- for (let i = 0; i < tbl.length; i++) {
359
- for (let j = 0; j < tbl[i].children.length; j++) {
360
- tbl[i].children[j].style.paddingTop = pad
361
- tbl[i].children[j].style.paddingBottom = pad
362
- }
372
+ if (group.arraystretch && group.arraystretch !== 1) {
373
+ // In LaTeX, \arraystretch is a factor applied to a 12pt strut height.
374
+ // It defines a baseline to baseline distance.
375
+ // Here, we do an approximation of that approach.
376
+ const pad = String(1.4 * group.arraystretch - 0.8) + "ex"
377
+ for (let i = 0; i < tbl.length; i++) {
378
+ for (let j = 0; j < tbl[i].children.length; j++) {
379
+ tbl[i].children[j].style.paddingTop = pad
380
+ tbl[i].children[j].style.paddingBottom = pad
363
381
  }
364
382
  }
365
- let sidePadding = group.envClasses.includes("abut")
383
+ }
384
+
385
+ let sidePadding
386
+ let sidePadUnit
387
+ if (group.envClasses.length > 0) {
388
+ sidePadding = group.envClasses.includes("abut")
366
389
  ? "0"
367
390
  : group.envClasses.includes("cases")
368
391
  ? "0"
@@ -371,13 +394,14 @@ const mathmlBuilder = function(group, style) {
371
394
  : group.envClasses.includes("cd")
372
395
  ? "0.25"
373
396
  : "0.4" // default side padding
374
- let sidePadUnit = "em"
375
- if (group.arraycolsep) {
376
- const arraySidePad = calculateSize(group.arraycolsep, style)
377
- sidePadding = arraySidePad.number.toFixed(4)
378
- sidePadUnit = arraySidePad.unit
379
- }
380
-
397
+ sidePadUnit = "em"
398
+ }
399
+ if (group.arraycolsep) {
400
+ const arraySidePad = calculateSize(group.arraycolsep, style)
401
+ sidePadding = arraySidePad.number.toFixed(4)
402
+ sidePadUnit = arraySidePad.unit
403
+ }
404
+ if (sidePadding) {
381
405
  const numCols = tbl.length === 0 ? 0 : tbl[0].children.length
382
406
 
383
407
  const sidePad = (j, hand) => {
@@ -399,7 +423,18 @@ const mathmlBuilder = function(group, style) {
399
423
  tbl[i].children[j].style.paddingRight = `${sidePad(j, 1)}${sidePadUnit}`
400
424
  }
401
425
  }
426
+ }
427
+ if (group.envClasses.length === 0) {
428
+ // Set zero padding on side of the matrix
429
+ for (let i = 0; i < tbl.length; i++) {
430
+ tbl[i].children[0].style.paddingLeft = "0em"
431
+ if (tbl[i].children.length === tbl[0].children.length) {
432
+ tbl[i].children[tbl[i].children.length - 1].style.paddingRight = "0em"
433
+ }
434
+ }
435
+ }
402
436
 
437
+ if (group.envClasses.length > 0) {
403
438
  // Justification
404
439
  const align = group.envClasses.includes("align") || group.envClasses.includes("alignat")
405
440
  for (let i = 0; i < tbl.length; i++) {
@@ -425,14 +460,6 @@ const mathmlBuilder = function(group, style) {
425
460
  }
426
461
  }
427
462
  }
428
- } else {
429
- // Set zero padding on side of the matrix
430
- for (let i = 0; i < tbl.length; i++) {
431
- tbl[i].children[0].style.paddingLeft = "0em"
432
- if (tbl[i].children.length === tbl[0].children.length) {
433
- tbl[i].children[tbl[i].children.length - 1].style.paddingRight = "0em"
434
- }
435
- }
436
463
  }
437
464
 
438
465
  let table = new mathMLTree.MathNode("mtable", tbl)
@@ -672,7 +699,7 @@ defineEnvironment({
672
699
  mathmlBuilder
673
700
  });
674
701
 
675
- // The matrix environments of amsmath builds on the array environment
702
+ // The matrix environments of amsmath build on the array environment
676
703
  // of LaTeX, which is discussed above.
677
704
  // The mathtools package adds starred versions of the same environments.
678
705
  // These have an optional argument to choose left|center|right justification.
@@ -732,6 +759,10 @@ defineEnvironment({
732
759
  const res = parseArray(context.parser, payload, "text")
733
760
  res.cols = new Array(res.body[0].length).fill({ type: "align", align: colAlign })
734
761
  const [arraystretch, arraycolsep] = arrayGaps(context.parser.gullet.macros)
762
+ res.arraystretch = arraystretch
763
+ if (arraycolsep && !(arraycolsep === 6 && arraycolsep === "pt")) {
764
+ res.arraycolsep = arraycolsep
765
+ }
735
766
  return delimiters
736
767
  ? {
737
768
  type: "leftright",
@@ -739,15 +770,31 @@ defineEnvironment({
739
770
  body: [res],
740
771
  left: delimiters[0],
741
772
  right: delimiters[1],
742
- rightColor: undefined, // \right uninfluenced by \color in array
743
- arraystretch,
744
- arraycolsep
773
+ rightColor: undefined // \right uninfluenced by \color in array
745
774
  }
746
775
  : res;
747
776
  },
748
777
  mathmlBuilder
749
778
  });
750
779
 
780
+ defineEnvironment({
781
+ type: "array",
782
+ names: ["bordermatrix"],
783
+ props: {
784
+ numArgs: 0
785
+ },
786
+ handler(context) {
787
+ const payload = { cols: [], envClasses: ["bordermatrix"] }
788
+ const res = parseArray(context.parser, payload, "text")
789
+ res.cols = new Array(res.body[0].length).fill({ type: "align", align: "c" })
790
+ res.envClasses = [];
791
+ res.arraystretch = 1
792
+ if (context.envName === "matrix") { return res}
793
+ return bordermatrixParseTree(res, context.delimiters)
794
+ },
795
+ mathmlBuilder
796
+ });
797
+
751
798
  defineEnvironment({
752
799
  type: "array",
753
800
  names: ["smallmatrix"],
@@ -0,0 +1,139 @@
1
+
2
+ const ordGroup = (body) => {
3
+ return {
4
+ "type": "ordgroup",
5
+ "mode": "math",
6
+ "body": body,
7
+ "semisimple": true
8
+ }
9
+ }
10
+
11
+ const phantom = (body, type) => {
12
+ return {
13
+ "type": type,
14
+ "mode": "math",
15
+ "body": ordGroup(body)
16
+ }
17
+ }
18
+
19
+ /*
20
+ * A helper for \bordermatrix.
21
+ * parseArray() has parsed the tokens as if the environment
22
+ * was \begin{matrix}. That parse tree is this function’s input.
23
+ * Here, we rearrange the parse tree to get one that will
24
+ * result in TeX \bordermatrix.
25
+ * The final result includes a {pmatrix}, which is the bottom
26
+ * half of a <mover> element. The top of the <mover> contains
27
+ * the \bordermatrix headings. The top section also contains the
28
+ * contents of the bottom {pmatrix}. Those elements are hidden via
29
+ * \hphantom, but they ensure that column widths are the same top and
30
+ * bottom.
31
+ *
32
+ * We also create a left {matrix} with a single column that contains
33
+ * elements shifted out of the matrix. The left {matrix} also
34
+ * contains \vphantom copies of the other {pmatrix} elements.
35
+ * As before, this ensures consistent row heights of left and main.
36
+ */
37
+
38
+ export const bordermatrixParseTree = (matrix, delimiters) => {
39
+ const body = matrix.body
40
+ body[0].shift() // dispose of top left cell
41
+
42
+ // Create an array for the left column
43
+ const leftColumnBody = new Array(body.length - 1).fill().map(() => [])
44
+ for (let i = 1; i < body.length; i++) {
45
+ // The visible part of the cell
46
+ leftColumnBody[i - 1].push(body[i].shift())
47
+ // A vphantom with contents from the pmatrix, to set minimum cell height
48
+ const phantomBody = [];
49
+ for (let j = 0; j < body[i].length; j++) {
50
+ phantomBody.push(structuredClone(body[i][j]))
51
+ }
52
+ leftColumnBody[i - 1].push(phantom(phantomBody, "vphantom"))
53
+ }
54
+
55
+ // Create an array for the top row
56
+ const topRowBody = new Array(body.length).fill().map(() => [])
57
+ for (let j = 0; j < body[0].length; j++) {
58
+ topRowBody[0].push(structuredClone(body[0][j]))
59
+ }
60
+ // Copy the rest of the pmatrix, but squashed via \hphantom
61
+ for (let i = 1; i < body.length; i++) {
62
+ for (let j = 0; j < body[0].length; j++) {
63
+ topRowBody[i].push(phantom(structuredClone(body[i][j]).body, "hphantom"))
64
+ }
65
+ }
66
+
67
+ // Squash the top row of the main {pmatrix}
68
+ for (let j = 0; j < body[0].length; j++) {
69
+ body[0][j] = phantom(structuredClone(body[0][j]).body, "hphantom")
70
+ }
71
+
72
+ // Now wrap the arrays in the proper parse nodes.
73
+
74
+ const leftColumn = {
75
+ type: "array",
76
+ mode: "math",
77
+ body: leftColumnBody,
78
+ cols: [{ type: "align", align: "c" }],
79
+ rowGaps: new Array(leftColumnBody.length - 1).fill(null),
80
+ hLinesBeforeRow: new Array(leftColumnBody.length + 1).fill().map(() => []),
81
+ envClasses: [],
82
+ scriptLevel: "text",
83
+ arraystretch: 1,
84
+ labels: new Array(leftColumnBody.length).fill(""),
85
+ arraycolsep: { "number": 0.04, unit: "em" }
86
+ }
87
+
88
+ const topRow = {
89
+ type: "array",
90
+ mode: "math",
91
+ body: topRowBody,
92
+ cols: new Array(topRowBody.length).fill({ type: "align", align: "c" }),
93
+ rowGaps: new Array(topRowBody.length - 1).fill(null),
94
+ hLinesBeforeRow: new Array(topRowBody.length + 1).fill().map(() => []),
95
+ envClasses: [],
96
+ scriptLevel: "text",
97
+ arraystretch: 1,
98
+ labels: new Array(topRowBody.length).fill(""),
99
+ arraycolsep: null
100
+ }
101
+
102
+ const topWrapper = {
103
+ type: "styling",
104
+ mode: "math",
105
+ scriptLevel: "text", // Must set this explicitly.
106
+ body: [topRow] // Default level is "script".
107
+ }
108
+
109
+ const container = {
110
+ type: "leftright",
111
+ mode: "math",
112
+ body: [matrix],
113
+ left: delimiters ? delimiters[0] : "(",
114
+ right: delimiters ? delimiters[1] : ")",
115
+ rightColor: undefined
116
+ }
117
+
118
+ const base = {
119
+ type: "op", // The base of a TeX \overset
120
+ mode: "math",
121
+ limits: true,
122
+ alwaysHandleSupSub: true,
123
+ parentIsSupSub: true,
124
+ symbol: false,
125
+ stack: true,
126
+ suppressBaseShift: true,
127
+ body: [container]
128
+ }
129
+
130
+ const mover = {
131
+ type: "supsub", // We're using the MathML equivalent
132
+ mode: "math", // of TeX \overset.
133
+ base: base, // That keeps the {pmatrix} aligned with
134
+ sup: topWrapper, // the math centerline.
135
+ sub: null
136
+ }
137
+
138
+ return ordGroup([leftColumn, mover])
139
+ }
@@ -6,7 +6,7 @@ import * as mml from "../buildMathML";
6
6
 
7
7
  // Helper functions
8
8
 
9
- const padding = width => {
9
+ export const padding = width => {
10
10
  const node = new mathMLTree.MathNode("mspace")
11
11
  node.setAttribute("width", width + "em")
12
12
  return node
@@ -0,0 +1,42 @@
1
+ import defineFunction from "../defineFunction"
2
+ import environments from "../environments"
3
+
4
+ // \bordermatrix from TeXbook pp 177 & 361
5
+ // Optional argument from Herbert Voß, Math mode, p 20
6
+ // Ref: https://tug.ctan.org/obsolete/info/math/voss/mathmode/Mathmode.pdf
7
+
8
+ defineFunction({
9
+ type: "bordermatrix",
10
+ names: ["\\bordermatrix", "\\matrix"],
11
+ props: {
12
+ numArgs: 0,
13
+ numOptionalArgs: 1
14
+ },
15
+ handler: ({ parser, funcName }, args, optArgs) => {
16
+ // Find out if the author has defined custom delimiters
17
+ let delimiters = ["(", ")"]
18
+ if (funcName === "\\bordermatrix" && optArgs[0] && optArgs[0].body) {
19
+ const body = optArgs[0].body
20
+ if (body.length === 2 && body[0].type === "atom" && body[1].type === "atom") {
21
+ if (body[0].family === "open" && body[1].family === "close") {
22
+ delimiters = [body[0].text, body[1].text]
23
+ }
24
+ }
25
+ }
26
+ // consume the opening brace
27
+ parser.consumeSpaces()
28
+ parser.consume()
29
+
30
+ // Pass control to the environment handler in array.js.
31
+ const env = environments["bordermatrix"];
32
+ const context = {
33
+ mode: parser.mode,
34
+ envName: funcName.slice(1),
35
+ delimiters,
36
+ parser
37
+ }
38
+ const result = env.handler(context)
39
+ parser.expect("}", true)
40
+ return result
41
+ }
42
+ });
@@ -1,18 +1,13 @@
1
1
  import defineFunction, { ordargument } from "../defineFunction";
2
2
  import symbols from "../symbols";
3
3
  import mathMLTree from "../mathMLTree";
4
- import utils from "../utils";
4
+ import utils from "../utils.js"
5
+ import { padding } from "./arrow";
5
6
 
6
7
  import * as mml from "../buildMathML";
7
8
 
8
9
  const textAtomTypes = ["text", "textord", "mathord", "atom"]
9
10
 
10
- const padding = width => {
11
- const node = new mathMLTree.MathNode("mspace")
12
- node.setAttribute("width", width + "em")
13
- return node
14
- }
15
-
16
11
  function mathmlBuilder(group, style) {
17
12
  let node;
18
13
  const inner = mml.buildExpression(group.body, style);
@@ -128,14 +123,19 @@ defineFunction({
128
123
  break
129
124
  }
130
125
  }
131
- return {
132
- type: "mclass",
133
- mode: parser.mode,
134
- mclass: "m" + funcName.slice(5),
135
- body: ordargument(mustPromote ? mord : body),
136
- isCharacterBox,
137
- mustPromote
138
- };
126
+ if (mustPromote && funcName === "\\mathord" && mord.type === "mathord"
127
+ && mord.text.length > 1) {
128
+ return mord
129
+ } else {
130
+ return {
131
+ type: "mclass",
132
+ mode: parser.mode,
133
+ mclass: "m" + funcName.slice(5),
134
+ body: ordargument(mustPromote ? mord : body),
135
+ isCharacterBox,
136
+ mustPromote
137
+ };
138
+ }
139
139
  },
140
140
  mathmlBuilder
141
141
  });
@@ -19,7 +19,7 @@ const mathmlBuilder = (group, style) => {
19
19
  for (let i = 0; i < expression.length; i++) {
20
20
  let node = expression[i]
21
21
  if (node instanceof mathMLTree.MathNode) {
22
- if (node.type === "mrow" && node.children.length === 1 &&
22
+ if ((node.type === "mrow" || node.type === "mpadded") && node.children.length === 1 &&
23
23
  node.children[0] instanceof mathMLTree.MathNode) {
24
24
  node = node.children[0]
25
25
  }
@@ -19,7 +19,7 @@ const symbolRegEx = /^m(over|under|underover)$/
19
19
  defineFunctionBuilders({
20
20
  type: "supsub",
21
21
  mathmlBuilder(group, style) {
22
- // Is the inner group a relevant horizonal brace?
22
+ // Is the inner group a relevant horizontal brace?
23
23
  let isBrace = false
24
24
  let isOver
25
25
  let isSup
@@ -1,6 +1,7 @@
1
1
  import { defineFunctionBuilders } from "../defineFunction";
2
2
  import mathMLTree from "../mathMLTree";
3
3
  import * as mml from "../buildMathML";
4
+ import { padding } from "./arrow";
4
5
 
5
6
  // Operator ParseNodes created in Parser.js from symbol Groups in src/symbols.js.
6
7
 
@@ -47,6 +48,14 @@ defineFunctionBuilders({
47
48
  // ":" is not in the MathML operator dictionary. Give it BIN spacing.
48
49
  node.attributes.lspace = "0.2222em"
49
50
  node.attributes.rspace = "0.2222em"
51
+ } else if (group.needsSpacing) {
52
+ // Fix a MathML bug that occurs when a <mo> is between two <mtext> elements.
53
+ if (group.family === "bin") {
54
+ return new mathMLTree.MathNode("mrow", [padding(0.222), node, padding(0.222)])
55
+ } else {
56
+ // REL spacing
57
+ return new mathMLTree.MathNode("mrow", [padding(0.2778), node, padding(0.2778)])
58
+ }
50
59
  }
51
60
  return node;
52
61
  }
@@ -41,7 +41,9 @@ defineFunctionBuilders({
41
41
  node.setAttribute("mathvariant", "normal")
42
42
  if (text.text.length === 1) {
43
43
  // A Firefox bug will apply spacing here, but there should be none. Fix it.
44
- node = new mathMLTree.MathNode("mrow", [node])
44
+ node = new mathMLTree.MathNode("mpadded", [node])
45
+ node.setAttribute("lspace", "0")
46
+ node.setAttribute("rspace", "0")
45
47
  }
46
48
  }
47
49
  return node
package/src/functions.js CHANGED
@@ -9,6 +9,7 @@ export default functions;
9
9
  import "./functions/accent";
10
10
  import "./functions/accentunder";
11
11
  import "./functions/arrow";
12
+ import "./functions/bordermatrix"
12
13
  //import "./functions/cancelto";
13
14
  import "./environments/cd";
14
15
  import "./functions/char";
@@ -5,7 +5,7 @@
5
5
  * https://mit-license.org/
6
6
  */
7
7
 
8
- export const version = "0.11.00";
8
+ export const version = "0.11.02";
9
9
 
10
10
  export function postProcess(block) {
11
11
  const labelMap = {}
@@ -66,6 +66,7 @@ export function postProcess(block) {
66
66
  mtext.appendChild(document.createTextNode(str))
67
67
  const math = document.createElementNS("http://www.w3.org/1998/Math/MathML", "math")
68
68
  math.appendChild(mtext)
69
+ ref.textContent = ''
69
70
  ref.appendChild(math)
70
71
  })
71
72
  }
package/src/symbols.js CHANGED
@@ -895,7 +895,7 @@ defineSymbol(text, accent, "\u02d9", "\\."); // dot above
895
895
  defineSymbol(text, accent, "\u00b8", "\\c"); // cedilla
896
896
  defineSymbol(text, accent, "\u02da", "\\r"); // ring above
897
897
  defineSymbol(text, accent, "\u02c7", "\\v"); // caron
898
- defineSymbol(text, accent, "\u00a8", '\\"'); // diaresis
898
+ defineSymbol(text, accent, "\u00a8", '\\"'); // diaeresis
899
899
  defineSymbol(text, accent, "\u02dd", "\\H"); // double acute
900
900
  defineSymbol(math, accent, "\u02ca", "\\'"); // acute
901
901
  defineSymbol(math, accent, "\u02cb", "\\`"); // grave
@@ -907,7 +907,7 @@ defineSymbol(math, accent, "\u02d9", "\\."); // dot above
907
907
  defineSymbol(math, accent, "\u00b8", "\\c"); // cedilla
908
908
  defineSymbol(math, accent, "\u02da", "\\r"); // ring above
909
909
  defineSymbol(math, accent, "\u02c7", "\\v"); // caron
910
- defineSymbol(math, accent, "\u00a8", '\\"'); // diaresis
910
+ defineSymbol(math, accent, "\u00a8", '\\"'); // diaeresis
911
911
  defineSymbol(math, accent, "\u02dd", "\\H"); // double acute
912
912
 
913
913
  // These ligatures are detected and created in Parser.js's `formLigatures`.
package/temml.js CHANGED
@@ -104,7 +104,7 @@ const renderError = function(error, expression, options) {
104
104
  if (options.throwOnError || !(error instanceof ParseError)) {
105
105
  throw error;
106
106
  }
107
- const node = new Span(["temml-error"], [new TextNode(expression + "\n" + error.toString())]);
107
+ const node = new Span(["temml-error"], [new TextNode(expression + "\n\n" + error.toString())]);
108
108
  node.style.color = options.errorColor
109
109
  node.style.whiteSpace = "pre-line"
110
110
  return node;