tex2typst 0.0.11 → 0.0.16

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.
@@ -0,0 +1,35 @@
1
+ name: tex2typst Tests
2
+ on:
3
+ push:
4
+ branches:
5
+ - main
6
+ - dev
7
+ pull_request:
8
+ branches:
9
+ - main
10
+
11
+ jobs:
12
+ unit-tests:
13
+ name: Unit tests
14
+ runs-on: ${{ matrix.os }}
15
+
16
+ strategy:
17
+ matrix:
18
+ os: [ubuntu-latest]
19
+ node: [18]
20
+
21
+ steps:
22
+ - name: Checkout
23
+ uses: actions/checkout@v3
24
+
25
+ - name: Setup node env
26
+ uses: actions/setup-node@v4
27
+ with:
28
+ node-version: ${{ matrix.node }}
29
+ cache: 'yarn'
30
+
31
+ - name: Install dependencies
32
+ run: yarn install
33
+
34
+ - name: Run Vitest Test
35
+ run: npx vitest run
package/dist/map.js CHANGED
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.symbolMap = void 0;
4
4
  exports.symbolMap = new Map([
5
5
  ['gets', 'arrow.l'],
6
- ['infty', 'infinity'], // infinity
7
6
  ['nonumber', ''],
8
7
  ['vec', 'arrow'],
9
8
  ['mathbf', 'bold'],
@@ -44,36 +43,6 @@ exports.symbolMap = new Map([
44
43
  ['boxplus', 'plus.square'],
45
44
  ['otimes', 'times.circle'],
46
45
  ['boxtimes', 'times.square'],
47
- /* symbols in mathematical logic */
48
- ['neg', 'not'],
49
- ['land', 'and'],
50
- ['lor', 'or'],
51
- ['varnothing', 'diameter'], // empty set
52
- /* symbols about comparing numbers */
53
- // tex: \neq \leq \geq \ll \gg \prec \succ \preceq \succeq
54
- // typst: eq.not lt.eq gt.eq lt.double gt.double prec succ prec.eq succ.eq
55
- ['neq', 'eq.not'],
56
- ['leq', 'lt.eq'],
57
- ['geq', 'gt.eq'],
58
- ['ll', 'lt.double'],
59
- ['gg', 'gt.double'],
60
- ['prec', 'prec'],
61
- ['succ', 'succ'],
62
- ['preceq', 'prec.eq'],
63
- ['succeq', 'succ.eq'],
64
- /* symbols about differential */
65
- ['Delta', 'Delta'],
66
- ['nabla', 'nabla'],
67
- ['partial', 'diff'],
68
- /* symbols about integral */
69
- // tex: \int \oint \iint \oiint \iiint \oiiint
70
- // typst: integral integral.cont integral.double integral.surf integral.triple integral.vol
71
- ['int', 'integral'],
72
- ['oint', 'integral.cont'],
73
- ['iint', 'integral.double'],
74
- ['oiint', 'integral.surf'],
75
- ['iiint', 'integral.triple'],
76
- ['oiiint', 'integral.vol'],
77
46
  /* wave */
78
47
  // tex: \sim \approx \cong \simeq \asymp \equiv \propto
79
48
  // typst: tilde approx tilde.equiv tilde.eq ≍ equiv prop
@@ -94,7 +63,6 @@ exports.symbolMap = new Map([
94
63
  ['leftrightarrow', 'arrow.l.r'],
95
64
  ['longleftrightarrow', 'arrow.l.r.long'],
96
65
  ['rightrightarrows', 'arrows.rr'],
97
- ['binom', 'binom'],
98
66
  /* left and right floor,ceil */
99
67
  // tex: \lfloor \rfloor \lceil \rceil
100
68
  // typst: ⌊ ⌋ ⌈ ⌉
@@ -181,12 +149,14 @@ exports.symbolMap = new Map([
181
149
  ['iiint', 'integral.triple'],
182
150
  ['iint', 'integral.double'],
183
151
  ['in', 'in'],
152
+ ['infty', 'infinity'],
184
153
  ['int', 'integral'],
185
154
  ['intercal', 'top'], // 'top' or 'tack.b'
186
155
  ['iota', 'iota'],
187
156
  ['jmath', 'dotless.j'],
188
157
  ['kappa', 'kappa'],
189
158
  ['lambda', 'lambda'],
159
+ ['land', 'and'],
190
160
  ['langle', 'angle.l'],
191
161
  ['lbrace', 'brace.l'],
192
162
  ['lbrack', 'bracket.l'],
@@ -202,6 +172,7 @@ exports.symbolMap = new Map([
202
172
  ['ll', 'lt.double'],
203
173
  ['longmapsto', 'arrow.long.bar'],
204
174
  ['longrightarrow', 'arrow.long'],
175
+ ['lor', 'or'],
205
176
  ['ltimes', 'times.l'],
206
177
  ['mapsto', 'arrow.bar'],
207
178
  ['measuredangle', 'angle.arc'],
@@ -215,14 +186,25 @@ exports.symbolMap = new Map([
215
186
  ['ne', 'eq.not'],
216
187
  ['neg', 'not'],
217
188
  ['neq', 'eq.not'],
189
+ ['nexists', 'exists.not'],
190
+ ['ni', "in.rev"],
191
+ ['nleftarrow', "arrow.l.not"],
192
+ ['nleq', "lt.eq.not"],
193
+ ['nparallel', "parallel.not"],
194
+ ['ngeq', 'gt.eq.not'],
218
195
  ['nmid', 'divides.not'],
219
196
  ['notin', 'in.not'],
220
- ['nleftarrow', 'arrow.l.not'],
221
197
  ['nrightarrow', 'arrow.not'],
222
198
  ['nsim', 'tilde.not'],
199
+ ['nsubseteq', 'subset.eq.not'],
223
200
  ['nu', 'nu'],
201
+ ['ntriangleleft', 'lt.tri.not'],
202
+ ['ntriangleright', 'gt.tri.not'],
203
+ ['nwarrow', 'arrow.tl'],
224
204
  ['odot', 'dot.circle'],
225
205
  ['oint', 'integral.cont'],
206
+ ['oiint', 'integral.surf'],
207
+ ['oiiint', 'integral.vol'],
226
208
  ['omega', 'omega'],
227
209
  // ['omicron', 'omicron'],
228
210
  ['ominus', 'minus.circle'],
@@ -234,6 +216,7 @@ exports.symbolMap = new Map([
234
216
  ['phi', 'phi.alt'],
235
217
  ['pi', 'pi'],
236
218
  ['pm', 'plus.minus'],
219
+ ['pounds', 'pound'],
237
220
  ['prec', 'prec'],
238
221
  ['preceq', 'prec.eq'],
239
222
  ['prime', 'prime'],
@@ -252,9 +235,14 @@ exports.symbolMap = new Map([
252
235
  ['sigma', 'sigma'],
253
236
  ['sim', 'tilde'],
254
237
  ['simeq', 'tilde.eq'],
238
+ ['slash', 'slash'],
239
+ ['smallsetminus', 'without'],
255
240
  // ['smile', 'paren.b'],
241
+ ['spadesuit', 'suit.spade'],
256
242
  ['sqcap', 'sect.sq'],
257
243
  ['sqcup', 'union.sq'],
244
+ ['sqsubseteq', 'subset.eq.sq'],
245
+ ['sqsupseteq', 'supset.eq.sq'],
258
246
  ['star', 'star'],
259
247
  ['subset', 'subset'],
260
248
  ['subseteq', 'subset.eq'],
@@ -265,19 +253,24 @@ exports.symbolMap = new Map([
265
253
  ['supset', 'supset'],
266
254
  ['supseteq', 'supset.eq'],
267
255
  ['supsetneq', 'supset.neq'],
256
+ ['swarrow', 'arrow.bl'],
268
257
  ['tau', 'tau'],
269
258
  ['theta', 'theta'],
270
259
  ['times', 'times'],
271
260
  ['to', 'arrow.r'],
261
+ ['top', 'top'],
272
262
  ['triangle', 'triangle.t'],
263
+ ['triangledown', 'triangle.b.small'],
273
264
  ['triangleleft', 'triangle.l.small'],
274
- ['triangleright', 'triangle.small'],
265
+ ['triangleright', 'triangle.r.small'],
266
+ ['twoheadrightarrow', 'arrow.r.twohead'],
275
267
  ['uparrow', 'arrow.t'],
276
268
  ['updownarrow', 'arrow.t.b'],
277
269
  ['upharpoonright', 'harpoon.tr'],
278
270
  ['uplus', 'union.plus'],
279
271
  ['upsilon', 'upsilon'],
280
272
  ['varepsilon', 'epsilon'],
273
+ ['varnothing', 'diameter'], // empty set
281
274
  ['varphi', 'phi'],
282
275
  ['varpi', 'pi.alt'],
283
276
  ['varrho', 'rho.alt'],
@@ -289,5 +282,10 @@ exports.symbolMap = new Map([
289
282
  ['wedge', 'and'],
290
283
  ['wr', 'wreath'],
291
284
  ['xi', 'xi'],
285
+ ['yen', 'yen'],
292
286
  ['zeta', 'zeta'],
287
+ // extended
288
+ ['mathscr', 'scr'],
289
+ ['LaTeX', '#LaTeX'],
290
+ ['TeX', '#TeX'],
293
291
  ]);
package/dist/parser.js CHANGED
@@ -246,7 +246,7 @@ function parseTex(tex, customTexMacros) {
246
246
  // displayMode=true. Otherwise, "KaTeX parse error: {align*} can be used only in display mode."
247
247
  const macros = Object.assign({
248
248
  // KaTeX parse these commands so complicatedly that we need some hacks to keep things simple.
249
- '\\mod': '\\operatorname{SyMb01-mod}', '\\liminf': '\\operatorname{SyMb01-liminf}', '\\limsup': '\\operatorname{SyMb01-limsup}', '\\qquad': '\\operatorname{SyMb01-qquad}', '\\quad': '\\operatorname{SyMb01-quad}', '\\cdots': '\\operatorname{SyMb01-cdots}', '\\colon': '\\operatorname{SyMb01-colon}', '\\imath': '\\operatorname{SyMb01-imath}', '\\\iiiint': '\\operatorname{SyMb01-iiiint}', '\\jmath': '\\operatorname{SyMb01-jmath}', '\\vdots': '\\operatorname{SyMb01-vdots}', '\\notin': '\\operatorname{SyMb01-notin}' }, customTexMacros);
249
+ '\\mod': '\\operatorname{SyMb01-mod}', '\\liminf': '\\operatorname{SyMb01-liminf}', '\\limsup': '\\operatorname{SyMb01-limsup}', '\\qquad': '\\operatorname{SyMb01-qquad}', '\\quad': '\\operatorname{SyMb01-quad}', '\\cdots': '\\operatorname{SyMb01-cdots}', '\\colon': '\\operatorname{SyMb01-colon}', '\\imath': '\\operatorname{SyMb01-imath}', '\\\iiiint': '\\operatorname{SyMb01-iiiint}', '\\jmath': '\\operatorname{SyMb01-jmath}', '\\vdots': '\\operatorname{SyMb01-vdots}', '\\notin': '\\operatorname{SyMb01-notin}', '\\slash': '\\operatorname{SyMb01-slash}', '\\LaTeX': '\\operatorname{SyMb01-LaTeX}', '\\TeX': '\\operatorname{SyMb01-TeX}' }, customTexMacros);
250
250
  const options = {
251
251
  macros: macros,
252
252
  displayMode: true,
package/dist/writer.js CHANGED
@@ -310,6 +310,22 @@ class TypstWriter {
310
310
  }
311
311
  finalize() {
312
312
  this.flushQueue();
313
+ const smartFloorPass = function (input) {
314
+ // Use regex to replace all "⌊ xxx ⌋" with "floor(xxx)"
315
+ let res = input.replace(/⌊\s*(.*?)\s*⌋/g, "floor($1)");
316
+ // Typst disallow "floor()" with empty argument, so add am empty string inside if it's empty.
317
+ res = res.replace(/floor\(\)/g, 'floor("")');
318
+ return res;
319
+ };
320
+ const smartCeilPass = function (input) {
321
+ // Use regex to replace all "⌈ xxx ⌉" with "ceil(xxx)"
322
+ let res = input.replace(/⌈\s*(.*?)\s*⌉/g, "ceil($1)");
323
+ // Typst disallow "ceil()" with empty argument, so add an empty string inside if it's empty.
324
+ res = res.replace(/ceil\(\)/g, 'ceil("")');
325
+ return res;
326
+ };
327
+ this.buffer = smartFloorPass(this.buffer);
328
+ this.buffer = smartCeilPass(this.buffer);
313
329
  return this.buffer;
314
330
  }
315
331
  }
@@ -321,6 +337,9 @@ function convertToken(token) {
321
337
  else if (token === '\\\\') {
322
338
  return '\\\n';
323
339
  }
340
+ else if (['\\$', '\\#', '\\&', '\\_'].includes(token)) {
341
+ return token;
342
+ }
324
343
  else if (token.startsWith('\\')) {
325
344
  const symbol = token.slice(1);
326
345
  if (map_1.symbolMap.has(symbol)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tex2typst",
3
- "version": "0.0.11",
3
+ "version": "0.0.16",
4
4
  "description": "JavaScript library for converting TeX code to Typst",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/map.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  export const symbolMap = new Map<string, string>([
2
2
  ['gets', 'arrow.l'],
3
- ['infty', 'infinity'], // infinity
4
3
  ['nonumber', ''],
5
4
  ['vec', 'arrow'],
6
5
  ['mathbf', 'bold'],
@@ -46,40 +45,6 @@ export const symbolMap = new Map<string, string>([
46
45
  ['otimes', 'times.circle'],
47
46
  ['boxtimes', 'times.square'],
48
47
 
49
- /* symbols in mathematical logic */
50
- ['neg', 'not'],
51
- ['land', 'and'],
52
- ['lor', 'or'],
53
-
54
- ['varnothing', 'diameter'], // empty set
55
-
56
- /* symbols about comparing numbers */
57
- // tex: \neq \leq \geq \ll \gg \prec \succ \preceq \succeq
58
- // typst: eq.not lt.eq gt.eq lt.double gt.double prec succ prec.eq succ.eq
59
- ['neq', 'eq.not'],
60
- ['leq', 'lt.eq'],
61
- ['geq', 'gt.eq'],
62
- ['ll', 'lt.double'],
63
- ['gg', 'gt.double'],
64
- ['prec', 'prec'],
65
- ['succ', 'succ'],
66
- ['preceq', 'prec.eq'],
67
- ['succeq', 'succ.eq'],
68
-
69
- /* symbols about differential */
70
- ['Delta', 'Delta'],
71
- ['nabla', 'nabla'],
72
- ['partial', 'diff'],
73
-
74
- /* symbols about integral */
75
- // tex: \int \oint \iint \oiint \iiint \oiiint
76
- // typst: integral integral.cont integral.double integral.surf integral.triple integral.vol
77
- ['int', 'integral'],
78
- ['oint', 'integral.cont'],
79
- ['iint', 'integral.double'],
80
- ['oiint', 'integral.surf'],
81
- ['iiint', 'integral.triple'],
82
- ['oiiint', 'integral.vol'],
83
48
 
84
49
  /* wave */
85
50
  // tex: \sim \approx \cong \simeq \asymp \equiv \propto
@@ -103,7 +68,6 @@ export const symbolMap = new Map<string, string>([
103
68
  ['longleftrightarrow', 'arrow.l.r.long'],
104
69
  ['rightrightarrows', 'arrows.rr'],
105
70
 
106
- ['binom', 'binom'],
107
71
 
108
72
  /* left and right floor,ceil */
109
73
  // tex: \lfloor \rfloor \lceil \rceil
@@ -193,12 +157,14 @@ export const symbolMap = new Map<string, string>([
193
157
  ['iiint', 'integral.triple'],
194
158
  ['iint', 'integral.double'],
195
159
  ['in', 'in'],
160
+ ['infty', 'infinity'],
196
161
  ['int', 'integral'],
197
162
  ['intercal', 'top'], // 'top' or 'tack.b'
198
163
  ['iota', 'iota'],
199
164
  ['jmath', 'dotless.j'],
200
165
  ['kappa', 'kappa'],
201
166
  ['lambda', 'lambda'],
167
+ ['land', 'and'],
202
168
  ['langle', 'angle.l'],
203
169
  ['lbrace', 'brace.l'],
204
170
  ['lbrack', 'bracket.l'],
@@ -214,6 +180,7 @@ export const symbolMap = new Map<string, string>([
214
180
  ['ll', 'lt.double'],
215
181
  ['longmapsto', 'arrow.long.bar'],
216
182
  ['longrightarrow', 'arrow.long'],
183
+ ['lor', 'or'],
217
184
  ['ltimes', 'times.l'],
218
185
  ['mapsto', 'arrow.bar'],
219
186
  ['measuredangle', 'angle.arc'],
@@ -227,14 +194,25 @@ export const symbolMap = new Map<string, string>([
227
194
  ['ne', 'eq.not'],
228
195
  ['neg', 'not'],
229
196
  ['neq', 'eq.not'],
197
+ ['nexists', 'exists.not'],
198
+ ['ni', "in.rev"],
199
+ ['nleftarrow', "arrow.l.not"],
200
+ ['nleq', "lt.eq.not"],
201
+ ['nparallel', "parallel.not"],
202
+ ['ngeq', 'gt.eq.not'],
230
203
  ['nmid', 'divides.not'],
231
204
  ['notin', 'in.not'],
232
- ['nleftarrow', 'arrow.l.not'],
233
205
  ['nrightarrow', 'arrow.not'],
234
206
  ['nsim', 'tilde.not'],
207
+ ['nsubseteq', 'subset.eq.not'],
235
208
  ['nu', 'nu'],
209
+ ['ntriangleleft', 'lt.tri.not'],
210
+ ['ntriangleright', 'gt.tri.not'],
211
+ ['nwarrow', 'arrow.tl'],
236
212
  ['odot', 'dot.circle'],
237
213
  ['oint', 'integral.cont'],
214
+ ['oiint', 'integral.surf'],
215
+ ['oiiint', 'integral.vol'],
238
216
  ['omega', 'omega'],
239
217
  // ['omicron', 'omicron'],
240
218
  ['ominus', 'minus.circle'],
@@ -246,6 +224,7 @@ export const symbolMap = new Map<string, string>([
246
224
  ['phi', 'phi.alt'],
247
225
  ['pi', 'pi'],
248
226
  ['pm', 'plus.minus'],
227
+ ['pounds', 'pound'],
249
228
  ['prec', 'prec'],
250
229
  ['preceq', 'prec.eq'],
251
230
  ['prime', 'prime'],
@@ -264,9 +243,14 @@ export const symbolMap = new Map<string, string>([
264
243
  ['sigma', 'sigma'],
265
244
  ['sim', 'tilde'],
266
245
  ['simeq', 'tilde.eq'],
246
+ ['slash', 'slash'],
247
+ ['smallsetminus', 'without'],
267
248
  // ['smile', 'paren.b'],
249
+ ['spadesuit', 'suit.spade'],
268
250
  ['sqcap', 'sect.sq'],
269
251
  ['sqcup', 'union.sq'],
252
+ ['sqsubseteq', 'subset.eq.sq'],
253
+ ['sqsupseteq', 'supset.eq.sq'],
270
254
  ['star', 'star'],
271
255
  ['subset', 'subset'],
272
256
  ['subseteq', 'subset.eq'],
@@ -277,19 +261,24 @@ export const symbolMap = new Map<string, string>([
277
261
  ['supset', 'supset'],
278
262
  ['supseteq', 'supset.eq'],
279
263
  ['supsetneq', 'supset.neq'],
264
+ ['swarrow', 'arrow.bl'],
280
265
  ['tau', 'tau'],
281
266
  ['theta', 'theta'],
282
267
  ['times', 'times'],
283
268
  ['to', 'arrow.r'],
269
+ ['top', 'top'],
284
270
  ['triangle', 'triangle.t'],
271
+ ['triangledown', 'triangle.b.small'],
285
272
  ['triangleleft', 'triangle.l.small'],
286
- ['triangleright', 'triangle.small'],
273
+ ['triangleright', 'triangle.r.small'],
274
+ ['twoheadrightarrow', 'arrow.r.twohead'],
287
275
  ['uparrow', 'arrow.t'],
288
276
  ['updownarrow', 'arrow.t.b'],
289
277
  ['upharpoonright', 'harpoon.tr'],
290
278
  ['uplus', 'union.plus'],
291
279
  ['upsilon', 'upsilon'],
292
280
  ['varepsilon', 'epsilon'],
281
+ ['varnothing', 'diameter'], // empty set
293
282
  ['varphi', 'phi'],
294
283
  ['varpi', 'pi.alt'],
295
284
  ['varrho', 'rho.alt'],
@@ -301,5 +290,11 @@ export const symbolMap = new Map<string, string>([
301
290
  ['wedge', 'and'],
302
291
  ['wr', 'wreath'],
303
292
  ['xi', 'xi'],
293
+ ['yen', 'yen'],
304
294
  ['zeta', 'zeta'],
295
+
296
+ // extended
297
+ ['mathscr', 'scr'],
298
+ ['LaTeX', '#LaTeX'],
299
+ ['TeX', '#TeX'],
305
300
  ]);
package/src/parser.ts CHANGED
@@ -253,6 +253,9 @@ export function parseTex(tex: string, customTexMacros: {[key: string]: string}):
253
253
  '\\jmath': '\\operatorname{SyMb01-jmath}',
254
254
  '\\vdots': '\\operatorname{SyMb01-vdots}',
255
255
  '\\notin': '\\operatorname{SyMb01-notin}',
256
+ '\\slash': '\\operatorname{SyMb01-slash}',
257
+ '\\LaTeX': '\\operatorname{SyMb01-LaTeX}',
258
+ '\\TeX': '\\operatorname{SyMb01-TeX}',
256
259
  ...customTexMacros
257
260
  };
258
261
  const options = {
package/src/writer.ts CHANGED
@@ -312,6 +312,22 @@ export class TypstWriter {
312
312
 
313
313
  public finalize(): string {
314
314
  this.flushQueue();
315
+ const smartFloorPass = function (input: string): string {
316
+ // Use regex to replace all "⌊ xxx ⌋" with "floor(xxx)"
317
+ let res = input.replace(/⌊\s*(.*?)\s*⌋/g, "floor($1)");
318
+ // Typst disallow "floor()" with empty argument, so add am empty string inside if it's empty.
319
+ res = res.replace(/floor\(\)/g, 'floor("")');
320
+ return res;
321
+ };
322
+ const smartCeilPass = function (input: string): string {
323
+ // Use regex to replace all "⌈ xxx ⌉" with "ceil(xxx)"
324
+ let res = input.replace(/⌈\s*(.*?)\s*⌉/g, "ceil($1)");
325
+ // Typst disallow "ceil()" with empty argument, so add an empty string inside if it's empty.
326
+ res = res.replace(/ceil\(\)/g, 'ceil("")');
327
+ return res;
328
+ }
329
+ this.buffer = smartFloorPass(this.buffer);
330
+ this.buffer = smartCeilPass(this.buffer);
315
331
  return this.buffer;
316
332
  }
317
333
  }
@@ -322,6 +338,8 @@ function convertToken(token: string): string {
322
338
  return token;
323
339
  } else if (token === '\\\\') {
324
340
  return '\\\n';
341
+ } else if (['\\$', '\\#', '\\&', '\\_'].includes(token)) {
342
+ return token;
325
343
  } else if (token.startsWith('\\')) {
326
344
  const symbol = token.slice(1);
327
345
  if (symbolMap.has(symbol)) {