tex2typst 0.3.29 → 0.4.0
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/dist/index.d.ts +13 -9
- package/dist/index.js +80 -33
- package/dist/tex2typst.min.js +13 -13
- package/package.json +1 -1
- package/src/convert.ts +51 -23
- package/src/exposed-types.ts +13 -9
- package/src/index.ts +23 -12
- package/src/map.ts +1 -0
- package/src/tex-types.ts +2 -0
- package/src/tex-writer.ts +10 -1
- package/tests/struct-bidirection.yaml +10 -1
- package/tests/struct-tex2typst.yaml +0 -6
- package/tests/tex-to-typst.test.ts +1 -1
- package/tests/typst-to-tex.test.ts +36 -12
package/dist/index.d.ts
CHANGED
|
@@ -6,17 +6,21 @@
|
|
|
6
6
|
* Any undocumented options may be not working at present or break in the future!
|
|
7
7
|
*/
|
|
8
8
|
export interface Tex2TypstOptions {
|
|
9
|
-
nonStrict
|
|
10
|
-
preferShorthands
|
|
11
|
-
keepSpaces
|
|
12
|
-
fracToSlash
|
|
13
|
-
inftyToOo
|
|
14
|
-
optimize
|
|
15
|
-
customTexMacros
|
|
9
|
+
nonStrict: boolean; /** default is true */
|
|
10
|
+
preferShorthands: boolean; /** default is true */
|
|
11
|
+
keepSpaces: boolean; /** default is false */
|
|
12
|
+
fracToSlash: boolean; /** default is true */
|
|
13
|
+
inftyToOo: boolean; /** default is false */
|
|
14
|
+
optimize: boolean; /** default is true */
|
|
15
|
+
customTexMacros: { [key: string]: string; };
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export
|
|
19
|
-
|
|
18
|
+
export interface Typst2TexOptions {
|
|
19
|
+
blockMathMode: boolean; /** default is true */
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export declare function tex2typst(tex: string, options?: Partial<Tex2TypstOptions>): string;
|
|
23
|
+
export declare function typst2tex(typst: string, options?: Partial<Typst2TexOptions>): string;
|
|
20
24
|
|
|
21
25
|
export declare const symbolMap: Map<string, string>;
|
|
22
26
|
export declare const shorthandMap: Map<string, string>;
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,8 @@ var TexToken = class _TexToken {
|
|
|
31
31
|
return new TexTerminal(this);
|
|
32
32
|
}
|
|
33
33
|
static EMPTY = new _TexToken(0 /* EMPTY */, "");
|
|
34
|
+
static COMMAND_DISPLAYSTYLE = new _TexToken(2 /* COMMAND */, "\\displaystyle");
|
|
35
|
+
static COMMAND_TEXTSTYLE = new _TexToken(2 /* COMMAND */, "\\textstyle");
|
|
34
36
|
};
|
|
35
37
|
var TexNode = class {
|
|
36
38
|
type;
|
|
@@ -1659,6 +1661,7 @@ var TypstWriter = class {
|
|
|
1659
1661
|
// src/map.ts
|
|
1660
1662
|
var symbolMap = /* @__PURE__ */ new Map([
|
|
1661
1663
|
["displaystyle", "display"],
|
|
1664
|
+
["textstyle", "inline"],
|
|
1662
1665
|
["hspace", "#h"],
|
|
1663
1666
|
["|", "bar.v.double"],
|
|
1664
1667
|
[",", "thin"],
|
|
@@ -2913,7 +2916,7 @@ function appendWithBracketsIfNeeded(node) {
|
|
|
2913
2916
|
return node;
|
|
2914
2917
|
}
|
|
2915
2918
|
}
|
|
2916
|
-
function convert_tex_node_to_typst(abstractNode, options
|
|
2919
|
+
function convert_tex_node_to_typst(abstractNode, options) {
|
|
2917
2920
|
switch (abstractNode.type) {
|
|
2918
2921
|
case "terminal": {
|
|
2919
2922
|
const node2 = abstractNode;
|
|
@@ -3225,7 +3228,8 @@ function typst_token_to_tex(token) {
|
|
|
3225
3228
|
}
|
|
3226
3229
|
}
|
|
3227
3230
|
var TEX_NODE_COMMA = new TexToken(1 /* ELEMENT */, ",").toNode();
|
|
3228
|
-
function convert_typst_node_to_tex(abstractNode) {
|
|
3231
|
+
function convert_typst_node_to_tex(abstractNode, options) {
|
|
3232
|
+
const convert_node = (node) => convert_typst_node_to_tex(node, options);
|
|
3229
3233
|
switch (abstractNode.type) {
|
|
3230
3234
|
case "terminal": {
|
|
3231
3235
|
const node = abstractNode;
|
|
@@ -3258,7 +3262,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3258
3262
|
}
|
|
3259
3263
|
case "group": {
|
|
3260
3264
|
const node = abstractNode;
|
|
3261
|
-
const args = node.items.map(
|
|
3265
|
+
const args = node.items.map(convert_node);
|
|
3262
3266
|
const alignment_char = new TexToken(7 /* CONTROL */, "&").toNode();
|
|
3263
3267
|
const newline_char = new TexToken(7 /* CONTROL */, "\\\\").toNode();
|
|
3264
3268
|
if (array_includes(args, alignment_char)) {
|
|
@@ -3274,7 +3278,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3274
3278
|
}
|
|
3275
3279
|
case "leftright": {
|
|
3276
3280
|
const node = abstractNode;
|
|
3277
|
-
const body =
|
|
3281
|
+
const body = convert_node(node.body);
|
|
3278
3282
|
let left = node.left ? typst_token_to_tex(node.left) : new TexToken(1 /* ELEMENT */, ".");
|
|
3279
3283
|
let right = node.right ? typst_token_to_tex(node.right) : new TexToken(1 /* ELEMENT */, ".");
|
|
3280
3284
|
if (node.isOverHigh()) {
|
|
@@ -3291,7 +3295,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3291
3295
|
// `\left\| a + \frac{1}{3} \right\|` <- `norm(a + 1/3)`
|
|
3292
3296
|
case "norm": {
|
|
3293
3297
|
const arg0 = node.args[0];
|
|
3294
|
-
const body =
|
|
3298
|
+
const body = convert_node(arg0);
|
|
3295
3299
|
if (node.isOverHigh()) {
|
|
3296
3300
|
return new TexLeftRight({
|
|
3297
3301
|
body,
|
|
@@ -3312,7 +3316,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3312
3316
|
const left = "\\l" + node.head.value;
|
|
3313
3317
|
const right = "\\r" + node.head.value;
|
|
3314
3318
|
const arg0 = node.args[0];
|
|
3315
|
-
const body =
|
|
3319
|
+
const body = convert_node(arg0);
|
|
3316
3320
|
const left_node = new TexToken(2 /* COMMAND */, left);
|
|
3317
3321
|
const right_node = new TexToken(2 /* COMMAND */, right);
|
|
3318
3322
|
if (node.isOverHigh()) {
|
|
@@ -3328,22 +3332,22 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3328
3332
|
// special hook for root
|
|
3329
3333
|
case "root": {
|
|
3330
3334
|
const [degree, radicand] = node.args;
|
|
3331
|
-
const data =
|
|
3332
|
-
return new TexFuncCall(new TexToken(2 /* COMMAND */, "\\sqrt"), [
|
|
3335
|
+
const data = convert_node(degree);
|
|
3336
|
+
return new TexFuncCall(new TexToken(2 /* COMMAND */, "\\sqrt"), [convert_node(radicand)], data);
|
|
3333
3337
|
}
|
|
3334
3338
|
// special hook for overbrace and underbrace
|
|
3335
3339
|
case "overbrace":
|
|
3336
3340
|
case "underbrace": {
|
|
3337
3341
|
const [body, label] = node.args;
|
|
3338
|
-
const base = new TexFuncCall(typst_token_to_tex(node.head), [
|
|
3339
|
-
const script =
|
|
3342
|
+
const base = new TexFuncCall(typst_token_to_tex(node.head), [convert_node(body)]);
|
|
3343
|
+
const script = convert_node(label);
|
|
3340
3344
|
const data = node.head.value === "overbrace" ? { base, sup: script, sub: null } : { base, sub: script, sup: null };
|
|
3341
3345
|
return new TexSupSub(data);
|
|
3342
3346
|
}
|
|
3343
3347
|
// special hook for vec
|
|
3344
3348
|
// "vec(a, b, c)" -> "\begin{pmatrix}a\\ b\\ c\end{pmatrix}"
|
|
3345
3349
|
case "vec": {
|
|
3346
|
-
const tex_matrix = node.args.map(
|
|
3350
|
+
const tex_matrix = node.args.map((arg) => [convert_node(arg)]);
|
|
3347
3351
|
return new TexBeginEnd(new TexToken(3 /* LITERAL */, "pmatrix"), tex_matrix);
|
|
3348
3352
|
}
|
|
3349
3353
|
// special hook for op
|
|
@@ -3374,20 +3378,46 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3374
3378
|
}
|
|
3375
3379
|
return new TexFuncCall(
|
|
3376
3380
|
new TexToken(2 /* COMMAND */, command),
|
|
3377
|
-
[
|
|
3381
|
+
[convert_node(node.args[1])]
|
|
3378
3382
|
);
|
|
3379
3383
|
}
|
|
3384
|
+
// display(...) -> \displaystyle ... \textstyle
|
|
3385
|
+
// The postprocessor will remove \textstyle if it is the end of the math code
|
|
3386
|
+
case "display": {
|
|
3387
|
+
const arg0 = node.args[0];
|
|
3388
|
+
const group = new TexGroup([
|
|
3389
|
+
TexToken.COMMAND_DISPLAYSTYLE.toNode(),
|
|
3390
|
+
convert_node(arg0)
|
|
3391
|
+
]);
|
|
3392
|
+
if (!options.blockMathMode) {
|
|
3393
|
+
group.items.push(TexToken.COMMAND_TEXTSTYLE.toNode());
|
|
3394
|
+
}
|
|
3395
|
+
return group;
|
|
3396
|
+
}
|
|
3397
|
+
// inline(...) -> \textstyle ... \displaystyle
|
|
3398
|
+
// The postprocessor will remove \displaystyle if it is the end of the math code
|
|
3399
|
+
case "inline": {
|
|
3400
|
+
const arg0 = node.args[0];
|
|
3401
|
+
const group = new TexGroup([
|
|
3402
|
+
TexToken.COMMAND_TEXTSTYLE.toNode(),
|
|
3403
|
+
convert_node(arg0)
|
|
3404
|
+
]);
|
|
3405
|
+
if (options.blockMathMode) {
|
|
3406
|
+
group.items.push(TexToken.COMMAND_DISPLAYSTYLE.toNode());
|
|
3407
|
+
}
|
|
3408
|
+
return group;
|
|
3409
|
+
}
|
|
3380
3410
|
// general case
|
|
3381
3411
|
default: {
|
|
3382
3412
|
const func_name_tex = typst_token_to_tex(node.head);
|
|
3383
3413
|
const is_known_func = TEX_UNARY_COMMANDS.includes(func_name_tex.value.substring(1)) || TEX_BINARY_COMMANDS.includes(func_name_tex.value.substring(1));
|
|
3384
3414
|
if (func_name_tex.value.length > 0 && is_known_func) {
|
|
3385
|
-
return new TexFuncCall(func_name_tex, node.args.map(
|
|
3415
|
+
return new TexFuncCall(func_name_tex, node.args.map(convert_node));
|
|
3386
3416
|
} else {
|
|
3387
3417
|
return new TexGroup([
|
|
3388
3418
|
typst_token_to_tex(node.head).toNode(),
|
|
3389
3419
|
new TexToken(1 /* ELEMENT */, "(").toNode(),
|
|
3390
|
-
...array_intersperse(node.args.map(
|
|
3420
|
+
...array_intersperse(node.args.map(convert_node), TEX_NODE_COMMA),
|
|
3391
3421
|
new TexToken(1 /* ELEMENT */, ")").toNode()
|
|
3392
3422
|
]);
|
|
3393
3423
|
}
|
|
@@ -3402,7 +3432,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3402
3432
|
const color = node.options["fill"];
|
|
3403
3433
|
return new TexFuncCall(
|
|
3404
3434
|
new TexToken(2 /* COMMAND */, "\\textcolor"),
|
|
3405
|
-
[
|
|
3435
|
+
[convert_node(color), convert_node(node.fragments[0])]
|
|
3406
3436
|
);
|
|
3407
3437
|
}
|
|
3408
3438
|
}
|
|
@@ -3414,11 +3444,11 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3414
3444
|
case "supsub": {
|
|
3415
3445
|
const node = abstractNode;
|
|
3416
3446
|
const { base, sup, sub } = node;
|
|
3417
|
-
const sup_tex = sup ?
|
|
3418
|
-
const sub_tex = sub ?
|
|
3447
|
+
const sup_tex = sup ? convert_node(sup) : null;
|
|
3448
|
+
const sub_tex = sub ? convert_node(sub) : null;
|
|
3419
3449
|
if (base.head.eq(new TypstToken(1 /* SYMBOL */, "limits"))) {
|
|
3420
3450
|
const limits = base;
|
|
3421
|
-
const body_in_limits =
|
|
3451
|
+
const body_in_limits = convert_node(limits.args[0]);
|
|
3422
3452
|
if (sup_tex !== null && sub_tex === null) {
|
|
3423
3453
|
return new TexFuncCall(new TexToken(2 /* COMMAND */, "\\overset"), [sup_tex, body_in_limits]);
|
|
3424
3454
|
} else if (sup_tex === null && sub_tex !== null) {
|
|
@@ -3428,7 +3458,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3428
3458
|
return new TexFuncCall(new TexToken(2 /* COMMAND */, "\\overset"), [sup_tex, underset_call]);
|
|
3429
3459
|
}
|
|
3430
3460
|
}
|
|
3431
|
-
const base_tex =
|
|
3461
|
+
const base_tex = convert_node(base);
|
|
3432
3462
|
const res = new TexSupSub({
|
|
3433
3463
|
base: base_tex,
|
|
3434
3464
|
sup: sup_tex,
|
|
@@ -3438,7 +3468,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3438
3468
|
}
|
|
3439
3469
|
case "matrixLike": {
|
|
3440
3470
|
const node = abstractNode;
|
|
3441
|
-
const tex_matrix = node.matrix.map((row) => row.map(
|
|
3471
|
+
const tex_matrix = node.matrix.map((row) => row.map(convert_node));
|
|
3442
3472
|
if (node.head.eq(TypstMatrixLike.MAT)) {
|
|
3443
3473
|
let env_type = "pmatrix";
|
|
3444
3474
|
if (node.options) {
|
|
@@ -3485,8 +3515,8 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3485
3515
|
case "fraction": {
|
|
3486
3516
|
const node = abstractNode;
|
|
3487
3517
|
const [numerator, denominator] = node.args;
|
|
3488
|
-
const num_tex =
|
|
3489
|
-
const den_tex =
|
|
3518
|
+
const num_tex = convert_node(numerator);
|
|
3519
|
+
const den_tex = convert_node(denominator);
|
|
3490
3520
|
return new TexFuncCall(new TexToken(2 /* COMMAND */, "\\frac"), [num_tex, den_tex]);
|
|
3491
3521
|
}
|
|
3492
3522
|
default:
|
|
@@ -3993,6 +4023,14 @@ var TexWriter = class {
|
|
|
3993
4023
|
this.queue = this.queue.concat(node.serialize());
|
|
3994
4024
|
}
|
|
3995
4025
|
flushQueue() {
|
|
4026
|
+
while (this.queue.length > 0) {
|
|
4027
|
+
const last_token = this.queue[this.queue.length - 1];
|
|
4028
|
+
if (last_token.eq(TexToken.COMMAND_DISPLAYSTYLE) || last_token.eq(TexToken.COMMAND_TEXTSTYLE)) {
|
|
4029
|
+
this.queue.pop();
|
|
4030
|
+
} else {
|
|
4031
|
+
break;
|
|
4032
|
+
}
|
|
4033
|
+
}
|
|
3996
4034
|
for (let i = 0; i < this.queue.length; i++) {
|
|
3997
4035
|
this.buffer = writeTexTokenBuffer(this.buffer, this.queue[i]);
|
|
3998
4036
|
}
|
|
@@ -4005,7 +4043,7 @@ var TexWriter = class {
|
|
|
4005
4043
|
};
|
|
4006
4044
|
|
|
4007
4045
|
// src/index.ts
|
|
4008
|
-
function tex2typst(tex, options) {
|
|
4046
|
+
function tex2typst(tex, options = {}) {
|
|
4009
4047
|
const opt = {
|
|
4010
4048
|
nonStrict: true,
|
|
4011
4049
|
preferShorthands: true,
|
|
@@ -4015,14 +4053,12 @@ function tex2typst(tex, options) {
|
|
|
4015
4053
|
optimize: true,
|
|
4016
4054
|
customTexMacros: {}
|
|
4017
4055
|
};
|
|
4018
|
-
if (options !==
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
opt[key] = options[key];
|
|
4025
|
-
}
|
|
4056
|
+
if (typeof options !== "object") {
|
|
4057
|
+
throw new Error("options must be an object");
|
|
4058
|
+
}
|
|
4059
|
+
for (const key in opt) {
|
|
4060
|
+
if (key in options) {
|
|
4061
|
+
opt[key] = options[key];
|
|
4026
4062
|
}
|
|
4027
4063
|
}
|
|
4028
4064
|
const texTree = parseTex(tex, opt.customTexMacros);
|
|
@@ -4031,9 +4067,20 @@ function tex2typst(tex, options) {
|
|
|
4031
4067
|
writer.serialize(typstTree);
|
|
4032
4068
|
return writer.finalize();
|
|
4033
4069
|
}
|
|
4034
|
-
function typst2tex(typst) {
|
|
4070
|
+
function typst2tex(typst, options = {}) {
|
|
4071
|
+
const opt = {
|
|
4072
|
+
blockMathMode: true
|
|
4073
|
+
};
|
|
4074
|
+
if (typeof options !== "object") {
|
|
4075
|
+
throw new Error("options must be an object");
|
|
4076
|
+
}
|
|
4077
|
+
for (const key in opt) {
|
|
4078
|
+
if (key in options) {
|
|
4079
|
+
opt[key] = options[key];
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
4035
4082
|
const typstTree = parseTypst(typst);
|
|
4036
|
-
const texTree = convert_typst_node_to_tex(typstTree);
|
|
4083
|
+
const texTree = convert_typst_node_to_tex(typstTree, opt);
|
|
4037
4084
|
const writer = new TexWriter();
|
|
4038
4085
|
writer.append(texTree);
|
|
4039
4086
|
return writer.finalize();
|