MathtoolsUtil.ts 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /*************************************************************
  2. * Copyright (c) 2021-2022 MathJax Consortium
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /**
  17. * @fileoverview Utility functions for the mathtools package.
  18. *
  19. * @author dpvc@mathjax.org (Davide P. Cervone)
  20. */
  21. import {EqnArrayItem} from '../base/BaseItems.js';
  22. import ParseUtil from '../ParseUtil.js';
  23. import TexParser from '../TexParser.js';
  24. import TexError from '../TexError.js';
  25. import {CommandMap} from '../SymbolMap.js';
  26. import {Macro} from '../Symbol.js';
  27. import ParseOptions from '../ParseOptions.js';
  28. import {lookup} from '../../../util/Options.js';
  29. import {MmlNode} from '../../../core/MmlTree/MmlNode.js';
  30. import {MathtoolsMethods} from './MathtoolsMethods.js';
  31. import {PAIREDDELIMS} from './MathtoolsConfiguration.js';
  32. /**
  33. * Utility functions for the Mathtools package.
  34. */
  35. export const MathtoolsUtil = {
  36. /**
  37. * Set the displaystyle and scriptlevel attributes of an mstyle element
  38. *
  39. * @param {MmlNode} mml The mstyle node to modify.
  40. * @param {string} style The TeX style macro to apply.
  41. */
  42. setDisplayLevel(mml: MmlNode, style: string) {
  43. if (!style) return;
  44. const [display, script] = lookup(style, {
  45. '\\displaystyle': [true, 0],
  46. '\\textstyle': [false, 0],
  47. '\\scriptstyle': [false, 1],
  48. '\\scriptscriptstyle': [false, 2]
  49. }, [null, null]);
  50. if (display !== null) {
  51. mml.attributes.set('displaystyle', display);
  52. mml.attributes.set('scriptlevel', script);
  53. }
  54. },
  55. /**
  56. * Check that the top stack item is an alignment table.
  57. *
  58. * @param {TexParser} parser The current TeX parser.
  59. * @param {string} name The name of the macro doing the checking.
  60. * @return {EqnArrayItem} The top item (an EqnArrayItem).
  61. */
  62. checkAlignment(parser: TexParser, name: string): EqnArrayItem {
  63. const top = parser.stack.Top() as EqnArrayItem;
  64. if (top.kind !== EqnArrayItem.prototype.kind) {
  65. throw new TexError('NotInAlignment', '%1 can only be used in aligment environments', name);
  66. }
  67. return top;
  68. },
  69. /**
  70. * Add a paired delimiter to the list of them.
  71. *
  72. * @param {ParseOptions} config The parse options to modify.
  73. * @param {string} cs The control sequence for the paired delimiters.
  74. * @param {string[]} args The definition for the paired delimiters. One of:
  75. * [left, right]
  76. * [left, right, body, argcount]
  77. * [left, right, body, argcount, pre, post]
  78. */
  79. addPairedDelims(config: ParseOptions, cs: string, args: string[]) {
  80. const delims = config.handlers.retrieve(PAIREDDELIMS) as CommandMap;
  81. delims.add(cs, new Macro(cs, MathtoolsMethods.PairedDelimiters, args));
  82. },
  83. /**
  84. * Adjust the line spacing for a table.
  85. *
  86. * @param {MmlNode} mtable The mtable node to adjust (if it is a table).
  87. * @param {string} spread The dimension to change by (number-with-units).
  88. */
  89. spreadLines(mtable: MmlNode, spread: string) {
  90. if (!mtable.isKind('mtable')) return;
  91. let rowspacing = mtable.attributes.get('rowspacing') as string;
  92. if (rowspacing) {
  93. const add = ParseUtil.dimen2em(spread);
  94. rowspacing = rowspacing
  95. .split(/ /)
  96. .map(s => ParseUtil.Em(Math.max(0, ParseUtil.dimen2em(s) + add)))
  97. .join(' ');
  98. } else {
  99. rowspacing = spread;
  100. }
  101. mtable.attributes.set('rowspacing', rowspacing);
  102. },
  103. /**
  104. * Check if a string is a number and return it with an explicit plus if there isn't one.
  105. *
  106. * @param {string} name The name of the macro doing the checking.
  107. * @param {string} n The string to test as a number.
  108. * @return {srtring} The number with an explicit sign.
  109. */
  110. plusOrMinus(name: string, n: string): string {
  111. n = n.trim();
  112. if (!n.match(/^[-+]?(?:\d+(?:\.\d*)?|\.\d+)$/)) {
  113. throw new TexError('NotANumber', 'Argument to %1 is not a number', name);
  114. }
  115. return (n.match(/^[-+]/) ? n : '+' + n);
  116. },
  117. /**
  118. * Parse a \prescript argument, with its associated format, if any.
  119. *
  120. * @param {TexParser} parser The active tex parser.
  121. * @param {string} name The name of the calling macro (\prescript).
  122. * @param {string} pos The position for the argument (sub, sup, arg).
  123. * @return {MmlNode} The parsed MML version of the argument.
  124. */
  125. getScript(parser: TexParser, name: string, pos: string): MmlNode {
  126. let arg = ParseUtil.trimSpaces(parser.GetArgument(name));
  127. if (arg === '') {
  128. return parser.create('node', 'none');
  129. }
  130. const format = parser.options.mathtools[`prescript-${pos}-format`];
  131. format && (arg = `${format}{${arg}}`);
  132. return new TexParser(arg, parser.stack.env, parser.configuration).mml();
  133. }
  134. };