docxMath.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import {
  2. Math as DocxMath,
  3. MathRun,
  4. MathFraction,
  5. MathRadical,
  6. MathSuperScript,
  7. MathSubScript,
  8. MathSubSuperScript
  9. } from "docx"
  10. type MathMLTag =
  11. | "MATH" // MathML集合
  12. | "MI" // 标识符
  13. | "MN" // 数字
  14. | "MO" // 符号
  15. | "MTEXT" // 一般文本(非数学内容)
  16. | "MS" // 字符串文本
  17. | "MGLYPH" // 自定义图形符号
  18. | "MROW" // 水平排列子表达式
  19. | "MFRAC" // 分数 (numerator/denominator)
  20. | "MSUP" // 上标(如 x²)
  21. | "MSUB" // 下标(如 a₁)
  22. | "MSUBSUP" // 同时上标与下标
  23. | "MSQRT" // 平方根
  24. | "MROOT" // 带根指数的根式(如 ∛x)
  25. | string; // 支持未列出的标签
  26. /**
  27. * docx支持的数学公式符合如下:
  28. * MathRun 文本
  29. * MathFraction 分数
  30. * - numerator 分子
  31. * - denominator 分母
  32. * MathSum 累加和
  33. * - subScript 变量和初值(i=1)
  34. * - superScript 最大值(n)
  35. * MathRadical 根号
  36. * - degree 次数
  37. * MathSuperScript 上标
  38. * - superScript 上标文本或指数
  39. * MathSubScript 下标
  40. * - subScript 下标文本
  41. * MathSubSuperScript 上下标
  42. * - superScript 上标文本
  43. * - subScript 下标文本
  44. * MathFunctions 函数
  45. * - name 函数名
  46. * MathSquareBrackets 方括号[]
  47. * MathRoundBrackets 圆括号()
  48. * MathCurlyBrackets 花括号{}
  49. * MathAngledBrackets 尖括号<>
  50. * MathLimitUpper 上限(效果未知)
  51. * - limit
  52. * MathLimitLower 下限(效果未知)
  53. * - limit
  54. **/
  55. interface ParsedMathMLNode {
  56. tag: MathMLTag;
  57. text: string | undefined;
  58. children: ParsedMathMLNode[];
  59. }
  60. function convertMathMLElement(node: Element): ParsedMathMLNode {
  61. const tag = node.tagName; // 如 'MFRAC', 'MI'
  62. const children = Array.from(node.children).map(convertMathMLElement);
  63. const text = node.textContent?.trim();
  64. return {
  65. tag,
  66. text,
  67. children
  68. };
  69. }
  70. function convertMathMLNode(node: ParsedMathMLNode) {
  71. switch (node.tag) {
  72. case "MATH":
  73. return new DocxMath({
  74. children: Array.from(node.children).map(convertMathMLNode),
  75. });
  76. case "MSUP":
  77. return new MathSuperScript({
  78. children: [convertMathMLNode(node.children[0])],
  79. superScript: convertMathMLNode(node.children[1]),
  80. });
  81. case "MSUB":
  82. return new MathSubScript({
  83. children: [convertMathMLNode(node.children[0])],
  84. subScript: convertMathMLNode(node.children[1]),
  85. });
  86. case "MSUBSUP":
  87. return new MathSubSuperScript({
  88. children: [convertMathMLNode(node.children[0])],
  89. subScript: convertMathMLNode(node.children[1]),
  90. superScript: convertMathMLNode(node.children[2]),
  91. });
  92. case "MFRAC":
  93. return new MathFraction({
  94. numerator: convertMathMLNode(node.children[0]),
  95. denominator: convertMathMLNode(node.children[1]),
  96. });
  97. case "MSQRT":
  98. return new MathRadical({
  99. children: [convertMathMLNode(node.children[0])],
  100. });
  101. case "MROOT":
  102. return new MathRadical({
  103. children: [convertMathMLNode(node.children[0])],
  104. degree: [convertMathMLNode(node.children[1])]
  105. });
  106. case "MI":
  107. case "MN":
  108. case "MO":
  109. case "MTEXT":
  110. case "MS":
  111. case "MGLYPH":
  112. return new MathRun(node.text ?? "");
  113. case "MROW":
  114. return Array.from(node.children).map(convertMathMLNode);
  115. default:
  116. console.warn("Unsupported MathML tag:", node.tag);
  117. return new MathRun(`[${node.tag}]`);
  118. }
  119. }
  120. const handleMath = (el: Element): DocxMath | undefined => {
  121. const tag = el.tagName.toLowerCase()
  122. // 段落处理
  123. if (tag === "math") {
  124. const mathMlNode = convertMathMLElement(el)
  125. return convertMathMLNode(mathMlNode)
  126. }
  127. }
  128. export default {
  129. handleMath,
  130. config: {} // 无配置
  131. }