temml 0.10.18 → 0.10.20

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.
package/dist/temml.mjs CHANGED
@@ -938,8 +938,8 @@ defineSymbol(math, textord, "\u2207", "\\nabla", true);
938
938
  defineSymbol(math, textord, "\u266d", "\\flat", true);
939
939
  defineSymbol(math, textord, "\u2113", "\\ell", true);
940
940
  defineSymbol(math, textord, "\u266e", "\\natural", true);
941
- defineSymbol(math, textord, "Å", "\\AA", true);
942
- defineSymbol(text, textord, "Å", "\\AA", true);
941
+ defineSymbol(math, textord, "Å", "\\Angstrom", true);
942
+ defineSymbol(text, textord, "Å", "\\Angstrom", true);
943
943
  defineSymbol(math, textord, "\u2663", "\\clubsuit", true);
944
944
  defineSymbol(math, textord, "\u2667", "\\varclubsuit", true);
945
945
  defineSymbol(math, textord, "\u2118", "\\wp", true);
@@ -2053,6 +2053,16 @@ const consolidateNumbers = expression => {
2053
2053
  expression[nums[i].start].text += expression[j].text;
2054
2054
  }
2055
2055
  expression.splice(nums[i].start + 1, nums[i].end - nums[i].start);
2056
+ // Check if the <mn> is followed by a numeric base in a supsub, e.g. the "3" in 123^4
2057
+ // If so, merge the first <mn> into the base.
2058
+ if (expression.length > nums[i].start + 1) {
2059
+ const nextTerm = expression[nums[i].start + 1];
2060
+ if (nextTerm.type === "supsub" && nextTerm.base && nextTerm.base.type === "textord" &&
2061
+ numberRegEx$1.test(nextTerm.base.text)) {
2062
+ nextTerm.base.text = expression[nums[i].start].text + nextTerm.base.text;
2063
+ expression.splice(nums[i].start, 1);
2064
+ }
2065
+ }
2056
2066
  }
2057
2067
  };
2058
2068
 
