ed-mathml2tex 0.2.1 → 0.2.3

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.
@@ -277,24 +277,24 @@ const MathSymbol = {
277
277
  bigCommand: {
278
278
  decimals: [8721, 8719, 8720, 10753, 10754, 10752, 8899, 8898, 10756, 10758, 8897, 8896, 8747, 8750, 8748, 8749, 10764, 8747],
279
279
  scripts: [
280
- "\\sum",
281
- "\\prod",
282
- "\\coprod",
283
- "\\bigoplus",
284
- "\\bigotimes",
285
- "\\bigodot",
286
- "\\bigcup",
287
- "\\bigcap",
288
- "\\biguplus",
289
- "\\bigsqcup",
290
- "\\bigvee",
291
- "\\bigwedge",
292
- "\\int",
293
- "\\oint",
294
- "\\iint",
295
- "\\iiint",
296
- "\\iiiint",
297
- "\\idotsint",
280
+ "\\sum ",
281
+ "\\prod ",
282
+ "\\coprod ",
283
+ "\\bigoplus ",
284
+ "\\bigotimes ",
285
+ "\\bigodot ",
286
+ "\\bigcup ",
287
+ "\\bigcap ",
288
+ "\\biguplus ",
289
+ "\\bigsqcup ",
290
+ "\\bigvee ",
291
+ "\\bigwedge ",
292
+ "\\int ",
293
+ "\\oint ",
294
+ "\\iint ",
295
+ "\\iiint ",
296
+ "\\iiiint ",
297
+ "\\idotsint ",
298
298
  ]
299
299
  },
300
300
 
@@ -612,9 +612,10 @@ function convert(mathmlHtml) {
612
612
  // Thêm xử lý cho các thẻ MathML khác
613
613
  result = result
614
614
  .replace(/∞/g, "\\infty") // Vô cực
615
- .replace(/∑/g, "\\sum") // Tổng
616
- .replace(/∏/g, "\\prod") // Tích
617
- .replace(/∫/g, "\\int"); // Tích phân
615
+ .replace(/∫/g, " \\int "); // Tích phân
616
+
617
+ // Đảm bảo dấu cách sau các lệnh LaTeX quan trọng để tránh dính biến (vd: \intf -> \int f)
618
+ result = result.replace(/\\(int|sum|prod|cos|sin|tan|cot|lim|log|ln)([a-zA-Z0-9])/g, "\\$1 $2");
618
619
 
619
620
  return result;
620
621
  }
@@ -773,7 +774,8 @@ function parseOperator(node) {
773
774
  "|": " \\mid ", // PATCH: set-builder mid
774
775
  π: " \\pi ", // PATCH: Greek letter
775
776
  };
776
- return operatorMap[it] || escapeSpecialChars(MathSymbol.parseOperator(it));
777
+ const res = operatorMap[it] || escapeSpecialChars(MathSymbol.parseOperator(it));
778
+ return res;
777
779
  }
778
780
  // --- END PATCH ---
779
781
 
