tex2typst 0.3.1 → 0.3.2
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 +5 -3
- package/dist/index.js +115 -73
- package/dist/tex2typst.min.js +15 -15
- package/dist/types.d.ts +3 -1
- package/dist/typst-shorthands.d.ts +3 -0
- package/dist/typst-writer.d.ts +9 -3
- package/docs/api-reference.md +64 -0
- package/package.json +1 -1
- package/src/convert.ts +15 -18
- package/src/index.ts +11 -14
- package/src/map.ts +13 -36
- package/src/types.ts +3 -1
- package/src/typst-parser.ts +22 -2
- package/src/typst-shorthands.ts +51 -0
- package/src/typst-writer.ts +29 -22
- package/tools/make-shorthand-map.py +33 -0
- package/tools/make-symbol-map.py +4 -3
package/README.md
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
# tex2typst
|
|
1
|
+
# tex2typst.js
|
|
2
|
+
|
|
2
3
|
JavaScript library for conversion between TeX/LaTeX and Typst math formula code.
|
|
3
4
|
|
|
4
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.
|
|
@@ -28,14 +29,13 @@ Replace `0.3.0` with the latest version number in case this README is outdated.
|
|
|
28
29
|
|
|
29
30
|
## Usage
|
|
30
31
|
|
|
31
|
-
|
|
32
32
|
```javascript
|
|
33
33
|
import { tex2typst, typst2tex } from 'tex2typst';
|
|
34
34
|
|
|
35
35
|
let tex = "e \overset{\text{def}}{=} \lim_{{n \to \infty}} \left(1 + \frac{1}{n}\right)^n";
|
|
36
36
|
let typst = tex2typst(tex);
|
|
37
37
|
console.log(typst);
|
|
38
|
-
// e eq.def lim_(n arrow.r infinity)(1 +
|
|
38
|
+
// e eq.def lim_(n arrow.r infinity)(1 + 1/n)^n
|
|
39
39
|
|
|
40
40
|
let tex_recovered = typst2tex(typst);
|
|
41
41
|
console.log(tex_recovered);
|
|
@@ -44,6 +44,8 @@ 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).
|
|
48
|
+
|
|
47
49
|
## Open-source license
|
|
48
50
|
|
|
49
51
|
GPL v3. See [LICENSE](LICENSE) for details.
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
// src/map.ts
|
|
2
2
|
var symbolMap = /* @__PURE__ */ new Map([
|
|
3
|
+
["cos", "cos"],
|
|
4
|
+
["sin", "sin"],
|
|
5
|
+
["tan", "tan"],
|
|
6
|
+
["cot", "cot"],
|
|
7
|
+
["sec", "sec"],
|
|
8
|
+
["csc", "csc"],
|
|
9
|
+
["mod", "mod"],
|
|
10
|
+
["omicron", "omicron"],
|
|
11
|
+
["Xi", "Xi"],
|
|
12
|
+
["Upsilon", "Upsilon"],
|
|
13
|
+
["lim", "lim"],
|
|
3
14
|
["nonumber", ""],
|
|
4
15
|
["vec", "arrow"],
|
|
5
16
|
["neq", "eq.not"],
|
|
@@ -59,44 +70,16 @@ var symbolMap = /* @__PURE__ */ new Map([
|
|
|
59
70
|
["propto", "prop"],
|
|
60
71
|
/* arrows */
|
|
61
72
|
["gets", "arrow.l"],
|
|
62
|
-
["hookleftarrow", "arrow.l.hook"],
|
|
63
|
-
["leftharpoonup", "harpoon.lt"],
|
|
64
|
-
["leftharpoondown", "harpoon.lb"],
|
|
65
|
-
["rightleftharpoons", "harpoons.rtlb"],
|
|
66
|
-
["longleftarrow", "arrow.l.long"],
|
|
67
|
-
["longrightarrow", "arrow.r.long"],
|
|
68
|
-
["longleftrightarrow", "arrow.l.r.long"],
|
|
69
|
-
["Longleftarrow", "arrow.l.double.long"],
|
|
70
|
-
["Longrightarrow", "arrow.r.double.long"],
|
|
71
|
-
["Longleftrightarrow", "arrow.l.r.double.long"],
|
|
72
73
|
// ['longmapsto', 'arrow.r.bar'],
|
|
73
|
-
["hookrightarrow", "arrow.r.hook"],
|
|
74
|
-
["rightharpoonup", "harpoon.rt"],
|
|
75
|
-
["rightharpoondown", "harpoon.rb"],
|
|
76
74
|
["iff", "arrow.l.r.double.long"],
|
|
77
75
|
["implies", "arrow.r.double.long"],
|
|
78
|
-
["uparrow", "arrow.t"],
|
|
79
|
-
["downarrow", "arrow.b"],
|
|
80
|
-
["updownarrow", "arrow.t.b"],
|
|
81
|
-
["Uparrow", "arrow.t.double"],
|
|
82
|
-
["Downarrow", "arrow.b.double"],
|
|
83
|
-
["Updownarrow", "arrow.t.b.double"],
|
|
84
|
-
["nearrow", "arrow.tr"],
|
|
85
|
-
["searrow", "arrow.br"],
|
|
86
|
-
["swarrow", "arrow.bl"],
|
|
87
|
-
["nwarrow", "arrow.tl"],
|
|
88
76
|
["leadsto", "arrow.squiggly"],
|
|
89
|
-
["leftleftarrows", "arrows.ll"],
|
|
90
|
-
["rightrightarrows", "arrows.rr"],
|
|
91
77
|
["Cap", "sect.double"],
|
|
92
78
|
["Cup", "union.double"],
|
|
93
79
|
["Delta", "Delta"],
|
|
94
80
|
["Gamma", "Gamma"],
|
|
95
81
|
["Join", "join"],
|
|
96
82
|
["Lambda", "Lambda"],
|
|
97
|
-
["Leftarrow", "arrow.l.double"],
|
|
98
|
-
["Leftrightarrow", "arrow.l.r.double"],
|
|
99
|
-
["Longrightarrow", "arrow.r.double.long"],
|
|
100
83
|
["Omega", "Omega"],
|
|
101
84
|
["P", "pilcrow"],
|
|
102
85
|
["Phi", "Phi"],
|
|
@@ -152,7 +135,6 @@ var symbolMap = /* @__PURE__ */ new Map([
|
|
|
152
135
|
["div", "div"],
|
|
153
136
|
["divideontimes", "times.div"],
|
|
154
137
|
["dotplus", "plus.dot"],
|
|
155
|
-
["downarrow", "arrow.b"],
|
|
156
138
|
["ell", "ell"],
|
|
157
139
|
["emptyset", "nothing"],
|
|
158
140
|
["epsilon", "epsilon.alt"],
|
|
@@ -186,8 +168,6 @@ var symbolMap = /* @__PURE__ */ new Map([
|
|
|
186
168
|
["lbrack", "bracket.l"],
|
|
187
169
|
["ldots", "dots.h"],
|
|
188
170
|
["le", "lt.eq"],
|
|
189
|
-
["leadsto", "arrow.squiggly"],
|
|
190
|
-
["leftarrow", "arrow.l"],
|
|
191
171
|
["leftthreetimes", "times.three.l"],
|
|
192
172
|
["leftrightarrow", "arrow.l.r"],
|
|
193
173
|
["leq", "lt.eq"],
|
|
@@ -224,7 +204,6 @@ var symbolMap = /* @__PURE__ */ new Map([
|
|
|
224
204
|
["nu", "nu"],
|
|
225
205
|
["ntriangleleft", "lt.tri.not"],
|
|
226
206
|
["ntriangleright", "gt.tri.not"],
|
|
227
|
-
["nwarrow", "arrow.tl"],
|
|
228
207
|
["odot", "dot.circle"],
|
|
229
208
|
["oint", "integral.cont"],
|
|
230
209
|
["oiint", "integral.surf"],
|
|
@@ -277,7 +256,6 @@ var symbolMap = /* @__PURE__ */ new Map([
|
|
|
277
256
|
["supset", "supset"],
|
|
278
257
|
["supseteq", "supset.eq"],
|
|
279
258
|
["supsetneq", "supset.neq"],
|
|
280
|
-
["swarrow", "arrow.bl"],
|
|
281
259
|
["tau", "tau"],
|
|
282
260
|
["theta", "theta"],
|
|
283
261
|
["times", "times"],
|
|
@@ -288,8 +266,6 @@ var symbolMap = /* @__PURE__ */ new Map([
|
|
|
288
266
|
// ['triangleleft', 'triangle.l.small'],
|
|
289
267
|
// ['triangleright', 'triangle.r.small'],
|
|
290
268
|
["twoheadrightarrow", "arrow.r.twohead"],
|
|
291
|
-
["uparrow", "arrow.t"],
|
|
292
|
-
["updownarrow", "arrow.t.b"],
|
|
293
269
|
["upharpoonright", "harpoon.tr"],
|
|
294
270
|
["uplus", "union.plus"],
|
|
295
271
|
["upsilon", "upsilon"],
|
|
@@ -1081,6 +1057,7 @@ for (const [key, value] of Array.from(symbolMap.entries()).reverse()) {
|
|
|
1081
1057
|
reverseSymbolMap.set(value, key);
|
|
1082
1058
|
}
|
|
1083
1059
|
reverseSymbolMap.set("dif", "mathrm{d}");
|
|
1060
|
+
reverseSymbolMap.set("oo", "infty");
|
|
1084
1061
|
var typst_to_tex_map = /* @__PURE__ */ new Map([
|
|
1085
1062
|
["top", "top"],
|
|
1086
1063
|
["frac", "frac"],
|
|
@@ -1977,17 +1954,55 @@ function parseTex(tex, customTexMacros) {
|
|
|
1977
1954
|
return parser.parse(tokens);
|
|
1978
1955
|
}
|
|
1979
1956
|
|
|
1957
|
+
// src/typst-shorthands.ts
|
|
1958
|
+
var shorthandMap = /* @__PURE__ */ new Map([
|
|
1959
|
+
["arrow.l.r.double.long", "<==>"],
|
|
1960
|
+
["arrow.l.r.long", "<-->"],
|
|
1961
|
+
["arrow.r.bar", "|->"],
|
|
1962
|
+
["arrow.r.double.bar", "|=>"],
|
|
1963
|
+
["arrow.r.double.long", "==>"],
|
|
1964
|
+
["arrow.r.long", "-->"],
|
|
1965
|
+
["arrow.r.long.squiggly", "~~>"],
|
|
1966
|
+
["arrow.r.tail", ">->"],
|
|
1967
|
+
["arrow.r.twohead", "->>"],
|
|
1968
|
+
["arrow.l.double.long", "<=="],
|
|
1969
|
+
["arrow.l.long", "<--"],
|
|
1970
|
+
["arrow.l.long.squiggly", "<~~"],
|
|
1971
|
+
["arrow.l.tail", "<-<"],
|
|
1972
|
+
["arrow.l.twohead", "<<-"],
|
|
1973
|
+
["arrow.l.r", "<->"],
|
|
1974
|
+
["arrow.l.r.double", "<=>"],
|
|
1975
|
+
["colon.double.eq", "::="],
|
|
1976
|
+
["dots.h", "..."],
|
|
1977
|
+
["gt.triple", ">>>"],
|
|
1978
|
+
["lt.triple", "<<<"],
|
|
1979
|
+
["arrow.r", "->"],
|
|
1980
|
+
["arrow.r.double", "=>"],
|
|
1981
|
+
["arrow.r.squiggly", "~>"],
|
|
1982
|
+
["arrow.l", "<-"],
|
|
1983
|
+
["arrow.l.squiggly", "<~"],
|
|
1984
|
+
["bar.v.double", "||"],
|
|
1985
|
+
["bracket.l.double", "[|"],
|
|
1986
|
+
["bracket.r.double", "|]"],
|
|
1987
|
+
["colon.eq", ":="],
|
|
1988
|
+
["eq.colon", "=:"],
|
|
1989
|
+
["eq.not", "!="],
|
|
1990
|
+
["gt.double", ">>"],
|
|
1991
|
+
["gt.eq", ">="],
|
|
1992
|
+
["lt.double", "<<"],
|
|
1993
|
+
["lt.eq", "<="],
|
|
1994
|
+
["ast.op", "*"],
|
|
1995
|
+
["minus", "-"],
|
|
1996
|
+
["tilde.op", "~"]
|
|
1997
|
+
]);
|
|
1998
|
+
var reverseShorthandMap = /* @__PURE__ */ new Map();
|
|
1999
|
+
for (const [key, value] of shorthandMap.entries()) {
|
|
2000
|
+
if (value.length > 1) {
|
|
2001
|
+
reverseShorthandMap.set(value, key);
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
|
|
1980
2005
|
// src/typst-writer.ts
|
|
1981
|
-
var TYPST_INTRINSIC_SYMBOLS = [
|
|
1982
|
-
"dim",
|
|
1983
|
-
"id",
|
|
1984
|
-
"im",
|
|
1985
|
-
"mod",
|
|
1986
|
-
"Pr",
|
|
1987
|
-
"sech",
|
|
1988
|
-
"csch"
|
|
1989
|
-
// 'sgn
|
|
1990
|
-
];
|
|
1991
2006
|
function is_delimiter(c) {
|
|
1992
2007
|
return c.type === "atom" && ["(", ")", "[", "]", "{", "}", "|", "\u230A", "\u230B", "\u2308", "\u2309"].includes(c.content);
|
|
1993
2008
|
}
|
|
@@ -2006,6 +2021,8 @@ function typst_primitive_to_string(value) {
|
|
|
2006
2021
|
default:
|
|
2007
2022
|
if (value === null) {
|
|
2008
2023
|
return "#none";
|
|
2024
|
+
} else if (value instanceof TypstToken) {
|
|
2025
|
+
return value.toString();
|
|
2009
2026
|
}
|
|
2010
2027
|
throw new TypstWriterError(`Invalid primitive value: ${value}`, value);
|
|
2011
2028
|
}
|
|
@@ -2018,13 +2035,14 @@ var TypstWriterError = class extends Error {
|
|
|
2018
2035
|
}
|
|
2019
2036
|
};
|
|
2020
2037
|
var TypstWriter = class {
|
|
2021
|
-
constructor(
|
|
2038
|
+
constructor(opt) {
|
|
2022
2039
|
this.buffer = "";
|
|
2023
2040
|
this.queue = [];
|
|
2024
2041
|
this.insideFunctionDepth = 0;
|
|
2025
|
-
this.nonStrict = nonStrict;
|
|
2026
|
-
this.
|
|
2027
|
-
this.keepSpaces = keepSpaces;
|
|
2042
|
+
this.nonStrict = opt.nonStrict;
|
|
2043
|
+
this.preferShorthands = opt.preferShorthands;
|
|
2044
|
+
this.keepSpaces = opt.keepSpaces;
|
|
2045
|
+
this.inftyToOo = opt.inftyToOo;
|
|
2028
2046
|
}
|
|
2029
2047
|
writeBuffer(token) {
|
|
2030
2048
|
const str = token.toString();
|
|
@@ -2063,9 +2081,19 @@ var TypstWriter = class {
|
|
|
2063
2081
|
}
|
|
2064
2082
|
break;
|
|
2065
2083
|
}
|
|
2066
|
-
case "symbol":
|
|
2067
|
-
|
|
2084
|
+
case "symbol": {
|
|
2085
|
+
let content = node.content;
|
|
2086
|
+
if (this.preferShorthands) {
|
|
2087
|
+
if (shorthandMap.has(content)) {
|
|
2088
|
+
content = shorthandMap.get(content);
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
if (this.inftyToOo && content === "infinity") {
|
|
2092
|
+
content = "oo";
|
|
2093
|
+
}
|
|
2094
|
+
this.queue.push(new TypstToken(0 /* SYMBOL */, content));
|
|
2068
2095
|
break;
|
|
2096
|
+
}
|
|
2069
2097
|
case "text":
|
|
2070
2098
|
this.queue.push(new TypstToken(2 /* TEXT */, node.content));
|
|
2071
2099
|
break;
|
|
@@ -2267,6 +2295,16 @@ var TypstWriter = class {
|
|
|
2267
2295
|
};
|
|
2268
2296
|
|
|
2269
2297
|
// src/convert.ts
|
|
2298
|
+
var TYPST_INTRINSIC_SYMBOLS = [
|
|
2299
|
+
"dim",
|
|
2300
|
+
"id",
|
|
2301
|
+
"im",
|
|
2302
|
+
"mod",
|
|
2303
|
+
"Pr",
|
|
2304
|
+
"sech",
|
|
2305
|
+
"csch"
|
|
2306
|
+
// 'sgn
|
|
2307
|
+
];
|
|
2270
2308
|
function tex_token_to_typst(token) {
|
|
2271
2309
|
if (/^[a-zA-Z0-9]$/.test(token)) {
|
|
2272
2310
|
return token;
|
|
@@ -2495,14 +2533,8 @@ function convert_tex_node_to_typst(node, options = {}) {
|
|
|
2495
2533
|
delim = "|";
|
|
2496
2534
|
break;
|
|
2497
2535
|
case "Vmatrix": {
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
const group = new TypstNode("group", "", [
|
|
2501
|
-
new TypstNode("symbol", "||"),
|
|
2502
|
-
matrix2,
|
|
2503
|
-
new TypstNode("symbol", "||")
|
|
2504
|
-
]);
|
|
2505
|
-
return new TypstNode("funcCall", "lr", [group]);
|
|
2536
|
+
delim = new TypstToken(0 /* SYMBOL */, "bar.v.double");
|
|
2537
|
+
break;
|
|
2506
2538
|
}
|
|
2507
2539
|
default:
|
|
2508
2540
|
throw new TypstWriterError(`Unimplemented beginend: ${node.content}`, node);
|
|
@@ -2731,6 +2763,8 @@ function convert_typst_node_to_tex(node) {
|
|
|
2731
2763
|
}
|
|
2732
2764
|
|
|
2733
2765
|
// src/typst-parser.ts
|
|
2766
|
+
var TYPST_EMPTY_NODE = new TypstNode("empty", "");
|
|
2767
|
+
var TYPST_SHORTHANDS = Array.from(reverseShorthandMap.keys());
|
|
2734
2768
|
function eat_primes2(tokens, start) {
|
|
2735
2769
|
let pos = start;
|
|
2736
2770
|
while (pos < tokens.length && tokens[pos].eq(new TypstToken(1 /* ELEMENT */, "'"))) {
|
|
@@ -2745,7 +2779,14 @@ function eat_identifier_name(typst, start) {
|
|
|
2745
2779
|
}
|
|
2746
2780
|
return typst.substring(start, pos);
|
|
2747
2781
|
}
|
|
2748
|
-
|
|
2782
|
+
function try_eat_shorthand(typst, start) {
|
|
2783
|
+
for (const shorthand of TYPST_SHORTHANDS) {
|
|
2784
|
+
if (typst.startsWith(shorthand, start)) {
|
|
2785
|
+
return shorthand;
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
return null;
|
|
2789
|
+
}
|
|
2749
2790
|
function tokenize_typst(typst) {
|
|
2750
2791
|
const tokens = [];
|
|
2751
2792
|
let pos = 0;
|
|
@@ -2831,6 +2872,12 @@ function tokenize_typst(typst) {
|
|
|
2831
2872
|
break;
|
|
2832
2873
|
}
|
|
2833
2874
|
default: {
|
|
2875
|
+
const shorthand = try_eat_shorthand(typst, pos);
|
|
2876
|
+
if (shorthand !== null) {
|
|
2877
|
+
token = new TypstToken(0 /* SYMBOL */, reverseShorthandMap.get(shorthand));
|
|
2878
|
+
pos += shorthand.length;
|
|
2879
|
+
break;
|
|
2880
|
+
}
|
|
2834
2881
|
if (isdigit(firstChar)) {
|
|
2835
2882
|
let newPos = pos;
|
|
2836
2883
|
while (newPos < typst.length && isdigit(typst[newPos])) {
|
|
@@ -3279,27 +3326,22 @@ function tex2typst(tex, options) {
|
|
|
3279
3326
|
const opt = {
|
|
3280
3327
|
nonStrict: true,
|
|
3281
3328
|
preferTypstIntrinsic: true,
|
|
3329
|
+
preferShorthands: true,
|
|
3282
3330
|
keepSpaces: false,
|
|
3283
3331
|
fracToSlash: true,
|
|
3332
|
+
inftyToOo: false,
|
|
3284
3333
|
customTexMacros: {}
|
|
3285
3334
|
};
|
|
3286
|
-
if (options) {
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
opt.preferTypstIntrinsic = options.preferTypstIntrinsic;
|
|
3292
|
-
}
|
|
3293
|
-
if (options.customTexMacros) {
|
|
3294
|
-
opt.customTexMacros = options.customTexMacros;
|
|
3295
|
-
}
|
|
3296
|
-
if (options.fracToSlash !== void 0) {
|
|
3297
|
-
opt.fracToSlash = options.fracToSlash;
|
|
3335
|
+
if (options !== void 0) {
|
|
3336
|
+
for (const key in opt) {
|
|
3337
|
+
if (options[key] !== void 0) {
|
|
3338
|
+
opt[key] = options[key];
|
|
3339
|
+
}
|
|
3298
3340
|
}
|
|
3299
3341
|
}
|
|
3300
3342
|
const texTree = parseTex(tex, opt.customTexMacros);
|
|
3301
3343
|
const typstTree = convert_tex_node_to_typst(texTree, opt);
|
|
3302
|
-
const writer = new TypstWriter(opt
|
|
3344
|
+
const writer = new TypstWriter(opt);
|
|
3303
3345
|
writer.serialize(typstTree);
|
|
3304
3346
|
return writer.finalize();
|
|
3305
3347
|
}
|