@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,112 @@
1
+ const DEFAULT_TABLENORMAL_XML = `
2
+ <w:style w:type="table" w:default="1" w:styleId="TableNormal">
3
+ <w:name w:val="Normal Table"/>
4
+ <w:uiPriority w:val="99"/>
5
+ <w:semiHidden/>
6
+ <w:unhideWhenUsed/>
7
+ <w:tblPr>
8
+ <w:tblInd w:w="0" w:type="dxa"/>
9
+ <w:tblCellMar>
10
+ <w:top w:w="0" w:type="dxa"/>
11
+ <w:left w:w="108" w:type="dxa"/>
12
+ <w:bottom w:w="0" w:type="dxa"/>
13
+ <w:right w:w="108" w:type="dxa"/>
14
+ </w:tblCellMar>
15
+ </w:tblPr>
16
+ </w:style>
17
+ `
18
+
19
+ const DEFAULT_TABLEGRID_XML = tableNormalStyle => `
20
+ <w:style w:type="table" w:styleId="TableGrid">
21
+ <w:name w:val="Table Grid"/>
22
+ <w:basedOn w:val="${tableNormalStyle}"/>
23
+ <w:uiPriority w:val="39"/>
24
+ <w:pPr>
25
+ <w:spacing w:after="0" w:line="240" w:lineRule="auto"/>
26
+ </w:pPr>
27
+ <w:tblPr>
28
+ <w:hMerge/>
29
+ <w:vMerge/>
30
+ <w:tblBorders>
31
+ <w:top w:val="single" w:sz="4" w:space="0" w:color="auto"/>
32
+ <w:left w:val="single" w:sz="4" w:space="0" w:color="auto"/>
33
+ <w:bottom w:val="single" w:sz="4" w:space="0" w:color="auto"/>
34
+ <w:right w:val="single" w:sz="4" w:space="0" w:color="auto"/>
35
+ <w:insideH w:val="single" w:sz="4" w:space="0" w:color="auto"/>
36
+ <w:insideV w:val="single" w:sz="4" w:space="0" w:color="auto"/>
37
+
38
+ </w:tblBorders>
39
+ </w:tblPr>
40
+
41
+
42
+ </w:style>
43
+ `
44
+
45
+ export class DOCXExporterTables {
46
+ constructor(xml) {
47
+ this.xml = xml
48
+ this.sideMargins = false
49
+ this.tableGridStyle = false
50
+ this.tableNormalStyle = false
51
+ this.styleXML = false
52
+ this.styleFilePath = "word/styles.xml"
53
+ }
54
+
55
+ init() {
56
+ return this.xml.getXml(this.styleFilePath).then(styleXML => {
57
+ this.styleXML = styleXML
58
+ return Promise.resolve()
59
+ })
60
+ }
61
+
62
+ addTableNormalStyle() {
63
+ if (this.tableNormalStyle) {
64
+ // already added
65
+ return
66
+ }
67
+ const tableNormalEl = this.styleXML.query("w:style", {
68
+ "w:type": "table",
69
+ "w:default": "1"
70
+ })
71
+ if (tableNormalEl) {
72
+ this.tableNormalStyle = tableNormalEl.getAttribute("w:styleId")
73
+ } else {
74
+ const stylesEl = this.styleXML.query("w:styles")
75
+ stylesEl.appendXML(DEFAULT_TABLENORMAL_XML)
76
+ this.tableNormalStyle = "TableNormal"
77
+ }
78
+ }
79
+
80
+ addTableGridStyle() {
81
+ if (this.tableGridStyle) {
82
+ // already added
83
+ return
84
+ }
85
+ this.addTableNormalStyle()
86
+ const tableGridEl = this.styleXML.query("w:style", {
87
+ "w:type": "table",
88
+ "w:customStyle": "1"
89
+ })
90
+ if (tableGridEl) {
91
+ this.tableGridStyle = tableGridEl.getAttribute("w:styleId")
92
+ } else {
93
+ const stylesEl = this.styleXML.query("w:styles")
94
+ stylesEl.appendXML(DEFAULT_TABLEGRID_XML(this.tableNormalStyle))
95
+ this.tableGridStyle = "TableGrid"
96
+ }
97
+ }
98
+
99
+ getSideMargins() {
100
+ if (!this.sideMargins) {
101
+ const marginsEl = this.styleXML.query("w:style", {
102
+ "w:styleId": this.tableGridStyle
103
+ })
104
+ const leftEl = marginsEl.query("w:left")
105
+ const rightEl = marginsEl.query("w:right")
106
+ const left = Number.parseInt(leftEl.getAttribute("w:w"))
107
+ const right = Number.parseInt(rightEl.getAttribute("w:w"))
108
+ this.sideMargins = (left + right) * 635
109
+ }
110
+ return this.sideMargins
111
+ }
112
+ }
@@ -0,0 +1,50 @@
1
+ import {descendantNodes} from "../tools/doc_content.js"
2
+
3
+ export const moveFootnoteComments = topNode => {
4
+ // DOCX doesn't support comments in footnotes. So we copy all comment marks from footnote
5
+ // to parent node.
6
+ descendantNodes(topNode).forEach(node => {
7
+ if (node.type === "footnote") {
8
+ descendantNodes({
9
+ type: "footnotecontainer",
10
+ content: node.attrs.footnote
11
+ }).forEach(fnNode => {
12
+ if (fnNode.marks) {
13
+ fnNode.marks
14
+ .filter(mark => mark.type === "comment")
15
+ .forEach(mark => {
16
+ if (!node.marks) {
17
+ node.marks = []
18
+ }
19
+ node.marks.push(mark)
20
+ })
21
+ }
22
+ })
23
+ }
24
+ })
25
+
26
+ return topNode
27
+ }
28
+
29
+ export const translateBlockType = blockType => {
30
+ switch (blockType) {
31
+ case "heading1":
32
+ return "Heading1"
33
+ case "heading2":
34
+ return "Heading2"
35
+ case "heading3":
36
+ return "Heading3"
37
+ case "heading4":
38
+ return "Heading4"
39
+ case "heading5":
40
+ return "Heading5"
41
+ case "heading6":
42
+ return "Heading6"
43
+ case "code_block":
44
+ return "Code"
45
+ case "blockquote":
46
+ return "Quote"
47
+ default:
48
+ return "Normal"
49
+ }
50
+ }
@@ -0,0 +1,142 @@
1
+ import pretty from "pretty"
2
+ import {HTMLExporter} from "../html/index.js"
3
+
4
+ import {
5
+ containerTemplate,
6
+ navTemplate,
7
+ ncxTemplate,
8
+ opfTemplate
9
+ } from "./templates.js"
10
+ import {
11
+ buildHierarchy,
12
+ getFontMimeType,
13
+ getImageMimeType,
14
+ getTimestamp
15
+ } from "./tools.js"
16
+
17
+ export class EpubExporter extends HTMLExporter {
18
+ constructor(doc, bibDB, imageDB, csl, updated, documentStyles) {
19
+ super(doc, bibDB, imageDB, csl, updated, documentStyles, {
20
+ xhtml: true,
21
+ epub: true
22
+ })
23
+ // Overriden properties
24
+ this.documentFileName = "document.xhtml"
25
+ this.fileEnding = "epub"
26
+ this.mimeType = "application/epub+zip"
27
+ }
28
+
29
+ createZip() {
30
+ this.prefixFiles()
31
+ this.createEPUBFiles()
32
+ return super.createZip()
33
+ }
34
+
35
+ prefixFiles() {
36
+ // prefix all files with "EPUB/"
37
+ this.textFiles = this.textFiles.map(file =>
38
+ Object.assign({}, file, {filename: `EPUB/${file.filename}`})
39
+ )
40
+ this.httpFiles = this.httpFiles.map(file =>
41
+ Object.assign({}, file, {filename: `EPUB/${file.filename}`})
42
+ )
43
+ this.includeZips = this.includeZips.map(file =>
44
+ Object.assign({}, file, {directory: `EPUB/${file.directory}`})
45
+ )
46
+ }
47
+
48
+ createEPUBFiles() {
49
+ // Generate the required EPUB-specific files using the converted content
50
+ this.textFiles.push(
51
+ {
52
+ filename: "META-INF/container.xml",
53
+ contents: pretty(containerTemplate(), {ocd: true})
54
+ },
55
+ {
56
+ filename: "EPUB/document.opf",
57
+ contents: pretty(this.createOPF(), {ocd: true})
58
+ },
59
+ {
60
+ filename: "EPUB/document.ncx",
61
+ contents: pretty(this.createNCX(), {ocd: true})
62
+ },
63
+ {
64
+ filename: "EPUB/document-nav.xhtml",
65
+ contents: pretty(this.createNav(), {ocd: true})
66
+ }
67
+ )
68
+ }
69
+
70
+ createOPF() {
71
+ const timestamp = getTimestamp(this.updated)
72
+ const images = this.httpFiles
73
+ .map(file =>
74
+ Object.assign({mimeType: getImageMimeType(file.filename)}, file)
75
+ )
76
+ .filter(image => image.mimeType)
77
+
78
+ const fontFiles = this.httpFiles
79
+ .map(file =>
80
+ Object.assign({mimeType: getFontMimeType(file.filename)}, file)
81
+ )
82
+ .filter(file => file.mimeType)
83
+
84
+ const styleSheets = this.textFiles.filter(file =>
85
+ file.filename.endsWith(".css")
86
+ )
87
+
88
+ // Extract authors and keywords from metaData
89
+ const authors = this.converter.metaData.authors.map(
90
+ ({attrs: author}) => {
91
+ if (author.firstname || author.lastname) {
92
+ const nameParts = []
93
+ if (author.firstname) {
94
+ nameParts.push(author.firstname)
95
+ }
96
+ if (author.lastname) {
97
+ nameParts.push(author.lastname)
98
+ }
99
+ return nameParts.join(" ")
100
+ } else if (author.institution) {
101
+ return author.institution
102
+ }
103
+ }
104
+ )
105
+ return opfTemplate({
106
+ language: this.lang,
107
+ title: this.docTitle,
108
+ authors,
109
+ keywords: this.converter.metaData.keywords,
110
+ idType: "fidus",
111
+ id: this.doc.id,
112
+ date: timestamp.slice(0, 10),
113
+ modified: timestamp,
114
+ styleSheets,
115
+ math: this.converter.features.math,
116
+ images,
117
+ fontFiles,
118
+ copyright: this.doc.settings.copyright
119
+ })
120
+ }
121
+
122
+ createNCX() {
123
+ return ncxTemplate({
124
+ shortLang: this.shortLang,
125
+ title: this.docTitle,
126
+ idType: "fidus",
127
+ id: this.doc.id,
128
+ toc: buildHierarchy(this.converter.metaData.toc)
129
+ })
130
+ }
131
+
132
+ createNav() {
133
+ const styleSheets = this.textFiles.filter(file =>
134
+ file.filename.endsWith(".css")
135
+ )
136
+ return navTemplate({
137
+ shortLang: this.shortLang,
138
+ toc: buildHierarchy(this.converter.metaData.toc),
139
+ styleSheets
140
+ })
141
+ }
142
+ }
@@ -0,0 +1,140 @@
1
+ import {escapeText} from "../../common/index.js"
2
+ import {mathliveOpfIncludes} from "../../mathlive/opf_includes.js"
3
+
4
+ /** A template for the OPF file of an epub. */
5
+ export const opfTemplate = ({
6
+ id,
7
+ idType,
8
+ title,
9
+ language,
10
+ authors,
11
+ keywords,
12
+ date,
13
+ modified,
14
+ images,
15
+ fontFiles,
16
+ styleSheets,
17
+ math,
18
+ copyright
19
+ }) =>
20
+ `<?xml version="1.0" encoding="UTF-8"?>
21
+ <package xmlns="http://www.idpf.org/2007/opf" version="3.0" unique-identifier="${idType}" xml:lang="${language}" prefix="cc: http://creativecommons.org/ns#">
22
+ <metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
23
+ <dc:identifier id="${idType}">${id}</dc:identifier>
24
+ <dc:title>${escapeText(title)}</dc:title>
25
+ ${authors
26
+ .map(author => `\t\t<dc:creator>${escapeText(author)}</dc:creator>\n`)
27
+ .join("")}${keywords
28
+ .map(keyword => `\t\t<dc:subject>${escapeText(keyword)}</dc:subject>\n`)
29
+ .join("")}
30
+ <dc:language>${language}</dc:language>
31
+ <dc:date>${date}</dc:date>
32
+ ${copyright && copyright.holder ? `<dc:rights>© ${copyright.year ? copyright.year : new Date().getFullYear()} ${escapeText(copyright.holder)}</dc:rights>` : ""}
33
+ <meta property="dcterms:modified">${modified}</meta>
34
+ </metadata>
35
+ <manifest>
36
+ <item id="t1" href="document.xhtml" media-type="application/xhtml+xml" />
37
+ <item id="nav" href="document-nav.xhtml" properties="nav" media-type="application/xhtml+xml" />
38
+ ${images
39
+ .map(
40
+ (image, index) =>
41
+ `\t\t\t<item ${
42
+ image.coverImage
43
+ ? 'id="cover-image" properties="cover-image"'
44
+ : `id="img${index}"`
45
+ } href="${image.filename}" media-type="${image.mimeType}" />\n`
46
+ )
47
+ .join("")}${fontFiles
48
+ .map(
49
+ (fontFile, index) =>
50
+ `\t\t\t<item ${`id="font${index}"`} href="${
51
+ fontFile.filename
52
+ }" media-type="${fontFile.mimeType}" />\n`
53
+ )
54
+ .join("")}${styleSheets
55
+ .map(
56
+ (sheet, index) =>
57
+ `\t\t\t<item id="css${index}" href="${sheet.filename}" media-type="text/css" />\n`
58
+ )
59
+ .join("")}${math ? mathliveOpfIncludes : ""}
60
+ <!-- ncx included for 2.0 reading system compatibility: -->
61
+ <item id="ncx" href="document.ncx" media-type="application/x-dtbncx+xml" />
62
+ </manifest>
63
+ <spine toc="ncx">
64
+ <itemref idref="t1" />
65
+ </spine>
66
+ </package>`
67
+
68
+ /** A template for the contianer XML of an epub file. */
69
+ export const containerTemplate = () =>
70
+ `<?xml version="1.0" encoding="UTF-8"?>
71
+ <container xmlns="urn:oasis:names:tc:opendocument:xmlns:container" version="1.0">
72
+ <rootfiles>
73
+ <rootfile full-path="EPUB/document.opf" media-type="application/oebps-package+xml"/>
74
+ </rootfiles>
75
+ </container>`
76
+
77
+ /** A template of the NCX file of an epub. */
78
+ export const ncxTemplate = ({shortLang, idType, id, title, toc}) =>
79
+ `<?xml version="1.0" encoding="UTF-8"?>
80
+ <ncx xmlns:ncx="http://www.daisy.org/z3986/2005/ncx/" xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1" xml:lang="${shortLang}">
81
+ <head>
82
+ <meta name="dtb:${idType}" content="${id}" />
83
+ </head>
84
+ <docTitle>
85
+ <text>${escapeText(title)}</text>
86
+ </docTitle>
87
+ <navMap>
88
+ <!-- 2.01 NCX: playOrder is optional -->
89
+ ${toc.map(item => ncxItemTemplate({item})).join("")}
90
+ </navMap>
91
+ </ncx>`
92
+
93
+ /** A template for each list item in the navMap of an epub's NCX file. */
94
+ export const ncxItemTemplate = ({item}) =>
95
+ ` <navPoint id="t${item.docNum ? `${item.id}-${item.docNum}` : item.id}">
96
+ <navLabel><text>${escapeText(item.title)}</text></navLabel>
97
+ <content src="${item.link ? item.link : item.docNum ? `document-${item.docNum}.xhtml#${item.id}` : `document.xhtml#${item.id}`}"/>
98
+ ${item.children?.map(item => ncxItemTemplate({item})).join("") || ""}
99
+ </navPoint>\n`
100
+
101
+ /** A template for each item in an epub's navigation document. */
102
+ const navItemTemplate = ({item}) =>
103
+ `\t\t\t\t<li><a href="${
104
+ item.link
105
+ ? item.link
106
+ : item.docNum
107
+ ? `document-${item.docNum}.xhtml#${item.id}`
108
+ : `document.xhtml#${item.id}`
109
+ }">${escapeText(item.title)}</a>
110
+ ${
111
+ item.children.length
112
+ ? `<ol>
113
+ ${item.children.map(item => navItemTemplate({item})).join("")}
114
+ </ol>`
115
+ : ""
116
+ }
117
+ </li>`
118
+
119
+ /** A template for an epub's navigation document. */
120
+ export const navTemplate = ({shortLang, toc, styleSheets}) =>
121
+ `<?xml version="1.0" encoding="UTF-8"?>
122
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="${shortLang}" lang="${shortLang}" xmlns:epub="http://www.idpf.org/2007/ops">
123
+ <head>
124
+ <meta charset="utf-8" />
125
+ <title>Navigation</title>
126
+ ${styleSheets
127
+ .map(
128
+ sheet =>
129
+ `<link rel="stylesheet" type="text/css" href="${sheet.filename}" />\n`
130
+ )
131
+ .join("")}
132
+ </head>
133
+ <body class="epub navigation">
134
+ <nav epub:type="toc" id="toc">
135
+ <ol>
136
+ ${toc.map(item => navItemTemplate({item})).join("")}
137
+ </ol>
138
+ </nav>
139
+ </body>
140
+ </html>`
@@ -0,0 +1,96 @@
1
+ export function getTimestamp(date) {
2
+ let second = date.getUTCSeconds()
3
+ let minute = date.getUTCMinutes()
4
+ let hour = date.getUTCHours()
5
+ let day = date.getUTCDate()
6
+ let month = date.getUTCMonth() + 1 //January is 0!
7
+ const year = date.getUTCFullYear()
8
+
9
+ if (second < 10) {
10
+ second = "0" + second
11
+ }
12
+ if (minute < 10) {
13
+ minute = "0" + minute
14
+ }
15
+ if (hour < 10) {
16
+ hour = "0" + hour
17
+ }
18
+ if (day < 10) {
19
+ day = "0" + day
20
+ }
21
+ if (month < 10) {
22
+ month = "0" + month
23
+ }
24
+
25
+ return `${year}-${month}-${day}T${hour}:${minute}:${second}Z`
26
+ }
27
+
28
+ export function getFontMimeType(filename) {
29
+ // Define a mapping of font file extensions to MIME types
30
+ const fontMimeTypes = {
31
+ ttf: "font/ttf",
32
+ otf: "font/otf",
33
+ woff: "font/woff",
34
+ woff2: "font/woff2",
35
+ eot: "application/vnd.ms-fontobject"
36
+ }
37
+
38
+ // Extract the file extension from the filename
39
+ const extension = filename.split(".").pop().toLowerCase()
40
+
41
+ // Check if the extension matches a known font type and return the MIME type
42
+ return fontMimeTypes[extension] || null // Return null if it's not a font file
43
+ }
44
+
45
+ export function getImageMimeType(filename) {
46
+ // Define a mapping of image file extensions to MIME types
47
+ const imageMimeTypes = {
48
+ jpg: "image/jpeg",
49
+ jpeg: "image/jpeg",
50
+ png: "image/png",
51
+ gif: "image/gif",
52
+ bmp: "image/bmp",
53
+ tiff: "image/tiff",
54
+ webp: "image/webp",
55
+ svg: "image/svg+xml",
56
+ ico: "image/vnd.microsoft.icon",
57
+ avif: "image/avif"
58
+ }
59
+
60
+ // Extract the file extension from the filename
61
+ const extension = filename.split(".").pop().toLowerCase()
62
+
63
+ // Check if the extension matches a known image type and return the MIME type
64
+ return imageMimeTypes[extension] || null // Return null if it's not an image file
65
+ }
66
+
67
+ export function buildHierarchy(flatList) {
68
+ const hierarchy = []
69
+ const levelMap = {}
70
+
71
+ flatList.forEach(item => {
72
+ // Ensure there's an array for the current level in the map
73
+ levelMap[item.level] = levelMap[item.level] || []
74
+
75
+ // Add the current item to its level in the map
76
+ levelMap[item.level].push({...item, children: []})
77
+
78
+ if (item.level === 0) {
79
+ // Top-level items are added directly to the hierarchy
80
+ hierarchy.push(
81
+ levelMap[item.level][levelMap[item.level].length - 1]
82
+ )
83
+ } else {
84
+ // Non-top-level items are added as children of the last item at the previous level
85
+ const parentLevel = levelMap[item.level - 1]
86
+ if (parentLevel) {
87
+ const parent = parentLevel[parentLevel.length - 1]
88
+ parent.children.push(
89
+ levelMap[item.level][levelMap[item.level].length - 1]
90
+ )
91
+ }
92
+ }
93
+ })
94
+
95
+ return hierarchy
96
+ }
@@ -0,0 +1,121 @@
1
+ import {FormatCitations} from "../../citations/format.js"
2
+ import {escapeText} from "../../common/index.js"
3
+ import {BIBLIOGRAPHY_HEADERS} from "../../schema/i18n.js"
4
+
5
+ export class HTMLExporterCitations {
6
+ constructor(docSettings, bibDB, csl) {
7
+ this.docSettings = docSettings
8
+ this.bibDB = bibDB
9
+ this.csl = csl
10
+
11
+ this.citationTexts = []
12
+ this.citFm = false
13
+ this.bibHTML = ""
14
+ this.bibCSS = ""
15
+ this.htmlIdConvert = {}
16
+ }
17
+
18
+ async init(citInfos) {
19
+ this.citInfos = citInfos
20
+ if (!citInfos.length) {
21
+ return this.getOutput()
22
+ }
23
+ await this.formatCitations()
24
+ return this.getOutput()
25
+ }
26
+
27
+ getOutput() {
28
+ return {
29
+ type: this.citFm ? this.citFm.citationType : "",
30
+ bibCSS: this.bibCSS,
31
+ bibHTML: this.bibHTML,
32
+ citationTexts: this.citationTexts
33
+ }
34
+ }
35
+
36
+ // Citations are highly interdependent -- so we need to format them all
37
+ // together before laying out the document.
38
+ async formatCitations() {
39
+ const citationstyle = await this.csl.getStyle(
40
+ this.docSettings.citationstyle
41
+ )
42
+
43
+ const modStyle = JSON.parse(JSON.stringify(citationstyle))
44
+ const citationLayout = modStyle.children
45
+ .find(section => section.name === "citation")
46
+ .children.find(section => section.name === "layout").attrs
47
+ const origCitationLayout = JSON.parse(JSON.stringify(citationLayout))
48
+ citationLayout.prefix = "{{prefix}}"
49
+ citationLayout.suffix = "{{suffix}}"
50
+ citationLayout.delimiter = "{{delimiter}}"
51
+ this.citFm = new FormatCitations(
52
+ this.csl,
53
+ this.citInfos,
54
+ modStyle,
55
+ "",
56
+ this.bibDB,
57
+ false,
58
+ this.docSettings.language
59
+ )
60
+
61
+ await this.citFm.init()
62
+
63
+ // We need to add links to the bibliography items. And there may be more than one work cited
64
+ // so we need to first split, then add the links and eventually put the citation back together
65
+ // again.
66
+ // The IDs used in the html bibliography are 1 and up in this order
67
+ this.citFm.bibliography[0].entry_ids.forEach(
68
+ (id, index) => (this.htmlIdConvert[id] = index + 1)
69
+ )
70
+ this.citationTexts = this.citFm.citationTexts.map((ref, index) => {
71
+ const content = ref
72
+ .split("{{delimiter}}")
73
+ .map((citationText, conIndex) => {
74
+ const prefixSplit = citationText.split("{{prefix}}")
75
+ const prefix =
76
+ prefixSplit.length > 1
77
+ ? prefixSplit.shift() +
78
+ (origCitationLayout.prefix || "")
79
+ : ""
80
+ citationText = prefixSplit[0]
81
+ const suffixSplit = citationText.split("{{suffix}}")
82
+ const suffix =
83
+ suffixSplit.length > 1
84
+ ? (origCitationLayout.suffix || "") +
85
+ suffixSplit.pop()
86
+ : ""
87
+ citationText = suffixSplit[0]
88
+ const citId =
89
+ this.citFm.citations[index].sortedItems[conIndex][1].id
90
+ const htmlId = this.htmlIdConvert[citId]
91
+ return `${prefix}<a class="bibliography" href="#ref-${htmlId}">${citationText}</a>${suffix}`
92
+ })
93
+ .join(origCitationLayout.delimiter || "")
94
+ return content
95
+ })
96
+
97
+ if (
98
+ this.citFm.bibliography?.length &&
99
+ this.citFm.bibliography[0].entry_ids.length
100
+ ) {
101
+ this.assembleBib()
102
+ }
103
+ }
104
+
105
+ assembleBib() {
106
+ const bibliographyHeader =
107
+ this.docSettings.bibliography_header[this.docSettings.language] ||
108
+ BIBLIOGRAPHY_HEADERS[this.docSettings.language]
109
+ let bibHTML = `<h1 class="doc-bibliography-header">${escapeText(bibliographyHeader)}</h1>`
110
+ bibHTML += this.citFm.bibliography[0].bibstart
111
+ bibHTML += this.citFm.bibliography[1]
112
+ .map(
113
+ (reference, index) =>
114
+ `<div id="ref-${index + 1}">${reference}</div>`
115
+ )
116
+ .join("")
117
+ bibHTML += this.citFm.bibliography[0].bibend
118
+ this.bibHTML = bibHTML
119
+ this.bibCSS = this.citFm.bibCSS
120
+ }
121
+ }