@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,92 @@
|
|
|
1
|
+
import {BibLatexExporter} from "biblatex-csl-converter"
|
|
2
|
+
import download from "downloadjs"
|
|
3
|
+
|
|
4
|
+
import {shortFileTitle} from "../../common/index.js"
|
|
5
|
+
import {fixTables, removeHidden} from "../tools/doc_content.js"
|
|
6
|
+
import {createSlug} from "../tools/file.js"
|
|
7
|
+
import {ZipFileCreator} from "../tools/zip.js"
|
|
8
|
+
import {PandocExporterCitations} from "./citations.js"
|
|
9
|
+
import {PandocExporterConvert} from "./convert.js"
|
|
10
|
+
import {readMe} from "./readme.js"
|
|
11
|
+
/*
|
|
12
|
+
Exporter to Pandoc JSON
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export class PandocExporter {
|
|
16
|
+
constructor(doc, bibDB, imageDB, csl, updated) {
|
|
17
|
+
this.doc = doc
|
|
18
|
+
this.docTitle = shortFileTitle(this.doc.title, this.doc.path)
|
|
19
|
+
this.bibDB = bibDB
|
|
20
|
+
this.imageDB = imageDB
|
|
21
|
+
this.csl = csl
|
|
22
|
+
this.updated = updated
|
|
23
|
+
|
|
24
|
+
this.docContent = false
|
|
25
|
+
this.zipFileName = ""
|
|
26
|
+
this.textFiles = []
|
|
27
|
+
this.httpFiles = []
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
init() {
|
|
31
|
+
//this.docContent = removeHidden(this.doc.content) //
|
|
32
|
+
this.docContent = fixTables(removeHidden(this.doc.content))
|
|
33
|
+
this.citations = new PandocExporterCitations(
|
|
34
|
+
this,
|
|
35
|
+
this.bibDB,
|
|
36
|
+
this.csl,
|
|
37
|
+
this.docContent
|
|
38
|
+
)
|
|
39
|
+
this.converter = new PandocExporterConvert(
|
|
40
|
+
this,
|
|
41
|
+
this.imageDB,
|
|
42
|
+
this.bibDB,
|
|
43
|
+
this.doc.settings
|
|
44
|
+
)
|
|
45
|
+
return this.citations.init().then(() => {
|
|
46
|
+
this.conversion = this.converter.init(this.docContent)
|
|
47
|
+
if (Object.keys(this.conversion.usedBibDB).length > 0) {
|
|
48
|
+
const bibExport = new BibLatexExporter(
|
|
49
|
+
this.conversion.usedBibDB
|
|
50
|
+
)
|
|
51
|
+
this.textFiles.push({
|
|
52
|
+
filename: "bibliography.bib",
|
|
53
|
+
contents: bibExport.parse()
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.conversion.imageIds.forEach(id => {
|
|
58
|
+
this.httpFiles.push({
|
|
59
|
+
filename: this.imageDB.db[id].image.split("/").pop(),
|
|
60
|
+
url: this.imageDB.db[id].image
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
return this.createExport()
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
createExport() {
|
|
68
|
+
// Override this function if adding a conversion-through-pandoc step.
|
|
69
|
+
this.textFiles.push({
|
|
70
|
+
filename: "document.json",
|
|
71
|
+
contents: JSON.stringify(this.conversion.json, null, 4)
|
|
72
|
+
})
|
|
73
|
+
this.textFiles.push({filename: "README.txt", contents: readMe})
|
|
74
|
+
this.zipFileName = `${createSlug(this.docTitle)}.pandoc.json.zip`
|
|
75
|
+
return this.createDownload()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
createDownload() {
|
|
79
|
+
// This creates a ZIP file with JSON sources included and then returns a promise for the download of the file.
|
|
80
|
+
const zipper = new ZipFileCreator(
|
|
81
|
+
this.textFiles,
|
|
82
|
+
this.httpFiles,
|
|
83
|
+
undefined,
|
|
84
|
+
undefined,
|
|
85
|
+
this.updated
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
return zipper
|
|
89
|
+
.init()
|
|
90
|
+
.then(blob => download(blob, this.zipFileName, "application/zip"))
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export const convertText = text => {
|
|
2
|
+
const textContent = []
|
|
3
|
+
if (!text.length) {
|
|
4
|
+
return []
|
|
5
|
+
}
|
|
6
|
+
const words = text.split(" ")
|
|
7
|
+
words.forEach((c, index) => {
|
|
8
|
+
if (c) {
|
|
9
|
+
textContent.push({
|
|
10
|
+
t: "Str",
|
|
11
|
+
c
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
if (index < words.length - 1) {
|
|
15
|
+
textContent.push({
|
|
16
|
+
t: "Space"
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
return textContent
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const convertContributor = contributor => {
|
|
24
|
+
const contributorContent = []
|
|
25
|
+
if (contributor.firstname || contributor.lastname) {
|
|
26
|
+
const nameParts = []
|
|
27
|
+
if (contributor.lastname) {
|
|
28
|
+
nameParts.push(contributor.lastname)
|
|
29
|
+
}
|
|
30
|
+
if (contributor.firstname) {
|
|
31
|
+
nameParts.push(contributor.firstname)
|
|
32
|
+
}
|
|
33
|
+
contributorContent.push(...convertText(nameParts.join(" ")))
|
|
34
|
+
} else if (contributor.institution) {
|
|
35
|
+
contributorContent.push(...convertText(contributor.institution))
|
|
36
|
+
}
|
|
37
|
+
if (contributor.email) {
|
|
38
|
+
contributorContent.push({
|
|
39
|
+
t: "Note",
|
|
40
|
+
c: [
|
|
41
|
+
{
|
|
42
|
+
t: "Para",
|
|
43
|
+
c: convertText(contributor.email)
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
return contributorContent.length
|
|
49
|
+
? {t: "MetaInlines", c: contributorContent}
|
|
50
|
+
: false
|
|
51
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import {printHTML} from "@vivliostyle/print"
|
|
2
|
+
import {addAlert, shortFileTitle} from "../../common/index.js"
|
|
3
|
+
import {PAPER_SIZES} from "../../schema/const.js"
|
|
4
|
+
import {HTMLExporter} from "../html/index.js"
|
|
5
|
+
import {HTMLExporterConvert} from "../html/convert.js"
|
|
6
|
+
import {removeHidden} from "../tools/doc_content.js"
|
|
7
|
+
|
|
8
|
+
export class PrintExporter extends HTMLExporter {
|
|
9
|
+
constructor(doc, bibDB, imageDB, csl, updated, documentStyles) {
|
|
10
|
+
super(doc, bibDB, imageDB, csl, updated, documentStyles, {
|
|
11
|
+
relativeUrls: false
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async init() {
|
|
16
|
+
addAlert(
|
|
17
|
+
"info",
|
|
18
|
+
`${shortFileTitle(this.doc.title, this.doc.path)}: ${gettext("Printing has been initiated.")}`
|
|
19
|
+
)
|
|
20
|
+
this.docContent = removeHidden(this.doc.content)
|
|
21
|
+
|
|
22
|
+
const styleSheets = [
|
|
23
|
+
{url: staticUrl("css/document.css")},
|
|
24
|
+
{
|
|
25
|
+
contents: `a.footnote, a.affiliation {
|
|
26
|
+
-adapt-template: url(data:application/xml,${encodeURI(
|
|
27
|
+
'<html xmlns="http://www.w3.org/1999/xhtml" xmlns:s="http://www.pyroxy.com/ns/shadow"><head><style>.footnote-content{float:footnote}</style></head><body><s:template id="footnote"><s:content/><s:include class="footnote-content"/></s:template></body></html>#footnote'
|
|
28
|
+
)});
|
|
29
|
+
text-decoration: none;
|
|
30
|
+
color: inherit;
|
|
31
|
+
vertical-align: baseline;
|
|
32
|
+
font-size: 70%;
|
|
33
|
+
position: relative;
|
|
34
|
+
top: -0.3em;
|
|
35
|
+
}
|
|
36
|
+
aside.footnote label:first-child, aside.footnote *:nth-child(2),
|
|
37
|
+
aside.affiliation label:first-child, aside.affiliation *:nth-child(2) {
|
|
38
|
+
display: inline;
|
|
39
|
+
}
|
|
40
|
+
aside.footnote label:first-child:after,
|
|
41
|
+
aside.affiliation label:first-child:after {
|
|
42
|
+
content: '. '
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
body, section[role=doc-footnotes] {
|
|
46
|
+
counter-reset: cat-figure cat-equation cat-photo cat-table footnote-counter footnote-marker-counter;
|
|
47
|
+
}
|
|
48
|
+
section#affiliations, section#footnotes {
|
|
49
|
+
display: none;
|
|
50
|
+
}
|
|
51
|
+
section:footnote-content {
|
|
52
|
+
display: block;
|
|
53
|
+
font-size: small;
|
|
54
|
+
font-style: normal;
|
|
55
|
+
font-weight: normal;
|
|
56
|
+
text-decoration: none;
|
|
57
|
+
text-indent: 0;
|
|
58
|
+
text-align: initial;
|
|
59
|
+
}
|
|
60
|
+
.table-of-contents a {
|
|
61
|
+
display: inline-flex;
|
|
62
|
+
width: 100%;
|
|
63
|
+
text-decoration: none;
|
|
64
|
+
color: currentColor;
|
|
65
|
+
break-inside: avoid;
|
|
66
|
+
align-items: baseline;
|
|
67
|
+
}
|
|
68
|
+
.table-of-contents a::before {
|
|
69
|
+
margin-left: 1px;
|
|
70
|
+
margin-right: 1px;
|
|
71
|
+
border-bottom: solid 1px lightgray;
|
|
72
|
+
content: "";
|
|
73
|
+
order: 1;
|
|
74
|
+
flex: auto;
|
|
75
|
+
}
|
|
76
|
+
.table-of-contents a::after {
|
|
77
|
+
text-align: right;
|
|
78
|
+
content: target-counter(attr(href, url), page);
|
|
79
|
+
align-self: flex-end;
|
|
80
|
+
flex: none;
|
|
81
|
+
order: 2;
|
|
82
|
+
}
|
|
83
|
+
body {
|
|
84
|
+
background-color: white;
|
|
85
|
+
}
|
|
86
|
+
@page {
|
|
87
|
+
size: ${PAPER_SIZES.find(size => size[0] === this.doc.settings.papersize)[1]};
|
|
88
|
+
@top-center {
|
|
89
|
+
content: env(doc-title);
|
|
90
|
+
}
|
|
91
|
+
@bottom-center {
|
|
92
|
+
content: counter(page);
|
|
93
|
+
}
|
|
94
|
+
}`
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
const docStyle = this.getDocStyle(this.doc)
|
|
99
|
+
|
|
100
|
+
if (docStyle) {
|
|
101
|
+
styleSheets.push(docStyle)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
await Promise.all(
|
|
105
|
+
styleSheets.map(async sheet => await this.loadStyle(sheet))
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
this.converter = new HTMLExporterConvert(
|
|
109
|
+
this.docTitle,
|
|
110
|
+
this.doc.settings,
|
|
111
|
+
this.docContent,
|
|
112
|
+
this.htmlExportTemplate,
|
|
113
|
+
this.imageDB,
|
|
114
|
+
this.bibDB,
|
|
115
|
+
this.csl,
|
|
116
|
+
styleSheets,
|
|
117
|
+
{
|
|
118
|
+
relativeUrls: false
|
|
119
|
+
}
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
const {html, metaData} = await this.converter.init()
|
|
123
|
+
|
|
124
|
+
const config = {title: metaData.title}
|
|
125
|
+
|
|
126
|
+
if (navigator.userAgent.includes("Gecko/")) {
|
|
127
|
+
// Firefox has issues printing images when in iframe. This workaround can be
|
|
128
|
+
// removed once that has been fixed. TODO: Add gecko bug number if there is one.
|
|
129
|
+
config.printCallback = iframeWin => {
|
|
130
|
+
const oldBody = document.body
|
|
131
|
+
document.body.parentElement.dataset.vivliostylePaginated = true
|
|
132
|
+
document.body = iframeWin.document.body
|
|
133
|
+
document.body
|
|
134
|
+
.querySelectorAll("figure, table")
|
|
135
|
+
.forEach(el => delete el.dataset.category)
|
|
136
|
+
iframeWin.document
|
|
137
|
+
.querySelectorAll("style")
|
|
138
|
+
.forEach(el => document.body.appendChild(el))
|
|
139
|
+
const backgroundStyle = document.createElement("style")
|
|
140
|
+
backgroundStyle.innerHTML = "body {background-color: white;}"
|
|
141
|
+
document.body.appendChild(backgroundStyle)
|
|
142
|
+
window.print()
|
|
143
|
+
document.body = oldBody
|
|
144
|
+
delete document.body.parentElement.dataset.vivliostylePaginated
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return printHTML(html, config)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
getDocStyle(doc) {
|
|
151
|
+
// Override the default as we need to use the original URLs in print.
|
|
152
|
+
const docStyle = this.documentStyles.find(
|
|
153
|
+
docStyle => docStyle.slug === doc.settings.documentstyle
|
|
154
|
+
)
|
|
155
|
+
if (!docStyle) {
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let contents = docStyle.contents
|
|
160
|
+
docStyle.documentstylefile_set.forEach(
|
|
161
|
+
([url, filename]) =>
|
|
162
|
+
(contents = contents.replace(
|
|
163
|
+
new RegExp(filename, "g"),
|
|
164
|
+
new URL(url, window.location).href
|
|
165
|
+
))
|
|
166
|
+
)
|
|
167
|
+
return {contents}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
loadStyle(sheet) {
|
|
171
|
+
if (sheet.url) {
|
|
172
|
+
sheet.filename = sheet.url
|
|
173
|
+
delete sheet.url
|
|
174
|
+
}
|
|
175
|
+
return Promise.resolve()
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// Return a json that is the same as the existing json, but with all parts
|
|
2
|
+
// marked as hidden removed.
|
|
3
|
+
|
|
4
|
+
export const removeHidden = (
|
|
5
|
+
node,
|
|
6
|
+
// Whether to leave the outer part of the removed node.
|
|
7
|
+
// True for tree-walking exporters, false for DOM-changing exporters.
|
|
8
|
+
leaveStub = true,
|
|
9
|
+
removeTableCaption = false,
|
|
10
|
+
removeTableCaptionText = false,
|
|
11
|
+
removeFigureCaption = false,
|
|
12
|
+
removeFigureCaptionText = false
|
|
13
|
+
) => {
|
|
14
|
+
const returnNode = {}
|
|
15
|
+
|
|
16
|
+
Object.keys(node).forEach(key => {
|
|
17
|
+
if (key !== "content") {
|
|
18
|
+
returnNode[key] = node[key]
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
if (node.attrs?.hidden) {
|
|
22
|
+
return leaveStub ? returnNode : false
|
|
23
|
+
} else if ("table_caption" === node.type) {
|
|
24
|
+
if (removeTableCaption) {
|
|
25
|
+
return leaveStub ? returnNode : false
|
|
26
|
+
} else if (removeTableCaptionText) {
|
|
27
|
+
return returnNode
|
|
28
|
+
}
|
|
29
|
+
} else if ("figure_caption" === node.type) {
|
|
30
|
+
if (removeFigureCaption) {
|
|
31
|
+
return leaveStub ? returnNode : false
|
|
32
|
+
} else if (removeFigureCaptionText) {
|
|
33
|
+
return returnNode
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (node.attrs?.caption === false) {
|
|
37
|
+
if (node.attrs.category === "none") {
|
|
38
|
+
if (node.type === "figure") {
|
|
39
|
+
removeFigureCaption = true
|
|
40
|
+
} else {
|
|
41
|
+
removeTableCaption = true
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
if (node.type === "figure") {
|
|
45
|
+
removeFigureCaptionText = true
|
|
46
|
+
} else {
|
|
47
|
+
removeTableCaptionText = true
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (node.content) {
|
|
52
|
+
returnNode.content = []
|
|
53
|
+
node.content.forEach(child => {
|
|
54
|
+
const cleanedChild = removeHidden(
|
|
55
|
+
child,
|
|
56
|
+
leaveStub,
|
|
57
|
+
removeTableCaption,
|
|
58
|
+
removeTableCaptionText,
|
|
59
|
+
removeFigureCaption,
|
|
60
|
+
removeFigureCaptionText
|
|
61
|
+
)
|
|
62
|
+
if (cleanedChild) {
|
|
63
|
+
returnNode.content.push(cleanedChild)
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
return returnNode
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const descendantNodes = node => {
|
|
71
|
+
let returnValue = [node]
|
|
72
|
+
if (node.content) {
|
|
73
|
+
node.content.forEach(childNode => {
|
|
74
|
+
returnValue = returnValue.concat(descendantNodes(childNode))
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
return returnValue
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const textContent = node =>
|
|
81
|
+
descendantNodes(node).reduce((returnString, subNode) => {
|
|
82
|
+
if (subNode.text) {
|
|
83
|
+
returnString += subNode.text
|
|
84
|
+
}
|
|
85
|
+
return returnString
|
|
86
|
+
}, "")
|
|
87
|
+
|
|
88
|
+
// PM/HTML don't have cells that have been covered, but in ODT/DOCX, these cells
|
|
89
|
+
// need to be present. So we add them.
|
|
90
|
+
|
|
91
|
+
const addCoveredTableCells = node => {
|
|
92
|
+
const columns = node.content[0].content.reduce(
|
|
93
|
+
(columns, cell) => columns + cell.attrs.colspan,
|
|
94
|
+
0
|
|
95
|
+
)
|
|
96
|
+
const rows = node.content.length
|
|
97
|
+
// Add empty cells for col/rowspan
|
|
98
|
+
const fixedTableMatrix = Array.apply(0, {length: rows}).map(_item => ({
|
|
99
|
+
type: "table_row",
|
|
100
|
+
content: Array.apply(0, {length: columns})
|
|
101
|
+
}))
|
|
102
|
+
let rowIndex = -1
|
|
103
|
+
node.content.forEach(row => {
|
|
104
|
+
let columnIndex = 0
|
|
105
|
+
rowIndex++
|
|
106
|
+
if (!row.content) {
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
row.content.forEach(cell => {
|
|
110
|
+
while (fixedTableMatrix[rowIndex].content[columnIndex]) {
|
|
111
|
+
columnIndex++
|
|
112
|
+
}
|
|
113
|
+
for (let i = 0; i < cell.attrs.rowspan; i++) {
|
|
114
|
+
for (let j = 0; j < cell.attrs.colspan; j++) {
|
|
115
|
+
let fixedCell
|
|
116
|
+
if (i === 0 && j === 0) {
|
|
117
|
+
fixedCell = cell
|
|
118
|
+
} else {
|
|
119
|
+
fixedCell = {
|
|
120
|
+
type: "table_cell",
|
|
121
|
+
attrs: {
|
|
122
|
+
rowspan: cell.attrs.rowspan > 1 ? 0 : 1,
|
|
123
|
+
colspan: cell.attrs.colspan > 1 ? 0 : 1
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
fixedTableMatrix[rowIndex + i].content[columnIndex + j] =
|
|
128
|
+
fixedCell
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
node.content = fixedTableMatrix
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export const fixTables = node => {
|
|
137
|
+
if (node.type === "table_body") {
|
|
138
|
+
addCoveredTableCells(node)
|
|
139
|
+
}
|
|
140
|
+
if (node.content) {
|
|
141
|
+
node.content.forEach(child => fixTables(child))
|
|
142
|
+
}
|
|
143
|
+
return node
|
|
144
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/** Same functionality as objToNode/nodeToObj in diffDOM.js, but also offers output in XHTML format (obj2Node) and without form support. */
|
|
2
|
+
export const obj2Node = (obj, docType) => {
|
|
3
|
+
let parser
|
|
4
|
+
if (obj === undefined) {
|
|
5
|
+
return false
|
|
6
|
+
}
|
|
7
|
+
if (docType === "xhtml") {
|
|
8
|
+
parser = new window.DOMParser().parseFromString("<xml/>", "text/xml")
|
|
9
|
+
} else {
|
|
10
|
+
parser = document
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function inner(obj, insideSvg) {
|
|
14
|
+
let node
|
|
15
|
+
if (obj.hasOwnProperty("t")) {
|
|
16
|
+
node = parser.createTextNode(obj.t)
|
|
17
|
+
} else if (obj.hasOwnProperty("co")) {
|
|
18
|
+
node = parser.createComment(obj.co)
|
|
19
|
+
} else {
|
|
20
|
+
if (obj.nn === "svg" || insideSvg) {
|
|
21
|
+
node = parser.createElementNS(
|
|
22
|
+
"http://www.w3.org/2000/svg",
|
|
23
|
+
obj.nn
|
|
24
|
+
)
|
|
25
|
+
insideSvg = true
|
|
26
|
+
} else if (obj.nn === "script") {
|
|
27
|
+
// Do not allow scripts
|
|
28
|
+
return parser.createTextNode("")
|
|
29
|
+
} else {
|
|
30
|
+
node = parser.createElement(obj.nn.toLowerCase())
|
|
31
|
+
}
|
|
32
|
+
if (obj.a) {
|
|
33
|
+
for (let i = 0; i < obj.a.length; i++) {
|
|
34
|
+
node.setAttribute(obj.a[i][0], obj.a[i][1])
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (obj.c) {
|
|
38
|
+
for (let i = 0; i < obj.c.length; i++) {
|
|
39
|
+
node.appendChild(inner(obj.c[i], insideSvg))
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return node
|
|
44
|
+
}
|
|
45
|
+
return inner(obj)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const node2Obj = node => {
|
|
49
|
+
const obj = {}
|
|
50
|
+
|
|
51
|
+
if (node.nodeType === 3) {
|
|
52
|
+
obj.t = node.data
|
|
53
|
+
} else if (node.nodeType === 8) {
|
|
54
|
+
obj.co = node.data
|
|
55
|
+
} else {
|
|
56
|
+
obj.nn = node.nodeName
|
|
57
|
+
if (node.attributes?.length > 0) {
|
|
58
|
+
obj.a = []
|
|
59
|
+
for (let i = 0; i < node.attributes.length; i++) {
|
|
60
|
+
obj.a.push([node.attributes[i].name, node.attributes[i].value])
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (node.childNodes?.length > 0) {
|
|
64
|
+
obj.c = []
|
|
65
|
+
for (let i = 0; i < node.childNodes.length; i++) {
|
|
66
|
+
if (node.childNodes[i]) {
|
|
67
|
+
obj.c.push(node2Obj(node.childNodes[i]))
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return obj
|
|
73
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {convertDataURIToBlob} from "../../common/index.js"
|
|
2
|
+
|
|
3
|
+
export function svg2png(blob) {
|
|
4
|
+
const img = document.createElement("img")
|
|
5
|
+
const src = URL.createObjectURL(blob)
|
|
6
|
+
img.src = src
|
|
7
|
+
img.setAttribute("style", "position:fixed;left:-200vw;")
|
|
8
|
+
return new Promise(resolve => {
|
|
9
|
+
img.onload = function onload() {
|
|
10
|
+
const canvas = document.createElement("canvas")
|
|
11
|
+
const ctx = canvas.getContext("2d")
|
|
12
|
+
const ratio = Math.min(
|
|
13
|
+
Math.min(img.width, img.height) / img.width,
|
|
14
|
+
Math.min(img.width, img.height) / img.height
|
|
15
|
+
)
|
|
16
|
+
const width = img.width * ratio
|
|
17
|
+
const height = img.height * ratio
|
|
18
|
+
canvas.width = width
|
|
19
|
+
canvas.height = height
|
|
20
|
+
ctx.drawImage(img, 0, 0, width, height)
|
|
21
|
+
const src = canvas.toDataURL("image/png")
|
|
22
|
+
img.parentElement.removeChild(img)
|
|
23
|
+
URL.revokeObjectURL(src)
|
|
24
|
+
const pngBlob = convertDataURIToBlob(src)
|
|
25
|
+
resolve({blob: pngBlob, width, height})
|
|
26
|
+
}
|
|
27
|
+
document.body.appendChild(img)
|
|
28
|
+
})
|
|
29
|
+
}
|