math-formula-processor.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /**
  2. * 数学公式预处理器
  3. * 用于标准化各种数学公式格式,避免渲染死循环
  4. */
  5. window.MathFormulaProcessor = {
  6. /**
  7. * 预处理数学公式内容
  8. * @param {string} content 原始内容
  9. * @return {string} 处理后的内容
  10. */
  11. processContent: function(content) {
  12. if (!content || typeof content !== 'string') {
  13. return content;
  14. }
  15. // 0. 先清理重复分隔符(如 $$$ -> $$),该步始终执行
  16. content = this.cleanupDuplicateMarkers(content);
  17. if (!this.shouldPreprocess(content)) {
  18. return content;
  19. }
  20. // 1. 标准化数学公式分隔符
  21. content = this.standardizeDelimiters(content);
  22. // 1.5 清理重复分隔符(如 $$$ -> $ 或 $$)
  23. content = this.cleanupDuplicateMarkers(content);
  24. // 2. 转义特殊字符
  25. content = this.escapeSpecialChars(content);
  26. // 3. 修复常见的LaTeX错误
  27. content = this.fixLatexErrors(content);
  28. return content;
  29. },
  30. /**
  31. * 是否需要预处理(平衡的 $…$ 且无占位词时跳过,避免过度修改)
  32. */
  33. shouldPreprocess: function(content) {
  34. // 已有占位/异常字符则需要处理
  35. if (content.includes('\\text{空') || content.includes('题目内容')) {
  36. return true;
  37. }
  38. // 连续三个及以上 $ 需要清理
  39. if (/\${3,}/.test(content)) {
  40. return true;
  41. }
  42. // 统计 $ 个数,偶数且>0 时视为平衡
  43. const dollarCount = (content.match(/\$/g) || []).length;
  44. if (dollarCount > 0 && dollarCount % 2 === 0) {
  45. return false;
  46. }
  47. // 含有 \(\ 或 \[\ 等特殊定界符时仍可处理
  48. if (content.includes('\\(') || content.includes('\\[')) {
  49. return true;
  50. }
  51. return true;
  52. },
  53. /**
  54. * 标准化数学公式分隔符
  55. */
  56. standardizeDelimiters: function(content) {
  57. // 统一使用 $ 和 $$ 作为分隔符
  58. content = content.replace(/\\\(\\s*\$/g, '$');
  59. content = content.replace(/\$\\s*\\\)/g, '$');
  60. content = content.replace(/\\\[\\s*\$/g, '$$');
  61. content = content.replace(/\$\\s*\\\]/g, '$$');
  62. return content;
  63. },
  64. /**
  65. * 转义特殊字符,防止渲染冲突
  66. */
  67. escapeSpecialChars: function(content) {
  68. // 检测是否已经在公式中
  69. let inFormula = false;
  70. let result = '';
  71. for (let i = 0; i < content.length; i++) {
  72. const char = content[i];
  73. const nextChar = content[i + 1];
  74. // 检测公式开始
  75. if (char === '$' && nextChar === '$') {
  76. inFormula = true;
  77. result += char;
  78. i++; // 跳过下一个$
  79. continue;
  80. }
  81. if (char === '$') {
  82. inFormula = !inFormula;
  83. result += char;
  84. continue;
  85. }
  86. // 在公式外转义某些字符
  87. if (!inFormula) {
  88. switch (char) {
  89. case '\\':
  90. // 只转义不是LaTeX命令的反斜杠
  91. if (this.isLatexCommand(content, i)) {
  92. result += char;
  93. } else {
  94. result += '\\\\';
  95. }
  96. break;
  97. case '{':
  98. case '}':
  99. result += char;
  100. break;
  101. default:
  102. result += char;
  103. }
  104. } else {
  105. result += char;
  106. }
  107. }
  108. return result;
  109. },
  110. /**
  111. * 检测当前位置是否在LaTeX命令中
  112. */
  113. isLatexCommand: function(content, index) {
  114. // 简单检测:查看反斜杠后面是否跟着字母
  115. let i = index + 1;
  116. while (i < content.length && /\s/.test(content[i])) {
  117. i++;
  118. }
  119. return i < content.length && /[a-zA-Z]/.test(content[i]);
  120. },
  121. /**
  122. * 修复常见的LaTeX错误
  123. */
  124. fixLatexErrors: function(content) {
  125. // 1. 修复不匹配的公式分隔符
  126. let formulaCount = 0;
  127. let inDisplayFormula = false;
  128. for (let i = 0; i < content.length; i++) {
  129. if (content[i] === '$' && content[i + 1] === '$') {
  130. if (inDisplayFormula) {
  131. formulaCount++;
  132. inDisplayFormula = false;
  133. } else {
  134. inDisplayFormula = true;
  135. }
  136. i++; // 跳过下一个$
  137. } else if (content[i] === '$') {
  138. formulaCount++;
  139. }
  140. }
  141. // 如果公式数量是奇数,补全分隔符
  142. if (formulaCount % 2 === 1) {
  143. content += '$';
  144. }
  145. // 2. 清理空的数学公式(不再插入“空”占位,直接移除)
  146. content = content.replace(/\$\s*\$/g, ' ');
  147. content = content.replace(/\$\$\s*\$\$/g, ' ');
  148. return content;
  149. },
  150. /**
  151. * 清理重复的数学公式标记
  152. */
  153. cleanupDuplicateMarkers: function(content) {
  154. // 移除连续的相同分隔符
  155. content = content.replace(/\$\$\$\$\$/g, '$$');
  156. content = content.replace(/\$\$\$/g, '$');
  157. // 处理起始为 "$$...$" 的不匹配情况,改为单行公式 "$...$"
  158. content = content.replace(/^\$\$(.+)\$/s, (_, inner) => `$${inner}$`);
  159. return content;
  160. }
  161. };