@fiduswriter/document 0.1.0-alpha.1

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.
Files changed (110) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +16 -0
  3. package/jest.config.js +23 -0
  4. package/package.json +59 -0
  5. package/schema.json +1 -0
  6. package/scripts/export-schema.js +16 -0
  7. package/src/bibliography/common.js +92 -0
  8. package/src/bibliography/csl_bib.js +139 -0
  9. package/src/citations/citeproc_sys.js +42 -0
  10. package/src/citations/format.js +194 -0
  11. package/src/common/blob.js +10 -0
  12. package/src/common/file.js +25 -0
  13. package/src/common/index.js +12 -0
  14. package/src/common/network.js +79 -0
  15. package/src/common/text.js +44 -0
  16. package/src/editor/e2ee/encryptor.js +228 -0
  17. package/src/exporter/docx/citations.js +177 -0
  18. package/src/exporter/docx/comments.js +165 -0
  19. package/src/exporter/docx/footnotes.js +240 -0
  20. package/src/exporter/docx/images.js +101 -0
  21. package/src/exporter/docx/index.js +185 -0
  22. package/src/exporter/docx/lists.js +260 -0
  23. package/src/exporter/docx/math.js +46 -0
  24. package/src/exporter/docx/metadata.js +289 -0
  25. package/src/exporter/docx/rels.js +193 -0
  26. package/src/exporter/docx/render.js +941 -0
  27. package/src/exporter/docx/richtext.js +1182 -0
  28. package/src/exporter/docx/tables.js +112 -0
  29. package/src/exporter/docx/tools.js +50 -0
  30. package/src/exporter/epub/index.js +142 -0
  31. package/src/exporter/epub/templates.js +140 -0
  32. package/src/exporter/epub/tools.js +96 -0
  33. package/src/exporter/html/citations.js +121 -0
  34. package/src/exporter/html/convert.js +813 -0
  35. package/src/exporter/html/index.js +192 -0
  36. package/src/exporter/html/templates.js +34 -0
  37. package/src/exporter/html/tools.js +50 -0
  38. package/src/exporter/jats/bibliography.js +183 -0
  39. package/src/exporter/jats/citations.js +109 -0
  40. package/src/exporter/jats/convert.js +871 -0
  41. package/src/exporter/jats/index.js +92 -0
  42. package/src/exporter/jats/templates.js +35 -0
  43. package/src/exporter/jats/text.js +72 -0
  44. package/src/exporter/latex/convert.js +934 -0
  45. package/src/exporter/latex/escape_latex.js +21 -0
  46. package/src/exporter/latex/index.js +74 -0
  47. package/src/exporter/latex/readme.js +22 -0
  48. package/src/exporter/native/shrink.js +132 -0
  49. package/src/exporter/odt/citations.js +101 -0
  50. package/src/exporter/odt/footnotes.js +147 -0
  51. package/src/exporter/odt/images.js +115 -0
  52. package/src/exporter/odt/index.js +156 -0
  53. package/src/exporter/odt/math.js +57 -0
  54. package/src/exporter/odt/metadata.js +251 -0
  55. package/src/exporter/odt/render.js +806 -0
  56. package/src/exporter/odt/richtext.js +865 -0
  57. package/src/exporter/odt/styles.js +387 -0
  58. package/src/exporter/odt/track.js +68 -0
  59. package/src/exporter/pandoc/citations.js +98 -0
  60. package/src/exporter/pandoc/convert.js +1017 -0
  61. package/src/exporter/pandoc/index.js +92 -0
  62. package/src/exporter/pandoc/readme.js +8 -0
  63. package/src/exporter/pandoc/tools.js +51 -0
  64. package/src/exporter/print/index.js +177 -0
  65. package/src/exporter/tools/doc_content.js +144 -0
  66. package/src/exporter/tools/file.js +9 -0
  67. package/src/exporter/tools/json.js +73 -0
  68. package/src/exporter/tools/svg.js +29 -0
  69. package/src/exporter/tools/xml.js +531 -0
  70. package/src/exporter/tools/xml_zip.js +95 -0
  71. package/src/exporter/tools/zip.js +90 -0
  72. package/src/exporter/tools/zotero_csl.js +93 -0
  73. package/src/importer/citations.js +129 -0
  74. package/src/importer/docx/citations.js +123 -0
  75. package/src/importer/docx/convert.js +1427 -0
  76. package/src/importer/docx/helpers.js +9 -0
  77. package/src/importer/docx/omml2mathml.js +1448 -0
  78. package/src/importer/docx/parse.js +735 -0
  79. package/src/importer/native/get_images.js +76 -0
  80. package/src/importer/native/update.js +29 -0
  81. package/src/importer/odt/citations.js +87 -0
  82. package/src/importer/odt/convert.js +1855 -0
  83. package/src/importer/pandoc/convert.js +884 -0
  84. package/src/importer/pandoc/helpers.js +84 -0
  85. package/src/importer/zip_analyzer.js +102 -0
  86. package/src/index.js +1 -0
  87. package/src/mathlive/opf_includes.js +24 -0
  88. package/src/schema/common/annotate.js +76 -0
  89. package/src/schema/common/base.js +118 -0
  90. package/src/schema/common/citation.js +62 -0
  91. package/src/schema/common/equation.js +31 -0
  92. package/src/schema/common/figure.js +190 -0
  93. package/src/schema/common/heading.js +43 -0
  94. package/src/schema/common/index.js +40 -0
  95. package/src/schema/common/list.js +95 -0
  96. package/src/schema/common/reference.js +100 -0
  97. package/src/schema/common/table.js +103 -0
  98. package/src/schema/common/track.js +190 -0
  99. package/src/schema/const.js +58 -0
  100. package/src/schema/convert.js +1272 -0
  101. package/src/schema/document/content.js +187 -0
  102. package/src/schema/document/index.js +117 -0
  103. package/src/schema/document/structure.js +452 -0
  104. package/src/schema/export.js +21 -0
  105. package/src/schema/footnotes.js +126 -0
  106. package/src/schema/footnotes_convert.js +31 -0
  107. package/src/schema/i18n.js +595 -0
  108. package/src/schema/index.js +5 -0
  109. package/src/schema/mini_json.js +61 -0
  110. package/src/schema/text.js +22 -0
