tex2typst 0.3.17 → 0.3.18

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # tex2typst.js
2
2
 
3
- JavaScript library for conversion between TeX/LaTeX and Typst math formula code.
3
+ JavaScript library for conversion between TeX/LaTeX and Typst math code.
4
4
 
5
5
  Despite the name `tex2typst` due to the initial goal of converting TeX to Typst, the library can also convert Typst to TeX since version 0.3.0.
6
6
 
@@ -44,7 +44,7 @@ console.log(tex_recovered);
44
44
 
45
45
  If you are using the library in a web page via a `<script>` tag, you don't need the line of `import`, function `tex2typst` and `typst2tex` should be available in the global scope.
46
46
 
47
- tex2typst.js supports some advanced options to customize the conversion. For more details, please refer to the [API Reference](docs/api-reference.md).
47
+ tex2typst.js supports some advanced options to customize the conversion. For details, please refer to the [API Reference](docs/api-reference.md).
48
48
 
49
49
  ## Open-source license
50
50
 
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  // src/map.ts
2
2
  var symbolMap = /* @__PURE__ */ new Map([
3
3
  ["displaystyle", "display"],
4
+ ["|", "bar.v.double"],
5
+ ["!", "#h(-math.thin.amount)"],
4
6
  [",", "thin"],
5
7
  [":", "med"],
6
8
  [";", "thick"],
@@ -1185,6 +1187,12 @@ var TexNode = class {
1185
1187
  case "ordgroup": {
1186
1188
  return this.args.map((n) => n.serialize()).flat();
1187
1189
  }
1190
+ case "leftright": {
1191
+ let tokens = this.args.map((n) => n.serialize()).flat();
1192
+ tokens.splice(0, 0, new TexToken(1 /* COMMAND */, "\\left"));
1193
+ tokens.splice(tokens.length - 1, 0, new TexToken(1 /* COMMAND */, "\\right"));
1194
+ return tokens;
1195
+ }
1188
1196
  case "unaryFunc": {
1189
1197
  let tokens = [];
1190
1198
  tokens.push(new TexToken(1 /* COMMAND */, this.content));
@@ -1666,7 +1674,8 @@ var BINARY_COMMANDS = [
1666
1674
  "dbinom",
1667
1675
  "dfrac",
1668
1676
  "tbinom",
1669
- "overset"
1677
+ "overset",
1678
+ "underset"
1670
1679
  ];
1671
1680
  var IGNORED_COMMANDS = [
1672
1681
  "bigl",
@@ -1701,7 +1710,7 @@ function eat_whitespaces(tokens, start) {
1701
1710
  }
1702
1711
  function eat_parenthesis(tokens, start) {
1703
1712
  const firstToken = tokens[start];
1704
- if (firstToken.type === 0 /* ELEMENT */ && ["(", ")", "[", "]", "|", "\\{", "\\}", "."].includes(firstToken.value)) {
1713
+ if (firstToken.type === 0 /* ELEMENT */ && ["(", ")", "[", "]", "|", "\\{", "\\}", ".", "\\|"].includes(firstToken.value)) {
1705
1714
  return firstToken;
1706
1715
  } else if (firstToken.type === 1 /* COMMAND */ && ["lfloor", "rfloor", "lceil", "rceil", "langle", "rangle"].includes(firstToken.value.slice(1))) {
1707
1716
  return firstToken;
@@ -1767,7 +1776,7 @@ var rules_map = /* @__PURE__ */ new Map([
1767
1776
  ],
1768
1777
  [String.raw`%[^\n]*`, (s) => new TexToken(3 /* COMMENT */, s.text().substring(1))],
1769
1778
  [String.raw`[{}_^&]`, (s) => new TexToken(6 /* CONTROL */, s.text())],
1770
- [String.raw`\\[\\,:; ]`, (s) => new TexToken(6 /* CONTROL */, s.text())],
1779
+ [String.raw`\\[\\,:;! ]`, (s) => new TexToken(6 /* CONTROL */, s.text())],
1771
1780
  [String.raw`\r?\n`, (_s) => new TexToken(5 /* NEWLINE */, "\n")],
1772
1781
  [String.raw`\s+`, (s) => new TexToken(4 /* SPACE */, s.text())],
1773
1782
  [String.raw`\\[{}%$&#_|]`, (s) => new TexToken(0 /* ELEMENT */, s.text())],
@@ -1973,6 +1982,7 @@ var LatexParser = class {
1973
1982
  case "}":
1974
1983
  throw new LatexParserError("Unmatched '}'");
1975
1984
  case "\\\\":
1985
+ case "\\!":
1976
1986
  case "\\,":
1977
1987
  case "\\:":
1978
1988
  case "\\;":
@@ -2285,14 +2295,15 @@ var TypstWriterError = class extends Error {
2285
2295
  }
2286
2296
  };
2287
2297
  var TypstWriter = class {
2288
- constructor(opt) {
2298
+ constructor(options) {
2289
2299
  this.buffer = "";
2290
2300
  this.queue = [];
2291
2301
  this.insideFunctionDepth = 0;
2292
- this.nonStrict = opt.nonStrict;
2293
- this.preferShorthands = opt.preferShorthands;
2294
- this.keepSpaces = opt.keepSpaces;
2295
- this.inftyToOo = opt.inftyToOo;
2302
+ this.nonStrict = options.nonStrict;
2303
+ this.preferShorthands = options.preferShorthands;
2304
+ this.keepSpaces = options.keepSpaces;
2305
+ this.inftyToOo = options.inftyToOo;
2306
+ this.optimize = options.optimize;
2296
2307
  }
2297
2308
  writeBuffer(token) {
2298
2309
  const str = token.toString();
@@ -2559,9 +2570,11 @@ var TypstWriter = class {
2559
2570
  res = res.replace(/round\(\)/g, 'round("")');
2560
2571
  return res;
2561
2572
  };
2562
- const all_passes = [smartFloorPass, smartCeilPass, smartRoundPass];
2563
- for (const pass of all_passes) {
2564
- this.buffer = pass(this.buffer);
2573
+ if (this.optimize) {
2574
+ const all_passes = [smartFloorPass, smartCeilPass, smartRoundPass];
2575
+ for (const pass of all_passes) {
2576
+ this.buffer = pass(this.buffer);
2577
+ }
2565
2578
  }
2566
2579
  return this.buffer;
2567
2580
  }
@@ -2583,8 +2596,6 @@ function tex_token_to_typst(token) {
2583
2596
  return token;
2584
2597
  } else if (token === "/") {
2585
2598
  return "\\/";
2586
- } else if (token === "\\|") {
2587
- return "parallel";
2588
2599
  } else if (token === "\\\\") {
2589
2600
  return "\\";
2590
2601
  } else if (["\\$", "\\#", "\\&", "\\_"].includes(token)) {
@@ -2601,39 +2612,48 @@ function tex_token_to_typst(token) {
2601
2612
  }
2602
2613
  function convert_overset(node, options) {
2603
2614
  const [sup, base] = node.args;
2604
- const is_def = (n) => {
2605
- if (n.eq(new TexNode("text", "def"))) {
2606
- return true;
2607
- }
2608
- if (n.type === "ordgroup" && n.args.length === 3) {
2609
- const [a1, a2, a3] = n.args;
2610
- const d = new TexNode("element", "d");
2611
- const e = new TexNode("element", "e");
2612
- const f = new TexNode("element", "f");
2613
- if (a1.eq(d) && a2.eq(e) && a3.eq(f)) {
2615
+ if (options.optimize) {
2616
+ const is_def = (n) => {
2617
+ if (n.eq(new TexNode("text", "def"))) {
2614
2618
  return true;
2615
2619
  }
2620
+ if (n.type === "ordgroup" && n.args.length === 3) {
2621
+ const [a1, a2, a3] = n.args;
2622
+ const d = new TexNode("element", "d");
2623
+ const e = new TexNode("element", "e");
2624
+ const f = new TexNode("element", "f");
2625
+ if (a1.eq(d) && a2.eq(e) && a3.eq(f)) {
2626
+ return true;
2627
+ }
2628
+ }
2629
+ return false;
2630
+ };
2631
+ const is_eq = (n) => n.eq(new TexNode("element", "="));
2632
+ if (is_def(sup) && is_eq(base)) {
2633
+ return new TypstNode("symbol", "eq.def");
2616
2634
  }
2617
- return false;
2618
- };
2619
- const is_eq = (n) => n.eq(new TexNode("element", "="));
2620
- if (is_def(sup) && is_eq(base)) {
2621
- return new TypstNode("symbol", "eq.def");
2622
2635
  }
2623
2636
  const limits_call = new TypstNode(
2624
2637
  "funcCall",
2625
2638
  "limits",
2626
2639
  [convert_tex_node_to_typst(base, options)]
2627
2640
  );
2628
- return new TypstNode(
2629
- "supsub",
2630
- "",
2631
- [],
2632
- {
2633
- base: limits_call,
2634
- sup: convert_tex_node_to_typst(sup, options)
2635
- }
2641
+ return new TypstNode("supsub", "", [], {
2642
+ base: limits_call,
2643
+ sup: convert_tex_node_to_typst(sup, options)
2644
+ });
2645
+ }
2646
+ function convert_underset(node, options) {
2647
+ const [sub, base] = node.args;
2648
+ const limits_call = new TypstNode(
2649
+ "funcCall",
2650
+ "limits",
2651
+ [convert_tex_node_to_typst(base, options)]
2636
2652
  );
2653
+ return new TypstNode("supsub", "", [], {
2654
+ base: limits_call,
2655
+ sub: convert_tex_node_to_typst(sub, options)
2656
+ });
2637
2657
  }
2638
2658
  function convert_tex_node_to_typst(node, options = {}) {
2639
2659
  switch (node.type) {
@@ -2693,39 +2713,51 @@ function convert_tex_node_to_typst(node, options = {}) {
2693
2713
  return new TypstNode("supsub", "", [], data);
2694
2714
  }
2695
2715
  case "leftright": {
2696
- const [left, body, right] = node.args;
2716
+ const [left, _body, right] = node.args;
2717
+ const [typ_left, typ_body, typ_right] = node.args.map((n) => convert_tex_node_to_typst(n, options));
2718
+ if (options.optimize) {
2719
+ if (left.content === "\\|" && right.content === "\\|") {
2720
+ return new TypstNode("funcCall", "norm", [typ_body]);
2721
+ }
2722
+ if ([
2723
+ "[]",
2724
+ "()",
2725
+ "\\{\\}",
2726
+ "\\lfloor\\rfloor",
2727
+ "\\lceil\\rceil",
2728
+ "\\lfloor\\rceil"
2729
+ ].includes(left.content + right.content)) {
2730
+ return new TypstNode("group", "", [typ_left, typ_body, typ_right]);
2731
+ }
2732
+ }
2697
2733
  const group = new TypstNode(
2698
2734
  "group",
2699
2735
  "",
2700
- node.args.map((n) => convert_tex_node_to_typst(n, options))
2736
+ [typ_left, typ_body, typ_right]
2701
2737
  );
2702
- if ([
2703
- "[]",
2704
- "()",
2705
- "\\{\\}",
2706
- "\\lfloor\\rfloor",
2707
- "\\lceil\\rceil",
2708
- "\\lfloor\\rceil"
2709
- ].includes(left.content + right.content)) {
2710
- return group;
2711
- }
2738
+ const escape_curly_or_paren = function(s) {
2739
+ if (["(", ")", "{", "["].includes(s)) {
2740
+ return "\\" + s;
2741
+ } else {
2742
+ return s;
2743
+ }
2744
+ };
2712
2745
  if (right.content === ".") {
2713
- group.args.pop();
2714
- return group;
2746
+ typ_left.content = escape_curly_or_paren(typ_left.content);
2747
+ group.args = [typ_left, typ_body];
2715
2748
  } else if (left.content === ".") {
2716
- group.args.shift();
2717
- return new TypstNode("funcCall", "lr", [group]);
2749
+ typ_right.content = escape_curly_or_paren(typ_right.content);
2750
+ group.args = [typ_body, typ_right];
2718
2751
  }
2719
- return new TypstNode(
2720
- "funcCall",
2721
- "lr",
2722
- [group]
2723
- );
2752
+ return new TypstNode("funcCall", "lr", [group]);
2724
2753
  }
2725
2754
  case "binaryFunc": {
2726
2755
  if (node.content === "\\overset") {
2727
2756
  return convert_overset(node, options);
2728
2757
  }
2758
+ if (node.content === "\\underset") {
2759
+ return convert_underset(node, options);
2760
+ }
2729
2761
  if (node.content === "\\frac") {
2730
2762
  if (options.fracToSlash) {
2731
2763
  return new TypstNode(
@@ -2922,7 +2954,9 @@ var TYPST_UNARY_FUNCTIONS = [
2922
2954
  "cal",
2923
2955
  "frak",
2924
2956
  "floor",
2925
- "ceil"
2957
+ "ceil",
2958
+ "norm",
2959
+ "limits"
2926
2960
  ];
2927
2961
  var TYPST_BINARY_FUNCTIONS = [
2928
2962
  "frac",
@@ -2939,8 +2973,6 @@ function apply_escape_if_needed2(c) {
2939
2973
  function typst_token_to_tex(token) {
2940
2974
  if (/^[a-zA-Z0-9]$/.test(token)) {
2941
2975
  return token;
2942
- } else if (token === "thin") {
2943
- return "\\,";
2944
2976
  } else if (reverseSymbolMap.has(token)) {
2945
2977
  return "\\" + reverseSymbolMap.get(token);
2946
2978
  }
@@ -3005,15 +3037,21 @@ function convert_typst_node_to_tex(node) {
3005
3037
  return new TexNode("ordgroup", "", node.args.map(convert_typst_node_to_tex));
3006
3038
  }
3007
3039
  }
3040
+ if (node.content === "norm") {
3041
+ const arg0 = node.args[0];
3042
+ const tex_node_type = node.isOverHigh() ? "leftright" : "ordgroup";
3043
+ return new TexNode(tex_node_type, "", [
3044
+ new TexNode("symbol", "\\|"),
3045
+ convert_typst_node_to_tex(arg0),
3046
+ new TexNode("symbol", "\\|")
3047
+ ]);
3048
+ }
3008
3049
  if (node.content === "floor" || node.content === "ceil") {
3009
- let left = "\\l" + node.content;
3010
- let right = "\\r" + node.content;
3050
+ const left = "\\l" + node.content;
3051
+ const right = "\\r" + node.content;
3011
3052
  const arg0 = node.args[0];
3012
- if (arg0.isOverHigh()) {
3013
- left = "\\left" + left;
3014
- right = "\\right" + right;
3015
- }
3016
- return new TexNode("ordgroup", "", [
3053
+ const tex_node_type = node.isOverHigh() ? "leftright" : "ordgroup";
3054
+ return new TexNode(tex_node_type, "", [
3017
3055
  new TexNode("symbol", left),
3018
3056
  convert_typst_node_to_tex(arg0),
3019
3057
  new TexNode("symbol", right)
@@ -3051,7 +3089,6 @@ function convert_typst_node_to_tex(node) {
3051
3089
  }
3052
3090
  case "supsub": {
3053
3091
  const { base, sup, sub } = node.data;
3054
- const base_tex = convert_typst_node_to_tex(base);
3055
3092
  let sup_tex;
3056
3093
  let sub_tex;
3057
3094
  if (sup) {
@@ -3060,6 +3097,18 @@ function convert_typst_node_to_tex(node) {
3060
3097
  if (sub) {
3061
3098
  sub_tex = convert_typst_node_to_tex(sub);
3062
3099
  }
3100
+ if (base.eq(new TypstNode("funcCall", "limits"))) {
3101
+ const body_in_limits = convert_typst_node_to_tex(base.args[0]);
3102
+ if (sup_tex !== void 0 && sub_tex === void 0) {
3103
+ return new TexNode("binaryFunc", "\\overset", [sup_tex, body_in_limits]);
3104
+ } else if (sup_tex === void 0 && sub_tex !== void 0) {
3105
+ return new TexNode("binaryFunc", "\\underset", [sub_tex, body_in_limits]);
3106
+ } else {
3107
+ const underset_call = new TexNode("binaryFunc", "\\underset", [sub_tex, body_in_limits]);
3108
+ return new TexNode("binaryFunc", "\\overset", [sup_tex, underset_call]);
3109
+ }
3110
+ }
3111
+ const base_tex = convert_typst_node_to_tex(base);
3063
3112
  const res = new TexNode("supsub", "", [], {
3064
3113
  base: base_tex,
3065
3114
  sup: sup_tex,
@@ -3689,6 +3738,7 @@ function tex2typst(tex, options) {
3689
3738
  keepSpaces: false,
3690
3739
  fracToSlash: true,
3691
3740
  inftyToOo: false,
3741
+ optimize: true,
3692
3742
  nonAsciiWrapper: "",
3693
3743
  customTexMacros: {}
3694
3744
  };