@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.
- package/LICENSE +661 -0
- package/README.md +16 -0
- package/jest.config.js +23 -0
- package/package.json +59 -0
- package/schema.json +1 -0
- package/scripts/export-schema.js +16 -0
- package/src/bibliography/common.js +92 -0
- package/src/bibliography/csl_bib.js +139 -0
- package/src/citations/citeproc_sys.js +42 -0
- package/src/citations/format.js +194 -0
- package/src/common/blob.js +10 -0
- package/src/common/file.js +25 -0
- package/src/common/index.js +12 -0
- package/src/common/network.js +79 -0
- package/src/common/text.js +44 -0
- package/src/editor/e2ee/encryptor.js +228 -0
- package/src/exporter/docx/citations.js +177 -0
- package/src/exporter/docx/comments.js +165 -0
- package/src/exporter/docx/footnotes.js +240 -0
- package/src/exporter/docx/images.js +101 -0
- package/src/exporter/docx/index.js +185 -0
- package/src/exporter/docx/lists.js +260 -0
- package/src/exporter/docx/math.js +46 -0
- package/src/exporter/docx/metadata.js +289 -0
- package/src/exporter/docx/rels.js +193 -0
- package/src/exporter/docx/render.js +941 -0
- package/src/exporter/docx/richtext.js +1182 -0
- package/src/exporter/docx/tables.js +112 -0
- package/src/exporter/docx/tools.js +50 -0
- package/src/exporter/epub/index.js +142 -0
- package/src/exporter/epub/templates.js +140 -0
- package/src/exporter/epub/tools.js +96 -0
- package/src/exporter/html/citations.js +121 -0
- package/src/exporter/html/convert.js +813 -0
- package/src/exporter/html/index.js +192 -0
- package/src/exporter/html/templates.js +34 -0
- package/src/exporter/html/tools.js +50 -0
- package/src/exporter/jats/bibliography.js +183 -0
- package/src/exporter/jats/citations.js +109 -0
- package/src/exporter/jats/convert.js +871 -0
- package/src/exporter/jats/index.js +92 -0
- package/src/exporter/jats/templates.js +35 -0
- package/src/exporter/jats/text.js +72 -0
- package/src/exporter/latex/convert.js +934 -0
- package/src/exporter/latex/escape_latex.js +21 -0
- package/src/exporter/latex/index.js +74 -0
- package/src/exporter/latex/readme.js +22 -0
- package/src/exporter/native/shrink.js +132 -0
- package/src/exporter/odt/citations.js +101 -0
- package/src/exporter/odt/footnotes.js +147 -0
- package/src/exporter/odt/images.js +115 -0
- package/src/exporter/odt/index.js +156 -0
- package/src/exporter/odt/math.js +57 -0
- package/src/exporter/odt/metadata.js +251 -0
- package/src/exporter/odt/render.js +806 -0
- package/src/exporter/odt/richtext.js +865 -0
- package/src/exporter/odt/styles.js +387 -0
- package/src/exporter/odt/track.js +68 -0
- package/src/exporter/pandoc/citations.js +98 -0
- package/src/exporter/pandoc/convert.js +1017 -0
- package/src/exporter/pandoc/index.js +92 -0
- package/src/exporter/pandoc/readme.js +8 -0
- package/src/exporter/pandoc/tools.js +51 -0
- package/src/exporter/print/index.js +177 -0
- package/src/exporter/tools/doc_content.js +144 -0
- package/src/exporter/tools/file.js +9 -0
- package/src/exporter/tools/json.js +73 -0
- package/src/exporter/tools/svg.js +29 -0
- package/src/exporter/tools/xml.js +531 -0
- package/src/exporter/tools/xml_zip.js +95 -0
- package/src/exporter/tools/zip.js +90 -0
- package/src/exporter/tools/zotero_csl.js +93 -0
- package/src/importer/citations.js +129 -0
- package/src/importer/docx/citations.js +123 -0
- package/src/importer/docx/convert.js +1427 -0
- package/src/importer/docx/helpers.js +9 -0
- package/src/importer/docx/omml2mathml.js +1448 -0
- package/src/importer/docx/parse.js +735 -0
- package/src/importer/native/get_images.js +76 -0
- package/src/importer/native/update.js +29 -0
- package/src/importer/odt/citations.js +87 -0
- package/src/importer/odt/convert.js +1855 -0
- package/src/importer/pandoc/convert.js +884 -0
- package/src/importer/pandoc/helpers.js +84 -0
- package/src/importer/zip_analyzer.js +102 -0
- package/src/index.js +1 -0
- package/src/mathlive/opf_includes.js +24 -0
- package/src/schema/common/annotate.js +76 -0
- package/src/schema/common/base.js +118 -0
- package/src/schema/common/citation.js +62 -0
- package/src/schema/common/equation.js +31 -0
- package/src/schema/common/figure.js +190 -0
- package/src/schema/common/heading.js +43 -0
- package/src/schema/common/index.js +40 -0
- package/src/schema/common/list.js +95 -0
- package/src/schema/common/reference.js +100 -0
- package/src/schema/common/table.js +103 -0
- package/src/schema/common/track.js +190 -0
- package/src/schema/const.js +58 -0
- package/src/schema/convert.js +1272 -0
- package/src/schema/document/content.js +187 -0
- package/src/schema/document/index.js +117 -0
- package/src/schema/document/structure.js +452 -0
- package/src/schema/export.js +21 -0
- package/src/schema/footnotes.js +126 -0
- package/src/schema/footnotes_convert.js +31 -0
- package/src/schema/i18n.js +595 -0
- package/src/schema/index.js +5 -0
- package/src/schema/mini_json.js +61 -0
- 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
|
+
}
|