@@ -0,0 +1,1017 @@
1
+ import {convertContributor, convertText} from "./tools.js"
2
+
3
+ export class PandocExporterConvert {
4
+ constructor(exporter, imageDB, bibDB, settings) {
5
+ this.exporter = exporter
6
+ this.settings = settings
7
+ this.imageDB = imageDB
8
+ this.bibDB = bibDB
9
+ this.imageIds = []
10
+ this.usedBibDB = {}
11
+
12
+ this.internalLinks = []
13
+ this.categoryCounter = {} // counters for each type of figure (figure/table/photo)
14
+
15
+ this.metaData = {
16
+ toc: []
17
+ }
18
+ }
19
+
20
+ init(doc) {
21
+ this.preWalkJson(doc)
22
+ const meta = {
23
+ lang: {
24
+ t: "MetaInlines",
25
+ c: [{t: "Str", c: this.settings.language.split("-")[0]}]
26
+ }
27
+ }
28
+ const json = {
29
+ "pandoc-api-version": [1, 23, 1],
30
+ meta,
31
+ blocks: this.convertContent(doc.content, meta)
32
+ }
33
+ const returnObject = {
34
+ json,
35
+ imageIds: this.imageIds,
36
+ usedBibDB: this.usedBibDB
37
+ }
38
+ return returnObject
39
+ }
40
+
41
+ // Find information for meta tags in header
42
+ preWalkJson(node) {
43
+ switch (node.type) {
44
+ case "heading1":
45
+ case "heading2":
46
+ case "heading3":
47
+ case "heading4":
48
+ case "heading5":
49
+ case "heading6": {
50
+ const level = Number.parseInt(node.type.slice(-1))
51
+ this.metaData.toc.push({
52
+ t: "Header",
53
+ c: [
54
+ level,
55
+ [node.attrs.id || "", [], []],
56
+ this.convertContent(node.content || [])
57
+ ]
58
+ })
59
+ break
60
+ }
61
+ default:
62
+ break
63
+ }
64
+ if (node.content) {
65
+ node.content.forEach(child => this.preWalkJson(child))
66
+ }
67
+ }
68
+
69
+ // Function to convert Fidus Writer content to Pandoc format
70
+ convertContent(
71
+ docContent,
72
+ meta,
73
+ options = {inFootnote: false, inCode: false}
74
+ ) {
75
+ const pandocContent = []
76
+ for (const node of docContent) {
77
+ switch (node.type) {
78
+ case "doc":
79
+ // We only handle doc children
80
+ break
81
+ case "blockquote": {
82
+ pandocContent.push({
83
+ t: "BlockQuote",
84
+ c: this.convertContent(node.content, meta, options)
85
+ })
86
+ break
87
+ }
88
+ case "bullet_list": {
89
+ const c = []
90
+ pandocContent.push({
91
+ t: "BulletList",
92
+ c
93
+ })
94
+ if (node.content) {
95
+ node.content.forEach(listItem =>
96
+ c.push(
97
+ this.convertContent(
98
+ listItem.content || [],
99
+ meta,
100
+ options
101
+ )
102
+ )
103
+ )
104
+ }
105
+ break
106
+ }
107
+ case "citation": {
108
+ if (options.inFootnote) {
109
+ // TODO: handle citations in footnotes
110
+ break
111
+ }
112
+ const cit = this.exporter.citations.pmCits.shift()
113
+
114
+ const pandocReferences = node.attrs.references
115
+ .map(reference => {
116
+ const bibDBEntry = this.bibDB.db[reference.id]
117
+ if (!bibDBEntry) {
118
+ // Not present in bibliography database, skip it.
119
+ return false
120
+ }
121
+ if (!this.usedBibDB[reference.id]) {
122
+ const citationKey =
123
+ this.createUniqueCitationKey(
124
+ bibDBEntry.entry_key
125
+ )
126
+ this.usedBibDB[reference.id] = Object.assign(
127
+ {},
128
+ bibDBEntry
129
+ )
130
+ this.usedBibDB[reference.id].entry_key =
131
+ citationKey
132
+ }
133
+
134
+ return {
135
+ citationId:
136
+ this.usedBibDB[reference.id].entry_key,
137
+ citationPrefix: convertText(
138
+ reference.prefix || ""
139
+ ),
140
+ citationSuffix: convertText(
141
+ reference.locator || ""
142
+ ),
143
+ citationMode: {
144
+ t:
145
+ node.attrs.format === "textcite"
146
+ ? "AuthorInText"
147
+ : "NormalCitation"
148
+ },
149
+ citationNoteNum: 1,
150
+ citationHash: 0
151
+ }
152
+ })
153
+ .filter(reference => reference)
154
+ if (!pandocReferences.length) {
155
+ break
156
+ }
157
+ const pandocRendering = this.convertContent(
158
+ cit.content,
159
+ meta,
160
+ options
161
+ )
162
+ const pandocElement = {
163
+ t: "Cite",
164
+ c: [pandocReferences, pandocRendering]
165
+ }
166
+ if (node.content) {
167
+ this.convertContent(
168
+ node.content,
169
+ meta,
170
+ options
171
+ ).forEach(el => pandocElement.c.push(el))
172
+ }
173
+ pandocContent.push(pandocElement)
174
+ break
175
+ }
176
+ case "code_block": {
177
+ options = Object.assign({}, options)
178
+ options.inCode = true
179
+ const classes = node.attrs.language
180
+ ? [node.attrs.language]
181
+ : []
182
+ const keyValuePairs = []
183
+
184
+ // Add caption if title is present
185
+ if (node.attrs.title) {
186
+ keyValuePairs.push(["caption", node.attrs.title])
187
+ }
188
+
189
+ // Add category as custom attribute for round-trip fidelity
190
+ if (node.attrs.category) {
191
+ keyValuePairs.push(["category", node.attrs.category])
192
+ }
193
+
194
+ // Use id if present, otherwise empty string
195
+ const id = node.attrs.id || ""
196
+ const attrs = [id, classes, keyValuePairs]
197
+
198
+ pandocContent.push({
199
+ t: "CodeBlock",
200
+ c: [
201
+ attrs,
202
+ this.convertContent(node.content, meta, options)
203
+ .map(item => {
204
+ if (item.t === "Str") {
205
+ return item.c
206
+ } else if (item.t === "Space") {
207
+ return " "
208
+ } else if (
209
+ item.t === "SoftBreak" ||
210
+ item.t === "LineBreak"
211
+ ) {
212
+ return "\n"
213
+ }
214
+ return ""
215
+ })
216
+ .join("")
217
+ ]
218
+ })
219
+ break
220
+ }
221
+ case "contributor":
222
+ // dealt with in contributors_part
223
+ break
224
+ case "contributors_part": {
225
+ if (!node.content || !node.content.length) {
226
+ break
227
+ }
228
+ if (node.attrs.metadata === "authors") {
229
+ if (!meta.author) {
230
+ meta.author = {t: "MetaList", c: []}
231
+ }
232
+ const convertedContributors = node.content
233
+ .map(contributor =>
234
+ convertContributor(contributor.attrs)
235
+ )
236
+ .filter(
237
+ convertedContributor => convertedContributor
238
+ )
239
+ convertedContributors.forEach(contributor =>
240
+ meta.author.c.push(contributor)
241
+ )
242
+ } else {
243
+ pandocContent.push({
244
+ t: "Div",
245
+ c: [
246
+ [
247
+ node.attrs.id || "",
248
+ [
249
+ "doc-part",
250
+ "doc-contributors",
251
+ node.attrs.id
252
+ ? `doc-${node.attrs.id}`
253
+ : "doc-div",
254
+ `doc-${node.attrs.metadata || "other"}`
255
+ ],
256
+ []
257
+ ],
258
+ [
259
+ {
260
+ t: "Para",
261
+ c: convertText(
262
+ node.content
263
+ .map(
264
+ contributor =>
265
+ `${contributor.attrs.firstname} ${contributor.attrs.lastname}, ${contributor.attrs.institution}, ${contributor.attrs.email}`
266
+ )
267
+ .join("; ")
268
+ )
269
+ }
270
+ ]
271
+ ]
272
+ })
273
+ }
274
+ break
275
+ }
276
+ case "cross_reference": {
277
+ // TODO: use real cross reference instead of link.
278
+ pandocContent.push({
279
+ t: "Link",
280
+ c: [
281
+ ["", ["reference"], []],
282
+ convertText(node.attrs.title || "MISSING TARGET"),
283
+ [`#${node.attrs.id || ""}`, ""]
284
+ ]
285
+ })
286
+ break
287
+ }
288
+ case "heading_part": {
289
+ if (!node.content || !node.content.length) {
290
+ break
291
+ }
292
+ if (node.attrs?.metadata === "subtitle" && !meta.subtitle) {
293
+ if (node.content?.length && node.content[0].content) {
294
+ meta.subtitle = {
295
+ t: "MetaInlines",
296
+ c: this.convertContent(
297
+ node.content[0].content,
298
+ meta,
299
+ options
300
+ )
301
+ }
302
+ }
303
+ } else {
304
+ const pandocElement = {
305
+ t: "Header",
306
+ c: [2, [node.attrs?.metadata || "", [], []]]
307
+ }
308
+ if (node.content) {
309
+ this.convertContent(
310
+ node.content,
311
+ meta,
312
+ options
313
+ ).forEach(el => pandocElement.c.push(el))
314
+ }
315
+ pandocContent.push({
316
+ t: "Div",
317
+ c: [
318
+ [
319
+ node.attrs.id || "",
320
+ [
321
+ "doc-part",
322
+ "doc-heading",
323
+ node.attrs.id
324
+ ? `doc-${node.attrs.id}`
325
+ : "doc-div",
326
+ `doc-${node.attrs.metadata || "other"}`
327
+ ],
328
+ []
329
+ ],
330
+ [pandocElement]
331
+ ]
332
+ })
333
+ }
334
+ break
335
+ }
336
+ case "equation": {
337
+ pandocContent.push({
338
+ t: "Span",
339
+ c: [
340
+ ["", ["equation"], []],
341
+ [
342
+ {
343
+ t: "Math",
344
+ c: [{t: "InlineMath"}, node.attrs.equation]
345
+ }
346
+ ]
347
+ ]
348
+ })
349
+ break
350
+ }
351
+ case "figure": {
352
+ const image =
353
+ node.content.find(node => node.type === "image")?.attrs
354
+ .image || false
355
+ const caption = node.attrs.caption
356
+ ? node.content.find(
357
+ node => node.type === "figure_caption"
358
+ )?.content || []
359
+ : []
360
+ const equation = node.content.find(
361
+ node => node.type === "figure_equation"
362
+ )?.attrs.equation
363
+ if (image !== false) {
364
+ this.imageIds.push(image)
365
+ const imageDBEntry = this.imageDB.db[image],
366
+ filePathName = imageDBEntry.image
367
+ const copyright = imageDBEntry.copyright
368
+ const imageFilename = filePathName.split("/").pop()
369
+ if (
370
+ node.attrs.category === "none" &&
371
+ imageFilename &&
372
+ !caption.length &&
373
+ (!copyright || !copyright.holder)
374
+ ) {
375
+ pandocContent.push({
376
+ t: "Plain",
377
+ c: [
378
+ {
379
+ t: "Image",
380
+ c: [
381
+ [
382
+ node.attrs.id || "",
383
+ [],
384
+ [
385
+ [
386
+ "data-width",
387
+ String(node.attrs.width)
388
+ ],
389
+ [
390
+ "width",
391
+ `${node.attrs.width}%`
392
+ ]
393
+ ]
394
+ ],
395
+ [],
396
+ [imageFilename, ""]
397
+ ]
398
+ }
399
+ ]
400
+ })
401
+ } else {
402
+ pandocContent.push({
403
+ t: "Figure",
404
+ c: [
405
+ [
406
+ node.attrs.id || "",
407
+ [
408
+ `aligned-${node.attrs.aligned}`,
409
+ `image-width-${node.attrs.width}`
410
+ ],
411
+ [
412
+ ["aligned", node.attrs.aligned],
413
+ [
414
+ "data-width",
415
+ String(node.attrs.width)
416
+ ],
417
+ ["width", `${node.attrs.width}%`],
418
+ ["category", node.attrs.category]
419
+ ]
420
+ ],
421
+ [
422
+ null,
423
+ caption.length
424
+ ? [
425
+ {
426
+ t: "Para",
427
+ c: this.convertContent(
428
+ caption,
429
+ meta,
430
+ options
431
+ )
432
+ }
433
+ ]
434
+ : []
435
+ ],
436
+ [
437
+ {
438
+ t: "Plain",
439
+ c: [
440
+ {
441
+ t: "Image",
442
+ c: [
443
+ [
444
+ "",
445
+ [],
446
+ [
447
+ [
448
+ "width",
449
+ `${node.attrs.width}%`
450
+ ]
451
+ ]
452
+ ],
453
+ [],
454
+ [imageFilename, ""]
455
+ ]
456
+ }
457
+ ]
458
+ }
459
+ ]
460
+ ]
461
+ })
462
+ }
463
+ } else if (equation) {
464
+ pandocContent.push({
465
+ t: "Figure",
466
+ c: [
467
+ [
468
+ node.attrs.id || "",
469
+ [
470
+ `aligned-${node.attrs.aligned}`,
471
+ `image-width-${node.attrs.width}`
472
+ ],
473
+ [
474
+ ["aligned", node.attrs.aligned],
475
+ [
476
+ "data-width",
477
+ String(node.attrs.width)
478
+ ],
479
+ ["width", `${node.attrs.width}%`],
480
+ ["category", node.attrs.category]
481
+ ]
482
+ ],
483
+ [
484
+ null,
485
+ caption.length
486
+ ? [
487
+ {
488
+ t: "Para",
489
+ c: this.convertContent(
490
+ caption,
491
+ meta,
492
+ options
493
+ )
494
+ }
495
+ ]
496
+ : []
497
+ ],
498
+ [
499
+ {
500
+ t: "Math",
501
+ c: [
502
+ {t: "DisplayMath"},
503
+ node.attrs.equation
504
+ ]
505
+ }
506
+ ]
507
+ ]
508
+ })
509
+ }
510
+ // TODO: figure attributes like copyright info etc.
511
+ break
512
+ }
513
+ case "figure_caption":
514
+ case "figure_equation":
515
+ // Dealt with in figure
516
+ break
517
+ case "footnote": {
518
+ options = Object.assign({}, options)
519
+ options.inFootnote = true
520
+ pandocContent.push({
521
+ t: "Note",
522
+ c: this.convertContent(
523
+ node.attrs.footnote,
524
+ meta,
525
+ options
526
+ )
527
+ })
528
+ break
529
+ }
530
+ case "footnotecontainer":
531
+ // Dealt with in footnote
532
+ break
533
+ case "hard_break":
534
+ pandocContent.push({t: "LineBreak"})
535
+ break
536
+ case "heading1":
537
+ case "heading2":
538
+ case "heading3":
539
+ case "heading4":
540
+ case "heading5":
541
+ case "heading6": {
542
+ const level = Number.parseInt(node.type.slice(-1))
543
+ pandocContent.push({
544
+ t: "Header",
545
+ c: [
546
+ level,
547
+ [node.attrs.id || "", [], []],
548
+ this.convertContent(
549
+ node.content || [],
550
+ meta,
551
+ options
552
+ )
553
+ ]
554
+ })
555
+ break
556
+ }
557
+ case "image":
558
+ // Handled by figure
559
+ break
560
+ case "list_item":
561
+ // handled by ordered_list and bullet_list
562
+ break
563
+ case "ordered_list": {
564
+ const c = []
565
+ pandocContent.push({
566
+ t: "OrderedList",
567
+ c: [
568
+ [
569
+ node.attrs?.order || 1,
570
+ {t: "DefaultStyle"},
571
+ {t: "DefaultDelim"}
572
+ ], // list attributes
573
+ c
574
+ ]
575
+ })
576
+
577
+ if (node.content) {
578
+ node.content.forEach(listItem =>
579
+ c.push(
580
+ this.convertContent(
581
+ listItem.content || [],
582
+ meta,
583
+ options
584
+ )
585
+ )
586
+ )
587
+ }
588
+ break
589
+ }
590
+ case "paragraph": {
591
+ pandocContent.push({
592
+ t: "Para",
593
+ c: node.content
594
+ ? this.convertContent(node.content, meta, options)
595
+ : []
596
+ })
597
+ break
598
+ }
599
+ case "richtext_part": {
600
+ if (!node.content || !node.content.length) {
601
+ break
602
+ }
603
+ if (node.attrs?.metadata === "abstract" && !meta.abstract) {
604
+ meta.abstract = {
605
+ t: "MetaBlocks",
606
+ c: this.convertContent(node.content, meta, options)
607
+ }
608
+ } else {
609
+ pandocContent.push({
610
+ t: "Div",
611
+ c: [
612
+ [
613
+ node.attrs.id || "",
614
+ [
615
+ "doc-part",
616
+ "doc-richtext",
617
+ node.attrs.id
618
+ ? `doc-${node.attrs.id}`
619
+ : "doc-div",
620
+ `doc-${node.attrs.metadata || "other"}`
621
+ ],
622
+ []
623
+ ],
624
+ this.convertContent(node.content, meta, options)
625
+ ]
626
+ })
627
+ }
628
+ break
629
+ }
630
+ case "separator_part":
631
+ pandocContent.push({
632
+ t: "HorizontalRule",
633
+ c: [
634
+ [
635
+ node.attrs.id || "",
636
+ [
637
+ "doc-part",
638
+ "doc-separator",
639
+ node.attrs.id
640
+ ? `doc-${node.attrs.id}`
641
+ : "doc-hr",
642
+ `doc-${node.attrs.metadata || "other"}`
643
+ ],
644
+ []
645
+ ],
646
+ []
647
+ ]
648
+ })
649
+ break
650
+ case "tag":
651
+ // Handled by tags_part
652
+ break
653
+ case "tags_part": {
654
+ if (!node.content || !node.content.length) {
655
+ break
656
+ }
657
+ pandocContent.push({
658
+ t: "Div",
659
+ c: [
660
+ [
661
+ node.attrs.id || "",
662
+ [
663
+ "doc-part",
664
+ "doc-tags",
665
+ node.attrs.id
666
+ ? `doc-${node.attrs.id}`
667
+ : "doc-div",
668
+ `doc-${node.attrs.metadata || "other"}`
669
+ ],
670
+ []
671
+ ],
672
+ [
673
+ {
674
+ t: "Para",
675
+ c: convertText(
676
+ node.content
677
+ .map(tag => tag.attrs.tag)
678
+ .join("; ")
679
+ )
680
+ }
681
+ ]
682
+ ]
683
+ })
684
+ break
685
+ }
686
+ case "table": {
687
+ // Tables seem to have this structure in pandoc json:
688
+ // If table has no rows with content, skip.
689
+ const tableBodyNode = node.content.find(
690
+ childNode =>
691
+ childNode.type === "table_body" &&
692
+ childNode.content &&
693
+ childNode.content.length
694
+ )
695
+ const tableFirstRow = tableBodyNode
696
+ ? tableBodyNode.content.find(
697
+ childNode =>
698
+ childNode.type === "table_row" &&
699
+ childNode.content &&
700
+ childNode.content.length
701
+ )
702
+ : false
703
+ if (!tableFirstRow) {
704
+ break
705
+ }
706
+
707
+ const c = []
708
+ pandocContent.push({
709
+ t: "Table",
710
+ c
711
+ })
712
+ // child 0: attributes of the table.
713
+ c.push([
714
+ node.attrs.id || "",
715
+ [
716
+ `table-${node.attrs.width}`,
717
+ `table-${node.attrs.aligned}`,
718
+ `table-${node.attrs.layout}`
719
+ ],
720
+ [
721
+ ["data-width", String(node.attrs.width)],
722
+ ["width", `${node.attrs.width}%`],
723
+ ["aligned", node.attrs.aligned],
724
+ ["layout", node.attrs.layout],
725
+ ["category", node.attrs.category]
726
+ ]
727
+ ])
728
+ // child 1: table caption
729
+ const tableCaptionNode = node.content.find(
730
+ childNode =>
731
+ childNode.type === "table_caption" &&
732
+ childNode.content &&
733
+ childNode.content.length
734
+ )
735
+ if (tableCaptionNode) {
736
+ c.push([
737
+ null,
738
+ [
739
+ {
740
+ t: "Plain",
741
+ c: this.convertContent(
742
+ tableCaptionNode.content,
743
+ meta,
744
+ options
745
+ )
746
+ }
747
+ ]
748
+ ])
749
+ } else {
750
+ c.push([null, []])
751
+ }
752
+ // child 2: settings for each column
753
+ c.push(
754
+ tableFirstRow.content.map(_column => [
755
+ {t: "AlignDefault"},
756
+ {t: "ColWidthDefault"}
757
+ ])
758
+ )
759
+ // child 3: ?
760
+ c.push([["", [], []], []])
761
+ // child 4: Each child represents one table row
762
+ const tableHead = []
763
+ const tableBody = []
764
+ c.push([[["", [], []], 0, tableHead, tableBody]])
765
+ let currentTablePart = tableHead
766
+
767
+ this.convertContent(
768
+ tableBodyNode.content,
769
+ meta,
770
+ options
771
+ ).forEach((row, index) => {
772
+ if (
773
+ currentTablePart === tableHead &&
774
+ tableBodyNode.content[index].content?.find(
775
+ node => node.type === "table_cell"
776
+ )
777
+ ) {
778
+ // If at least one regular table cell is found in the row, we assume the table header hs finished.
779
+ currentTablePart = tableBody
780
+ }
781
+ currentTablePart.push(row)
782
+ })
783
+ // last child: Unclear meaning
784
+ c.push([["", [], []], []])
785
+ // Don't process content as we do that by calling convertContent above already.
786
+ //processContent = false
787
+ break
788
+ }
789
+ case "table_body":
790
+ case "table_caption":
791
+ // Handled directly through table tag.
792
+ break
793
+ case "table_cell":
794
+ case "table_header": {
795
+ if (node.content) {
796
+ pandocContent.push([
797
+ ["", [], []],
798
+ {t: "AlignDefault"},
799
+ node.attrs?.rowspan || 1,
800
+ node.attrs?.colspan || 1,
801
+ this.convertContent(node.content, meta, options)
802
+ ])
803
+ }
804
+ break
805
+ }
806
+ case "table_part":
807
+ pandocContent.push({
808
+ t: "Div",
809
+ c: [
810
+ [
811
+ node.attrs.id || "",
812
+ [
813
+ "doc-part",
814
+ "doc-table",
815
+ node.attrs.id
816
+ ? `doc-${node.attrs.id}`
817
+ : "doc-div",
818
+ `doc-${node.attrs.metadata || "other"}`
819
+ ],
820
+ []
821
+ ],
822
+ this.convertContent(node.content, meta, options)
823
+ ]
824
+ })
825
+ break
826
+ case "table_of_contents": {
827
+ pandocContent.push({
828
+ t: "Div",
829
+ c: [
830
+ [
831
+ node.attrs.id || "",
832
+ [
833
+ "doc-part",
834
+ "doc-table-of-contents",
835
+ node.attrs.id
836
+ ? `doc-${node.attrs.id}`
837
+ : "doc-div",
838
+ `doc-${node.attrs.metadata || "other"}`
839
+ ],
840
+ []
841
+ ],
842
+ [
843
+ {
844
+ t: "Header",
845
+ c: [
846
+ 1,
847
+ ["", ["toc"], []],
848
+ convertText(node.attrs.title)
849
+ ]
850
+ }
851
+ ].concat(this.metaData.toc)
852
+ ]
853
+ })
854
+ break
855
+ }
856
+ case "table_row": {
857
+ pandocContent.push([
858
+ ["", [], []],
859
+ this.convertContent(node.content, meta, options)
860
+ ])
861
+ break
862
+ }
863
+ case "text": {
864
+ if (node.text) {
865
+ let containerContent = pandocContent
866
+ let strong,
867
+ em,
868
+ underline,
869
+ hyperlink,
870
+ anchor,
871
+ sup,
872
+ sub,
873
+ code
874
+ if (node.marks) {
875
+ strong = node.marks.find(
876
+ mark => mark.type === "strong"
877
+ )
878
+ em = node.marks.find(mark => mark.type === "em")
879
+ underline = node.marks.find(
880
+ mark => mark.type === "underline"
881
+ )
882
+ hyperlink = node.marks.find(
883
+ mark => mark.type === "link"
884
+ )
885
+ anchor = node.marks.find(
886
+ mark => mark.type === "anchor"
887
+ )
888
+ sup = node.marks.find(mark => mark.type === "sup")
889
+ sub = node.marks.find(mark => mark.type === "sub")
890
+ code = node.marks.find(mark => mark.type === "code")
891
+ }
892
+ if (em) {
893
+ const c = []
894
+ containerContent.push({
895
+ t: "Emph",
896
+ c
897
+ })
898
+ containerContent = c
899
+ }
900
+ if (strong) {
901
+ const c = []
902
+ containerContent.push({
903
+ t: "Strong",
904
+ c
905
+ })
906
+ containerContent = c
907
+ }
908
+ if (underline) {
909
+ const c = []
910
+ containerContent.push({
911
+ t: "Underline",
912
+ c
913
+ })
914
+ containerContent = c
915
+ }
916
+ if (sup) {
917
+ const c = []
918
+ containerContent.push({
919
+ t: "Superscript",
920
+ c
921
+ })
922
+ containerContent = c
923
+ }
924
+ if (sub) {
925
+ const c = []
926
+ containerContent.push({
927
+ t: "Subscript",
928
+ c
929
+ })
930
+ containerContent = c
931
+ }
932
+ if (code && !options.inCode) {
933
+ containerContent.push({
934
+ t: "Code",
935
+ c: [["", [], []], node.text]
936
+ })
937
+ break
938
+ }
939
+ if (hyperlink) {
940
+ const c = []
941
+ containerContent.push({
942
+ t: "Link",
943
+ c: [["", [], []], c, [hyperlink.attrs.href, ""]]
944
+ })
945
+ containerContent = c
946
+ }
947
+ if (anchor) {
948
+ const c = []
949
+ containerContent.push({
950
+ t: "Span",
951
+ c: [[anchor.attrs.id, [], []], c]
952
+ })
953
+ containerContent = c
954
+ }
955
+
956
+ if (options.inCode) {
957
+ containerContent.push({
958
+ t: "Code",
959
+ c: [["", [], []], node.text]
960
+ })
961
+ } else {
962
+ containerContent.push(
963
+ ...convertText(node.text || "")
964
+ )
965
+ }
966
+ }
967
+ break
968
+ }
969
+ case "title": {
970
+ if (!node.content || !node.content.length) {
971
+ break
972
+ }
973
+ if (!meta.title) {
974
+ meta.title = {
975
+ t: "MetaInlines",
976
+ c: this.convertContent(node.content, meta, options)
977
+ }
978
+ } else {
979
+ const pandocElement = {
980
+ t: "Header",
981
+ c: [1, ["title", [], []]]
982
+ }
983
+ if (node.content) {
984
+ this.convertContent(
985
+ node.content,
986
+ meta,
987
+ options
988
+ ).forEach(el => pandocElement.c.push(el))
989
+ }
990
+ pandocContent.push(pandocElement)
991
+ }
992
+ break
993
+ }
994
+ default: {
995
+ console.warn(`Not handled: ${node.type}`, {node})
996
+ break
997
+ }
998
+ }
999
+ }
1000
+ return pandocContent
1001
+ }
1002
+
1003
+ // The database doesn't ensure that citation keys are unique.
1004
+ // So here we need to make sure that the same key is not used twice in one
1005
+ // document.
1006
+ createUniqueCitationKey(suggestedKey) {
1007
+ const usedKeys = Object.keys(this.usedBibDB).map(key => {
1008
+ return this.usedBibDB[key].entry_key
1009
+ })
1010
+ if (usedKeys.includes(suggestedKey)) {
1011
+ suggestedKey += "X"
1012
+ return this.createUniqueCitationKey(suggestedKey)
1013
+ } else {
1014
+ return suggestedKey
1015
+ }
1016
+ }
1017
+ }