@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,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,8 @@
1
+ export const readMe = `Unzip the contents of this file to a folder and convert it then to another format
2
+ using pandoc like this:
3
+
4
+ pandoc document.json -o document.html
5
+
6
+ Replace "document.html" with other formats you may want.
7
+
8
+ `
@@ -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,9 @@
1
+ export const createSlug = str => {
2
+ if (str === "") {
3
+ str = gettext("Untitled")
4
+ }
5
+ str = str.replace(/[^a-zA-Z0-9\s]/g, "")
6
+ str = str.toLowerCase()
7
+ str = str.replace(/\s/g, "-")
8
+ return str
9
+ }
@@ -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
+ }