@@ -800,6 +802,20 @@ function parseElementMi(node) {
800
802
  // Math Number
801
803
  function parseElementMn(node) {
802
804
  let it = NodeTool.getNodeText(node).trim();
805
+ // Loại bỏ các ký tự điều khiển hoặc khoảng trắng lạ
806
+ it = it.replace(/[\u0000-\u001F\u007F-\u009F\u00A0]/g, "");
807
+
808
+ // Danh sách các hàm toán học mở rộng
809
+ const mathFunctions = ["cos", "sin", "tan", "cot", "arccos", "arcsin", "arctan", "arccot", "log", "ln", "lim", "sinh", "cosh", "tanh", "sec", "csc"];
810
+
811
+ if (mathFunctions.some(fn => it.toLowerCase().includes(fn))) {
812
+ // Tìm hàm khớp chính xác nhất
813
+ for (const fn of mathFunctions) {
814
+ if (it.toLowerCase() === fn) {
815
+ return "\\" + fn + " ";
816
+ }
817
+ }
818
+ }
803
819
  return escapeSpecialChars(it);
804
820
  }
805
821
 
@@ -812,7 +828,7 @@ function parseElementMs(node) {
812
828
 
813
829
  // Math Text
814
830
  function parseElementMtext(node) {
815
- let content = NodeTool.getNodeText(node)
831
+ let content = escapeSpecialChars(NodeTool.getNodeText(node))
816
832
  .replace(/\s*=\s*/g, " = ")
817
833
  .replace(/\s*\.\s*/g, " \\cdot ")
818
834
  .trim();
@@ -866,6 +882,14 @@ function parseElementMspace(node) {
866
882
  }
867
883
 
868
884
  function escapeSpecialChars(text) {
885
+ // Strip problematic Unicode characters:
886
+ // - Control characters: \u0000-\u0008, \u000B-\u000C, \u000E-\u001F, \u007F-\u009F
887
+ // - Zero-width & Invisible: \u200B-\u200F, \u202A-\u202E, \u2060-\u206F, \uFEFF
888
+ // - Variation Selectors: \uFE00-\uFE0F
889
+ // - Private Use Area (PUA): \uE000-\uF8FF
890
+ // - Non-characters: \uFDD0-\uFDEF
891
+ text = text.replace(/[\u0000-\u0008\u000B-\u000C\u000E-\u001F\u007F-\u009F\u200B-\u200F\u202A-\u202E\u2060-\u206F\uFE00-\uFE0F\uFEFF\uE000-\uF8FF\uFDD0-\uFDEF]/g, "");
892
+
869
893
  // Don't escape pi, Greek, or just a-z0-9 or Unicode Greek, or empty
870
894
  if (
871
895
  /^\\?[a-zA-Z0-9]+$/.test(text) ||
@@ -275,24 +275,24 @@ const MathSymbol = {
275
275
  bigCommand: {
276
276
  decimals: [8721, 8719, 8720, 10753, 10754, 10752, 8899, 8898, 10756, 10758, 8897, 8896, 8747, 8750, 8748, 8749, 10764, 8747],
277
277
  scripts: [
278
- "\\sum",
279
- "\\prod",
280
- "\\coprod",
281
- "\\bigoplus",
282
- "\\bigotimes",
283
- "\\bigodot",
284
- "\\bigcup",
285
- "\\bigcap",
286
- "\\biguplus",
287
- "\\bigsqcup",
288
- "\\bigvee",
289
- "\\bigwedge",
290
- "\\int",
291
- "\\oint",
292
- "\\iint",
293
- "\\iiint",
294
- "\\iiiint",
295
- "\\idotsint",
278
+ "\\sum ",
279
+ "\\prod ",
280
+ "\\coprod ",
281
+ "\\bigoplus ",
282
+ "\\bigotimes ",
283
+ "\\bigodot ",
284
+ "\\bigcup ",
285
+ "\\bigcap ",
286
+ "\\biguplus ",
287
+ "\\bigsqcup ",
288
+ "\\bigvee ",
289
+ "\\bigwedge ",
290
+ "\\int ",
291
+ "\\oint ",
292
+ "\\iint ",
293
+ "\\iiint ",
294
+ "\\iiiint ",
295
+ "\\idotsint ",
296
296
  ]
297
297
  },
298
298
 
@@ -610,9 +610,10 @@ function convert(mathmlHtml) {
610
610
  // Thêm xử lý cho các thẻ MathML khác
611
611
  result = result
612
612
  .replace(/∞/g, "\\infty") // Vô cực
613
- .replace(/∑/g, "\\sum") // Tổng
614
- .replace(/∏/g, "\\prod") // Tích
615
- .replace(/∫/g, "\\int"); // Tích phân
613
+ .replace(/∫/g, " \\int "); // Tích phân
614
+
615
+ // Đảm bảo dấu cách sau các lệnh LaTeX quan trọng để tránh dính biến (vd: \intf -> \int f)
616
+ result = result.replace(/\\(int|sum|prod|cos|sin|tan|cot|lim|log|ln)([a-zA-Z0-9])/g, "\\$1 $2");
616
617
 
617
618
  return result;
618
619
  }
@@ -771,7 +772,8 @@ function parseOperator(node) {
771
772
  "|": " \\mid ", // PATCH: set-builder mid
772
773
  π: " \\pi ", // PATCH: Greek letter
773
774
  };
774
- return operatorMap[it] || escapeSpecialChars(MathSymbol.parseOperator(it));
775
+ const res = operatorMap[it] || escapeSpecialChars(MathSymbol.parseOperator(it));
776
+ return res;
775
777
  }
776
778
  // --- END PATCH ---
777
779
 
@@ -798,6 +800,20 @@ function parseElementMi(node) {
798
800
  // Math Number
799
801
  function parseElementMn(node) {
800
802
  let it = NodeTool.getNodeText(node).trim();
803
+ // Loại bỏ các ký tự điều khiển hoặc khoảng trắng lạ
804
+ it = it.replace(/[\u0000-\u001F\u007F-\u009F\u00A0]/g, "");
805
+
806
+ // Danh sách các hàm toán học mở rộng
807
+ const mathFunctions = ["cos", "sin", "tan", "cot", "arccos", "arcsin", "arctan", "arccot", "log", "ln", "lim", "sinh", "cosh", "tanh", "sec", "csc"];
808
+
809
+ if (mathFunctions.some(fn => it.toLowerCase().includes(fn))) {
810
+ // Tìm hàm khớp chính xác nhất
811
+ for (const fn of mathFunctions) {
812
+ if (it.toLowerCase() === fn) {
813
+ return "\\" + fn + " ";
814
+ }
815
+ }
816
+ }
801
817
  return escapeSpecialChars(it);
802
818
  }
803
819
 
@@ -810,7 +826,7 @@ function parseElementMs(node) {
810
826
 
811
827
  // Math Text
812
828
  function parseElementMtext(node) {
813
- let content = NodeTool.getNodeText(node)
829
+ let content = escapeSpecialChars(NodeTool.getNodeText(node))
814
830
  .replace(/\s*=\s*/g, " = ")
815
831
  .replace(/\s*\.\s*/g, " \\cdot ")
816
832
  .trim();
@@ -864,6 +880,14 @@ function parseElementMspace(node) {
864
880
  }
865
881
 
866
882
  function escapeSpecialChars(text) {
883
+ // Strip problematic Unicode characters:
884
+ // - Control characters: \u0000-\u0008, \u000B-\u000C, \u000E-\u001F, \u007F-\u009F
885
+ // - Zero-width & Invisible: \u200B-\u200F, \u202A-\u202E, \u2060-\u206F, \uFEFF
886
+ // - Variation Selectors: \uFE00-\uFE0F
887
+ // - Private Use Area (PUA): \uE000-\uF8FF
888
+ // - Non-characters: \uFDD0-\uFDEF
889
+ text = text.replace(/[\u0000-\u0008\u000B-\u000C\u000E-\u001F\u007F-\u009F\u200B-\u200F\u202A-\u202E\u2060-\u206F\uFE00-\uFE0F\uFEFF\uE000-\uF8FF\uFDD0-\uFDEF]/g, "");
890
+
867
891
  // Don't escape pi, Greek, or just a-z0-9 or Unicode Greek, or empty
868
892
  if (
869
893
  /^\\?[a-zA-Z0-9]+$/.test(text) ||
@@ -281,24 +281,24 @@
281
281
  bigCommand: {
282
282
  decimals: [8721, 8719, 8720, 10753, 10754, 10752, 8899, 8898, 10756, 10758, 8897, 8896, 8747, 8750, 8748, 8749, 10764, 8747],
283
283
  scripts: [
284
- "\\sum",
285
- "\\prod",
286
- "\\coprod",
287
- "\\bigoplus",
288
- "\\bigotimes",
289
- "\\bigodot",
290
- "\\bigcup",
291
- "\\bigcap",
292
- "\\biguplus",
293
- "\\bigsqcup",
294
- "\\bigvee",
295
- "\\bigwedge",
296
- "\\int",
297
- "\\oint",
298
- "\\iint",
299
- "\\iiint",
300
- "\\iiiint",
301
- "\\idotsint",
284
+ "\\sum ",
285
+ "\\prod ",
286
+ "\\coprod ",
287
+ "\\bigoplus ",
288
+ "\\bigotimes ",
289
+ "\\bigodot ",
290
+ "\\bigcup ",
291
+ "\\bigcap ",
292
+ "\\biguplus ",
293
+ "\\bigsqcup ",
294
+ "\\bigvee ",
295
+ "\\bigwedge ",
296
+ "\\int ",
297
+ "\\oint ",
298
+ "\\iint ",
299
+ "\\iiint ",
300
+ "\\iiiint ",
301
+ "\\idotsint ",
302
302
  ]
303
303
  },
304
304
 
@@ -616,9 +616,10 @@
616
616
  // Thêm xử lý cho các thẻ MathML khác
617
617
  result = result
618
618
  .replace(/∞/g, "\\infty") // Vô cực
619
- .replace(/∑/g, "\\sum") // Tổng
620
- .replace(/∏/g, "\\prod") // Tích
621
- .replace(/∫/g, "\\int"); // Tích phân
619
+ .replace(/∫/g, " \\int "); // Tích phân
620
+
621
+ // Đảm bảo dấu cách sau các lệnh LaTeX quan trọng để tránh dính biến (vd: \intf -> \int f)
622
+ result = result.replace(/\\(int|sum|prod|cos|sin|tan|cot|lim|log|ln)([a-zA-Z0-9])/g, "\\$1 $2");
622
623
 
623
624
  return result;
624
625
  }
@@ -777,7 +778,8 @@
777
778
  "|": " \\mid ", // PATCH: set-builder mid
778
779
  π: " \\pi ", // PATCH: Greek letter
779
780
  };
780
- return operatorMap[it] || escapeSpecialChars(MathSymbol.parseOperator(it));
781
+ const res = operatorMap[it] || escapeSpecialChars(MathSymbol.parseOperator(it));
782
+ return res;
781
783
  }
782
784
  // --- END PATCH ---
783
785
 
@@ -804,6 +806,20 @@
804
806
  // Math Number
805
807
  function parseElementMn(node) {
806
808
  let it = NodeTool.getNodeText(node).trim();
809
+ // Loại bỏ các ký tự điều khiển hoặc khoảng trắng lạ
810
+ it = it.replace(/[\u0000-\u001F\u007F-\u009F\u00A0]/g, "");
811
+
812
+ // Danh sách các hàm toán học mở rộng
813
+ const mathFunctions = ["cos", "sin", "tan", "cot", "arccos", "arcsin", "arctan", "arccot", "log", "ln", "lim", "sinh", "cosh", "tanh", "sec", "csc"];
814
+
815
+ if (mathFunctions.some(fn => it.toLowerCase().includes(fn))) {
816
+ // Tìm hàm khớp chính xác nhất
817
+ for (const fn of mathFunctions) {
818
+ if (it.toLowerCase() === fn) {
819
+ return "\\" + fn + " ";
820
+ }
821
+ }
822
+ }
807
823
  return escapeSpecialChars(it);
808
824
  }
809
825
 
@@ -816,7 +832,7 @@
816
832
 
817
833
  // Math Text
818
834
  function parseElementMtext(node) {
819
- let content = NodeTool.getNodeText(node)
835
+ let content = escapeSpecialChars(NodeTool.getNodeText(node))
820
836
  .replace(/\s*=\s*/g, " = ")
821
837
  .replace(/\s*\.\s*/g, " \\cdot ")
822
838
  .trim();
@@ -870,6 +886,14 @@
870
886
  }
871
887
 
872
888
  function escapeSpecialChars(text) {
889
+ // Strip problematic Unicode characters:
890
+ // - Control characters: \u0000-\u0008, \u000B-\u000C, \u000E-\u001F, \u007F-\u009F
891
+ // - Zero-width & Invisible: \u200B-\u200F, \u202A-\u202E, \u2060-\u206F, \uFEFF
892
+ // - Variation Selectors: \uFE00-\uFE0F
893
+ // - Private Use Area (PUA): \uE000-\uF8FF
894
+ // - Non-characters: \uFDD0-\uFDEF
895
+ text = text.replace(/[\u0000-\u0008\u000B-\u000C\u000E-\u001F\u007F-\u009F\u200B-\u200F\u202A-\u202E\u2060-\u206F\uFE00-\uFE0F\uFEFF\uE000-\uF8FF\uFDD0-\uFDEF]/g, "");
896
+
873
897
  // Don't escape pi, Greek, or just a-z0-9 or Unicode Greek, or empty
874
898
  if (
875
899
  /^\\?[a-zA-Z0-9]+$/.test(text) ||
@@ -277,24 +277,24 @@ const MathSymbol = {
277
277
  bigCommand: {
278
278
  decimals: [8721, 8719, 8720, 10753, 10754, 10752, 8899, 8898, 10756, 10758, 8897, 8896, 8747, 8750, 8748, 8749, 10764, 8747],
279
279
  scripts: [
280
- "\\sum",
281
- "\\prod",
282
- "\\coprod",
283
- "\\bigoplus",
284
- "\\bigotimes",
285
- "\\bigodot",
286
- "\\bigcup",
287
- "\\bigcap",
288
- "\\biguplus",
289
- "\\bigsqcup",
290
- "\\bigvee",
291
- "\\bigwedge",
292
- "\\int",
293
- "\\oint",
294
- "\\iint",
295
- "\\iiint",
296
- "\\iiiint",
297
- "\\idotsint",
280
+ "\\sum ",
281
+ "\\prod ",
282
+ "\\coprod ",
283
+ "\\bigoplus ",
284
+ "\\bigotimes ",
285
+ "\\bigodot ",
286
+ "\\bigcup ",
287
+ "\\bigcap ",
288
+ "\\biguplus ",
289
+ "\\bigsqcup ",
290
+ "\\bigvee ",
291
+ "\\bigwedge ",
292
+ "\\int ",
293
+ "\\oint ",
294
+ "\\iint ",
295
+ "\\iiint ",
296
+ "\\iiiint ",
297
+ "\\idotsint ",
298
298
  ]
299
299
  },
300
300
 
@@ -612,9 +612,10 @@ function convert(mathmlHtml) {
612
612
  // Thêm xử lý cho các thẻ MathML khác
613
613
  result = result
614
614
  .replace(/∞/g, "\\infty") // Vô cực
615
- .replace(/∑/g, "\\sum") // Tổng
616
- .replace(/∏/g, "\\prod") // Tích
617
- .replace(/∫/g, "\\int"); // Tích phân
615
+ .replace(/∫/g, " \\int "); // Tích phân
616
+
617
+ // Đảm bảo dấu cách sau các lệnh LaTeX quan trọng để tránh dính biến (vd: \intf -> \int f)
618
+ result = result.replace(/\\(int|sum|prod|cos|sin|tan|cot|lim|log|ln)([a-zA-Z0-9])/g, "\\$1 $2");
618
619
 
619
620
  return result;
620
621
  }
@@ -773,7 +774,8 @@ function parseOperator(node) {
773
774
  "|": " \\mid ", // PATCH: set-builder mid
774
775
  π: " \\pi ", // PATCH: Greek letter
775
776
  };
776
- return operatorMap[it] || escapeSpecialChars(MathSymbol.parseOperator(it));
777
+ const res = operatorMap[it] || escapeSpecialChars(MathSymbol.parseOperator(it));
778
+ return res;
777
779
  }
778
780
  // --- END PATCH ---
779
781
 
@@ -800,6 +802,20 @@ function parseElementMi(node) {
800
802
  // Math Number
801
803
  function parseElementMn(node) {
802
804
  let it = NodeTool.getNodeText(node).trim();
805
+ // Loại bỏ các ký tự điều khiển hoặc khoảng trắng lạ
806
+ it = it.replace(/[\u0000-\u001F\u007F-\u009F\u00A0]/g, "");
807
+
808
+ // Danh sách các hàm toán học mở rộng
809
+ const mathFunctions = ["cos", "sin", "tan", "cot", "arccos", "arcsin", "arctan", "arccot", "log", "ln", "lim", "sinh", "cosh", "tanh", "sec", "csc"];
810
+
811
+ if (mathFunctions.some(fn => it.toLowerCase().includes(fn))) {
812
+ // Tìm hàm khớp chính xác nhất
813
+ for (const fn of mathFunctions) {
814
+ if (it.toLowerCase() === fn) {
815
+ return "\\" + fn + " ";
816
+ }
817
+ }
818
+ }
803
819
  return escapeSpecialChars(it);
804
820
  }
805
821
 
@@ -812,7 +828,7 @@ function parseElementMs(node) {
812
828
 
813
829
  // Math Text
814
830
  function parseElementMtext(node) {
815
- let content = NodeTool.getNodeText(node)
831
+ let content = escapeSpecialChars(NodeTool.getNodeText(node))
816
832
  .replace(/\s*=\s*/g, " = ")
817
833
  .replace(/\s*\.\s*/g, " \\cdot ")
818
834
  .trim();
@@ -866,6 +882,14 @@ function parseElementMspace(node) {
866
882
  }
867
883
 
868
884
  function escapeSpecialChars(text) {
885
+ // Strip problematic Unicode characters:
886
+ // - Control characters: \u0000-\u0008, \u000B-\u000C, \u000E-\u001F, \u007F-\u009F
887
+ // - Zero-width & Invisible: \u200B-\u200F, \u202A-\u202E, \u2060-\u206F, \uFEFF
888
+ // - Variation Selectors: \uFE00-\uFE0F
889
+ // - Private Use Area (PUA): \uE000-\uF8FF
890
+ // - Non-characters: \uFDD0-\uFDEF
891
+ text = text.replace(/[\u0000-\u0008\u000B-\u000C\u000E-\u001F\u007F-\u009F\u200B-\u200F\u202A-\u202E\u2060-\u206F\uFE00-\uFE0F\uFEFF\uE000-\uF8FF\uFDD0-\uFDEF]/g, "");
892
+
869
893
  // Don't escape pi, Greek, or just a-z0-9 or Unicode Greek, or empty
870
894
  if (
871
895
  /^\\?[a-zA-Z0-9]+$/.test(text) ||
@@ -275,24 +275,24 @@ const MathSymbol = {
275
275
  bigCommand: {
276
276
  decimals: [8721, 8719, 8720, 10753, 10754, 10752, 8899, 8898, 10756, 10758, 8897, 8896, 8747, 8750, 8748, 8749, 10764, 8747],
277
277
  scripts: [
278
- "\\sum",
279
- "\\prod",
280
- "\\coprod",
281
- "\\bigoplus",
282
- "\\bigotimes",
283
- "\\bigodot",
284
- "\\bigcup",
285
- "\\bigcap",
286
- "\\biguplus",
287
- "\\bigsqcup",
288
- "\\bigvee",
289
- "\\bigwedge",
290
- "\\int",
291
- "\\oint",
292
- "\\iint",
293
- "\\iiint",
294
- "\\iiiint",
295
- "\\idotsint",
278
+ "\\sum ",
279
+ "\\prod ",
280
+ "\\coprod ",
281
+ "\\bigoplus ",
282
+ "\\bigotimes ",
283
+ "\\bigodot ",
284
+ "\\bigcup ",
285
+ "\\bigcap ",
286
+ "\\biguplus ",
287
+ "\\bigsqcup ",
288
+ "\\bigvee ",
289
+ "\\bigwedge ",
290
+ "\\int ",
291
+ "\\oint ",
292
+ "\\iint ",
293
+ "\\iiint ",
294
+ "\\iiiint ",
295
+ "\\idotsint ",
296
296
  ]
297
297
  },
298
298
 
@@ -610,9 +610,10 @@ function convert(mathmlHtml) {
610
610
  // Thêm xử lý cho các thẻ MathML khác
611
611
  result = result
612
612
  .replace(/∞/g, "\\infty") // Vô cực
613
- .replace(/∑/g, "\\sum") // Tổng
614
- .replace(/∏/g, "\\prod") // Tích
615
- .replace(/∫/g, "\\int"); // Tích phân
613
+ .replace(/∫/g, " \\int "); // Tích phân
614
+
615
+ // Đảm bảo dấu cách sau các lệnh LaTeX quan trọng để tránh dính biến (vd: \intf -> \int f)
616
+ result = result.replace(/\\(int|sum|prod|cos|sin|tan|cot|lim|log|ln)([a-zA-Z0-9])/g, "\\$1 $2");
616
617
 
617
618
  return result;
618
619
  }
@@ -771,7 +772,8 @@ function parseOperator(node) {
771
772
  "|": " \\mid ", // PATCH: set-builder mid
772
773
  π: " \\pi ", // PATCH: Greek letter
773
774
  };
774
- return operatorMap[it] || escapeSpecialChars(MathSymbol.parseOperator(it));
775
+ const res = operatorMap[it] || escapeSpecialChars(MathSymbol.parseOperator(it));
776
+ return res;
775
777
  }
776
778
  // --- END PATCH ---
777
779
 
@@ -798,6 +800,20 @@ function parseElementMi(node) {
798
800
  // Math Number
799
801
  function parseElementMn(node) {
800
802
  let it = NodeTool.getNodeText(node).trim();
803
+ // Loại bỏ các ký tự điều khiển hoặc khoảng trắng lạ
804
+ it = it.replace(/[\u0000-\u001F\u007F-\u009F\u00A0]/g, "");
805
+
806
+ // Danh sách các hàm toán học mở rộng
807
+ const mathFunctions = ["cos", "sin", "tan", "cot", "arccos", "arcsin", "arctan", "arccot", "log", "ln", "lim", "sinh", "cosh", "tanh", "sec", "csc"];
808
+
809
+ if (mathFunctions.some(fn => it.toLowerCase().includes(fn))) {
810
+ // Tìm hàm khớp chính xác nhất
811
+ for (const fn of mathFunctions) {
812
+ if (it.toLowerCase() === fn) {
813
+ return "\\" + fn + " ";
814
+ }
815
+ }
816
+ }
801
817
  return escapeSpecialChars(it);
802
818
  }
803
819
 
@@ -810,7 +826,7 @@ function parseElementMs(node) {
810
826
 
811
827
  // Math Text
812
828
  function parseElementMtext(node) {
813
- let content = NodeTool.getNodeText(node)
829
+ let content = escapeSpecialChars(NodeTool.getNodeText(node))
814
830
  .replace(/\s*=\s*/g, " = ")
815
831
  .replace(/\s*\.\s*/g, " \\cdot ")
816
832
  .trim();
@@ -864,6 +880,14 @@ function parseElementMspace(node) {
864
880
  }
865
881
 
866
882
  function escapeSpecialChars(text) {
883
+ // Strip problematic Unicode characters:
884
+ // - Control characters: \u0000-\u0008, \u000B-\u000C, \u000E-\u001F, \u007F-\u009F
885
+ // - Zero-width & Invisible: \u200B-\u200F, \u202A-\u202E, \u2060-\u206F, \uFEFF
886
+ // - Variation Selectors: \uFE00-\uFE0F
887
+ // - Private Use Area (PUA): \uE000-\uF8FF
888
+ // - Non-characters: \uFDD0-\uFDEF
889
+ text = text.replace(/[\u0000-\u0008\u000B-\u000C\u000E-\u001F\u007F-\u009F\u200B-\u200F\u202A-\u202E\u2060-\u206F\uFE00-\uFE0F\uFEFF\uE000-\uF8FF\uFDD0-\uFDEF]/g, "");
890
+
867
891
  // Don't escape pi, Greek, or just a-z0-9 or Unicode Greek, or empty
868
892
  if (
869
893
  /^\\?[a-zA-Z0-9]+$/.test(text) ||
@@ -281,24 +281,24 @@
281
281
  bigCommand: {
282
282
  decimals: [8721, 8719, 8720, 10753, 10754, 10752, 8899, 8898, 10756, 10758, 8897, 8896, 8747, 8750, 8748, 8749, 10764, 8747],
283
283
  scripts: [
284
- "\\sum",
285
- "\\prod",
286
- "\\coprod",
287
- "\\bigoplus",
288
- "\\bigotimes",
289
- "\\bigodot",
290
- "\\bigcup",
291
- "\\bigcap",
292
- "\\biguplus",
293
- "\\bigsqcup",
294
- "\\bigvee",
295
- "\\bigwedge",
296
- "\\int",
297
- "\\oint",
298
- "\\iint",
299
- "\\iiint",
300
- "\\iiiint",
301
- "\\idotsint",
284
+ "\\sum ",
285
+ "\\prod ",
286
+ "\\coprod ",
287
+ "\\bigoplus ",
288
+ "\\bigotimes ",
289
+ "\\bigodot ",
290
+ "\\bigcup ",
291
+ "\\bigcap ",
292
+ "\\biguplus ",
293
+ "\\bigsqcup ",
294
+ "\\bigvee ",
295
+ "\\bigwedge ",
296
+ "\\int ",
297
+ "\\oint ",
298
+ "\\iint ",
299
+ "\\iiint ",
300
+ "\\iiiint ",
301
+ "\\idotsint ",
302
302
  ]
303
303
  },
304
304
 
@@ -616,9 +616,10 @@
616
616
  // Thêm xử lý cho các thẻ MathML khác
617
617
  result = result
618
618
  .replace(/∞/g, "\\infty") // Vô cực
619
- .replace(/∑/g, "\\sum") // Tổng
620
- .replace(/∏/g, "\\prod") // Tích
621
- .replace(/∫/g, "\\int"); // Tích phân
619
+ .replace(/∫/g, " \\int "); // Tích phân
620
+
621
+ // Đảm bảo dấu cách sau các lệnh LaTeX quan trọng để tránh dính biến (vd: \intf -> \int f)
622
+ result = result.replace(/\\(int|sum|prod|cos|sin|tan|cot|lim|log|ln)([a-zA-Z0-9])/g, "\\$1 $2");
622
623
 
623
624
  return result;
624
625
  }
@@ -777,7 +778,8 @@
777
778
  "|": " \\mid ", // PATCH: set-builder mid
778
779
  π: " \\pi ", // PATCH: Greek letter
779
780
  };
780
- return operatorMap[it] || escapeSpecialChars(MathSymbol.parseOperator(it));
781
+ const res = operatorMap[it] || escapeSpecialChars(MathSymbol.parseOperator(it));
782
+ return res;
781
783
  }
782
784
  // --- END PATCH ---
783
785
 
@@ -804,6 +806,20 @@
804
806
  // Math Number
805
807
  function parseElementMn(node) {
806
808
  let it = NodeTool.getNodeText(node).trim();
809
+ // Loại bỏ các ký tự điều khiển hoặc khoảng trắng lạ
810
+ it = it.replace(/[\u0000-\u001F\u007F-\u009F\u00A0]/g, "");
811
+
812
+ // Danh sách các hàm toán học mở rộng
813
+ const mathFunctions = ["cos", "sin", "tan", "cot", "arccos", "arcsin", "arctan", "arccot", "log", "ln", "lim", "sinh", "cosh", "tanh", "sec", "csc"];
814
+
815
+ if (mathFunctions.some(fn => it.toLowerCase().includes(fn))) {
816
+ // Tìm hàm khớp chính xác nhất
817
+ for (const fn of mathFunctions) {
818
+ if (it.toLowerCase() === fn) {
819
+ return "\\" + fn + " ";
820
+ }
821
+ }
822
+ }
807
823
  return escapeSpecialChars(it);
808
824
  }
809
825
 
@@ -816,7 +832,7 @@
816
832
 
817
833
  // Math Text
818
834
  function parseElementMtext(node) {
819
- let content = NodeTool.getNodeText(node)
835
+ let content = escapeSpecialChars(NodeTool.getNodeText(node))
820
836
  .replace(/\s*=\s*/g, " = ")
821
837
  .replace(/\s*\.\s*/g, " \\cdot ")
822
838
  .trim();
@@ -870,6 +886,14 @@
870
886
  }
871
887
 
872
888
  function escapeSpecialChars(text) {
889
+ // Strip problematic Unicode characters:
890
+ // - Control characters: \u0000-\u0008, \u000B-\u000C, \u000E-\u001F, \u007F-\u009F
891
+ // - Zero-width & Invisible: \u200B-\u200F, \u202A-\u202E, \u2060-\u206F, \uFEFF
892
+ // - Variation Selectors: \uFE00-\uFE0F
893
+ // - Private Use Area (PUA): \uE000-\uF8FF
894
+ // - Non-characters: \uFDD0-\uFDEF
895
+ text = text.replace(/[\u0000-\u0008\u000B-\u000C\u000E-\u001F\u007F-\u009F\u200B-\u200F\u202A-\u202E\u2060-\u206F\uFE00-\uFE0F\uFEFF\uE000-\uF8FF\uFDD0-\uFDEF]/g, "");
896
+
873
897
  // Don't escape pi, Greek, or just a-z0-9 or Unicode Greek, or empty
874
898
  if (
875
899
  /^\\?[a-zA-Z0-9]+$/.test(text) ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ed-mathml2tex",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Convert mathml to latex.",
5
5
  "author": "Mika",
6
6
  "license": "MIT",