@@ -2060,20 +2070,22 @@ const consolidateNumbers = expression => {
2060
2070
  * Wrap the given array of nodes in an <mrow> node if needed, i.e.,
2061
2071
  * unless the array has length 1. Always returns a single node.
2062
2072
  */
2063
- const makeRow = function(body) {
2073
+ const makeRow = function(body, semisimple = false) {
2064
2074
  if (body.length === 1 && !(body[0] instanceof DocumentFragment)) {
2065
2075
  return body[0];
2066
- } else {
2076
+ } else if (!semisimple) {
2067
2077
  // Suppress spacing on <mo> nodes at both ends of the row.
2068
2078
  if (body[0] instanceof MathNode && body[0].type === "mo" && !body[0].attributes.fence) {
2069
2079
  body[0].attributes.lspace = "0em";
2080
+ body[0].attributes.rspace = "0em";
2070
2081
  }
2071
2082
  const end = body.length - 1;
2072
2083
  if (body[end] instanceof MathNode && body[end].type === "mo" && !body[end].attributes.fence) {
2084
+ body[end].attributes.lspace = "0em";
2073
2085
  body[end].attributes.rspace = "0em";
2074
2086
  }
2075
- return new mathMLTree.MathNode("mrow", body);
2076
2087
  }
2088
+ return new mathMLTree.MathNode("mrow", body);
2077
2089
  };
2078
2090
 
2079
2091
  const isRel = item => {
@@ -2087,10 +2099,10 @@ const isRel = item => {
2087
2099
  * (1) Suppress spacing when an author wraps an operator w/braces, as in {=}.
2088
2100
  * (2) Suppress spacing between two adjacent relations.
2089
2101
  */
2090
- const buildExpression = function(expression, style, isOrdgroup) {
2091
- if (expression.length === 1) {
2102
+ const buildExpression = function(expression, style, semisimple = false) {
2103
+ if (!semisimple && expression.length === 1) {
2092
2104
  const group = buildGroup$1(expression[0], style);
2093
- if (isOrdgroup && group instanceof MathNode && group.type === "mo") {
2105
+ if (group instanceof MathNode && group.type === "mo") {
2094
2106
  // When TeX writers want to suppress spacing on an operator,
2095
2107
  // they often put the operator by itself inside braces.
2096
2108
  group.setAttribute("lspace", "0em");
@@ -2120,8 +2132,8 @@ const buildExpression = function(expression, style, isOrdgroup) {
2120
2132
  * Equivalent to buildExpression, but wraps the elements in an <mrow>
2121
2133
  * if there's more than one. Returns a single node instead of an array.
2122
2134
  */
2123
- const buildExpressionRow = function(expression, style, isOrdgroup) {
2124
- return makeRow(buildExpression(expression, style, isOrdgroup));
2135
+ const buildExpressionRow = function(expression, style, semisimple = false) {
2136
+ return makeRow(buildExpression(expression, style, semisimple), semisimple);
2125
2137
  };
2126
2138
 
2127
2139
  /**
@@ -2806,7 +2818,8 @@ function cdArrow(arrowChar, labels, parser) {
2806
2818
  const arrowGroup = {
2807
2819
  type: "ordgroup",
2808
2820
  mode: "math",
2809
- body: [leftLabel, sizedArrow, rightLabel]
2821
+ body: [leftLabel, sizedArrow, rightLabel],
2822
+ semisimple: true
2810
2823
  };
2811
2824
  return parser.callFunction("\\\\cdparent", [arrowGroup], []);
2812
2825
  }
@@ -3928,20 +3941,12 @@ const mathmlBuilder$8 = (group, style) => {
3928
3941
  node.style.borderBottom = "0.065em solid";
3929
3942
  break
3930
3943
  case "\\cancel":
3931
- node.style.background = `linear-gradient(to top left,
3932
- rgba(0,0,0,0) 0%,
3933
- rgba(0,0,0,0) calc(50% - 0.06em),
3934
- rgba(0,0,0,1) 50%,
3935
- rgba(0,0,0,0) calc(50% + 0.06em),
3936
- rgba(0,0,0,0) 100%);`;
3944
+ // We can't use an inline background-gradient. It does not work client-side.
3945
+ // So set a class and put the rule in the external CSS file.
3946
+ node.classes.push("tml-cancel");
3937
3947
  break
3938
3948
  case "\\bcancel":
3939
- node.style.background = `linear-gradient(to top right,
3940
- rgba(0,0,0,0) 0%,
3941
- rgba(0,0,0,0) calc(50% - 0.06em),
3942
- rgba(0,0,0,1) 50%,
3943
- rgba(0,0,0,0) calc(50% + 0.06em),
3944
- rgba(0,0,0,0) 100%);`;
3949
+ node.classes.push("tml-bcancel");
3945
3950
  break
3946
3951
  /*
3947
3952
  case "\\longdiv":
@@ -3988,18 +3993,7 @@ rgba(0,0,0,0) 100%);`;
3988
3993
  break
3989
3994
  }
3990
3995
  case "\\xcancel":
3991
- node.style.background = `linear-gradient(to top left,
3992
- rgba(0,0,0,0) 0%,
3993
- rgba(0,0,0,0) calc(50% - 0.06em),
3994
- rgba(0,0,0,1) 50%,
3995
- rgba(0,0,0,0) calc(50% + 0.06em),
3996
- rgba(0,0,0,0) 100%),
3997
- linear-gradient(to top right,
3998
- rgba(0,0,0,0) 0%,
3999
- rgba(0,0,0,0) calc(50% - 0.06em),
4000
- rgba(0,0,0,1) 50%,
4001
- rgba(0,0,0,0) calc(50% + 0.06em),
4002
- rgba(0,0,0,0) 100%);`;
3996
+ node.classes.push("tml-xcancel");
4003
3997
  break
4004
3998
  }
4005
3999
  if (group.backgroundColor) {
@@ -4197,7 +4191,7 @@ const getTag = (group, style, rowNum) => {
4197
4191
  if (tagContents) {
4198
4192
  // The author has written a \tag or a \notag in this row.
4199
4193
  if (tagContents.body) {
4200
- tag = buildExpressionRow(tagContents.body, style);
4194
+ tag = buildExpressionRow(tagContents.body, style, true);
4201
4195
  tag.classes = ["tml-tag"];
4202
4196
  } else {
4203
4197
  // \notag. Return an empty span.
@@ -4244,7 +4238,9 @@ function parseArray(
4244
4238
  parser.gullet.macros.set("\\cr", "\\\\\\relax");
4245
4239
  }
4246
4240
  if (addEqnNum) {
4247
- parser.gullet.macros.set("\\tag", "\\env@tag{\\text{#1}}");
4241
+ parser.gullet.macros.set("\\tag", "\\@ifstar\\envtag@literal\\envtag@paren");
4242
+ parser.gullet.macros.set("\\envtag@paren", "\\env@tag{{(\\text{#1})}}");
4243
+ parser.gullet.macros.set("\\envtag@literal", "\\env@tag{\\text{#1}}");
4248
4244
  parser.gullet.macros.set("\\notag", "\\env@notag");
4249
4245
  parser.gullet.macros.set("\\nonumber", "\\env@notag");
4250
4246
  }
@@ -4285,7 +4281,8 @@ function parseArray(
4285
4281
  cell = {
4286
4282
  type: "ordgroup",
4287
4283
  mode: parser.mode,
4288
- body: cell
4284
+ body: cell,
4285
+ semisimple: true
4289
4286
  };
4290
4287
  row.push(cell);
4291
4288
  const next = parser.fetch().text;
@@ -5118,7 +5115,7 @@ const mathmlBuilder$6 = (group, style) => {
5118
5115
  const mathGroup = buildGroup$1(group.body, newStyle);
5119
5116
 
5120
5117
  if (mathGroup.children.length === 0) { return mathGroup } // empty group, e.g., \mathrm{}
5121
- if (font === "boldsymbol" && ["mo", "mpadded"].includes(mathGroup.type)) {
5118
+ if (font === "boldsymbol" && ["mo", "mpadded", "mrow"].includes(mathGroup.type)) {
5122
5119
  mathGroup.style.fontWeight = "bold";
5123
5120
  return mathGroup
5124
5121
  }
@@ -6475,10 +6472,10 @@ defineFunction({
6475
6472
  },
6476
6473
  mathmlBuilder(group, style) {
6477
6474
  if (group.isCharacterBox) {
6478
- const inner = buildExpression(group.body, style);
6475
+ const inner = buildExpression(group.body, style, true);
6479
6476
  return inner[0]
6480
6477
  } else {
6481
- return buildExpressionRow(group.body, style, true)
6478
+ return buildExpressionRow(group.body, style)
6482
6479
  }
6483
6480
  }
6484
6481
  });
@@ -6498,6 +6495,13 @@ const ordTypes = ["textord", "mathord", "ordgroup", "close", "leftright"];
6498
6495
  // NOTE: Unlike most `builders`s, this one handles not only "op", but also
6499
6496
  // "supsub" since some of them (like \int) can affect super/subscripting.
6500
6497
 
6498
+ const setSpacing = node => {
6499
+ // The user wrote a \mathop{…} function. Change spacing from default to OP spacing.
6500
+ // The most likely spacing for an OP is a thin space per TeXbook p170.
6501
+ node.attributes.lspace = "0.1667em";
6502
+ node.attributes.rspace = "0.1667em";
6503
+ };
6504
+
6501
6505
  const mathmlBuilder$2 = (group, style) => {
6502
6506
  let node;
6503
6507
 
@@ -6509,9 +6513,11 @@ const mathmlBuilder$2 = (group, style) => {
6509
6513
  } else {
6510
6514
  node.setAttribute("movablelimits", "false");
6511
6515
  }
6516
+ if (group.fromMathOp) { setSpacing(node); }
6512
6517
  } else if (group.body) {
6513
6518
  // This is an operator with children. Add them.
6514
6519
  node = new MathNode("mo", buildExpression(group.body, style));
6520
+ if (group.fromMathOp) { setSpacing(node); }
6515
6521
  } else {
6516
6522
  // This is a text operator. Add all of the characters from the operator's name.
6517
6523
  node = new MathNode("mi", [new TextNode(group.name.slice(1))]);
@@ -6631,6 +6637,7 @@ defineFunction({
6631
6637
  limits: true,
6632
6638
  parentIsSupSub: false,
6633
6639
  symbol: isSymbol,
6640
+ fromMathOp: true,
6634
6641
  stack: false,
6635
6642
  name: isSymbol ? arr[0].text : null,
6636
6643
  body: isSymbol ? null : ordargument(body)
@@ -6964,7 +6971,7 @@ defineMacro("\\operatorname",
6964
6971
  defineFunctionBuilders({
6965
6972
  type: "ordgroup",
6966
6973
  mathmlBuilder(group, style) {
6967
- return buildExpressionRow(group.body, style, true);
6974
+ return buildExpressionRow(group.body, style, group.semisimple);
6968
6975
  }
6969
6976
  });
6970
6977
 
@@ -8914,6 +8921,8 @@ defineMacro("\\quad", "\\hskip1em\\relax");
8914
8921
  // \def\qquad{\hskip2em\relax}
8915
8922
  defineMacro("\\qquad", "\\hskip2em\\relax");
8916
8923
 
8924
+ defineMacro("\\AA", "\\TextOrMath{\\Angstrom}{\\mathring{A}}\\relax");
8925
+
8917
8926
  // \tag@in@display form of \tag
8918
8927
  defineMacro("\\tag", "\\@ifstar\\tag@literal\\tag@paren");
8919
8928
  defineMacro("\\tag@paren", "\\tag@literal{({#1})}");
@@ -11991,6 +12000,8 @@ var unicodeSymbols = {
11991
12000
 
11992
12001
  /* eslint no-constant-condition:0 */
11993
12002
 
12003
+ const binLeftCancellers = ["bin", "op", "open", "punct", "rel"];
12004
+
11994
12005
  /**
11995
12006
  * This file contains the parser used to parse out a TeX expression from the
11996
12007
  * input. Since TeX isn't context-free, standard parsers don't work particularly
@@ -12760,8 +12771,7 @@ class Parser {
12760
12771
  body: expression,
12761
12772
  // A group formed by \begingroup...\endgroup is a semi-simple group
12762
12773
  // which doesn't affect spacing in math mode, i.e., is transparent.
12763
- // https://tex.stackexchange.com/questions/1930/when-should-one-
12764
- // use-begingroup-instead-of-bgroup
12774
+ // https://tex.stackexchange.com/questions/1930/
12765
12775
  semisimple: text === "\\begingroup" || undefined
12766
12776
  };
12767
12777
  } else {
@@ -12875,7 +12885,11 @@ class Parser {
12875
12885
  // Recognize base symbol
12876
12886
  let symbol;
12877
12887
  if (symbols[this.mode][text]) {
12878
- const group = symbols[this.mode][text].group;
12888
+ let group = symbols[this.mode][text].group;
12889
+ if (group === "bin" && binLeftCancellers.includes(this.prevAtomType)) {
12890
+ // Change from a binary operator to a unary (prefix) operator
12891
+ group = "open";
12892
+ }
12879
12893
  const loc = SourceLocation.range(nucleus);
12880
12894
  let s;
12881
12895
  if (Object.prototype.hasOwnProperty.call(ATOMS, group )) {
@@ -13145,7 +13159,7 @@ class Style {
13145
13159
  * https://mit-license.org/
13146
13160
  */
13147
13161
 
13148
- const version = "0.10.18";
13162
+ const version = "0.10.20";
13149
13163
 
13150
13164
  function postProcess(block) {
13151
13165
  const labelMap = {};
@@ -14,7 +14,7 @@
14
14
  * https://mit-license.org/
15
15
  */
16
16
 
17
- const version = "0.10.18";
17
+ const version = "0.10.20";
18
18
 
19
19
  function postProcess(block) {
20
20
  const labelMap = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "temml",
3
- "version": "0.10.18",
3
+ "version": "0.10.20",
4
4
  "description": "TeX to MathML conversion in JavaScript.",
5
5
  "main": "dist/temml.js",
6
6
  "engines": {
package/src/Parser.js CHANGED
@@ -15,6 +15,8 @@ import { isDelimiter } from "./functions/delimsizing"
15
15
  import unicodeAccents from /*preval*/ "./unicodeAccents";
16
16
  import unicodeSymbols from /*preval*/ "./unicodeSymbols";
17
17
 
18
+ const binLeftCancellers = ["bin", "op", "open", "punct", "rel"];
19
+
18
20
  /**
19
21
  * This file contains the parser used to parse out a TeX expression from the
20
22
  * input. Since TeX isn't context-free, standard parsers don't work particularly
@@ -784,8 +786,7 @@ export default class Parser {
784
786
  body: expression,
785
787
  // A group formed by \begingroup...\endgroup is a semi-simple group
786
788
  // which doesn't affect spacing in math mode, i.e., is transparent.
787
- // https://tex.stackexchange.com/questions/1930/when-should-one-
788
- // use-begingroup-instead-of-bgroup
789
+ // https://tex.stackexchange.com/questions/1930/
789
790
  semisimple: text === "\\begingroup" || undefined
790
791
  };
791
792
  } else {
@@ -899,7 +900,11 @@ export default class Parser {
899
900
  // Recognize base symbol
900
901
  let symbol;
901
902
  if (symbols[this.mode][text]) {
902
- const group = symbols[this.mode][text].group;
903
+ let group = symbols[this.mode][text].group;
904
+ if (group === "bin" && binLeftCancellers.includes(this.prevAtomType)) {
905
+ // Change from a binary operator to a unary (prefix) operator
906
+ group = "open"
907
+ }
903
908
  const loc = SourceLocation.range(nucleus);
904
909
  let s;
905
910
  if (Object.prototype.hasOwnProperty.call(ATOMS, group )) {
@@ -118,6 +118,16 @@ const consolidateNumbers = expression => {
118
118
  expression[nums[i].start].text += expression[j].text
119
119
  }
120
120
  expression.splice(nums[i].start + 1, nums[i].end - nums[i].start)
121
+ // Check if the <mn> is followed by a numeric base in a supsub, e.g. the "3" in 123^4
122
+ // If so, merge the first <mn> into the base.
123
+ if (expression.length > nums[i].start + 1) {
124
+ const nextTerm = expression[nums[i].start + 1];
125
+ if (nextTerm.type === "supsub" && nextTerm.base && nextTerm.base.type === "textord" &&
126
+ numberRegEx.test(nextTerm.base.text)) {
127
+ nextTerm.base.text = expression[nums[i].start].text + nextTerm.base.text
128
+ expression.splice(nums[i].start, 1)
129
+ }
130
+ }
121
131
  }
122
132
  }
123
133
 
@@ -125,20 +135,22 @@ const consolidateNumbers = expression => {
125
135
  * Wrap the given array of nodes in an <mrow> node if needed, i.e.,
126
136
  * unless the array has length 1. Always returns a single node.
127
137
  */
128
- export const makeRow = function(body) {
138
+ export const makeRow = function(body, semisimple = false) {
129
139
  if (body.length === 1 && !(body[0] instanceof DocumentFragment)) {
130
140
  return body[0];
131
- } else {
141
+ } else if (!semisimple) {
132
142
  // Suppress spacing on <mo> nodes at both ends of the row.
133
143
  if (body[0] instanceof MathNode && body[0].type === "mo" && !body[0].attributes.fence) {
134
144
  body[0].attributes.lspace = "0em"
145
+ body[0].attributes.rspace = "0em"
135
146
  }
136
147
  const end = body.length - 1
137
148
  if (body[end] instanceof MathNode && body[end].type === "mo" && !body[end].attributes.fence) {
149
+ body[end].attributes.lspace = "0em"
138
150
  body[end].attributes.rspace = "0em"
139
151
  }
140
- return new mathMLTree.MathNode("mrow", body);
141
152
  }
153
+ return new mathMLTree.MathNode("mrow", body);
142
154
  };
143
155
 
144
156
  const isRel = item => {
@@ -152,10 +164,10 @@ const isRel = item => {
152
164
  * (1) Suppress spacing when an author wraps an operator w/braces, as in {=}.
153
165
  * (2) Suppress spacing between two adjacent relations.
154
166
  */
155
- export const buildExpression = function(expression, style, isOrdgroup) {
156
- if (expression.length === 1) {
167
+ export const buildExpression = function(expression, style, semisimple = false) {
168
+ if (!semisimple && expression.length === 1) {
157
169
  const group = buildGroup(expression[0], style);
158
- if (isOrdgroup && group instanceof MathNode && group.type === "mo") {
170
+ if (group instanceof MathNode && group.type === "mo") {
159
171
  // When TeX writers want to suppress spacing on an operator,
160
172
  // they often put the operator by itself inside braces.
161
173
  group.setAttribute("lspace", "0em");
@@ -185,8 +197,8 @@ export const buildExpression = function(expression, style, isOrdgroup) {
185
197
  * Equivalent to buildExpression, but wraps the elements in an <mrow>
186
198
  * if there's more than one. Returns a single node instead of an array.
187
199
  */
188
- export const buildExpressionRow = function(expression, style, isOrdgroup) {
189
- return makeRow(buildExpression(expression, style, isOrdgroup));
200
+ export const buildExpressionRow = function(expression, style, semisimple = false) {
201
+ return makeRow(buildExpression(expression, style, semisimple), semisimple);
190
202
  };
191
203
 
192
204
  /**
@@ -44,7 +44,7 @@ const getTag = (group, style, rowNum) => {
44
44
  if (tagContents) {
45
45
  // The author has written a \tag or a \notag in this row.
46
46
  if (tagContents.body) {
47
- tag = mml.buildExpressionRow(tagContents.body, style)
47
+ tag = mml.buildExpressionRow(tagContents.body, style, true)
48
48
  tag.classes = ["tml-tag"]
49
49
  } else {
50
50
  // \notag. Return an empty span.
@@ -91,7 +91,9 @@ function parseArray(
91
91
  parser.gullet.macros.set("\\cr", "\\\\\\relax");
92
92
  }
93
93
  if (addEqnNum) {
94
- parser.gullet.macros.set("\\tag", "\\env@tag{\\text{#1}}");
94
+ parser.gullet.macros.set("\\tag", "\\@ifstar\\envtag@literal\\envtag@paren")
95
+ parser.gullet.macros.set("\\envtag@paren", "\\env@tag{{(\\text{#1})}}");
96
+ parser.gullet.macros.set("\\envtag@literal", "\\env@tag{\\text{#1}}")
95
97
  parser.gullet.macros.set("\\notag", "\\env@notag");
96
98
  parser.gullet.macros.set("\\nonumber", "\\env@notag")
97
99
  }
@@ -132,7 +134,8 @@ function parseArray(
132
134
  cell = {
133
135
  type: "ordgroup",
134
136
  mode: parser.mode,
135
- body: cell
137
+ body: cell,
138
+ semisimple: true
136
139
  };
137
140
  row.push(cell);
138
141
  const next = parser.fetch().text;
@@ -49,7 +49,8 @@ function cdArrow(arrowChar, labels, parser) {
49
49
  const arrowGroup = {
50
50
  type: "ordgroup",
51
51
  mode: "math",
52
- body: [leftLabel, sizedArrow, rightLabel]
52
+ body: [leftLabel, sizedArrow, rightLabel],
53
+ semisimple: true
53
54
  };
54
55
  return parser.callFunction("\\\\cdparent", [arrowGroup], []);
55
56
  }
@@ -34,20 +34,12 @@ const mathmlBuilder = (group, style) => {
34
34
  node.style.borderBottom = "0.065em solid"
35
35
  break
36
36
  case "\\cancel":
37
- node.style.background = `linear-gradient(to top left,
38
- rgba(0,0,0,0) 0%,
39
- rgba(0,0,0,0) calc(50% - 0.06em),
40
- rgba(0,0,0,1) 50%,
41
- rgba(0,0,0,0) calc(50% + 0.06em),
42
- rgba(0,0,0,0) 100%);`
37
+ // We can't use an inline background-gradient. It does not work client-side.
38
+ // So set a class and put the rule in the external CSS file.
39
+ node.classes.push("tml-cancel")
43
40
  break
44
41
  case "\\bcancel":
45
- node.style.background = `linear-gradient(to top right,
46
- rgba(0,0,0,0) 0%,
47
- rgba(0,0,0,0) calc(50% - 0.06em),
48
- rgba(0,0,0,1) 50%,
49
- rgba(0,0,0,0) calc(50% + 0.06em),
50
- rgba(0,0,0,0) 100%);`
42
+ node.classes.push("tml-bcancel")
51
43
  break
52
44
  /*
53
45
  case "\\longdiv":
@@ -94,18 +86,7 @@ rgba(0,0,0,0) 100%);`
94
86
  break
95
87
  }
96
88
  case "\\xcancel":
97
- node.style.background = `linear-gradient(to top left,
98
- rgba(0,0,0,0) 0%,
99
- rgba(0,0,0,0) calc(50% - 0.06em),
100
- rgba(0,0,0,1) 50%,
101
- rgba(0,0,0,0) calc(50% + 0.06em),
102
- rgba(0,0,0,0) 100%),
103
- linear-gradient(to top right,
104
- rgba(0,0,0,0) 0%,
105
- rgba(0,0,0,0) calc(50% - 0.06em),
106
- rgba(0,0,0,1) 50%,
107
- rgba(0,0,0,0) calc(50% + 0.06em),
108
- rgba(0,0,0,0) 100%);`
89
+ node.classes.push("tml-xcancel")
109
90
  break
110
91
  }
111
92
  if (group.backgroundColor) {
@@ -8,7 +8,7 @@ const mathmlBuilder = (group, style) => {
8
8
  const mathGroup = mml.buildGroup(group.body, newStyle)
9
9
 
10
10
  if (mathGroup.children.length === 0) { return mathGroup } // empty group, e.g., \mathrm{}
11
- if (font === "boldsymbol" && ["mo", "mpadded"].includes(mathGroup.type)) {
11
+ if (font === "boldsymbol" && ["mo", "mpadded", "mrow"].includes(mathGroup.type)) {
12
12
  mathGroup.style.fontWeight = "bold"
13
13
  return mathGroup
14
14
  }
@@ -37,10 +37,10 @@ defineFunction({
37
37
  },
38
38
  mathmlBuilder(group, style) {
39
39
  if (group.isCharacterBox) {
40
- const inner = mml.buildExpression(group.body, style);
40
+ const inner = mml.buildExpression(group.body, style, true);
41
41
  return inner[0]
42
42
  } else {
43
- return mml.buildExpressionRow(group.body, style, true)
43
+ return mml.buildExpressionRow(group.body, style)
44
44
  }
45
45
  }
46
46
  });
@@ -17,6 +17,13 @@ export const ordTypes = ["textord", "mathord", "ordgroup", "close", "leftright"]
17
17
  // NOTE: Unlike most `builders`s, this one handles not only "op", but also
18
18
  // "supsub" since some of them (like \int) can affect super/subscripting.
19
19
 
20
+ const setSpacing = node => {
21
+ // The user wrote a \mathop{…} function. Change spacing from default to OP spacing.
22
+ // The most likely spacing for an OP is a thin space per TeXbook p170.
23
+ node.attributes.lspace = "0.1667em"
24
+ node.attributes.rspace = "0.1667em"
25
+ }
26
+
20
27
  const mathmlBuilder = (group, style) => {
21
28
  let node;
22
29
 
@@ -28,9 +35,11 @@ const mathmlBuilder = (group, style) => {
28
35
  } else {
29
36
  node.setAttribute("movablelimits", "false")
30
37
  }
38
+ if (group.fromMathOp) { setSpacing(node) }
31
39
  } else if (group.body) {
32
40
  // This is an operator with children. Add them.
33
41
  node = new mathMLTree.MathNode("mo", mml.buildExpression(group.body, style));
42
+ if (group.fromMathOp) { setSpacing(node) }
34
43
  } else {
35
44
  // This is a text operator. Add all of the characters from the operator's name.
36
45
  node = new mathMLTree.MathNode("mi", [new mathMLTree.TextNode(group.name.slice(1))]);
@@ -150,6 +159,7 @@ defineFunction({
150
159
  limits: true,
151
160
  parentIsSupSub: false,
152
161
  symbol: isSymbol,
162
+ fromMathOp: true,
153
163
  stack: false,
154
164
  name: isSymbol ? arr[0].text : null,
155
165
  body: isSymbol ? null : ordargument(body)
@@ -4,6 +4,6 @@ import * as mml from "../buildMathML";
4
4
  defineFunctionBuilders({
5
5
  type: "ordgroup",
6
6
  mathmlBuilder(group, style) {
7
- return mml.buildExpressionRow(group.body, style, true);
7
+ return mml.buildExpressionRow(group.body, style, group.semisimple);
8
8
  }
9
9
  });
package/src/macros.js CHANGED
@@ -436,6 +436,8 @@ defineMacro("\\quad", "\\hskip1em\\relax");
436
436
  // \def\qquad{\hskip2em\relax}
437
437
  defineMacro("\\qquad", "\\hskip2em\\relax");
438
438
 
439
+ defineMacro("\\AA", "\\TextOrMath{\\Angstrom}{\\mathring{A}}\\relax")
440
+
439
441
  // \tag@in@display form of \tag
440
442
  defineMacro("\\tag", "\\@ifstar\\tag@literal\\tag@paren");
441
443
  defineMacro("\\tag@paren", "\\tag@literal{({#1})}");
@@ -8,7 +8,7 @@
8
8
  * https://mit-license.org/
9
9
  */
10
10
 
11
- export const version = "0.10.18";
11
+ export const version = "0.10.20";
12
12
 
13
13
  export function postProcess(block) {
14
14
  const labelMap = {}
package/src/symbols.js CHANGED
@@ -132,8 +132,8 @@ defineSymbol(math, textord, "\u2207", "\\nabla", true);
132
132
  defineSymbol(math, textord, "\u266d", "\\flat", true);
133
133
  defineSymbol(math, textord, "\u2113", "\\ell", true);
134
134
  defineSymbol(math, textord, "\u266e", "\\natural", true);
135
- defineSymbol(math, textord, "Å", "\\AA", true);
136
- defineSymbol(text, textord, "Å", "\\AA", true);
135
+ defineSymbol(math, textord, "Å", "\\Angstrom", true);
136
+ defineSymbol(text, textord, "Å", "\\Angstrom", true);
137
137
  defineSymbol(math, textord, "\u2663", "\\clubsuit", true);
138
138
  defineSymbol(math, textord, "\u2667", "\\varclubsuit", true);
139
139
  defineSymbol(math, textord, "\u2118", "\\wp", true);